When the host attaches to an already-running game it never saw the stream's Initialize, so the render-hook assumes the device mix format and measures only the sample rate from the render cadence -- which a jittery game can make wrong (intermittent pitch shift). But during the measurement window the game is still audible, so we have the same audio twice: the hook (pre-mix, unknown format) and a process-loopback (post-mix, the known device format). Cross-correlating them pins the true rate from ground truth. - common/include/coop/audio_correlate.hpp: the pure correlator. Resample the hook by each candidate standard rate up to the device rate and score how well it aligns with the loopback across the window (drift-detecting). audio_correlation_test recovers every rate (score ~1.0 vs ~0.01 for wrong ones), incl. 44100-vs-48000, and rejects unrelated signals. - Hook measurement tap: a host-set verify_capture ring flag makes the hook push a still-being-measured (guessed) stream's raw pre-mix bytes WITHOUT silencing, so the host can co-capture both signals (a silenced game's loopback is silent). Inert by default -- the shipping no-echo path is untouched. - host/src/audio/audio_format_verifier: co-captures hook + loopback and correlates, feeding a correction into the existing override channel. Wired into AudioMirror's measurement window (hidden in the gap loopback already covers, so exact streams pay nothing). audio_verify_test drives it end-to-end against coop_mock_game. Rate vs layout are coupled (correlating the waveform needs the right channel de-interleaving), so this step assumes the hook layout matches the device (the common stereo-on-stereo case); recovering a different channel count / bit depth is step b. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
30 lines
1.7 KiB
CMake
30 lines
1.7 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/audio_format_verifier.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/
|