fix: prevent audio truncation and eliminate recording start delay - #61
Merged
Conversation
…detected The AudioEngine.processAudioBuffer method had a bug where audio frames were discarded when silence detection or max duration conditions fired. Both checks returned early BEFORE appending the current audio chunk to the buffer, causing the last few seconds of speech to be lost. Changes: - Reorder processAudioBuffer to always append audio samples to the buffer BEFORE checking silence/max-duration stop conditions - Add silenceCallbackFired and maxDurationCallbackFired flags to prevent repeated callback invocations (previously, silence detection fired on every audio callback once the duration threshold was met) - Reset both flags in startRecording() for clean state on each session This fixes push-to-talk mode where releasing the key after a brief pause would result in truncated transcription, because trailing audio frames with low energy were silently dropped.
The startRecording() method awaited playStartSoundAsync() which blocked for the entire sound duration (up to 1s timeout) before activating the microphone. This caused a noticeable lag between pressing the hotkey and the mic actually capturing audio. Now the mic starts immediately and the start sound plays fire-and-forget afterward. Any brief sound bleed into the mic buffer is negligible and handled well by WhisperKit's noise model.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problems
1. Transcription cut off at the end (audio buffer truncation)
When using push-to-talk mode, releasing the hotkey after a brief pause would result in truncated transcription — the last few seconds of speech were lost.
2. ~1 second delay before mic activates
Pressing the hotkey had a noticeable lag before the microphone actually started capturing audio, making the app feel sluggish.
Root Causes
Audio buffer truncation
In
AudioEngine.processAudioBuffer, audio frames were being discarded when silence detection or max-duration conditions fired. Both checks performed an earlyreturnbefore appending the current audio chunk to the buffer:This meant:
Recording start delay
AppState.startRecording()calledawait soundManager.playStartSoundAsync()which blocked for the entire sound duration (up to a 1-second timeout) before activating the microphone:Fixes
AudioEngine.processAudioBuffer (
AudioEngine.swift)silenceCallbackFiredflag: Ensures the silence detection callback fires only once per silence period (resets when speech resumes)maxDurationCallbackFiredflag: Ensures the max duration callback fires only once per recording sessionstartRecording()for clean state on each new sessionAppState.startRecording (
AppState.swift)Tests Added
6 new tests in
AudioEngineTests:testStopRecordingWithoutStartReturnsEmpty— baseline behaviortestSilenceCallbackFiresOnlyOnce— verifies the new dedup guardtestMaxDurationCallbackFiresOnlyOnce— verifies the new dedup guardtestAudioBufferNotEmptyAfterRecording— baseline recording workstestAudioBufferPreservedWhenSilenceDetected— key test: buffer has data even after silence firestestAudioBufferPreservedWhenMaxDurationReached— buffer has data even after max duration firesTesting
All 57 tests pass (
swift test), including the 6 new AudioEngine tests. Manually verified both fixes work correctly — recording starts instantly and full audio is captured through key release.