@@ -206,6 +206,22 @@ final class WhisperService: @unchecked Sendable {
206206 fullText = Self . filterHallucinationTokens ( rawText)
207207 }
208208
209+ // Whisper Tiny can return no tokens for valid sub-second speech.
210+ // Retry only an empty short result with trailing silence so normal
211+ // successful dictation keeps the single-pass fast path.
212+ if let paddedAudio = Self . paddedAudioForShortEmptyTranscription (
213+ audioData,
214+ transcription: fullText
215+ ) {
216+ VocaLogger . warning (
217+ . whisperService,
218+ " Short transcription was empty for \( loadedModelName ?? " unknown model " ) ; retrying with trailing silence "
219+ )
220+ results = try await kit. transcribe ( audioArray: paddedAudio, decodeOptions: options)
221+ rawText = results. map { $0. text } . joined ( separator: " " )
222+ fullText = Self . filterHallucinationTokens ( rawText)
223+ }
224+
209225 let elapsed = CFAbsoluteTimeGetCurrent ( ) - startTime
210226
211227 // Get detected language from first result
@@ -293,6 +309,24 @@ final class WhisperService: @unchecked Sendable {
293309 promptTokens != nil && rawText. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty
294310 }
295311
312+ /// Some Whisper models return no tokens for otherwise valid sub-second
313+ /// speech. A small trailing-silence pad gives the retry more context without
314+ /// changing the reported audio length.
315+ static func paddedAudioForShortEmptyTranscription(
316+ _ audio: [ Float ] ,
317+ transcription: String
318+ ) -> [ Float ] ? {
319+ let minimumSampleCount = 17_600 // 1.1 seconds at 16 kHz
320+ guard transcription. isEmpty,
321+ !audio. isEmpty,
322+ audio. count < minimumSampleCount else {
323+ return nil
324+ }
325+ var padded = audio
326+ padded. append ( contentsOf: repeatElement ( 0 , count: minimumSampleCount - audio. count) )
327+ return padded
328+ }
329+
296330 /// Encode custom vocabulary into WhisperKit conditioning tokens.
297331 /// Returns nil when there are no terms or the tokenizer isn't ready yet.
298332 /// Framed as a "Glossary:" prompt, which nudges Whisper to treat the terms
0 commit comments