fix(audio): apply receive-side NR to stereo mic streams
The per-listener noise-reduction toggle (vc_set_remote_stream) did nothing on Windows/macOS/iOS. The decode loop gated the RNNoise pass on dec_channels == 1 as a proxy for "this stream is voice" (assuming stereo => screen-share). The stereo-mic capture commit broke that: a stereo mic with send-side NR off transmits stereo Opus, so the receiver decoded two channels and skipped NR entirely. gain/mute have no channel guard, which is why only NR appeared broken. Thread the stream kind through init_recv_stream into RemoteStream::is_voice (set from si.kind() == STREAM_MIC), gate receive NR on is_voice instead of channel count, and fold a stereo voice frame to mono -> denoise -> duplicate back across both channels in place (symmetric with the send-side downmix; RNNoise is mono-only). Screen-audio shares are never denoised. New test test_recv_noise_reduction drives AudioEngine and asserts a stereo voice stream's noise floor collapses with NR on (RMS 1046 -> 0.1) while a screen-audio share stays unchanged. ctest --preset dev green 29/29. Docs: voice.md section 10. Shared-core fix; clients need only a rebuild. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -321,8 +321,12 @@ and every Opus frame size (480/960/1920/2880) is a multiple of 480, so frames ar
|
||||
whole 480-sample chunks with no resampling. Because it's mono-only:
|
||||
- **Send-side:** a stereo mic is downmixed to mono **only when NR is enabled** — with NR off a
|
||||
stereo mic keeps full stereo (we never collapse mic quality unless asked).
|
||||
- **Receive-side:** NR is skipped on stereo streams (a stereo stream is a screen-audio share,
|
||||
not voice).
|
||||
- **Receive-side:** NR applies to **voice (MIC) streams only**, gated on the stream *kind* — not
|
||||
on its channel count, since a stereo mic with send-side NR off now arrives as stereo voice.
|
||||
When enabled on such a stream the decoded stereo frame is folded to mono, denoised, and
|
||||
duplicated back across both channels (symmetric with the send-side downmix), so that stream
|
||||
plays as mono while NR is on. A **screen-audio share is never voice and is left untouched** —
|
||||
denoising music/video with a speech denoiser would mangle it.
|
||||
|
||||
Implementation: a per-`ssrc` NS instance (`RemoteStream::recv_ns`) on the receive path,
|
||||
instantiated lazily only for streams the listener has flagged; the send-side instance
|
||||
|
||||
Reference in New Issue
Block a user