Extract a RAII RenderEndpoint that owns the enumerator/endpoint/client/
render-event/buffer for both AudioMirror::run_hooked and run_loopback,
replacing two hand-rolled setup+teardown blocks and their two identical
fail lambdas with one set_error helper; every COM object now frees on
each exit path automatically.
Route audio_format_verifier's mono decode through the shared
correlate_detail::decode_layout instead of a second copy, and read the
debug env var via GetEnvironmentVariableA (drops the getenv C4996).
Fold the near-identical read_frame/read_pixel staging-copy setup in
SharedTextureSource into one map_staging_copy, and the three separate
case-insensitive filter helpers (log/injection panels) into
ui/text_match.hpp.
Comments: drop game-name anecdotes and "the old code"/version phrasing;
genericize the override-file example; fix a stale heartbeat-interval
note. Behavior unchanged (host tests + mock_game_test pass).
The Log window's filter used a case-sensitive substring match, so "error" missed
"ERROR" -- inconsistent with the rest of the app. Match case-insensitively.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Harden the guessed-stream sample-rate measurement that produced wrong rates
(e.g. 44100 read as ~46205). New rate_estimator.hpp measures over longer
~0.5 s windows, rejects any window that doesn't snap to a standard rate
(standard rates are >8% apart, so a quantization/burst error big enough to
miss one lands in no-man's-land, never on a wrong neighbour), and requires
consensus across windows before committing. If consensus isn't reached it
commits a low-confidence estimate (new AudioFormat_LowConfidence, shown red)
rather than spinning or publishing garbage. Pure logic, unit-tested with
adversarial cadences (rate_estimator_test) incl. the real 46205 bug value.
Add log severity levels: hook logw/loge set LogRecord.level; the host Log
window colors warnings amber and errors red. The low-confidence rate logs a
warning. Protocol -> v15 (new format states); also reserves AudioFormat_Override.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Panels used to Begin at cascade positions, so they overlapped and clipped. A new
apply_panel_layout() in app_chrome positions/sizes each panel from the main
viewport work area (ImGuiCond_FirstUseEver, still movable): Injection left column
full height (room for hook diagnostics), Controllers/Video/Audio stacked in the
center column, Log right edge full height (max room for the log stream). Added a
"View -> Reset layout" menu item (request_layout_reset / apply_layout_end_frame
re-apply the defaults once via ImGuiCond_Always). Each panel now calls
apply_panel_layout(Panel::X) instead of its own ad-hoc SetNextWindowPos/Size.
Verified live: captured the host overlay -- Injection (left, full height),
Controllers/Video/Audio (center stack), Log (right, full height), no overlap among
the panels. x64 build + ctest green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a shared log ring (common/coop/log_ring.hpp): a lossy multi-producer /
single-consumer ring named coop_log_<pid>. The hook logs from several threads,
so producers claim a slot with fetch_add and publish each record with a
release store of its sequence; the consumer reads in order and tolerates
losing the oldest lines if it falls a whole ring behind.
The DLL's logf() now formats once and pushes every line to the ring (the file
trace stays as an opt-in mirror); the worker attaches the ring right after IPC
connect so bring-up is captured. The host (IpcServer) creates the ring at
injection time and exposes drain_logs(); a new LogPanel pulls new lines each
frame into a bounded rolling buffer and renders them with auto-scroll, a
filter, and clear. Added to the View menu (and UiState.show_log).
Verified against Phantom Brave via coop_audio_probe, which now also creates the
ring and drains it: the full hook bring-up trace streamed over IPC. All four
tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>