Audio: operator re-measure + format override (host<->hook op channel)

Add a per-stream op channel in AudioRingHeader (op_seq + op_* fields, version
2): the host posts re-measure / override commands, the hook applies them and
re-publishes (bumping format_generation), and the host rebuilds its render
client live on the change. The Audio panel (under Debug details) gains a
"Re-measure rate" button and a rate/channels/bit-depth/format override -- for
when detection is wrong or the channels/bit-depth were unrecoverable.

Also add a debug-only IPC test harness (-DCOOP_TEST_HARNESS, off by default,
absent from the shipped host): a file-based command channel that drives the
host's real UI code paths (inject / audio / re-measure / override / screenshot
/ status) for scripted validation, instead of unreliable synthetic mouse input.
Used to validate live: late-attach to coop_tone@44100 -> measured 44100,
promoted to hooked; override -> 2ch state, re-measure -> reconverge.

Trim the README roadmap to what's left; document the harness + rate_estimator_test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 01:46:59 +02:00
parent c24bfdd64f
commit 90f40ae479
13 changed files with 607 additions and 111 deletions

51
host/src/test_harness.cpp Normal file
View File

@@ -0,0 +1,51 @@
#include "test_harness.hpp"
#ifdef COOP_TEST_HARNESS
#include <fstream>
#include <windows.h>
namespace coop
{
namespace
{
std::wstring temp_file(const wchar_t* name)
{
wchar_t dir[MAX_PATH] = {};
const DWORD n = GetTempPathW(MAX_PATH, dir);
return (n != 0 && n < MAX_PATH) ? std::wstring(dir) + name : std::wstring(name);
}
} // namespace
void TestHarness::init()
{
cmd_path_ = temp_file(L"coop_test_cmd.txt");
resp_path_ = temp_file(L"coop_test_resp.txt");
DeleteFileW(cmd_path_.c_str()); // drop any stale command from a previous run
DeleteFileW(resp_path_.c_str());
}
std::string TestHarness::poll_command()
{
std::ifstream f(cmd_path_.c_str()); // MSVC accepts a wide path
if (!f)
{
return {};
}
std::string line;
std::getline(f, line);
f.close();
DeleteFileW(cmd_path_.c_str()); // ack: the command has been taken
return line;
}
void TestHarness::write_response(const std::string& resp)
{
std::ofstream f(resp_path_.c_str(), std::ios::trunc);
f << resp;
}
} // namespace coop
#endif // COOP_TEST_HARNESS