Skip to content

FM Synth 0.1.4: the author's 2.21 engine; MIDI quantizing moves to Scale Quantizer - #1146

Open
timhastie wants to merge 19 commits into
repeat98:mainfrom
timhastie:synth-qz-main2
Open

timhastie wants to merge 19 commits into
repeat98:mainfrom
timhastie:synth-qz-main2

Conversation

@timhastie

Copy link
Copy Markdown
Contributor

FM Synth 0.1.4: the author's 2.21 engine; MIDI quantizing moves to Scale Quantizer

The FM Synth re-cut as its own PR off current main, as agreed on #555 on 31 October and confirmed there on 1 November ("Route 4 for now": the two modules stay declared as conflicting, FM Synth re-cut and Scale Quantizer split sent as their own PRs). It supersedes #555's content. The MIDI-track quantizing of the earlier 0.1.5 content is out, as agreed on 29 October: FM Synth keeps its bundled quantizer to what FM tracks need, and MIDI tracks, the trig-time snap of sample tracks and the chromatic keyboard's mark go to Scale Quantizer 0.1.4 (its own PR, next). Main has synth 0.1.2; this is 0.1.2-experimental -> 0.1.4-experimental. I'd use 0.1.5-experimental for FM Synth + SY DRUM, after your base PR for several ot_machines records per module and the SETUP field.

The branch is off current main 501d8a42, nineteen commits: the module folder, the machine adapters (machine-ports.json, machine-recipes.json, machine_ports.py: the dropped detours), sdk/catalog.json, the import record, the two FM release tests, a new SDK test (test_machine_events.py), scripts/module-source.test.mjs, the module changelog, the package lag, the record of the hardware pass below and, last, the upstream pin moved to the commit of my released 2.22 line. It is an author update of a module I am registered for.

What is in

The author's Octatrick 2.21 engine and machine list (upstream/synth, upstream/quantizer, byte for byte), on the 0.1.4 wrapper:

What is out

  • MIDI-track quantizing: the seven quantizer hooks on MIDI and sample tracks (qz_mknob, qz_mplock, qz_mkey, qz_mnote, qz_mprev, qz_atrig, qz_heldmark) are dropped from the module (manifest.py _DROPPED); the image keeps those spans stock. They are Scale Quantizer's.
  • SY DRUM: not in this image; it comes as 0.1.5 after your base PR.

The FOLLOW OFF fix and the slot GAIN fix

upstream/ is pinned to my bb784c413d86cc6ef2a895f7291af46cb851065f, released with Octatrick 2.22 (v2.22 = 73686de, on v2.21 9e4d34a; bb784c4 on 342c533 on 4d25490 on 8f6f564, the commits of the release line; my working commits of 29-31 October, 990eff8 / be2810f / 3531b8e / 2fba0e5, carry the same code and differ from them only in README wording, and the vendored READMEs are the released ones): 2.21's engine, the quantizer source with the TRACK / FOLLOW rows that Scale Quantizer 0.1.4 carries, poly.s exporting sy_state, and two engine fixes. 342c533 (30 October): an FM SYNTH track whose per-track scale bit (#SEQUENCER_AUDIO_SCALE_OFF) is set now plays SCALE OFF's pitches; before, po_start read the scale mask without the track's bit, so only the ROOT transpose was dropped and the keys were still snapped (btst %d2,QZ_AUDIO_OFF_AT / beq / moveq #0,%d0 after jsr SCALE_AT, so T_SCALE is 0 for that track: 12 bytes, registers only, no project-format change). bb784c4 (31 October): an FM SYNTH track's level is the engine's. The stock frame builder copies the assigned slot's GAIN attribute (the per-track word 0x80001850 + 2t, the slot record's linear gain, 2048 = 0 dB) into halfword 29 of the track's DSP voice record every frame and the DSP applies it every frame; an FM SYNTH track is a FLEX-kind track with no sample, so a sample loaded into its slot (its .ot's GAIN, +12 dB after CLEAR SLOT) moved the FM level by that much on the sounding note, which I heard on my MKI. sy_render's sample-free path now writes 2048 into that halfword on every call for a chosen FM SYNTH track (registers only, no stock site; it runs after the writer and before the eDMA ship), so the slot's GAIN attribute is ignored on an FM SYNTH track; a sample track keeps the stock word, and a track that leaves FM SYNTH for FLEX takes its slot gain again at once. The pin is public with the Octatrick 2.22 push of octatrick-modules, which precedes this PR. sdk/imports/synth-9e4d34a.json still records the v2.21 files; five vendored files differ from that record (upstream/quantizer/README.md, manifest.py, quantizer.s, upstream/synth/README.md, poly.s), so test_source_identities_and_complete_notices fails until the import record is refreshed at bb784c4.

Compatibility

compatibility.conflicts names Analog BD and Scale Quantizer, as on main and as agreed on #555 (31 October, 1 November): FM Synth bundles its own copy of the quantizer, both modules would patch the same sites, and a module may not hook into another module's code, rows, tables or files. The marked note is in release-notes.json and src/community/module-changelogs/synth.json. Beside POLY8 this version still does not build (POLY8's composition record covers 0.1.2's chooser sites, #941).

The USB package stays 0.1.2 with a declared lag (lagsBehind: 0.1.4-experimental in octatrack-module-packages.json), its lagReason now written for players as #1133 asks ("Over USB this installs 0.1.2-experimental: 0.1.4's engine needs more memory than a USB package can be given today, so 0.1.4 comes as a card build; USB installs 0.1.2, without 0.1.4's step and scene locks, chord and envelope fixes and level change (#777)"). The engine's four DRAM regions (2 MiB step-lock work table, 32 KiB scene table, 2.5 KiB + 256 KiB migration buffers) and the units' generated includes are refused in a runtime package. The slim USB package you allowed on #777 (0.1.2 plus the ev_trig_muted ask and the stock LFO 3 depth read) is not in this PR; it comes separately.

Evidence

Images from the native exporter (scripts/export-composition-proofs.py --vendored-sdk --static-stock --modules synth), rebuilt byte-identical at the documentation commits:

Image Upgrade file MAIN OS
FM Synth alone (TEST9, on main 00ecaf52; tested on my MKI, below) 483,768 B 296f9c0e79086445419a47e167d706c0346c2f6e40f8fd12d314affd7fe02dec cd7c72c82229ffa1f4089cb11337cea30563ec3a83fea0094bce1d265fb7f9d0
superseded: FM Synth alone (TEST8, po_lfdname back; not tested on a unit) 483,780 B f726d4e540ba7a7e138be99fef91a957123dbf6881a46a589fb09d1f3aeb99ad d2d250a66ccadba18defb7b72f7c7981fc1aacbbd9f6e535c96ea4ce5853b142
superseded: FM Synth alone (TEST7, po_lfdname dropped; tested on my MKI on 30 October) 483,776 B 26ace38846b8c806324fe06559c74f8ca2f5a8ea49d46632879d5210b1885f94 265308da8e8e62a9daa7538b6f886198170578005737cd96cd3b16e06f586589

TEST8 is TEST7 plus the hook: the MAIN OS differs in 6 bytes, all inside the 8-byte site, and the runtime is byte-identical (the routine was linked all along). TEST9 is TEST8 plus the level fix: the branch head before the fix, rebased onto 00ecaf52, rebuilds TEST8 byte for byte, and TEST9's MAIN OS differs from TEST8's in 192 bytes of the stock section, every one a jump or reference target into the appended DRAM runtime moved by 40 or 42 bytes (the poly unit grew by the fix's 42 bytes), plus the runtime itself.

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 TEST9 image is unchanged; it was nevertheless rebuilt once from the branch head (21fd2357) on 1 November through the same exporter: byte-identical (every output file). This is the check #1138 proposes, done by hand.

Emulator (ot_emu, 29-31 October, builder and an independent verifier; the TEST9 image re-walked on 31 October): the level fix, in my own flow with my guitar.wav / guitar.ot (+12 dB), on the MKI panel: T1 FM SYNTH playing trigs, T5 FLEX, guitar.wav loaded into slot 1 from T5's slot list through the browser: the slot record reads +12 dB and the per-track word 8153, halfword 29 of T1's DSP record reads 0 dB at every snapshot, and MAIN rms stays at -31.2 dBFS (TEST8 at the same point: -19.2), after STOP / PLAY and on a held MIDI note (-30.0 before and after); a 40 ms watch of every write to that halfword shows the stock writer's +12 dB followed by the engine's 0 dB on every frame; FM SYNTH on T2 with the sample into slot 1 unchanged and into slot 2 unchanged; T1 switched to FLEX takes the slot's +12 dB word again, switched back to FM SYNTH reads 0 dB and the old level. The carry-over on TEST9: the 46-screen walk, the LFO SETUP names (MKI and MKII), INDX under LFO 1, the chords, the first chord after power-on (MKI and MKII), the AUDIO_SCALE_OFF project, the SEQUENCER rows, a MIDI track's NOTE C 3 -> C#3, and a save and power-cycle of an FM SYNTH track with VOIC 4 and SCALE MAJOR, all as on TEST8; the six media captures byte-identical to the 2.21 cut's and re-bound to this cut's MAIN cd7c72c8. Details and the gate table are in TESTING.md.

Hardware

I flashed TEST9 (the image above, 483,768 B, 296f9c0e…) on my Octatrack MKI on 1 November 2026 and ran its test sheet: every item passed. The record is in octamod.module.json (tests.hardwareStatus reported, a tests.gates line) and TESTING.md ("Hardware report — 1 November 2026"):

  • tested on: 2026-11-01
  • tester: timhastie
  • model: MKI
  • summary: the sheet's checks. The level fix in my own flow (FM SYNTH on track 1 with trigs playing, FLEX on track 5, guitar.wav with its +12 dB .ot loaded into slot 1 from track 5's slot list: the FM level unchanged at the load, on the next notes, after STOP / PLAY, and on a held note, a chromatic key and a MIDI note); FUNC + LFO on the FM SYNTH track lists PTCH RATO INDX FINE FDBK DEC then the AMP entries, a FLEX track still PTCH STRT LEN RATE RTRG RTIM, and INDX under LFO 1 moves the brightness; stock kept (a FLEX track plays guitar.wav at its +12 dB, track 1 switched to FLEX on slot 1 plays it at +12 dB too, back to FM SYNTH the FM level is what it was); FM SYNTH the sixth row of FUNC + SRC, a trig played without a sample; E PHRYGN, VOIC 4, chromatic mode: the same four keys together ten times and then fast one after the other give the same four notes; PROJECT 261028 power-cycled with PLAY pressed at once gives a chord on the first step; SEQUENCER shows the three stock rows then SCALE, ROOT and GLIDE only, no TRACK or FOLLOW row; with C MAJOR a MIDI track's NOTE steps C to C#.
  • limitations: play time, readings and the MIDI gear used were not recorded; the sheet's optional step (the FOLLOW OFF fix with a 2.21-saved project, and the sanity items after it) is not confirmed as run. 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.

TEST7 (MAIN 265308da…, the same engine without the LFO-names hook and the level fix) passed the same way on my MKI on 30 October 2026; that report stays in TESTING.md as the record of that image. TEST8 was not tested 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, before and after the record commit, and modules:check, module:doctor, licenses:check and the changelog test once more at the branch head 21fd2357 (the same results):

Check Result
npm run modules:check -- --base 501d8a42 fails: synth: owner-approved update does not cover this module version (sdk/synth-build-approval.json covers 0.1.2; the owner's record).
npm run module:doctor -- synth ✓ catalog entry, library, category guide, thumbnail, declaration checks (refused beside analog-bassdrum, quantizer, vector, each in conflicts), board claim; the USB package line reads the declared lag; ✗ module rules (the owner record above), packages (the committed packages lack 0.1.4), native comparison and package fingerprint (module:verify -- synth --os <OS> after the records), cross-compatibility (the 3 below).
npm run modules:compat -- synth 3 problems, as for every earlier cut, none new: Euclid's recipe copy inside the step-lock clip hook (0x4002b654), fl_io_dispatch (0x4008485e) on the base's own job hook, POLY8's composition record (0x400786c8).
npm run modules:pairs stops at POLY8 composition seam drift: synth detours 0x400786c8 (the owner's composition.json record).
npm run licenses:check passed (12 of 12).
npm run sdk:imports:check passed.
npm run sdk:check 503 tests: 20 failures, 7 errors, 28 skipped. The failures: test_module_compat (16 + test_repository_passes: the Euclid copy above; the same at the branch head before this commit), test_source_identities_and_complete_notices (the five vendored files past the public v2.21 import record, above), test_experimental_release_does_not_claim_hardware_or_timing (now at its version assertion: the approval record covers 0.1.2), test_changed_quantizer_layout_is_not_silently_supplied_by_fm (the POLY8 seam drift). The errors: the fm_runtime adapter tests (native binutils and the pinned Elekloader checkout, not on this machine; fm_runtime.py's update for 0.1.4's sources is mine, separately).
npx vitest run scripts/changelog.test.mjs passed (3 of 3).
npx vitest run src/catalog 247 of 251: module-qualification.test.ts and synth-release.test.ts (two) stop at the owner record above; native-comparisons.test.ts wants module:verify -- synth --os <OS> for the new code.
npx vitest run scripts/module-source.test.mjs 6 of 7: the committed module-build.json identity is not this source tree's (the packages are not rebuilt for 0.1.4; the same before this commit).
test_machine_events 13 of 13 (the stash-free run of sdk/tests/test_machine_events.py at the branch head).

npm run check -- --base 501d8a42 (the full app suite) stops at modules:check, the owner record above, as on #555; prepare, licenses:check and machines:check pass before it. The stock scan over every file this branch authors finds no run of 16 or more stock bytes; the linked runtime holds only Modwerk's displaced instructions, copied from the developer's own OS at build time.

How to test

  1. Build FM Synth alone with the native exporter (the configurator cannot regenerate the catalogue until the records are updated) and install it with PROJECT > OS UPGRADE.
  2. FM SYNTH on track 1 with trigs, PLAY; FLEX on track 5; load a sample whose .ot carries a GAIN other than 0 dB into slot 1 from track 5's slot list: the FM level does not change, not on the load and not after STOP / PLAY.
  3. FUNC + SRC on an audio track: FM SYNTH is the sixth row; it plays a trig without a sample.
  4. PROJECT > CONTROL > SEQUENCER: SCALE PHRYGN, ROOT E. On the FM track, VOIC 4, chromatic mode: press four keys together several times, then fast one after the other: the same notes every time.
  5. With C MAJOR, a MIDI track's NOTE steps C to C#: MIDI tracks are not quantized by FM Synth.
  6. Save, power-cycle, PLAY at once: the first step of a chord trig is a chord.

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, FM Synth is mine.
  • I read the guide for its category in docs/module-guides/ (machines, and sequencing.md), 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, 1 November 2026); emulator results are labelled as emulator results.

Owner review, not automatic publication: this version names addresses 0.1.2 never used, and the owner records (sdk/synth-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 #557, #710, #751, #589.

…current base

The module work of synth-2.20 brought onto main 1bbc8b4, with upstream/
re-vendored from the author's release 2.21 (v2.21 = 9e4d34a), byte for byte,
including the two new panel_transaction.inc.s files.

As agreed with Jannik on repeat98#555, the bundled quantizer's seven hooks on MIDI and
sample tracks (qz_mknob, qz_mplock, qz_mkey, qz_mnote, qz_mprev, qz_atrig,
qz_heldmark) and the LFO SETUP names hook po_lfdname (0x4003bff2, which meets
the base's 0x4003bff4) are left out of the module; FM tracks keep their
quantizing in the engine. SEQUENCER adds SCALE, ROOT and GLIDE only.

2.21's own manifest now links the PLAYBACK page in DRAM (repeat98#895), so the
wrapper's page unit and resolver detour are gone. The wrapper counts the
bundled quantizer as present, so the author's panel transaction is assembled
in both units, and exports sy_state from the poly unit for it (poly.s exports
it only with SY DRUM in the remix).

Machine record: LIST (the author's 2.18 double tap to the machine list), the
five row clamps and fm_chooser_col replaced by the base's ot_machines sites;
stock guards, import record and source pins regenerated.
README, TESTING, release notes and changelog for the author's 2.21: the
chord and first-chord fixes, the eight hooks left out (repeat98#555), SEQUENCER with
SCALE, ROOT and GLIDE, LFO SETUP's FLEX names (repeat98#589), the machine record's
flags. The six captures retaken from the TEST5 image are byte-identical;
ot-track-rows.png is gone. hardwareStatus untested: the TEST4 report was for
the 2.20 cut. The SEQUENCER row count's pea is built from its opcode and
count.
…ntizer source, sy_state exported by poly.s); rows stay SCALE ROOT GLIDE
…ons describe the install beside it and pin be2810f
…entical) and re-bound; TESTING records the build beside Scale Quantizer
… on an FM SYNTH track whose FOLLOW is OFF (po_start stores T_SCALE = 0 for a track with the AUDIO SCALE OFF bit); limitation text: a project's AUDIO_SCALE_OFF now covers FM SYNTH tracks too
…entical) and re-bound; TESTING records TEST7 / TEST2 and the FOLLOW OFF fix
…hared_supply rule is merged (owner, repeat98#555, 30 October); the author's MKI report of TEST7 and of the pair's TEST2 (30 October 2026), hardwareStatus reported without a qualification object; release notes carry the marked compatibility note; captures re-bound to the source
…m the public v2.21 record; hardware limitations trimmed to what was stated
…ild again, listed under the ot_machines contribution's replaces (owner, repeat98#555, 30 October); the synth port's source pins follow the 3531b8e re-vendor

Since the base's repeat98#1080 ot_machines owns the whole 8-byte site 0x4003bff2 and asks
mw_page first, the owner's note of 30 October on repeat98#555 applies: a card build, which
has no base, keeps the author's detour, and the package drops it for the table
(build.py replaced(): a table detour that starts inside an owned site). So
po_lfdname leaves _DROPPED, its guard moves from _READ_ONLY to the declared
guards (stock-guards.json as the 2.20 cut had it; digests only), and
machine-ports.json lists it under replaces. On an FM SYNTH track FUNC + LFO
now names the PLAYBACK entries PTCH RATO INDX FINE FDBK DEC (repeat98#589); FLEX tracks
keep PTCH STRT LEN RATE RTRG RTIM. The other seven dropped hooks stay out.

machine-ports.json's sources: manifest.py re-pinned for this change, and the
three vendored files the 3531b8e re-vendor changed without re-pinning
(upstream/quantizer/manifest.py, quantizer.s, upstream/synth/poly.s), so
test_ports_are_bound_to_the_sources_they_were_reviewed_against passes again;
machine-recipes.json's five pins of the port file follow. modules:compat --
synth: the same 3 problems as before, none at 0x4003bff2.
…hook back in the card build (TEST8, pair TEST3); hardwareStatus untested again for this image, the TEST7 report kept

TESTING: the owner's 30 October words on repeat98#555 quoted with the date; the image
table TEST8 (alone, 483,780 B f726d4e5..., MAIN d2d250a6...) and TEST3 (with
Scale Quantizer, 483,800 B e1b54ae1..., MAIN 779917c4...), TEST7 / TEST2 kept
as superseded rows with the author's MKI report of 30 October as their record;
the emulator evidence: FUNC + LFO on an FM SYNTH track lists PTCH RATO INDX
FINE FDBK DEC (MKI and MKII, both images), a FLEX track PTCH STRT LEN RATE
RTRG RTIM, the engine's index word T_I moves under LFO 1 on INDX, fm014's
46-screen walk equals TEST7's but the five PMTR screens, the author's own
remix at 3531b8e still 3f5e00ee...; seven hooks left out, not eight. README,
octamod.module.json (the LFO-names limitation removed; hardwareStatus untested
for TEST8 with the TEST7 report in TESTING, as the TEST4 report was kept),
release notes and the players' changelog (repeat98#589), the two release tests' pins.
Media: the six captures from TEST8 are byte-identical to TEST7's; re-bound to
MAIN d2d250a6... and to this source (capture.json sourceSha256).
…YNTH track's level is the engine's and the slot's GAIN attribute is ignored, so a sample loaded into the track's slot leaves the level unchanged; the synth port's poly.s pin and the recipes' pin of the port file follow; beside Scale Quantizer the module stays a declared conflict (the owner, repeat98#555, 31 October)
…) and the pair withdrawn (the owner, repeat98#555, 31 October: a module may not hook into another module's code, rows, tables or files); Scale Quantizer stays a declared conflict in one sentence; hardwareStatus untested for TEST9, the TEST7 report of 30 October kept
…EST9, upgrade file 296f9c0e, 483,768 B); the USB package's lagReason written for players (repeat98#1133)

The author flashed OCTATRACK_MODWERK_FMSYNTH_TEST9.bin (sha256 296f9c0e..., MAIN cd7c72c8...) on his MKI on 1 November 2026 and reported its test sheet passing. The record: tests.hardwareStatus untested -> reported, a tests.gates line, tests.summary and the last limitation updated, a "Hardware report — 1 November 2026 (TEST9)" section in TESTING.md naming the sheet's checks and what was not recorded (play time, readings, MIDI gear; the optional FOLLOW OFF step not confirmed), README's Tests and measurements, and the two release tests' hardwareStatus expectation (sdk/tests/test_synth_release.py, src/catalog/synth-release.test.ts). The TEST7 report of 30 October 2026 stays as that image's record; TEST8 was not tested on a unit. 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.

octatrack-module-packages.json: the synth package's lagReason (lagsBehind 0.1.4-experimental) rewritten for players, as repeat98#1133 asks: the module page and the sync dialog show it.
…eased 2.22 line (bb784c4) whose files these are; the vendored READMEs are that commit's

source.revision moves from v2.21 (9e4d34a) to bb784c413d86cc6ef2a895f7291af46cb851065f, the commit of the author's Octatrick 2.22 line (on v2.21) whose synth/ and quantizer/ files upstream/ carries. The manifest docstring, README, TESTING and the capture record name it, and the quantizer rows commit by its release id 8f6f564; the author's working commits of 29-31 October (990eff8, be2810f, 3531b8e, 2fba0e5) carry the same code and differed from the release line only in README wording. upstream/synth/README.md and upstream/quantizer/README.md are bb784c4's files, so every vendored file is that commit's byte for byte. The port's manifest.py pin and the recipes' pin of machine-ports.json follow the docstring; media/capture.json's sourceSha256 is re-bound to the native source. No code byte changes. tests.evidenceRevision stays 73490ef, the Modwerk commit the evidence files name.
@repeat98

repeat98 commented Nov 2, 2026

Copy link
Copy Markdown
Owner

Thanks Tim, and for the careful check table. Two answers:

  1. The level. 12 dB louder than 0.1.2 is fine, with the release note saying so as it does.
  2. The records and the three modules:compat items (POLY8's composition record, Euclid's recipe span at 0x4002b657, and 0x4008485e, which the core logger also patches on every card build) are mine. I'm working through them now. If one of them needs a change on your side, I'll say so here. The two approval records (FM Synth and Scale Quantizer 0.1.4) come after that.

Generated by Claude Code

@repeat98

Copy link
Copy Markdown
Owner

Tim, I owe you an answer here, and the ground has moved under this one, so both at once.

The card builder is retired. Since today (#1451, #1453) the Octatrack pages build no card firmware any more, and the composer, the native comparison and the pair table are gone from the repository. Every Octatrack module now ships as a package for the Modwerk base, and nothing else. That decision is about the whole platform, not about FM Synth, but it hits this pull request hardest: 0.1.4 was cut as a card build with USB staying at 0.1.2, and there is no card build to publish it through now.

What that means for the open items:

The way forward I'd suggest:

  1. The slim USB package you offered on [synth × mute-modes] FM Synth plays muted trigs in DT-T / OTFX-T (reproduced): synth starts notes from the START bit before Mute Modes' trig gate #777 (0.1.2 plus the ev_trig_muted ask and the stock LFO 3 depth read) is now the quickest thing that reaches players. Please send it as its own pull request.
  2. For the 2.21 engine over USB, the question is memory: the 2 MiB step-lock table, the 32 KiB scene table and the two migration buffers are more than the loader gives a package today. Could you tell me what the engine needs at run time at the least, and whether the step-lock table could be claimed when the first lock is written rather than at install? With that I can decide what the base has to offer.

I know this is a lot of careful work to put on hold, and I'm sorry it took me ten days to say so.

@timhastie

Copy link
Copy Markdown
Contributor Author

Thanks Jannik, no need to be sorry, and the card decision is fine with me. I'd like to go straight for 0.1.4 as a package for the base rather than a slim package first. Here is what I found and what I'm building.

Your two questions

  1. The least memory at run time. As FM Synth 0.1.4: the author's 2.21 engine; MIDI quantizing moves to Scale Quantizer #1146 declares it: 99,780 B of code and data in one image (linked with today's link_units: 739 relocations, 111 sites, 47 stock copies), plus 2,394,624 B of DRAM regions. Most of that is padding. The step-lock table uses 2 bytes of each 16-byte row, so its content is 262,144 B. The 256 KiB text buffer only holds project.work to find one comment line and can be streamed. The scene table is 32 KiB.
  2. Claiming the step-lock table late. Yes. I'm changing the engine to do that inside the package's own memory: rows packed to 2 bytes, and 128-byte lock lanes taken when the first lock is written in a track's pattern, or when a project with lock files loads. A project without FM locks takes none. With no DRAM region the package should fit the 131,072 B limit with room for about 260 lanes (an estimate until it's built). Card files and sound stay the same.

What I'd need from you

No rush on any of it. I'm building and testing locally first.

@repeat98

Copy link
Copy Markdown
Owner

Thanks Tim, going straight for 0.1.4 as a package is fine with me, and packing the lock rows to 2 bytes and taking lanes on first use is exactly what I hoped for. In order:

ev_engine_job (#1272). Most of it is in the contract already: stock runs job types 0-45 and skips the rest, and order 10-29 is for a module's own job types above 45. I'll add three lines to #1272:

  • A module may post a job of its own type to the engine and take it there (return 1). Each type above 45 belongs to one module, listed in HOOKS.md, so two modules never pick the same number. Tell me which numbers FM uses.
  • A job still queued when its module is removed reaches stock's switch, which skips types above 45, so it is dropped. A job should therefore carry values only, nothing that has to be freed or answered.
  • Removal never frees a module while the engine task is inside its handler (the loader's paused-stack rule).

If posting needs stock's send routine as something the base exports, rather than a call into stock, say which and I'll add it to the same pull request.

The late claim: not for now. All modules share one 768 KiB pool on the Octatrack. Every published module installed at once already comes to about 430 KiB, and FM at about 128 KiB plus 288 KiB on top would no longer fit beside them. A claim that fails halfway through a set is the worst moment to find out. Please go with the in-package pool and the message when it's full. Once players have it, we'll see how many lanes real projects use, and if 260 isn't enough we'll look for memory outside the pool.

Euclid is mine to fix. Those 5 bytes at 0x4002b657 are Euclid's own instructions that happen to equal stock, so its recipe rebuilds them from stock, and that fails once FM has patched its site there. Euclid's next update will carry them itself. Nothing to change on your side.

Scale Quantizer: yes. Send 0.1.5 without the two panel sites first; once it's in, drop FM's catalogue conflict with it.

This pull request: yes, please close it and open a new one from current main when the package is ready. One thing to build on: my FM Synth 0.1.3 (#1473) makes 0.1.2's render path cheaper on tracks that don't use FM (one Part read a frame, and stock renders a non-FM track straight away), and since #1483 the base runs FM's renderer through the frame guard and CPU budget. 0.1.3 lands before 0.1.4, so please keep both in 0.1.4's render path.

The shared scale (#1145): yes, send it now. Claim its hook index on the agent board first.

This branch has not been deployed

No deployments
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