Follow-up to #565 (snap illegal AAC sample rates to 48 kHz on Windows), which introduced the integer-factor downsample path in electron/native/wgc-capture/src/audio_sample_utils.cpp (convertAudioWithGain).
Context. The 96 kHz / 192 kHz → 48 kHz path currently averages each group of factor source frames (box average) instead of picking one frame — a clear improvement over the previous nearest-neighbour decimation, and state (the remainder) is correctly carried across packets by AudioMixer. This tradeoff was accepted when #565 was reviewed (CodeRabbit flagged it twice as 🟠 Major / Heavy lift), with this issue recorded so the subject is not lost.
Problem. A box average is not an adequate anti-alias filter: for 96 kHz → 48 kHz, a 36 kHz source tone survives at roughly 38% amplitude and aliases to 12 kHz in the output. The existing Nyquist test passes only because the two-tap average happens to have a zero exactly at 48 kHz. In practice desktop system audio carries almost no energy above 24 kHz, which is why this was accepted — but it is a real limitation, not a solved problem.
Acceptance criteria.
- Replace the box average with a stateful low-pass decimator whose cutoff is at or below the target Nyquist frequency, preserving the existing cross-packet remainder handling in
AudioMixer (systemResampleRemainder_ / microphoneResampleRemainder_).
- Add unit-test coverage in
audio_sample_utils_test.cpp for an input tone between 24 kHz and 48 kHz, asserting the aliased component stays below an explicit threshold.
- No regression on the existing suite (sub-factor packets, one-frame packets, remainder three-then-one/full), which runs as part of
npm run build:native:win.
Follow-up to #565 (snap illegal AAC sample rates to 48 kHz on Windows), which introduced the integer-factor downsample path in
electron/native/wgc-capture/src/audio_sample_utils.cpp(convertAudioWithGain).Context. The 96 kHz / 192 kHz → 48 kHz path currently averages each group of
factorsource frames (box average) instead of picking one frame — a clear improvement over the previous nearest-neighbour decimation, and state (the remainder) is correctly carried across packets byAudioMixer. This tradeoff was accepted when #565 was reviewed (CodeRabbit flagged it twice as🟠 Major / Heavy lift), with this issue recorded so the subject is not lost.Problem. A box average is not an adequate anti-alias filter: for 96 kHz → 48 kHz, a 36 kHz source tone survives at roughly 38% amplitude and aliases to 12 kHz in the output. The existing Nyquist test passes only because the two-tap average happens to have a zero exactly at 48 kHz. In practice desktop system audio carries almost no energy above 24 kHz, which is why this was accepted — but it is a real limitation, not a solved problem.
Acceptance criteria.
AudioMixer(systemResampleRemainder_/microphoneResampleRemainder_).audio_sample_utils_test.cppfor an input tone between 24 kHz and 48 kHz, asserting the aliased component stays below an explicit threshold.npm run build:native:win.