• src/doors/syncretro/probe

    From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Tuesday, July 21, 2026 01:41:00
    https://gitlab.synchro.net/main/sbbs/-/commit/f72a5729d772dc5db32a1c2d
    Added Files:
    src/doors/syncretro/probe_core.c
    Modified Files:
    src/doors/syncretro/CMakeLists.txt
    Log Message:
    syncretro: add probe_core, a libretro core diagnostic

    Every SyncRetro console so far was scoped from measurement rather than from
    a core's documentation -- M3_MULTICORE.md sec 2 is a table of what fceumm
    was observed to do -- but the harness that produced it was thrown away. This
    is that harness, kept, because the questions that decide a console's cost
    are all empirical and all cheap to get wrong from a distance:

    - does the core ask the FRONTEND to rotate (env 1), or hand us an
    already-rotated buffer? Decides whether a portrait arcade game needs a
    rotate path in retro_bridge.c or nothing at all;
    - what geometry/fps/sample_rate does it report, and does it CHANGE them
    after load (env 32/37), which the door reads only once;
    - which core options does it actually read, and is it playable on their
    defaults;
    - how many distinct colours does a frame really have, i.e. is the sixel
    quantization exact.

    It links the door's real retro_core.c and retro_options.c and answers env exactly as retro_env.c does, so the report is the door's own view of the
    core and a probe run is a live test of the shipped option store. Each policy the door does not implement can be flipped on (-accept-rotation, -optver)
    and each one it does can be flipped off (-no-options); that difference is
    the work estimate. It links no termgfx, no xpdev and no libjxl, opens no terminal and needs no BBS.

    Opt-in: cmake -DSYNCRETRO_PROBE=ON. Not built by default and not part of
    the door.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Tuesday, July 21, 2026 02:32:00
    https://gitlab.synchro.net/main/sbbs/-/commit/82010f9a06b1c941907003d1
    Modified Files:
    src/doors/syncretro/probe_core.c test_audio_bytes.c test_audio_bytes.golden src/doors/termgfx/audio_stream.c audio_stream.h chunk.c src/doors/termgfx/test/test_audio_stream.c
    Log Message:
    termgfx: remove any DC offset before the encoder sees it

    Ogg/Opus cannot represent DC. Encode a chunk holding a constant level and the decoder hands back a decay from that level toward zero -- measured, a constant -17536 comes out around -6000 by the end of a 50 ms chunk. A door streams
    those chunks back to back, so every boundary is a step: a 20 Hz sawtooth at about a third of full scale, heard as a continuous low click or rumble that stops only when louder audio masks it.

    Nothing about that is specific to one door or one source. SyncConquer hit it through an IMA-ADPCM predictor drifting into a growing offset (its PROVENANCE.md patch 8; the hazard is written up in termgfx/README.md). SyncArcade hit it from an unrelated direction: MAME 2003-Plus drivers whose
    DAC idles off zero -- Joust sits at a constant +7710 and Xenophobe at -17536, peak to peak ZERO. That is silence at the wrong level, and termgfx_chunk_is_silent() tests for exact zeros, so those chunks were encoded and uploaded as if they were music.

    The constraint belongs to the encoder, so the defense goes here rather than in either door: a one-pole ~10 Hz high-pass over the source's PCM in termgfx_stream_feed(), before anything is chunked. Ten Hz is three decades below anything a game plays and below the floor of hearing, so no audible content is touched. The filter state is double precision because an integer accumulator at this pole rounds its own feedback every sample and settles into a limit cycle -- trading the buzz for a quieter buzz.

    Removing the offset also restores the free silence path. Once the filter settles, an idle source at a constant level reads as exact zeros again, and a zero chunk is the cached "<prefix>/z" replay that costs no bytes at all -- so the games that provoked this now cost less than they did before. The price is at the transition: the filter decays over ~170 ms rather than snapping, so about two chunks after a sound stops still encode. test_audio_bytes' scenario feeds three silent chunks instead of one and its golden pins that whole shape, rather than hiding the cost by pinning only the settled end of it.

    A DC offset is usually a symptom of something else being wrong -- a mis-converted unsigned format, a drifting decoder, a core's idling DAC -- so fixing it silently would be the wrong trade. The stream logs the raw offset once per session, which is how the next one gets found from a log rather than from a player hearing a hum.

    probe_core grows -wav and a DC/peak/silent-frames tally, which is what
    measured all of the above without needing a terminal.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, July 24, 2026 11:06:00
    https://gitlab.synchro.net/main/sbbs/-/commit/7e99193e64e08c63d2c7f2cd
    Modified Files:
    src/doors/syncretro/probe_core.c
    Log Message:
    Add -hold option to probe_core for button measurement

    probe_core now supports holding a single RetroPad button (0-15) from a specified frame to the end of the run. This enables measuring which
    buttons fire arcade game actions, essential for filling the cabinet
    descriptor table. The -hold-from option defaults to 500 frames, after
    the game has started via -coin injection.

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