Skip to main content

sample_resource/
lib.rs

1//! # sample-resource
2//! Traits for working with arbitrary resources of audio samples
3
4#![forbid(missing_docs)]
5#![forbid(unsafe_code)]
6#![cfg_attr(not(feature = "std"), no_std)]
7
8pub mod helpers;
9
10use core::ops::Range;
11
12/// An immutable resource of audio samples which has its data stored in de-interleaved
13/// `f32` format, and which has all of its data loaded into memory.
14///
15/// # Implementation example
16/// ```
17/// # use sample_resource::{SampleResourceF32, SampleResourceInfo};
18/// struct MyResource {
19///     data: Vec<Vec<f32>>,
20///     frames: usize,
21///     sample_rate: Option<f64>,
22/// }
23///
24/// impl SampleResourceF32 for MyResource {
25///     fn info(&self) -> SampleResourceInfo {
26///         SampleResourceInfo {
27///             num_channels: self.data.len(),
28///             len_frames: self.frames as u64,
29///             sample_rate: self.sample_rate,
30///         }
31///     }
32///
33///     fn channel(&self, i: usize) -> Option<&[f32]> {
34///         self.data.get(i).map(|ch| ch.as_slice())
35///     }
36/// }
37/// ```
38///
39/// # Realtime Safety
40/// 3rd party libraries that implement this trait should expect that all methods in
41/// this trait may be called in a realtime thread. For more information, see:
42/// <http://www.rossbencina.com/code/real-time-audio-programming-101-time-waits-for-nothing>
43pub trait SampleResourceF32 {
44    /// Retrieve information about this sample resource. This information is
45    /// not allowed to change for the lifetime of this sample resource.
46    fn info(&self) -> SampleResourceInfo;
47
48    /// Return an immutable slice to the entire channel at index `i`.
49    ///
50    /// Returns `None` if `i` is out-of-bounds.
51    fn channel(&self, i: usize) -> Option<&[f32]>;
52}
53
54/// A mutable resource of audio samples which has its data stored in de-interleaved
55/// `f32` format, and which has all of its data loaded into memory.
56///
57/// # Implementation example
58/// ```
59/// # use sample_resource::{SampleResourceF32, SampleResourceF32Mut, SampleResourceInfo};
60/// struct MyResource {
61///     data: Vec<Vec<f32>>,
62///     frames: usize,
63///     sample_rate: Option<f64>,
64/// }
65///
66/// impl SampleResourceF32 for MyResource {
67///     fn info(&self) -> SampleResourceInfo {
68///         SampleResourceInfo {
69///             num_channels: self.data.len(),
70///             len_frames: self.frames as u64,
71///             sample_rate: self.sample_rate,
72///         }
73///     }
74///     fn channel(&self, i: usize) -> Option<&[f32]> {
75///         self.data.get(i).map(|ch| ch.as_slice())
76///     }
77/// }
78///
79/// impl SampleResourceF32Mut for MyResource {
80///     fn channel_mut(&mut self, i: usize) -> Option<&mut [f32]> {
81///         self.data.get_mut(i).map(|ch| ch.as_mut_slice())
82///     }
83///
84///     fn channel_mut_2(&mut self, indices: [usize; 2]) -> Option<[&mut [f32]; 2]> {
85///         self.data
86///             .get_disjoint_mut(indices)
87///             .map(|ch| ch.map(|v| v.as_mut_slice()))
88///             .ok()
89///     }
90/// }
91/// ```
92///
93/// # Realtime Safety
94/// 3rd party libraries that implement this trait should expect that all methods in
95/// this trait may be called in a realtime thread. For more information, see:
96/// <http://www.rossbencina.com/code/real-time-audio-programming-101-time-waits-for-nothing>
97pub trait SampleResourceF32Mut: SampleResourceF32 {
98    /// Return a mutable slice to the entire channel at index `i`.
99    ///
100    /// Returns `None` if `i` is out-of-bounds.
101    fn channel_mut(&mut self, i: usize) -> Option<&mut [f32]>;
102
103    /// Return a mutable slice to two channels at once.
104    ///
105    /// Returns `None` if any index is out-of-bounds or if the
106    /// indices overlap.
107    fn channel_mut_2(&mut self, indices: [usize; 2]) -> Option<[&mut [f32]; 2]>;
108}
109
110/// Information about a [`SampleResource`] or a [`SampleResourceF32`], a resource
111/// of audio samples with a known length.
112///
113/// This information is *NOT* allowed to change for the lifetime of this sample
114/// resource.
115#[derive(Default, Debug, Clone, Copy, PartialEq)]
116pub struct SampleResourceInfo {
117    /// The number of audio channels in this resource.
118    pub num_channels: usize,
119
120    /// The length of this resource in frames (samples of a single channel
121    /// of audio, not to be confused with video frames).
122    pub len_frames: u64,
123
124    /// The sample rate of this resource. This will be`None` if the sample
125    /// rate is unknown.
126    pub sample_rate: Option<f64>,
127}
128
129/// Information about the caching behavior of a [`SampleResource`].
130///
131/// This is only relevant for [`SampleResource`]s which don't have all of
132/// their data loaded in memory, such as those that stream data from a disk or
133/// over a network. [`SampleResource`]s which already have all of their data
134/// loaded in memory do not need to implement this.
135///
136/// This information is allowed to change (usually in response to a call
137/// to [`SampleResource::notify_playhead_behavior()`]).
138#[derive(Default, Debug, Clone, Copy, PartialEq)]
139pub struct SampleCacheInfo {
140    /// The number of frames (samples in a single channel of audio)
141    /// contained in a single cached block.
142    ///
143    /// This size must be sufficient enough to where if
144    /// [`SampleResource::fill_buffers_f32`] is called at the starting frame of
145    /// a cached block, there is ample enough time* (when playing back at the
146    /// speed reported in [`SampleResource::notify_playhead_behavior()`]) for
147    /// the stream to catch up before the cache is exhausted.
148    pub cache_block_len: usize,
149
150    /// The maximum supported playback speed. (Where `1.0` is playing at the
151    /// sample rate of this resource, `0.5` is playing at half the sample rate
152    /// of this resource, and `2.0` is playing at twice the speed of the
153    /// sample rate of this resource).
154    pub max_playback_speed: f64,
155
156    /// The number of available cache blocks.
157    pub num_cache_blocks: usize,
158}
159
160/// Information a [`SampleResource`] can use to make optimal caching decisions
161/// based on the behavior of the playhead.
162#[derive(Debug, Clone, Copy, PartialEq)]
163pub struct PlayheadBehavior {
164    /// The speed at which the playhead is moving.  (Where `1.0` is playing at
165    /// the sample rate of this resource, `0.5` is playing at half the sample
166    /// rate of this resource, and `2.0` is playing at twice the speed of the
167    /// sample rate of this resource).
168    pub playback_speed: f64,
169
170    /// `True` if playing backwards, `false` if playing forwards.
171    pub is_playing_backwards: bool,
172}
173
174impl Default for PlayheadBehavior {
175    fn default() -> Self {
176        Self {
177            playback_speed: 1.0,
178            is_playing_backwards: false,
179        }
180    }
181}
182
183/// A resource of audio samples with a known length.
184///
185/// # Implementation examples
186/// ```
187/// # use core::ops::Range;
188/// # use sample_resource::{SampleResource, SampleResourceInfo};
189/// // Interleaved example
190/// struct MyInterleavedResource {
191///     data: Vec<i16>,
192///     channels: usize,
193///     sample_rate: Option<f64>,
194/// }
195///
196/// impl SampleResource for MyInterleavedResource {
197///     fn info(&self) -> SampleResourceInfo {
198///         SampleResourceInfo {
199///             num_channels: self.channels,
200///             len_frames: (self.data.len() / self.channels) as u64,
201///             sample_rate: self.sample_rate,
202///         }
203///     }
204///
205///     fn fill_buffers_f32(
206///         &self,
207///         buffer: &mut [&mut [f32]],
208///         buffer_range: Range<usize>,
209///         start_frame: u64,
210///     ) -> usize {
211///         sample_resource::helpers::fill_buffers_interleaved(
212///             buffer,
213///             buffer_range,
214///             start_frame,
215///             self.channels,
216///             self.data.len() / self.channels,
217///             &self.data,
218///         )
219///     }
220/// }
221///
222/// // Deinterleaved example
223/// struct MyDeinterleavedResource {
224///     data: Vec<Vec<i16>>,
225///     frames: usize,
226///     channels: usize,
227///     sample_rate: Option<f64>,
228/// }
229///
230/// impl SampleResource for MyDeinterleavedResource {
231///     fn info(&self) -> SampleResourceInfo {
232///         SampleResourceInfo {
233///             num_channels: self.channels,
234///             len_frames: self.frames as u64,
235///             sample_rate: self.sample_rate,
236///         }
237///     }
238///
239///     fn fill_buffers_f32(
240///         &self,
241///         buffer: &mut [&mut [f32]],
242///         buffer_range: Range<usize>,
243///         start_frame: u64,
244///     ) -> usize {
245///         sample_resource::helpers::fill_buffers_deinterleaved(
246///             buffer,
247///             buffer_range,
248///             start_frame,
249///             self.frames,
250///             &self.data,
251///         )
252///     }
253/// }
254/// ```
255///
256/// # Realtime Safety
257/// 3rd party libraries that implement this trait should expect that all methods in
258/// this trait may be called in a realtime thread. For more information, see:
259/// <http://www.rossbencina.com/code/real-time-audio-programming-101-time-waits-for-nothing>
260pub trait SampleResource {
261    /// Retrieve information about this sample resource. This information is
262    /// *NOT* allowed to change for the lifetime of this sample resource.
263    fn info(&self) -> SampleResourceInfo;
264
265    /// Fill the given buffers with audio data in `f32` format starting from the
266    /// given starting frame in the sample resource.
267    ///
268    /// Returns the number of frames (samples in a single channel of audio) that
269    /// were successfully written to the buffer. This may be less than the requested
270    /// number of frames if `buffer_range` falls outside the range of the sample
271    /// resource or if a cache miss occured.
272    ///
273    /// * `buffer` - The buffers to fill with data. If the length of `buffers`
274    ///   is greater than the number of channels in this resource, then the extra
275    ///   channels will be left untouched.
276    /// * `buffer_range` - The range inside each channel slice in `buffers` to
277    ///   fill with data.
278    ///     * If a range falls outside the range of the sample
279    ///       resource, then that range will be left untouched. (Note it is the
280    ///       caller's responsibility to handle looping behavior, not the sample
281    ///       resource's responsibility.)
282    ///     * If a cache miss occured, then the range
283    ///       [`buffer_range.start + return_value..buffer_range.end`] will be filled
284    ///       with zeros.
285    /// * `start_frame` - The frame (sample of a single channel of audio) in the
286    ///   sample resource to start copying from (inclusive).
287    ///
288    /// The data will *NOT* be declicked (as in clicking caused by pausing/resuming,
289    /// the playhead jumping to a different position, or by a cache miss). It is up
290    /// to the caller to do their own declicking.
291    ///
292    /// When playing backwards (notified in
293    /// [`SampleResource::notify_playhead_behavior()`]), the data in the buffer
294    /// will *NOT* be reversed. It is up to the caller to reverse the data
295    /// themselves.
296    ///
297    /// # Panics
298    /// Panics if `buffer_range` is out-of-bounds for any channel slice in `buffer`.
299    fn fill_buffers_f32(
300        &self,
301        buffer: &mut [&mut [f32]],
302        buffer_range: Range<usize>,
303        start_frame: u64,
304    ) -> usize;
305
306    // -- Optional methods ---------------------------------------------------------
307
308    /// Information about the caching behavior of this sample resource. Returns
309    /// `None` if caching is not used by this resource.
310    ///
311    /// This information is allowed to change (usually in response to a call
312    /// to [`SampleResource::notify_playhead_behavior()`]).
313    ///
314    /// This is only relevant for sample resources which don't have all of
315    /// their data loaded in memory, such as those that stream data from a disk or
316    /// over a network. Sample resources which already have all of their data
317    /// loaded in memory should return `None`.
318    ///
319    /// By default this returns `None`.
320    fn cache_info(&self) -> Option<&SampleCacheInfo> {
321        None
322    }
323
324    /// Request the sample resource to cache a block of frames (samples
325    /// in a single channel of audio).
326    ///
327    /// * `start_frame` - The starting frame (samples in a single channel of audio)
328    ///   in the sample resource to cache a new block (inclusive). If a resulting
329    ///   range falls outside the range of the sample resource, then ignore that
330    ///   range.
331    /// * `priority` - A number describing how important this cache is (higher
332    ///   means more important). This can be used by the resource to determine
333    ///   which caches to discard if there are no more cache blocks available.
334    ///
335    /// This is only relevant for sample resources which stream data from disk
336    /// or over a network. Sample resources which already have all their contents
337    /// stored in memory do not need to implement this method.
338    fn cache(&self, start_frame: u64, priority: u32) {
339        let _ = start_frame;
340        let _ = priority;
341    }
342
343    /// Notify the sample resource the speed and the direction that the playhead
344    /// is moving. This can be used to help the sample resource make optimal
345    /// caching decisions.
346    ///
347    /// Until this method is called, sample resources should assume that
348    /// `playback_speed = 1.0` and `is_playing_backwards = false`.
349    ///
350    /// This is only relevant for sample resources which stream data from disk
351    /// or over a network. Sample resources which already have all their contents
352    /// stored in memory do not need to implement this method.
353    fn notify_playhead_behavior(&self, behavior: PlayheadBehavior) {
354        let _ = behavior;
355    }
356}