firewheel_nodes/
delay_compensation.rs1use bevy_platform::prelude::Vec;
2use firewheel_core::{
3 channel_config::{ChannelConfig, NonZeroChannelCount},
4 event::ProcEvents,
5 mask::{MaskType, SilenceMask},
6 node::{
7 AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, ProcBuffers,
8 ProcExtra, ProcInfo, ProcStreamCtx, ProcessStatus,
9 },
10};
11use smallvec::{smallvec, SmallVec};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
16#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct DelayCompNodeConfig {
19 pub channels: NonZeroChannelCount,
21 pub delay_frames: usize,
24}
25
26impl Default for DelayCompNodeConfig {
27 fn default() -> Self {
28 Self {
29 channels: NonZeroChannelCount::STEREO,
30 delay_frames: 0,
31 }
32 }
33}
34
35#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
40#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
41#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43pub struct DelayCompensationNode;
44
45impl AudioNode for DelayCompensationNode {
46 type Configuration = DelayCompNodeConfig;
47
48 fn info(&self, config: &Self::Configuration) -> AudioNodeInfo {
49 AudioNodeInfo::new()
50 .debug_name("stereo_to_mono")
51 .channel_config(ChannelConfig {
52 num_inputs: config.channels.get(),
53 num_outputs: config.channels.get(),
54 })
55 }
56
57 fn construct_processor(
58 &self,
59 config: &Self::Configuration,
60 _cx: ConstructProcessorContext,
61 ) -> impl AudioNodeProcessor {
62 let channels = config.channels.get().get() as usize;
63 let buffer_len = channels * config.delay_frames;
64
65 let mut buffer: Vec<f32> = Vec::new();
66 buffer.reserve_exact(buffer_len);
67 buffer.resize(buffer_len, 0.0);
68
69 Processor {
70 buffer,
71 delay_frames: config.delay_frames,
72 ptr: 0,
73 num_silent_frames_per_channel: smallvec![config.delay_frames; channels],
74 }
75 }
76}
77
78struct Processor {
79 buffer: Vec<f32>,
80 delay_frames: usize,
81 ptr: usize,
82 num_silent_frames_per_channel: SmallVec<[usize; 4]>,
83}
84
85impl AudioNodeProcessor for Processor {
86 fn process(
87 &mut self,
88 info: &ProcInfo,
89 buffers: ProcBuffers,
90 _events: &mut ProcEvents,
91 _extra: &mut ProcExtra,
92 ) -> ProcessStatus {
93 if self.delay_frames == 0 {
94 return ProcessStatus::Bypass;
95 }
96
97 let mut out_silence_mask = SilenceMask::NONE_SILENT;
99
100 let extra_input_frames = if info.frames > self.delay_frames {
101 info.frames - self.delay_frames
102 } else {
103 0
104 };
105 let first_copy_frames = info.frames.min(self.delay_frames - self.ptr);
106 let second_copy_frames = (info.frames - first_copy_frames).min(self.ptr);
107
108 for (ch_i, (((in_buf, out_buf), delay_buf), num_silent_frames)) in buffers
109 .inputs
110 .iter()
111 .zip(buffers.outputs.iter_mut())
112 .zip(self.buffer.chunks_exact_mut(self.delay_frames))
113 .zip(self.num_silent_frames_per_channel.iter_mut())
114 .enumerate()
115 {
116 let is_input_silent = info.in_silence_mask.is_channel_silent(ch_i);
117
118 let clear_output = *num_silent_frames == self.delay_frames
119 && (info.frames <= self.delay_frames || is_input_silent);
120
121 if clear_output {
122 if !info.out_silence_mask.is_channel_silent(ch_i) {
123 out_buf[..info.frames].fill(0.0);
124 }
125
126 out_silence_mask.set_channel(ch_i, true);
127 } else {
128 out_buf[..first_copy_frames]
129 .copy_from_slice(&delay_buf[self.ptr..self.ptr + first_copy_frames]);
130
131 if second_copy_frames > 0 {
132 out_buf[first_copy_frames..first_copy_frames + second_copy_frames]
133 .copy_from_slice(&delay_buf[..second_copy_frames]);
134 }
135
136 if extra_input_frames > 0 {
137 if is_input_silent {
138 out_buf[self.delay_frames..info.frames].fill(0.0);
139 } else {
140 out_buf[self.delay_frames..info.frames]
141 .copy_from_slice(&in_buf[..extra_input_frames]);
142 }
143 }
144 }
145
146 if !is_input_silent || *num_silent_frames < self.delay_frames {
147 delay_buf[self.ptr..self.ptr + first_copy_frames].copy_from_slice(
148 &in_buf[extra_input_frames..extra_input_frames + first_copy_frames],
149 );
150
151 if second_copy_frames > 0 {
152 delay_buf[..second_copy_frames].copy_from_slice(
153 &in_buf[extra_input_frames + first_copy_frames..info.frames],
154 );
155 }
156 }
157
158 *num_silent_frames = if is_input_silent {
159 (*num_silent_frames + info.frames).min(self.delay_frames)
160 } else {
161 0
162 };
163 }
164
165 if info.frames < self.delay_frames {
166 self.ptr += info.frames;
167 if self.ptr >= self.delay_frames {
168 self.ptr -= self.delay_frames;
169 }
170 }
171
172 ProcessStatus::OutputsModifiedWithMask(MaskType::Silence(out_silence_mask))
173 }
174
175 fn new_stream(
176 &mut self,
177 _stream_info: &firewheel_core::StreamInfo,
178 _context: &mut ProcStreamCtx,
179 ) {
180 self.buffer.fill(0.0);
181 self.num_silent_frames_per_channel.fill(self.delay_frames);
182 }
183}