rodio/
buffer.rs

1//! A simple source of samples coming from a buffer.
2//!
3//! The `SamplesBuffer` struct can be used to treat a list of values as a `Source`.
4//!
5//! # Example
6//!
7//! ```
8//! use rodio::buffer::SamplesBuffer;
9//! let _ = SamplesBuffer::new(1, 44100, vec![1i16, 2, 3, 4, 5, 6]);
10//! ```
11//!
12
13use std::time::Duration;
14
15use crate::source::SeekError;
16use crate::{Sample, Source};
17
18/// A buffer of samples treated as a source.
19#[derive(Debug, Clone)]
20pub struct SamplesBuffer<S> {
21    data: Vec<S>,
22    pos: usize,
23    channels: u16,
24    sample_rate: u32,
25    duration: Duration,
26}
27
28impl<S> SamplesBuffer<S>
29where
30    S: Sample,
31{
32    /// Builds a new `SamplesBuffer`.
33    ///
34    /// # Panic
35    ///
36    /// - Panics if the number of channels is zero.
37    /// - Panics if the samples rate is zero.
38    /// - Panics if the length of the buffer is larger than approximately 16 billion elements.
39    ///   This is because the calculation of the duration would overflow.
40    ///
41    pub fn new<D>(channels: u16, sample_rate: u32, data: D) -> SamplesBuffer<S>
42    where
43        D: Into<Vec<S>>,
44    {
45        assert!(channels != 0);
46        assert!(sample_rate != 0);
47
48        let data = data.into();
49        let duration_ns = 1_000_000_000u64.checked_mul(data.len() as u64).unwrap()
50            / sample_rate as u64
51            / channels as u64;
52        let duration = Duration::new(
53            duration_ns / 1_000_000_000,
54            (duration_ns % 1_000_000_000) as u32,
55        );
56
57        SamplesBuffer {
58            data,
59            pos: 0,
60            channels,
61            sample_rate,
62            duration,
63        }
64    }
65}
66
67impl<S> Source for SamplesBuffer<S>
68where
69    S: Sample,
70{
71    #[inline]
72    fn current_frame_len(&self) -> Option<usize> {
73        None
74    }
75
76    #[inline]
77    fn channels(&self) -> u16 {
78        self.channels
79    }
80
81    #[inline]
82    fn sample_rate(&self) -> u32 {
83        self.sample_rate
84    }
85
86    #[inline]
87    fn total_duration(&self) -> Option<Duration> {
88        Some(self.duration)
89    }
90
91    // this is fast because all the samples are in memory already
92    // and due to the constant sample_rate we can jump to the right
93    // sample directly
94    //
95    /// This jumps in memory till the sample for `pos`.
96    #[inline]
97    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
98        let curr_channel = self.pos % self.channels() as usize;
99        let new_pos = pos.as_secs_f32() * self.sample_rate() as f32 * self.channels() as f32;
100        // saturate pos at the end of the source
101        let new_pos = new_pos as usize;
102        let new_pos = new_pos.min(self.data.len());
103
104        // make sure the next sample is for the right channel
105        let new_pos = new_pos.next_multiple_of(self.channels() as usize);
106        let new_pos = new_pos - curr_channel;
107
108        self.pos = new_pos;
109        Ok(())
110    }
111}
112
113impl<S> Iterator for SamplesBuffer<S>
114where
115    S: Sample,
116{
117    type Item = S;
118
119    #[inline]
120    fn next(&mut self) -> Option<S> {
121        let sample = self.data.get(self.pos)?;
122        self.pos += 1;
123        Some(*sample)
124    }
125
126    #[inline]
127    fn size_hint(&self) -> (usize, Option<usize>) {
128        (self.data.len(), Some(self.data.len()))
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use crate::buffer::SamplesBuffer;
135    use crate::source::Source;
136
137    #[test]
138    fn basic() {
139        let _ = SamplesBuffer::new(1, 44100, vec![0i16, 0, 0, 0, 0, 0]);
140    }
141
142    #[test]
143    #[should_panic]
144    fn panic_if_zero_channels() {
145        SamplesBuffer::new(0, 44100, vec![0i16, 0, 0, 0, 0, 0]);
146    }
147
148    #[test]
149    #[should_panic]
150    fn panic_if_zero_sample_rate() {
151        SamplesBuffer::new(1, 0, vec![0i16, 0, 0, 0, 0, 0]);
152    }
153
154    #[test]
155    fn duration_basic() {
156        let buf = SamplesBuffer::new(2, 2, vec![0i16, 0, 0, 0, 0, 0]);
157        let dur = buf.total_duration().unwrap();
158        assert_eq!(dur.as_secs(), 1);
159        assert_eq!(dur.subsec_nanos(), 500_000_000);
160    }
161
162    #[test]
163    fn iteration() {
164        let mut buf = SamplesBuffer::new(1, 44100, vec![1i16, 2, 3, 4, 5, 6]);
165        assert_eq!(buf.next(), Some(1));
166        assert_eq!(buf.next(), Some(2));
167        assert_eq!(buf.next(), Some(3));
168        assert_eq!(buf.next(), Some(4));
169        assert_eq!(buf.next(), Some(5));
170        assert_eq!(buf.next(), Some(6));
171        assert_eq!(buf.next(), None);
172    }
173
174    #[cfg(test)]
175    mod try_seek {
176        use super::*;
177        use std::time::Duration;
178
179        #[test]
180        fn channel_order_stays_correct() {
181            const SAMPLE_RATE: u32 = 100;
182            const CHANNELS: u16 = 2;
183            let mut buf = SamplesBuffer::new(
184                CHANNELS,
185                SAMPLE_RATE,
186                (0..2000i16).into_iter().collect::<Vec<_>>(),
187            );
188            buf.try_seek(Duration::from_secs(5)).unwrap();
189            assert_eq!(
190                buf.next(),
191                Some(5i16 * SAMPLE_RATE as i16 * CHANNELS as i16)
192            );
193
194            assert!(buf.next().is_some_and(|s| s % 2 == 1));
195            assert!(buf.next().is_some_and(|s| s % 2 == 0));
196
197            buf.try_seek(Duration::from_secs(6)).unwrap();
198            assert!(buf.next().is_some_and(|s| s % 2 == 1),);
199        }
200    }
201}