Skip to content

feat/sinc pcm resampler - #23

Merged
dangusev merged 8 commits into
fix/vp9-decode-and-keyframe-requestfrom
feat/sinc-pcm-resampler
Oct 13, 2026
Merged

dangusev merged 8 commits into
fix/vp9-decode-and-keyframe-requestfrom
feat/sinc-pcm-resampler

Conversation

@dangusev

Copy link
Copy Markdown
Collaborator

Stack created with GitHub Stacks CLI • Give Feedback 💬

@dangusev
dangusev added this pull request to stack #31 October 13, 2026 14:22
@dangusev
dangusev force-pushed the feat/sinc-pcm-resampler branch 2 times, most recently from 49d4b22 to 8181267 Compare October 13, 2026 14:54
StreamResampler interpolated linearly. It did not filter, so it aliased
when it downsampled: from 48 kHz to 16 kHz, a 12 kHz tone came out at
4 kHz at full level. It now uses a rubato windowed-sinc filter.

StreamResampler::new takes a fixed input rate, output rate, and channel
count, and rejects a frame with other settings. push and flush return a
PcmFrame with the input channels. flush pushes the audio that the filter
holds (128 input frames) out with silence. Equal rates copy the audio.

LocalAudioTrack accepts PCM at LocalAudioTrackConfig::pcm_sample_rate
(48 kHz by default) and averages its channels to mono. drain queues the
audio that the resampler holds, for the end of an utterance. flush also
drops that audio. write_pcm keeps the resampler lock until its audio is
queued, so a flush cannot put audio from before it in the queue.
Users usually do not know the rate of their TTS audio, and a track does
not change its rate after it is created. LocalAudioTrackConfig no longer
has pcm_sample_rate: the first write_pcm frame sets the input rate of the
track, and a later frame at another rate gets IllegalState.

StreamResampler::new(out_rate, channels) cannot fail. The first push sets
the input rate and builds the filter. If the filter rejects the rate, the
rate stays unset.
When the queue holds less than one 20 ms frame, the pacer adds the audio that the resampler still holds and cuts the silence after it. The end of the written audio plays without the next write. LocalAudioTrack::drain is removed.
write_pcm and StreamResampler::push return RtcError::PcmRateMismatch { expected, actual } when a frame is not at the rate of the first frame, so a caller can match the error without its text. A frame with another channel count still gets IllegalState.
@dangusev
dangusev force-pushed the feat/sinc-pcm-resampler branch from 8181267 to 1b756a2 Compare October 13, 2026 15:02
@dangusev
dangusev merged commit 4e540d0 into main Oct 13, 2026
6 of 11 checks passed
@dangusev
dangusev deleted the feat/sinc-pcm-resampler branch October 13, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants