Build coop_audio_validate, a tool that turns "the mirror audio sounds off" into numbers. It plays a known sine (coop_tone, 44.1 kHz on a 48 kHz endpoint -- the Godot/Brotato case), injects the hook as the host does, and runs a fidelity analyzer (coop/tone_analysis.hpp: pitch error in cents, SNR/THD, click + dropout counts), dumping a .wav to listen to. Modes: --render drives the real AudioMirror and measures its rendered output; --baseline/--selfcheck give the measurement floor; --listen <pid> records a live coop_host's output; --wav analyzes a recording. Analyzer + WAV I/O are unit-tested (tone_analysis_test) against synthesized defects. Using it, the capture ring measures pristine (~68 dB, 0 gaps) while the render path dropped to ~18 dB with gaps -- localizing a real defect in AudioMirror::run_hooked: it re-primed (withheld the feed until ~30 ms had rebuffered) on any partial fill (to_write < avail). A partial fill is normal producer jitter, and withholding the feed drains the device, so a one-frame ring dip became a full ~30 ms drop-out; on a jittery game it fired constantly. Fix: feed whatever is available each tick and re-prime only on a genuine starvation (device empty AND ring empty). The policy is factored into a pure RenderPacer reused by run_hooked + run_loopback and proven by render_pacer_test (the old policy withholds available data ~168x and drains to one period from silence on a jittery schedule; the new one never withholds). ctest 17/17. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
29 lines
1.6 KiB
CMake
29 lines
1.6 KiB
CMake
# Dev harness: quantify the fidelity of the injection audio path. Two complementary modes:
|
|
# - default: play a known sine (coop_tone), inject coop_hook.dll as the host does (late
|
|
# attach), capture the hook's ring, and analyze it -- proves the CAPTURE side.
|
|
# - --render: drive the REAL shipping AudioMirror (its run_hooked re-renders the ring to
|
|
# the device) while self-loopback-capturing this process's own output -- proves the
|
|
# RENDER side, surfacing under-run / re-prime "metallic" gaps a write-side tap can't see.
|
|
# Both run the analyzer (pitch error in cents, SNR/THD, click + dropout counts) + dump a .wav.
|
|
add_executable(coop_audio_validate
|
|
main.cpp
|
|
${CMAKE_SOURCE_DIR}/host/src/audio/audio_loopback.cpp
|
|
${CMAKE_SOURCE_DIR}/host/src/audio/process_loopback_capture.cpp
|
|
${CMAKE_SOURCE_DIR}/host/src/audio/audio_overrides.cpp)
|
|
|
|
target_include_directories(coop_audio_validate PRIVATE
|
|
${CMAKE_SOURCE_DIR}/host/src
|
|
${CMAKE_SOURCE_DIR}/tools/audio_tone) # shared ToneSource (for --selfcheck)
|
|
|
|
# AudioMirror's run_hooked re-renders the ring; process loopback (the --render self-capture)
|
|
# needs the Windows 10 20H1 (NTDDI_WIN10_CO) headers, same as the host build.
|
|
target_compile_definitions(coop_audio_validate PRIVATE NTDDI_VERSION=0x0A00000B)
|
|
|
|
target_link_libraries(coop_audio_validate PRIVATE coop_common ole32 mmdevapi)
|
|
set_target_properties(coop_audio_validate PROPERTIES OUTPUT_NAME "coop_audio_validate")
|
|
|
|
# Needs coop_hook.dll (the deployable root) and coop_tone.exe (staged in tests/) at runtime.
|
|
add_dependencies(coop_audio_validate coop_hook coop_tone)
|
|
|
|
coop_output_subdir(tools coop_audio_validate) # dev tool -> bin/<config>/tools/
|