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}