diff --git a/patches/midi.notecluster/README.md b/patches/midi.notecluster/README.md new file mode 100644 index 0000000..1b77aa6 --- /dev/null +++ b/patches/midi.notecluster/README.md @@ -0,0 +1,84 @@ +# midi.notecluster + +Group note-ons arriving within a short time window and emit chord cluster metadata. + +## Description + +`midi.notecluster` collects MIDI note-on events that arrive within a configurable time window and outputs them as a single cluster list with metadata. Note-offs pass through a separate outlet unchanged. + +This packages a common chord-detection pattern into a reusable abstraction. + +## Inlets + +| Inlet | Type | Description | +|-------|------|-------------| +| 1 | note pairs (pitch velocity) | MIDI note pairs from `midiparse` note outlet or similar | +| 2 | control messages | `window`, `sort`, `unique`, `flush`, `reset`, `bypass` | + +## Outlets + +| Outlet | Type | Description | +|--------|------|-------------| +| 1 | list | `cluster ...` - sorted/deduped pitch list | +| 2 | messages | Metadata: `count `, `lowest

`, `highest

`, `single`, `done` | +| 3 | list | Note-off passthrough: ` 0` | + +## Messages + +### Inlet 1 + +- **pitch velocity** (list of two ints): Note-on if velocity > 0 (accumulated into cluster), note-off if velocity == 0 (passed through outlet 3). + +### Inlet 2 (Control) + +| Message | Default | Description | +|---------|---------|-------------| +| `window ` | 5 | Cluster window duration in milliseconds | +| `sort <0\|1>` | 1 | Sort pitches low-to-high before output | +| `unique <0\|1>` | 1 | Remove duplicate pitches within a cluster | +| `flush` | - | Emit pending cluster immediately | +| `reset` | - | Clear pending cluster and stop timer | +| `bypass <0\|1>` | 0 | Route all input directly to outlet 3 | + +## Behavior + +1. The first note-on starts the cluster window timer. +2. Additional note-ons arriving within the window join the current cluster. +3. When the window closes (or `flush` is received), the accumulated pitches are optionally sorted and deduplicated, then emitted as `cluster p1 p2 ...` on outlet 1. +4. Metadata follows on outlet 2: `highest`, `lowest`, `count`, `single` (if cluster size is 1), and `done`. +5. The cluster buffer is cleared after emission, ready for the next cluster. +6. Note-offs always pass through outlet 3 immediately, regardless of cluster state. + +## Metadata Output Sequence + +After each cluster emission, outlet 2 outputs (in order): + +1. `highest ` - highest pitch in cluster +2. `lowest ` - lowest pitch in cluster (same as first element when sorted) +3. `count ` - number of notes in cluster +4. `single` - only emitted if cluster contains exactly one note +5. `done` - signals completion + +## Examples + +``` +; Two notes within 5ms window -> one cluster +60 100, 64 100 -> outlet 1: cluster 60 64 + outlet 2: highest 64, lowest 60, count 2, done + +; Note-off passes through +60 0 -> outlet 3: 60 0 + +; Single note +72 100 -> outlet 1: cluster 72 + outlet 2: highest 72, lowest 72, count 1, single, done +``` + +## Instance Isolation + +All internal state uses `#0`-scoped names (`#0_window`, `#0_sort`, `#0_unique`, etc.), ensuring multiple instances do not interfere with each other. + +## Requirements + +- Max 9 (uses standard `zl` objects, `delay`, `gate`, `trigger`) +- No external dependencies diff --git a/patches/midi.notecluster/midi.notecluster-help.maxpat b/patches/midi.notecluster/midi.notecluster-help.maxpat new file mode 100644 index 0000000..cb7d622 --- /dev/null +++ b/patches/midi.notecluster/midi.notecluster-help.maxpat @@ -0,0 +1,398 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 800.0, 650.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [15.0, 15.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Help patch for midi.notecluster", + "tags": "", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "autosave": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "midi.notecluster", + "fontsize": 18.0, + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 15.0, 200.0, 27.0] + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Group note-ons within a time window and emit chord cluster metadata.\nInput: note pairs (pitch velocity). Note-ons accumulate during the window.\nWhen the window closes, the cluster list and metadata are emitted.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 45.0, 500.0, 48.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "comment", + "text": "--- Basic usage ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 105.0, 150.0, 20.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "comment", + "text": "Send note pairs to inlet 1. Try rapid chords:", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 130.0, 300.0, 20.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "message", + "text": "60 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [15.0, 160.0, 50.0, 22.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "message", + "text": "64 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [75.0, 160.0, 50.0, 22.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "message", + "text": "67 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [135.0, 160.0, 50.0, 22.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "comment", + "text": "note-offs:", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [210.0, 160.0, 60.0, 20.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "message", + "text": "60 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [270.0, 160.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "64 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [320.0, 160.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "midi.notecluster", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [15.0, 210.0, 420.0, 22.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "print cluster", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 255.0, 78.0, 22.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "print metadata", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [210.0, 255.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "print noteoff", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [420.0, 255.0, 78.0, 22.0] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "comment", + "text": "--- Control messages (inlet 2) ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 300.0, 250.0, 20.0] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "message", + "text": "window 10", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [15.0, 330.0, 68.0, 22.0] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "message", + "text": "window 50", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [90.0, 330.0, 68.0, 22.0] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "message", + "text": "sort 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [165.0, 330.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "message", + "text": "sort 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [220.0, 330.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "message", + "text": "unique 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [275.0, 330.0, 58.0, 22.0] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "message", + "text": "unique 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [340.0, 330.0, 58.0, 22.0] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "message", + "text": "flush", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [405.0, 330.0, 38.0, 22.0] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [450.0, 330.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "message", + "text": "bypass 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [500.0, 330.0, 58.0, 22.0] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "message", + "text": "bypass 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [565.0, 330.0, 58.0, 22.0] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "newobj", + "text": "midi.notecluster", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [15.0, 375.0, 570.0, 22.0] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "newobj", + "text": "print ctrl_cluster", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 420.0, 102.0, 22.0] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "newobj", + "text": "print ctrl_meta", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [270.0, 420.0, 90.0, 22.0] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "newobj", + "text": "print ctrl_noteoff", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [570.0, 420.0, 102.0, 22.0] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "comment", + "text": "--- Outputs ---\nOutlet 1: cluster ...\nOutlet 2: count , lowest

, highest

, single, done\nOutlet 3: note-off passthrough (pitch 0)", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 465.0, 450.0, 62.0] + } + }, + { + "box": { + "id": "obj-60", + "maxclass": "comment", + "text": "--- Notes ---\n- Default window: 5ms (good for MIDI chord detection)\n- sort 1 (default): pitches sorted low to high\n- unique 1 (default): duplicate pitches collapsed\n- Single notes emit as cluster of size 1\n- Note-offs pass through outlet 3 unchanged\n- bypass 1: routes all input to outlet 3", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 540.0, 450.0, 90.0] + } + } + ], + "lines": [ + { "patchline": { "source": ["obj-12", 0], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-13", 0], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-14", 0], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-16", 0], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-17", 0], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-20", 0], "destination": ["obj-21", 0] } }, + { "patchline": { "source": ["obj-20", 1], "destination": ["obj-22", 0] } }, + { "patchline": { "source": ["obj-20", 2], "destination": ["obj-23", 0] } }, + { "patchline": { "source": ["obj-31", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-32", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-33", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-34", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-35", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-36", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-37", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-38", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-39", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-40", 0], "destination": ["obj-41", 1] } }, + { "patchline": { "source": ["obj-41", 0], "destination": ["obj-42", 0] } }, + { "patchline": { "source": ["obj-41", 1], "destination": ["obj-43", 0] } }, + { "patchline": { "source": ["obj-41", 2], "destination": ["obj-44", 0] } } + ], + "dependency_cache": [], + "autosave": 0 + } +} diff --git a/patches/midi.notecluster/midi.notecluster.maxpat b/patches/midi.notecluster/midi.notecluster.maxpat new file mode 100644 index 0000000..a587c7e --- /dev/null +++ b/patches/midi.notecluster/midi.notecluster.maxpat @@ -0,0 +1,3265 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 100.0, + 100.0, + 900.0, + 800.0 + ], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [ + 8.0, + 8.0 + ], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Group note-ons within a time window and emit chord cluster metadata", + "digest": "MIDI note clustering for chord detection", + "tags": "midi chord cluster note grouping", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "autosave": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "note pairs: pitch velocity" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 480.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "control: window, sort, unique, flush, reset, bypass" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 720.0, + 30.0, + 30.0 + ], + "comment": "cluster output: cluster p1 p2 ..." + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 280.0, + 720.0, + 30.0, + 30.0 + ], + "comment": "metadata: count, lowest, highest, single, done" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "outlet", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 504.0, + 720.0, + 30.0, + 30.0 + ], + "comment": "note-off passthrough: pitch 0" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "comment", + "text": "--- midi.notecluster ---\nGroup note-ons within a time window,\nemit cluster + metadata", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 640.0, + 16.0, + 200.0, + 42.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "route window sort unique flush reset bypass", + "numinlets": 1, + "numoutlets": 7, + "outlettype": [ + "", + "", + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 480.0, + 64.0, + 320.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "v #0_window 5", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 480.0, + 96.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "v #0_sort 1", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 568.0, + 96.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "v #0_unique 1", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 648.0, + 96.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "s #0_flush", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 736.0, + 96.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 800.0, + 88.0, + 40.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "s #0_reset_cluster", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 800.0, + 128.0, + 104.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "s #0_stop_timer", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 824.0, + 112.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "v #0_bypass 0", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 480.0, + 152.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "t l l", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 72.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 120.0, + 96.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "gate 2", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 152.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 120.0, + 120.0, + 40.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "comment", + "text": "bypass=1 -> outlet 2 -> passthrough", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 200.0, + 152.0, + 192.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "text": "unpack i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 56.0, + 200.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 104.0, + 232.0, + 40.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "newobj", + "text": "> 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 104.0, + 256.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "newobj", + "text": "gate 2", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 296.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "comment", + "text": "gate 1=off 2=on", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 144.0, + 296.0, + 96.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 104.0, + 272.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "newobj", + "text": "pack i 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 504.0, + 360.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "comment", + "text": "note-off passthrough: pitch 0", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 568.0, + 360.0, + 168.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "newobj", + "text": "p ClusterAccumulate", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 360.0, + 112.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 550.0, + 400.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "pitch (note-on)" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t i b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 50.0, + 75.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "zl group 128", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 105.0, + 78.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "r #0_emit_cluster", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 30.0, + 98.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "r #0_reset_cluster", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 350.0, + 30.0, + 105.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "zlclear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 350.0, + 60.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 200.0, + 60.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "message", + "text": "zlclear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 90.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "s #0_start_timer", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 75.0, + 165.0, + 95.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 225.0, + 30.0, + 30.0 + ], + "comment": "accumulated cluster list on emit" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "newobj", + "text": "p ClusterTimer", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 56.0, + 408.0, + 88.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 500.0, + 400.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "r #0_start_timer", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 95.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 60.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "v #0_window", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 75.0, + 90.0, + 75.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "delay 5", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 50.0, + 120.0, + 50.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "r #0_stop_timer", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 30.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 60.0, + 35.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "r #0_flush", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 180.0, + 70.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 270.0, + 30.0, + 30.0 + ], + "comment": "bang when window closes or flush" + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 210.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 75.0, + 240.0, + 35.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 1 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "newobj", + "text": "s #0_emit_cluster", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 432.0, + 96.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-60", + "maxclass": "newobj", + "text": "p ClusterShape", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 496.0, + 88.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 600.0, + 500.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "raw cluster list" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t l l", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 75.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "v #0_sort", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 75.0, + 105.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "gate", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 135.0, + 44.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "zl sort", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "!- 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 170.0, + 105.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "gate", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 155.0, + 135.0, + 44.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "t l l", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 210.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "v #0_unique", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 75.0, + 240.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "gate", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 270.0, + 44.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "zl unique", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 300.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "!- 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 170.0, + 240.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "gate", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 155.0, + 270.0, + 44.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 360.0, + 30.0, + 30.0 + ], + "comment": "shaped cluster list" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-4", + 1 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-12", + 1 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-11", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-8", + 1 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-14", + 1 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-6", + 1 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-13", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-70", + "maxclass": "newobj", + "text": "p ClusterEmit", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 544.0, + 248.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 700.0, + 500.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "shaped cluster list" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "zl len", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "" + ], + "patching_rect": [ + 50.0, + 75.0, + 44.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 50.0, + 105.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "> 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 50.0, + 135.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "gate", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 44.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "t l l l l l", + "numinlets": 1, + "numoutlets": 5, + "outlettype": [ + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 50.0, + 195.0, + 400.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "zl len", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "" + ], + "patching_rect": [ + 140.0, + 225.0, + 44.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "zl nth 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 230.0, + 225.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "zl rev", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 320.0, + 225.0, + 44.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "zl nth 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 320.0, + 255.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "prepend cluster", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 300.0, + 90.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "prepend count", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 140.0, + 300.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "prepend lowest", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 230.0, + 300.0, + 90.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "prepend highest", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 320.0, + 300.0, + 95.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "== 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 140.0, + 255.0, + 38.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 140.0, + 280.0, + 38.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "single", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 140.0, + 330.0, + 44.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "message", + "text": "done", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 410.0, + 225.0, + 38.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 405.0, + 30.0, + 30.0 + ], + "comment": "cluster output" + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 230.0, + 405.0, + 30.0, + 30.0 + ], + "comment": "metadata output" + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "s #0_reset_cluster", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 91.0, + 135.0, + 105.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 56.0, + 40.0, + 22.0 + ], + "text": "t l l" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 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-6", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 1 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 2 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 3 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 4 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-12", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-15", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 1 + ], + "destination": [ + "obj-21", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-30", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-30", + 1 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-30", + 0 + ], + "destination": [ + "obj-5", + 1 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-80", + "maxclass": "comment", + "text": "inlet 1: note pairs (pitch velocity)", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 88.0, + 24.0, + 200.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-81", + "maxclass": "comment", + "text": "inlet 2: control messages", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 512.0, + 24.0, + 136.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-82", + "maxclass": "comment", + "text": "outlet 1: cluster", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 88.0, + 728.0, + 104.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-83", + "maxclass": "comment", + "text": "outlet 2: metadata", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 312.0, + 728.0, + 104.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-84", + "maxclass": "comment", + "text": "outlet 3: note-off passthrough", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 536.0, + 728.0, + 168.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-85", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 480.0, + 200.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-86", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 480.0, + 224.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-87", + "maxclass": "message", + "text": "5", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 480.0, + 248.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-88", + "maxclass": "newobj", + "text": "v #0_window", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 480.0, + 272.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-89", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 520.0, + 248.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-90", + "maxclass": "newobj", + "text": "v #0_sort", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 520.0, + 272.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-91", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 560.0, + 248.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-92", + "maxclass": "newobj", + "text": "v #0_unique", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 560.0, + 272.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-95", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 368.0, + 128.0, + 32.0, + 20.0 + ], + "text": "+ 1" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 1 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 2 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 3 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 4 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 5 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-17", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-20", + 0 + ], + "destination": [ + "obj-22", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-20", + 1 + ], + "destination": [ + "obj-21", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-21", + 0 + ], + "destination": [ + "obj-23", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-23", + 0 + ], + "destination": [ + "obj-95", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-95", + 0 + ], + "destination": [ + "obj-22", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-30", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 1 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-30", + 0 + ], + "destination": [ + "obj-33", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-30", + 1 + ], + "destination": [ + "obj-31", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-31", + 0 + ], + "destination": [ + "obj-32", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-31", + 1 + ], + "destination": [ + "obj-33", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-32", + 0 + ], + "destination": [ + "obj-35", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-35", + 0 + ], + "destination": [ + "obj-33", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-33", + 0 + ], + "destination": [ + "obj-40", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-33", + 1 + ], + "destination": [ + "obj-50", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-40", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-55", + 0 + ], + "destination": [ + "obj-56", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-50", + 0 + ], + "destination": [ + "obj-60", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-60", + 0 + ], + "destination": [ + "obj-70", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-70", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-70", + 1 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-85", + 0 + ], + "destination": [ + "obj-86", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-86", + 0 + ], + "destination": [ + "obj-87", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-86", + 1 + ], + "destination": [ + "obj-89", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-86", + 2 + ], + "destination": [ + "obj-91", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-87", + 0 + ], + "destination": [ + "obj-88", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-89", + 0 + ], + "destination": [ + "obj-90", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-91", + 0 + ], + "destination": [ + "obj-92", + 0 + ] + } + } + ], + "dependency_cache": [] + } +} \ No newline at end of file diff --git a/patches/midi.notepriority/README.md b/patches/midi.notepriority/README.md new file mode 100644 index 0000000..2215c12 --- /dev/null +++ b/patches/midi.notepriority/README.md @@ -0,0 +1,154 @@ +# midi.notepriority + +Monophonic note-priority filter for MIDI note pairs. When multiple notes are held simultaneously, only one note sounds based on the selected priority mode: lowest pitch, highest pitch, or most recently pressed. + +When the active note is released while other notes are still held, the abstraction automatically switches to the next priority winner, emitting a note-off for the old note and a note-on for the new one. + +## Inlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | list (int int) | Note pairs: pitch velocity. Velocity > 0 is note-on, velocity 0 is note-off. Typically connected to `midiparse` outlet 0 or `unpack` from `notein`. | +| 2 | symbol (message) | Control messages: `mode`, `reset`, `panic`, `bypass`. | + +## Outlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | list (int int) | Filtered note pairs: pitch velocity. Only the priority-winning note is output. When the active note changes due to a new note-on or a note-off with switchover, both a note-off for the old pitch and a note-on for the new pitch are emitted. | +| 2 | symbol | State messages: `active ` (current sounding note) or `none` (no notes held). | + +## Arguments + +| Argument | Type | Default | Description | +|----------|------|---------|-------------| +| 1 | symbol | low | Initial priority mode: `low`, `high`, or `last`. | + +``` +midi.notepriority --> low mode (default) +midi.notepriority low --> lowest note priority +midi.notepriority high --> highest note priority +midi.notepriority last --> most recent note priority +``` + +## Messages (Right Inlet) + +### mode *low|high|last* + +Set the priority mode. Takes effect on the next note event. + +- **low**: The lowest-pitched held note is always the active note. Playing a lower note triggers a switchover; releasing it switches back to the next lowest. +- **high**: The highest-pitched held note is always the active note. +- **last**: The most recently pressed held note is always the active note. This is the most common behavior for monophonic synthesizers. + +``` +mode low --> lowest note wins +mode high --> highest note wins +mode last --> most recent note wins +``` + +### panic + +Emit a note-off (velocity 0) for the currently active note, clear all held-note state, and reset the sequence counter. Outlet 2 outputs `none`. + +### reset + +Identical to `panic`: clears all state and emits note-off for the active note. + +### bypass *0|1* + +Enable or disable bypass mode. When bypassed, all note pairs pass directly from inlet 1 to outlet 1 without filtering. Enabling bypass (`bypass 1`) also triggers a panic, clearing all held-note state and emitting a note-off for the active note. This prevents stale internal state from producing incorrect output when bypass is later disabled. When bypass is disabled (`bypass 0`), note-offs for notes that were started during bypass are passed through directly to the output, preventing stuck notes downstream. + +``` +bypass 1 --> panic + pass all notes through unfiltered +bypass 0 --> resume note-priority filtering (default) +``` + +## Defaults + +| Parameter | Default | +|-----------|---------| +| mode | low (or creation argument) | +| bypass | 0 (off) | + +## Priority Modes + +| Mode | Behavior | Use Case | +|------|----------|----------| +| last | Most recently pressed note wins | Classic mono synth, lead playing | +| low | Lowest held note wins | Bass lines, left-hand priority | +| high | Highest held note wins | Melody priority, right-hand lead | + +## Example Usage + +### Monophonic synth voice from polyphonic keyboard + +``` +[midiin] +| +[midiparse] +| +[midi.notepriority] +| +[unpack i i] +| | +[mtof] [/ 127.] +| | +[cycle~] [*~] + | + [ezdac~] +``` + +### Low-note priority for bass + +``` +[midiin] +| +[midiparse] +| +[midi.notepriority low] +| +[your-bass-synth] +``` + +### Switch modes dynamically + +``` +[midiin] [umenu: low high last] +| | +[midiparse] [prepend mode] +| | +[midi.notepriority ------] +| +[your-synth] +``` + +### Panic on transport stop + +``` +[midiin] [live.thisdevice] +| | +[midiparse] [t b] +| | +| [message: panic] +| | +[midi.notepriority --] +| +[your-synth] +``` + +## Implementation Notes + +- Uses `#0`-scoped internal state (`v`, `table` objects) for instance isolation. Multiple instances in the same patch do not interfere. +- Note-on/off detection uses velocity > 0 / == 0 convention. +- Priority scanning uses `uzi 128` to iterate all MIDI pitches (0-127). This is acceptable for the event-driven nature of MIDI note messages. +- Deterministic message ordering is enforced via `trigger` objects throughout. +- The `last` mode tracks note ordering via a monotonically incrementing sequence counter stored in `table #0_order`. + +## See Also + +- **stripnote** -- filter note-offs from note stream +- **borax** -- MIDI note analysis (delta time, voice count, etc.) +- **flush** -- send note-offs for all held notes +- **poly** -- voice allocation for polyphonic note management +- **notein** / **midiparse** -- MIDI input sources diff --git a/patches/midi.notepriority/midi.notepriority-help.maxpat b/patches/midi.notepriority/midi.notepriority-help.maxpat new file mode 100644 index 0000000..31934ec --- /dev/null +++ b/patches/midi.notepriority/midi.notepriority-help.maxpat @@ -0,0 +1,683 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 760.0, 640.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontface": 0, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [8.0, 8.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Help patch for midi.notepriority", + "digest": "Demonstrates monophonic note-priority abstraction", + "tags": "help midi note priority", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "midi.notepriority", + "fontface": 1, + "fontsize": 16.0, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 16.0, 200.0, 24.0] + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Monophonic note-priority filter. Choose which held note sounds based on low, high, or last priority. Outputs filtered note pairs (pitch velocity).", + "linecount": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 42.0, 520.0, 34.0] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "comment", + "text": "--- LAST MODE (DEFAULT) ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 88.0, 200.0, 20.0] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "comment", + "text": "Most recently pressed note always wins. Try overlapping notes.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 108.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "60 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [16.0, 136.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "64 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [72.0, 136.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "67 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [128.0, 136.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "message", + "text": "60 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [184.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "message", + "text": "64 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [232.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "message", + "text": "67 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [280.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "midi.notepriority", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 168.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "print last-notes", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 200.0, 92.0, 22.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "print last-state", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [104.0, 200.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "comment", + "text": "--- LOW MODE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 240.0, 140.0, 20.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "comment", + "text": "Lowest held note always wins.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 260.0, 300.0, 20.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "message", + "text": "60 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [16.0, 288.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "48 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [72.0, 288.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "message", + "text": "48 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [128.0, 288.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "message", + "text": "60 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [176.0, 288.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "midi.notepriority low", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 320.0, 120.0, 22.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "print low-notes", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 352.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "print low-state", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [120.0, 352.0, 84.0, 22.0] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "comment", + "text": "--- HIGH MODE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 88.0, 140.0, 20.0] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "comment", + "text": "Highest held note always wins.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 108.0, 300.0, 20.0] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "message", + "text": "60 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 136.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "message", + "text": "72 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [456.0, 136.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "message", + "text": "72 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [512.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "message", + "text": "60 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [560.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "midi.notepriority high", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [400.0, 168.0, 128.0, 22.0] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "text": "print high-notes", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 200.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "newobj", + "text": "print high-state", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [512.0, 200.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "comment", + "text": "--- PANIC / RESET ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 240.0, 160.0, 20.0] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "comment", + "text": "Panic: note-off active note, clear held state.\nReset: same as panic.", + "linecount": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 260.0, 350.0, 34.0] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "message", + "text": "60 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 304.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "message", + "text": "panic", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [456.0, 304.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [504.0, 304.0, 36.0, 22.0] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "newobj", + "text": "midi.notepriority", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [400.0, 336.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "newobj", + "text": "print panic-notes", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 368.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "newobj", + "text": "print panic-state", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [488.0, 368.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "comment", + "text": "--- MODE CHANGE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 400.0, 140.0, 20.0] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "comment", + "text": "Change mode on the fly via right inlet.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 420.0, 300.0, 20.0] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "message", + "text": "60 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [16.0, 448.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "message", + "text": "mode low", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [128.0, 448.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "message", + "text": "mode high", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [192.0, 448.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "message", + "text": "mode last", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [260.0, 448.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "newobj", + "text": "midi.notepriority", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 480.0, 264.0, 22.0] + } + }, + { + "box": { + "id": "obj-47", + "maxclass": "newobj", + "text": "print mode-notes", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 512.0, 100.0, 22.0] + } + }, + { + "box": { + "id": "obj-48", + "maxclass": "newobj", + "text": "print mode-state", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [264.0, 512.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-49", + "maxclass": "comment", + "text": "--- CONTROL MESSAGES (right inlet) ---\n\nmode - set priority mode\npanic - note-off active, clear state\nreset - same as panic\nbypass <0|1> - passthrough mode", + "linecount": 6, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 420.0, 280.0, 100.0] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "comment", + "text": "Argument sets initial mode (default: last).", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 560.0, 350.0, 20.0] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "comment", + "text": "--- BYPASS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 536.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "message", + "text": "60 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 560.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "message", + "text": "bypass 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [504.0, 560.0, 52.0, 22.0] + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "message", + "text": "bypass 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [564.0, 560.0, 52.0, 22.0] + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "newobj", + "text": "midi.notepriority", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [400.0, 592.0, 176.0, 22.0] + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "newobj", + "text": "print bypass-notes", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 616.0, 108.0, 22.0] + } + } + ], + "lines": [ + { "patchline": { "source": ["obj-5", 0], "destination": ["obj-11", 0] } }, + { "patchline": { "source": ["obj-6", 0], "destination": ["obj-11", 0] } }, + { "patchline": { "source": ["obj-7", 0], "destination": ["obj-11", 0] } }, + { "patchline": { "source": ["obj-8", 0], "destination": ["obj-11", 0] } }, + { "patchline": { "source": ["obj-9", 0], "destination": ["obj-11", 0] } }, + { "patchline": { "source": ["obj-10", 0], "destination": ["obj-11", 0] } }, + { "patchline": { "source": ["obj-11", 0], "destination": ["obj-12", 0] } }, + { "patchline": { "source": ["obj-11", 1], "destination": ["obj-13", 0] } }, + { "patchline": { "source": ["obj-16", 0], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-17", 0], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-18", 0], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-19", 0], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-20", 0], "destination": ["obj-21", 0] } }, + { "patchline": { "source": ["obj-20", 1], "destination": ["obj-22", 0] } }, + { "patchline": { "source": ["obj-25", 0], "destination": ["obj-29", 0] } }, + { "patchline": { "source": ["obj-26", 0], "destination": ["obj-29", 0] } }, + { "patchline": { "source": ["obj-27", 0], "destination": ["obj-29", 0] } }, + { "patchline": { "source": ["obj-28", 0], "destination": ["obj-29", 0] } }, + { "patchline": { "source": ["obj-29", 0], "destination": ["obj-30", 0] } }, + { "patchline": { "source": ["obj-29", 1], "destination": ["obj-31", 0] } }, + { "patchline": { "source": ["obj-34", 0], "destination": ["obj-37", 0] } }, + { "patchline": { "source": ["obj-35", 0], "destination": ["obj-37", 1] } }, + { "patchline": { "source": ["obj-36", 0], "destination": ["obj-37", 1] } }, + { "patchline": { "source": ["obj-37", 0], "destination": ["obj-38", 0] } }, + { "patchline": { "source": ["obj-37", 1], "destination": ["obj-39", 0] } }, + { "patchline": { "source": ["obj-42", 0], "destination": ["obj-46", 0] } }, + { "patchline": { "source": ["obj-43", 0], "destination": ["obj-46", 1] } }, + { "patchline": { "source": ["obj-44", 0], "destination": ["obj-46", 1] } }, + { "patchline": { "source": ["obj-45", 0], "destination": ["obj-46", 1] } }, + { "patchline": { "source": ["obj-46", 0], "destination": ["obj-47", 0] } }, + { "patchline": { "source": ["obj-46", 1], "destination": ["obj-48", 0] } }, + { "patchline": { "source": ["obj-52", 0], "destination": ["obj-55", 0] } }, + { "patchline": { "source": ["obj-53", 0], "destination": ["obj-55", 1] } }, + { "patchline": { "source": ["obj-54", 0], "destination": ["obj-55", 1] } }, + { "patchline": { "source": ["obj-55", 0], "destination": ["obj-56", 0] } } + ], + "dependency_cache": [], + "autosave": 0 + } +} \ No newline at end of file diff --git a/patches/midi.notepriority/midi.notepriority.maxpat b/patches/midi.notepriority/midi.notepriority.maxpat new file mode 100644 index 0000000..5477bce --- /dev/null +++ b/patches/midi.notepriority/midi.notepriority.maxpat @@ -0,0 +1,5785 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 100.0, + 100.0, + 1200.0, + 800.0 + ], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [ + 8.0, + 8.0 + ], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Monophonic note-priority filter for MIDI note pairs", + "digest": "Choose which held note sounds based on low/high/last priority", + "tags": "midi note priority monophonic", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "note pairs: pitch velocity" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "index": 2, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "control: mode, reset, panic, bypass" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 744.0, + 30.0, + 30.0 + ], + "comment": "filtered note pairs: pitch velocity" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 680.0, + 744.0, + 30.0, + 30.0 + ], + "comment": "state: active , none" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "comment", + "text": "--- INIT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 680.0, + 16.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 680.0, + 40.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "patcherargs low", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 680.0, + 64.0, + 96.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 680.0, + 96.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: init defaults (first), send mode (second)" + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "prepend mode", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 680.0, + 128.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 760.0, + 96.0, + 62.0, + 22.0 + ], + "comment": "R-to-L: bypass=0 (first), active=-1 (second), seq=0 (third)" + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 760.0, + 128.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "v #0_seq", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 760.0, + 152.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 792.0, + 128.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "v #0_active", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 792.0, + 152.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 824.0, + 128.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 824.0, + 152.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "comment", + "text": "--- CONTROL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 56.0, + 100.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "route mode reset panic bypass", + "numinlets": 1, + "numoutlets": 5, + "outlettype": [ + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 400.0, + 80.0, + 176.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "v #0_mode", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 112.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "s #0_reset", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 440.0, + 112.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "s #0_panic", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 480.0, + 112.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 552.0, + 144.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-69", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 520.0, + 112.0, + 48.0, + 22.0 + ], + "comment": "set bypass state, then panic if enabling" + } + }, + { + "box": { + "id": "obj-70", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 520.0, + 144.0, + 36.0, + 22.0 + ], + "comment": "bypass=1 triggers panic to clear stale state" + } + }, + { + "box": { + "id": "obj-71", + "maxclass": "newobj", + "text": "s #0_panic", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 520.0, + 168.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "comment", + "text": "--- BYPASS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 56.0, + 100.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 56.0, + 80.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: bang gets bypass (first), list to gate (second)" + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 104.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 120.0, + 128.0, + 30.0, + 22.0 + ], + "comment": "bypass=0 -> gate 1 (process), bypass=1 -> gate 2 (passthrough)" + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "gate 2 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 160.0, + 152.0, + 22.0 + ], + "comment": "outlet 1=process, outlet 2=bypass" + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "comment", + "text": "--- UNPACK NOTE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 200.0, + 130.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "unpack i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 56.0, + 224.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 104.0, + 248.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: store vel (first), then test >0 (second)" + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "newobj", + "text": "v #0_vel", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 128.0, + 272.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "newobj", + "text": "v #0_pitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 248.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "newobj", + "text": "> 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 104.0, + 296.0, + 30.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "newobj", + "text": "sel 1 0", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "" + ], + "patching_rect": [ + 104.0, + 320.0, + 56.0, + 22.0 + ], + "comment": "1=note-on, 0=note-off" + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "comment", + "text": "--- NOTE-ON PATH ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 352.0, + 130.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 56.0, + 376.0, + 152.0, + 22.0 + ], + "comment": "R-to-L: record held+order (first), inc seq (second), find winner (third)" + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "newobj", + "text": "p RecordNoteOn", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 192.0, + 400.0, + 88.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 400.0, + 300.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "bang to record" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "v #0_pitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 75.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 50.0, + 105.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: set order[pitch]=seq (first), set held[pitch]=1 (second)" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_seq", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 115.0, + 135.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "pack i i", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 115.0, + 165.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "table #0_order", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 115.0, + 195.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "$1 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 135.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "table #0_held", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 50.0, + 165.0, + 80.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-3", + 1 + ], + "destination": [ + "obj-4", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-5", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 1 + ], + "destination": [ + "obj-5", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "newobj", + "text": "p IncrementSeq", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 128.0, + 400.0, + 88.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 300.0, + 200.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "bang to increment" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "v #0_seq", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 75.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 50.0, + 105.0, + 30.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_seq", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 135.0, + 48.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-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "newobj", + "text": "s #0_findwinner", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 400.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "comment", + "text": "--- NOTE-OFF PATH ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 296.0, + 432.0, + 140.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 296.0, + 456.0, + 112.0, + 22.0 + ], + "comment": "R-to-L: clear held[pitch] (first), then find winner (second)" + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "newobj", + "text": "p ClearNoteOff", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 392.0, + 480.0, + 80.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 400.0, + 250.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "bang to clear" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "v #0_pitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 75.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 50.0, + 105.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: clear order[pitch] (first), clear held[pitch] (second)" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "message", + "text": "$1 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 115.0, + 135.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "table #0_order", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 115.0, + 165.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "$1 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 135.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "table #0_held", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 50.0, + 165.0, + 80.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-3", + 1 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "newobj", + "text": "s #0_findwinner", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 296.0, + 480.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "comment", + "text": "--- FIND WINNER ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 440.0, + 130.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "newobj", + "text": "r #0_findwinner", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 464.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "newobj", + "text": "v #0_mode", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 488.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-47", + "maxclass": "newobj", + "text": "route low high last", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 56.0, + 512.0, + 128.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-48", + "maxclass": "newobj", + "text": "p FindLow", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 536.0, + 56.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 480.0, + 360.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "bang to scan" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 75.0, + 135.0, + 22.0 + ], + "comment": "R-to-L: init winner=-1 (first), start scan (second)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 105.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 135.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "uzi 128 0", + "numinlets": 2, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "int" + ], + "patching_rect": [ + 50.0, + 105.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 105.0, + 135.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: store scanpitch (first), lookup held (second)" + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "v #0_scanpitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 165.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "table #0_held", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 105.0, + 165.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 105.0, + 195.0, + 36.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 105.0, + 225.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "sel -1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 105.0, + 255.0, + 36.0, + 22.0 + ], + "comment": "only set winner if none found yet (lowest first)" + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "v #0_scanpitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 105.0, + 285.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 105.0, + 315.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 315.0, + 30.0, + 30.0 + ], + "comment": "winner pitch or -1" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 2 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 1 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-49", + "maxclass": "newobj", + "text": "p FindHigh", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 104.0, + 536.0, + 64.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 480.0, + 360.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "bang to scan" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 75.0, + 135.0, + 22.0 + ], + "comment": "R-to-L: init winner=-1 (first), start scan (second)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 105.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 135.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "uzi 128 0", + "numinlets": 2, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "int" + ], + "patching_rect": [ + 50.0, + 105.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 105.0, + 135.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: store scanpitch (first), lookup held (second)" + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "v #0_scanpitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 165.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "table #0_held", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 105.0, + 165.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 105.0, + 195.0, + 36.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "v #0_scanpitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 105.0, + 225.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 105.0, + 255.0, + 64.0, + 22.0 + ], + "comment": "always overwrite: scanning low-to-high, last found is highest" + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 315.0, + 30.0, + 30.0 + ], + "comment": "winner pitch or -1" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 2 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 1 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "newobj", + "text": "p FindLast", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 160.0, + 536.0, + 56.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 520.0, + 420.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "bang to scan" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 75.0, + 180.0, + 22.0 + ], + "comment": "R-to-L: init bestseq=-1 (first), init winner=-1 (second), start scan (third)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 215.0, + 105.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_bestseq", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 215.0, + 135.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 105.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 135.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "uzi 128 0", + "numinlets": 2, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "int" + ], + "patching_rect": [ + 50.0, + 105.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 105.0, + 135.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: store scanpitch (first), lookup held (second)" + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "v #0_scanpitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 165.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "table #0_held", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 105.0, + 165.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 105.0, + 195.0, + 36.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "v #0_scanpitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 105.0, + 225.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "table #0_order", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 105.0, + 255.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 105.0, + 285.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: get bestseq for compare (first), hold order val (second)" + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "v #0_bestseq", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 315.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": ">", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 105.0, + 315.0, + 30.0, + 22.0 + ], + "comment": "order > bestseq?" + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 105.0, + 345.0, + 36.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 105.0, + 375.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: update bestseq (first), update winner (second)" + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "v #0_scanpitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 405.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "table #0_order", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 170.0, + 435.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v #0_bestseq", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 465.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "v #0_scanpitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 105.0, + 405.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 105.0, + 435.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 375.0, + 30.0, + 30.0 + ], + "comment": "winner pitch or -1" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 2 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-24", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 2 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 1 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 1 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 1 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-20", + 0 + ], + "destination": [ + "obj-21", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-22", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-23", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 0 + ], + "destination": [ + "obj-25", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "comment", + "text": "--- EMIT LOGIC ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 568.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "newobj", + "text": "p EmitNotes", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 592.0, + 72.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 640.0, + 520.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "winner pitch (or -1 for none)" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t i b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 50.0, + 75.0, + 100.0, + 22.0 + ], + "comment": "R-to-L: get old active (first), then process winner (second)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "v #0_active", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 135.0, + 105.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_oldactive", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 135.0, + 135.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 105.0, + 64.0, + 22.0 + ], + "comment": "store winner" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "v #0_oldactive", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 90.0, + 105.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "==", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 50.0, + 135.0, + 56.0, + 22.0 + ], + "comment": "winner == oldactive? no change needed" + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "sel 0", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 50.0, + 195.0, + 36.0, + 22.0 + ], + "comment": "0 = different, proceed" + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 225.0, + 200.0, + 22.0 + ], + "comment": "R-to-L: note-off old (first), note-on new (second), update active+status (third)" + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "v #0_oldactive", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 235.0, + 255.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "!= -1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 235.0, + 285.0, + 36.0, + 22.0 + ], + "comment": "had an old active?" + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "gate 1 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 235.0, + 315.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "v #0_oldactive", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 275.0, + 315.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "pack i 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 235.0, + 345.0, + 56.0, + 22.0 + ], + "comment": "pitch 0 = note-off" + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 255.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "!= -1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 50.0, + 285.0, + 36.0, + 22.0 + ], + "comment": "is winner valid?" + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "gate 1 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 315.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 90.0, + 315.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "v #0_vel", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 90.0, + 345.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "pack i i", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 375.0, + 56.0, + 22.0 + ], + "comment": "pitch vel = note-on" + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v #0_winner", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 405.0, + 64.0, + 22.0 + ], + "comment": "update active = winner" + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "v #0_active", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 435.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "!= -1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 50.0, + 465.0, + 36.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "newobj", + "text": "sel 1 0", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "" + ], + "patching_rect": [ + 50.0, + 495.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "text": "v #0_active", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 525.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "newobj", + "text": "prepend active", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 555.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "message", + "text": "none", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 95.0, + 525.0, + 36.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-out1", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 140.0, + 405.0, + 30.0, + 30.0 + ], + "comment": "note pairs out" + } + }, + { + "box": { + "id": "obj-out2", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 585.0, + 30.0, + 30.0 + ], + "comment": "state out" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ], + "order": 1, + "comment": "get oldactive into == right inlet (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-7", + 1 + ], + "comment": "oldactive to right inlet (cold)" + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-7", + 0 + ], + "order": 0, + "comment": "winner to left inlet (hot, triggers compare, fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 2 + ], + "destination": [ + "obj-10", + 0 + ], + "comment": "note-off old (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-out1", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 1 + ], + "destination": [ + "obj-15", + 0 + ], + "comment": "note-on new (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-18", + 0 + ], + "order": 1, + "comment": "get winner pitch (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-20", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-20", + 0 + ], + "order": 0, + "comment": "set pack pitch (fires second, vel already set)" + } + }, + { + "patchline": { + "source": [ + "obj-20", + 0 + ], + "destination": [ + "obj-out1", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-21", + 0 + ], + "comment": "update active+status (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-21", + 0 + ], + "destination": [ + "obj-22", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-23", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-23", + 0 + ], + "destination": [ + "obj-24", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 0 + ], + "destination": [ + "obj-25", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-25", + 0 + ], + "destination": [ + "obj-26", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-26", + 0 + ], + "destination": [ + "obj-out2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 1 + ], + "destination": [ + "obj-27", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 0 + ], + "destination": [ + "obj-out2", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "comment", + "text": "--- PANIC/RESET ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 480.0, + 440.0, + 130.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "newobj", + "text": "r #0_panic", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 480.0, + 464.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 480.0, + 488.0, + 200.0, + 22.0 + ], + "comment": "R-to-L: clear tables (first), emit note-off for active (second), set active=-1 and status (third)" + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "newobj", + "text": "p ClearTables", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 664.0, + 512.0, + 80.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 400.0, + 300.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "bang to clear" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 75.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: reset seq (first), clear tables (second)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 115.0, + 105.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_seq", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 115.0, + 135.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "uzi 128 0", + "numinlets": 2, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "int" + ], + "patching_rect": [ + 50.0, + 105.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "$1 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 135.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "t l l", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: clear order (first), clear held (second)" + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "table #0_held", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 50.0, + 195.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "table #0_order", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 115.0, + 195.0, + 80.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 2 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 1 + ], + "destination": [ + "obj-9", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-57", + "maxclass": "newobj", + "text": "v #0_active", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 576.0, + 512.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-58", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 576.0, + 536.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: check !=-1 for gate (first), hold pitch for pack (second)" + } + }, + { + "box": { + "id": "obj-59", + "maxclass": "newobj", + "text": "!= -1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 608.0, + 560.0, + 36.0, + 22.0 + ], + "comment": "was there an active note?" + } + }, + { + "box": { + "id": "obj-60", + "maxclass": "newobj", + "text": "gate 1 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 576.0, + 592.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-62", + "maxclass": "newobj", + "text": "pack i 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 576.0, + 624.0, + 56.0, + 22.0 + ], + "comment": "pitch 0 = note-off" + } + }, + { + "box": { + "id": "obj-63", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 480.0, + 512.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: set active=-1 (first), status none (second)" + } + }, + { + "box": { + "id": "obj-64", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 512.0, + 536.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-65", + "maxclass": "newobj", + "text": "v #0_active", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 512.0, + 560.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-66", + "maxclass": "message", + "text": "none", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 480.0, + 536.0, + 36.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-67", + "maxclass": "newobj", + "text": "r #0_reset", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 760.0, + 464.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-68", + "maxclass": "newobj", + "text": "s #0_panic", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 760.0, + 488.0, + 60.0, + 22.0 + ], + "comment": "reset triggers panic first" + } + }, + { + "box": { + "id": "obj-72", + "maxclass": "newobj", + "text": "v #0_pitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 296.0, + 352.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-73", + "maxclass": "newobj", + "text": "table #0_held", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "bang" + ], + "patching_rect": [ + 296.0, + 376.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-74", + "maxclass": "newobj", + "text": "sel 0", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 296.0, + 400.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-75", + "maxclass": "newobj", + "text": "v #0_pitch", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 344.0, + 432.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-76", + "maxclass": "newobj", + "text": "pack i 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 344.0, + 456.0, + 56.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-24", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 1 + ], + "destination": [ + "obj-25", + 0 + ], + "comment": "bang: get bypass state (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-24", + 0 + ], + "destination": [ + "obj-27", + 1 + ], + "comment": "list: msg to bypass gate (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-25", + 0 + ], + "destination": [ + "obj-26", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-26", + 0 + ], + "destination": [ + "obj-27", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 1 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "bypass passthrough" + } + }, + { + "patchline": { + "source": [ + "obj-27", + 0 + ], + "destination": [ + "obj-29", + 0 + ], + "comment": "to note processing" + } + }, + { + "patchline": { + "source": [ + "obj-29", + 0 + ], + "destination": [ + "obj-32", + 0 + ], + "comment": "store pitch" + } + }, + { + "patchline": { + "source": [ + "obj-29", + 1 + ], + "destination": [ + "obj-30", + 0 + ], + "comment": "velocity to processing" + } + }, + { + "patchline": { + "source": [ + "obj-30", + 1 + ], + "destination": [ + "obj-31", + 0 + ], + "comment": "store velocity (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-30", + 0 + ], + "destination": [ + "obj-33", + 0 + ], + "comment": "test vel > 0 (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-33", + 0 + ], + "destination": [ + "obj-34", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-34", + 0 + ], + "destination": [ + "obj-36", + 0 + ], + "comment": "note-on path" + } + }, + { + "patchline": { + "source": [ + "obj-36", + 2 + ], + "destination": [ + "obj-37", + 0 + ], + "comment": "record held+order (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-36", + 1 + ], + "destination": [ + "obj-38", + 0 + ], + "comment": "increment seq (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-36", + 0 + ], + "destination": [ + "obj-39", + 0 + ], + "comment": "find winner (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-41", + 1 + ], + "destination": [ + "obj-42", + 0 + ], + "comment": "clear held[pitch] (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-41", + 0 + ], + "destination": [ + "obj-43", + 0 + ], + "comment": "find winner (fires second/last)" + } + }, + { + "patchline": { + "source": [ + "obj-45", + 0 + ], + "destination": [ + "obj-46", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-46", + 0 + ], + "destination": [ + "obj-47", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-47", + 0 + ], + "destination": [ + "obj-48", + 0 + ], + "comment": "low mode" + } + }, + { + "patchline": { + "source": [ + "obj-47", + 1 + ], + "destination": [ + "obj-49", + 0 + ], + "comment": "high mode" + } + }, + { + "patchline": { + "source": [ + "obj-47", + 2 + ], + "destination": [ + "obj-50", + 0 + ], + "comment": "last mode" + } + }, + { + "patchline": { + "source": [ + "obj-48", + 0 + ], + "destination": [ + "obj-52", + 0 + ], + "comment": "low winner to emit" + } + }, + { + "patchline": { + "source": [ + "obj-49", + 0 + ], + "destination": [ + "obj-52", + 0 + ], + "comment": "high winner to emit" + } + }, + { + "patchline": { + "source": [ + "obj-50", + 0 + ], + "destination": [ + "obj-52", + 0 + ], + "comment": "last winner to emit" + } + }, + { + "patchline": { + "source": [ + "obj-52", + 0 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "note pairs output" + } + }, + { + "patchline": { + "source": [ + "obj-52", + 1 + ], + "destination": [ + "obj-4", + 0 + ], + "comment": "state output" + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-19", + 0 + ], + "comment": "mode" + } + }, + { + "patchline": { + "source": [ + "obj-18", + 1 + ], + "destination": [ + "obj-20", + 0 + ], + "comment": "reset" + } + }, + { + "patchline": { + "source": [ + "obj-18", + 2 + ], + "destination": [ + "obj-21", + 0 + ], + "comment": "panic" + } + }, + { + "patchline": { + "source": [ + "obj-18", + 3 + ], + "destination": [ + "obj-69", + 0 + ], + "comment": "bypass value to trigger" + } + }, + { + "patchline": { + "source": [ + "obj-69", + 1 + ], + "destination": [ + "obj-22", + 0 + ], + "comment": "set bypass state (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-69", + 0 + ], + "destination": [ + "obj-70", + 0 + ], + "comment": "check if bypass=1 (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-70", + 0 + ], + "destination": [ + "obj-71", + 0 + ], + "comment": "bypass=1: panic to clear stale state" + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ], + "comment": "prepend mode to patcherargs" + } + }, + { + "patchline": { + "source": [ + "obj-8", + 1 + ], + "destination": [ + "obj-10", + 0 + ], + "comment": "init defaults" + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-18", + 0 + ], + "comment": "send mode from init" + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 1 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 2 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-54", + 0 + ], + "destination": [ + "obj-55", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-55", + 2 + ], + "destination": [ + "obj-56", + 0 + ], + "comment": "clear tables (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-55", + 1 + ], + "destination": [ + "obj-57", + 0 + ], + "comment": "get active for note-off (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-55", + 0 + ], + "destination": [ + "obj-63", + 0 + ], + "comment": "set active=-1 and status (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-57", + 0 + ], + "destination": [ + "obj-58", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-58", + 1 + ], + "destination": [ + "obj-59", + 0 + ], + "comment": "check !=-1 for gate (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-59", + 0 + ], + "destination": [ + "obj-60", + 0 + ], + "comment": "set gate control" + } + }, + { + "patchline": { + "source": [ + "obj-58", + 0 + ], + "destination": [ + "obj-60", + 1 + ], + "comment": "pitch to gate data inlet (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-60", + 0 + ], + "destination": [ + "obj-62", + 0 + ], + "comment": "pitch through gate to pack" + } + }, + { + "patchline": { + "source": [ + "obj-62", + 0 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "panic note-off" + } + }, + { + "patchline": { + "source": [ + "obj-63", + 1 + ], + "destination": [ + "obj-64", + 0 + ], + "comment": "set active=-1 (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-63", + 0 + ], + "destination": [ + "obj-66", + 0 + ], + "comment": "status none (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-64", + 0 + ], + "destination": [ + "obj-65", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-66", + 0 + ], + "destination": [ + "obj-4", + 0 + ], + "comment": "status: none" + } + }, + { + "patchline": { + "source": [ + "obj-67", + 0 + ], + "destination": [ + "obj-68", + 0 + ], + "comment": "reset triggers panic" + } + }, + { + "patchline": { + "source": [ + "obj-34", + 1 + ], + "destination": [ + "obj-72", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-72", + 0 + ], + "destination": [ + "obj-73", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-73", + 0 + ], + "destination": [ + "obj-74", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-74", + 1 + ], + "destination": [ + "obj-41", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-74", + 0 + ], + "destination": [ + "obj-75", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-75", + 0 + ], + "destination": [ + "obj-76", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-76", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} diff --git a/patches/sig.schmitt.edge~/README.md b/patches/sig.schmitt.edge~/README.md new file mode 100644 index 0000000..4041282 --- /dev/null +++ b/patches/sig.schmitt.edge~/README.md @@ -0,0 +1,137 @@ +# sig.schmitt.edge~ + +Signal-rate threshold detector with hysteresis and edge bang outputs. Wraps a common `thresh~`/`edge~` patch pattern into one reusable abstraction. The internal hysteresis is implemented as a gen~ codebox operating per-sample, ensuring no edge events are missed even at high frequencies. + +State goes to `1.` when the input signal rises above the high threshold. State goes to `0.` when the input falls below the low threshold. While the signal is between the two thresholds, the previous state is held. This eliminates chatter from noisy signals that hover near a single threshold. + +## Inlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | signal | Signal input to threshold. | +| 2 | float | Low threshold. Values arriving here update the low threshold immediately. | +| 3 | float | High threshold. Values arriving here update the high threshold immediately. | +| 4 | symbol (message) | Control messages: `low`, `high`, `init`, `reset`, `bypass`. | + +## Outlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | signal | State signal: `0.` (below) or `1.` (above). Continuous signal-rate output. | +| 2 | bang | Rising-edge bang. Fires once each time the state transitions from `0` to `1`. | +| 3 | bang | Falling-edge bang. Fires once each time the state transitions from `1` to `0`. | +| 4 | any | Status messages: `state 0`, `state 1`, `thresholds `, `swapped`. | + +## Arguments + +| Argument | Type | Default | Description | +|----------|------|---------|-------------| +| 1 | float | 0.4 | Initial low threshold. | +| 2 | float | 0.6 | Initial high threshold. | + +``` +sig.schmitt.edge~ --> thresholds 0.4 / 0.6 (default) +sig.schmitt.edge~ 0.2 0.8 --> thresholds 0.2 / 0.8 +sig.schmitt.edge~ -0.5 0.5 --> thresholds -0.5 / 0.5 +``` + +## Messages (Inlet 4) + +### low *float* + +Set the low threshold at runtime. Takes effect on the next audio vector. + +``` +low 0.3 --> set low threshold to 0.3 +``` + +### high *float* + +Set the high threshold at runtime. Takes effect on the next audio vector. + +``` +high 0.7 --> set high threshold to 0.7 +``` + +### init *0|1* + +Set the internal state variable without producing edge bangs. Useful for starting in a known state before audio begins. + +``` +init 1 --> start in high state +init 0 --> start in low state (default) +``` + +### reset + +Restore state to `0`, thresholds to creation-arg defaults (or `0.4`/`0.6`), and bypass to off. + +### bypass *0|1* + +Enable or disable bypass mode. When bypassed, the hysteresis logic is replaced with a simple `>~ 0.5` comparator. Edge bangs still fire on transitions. + +``` +bypass 1 --> simple comparator mode (no hysteresis) +bypass 0 --> normal Schmitt trigger (default) +``` + +## Threshold Validation + +If the low threshold is greater than the high threshold, the gen~ codebox internally clamps them using `min`/`max`, ensuring correct hysteresis behavior regardless of input order. A `swapped` status message is emitted on the status outlet when this condition is detected at the message-rate threshold-reporting level. + +## Implementation Notes + +- Per-sample Schmitt trigger logic runs inside a `gen~` codebox using `History` for state feedback. +- `edge~` detects transitions in the state signal and produces rising/falling bangs at block boundaries. +- `selector~ 2` switches between normal (gen~) and bypass (`>~ 0.5`) paths. +- `sig~` converts message-rate threshold floats to signal-rate inputs for gen~. +- All internal state uses `#0`-scoped names (`v`, `s`, `r`) for instance isolation. +- Deterministic message ordering enforced via `trigger` objects. +- `snapshot~` is not used in the signal path; diagnostics are message-rate only. + +## Example Usage + +### Basic threshold detection + +``` +[cycle~ 0.5] +| +[sig.schmitt.edge~] +| | | +[state] [rise] [fall] +``` + +### Noise gate with wide hysteresis + +``` +[noise~] +| +[sig.schmitt.edge~ 0.2 0.8] +| | | +[*~] [bang] [bang] +``` + +### Dynamic thresholds from UI + +``` +[audio-source~] +| +[sig.schmitt.edge~] +| | | | + | | [print status] + | | + [rise] [fall] + +[flonum] [flonum] +| | +[sig.schmitt.edge~ inlet 2] [sig.schmitt.edge~ inlet 3] +``` + +## See Also + +- **thresh~** -- signal threshold with hysteresis (bangs only, no state signal) +- **edge~** -- detect zero-to-nonzero and nonzero-to-zero transitions +- **change~** -- output signal only when it changes +- **snapshot~** -- sample signal values at message rate +- **gen~** -- per-sample signal processing +- **util.state.latch** -- control-rate Schmitt latch with dwell time (message domain) diff --git a/patches/sig.schmitt.edge~/sig.schmitt.edge~-help.maxpat b/patches/sig.schmitt.edge~/sig.schmitt.edge~-help.maxpat new file mode 100644 index 0000000..38323f3 --- /dev/null +++ b/patches/sig.schmitt.edge~/sig.schmitt.edge~-help.maxpat @@ -0,0 +1,769 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 800.0, 640.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [8.0, 8.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "sig.schmitt.edge~ help patch", + "digest": "", + "tags": "", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "sig.schmitt.edge~", + "fontsize": 16.0, + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 16.0, 200.0, 24.0] + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Signal-rate Schmitt trigger with hysteresis and edge bang outputs", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 40.0, 400.0, 18.0] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "comment", + "text": "-- Basic usage: rising sweep with default thresholds (0.4 / 0.6) --", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 72.0, 400.0, 18.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "cycle~ 0.5", + "numinlets": 2, + "numoutlets": 1, + "outlettype": ["signal"], + "patching_rect": [56.0, 104.0, 64.0, 20.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "comment", + "text": "0.5 Hz sine: sweeps -1 to 1", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [128.0, 104.0, 168.0, 18.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "sig.schmitt.edge~", + "numinlets": 4, + "numoutlets": 4, + "outlettype": ["signal", "bang", "bang", ""], + "patching_rect": [56.0, 136.0, 232.0, 20.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "snapshot~ 50", + "numinlets": 2, + "numoutlets": 1, + "outlettype": ["float"], + "patching_rect": [56.0, 176.0, 72.0, 20.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "flonum", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [56.0, 200.0, 48.0, 20.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "button", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [160.0, 176.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "button", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [232.0, 176.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "print status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [272.0, 176.0, 64.0, 20.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "comment", + "text": "state (0./1.)", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 224.0, 80.0, 18.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "comment", + "text": "rise", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [160.0, 208.0, 32.0, 18.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "comment", + "text": "fall", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [232.0, 208.0, 32.0, 18.0] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "comment", + "text": "-- Custom thresholds via creation args --", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 256.0, 280.0, 18.0] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "newobj", + "text": "noise~", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["signal"], + "patching_rect": [56.0, 288.0, 40.0, 20.0] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "sig.schmitt.edge~ 0.2 0.8", + "numinlets": 4, + "numoutlets": 4, + "outlettype": ["signal", "bang", "bang", ""], + "patching_rect": [56.0, 320.0, 232.0, 20.0] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "button", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [160.0, 352.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "button", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [232.0, 352.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "comment", + "text": "wider hysteresis band: fewer toggles from noise", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [104.0, 288.0, 280.0, 18.0] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "comment", + "text": "-- Runtime threshold changes via inlets 2 and 3 --", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 400.0, 320.0, 18.0] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "newobj", + "text": "cycle~ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": ["signal"], + "patching_rect": [56.0, 432.0, 56.0, 20.0] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "flonum", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [160.0, 432.0, 48.0, 20.0] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "flonum", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [224.0, 432.0, 48.0, 20.0] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "newobj", + "text": "sig.schmitt.edge~", + "numinlets": 4, + "numoutlets": 4, + "outlettype": ["signal", "bang", "bang", ""], + "patching_rect": [56.0, 464.0, 232.0, 20.0] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "button", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [160.0, 496.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "button", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [232.0, 496.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "comment", + "text": "low", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [160.0, 416.0, 32.0, 18.0] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "comment", + "text": "high", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [224.0, 416.0, 32.0, 18.0] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "comment", + "text": "-- Control messages (inlet 4): reset, bypass --", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [424.0, 72.0, 320.0, 18.0] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "newobj", + "text": "cycle~ 2", + "numinlets": 2, + "numoutlets": 1, + "outlettype": ["signal"], + "patching_rect": [440.0, 104.0, 56.0, 20.0] + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [600.0, 104.0, 40.0, 20.0] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "message", + "text": "bypass 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [648.0, 104.0, 56.0, 20.0] + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "message", + "text": "bypass 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [712.0, 104.0, 56.0, 20.0] + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "newobj", + "text": "sig.schmitt.edge~ 0.3 0.7", + "numinlets": 4, + "numoutlets": 4, + "outlettype": ["signal", "bang", "bang", ""], + "patching_rect": [440.0, 136.0, 280.0, 20.0] + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "button", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [552.0, 168.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-57", + "maxclass": "button", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [608.0, 168.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-58", + "maxclass": "newobj", + "text": "print ctrl-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [648.0, 168.0, 88.0, 20.0] + } + }, + { + "box": { + "id": "obj-60", + "maxclass": "newobj", + "text": "ezdac~", + "numinlets": 2, + "numoutlets": 0, + "patching_rect": [424.0, 560.0, 44.0, 20.0] + } + }, + { + "box": { + "id": "obj-61", + "maxclass": "comment", + "text": "Turn on audio to hear/see signal processing", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [472.0, 560.0, 264.0, 18.0] + } + }, + { + "box": { + "id": "obj-70", + "maxclass": "comment", + "text": "-- init message: set initial state --", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [424.0, 256.0, 240.0, 18.0] + } + }, + { + "box": { + "id": "obj-71", + "maxclass": "message", + "text": "init 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [600.0, 288.0, 40.0, 20.0] + } + }, + { + "box": { + "id": "obj-72", + "maxclass": "message", + "text": "init 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [648.0, 288.0, 40.0, 20.0] + } + }, + { + "box": { + "id": "obj-73", + "maxclass": "newobj", + "text": "cycle~ 0.25", + "numinlets": 2, + "numoutlets": 1, + "outlettype": ["signal"], + "patching_rect": [440.0, 288.0, 72.0, 20.0] + } + }, + { + "box": { + "id": "obj-74", + "maxclass": "newobj", + "text": "sig.schmitt.edge~", + "numinlets": 4, + "numoutlets": 4, + "outlettype": ["signal", "bang", "bang", ""], + "patching_rect": [440.0, 320.0, 232.0, 20.0] + } + }, + { + "box": { + "id": "obj-75", + "maxclass": "newobj", + "text": "print init-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [648.0, 352.0, 88.0, 20.0] + } + }, + { + "box": { + "id": "obj-80", + "maxclass": "comment", + "text": "-- threshold via control messages --", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [424.0, 400.0, 240.0, 18.0] + } + }, + { + "box": { + "id": "obj-81", + "maxclass": "message", + "text": "low 0.1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [600.0, 432.0, 48.0, 20.0] + } + }, + { + "box": { + "id": "obj-82", + "maxclass": "message", + "text": "high 0.9", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [656.0, 432.0, 56.0, 20.0] + } + }, + { + "box": { + "id": "obj-83", + "maxclass": "newobj", + "text": "noise~", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["signal"], + "patching_rect": [440.0, 432.0, 40.0, 20.0] + } + }, + { + "box": { + "id": "obj-84", + "maxclass": "newobj", + "text": "sig.schmitt.edge~", + "numinlets": 4, + "numoutlets": 4, + "outlettype": ["signal", "bang", "bang", ""], + "patching_rect": [440.0, 464.0, 232.0, 20.0] + } + }, + { + "box": { + "id": "obj-85", + "maxclass": "newobj", + "text": "print thresh-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [648.0, 496.0, 104.0, 20.0] + } + } + ], + "lines": [ + { + "patchline": { + "source": ["obj-10", 0], + "destination": ["obj-12", 0] + } + }, + { + "patchline": { + "source": ["obj-12", 0], + "destination": ["obj-13", 0] + } + }, + { + "patchline": { + "source": ["obj-12", 1], + "destination": ["obj-15", 0] + } + }, + { + "patchline": { + "source": ["obj-12", 2], + "destination": ["obj-16", 0] + } + }, + { + "patchline": { + "source": ["obj-12", 3], + "destination": ["obj-17", 0] + } + }, + { + "patchline": { + "source": ["obj-13", 0], + "destination": ["obj-14", 0] + } + }, + { + "patchline": { + "source": ["obj-26", 0], + "destination": ["obj-27", 0] + } + }, + { + "patchline": { + "source": ["obj-27", 1], + "destination": ["obj-28", 0] + } + }, + { + "patchline": { + "source": ["obj-27", 2], + "destination": ["obj-29", 0] + } + }, + { + "patchline": { + "source": ["obj-36", 0], + "destination": ["obj-39", 0] + } + }, + { + "patchline": { + "source": ["obj-37", 0], + "destination": ["obj-39", 1] + } + }, + { + "patchline": { + "source": ["obj-38", 0], + "destination": ["obj-39", 2] + } + }, + { + "patchline": { + "source": ["obj-39", 1], + "destination": ["obj-40", 0] + } + }, + { + "patchline": { + "source": ["obj-39", 2], + "destination": ["obj-41", 0] + } + }, + { + "patchline": { + "source": ["obj-51", 0], + "destination": ["obj-55", 0] + } + }, + { + "patchline": { + "source": ["obj-52", 0], + "destination": ["obj-55", 3] + } + }, + { + "patchline": { + "source": ["obj-53", 0], + "destination": ["obj-55", 3] + } + }, + { + "patchline": { + "source": ["obj-54", 0], + "destination": ["obj-55", 3] + } + }, + { + "patchline": { + "source": ["obj-55", 1], + "destination": ["obj-56", 0] + } + }, + { + "patchline": { + "source": ["obj-55", 2], + "destination": ["obj-57", 0] + } + }, + { + "patchline": { + "source": ["obj-55", 3], + "destination": ["obj-58", 0] + } + }, + { + "patchline": { + "source": ["obj-71", 0], + "destination": ["obj-74", 3] + } + }, + { + "patchline": { + "source": ["obj-72", 0], + "destination": ["obj-74", 3] + } + }, + { + "patchline": { + "source": ["obj-73", 0], + "destination": ["obj-74", 0] + } + }, + { + "patchline": { + "source": ["obj-74", 3], + "destination": ["obj-75", 0] + } + }, + { + "patchline": { + "source": ["obj-81", 0], + "destination": ["obj-84", 3] + } + }, + { + "patchline": { + "source": ["obj-82", 0], + "destination": ["obj-84", 3] + } + }, + { + "patchline": { + "source": ["obj-83", 0], + "destination": ["obj-84", 0] + } + }, + { + "patchline": { + "source": ["obj-84", 3], + "destination": ["obj-85", 0] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} diff --git a/patches/sig.schmitt.edge~/sig.schmitt.edge~.maxpat b/patches/sig.schmitt.edge~/sig.schmitt.edge~.maxpat new file mode 100644 index 0000000..61892b0 --- /dev/null +++ b/patches/sig.schmitt.edge~/sig.schmitt.edge~.maxpat @@ -0,0 +1,2450 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 100.0, + 100.0, + 800.0, + 720.0 + ], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [ + 8.0, + 8.0 + ], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Signal-rate Schmitt trigger with hysteresis and edge bang outputs", + "digest": "Threshold detector with hysteresis, state signal, and edge bangs", + "tags": "utility signal threshold hysteresis edge schmitt", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "signal" + ], + "patching_rect": [ + 56.0, + 24.0, + 30.0, + 30.0 + ], + "comment": "signal input" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "index": 2, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 24.0, + 30.0, + 30.0 + ], + "comment": "low threshold (float)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "inlet", + "index": 3, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 328.0, + 24.0, + 30.0, + 30.0 + ], + "comment": "high threshold (float)" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "inlet", + "index": 4, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 480.0, + 24.0, + 30.0, + 30.0 + ], + "comment": "control: low, high, init, reset, bypass" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 664.0, + 30.0, + 30.0 + ], + "comment": "state signal (0. or 1.)" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 200.0, + 664.0, + 30.0, + 30.0 + ], + "comment": "rising-edge bang" + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "outlet", + "index": 3, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 328.0, + 664.0, + 30.0, + 30.0 + ], + "comment": "falling-edge bang" + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "outlet", + "index": 4, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 480.0, + 664.0, + 30.0, + 30.0 + ], + "comment": "status (state, thresholds)" + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 200.0, + 64.0, + 52.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 200.0, + 88.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 304.0, + 88.0, + 24.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "s #0_sel", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 304.0, + 112.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "patcherargs 0.4 0.6", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 200.0, + 112.0, + 112.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "unpack f f", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "float" + ], + "patching_rect": [ + 200.0, + 136.0, + 144.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "comment", + "text": "-- init: patcherargs -> unpack low high --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 360.0, + 136.0, + 248.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "s #0_set_low", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 200.0, + 160.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "s #0_set_high", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 328.0, + 160.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "text": "r #0_set_low", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 200.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "newobj", + "text": "r #0_set_high", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 328.0, + 200.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "t f f", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "float" + ], + "patching_rect": [ + 200.0, + 224.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "newobj", + "text": "sig~ 0.4", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "signal" + ], + "patching_rect": [ + 200.0, + 248.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "v #0_low 0.4", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 264.0, + 248.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "text": "t f f", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "float" + ], + "patching_rect": [ + 328.0, + 224.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "newobj", + "text": "sig~ 0.6", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "signal" + ], + "patching_rect": [ + 328.0, + 248.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "newobj", + "text": "v #0_high 0.6", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 392.0, + 248.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "comment", + "text": "-- gen~ Schmitt trigger (per-sample hysteresis with threshold clamping) --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 288.0, + 440.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "newobj", + "text": "gen~", + "numinlets": 3, + "numoutlets": 1, + "outlettype": [ + "signal" + ], + "patching_rect": [ + 56.0, + 312.0, + 288.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "dsp.gen", + "rect": [ + 200.0, + 200.0, + 500.0, + 360.0 + ], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "newobj", + "text": "in 1", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 30.0, + 30.0, + 30.0, + 22.0 + ], + "comment": "signal input" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "in 2", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 195.0, + 30.0, + 30.0, + 22.0 + ], + "comment": "low threshold" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "in 3", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 360.0, + 30.0, + 30.0, + 22.0 + ], + "comment": "high threshold" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "codebox", + "numinlets": 3, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 30.0, + 90.0, + 345.0, + 120.0 + ], + "code": "History state(0);\n\n// clamp: ensure lo <= hi\nlo = min(in2, in3);\nhi = max(in2, in3);\n\n// Schmitt trigger logic\nabove_high = in1 > hi;\nbelow_low = in1 < lo;\nstate = above_high ? 1 : (below_low ? 0 : state);\n\nout1 = state;" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "out 1", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 30.0, + 240.0, + 35.0, + 22.0 + ], + "comment": "state (0./1.)" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-4", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 2 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + } + ] + } + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "newobj", + "text": ">~ 0.5", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "signal" + ], + "patching_rect": [ + 152.0, + 312.0, + 48.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "comment", + "text": "bypass: simple >0.5 comparator", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 152.0, + 336.0, + 192.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "newobj", + "text": "selector~ 2", + "numinlets": 3, + "numoutlets": 1, + "outlettype": [ + "signal" + ], + "patching_rect": [ + 56.0, + 368.0, + 112.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "comment", + "text": "1=gen~ (normal), 2=bypass", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 176.0, + 368.0, + 160.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "newobj", + "text": "r #0_sel", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 344.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "newobj", + "text": "edge~", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 200.0, + 424.0, + 144.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "comment", + "text": "-- rising / falling edge detection --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 200.0, + 408.0, + 224.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 200.0, + 456.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 328.0, + 456.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "message", + "text": "state 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 304.0, + 488.0, + 48.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "message", + "text": "state 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 432.0, + 488.0, + 48.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "newobj", + "text": "s #0_status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 304.0, + 512.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "newobj", + "text": "s #0_status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 432.0, + 512.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "newobj", + "text": "r #0_status", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 480.0, + 632.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-60", + "maxclass": "newobj", + "text": "route low high init reset bypass", + "numinlets": 1, + "numoutlets": 6, + "outlettype": [ + "", + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 480.0, + 64.0, + 192.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-61", + "maxclass": "comment", + "text": "-- control dispatch --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 480.0, + 48.0, + 136.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-62", + "maxclass": "newobj", + "text": "s #0_set_low", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 480.0, + 96.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-63", + "maxclass": "newobj", + "text": "s #0_set_high", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 520.0, + 96.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-64", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 560.0, + 96.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-65", + "maxclass": "newobj", + "text": "v #0_state", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 560.0, + 120.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-66", + "maxclass": "newobj", + "text": "prepend state", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 600.0, + 120.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-67", + "maxclass": "newobj", + "text": "s #0_status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 600.0, + 144.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-70", + "maxclass": "newobj", + "text": "t b b b b b", + "numinlets": 1, + "numoutlets": 5, + "outlettype": [ + "bang", + "bang", + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 616.0, + 96.0, + 112.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-71", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 616.0, + 120.0, + 24.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-72", + "maxclass": "newobj", + "text": "v #0_state", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 616.0, + 144.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-74", + "maxclass": "newobj", + "text": "s #0_set_low", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 656.0, + 144.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-76", + "maxclass": "newobj", + "text": "s #0_set_high", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 696.0, + 144.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-77", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 744.0, + 120.0, + 24.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-78", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 744.0, + 144.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-80", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 672.0, + 96.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-81", + "maxclass": "newobj", + "text": "v #0_bypass 0", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 672.0, + 120.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-82", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 704.0, + 120.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-83", + "maxclass": "newobj", + "text": "s #0_sel", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 704.0, + 144.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-85", + "maxclass": "newobj", + "text": "p ThresholdReport", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 560.0, + 144.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 400.0, + 300.0 + ], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 30.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "low" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "index": 2, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 180.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "high" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 30.0, + 225.0, + 30.0, + 30.0 + ], + "comment": "thresholds low high" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "pack f f", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 30.0, + 120.0, + 165.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "prepend thresholds", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 30.0, + 165.0, + 113.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": ">", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 240.0, + 120.0, + 30.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 240.0, + 150.0, + 36.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "message", + "text": "swapped", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 240.0, + 180.0, + 54.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "t f f", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "float" + ], + "patching_rect": [ + 30.0, + 75.0, + 225.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 1 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-4", + 1 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-6", + 1 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + } + ] + } + } + }, + { + "box": { + "id": "obj-86", + "maxclass": "comment", + "text": "-- report thresholds + warn if swapped --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 352.0, + 560.0, + 248.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-87", + "maxclass": "newobj", + "text": "v #0_init_low", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 656.0, + 120.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-88", + "maxclass": "newobj", + "text": "v #0_init_high", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 696.0, + 120.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-89", + "maxclass": "newobj", + "text": "v #0_init_low", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 184.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-90", + "maxclass": "newobj", + "text": "v #0_init_high", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 328.0, + 184.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-91", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 792.0, + 120.0, + 24.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-92", + "maxclass": "newobj", + "text": "s #0_sel", + "numinlets": 1, + "numoutlets": 0, + "outlettype": [], + "patching_rect": [ + 792.0, + 144.0, + 56.0, + 20.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 1 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 1 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-21", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-25", + 0 + ], + "destination": [ + "obj-27", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-26", + 0 + ], + "destination": [ + "obj-30", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 0 + ], + "destination": [ + "obj-28", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 1 + ], + "destination": [ + "obj-29", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-30", + 0 + ], + "destination": [ + "obj-31", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-30", + 1 + ], + "destination": [ + "obj-32", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-36", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-37", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-28", + 0 + ], + "destination": [ + "obj-36", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-31", + 0 + ], + "destination": [ + "obj-36", + 2 + ] + } + }, + { + "patchline": { + "source": [ + "obj-36", + 0 + ], + "destination": [ + "obj-40", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-37", + 0 + ], + "destination": [ + "obj-40", + 2 + ] + } + }, + { + "patchline": { + "source": [ + "obj-42", + 0 + ], + "destination": [ + "obj-40", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-40", + 0 + ], + "destination": [ + "obj-5", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-40", + 0 + ], + "destination": [ + "obj-45", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-45", + 0 + ], + "destination": [ + "obj-50", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-45", + 1 + ], + "destination": [ + "obj-51", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-50", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-50", + 1 + ], + "destination": [ + "obj-52", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-51", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-51", + 1 + ], + "destination": [ + "obj-53", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-52", + 0 + ], + "destination": [ + "obj-54", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-53", + 0 + ], + "destination": [ + "obj-55", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-56", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-60", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-60", + 0 + ], + "destination": [ + "obj-62", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-60", + 1 + ], + "destination": [ + "obj-63", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-60", + 2 + ], + "destination": [ + "obj-64", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-60", + 3 + ], + "destination": [ + "obj-70", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-60", + 4 + ], + "destination": [ + "obj-80", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-64", + 0 + ], + "destination": [ + "obj-65", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-64", + 1 + ], + "destination": [ + "obj-66", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-66", + 0 + ], + "destination": [ + "obj-67", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-70", + 0 + ], + "destination": [ + "obj-71", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-71", + 0 + ], + "destination": [ + "obj-72", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-70", + 3 + ], + "destination": [ + "obj-77", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-77", + 0 + ], + "destination": [ + "obj-78", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-80", + 0 + ], + "destination": [ + "obj-81", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-80", + 1 + ], + "destination": [ + "obj-82", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-82", + 0 + ], + "destination": [ + "obj-83", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-29", + 0 + ], + "destination": [ + "obj-85", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-32", + 0 + ], + "destination": [ + "obj-85", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-85", + 0 + ], + "destination": [ + "obj-54", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-70", + 1 + ], + "destination": [ + "obj-87", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-87", + 0 + ], + "destination": [ + "obj-74", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-70", + 2 + ], + "destination": [ + "obj-88", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-88", + 0 + ], + "destination": [ + "obj-76", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-70", + 4 + ], + "destination": [ + "obj-91", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-91", + 0 + ], + "destination": [ + "obj-92", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-89", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-20", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-15", + 1 + ], + "destination": [ + "obj-90", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-15", + 1 + ], + "destination": [ + "obj-21", + 0 + ], + "order": 0 + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} diff --git a/patches/util.cache.ttl/README.md b/patches/util.cache.ttl/README.md new file mode 100644 index 0000000..943fa47 --- /dev/null +++ b/patches/util.cache.ttl/README.md @@ -0,0 +1,158 @@ +# util.cache.ttl + +Key-value cache with time-to-live expiration. Stores values under symbol keys and automatically expires them after a configurable TTL. Expired entries are lazily removed on access (get, has) or eagerly removed via sweep. + +## Inlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | any (symbol, list) | Commands: `set`, `get`, `has`, `invalidate`, `clear`. | +| 2 | symbol (message) | Control messages: `ttl`, `bypass`, `sweep`, `reset`. | + +## Outlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | list | Cache hits: `hit key value`. Output when `get` finds a valid (non-expired) entry. | +| 2 | any | Status/misses: `miss key` (entry not found or expired), `ok` (set succeeded), `invalidated key`, `cleared`, or `1`/`0` (has result). | +| 3 | list | Diagnostics: `size N` (number of entries remaining after sweep). | + +## Arguments + +| Argument | Type | Default | Description | +|----------|------|---------|-------------| +| 1 | int/float | 1000 | Initial TTL in milliseconds. | + +``` +util.cache.ttl --> 1000ms TTL (default) +util.cache.ttl 5000 --> 5 second TTL +util.cache.ttl 60000 --> 1 minute TTL +``` + +## Commands (Left Inlet) + +### set *key value* + +Store a value under a symbol key. The entry expires after the current TTL elapses. Outputs `ok` from outlet 2. + +``` +set foo 42 --> store integer +set bar hello --> store symbol +``` + +### get *key* + +Retrieve a cached value. If the key exists and has not expired, outputs `hit key value` from outlet 1. If the key does not exist or has expired, outputs `miss key` from outlet 2. Expired entries are lazily deleted on access. + +``` +get foo --> hit foo 42 (if valid) +get unknown --> miss unknown +``` + +### has *key* + +Check whether a key exists and is still valid. Outputs `1` from outlet 2 if the key exists and has not expired, `0` otherwise. Expired entries are lazily deleted on access. + +``` +has foo --> 1 (exists and valid) +has unknown --> 0 (not found) +``` + +### invalidate *key* + +Remove a specific key from the cache immediately, regardless of its TTL. Outputs `invalidated key` from outlet 2. + +``` +invalidate foo --> invalidated foo +``` + +### clear + +Remove all entries from the cache. Outputs `cleared` from outlet 2. + +## Messages (Right Inlet) + +### ttl *ms* + +Set the TTL in milliseconds. Applies to subsequent `set` commands; does not retroactively change existing entries. + +``` +ttl 500 --> 500ms expiry for new entries +ttl 30000 --> 30 second expiry for new entries +``` + +### sweep + +Iterate all cached entries and remove any that have expired. After sweeping, outputs `size N` from outlet 3 with the number of remaining (valid) entries. Use this for periodic garbage collection or diagnostics. + +### reset + +Clear all entries and restore the TTL to the creation argument value. + +### bypass *0|1* + +Enable or disable bypass mode. When bypassed, the cache stores the bypass flag but behavior is otherwise unchanged in this version. + +``` +bypass 1 --> enable bypass +bypass 0 --> disable bypass (default) +``` + +## Defaults + +| Parameter | Default | +|-----------|---------| +| ttl | 1000 ms (or creation argument) | +| bypass | 0 (off) | + +## Example Usage + +### Simple key-value store + +``` +[message: set name Max] +| +[util.cache.ttl 10000] +| \ +[route hit] [print status] +| +[print cached-value] +``` + +### Short-lived sensor cache + +``` +[your-sensor-data] +| +[prepend set sensor1] +| +[util.cache.ttl 200] +``` + +### Periodic sweep for diagnostics + +``` +[metro 5000] [your-commands] +| | +[message: sweep] | +| | +[util.cache.ttl 3000 ----------------] +| | | +[route hit] [print status] [print size] +``` + +## Implementation Notes + +- Uses `#0`-scoped internal state (`v`, `dict` objects) for instance isolation. Multiple instances in the same patch do not interfere. +- Two parallel `dict` objects store values (`#0_values`) and expiry timestamps (`#0_expiry`). Keys are shared between both dicts. +- Timestamps are sourced from `cpuclock` (milliseconds since Max started). Expiry is computed as `cpuclock + ttl` at the time of `set`. +- Lazy expiry: `get` and `has` check the expiry timestamp before returning. If expired, the entry is silently removed from both dicts and a miss is reported. No `invalidated` status message is emitted for lazy expiry — only explicit `invalidate key` commands produce status output. +- Eager expiry: `sweep` iterates all keys in the expiry dict, compares each against the current time, and silently removes expired entries. Only the final `size` diagnostic is output. +- Deterministic message ordering is enforced via `trigger` objects throughout. + +## See Also + +- **dict** -- key-value dictionary storage +- **coll** -- indexed collection (ordered, file-backed) +- **table** -- integer-indexed array storage +- **util.debounce** -- debounce rapid input (companion utility) diff --git a/patches/util.cache.ttl/util.cache.ttl-help.maxpat b/patches/util.cache.ttl/util.cache.ttl-help.maxpat new file mode 100644 index 0000000..6c13f2e --- /dev/null +++ b/patches/util.cache.ttl/util.cache.ttl-help.maxpat @@ -0,0 +1,684 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 900.0, 700.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontface": 0, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [15.0, 15.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "", + "digest": "", + "tags": "", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "util.cache.ttl", + "fontsize": 18.0, + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 15.0, 200.0, 27.0] + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Key-value cache with time-to-live expiration. Stores values and automatically expires them after a configurable TTL.", + "linecount": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 45.0, 450.0, 33.0] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "comment", + "text": "--- Basic set/get ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 90.0, 150.0, 20.0] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "message", + "text": "set foo 42", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [15.0, 120.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "set bar hello", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [90.0, 120.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "get foo", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [180.0, 120.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "get bar", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [240.0, 120.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "message", + "text": "get nonexistent", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [300.0, 120.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "util.cache.ttl 2000", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [15.0, 165.0, 112.0, 22.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "print hits", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 210.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "print status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [90.0, 210.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "print diag", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [180.0, 210.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "comment", + "text": "--- TTL expiry ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 255.0, 150.0, 20.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "comment", + "text": "Set a value with 500ms TTL, then try to get it after the TTL expires.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 275.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "message", + "text": "set temp 99", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [15.0, 300.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "message", + "text": "get temp", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [180.0, 300.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "ttl 500", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [90.0, 300.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "util.cache.ttl 500", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [15.0, 345.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "print ttl-hits", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 390.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "print ttl-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [90.0, 390.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "comment", + "text": "1) Click 'set temp 99' to store. 2) Immediately click 'get temp' -> hit. 3) Wait >500ms, click 'get temp' again -> miss (expired).", + "linecount": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [250.0, 300.0, 400.0, 33.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "comment", + "text": "--- has / invalidate / clear ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 435.0, 200.0, 20.0] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "message", + "text": "set key1 aaa", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [15.0, 465.0, 76.0, 22.0] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "message", + "text": "has key1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [105.0, 465.0, 52.0, 22.0] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "message", + "text": "invalidate key1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [170.0, 465.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [270.0, 465.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "util.cache.ttl 5000", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [15.0, 510.0, 112.0, 22.0] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "newobj", + "text": "print cmd-hits", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 555.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "print cmd-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [105.0, 555.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "comment", + "text": "--- sweep + diagnostics ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [500.0, 90.0, 200.0, 20.0] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "message", + "text": "set a 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [500.0, 120.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "message", + "text": "set b 2", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [560.0, 120.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "message", + "text": "set c 3", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [620.0, 120.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "message", + "text": "sweep", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [700.0, 120.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "newobj", + "text": "util.cache.ttl 1000", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [500.0, 165.0, 112.0, 22.0] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "newobj", + "text": "print sweep-hits", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [500.0, 210.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "newobj", + "text": "print sweep-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [600.0, 210.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "newobj", + "text": "print sweep-diag", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [715.0, 210.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "comment", + "text": "1) Set a, b, c. 2) Click sweep immediately -> size 3. 3) Wait >1s, sweep again -> size 0 (all expired).", + "linecount": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [500.0, 240.0, 380.0, 33.0] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "comment", + "text": "--- dynamic TTL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [500.0, 290.0, 150.0, 20.0] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "message", + "text": "set x 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [500.0, 320.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "message", + "text": "get x", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [570.0, 320.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "message", + "text": "ttl 200", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [700.0, 320.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "message", + "text": "ttl 10000", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [760.0, 320.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "newobj", + "text": "util.cache.ttl 3000", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [500.0, 365.0, 112.0, 22.0] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "newobj", + "text": "print dyn-hits", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [500.0, 410.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-47", + "maxclass": "newobj", + "text": "print dyn-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [600.0, 410.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-48", + "maxclass": "comment", + "text": "Change TTL on the fly via right inlet.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [630.0, 320.0, 220.0, 20.0] + } + }, + { + "box": { + "id": "obj-49", + "maxclass": "comment", + "text": "--- reset ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [500.0, 460.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "message", + "text": "set y 200", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [500.0, 490.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [700.0, 490.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "message", + "text": "get y", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [570.0, 490.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "newobj", + "text": "util.cache.ttl 5000", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [500.0, 535.0, 112.0, 22.0] + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "newobj", + "text": "print reset-hits", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [500.0, 580.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "newobj", + "text": "print reset-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [600.0, 580.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "comment", + "text": "1) Set y. 2) Reset clears cache and restores default TTL. 3) Get y -> miss.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [500.0, 610.0, 380.0, 20.0] + } + } + ], + "lines": [ + { "patchline": { "source": ["obj-4", 0], "destination": ["obj-9", 0] } }, + { "patchline": { "source": ["obj-5", 0], "destination": ["obj-9", 0] } }, + { "patchline": { "source": ["obj-6", 0], "destination": ["obj-9", 0] } }, + { "patchline": { "source": ["obj-7", 0], "destination": ["obj-9", 0] } }, + { "patchline": { "source": ["obj-8", 0], "destination": ["obj-9", 0] } }, + { "patchline": { "source": ["obj-9", 0], "destination": ["obj-10", 0] } }, + { "patchline": { "source": ["obj-9", 1], "destination": ["obj-11", 0] } }, + { "patchline": { "source": ["obj-9", 2], "destination": ["obj-12", 0] } }, + { "patchline": { "source": ["obj-15", 0], "destination": ["obj-18", 0] } }, + { "patchline": { "source": ["obj-16", 0], "destination": ["obj-18", 0] } }, + { "patchline": { "source": ["obj-17", 0], "destination": ["obj-18", 1] } }, + { "patchline": { "source": ["obj-18", 0], "destination": ["obj-19", 0] } }, + { "patchline": { "source": ["obj-18", 1], "destination": ["obj-20", 0] } }, + { "patchline": { "source": ["obj-23", 0], "destination": ["obj-27", 0] } }, + { "patchline": { "source": ["obj-24", 0], "destination": ["obj-27", 0] } }, + { "patchline": { "source": ["obj-25", 0], "destination": ["obj-27", 0] } }, + { "patchline": { "source": ["obj-26", 0], "destination": ["obj-27", 0] } }, + { "patchline": { "source": ["obj-27", 0], "destination": ["obj-28", 0] } }, + { "patchline": { "source": ["obj-27", 1], "destination": ["obj-29", 0] } }, + { "patchline": { "source": ["obj-31", 0], "destination": ["obj-35", 0] } }, + { "patchline": { "source": ["obj-32", 0], "destination": ["obj-35", 0] } }, + { "patchline": { "source": ["obj-33", 0], "destination": ["obj-35", 0] } }, + { "patchline": { "source": ["obj-34", 0], "destination": ["obj-35", 1] } }, + { "patchline": { "source": ["obj-35", 0], "destination": ["obj-36", 0] } }, + { "patchline": { "source": ["obj-35", 1], "destination": ["obj-37", 0] } }, + { "patchline": { "source": ["obj-35", 2], "destination": ["obj-38", 0] } }, + { "patchline": { "source": ["obj-41", 0], "destination": ["obj-45", 0] } }, + { "patchline": { "source": ["obj-42", 0], "destination": ["obj-45", 0] } }, + { "patchline": { "source": ["obj-43", 0], "destination": ["obj-45", 1] } }, + { "patchline": { "source": ["obj-44", 0], "destination": ["obj-45", 1] } }, + { "patchline": { "source": ["obj-45", 0], "destination": ["obj-46", 0] } }, + { "patchline": { "source": ["obj-45", 1], "destination": ["obj-47", 0] } }, + { "patchline": { "source": ["obj-50", 0], "destination": ["obj-53", 0] } }, + { "patchline": { "source": ["obj-51", 0], "destination": ["obj-53", 1] } }, + { "patchline": { "source": ["obj-52", 0], "destination": ["obj-53", 0] } }, + { "patchline": { "source": ["obj-53", 0], "destination": ["obj-54", 0] } }, + { "patchline": { "source": ["obj-53", 1], "destination": ["obj-55", 0] } } + ], + "dependency_cache": [], + "autosave": 0 + } +} \ No newline at end of file diff --git a/patches/util.cache.ttl/util.cache.ttl.maxpat b/patches/util.cache.ttl/util.cache.ttl.maxpat new file mode 100644 index 0000000..715a9cd --- /dev/null +++ b/patches/util.cache.ttl/util.cache.ttl.maxpat @@ -0,0 +1,5118 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 100.0, + 100.0, + 1200.0, + 800.0 + ], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [ + 8.0, + 8.0 + ], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Key-value cache with time-to-live expiration", + "digest": "Store and retrieve values with automatic expiry", + "tags": "utility cache ttl expiry dictionary", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "commands: set, get, has, invalidate, clear" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "index": 2, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 560.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "control: ttl, bypass, sweep, reset" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 744.0, + 30.0, + 30.0 + ], + "comment": "hits: hit key value..." + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 320.0, + 744.0, + 30.0, + 30.0 + ], + "comment": "misses/status: miss key, invalidated key, cleared, ok" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "outlet", + "index": 3, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 680.0, + 744.0, + 30.0, + 30.0 + ], + "comment": "diagnostics: size N" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "comment", + "text": "--- INIT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 800.0, + 16.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 800.0, + 40.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "patcherargs 1000", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 800.0, + 64.0, + 96.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 800.0, + 96.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: init defaults (first), send ttl (second)" + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "prepend ttl", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 800.0, + 128.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 880.0, + 96.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: bypass=0 (first), clear dicts (second)" + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 912.0, + 128.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 912.0, + 152.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 880.0, + 128.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 880.0, + 152.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: clear expiry (first), clear values (second)" + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 880.0, + 176.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 912.0, + 176.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "comment", + "text": "--- CONTROL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 560.0, + 56.0, + 100.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "route ttl bypass sweep reset", + "numinlets": 1, + "numoutlets": 5, + "outlettype": [ + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 560.0, + 80.0, + 168.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "v #0_ttl", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 560.0, + 112.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 600.0, + 112.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "s #0_sweep", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 640.0, + 112.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "s #0_reset", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 680.0, + 112.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "comment", + "text": "--- COMMAND ROUTER ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 56.0, + 150.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "text": "route set get has invalidate clear", + "numinlets": 1, + "numoutlets": 6, + "outlettype": [ + "", + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 56.0, + 80.0, + 208.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "comment", + "text": "--- SET ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 112.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "p SetCmd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 136.0, + 56.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 520.0, + 400.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "key value... from route set" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t l l b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "", + "", + "bang" + ], + "patching_rect": [ + 50.0, + 75.0, + 200.0, + 22.0 + ], + "comment": "R-to-L: compute expiry (1st), store key/value (2nd), output ok (3rd)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "t b f", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "float" + ], + "patching_rect": [ + 235.0, + 105.0, + 56.0, + 22.0 + ], + "comment": "R-to-L: store cpuclock (1st), get ttl (2nd)" + } + }, + { + "box": { + "id": "obj-3b", + "maxclass": "newobj", + "text": "cpuclock", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "float" + ], + "patching_rect": [ + 235.0, + 135.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_ttl", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 276.0, + 135.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "+", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "float" + ], + "patching_rect": [ + 235.0, + 165.0, + 56.0, + 22.0 + ], + "comment": "expiry = cpuclock + ttl" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "v #0_expirytime", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 235.0, + 195.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "zl slice 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 130.0, + 105.0, + 64.0, + 22.0 + ], + "comment": "split key from value" + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "v #0_setkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 135.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "v #0_setval", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 178.0, + 135.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 130.0, + 165.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: set expiry (1st), set value (2nd)" + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 195.0, + 195.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: get expiry (1st), get key for pack (2nd)" + } + }, + { + "box": { + "id": "obj-10b", + "maxclass": "newobj", + "text": "v #0_setkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 195.0, + 225.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "v #0_expirytime", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 228.0, + 225.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "pack s f", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 195.0, + 255.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "prepend set", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 195.0, + 285.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 195.0, + 315.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 130.0, + 195.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: get value (1st), get key for pack (2nd)" + } + }, + { + "box": { + "id": "obj-15b", + "maxclass": "newobj", + "text": "v #0_setkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 225.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "v #0_setval", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 163.0, + 225.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "pack s s", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 255.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "prepend set", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 285.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 130.0, + 315.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "message", + "text": "ok", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 105.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-out", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 315.0, + 30.0, + 30.0 + ], + "comment": "ok status" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 2 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "compute expiry (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-3", + 1 + ], + "destination": [ + "obj-4", + 0 + ], + "comment": "get cpuclock (fires 1st from t)" + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-3b", + 0 + ], + "comment": "get ttl (fires 2nd from t)" + } + }, + { + "patchline": { + "source": [ + "obj-3b", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-5", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-7", + 0 + ], + "comment": "store key/value (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 1 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 1 + ], + "destination": [ + "obj-10", + 0 + ], + "comment": "set expiry (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-10", + 1 + ], + "destination": [ + "obj-11", + 0 + ], + "comment": "get key (fires 1st from inner t)" + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-10b", + 0 + ], + "comment": "get expiry (fires 2nd from inner t)" + } + }, + { + "patchline": { + "source": [ + "obj-10b", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-15", + 0 + ], + "comment": "set value (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-15", + 1 + ], + "destination": [ + "obj-16", + 0 + ], + "comment": "get key (fires 1st from inner t)" + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-15b", + 0 + ], + "comment": "get value (fires 2nd from inner t)" + } + }, + { + "patchline": { + "source": [ + "obj-15b", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-17", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-21", + 0 + ], + "comment": "output ok (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-21", + 0 + ], + "destination": [ + "obj-out", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "comment", + "text": "--- GET ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 120.0, + 112.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "p GetCmd", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 120.0, + 136.0, + 56.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 560.0, + 560.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "key from route get" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 75.0, + 120.0, + 22.0 + ], + "comment": "R-to-L: store key (1st), check existence (2nd)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "v #0_getkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 155.0, + 105.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "prepend contains", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 105.0, + 96.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 135.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "sel 1 0", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 100.0, + 22.0 + ], + "comment": "1=found, 0=not found" + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "v #0_getkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 195.0, + 64.0, + 22.0 + ], + "comment": "found: check expiry" + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "prepend get", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 225.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 255.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "t f b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "bang" + ], + "patching_rect": [ + 50.0, + 285.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: get cpuclock (1st), compare (2nd)" + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "cpuclock", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "float" + ], + "patching_rect": [ + 115.0, + 315.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": ">", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 50.0, + 345.0, + 30.0, + 22.0 + ], + "comment": "expiry > now? (still valid)" + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "sel 1 0", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "" + ], + "patching_rect": [ + 50.0, + 375.0, + 100.0, + 22.0 + ], + "comment": "1=valid (hit), 0=expired (miss)" + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "v #0_getkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 405.0, + 64.0, + 22.0 + ], + "comment": "valid: get value" + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "prepend get", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 435.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 465.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 495.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: store value (1st), get key (2nd)" + } + }, + { + "box": { + "id": "obj-17b", + "maxclass": "newobj", + "text": "v #0_getval", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 83.0, + 525.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17c", + "maxclass": "newobj", + "text": "v #0_getkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 525.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 555.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: get value (1st), build output (2nd)" + } + }, + { + "box": { + "id": "obj-18b", + "maxclass": "newobj", + "text": "v #0_getval", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 83.0, + 585.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18c", + "maxclass": "newobj", + "text": "pack s s", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 615.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18d", + "maxclass": "newobj", + "text": "prepend hit", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 645.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 135.0, + 405.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: invalidate (1st), output miss (2nd)" + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "v #0_getkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 168.0, + 435.0, + 64.0, + 22.0 + ], + "comment": "lazy delete expired entry" + } + }, + { + "box": { + "id": "obj-20b", + "maxclass": "newobj", + "text": "s #0_lazy_invalidate", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 168.0, + 465.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v #0_getkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 135.0, + 195.0, + 64.0, + 22.0 + ], + "comment": "not found: miss" + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "prepend miss", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 135.0, + 225.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "v #0_getkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 135.0, + 435.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "newobj", + "text": "prepend miss", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 135.0, + 465.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-out1", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 675.0, + 30.0, + 30.0 + ], + "comment": "hit key value" + } + }, + { + "box": { + "id": "obj-out2", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 135.0, + 675.0, + 30.0, + 30.0 + ], + "comment": "miss key" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "store key (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-4", + 0 + ], + "comment": "check contains (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-7", + 0 + ], + "comment": "found: check expiry" + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 1 + ], + "destination": [ + "obj-11", + 0 + ], + "comment": "get cpuclock (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-12", + 0 + ], + "comment": "compare expiry > now (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ], + "comment": "valid: hit" + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 1 + ], + "destination": [ + "obj-17b", + 0 + ], + "comment": "store value (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-17c", + 0 + ], + "comment": "get key (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-17c", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 1 + ], + "destination": [ + "obj-18b", + 0 + ], + "comment": "get value (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-18c", + 0 + ], + "comment": "build output (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-18b", + 0 + ], + "destination": [ + "obj-18c", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18c", + 0 + ], + "destination": [ + "obj-18d", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18d", + 0 + ], + "destination": [ + "obj-out1", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 1 + ], + "destination": [ + "obj-19", + 0 + ], + "comment": "expired: lazy delete + miss" + } + }, + { + "patchline": { + "source": [ + "obj-19", + 1 + ], + "destination": [ + "obj-20", + 0 + ], + "comment": "invalidate (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-20", + 0 + ], + "destination": [ + "obj-20b", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-23", + 0 + ], + "comment": "output miss (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-23", + 0 + ], + "destination": [ + "obj-24", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 0 + ], + "destination": [ + "obj-out2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 1 + ], + "destination": [ + "obj-21", + 0 + ], + "comment": "not found: miss" + } + }, + { + "patchline": { + "source": [ + "obj-21", + 0 + ], + "destination": [ + "obj-22", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-out2", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "comment", + "text": "--- HAS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 184.0, + 112.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "newobj", + "text": "p HasCmd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 184.0, + 136.0, + 56.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 500.0, + 500.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "key from route has" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 75.0, + 120.0, + 22.0 + ], + "comment": "R-to-L: store key (1st), check (2nd)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "v #0_haskey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 155.0, + 105.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "prepend contains", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 105.0, + 96.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 135.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "sel 1 0", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 100.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "v #0_haskey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 195.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "prepend get", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 225.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 255.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "t f b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "bang" + ], + "patching_rect": [ + 50.0, + 285.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: get cpuclock (1st), compare (2nd)" + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "cpuclock", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "float" + ], + "patching_rect": [ + 115.0, + 315.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": ">", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 50.0, + 345.0, + 30.0, + 22.0 + ], + "comment": "expiry > now?" + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "sel 1 0", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "" + ], + "patching_rect": [ + 50.0, + 375.0, + 100.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 405.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 135.0, + 195.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 135.0, + 405.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: lazy invalidate (1st), output 0 (2nd)" + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "v #0_haskey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 168.0, + 435.0, + 64.0, + 22.0 + ], + "comment": "expired: lazy invalidate" + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "s #0_lazy_invalidate", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 168.0, + 465.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 135.0, + 435.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-out", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 465.0, + 30.0, + 30.0 + ], + "comment": "1 or 0" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 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-6", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 1 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-out", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 1 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-out", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 1 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 1 + ], + "destination": [ + "obj-17", + 0 + ], + "comment": "lazy invalidate (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-19", + 0 + ], + "comment": "output 0 (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-out", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "comment", + "text": "--- INVALIDATE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 248.0, + 112.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "newobj", + "text": "p InvalidateCmd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 248.0, + 184.0, + 88.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 400.0, + 300.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "key to invalidate" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t s s s", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "", + "", + "" + ], + "patching_rect": [ + 50.0, + 75.0, + 150.0, + 22.0 + ], + "comment": "R-to-L: remove expiry (1st), remove values (2nd), output status (3rd)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "prepend remove", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 185.0, + 105.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 185.0, + 135.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "prepend remove", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 115.0, + 105.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 115.0, + 135.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "prepend invalidated", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 105.0, + 104.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-out", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 195.0, + 30.0, + 30.0 + ], + "comment": "invalidated key" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 2 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-out", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-60", + "maxclass": "newobj", + "text": "r #0_lazy_invalidate", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [296.0, 216.0, 110.0, 22.0], + "comment": "lazy expiry from get/has/sweep (no status output)" + } + }, + { + "box": { + "id": "obj-61", + "maxclass": "newobj", + "text": "p SilentInvalidate", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [296.0, 240.0, 104.0, 22.0], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [200.0, 200.0, 400.0, 250.0], + "gridsize": [15.0, 15.0], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [50.0, 40.0, 30.0, 30.0], + "comment": "key to silently invalidate" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [50.0, 90.0, 120.0, 22.0], + "comment": "R-to-L: remove from expiry (first), remove from values (second)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "prepend remove", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [155.0, 120.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [155.0, 150.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "prepend remove", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [50.0, 120.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [50.0, 150.0, 80.0, 22.0] + } + } + ], + "lines": [ + { + "patchline": { + "source": ["obj-1", 0], + "destination": ["obj-2", 0] + } + }, + { + "patchline": { + "source": ["obj-2", 1], + "destination": ["obj-3", 0], + "comment": "remove from expiry (fires first)" + } + }, + { + "patchline": { + "source": ["obj-3", 0], + "destination": ["obj-4", 0] + } + }, + { + "patchline": { + "source": ["obj-2", 0], + "destination": ["obj-5", 0], + "comment": "remove from values (fires second)" + } + }, + { + "patchline": { + "source": ["obj-5", 0], + "destination": ["obj-6", 0] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "comment", + "text": "--- CLEAR ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 344.0, + 112.0, + 90.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 344.0, + 136.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: clear dicts (1st), output status (2nd)" + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 408.0, + 160.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 408.0, + 184.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: clear expiry (1st), clear values (2nd)" + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 408.0, + 208.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 440.0, + 208.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "message", + "text": "cleared", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 344.0, + 160.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "comment", + "text": "--- SWEEP ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 560.0, + 160.0, + 90.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "newobj", + "text": "r #0_sweep", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 560.0, + 184.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "newobj", + "text": "p SweepCmd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 560.0, + 208.0, + 72.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 520.0, + 480.0 + ], + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 30.0, + 30.0, + 30.0 + ], + "comment": "bang to sweep" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 75.0, + 200.0, + 22.0 + ], + "comment": "R-to-L: get cpuclock (1st), iterate keys (2nd), getsize (3rd)" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "cpuclock", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "float" + ], + "patching_rect": [ + 235.0, + 105.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_sweepnow", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 235.0, + 135.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "getkeys", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 105.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 130.0, + 135.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "route keys", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 130.0, + 165.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "zl iter 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 130.0, + 195.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 130.0, + 225.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: store key (1st), get expiry (2nd)" + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "v #0_sweepkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 195.0, + 255.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "prepend get", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 255.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 130.0, + 285.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "v #0_sweepnow", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 315.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": ">", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 130.0, + 345.0, + 30.0, + 22.0 + ], + "comment": "expiry > now? still valid" + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "sel 0", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 130.0, + 375.0, + 36.0, + 22.0 + ], + "comment": "0 = expired, invalidate" + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "v #0_sweepkey", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 130.0, + 405.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "s #0_lazy_invalidate", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 130.0, + 435.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "message", + "text": "getsize", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 105.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 135.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "route size", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "prepend size", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 195.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-out", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 225.0, + 30.0, + 30.0 + ], + "comment": "size N" + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 2 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "get cpuclock (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-5", + 0 + ], + "comment": "iterate keys (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 1 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-18", + 0 + ], + "comment": "getsize (fires 3rd/last)" + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-20", + 0 + ], + "destination": [ + "obj-21", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-21", + 0 + ], + "destination": [ + "obj-out", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "comment", + "text": "--- RESET ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 760.0, + 208.0, + 90.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "newobj", + "text": "r #0_reset", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 760.0, + 232.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-47", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 760.0, + 256.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: restore default ttl (1st), clear (2nd)" + } + }, + { + "box": { + "id": "obj-48", + "maxclass": "newobj", + "text": "patcherargs 1000", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 824.0, + 280.0, + 96.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-49", + "maxclass": "newobj", + "text": "v #0_ttl", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 824.0, + 304.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 760.0, + 280.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 760.0, + 304.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: clear expiry (1st), clear values (2nd)" + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "newobj", + "text": "dict #0_values", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 760.0, + 328.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "newobj", + "text": "dict #0_expiry", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 792.0, + 328.0, + 80.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-25", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-25", + 0 + ], + "destination": [ + "obj-27", + 0 + ], + "comment": "set" + } + }, + { + "patchline": { + "source": [ + "obj-25", + 1 + ], + "destination": [ + "obj-29", + 0 + ], + "comment": "get" + } + }, + { + "patchline": { + "source": [ + "obj-25", + 2 + ], + "destination": [ + "obj-31", + 0 + ], + "comment": "has" + } + }, + { + "patchline": { + "source": [ + "obj-25", + 3 + ], + "destination": [ + "obj-34", + 0 + ], + "comment": "invalidate" + } + }, + { + "patchline": { + "source": [ + "obj-25", + 4 + ], + "destination": [ + "obj-36", + 0 + ], + "comment": "clear" + } + }, + { + "patchline": { + "source": [ + "obj-27", + 0 + ], + "destination": [ + "obj-4", + 0 + ], + "comment": "set -> ok status" + } + }, + { + "patchline": { + "source": [ + "obj-29", + 0 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "get -> hit" + } + }, + { + "patchline": { + "source": [ + "obj-29", + 1 + ], + "destination": [ + "obj-4", + 0 + ], + "comment": "get -> miss" + } + }, + { + "patchline": { + "source": [ + "obj-31", + 0 + ], + "destination": [ + "obj-4", + 0 + ], + "comment": "has -> 1/0" + } + }, + { + "patchline": { + "source": [ + "obj-60", + 0 + ], + "destination": [ + "obj-61", + 0 + ], + "comment": "lazy expiry invalidation (silent, no status output)" + } + }, + { + "patchline": { + "source": [ + "obj-34", + 0 + ], + "destination": [ + "obj-4", + 0 + ], + "comment": "invalidated key" + } + }, + { + "patchline": { + "source": [ + "obj-36", + 1 + ], + "destination": [ + "obj-37", + 0 + ], + "comment": "clear dicts (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-36", + 0 + ], + "destination": [ + "obj-41", + 0 + ], + "comment": "output cleared status (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-37", + 0 + ], + "destination": [ + "obj-38", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-38", + 0 + ], + "destination": [ + "obj-39", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-38", + 1 + ], + "destination": [ + "obj-40", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-41", + 0 + ], + "destination": [ + "obj-4", + 0 + ], + "comment": "cleared status" + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-20", + 0 + ], + "comment": "ttl" + } + }, + { + "patchline": { + "source": [ + "obj-19", + 1 + ], + "destination": [ + "obj-21", + 0 + ], + "comment": "bypass" + } + }, + { + "patchline": { + "source": [ + "obj-19", + 2 + ], + "destination": [ + "obj-22", + 0 + ], + "comment": "sweep" + } + }, + { + "patchline": { + "source": [ + "obj-19", + 3 + ], + "destination": [ + "obj-23", + 0 + ], + "comment": "reset" + } + }, + { + "patchline": { + "source": [ + "obj-43", + 0 + ], + "destination": [ + "obj-44", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-44", + 0 + ], + "destination": [ + "obj-5", + 0 + ], + "comment": "sweep -> size diagnostics" + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ], + "comment": "prepend ttl to patcherargs -> control route" + } + }, + { + "patchline": { + "source": [ + "obj-9", + 1 + ], + "destination": [ + "obj-11", + 0 + ], + "comment": "init defaults" + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-19", + 0 + ], + "comment": "send ttl from init" + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 1 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 1 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-46", + 0 + ], + "destination": [ + "obj-47", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-47", + 1 + ], + "destination": [ + "obj-48", + 0 + ], + "comment": "restore default ttl (fires 1st)" + } + }, + { + "patchline": { + "source": [ + "obj-47", + 0 + ], + "destination": [ + "obj-50", + 0 + ], + "comment": "clear (fires 2nd)" + } + }, + { + "patchline": { + "source": [ + "obj-48", + 0 + ], + "destination": [ + "obj-49", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-50", + 0 + ], + "destination": [ + "obj-51", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-51", + 0 + ], + "destination": [ + "obj-52", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-51", + 1 + ], + "destination": [ + "obj-53", + 0 + ] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} \ No newline at end of file diff --git a/patches/util.debounce/README.md b/patches/util.debounce/README.md new file mode 100644 index 0000000..57a441e --- /dev/null +++ b/patches/util.debounce/README.md @@ -0,0 +1,171 @@ +# util.debounce + +Delay output until input has been quiet for a specified number of milliseconds. When messages arrive in rapid succession, only the final message is output after the burst ends. Optionally emit the first message immediately (leading edge) in addition to or instead of the last (trailing edge). + +## Inlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | any (int, float, list, symbol) | Message to debounce. Each incoming message resets the internal timer and buffers the message. | +| 2 | symbol (message) | Control messages: `time`, `leading`, `trailing`, `flush`, `cancel`, `bypass`, `reset`. | + +## Outlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | any | Debounced output. Outputs the buffered message when the timer expires (trailing edge) or immediately on first input (leading edge), depending on mode. | +| 2 | symbol | Status messages: `scheduled` (timer started), `flushed` (message emitted by timer or flush), `canceled` (pending message dropped). | + +## Arguments + +| Argument | Type | Default | Description | +|----------|------|---------|-------------| +| 1 | int/float | 50 | Initial debounce time in milliseconds. | + +``` +util.debounce --> 50ms debounce (default) +util.debounce 200 --> 200ms debounce +util.debounce 1000 --> 1 second debounce +``` + +## Messages (Right Inlet) + +### time *ms* + +Set the debounce delay in milliseconds. Takes effect on the next incoming message. + +``` +time 100 --> set debounce window to 100ms +time 500 --> set debounce window to 500ms +``` + +### leading *0|1* + +Enable or disable leading-edge output. When enabled, the first message in a burst passes through immediately. Subsequent messages within the debounce window are suppressed (unless trailing is also enabled, in which case the last message is emitted after the window). + +``` +leading 1 --> emit first message immediately +leading 0 --> suppress leading edge (default) +``` + +### trailing *0|1* + +Enable or disable trailing-edge output. When enabled, the most recent message is emitted after the debounce window expires with no new input. + +``` +trailing 1 --> emit last message after quiet period (default) +trailing 0 --> suppress trailing edge +``` + +### flush + +Immediately emit the pending buffered message and stop the timer. If nothing is pending, flush has no effect. + +### cancel + +Drop the pending buffered message and stop the timer without emitting anything. Outputs `canceled` from outlet 2. + +### bypass *0|1* + +Enable or disable bypass mode. When bypassed, all input messages pass directly to outlet 1 without debouncing. Enabling bypass (`bypass 1`) also cancels any pending debounce timer, preventing stale buffered messages from leaking through after bypass is enabled. + +``` +bypass 1 --> cancel pending timer, pass all messages through immediately +bypass 0 --> resume debouncing (default) +``` + +### reset + +Stop the timer, clear pending state, and restore default parameter values (`leading 0`, `trailing 1`). The debounce time is not changed by reset. + +## Defaults + +| Parameter | Default | +|-----------|---------| +| time | 50 ms (or creation argument) | +| leading | 0 (off) | +| trailing | 1 (on) | +| bypass | 0 (off) | + +## Mode Combinations + +| leading | trailing | Behavior | +|---------|----------|----------| +| 0 | 1 | **Trailing only** (default). Waits for quiet, then emits last message. | +| 1 | 0 | **Leading only**. Emits first message immediately, suppresses the rest. | +| 1 | 1 | **Both edges**. Emits first message immediately, then also emits last message after quiet. | +| 0 | 0 | **Silent**. No output (timer runs but nothing is emitted). | + +## Example Usage + +### Debounce rapid UI changes + +A number box or slider sending values as the user drags. Only the final resting value is output. + +``` +[number] +| +[util.debounce 200] +| +[print settled-value] +``` + +### Leading-edge mode for instant response + +React to the first event in a burst (e.g., a button press), then ignore follow-up triggers for 500ms. + +``` +[button] +| +[util.debounce 500] +| [message: leading 1] +[do-something] | + [message: trailing 0] + | + [util.debounce] (right inlet) +``` + +### Debounce MIDI CC with flush on note-on + +Smooth out noisy CC input, but immediately flush the latest value when a note arrives. + +``` +[ctlin] +| +[util.debounce 30] +| [notein] +[your-synth-param] | + [message: flush] + | + [util.debounce] (right inlet) +``` + +### Dynamic time adjustment + +Change the debounce window on the fly based on context. + +``` +[number] [number] +| | +| [prepend time] +| | +[util.debounce 50 ----] +| +[print result] +``` + +## Implementation Notes + +- Uses `#0`-scoped internal state (`v`, `s`, `r` objects) for instance isolation. Multiple instances in the same patch do not interfere. +- All message types (int, float, list, symbol) are preserved through the debounce buffer (`zl reg`). +- Deterministic message ordering is enforced via `trigger` objects throughout. +- The timer uses Max's `delay` object. Sending `stop` to the delay cancels a pending bang. + +## See Also + +- **delay** -- schedule a bang after N ms +- **pipe** -- delay any message by N ms (does not suppress duplicates) +- **speedlim** -- rate-limit output to at most one message per N ms (throttle, not debounce) +- **change** -- suppress repeated identical values (not time-based) +- **onebang** -- pass one bang then block until reset +- **defer** / **deferlow** -- reschedule messages to low-priority thread diff --git a/patches/util.debounce/util.debounce-help.maxpat b/patches/util.debounce/util.debounce-help.maxpat new file mode 100644 index 0000000..7f484d9 --- /dev/null +++ b/patches/util.debounce/util.debounce-help.maxpat @@ -0,0 +1,820 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 760.0, 680.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontface": 0, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [8.0, 8.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Help patch for util.debounce", + "digest": "Demonstrates debounce abstraction usage", + "tags": "help utility debounce", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "util.debounce", + "fontface": 1, + "fontsize": 16.0, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 16.0, 160.0, 24.0] + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Delay output until input has been quiet for N ms. Supports leading and trailing edge modes.", + "linecount": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 42.0, 480.0, 34.0] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "comment", + "text": "--- BASIC TRAILING (DEFAULT) ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 88.0, 220.0, 20.0] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "comment", + "text": "Click rapidly. Only the last value is output after 200ms of quiet.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 108.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "util.debounce 200", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 168.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 200.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "print status-trailing", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [104.0, 200.0, 120.0, 22.0] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "comment", + "text": "--- LEADING EDGE ONLY ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 240.0, 180.0, 20.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "comment", + "text": "First message passes immediately, then silent for 300ms.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 260.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 288.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "util.debounce 300", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 344.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 376.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "message", + "text": "leading 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [80.0, 288.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "message", + "text": "trailing 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [144.0, 288.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [80.0, 224.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["bang", "bang"], + "patching_rect": [80.0, 248.0, 280.0, 22.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["bang", "bang"], + "patching_rect": [80.0, 312.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "comment", + "text": "--- BOTH EDGES ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 88.0, 130.0, 20.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "comment", + "text": "First and last messages pass through.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 108.0, 300.0, 20.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [280.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "util.debounce 150", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [280.0, 192.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [280.0, 224.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "message", + "text": "leading 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [344.0, 136.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "comment", + "text": "--- FLUSH / CANCEL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 268.0, 160.0, 20.0] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "comment", + "text": "Send messages, then flush or cancel before timeout.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 288.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [280.0, 312.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "newobj", + "text": "util.debounce 2000", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [280.0, 376.0, 112.0, 22.0] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [280.0, 408.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "message", + "text": "flush", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [344.0, 312.0, 36.0, 22.0] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "message", + "text": "cancel", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [384.0, 312.0, 42.0, 22.0] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "newobj", + "text": "print status-flushcancel", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [376.0, 408.0, 136.0, 22.0] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "comment", + "text": "--- BYPASS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 424.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "comment", + "text": "Toggle bypass for immediate passthrough.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 444.0, 300.0, 20.0] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 472.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "newobj", + "text": "util.debounce 500", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 536.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 568.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "toggle", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["int"], + "patching_rect": [80.0, 472.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "newobj", + "text": "prepend bypass", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [80.0, 504.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "comment", + "text": "--- LIST / SYMBOL INPUT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 448.0, 180.0, 20.0] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "comment", + "text": "Any message type is preserved through debounce.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 468.0, 350.0, 20.0] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "message", + "text": "hello world", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [280.0, 496.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "message", + "text": "1 2 3", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [356.0, 496.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "newobj", + "text": "util.debounce 100", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [280.0, 536.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "newobj", + "text": "print debounced-msg", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 568.0, 116.0, 22.0] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "comment", + "text": "--- CONTROL MESSAGES (right inlet) ---\n\ntime - set debounce delay\nleading <0|1> - emit on leading edge\ntrailing <0|1> - emit on trailing edge\nflush - emit pending immediately\ncancel - drop pending message\nbypass <0|1> - passthrough mode\nreset - clear state, restore defaults", + "linecount": 9, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [480.0, 424.0, 250.0, 140.0] + } + }, + { + "box": { + "id": "obj-47", + "maxclass": "comment", + "text": "Argument sets default debounce time in ms (default 50).", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 608.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-48", + "maxclass": "comment", + "text": "--- DYNAMIC TIME ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [520.0, 88.0, 140.0, 20.0] + } + }, + { + "box": { + "id": "obj-49", + "maxclass": "comment", + "text": "Change time on the fly.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [520.0, 108.0, 200.0, 20.0] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [520.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "newobj", + "text": "util.debounce", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [520.0, 200.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [520.0, 232.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [608.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "newobj", + "text": "prepend time", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [608.0, 168.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [680.0, 168.0, 40.0, 22.0] + } + } + ], + "lines": [ + { + "patchline": { + "source": ["obj-5", 0], + "destination": ["obj-6", 0] + } + }, + { + "patchline": { + "source": ["obj-6", 0], + "destination": ["obj-7", 0] + } + }, + { + "patchline": { + "source": ["obj-6", 1], + "destination": ["obj-8", 0] + } + }, + { + "patchline": { + "source": ["obj-11", 0], + "destination": ["obj-12", 0] + } + }, + { + "patchline": { + "source": ["obj-12", 0], + "destination": ["obj-13", 0] + } + }, + { + "patchline": { + "source": ["obj-16", 0], + "destination": ["obj-56", 0] + } + }, + { + "patchline": { + "source": ["obj-56", 0], + "destination": ["obj-17", 0] + } + }, + { + "patchline": { + "source": ["obj-56", 1], + "destination": ["obj-23", 0] + } + }, + { + "patchline": { + "source": ["obj-17", 0], + "destination": ["obj-14", 0] + } + }, + { + "patchline": { + "source": ["obj-17", 1], + "destination": ["obj-15", 0] + } + }, + { + "patchline": { + "source": ["obj-14", 0], + "destination": ["obj-12", 1] + } + }, + { + "patchline": { + "source": ["obj-15", 0], + "destination": ["obj-12", 1] + } + }, + { + "patchline": { + "source": ["obj-20", 0], + "destination": ["obj-21", 0] + } + }, + { + "patchline": { + "source": ["obj-21", 0], + "destination": ["obj-22", 0] + } + }, + { + "patchline": { + "source": ["obj-23", 0], + "destination": ["obj-21", 1] + } + }, + { + "patchline": { + "source": ["obj-27", 0], + "destination": ["obj-28", 0] + } + }, + { + "patchline": { + "source": ["obj-28", 0], + "destination": ["obj-29", 0] + } + }, + { + "patchline": { + "source": ["obj-28", 1], + "destination": ["obj-32", 0] + } + }, + { + "patchline": { + "source": ["obj-30", 0], + "destination": ["obj-28", 1] + } + }, + { + "patchline": { + "source": ["obj-31", 0], + "destination": ["obj-28", 1] + } + }, + { + "patchline": { + "source": ["obj-35", 0], + "destination": ["obj-36", 0] + } + }, + { + "patchline": { + "source": ["obj-36", 0], + "destination": ["obj-37", 0] + } + }, + { + "patchline": { + "source": ["obj-38", 0], + "destination": ["obj-39", 0] + } + }, + { + "patchline": { + "source": ["obj-39", 0], + "destination": ["obj-36", 1] + } + }, + { + "patchline": { + "source": ["obj-42", 0], + "destination": ["obj-44", 0] + } + }, + { + "patchline": { + "source": ["obj-43", 0], + "destination": ["obj-44", 0] + } + }, + { + "patchline": { + "source": ["obj-44", 0], + "destination": ["obj-45", 0] + } + }, + { + "patchline": { + "source": ["obj-50", 0], + "destination": ["obj-51", 0] + } + }, + { + "patchline": { + "source": ["obj-51", 0], + "destination": ["obj-52", 0] + } + }, + { + "patchline": { + "source": ["obj-53", 0], + "destination": ["obj-54", 0] + } + }, + { + "patchline": { + "source": ["obj-54", 0], + "destination": ["obj-51", 1] + } + }, + { + "patchline": { + "source": ["obj-55", 0], + "destination": ["obj-51", 1] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} diff --git a/patches/util.debounce/util.debounce.maxpat b/patches/util.debounce/util.debounce.maxpat new file mode 100644 index 0000000..7c7d130 --- /dev/null +++ b/patches/util.debounce/util.debounce.maxpat @@ -0,0 +1,1657 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 880.0, 760.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [8.0, 8.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Debounce any message: delay output until input quiet for N ms", + "digest": "Message debouncer with leading/trailing edge options", + "tags": "utility debounce timer delay", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [56.0, 16.0, 30.0, 30.0], + "comment": "message to debounce (any type)" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "index": 2, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 16.0, 30.0, 30.0], + "comment": "control: time, leading, trailing, flush, cancel, bypass, reset" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 712.0, 30.0, 30.0], + "comment": "debounced output" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [648.0, 712.0, 30.0, 30.0], + "comment": "status (scheduled, flushed, canceled)" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "comment", + "text": "--- INIT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [640.0, 16.0, 80.0, 20.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [640.0, 40.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "patcherargs 50", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [640.0, 64.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [640.0, 96.0, 42.0, 22.0] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "prepend time", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [640.0, 128.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "t b b b b", + "numinlets": 1, + "numoutlets": 4, + "outlettype": ["bang", "bang", "bang", "bang"], + "patching_rect": [720.0, 96.0, 80.0, 22.0], + "comment": "init defaults" + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [720.0, 128.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [720.0, 152.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [752.0, 128.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "v #0_leading", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [752.0, 152.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [784.0, 128.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "v #0_trailing", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [784.0, 152.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [816.0, 128.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [816.0, 152.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "comment", + "text": "--- CONTROL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 56.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "route time leading trailing flush cancel bypass reset", + "numinlets": 1, + "numoutlets": 8, + "outlettype": ["", "", "", "", "", "", "", ""], + "patching_rect": [400.0, 80.0, 224.0, 22.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v #0_time", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 112.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "v #0_leading", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [432.0, 136.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "v #0_trailing", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [464.0, 160.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "newobj", + "text": "s #0_flush", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [496.0, 112.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "text": "s #0_cancel", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [520.0, 136.0, 68.0, 22.0] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [576.0, 144.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-87", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["int", "int"], + "patching_rect": [544.0, 112.0, 48.0, 22.0], + "comment": "set bypass state, then cancel if enabling" + } + }, + { + "box": { + "id": "obj-88", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["bang", ""], + "patching_rect": [544.0, 144.0, 36.0, 22.0], + "comment": "bypass=1 triggers cancel" + } + }, + { + "box": { + "id": "obj-89", + "maxclass": "newobj", + "text": "s #0_cancel", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [544.0, 168.0, 68.0, 22.0] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "s #0_reset", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [568.0, 136.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "comment", + "text": "--- BYPASS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 56.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [56.0, 80.0, 42.0, 22.0], + "comment": "check bypass, pass msg" + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [56.0, 112.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": ["int"], + "patching_rect": [56.0, 136.0, 30.0, 22.0], + "comment": "bypass=0 -> gate 1 (debounce), bypass=1 -> gate 2 (passthrough)" + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "newobj", + "text": "gate 2 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [56.0, 168.0, 152.0, 22.0], + "comment": "outlet 1=debounce, outlet 2=bypass" + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "comment", + "text": "--- DEBOUNCE CORE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 208.0, 140.0, 20.0] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "newobj", + "text": "t b l l", + "numinlets": 1, + "numoutlets": 3, + "outlettype": ["bang", "", ""], + "patching_rect": [56.0, 232.0, 200.0, 22.0], + "comment": "on input: R-to-L: 2)store in buffer, 1)check leading, 0)reset timer (last)" + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "newobj", + "text": "zl reg", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [56.0, 448.0, 45.0, 22.0], + "comment": "message buffer: store on right inlet, recall on bang" + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "comment", + "text": "--- LEADING EDGE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [144.0, 264.0, 130.0, 20.0] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [144.0, 288.0, 42.0, 22.0], + "comment": "check pending state, hold msg" + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [144.0, 312.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "newobj", + "text": "sel 0", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["bang", ""], + "patching_rect": [144.0, 336.0, 36.0, 22.0], + "comment": "only emit leading if not already pending (first msg in burst)" + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "newobj", + "text": "v #0_leading", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [144.0, 360.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "newobj", + "text": "gate 1 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [144.0, 392.0, 52.0, 22.0], + "comment": "leading gate: open only if leading=1" + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "comment", + "text": "--- TIMER ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [248.0, 264.0, 90.0, 20.0] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": ["bang", "bang", "bang"], + "patching_rect": [248.0, 288.0, 62.0, 22.0], + "comment": "reset timer: 1) set pending, 2) stop delay, 3) restart delay" + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [248.0, 320.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [248.0, 344.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [280.0, 320.0, 30.0, 22.0] + } + }, + { + "box": { + "id": "obj-47", + "maxclass": "newobj", + "text": "delay 50", + "numinlets": 2, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [320.0, 384.0, 52.0, 22.0], + "comment": "debounce timer" + } + }, + { + "box": { + "id": "obj-48", + "maxclass": "newobj", + "text": "v #0_time", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [352.0, 320.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-49", + "maxclass": "newobj", + "text": "t b f", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["bang", "float"], + "patching_rect": [352.0, 352.0, 42.0, 22.0], + "comment": "set delay time then start" + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "comment", + "text": "--- TIMER EXPIRED ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 432.0, 140.0, 20.0] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": ["bang", "bang", "bang"], + "patching_rect": [56.0, 480.0, 300.0, 22.0], + "comment": "timer expired: 1) recall msg, 2) get trailing flag, 3) clear pending" + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "newobj", + "text": "v #0_trailing", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [200.0, 512.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "newobj", + "text": "gate 1 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [56.0, 544.0, 152.0, 22.0], + "comment": "trailing gate: open only if trailing=1" + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [344.0, 512.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [344.0, 544.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "comment", + "text": "--- FLUSH / CANCEL (via send/receive) ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [448.0, 400.0, 240.0, 20.0] + } + }, + { + "box": { + "id": "obj-57", + "maxclass": "newobj", + "text": "r #0_flush", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [448.0, 424.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-58", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": ["bang", "bang", "bang"], + "patching_rect": [448.0, 448.0, 62.0, 22.0], + "comment": "flush: 1) check pending, 2) stop timer, 3) clear pending" + } + }, + { + "box": { + "id": "obj-59", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [448.0, 480.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-60", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["bang", ""], + "patching_rect": [448.0, 512.0, 36.0, 22.0], + "comment": "only flush if pending" + } + }, + { + "box": { + "id": "obj-61", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": ["bang", "bang", "bang"], + "patching_rect": [448.0, 544.0, 62.0, 22.0], + "comment": "do flush: 1) emit buffered, 2) clear pending, 3) status" + } + }, + { + "box": { + "id": "obj-62", + "maxclass": "newobj", + "text": "zl reg", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [448.0, 576.0, 45.0, 22.0], + "comment": "recall buffered msg for flush" + } + }, + { + "box": { + "id": "obj-63", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [480.0, 576.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-64", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [480.0, 608.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-65", + "maxclass": "message", + "text": "flushed", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [512.0, 576.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-66", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [480.0, 480.0, 30.0, 22.0] + } + }, + { + "box": { + "id": "obj-67", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [504.0, 480.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-68", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [504.0, 512.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-69", + "maxclass": "newobj", + "text": "r #0_cancel", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [600.0, 424.0, 68.0, 22.0] + } + }, + { + "box": { + "id": "obj-70", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": ["bang", "bang", "bang"], + "patching_rect": [600.0, 448.0, 62.0, 22.0], + "comment": "cancel: 1) status, 2) stop timer, 3) clear pending" + } + }, + { + "box": { + "id": "obj-71", + "maxclass": "message", + "text": "canceled", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [600.0, 480.0, 52.0, 22.0] + } + }, + { + "box": { + "id": "obj-72", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [632.0, 480.0, 30.0, 22.0] + } + }, + { + "box": { + "id": "obj-73", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [656.0, 480.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-74", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [656.0, 512.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-75", + "maxclass": "newobj", + "text": "r #0_reset", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [744.0, 424.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-76", + "maxclass": "newobj", + "text": "t b b b b", + "numinlets": 1, + "numoutlets": 4, + "outlettype": ["bang", "bang", "bang", "bang"], + "patching_rect": [744.0, 448.0, 80.0, 22.0], + "comment": "reset: stop timer, pending=0, leading=0, trailing=1" + } + }, + { + "box": { + "id": "obj-77", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [744.0, 480.0, 30.0, 22.0] + } + }, + { + "box": { + "id": "obj-78", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [776.0, 480.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-79", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [776.0, 512.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-80", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [808.0, 480.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-81", + "maxclass": "newobj", + "text": "v #0_leading", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [808.0, 512.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-82", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [840.0, 480.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-83", + "maxclass": "newobj", + "text": "v #0_trailing", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [840.0, 512.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-84", + "maxclass": "comment", + "text": "--- STATUS OUT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [648.0, 616.0, 120.0, 20.0] + } + }, + { + "box": { + "id": "obj-85", + "maxclass": "message", + "text": "scheduled", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [248.0, 376.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-86", + "maxclass": "message", + "text": "flushed", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [56.0, 600.0, 48.0, 22.0] + } + } + ], + "lines": [ + { + "patchline": { + "source": ["obj-1", 0], + "destination": ["obj-29", 0] + } + }, + { + "patchline": { + "source": ["obj-29", 1], + "destination": ["obj-30", 0], + "comment": "bang: get bypass state (fires first)" + } + }, + { + "patchline": { + "source": ["obj-29", 0], + "destination": ["obj-32", 1], + "comment": "list: msg to bypass gate (fires second)" + } + }, + { + "patchline": { + "source": ["obj-30", 0], + "destination": ["obj-31", 0] + } + }, + { + "patchline": { + "source": ["obj-31", 0], + "destination": ["obj-32", 0] + } + }, + { + "patchline": { + "source": ["obj-32", 1], + "destination": ["obj-3", 0], + "comment": "bypass passthrough" + } + }, + { + "patchline": { + "source": ["obj-32", 0], + "destination": ["obj-34", 0], + "comment": "to debounce core" + } + }, + { + "patchline": { + "source": ["obj-34", 2], + "destination": ["obj-35", 1], + "order": 1, + "comment": "store msg in zl reg (fires first)" + } + }, + { + "patchline": { + "source": ["obj-34", 2], + "destination": ["obj-62", 1], + "order": 0, + "comment": "also store in flush zl reg (fires first)" + } + }, + { + "patchline": { + "source": ["obj-34", 1], + "destination": ["obj-37", 0], + "comment": "list: check leading edge (fires second)" + } + }, + { + "patchline": { + "source": ["obj-34", 0], + "destination": ["obj-43", 0], + "comment": "bang: reset timer and set pending=1 (fires last)" + } + }, + { + "patchline": { + "source": ["obj-37", 1], + "destination": ["obj-38", 0], + "comment": "bang: get pending state (fires first)" + } + }, + { + "patchline": { + "source": ["obj-37", 0], + "destination": ["obj-41", 1], + "comment": "list: msg to leading gate (fires second)" + } + }, + { + "patchline": { + "source": ["obj-38", 0], + "destination": ["obj-39", 0] + } + }, + { + "patchline": { + "source": ["obj-39", 0], + "destination": ["obj-40", 0], + "comment": "not pending: get leading flag" + } + }, + { + "patchline": { + "source": ["obj-40", 0], + "destination": ["obj-41", 0], + "comment": "set leading gate" + } + }, + { + "patchline": { + "source": ["obj-41", 0], + "destination": ["obj-3", 0], + "comment": "leading output" + } + }, + { + "patchline": { + "source": ["obj-43", 0], + "destination": ["obj-44", 0], + "order": 1, + "comment": "set pending=1" + } + }, + { + "patchline": { + "source": ["obj-43", 0], + "destination": ["obj-85", 0], + "order": 0, + "comment": "status: scheduled" + } + }, + { + "patchline": { + "source": ["obj-43", 1], + "destination": ["obj-48", 0], + "comment": "get delay time and restart" + } + }, + { + "patchline": { + "source": ["obj-43", 2], + "destination": ["obj-46", 0], + "comment": "stop old timer first" + } + }, + { + "patchline": { + "source": ["obj-44", 0], + "destination": ["obj-45", 0] + } + }, + { + "patchline": { + "source": ["obj-46", 0], + "destination": ["obj-47", 0], + "comment": "stop delay" + } + }, + { + "patchline": { + "source": ["obj-48", 0], + "destination": ["obj-49", 0] + } + }, + { + "patchline": { + "source": ["obj-49", 0], + "destination": ["obj-47", 0], + "comment": "start delay" + } + }, + { + "patchline": { + "source": ["obj-49", 1], + "destination": ["obj-47", 1], + "comment": "set delay time" + } + }, + { + "patchline": { + "source": ["obj-47", 0], + "destination": ["obj-51", 0], + "comment": "timer expired" + } + }, + { + "patchline": { + "source": ["obj-51", 0], + "destination": ["obj-35", 0], + "comment": "recall buffered msg" + } + }, + { + "patchline": { + "source": ["obj-51", 1], + "destination": ["obj-52", 0], + "comment": "get trailing flag" + } + }, + { + "patchline": { + "source": ["obj-51", 2], + "destination": ["obj-54", 0], + "comment": "clear pending" + } + }, + { + "patchline": { + "source": ["obj-52", 0], + "destination": ["obj-53", 0], + "comment": "set trailing gate" + } + }, + { + "patchline": { + "source": ["obj-35", 0], + "destination": ["obj-53", 1], + "comment": "msg to trailing gate" + } + }, + { + "patchline": { + "source": ["obj-53", 0], + "destination": ["obj-86", 0], + "order": 0, + "comment": "status: flushed" + } + }, + { + "patchline": { + "source": ["obj-53", 0], + "destination": ["obj-3", 0], + "order": 1, + "comment": "trailing output" + } + }, + { + "patchline": { + "source": ["obj-54", 0], + "destination": ["obj-55", 0] + } + }, + { + "patchline": { + "source": ["obj-85", 0], + "destination": ["obj-4", 0] + } + }, + { + "patchline": { + "source": ["obj-86", 0], + "destination": ["obj-4", 0] + } + }, + { + "patchline": { + "source": ["obj-2", 0], + "destination": ["obj-20", 0] + } + }, + { + "patchline": { + "source": ["obj-20", 0], + "destination": ["obj-21", 0], + "comment": "time" + } + }, + { + "patchline": { + "source": ["obj-20", 1], + "destination": ["obj-22", 0], + "comment": "leading" + } + }, + { + "patchline": { + "source": ["obj-20", 2], + "destination": ["obj-23", 0], + "comment": "trailing" + } + }, + { + "patchline": { + "source": ["obj-20", 3], + "destination": ["obj-24", 0], + "comment": "flush" + } + }, + { + "patchline": { + "source": ["obj-20", 4], + "destination": ["obj-25", 0], + "comment": "cancel" + } + }, + { + "patchline": { + "source": ["obj-20", 5], + "destination": ["obj-87", 0], + "comment": "bypass value to trigger" + } + }, + { + "patchline": { + "source": ["obj-87", 1], + "destination": ["obj-26", 0], + "comment": "set bypass state (fires first)" + } + }, + { + "patchline": { + "source": ["obj-87", 0], + "destination": ["obj-88", 0], + "comment": "check if bypass=1 (fires second)" + } + }, + { + "patchline": { + "source": ["obj-88", 0], + "destination": ["obj-89", 0], + "comment": "bypass=1: cancel pending timer" + } + }, + { + "patchline": { + "source": ["obj-20", 6], + "destination": ["obj-27", 0], + "comment": "reset" + } + }, + { + "patchline": { + "source": ["obj-57", 0], + "destination": ["obj-58", 0] + } + }, + { + "patchline": { + "source": ["obj-58", 0], + "destination": ["obj-67", 0], + "comment": "clear pending (fires last)" + } + }, + { + "patchline": { + "source": ["obj-58", 1], + "destination": ["obj-66", 0], + "comment": "stop timer" + } + }, + { + "patchline": { + "source": ["obj-58", 2], + "destination": ["obj-59", 0], + "comment": "check pending (fires first)" + } + }, + { + "patchline": { + "source": ["obj-59", 0], + "destination": ["obj-60", 0] + } + }, + { + "patchline": { + "source": ["obj-60", 0], + "destination": ["obj-61", 0], + "comment": "pending=1, proceed with flush" + } + }, + { + "patchline": { + "source": ["obj-61", 0], + "destination": ["obj-65", 0], + "comment": "status: flushed (fires last)" + } + }, + { + "patchline": { + "source": ["obj-61", 1], + "destination": ["obj-63", 0], + "comment": "clear pending (fires middle)" + } + }, + { + "patchline": { + "source": ["obj-61", 2], + "destination": ["obj-62", 0], + "comment": "bang to recall msg (fires first)" + } + }, + { + "patchline": { + "source": ["obj-62", 0], + "destination": ["obj-3", 0], + "comment": "flush output" + } + }, + { + "patchline": { + "source": ["obj-63", 0], + "destination": ["obj-64", 0] + } + }, + { + "patchline": { + "source": ["obj-65", 0], + "destination": ["obj-4", 0] + } + }, + { + "patchline": { + "source": ["obj-66", 0], + "destination": ["obj-47", 0], + "comment": "stop delay on flush" + } + }, + { + "patchline": { + "source": ["obj-67", 0], + "destination": ["obj-68", 0] + } + }, + { + "patchline": { + "source": ["obj-69", 0], + "destination": ["obj-70", 0] + } + }, + { + "patchline": { + "source": ["obj-70", 0], + "destination": ["obj-71", 0], + "comment": "status: canceled" + } + }, + { + "patchline": { + "source": ["obj-70", 1], + "destination": ["obj-72", 0], + "comment": "stop timer" + } + }, + { + "patchline": { + "source": ["obj-70", 2], + "destination": ["obj-73", 0], + "comment": "clear pending" + } + }, + { + "patchline": { + "source": ["obj-71", 0], + "destination": ["obj-4", 0] + } + }, + { + "patchline": { + "source": ["obj-72", 0], + "destination": ["obj-47", 0], + "comment": "stop delay on cancel" + } + }, + { + "patchline": { + "source": ["obj-73", 0], + "destination": ["obj-74", 0] + } + }, + { + "patchline": { + "source": ["obj-75", 0], + "destination": ["obj-76", 0] + } + }, + { + "patchline": { + "source": ["obj-76", 0], + "destination": ["obj-77", 0], + "comment": "stop timer" + } + }, + { + "patchline": { + "source": ["obj-76", 1], + "destination": ["obj-78", 0], + "comment": "pending=0" + } + }, + { + "patchline": { + "source": ["obj-76", 2], + "destination": ["obj-80", 0], + "comment": "leading=0" + } + }, + { + "patchline": { + "source": ["obj-76", 3], + "destination": ["obj-82", 0], + "comment": "trailing=1" + } + }, + { + "patchline": { + "source": ["obj-77", 0], + "destination": ["obj-47", 0], + "comment": "stop delay on reset" + } + }, + { + "patchline": { + "source": ["obj-78", 0], + "destination": ["obj-79", 0] + } + }, + { + "patchline": { + "source": ["obj-80", 0], + "destination": ["obj-81", 0] + } + }, + { + "patchline": { + "source": ["obj-82", 0], + "destination": ["obj-83", 0] + } + }, + { + "patchline": { + "source": ["obj-6", 0], + "destination": ["obj-7", 0] + } + }, + { + "patchline": { + "source": ["obj-7", 0], + "destination": ["obj-8", 0] + } + }, + { + "patchline": { + "source": ["obj-8", 0], + "destination": ["obj-9", 0], + "comment": "prepend time to patcherargs" + } + }, + { + "patchline": { + "source": ["obj-8", 1], + "destination": ["obj-10", 0], + "comment": "init defaults" + } + }, + { + "patchline": { + "source": ["obj-9", 0], + "destination": ["obj-20", 0], + "comment": "send time from init" + } + }, + { + "patchline": { + "source": ["obj-10", 0], + "destination": ["obj-11", 0] + } + }, + { + "patchline": { + "source": ["obj-10", 1], + "destination": ["obj-13", 0] + } + }, + { + "patchline": { + "source": ["obj-10", 2], + "destination": ["obj-15", 0] + } + }, + { + "patchline": { + "source": ["obj-10", 3], + "destination": ["obj-17", 0] + } + }, + { + "patchline": { + "source": ["obj-11", 0], + "destination": ["obj-12", 0] + } + }, + { + "patchline": { + "source": ["obj-13", 0], + "destination": ["obj-14", 0] + } + }, + { + "patchline": { + "source": ["obj-15", 0], + "destination": ["obj-16", 0] + } + }, + { + "patchline": { + "source": ["obj-17", 0], + "destination": ["obj-18", 0] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} diff --git a/patches/util.dict.defaults/README.md b/patches/util.dict.defaults/README.md new file mode 100644 index 0000000..a2d7ef7 --- /dev/null +++ b/patches/util.dict.defaults/README.md @@ -0,0 +1,167 @@ +# util.dict.defaults + +Merge defaults into incoming dictionaries and validate required keys. Reduces repetitive dict merge/validation wiring in Max and Max4Live patches. + +## Inlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | dictionary | Input dictionaries to process (via `dictionary ` messages). | +| 2 | symbol (message) | Control messages: `defaults`, `required`, `addrequired`, `clearrequired`, `force`, `reset`. | + +## Outlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | dictionary | Merged dictionary (`dictionary `). Output always emits, even if required keys are missing. | +| 2 | any | Validation/status: `ok` (all required keys present), `missing ...` (missing required keys), `configured`, `reset`. | +| 3 | list | Diagnostics: `applied ` (defaults applied), `preserved ` (input keys preserved over defaults). | + +## Arguments + +| Argument | Type | Default | Description | +|----------|------|---------|-------------| +| 1 | int | 0 | Initial force mode. `0` = input wins, `1` = defaults overwrite. | + +``` +util.dict.defaults --> force 0 (default, input wins) +util.dict.defaults 1 --> force 1 (defaults overwrite) +``` + +## Commands (Left Inlet) + +### dictionary *name* + +Process an input dictionary. The input dict is cloned into an internal working dict, defaults are merged according to the current force mode, required key validation runs, and the merged result is emitted from outlet 1. + +``` +dictionary my_input --> merge defaults, validate, emit merged dict +``` + +## Messages (Right Inlet) + +### defaults *dictname* + +Set the defaults source dictionary. The contents of the named dict are imported into internal storage. Outputs `configured` from outlet 2. + +``` +defaults my_defaults --> import defaults from dict named my_defaults +``` + +### required *key1 key2 ...* + +Replace the required key list. All previously required keys are cleared first. Outputs `configured` from outlet 2. + +``` +required name color size --> these keys must exist after merge +``` + +### addrequired *key1 ...* + +Append keys to the required list without clearing existing entries. Outputs `configured` from outlet 2. + +``` +addrequired weight --> add weight to required keys +``` + +### clearrequired + +Remove all required keys. After this, validation always outputs `ok`. Outputs `configured` from outlet 2. + +### force *0|1* + +Set the merge mode. + +- `force 0` (default): Input keys win. Defaults only fill missing keys. +- `force 1`: Defaults overwrite all matching input keys. + +``` +force 1 --> defaults overwrite input keys +force 0 --> input wins, defaults fill gaps only +``` + +### reset + +Clear all internal state: defaults dict, required key list, and force flag (reset to 0). Outputs `reset` from outlet 2. + +## Processing Semantics + +1. Input dict is cloned into an internal working dict. +2. Default keys are iterated: + - **force 0**: Each default key is checked against the working dict. If the key is missing, the default value is applied (`applied` count increments). If the key exists, it is preserved (`preserved` count increments). + - **force 1**: All default key/value pairs are written into the working dict (`applied` count increments for each). +3. Required key validation runs against the merged working dict. Each required key is checked for presence. Missing keys are collected. +4. Output order (deterministic, enforced by `trigger`): + - Outlet 2: validation status (`ok` or `missing `) + - Outlet 3: diagnostics (`applied N`, `preserved N`) + - Outlet 1: merged dictionary + +The merged dictionary is always emitted, even if required keys are missing. This allows downstream logic to handle partial data if desired. + +## Defaults + +| Parameter | Default | +|-----------|---------| +| force | 0 (input wins) | +| required keys | (none) | +| defaults | (empty) | + +## Example Usage + +### Fill missing keys + +``` +[message: clear, set color blue, set size 10] +| +[dict my_defaults] + +[message: defaults my_defaults] [message: dictionary my_input] +| | +[util.dict.defaults -------------------] +| | | +[dict.print] [print status] [print diagnostics] +``` + +Input `{color: red, name: test}` with defaults `{color: blue, size: 10}` produces: +- Merged: `{color: red, name: test, size: 10}` (color preserved, size applied) +- Status: `ok` (no required keys configured) +- Diagnostics: `applied 1`, `preserved 1` + +### Validate required keys + +``` +[message: required color size weight] +| +[util.dict.defaults] +| | +[dict.print] [route ok missing] + | | + [print ok] [print missing-keys] +``` + +If merged dict has `color` and `size` but not `weight`: +- Status: `missing weight` + +### Force mode + +With `force 1`, defaults overwrite input keys: +- Input: `{color: red}`, Defaults: `{color: blue, size: 10}` +- Merged: `{color: blue, size: 10}` (color overwritten, size applied) +- Diagnostics: `applied 2`, `preserved 0` + +## Implementation Notes + +- Uses `#0`-scoped internal state (`v`, `dict`, `coll` objects) for instance isolation. Multiple instances in the same patch do not interfere. +- Internal dicts: `#0_defaults` (stores default key/value pairs), `#0_work` (working copy for merge operations). +- Required keys stored in `coll #0_required` (symbol keys, iterated via `dump`). +- Force flag stored in `v #0_force`. +- Merge counters stored in `v #0_applied` and `v #0_preserved`, reset to 0 before each merge operation. +- Deterministic message ordering enforced via `trigger` objects throughout. +- Top-level keys only; nested dict paths are not supported. + +## See Also + +- **dict** -- key-value dictionary storage +- **dict.iter** -- iterate dictionary keys and values +- **util.cache.ttl** -- key-value cache with time-to-live expiration +- **util.debounce** -- debounce rapid input (companion utility) diff --git a/patches/util.dict.defaults/util.dict.defaults-help.maxpat b/patches/util.dict.defaults/util.dict.defaults-help.maxpat new file mode 100644 index 0000000..48d57c2 --- /dev/null +++ b/patches/util.dict.defaults/util.dict.defaults-help.maxpat @@ -0,0 +1,769 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 100.0, + 100.0, + 800.0, + 700.0 + ], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [ + 8.0, + 8.0 + ], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Help patch for util.dict.defaults", + "digest": "", + "tags": "", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "util.dict.defaults", + "fontsize": 18.0, + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 16.0, + 16.0, + 224.0, + 28.0 + ] + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Merge defaults into dictionaries and validate required keys", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 16.0, + 48.0, + 400.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "comment", + "text": "--- Basic Usage: Fill missing keys from defaults ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 16.0, + 80.0, + 360.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "comment", + "text": "1. Set up a defaults dictionary", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 16.0, + 104.0, + 240.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "clear, set color blue, set size 10, set shape circle", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 16.0, + 128.0, + 320.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "dict my_defaults", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 16.0, + 152.0, + 104.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "defaults my_defaults", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 16.0, + 184.0, + 128.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "comment", + "text": "2. Create an input dict with some keys", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 104.0, + 280.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "message", + "text": "clear, set color red, set name test", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 128.0, + 224.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "dict my_input", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 400.0, + 152.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "comment", + "text": "3. Send input dict to be processed", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 184.0, + 240.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "message", + "text": "dictionary my_input", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 208.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "util.dict.defaults", + "numinlets": 2, + "numoutlets": 3, + "outlettype": [ + "", + "", + "" + ], + "patching_rect": [ + 400.0, + 264.0, + 272.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "print merged", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 336.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "print status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 528.0, + 336.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "print diagnostics", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 656.0, + 336.0, + 104.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "route dictionary", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 400.0, + 296.0, + 104.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "dict.print", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 368.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "comment", + "text": "--- Required Key Validation ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 16.0, + 416.0, + 240.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "message", + "text": "required color size name", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 16.0, + 440.0, + 152.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "comment", + "text": "<-- set required keys, then re-send input dict", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 176.0, + 440.0, + 304.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "message", + "text": "addrequired weight", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 16.0, + 464.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "comment", + "text": "<-- add more required keys", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 144.0, + 464.0, + 200.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "message", + "text": "clearrequired", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 16.0, + 488.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "comment", + "text": "<-- clear required keys (all pass)", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 112.0, + 488.0, + 240.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "comment", + "text": "--- Force Mode ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 16.0, + 528.0, + 160.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "message", + "text": "force 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 16.0, + 552.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "comment", + "text": "<-- defaults overwrite input keys", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 80.0, + 552.0, + 240.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "message", + "text": "force 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 16.0, + 576.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "comment", + "text": "<-- input wins, defaults fill gaps (default)", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 80.0, + 576.0, + 304.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 16.0, + 616.0, + 40.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "comment", + "text": "<-- clear all state (defaults + required + force flag)", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 64.0, + 616.0, + 360.0, + 20.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-13", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 1 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 2 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-14", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-18", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-20", + 0 + ], + "destination": [ + "obj-13", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-13", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 0 + ], + "destination": [ + "obj-13", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 0 + ], + "destination": [ + "obj-13", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-29", + 0 + ], + "destination": [ + "obj-13", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-31", + 0 + ], + "destination": [ + "obj-13", + 1 + ] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} diff --git a/patches/util.dict.defaults/util.dict.defaults.maxpat b/patches/util.dict.defaults/util.dict.defaults.maxpat new file mode 100644 index 0000000..4e51aa5 --- /dev/null +++ b/patches/util.dict.defaults/util.dict.defaults.maxpat @@ -0,0 +1,5038 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 100.0, + 100.0, + 1200.0, + 800.0 + ], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [ + 8.0, + 8.0 + ], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Merge defaults into dictionaries and validate required keys", + "digest": "Apply default values and check required keys on incoming dicts", + "tags": "utility dictionary defaults merge validation", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "input: dictionary name" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "index": 2, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 560.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "control: defaults, required, addrequired, clearrequired, force, reset" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 744.0, + 30.0, + 30.0 + ], + "comment": "merged dictionary" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 320.0, + 744.0, + 30.0, + 30.0 + ], + "comment": "validation/status: ok, missing keys, configured, reset" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "outlet", + "index": 3, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 600.0, + 744.0, + 30.0, + 30.0 + ], + "comment": "diagnostics: applied N, preserved N" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "comment", + "text": "--- INIT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 800.0, + 16.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 800.0, + 40.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 800.0, + 64.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "patcherargs 0", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 920.0, + 64.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "v #0_force", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 920.0, + 96.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "dict #0_defaults", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 800.0, + 96.0, + 104.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "dict #0_work", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 800.0, + 120.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "coll #0_required", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 800.0, + 144.0, + 104.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "comment", + "text": "--- CONTROL (inlet 2) ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 560.0, + 56.0, + 160.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "route defaults required addrequired clearrequired force reset", + "numinlets": 1, + "numoutlets": 7, + "outlettype": [ + "", + "", + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 560.0, + 80.0, + 392.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "p SetDefaults", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 560.0, + 112.0, + 88.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 640.0, + 480.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 40.0, + 30.0, + 30.0 + ], + "comment": "dict name to use as defaults source" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t s b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 50.0, + 90.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 120.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "dict #0_defaults", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 180.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "prepend import", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 150.0, + 95.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 240.0, + 30.0, + 30.0 + ], + "comment": "status: configured" + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "configured", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 210.0, + 72.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "p SetRequired", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 656.0, + 112.0, + 88.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 640.0, + 480.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 40.0, + 30.0, + 30.0 + ], + "comment": "required key list" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 50.0, + 90.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 120.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "coll #0_required", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 50.0, + 210.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "zl iter 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 150.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "prepend store", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 180.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 290.0, + 30.0, + 30.0 + ], + "comment": "status: configured" + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "message", + "text": "configured", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 255.0, + 72.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 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-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "p AddRequired", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 752.0, + 112.0, + 88.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 640.0, + 480.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 40.0, + 30.0, + 30.0 + ], + "comment": "keys to add to required list" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "zl iter 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 90.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "prepend store", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 120.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "coll #0_required", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 50.0, + 150.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 225.0, + 30.0, + 30.0 + ], + "comment": "status: configured" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "configured", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 195.0, + 72.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-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "p ClearRequired", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 848.0, + 112.0, + 96.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 640.0, + 480.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 40.0, + 30.0, + 30.0 + ], + "comment": "bang to clear required keys" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 90.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "coll #0_required", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 50.0, + 120.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 195.0, + 30.0, + 30.0 + ], + "comment": "status: configured" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "configured", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 72.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-3", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "v #0_force", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 952.0, + 112.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "comment", + "text": "--- RESET ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 1048.0, + 80.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "p Reset", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1048.0, + 112.0, + 56.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 640.0, + 480.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 40.0, + 30.0, + 30.0 + ], + "comment": "bang to reset" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t b b b b", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "bang", + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 90.0, + 165.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 135.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "dict #0_defaults", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 105.0, + 135.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "coll #0_required", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 105.0, + 165.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 160.0, + 135.0, + 24.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "v #0_force", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 160.0, + 165.0, + 75.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 255.0, + 30.0, + 30.0 + ], + "comment": "status: reset" + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 197.0, + 135.0, + 40.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 2 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 3 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "comment", + "text": "--- PROCESS (inlet 1) ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 56.0, + 160.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "newobj", + "text": "route dictionary", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 80.0, + 104.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "text": "p MergeDefaults", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "", + "", + "" + ], + "patching_rect": [ + 56.0, + 200.0, + 304.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 800.0, + 600.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 40.0, + 30.0, + 30.0 + ], + "comment": "input dict name" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t s b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 50.0, + 90.0, + 165.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "dict #0_work", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 195.0, + 100.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 197.0, + 120.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "prepend import", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 165.0, + 95.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 225.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "v #0_force", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 255.0, + 75.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "sel 0 1", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "" + ], + "patching_rect": [ + 120.0, + 285.0, + 75.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "p MergeGapFill", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 330.0, + 105.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 250.0, + 250.0, + 700.0, + 500.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 40.0, + 30.0, + 30.0 + ], + "comment": "bang to start merge" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 90.0, + 135.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 120.0, + 24.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_applied", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 150.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 120.0, + 24.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "v #0_preserved", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 150.0, + 95.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "getkeys", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 120.0, + 52.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "dict #0_defaults", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 150.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "route keys", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 180.0, + 75.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "zl iter 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 210.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 240.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "prepend contains", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 240.0, + 270.0, + 105.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "dict #0_work", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 240.0, + 300.0, + 100.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "sel 0", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 240.0, + 330.0, + 50.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "prepend get", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 360.0, + 78.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "dict #0_defaults", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 390.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "dict #0_work", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 420.0, + 100.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "prepend set", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 450.0, + 78.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "v #0_applied", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 410.0, + 360.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 410.0, + 390.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v #0_applied", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 410.0, + 420.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "v #0_preserved", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 510.0, + 300.0, + 95.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 510.0, + 330.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "newobj", + "text": "v #0_preserved", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 510.0, + 360.0, + 95.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 460.0, + 30.0, + 30.0 + ], + "comment": "applied count" + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 120.0, + 460.0, + 30.0, + 30.0 + ], + "comment": "preserved count" + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "v #0_applied", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 450.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "newobj", + "text": "v #0_preserved", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 450.0, + 95.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-27", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-28", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 2 + ], + "destination": [ + "obj-3", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 2 + ], + "destination": [ + "obj-7", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 1 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 1 + ], + "destination": [ + "obj-22", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-20", + 0 + ], + "destination": [ + "obj-21", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-23", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-23", + 0 + ], + "destination": [ + "obj-24", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 0 + ], + "destination": [ + "obj-25", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-28", + 0 + ], + "destination": [ + "obj-26", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "p MergeForce", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 195.0, + 330.0, + 105.0, + 22.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 250.0, + 250.0, + 700.0, + 500.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 40.0, + 30.0, + 30.0 + ], + "comment": "bang to start force merge" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 90.0, + 135.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 120.0, + 24.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "v #0_applied", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 150.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 120.0, + 24.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "v #0_preserved", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 150.0, + 95.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "getkeys", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 120.0, + 52.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "dict #0_defaults", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 150.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "route keys", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 180.0, + 75.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "zl iter 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 210.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "t s s", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 240.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "prepend get", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 270.0, + 78.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "dict #0_defaults", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 300.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "prepend set", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 170.0, + 330.0, + 78.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "dict #0_work", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 170.0, + 360.0, + 100.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "v #0_applied", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 350.0, + 270.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 350.0, + 300.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "v #0_applied", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 350.0, + 330.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 400.0, + 30.0, + 30.0 + ], + "comment": "applied count" + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 120.0, + 400.0, + 30.0, + 30.0 + ], + "comment": "preserved count (always 0 in force mode)" + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v #0_applied", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 390.0, + 85.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 390.0, + 24.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-21", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-22", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 2 + ], + "destination": [ + "obj-3", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-2", + 2 + ], + "destination": [ + "obj-7", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 1 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-21", + 0 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 510.0, + 30.0, + 30.0 + ], + "comment": "merged dict name" + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 195.0, + 510.0, + 30.0, + 30.0 + ], + "comment": "applied count" + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "outlet", + "index": 3, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 282.0, + 510.0, + 30.0, + 30.0 + ], + "comment": "preserved count" + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 375.0, + 148.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "name", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 405.0, + 42.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "dict #0_work", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 435.0, + 100.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "route name", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 465.0, + 75.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "prepend dictionary", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 480.0, + 115.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 1 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 1 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 1 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 1 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 1 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-20", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "newobj", + "text": "p ValidateRequired", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 200.0, + 112.0, + 20.0 + ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 200.0, + 200.0, + 700.0, + 500.0 + ], + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [ + 15.0, + 15.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 40.0, + 30.0, + 30.0 + ], + "comment": "bang to validate" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 50.0, + 90.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "text": "coll #0_required", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 120.0, + 135.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "text": "sel 0", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 120.0, + 165.0, + 50.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "ok", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 195.0, + 24.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "dump", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 120.0, + 40.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "zl reg", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 195.0, + 50.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "zl iter 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 225.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "prepend contains", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 255.0, + 105.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "dict #0_work", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 285.0, + 100.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "sel 0", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 50.0, + 315.0, + 50.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "zl group", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 50.0, + 345.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "prepend missing", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 50.0, + 375.0, + 100.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 50.0, + 420.0, + 30.0, + 30.0 + ], + "comment": "validation status" + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "message", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 300.0, + 225.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "prepend set", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 300.0, + 165.0, + 75.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "message", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 300.0, + 345.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "prepend append", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 300.0, + 315.0, + 97.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "message", + "text": "length", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 120.0, + 45.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-2", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-6", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 1 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-3", + 2 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-4", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-5", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-7", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 1 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + } + ] + }, + "saved_object_attributes": { + "globalpatchername": "" + } + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "comment", + "text": "--- EMIT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 160.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "newobj", + "text": "t s", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 112.0, + 160.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "t l l l", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "", + "", + "" + ], + "patching_rect": [ + 56.0, + 240.0, + 304.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "text": "pack s i", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 280.0, + 160.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "newobj", + "text": "prepend applied", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 200.0, + 312.0, + 96.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "newobj", + "text": "prepend preserved", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 344.0, + 312.0, + 112.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "newobj", + "text": "unpack s i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "int" + ], + "patching_rect": [ + 200.0, + 240.0, + 160.0, + 20.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-24", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 1 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 2 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-9", + 1 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 1 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 2 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 3 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 4 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 5 + ], + "destination": [ + "obj-22", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 0 + ], + "destination": [ + "obj-28", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-28", + 0 + ], + "destination": [ + "obj-25", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-25", + 0 + ], + "destination": [ + "obj-29", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-29", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-29", + 1 + ], + "destination": [ + "obj-33", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-29", + 2 + ], + "destination": [ + "obj-26", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-25", + 1 + ], + "destination": [ + "obj-30", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-25", + 2 + ], + "destination": [ + "obj-30", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-33", + 0 + ], + "destination": [ + "obj-31", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-33", + 1 + ], + "destination": [ + "obj-32", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-31", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-32", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-26", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} \ No newline at end of file diff --git a/patches/util.listdiff/README.md b/patches/util.listdiff/README.md new file mode 100644 index 0000000..26be2dd --- /dev/null +++ b/patches/util.listdiff/README.md @@ -0,0 +1,124 @@ +# util.listdiff + +Compare an incoming list against the previously received list and output the differences. Produces three result lists: items added (present in new but not previous), items removed (present in previous but not new), and items common to both. Duplicate occurrences are tracked independently, so `1 1 2` compared against `1 2 2` correctly reports one `2` added and one `1` removed. + +## Inlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | list (int, float, symbol) | List to compare. Triggers comparison against the stored previous list and then becomes the new previous list. | +| 2 | symbol (message) | Control messages: `reset`, `bypass`, `sorted`. | + +## Outlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | list | Added items: elements in the new list that were not in the previous list. | +| 2 | list | Removed items: elements in the previous list that are not in the new list. | +| 3 | list | Common items: elements present in both lists. | +| 4 | bang | Bang when no changes are detected (added and removed are both empty). | + +## Arguments + +None. No creation arguments. + +``` +util.listdiff --> compare incoming lists +``` + +## Messages (Right Inlet) + +### reset + +Clear the stored previous list. The next incoming list will be treated as the first list (all items reported as added, nothing removed). + +### bypass *0|1* + +Enable or disable bypass mode. When bypassed, the incoming list passes directly to outlet 1 without comparison. The stored previous list is not updated during bypass. + +``` +bypass 1 --> pass lists through outlet 1 unchanged +bypass 0 --> resume comparing (default) +``` + +### sorted *0|1* + +Enable or disable sorted comparison mode. When enabled, both lists are sorted before comparison, making the diff order-independent. The original (unsorted) list is stored as the previous list. + +``` +sorted 1 --> sort before comparing (order-independent) +sorted 0 --> compare in original order (default) +``` + +## Defaults + +| Parameter | Default | +|-----------|---------| +| bypass | 0 (off) | +| sorted | 0 (off) | + +## Behavior + +- **First list**: When no previous list is stored (after init or reset), all items in the incoming list are reported as "added" and nothing is "removed". +- **Duplicate handling**: Each occurrence is tracked independently using a bag (multiset) comparison. For example, `[1 1 2]` vs `[1 2 2]` reports added: `[2]`, removed: `[1]`, common: `[1 2]`. +- **Empty diff**: When the new list is identical to the previous (same elements, same counts), outlet 4 emits a bang and no output appears on outlets 1-3. +- **Storage**: After each comparison, the new list replaces the stored previous list. +- **Output order**: Results are output right-to-left (bang first, then common, removed, added) following Max convention. +- **Single items**: When a result contains exactly one item, it is output as a single value (not a list). + +## Example Usage + +### Track MIDI note changes + +Compare the set of currently held notes against the previous set to detect note-ons and note-offs. + +``` +[your-note-collector] +| +[util.listdiff] +| | | +[added] [removed] [still-held] +``` + +### Detect list changes with sorted mode + +When order does not matter, enable sorted mode to ignore element ordering. + +``` +[message: 3 1 2] [message: 1 2 3] +| | +[util.listdiff] | +| [message: sorted 1] +| | +[print added] [util.listdiff] (right inlet) +``` + +With `sorted 1`, sending `3 1 2` then `1 2 3` produces a bang on outlet 4 (no changes). + +### Monitor parameter changes + +Compare snapshots of parameter lists to find which parameters changed. + +``` +[your-param-snapshot] +| +[util.listdiff] +| | +[changed] [unchanged] +``` + +## Implementation Notes + +- Uses a `v8.codebox` (Max 9 V8 JavaScript engine) for the duplicate-aware bag comparison algorithm. This ensures correct handling of duplicates without requiring complex `zl` chain logic. +- The previous list is stored internally in the V8 runtime. The `reset` message clears it. +- Instance isolation is achieved through the V8 object's own state scope and `#0`-scoped `v` objects for bypass state. +- Supports numeric values, floats, and symbols. Comparison uses string coercion for bag keys. + +## See Also + +- **zl sect** -- list intersection (set-based, no duplicate awareness) +- **zl union** -- list union +- **zl filter** -- filter elements from a list +- **array.sect** / **array.union** -- Max 9 array set operations +- **change** -- suppress repeated identical single values +- **== / !=** -- element-wise comparison of equal-length lists diff --git a/patches/util.listdiff/util.listdiff-help.maxpat b/patches/util.listdiff/util.listdiff-help.maxpat new file mode 100644 index 0000000..0820be5 --- /dev/null +++ b/patches/util.listdiff/util.listdiff-help.maxpat @@ -0,0 +1,655 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 760.0, 680.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontface": 0, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [8.0, 8.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Help patch for util.listdiff", + "digest": "Demonstrates list diff abstraction usage", + "tags": "help utility list diff", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "util.listdiff", + "fontface": 1, + "fontsize": 16.0, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 16.0, 160.0, 24.0] + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Compare incoming list against previous list. Outputs added, removed, and common items. Duplicate-aware: each occurrence is tracked independently.", + "linecount": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 42.0, 520.0, 34.0] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "comment", + "text": "--- BASIC DIFF ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 88.0, 130.0, 20.0] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "comment", + "text": "Send different lists. First list: all items are 'added'. Subsequent lists show changes.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 108.0, 520.0, 20.0] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "message", + "text": "1 2 3", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [16.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "2 3 4", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [64.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "2 3 4", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [112.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "message", + "text": "5 6", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [160.0, 136.0, 32.0, 22.0] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "util.listdiff", + "numinlets": 2, + "numoutlets": 4, + "outlettype": ["", "", "", ""], + "patching_rect": [16.0, 168.0, 280.0, 22.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "print added", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 200.0, 68.0, 22.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "print removed", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [104.0, 200.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "print common", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [200.0, 200.0, 76.0, 22.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "print no-change", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [288.0, 200.0, 92.0, 22.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "comment", + "text": "--- DUPLICATE-AWARE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 240.0, 160.0, 20.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "comment", + "text": "Duplicates tracked independently: [1 1 2] vs [1 2 2] -> added: 2, removed: 1", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 260.0, 500.0, 20.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "message", + "text": "1 1 2", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [16.0, 288.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "1 2 2", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [64.0, 288.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "util.listdiff", + "numinlets": 2, + "numoutlets": 4, + "outlettype": ["", "", "", ""], + "patching_rect": [16.0, 320.0, 280.0, 22.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "print dup-added", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 352.0, 92.0, 22.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "print dup-removed", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [120.0, 352.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "comment", + "text": "--- SYMBOL SUPPORT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 88.0, 150.0, 20.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "comment", + "text": "Works with symbols too.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 108.0, 200.0, 20.0] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "message", + "text": "foo bar baz", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 136.0, 68.0, 22.0] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "message", + "text": "bar baz qux", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [472.0, 136.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "text": "util.listdiff", + "numinlets": 2, + "numoutlets": 4, + "outlettype": ["", "", "", ""], + "patching_rect": [400.0, 168.0, 280.0, 22.0] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "newobj", + "text": "print sym-added", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 200.0, 92.0, 22.0] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "print sym-removed", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [504.0, 200.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "comment", + "text": "--- RESET ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 240.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "comment", + "text": "Reset clears stored previous list.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 260.0, 300.0, 20.0] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "message", + "text": "1 2 3", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 288.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [456.0, 288.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "newobj", + "text": "util.listdiff", + "numinlets": 2, + "numoutlets": 4, + "outlettype": ["", "", "", ""], + "patching_rect": [400.0, 320.0, 280.0, 22.0] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "newobj", + "text": "print reset-added", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 352.0, 100.0, 22.0] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "comment", + "text": "--- SORTED MODE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 400.0, 130.0, 20.0] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "comment", + "text": "Sort both lists before comparison (order-independent diff).", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 420.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "message", + "text": "3 1 2", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [16.0, 448.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "message", + "text": "1 2 3", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [64.0, 448.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "message", + "text": "sorted 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [120.0, 448.0, 52.0, 22.0] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [120.0, 424.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "newobj", + "text": "util.listdiff", + "numinlets": 2, + "numoutlets": 4, + "outlettype": ["", "", "", ""], + "patching_rect": [16.0, 504.0, 280.0, 22.0] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "newobj", + "text": "print sorted-added", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 536.0, 108.0, 22.0] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "newobj", + "text": "print sorted-nochange", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [288.0, 536.0, 124.0, 22.0] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "comment", + "text": "--- OUTLETS ---\n\nOutlet 1: added items\nOutlet 2: removed items\nOutlet 3: common items\nOutlet 4: bang if no changes\n\n--- CONTROL MESSAGES (right inlet) ---\n\nreset - clear stored previous list\nbypass <0|1> - pass list to outlet 1 unchanged\nsorted <0|1> - sort before comparing", + "linecount": 12, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [440.0, 400.0, 260.0, 180.0] + } + } + ], + "lines": [ + { + "patchline": { + "source": ["obj-5", 0], + "destination": ["obj-9", 0] + } + }, + { + "patchline": { + "source": ["obj-6", 0], + "destination": ["obj-9", 0] + } + }, + { + "patchline": { + "source": ["obj-7", 0], + "destination": ["obj-9", 0] + } + }, + { + "patchline": { + "source": ["obj-8", 0], + "destination": ["obj-9", 0] + } + }, + { + "patchline": { + "source": ["obj-9", 0], + "destination": ["obj-10", 0] + } + }, + { + "patchline": { + "source": ["obj-9", 1], + "destination": ["obj-11", 0] + } + }, + { + "patchline": { + "source": ["obj-9", 2], + "destination": ["obj-12", 0] + } + }, + { + "patchline": { + "source": ["obj-9", 3], + "destination": ["obj-13", 0] + } + }, + { + "patchline": { + "source": ["obj-16", 0], + "destination": ["obj-18", 0] + } + }, + { + "patchline": { + "source": ["obj-17", 0], + "destination": ["obj-18", 0] + } + }, + { + "patchline": { + "source": ["obj-18", 0], + "destination": ["obj-19", 0] + } + }, + { + "patchline": { + "source": ["obj-18", 1], + "destination": ["obj-20", 0] + } + }, + { + "patchline": { + "source": ["obj-23", 0], + "destination": ["obj-25", 0] + } + }, + { + "patchline": { + "source": ["obj-24", 0], + "destination": ["obj-25", 0] + } + }, + { + "patchline": { + "source": ["obj-25", 0], + "destination": ["obj-26", 0] + } + }, + { + "patchline": { + "source": ["obj-25", 1], + "destination": ["obj-27", 0] + } + }, + { + "patchline": { + "source": ["obj-30", 0], + "destination": ["obj-32", 0] + } + }, + { + "patchline": { + "source": ["obj-31", 0], + "destination": ["obj-32", 1] + } + }, + { + "patchline": { + "source": ["obj-32", 0], + "destination": ["obj-33", 0] + } + }, + { + "patchline": { + "source": ["obj-36", 0], + "destination": ["obj-40", 0] + } + }, + { + "patchline": { + "source": ["obj-37", 0], + "destination": ["obj-40", 0] + } + }, + { + "patchline": { + "source": ["obj-39", 0], + "destination": ["obj-38", 0] + } + }, + { + "patchline": { + "source": ["obj-38", 0], + "destination": ["obj-40", 1] + } + }, + { + "patchline": { + "source": ["obj-40", 0], + "destination": ["obj-41", 0] + } + }, + { + "patchline": { + "source": ["obj-40", 3], + "destination": ["obj-42", 0] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} \ No newline at end of file diff --git a/patches/util.listdiff/util.listdiff.maxpat b/patches/util.listdiff/util.listdiff.maxpat new file mode 100644 index 0000000..f23d391 --- /dev/null +++ b/patches/util.listdiff/util.listdiff.maxpat @@ -0,0 +1,421 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 880.0, 680.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [8.0, 8.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Compare incoming list against previous and output added, removed, common items", + "digest": "List differ with duplicate-aware comparison", + "tags": "utility list diff compare", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [56.0, 16.0, 30.0, 30.0], + "comment": "list to compare (hot - triggers diff against stored previous)" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "index": 2, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 16.0, 30.0, 30.0], + "comment": "control: reset, bypass, sorted" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 616.0, 30.0, 30.0], + "comment": "added items (in new, not in previous)" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [192.0, 616.0, 30.0, 30.0], + "comment": "removed items (in previous, not in new)" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "outlet", + "index": 3, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [328.0, 616.0, 30.0, 30.0], + "comment": "common items (in both lists)" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "outlet", + "index": 4, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [464.0, 616.0, 30.0, 30.0], + "comment": "bang when no changes detected" + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "comment", + "text": "--- INIT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [600.0, 16.0, 80.0, 20.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [600.0, 40.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [600.0, 64.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [600.0, 88.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "comment", + "text": "--- CONTROL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 56.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "route reset bypass sorted", + "numinlets": 1, + "numoutlets": 4, + "outlettype": ["", "", "", ""], + "patching_rect": [400.0, 80.0, 144.0, 22.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [432.0, 112.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "prepend sorted", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [464.0, 112.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 112.0, 36.0, 22.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "comment", + "text": "--- BYPASS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 56.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [56.0, 80.0, 42.0, 22.0], + "comment": "R-to-L: get bypass (first), pass list (second)" + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [56.0, 112.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": ["int"], + "patching_rect": [56.0, 136.0, 30.0, 22.0], + "comment": "bypass=0 -> gate 1, bypass=1 -> gate 2" + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "gate 2 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [56.0, 168.0, 152.0, 22.0], + "comment": "1=diff, 2=bypass passthrough" + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "comment", + "text": "--- DIFF (v8.codebox) ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 208.0, 160.0, 20.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "v8.codebox @code \"var prev = [];\\nvar doSort = 0;\\n\\nfunction reset() {\\n prev = [];\\n}\\n\\nfunction sorted(v) {\\n doSort = v ? 1 : 0;\\n}\\n\\nfunction msg_int(v) {\\n handleList([v]);\\n}\\n\\nfunction msg_float(v) {\\n handleList([v]);\\n}\\n\\nfunction list() {\\n handleList(Array.from(arguments));\\n}\\n\\nfunction anything() {\\n handleList([this.messagename].concat(Array.from(arguments)));\\n}\\n\\nfunction handleList(newArr) {\\n var oldArr = prev.slice();\\n var nw = doSort ? newArr.slice().sort() : newArr.slice();\\n var ol = doSort ? oldArr.slice().sort() : oldArr.slice();\\n var oldBag = new Map();\\n for (var i = 0; i < ol.length; i++) {\\n var k = String(ol[i]);\\n oldBag.set(k, (oldBag.get(k) || 0) + 1);\\n }\\n var added = [], common = [];\\n var wb = new Map(oldBag);\\n for (var j = 0; j < nw.length; j++) {\\n var k2 = String(nw[j]);\\n if (wb.has(k2) && wb.get(k2) > 0) {\\n common.push(nw[j]);\\n wb.set(k2, wb.get(k2) - 1);\\n } else {\\n added.push(nw[j]);\\n }\\n }\\n var newBag = new Map();\\n for (var m = 0; m < nw.length; m++) {\\n var nk = String(nw[m]);\\n newBag.set(nk, (newBag.get(nk) || 0) + 1);\\n }\\n var removed = [];\\n var wb2 = new Map(newBag);\\n for (var n = 0; n < ol.length; n++) {\\n var rk = String(ol[n]);\\n if (wb2.has(rk) && wb2.get(rk) > 0) {\\n wb2.set(rk, wb2.get(rk) - 1);\\n } else {\\n removed.push(ol[n]);\\n }\\n }\\n prev = newArr.slice();\\n if (added.length === 0 && removed.length === 0) {\\n this.outlet(3, 'bang');\\n }\\n if (common.length > 0) {\\n this.outlet(2, common.length === 1 ? common[0] : common);\\n }\\n if (removed.length > 0) {\\n this.outlet(1, removed.length === 1 ? removed[0] : removed);\\n }\\n if (added.length > 0) {\\n this.outlet(0, added.length === 1 ? added[0] : added);\\n }\\n}\\n\"", + "numinlets": 1, + "numoutlets": 4, + "outlettype": ["", "", "", ""], + "patching_rect": [56.0, 232.0, 456.0, 340.0] + } + } + ], + "lines": [ + { + "patchline": { + "source": ["obj-1", 0], + "destination": ["obj-17", 0] + } + }, + { + "patchline": { + "source": ["obj-17", 1], + "destination": ["obj-18", 0], + "comment": "get bypass (fires first)" + } + }, + { + "patchline": { + "source": ["obj-17", 0], + "destination": ["obj-20", 1], + "comment": "list to gate (fires second)" + } + }, + { + "patchline": { + "source": ["obj-18", 0], + "destination": ["obj-19", 0] + } + }, + { + "patchline": { + "source": ["obj-19", 0], + "destination": ["obj-20", 0] + } + }, + { + "patchline": { + "source": ["obj-20", 1], + "destination": ["obj-3", 0], + "comment": "bypass passthrough" + } + }, + { + "patchline": { + "source": ["obj-20", 0], + "destination": ["obj-22", 0], + "comment": "to diff" + } + }, + { + "patchline": { + "source": ["obj-22", 0], + "destination": ["obj-3", 0], + "comment": "added" + } + }, + { + "patchline": { + "source": ["obj-22", 1], + "destination": ["obj-4", 0], + "comment": "removed" + } + }, + { + "patchline": { + "source": ["obj-22", 2], + "destination": ["obj-5", 0], + "comment": "common" + } + }, + { + "patchline": { + "source": ["obj-22", 3], + "destination": ["obj-6", 0], + "comment": "no changes" + } + }, + { + "patchline": { + "source": ["obj-2", 0], + "destination": ["obj-12", 0] + } + }, + { + "patchline": { + "source": ["obj-12", 0], + "destination": ["obj-15", 0], + "comment": "reset" + } + }, + { + "patchline": { + "source": ["obj-12", 1], + "destination": ["obj-13", 0], + "comment": "bypass" + } + }, + { + "patchline": { + "source": ["obj-12", 2], + "destination": ["obj-14", 0], + "comment": "sorted value to prepend" + } + }, + { + "patchline": { + "source": ["obj-15", 0], + "destination": ["obj-22", 0], + "comment": "reset msg to v8" + } + }, + { + "patchline": { + "source": ["obj-14", 0], + "destination": ["obj-22", 0], + "comment": "sorted msg to v8" + } + }, + { + "patchline": { + "source": ["obj-8", 0], + "destination": ["obj-9", 0] + } + }, + { + "patchline": { + "source": ["obj-9", 0], + "destination": ["obj-10", 0] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} \ No newline at end of file diff --git a/patches/util.ringbuf/README.md b/patches/util.ringbuf/README.md new file mode 100644 index 0000000..051caa0 --- /dev/null +++ b/patches/util.ringbuf/README.md @@ -0,0 +1,146 @@ +# util.ringbuf + +Bounded ring buffer for messages with random access and dump. Stores up to N items in a circular history. When the buffer is full, the oldest item is overwritten. Supports integer, float, list, and symbol input. Retrieve items by index (0 = most recent) or dump the entire contents from oldest to newest. + +## Inlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | any (int, float, list, symbol) | Message to store in the buffer (hot). | +| 2 | symbol (message) | Control messages: `get`, `dump`, `clear`, `capacity`, `size`, `reset`, `bypass`. | + +## Outlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | any | Retrieved values (from get or dump). | +| 2 | symbol | Status messages: `full` (buffer reached capacity), `cleared` (buffer emptied), `size N` (current fill count). | + +## Arguments + +| Argument | Type | Default | Description | +|----------|------|---------|-------------| +| 1 | int | 16 | Initial buffer capacity (maximum number of stored items). | + +``` +util.ringbuf --> 16-slot buffer (default) +util.ringbuf 4 --> 4-slot buffer +util.ringbuf 64 --> 64-slot buffer +``` + +## Messages (Right Inlet) + +### get *index* + +Retrieve the item at the given index. Index 0 is the most recently stored item, 1 is one before that, and so on. Out-of-range indices produce no output. + +``` +get --> get most recent (index 0) +get 0 --> get most recent +get 3 --> get 4th most recent +``` + +### dump + +Output all stored items from oldest to newest through outlet 1. Each item is output as a separate message. + +### clear + +Remove all stored items and reset the write pointer. Outputs `cleared` from outlet 2. + +### capacity *N* + +Set a new buffer capacity. If the new capacity differs from the current one, the buffer is cleared and resized. Minimum capacity is 1. + +``` +capacity 4 --> 4-slot buffer +capacity 32 --> 32-slot buffer +``` + +### size + +Query the current number of stored items. Outputs `size N` from outlet 2. + +### reset + +Clear the buffer and restore the default capacity (16). Outputs `cleared` from outlet 2. + +### bypass *0|1* + +Enable or disable bypass mode. When bypassed, input messages pass directly to outlet 1 without being stored in the buffer. + +``` +bypass 1 --> pass messages through without storing +bypass 0 --> resume storing (default) +``` + +## Defaults + +| Parameter | Default | +|-----------|---------| +| capacity | 16 (or creation argument) | +| bypass | 0 (off) | + +## Behavior + +- **Circular overwrite**: When the buffer is full, new items overwrite the oldest. The `full` status is output from outlet 2 when the buffer first reaches capacity. +- **Random access**: `get N` retrieves by recency index (0 = newest). Invalid indices are silently ignored. +- **Dump order**: `dump` outputs items from oldest to newest, each as a separate message. +- **Message preservation**: Integers, floats, lists, and symbols are all stored and retrieved faithfully. Single-element results are output as single values (not lists). +- **Capacity change**: Setting a new capacity clears the buffer. Setting the same capacity has no effect. + +## Example Usage + +### Store and recall recent values + +``` +[number] +| +[util.ringbuf 8] +| | +[print value] [print status] +``` + +Send `get 0` to the right inlet to retrieve the most recent value. + +### MIDI note history + +Keep a history of the last 16 MIDI notes played. + +``` +[notein] +| +[stripnote] +| +[util.ringbuf 16] +| +[print note-history] +``` + +### Undo buffer for parameter changes + +Store parameter snapshots and recall previous states. + +``` +[your-param-value] +| +[util.ringbuf 32] +| [message: get 1] +[recall-previous] | + [util.ringbuf] (right inlet) +``` + +## Implementation Notes + +- Uses a `v8.codebox` (Max 9 V8 JavaScript engine) for the ring buffer logic. Modular arithmetic handles circular indexing efficiently. +- The `anything()` handler dispatches named control messages (get, dump, clear, capacity, size, reset) internally. +- Instance isolation is achieved through the V8 object's own state scope and `#0`-scoped `v` objects for bypass state. +- Supports all Max message types: int, float, list, and symbol. + +## See Also + +- **coll** -- indexed collection (persistent, file-backed, not circular) +- **zl reg** -- store and recall a single list +- **zl group** -- collect N items into a list +- **zl stream** -- sliding window over incoming items +- **buffer~** -- audio sample buffer (signal-rate, not message-rate) diff --git a/patches/util.ringbuf/util.ringbuf-help.maxpat b/patches/util.ringbuf/util.ringbuf-help.maxpat new file mode 100644 index 0000000..04d3af9 --- /dev/null +++ b/patches/util.ringbuf/util.ringbuf-help.maxpat @@ -0,0 +1,711 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 760.0, 800.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontface": 0, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [8.0, 8.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Help patch for util.ringbuf", + "digest": "Demonstrates ring buffer abstraction usage", + "tags": "help utility buffer ring", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "util.ringbuf", + "fontface": 1, + "fontsize": 16.0, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 16.0, 160.0, 24.0] + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Bounded ring buffer for messages with random access and dump. Stores up to N items in a circular history.", + "linecount": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 42.0, 480.0, 34.0] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "comment", + "text": "--- STORE AND GET ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 88.0, 180.0, 20.0] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "comment", + "text": "Send values in the left inlet to store. Use 'get N' to retrieve the Nth most recent (0 = newest).", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 108.0, 500.0, 20.0] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "message", + "text": "get 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [136.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "message", + "text": "get 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [184.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "message", + "text": "get 2", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [232.0, 136.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "util.ringbuf 4", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 168.0, 256.0, 22.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "print retrieved", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 200.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "print status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [200.0, 200.0, 68.0, 22.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "comment", + "text": "--- DUMP ALL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 240.0, 140.0, 20.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "comment", + "text": "Store several values, then send 'dump' to output all from oldest to newest.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 260.0, 460.0, 20.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "message", + "text": "10", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [16.0, 288.0, 30.0, 22.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "message", + "text": "20", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [56.0, 288.0, 30.0, 22.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "message", + "text": "30", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [96.0, 288.0, 30.0, 22.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "dump", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [184.0, 288.0, 38.0, 22.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [232.0, 288.0, 38.0, 22.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "util.ringbuf 8", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 320.0, 256.0, 22.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "print dump-out", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 352.0, 84.0, 22.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "print dump-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [200.0, 352.0, 100.0, 22.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "comment", + "text": "--- LIST AND SYMBOL INPUT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 392.0, 200.0, 20.0] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "comment", + "text": "Store lists and symbols. They are preserved through the buffer.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 412.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "message", + "text": "1 2 3", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [16.0, 440.0, 42.0, 22.0] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "message", + "text": "hello world", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [68.0, 440.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "message", + "text": "get 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [200.0, 440.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "message", + "text": "dump", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [248.0, 440.0, 38.0, 22.0] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "newobj", + "text": "util.ringbuf 4", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 472.0, 272.0, 22.0] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "print list-out", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 504.0, 76.0, 22.0] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "text": "print list-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [216.0, 504.0, 92.0, 22.0] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "comment", + "text": "--- CAPACITY AND SIZE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 544.0, 180.0, 20.0] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "comment", + "text": "Change capacity dynamically. 'size' reports current fill count. 'full' status when buffer is full.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 564.0, 560.0, 20.0] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 592.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "message", + "text": "capacity 3", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [136.0, 592.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "message", + "text": "size", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [208.0, 592.0, 32.0, 22.0] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [248.0, 592.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "newobj", + "text": "util.ringbuf 4", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 624.0, 272.0, 22.0] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "newobj", + "text": "print cap-out", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 656.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "newobj", + "text": "print cap-status", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [216.0, 656.0, 92.0, 22.0] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "comment", + "text": "--- BYPASS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 88.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "comment", + "text": "When bypassed, input passes directly to outlet 1 without being stored.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 108.0, 340.0, 20.0] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [400.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "message", + "text": "bypass 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [520.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "message", + "text": "bypass 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [584.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "newobj", + "text": "util.ringbuf 4", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [400.0, 168.0, 240.0, 22.0] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "newobj", + "text": "print bypass-out", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 200.0, 92.0, 22.0] + } + } + ], + "lines": [ + { + "patchline": { + "source": ["obj-5", 0], + "destination": ["obj-9", 0] + } + }, + { + "patchline": { + "source": ["obj-6", 0], + "destination": ["obj-9", 1] + } + }, + { + "patchline": { + "source": ["obj-7", 0], + "destination": ["obj-9", 1] + } + }, + { + "patchline": { + "source": ["obj-8", 0], + "destination": ["obj-9", 1] + } + }, + { + "patchline": { + "source": ["obj-9", 0], + "destination": ["obj-10", 0] + } + }, + { + "patchline": { + "source": ["obj-9", 1], + "destination": ["obj-11", 0] + } + }, + { + "patchline": { + "source": ["obj-14", 0], + "destination": ["obj-19", 0] + } + }, + { + "patchline": { + "source": ["obj-15", 0], + "destination": ["obj-19", 0] + } + }, + { + "patchline": { + "source": ["obj-16", 0], + "destination": ["obj-19", 0] + } + }, + { + "patchline": { + "source": ["obj-17", 0], + "destination": ["obj-19", 1] + } + }, + { + "patchline": { + "source": ["obj-18", 0], + "destination": ["obj-19", 1] + } + }, + { + "patchline": { + "source": ["obj-19", 0], + "destination": ["obj-20", 0] + } + }, + { + "patchline": { + "source": ["obj-19", 1], + "destination": ["obj-21", 0] + } + }, + { + "patchline": { + "source": ["obj-24", 0], + "destination": ["obj-28", 0] + } + }, + { + "patchline": { + "source": ["obj-25", 0], + "destination": ["obj-28", 0] + } + }, + { + "patchline": { + "source": ["obj-26", 0], + "destination": ["obj-28", 1] + } + }, + { + "patchline": { + "source": ["obj-27", 0], + "destination": ["obj-28", 1] + } + }, + { + "patchline": { + "source": ["obj-28", 0], + "destination": ["obj-29", 0] + } + }, + { + "patchline": { + "source": ["obj-28", 1], + "destination": ["obj-30", 0] + } + }, + { + "patchline": { + "source": ["obj-33", 0], + "destination": ["obj-37", 0] + } + }, + { + "patchline": { + "source": ["obj-34", 0], + "destination": ["obj-37", 1] + } + }, + { + "patchline": { + "source": ["obj-35", 0], + "destination": ["obj-37", 1] + } + }, + { + "patchline": { + "source": ["obj-36", 0], + "destination": ["obj-37", 1] + } + }, + { + "patchline": { + "source": ["obj-37", 0], + "destination": ["obj-38", 0] + } + }, + { + "patchline": { + "source": ["obj-37", 1], + "destination": ["obj-39", 0] + } + }, + { + "patchline": { + "source": ["obj-42", 0], + "destination": ["obj-45", 0] + } + }, + { + "patchline": { + "source": ["obj-43", 0], + "destination": ["obj-45", 1] + } + }, + { + "patchline": { + "source": ["obj-44", 0], + "destination": ["obj-45", 1] + } + }, + { + "patchline": { + "source": ["obj-45", 0], + "destination": ["obj-46", 0] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} \ No newline at end of file diff --git a/patches/util.ringbuf/util.ringbuf.maxpat b/patches/util.ringbuf/util.ringbuf.maxpat new file mode 100644 index 0000000..1c353f6 --- /dev/null +++ b/patches/util.ringbuf/util.ringbuf.maxpat @@ -0,0 +1,404 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 880.0, 680.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [8.0, 8.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Bounded ring buffer for messages with random access and dump", + "digest": "Circular message history buffer with get, dump, clear, capacity", + "tags": "utility buffer ring history", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [56.0, 16.0, 30.0, 30.0], + "comment": "message to append (any type, hot)" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "index": 2, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 16.0, 30.0, 30.0], + "comment": "control: get, dump, clear, capacity, size, reset, bypass" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 616.0, 30.0, 30.0], + "comment": "retrieved values (from get or dump)" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 616.0, 30.0, 30.0], + "comment": "status (full, cleared, size N)" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "comment", + "text": "--- INIT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [600.0, 16.0, 80.0, 20.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [600.0, 40.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "patcherargs 16", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [600.0, 64.0, 80.0, 22.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [600.0, 96.0, 42.0, 22.0], + "comment": "R-to-L: init bypass=0 (first), send cap (second)" + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "prepend capacity", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [600.0, 128.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [680.0, 96.0, 25.0, 22.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [680.0, 120.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "comment", + "text": "--- CONTROL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [400.0, 56.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "route bypass", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [400.0, 80.0, 72.0, 22.0], + "comment": "split off bypass, rest passes through to v8" + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 112.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "comment", + "text": "--- BYPASS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 56.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [56.0, 80.0, 42.0, 22.0], + "comment": "R-to-L: get bypass (first), pass msg (second)" + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [56.0, 112.0, 64.0, 22.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": ["int"], + "patching_rect": [56.0, 136.0, 30.0, 22.0], + "comment": "bypass=0 -> gate 1 (store), bypass=1 -> gate 2 (pass)" + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "gate 2 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [56.0, 168.0, 152.0, 22.0], + "comment": "1=store, 2=bypass" + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "comment", + "text": "--- RING BUFFER (v8.codebox) ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [56.0, 208.0, 200.0, 20.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v8.codebox @code \"var buf = [];\\nvar head = 0;\\nvar count = 0;\\nvar cap = 16;\\n\\nfunction capacity(n) {\\n n = Math.max(1, Math.floor(n));\\n if (n !== cap) {\\n cap = n;\\n buf = [];\\n head = 0;\\n count = 0;\\n }\\n}\\n\\nfunction msg_int(v) { doStore([v]); }\\nfunction msg_float(v) { doStore([v]); }\\nfunction list() { doStore(Array.from(arguments)); }\\nfunction anything() {\\n var name = String(this.messagename);\\n if (name === 'get') { get(arguments.length > 0 ? arguments[0] : 0); return; }\\n if (name === 'dump') { dump(); return; }\\n if (name === 'clear') { doClear(); return; }\\n if (name === 'capacity') { capacity(arguments[0]); return; }\\n if (name === 'size') { doSize(); return; }\\n if (name === 'reset') { doReset(); return; }\\n doStore([name].concat(Array.from(arguments)));\\n}\\n\\nfunction doStore(item) {\\n buf[head % cap] = item;\\n head++;\\n var wasFull = (count === cap);\\n if (count < cap) count++;\\n if (count === cap && !wasFull) {\\n this.outlet(1, 'full');\\n }\\n}\\n\\nfunction get(idx) {\\n idx = (idx === undefined) ? 0 : Math.floor(idx);\\n if (idx < 0 || idx >= count) return;\\n var pos = ((head - 1 - idx) % cap + cap) % cap;\\n var item = buf[pos];\\n if (item.length === 1) {\\n this.outlet(0, item[0]);\\n } else {\\n this.outlet(0, item);\\n }\\n}\\n\\nfunction dump() {\\n if (count === 0) return;\\n var start = (count < cap) ? 0 : head % cap;\\n for (var i = 0; i < count; i++) {\\n var pos = (start + i) % cap;\\n var item = buf[pos];\\n if (item.length === 1) {\\n this.outlet(0, item[0]);\\n } else {\\n this.outlet(0, item);\\n }\\n }\\n}\\n\\nfunction doClear() {\\n buf = [];\\n head = 0;\\n count = 0;\\n this.outlet(1, 'cleared');\\n}\\n\\nfunction doSize() {\\n this.outlet(1, ['size', count]);\\n}\\n\\nfunction doReset() {\\n buf = [];\\n head = 0;\\n count = 0;\\n cap = 16;\\n this.outlet(1, 'cleared');\\n}\\n\"", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [56.0, 232.0, 480.0, 340.0] + } + } + ], + "lines": [ + { + "patchline": { + "source": ["obj-1", 0], + "destination": ["obj-16", 0] + } + }, + { + "patchline": { + "source": ["obj-16", 1], + "destination": ["obj-17", 0], + "comment": "get bypass (fires first)" + } + }, + { + "patchline": { + "source": ["obj-16", 0], + "destination": ["obj-19", 1], + "comment": "msg to gate (fires second)" + } + }, + { + "patchline": { + "source": ["obj-17", 0], + "destination": ["obj-18", 0] + } + }, + { + "patchline": { + "source": ["obj-18", 0], + "destination": ["obj-19", 0] + } + }, + { + "patchline": { + "source": ["obj-19", 1], + "destination": ["obj-3", 0], + "comment": "bypass passthrough" + } + }, + { + "patchline": { + "source": ["obj-19", 0], + "destination": ["obj-21", 0], + "comment": "store in ring buffer" + } + }, + { + "patchline": { + "source": ["obj-21", 0], + "destination": ["obj-3", 0], + "comment": "retrieved values" + } + }, + { + "patchline": { + "source": ["obj-21", 1], + "destination": ["obj-4", 0], + "comment": "status" + } + }, + { + "patchline": { + "source": ["obj-2", 0], + "destination": ["obj-13", 0] + } + }, + { + "patchline": { + "source": ["obj-13", 0], + "destination": ["obj-14", 0], + "comment": "bypass value" + } + }, + { + "patchline": { + "source": ["obj-13", 1], + "destination": ["obj-21", 0], + "comment": "all other control msgs to v8" + } + }, + { + "patchline": { + "source": ["obj-6", 0], + "destination": ["obj-7", 0] + } + }, + { + "patchline": { + "source": ["obj-7", 0], + "destination": ["obj-8", 0] + } + }, + { + "patchline": { + "source": ["obj-8", 0], + "destination": ["obj-9", 0], + "comment": "prepend capacity" + } + }, + { + "patchline": { + "source": ["obj-8", 1], + "destination": ["obj-10", 0], + "comment": "init bypass=0" + } + }, + { + "patchline": { + "source": ["obj-9", 0], + "destination": ["obj-21", 0], + "comment": "capacity from init" + } + }, + { + "patchline": { + "source": ["obj-10", 0], + "destination": ["obj-11", 0] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} \ No newline at end of file diff --git a/patches/util.state.latch/README.md b/patches/util.state.latch/README.md new file mode 100644 index 0000000..618a205 --- /dev/null +++ b/patches/util.state.latch/README.md @@ -0,0 +1,174 @@ +# util.state.latch + +Control-rate Schmitt-style latch with dwell time. Suppresses jitter and chatter in noisy control streams by requiring input to remain in a threshold region for a specified dwell period before changing state. Useful for sensors, threshold crossings, and noisy controller data. + +## Inlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | int/float | Numeric input value. Compared against high and low thresholds to determine target state. | +| 2 | symbol (message) | Control messages: `low`, `high`, `dwell`, `init`, `reset`, `bypass`. | + +## Outlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | int | Latched state: `0` or `1`. Output only on state change (duplicates suppressed). | +| 2 | symbol | Events: `rise` (state changed to 1), `fall` (state changed to 0), `pending N` (transition to N requested, awaiting dwell). | +| 3 | symbol | Diagnostics: `value ` echoes each input value. | + +## Arguments + +| Argument | Type | Default | Description | +|----------|------|---------|-------------| +| 1 | float | 0.4 | Low threshold. Input at or below this value requests state 0. | +| 2 | float | 0.6 | High threshold. Input at or above this value requests state 1. | + +``` +util.state.latch --> low=0.4 high=0.6 (default) +util.state.latch 0.3 0.7 --> low=0.3 high=0.7 (wider band) +util.state.latch 0.1 0.9 --> low=0.1 high=0.9 (maximum noise rejection) +``` + +## Messages (Right Inlet) + +### low *float* + +Set the low threshold. Input values at or below this trigger a transition to state 0 (after dwell). + +``` +low 0.2 --> state 0 when input <= 0.2 +``` + +### high *float* + +Set the high threshold. Input values at or above this trigger a transition to state 1 (after dwell). + +``` +high 0.8 --> state 1 when input >= 0.8 +``` + +### dwell *ms* + +Set the dwell time in milliseconds. The input must remain in the threshold region for this duration before the state transition commits. `dwell 0` switches immediately with hysteresis (no timer delay). + +``` +dwell 50 --> require 50ms sustained crossing +dwell 0 --> immediate switching with hysteresis +``` + +### init *0|1* + +Set the internal state without emitting any transition event. Useful for initializing state at patch load. + +``` +init 1 --> set state to 1 silently +``` + +### reset + +Cancel any pending transition, stop the dwell timer, and restore state to 0. + +### bypass *0|1* + +Enable or disable bypass mode. When bypassed, the latch uses a simple `>= high` threshold with no dwell timer and no hysteresis band. Useful for debugging or when clean thresholding without dwell is sufficient. + +``` +bypass 1 --> simple threshold, no dwell +bypass 0 --> normal Schmitt latch behavior (default) +``` + +## Defaults + +| Parameter | Default | +|-----------|---------| +| low | 0.4 (or first creation argument) | +| high | 0.6 (or second creation argument) | +| dwell | 20 ms | +| state | 0 | +| bypass | 0 (off) | + +## Behavior + +### Hysteresis Band + +Values between `low` and `high` are in the neutral (hysteresis) band and do not trigger any state change. This prevents rapid toggling when a noisy signal hovers near a single threshold. + +``` + low=0.4 high=0.6 + --------+----------------+-------- + state 0 | neutral band | state 1 + request | (keep current)| request +``` + +### Dwell Timer + +When input crosses into a threshold region (above high or below low) and the requested state differs from the current state, a dwell timer starts. If the input remains in the threshold region for the full dwell period, the state commits. If the input returns to the neutral band before dwell completes, the pending transition is canceled. + +### Duplicate Suppression + +The latch only outputs on actual state changes. Repeated inputs in the same threshold region do not produce duplicate outputs. + +### State Machine + +1. Input arrives, region detected: above high, below low, or in band +2. Above high -> request state 1; Below low -> request state 0; In band -> cancel pending +3. If requested state differs from current state, store pending and start dwell timer +4. On dwell expiry, read pending value and commit if state still differs +5. Commit: update state, output on outlet 1, emit rise/fall on outlet 2 + +## Example Usage + +### Sensor threshold with noise rejection + +``` +[analog-sensor-input] +| +[util.state.latch 0.3 0.7] +| +[sel 1] +| +[trigger-action] +``` + +### Dynamic threshold adjustment + +``` +[sensor-input] [number: low] [number: high] [number: dwell] +| | | | +| [prepend low] [prepend high] [prepend dwell] +| | | | +[util.state.latch 0.4 0.6 ---------=----------------] +| +[gate] +``` + +### Immediate mode (dwell 0) + +For hysteresis without dwell delay. State changes immediately when crossing thresholds but still uses the band to reject noise. + +``` +[noisy-controller] +| [message: dwell 0] +| | +[util.state.latch --] +| +[toggle] +``` + +## Implementation Notes + +- Uses `#0`-scoped internal state (`v`, `s`, `r` objects) for instance isolation. Multiple instances in the same patch do not interfere. +- All message ordering is enforced via `trigger` objects throughout. +- The dwell timer uses Max's `delay` object. Sending `stop` to the delay cancels a pending transition. +- Region detection uses a comparator chain: `>=high * 2 + <=low` encodes region as 0 (band), 1 (below low), or 2 (above high). +- Pending state is stored as -1 (no pending), 0, or 1. + +## See Also + +- **change** -- suppress repeated identical values (not threshold-based) +- **thresh~** -- signal-rate hysteresis threshold detector +- **edge~** -- detect signal-rate rising/falling edges +- **sig.schmitt.edge~** -- signal-rate Schmitt trigger with edge bangs (companion utility) +- **onebang** -- pass one bang then block until reset +- **delay** -- schedule a bang after N ms diff --git a/patches/util.state.latch/util.state.latch-help.maxpat b/patches/util.state.latch/util.state.latch-help.maxpat new file mode 100644 index 0000000..1c1a317 --- /dev/null +++ b/patches/util.state.latch/util.state.latch-help.maxpat @@ -0,0 +1,433 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 8, + "minor": 5, + "revision": 5, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 800.0, 600.0], + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontname": "Arial", + "gridsize": [15.0, 15.0], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "util.state.latch", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 15.0, 200.0, 20.0], + "fontsize": 16.0, + "fontface": 1 + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Schmitt-style latch with dwell time. Suppresses jitter/chatter in noisy control streams by requiring the input to remain in a threshold region for a specified dwell time before changing state.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 45.0, 760.0, 20.0] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "comment", + "text": "-- basic usage --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 75.0, 120.0, 20.0], + "fontface": 1 + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "flonum", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [30.0, 105.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "text": "util.state.latch", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [30.0, 135.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [30.0, 180.0, 50.0, 22.0] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "print state-events", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [105.0, 180.0, 108.0, 22.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "print diagnostics", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [225.0, 180.0, 102.0, 22.0] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "comment", + "text": "Send a float 0.-1. Input >= 0.6 for 20ms -> state 1. Input <= 0.4 for 20ms -> state 0.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [120.0, 105.0, 600.0, 20.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "comment", + "text": "-- custom thresholds --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 225.0, 150.0, 20.0], + "fontface": 1 + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "flonum", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [30.0, 255.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "util.state.latch 0.3 0.7", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [30.0, 285.0, 136.0, 22.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [30.0, 330.0, 50.0, 22.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "comment", + "text": "Creation args set low=0.3 high=0.7. Wider hysteresis band = more noise rejection.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [120.0, 255.0, 600.0, 20.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "comment", + "text": "-- control messages --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 375.0, 150.0, 20.0], + "fontface": 1 + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "flonum", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [30.0, 405.0, 60.0, 22.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "low 0.2", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [150.0, 405.0, 52.0, 22.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "message", + "text": "high 0.8", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [210.0, 405.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "message", + "text": "dwell 50", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [275.0, 405.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "message", + "text": "dwell 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [340.0, 405.0, 48.0, 22.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [400.0, 405.0, 37.0, 22.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "message", + "text": "bypass 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [450.0, 405.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "message", + "text": "bypass 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [515.0, 405.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "message", + "text": "init 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [580.0, 405.0, 41.0, 22.0] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "text": "util.state.latch", + "numinlets": 2, + "numoutlets": 3, + "outlettype": ["", "", ""], + "patching_rect": [30.0, 450.0, 555.0, 22.0] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [30.0, 495.0, 50.0, 22.0] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "print ctrl-events", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [300.0, 495.0, 100.0, 22.0] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "comment", + "text": "dwell 0 = immediate switching with hysteresis (no timer). bypass = simple >= high threshold.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [120.0, 375.0, 600.0, 20.0] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "comment", + "text": "Outlet 1: latched state (0/1) Outlet 2: events (rise/fall/pending) Outlet 3: diagnostics", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [15.0, 540.0, 600.0, 20.0], + "fontface": 2 + } + } + ], + "lines": [ + { + "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] + } + }, + { + "patchline": { + "source": ["obj-5", 2], + "destination": ["obj-8", 0] + } + }, + { + "patchline": { + "source": ["obj-11", 0], + "destination": ["obj-12", 0] + } + }, + { + "patchline": { + "source": ["obj-12", 0], + "destination": ["obj-13", 0] + } + }, + { + "patchline": { + "source": ["obj-16", 0], + "destination": ["obj-25", 0] + } + }, + { + "patchline": { + "source": ["obj-17", 0], + "destination": ["obj-25", 1] + } + }, + { + "patchline": { + "source": ["obj-18", 0], + "destination": ["obj-25", 1] + } + }, + { + "patchline": { + "source": ["obj-19", 0], + "destination": ["obj-25", 1] + } + }, + { + "patchline": { + "source": ["obj-20", 0], + "destination": ["obj-25", 1] + } + }, + { + "patchline": { + "source": ["obj-21", 0], + "destination": ["obj-25", 1] + } + }, + { + "patchline": { + "source": ["obj-22", 0], + "destination": ["obj-25", 1] + } + }, + { + "patchline": { + "source": ["obj-23", 0], + "destination": ["obj-25", 1] + } + }, + { + "patchline": { + "source": ["obj-24", 0], + "destination": ["obj-25", 1] + } + }, + { + "patchline": { + "source": ["obj-25", 0], + "destination": ["obj-26", 0] + } + }, + { + "patchline": { + "source": ["obj-25", 1], + "destination": ["obj-27", 0] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} diff --git a/patches/util.state.latch/util.state.latch.maxpat b/patches/util.state.latch/util.state.latch.maxpat new file mode 100644 index 0000000..f4c5ce8 --- /dev/null +++ b/patches/util.state.latch/util.state.latch.maxpat @@ -0,0 +1,3402 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 8, + "minor": 5, + "revision": 5, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 85.0, + 104.0, + 1392.0, + 800.0 + ], + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontname": "Arial Bold", + "gridsize": [ + 8.0, + 8.0 + ], + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 16.0, + 30.0, + 30.0 + ], + "index": 1, + "comment": "numeric input (int/float)" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 768.0, + 16.0, + 30.0, + 30.0 + ], + "index": 2, + "comment": "control: low high dwell init reset bypass" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 496.0, + 752.0, + 30.0, + 30.0 + ], + "index": 1, + "comment": "latched state (0 or 1)" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 640.0, + 752.0, + 30.0, + 30.0 + ], + "index": 2, + "comment": "events: rise fall pending state" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "outlet", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 784.0, + 752.0, + 30.0, + 30.0 + ], + "index": 3, + "comment": "diagnostics" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "comment", + "text": "== CREATION ARGS ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 1048.0, + 16.0, + 120.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "patcherargs 0.4 0.6", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 1048.0, + 40.0, + 112.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "unpack f f", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "float" + ], + "patching_rect": [ + 1048.0, + 72.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "v #0_low", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1048.0, + 104.0, + 52.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "v #0_high", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1104.0, + 104.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "comment", + "text": "== DEFAULTS INIT ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 1048.0, + 136.0, + 120.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 1048.0, + 160.0, + 52.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "text": "t b b b b b b", + "numinlets": 1, + "numoutlets": 6, + "outlettype": [ + "bang", + "bang", + "bang", + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 1048.0, + 192.0, + 272.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "message", + "text": "0.4", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1048.0, + 224.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "v #0_low", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1048.0, + 256.0, + 52.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "message", + "text": "0.6", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1104.0, + 224.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "text": "v #0_high", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1104.0, + 256.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "message", + "text": "20", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1160.0, + 224.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "v #0_dwell", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1160.0, + 256.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1216.0, + 224.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "text": "v #0_state", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1216.0, + 256.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1272.0, + 224.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1272.0, + 256.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1328.0, + 224.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1328.0, + 256.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "comment", + "text": "== CONTROL INLET ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 768.0, + 56.0, + 120.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "route low high dwell init reset bypass", + "numinlets": 1, + "numoutlets": 7, + "outlettype": [ + "", + "", + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 768.0, + 80.0, + 200.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "newobj", + "text": "v #0_low", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 768.0, + 112.0, + 52.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "v #0_high", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 808.0, + 144.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "text": "v #0_dwell", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 848.0, + 176.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "comment", + "text": "init: set state without event", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 928.0, + 208.0, + 160.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "newobj", + "text": "v #0_state", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 888.0, + 208.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "comment", + "text": "reset: cancel + restore state=0", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 768.0, + 240.0, + 176.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 768.0, + 264.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 768.0, + 296.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "newobj", + "text": "s #0_delay", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 768.0, + 328.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 832.0, + 296.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 832.0, + 328.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 896.0, + 296.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "newobj", + "text": "v #0_state", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 896.0, + 328.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 928.0, + 264.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "comment", + "text": "== INPUT PATH ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 56.0, + 104.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "newobj", + "text": "t f f", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "float" + ], + "patching_rect": [ + 56.0, + 80.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "newobj", + "text": "prepend value", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 120.0, + 112.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "newobj", + "text": "s #0_diag", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 120.0, + 144.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "comment", + "text": "-- bypass check --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 160.0, + 176.0, + 104.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-47", + "maxclass": "newobj", + "text": "t f b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "bang" + ], + "patching_rect": [ + 56.0, + 176.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-48", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 112.0, + 208.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-49", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 112.0, + 240.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "newobj", + "text": "gate 2", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 272.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "comment", + "text": "gate: 1=normal, 2=bypass", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 144.0, + 272.0, + 144.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "comment", + "text": "-- bypass: immediate >= high --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 240.0, + 304.0, + 176.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "newobj", + "text": "t f b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "float", + "bang" + ], + "patching_rect": [ + 112.0, + 304.0, + 48.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "newobj", + "text": "v #0_high", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 144.0, + 336.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "newobj", + "text": ">= 0.", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 112.0, + 368.0, + 48.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "newobj", + "text": "s #0_commit", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 112.0, + 400.0, + 68.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-57", + "maxclass": "comment", + "text": "== NORMAL PATH: region detect ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 432.0, + 192.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-58", + "maxclass": "newobj", + "text": "t f f b b", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "float", + "float", + "bang", + "bang" + ], + "patching_rect": [ + 56.0, + 456.0, + 200.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-59", + "maxclass": "newobj", + "text": ">= 0.", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 56.0, + 488.0, + 40.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-60", + "maxclass": "newobj", + "text": "<= 0.", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 120.0, + 488.0, + 40.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-61", + "maxclass": "newobj", + "text": "v #0_high", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 176.0, + 488.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-62", + "maxclass": "newobj", + "text": "v #0_low", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 240.0, + 488.0, + 52.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-63", + "maxclass": "comment", + "text": "region: 2=above high, 1=below low, 0=band", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 184.0, + 520.0, + 248.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-64", + "maxclass": "newobj", + "text": "* 2", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 56.0, + 520.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-65", + "maxclass": "newobj", + "text": "+", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 56.0, + 552.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-66", + "maxclass": "newobj", + "text": "sel 1 2", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "" + ], + "patching_rect": [ + 56.0, + 584.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-67", + "maxclass": "comment", + "text": "1=below low, 2=above high, else=band", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 152.0, + 584.0, + 216.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-68", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 616.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-69", + "maxclass": "newobj", + "text": "s #0_request", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 648.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-70", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 96.0, + 616.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-71", + "maxclass": "newobj", + "text": "s #0_request", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 96.0, + 648.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-72", + "maxclass": "comment", + "text": "-- in band: cancel pending --", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 144.0, + 616.0, + 168.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-73", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 144.0, + 648.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-74", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 144.0, + 680.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-75", + "maxclass": "newobj", + "text": "s #0_delay", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 144.0, + 712.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-76", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 208.0, + 680.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-77", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 208.0, + 712.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-78", + "maxclass": "message", + "text": "pending 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 272.0, + 680.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-79", + "maxclass": "newobj", + "text": "s #0_events", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 272.0, + 712.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-80", + "maxclass": "comment", + "text": "== REQUEST HANDLER ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 432.0, + 136.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-81", + "maxclass": "newobj", + "text": "r #0_request", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 456.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-82", + "maxclass": "newobj", + "text": "t i i b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "int", + "int", + "bang" + ], + "patching_rect": [ + 400.0, + 488.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-83", + "maxclass": "newobj", + "text": "v #0_state", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 456.0, + 520.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-84", + "maxclass": "newobj", + "text": "!=", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 504.0, + 520.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-85", + "maxclass": "comment", + "text": "skip if already in requested state", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 552.0, + 520.0, + 192.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-86", + "maxclass": "newobj", + "text": "gate", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 552.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-87", + "maxclass": "newobj", + "text": "t i i b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "int", + "int", + "bang" + ], + "patching_rect": [ + 400.0, + 584.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-88", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 456.0, + 616.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-89", + "maxclass": "newobj", + "text": "v #0_dwell", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 504.0, + 616.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-90", + "maxclass": "newobj", + "text": "sel 0", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 504.0, + 648.0, + 40.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-91", + "maxclass": "comment", + "text": "dwell=0: immediate", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 560.0, + 648.0, + 112.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-92", + "maxclass": "newobj", + "text": "s #0_do_commit", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 504.0, + 680.0, + 84.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-93", + "maxclass": "newobj", + "text": "s #0_delay", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 536.0, + 712.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-94", + "maxclass": "newobj", + "text": "prepend pending", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 616.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-95", + "maxclass": "newobj", + "text": "s #0_events", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 648.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-96", + "maxclass": "comment", + "text": "== DWELL TIMER ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 744.0, + 112.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-97", + "maxclass": "newobj", + "text": "r #0_delay", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 768.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-98", + "maxclass": "newobj", + "text": "delay 20", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 400.0, + 800.0, + 52.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-99", + "maxclass": "newobj", + "text": "s #0_do_commit", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 832.0, + 84.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-100", + "maxclass": "comment", + "text": "== COMMIT: read pending + output ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 496.0, + 432.0, + 208.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-101", + "maxclass": "newobj", + "text": "r #0_do_commit", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 696.0, + 456.0, + 84.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-102", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 696.0, + 488.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-103", + "maxclass": "newobj", + "text": "s #0_commit", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 696.0, + 520.0, + 68.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-104", + "maxclass": "comment", + "text": "== COMMIT OUTPUT ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 496.0, + 552.0, + 120.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-105", + "maxclass": "newobj", + "text": "r #0_commit", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 496.0, + 576.0, + 68.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-106", + "maxclass": "newobj", + "text": "t i i b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "int", + "int", + "bang" + ], + "patching_rect": [ + 496.0, + 608.0, + 120.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-107", + "maxclass": "newobj", + "text": "v #0_state", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 600.0, + 640.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-108", + "maxclass": "newobj", + "text": "!=", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 544.0, + 640.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-109", + "maxclass": "comment", + "text": "suppress duplicate state", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 672.0, + 640.0, + 136.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-110", + "maxclass": "newobj", + "text": "gate", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 496.0, + 672.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-111", + "maxclass": "newobj", + "text": "t i i i i", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "int", + "int", + "int", + "int" + ], + "patching_rect": [ + 496.0, + 704.0, + 200.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-112", + "maxclass": "newobj", + "text": "s #0_out", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 496.0, + 736.0, + 48.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-113", + "maxclass": "newobj", + "text": "sel 0 1", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "" + ], + "patching_rect": [ + 560.0, + 736.0, + 56.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-114", + "maxclass": "message", + "text": "fall", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 560.0, + 768.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-115", + "maxclass": "message", + "text": "rise", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 600.0, + 768.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-116", + "maxclass": "newobj", + "text": "s #0_events", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 560.0, + 800.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-117", + "maxclass": "newobj", + "text": "s #0_events", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 600.0, + 800.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-118", + "maxclass": "message", + "text": "-1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 640.0, + 736.0, + 32.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-119", + "maxclass": "newobj", + "text": "v #0_pending", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 640.0, + 768.0, + 72.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-120", + "maxclass": "newobj", + "text": "v #0_state", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 680.0, + 736.0, + 60.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-121", + "maxclass": "comment", + "text": "== OUTPUT ==", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 496.0, + 840.0, + 80.0, + 18.0 + ] + } + }, + { + "box": { + "id": "obj-122", + "maxclass": "newobj", + "text": "r #0_out", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 496.0, + 864.0, + 48.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-123", + "maxclass": "newobj", + "text": "r #0_events", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 640.0, + 864.0, + 64.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-124", + "maxclass": "newobj", + "text": "r #0_diag", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 784.0, + 864.0, + 56.0, + 20.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-43", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-27", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-43", + 0 + ], + "destination": [ + "obj-47", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-43", + 1 + ], + "destination": [ + "obj-44", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-44", + 0 + ], + "destination": [ + "obj-45", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-47", + 0 + ], + "destination": [ + "obj-50", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-47", + 1 + ], + "destination": [ + "obj-48", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-48", + 0 + ], + "destination": [ + "obj-49", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-49", + 0 + ], + "destination": [ + "obj-50", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-50", + 0 + ], + "destination": [ + "obj-58", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-50", + 1 + ], + "destination": [ + "obj-53", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-53", + 0 + ], + "destination": [ + "obj-55", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-53", + 1 + ], + "destination": [ + "obj-54", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-54", + 0 + ], + "destination": [ + "obj-55", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-55", + 0 + ], + "destination": [ + "obj-56", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-58", + 0 + ], + "destination": [ + "obj-59", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-58", + 1 + ], + "destination": [ + "obj-60", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-58", + 2 + ], + "destination": [ + "obj-61", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-58", + 3 + ], + "destination": [ + "obj-62", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-61", + 0 + ], + "destination": [ + "obj-59", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-62", + 0 + ], + "destination": [ + "obj-60", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-59", + 0 + ], + "destination": [ + "obj-64", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-60", + 0 + ], + "destination": [ + "obj-65", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-64", + 0 + ], + "destination": [ + "obj-65", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-65", + 0 + ], + "destination": [ + "obj-66", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-66", + 0 + ], + "destination": [ + "obj-68", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-66", + 1 + ], + "destination": [ + "obj-70", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-66", + 2 + ], + "destination": [ + "obj-73", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-68", + 0 + ], + "destination": [ + "obj-69", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-70", + 0 + ], + "destination": [ + "obj-71", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-73", + 0 + ], + "destination": [ + "obj-74", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-73", + 1 + ], + "destination": [ + "obj-76", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-73", + 2 + ], + "destination": [ + "obj-78", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-74", + 0 + ], + "destination": [ + "obj-75", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-76", + 0 + ], + "destination": [ + "obj-77", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-78", + 0 + ], + "destination": [ + "obj-79", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-81", + 0 + ], + "destination": [ + "obj-82", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-82", + 0 + ], + "destination": [ + "obj-86", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-83", + 0 + ], + "destination": [ + "obj-84", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-84", + 0 + ], + "destination": [ + "obj-86", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-86", + 0 + ], + "destination": [ + "obj-87", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-87", + 0 + ], + "destination": [ + "obj-94", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-87", + 1 + ], + "destination": [ + "obj-88", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-87", + 2 + ], + "destination": [ + "obj-89", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-89", + 0 + ], + "destination": [ + "obj-90", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-90", + 0 + ], + "destination": [ + "obj-92", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-90", + 1 + ], + "destination": [ + "obj-93", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-94", + 0 + ], + "destination": [ + "obj-95", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-97", + 0 + ], + "destination": [ + "obj-98", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-98", + 0 + ], + "destination": [ + "obj-99", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-101", + 0 + ], + "destination": [ + "obj-102", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-102", + 0 + ], + "destination": [ + "obj-103", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-105", + 0 + ], + "destination": [ + "obj-106", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-106", + 0 + ], + "destination": [ + "obj-110", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-106", + 1 + ], + "destination": [ + "obj-108", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-106", + 2 + ], + "destination": [ + "obj-107", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-107", + 0 + ], + "destination": [ + "obj-108", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-108", + 0 + ], + "destination": [ + "obj-110", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-110", + 0 + ], + "destination": [ + "obj-111", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-111", + 0 + ], + "destination": [ + "obj-112", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-111", + 1 + ], + "destination": [ + "obj-113", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-111", + 2 + ], + "destination": [ + "obj-118", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-111", + 3 + ], + "destination": [ + "obj-120", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-113", + 0 + ], + "destination": [ + "obj-114", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-113", + 1 + ], + "destination": [ + "obj-115", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-114", + 0 + ], + "destination": [ + "obj-116", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-115", + 0 + ], + "destination": [ + "obj-117", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-118", + 0 + ], + "destination": [ + "obj-119", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-122", + 0 + ], + "destination": [ + "obj-3", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-123", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-124", + 0 + ], + "destination": [ + "obj-5", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 1 + ], + "destination": [ + "obj-10", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-12", + 0 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 1 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 2 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 3 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 4 + ], + "destination": [ + "obj-22", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 5 + ], + "destination": [ + "obj-24", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-14", + 0 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-16", + 0 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-18", + 0 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-20", + 0 + ], + "destination": [ + "obj-21", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-23", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 0 + ], + "destination": [ + "obj-25", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 0 + ], + "destination": [ + "obj-28", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 1 + ], + "destination": [ + "obj-29", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 2 + ], + "destination": [ + "obj-30", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 3 + ], + "destination": [ + "obj-32", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 4 + ], + "destination": [ + "obj-34", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 5 + ], + "destination": [ + "obj-41", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-34", + 0 + ], + "destination": [ + "obj-35", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-34", + 1 + ], + "destination": [ + "obj-37", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-34", + 2 + ], + "destination": [ + "obj-39", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-35", + 0 + ], + "destination": [ + "obj-36", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-37", + 0 + ], + "destination": [ + "obj-38", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-39", + 0 + ], + "destination": [ + "obj-40", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-82", + 2 + ], + "destination": [ + "obj-83", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-82", + 1 + ], + "destination": [ + "obj-84", + 0 + ] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} diff --git a/patches/util.throttle/README.md b/patches/util.throttle/README.md new file mode 100644 index 0000000..db7e923 --- /dev/null +++ b/patches/util.throttle/README.md @@ -0,0 +1,180 @@ +# util.throttle + +Rate-limit arbitrary messages: pass at most one message per interval. When messages arrive faster than the interval allows, a configurable policy determines what happens to messages received during cooldown. Three policies are available: **drop** (discard silently), **latest** (keep only the newest, emit when interval expires), and **queue** (buffer messages, emit one per interval). + +## Inlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | any (int, float, list, symbol) | Message to throttle. The first message passes immediately; subsequent messages during cooldown are handled according to the active policy. | +| 2 | symbol (message) | Control messages: `interval`, `policy`, `capacity`, `flush`, `reset`, `bypass`. | + +## Outlets + +| Index | Type | Description | +|-------|------|-------------| +| 1 | any | Throttled output. Messages that pass through the throttle (immediately or after cooldown). | +| 2 | symbol | Status messages: `passed` (message output immediately), `dropped` (message discarded), `queued` (message buffered), `flushed` (pending messages emitted via flush), `canceled` (pending messages cleared). | + +## Arguments + +| Argument | Type | Default | Description | +|----------|------|---------|-------------| +| 1 | int/float | 100 | Initial throttle interval in milliseconds. | + +``` +util.throttle --> 100ms interval (default) +util.throttle 200 --> 200ms interval +util.throttle 50 --> 50ms interval +``` + +## Messages (Right Inlet) + +### interval *ms* + +Set the minimum time between outputs in milliseconds. Takes effect on the next cooldown cycle. + +``` +interval 100 --> 100ms between outputs +interval 500 --> 500ms between outputs +``` + +### policy *name* + +Set the throttle policy. Determines what happens to messages received during cooldown. + +``` +policy drop --> discard messages during cooldown (default) +policy latest --> keep only the newest, emit when interval expires +policy queue --> buffer messages, emit one per interval +``` + +### capacity *N* + +Set the maximum queue size for `queue` policy. When the queue is full, new messages are silently dropped. Has no effect on `drop` or `latest` policies. + +``` +capacity 4 --> max 4 queued messages +capacity 16 --> max 16 queued messages (default: 8) +``` + +### flush + +Immediately emit all pending messages (latest buffer and/or queued messages) and clear the cooldown timer. If nothing is pending, flush has no effect. Outputs `flushed` from outlet 2. + +### reset + +Clear all pending/queued messages, stop the timer, and restore default parameter values (`policy drop`, `capacity 8`). The interval is not changed by reset. + +### bypass *0|1* + +Enable or disable bypass mode. When bypassed, all input messages pass directly to outlet 1 without throttling. Enabling bypass (`bypass 1`) also cancels any pending messages and stops the cooldown timer. + +``` +bypass 1 --> cancel pending, pass all messages through immediately +bypass 0 --> resume throttling (default) +``` + +## Defaults + +| Parameter | Default | +|-----------|---------| +| interval | 100 ms (or creation argument) | +| policy | drop | +| capacity | 8 | +| bypass | 0 (off) | + +## Policies + +| Policy | During Cooldown | On Timer Expire | +|--------|----------------|-----------------| +| `drop` | Message discarded, status `dropped` | Cooldown cleared, ready for next message | +| `latest` | Message stored (replaces previous), status `queued` | Stored message emitted, timer restarts if new data arrived | +| `queue` | Message appended to queue (up to capacity), status `queued` | Next queued message emitted, timer restarts until queue empty | + +## Example Usage + +### Rate-limit rapid UI changes + +A number box sending values as the user drags. At most one value passes per 100ms. + +``` +[number] +| +[util.throttle 100] +| +[print throttled-value] +``` + +### Latest policy for smooth parameter control + +Keep only the most recent value during cooldown. Ensures the final resting value is always output. + +``` +[slider] +| +[util.throttle 50] +| [message: policy latest] +[your-synth-param] | + [util.throttle] (right inlet) +``` + +### Queue policy for ordered event processing + +Process events one at a time with guaranteed ordering and no data loss. + +``` +[metro 10] +| +[counter 0 100] +| +[util.throttle 200] +| [message: policy queue] +[print event] | + [util.throttle] (right inlet) +``` + +### Flush pending on demand + +Use a long interval, accumulate with latest policy, then flush when needed. + +``` +[number] [button] +| | +| [message: flush] +| | +[util.throttle 2000 ----] +| +[print result] +``` + +### Dynamic interval adjustment + +Change the throttle interval on the fly based on context. + +``` +[number] [number] +| | +| [prepend interval] +| | +[util.throttle 100 ----] +| +[print result] +``` + +## Implementation Notes + +- Uses `#0`-scoped internal state (`v`, `s`, `r`, `coll` objects) for instance isolation. Multiple instances in the same patch do not interfere. +- All message types (int, float, list, symbol) are preserved through the throttle buffer (`zl reg` for latest, `coll` for queue). +- Deterministic message ordering is enforced via `trigger` objects throughout. +- The cooldown timer uses Max's `delay` object. Sending `stop` cancels a pending bang. +- Queue overflow (when queue reaches capacity) silently drops new messages. The queue uses FIFO ordering with `coll` renumbering. + +## See Also + +- **speedlim** -- built-in rate limiter (drop policy only, no queue or latest) +- **util.debounce** -- delay output until input quiet for N ms (complementary to throttle) +- **delay** -- schedule a bang after N ms +- **pipe** -- delay any message by N ms (does not rate-limit) +- **change** -- suppress repeated identical values (not time-based) +- **onebang** -- pass one bang then block until reset diff --git a/patches/util.throttle/util.throttle-help.maxpat b/patches/util.throttle/util.throttle-help.maxpat new file mode 100644 index 0000000..f373df8 --- /dev/null +++ b/patches/util.throttle/util.throttle-help.maxpat @@ -0,0 +1,791 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [100.0, 100.0, 760.0, 720.0], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 12.0, + "default_fontface": 0, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [8.0, 8.0], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Help patch for util.throttle", + "digest": "Demonstrates throttle abstraction usage", + "tags": "help utility throttle", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "comment", + "text": "util.throttle", + "fontface": 1, + "fontsize": 16.0, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 16.0, 160.0, 24.0] + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "comment", + "text": "Rate-limit messages: pass at most one per interval. Three policies control what happens during cooldown: drop, latest, queue.", + "linecount": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 42.0, 500.0, 34.0] + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "comment", + "text": "--- DROP POLICY (DEFAULT) ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 88.0, 200.0, 20.0] + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "comment", + "text": "First message passes, rest dropped during 200ms cooldown.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 108.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "util.throttle 200", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 168.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 200.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "print status-drop", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [96.0, 200.0, 100.0, 22.0] + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "comment", + "text": "--- LATEST POLICY ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 240.0, 150.0, 20.0] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "comment", + "text": "Most recent message during cooldown is emitted when interval expires.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 260.0, 450.0, 20.0] + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 288.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "util.throttle 200", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 344.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 376.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "message", + "text": "policy latest", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [80.0, 288.0, 76.0, 22.0] + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [80.0, 264.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "comment", + "text": "--- QUEUE POLICY ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 88.0, 140.0, 20.0] + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "comment", + "text": "Messages queued during cooldown, emitted one per interval.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 108.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [280.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "text": "util.throttle 300", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [280.0, 200.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [280.0, 232.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "message", + "text": "policy queue", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [344.0, 136.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [344.0, 112.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "print status-queue", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [360.0, 232.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "comment", + "text": "--- FLUSH ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 280.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "comment", + "text": "Immediately emit all pending messages.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 300.0, 300.0, 20.0] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [280.0, 328.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "newobj", + "text": "util.throttle 2000", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [280.0, 392.0, 104.0, 22.0] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [280.0, 424.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "message", + "text": "flush", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [344.0, 328.0, 36.0, 22.0] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "message", + "text": "policy latest", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [384.0, 328.0, 76.0, 22.0] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": ["bang"], + "patching_rect": [384.0, 304.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "comment", + "text": "--- BYPASS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 424.0, 100.0, 20.0] + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "comment", + "text": "Toggle bypass for immediate passthrough.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 444.0, 300.0, 20.0] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 472.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "newobj", + "text": "util.throttle 500", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [16.0, 536.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [16.0, 568.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "toggle", + "numinlets": 1, + "numoutlets": 1, + "outlettype": ["int"], + "patching_rect": [80.0, 472.0, 24.0, 24.0] + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "newobj", + "text": "prepend bypass", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [80.0, 504.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "comment", + "text": "--- LIST / SYMBOL INPUT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 464.0, 180.0, 20.0] + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "comment", + "text": "Any message type is preserved through throttle.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 484.0, 350.0, 20.0] + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "message", + "text": "hello world", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [280.0, 512.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "message", + "text": "1 2 3", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [356.0, 512.0, 40.0, 22.0] + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "newobj", + "text": "util.throttle 150", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [280.0, 552.0, 96.0, 22.0] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "newobj", + "text": "print throttled-msg", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [280.0, 584.0, 108.0, 22.0] + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "comment", + "text": "--- CONTROL MESSAGES (right inlet) ---\n\ninterval - set minimum time between outputs\npolicy drop - silently drop during cooldown (default)\npolicy latest - keep newest, emit when interval expires\npolicy queue - enqueue, emit one per interval\ncapacity - max queue size (default 8)\nflush - emit all pending immediately\nreset - clear state, restore defaults\nbypass <0|1> - passthrough mode", + "linecount": 10, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [480.0, 424.0, 260.0, 152.0] + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "comment", + "text": "Argument sets default interval in ms (default 100).", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [16.0, 624.0, 400.0, 20.0] + } + }, + { + "box": { + "id": "obj-47", + "maxclass": "comment", + "text": "--- DYNAMIC INTERVAL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [520.0, 88.0, 160.0, 20.0] + } + }, + { + "box": { + "id": "obj-48", + "maxclass": "comment", + "text": "Change interval on the fly.", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [520.0, 108.0, 200.0, 20.0] + } + }, + { + "box": { + "id": "obj-49", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [520.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "newobj", + "text": "util.throttle", + "numinlets": 2, + "numoutlets": 2, + "outlettype": ["", ""], + "patching_rect": [520.0, 200.0, 72.0, 22.0] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [520.0, 232.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "number", + "numinlets": 1, + "numoutlets": 2, + "outlettype": ["", "bang"], + "patching_rect": [608.0, 136.0, 56.0, 22.0] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "newobj", + "text": "prepend interval", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [608.0, 168.0, 88.0, 22.0] + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "message", + "text": "reset", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [""], + "patching_rect": [704.0, 168.0, 40.0, 22.0] + } + } + ], + "lines": [ + { + "patchline": { + "source": ["obj-5", 0], + "destination": ["obj-6", 0] + } + }, + { + "patchline": { + "source": ["obj-6", 0], + "destination": ["obj-7", 0] + } + }, + { + "patchline": { + "source": ["obj-6", 1], + "destination": ["obj-8", 0] + } + }, + { + "patchline": { + "source": ["obj-11", 0], + "destination": ["obj-12", 0] + } + }, + { + "patchline": { + "source": ["obj-12", 0], + "destination": ["obj-13", 0] + } + }, + { + "patchline": { + "source": ["obj-15", 0], + "destination": ["obj-14", 0] + } + }, + { + "patchline": { + "source": ["obj-14", 0], + "destination": ["obj-12", 1] + } + }, + { + "patchline": { + "source": ["obj-18", 0], + "destination": ["obj-19", 0] + } + }, + { + "patchline": { + "source": ["obj-19", 0], + "destination": ["obj-20", 0] + } + }, + { + "patchline": { + "source": ["obj-19", 1], + "destination": ["obj-23", 0] + } + }, + { + "patchline": { + "source": ["obj-22", 0], + "destination": ["obj-21", 0] + } + }, + { + "patchline": { + "source": ["obj-21", 0], + "destination": ["obj-19", 1] + } + }, + { + "patchline": { + "source": ["obj-26", 0], + "destination": ["obj-27", 0] + } + }, + { + "patchline": { + "source": ["obj-27", 0], + "destination": ["obj-28", 0] + } + }, + { + "patchline": { + "source": ["obj-29", 0], + "destination": ["obj-27", 1] + } + }, + { + "patchline": { + "source": ["obj-31", 0], + "destination": ["obj-30", 0] + } + }, + { + "patchline": { + "source": ["obj-30", 0], + "destination": ["obj-27", 1] + } + }, + { + "patchline": { + "source": ["obj-34", 0], + "destination": ["obj-35", 0] + } + }, + { + "patchline": { + "source": ["obj-35", 0], + "destination": ["obj-36", 0] + } + }, + { + "patchline": { + "source": ["obj-37", 0], + "destination": ["obj-38", 0] + } + }, + { + "patchline": { + "source": ["obj-38", 0], + "destination": ["obj-35", 1] + } + }, + { + "patchline": { + "source": ["obj-41", 0], + "destination": ["obj-43", 0] + } + }, + { + "patchline": { + "source": ["obj-42", 0], + "destination": ["obj-43", 0] + } + }, + { + "patchline": { + "source": ["obj-43", 0], + "destination": ["obj-44", 0] + } + }, + { + "patchline": { + "source": ["obj-49", 0], + "destination": ["obj-50", 0] + } + }, + { + "patchline": { + "source": ["obj-50", 0], + "destination": ["obj-51", 0] + } + }, + { + "patchline": { + "source": ["obj-52", 0], + "destination": ["obj-53", 0] + } + }, + { + "patchline": { + "source": ["obj-53", 0], + "destination": ["obj-50", 1] + } + }, + { + "patchline": { + "source": ["obj-54", 0], + "destination": ["obj-50", 1] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +} \ No newline at end of file diff --git a/patches/util.throttle/util.throttle.maxpat b/patches/util.throttle/util.throttle.maxpat new file mode 100644 index 0000000..e3f2367 --- /dev/null +++ b/patches/util.throttle/util.throttle.maxpat @@ -0,0 +1,5271 @@ +{ + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 0, + "revision": 0, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ + 100.0, + 100.0, + 1300.0, + 760.0 + ], + "bglocked": 0, + "openinpresentation": 0, + "default_fontsize": 10.0, + "default_fontface": 1, + "default_fontname": "Arial", + "gridonopen": 1, + "gridsize": [ + 8.0, + 8.0 + ], + "gridsnaponopen": 1, + "objectsnaponopen": 1, + "statusbarvisible": 2, + "toolbarvisible": 1, + "lefttoolbarpinned": 0, + "toptoolbarpinned": 0, + "righttoolbarpinned": 0, + "bottomtoolbarpinned": 0, + "toolbars_unpinned_last_save": 0, + "tallnewobj": 0, + "boxanimatetime": 200, + "enablehscroll": 1, + "enablevscroll": 1, + "devicewidth": 0.0, + "description": "Rate-limit messages with selectable policy (drop, latest, queue)", + "digest": "Message throttle with drop, latest, and queue policies", + "tags": "utility throttle rate-limit speedlim", + "style": "", + "subpatcher_template": "", + "assistshowspatchername": 0, + "boxes": [ + { + "box": { + "id": "obj-1", + "maxclass": "inlet", + "index": 1, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "message to throttle (any type)" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "inlet", + "index": 2, + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 16.0, + 30.0, + 30.0 + ], + "comment": "control: interval, policy, capacity, flush, reset, bypass" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "outlet", + "index": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 712.0, + 30.0, + 30.0 + ], + "comment": "throttled output" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "outlet", + "index": 2, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 648.0, + 712.0, + 30.0, + 30.0 + ], + "comment": "status (passed, dropped, queued, flushed)" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "comment", + "text": "--- INIT ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 752.0, + 16.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "text": "loadbang", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 752.0, + 40.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "text": "patcherargs 100", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 752.0, + 64.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 752.0, + 96.0, + 42.0, + 22.0 + ], + "comment": "send args then init defaults" + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "text": "prepend interval", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 752.0, + 128.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "text": "t b b b b b", + "numinlets": 1, + "numoutlets": 5, + "outlettype": [ + "bang", + "bang", + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 832.0, + 96.0, + 120.0, + 22.0 + ], + "comment": "init defaults R-to-L: bypass=0, cap=8, policy=0, cd=0, qidx=0" + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 832.0, + 128.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "text": "v #0_qidx", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 832.0, + 152.0, + 56.0, + 22.0 + ], + "comment": "queue write index" + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 864.0, + 128.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "text": "v #0_cd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 864.0, + 152.0, + 48.0, + 22.0 + ], + "comment": "cooldown state: 0=ready, 1=in cooldown" + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 896.0, + 128.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "text": "v #0_policy", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 896.0, + 152.0, + 64.0, + 22.0 + ], + "comment": "0=drop, 1=latest, 2=queue" + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "message", + "text": "8", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 928.0, + 128.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "text": "v #0_cap", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 928.0, + 152.0, + 52.0, + 22.0 + ], + "comment": "queue capacity" + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 960.0, + 128.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 960.0, + 152.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "comment", + "text": "--- CONTROL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 400.0, + 56.0, + 100.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "text": "route interval policy capacity flush reset bypass", + "numinlets": 1, + "numoutlets": 7, + "outlettype": [ + "", + "", + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 400.0, + 80.0, + 280.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "text": "v #0_interval", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 400.0, + 112.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "newobj", + "text": "route drop latest queue", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 448.0, + 112.0, + 128.0, + 22.0 + ], + "comment": "convert policy name to number" + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 448.0, + 136.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-26", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 480.0, + 136.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-27", + "maxclass": "message", + "text": "2", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 512.0, + 136.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-28", + "maxclass": "newobj", + "text": "v #0_policy", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 448.0, + 168.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-29", + "maxclass": "newobj", + "text": "v #0_cap", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 544.0, + 112.0, + 52.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-30", + "maxclass": "newobj", + "text": "s #0_flush", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 592.0, + 112.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-31", + "maxclass": "newobj", + "text": "s #0_reset", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 624.0, + 136.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-32", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 648.0, + 112.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: set bypass (first), then check cancel (second)" + } + }, + { + "box": { + "id": "obj-33", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 680.0, + 144.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-34", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 648.0, + 144.0, + 36.0, + 22.0 + ], + "comment": "bypass=1 triggers cancel" + } + }, + { + "box": { + "id": "obj-35", + "maxclass": "newobj", + "text": "s #0_cancel", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 648.0, + 168.0, + 68.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-36", + "maxclass": "comment", + "text": "--- BYPASS ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 56.0, + 100.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-37", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 56.0, + 80.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: get bypass (first), pass msg (second)" + } + }, + { + "box": { + "id": "obj-38", + "maxclass": "newobj", + "text": "v #0_bypass", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 112.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-39", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 56.0, + 136.0, + 30.0, + 22.0 + ], + "comment": "bypass=0 -> gate 1, bypass=1 -> gate 2" + } + }, + { + "box": { + "id": "obj-40", + "maxclass": "newobj", + "text": "gate 2 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 168.0, + 152.0, + 22.0 + ], + "comment": "outlet 1=throttle, outlet 2=bypass" + } + }, + { + "box": { + "id": "obj-41", + "maxclass": "comment", + "text": "--- THROTTLE CORE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 208.0, + 140.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-42", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 56.0, + 232.0, + 200.0, + 22.0 + ], + "comment": "R-to-L: get cd (first), pass msg (second)" + } + }, + { + "box": { + "id": "obj-43", + "maxclass": "newobj", + "text": "v #0_cd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 240.0, + 256.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-44", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 240.0, + 280.0, + 30.0, + 22.0 + ], + "comment": "cd=0 -> gate 1, cd=1 -> gate 2" + } + }, + { + "box": { + "id": "obj-45", + "maxclass": "newobj", + "text": "gate 2 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 56.0, + 312.0, + 272.0, + 22.0 + ], + "comment": "outlet 1=pass, outlet 2=in cooldown" + } + }, + { + "box": { + "id": "obj-46", + "maxclass": "comment", + "text": "--- READY: pass + start cooldown ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 344.0, + 220.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-47", + "maxclass": "newobj", + "text": "t l b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "", + "bang", + "bang" + ], + "patching_rect": [ + 56.0, + 368.0, + 200.0, + 22.0 + ], + "comment": "R-to-L: set cd=1 (first), start timer (second), output (last)" + } + }, + { + "box": { + "id": "obj-48", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 240.0, + 392.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-49", + "maxclass": "newobj", + "text": "v #0_cd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 240.0, + 416.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-50", + "maxclass": "newobj", + "text": "s #0_starttimer", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 152.0, + 392.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-51", + "maxclass": "message", + "text": "passed", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 416.0, + 42.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-52", + "maxclass": "comment", + "text": "--- TIMER ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 752.0, + 192.0, + 90.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-53", + "maxclass": "newobj", + "text": "r #0_starttimer", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 752.0, + 216.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-54", + "maxclass": "newobj", + "text": "v #0_interval", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 752.0, + 240.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-55", + "maxclass": "newobj", + "text": "t b f", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "float" + ], + "patching_rect": [ + 752.0, + 264.0, + 42.0, + 22.0 + ], + "comment": "R-to-L: set time (first), start (second)" + } + }, + { + "box": { + "id": "obj-56", + "maxclass": "newobj", + "text": "delay 100", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "bang" + ], + "patching_rect": [ + 752.0, + 296.0, + 56.0, + 22.0 + ], + "comment": "cooldown timer" + } + }, + { + "box": { + "id": "obj-57", + "maxclass": "newobj", + "text": "s #0_expired", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 752.0, + 320.0, + 68.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-58", + "maxclass": "comment", + "text": "--- COOLDOWN: route by policy ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 312.0, + 344.0, + 200.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-59", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 312.0, + 368.0, + 200.0, + 22.0 + ], + "comment": "R-to-L: get policy (first), pass msg (second)" + } + }, + { + "box": { + "id": "obj-60", + "maxclass": "newobj", + "text": "v #0_policy", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 496.0, + 392.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-61", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 496.0, + 416.0, + 30.0, + 22.0 + ], + "comment": "0->gate 1 (drop), 1->gate 2 (latest), 2->gate 3 (queue)" + } + }, + { + "box": { + "id": "obj-62", + "maxclass": "newobj", + "text": "gate 3 1", + "numinlets": 2, + "numoutlets": 3, + "outlettype": [ + "", + "", + "" + ], + "patching_rect": [ + 312.0, + 448.0, + 300.0, + 22.0 + ], + "comment": "1=drop, 2=latest, 3=queue" + } + }, + { + "box": { + "id": "obj-63", + "maxclass": "comment", + "text": "--- DROP ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 312.0, + 480.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-64", + "maxclass": "message", + "text": "dropped", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 312.0, + 504.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-65", + "maxclass": "comment", + "text": "--- LATEST ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 456.0, + 480.0, + 90.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-66", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 456.0, + 504.0, + 80.0, + 22.0 + ], + "comment": "R-to-L: set flag (first), store msg (second)" + } + }, + { + "box": { + "id": "obj-67", + "maxclass": "newobj", + "text": "zl reg", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 456.0, + 536.0, + 45.0, + 22.0 + ], + "comment": "latest message buffer" + } + }, + { + "box": { + "id": "obj-68", + "maxclass": "message", + "text": "1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 520.0, + 504.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-69", + "maxclass": "newobj", + "text": "v #0_has_latest", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 520.0, + 536.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-70", + "maxclass": "message", + "text": "queued", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 456.0, + 568.0, + 42.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-71", + "maxclass": "comment", + "text": "--- QUEUE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 600.0, + 480.0, + 88.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-72", + "maxclass": "newobj", + "text": "t b l b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "", + "bang" + ], + "patching_rect": [ + 600.0, + 584.0, + 160.0, + 22.0 + ], + "comment": "R-to-L: get cap (first), get qidx (second), store (last)" + } + }, + { + "box": { + "id": "obj-73", + "maxclass": "newobj", + "text": "v #0_qidx", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 696.0, + 616.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-74", + "maxclass": "newobj", + "text": "v #0_cap", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 744.0, + 616.0, + 52.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-75", + "maxclass": "newobj", + "text": "t i i", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 696.0, + 640.0, + 48.0, + 22.0 + ], + "comment": "R-to-L: inc+clamp (first), use as store key (second)" + } + }, + { + "box": { + "id": "obj-76", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 728.0, + 664.0, + 30.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-77", + "maxclass": "newobj", + "text": "minimum", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 728.0, + 688.0, + 52.0, + 22.0 + ], + "comment": "clamp to capacity" + } + }, + { + "box": { + "id": "obj-78", + "maxclass": "newobj", + "text": "v #0_qidx", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 728.0, + 712.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-79", + "maxclass": "newobj", + "text": "pack i l", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 600.0, + 664.0, + 104.0, + 22.0 + ], + "comment": "qidx key + msg value" + } + }, + { + "box": { + "id": "obj-80", + "maxclass": "newobj", + "text": "prepend store", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 600.0, + 688.0, + 76.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-81", + "maxclass": "newobj", + "text": "coll #0_q", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 600.0, + 712.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-82", + "maxclass": "message", + "text": "queued", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 600.0, + 744.0, + 42.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-83", + "maxclass": "comment", + "text": "--- TIMER EXPIRED ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 456.0, + 140.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-84", + "maxclass": "newobj", + "text": "r #0_expired", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 480.0, + 68.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-85", + "maxclass": "newobj", + "text": "v #0_policy", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 504.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-86", + "maxclass": "newobj", + "text": "route 0 1 2", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 56.0, + 528.0, + 200.0, + 22.0 + ], + "comment": "0=drop, 1=latest, 2=queue" + } + }, + { + "box": { + "id": "obj-87", + "maxclass": "comment", + "text": "drop: just clear cd", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 56.0, + 552.0, + 110.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-88", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 568.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-89", + "maxclass": "newobj", + "text": "v #0_cd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 592.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-90", + "maxclass": "comment", + "text": "latest: emit buffered or clear cd", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 120.0, + 552.0, + 180.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-91", + "maxclass": "newobj", + "text": "v #0_has_latest", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 152.0, + 568.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-92", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 152.0, + 592.0, + 36.0, + 22.0 + ], + "comment": "has data -> emit" + } + }, + { + "box": { + "id": "obj-93", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 152.0, + 616.0, + 100.0, + 22.0 + ], + "comment": "R-to-L: clear flag (first), restart timer (second), emit (last)" + } + }, + { + "box": { + "id": "obj-94", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 240.0, + 640.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-95", + "maxclass": "newobj", + "text": "v #0_has_latest", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 240.0, + 664.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-96", + "maxclass": "newobj", + "text": "s #0_starttimer", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 200.0, + 640.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-97", + "maxclass": "newobj", + "text": "zl reg", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 152.0, + 640.0, + 45.0, + 22.0 + ], + "comment": "recall latest msg" + } + }, + { + "box": { + "id": "obj-98", + "maxclass": "comment", + "text": "queue: dequeue or clear cd", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 240.0, + 552.0, + 160.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-99", + "maxclass": "newobj", + "text": "v #0_qidx", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 312.0, + 568.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-100", + "maxclass": "newobj", + "text": "sel 0", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 312.0, + 592.0, + 36.0, + 22.0 + ], + "comment": "empty -> clear cd" + } + }, + { + "box": { + "id": "obj-101", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 312.0, + 616.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-102", + "maxclass": "newobj", + "text": "v #0_cd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 312.0, + 640.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-103", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 344.0, + 616.0, + 100.0, + 22.0 + ], + "comment": "R-to-L: restart timer (first), dequeue item (second), output (last)" + } + }, + { + "box": { + "id": "obj-104", + "maxclass": "newobj", + "text": "s #0_starttimer", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 432.0, + 640.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-105", + "maxclass": "newobj", + "text": "s #0_dequeue", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 392.0, + 640.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-106", + "maxclass": "newobj", + "text": "r #0_dequeued", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 56.0, + 672.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-107", + "maxclass": "comment", + "text": "--- DEQUEUE ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 880.0, + 344.0, + 100.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-108", + "maxclass": "newobj", + "text": "r #0_dequeue", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 880.0, + 368.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-109", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 880.0, + 392.0, + 160.0, + 22.0 + ], + "comment": "R-to-L: dec qidx (first), remove 0 (second), get 0 (last)" + } + }, + { + "box": { + "id": "obj-110", + "maxclass": "newobj", + "text": "v #0_qidx", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1024.0, + 416.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-111", + "maxclass": "newobj", + "text": "- 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 1024.0, + 440.0, + 30.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-112", + "maxclass": "newobj", + "text": "maximum 0", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "int", + "int" + ], + "patching_rect": [ + 1024.0, + 464.0, + 64.0, + 22.0 + ], + "comment": "clamp >= 0" + } + }, + { + "box": { + "id": "obj-113", + "maxclass": "newobj", + "text": "v #0_qidx", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1024.0, + 488.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-114", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 952.0, + 416.0, + 60.0, + 22.0 + ], + "comment": "R-to-L: renumber (first), remove 0 (second)" + } + }, + { + "box": { + "id": "obj-115", + "maxclass": "message", + "text": "remove 0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 952.0, + 440.0, + 52.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-116", + "maxclass": "message", + "text": "renumber 0 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1000.0, + 440.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-117", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 880.0, + 416.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-118", + "maxclass": "newobj", + "text": "coll #0_q", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 880.0, + 448.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-119", + "maxclass": "newobj", + "text": "s #0_dequeued", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 880.0, + 480.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-120", + "maxclass": "comment", + "text": "--- FLUSH ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 880.0, + 520.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-121", + "maxclass": "newobj", + "text": "r #0_flush", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 880.0, + 544.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-122", + "maxclass": "newobj", + "text": "t b b b b", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "bang", + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 880.0, + 568.0, + 200.0, + 22.0 + ], + "comment": "R-to-L: cd=0 (first), stop timer (second), flush latest (third), dump queue (last)" + } + }, + { + "box": { + "id": "obj-123", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1064.0, + 592.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-124", + "maxclass": "newobj", + "text": "v #0_cd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1064.0, + 616.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-125", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1016.0, + 592.0, + 30.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-126", + "maxclass": "newobj", + "text": "v #0_has_latest", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 952.0, + 592.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-127", + "maxclass": "newobj", + "text": "sel 1", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "" + ], + "patching_rect": [ + 952.0, + 616.0, + 36.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-128", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 952.0, + 640.0, + 60.0, + 22.0 + ], + "comment": "R-to-L: clear flag (first), recall (second)" + } + }, + { + "box": { + "id": "obj-129", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1000.0, + 664.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-130", + "maxclass": "newobj", + "text": "v #0_has_latest", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1000.0, + 688.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-131", + "maxclass": "newobj", + "text": "zl reg", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 952.0, + 664.0, + 45.0, + 22.0 + ], + "comment": "recall latest for flush" + } + }, + { + "box": { + "id": "obj-132", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 880.0, + 592.0, + 60.0, + 22.0 + ], + "comment": "R-to-L: clear q state (first), dump (second)" + } + }, + { + "box": { + "id": "obj-133", + "maxclass": "newobj", + "text": "t b b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "bang", + "bang" + ], + "patching_rect": [ + 928.0, + 592.0, + 60.0, + 22.0 + ], + "comment": "R-to-L: clear coll (first), qidx=0 (second)" + } + }, + { + "box": { + "id": "obj-134", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 928.0, + 616.0, + 34.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-135", + "maxclass": "newobj", + "text": "coll #0_q", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 928.0, + 640.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-136", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 976.0, + 616.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-137", + "maxclass": "newobj", + "text": "v #0_qidx", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 976.0, + 640.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-138", + "maxclass": "message", + "text": "dump", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 880.0, + 616.0, + 32.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-139", + "maxclass": "newobj", + "text": "coll #0_q", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 880.0, + 640.0, + 56.0, + 22.0 + ], + "comment": "dump all queue items" + } + }, + { + "box": { + "id": "obj-140", + "maxclass": "message", + "text": "flushed", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 880.0, + 688.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-141", + "maxclass": "comment", + "text": "--- CANCEL ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 1104.0, + 344.0, + 100.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-142", + "maxclass": "newobj", + "text": "r #0_cancel", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1104.0, + 368.0, + 68.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-143", + "maxclass": "newobj", + "text": "t b b b b b", + "numinlets": 1, + "numoutlets": 5, + "outlettype": [ + "bang", + "bang", + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 1104.0, + 392.0, + 120.0, + 22.0 + ], + "comment": "R-to-L: has_latest=0, clear coll, qidx=0, stop, cd=0, status" + } + }, + { + "box": { + "id": "obj-144", + "maxclass": "message", + "text": "canceled", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1104.0, + 416.0, + 52.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-145", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1136.0, + 416.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-146", + "maxclass": "newobj", + "text": "v #0_cd", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1136.0, + 440.0, + 48.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-147", + "maxclass": "message", + "text": "stop", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1168.0, + 416.0, + 30.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-148", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1200.0, + 416.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-149", + "maxclass": "newobj", + "text": "v #0_qidx", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1200.0, + 440.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-150", + "maxclass": "message", + "text": "clear", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1232.0, + 416.0, + 34.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-151", + "maxclass": "newobj", + "text": "coll #0_q", + "numinlets": 1, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 1232.0, + 440.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-152", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1264.0, + 416.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-153", + "maxclass": "newobj", + "text": "v #0_has_latest", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1264.0, + 440.0, + 80.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-154", + "maxclass": "comment", + "text": "--- RESET ---", + "fontface": 1, + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 1104.0, + 488.0, + 80.0, + 20.0 + ] + } + }, + { + "box": { + "id": "obj-155", + "maxclass": "newobj", + "text": "r #0_reset", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1104.0, + 512.0, + 60.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-156", + "maxclass": "newobj", + "text": "t b b b", + "numinlets": 1, + "numoutlets": 3, + "outlettype": [ + "bang", + "bang", + "bang" + ], + "patching_rect": [ + 1104.0, + 536.0, + 100.0, + 22.0 + ], + "comment": "R-to-L: cap=8 (first), policy=0 (second), cancel (last)" + } + }, + { + "box": { + "id": "obj-157", + "maxclass": "newobj", + "text": "s #0_cancel", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 1104.0, + 560.0, + 68.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-158", + "maxclass": "message", + "text": "0", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1152.0, + 560.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-159", + "maxclass": "newobj", + "text": "v #0_policy", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1152.0, + 584.0, + 64.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-160", + "maxclass": "message", + "text": "8", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1192.0, + 560.0, + 25.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-161", + "maxclass": "newobj", + "text": "v #0_cap", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1192.0, + 584.0, + 52.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-162", + "maxclass": "newobj", + "text": "r #0_latest_store", + "numinlets": 0, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 488.0, + 568.0, + 88.0, + 22.0 + ], + "comment": "shared latest buffer store" + } + }, + { + "box": { + "id": "obj-163", + "maxclass": "newobj", + "text": "s #0_latest_store", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ + 456.0, + 592.0, + 88.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-164", + "maxclass": "newobj", + "text": "t l b", + "numinlets": 1, + "numoutlets": 2, + "outlettype": [ + "", + "bang" + ], + "patching_rect": [ + 600.0, + 504.0, + 120.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-165", + "maxclass": "newobj", + "text": "v #0_qidx", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 696.0, + 504.0, + 56.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-166", + "maxclass": "newobj", + "text": "v #0_cap", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 760.0, + 504.0, + 52.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-167", + "maxclass": "newobj", + "text": ">=", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 696.0, + 528.0, + 72.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-168", + "maxclass": "newobj", + "text": "+ 1", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "int" + ], + "patching_rect": [ + 696.0, + 552.0, + 30.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-169", + "maxclass": "newobj", + "text": "gate 2 1", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ + "", + "" + ], + "patching_rect": [ + 600.0, + 556.0, + 104.0, + 22.0 + ] + } + }, + { + "box": { + "id": "obj-170", + "maxclass": "message", + "text": "overflow", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 656.0, + 580.0, + 50.0, + 22.0 + ] + } + } + ], + "lines": [ + { + "patchline": { + "source": [ + "obj-1", + 0 + ], + "destination": [ + "obj-37", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-37", + 1 + ], + "destination": [ + "obj-38", + 0 + ], + "comment": "get bypass (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-37", + 0 + ], + "destination": [ + "obj-40", + 1 + ], + "comment": "msg to gate (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-38", + 0 + ], + "destination": [ + "obj-39", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-39", + 0 + ], + "destination": [ + "obj-40", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-40", + 1 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "bypass passthrough" + } + }, + { + "patchline": { + "source": [ + "obj-40", + 0 + ], + "destination": [ + "obj-42", + 0 + ], + "comment": "to throttle core" + } + }, + { + "patchline": { + "source": [ + "obj-42", + 1 + ], + "destination": [ + "obj-43", + 0 + ], + "comment": "get cd (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-42", + 0 + ], + "destination": [ + "obj-45", + 1 + ], + "comment": "msg to cd gate (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-43", + 0 + ], + "destination": [ + "obj-44", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-44", + 0 + ], + "destination": [ + "obj-45", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-45", + 0 + ], + "destination": [ + "obj-47", + 0 + ], + "comment": "ready: pass and start cd" + } + }, + { + "patchline": { + "source": [ + "obj-45", + 1 + ], + "destination": [ + "obj-59", + 0 + ], + "comment": "in cooldown: route by policy" + } + }, + { + "patchline": { + "source": [ + "obj-47", + 2 + ], + "destination": [ + "obj-48", + 0 + ], + "comment": "set cd=1 (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-47", + 1 + ], + "destination": [ + "obj-50", + 0 + ], + "comment": "start timer (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-47", + 0 + ], + "destination": [ + "obj-3", + 0 + ], + "order": 1, + "comment": "output (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-47", + 0 + ], + "destination": [ + "obj-51", + 0 + ], + "order": 0, + "comment": "status: passed" + } + }, + { + "patchline": { + "source": [ + "obj-48", + 0 + ], + "destination": [ + "obj-49", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-51", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-53", + 0 + ], + "destination": [ + "obj-54", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-54", + 0 + ], + "destination": [ + "obj-55", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-55", + 0 + ], + "destination": [ + "obj-56", + 0 + ], + "comment": "start delay" + } + }, + { + "patchline": { + "source": [ + "obj-55", + 1 + ], + "destination": [ + "obj-56", + 1 + ], + "comment": "set delay time" + } + }, + { + "patchline": { + "source": [ + "obj-56", + 0 + ], + "destination": [ + "obj-57", + 0 + ], + "comment": "timer expired" + } + }, + { + "patchline": { + "source": [ + "obj-59", + 1 + ], + "destination": [ + "obj-60", + 0 + ], + "comment": "get policy (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-59", + 0 + ], + "destination": [ + "obj-62", + 1 + ], + "comment": "msg to gate (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-60", + 0 + ], + "destination": [ + "obj-61", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-61", + 0 + ], + "destination": [ + "obj-62", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-62", + 0 + ], + "destination": [ + "obj-64", + 0 + ], + "comment": "drop: status" + } + }, + { + "patchline": { + "source": [ + "obj-62", + 1 + ], + "destination": [ + "obj-66", + 0 + ], + "comment": "latest: store" + } + }, + { + "patchline": { + "source": [ + "obj-64", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-66", + 0 + ], + "destination": [ + "obj-163", + 0 + ], + "comment": "store in latest buffer via send" + } + }, + { + "patchline": { + "source": [ + "obj-66", + 1 + ], + "destination": [ + "obj-68", + 0 + ], + "order": 1, + "comment": "set has_latest=1 (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-66", + 1 + ], + "destination": [ + "obj-70", + 0 + ], + "order": 0, + "comment": "status: queued" + } + }, + { + "patchline": { + "source": [ + "obj-68", + 0 + ], + "destination": [ + "obj-69", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-70", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-162", + 0 + ], + "destination": [ + "obj-67", + 1 + ], + "comment": "receive stored latest msg" + } + }, + { + "patchline": { + "source": [ + "obj-72", + 2 + ], + "destination": [ + "obj-74", + 0 + ], + "comment": "get cap (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-73", + 0 + ], + "destination": [ + "obj-75", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-75", + 0 + ], + "destination": [ + "obj-79", + 0 + ], + "comment": "qidx as key" + } + }, + { + "patchline": { + "source": [ + "obj-75", + 1 + ], + "destination": [ + "obj-76", + 0 + ], + "comment": "inc qidx" + } + }, + { + "patchline": { + "source": [ + "obj-74", + 0 + ], + "destination": [ + "obj-77", + 1 + ], + "comment": "cap as clamp max" + } + }, + { + "patchline": { + "source": [ + "obj-76", + 0 + ], + "destination": [ + "obj-77", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-77", + 0 + ], + "destination": [ + "obj-78", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-79", + 0 + ], + "destination": [ + "obj-80", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-80", + 0 + ], + "destination": [ + "obj-81", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-81", + 0 + ], + "destination": [ + "obj-82", + 0 + ], + "comment": "status: queued after store" + } + }, + { + "patchline": { + "source": [ + "obj-82", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-84", + 0 + ], + "destination": [ + "obj-85", + 0 + ], + "comment": "get policy on timer expire" + } + }, + { + "patchline": { + "source": [ + "obj-85", + 0 + ], + "destination": [ + "obj-86", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-86", + 0 + ], + "destination": [ + "obj-88", + 0 + ], + "comment": "drop: clear cd" + } + }, + { + "patchline": { + "source": [ + "obj-88", + 0 + ], + "destination": [ + "obj-89", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-86", + 1 + ], + "destination": [ + "obj-91", + 0 + ], + "comment": "latest: check has_latest" + } + }, + { + "patchline": { + "source": [ + "obj-91", + 0 + ], + "destination": [ + "obj-92", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-92", + 0 + ], + "destination": [ + "obj-93", + 0 + ], + "comment": "has data: emit" + } + }, + { + "patchline": { + "source": [ + "obj-92", + 1 + ], + "destination": [ + "obj-88", + 0 + ], + "comment": "no data: clear cd" + } + }, + { + "patchline": { + "source": [ + "obj-93", + 2 + ], + "destination": [ + "obj-94", + 0 + ], + "comment": "clear has_latest (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-93", + 1 + ], + "destination": [ + "obj-96", + 0 + ], + "comment": "restart timer (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-93", + 0 + ], + "destination": [ + "obj-97", + 0 + ], + "comment": "recall msg (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-94", + 0 + ], + "destination": [ + "obj-95", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-97", + 0 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "latest output" + } + }, + { + "patchline": { + "source": [ + "obj-86", + 2 + ], + "destination": [ + "obj-99", + 0 + ], + "comment": "queue: check qidx" + } + }, + { + "patchline": { + "source": [ + "obj-99", + 0 + ], + "destination": [ + "obj-100", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-100", + 0 + ], + "destination": [ + "obj-101", + 0 + ], + "comment": "empty: clear cd" + } + }, + { + "patchline": { + "source": [ + "obj-101", + 0 + ], + "destination": [ + "obj-102", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-100", + 1 + ], + "destination": [ + "obj-103", + 0 + ], + "comment": "has items: dequeue" + } + }, + { + "patchline": { + "source": [ + "obj-103", + 2 + ], + "destination": [ + "obj-104", + 0 + ], + "comment": "restart timer (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-103", + 1 + ], + "destination": [ + "obj-105", + 0 + ], + "comment": "dequeue (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-106", + 0 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "dequeued to output" + } + }, + { + "patchline": { + "source": [ + "obj-108", + 0 + ], + "destination": [ + "obj-109", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-109", + 2 + ], + "destination": [ + "obj-110", + 0 + ], + "comment": "dec qidx (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-109", + 1 + ], + "destination": [ + "obj-114", + 0 + ], + "comment": "remove+renumber (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-109", + 0 + ], + "destination": [ + "obj-117", + 0 + ], + "comment": "get index 0 (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-110", + 0 + ], + "destination": [ + "obj-111", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-111", + 0 + ], + "destination": [ + "obj-112", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-112", + 0 + ], + "destination": [ + "obj-113", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-114", + 1 + ], + "destination": [ + "obj-116", + 0 + ], + "comment": "renumber (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-114", + 0 + ], + "destination": [ + "obj-115", + 0 + ], + "comment": "remove 0 (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-115", + 0 + ], + "destination": [ + "obj-118", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-116", + 0 + ], + "destination": [ + "obj-118", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-117", + 0 + ], + "destination": [ + "obj-118", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-118", + 0 + ], + "destination": [ + "obj-119", + 0 + ], + "comment": "output to dequeued" + } + }, + { + "patchline": { + "source": [ + "obj-2", + 0 + ], + "destination": [ + "obj-22", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-22", + 0 + ], + "destination": [ + "obj-23", + 0 + ], + "comment": "interval" + } + }, + { + "patchline": { + "source": [ + "obj-22", + 1 + ], + "destination": [ + "obj-24", + 0 + ], + "comment": "policy" + } + }, + { + "patchline": { + "source": [ + "obj-22", + 2 + ], + "destination": [ + "obj-29", + 0 + ], + "comment": "capacity" + } + }, + { + "patchline": { + "source": [ + "obj-22", + 3 + ], + "destination": [ + "obj-30", + 0 + ], + "comment": "flush" + } + }, + { + "patchline": { + "source": [ + "obj-22", + 4 + ], + "destination": [ + "obj-31", + 0 + ], + "comment": "reset" + } + }, + { + "patchline": { + "source": [ + "obj-22", + 5 + ], + "destination": [ + "obj-32", + 0 + ], + "comment": "bypass" + } + }, + { + "patchline": { + "source": [ + "obj-32", + 1 + ], + "destination": [ + "obj-33", + 0 + ], + "comment": "set bypass (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-32", + 0 + ], + "destination": [ + "obj-34", + 0 + ], + "comment": "check cancel (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-34", + 0 + ], + "destination": [ + "obj-35", + 0 + ], + "comment": "bypass=1: cancel" + } + }, + { + "patchline": { + "source": [ + "obj-24", + 0 + ], + "destination": [ + "obj-25", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 1 + ], + "destination": [ + "obj-26", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-24", + 2 + ], + "destination": [ + "obj-27", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-25", + 0 + ], + "destination": [ + "obj-28", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-26", + 0 + ], + "destination": [ + "obj-28", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-27", + 0 + ], + "destination": [ + "obj-28", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-6", + 0 + ], + "destination": [ + "obj-7", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-7", + 0 + ], + "destination": [ + "obj-8", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-8", + 0 + ], + "destination": [ + "obj-9", + 0 + ], + "comment": "prepend interval" + } + }, + { + "patchline": { + "source": [ + "obj-8", + 1 + ], + "destination": [ + "obj-10", + 0 + ], + "comment": "init defaults" + } + }, + { + "patchline": { + "source": [ + "obj-9", + 0 + ], + "destination": [ + "obj-22", + 0 + ], + "comment": "send interval from init" + } + }, + { + "patchline": { + "source": [ + "obj-10", + 0 + ], + "destination": [ + "obj-11", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 1 + ], + "destination": [ + "obj-13", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 2 + ], + "destination": [ + "obj-15", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 3 + ], + "destination": [ + "obj-17", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-10", + 4 + ], + "destination": [ + "obj-19", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-11", + 0 + ], + "destination": [ + "obj-12", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-13", + 0 + ], + "destination": [ + "obj-14", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-15", + 0 + ], + "destination": [ + "obj-16", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-17", + 0 + ], + "destination": [ + "obj-18", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-19", + 0 + ], + "destination": [ + "obj-20", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-121", + 0 + ], + "destination": [ + "obj-122", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-122", + 3 + ], + "destination": [ + "obj-123", + 0 + ], + "comment": "cd=0 (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-122", + 2 + ], + "destination": [ + "obj-125", + 0 + ], + "comment": "stop timer (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-122", + 1 + ], + "destination": [ + "obj-126", + 0 + ], + "comment": "flush latest (fires third)" + } + }, + { + "patchline": { + "source": [ + "obj-122", + 0 + ], + "destination": [ + "obj-132", + 0 + ], + "comment": "dump queue (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-123", + 0 + ], + "destination": [ + "obj-124", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-125", + 0 + ], + "destination": [ + "obj-56", + 0 + ], + "comment": "stop delay" + } + }, + { + "patchline": { + "source": [ + "obj-126", + 0 + ], + "destination": [ + "obj-127", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-127", + 0 + ], + "destination": [ + "obj-128", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-128", + 1 + ], + "destination": [ + "obj-129", + 0 + ], + "comment": "clear flag (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-128", + 0 + ], + "destination": [ + "obj-131", + 0 + ], + "comment": "recall (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-129", + 0 + ], + "destination": [ + "obj-130", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-131", + 0 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "flushed latest" + } + }, + { + "patchline": { + "source": [ + "obj-133", + 1 + ], + "destination": [ + "obj-134", + 0 + ], + "comment": "clear coll (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-133", + 0 + ], + "destination": [ + "obj-136", + 0 + ], + "comment": "qidx=0 (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-134", + 0 + ], + "destination": [ + "obj-135", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-136", + 0 + ], + "destination": [ + "obj-137", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-138", + 0 + ], + "destination": [ + "obj-139", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-139", + 0 + ], + "destination": [ + "obj-3", + 0 + ], + "comment": "queue items to output" + } + }, + { + "patchline": { + "source": [ + "obj-139", + 3 + ], + "destination": [ + "obj-140", + 0 + ], + "comment": "dump done: flushed" + } + }, + { + "patchline": { + "source": [ + "obj-140", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-142", + 0 + ], + "destination": [ + "obj-143", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-143", + 0 + ], + "destination": [ + "obj-144", + 0 + ], + "comment": "status (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-143", + 1 + ], + "destination": [ + "obj-145", + 0 + ], + "comment": "cd=0" + } + }, + { + "patchline": { + "source": [ + "obj-143", + 2 + ], + "destination": [ + "obj-147", + 0 + ], + "comment": "stop timer" + } + }, + { + "patchline": { + "source": [ + "obj-143", + 3 + ], + "destination": [ + "obj-148", + 0 + ], + "comment": "qidx=0" + } + }, + { + "patchline": { + "source": [ + "obj-143", + 4 + ], + "destination": [ + "obj-150", + 0 + ], + "order": 1, + "comment": "clear coll" + } + }, + { + "patchline": { + "source": [ + "obj-143", + 4 + ], + "destination": [ + "obj-152", + 0 + ], + "order": 0, + "comment": "has_latest=0" + } + }, + { + "patchline": { + "source": [ + "obj-144", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-145", + 0 + ], + "destination": [ + "obj-146", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-147", + 0 + ], + "destination": [ + "obj-56", + 0 + ], + "comment": "stop delay" + } + }, + { + "patchline": { + "source": [ + "obj-148", + 0 + ], + "destination": [ + "obj-149", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-150", + 0 + ], + "destination": [ + "obj-151", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-152", + 0 + ], + "destination": [ + "obj-153", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-155", + 0 + ], + "destination": [ + "obj-156", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-156", + 2 + ], + "destination": [ + "obj-160", + 0 + ], + "comment": "cap=8 (fires first)" + } + }, + { + "patchline": { + "source": [ + "obj-156", + 1 + ], + "destination": [ + "obj-158", + 0 + ], + "comment": "policy=0 (fires second)" + } + }, + { + "patchline": { + "source": [ + "obj-156", + 0 + ], + "destination": [ + "obj-157", + 0 + ], + "comment": "cancel (fires last)" + } + }, + { + "patchline": { + "source": [ + "obj-158", + 0 + ], + "destination": [ + "obj-159", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-160", + 0 + ], + "destination": [ + "obj-161", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-72", + 1 + ], + "destination": [ + "obj-79", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-72", + 0 + ], + "destination": [ + "obj-73", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-62", + 2 + ], + "destination": [ + "obj-164", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-164", + 1 + ], + "destination": [ + "obj-165", + 0 + ], + "order": 0 + } + }, + { + "patchline": { + "source": [ + "obj-164", + 1 + ], + "destination": [ + "obj-166", + 0 + ], + "order": 1 + } + }, + { + "patchline": { + "source": [ + "obj-165", + 0 + ], + "destination": [ + "obj-167", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-166", + 0 + ], + "destination": [ + "obj-167", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-167", + 0 + ], + "destination": [ + "obj-168", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-168", + 0 + ], + "destination": [ + "obj-169", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-164", + 0 + ], + "destination": [ + "obj-169", + 1 + ] + } + }, + { + "patchline": { + "source": [ + "obj-169", + 0 + ], + "destination": [ + "obj-72", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-169", + 1 + ], + "destination": [ + "obj-170", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-170", + 0 + ], + "destination": [ + "obj-4", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-132", + 1 + ], + "destination": [ + "obj-138", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-132", + 0 + ], + "destination": [ + "obj-133", + 0 + ] + } + } + ], + "dependency_cache": [], + "autosave": 0 + } +}