1use bevy_platform::sync::{Arc, Mutex, MutexGuard};
2use core::num::{NonZeroU32, NonZeroUsize};
3use firewheel_core::node::NodeError;
4use firewheel_core::{
5 StreamInfo,
6 channel_config::{ChannelConfig, ChannelCount, NonZeroChannelCount},
7 diff::{Diff, EventQueue, Patch, PatchError, PathBuilder},
8 dsp::buffer::SequentialBuffer,
9 event::{ParamData, ProcEvents},
10 node::{
11 AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, ProcBuffers,
12 ProcExtra, ProcInfo, ProcStreamCtx, ProcessStatus,
13 },
14};
15
16#[cfg(not(feature = "std"))]
17use num_traits::Float;
18
19#[derive(Debug, Clone, Copy, PartialEq)]
21#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
22#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct TripleBufferConfig {
25 pub channels: NonZeroChannelCount,
27 pub max_window_size: WindowSize,
29}
30
31impl Default for TripleBufferConfig {
32 fn default() -> Self {
33 Self {
34 channels: NonZeroChannelCount::STEREO,
35 max_window_size: WindowSize::default(),
36 }
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq)]
42#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
43#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45pub enum WindowSize {
46 Samples(u32),
49 Seconds(f64),
51}
52
53impl WindowSize {
54 pub fn as_frames(&self, sample_rate: NonZeroU32) -> u32 {
55 match self {
56 Self::Samples(samples) => *samples,
57 Self::Seconds(seconds) => (seconds * (sample_rate.get() as f64)).round() as u32,
58 }
59 }
60}
61
62impl Default for WindowSize {
63 fn default() -> Self {
64 Self::Samples(2048)
65 }
66}
67
68impl Diff for WindowSize {
69 fn diff<E: EventQueue>(&self, baseline: &Self, path: PathBuilder, event_queue: &mut E) {
70 if self != baseline {
71 match self {
72 WindowSize::Samples(samples) => event_queue.push_param(*samples, path),
73 WindowSize::Seconds(seconds) => event_queue.push_param(*seconds, path),
74 }
75 }
76 }
77}
78
79impl Patch for WindowSize {
80 type Patch = Self;
81
82 fn patch(data: &ParamData, _: &[u32]) -> Result<Self::Patch, PatchError> {
83 match data {
84 ParamData::U32(samples) => Ok(Self::Samples(*samples)),
85 ParamData::F64(seconds) => Ok(Self::Seconds(*seconds)),
86 _ => Err(PatchError::InvalidData),
87 }
88 }
89
90 fn apply(&mut self, value: Self::Patch) {
91 *self = value;
92 }
93}
94
95#[derive(Default, Diff, Patch, Debug, Clone, Copy, PartialEq)]
99#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
100#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
101#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
102pub struct TripleBufferNode {
103 pub window_size: WindowSize,
105}
106
107#[derive(Clone)]
108pub struct TripleBufferState {
109 num_channels: NonZeroChannelCount,
110 active_state: Arc<Mutex<Option<ActiveState>>>,
111}
112
113impl TripleBufferState {
114 pub fn num_channels(&self) -> NonZeroChannelCount {
116 self.num_channels
117 }
118
119 pub fn output<'a>(&'a mut self) -> OutputDataGuard<'a> {
121 OutputDataGuard {
122 guarded_state: self.active_state.lock().unwrap(),
123 }
124 }
125}
126
127struct ActiveState {
128 consumer: triple_buffer::Output<TripleBufferData>,
129 sample_rate: NonZeroU32,
130}
131
132pub struct OutputData<'a> {
133 pub buffer: &'a SequentialBuffer<f32>,
139
140 pub frames: usize,
142
143 pub generation: u64,
147}
148
149pub struct OutputDataGuard<'a> {
150 guarded_state: MutexGuard<'a, Option<ActiveState>>,
151}
152
153impl<'a> OutputDataGuard<'a> {
154 pub fn is_active(&self) -> bool {
156 self.guarded_state.is_some()
157 }
158
159 pub fn sample_rate(&self) -> Option<NonZeroU32> {
163 self.guarded_state.as_ref().map(|s| s.sample_rate)
164 }
165
166 pub fn data<'b>(&'b mut self) -> Option<OutputData<'b>> {
170 self.guarded_state.as_mut().map(|s| {
171 let c = s.consumer.read();
172 OutputData {
173 buffer: &c.buffer,
174 frames: c.frames,
175 generation: c.generation,
176 }
177 })
178 }
179
180 pub fn peek_data<'b>(&'b self) -> Option<OutputData<'b>> {
185 self.guarded_state.as_ref().map(|s| {
186 let c = s.consumer.output_buffer();
187 OutputData {
188 buffer: &c.buffer,
189 frames: c.frames,
190 generation: c.generation,
191 }
192 })
193 }
194}
195
196impl AudioNode for TripleBufferNode {
197 type Configuration = TripleBufferConfig;
198
199 fn info(&self, config: &Self::Configuration) -> Result<AudioNodeInfo, NodeError> {
200 Ok(AudioNodeInfo::new()
201 .debug_name("triple_buffer")
202 .channel_config(ChannelConfig {
203 num_inputs: config.channels.get(),
204 num_outputs: ChannelCount::ZERO,
205 })
206 .custom_state(TripleBufferState {
207 num_channels: config.channels,
208 active_state: Arc::new(Mutex::new(None)),
209 }))
210 }
211
212 fn construct_processor(
213 &self,
214 config: &Self::Configuration,
215 mut cx: ConstructProcessorContext,
216 ) -> Result<impl AudioNodeProcessor, NodeError> {
217 let sample_rate = cx.stream_info.sample_rate;
218 let max_window_size_frames = config.max_window_size.as_frames(sample_rate) as usize;
219
220 let (producer, consumer) =
221 triple_buffer::triple_buffer::<TripleBufferData>(&TripleBufferData::new(
222 NonZeroUsize::new(config.channels.get().get() as usize).unwrap(),
223 max_window_size_frames,
224 0,
225 ));
226
227 let state = cx.custom_state_mut::<TripleBufferState>().unwrap();
228
229 *state.active_state.lock().unwrap() = Some(ActiveState {
230 consumer,
231 sample_rate,
232 });
233 let active_state = Arc::clone(&state.active_state);
234
235 let window_size_frames =
236 (self.window_size.as_frames(sample_rate) as usize).min(max_window_size_frames);
237
238 Ok(Processor {
239 producer: Some(producer),
240 config: *config,
241 max_window_size_frames,
242 params: *self,
243 window_size_frames,
244 tmp_ring_buffer: SequentialBuffer::new(
245 NonZeroUsize::new(config.channels.get().get() as usize).unwrap(),
246 max_window_size_frames,
247 ),
248 ring_buf_ptr: 0,
249 active_state,
250 generation: 0,
251 prev_publish_was_silent: true,
252 num_silent_frames_in_tmp: window_size_frames,
253 tmp_buffer_needs_cleared: false,
254 num_inputs: config.channels.get().get() as usize,
255 did_resize: false,
256 })
257 }
258}
259
260struct Processor {
261 producer: Option<triple_buffer::Input<TripleBufferData>>,
262 config: TripleBufferConfig,
263 max_window_size_frames: usize,
264
265 params: TripleBufferNode,
266 window_size_frames: usize,
267
268 tmp_ring_buffer: SequentialBuffer<f32>,
269 ring_buf_ptr: usize,
270
271 active_state: Arc<Mutex<Option<ActiveState>>>,
273 generation: u64,
274
275 prev_publish_was_silent: bool,
276 num_silent_frames_in_tmp: usize,
277 tmp_buffer_needs_cleared: bool,
278 num_inputs: usize,
279 did_resize: bool,
280}
281
282impl AudioNodeProcessor for Processor {
283 fn events(&mut self, info: &ProcInfo, events: &mut ProcEvents, _extra: &mut ProcExtra) {
284 let mut new_window_size_frames = self.window_size_frames;
285 for patch in events.drain_patches::<TripleBufferNode>() {
286 match patch {
287 TripleBufferNodePatch::WindowSize(window_size) => {
288 new_window_size_frames = (window_size.as_frames(info.sample_rate) as usize)
289 .min(self.max_window_size_frames);
290 }
291 }
292
293 self.params.apply(patch);
294 }
295
296 let producer = self.producer.as_mut().unwrap();
297
298 if self.window_size_frames != new_window_size_frames {
299 let prev = self.window_size_frames;
300
301 let data = producer.input_buffer_mut();
303
304 for (buf_ch, tmp_ch) in data
305 .buffer
306 .iter_channels_mut()
307 .zip(self.tmp_ring_buffer.iter_channels_mut())
308 {
309 let (head, tail) = tmp_ch[..prev].split_at(self.ring_buf_ptr);
310 buf_ch[..tail.len()].copy_from_slice(tail);
311 if tail.len() < prev {
312 buf_ch[tail.len()..prev].copy_from_slice(head);
313 }
314
315 if prev >= new_window_size_frames {
317 tmp_ch[..new_window_size_frames]
318 .copy_from_slice(&buf_ch[prev - new_window_size_frames..prev]);
319 } else {
320 let pad = new_window_size_frames - prev;
321 tmp_ch[..pad].fill(0.0);
322 tmp_ch[pad..new_window_size_frames].copy_from_slice(&buf_ch[..prev]);
323 }
324 }
325
326 self.window_size_frames = new_window_size_frames;
327 self.ring_buf_ptr = 0;
328 self.num_silent_frames_in_tmp = 0;
329 self.did_resize = true;
330 }
331 }
332
333 fn bypassed(&mut self, bypassed: bool) {
334 let Some(producer) = self.producer.as_mut() else {
335 return;
336 };
337
338 if bypassed {
339 {
340 let data = producer.input_buffer_mut();
341
342 for buf_ch in data.buffer.iter_channels_mut() {
343 buf_ch[..self.window_size_frames].fill(0.0);
344 }
345
346 self.generation += 1;
347 data.generation = self.generation;
348 data.frames = self.window_size_frames;
349 }
350
351 producer.publish();
352
353 for tmp_ch in self.tmp_ring_buffer.iter_channels_mut() {
354 tmp_ch[..self.window_size_frames].fill(0.0);
355 }
356
357 self.ring_buf_ptr = 0;
358 self.prev_publish_was_silent = true;
359 self.num_silent_frames_in_tmp = self.window_size_frames;
360 self.tmp_buffer_needs_cleared = false;
361 }
362 }
363
364 fn process(
365 &mut self,
366 info: &ProcInfo,
367 buffers: ProcBuffers,
368 _extra: &mut ProcExtra,
369 ) -> ProcessStatus {
370 let input_is_silent = info.in_silence_mask.all_channels_silent(self.num_inputs);
371 if input_is_silent {
372 self.num_silent_frames_in_tmp =
373 (self.num_silent_frames_in_tmp + info.frames).min(self.window_size_frames);
374 } else {
375 self.num_silent_frames_in_tmp = 0;
376 }
377
378 if self.num_silent_frames_in_tmp == self.window_size_frames
379 && self.prev_publish_was_silent
380 && !self.did_resize
381 {
382 self.tmp_buffer_needs_cleared = true;
384 return ProcessStatus::ClearAllOutputs;
385 }
386 self.did_resize = false;
387
388 if info.frames >= self.window_size_frames {
389 for (tmp_ch, in_ch) in self
391 .tmp_ring_buffer
392 .iter_channels_mut()
393 .zip(buffers.inputs.iter())
394 {
395 tmp_ch[..self.window_size_frames]
396 .copy_from_slice(&in_ch[info.frames - self.window_size_frames..info.frames]);
397 }
398 self.ring_buf_ptr = 0;
399 self.tmp_buffer_needs_cleared = false;
400 } else {
401 if self.tmp_buffer_needs_cleared {
402 self.tmp_buffer_needs_cleared = false;
403
404 for tmp_ch in self.tmp_ring_buffer.iter_channels_mut() {
405 tmp_ch[..self.window_size_frames].fill(0.0);
406 }
407 self.ring_buf_ptr = 0;
408
409 self.num_silent_frames_in_tmp = self.window_size_frames;
410 }
411
412 let first_copy_frames = info.frames.min(self.window_size_frames - self.ring_buf_ptr);
413 let second_copy_frames = info.frames - first_copy_frames;
414
415 for (tmp_ch, in_ch) in self
416 .tmp_ring_buffer
417 .iter_channels_mut()
418 .zip(buffers.inputs.iter())
419 {
420 if first_copy_frames > 0 {
421 tmp_ch[self.ring_buf_ptr..self.ring_buf_ptr + first_copy_frames]
422 .copy_from_slice(&in_ch[..first_copy_frames]);
423 }
424
425 if second_copy_frames > 0 {
426 tmp_ch[..second_copy_frames]
427 .copy_from_slice(&in_ch[first_copy_frames..info.frames]);
428 }
429 }
430
431 self.ring_buf_ptr = if second_copy_frames > 0 {
432 second_copy_frames
433 } else {
434 self.ring_buf_ptr + first_copy_frames
435 };
436 }
437
438 let producer = self.producer.as_mut().unwrap();
439
440 {
441 let buffer = producer.input_buffer_mut();
442
443 for (buf_ch, tmp_ch) in buffer
444 .buffer
445 .iter_channels_mut()
446 .zip(self.tmp_ring_buffer.iter_channels())
447 {
448 let (head, tail) = tmp_ch[..self.window_size_frames].split_at(self.ring_buf_ptr);
449 buf_ch[..tail.len()].copy_from_slice(tail);
450 buf_ch[tail.len()..self.window_size_frames].copy_from_slice(head);
451 }
452
453 self.generation += 1;
454 buffer.generation = self.generation;
455 buffer.frames = self.window_size_frames;
456 }
457
458 producer.publish();
459
460 self.prev_publish_was_silent = self.num_silent_frames_in_tmp == self.window_size_frames;
461
462 ProcessStatus::ClearAllOutputs
463 }
464
465 fn stream_stopped(&mut self, _context: &mut ProcStreamCtx) {
466 *self.active_state.lock().unwrap() = None;
467 self.producer = None;
468 }
469
470 fn new_stream(&mut self, stream_info: &StreamInfo, _context: &mut ProcStreamCtx) {
471 self.max_window_size_frames = self
472 .config
473 .max_window_size
474 .as_frames(stream_info.sample_rate) as usize;
475
476 self.window_size_frames = (self.params.window_size.as_frames(stream_info.sample_rate)
477 as usize)
478 .min(self.max_window_size_frames);
479
480 self.tmp_ring_buffer = SequentialBuffer::new(
481 NonZeroUsize::new(self.config.channels.get().get() as usize).unwrap(),
482 self.max_window_size_frames,
483 );
484
485 self.ring_buf_ptr = 0;
486 self.num_silent_frames_in_tmp = self.window_size_frames;
487 self.tmp_buffer_needs_cleared = false;
488 self.prev_publish_was_silent = true;
489
490 self.generation += 1;
491
492 let (producer, consumer) =
493 triple_buffer::triple_buffer::<TripleBufferData>(&TripleBufferData::new(
494 NonZeroUsize::new(self.config.channels.get().get() as usize).unwrap(),
495 self.max_window_size_frames,
496 self.generation,
497 ));
498
499 *self.active_state.lock().unwrap() = Some(ActiveState {
500 consumer,
501 sample_rate: stream_info.sample_rate,
502 });
503
504 self.producer = Some(producer);
505 }
506}
507
508struct TripleBufferData {
511 buffer: SequentialBuffer<f32>,
512 max_frames: usize,
513 frames: usize,
514 generation: u64,
515}
516
517impl TripleBufferData {
518 fn new(num_channels: NonZeroUsize, max_frames: usize, generation: u64) -> Self {
519 Self {
520 buffer: SequentialBuffer::new(num_channels, max_frames),
521 max_frames,
522 frames: 0,
523 generation,
524 }
525 }
526}
527
528impl Clone for TripleBufferData {
529 fn clone(&self) -> Self {
530 Self::new(self.buffer.num_channels(), self.max_frames, self.generation)
531 }
532}