Audio: per-game persisted format overrides + auto-learn
Persist audio overrides keyed by game image name (coop_audio_overrides.ini next to the exe) so a known-bad game is auto-corrected on its next launch: on attach to a guessed stream the host applies any saved override, and a manual Override now saves too. A format the hook catches exactly at IAudioClient::Initialize is auto-saved as that game's override (ground truth); if it overwrites a differing stored value, a warning is logged. Host-originated log lines now reach the Log window via IpcServer::host_log (color-coded). Validated live (harness): a pre-seeded override for coop_tone is auto-applied over the guess (state -> manual override, 48000/2/16). audio_overrides_test covers persist/reload/case-insensitive lookup/differing-overwrite. Trim README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
88
tests/audio_overrides_test.cpp
Normal file
88
tests/audio_overrides_test.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
// Unit test for the per-game audio override store (save/load round-trip, case-insensitive
|
||||
// lookup by image name, and the differing-overwrite detection that drives the warning).
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <mmreg.h>
|
||||
|
||||
#include "audio/audio_overrides.hpp"
|
||||
|
||||
using namespace coop;
|
||||
|
||||
namespace
|
||||
{
|
||||
int g_failures = 0;
|
||||
void check(bool ok, const char* what)
|
||||
{
|
||||
if (!ok)
|
||||
{
|
||||
std::printf("FAIL: %s\n", what);
|
||||
++g_failures;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::printf(" ok: %s\n", what);
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring temp_path()
|
||||
{
|
||||
wchar_t dir[MAX_PATH] = {};
|
||||
GetTempPathW(MAX_PATH, dir);
|
||||
return std::wstring(dir) + L"coop_overrides_test_" + std::to_wstring(GetCurrentProcessId()) + L".ini";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::wstring path = temp_path();
|
||||
DeleteFileW(path.c_str());
|
||||
|
||||
const AudioFormatOverride brotato{44100, 2, 32, WAVE_FORMAT_IEEE_FLOAT};
|
||||
const AudioFormatOverride snb{48000, 2, 16, WAVE_FORMAT_PCM};
|
||||
|
||||
{
|
||||
AudioOverrideStore store(path);
|
||||
store.load(); // missing file -> empty
|
||||
AudioFormatOverride got;
|
||||
check(!store.find(L"brotato.exe", got), "empty store: no entry");
|
||||
|
||||
bool differed = true;
|
||||
store.set(L"C:\\games\\Brotato.exe", brotato, &differed); // full path -> basename key
|
||||
check(!differed, "first set: not a differing overwrite");
|
||||
store.set(L"snb.exe", snb, &differed);
|
||||
check(!differed, "new game set: not a differing overwrite");
|
||||
|
||||
// Same value again -> not differing.
|
||||
store.set(L"brotato.exe", brotato, &differed);
|
||||
check(!differed, "identical re-set: not differing");
|
||||
|
||||
// Different value -> differing (drives the warning).
|
||||
const AudioFormatOverride brotato2{48000, 2, 32, WAVE_FORMAT_IEEE_FLOAT};
|
||||
store.set(L"brotato.exe", brotato2, &differed);
|
||||
check(differed, "changed value: flagged as differing overwrite");
|
||||
}
|
||||
|
||||
// Reload from disk in a fresh store: persistence + case-insensitive basename lookup.
|
||||
{
|
||||
AudioOverrideStore store(path);
|
||||
store.load();
|
||||
AudioFormatOverride got;
|
||||
check(store.find(L"BROTATO.EXE", got), "reload: found case-insensitively");
|
||||
check(got == AudioFormatOverride{48000, 2, 32, WAVE_FORMAT_IEEE_FLOAT}, "reload: latest value persisted");
|
||||
check(store.find(L"D:\\steam\\snb.exe", got) && got == snb, "reload: found by full path basename");
|
||||
check(!store.find(L"unknown.exe", got), "reload: unknown game absent");
|
||||
}
|
||||
|
||||
DeleteFileW(path.c_str());
|
||||
if (g_failures == 0)
|
||||
{
|
||||
std::printf("PASS audio_overrides_test\n");
|
||||
return 0;
|
||||
}
|
||||
std::printf("FAILED audio_overrides_test (%d)\n", g_failures);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user