feat(audio): per-stream mix controls in Windows client + vc_get_remote_stream getter

PerUserTuningDialog previously broadcast one gain/mute/NR set to *all* of a
user's streams, even though the core mixer (AudioEngine::RemoteStream) and
the C ABI (vc_set_remote_stream) were already per-stream. The UI had no
per-mix controls anywhere.

Reworks the dialog to enumerate ListUserStreams on open and render one row
per stream (kind + label + Gain + Mute + NR), each wiring only to its own
stream_id. Adds a read-back ABI counterpart, vc_get_remote_stream, so the
dialog opens at the listener's actual current per-stream settings (defaults
1.0/unmuted/NR-off) rather than always 100%. Additive ABI change only; no
existing symbols touched.

Tests: test_m3_multistream extended with getter round-trip assertions; new
C# smoke test exercises the full P/Invoke marshaling path with two clients.
Docs: voice.md §10 notes the getter. NR checkbox keeps its honest
'passthrough' label (NS DSP still unbuilt per §8).
This commit is contained in:
2026-06-18 02:06:44 +02:00
parent 1dfe3c95ed
commit d397731db9
15 changed files with 402 additions and 82 deletions

View File

@@ -289,6 +289,28 @@ int main() {
}
{ std::lock_guard lk(evB.mu); CHECK(!evB.disconnected); }
// ── 3b. Read back what B just set (vc_get_remote_stream round-trips the recv state) ─
{
vc_remote_stream_state st{};
// mic_sid: last write above was (1.0, mute=0, nr=0)
CHECK(vc_get_remote_stream(clientB, a_uid, mic_sid, &st) == VC_OK);
CHECK(fabsf(st.gain - 1.0f) < 1e-5f);
CHECK(st.muted == 0);
CHECK(st.noise_reduction == 0);
// screen_sid: set to (0.3, mute=1, nr=1) at line ~282
CHECK(vc_get_remote_stream(clientB, a_uid, screen_sid, &st) == VC_OK);
CHECK(fabsf(st.gain - 0.3f) < 1e-5f);
CHECK(st.muted == 1);
CHECK(st.noise_reduction == 1);
// Unknown stream_id on a known user -> INVALID_ARG.
CHECK(vc_get_remote_stream(clientB, a_uid, 0xDEADBEEF, &st) == VC_ERR_INVALID_ARG);
// Null out -> INVALID_ARG.
CHECK(vc_get_remote_stream(clientB, a_uid, mic_sid, nullptr) == VC_ERR_INVALID_ARG);
}
// ── 4. Per-channel Opus configurability ──────────────────────────────────
// A moves to "Music Room" (channel 2: stereo/128kbps/OPUS_AUDIO/no DTX) and announces a
// fresh MIC stream there; B stays in "Lobby" (channel 1: mono/24kbps/OPUS_VOIP/DTX) with