Skip to main content

playterm_player/
tap.rs

1use std::collections::VecDeque;
2use std::sync::{Arc, Mutex};
3use std::time::Duration;
4
5use rodio::source::SeekError;
6use rodio::{ChannelCount, SampleRate, Source};
7
8pub struct SampleTap<S: Source<Item = f32>> {
9    inner: S,
10    buffer: Arc<Mutex<VecDeque<f32>>>,
11}
12
13impl<S: Source<Item = f32>> SampleTap<S> {
14    pub fn new(inner: S, buffer: Arc<Mutex<VecDeque<f32>>>) -> Self {
15        Self { inner, buffer }
16    }
17}
18
19impl<S: Source<Item = f32>> Iterator for SampleTap<S> {
20    type Item = f32;
21    fn next(&mut self) -> Option<f32> {
22        let sample = self.inner.next()?;
23        if let Ok(mut buf) = self.buffer.try_lock() {
24            buf.push_back(sample);
25            if buf.len() > 4096 {
26                buf.pop_front();
27            }
28        }
29        Some(sample)
30    }
31}
32
33impl<S: Source<Item = f32>> Source for SampleTap<S> {
34    fn current_span_len(&self) -> Option<usize> { self.inner.current_span_len() }
35    fn channels(&self) -> ChannelCount { self.inner.channels() }
36    fn sample_rate(&self) -> SampleRate { self.inner.sample_rate() }
37    fn total_duration(&self) -> Option<Duration> { self.inner.total_duration() }
38
39    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
40        // Flush stale samples so the visualizer doesn't show pre-seek audio.
41        if let Ok(mut buf) = self.buffer.try_lock() {
42            buf.clear();
43        }
44        self.inner.try_seek(pos)
45    }
46}