From b8ee014cc329d97f2a02f3b213a7e49fd8b9396f Mon Sep 17 00:00:00 2001 From: Mute Date: Mon, 17 Aug 2026 06:58:18 +0000 Subject: [PATCH] rvsttd: hold-back last drain chunk for fade, same faded audio to Moonshine + WAV --- rvsttd/src/main.rs | 59 ++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/rvsttd/src/main.rs b/rvsttd/src/main.rs index f81fc4f..4d559ef 100644 --- a/rvsttd/src/main.rs +++ b/rvsttd/src/main.rs @@ -113,14 +113,6 @@ impl DebugRecorder { self.audio.extend_from_slice(samples); } - fn audio_len(&self) -> usize { - self.audio.len() - } - - fn truncate_audio(&mut self, len: usize) { - self.audio.truncate(len); - } - fn save(self) { // Write log let log_path = self.dir.join("session.log"); @@ -332,45 +324,50 @@ fn transcriber_loop( // Drain trailing audio from ALSA buffer (cpal stream still alive). // Fixed 100ms window — cpal delivers every ~50ms (800 samples @ 16kHz), // so this captures 1-2 more callbacks worth of trailing audio. + // Hold one chunk back so we can apply a fade-out to the very last one. let drain_deadline = std::time::Instant::now() + Duration::from_millis(100); - let mut drained: Vec = Vec::new(); + let mut held_chunk: Option> = None; while std::time::Instant::now() < drain_deadline { match rx.recv_timeout(drain_deadline - std::time::Instant::now()) { Ok(chunk) => { - if let Some(ref mut r) = recorder { - r.add_audio(&chunk); + // Feed previously held chunk to Moonshine + recorder (no fade) + if let Some(prev) = held_chunk.take() { + if let Some(ref mut r) = recorder { + r.add_audio(&prev); + } + unsafe { + moonshine_transcribe_add_audio_to_stream( + handle, stream_handle, + prev.as_ptr(), prev.len() as u64, + SAMPLE_RATE, 0, + ); + } } - drained.extend_from_slice(&chunk); + held_chunk = Some(chunk); } Err(mpsc::RecvTimeoutError::Timeout) => break, Err(mpsc::RecvTimeoutError::Disconnected) => break, } } - // Apply 5ms fade-out to the trailing edge of the drained audio. - // Both the debug WAV and Moonshine receive the same faded audio. - let fade_samples = (SAMPLE_RATE as usize * 5) / 1000; - if drained.len() > fade_samples { - let start = drained.len() - fade_samples; - for i in 0..fade_samples { - let t = 1.0 - (i as f32 / fade_samples as f32); - drained[start + i] *= t; + // Apply 5ms fade-out to the last chunk, then feed to both Moonshine and recorder + if let Some(mut chunk) = held_chunk.take() { + let fade_samples = (SAMPLE_RATE as usize * 5) / 1000; + if chunk.len() > fade_samples { + let start = chunk.len() - fade_samples; + for i in 0..fade_samples { + let t = 1.0 - (i as f32 / fade_samples as f32); + chunk[start + i] *= t; + } } - } - // Update recorder with the faded audio (replaces the raw versions added above) - if let Some(ref mut r) = recorder { - // Remove the raw drained samples and re-add the faded ones - let raw_drained_len = r.audio_len(); - r.truncate_audio(raw_drained_len.saturating_sub(drained.len())); - r.add_audio(&drained); - } - - if !drained.is_empty() { + if let Some(ref mut r) = recorder { + r.add_audio(&chunk); + } unsafe { moonshine_transcribe_add_audio_to_stream( handle, stream_handle, - drained.as_ptr(), drained.len() as u64, + chunk.as_ptr(), chunk.len() as u64, SAMPLE_RATE, 0, ); }