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