• src/doors/syncscumm/door/audio_term.cpp audio_term.h src/doors/syncscu

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Fri Jul 17 02:57:08 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/54648707ec2bc73ecb4afca3
    Added Files:
    src/doors/syncscumm/door/audio_term.cpp audio_term.h src/doors/syncscumm/test/test_sst_io_audio.c test_sst_io_audio_backlog.c test_sst_io_audio_ini_headroom.c test_sst_io_audio_ini_off.c test_sst_io_audio_ini_tune.c test_sst_io_audio_static.c test_sst_io_audio_tone.c test_sst_io_audio_underrun.c
    Modified Files:
    .gitlab-ci-unix.yml docs/superpowers/plans/2026-07-16-syncscumm-m4.md src/doors/syncretro/CMakeLists.txt M4_AUDIO.md syncretro_audio.h test_audio_bytes.c src/doors/syncscumm/DESIGN.md build.sh src/doors/syncscumm/door/CMakeLists.txt module.mk sst_io.c sst_io.h syncscumm.cpp video_term.cpp src/doors/syncscumm/syncscumm.example.ini src/doors/syncscumm/test/subtitles.sh unit_sst_io.sh src/doors/termgfx/audio_stream.c audio_stream.h chunk.c src/doors/termgfx/test/CMakeLists.txt test_audio_stream.c
    Log Message:
    syncscumm: stream the game's audio to SyncTERM

    Speech, effects and music, over the existing door connection. A terminal
    that cannot play audio is untouched: silent, subtitles auto-on, not one
    audio byte on the wire.

    ScummVM sums every voice, effect and synthesized track into one mixer, so
    one pull covers every engine and every sound type -- there is no useful per-sound boundary to hook, and music arrives already rendered. The pull
    rides the wall clock rather than the game loop, so the game's pacing can neither starve nor flood the stream and audio cannot drift against video;
    it ticks from both pollEvent() and the present path, because the intro
    drives updateScreen() in a tight loop without pumping events, which is
    exactly the cutscene the audio exists for.

    The terminal is asked what it can decode, and the answer drives subtitles:
    auto now means "will this player actually hear it", which the sysop's
    switch also decides. Audio and video share one staged queue with nothing reordered; the door reports audio's own share of it as backlog, so the
    drop rule fires on real audio congestion rather than on a large frame.

    Three things had to be found by listening and then measured, and all three
    had a plausible wrong answer first:

    Dialogue dropped out for seconds at a time. The streaming module flushes
    when its cushion releases and at stop, and otherwise leaves writing to the door's loop -- which only flushed while presenting a frame. A comic panel
    is a still: drawn once, talked over. Nothing drawn, nothing flushed, so
    chunks sat staged while the module blamed a link that was idle.

    It popped ten times a second. Each chunk is its own Opus stream, so every boundary carried an encoder delay and a decoder ramp with nothing to lap against. Chunking is not the problem -- SyncTERM's queues are for exactly
    this -- doing it ten times a second was. 250ms chunks put the boundary
    rate back at chance and cut a third of the payload.

    The loudest scene tore. The mix hard-clips there, a codec reconstructs
    flat tops with ringing that overshoots, and libsndfile's short reader
    wraps instead of clamping -- so an overshoot came back as a full-scale
    sign flip, thousands of them. Merely loud passages never overshoot. The
    stream is attenuated before the encoder and the channel plays at unity,
    which nets to the gain it always had: the headroom simply arrives before
    the encoder rather than after the decoder, where it could do nothing.

    Sysops get [audio] for the rest: enabled, quality, volume, chunk_ms,
    prebuffer, headroom, channels.

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