Files
CoopAllTheThings/host/CMakeLists.txt
BlackMark 00244bcfd7 Recover a guessed audio stream's rate by correlating hook vs loopback (step a)
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>
2026-06-23 02:26:06 +02:00

107 lines
4.4 KiB
CMake

add_executable(coop_host WIN32
src/main.cpp
src/d3d11_window.cpp
src/imgui_layer.cpp
src/controllers_panel.cpp
src/injection_panel.cpp
src/capture_panel.cpp
src/audio_panel.cpp
src/log_panel.cpp
src/ui/app_chrome.cpp
src/input/xinput_source.cpp
src/input/input_worker.cpp
src/inject/process_list.cpp
src/inject/window_list.cpp
src/inject/injector.cpp
src/inject/mkb_forward.cpp
src/ipc/ipc_server.cpp
src/capture/frame_renderer.cpp
src/capture/window_capture.cpp
src/capture/shared_texture.cpp
src/audio/audio_loopback.cpp
src/audio/audio_format_verifier.cpp
src/audio/audio_overrides.cpp
src/audio/process_loopback_capture.cpp
src/vk_layer_setup.cpp
src/test_harness.cpp)
target_include_directories(coop_host PRIVATE src)
# Debug-only IPC test harness: lets a script drive the real UI code paths (inject /
# audio / override / ...) and read state back, instead of simulating mouse input. OFF
# by default, so the shipped host never contains it (the source compiles to nothing
# without the macro). Enable with -DCOOP_TEST_HARNESS=ON for a debuggable build.
option(COOP_TEST_HARNESS "Build the host with the debug IPC test harness (dev only)" OFF)
if(COOP_TEST_HARNESS)
target_compile_definitions(coop_host PRIVATE COOP_TEST_HARNESS)
message(STATUS "Host test harness ENABLED (debug IPC driver)")
endif()
# Process-loopback capture (AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK) needs the
# Windows 10 20H1 (NTDDI_WIN10_CO) headers; raise the target SDK version for it.
target_compile_definitions(coop_host PRIVATE NTDDI_VERSION=0x0A00000B)
target_link_libraries(coop_host PRIVATE
coop_common
imgui
d3d11
dxgi
dwmapi
d3dcompiler
windowsapp
xinput
mmdevapi
winmm
ole32)
set_target_properties(coop_host PROPERTIES OUTPUT_NAME "coop_host")
# Optional Steam Input: enabled automatically when the Steamworks SDK is vendored
# under third_party/steamworks_sdk (it isn't redistributable, so it's gitignored
# and never committed). Without it, the host builds XInput-only.
set(COOP_STEAM_SDK_DIR "${CMAKE_SOURCE_DIR}/third_party/steamworks_sdk/sdk")
if(EXISTS "${COOP_STEAM_SDK_DIR}/public/steam/steam_api.h")
message(STATUS "Steamworks SDK found -> Steam Input enabled")
target_sources(coop_host PRIVATE src/input/steam_input_source.cpp)
target_compile_definitions(coop_host PRIVATE COOP_WITH_STEAM)
target_include_directories(coop_host SYSTEM PRIVATE "${COOP_STEAM_SDK_DIR}/public")
# The SDK headers trip /W4 (strncpy, non-UTF-8 bytes); silence for this TU only.
set_source_files_properties(src/input/steam_input_source.cpp PROPERTIES
COMPILE_OPTIONS "/wd4996;/wd4828")
target_link_libraries(coop_host PRIVATE
"${COOP_STEAM_SDK_DIR}/redistributable_bin/win64/steam_api64.lib")
# Stage steam_api64.dll + the action manifest next to coop_host.exe.
add_custom_command(TARGET coop_host POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${COOP_STEAM_SDK_DIR}/redistributable_bin/win64/steam_api64.dll"
"$<TARGET_FILE_DIR:coop_host>/steam_api64.dll"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/assets/steam_input_actions.vdf"
"$<TARGET_FILE_DIR:coop_host>/steam_input_actions.vdf"
VERBATIM)
# Console smoke test for Steam Input (reuses the shipping source). Not a ctest
# (needs Steam running + an appid); run manually from bin/<config>/.
add_executable(coop_steam_input_probe
${CMAKE_SOURCE_DIR}/tools/steam_input_probe/main.cpp
src/input/steam_input_source.cpp
src/input/xinput_source.cpp)
target_include_directories(coop_steam_input_probe PRIVATE src)
target_include_directories(coop_steam_input_probe SYSTEM PRIVATE "${COOP_STEAM_SDK_DIR}/public")
target_compile_definitions(coop_steam_input_probe PRIVATE COOP_WITH_STEAM)
set_source_files_properties(src/input/steam_input_source.cpp PROPERTIES COMPILE_OPTIONS "/wd4996;/wd4828")
target_link_libraries(coop_steam_input_probe PRIVATE
coop_common
xinput
"${COOP_STEAM_SDK_DIR}/redistributable_bin/win64/steam_api64.lib")
# Dev tool -> bin/<config>/tools/ (its steam_api64.dll copy follows via TARGET_FILE_DIR).
coop_output_subdir(tools coop_steam_input_probe)
add_custom_command(TARGET coop_steam_input_probe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${COOP_STEAM_SDK_DIR}/redistributable_bin/win64/steam_api64.dll"
"$<TARGET_FILE_DIR:coop_steam_input_probe>/steam_api64.dll"
VERBATIM)
else()
message(STATUS "Steamworks SDK not found -> Steam Input disabled (XInput only)")
endif()