Skip to content

fix(voice): stop the recorder writing over audio still in the resampler - #7079

Open
longcw wants to merge 2 commits into
mainfrom
longc/recorder-input-settled
Open

fix(voice): stop the recorder writing over audio still in the resampler#7079
longcw wants to merge 2 commits into
mainfrom
longc/recorder-input-settled

Conversation

@longcw

@longcw longcw commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Problem

The recorder writes each block up to _input_settled, which is the arrival time of the frame. The default 24kHz input passes through a resampler into a 48kHz recording, and a resampler emits only whole output frames, so the last part of the audio is still inside it. The writer puts silence in that place, and take() discards those samples later because their place is behind the cursor.

The recording loses 227.8ms of every 30s at a 24kHz input, and 284.2ms at 16kHz. A 48kHz input needs no resampler and loses nothing.

The two channels stay aligned. This audio is dropped, never moved, so the loss is a small gap in the caller channel and not a drift against the agent channel.

Fix

_Track now reports placed_through, the point that the open run reaches, or None when the resampler holds nothing back. The encode thread keeps each write behind that point.

A source can stop while its resampler still holds audio. Both channels share one cursor, so the writer must not wait for that source. It waits at most MAX_RESAMPLER_LAG (100ms), then ends the run of that source, which places the tail while the cursor can still reach it.

The writer flushes each block up to _input_settled, which comes from frame
arrival, but the default 24kHz input passes through a resampler into a
48kHz recording and a resampler emits only whole output frames. The writer
puts silence where the last 24ms belongs, and take() then discards those
samples because their place is behind the cursor.

_Track now reports placed_through, and the encode thread keeps each write
behind it. The clamp stays no more than MAX_RESAMPLER_LAG behind so that a
stalled source cannot pin the cursor the two channels share.
@longcw
longcw requested a review from a team as a code owner September 1, 2026 07:57
devin-ai-integration[bot]

This comment was marked as resolved.

The clamp let the cursor move on once a source went quiet for longer than
MAX_RESAMPLER_LAG, which left the samples still inside its resampler behind
the cursor. end_run() flushed them at their own place, and take() then
discarded them.

The writer now ends the run of a source that stopped delivering, so its tail
is placed while the cursor can still reach it.
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.

1 participant