Skip to content

Scale Quantizer 0.1.4: TRACK / FOLLOW rows (five SEQUENCER rows instead of 19), the author's 2.21 quantizer - #1147

Merged
repeat98 merged 29 commits into
repeat98:mainfrom
timhastie:quantizer-fm-main2
Nov 5, 2026
Merged

repeat98 merged 29 commits into
repeat98:mainfrom
timhastie:quantizer-fm-main2

Conversation

@timhastie

Copy link
Copy Markdown
Contributor

Scale Quantizer 0.1.4: TRACK / FOLLOW rows (five SEQUENCER rows instead of 19), the author's 2.21 quantizer

This is an author update of Scale Quantizer (quantizer), 0.1.3-experimental -> 0.1.4-experimental, on a fresh branch off current main 501d8a42, as agreed on #555 on 31 October and confirmed there on 1 November ("Route 4 for now"). It carries the MIDI-track quantizing that FM Synth 0.1.5 (#555) had inside FM Synth, as agreed there on 29 October: these sites are Scale Quantizer's, FM Synth keeps its bundled copy to what FM tracks need. Nineteen commits: the module, its packages and proofs, the adapters, the tests, the changelog, the record of the hardware passes below and, last, the upstream pin moved to the commit of my released 2.22 line.

What changed since 0.1.3

upstream/quantizer is my 2.21 quantizer plus the TRACK / FOLLOW rows, 8f6f564a9c42636ed4c5cc6ad314ee6b7ce69e5c, byte for byte, released with Octatrick 2.22 (v2.22 = 73686de, on v2.21 9e4d34a; the import record sdk/imports/octabam-363861e.json lists its files at that commit). The files were vendored on 29 October from my working commit 990eff8, whose quantizer/ is identical to 8f6f564's; the wrapper manifest.py's docstring still names 990eff8 and is left as is, because that file's SHA-256 is bound in the committed package records (src/engine/assets/requested-packages.json, media/capture.json, the machine adapters), to move with the next package rebuild.

  • Recorded notes follow the scale ([quantizer] Suggestion #439): what a Static or Flex track plays is snapped to SCALE and ROOT as the trig is played (its PTCH lock or the Part's value; the stored locks are not changed), and so are the notes a MIDI track sends: NOTE, the chord notes, TRAN and the arpeggiator.
  • MIDI tracks: the NOTE knob, NOTE locks and the chromatic keys step through the scale from ROOT; the held chromatic key on an audio track is marked on the note it sounds.
  • TRACK and FOLLOW rows instead of the sixteen per-track rows of 2.19 / 2.20: TRACK picks AUDIO 1-8 or MIDI 1-8 (a RAM byte, AUDIO 1 at power-on, not saved, changes no sound), FOLLOW sets that track ON or OFF on the same battery-RAM bits as before. The module contributes five SEQUENCER rows (SCALE, ROOT, GLIDE, TRACK, FOLLOW): within the eight contributed rows the base gives over USB (your 30 October note: SYNTIZ raised the room), the same five on the card (row-count poke 3 -> 8). No base change is needed for the rows.
  • The project format is unchanged: the same #SEQUENCER_MIDI_SCALE_OFF / #SEQUENCER_AUDIO_SCALE_OFF lines and battery-RAM bits as 2.19-2.21, so projects saved on either load the same (checked in the emulator: a project.work byte-identical to 2.21's for the same settings; a 2.21-saved project with tracks OFF loads with the same tracks OFF).
  • 0.1.3's [quantizer] OT Crash/Freeze in midi mode when selecting Trig Mode (FUNC + Down) #181 / [synth] error #264 trig-mode fix is in my line since 2.13; quantizer.s is no longer edited by the wrapper.

The seven guarded sites

These are the sites no base event covers, listed on #555 on 29 October against main 3b79e797 and kept as ordinary guarded patches in this module only: qz_mknob (0x40055382, 8 B, MIDI NOTE and chord-offset knob edits), qz_mplock (0x40050b98, 10 B, held-trig MIDI NOTE locks), qz_mkey (0x400436a8, 10 B, a MIDI chromatic key press before note-on, arp insertion, held-note storage and recording), qz_mnote (0x4009fb80, 6 B, each sequenced, chord or arp note after transpose and KEY), qz_atrig (0x4000c614, 6 B, the resolved STATIC / FLEX trig pitch), qz_mprev (0x400439d6, 8 B, the four-note MIDI trig preview) and qz_heldmark (0x40044abc, 8 B, the audio chromatic keyboard's held-key mark). None overlaps a span the base owns. 2.21 adds two more guarded detours, qz_panel_stage (0x4004fc84, 16 B) and qz_panel_commit (0x40005162, 8 B), the panel publication transaction, which assemble to their replays in a selection without FM Synth. Every guard is Modwerk's stock_guard(address, length, sha256): 28 detours and the row-count poke (manifest.py, stock-guards in the import record); the vendored manifest names no stock bytes.

Compatibility

compatibility.conflicts stays [], as on main: the module whose change makes the pair collide flags it, and that is FM Synth 0.1.4, which declares quantizer (it bundles its own copy of the same quantizer source and the two would patch the same sites). The two stay declared as conflicting, as agreed on #555; nothing here hooks into FM Synth's code, rows, tables or files. Over USB, the eighteen quantizer sites FM Synth and POLY8 also use are base take events (this module at order 50) and the rows are the base's table; the nine other hooks stay guarded patches. modules:compat -- quantizer reports 0 problems (29 active modules, 406 pairs).

Evidence

Card build (native exporter, --vendored-sdk --static-stock --modules quantizer): MAIN 22541cd628f8064cb6d12e81a0481b17ffad9e737fd968454ae6eb419bb735e8 (1,116,202 B), upgrade file ec4b811fe6620f06eb6ba9fd23c18e996830f8fea255cf6552fdcc4d089ff808 (449,848 B); rebuilt byte-identical from every later commit of the branch (the documents and records do not feed the build), and byte-identical again at the head rebased onto main 00ecaf52 on 31 October (the image I call TEST3 is TEST2).

Base: the branch was rebased from 00ecaf52 onto c4515136 on 1 November (the image rebuilt there twice, byte-identical), then onto 4ce910bb and, the same day, onto 501d8a42, after the hardware pass. Between c4515136 and 501d8a42 main changed 34 files: 31 under sdk/analog-rytm-mkii/modules/_submissions/, sdk/machines/analog-rytm-mkii/README.md and two under changelog/; the diff over the card build's inputs (the module folder, sdk/octabam/{platform,tools,dsp,licenses}, scripts/export-composition-proofs.py, the module adapters, scripts/octatrack-module-packages.py, build_core.py) is empty, so the image is unchanged; it was nevertheless rebuilt once from the branch head (a927f6ae) on 1 November through the same exporter: byte-identical (every output file). This is the check #1138 proposes, done by hand.

Emulator (ot_emu, MKII panel, a card with tracks 1 and 8 FLEX on one sine sample, trigs 1 / 5 / 9 / 13, Part PTCH +1):

  • SEQUENCER lists stock's three rows, SCALE, ROOT, GLIDE, TRACK (AUDIO 1), FOLLOW (ON); DOWN past FOLLOW stays there.
  • TRACK AUDIO 8 with FOLLOW OFF sets bit 7 of 0x100b14eb; TRACK to MIDI 1 shows FOLLOW ON in the first LCD read after the turn and back to AUDIO 8 shows OFF; TRACK turns change no stored byte; TRACK stops at AUDIO 1 and MIDI 8.
  • Playing in C MAJOR, track 1's PTCH word is snapped from +1 to 0 at every trig (qz_atrig) while track 8 keeps +1; with AUDIO 8 FOLLOW ON both snap.
  • SAVE PROJECT writes #SEQUENCER_MIDI_SCALE_OFF=2 and #SEQUENCER_AUDIO_SCALE_OFF=132 and a project.work byte-identical to 2.21's; after a power cycle the bits are back and TRACK starts at AUDIO 1; a 2.21-saved project loads with the same tracks OFF.
  • Every screen of these walks is pixel-identical to my standalone 2.21 + TRACK / FOLLOW build; the six media PNGs are re-captured from this image (only ot-track-rows.png changed).
    Details in TESTING.md.

Hardware

I tested the exact image above on my Octatrack MKI twice, the same bytes each time: TEST2 on 30 October 2026 and TEST3 (the rebuild on current main) on 1 November 2026, every item of the sheet passing both times. The record is in octamod.module.json (tests.hardwareStatus reported, two tests.gates lines) and TESTING.md ("Author-reported hardware — 30 October 2026" and "— 1 November 2026"):

  • tested on: 2026-10-30 (TEST2) and 2026-11-01 (TEST3)
  • tester: timhastie
  • model: MKI
  • summary: the sheet's checks. SEQUENCER has eight rows ending in TRACK and FOLLOW and DOWN past FOLLOW does nothing; TRACK AUDIO 8 with FOLLOW OFF, then TRACK to MIDI 1 and back, reads ON and OFF as it turns without leaving the page and stops at AUDIO 1 and MIDI 8; with the same pitched sample on T1 and T8 at PTCH +1 and trigs on 1, 5, 9 and 13, C MAJOR with AUDIO 8 FOLLOW OFF plays T1 at the root and T8 a semitone higher, and FOLLOW ON plays both the same; after SAVE and a power-cycle SCALE reads MAJOR, TRACK is back at AUDIO 1, AUDIO 8 still reads FOLLOW OFF and T8 still plays +1.
  • limitations: play time, readings and the MIDI gear used were not recorded; whether the optional step (a 2.21-saved project with rows OFF) was run is not stated. A functional check of the sheet's items, not a load or stress qualification; no tests.qualification is added, because the contract wants cycle and memory figures beside it and this cut measures neither.

USB package

The branch ships quantizer/0.1.4-experimental.mwrm (7,210 B, 33700b9339c0d0e619716cf6358ac83c9912c9881e30721411d2bf79897bbe04; 19 hooks, 9 stock-code sites, 6 copies), built on this Mac from my own unit's OS with the machine recipe re-captured for the new sources; octatrack:module-packages --check --stock reports 27 packages identical. The card-build packages, the GNU link proofs and the standalone recipe pins are refreshed (Docker isolated build, modules:import --development). Not tested over USB on a unit.

Checks and known CI items

Run on 1 November 2026 at the record commit, on main 4ce910bb and again on 501d8a42 for modules:check, module:doctor, licenses:check, the changelog test and npm run check (the same results), in this checkout without the owner's records, and modules:check, module:doctor, licenses:check, the changelog test, sdk-imports and test_requested_imports once more at the branch head a927f6ae (the same results):

Check Result
npm run modules:check -- --base 501d8a42 fails: quantizer: owner-approved update does not cover this module version (sdk/quantizer-build-approval.json covers 0.1.3; the owner's record).
npm run module:doctor -- quantizer ✓ catalog entry, library, category guide, thumbnail, packages (compiled at 0.1.4), USB package (at 0.1.4), declaration checks (refused beside synth, flagged in FM Synth's conflicts), cross-compatibility, board claim; ✗ module rules (the owner record above), native comparison and package fingerprint (module:verify -- quantizer --os <OS> after the records).
npm run modules:compat -- quantizer 0 problems (29 active modules, 406 pairs, 92 hooks).
npm run modules:pairs src/catalog/compatibility-pairs.json is stale: run npm run modules:pairs -- --write (the owner's regeneration after his records).
npm run licenses:check passed (12 of 12).
npm run sdk:imports:check passed.
npm run sdk:check passed: 502 tests OK (skips for the native toolchain).
npx vitest run scripts/changelog.test.mjs passed (3 of 3).
npx vitest run src/catalog 246 of 251: build-support.test.ts (three) expects 0.1.4 from the generated catalogue, which stays 0.1.3's until the owner's record lets modules:generate run; module-qualification.test.ts stops at that record; native-comparisons.test.ts wants module:verify -- quantizer --os <OS> for the new code.

npm run check -- --base 501d8a42 (the full app suite) stops at modules:check, the owner record above; prepare, licenses:check and machines:check pass before it. src/engine/assets/analog-bd-composition-proofs.json is not refreshed (this Mac's exporter output differs from the committed record also for selections without the quantizer; the reference toolchain's run). The stock scan over the files this branch authors finds no run of 16 or more stock bytes.

Review scope

Beside the module folder the branch changes sdk/catalog.json, the import record, machine-ports.json / machine-recipes.json / standalone-recipes.json, quantizer_runtime.py (the standalone include at both panel-transaction sites), two SDK tests, src/catalog/build-support.test.ts and scripts/module-source.test.mjs (version expectations), the module changelog, the rebuilt packages and proofs under src/engine/assets/, and public/octatrack-modules/quantizer/ (0.1.3's .mwrm replaced by 0.1.4's). Owner: the approval record for 0.1.4 and POLY8's composition signature; then module:verify, modules:pairs -- --write and modules:generate.

How to test

  1. Build Scale Quantizer alone with the native exporter and install it with PROJECT > OS UPGRADE.
  2. PROJECT > CONTROL > SEQUENCER: eight rows, ending with TRACK and FOLLOW.
  3. Two Flex tracks on the same sample, PTCH +1, trigs on 1, 5, 9, 13. SCALE MAJOR, TRACK AUDIO 8, FOLLOW OFF, PLAY: T1 plays the root, T8 a semitone higher. FOLLOW ON: both the same.
  4. Save, power-cycle: SCALE MAJOR, AUDIO 8 FOLLOW OFF kept, TRACK back at AUDIO 1.
  5. On a MIDI track with C MAJOR, NOTE steps C to D; TRACK MIDI 1, FOLLOW OFF: C to C#.

The guides

Nothing lacking for this cut. The check I'd like on every PR is #1138 (say whether the card image's inputs changed since the tested build), which I did by hand above.

  • Changing a module someone else wrote: not applicable, Scale Quantizer is mine.
  • I read the guide for its category in docs/module-guides/ (sequencing), and TESTING.md lists what I did not test. An effect module holds one effect.
  • Source and media are mine or properly licensed, with authors and full licence texts kept. Nothing comes from Elektron firmware: no extracted code or tables, upgrade files or factory content, and nothing is modelled on another current Elektron product.
  • Screenshots are real captures of the current module UI. Hardware results are what a named person saw on this exact build (timhastie, MKI, 30 October and 1 November 2026); emulator results are labelled as emulator results.

Owner review, not automatic publication: this version names addresses 0.1.3 never used, and the owner records (sdk/quantizer-build-approval.json, POLY8's composition signature) are yours.

  • Automatically merge and publish my module update after checks pass.
  • I verified the source, licences, UI and qualification reports, including instance isolation and Part/project/reboot persistence where applicable; no required acceptance check is failed or untested.

Validation commands and results: see "Checks and known CI items" above.

Related to #439.

… switches as two SEQUENCER rows, TRACK and FOLLOW

upstream/ is timhastie/octatrick-modules 990eff8 byte for byte (2.21 + TRACK / FOLLOW, not yet
published). The module contributes five SEQUENCER rows (SCALE, ROOT, GLIDE, TRACK, FOLLOW) to
ot_sequencer_rows, within the base's eight; the per-track bits and project-file lines are 2.21's.
The wrapper declares the two 2.21 detour guards (qz_panel_stage, qz_panel_commit); the runtime
port writes 2.21's two-pass remix.inc for a standalone package.
…rds, and the participants test reads 2.21's two-pass remix.inc
The runtime port reads 2.21's two-pass remix.inc from the octabam root, where the author's manifest
names it; the quantizer's machine recipe is re-captured for the new sources (6 copies, hashes only),
and the other machine recipes' pin of machine-ports.json is refreshed.
… Synth

The author's 2.21 panel publication transaction is enabled only beside SYNTH MACHINE and writes FM's
engine state (sy_state). FM Synth bundles its own copy of this quantizer and conflicts with this module,
so the wrapper gives remix.inc a selection without FM Synth: the card-build object no longer names
sy_state, and the card image and USB package are unchanged.
…ndled copy assembles from the same source, with this declaration's signature
…side FM Synth (one quantizer on FM Synth's copy of the same source)
…uantizer TEST2 image (FM Synth on the author's 3531b8e: an FM SYNTH track whose FOLLOW is OFF now plays SCALE OFF's pitches; on TEST1 only ROOT was skipped)
…y rule is merged (this module's conflicts stays empty, as on main); the author's MKI report of TEST2 and of the pair (30 October 2026), hardwareStatus reported; the changelog carries the marked compatibility note
…us FM Synth's LFO SETUP names hook); the author's MKI report of 30 October stays TEST2's record
…ir's TEST2 and states what TEST3 re-ran (55 of 55 captures byte-identical to TEST2)
…1 October: a module may not hook into another module's code, rows, tables or files); FM Synth stays a declared conflict in one sentence; on main 00ecaf5 the image rebuilds byte-identical to TEST2, the author's MKI report of 30 October is its record
…2026 (TEST3, the same bytes as TEST2: upgrade file ec4b811f, 449,848 B)

The author flashed OCTATRACK_MODWERK_QUANTIZER_TEST3.bin (sha256 ec4b811f..., MAIN 22541cd6..., byte-identical to the TEST2 image he reported on 30 October 2026) on his MKI on 1 November 2026 and reported its test sheet passing. The record: a second tests.gates line, tests.summary and the last limitation updated, an "Author-reported hardware — 1 November 2026 (TEST3)" section in TESTING.md naming the sheet's checks and what was not recorded (play time, readings, MIDI gear; the optional 2.21-project step not stated), the Open list, and README's compatibility and Tests paragraphs. tests.hardwareStatus stays reported; no tests.qualification object: this cut measures neither cycles nor memory.

The branch was rebased onto main 4ce910b after the pass; the card build's inputs did not change between 00ecaf5 and 4ce910b, so the image is unchanged and not rebuilt.
… his released 2.22 line (8f6f564) whose files these are; the vendored README is that commit's

The import record's per-file pins, README and TESTING name 8f6f564a9c42636ed4c5cc6ad314ee6b7ce69e5c, the commit of the author's Octatrick 2.22 line (on v2.21) whose quantizer/ upstream/ carries, in place of his working commit 990eff8 of 29 October (the same code; the two differ in one README line). upstream/quantizer/README.md is 8f6f564's file, so every vendored file is that commit's byte for byte; the record's entry for it carries the new identity (size, blob, SHA-256). source.revision and tests.evidenceRevision stay 363861e3, the octabam import this wrapper descends from (main's own pin, which src/catalog/build-support.test.ts requires). The wrapper manifest.py is unchanged: its docstring still names 990eff8, because the file's SHA-256 is bound in the committed package records (requested-packages.json, media/capture.json, the machine adapters); TESTING.md says so. No code byte changes.
repeat98 and others added 9 commits November 4, 2026 11:28
The package assets and the standalone recipes are main's here; they are rebuilt
for Scale Quantizer 0.1.4 in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner approved the update on 4 November 2026. It waives what the 0.1.3
record waived: the author's MKI reports cover the card image (TEST2, TEST3); the
USB package was not run on a unit, and chip timing and complete memory bounds
are unmeasured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… Tape Echo's and Euclid's recipes re-pinned

bash scripts/build-modules-isolated.sh at c885b82 in modwerk-source-tools,
then npm run modules:import -- --development. The four quantizer objects are
byte-identical to ff28c23's; requested-packages.json's composition section now
follows composition.json as changed in 39ecde9, which the committed file did not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l record

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pair table for this source

- npm run module:verify -- quantizer --os <1.40C>: 166 selections, 74 native
  images match outside the platform writes, 92 matching refusals, no mismatch
  (the committed record was still 0.1.3's).
- export-composition-proofs.py --suite analog-bd --static-stock --vendored-sdk,
  4 shards merged in case order: the 26 selections with Scale Quantizer change,
  the other 186 are identical, no refusal flips; moduleSourceTreeSha256 moves.
- modules:pairs -- --write: the shared 0x40065c7c note names 0.1.4's 8 rows.
- The approval record says plainly that nobody has run the USB package on a
  unit and the owner has run neither it nor the card build on hardware.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e Analog BD (owner-approved)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 0.1.0 and Mute Modes 0.2.2) and imported; Tape Echo's and Euclid's recipes re-pinned

bash scripts/build-modules-isolated.sh at 1c81f14 in modwerk-source-tools
(sha256:28323550), then npm run modules:import -- --development.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… rebuilt packages

export-composition-proofs.py --suite analog-bd --static-stock --vendored-sdk in
modwerk-source-tools, 8 shards merged in case order: all 212 proofs identical to
this branch's, only moduleSourceTreeSha256 moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@repeat98

repeat98 commented Nov 5, 2026

Copy link
Copy Markdown
Owner

Thanks Tim. I've pushed my part of 0.1.4 to your branch, as a fast-forward on top of your a927f6ae:

  • The owner's approval record for this exact source (sdk/quantizer-build-approval.json, 26e51a9c…). It still matches after the refresh.
  • Main merged (5965121e, after the Tuner Tuner 0.1.0: selected-track pitch readout on UP + TEMPO #1148 and Mute Modes 0.2.2). The packages were rebuilt in the pinned image and imported, Tape Echo's and Euclid's recipes re-pinned, and Analog BD's proofs re-exported. All 212 proofs match your branch's, and only the source-tree hash moved. The regenerated catalogue and pair table didn't change.
  • One test line. src/catalog/build-support.test.ts expected Scale Quantizer's hardwareStatus to be untested, but your MKI reports make 0.1.4 reported. It now sits in the reported branch next to Analog BD.

npm run pr:ready says Ready, and CI's package check passes locally on the pushed commit. I'll merge once the checks are green. Nothing is needed from you.


Generated by Claude Code

@repeat98
repeat98 merged commit 3ae8c38 into repeat98:main Nov 5, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants