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}