diff --git a/notes/performance-moonshot/MAX-TESTING.md b/notes/performance-moonshot/MAX-TESTING.md new file mode 100644 index 00000000..b7afc9b1 --- /dev/null +++ b/notes/performance-moonshot/MAX-TESTING.md @@ -0,0 +1,27 @@ +# Max 9 test procedure + +General instructions. Read this before you start. + +- Start each test from a terminal with the command shown in the table. `max9` is the Max launcher command. +- The log used is this file: `~/.wine-ableton/drive_c/users/theo/AppData/Roaming/Cycling '74/Max 9/Logs/Max.log`. To search it, run: `grep "TEXT" ""`. +- The harness patches are in the repository folder `tools/max-harness/`. Use the full path to a patch in each command. +- The community patches (jit.mo-Worms, jit.mo-Alphablend, cpu-tester-gen, waveshaper, mc.karplus, Big Scope best practices) are not in the repository. Get them with the source links in `tools/max-harness/DOWNLOADED-PATCHES-MANIFEST.md`. The Node test also needs the file `outlet-methods.js` in the same folder as its patch; the manifest gives its source. +- If Max is open when a test tells you to start it fresh, do MAX-T14 first to stop Max completely. +- Do each test in order. One test at a time. Record the result of each test. + +| Test | Name | Max file | Description | Instructions | Pass conditions | +|---|---|---|---|---|---| +| MAX-T01 | Start and draw check | `cycle~.maxhelp` (comes with Max, in `resources/help/msp/` in the Max program folder) | Max starts and draws a patch window correctly. | 1. Run: `max9 ""`.
2. Wait 20 seconds.
3. Look at the window. | A window with the name cycle~.maxhelp is open. The text in it is sharp and complete. The boxes and the lines between them are visible. Max did not crash. | +| MAX-T02 | Second file goes to the same Max | any small patch, for example `t01-dsp-probe.maxpat` | A second start of the launcher sends the file to the Max that already runs. | 1. Keep Max open from MAX-T01.
2. Run: `max9 ""`.
3. Wait 10 seconds. | The patch opens as a new window in the same Max. There is only one Max program in the process list (`pgrep -c Max.exe` shows 1). | +| MAX-T03 | Audio engine start and driver list | `t01-dsp-probe.maxpat` | The patch lists the audio drivers and starts the audio engine. It runs by itself when it opens. | 1. Run: `max9 ""`.
2. Wait 15 seconds.
3. Search the log for `HARNESS t01` and for `PipeASIO`. | The log shows `HARNESS t01 loaded`. The log shows a driver list that contains `ad_asio "PipeASIO Driver"`. No error lines about the audio engine appear after the marker. | +| MAX-T04 | Switch to PipeASIO | `t02-pipeasio-switch.maxpat` | The patch switches the audio driver to PipeASIO while Max runs, then starts the audio engine. It runs by itself when it opens. | 1. Run: `max9 ""`.
2. Wait 20 seconds.
3. Search the log for `HARNESS t02`.
4. Run: `pw-dump \| grep '"Max"'`. | The log shows `HARNESS t02 loaded`. The pw-dump output shows a node with the name Max. In Max, the Audio Status window shows the driver ad_asio with the device PipeASIO Driver. Max did not crash. | +| MAX-T05 | Web view (CEF) | `t03-jweb-cef.maxpat` | The patch shows a web page inside a patch window. This tests the built-in browser component. | 1. Run: `max9 ""`.
2. Wait 20 seconds.
3. Look at the large area in the patch window. | The area shows the page "Example Domain" with black text on a light background. An empty gray area is a fail. | +| MAX-T06 | 3D graphics | `jit.mo-Worms.maxpat` | The patch opens a separate render window and draws moving colored lines with OpenGL. | 1. Run: `max9 ""`.
2. Wait 15 seconds.
3. Look at the render window (its name is one letter: w).
4. Search the log for `OpenGL`. | The render window shows colored lines that move all the time. The log shows a line with `OpenGL Version`. Max did not crash. | +| MAX-T07 | Video playback | `jit.mo-Alphablend.maxpat` | The patch plays two movie files that come with Max and mixes them in a render window. | 1. Run: `max9 ""`.
2. Wait 20 seconds.
3. Look at the render window. | The render window shows moving video: ducks on water, mixed with a second picture. A black or frozen window is a fail. | +| MAX-T08 | Node for Max | `t06-node-auto.maxpat` (must be in one folder together with `outlet-methods.js`) | The patch starts a JavaScript program in the bundled Node.js, sends it a message, and receives the answer. It runs by itself when it opens. | 1. Run: `max9 ""`.
2. Wait 25 seconds.
3. Search the log for `example2`. | The log shows `HARNESS t06 node loaded`, then `start success`, then the answer line `example2 4 5 6 seven`. | +| MAX-T09 | gen~ code build | `cpu-tester-gen.maxpat`, then `waveshaper.maxpat` (the first one also needs `cpu-tester-gen-sub.maxpat` in the same folder) | gen~ builds machine code inside Max when a patch loads. The first patch builds 64 gen~ objects. | 1. Run: `max9 ""`.
2. Wait 20 seconds.
3. Run: `max9 ""`.
4. Wait 15 seconds.
5. Search the log for `gen` and for `error`. | Both patches open. The log shows no error lines that contain gen. Max did not crash. | +| MAX-T10 | Many-channel audio objects | `mc.karplus.maxpat` | The patch loads multichannel (mc) objects. | 1. Run: `max9 ""`.
2. Wait 15 seconds.
3. Search the log for `error`. | The patch opens. No new error lines appear in the log. Max did not crash. | +| MAX-T11 | Dense interface | `Big Scope best practices.maxpat` | The patch loads a display object that redraws all the time. Caution: do not start the audio engine with this patch open. It has a direct sound output. | 1. Run: `max9 ""`.
2. Wait 15 seconds.
3. Look at the window. Do not click the speaker button. | The patch opens and draws completely. Max stays responsive: the window moves when you drag its title bar. Max did not crash. | +| MAX-T12 | MIDI ports | `t09-midi-probe.maxpat` | The patch lists the MIDI ports. It runs by itself when it opens. Connect a MIDI device by USB before you start Max. | 1. Connect a MIDI device.
2. Run: `max9 ""`.
3. Wait 15 seconds.
4. Search the log for `midi`. | The log shows the name of the connected device. No line shows `midi_mme: error` for the device. If the device port does not open, the test is a fail. | +| MAX-T13 | Menu object click | `t11-umenu-click.maxpat` | A person clicks a menu object. This click crashed old Max versions. This test needs a person or an input tool. It cannot run by itself. | 1. Run: `max9 ""`.
2. Wait 10 seconds.
3. Click the menu that shows the word alpha.
4. Click the entry gamma in the list that opens.
5. Search the log for `gamma`. | The list opens under the menu. After the click, the menu shows gamma and the log shows a line with `gamma`. Max did not crash. | +| MAX-T14 | Complete stop | no file | Max stops completely when you close it. | 1. Close all Max windows, or run: `WINEPREFIX=~/.wine-ableton ~/.local/opt/wine-d2d1-nspa-11.13/bin/wine taskkill /IM Max.exe`.
2. Wait 15 seconds.
3. Run: `pgrep -f "Max.exe\|Max Helper"`. | The command in step 3 shows no processes. If Max processes stay after 15 seconds, the test is a fail. Record a fail and then force the stop: run the same taskkill command two more times, first with `/F /IM Max.exe`, then with `/F /IM "Max Helper.exe"` in place of `/IM Max.exe`. | diff --git a/notes/performance-moonshot/MAX9-STABILITY-2026-08-05-RESEARCH-SOURCES.md b/notes/performance-moonshot/MAX9-STABILITY-2026-08-05-RESEARCH-SOURCES.md new file mode 100644 index 00000000..68cc10e8 --- /dev/null +++ b/notes/performance-moonshot/MAX9-STABILITY-2026-08-05-RESEARCH-SOURCES.md @@ -0,0 +1,125 @@ +# Max 9 on Wine: research source catalog + +Date: 2026-08-05. This file is source material for MAX9-STABILITY-2026-08-05.md. It keeps the original technical form and the links. Read the main document first. + +--- + +Compiled 2026-08-05 from Cycling '74 forums, Reddit (via Arctic Shift), WineHQ AppDB/bugzilla, GitHub, and blog reports. Confidence labels: [confirmed] = firsthand user report(s); [secondhand] = summarized/relayed claim; [speculation] = inference, no direct report. + +Coverage notes: +- WineHQ AppDB: effectively dead for Max. Only entries: Max/MSP 4.6.3 Runtime (ancient, PACE era) and a Max 8.15 version entry (iId=38431) whose test result concerns the file browser failing until a stray system libcef.dll was removed. No Max 9 entry exists. +- Wine bugzilla: no bugs filed against Max.exe/Cycling '74 specifically. Relevant generic bugs: 21232 and 39403 (Chromium sandbox incompatible with Wine, use --no-sandbox), 38960 (CEF apps, winver-XP workaround, old), 37797 (CEF/V8 crash when Arial is Liberation-substituted, install corefonts). +- Master threads (each spans 2016 to 2026): + - https://cycling74.com/forums/max-on-linux (replies/2, replies/3, ?replyPage=4) + - https://cycling74.com/forums/max-from-windows-to-linux-wine-for-realtime-sound-processing (+ /replies/1) + - https://cycling74.com/forums/max-9-installer-hanging-under-wine-caculating-disk-space + - https://reddit.com/r/MaxMSP/comments/17qjet3/max_on_linux/ (2023 post; key Max 9 report in comments 2026-02-20) + - https://roosnaflak.com/tech-and-research/run-max-msp-on-arch-linux.html (2022-02-04, Max 8.1.11) + +Big picture: Max 8 under wine-staging (with full VC redists) reached "runs perfectly / 95% functionality" consensus by 2019. Max 9 is more Wine-hostile than Max 8 per multiple reports; successes exist (CachyOS+KDE, Manjaro+Crossover 26, Debian+Bottles+GE-Proton11-1, all late-2025/early-2026) but licensing is the recurring wall. Max's UI platform is JUCE. Max 9 ships CEF 122 (9.0.0) then CEF 135 (9.1.0), V8 JS objects, and gen~/RNBO compile in-process via bundled LLVM/Clang JIT. + +## DEALBREAKERS + +### D1. License activation / sign-in fails (CEF + TLS chain), the #1 Max 9 blocker +- Licensing is in-app: Help > User Account and Licenses, CEF-rendered. Internet required to authorize; perpetual license = 180 days offline, subscription = 30 days. Ref: https://support.cycling74.com/hc/en-us/articles/360049995834-Max-7-9-Authorization +- [confirmed] Max 9 + wine-staging 11.2 (Lutris, CachyOS, KDE Wayland, 2026-02-20): stable except "License activation / License Manager not working" (reddit 17qjet3 comment o6h5b08). +- [confirmed] Max 9 License Manager crashes with "network change notifier" errors; resolved by newer GE-Proton (10-27/10-34 era) (max-on-linux?replyPage=4). +- [confirmed] Max 8 auth: "handshake failed; SSL error code 1, net_error -207"; workaround `wine Max.exe --disable-web-security --ignore-certificate-errors` then sign-in succeeds (max-on-linux/replies/3). +- [confirmed] Max 8 trial reauthorization needs working jweb/CEF ("CEF missing" in console = auth impossible); fresh clean prefix fixed it. +- [confirmed] Max 8.1.11 auth dialog: text input invisible, clipboard broken; workaround: toggle virtual desktop off during auth, right-click-paste password (roosnaflak.com). +- [confirmed] Working: Manjaro + Crossover 26.0 + Max 9.1 incl. license; Linux Mint + Proton "license authentication possible after a few attempts". +- iLok escape hatch not viable: PACE historically hard-blocks Max under Wine. + +### D2. Autocomplete/focus-steal typing bug, patching unusable +- [confirmed, many reports 2022-2026] typing one letter into a new object box instantly selects the first completion and commits it. WM-dependent. +- Workarounds [confirmed]: KDE KWin focus-protection rule; winecfg "Allow the window manager to control the windows" unticked (KDE/XFCE); GNOME `gsettings set org.gnome.desktop.wm.preferences focus-new-windows 'strict'` suggested, but replies/3 states "no confirmed solution for GNOME/Wayland"; Wine virtual desktop (conflicts with our first-class-window policy); type in external editor and paste. +- Counter-report: CachyOS + KDE Plasma 6 Wayland + wine-staging 11.2: autocomplete works out of the box (2026-02-20). + +### D3. Max 9 MSI installer hangs at "calculating disk space" (+ GNOME kills it) +- [confirmed] Max 9.03 installer hangs on disk-space calculation; Ubuntu/GNOME force-quit prompt kills it (cycling74 forum thread above). +- Workarounds [confirmed]: wait + `gsettings set org.gnome.mutter check-alive-timeout 120000`; administrative extraction `msiexec /a Max903.msi /qb TARGETDIR=...` then move runtime DLLs. +- Related [confirmed]: post-install generated launcher unreliable; point the launch target at Max.exe directly, never the .msi. + +### D4. umenu / popup-menu crash class +- [confirmed, historical] Max 7: clicking any umenu dropdown = page fault, instant crash, consistent across distros/GPUs/Wine 2.x-9.23. +- Resolved in Max 8 on wine-staging with full VC redists. But one Max 9-in-Bottles report says "umenu object non-functional, unresolved" (replyPage=4). + +### D5. Whole-UI failure modes: black UI / frozen GUI +- [confirmed] "UI goes black and stays that way, program still running underneath" (Max 7 era, GPU mismatch). +- [confirmed] GUI frozen on Debian + Wayland; workaround: X11 session or older GE-Proton. +- [confirmed] Intel HD 5500 + DXVK: startup failures; disable DXVK on iGPU, use wined3d GL. +- [secondhand] "Wayland rendering glitches on some setups". + +### D6. Wine-version sensitivity / regressions (meta-issue) +- [confirmed] strong version-lock culture: wine-staging 4.21 golden for Max 8; Max 9 "worse compatibility on newer Wine"; several users stuck on Max 8; distro wine builds repeatedly reported broken. +- Known-good recent configs [confirmed]: wine-staging 11.2 minus licensing; GE-Proton 10-27/10-34 incl. License Manager; Crossover 26.0 full stack; Bottles + ge-proton11-1 + vcredist2022 + esync (Max 9.1.3, Jan 2026; recipe includes "install FL Studio first", 160-180 DPI scaling). + +## MAJOR + +### M1. All CEF surfaces: Package Manager, File Browser, in-app docs, Welcome window, jweb/jweb~ +- Max 9.0.0 ships CEF 122.1.13; 9.1.0 ships CEF v135. Bundled at resources/support/CEF/. +- [confirmed] Package Manager / File Browser: black or blank screens, "connection refused", install failures; file browser dead until stray system-wide libcef.dll deleted. +- [confirmed] "CEF missing" console error = jweb dead = auth dead. +- [confirmed, minor variant] Package Manager renders but UI hit-targets offset (webview scaling); buttons still clickable. +- Wine-generic CEF facts: Chromium sandbox incompatible (bugs 21232/39403) so --no-sandbox; GPU-process crashes: --disable-gpu, --use-angle, last-resort --single-process (CEF issue 3028); winver-XP workaround for old CEF (bug 38960); corefonts against the Arial-substitution crash (bug 37797). Max forwards Chromium-style switches from its own command line (the D1 SSL workaround proves it). +- Escape hatch [confirmed]: `Max.exe --nocef` (since 9.0.0) disables CEF entirely; manual package install into ~/Documents/Max 9/Packages is the fallback. +- LOCAL RESULT 2026-08-05: jweb blank under default flags on our 11.13 fork; `--disable-gpu --disable-gpu-compositing` fixes it (verified, example.com renders). + +### M2. jweb inside M4L devices crashes Live at set-load (cef_initialize) +- [confirmed on native macOS + one Windows system, Live 11.3.3] any M4L device containing jweb, saved in a set, crashes Live at set-load, 100% repro, stack ends in cef_initialize. Loading the device manually after startup is fine. Repro: export node4max express example as M4L device, put on track, save set, reopen Live. https://cycling74.com/forums/crash-at-launch-with-any-max-for-live-pluging-with-cef-in-them-percent100-reproducibility +- Wine amplification [speculation]: startup race class that Wine timing differences make worse. + +### M3. Node for Max (node.script / node.debug) +- [confirmed] "no connection to node process manager" / "process manager quit after 5 restarts" (Max 8.1.11 under Wine; same bug existed natively, fixed by 8.2.2/8.2.6). +- [confirmed, secondhand] Max 9 under GE-Proton: "errors spawn; functionality continues". +- [background] node.exe under Wine generally works; scripts on Z:\ paths misbehave vs C:\. +- LOCAL RESULT 2026-08-05: three node.exe helpers spawn, gRPC + websocket connect, RNBO mdns fails and falls back to bonjour-service; one "process_id already set" warning. + +### M4. Audio drivers (ad_mme / ad_directsound / ad_wasapi / ad_asio) +- [confirmed, good] Max 8 + wineasio + JACK + wine-staging 4.21: stable, 64-sample buffers, 4-9 ms round trip; "Audio Interrupt off improves results". +- [confirmed, bad] wineasio + PipeWire 1.0.5 (Ubuntu Studio 24.04): driver recognized but scratchy audio, buffer over/underruns; open (github.com/wineasio/wineasio/issues/108). Baseline warning for ASIO hosts on PipeWire; PipeASIO is a distinct implementation. +- [confirmed, good] Max 9 ad_directsound over PipeWire-Pulse in Lutris/GE-Proton: works, stable, incl. Bluetooth A2DP. +- [secondhand] wine WASAPI timing not stable. +- Max 9.0.0 fixed FlexASIO usage + "invalid memory access when switching audio devices" (native crash class relevant to device-switch stress). +- LOCAL RESULT 2026-08-05: PipeASIO enumerates and runs at 256/48000 when selected; default falls back to ad_mme because shared prefs name the "Live" driver (M4L writes prefdrivername Live). + +### M5. MIDI (winmm): no hotplug, static port list +- [confirmed] ports enumerated only at launch; hardware changes invisible until restart; dynamic virtual outs impossible. Workaround: create ALSA virtual ports / a2jmidid before launch. Same class as moonshot P12; Max is a second validation host. + +### M6. Jitter video: engine-dependent +- Windows engines: viddll (default, bundled FFmpeg, no Media Foundation dependency) and qt (= DirectShow, legacy). jit.grab ignores the engine pref, uses DirectShow capture. +- [confirmed, dated] jit.grab/viddll crashes in some Max 8-era setups. +- [confirmed] jit.grab camera freezes on second open (Max 9 era). +- @engine qt paths hit Wine quartz/DirectShow, expect codec failures [speculation]. + +### M7. Jitter OpenGL (jit.gl.* / jit.world) +- [confirmed] GL worked even in old setups; Max 8 GL3 + VIDDLL "quite well"; Max 9 "Jitter functional, ~50 fps, DXVK recommended". Failure mode is machine-level (D5), not object-level. Multi-window/multi-context GL: no reports, untested territory. + +### M8. hid object crashes Max +- [confirmed, 2 reports, 2019] hid crashes: "unimplemented function" in 64-bit code (Max 8, wine-staging ~4.x). Needs retest on 11.x. + +### M9. shell / OS-integration objects +- [confirmed, brief] community shell object non-functional under Wine (likely path/quoting). + +### M10. Standalone export +- [confirmed, brief] exporting standalones has inconsistent path handling; exported standalones themselves run (umenu caveat applies). + +## MINOR and background + +1. Package Manager webview hit-target offset at scaling; test 100% vs 160-180 DPI. +2. Fonts: winetricks corefonts on every working recipe (CEF/V8 Arial crash class, wine bug 37797). JUCE 8 Windows font-rendering-quality regression exists natively (forum.juce.com/t/juce-8-decline-in-windows-font-rendering-quality/65557); whether Max 9 is on JUCE 8 Direct2D is unverified. Local action: verify whether Max 9's UI hits our custom d2d1/DirectWrite stack. +3. Wine-mono conflict: one recipe says do NOT install wine-mono; another guide installed it. Unresolved. +4. Installer download UA-gate: cycling74.com serves the Mac installer to Linux browsers; spoof a Windows UA for the .msi. +5. Winetricks base recipe (Max 8 consensus): corefonts + vcrun2010/2013/2015 (+2017/2022 for Max 9), winver win7+; Bottles recipe: vcredist2022 + esync. +6. gen~ compiles in-process via bundled LLVM/Clang JIT; zero Wine-specific failure reports. RNBO plugin/C++ export is the risky half: cloud build jobs + sample-dependency packaging (path bugs fixed in RNBO 1.1). +7. V8 objects: no Wine-specific reports. Test ES6 + XMLHttpRequest (9.1.0). +8. MC: no reports; low risk. +9. ABL objects (9.1): no reports; if Link discovery, UDP multicast through winsock; cross-check linkd. +10. Multiple instances: --new-instance since 9.0.0. +11. File dialogs: 9.0.0 moved to IFileDialog; same common-dialog surface we patched for Live; regression-test. +12. Preferences corruption: native remedy = delete Settings folder; keep as reset lever. +13. Performance: Max 8 under Wine "2-5% higher CPU than Windows, 95% functionality" (one user). +14. "Audio Interrupt: off improves results significantly" under Wine (Max 8). + +## Suggested test order (mapping) +1. Clean-prefix MSI install, timed (D3). 2. Welcome paint (M1) + sign-in (D1). 3. Object-box typing on GNOME Wayland (D2). 4. umenu + context menus (D4). 5. Audio Status all drivers + device-switch stress (M4). 6. MIDI hotplug (M5). 7. CEF trio + --nocef A/B (M1). 8. Node for Max + node.debug (M3). 9. jit.movie viddll/qt, jit.grab reopen, jit.world multi-window (M6/M7). 10. gen~ live-recompile, v8, RNBO export (minor 6/7). 11. M4L jweb-device set-load (M2). diff --git a/notes/performance-moonshot/MAX9-STABILITY-2026-08-05.md b/notes/performance-moonshot/MAX9-STABILITY-2026-08-05.md new file mode 100644 index 00000000..a1e0a484 --- /dev/null +++ b/notes/performance-moonshot/MAX9-STABILITY-2026-08-05.md @@ -0,0 +1,164 @@ +# Max 9 on our Wine stack: stability findings + +Date: 2026-08-05. Status: first test day complete. Some tests wait for Theo, see "Next steps". + +## What this document is + +Max 9 is a music programming tool from Cycling '74. Theo has Max 9.1.4 installed in the same Wine prefix as Ableton Live. This document lists the results of our checks and tests. It also lists the fixes we made and the fixes we recommend. A short word list is at the end. + +## Summary + +The good news: Max 9 starts, shows its windows correctly, and makes sound. The text is sharp. The patcher editor draws correctly. 3D graphics, video playback, gen~ code generation, and Node for Max all work. Audio through PipeASIO works when you select it. + +The problems: web content inside Max showed as an empty area. We found the cause and made a fix. Max starts with the slow audio path, not with PipeASIO. Max does not stop fully when you close it. Two known problems from other users still need a short manual test by Theo. + +We updated the Max launcher in this work folder (worktree performance-moonshot-m4l-stability). The changes are listed below. They are not installed yet. + +## Check 1: the desktop launcher is safe + +Task: make sure that the Max 9 launcher does not change the Wine prefix when it does not need to. + +Result: the launcher is safe. The launcher file on this machine is identical to the copy in the repository. It uses the same Wine build as the Ableton Live launcher. Because both launchers use one Wine build, a Max start does not cause a prefix update. The launcher also blocks the Wine components for .NET and web pages that Max does not need. The launcher only reads the prefix. It does not write to it. + +We found one risk near the launcher, not in it. In July, Wine created four menu entries for Max file types. These entries open files with the system Wine, not with our patched Wine. The system Wine is a different version. If one of these entries runs, the system Wine writes version data into the shared prefix. One entry also claimed all files of the general binary type, and one claimed all .json files. A double click on the wrong file could start it by accident. + +Fix, done today: we removed the four entries and refreshed the desktop database. A backup is in `~/.local/state/ableton-wine/removed-handlers-20260805/`. We also added a guard to the launcher in the worktree. The guard removes such entries again if they come back. The correct Max URL handler stays; it points at our launcher. + +## Check 2: how we tested Max + +We could not send mouse clicks or key presses to Max from the outside. The X test path does not reach Wine windows on this desktop. The Wine-internal mouse path also fails, because the Wayland display does not let a program move the pointer. Key presses from inside the prefix do work (the liveinject tool), but they go to the window that has focus. Theo used the computer during the tests, so we did not send key presses. + +So we used a different method. Max can run commands from a patch file when the patch opens. We wrote small patch files that run one test each and write the result into the Max log file. We then read the log file and take screenshots. This method works well and needs no input. The test patches are saved in `tools/max-harness/` in the worktree, with a README. + +## Check 3: what works + +- Max 9.1.4 starts in about 12 seconds. +- The Max Console and the patcher windows draw correctly. Text is sharp. +- A second start of Max sends the file to the first Max. This is correct behavior. +- The audio engine starts on command. +- PipeASIO works. We switched Max to PipeASIO while it ran. The audio engine connected at buffer size 256 at 48000 Hz. +- 3D graphics work. The jit.world test drew a moving scene at full window size. The log reports OpenGL 4.6 with the Mesa driver. +- Video playback works. Two movie files played at the same time, mixed by a small GPU program. The video engine is VIDDLL. It is built on FFmpeg and does not need the Windows media system that Wine lacks. +- gen~ works. gen~ builds machine code inside Max when a patch loads. A torture patch with 64 gen~ objects loaded with no errors. +- Node for Max works. The bundled Windows Node.js starts, and a test message went to a script and came back correctly. +- Many-channel audio objects (mc) and a dense scope patch loaded with no errors. +- Max reaches the internet. It found the 9.1.5 update. + +## Check 4: problems we found on this machine + +### Problem 1: web content inside Max shows as an empty area. Fixed in the worktree. + +Max uses a built-in web browser component (CEF) for several features: the welcome window, the documentation, the package manager, sign-in, and the jweb object in patches. In our test, a patch with a web view showed only an empty area. The browser processes ran, but the picture never reached the window. + +The fix: start Max with two extra options: + + Max.exe --disable-gpu --disable-gpu-compositing + +With these options, the web view draws correctly. We loaded a test web page and it was sharp and complete. The options tell the browser component to draw with the processor, not with the graphics card. This is the same type of fix that we use for the Learn View in Ableton Live. We also confirmed that the fix does not harm the 3D graphics or the video playback; they ran in the same session. + +Status: the launcher in the worktree now adds these options at each start. The variable ABLETON_MAX_CEF=gpu turns the fix off for comparison tests. + +### Problem 2: Max does not use PipeASIO when it starts + +Fresh Max uses the old, slow Windows audio path (MME). Our fast path is PipeASIO. Max does not select it. + +Cause: Max and the Max for Live editor share one audio settings file. When you use Max for Live, it writes the driver name "Live" into that file. That driver only exists inside Ableton Live. When Max starts alone, it cannot find a driver with that name. It then falls back to the slow path. + +Result for the user: more delay between action and sound, and more processor load. The user can select "PipeASIO Driver" in the Audio Status window by hand. Our test shows that this works. + +Open question: does a hand selection stay after a restart? Our test could not answer this, because we had to force Max to stop (Problem 3). The next clean session should select PipeASIO, quit Max normally, and check the settings file. + +Recommendation: make standalone Max select PipeASIO without user action. The correct method needs a design decision, because the settings file is shared with Max for Live. + +### Problem 3: Max does not stop fully. Seen two times. + +We closed Max in the normal way, two times in this session. All windows closed. But the Max process stayed alive both times. The browser helper processes also stayed alive. We had to force them to stop. + +A process that stays alive can block the next start of Max. It can also hold the audio device. The browser component is the first suspect, because this problem class is known for it. A useful next test: close Max with the browser fix active and with the browser component fully off (Max has a start option --nocef for that), and compare. + +### Problem 4: an error message at each start. Cause found. + +At each start, Max writes this error: "Thrift: TPipe::open ::CreateFile errored, errno = 2". We found the source. It is the copy-protection layer (PACE) inside Max. At start, it tries to reach a Windows license service. That service does not exist under Wine, so the connection fails. Max then continues with its stored license data. Max works normally. + +Why this still matters: sign-in and license renewal are the top reported blockers for Max 9 on Wine (see Check 5). Our license data is valid now, but Max asks for a renewal at intervals (180 days for a normal license, 30 days for a subscription). When that day comes, this area is the risk point. The web view fix (Problem 1) helps, because the sign-in window is a web view. + +### Problem 5: a warning from node.script at each start + +At each start, the console shows: "Trying to set the process_id for a child process, but it already has one". Node for Max continues normally. Low priority. + +### Problem 6: one font is missing + +The log shows: "typeface name Lucida Sans with style Bold not found". Windows has this font, our prefix does not. Max uses a replacement font. We saw no bad text in our screenshots. Low priority. + +### Problem 7: Max crashed before, causes unknown + +Max wrote a crash dump file on July 3. The log from August 1 also shows that that session ended in a crash. In the August 1 session, an error repeated two times per second for minutes: "get: no valid object set". We do not know the causes yet. The dump file is kept: `AppData/Roaming/Cycling '74/Logs/Max-2026-07-03_13-33-44-6cc4519e5c9.dmp` in the prefix. + +### Problem 8: MIDI input ports do not open + +With no MIDI hardware connected, Max tried to open the two PipeWire system MIDI ports and failed both times: "midi_mme: error 3". These system ports are not normal instrument ports, so this failure alone proves little. We must repeat the test with a real controller connected before Max starts. Also, under Wine the MIDI port list is fixed at start: a device connected later stays invisible. That matches the moonshot device hotplug work (P12). Max is a good second test program for it. + +### Problem 9: the Max log file lost its content one time + +During the video test, the Max log file went to size zero while Max ran, and then filled again. Our test method reads this log, so we note it. Cause unknown. One time only. Low priority. + +## Check 5: problems that other users report + +We collected reports from other users who run Max on Wine. Sources and details are in MAX9-STABILITY-2026-08-05-RESEARCH-SOURCES.md. For each report, our local status: + +- Sign-in and license activation fail. The most common Max 9 blocker. Related to our Problems 1 and 4. Our web view fix probably helps. A real test needs Theo's account and a quiet moment. +- Typing a new object name goes wrong. When the suggestion list opens, it takes the keyboard focus and accepts the wrong entry. Reported on many desktops. No known solution for the GNOME desktop. Not testable from the outside. A 30-second manual test by Theo settles it: press "n" in a patcher, type "cycle~" slowly, and check that the text stays correct. +- Clicks on menu objects (umenu) crashed Max 7 every time. One Max 9 report says the problem is back. Our click test patch is ready (tools/max-harness/t11-umenu-click.maxpat), but the click itself must come from a person or from a quiet automated session. +- The Max 9 installer stops for a long time at "calculating disk space". Known workarounds exist. Only matters for new installs. +- On some computers the whole Max window turns black or freezes. We did not see this on this machine. +- The camera object (jit.grab) freezes on the second open. Not tested, needs a camera. +- The hid object (game controllers) crashed Max in old reports. Not tested yet. +- A Max for Live device that contains a web view crashes Live while a set loads. Reported with full repeat rate on Windows and macOS. Our prefix runs both programs, so we must test this exact case in a quiet session. + +## Test coverage + +| Test | Area | Result | +|---|---|---| +| t01 | audio engine start, driver list | pass, slow path selected (Problem 2) | +| t02 | switch to PipeASIO while running | pass, 256 at 48000 | +| t03 | web view (CEF) | fail, then pass with the fix (Problem 1) | +| help patch | patcher drawing, fonts | pass | +| t04 | 3D graphics (jit.world, jit.mo) | pass, OpenGL 4.6 | +| t05 | video playback (VIDDLL, two movies) | pass | +| t06 | Node for Max round trip | pass | +| gen | gen~ code generation, 64 objects | pass, no errors | +| mc | many-channel objects | load pass, sound test open | +| ui | dense scope patch | load pass | +| t09 | MIDI port list | ports fail to open (Problem 8), needs hardware | +| t11 | menu object click | ready, needs a quiet session or Theo | +| manual | sign-in, typing test | needs Theo | + +## What changed on the machine today + +1. Four stale Wine menu entries removed, backup kept (Check 1). +2. Nothing else. The launcher changes are only in the worktree, not installed. The Wine prefix itself was not changed by us; Max wrote its normal settings and log files while it ran. + +## Next steps + +1. Install the updated launcher after review, or build it into the next release. +2. Theo, three short manual tests when convenient: + - Sign-in: open Help, then User Account, and sign in. Report any error text. + - Typing: press "n" in a patcher, type "cycle~" slowly, press enter. Wrong object = the known suggestion-list problem. + - Menu click: open tools/max-harness/t11-umenu-click.maxpat, click the menu that shows "alpha", select "gamma". A crash = the known menu problem. +3. Quiet-session automated tests, in order: PipeASIO selection with a clean quit (Problem 2); stop behavior with and without --nocef (Problem 3); MIDI with a controller connected (Problem 8); the Max for Live web view set-load crash; H.264 video file decode; idle processor load measurement. +4. Design the PipeASIO default method (Problem 2 recommendation). +5. Analyze the July 3 crash dump (Problem 7). + +## Word list + +- Prefix: the folder where Wine keeps a Windows-like system for one program set. +- CEF: the built-in web browser component inside Max. +- PACE: the copy-protection and license layer inside Max. +- PipeASIO: our audio driver. It connects Windows programs to the Linux sound system with low delay. +- MME: the oldest Windows audio path. High delay. +- jweb: the Max object that shows a web page inside a patch. +- Node for Max: a part of Max that runs JavaScript programs. +- gen~: a Max feature that builds fast machine code from a patch. +- DSP: the audio engine of Max. +- Patch: a Max document. +- Worktree: a separate working copy of the repository for one line of work. diff --git a/scripts/max9 b/scripts/max9 index b0ba0742..75d23be3 100644 --- a/scripts/max9 +++ b/scripts/max9 @@ -3,13 +3,18 @@ # ableton-live, so standalone Max, Max for Live editing and PipeASIO all # share one wineserver. Args: Max documents as unix paths, or c74max: URLs. # Overrides: ABLETON_WINE_ROOT, ABLETON_WINEPREFIX, ABLETON_RT=off, -# ABLETON_LINKD=/path/to/ableton-linkd (Ableton Link session anchor). +# ABLETON_LINKD=/path/to/ableton-linkd (Ableton Link session anchor), +# ABLETON_MAX_CEF=gpu (let Max's embedded browser composite on the +# GPU again; default forces the software path, see below). set -u unset WINELOADER WINEDLLPATH WINEDLLOVERRIDES WINEARCH WINE_ROOT="${ABLETON_WINE_ROOT:-$HOME/.local/opt/wine-d2d1-nspa-11.13}" export WINEPREFIX="${ABLETON_WINEPREFIX:-$HOME/.wine-ableton}" export PATH="$WINE_ROOT/bin:$PATH" -export WINEDEBUG="${WINEDEBUG:--all}" +# Keep PipeASIO's warnings and errors visible (channel asio), same as the +# ableton-live launcher: they name audio faults such as a lost quantum +# negotiation, and the session log below captures them. +export WINEDEBUG="${WINEDEBUG:--all,warn+asio,err+asio}" export WINE_D3D_CONFIG="${WINE_D3D_CONFIG:-csmt=0x1}" export WINE_DISABLE_UNIX_MOUNT_REPARSE="${WINE_DISABLE_UNIX_MOUNT_REPARSE:-1}" export WINESERVER="$WINE_ROOT/bin/wineserver" @@ -22,6 +27,54 @@ if [ ! -f "$MAX_UNIX" ]; then exit 1 fi +# Session log: capture launcher warnings and Wine/driver stderr so audio and +# CEF faults stay inspectable after the fact. Rotation keeps the last three +# launches. Same shape as the ableton-live launcher, separate file. +MAX_SESSION_LOG="${XDG_STATE_HOME:-$HOME/.local/state}/ableton-wine/max9-session.log" +if mkdir -p "$(dirname "$MAX_SESSION_LOG")" 2>/dev/null; then + [ -f "$MAX_SESSION_LOG.1" ] && mv -f "$MAX_SESSION_LOG.1" "$MAX_SESSION_LOG.2" + [ -f "$MAX_SESSION_LOG" ] && mv -f "$MAX_SESSION_LOG" "$MAX_SESSION_LOG.1" + echo "== max9 session $(date -u +%FT%TZ) runtime $(basename "$WINE_ROOT")" > "$MAX_SESSION_LOG" + exec 2> >(tee -a "$MAX_SESSION_LOG" >&2) +fi + +# Max embeds a Chromium browser (CEF) for the welcome window, the in-app +# documentation, the package manager, sign-in and the jweb object. Under the +# patched stack CEF's GPU compositor produces no picture: the browser +# processes run but the composited page never reaches Max's window (verified +# 2026-08-05, notes/performance-moonshot/MAX9-STABILITY-2026-08-05.md). +# Max forwards Chromium-style switches from its own command line, so force +# the software path the same way the ableton-live launcher does for the +# Learn View's WebView2. ABLETON_MAX_CEF=gpu drops the switches for an A/B. +cef_args=(--disable-gpu --disable-gpu-compositing) +if [ "${ABLETON_MAX_CEF:-soft}" = "gpu" ]; then + cef_args=() +fi + +# Wine's winemenubuilder is disabled in this prefix, but entries it created +# before that (2026-07-16) survive in the desktop database. They open Max +# file types, and the generic binary type, with stock wine against this +# prefix: stock wine is a different build, and every such launch restamps +# the prefix with foreign version data. Remove exactly those fossils: the +# winemenubuilder Exec signature, targeting this prefix, for Max9 ProgIDs. +# The canonical c74max URL handler (wine-protocol-c74max.desktop) points at +# this launcher and stays. +scrub_menubuilder_fossils() { + local apps="${XDG_DATA_HOME:-$HOME/.local/share}/applications" + local f removed=0 + for f in "$apps"/wine-extension-*.desktop; do + [ -f "$f" ] || continue + if grep -qE "^Exec=env \"WINEPREFIX=$WINEPREFIX\" wine start /ProgIDOpen \"Max9\." "$f"; then + echo "max9: removing stale wine handler $(basename "$f") (opens this prefix with stock wine)" >&2 + rm -f "$f" + removed=1 + fi + done + [ "$removed" -eq 1 ] && update-desktop-database "$apps" 2>/dev/null + return 0 +} +scrub_menubuilder_fossils + run=(wine) # Same realtime probe as the ableton-live launcher. if [ "${ABLETON_RT:-on}" != off ] && command -v chrt >/dev/null 2>&1 \ @@ -47,4 +100,4 @@ if [ -x "$LINKD" ] && ! pgrep -x ableton-linkd >/dev/null 2>&1; then "$LINKD" --daemon || echo "max9: ableton-linkd failed to start" >&2 fi -exec "${run[@]}" "$MAX_EXE" ${args[@]+"${args[@]}"} +exec "${run[@]}" "$MAX_EXE" ${cef_args[@]+"${cef_args[@]}"} ${args[@]+"${args[@]}"} diff --git a/tools/max-harness/DOWNLOADED-PATCHES-MANIFEST.md b/tools/max-harness/DOWNLOADED-PATCHES-MANIFEST.md new file mode 100644 index 00000000..6791aaf2 --- /dev/null +++ b/tools/max-harness/DOWNLOADED-PATCHES-MANIFEST.md @@ -0,0 +1,161 @@ +# Max 9 stress-test patches + +Collected 2026-08-05 for exercising Max 9 for Windows under Wine on Linux. +All .maxpat files verified to be JSON starting with `{` and containing `"patcher"`. + +Repos used (all fetched from GitHub default branches on 2026-08-05): + +- https://github.com/Cycling74/gen-workshop +- https://github.com/Cycling74/jit.mo +- https://github.com/Cycling74/eyebeam-workshop-2 +- https://github.com/Cycling74/n4m-core-examples +- https://github.com/Cycling74/n4m-examples +- https://github.com/stretta/BEAP (raw files from `master`) +- https://github.com/stretta/MC-Beap +- https://github.com/mediaelement/mediaelement-files (test video only) + +"Bundled" below means the dependency ships inside the stock Max 8/9 install +(BEAP, VIZZIE, jit.mo packages and the stock media files), so nothing extra +needs installing. + +## audio-msp + +- `cpu-tester-poly.maxpat` + `cpu-tester-poly-sub.maxpat` + - Source: https://github.com/Cycling74/gen-workshop (patchers/) + - Exercises: `poly~` with 128 voices of pure MSP (`cycle~`, `noise~`, `lores~`, `scale~`). Deliberate CPU-load tester. + - Correct behavior: audio starts on `startwindow`, CPU meter climbs with voice count, no dropouts/clicks at moderate voice counts, no DSP-chain rebuild crash. + - Dependencies: none (the -sub patch must stay in the same folder). +- `peakamp-svf.maxpat` + - Source: https://github.com/Cycling74/eyebeam-workshop-2 (audio-analysis/) + - Exercises: MSP filter (`svf~`), amplitude analysis, `ezdac~`, `multislider` redraw at audio-driven rate. + - Correct behavior: patch loads clean, meters/multislider animate while DSP is on. + - Dependencies: none. + +## jitter-gl + +- `jit.mo-Worms.maxpat`, `jit.mo-AnimPath.maxpat` + - Source: https://github.com/Cycling74/jit.mo (patchers/) + - Exercises: `jit.world` (OpenGL context + window), `jit.gl.mesh` / `jit.gl.*` pipeline driven by jit.mo generators. + - Correct behavior: render window opens, smooth animated geometry at the world fps, no context loss on window resize. + - Dependencies: jit.mo package (bundled with Max 8+). +- `jit.mo-AudioVisuals.maxpat` + - Same source. Adds `adc~` audio input driving GL visuals: cross-subsystem audio+GL stress. + - Correct behavior: renders even without a mic; with an input device the visuals react. + - Dependencies: jit.mo (bundled); audio input device optional. + +## jitter-video + +- `jit.mo-Alphablend.maxpat` + - Source: https://github.com/Cycling74/jit.mo (patchers/) + - Exercises: two `jit.movie` instances (`@moviefile dvducks.mov`, `@moviefile dvkite.mov`) decoded and alpha-blended into a GL scene. + - Correct behavior: both movies decode and play, blended output animates in the render window. + - Dependencies: `dvducks.mov` / `dvkite.mov` from Max's bundled media folder (ships with Max); jit.mo (bundled). +- `test-video.mp4` + - Source: https://raw.githubusercontent.com/mediaelement/mediaelement-files/master/echo-hereweare.mp4 (H.264/AAC, 5.4 MB) + - Not a patch. Drop onto any `jit.movie` or send `read ` to test arbitrary-file decode outside the bundled media, i.e. the codec path Wine users will actually hit. + +## gen + +- `waveshaper.maxpat`, `codebox-if-for.maxpat`, `simplistic-karplus-strong.maxpat` + - Source: https://github.com/Cycling74/gen-workshop (patchers/) + - Exercises: `gen~` runtime code generation: expr patching, codebox with if/for constructs, feedback (`history`/`delay`) DSP. + - Correct behavior: gen~ compiles silently on load (no gen compile errors in the Max console), audio runs when DSP is on. + - Dependencies: none. +- `grranulator-patch.maxpat` + `grranulator.genexpr` + - Same source. gen~ loading external `.genexpr` code (granulator) plus several control `buffer~`s. + - Correct behavior: compiles, granular texture audible; multislider UI animates. + - Dependencies: the `.genexpr` file must stay next to the patch. +- `cpu-tester-gen.maxpat` + `cpu-tester-gen-sub.maxpat` + - Same source. `poly~` wrapping gen~ voices: many simultaneous gen~ compilations and instances. Companion to the MSP cpu tester, lets you compare gen vs classic MSP CPU cost under Wine. + - Correct behavior: all voices compile, CPU scales linearly-ish with voices. + - Dependencies: -sub patch in the same folder. + +## jweb + +- `facetracking/patchers/facetracker.maxpat` (+ `getpoint.maxpat`, `pointdist.maxpat`, `src/`) + - Source: https://github.com/Cycling74/eyebeam-workshop-2 (facetracking/) + - Exercises: `jweb` (CEF) loading a local HTML page (`src/mesh-tracker.html`) that runs a JS face-tracking library and getUserMedia webcam capture inside the embedded browser. + - Correct behavior: jweb boots CEF and renders the local page. That alone is the core Wine test (CEF init, GPU/compositor path). Full function additionally needs a webcam and camera permission inside CEF. + - Dependencies: keep the folder structure (patchers/ + src/) intact; webcam optional. +- `express/patchers/express.maxpat` (+ `express-node/`) + - Source: https://github.com/Cycling74/n4m-examples (express/) + - Exercises: `jweb` pointed at a local HTTP server run by `node.script` (Express). Tests CEF networking to localhost plus Node for Max in one patch. + - Correct behavior: after `script npm install` and `script start`, jweb displays the served page. + - Dependencies: Node for Max runtime (ships with Max), network-less npm install of `express` (needs internet once). + +## node4max + +- `02-message-handlers/`, `04-outlet-methods/`, `06-using-dicts/` + - Source: https://github.com/Cycling74/n4m-core-examples + - Exercises: `node.script` lifecycle (spawn Node process, message handlers, outlets, dict transfer). Each folder is a self-contained .maxpat + .js pair with no npm dependencies. + - Correct behavior: `script start` spawns node, messages round-trip patch <-> Node, `node.debug`/console shows no spawn errors. Under Wine this exercises the bundled Node runtime process spawn path. + - Dependencies: Node for Max (ships with Max). No npm install needed. +- `tonal-chord-builder/` + - Source: https://github.com/Cycling74/n4m-examples + - Exercises: `node.script` with an npm dependency (`tonal`), driving a `poly~` synth (`poly.phatness.maxpat`, which also uses mc objects), with `notein`/`midiin` input. + - Correct behavior: after `script npm install` + `script start`, playing MIDI notes or clicking builds chords that sound through the poly~ synth. + - Dependencies: Node for Max; one-time npm install; MIDI input optional. + +## ui-objects + +- `Simple Synth.maxpat` + - Source: https://github.com/stretta/BEAP (examples/Synthesis Examples/), raw: https://raw.githubusercontent.com/stretta/BEAP/master/examples/Synthesis%20Examples/Simple%20Synth.maxpat + - Exercises: dense bpatcher-based BEAP modules, each full of `live.dial`, `live.slider`, `live.toggle`, panels: 2D vector drawing stress plus presentation-mode layout. + - Correct behavior: all modules draw correctly (no blank bpatchers), knobs drag smoothly, no redraw trails. + - Dependencies: BEAP package (bundled with Max since Max 7). +- `Big Scope best practices.maxpat` + - Same source (raw master, `examples/Synthesis%20Examples/Big%20Scope%20best%20practices.maxpat`). + - Exercises: large `scope~` displays: continuous high-rate 2D redraw while DSP runs. Good frame-pacing / GDI-vs-D2D stress. + - Correct behavior: scopes animate fluidly with DSP on; UI stays responsive. + - Dependencies: BEAP (bundled). + +## mc + +- `mc.karplus.maxpat` + - Source: https://github.com/Cycling74/gen-workshop (patchers/) + - Exercises: pure mc chain: `mc.noise~`, `mc.sig~`, `mc.line~`, `mc.gen~` (8-channel Karplus-Strong), `mc.mixdown~`. Also covers mc+gen interaction. + - Correct behavior: 8 plucked-string voices sound and pan down to stereo; no channel-count errors in the console. + - Dependencies: none. +- `mc-beap/Supersaw.maxpat`, `mc-beap/Getting Started.maxpat` + - Source: https://github.com/stretta/MC-Beap (examples/ + patchers/, flattened into one folder so the bp.mc.* modules resolve via the patch's own folder) + - Exercises: mc.* everywhere (`mc.cycle~`-based oscillator banks, mc envelopes, mc filters via `mc.svf~`/ladder, voice combining) wrapped in UI-dense bpatchers. + - Correct behavior: modules load (no "no such object" for bp.mc.*), audio plays; Getting Started responds to MIDI input. + - Dependencies: bp.mc.* module patches in the same folder (included here); `bp.Stereo`, `bp.MIDI In`, `bp.Poly MIDI to mc.Signal` come from the bundled BEAP package (the last one is also included in this folder); MIDI input optional for Getting Started. + +## midi + +- `Virtual Instrument.maxpat` + - Source: https://github.com/stretta/BEAP, raw: https://raw.githubusercontent.com/stretta/BEAP/master/examples/MIDI%20Examples/Virtual%20Instrument.maxpat + - Exercises: `notein` + `midiin` directly, feeding `vst~` plugin hosting. + - Correct behavior: MIDI input device appears and notes arrive (print/monitor); with a VST loaded into vst~ it sounds. Under Wine this is the winmm/MIDI-port enumeration test. + - Dependencies: BEAP (bundled); a MIDI input device (hardware or virtual) to be meaningful; VST optional. +- `Arpeggiator Synth.maxpat` + - Same source (raw master, `examples/MIDI%20Examples/Arpeggiator%20Synth.maxpat`). + - Exercises: BEAP MIDI modules (MIDI in, arpeggiator clocking) driving a synth voice: sustained scheduler-timed MIDI event stream plus audio. + - Correct behavior: arpeggio runs steadily from held notes; timing stays stable while UI is dragged. + - Dependencies: BEAP (bundled); MIDI input optional (toggle/kslider style control inside). + +## misc + +- `Gradient Rot.maxpat` + - Source: https://github.com/stretta/BEAP, raw: https://raw.githubusercontent.com/stretta/BEAP/master/examples/Misc%20Examples/Gradient%20Rot.maxpat + - Exercises: large (630 KB) patch, heavy UI + modulation graph. General big-patch load/instantiation and redraw torture. + - Correct behavior: loads without multi-second hangs, UI animates. + - Dependencies: BEAP (bundled). +- `tempo-tests.maxpat` + - Source: https://github.com/Cycling74/jit.mo (testpatches/) + - Exercises: Max transport/tempo driving jit.mo + `jit.world` + `jit.gl.mesh`: scheduler/transport plus GL in one patch. + - Correct behavior: geometry animates in sync with the transport tempo. + - Dependencies: jit.mo (bundled). +- `classic-ringtone.maxpat` + - Source: https://github.com/Cycling74/gen-workshop (patchers/) + - Exercises: gen~ sequencing a melody: quick audible sanity check that scheduler + gen + audio out all work. + - Correct behavior: plays the Nokia-style ringtone melody. + - Dependencies: none. + +## Not covered / notes + +- No patch here uses `jit.grab` (webcam via DirectShow). eyebeam's optical-flow patch was the candidate but it references `my.viewr.maxpat`, which is missing from the repo, so it was dropped. The facetracking jweb patch covers webcam capture via CEF instead. +- maxforlive.com was skipped per instructions (.amxd, login-gated). stretta/monosequencer turned out to be .amxd-only and was dropped. +- Cycling74/max-test (the automated test harness) was fetched and inspected but its patches require the repo's compiled `test.*`/oscar externals, so none were included. +- BEAP example patches are large because they embed snapshot data; that is normal. diff --git a/tools/max-harness/README.md b/tools/max-harness/README.md new file mode 100644 index 00000000..5f20aa73 --- /dev/null +++ b/tools/max-harness/README.md @@ -0,0 +1,33 @@ +# Max 9 test harness + +Small Max patches that test one subsystem each without mouse or keyboard +input. Each patch runs its test from a loadbang when it opens and prints +markers. Max mirrors console prints into +`AppData/Roaming/Cycling '74/Max 9/Logs/Max.log` in the prefix, so a shell +can open a patch with the max9 launcher, wait, and grep the log for the +result. This works where outside input injection does not: XTEST never +reaches Wine windows, and SetCursorPos cannot warp the pointer under +XWayland, so SendInput mouse clicks land nowhere. SendInput keyboard from +inside the prefix (liveinject) still works, but needs the target window +foreground, so it must not run while the desktop is in use. + +- t01-dsp-probe: lists the audio drivers, reports the current one, starts DSP. +- t02-pipeasio-switch: switches the driver to PipeASIO by menu index (2), + starts DSP, reports the final driver. Index order observed on this + install: None, FL Studio ASIO, PipeASIO Driver, ad_directsound, ad_mme. +- t03-jweb-cef: embeds a jweb browser pointed at https://example.com. + Blank area = the CEF GPU-compositor fault; page visible = fixed. +- t06-node-auto: goes into max-test-patches/node4max/04-outlet-methods/ + (needs outlet-methods.js beside it); round-trips a message through the + bundled Windows node.exe. +- t09-midi-probe: dumps the winmm MIDI port lists. Known authoring flaw: + midiinfo has one outlet, the second patchcord gets dropped at load. +- t11-umenu-click: a umenu wired to print. Needs a real click to open the + dropdown (the historical Max 7 crash class): run it in a quiet session + with SendInput keyboard/mouse from inside the prefix. + +The downloaded community patch corpus (55 patches, ten subsystems) lives in +the session scratchpad; DOWNLOADED-PATCHES-MANIFEST.md preserves its source +URLs and per-file notes so it can be refetched. + +Results and findings: notes/performance-moonshot/MAX9-STABILITY-2026-08-05.md diff --git a/tools/max-harness/t01-dsp-probe.maxpat b/tools/max-harness/t01-dsp-probe.maxpat new file mode 100644 index 00000000..8332f643 --- /dev/null +++ b/tools/max-harness/t01-dsp-probe.maxpat @@ -0,0 +1,211 @@ +{ + "patcher" : { + "fileversion" : 1, + "appversion" : { + "major" : 9, + "minor" : 1, + "revision" : 4, + "architecture" : "x64", + "modernui" : 1 + }, + "classnamespace" : "box", + "rect" : [ 100.0, 100.0, 640.0, 480.0 ], + "boxes" : [ { + "box" : { + "id" : "obj-1", + "maxclass" : "newobj", + "text" : "loadbang", + "numinlets" : 0, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 30.0, 30.0, 60.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-2", + "maxclass" : "message", + "text" : "HARNESS t01 loaded", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 30.0, 70.0, 130.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-3", + "maxclass" : "newobj", + "text" : "print HARNESS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 30.0, 110.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-4", + "maxclass" : "newobj", + "text" : "delay 1500", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 200.0, 70.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-5", + "maxclass" : "newobj", + "text" : "adstatus driver", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "int" ], + "patching_rect" : [ 200.0, 110.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-6", + "maxclass" : "newobj", + "text" : "print DRIVER", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 200.0, 150.0, 80.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-7", + "maxclass" : "newobj", + "text" : "delay 3000", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 380.0, 70.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-8", + "maxclass" : "message", + "text" : "; dsp start", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 380.0, 110.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-9", + "maxclass" : "newobj", + "text" : "delay 6000", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 500.0, 70.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-10", + "maxclass" : "newobj", + "text" : "adstatus switch", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "int" ], + "patching_rect" : [ 500.0, 110.0, 94.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-11", + "maxclass" : "newobj", + "text" : "print DSPON", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 500.0, 150.0, 80.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-12", + "maxclass" : "newobj", + "text" : "t b b b b", + "numinlets" : 1, + "numoutlets" : 4, + "outlettype" : [ "bang", "bang", "bang", "bang" ], + "patching_rect" : [ 30.0, 200.0, 60.0, 22.0 ] + } + } + ], + "lines" : [ { + "patchline" : { + "source" : [ "obj-1", 0 ], + "destination" : [ "obj-12", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-12", 0 ], + "destination" : [ "obj-2", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-12", 1 ], + "destination" : [ "obj-4", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-12", 2 ], + "destination" : [ "obj-7", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-12", 3 ], + "destination" : [ "obj-9", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-2", 0 ], + "destination" : [ "obj-3", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-4", 0 ], + "destination" : [ "obj-5", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-5", 0 ], + "destination" : [ "obj-6", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-7", 0 ], + "destination" : [ "obj-8", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-9", 0 ], + "destination" : [ "obj-10", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-10", 0 ], + "destination" : [ "obj-11", 0 ] + } + } + ] + } +} diff --git a/tools/max-harness/t02-pipeasio-switch.maxpat b/tools/max-harness/t02-pipeasio-switch.maxpat new file mode 100644 index 00000000..0a3e13ad --- /dev/null +++ b/tools/max-harness/t02-pipeasio-switch.maxpat @@ -0,0 +1,245 @@ +{ + "patcher" : { + "fileversion" : 1, + "appversion" : { + "major" : 9, + "minor" : 1, + "revision" : 4, + "architecture" : "x64", + "modernui" : 1 + }, + "classnamespace" : "box", + "rect" : [ 120.0, 120.0, 640.0, 480.0 ], + "boxes" : [ { + "box" : { + "id" : "obj-1", + "maxclass" : "newobj", + "text" : "loadbang", + "numinlets" : 0, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 30.0, 30.0, 60.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-2", + "maxclass" : "message", + "text" : "HARNESS t02 loaded", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 30.0, 70.0, 130.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-3", + "maxclass" : "newobj", + "text" : "print HARNESS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 30.0, 110.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-4", + "maxclass" : "newobj", + "text" : "delay 2000", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 200.0, 70.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-5", + "maxclass" : "message", + "text" : "2", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 200.0, 110.0, 30.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-6", + "maxclass" : "newobj", + "text" : "adstatus driver", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "int" ], + "patching_rect" : [ 200.0, 150.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-7", + "maxclass" : "newobj", + "text" : "delay 7000", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 380.0, 70.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-8", + "maxclass" : "message", + "text" : "; dsp start", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 380.0, 110.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-9", + "maxclass" : "newobj", + "text" : "delay 12000", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 500.0, 70.0, 78.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-10", + "maxclass" : "newobj", + "text" : "adstatus driver", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "int" ], + "patching_rect" : [ 500.0, 110.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-11", + "maxclass" : "newobj", + "text" : "print T02FINAL", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 500.0, 150.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-12", + "maxclass" : "newobj", + "text" : "t b b b b", + "numinlets" : 1, + "numoutlets" : 4, + "outlettype" : [ "bang", "bang", "bang", "bang" ], + "patching_rect" : [ 30.0, 200.0, 60.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-13", + "maxclass" : "newobj", + "text" : "adstatus sigvs", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "int" ], + "patching_rect" : [ 640.0, 110.0, 86.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-14", + "maxclass" : "newobj", + "text" : "print T02SIGVS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 640.0, 150.0, 92.0, 22.0 ] + } + } + ], + "lines" : [ { + "patchline" : { + "source" : [ "obj-1", 0 ], + "destination" : [ "obj-12", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-12", 0 ], + "destination" : [ "obj-2", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-12", 1 ], + "destination" : [ "obj-4", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-12", 2 ], + "destination" : [ "obj-7", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-12", 3 ], + "destination" : [ "obj-9", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-2", 0 ], + "destination" : [ "obj-3", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-4", 0 ], + "destination" : [ "obj-5", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-5", 0 ], + "destination" : [ "obj-6", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-7", 0 ], + "destination" : [ "obj-8", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-9", 0 ], + "destination" : [ "obj-10", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-10", 0 ], + "destination" : [ "obj-11", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-9", 0 ], + "destination" : [ "obj-13", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-13", 0 ], + "destination" : [ "obj-14", 0 ] + } + } + ] + } +} diff --git a/tools/max-harness/t03-jweb-cef.maxpat b/tools/max-harness/t03-jweb-cef.maxpat new file mode 100644 index 00000000..1a59a226 --- /dev/null +++ b/tools/max-harness/t03-jweb-cef.maxpat @@ -0,0 +1,71 @@ +{ + "patcher" : { + "fileversion" : 1, + "appversion" : { + "major" : 9, + "minor" : 1, + "revision" : 4, + "architecture" : "x64", + "modernui" : 1 + }, + "classnamespace" : "box", + "rect" : [ 140.0, 140.0, 900.0, 700.0 ], + "boxes" : [ { + "box" : { + "id" : "obj-1", + "maxclass" : "newobj", + "text" : "loadbang", + "numinlets" : 0, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 30.0, 30.0, 60.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-2", + "maxclass" : "message", + "text" : "HARNESS t03 jweb loaded", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 130.0, 30.0, 160.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-3", + "maxclass" : "newobj", + "text" : "print HARNESS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 310.0, 30.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-4", + "maxclass" : "jweb", + "url" : "https://example.com", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "" ], + "patching_rect" : [ 30.0, 80.0, 820.0, 560.0 ] + } + } + ], + "lines" : [ { + "patchline" : { + "source" : [ "obj-1", 0 ], + "destination" : [ "obj-2", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-2", 0 ], + "destination" : [ "obj-3", 0 ] + } + } + ] + } +} diff --git a/tools/max-harness/t06-node-auto.maxpat b/tools/max-harness/t06-node-auto.maxpat new file mode 100644 index 00000000..225f06c5 --- /dev/null +++ b/tools/max-harness/t06-node-auto.maxpat @@ -0,0 +1,143 @@ +{ + "patcher" : { + "fileversion" : 1, + "appversion" : { + "major" : 9, + "minor" : 1, + "revision" : 4, + "architecture" : "x64", + "modernui" : 1 + }, + "classnamespace" : "box", + "rect" : [ 160.0, 160.0, 620.0, 420.0 ], + "boxes" : [ { + "box" : { + "id" : "obj-1", + "maxclass" : "newobj", + "text" : "loadbang", + "numinlets" : 0, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 30.0, 30.0, 60.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-2", + "maxclass" : "message", + "text" : "HARNESS t06 node loaded", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 120.0, 30.0, 170.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-3", + "maxclass" : "newobj", + "text" : "print HARNESS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 310.0, 30.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-4", + "maxclass" : "newobj", + "text" : "node.script outlet-methods.js @autostart 1 @watch 0", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "" ], + "patching_rect" : [ 30.0, 160.0, 300.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-5", + "maxclass" : "newobj", + "text" : "delay 8000", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 30.0, 70.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-6", + "maxclass" : "message", + "text" : "example2", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 30.0, 110.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-7", + "maxclass" : "newobj", + "text" : "print NODEOUT", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 30.0, 210.0, 95.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-8", + "maxclass" : "newobj", + "text" : "print NODESTATUS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 160.0, 210.0, 110.0, 22.0 ] + } + } + ], + "lines" : [ { + "patchline" : { + "source" : [ "obj-1", 0 ], + "destination" : [ "obj-2", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-2", 0 ], + "destination" : [ "obj-3", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-1", 0 ], + "destination" : [ "obj-5", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-5", 0 ], + "destination" : [ "obj-6", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-6", 0 ], + "destination" : [ "obj-4", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-4", 0 ], + "destination" : [ "obj-7", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-4", 1 ], + "destination" : [ "obj-8", 0 ] + } + } + ] + } +} diff --git a/tools/max-harness/t09-midi-probe.maxpat b/tools/max-harness/t09-midi-probe.maxpat new file mode 100644 index 00000000..62162131 --- /dev/null +++ b/tools/max-harness/t09-midi-probe.maxpat @@ -0,0 +1,137 @@ +{ + "patcher" : { + "fileversion" : 1, + "appversion" : { + "major" : 9, + "minor" : 1, + "revision" : 4, + "architecture" : "x64", + "modernui" : 1 + }, + "classnamespace" : "box", + "rect" : [ 180.0, 180.0, 560.0, 380.0 ], + "boxes" : [ { + "box" : { + "id" : "obj-1", + "maxclass" : "newobj", + "text" : "loadbang", + "numinlets" : 0, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 30.0, 30.0, 60.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-2", + "maxclass" : "message", + "text" : "HARNESS t09 midi loaded", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 120.0, 30.0, 160.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-3", + "maxclass" : "newobj", + "text" : "print HARNESS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 300.0, 30.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-4", + "maxclass" : "newobj", + "text" : "delay 2500", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 30.0, 70.0, 70.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-5", + "maxclass" : "newobj", + "text" : "midiinfo", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "" ], + "patching_rect" : [ 30.0, 110.0, 60.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-6", + "maxclass" : "newobj", + "text" : "print MIDIOUTS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 30.0, 150.0, 95.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-7", + "maxclass" : "newobj", + "text" : "print MIDIINS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 150.0, 150.0, 85.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-8", + "maxclass" : "newobj", + "text" : "notein", + "numinlets" : 1, + "numoutlets" : 3, + "outlettype" : [ "int", "int", "int" ], + "patching_rect" : [ 300.0, 110.0, 50.0, 22.0 ] + } + } + ], + "lines" : [ { + "patchline" : { + "source" : [ "obj-1", 0 ], + "destination" : [ "obj-2", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-2", 0 ], + "destination" : [ "obj-3", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-1", 0 ], + "destination" : [ "obj-4", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-4", 0 ], + "destination" : [ "obj-5", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-5", 0 ], + "destination" : [ "obj-6", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-5", 1 ], + "destination" : [ "obj-7", 0 ] + } + } + ] + } +} diff --git a/tools/max-harness/t11-umenu-click.maxpat b/tools/max-harness/t11-umenu-click.maxpat new file mode 100644 index 00000000..ab607d45 --- /dev/null +++ b/tools/max-harness/t11-umenu-click.maxpat @@ -0,0 +1,88 @@ +{ + "patcher" : { + "fileversion" : 1, + "appversion" : { + "major" : 9, + "minor" : 1, + "revision" : 4, + "architecture" : "x64", + "modernui" : 1 + }, + "classnamespace" : "box", + "rect" : [ 300.0, 300.0, 500.0, 400.0 ], + "boxes" : [ { + "box" : { + "id" : "obj-1", + "maxclass" : "newobj", + "text" : "loadbang", + "numinlets" : 0, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 300.0, 30.0, 60.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-2", + "maxclass" : "message", + "text" : "HARNESS t11 umenu loaded", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 300.0, 70.0, 170.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-3", + "maxclass" : "newobj", + "text" : "print HARNESS", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 300.0, 110.0, 90.0, 22.0 ] + } + } +, { + "box" : { + "id" : "obj-4", + "maxclass" : "umenu", + "items" : [ "alpha", ",", "beta", ",", "gamma", ",", "delta", ",", "epsilon" ], + "numinlets" : 1, + "numoutlets" : 3, + "outlettype" : [ "int", "", "" ], + "parameter_enable" : 0, + "patching_rect" : [ 40.0, 60.0, 180.0, 30.0 ] + } + } +, { + "box" : { + "id" : "obj-5", + "maxclass" : "newobj", + "text" : "print UMENUSEL", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 40.0, 120.0, 100.0, 22.0 ] + } + } + ], + "lines" : [ { + "patchline" : { + "source" : [ "obj-1", 0 ], + "destination" : [ "obj-2", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-2", 0 ], + "destination" : [ "obj-3", 0 ] + } + } +, { + "patchline" : { + "source" : [ "obj-4", 0 ], + "destination" : [ "obj-5", 0 ] + } + } + ] + } +}