Repository navigation
Fix: Octatrack card builds, too many module voices no longer stall the unit (frame guard) - #1030
Merged
Merged
Conversation
…e unit (frame guard in the core logger) Card builds have no base, so the USB base's frame guard (#874) never reached them: a busy project with POLY8, FM Synth or VECTOR voices kept the frame interrupt near a whole frame and the DSP stopped until a power cycle (#844, #1015). The core logger every card build carries now holds the same guard: it stamps the frame interrupt's entry and exit and wraps the record packer's two renderer calls. A module machine track (Part signature PL, FM or S2, read once a frame) whose render would overdraw 80 % of the frame renders stock's silent source instead. Stock tracks and Analog BD always render; their renders cost four instructions in the hook. GRD records in OCTAMOD.LOG count the dropouts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(identity only) The USB base compiles the logger's C and reuses its hook generator, and has its own frame guard (machine_registry.c). build_core.py now skips frame_guard.c and asks package.hooks() for the hooks without the guard, and the GRD report moves from the recorder into frame_guard.c, called from the card build's idle hook. The base still hashes every file of the logger folder, so it is regenerated: its code and data are unchanged, only the identity strings move (88c68eed -> 9b7e0eb3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the card-build half of #844 and #1015 (and probably #981).
What was wrong
Card builds have no base, so the frame guard from #874 never reached them. With POLY8, FM Synth or VECTOR voices in a busy project, the frame interrupt ran close to a whole frame on every frame. The DSP then stopped until a power cycle. #1015's log shows exactly that on a card build: POLY8 reaches its 8-voice cap, then its render counter and the sequencer's command counter stop for good while jobs and USB keep logging.
What this changes
Every card build carries the core logger (
sdk/runtime/logging), so the guard goes in there and no module changes:frame_guard.cis the USB base'sguarded()/mw_frame_exit(same budget: 80 % of a frame, up to half a frame saved, a probe every 1,024 frames within 10 %). A module machine track whose render would overdraw it renders stock's silent source (0x400047f0).0x4000aad0and exit0x4000d9a6are the same as the USB base'sFRAME_SITES, same hashes. The record packer's two renderer calls are0x4000d43a(replays 4 of 6 bytes) and0x4000d524(replays 8 of 10 bytes); the newreplayfield instock-guards.jsonkeeps stock's argument pushes and drops thejsr.PL,FMorS2, the same test the card machine registry (shared-machine.c owner()) and FM Synth (fm_is_native) use. The mask is computed once a frame at the exit, so any other track's render costs 4 instructions in the hook. Analog BD (its voice runs on the DSP) and stock tracks always render.0x4000d9a0rejoins at0x4000d9a6, the guard's exit. FM Synth's packer site0x4000d514rejoins at0x4000d51c, before the second call. Mute Modes'0x4000d498sits between the two calls.GRD 0001(W) in OCTAMOD.LOG reports the silenced renders and which tracks, at most one record per 10 s, from the card build's idle hook (olog_frame_report), never from the interrupt. The site's log parser takes it as is.core-logger.tsandbuild_local.pyinstall the four new hooks.package.hooks(), sobuild_core.pynow skipsframe_guard.cand asks for the hooks without the guard (frame_guard=False). The base still hashes every file in the logger folder, so the beta base is regenerated. Its code and data are unchanged; only its identity strings move,88c68eed→9b7e0eb3, and the browser.binstill equalsbuild_core.py's. Testers would see one base-update prompt for nothing new. If you'd rather avoid that, merge this with the next real base change (Octatrack base: PERSONALIZE rows as a base table (ot_personalize_rows), so table owners no longer pin package hashes #1023, say) and regenerate once.How I checked it
sh sdk/runtime/logging/tests/run_host.sh: all pass, including the newguard_test.c(which also checks theGRDrecord's count, track bits and 10 s spacing). Under a simulated frame timer and Part, a heavy POLY8 track is silenced on some renders while the stock track beside it renders every time. A light one is never silenced.FMandS2tracks are guarded;AB, an unsigned slot, a THRU track with stale bytes and an unloaded bank pointer are not.package.py: main's 0.2.0 sources rebuild byte-identical to the committed package with this toolchain, so the new bytes come only from the change.node scripts/verify-core-logger-native.mjs: 12 native link cases match GNU bytes and symbols.composeStaticOsfrom the local 1.40C, ten selections. Core only, POLY8, FM Synth, VECTOR, the three together, USB Audio + POLY8, Mute Modes + FM Synth, Analog BD, Analog BD + POLY8 + FM Synth, and [poly8] Audio freezes and sequencer won't play #1015's eight modules all compose, with the four sites patched. Standalone MIDI Scenes composes too.ot_emu --mkii --dsp --framewith [poly8] Audio freezes and sequencer won't play #1015's set and no card. Frames run at 44.1 kHz / 16 through all four hooks, and idle after 4.9 s matches main's image of the same set (21,978 vs 21,979). WithPL,1 and FLEX poked into T1's Part slot, the mask reads 1 and the guard's render runs every frame for 2 s with no fault.npm run pr:ready: Ready.Not tested: any unit. Nobody has yet seen an overload silenced (or a stall prevented) with this guard on hardware. Also untested: an MKI, a POLY8 project in the emulator, and a
GRDrecord on a real card. Before merging, it needs a card image of an overloaded POLY8 project on the MKII; that's a flash, so it needs your go-ahead.Limits
What the guides lacked
Two lines said card builds have no guard: in
docs/OCTATRACK_HARDWARE_TRAPS.mdand in the machines guide's "bounded render" item. Both now say the card build's logger has the same guard. The logger's README and FORMAT.md describe the guard and theGRDrecord.🤖 Generated with Claude Code