Audio: keep rings live, auto-promote loopback->hooked, show the reason

The host used to wait a fixed 1 s for the hook to publish a format and, on
timeout, fall to loopback permanently with no explanation -- which the now
slower (consensus) measurement made common. Restructure the audio thread to
keep the rings live the whole session and alternate: prefer hooked, and while
it isn't ready run loopback (echo) so guests still hear audio, watching the
ring to promote to the no-echo hooked path the instant the hook publishes a
format. Surface the concrete reason loopback is active (no format yet / not
renderable / no ring) in the Audio panel, in amber.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 01:16:46 +02:00
parent cb6749b511
commit c24bfdd64f
3 changed files with 115 additions and 16 deletions

View File

@@ -137,6 +137,16 @@ void AudioPanel::draw_ui(const HookStatusView& status, bool debug_details)
ImGui::TextWrapped("%s", mirror_status.c_str());
}
// Why we're on loopback instead of the no-echo hooked path (empty when hooked). Amber
// because it's a degraded-but-working state that auto-resolves when the hook catches up.
const std::string reason = mirror_.fallback_reason();
if (!reason.empty())
{
ImGui::PushStyleColor(ImGuiCol_Text, kAmber);
ImGui::TextWrapped("Why loopback: %s", reason.c_str());
ImGui::PopStyleColor();
}
// Only the loopback path leaves the game audible locally (the echo); the
// hooked path silences it, so don't warn there.
if (mirror_.source() == AudioMirror::Source::Loopback)