Skip to main content

firewheel_nodes/sampler/
resource.rs

1use core::{
2    num::{NonZeroU32, NonZeroUsize},
3    ops::Range,
4};
5use firewheel_core::{
6    collector::{ArcGc, OwnedGcUnsized},
7    sample_resource::{SampleResource, SampleResourceInfo},
8};
9
10#[cfg(not(feature = "std"))]
11use bevy_platform::prelude::Box;
12
13/// A source of audio samples for a [`SamplerNode`](super::SamplerNode).
14pub enum SamplerNodeResource {
15    /// A resource of audio samples where the entire contents of the sample are
16    /// already loaded into memory.
17    ///
18    /// Prefer this for resources which are less than 20 or so seconds long
19    /// (i.e. sound effects).
20    InMemory(ArcGc<dyn SampleResource + Send + Sync + 'static>),
21
22    /// NOT IMPLEMENTED YET! Will lead to a panic if used.
23    ///
24    /// A resource of audio samples that are streamed from disk or over a network.
25    ///
26    /// Prefer this for resources which are greater than 20 or so seconds long
27    /// (i.e. music tracks and ambience).
28    ///
29    /// This uses considerably less memory, but requires a more complicated setup.
30    /// It also has the potential to run into cache misses if the playhead is moved
31    /// to a region that hasn't been loaded yet, or if the stream fails to send
32    /// enough samples in time.
33    Streamed(OwnedGcUnsized<dyn StreamedSample>),
34}
35
36impl SamplerNodeResource {
37    pub fn from_sample<T: SampleResource + Send + Sync + 'static>(sample: T) -> Self {
38        Self::InMemory(sample.into())
39    }
40
41    pub fn from_streamed<T: StreamedSample>(sample: T) -> Self {
42        Self::Streamed(OwnedGcUnsized::new_unsized(Box::new(sample)))
43    }
44
45    /// The number of channels in this resource.
46    pub fn num_channels(&self) -> NonZeroUsize {
47        match self {
48            Self::InMemory(s) => s.num_channels(),
49            Self::Streamed(s) => s.num_channels(),
50        }
51    }
52
53    /// The length of this resource in samples (of a single channel of audio).
54    ///
55    /// Not to be confused with video frames.
56    pub fn len_frames(&self) -> u64 {
57        match self {
58            Self::InMemory(s) => s.len_frames(),
59            Self::Streamed(s) => s.len_frames(),
60        }
61    }
62
63    /// The sample rate of this resource.
64    ///
65    /// Returns `None` if the sample rate is unknown.
66    pub fn sample_rate(&self) -> Option<NonZeroU32> {
67        match self {
68            Self::InMemory(s) => s.sample_rate(),
69            Self::Streamed(s) => s.sample_rate(),
70        }
71    }
72
73    /// Fill the given buffers with audio data starting from the given
74    /// starting frame in the resource.
75    ///
76    /// * `out_buffer` - The buffers to fill with data. If the length of `buffers`
77    ///   is greater than the number of channels in this resource, then ignore
78    ///   the extra buffers.
79    /// * `out_buffer_range` - The range inside each buffer slice in which to
80    ///   fill with data. Do not fill any data outside of this range.
81    /// * `start_frame` - The sample (of a single channel of audio) in the
82    ///   resource at which to start copying from. Not to be confused with video
83    ///   frames.
84    /// * `speed` - The speed at which playback is occurring, where `1.0` is
85    ///   playing at the sample rate of this resource, `0.5` is playing at half
86    ///   the sample rate, and `2.0` is playing at twice the sample rate.
87    ///
88    /// Returns the number of frames that were successfully filled. This may
89    /// be less than the length of `out_buffer_range` if the range is all or
90    /// partly out of bounds of the resource, or if a cache miss occurred.
91    /// Any frames that were not successfully filled will be left untouched.
92    pub fn fill_buffers(
93        &mut self,
94        out_buffer: &mut [&mut [f32]],
95        out_buffer_range: Range<usize>,
96        start_frame: u64,
97        speed: f64,
98        is_playing_backwards: bool,
99    ) -> usize {
100        match self {
101            SamplerNodeResource::InMemory(s) => {
102                s.fill_buffers(out_buffer, out_buffer_range.clone(), start_frame)
103            }
104            SamplerNodeResource::Streamed(s) => s.fill_buffers(
105                out_buffer,
106                out_buffer_range,
107                start_frame,
108                speed,
109                is_playing_backwards,
110            ),
111        }
112    }
113
114    /// Returns `true` if the given range of frames is loaded
115    /// into memory and ready to be read.
116    pub fn range_is_ready(&mut self, range: Range<u64>) -> bool {
117        if let SamplerNodeResource::Streamed(s) = self {
118            s.range_is_ready(range)
119        } else {
120            true
121        }
122    }
123
124    /// Request to cache a new region at the given starting frame.
125    pub fn cache_new_starting_frame(&mut self, frame: u64, speed: f64, will_play_backwards: bool) {
126        if let SamplerNodeResource::Streamed(s) = self {
127            s.cache_new_starting_frame(frame, speed, will_play_backwards);
128        }
129    }
130}
131
132impl From<ArcGc<dyn SampleResource + Send + Sync + 'static>> for SamplerNodeResource {
133    fn from(value: ArcGc<dyn SampleResource + Send + Sync + 'static>) -> Self {
134        Self::InMemory(value)
135    }
136}
137
138impl From<OwnedGcUnsized<dyn StreamedSample>> for SamplerNodeResource {
139    fn from(value: OwnedGcUnsized<dyn StreamedSample>) -> Self {
140        Self::Streamed(value)
141    }
142}
143
144/// A resource of audio samples that are streamed from disk or over a network.
145///
146/// This uses considerably less memory, but requires a more complicated setup. It
147/// also has the potential to run into cache misses if the playhead is moved to a
148/// region that hasn't been loaded yet, or if the stream fails to send enough samples
149/// in time.
150pub trait StreamedSample: SampleResourceInfo + Send + Sync + 'static {
151    /// Fill the given buffers with audio data starting from the given
152    /// starting frame in the resource.
153    ///
154    /// * `out_buffer` - The buffers to fill with data. If the length of `buffers`
155    ///   is greater than the number of channels in this resource, then ignore
156    ///   the extra buffers.
157    /// * `out_buffer_range` - The range inside each buffer slice in which to
158    ///   fill with data. Do not fill any data outside of this range.
159    /// * `start_frame` - The sample (of a single channel of audio) in the
160    ///   resource at which to start copying from. Not to be confused with video
161    ///   frames.
162    /// * `speed` - The speed at which playback is occurring, where `1.0` is
163    ///   playing at the sample rate of this resource, `0.5` is playing at half
164    ///   the sample rate, and `2.0` is playing at twice the sample rate.
165    ///
166    /// Returns the number of frames that were successfully filled. This may
167    /// be less than the length of `out_buffer_range` if the range is all or
168    /// partly out of bounds of the resource, or if a cache miss occurred.
169    /// Any frames that were not successfully filled will be left untouched.
170    fn fill_buffers(
171        &mut self,
172        out_buffer: &mut [&mut [f32]],
173        out_buffer_range: Range<usize>,
174        start_frame: u64,
175        speed: f64,
176        is_playing_backwards: bool,
177    ) -> usize;
178
179    /// Returns `true` if the given range of frames is loaded
180    /// into memory and ready to be read.
181    fn range_is_ready(&mut self, range: Range<u64>) -> bool;
182
183    /// Request to cache a new region at the given starting frame.
184    fn cache_new_starting_frame(&mut self, frame: u64, speed: f64, will_play_backwards: bool);
185}