• src/doors/syncdoom/i_termmusic.c syncdoom.c src/doors/syncduke/syncduk

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Wed Jul 1 02:20:56 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/f13692ed01697574f95d9002
    Modified Files:
    src/doors/syncdoom/i_termmusic.c syncdoom.c src/doors/syncduke/syncduke_plat.c syncduke_stubs.c src/doors/termgfx/audio_mgr.c audio_mgr.h
    Log Message:
    doors: async music transcode (render off the game thread)

    A first-play (cold-miss) track had to render MIDI->OPL PCM (~seconds) then encode
    OGG inline, freezing the game during the level load (5-9s observed). termgfx now
    offloads that to a worker thread: the door SUBMITs the raw MIDI/MUS bytes and POLLs each frame; the game keeps running and the track fades in when ready. Cache
    hits still play instantly on the main thread.

    - termgfx (audio_mgr.c/.h): termgfx_audio_music_async_submit/_poll. The worker
    renders + encodes + writes the disk cache + pre-builds the C;S upload APC, so
    the main thread only memcpy-stages ready-to-send bytes (out_flush drains them
    over frames) and starts the loop. Latest-wins supersede via a generation token;
    worker created lazily, joined on destroy. POSIX threads; Windows keeps the
    synchronous path (no regression).
    - SyncDuke: PlayMusic -> submit (syncduke_stubs.c), poll in _handle_events
    (syncduke_plat.c). SyncDOOM: term_emit -> submit (i_termmusic.c), poll in
    DG_DrawFrame (syncdoom.c).

    Live: a ~7s SyncDOOM render and a ~5s SyncDuke render each ran fully in the background at sustained fps with no frame-stall, and the worker populated the disk
    cache so replays are instant. Standalone harness (submit/ship, supersede, clean destroy) passes; helgrind clean (0 races).

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

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net