Skip to content

feat(tfmx): play Dynamic Synthesizer via libtfmx 1.0.14 bump - #105

Merged
indigo423 merged 1 commit into
mainfrom
chore/bump-libtfmx-1.0.14
Jul 10, 2026
Merged

feat(tfmx): play Dynamic Synthesizer via libtfmx 1.0.14 bump#105
indigo423 merged 1 commit into
mainfrom
chore/bump-libtfmx-1.0.14

Conversation

@indigo423

Copy link
Copy Markdown
Collaborator

Completes Dynamic Synthesizer support. Companion to the routing slice in #104 (now on main) — this brings the decoder and makes DNS pairs actually play. Closes the goal of #103.

What

  • Vendor bump v1.0.7 → v1.0.14 (vendor/libtfmxaudiodecoder/, tag 1.0.14, commit a07d8860) + rebuilt public/libtfmx.worklet.js from source under emscripten 6.0.2.

    • 1.0.10 adds the Dynamic Synthesizer decoder (src/Chris/DNS/).
    • 1.0.11–1.0.14 add playback fixes we were missing at 1.0.7: the v1.0.9 loop-mode duration-regression fix (some TFMX songs currently play short), Turrican II jingle, R-Type, and Turrican III (1.0.14 macros). Retargeted 1.0.10→1.0.14 because we recompile from source, so newest is free.
    • No public C-API change (only the version define), so the worklet's ccall bindings are untouched.
  • Make DNS pairs decode (public/tfmx.worklet.js, lib/audio-player.ts, components/Player.tsx). This is the part that went beyond the original routing-only scope, and it's the crux:

    libtfmx's Dynamic Synthesizer decoder finds its sample bank by the dnssmp filename token, not the generic .tfx.sam guess the Hülsbeck-TFMX conventions use.

    The worklet's hardcoded .tfx/.sam MEMFS normalisation therefore makes tfx_load fail for DNS. Fix: the play message carries a dns boolean (derived from the dns.-prefixed data-half filename), and the worklet writes DNS halves as dns.<base>/smp.<base>. Every other pair convention is untouched.

How it was found + verified

A headless Node harness loading the built wasm and replaying the worklet's exact load sequence:

MEMFS naming tfx_load
<base>.tfx / <base>.sam (old pair path) FAIL fmt="???"
dns.<base> / smp.<base> (new) OK — "Dynamic Synthesizer (AMIGA)", 17.3s, non-silent PCM

End-to-end through the real app (run-coolmodfiles driver, LIBRARY_ROOT fixture with a real Modland dns.ptc/smp.ptc pair):

  • DNS pair playsplay: OK (tfmx-library), no error, renders as ptc (TFMX).
  • Regression → FC single (Future Composer 1.0-1.3) and Hippel (Compressed TFMX/Hippel) still decode (wasm harness) and FC plays through the app (tfmx-single-library).
  • Byte-server: dns./smp. halves served (200) with partner; lone dns. half → 404 (orphan rejection intact).
  • make verify green (lint + typecheck + 252 unit tests + audit + next build).

Notes

  • The rebuilt wasm (public/libtfmx.worklet.js, 261→274 KB) is a committed generated asset per VENDORING.md; the vendored source it was built from is in the same commit (GPL corresponding-source requirement).
  • Follow-up unchanged: coordinate the Amiga-mode pair label with add-amiga-prefix-filenames (its renderTfmxPairLabel hardcodes mdat./smpl.).

Refs #103

🤖 Generated with Claude Code

Bump the vendored decoder v1.0.7 -> v1.0.14 (upstream tag `1.0.14`,
commit a07d8860) and rebuild public/libtfmx.worklet.js. 1.0.10 added
Chris Hülsbeck's Dynamic Synthesizer decoder (src/Chris/DNS/); 1.0.11-14
add playback fixes incl. Turrican III and the v1.0.9 loop-mode duration
regression fix our old 1.0.7 pin was missing. Rebuilt from source under
emscripten 6.0.2; no public C-API change, so the worklet bindings are
untouched.

Make Dynamic Synthesizer pairs actually decode. libtfmx's DNS decoder
discovers its sample bank by the `dns.`<->`smp.` filename token, NOT the
generic `.tfx`->`.sam` guess the Hülsbeck-TFMX conventions use — so the
worklet's hardcoded `.tfx`/`.sam` MEMFS normalisation makes tfx_load
fail (verified `load=0` against a real Modland dns.ptc/smp.ptc pair).
Fix: the play message carries a `dns` boolean (derived from the
`dns.`-prefixed data-half filename) and the worklet writes DNS halves as
`dns.<base>`/`smp.<base>`. Other pair conventions are unchanged.

Verified: DNS pair decodes ("Dynamic Synthesizer (AMIGA)", 17.3s,
non-silent PCM) via a headless wasm harness AND end-to-end through the
app's tfmx-library path (play OK, no error). Regression: FC + Hippel
still decode and play post-bump. `make verify` green; 252 unit tests.

Companion to the routing slice in #104. Refs #103.

Assisted-by: ClaudeCode:claude-opus-4-8
Signed-off-by: Ronny Trommer <ronny@no42.org>
@indigo423
indigo423 merged commit d3e4072 into main Jul 10, 2026
4 checks passed
@indigo423
indigo423 deleted the chore/bump-libtfmx-1.0.14 branch July 10, 2026 08:23
indigo423 added a commit that referenced this pull request Jul 10, 2026
…106)

Complete the two deferred test tasks for Dynamic Synthesizer support
(#104/#105 shipped the feature; these add unit coverage).

- Extract the worklet `dns` flag derivation from Player.tsx's inline
  regex into a pure exported helper `isDnsDataHalf(name)` in
  lib/tfmx/pairs.ts, and unit-test it: `dns.`-prefixed true (spaces,
  case-insensitive), literal dot required (`dnsomething.mod` false),
  sample half + other conventions false, empty false.
- Test the byte-server's two gates for dns./smp. by exercising the
  predicates pages/api/library/file.ts uses — parseHalfName (allowlist)
  + detectPairsInDir (partner existence) — mirroring the repo's
  predicate-level convention (random.test.ts), not a mounted handler.
  Served when the partner exists, 404 for an orphan dns. half, 404 for
  a non-allowlisted path.

Full suite 260 passed; typecheck + lint clean.

Refs #103

Assisted-by: ClaudeCode:claude-opus-4-8

Signed-off-by: Ronny Trommer <ronny@no42.org>
indigo423 added a commit that referenced this pull request Jul 10, 2026
New format: Chris Hülsbeck's Dynamic Synthesizer (#104, #105), on the
libtfmx v1.0.7 → v1.0.14 decoder bump. Test coverage in #106.

Assisted-by: ClaudeCode:claude-opus-4-8

Signed-off-by: Ronny Trommer <ronny@no42.org>
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