Skip to content

SDK: a card-build counterpart of ev_trig_muted (#777) - #1312

Open
timhastie wants to merge 1 commit into
repeat98:mainfrom
timhastie:sdk-card-trig-muted
Open

timhastie wants to merge 1 commit into
repeat98:mainfrom
timhastie:sdk-card-trig-muted

Conversation

@timhastie

Copy link
Copy Markdown
Contributor

SDK: a card-build counterpart of ev_trig_muted (#777)

@repeat98 @Zac-Kyoti, this is the SDK part of option 2 on #777: a meeting point where Mute Modes can answer and FM Synth can ask on a card build, with neither module reading the other's code or data. It changes no module and no card image.

What is in

sdk/machines/octatrack/card/trig_muted.inc (GNU as) defines one symbol, modwerk_card_ev_trig_muted.

  • The answer is unsigned f(void) in the C convention and writes nothing. Bits 0-7 have the same meaning as in ev_trig_muted: bit t means track t+1's new trigs are dropped now.
  • Bits 8-15 are new. They mark which of those tracks also have their sounding note ended at the mute, as Mute Modes ends a sample track's note in OTFX-T with a note-off. FM Synth holds its own voice and ignores that note-off, so without this byte it cannot tell OTFX-T from DT-T. The base event ignores bits above 7 today, so the USB answer could take the same byte later.
  • The ask is a macro, MODWERK_CARD_ASK_TRIG_MUTED. It makes a weak reference to the answer and returns 0 when no module answers, so the machine plays every trig, as stock does.
  • Placement. Both the answer and the ask sit in DRAM units. Every card build links its DRAM units in one link, natively in remix/platform_build.py and in the configurator in coldfire-link.ts, so the two meet whatever the module order. ROM units can't take part: the native builder links them one at a time before the DRAM link, and the configurator links them after it.
  • One answer per image. Both linkers refuse a second definition. An OR over several answering modules would need a table in the builder; I left that out until a second mute module exists.
  • Docs. card/README.md, a line in sdk/machines/octatrack/README.md, the muted-trigs item in docs/module-guides/machines.md, and the changelog entry.

Tests

  • sdk/tests/test_card_trig_muted.py links an asking unit, an answering unit and a bystander unit with the native DRAM link (platform_build.link_runtime). It covers neither unit, asker only, answerer only, both, and both in the other order.
    • The asker alone loads 0.
    • The answerer alone is linked, and nothing calls it.
    • Together, the ask loads the answer's address in either order.
    • The bystander's bytes never change.
    • A second answer is refused.
    • It also checks the contract against events.json.
  • src/engine/card-trig-muted.test.ts links the same objects with the configurator's linker. It gives GNU's bytes and symbols in all five cases and refuses a second answer.
  • npm run sdk:check: 568 tests OK. modules:generate leaves the tree unchanged. modules:check -- --base origin/main and sdk:imports:check pass. In npm run pr:ready on f577d62 every stage passes except vitest, whose failures were timeouts on a loaded machine. Those test files pass when re-run under Node 24 with a long timeout (122 files, 1,115 tests).

End to end, on prototypes that are not in this PR

I wrote local prototypes of both sides to check that the contract does the job:

I built them with the native exporter and ran them on ot_emu (MKI, DSP lockstep). T1 was FM SYNTH and T2 a FLEX sine with the same trigs, both muted mid-pattern:

MUTE MODE 0.1.4 + Mute Modes 0.2.2 with the ask and the answer
DT-T: the six trigs after the mute all play, -27.0 dBFS peak none, -90.3 dBFS
DT-T: the note sounding at the mute (HOLD INF) rides on, -30.1 dBFS rides on, -30.1 dBFS
DT-T: after STOP, still muted rings, -30.0 dBFS ended, -106.2 dBFS
OTFX-T: the six trigs after the mute all play, -27.0 dBFS none, -90.3 dBFS
OTFX-T: the note sounding at the mute rings on, -30.1 dBFS cut: -60 dBFS within 20 ms, under -80 dBFS by 130 ms
OT and OTFX not run silent after the mute (answer 0)

T2, the plain sample, behaved the same in both images.

The image with both prototypes differs from the 0.1.4 + Mute Modes image in 3 ROM bytes (one detour target) plus the runtime. FM Synth alone with the ask differs from #1146's image in the same 3 bytes, and its ask loads 0.

One question before anyone builds on it

A DRAM unit reserves 1,707 pages (10,487,808 B) of the sample arena (coldfire-runtime.ts:28). So a card build with Mute Modes but no other DRAM unit loses that much sample memory just to carry an answer nobody asks. Beside FM Synth, which already has a DRAM unit, the answer adds only its own bytes. Jannik, which would you prefer?

  1. Accept that cost.
  2. A small builder flag so the answer is linked only when another requested DRAM unit is already linked. Every asker is a DRAM unit, so the answer would then cost nothing extra. I can add it here or as its own PR.
  3. Let a ROM unit answer. That needs the configurator to link ROM units before the DRAM link, or to pass ROM symbols into it.

Zac, thanks for agreeing on #777. Are you OK with the meaning of bits 8-15? On your STOP point: with both prototypes, a muted FM note at HOLD INF ends at STOP (the DT-T row above). FM Synth's ask comes as its own release after this lands, and nothing here changes 0.1.4.

A card build has no base events. sdk/machines/octatrack/card/trig_muted.inc names one
meeting point, modwerk_card_ev_trig_muted: a mute module answers it from a DRAM unit, a
machine asks it from a DRAM unit (a weak reference), and with either one missing the ask is
0 and nothing changes in what plays. Bits 0-7 are ev_trig_muted's answer; bits 8-15 name the tracks whose
sounding note the mute ends (OTFX-T's note-off), which an engine that keeps its own notes
needs. Host tests: the native DRAM link and the configurator's linker give the same bytes for
every combination and refuse a second answer.

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.

1 participant