firewheel_nodes/
delay_compensation.rs1use bevy_platform::prelude::Vec;
2use firewheel_core::node::NodeError;
3use firewheel_core::{
4 channel_config::{ChannelConfig, NonZeroChannelCount},
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) -> Result<AudioNodeInfo, NodeError> {
49 Ok(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 }
58
59 fn construct_processor(
60 &self,
61 config: &Self::Configuration,
62 _cx: ConstructProcessorContext,
63 ) -> Result<impl AudioNodeProcessor, NodeError> {
64 let channels = config.channels.get().get() as usize;
65 let buffer_len = channels * config.delay_frames;
66
67 let mut buffer: Vec<f32> = Vec::new();
68 buffer.reserve_exact(buffer_len);
69 buffer.resize(buffer_len, 0.0);
70
71 Ok(Processor {
72 buffer,
73 delay_frames: config.delay_frames,
74 ptr: 0,
75 num_silent_frames_per_channel: smallvec![config.delay_frames; channels],
76 })
77 }
78}
79
80struct Processor {
81 buffer: Vec<f32>,
82 delay_frames: usize,
83 ptr: usize,
84 num_silent_frames_per_channel: SmallVec<[usize; 4]>,
85}
86
87impl AudioNodeProcessor for Processor {
88 fn bypassed(&mut self, bypassed: bool) {
89 if !bypassed {
90 self.buffer.fill(0.0);
91 self.ptr = 0;
92 for ch in self.num_silent_frames_per_channel.iter_mut() {
93 *ch = self.buffer.len();
94 }
95 }
96 }
97
98 fn process(
99 &mut self,
100 info: &ProcInfo,
101 buffers: ProcBuffers,
102 _extra: &mut ProcExtra,
103 ) -> ProcessStatus {
104 if self.delay_frames == 0 {
105 return ProcessStatus::Bypass;
106 }
107
108 let mut out_silence_mask = SilenceMask::NONE_SILENT;
110
111 let extra_input_frames = info.frames.saturating_sub(self.delay_frames);
112 let first_copy_frames = info.frames.min(self.delay_frames - self.ptr);
113 let second_copy_frames = (info.frames - first_copy_frames).min(self.ptr);
114
115 for (ch_i, (((in_buf, out_buf), delay_buf), num_silent_frames)) in buffers
116 .inputs
117 .iter()
118 .zip(buffers.outputs.iter_mut())
119 .zip(self.buffer.chunks_exact_mut(self.delay_frames))
120 .zip(self.num_silent_frames_per_channel.iter_mut())
121 .enumerate()
122 {
123 let is_input_silent = info.in_silence_mask.is_channel_silent(ch_i);
124
125 let clear_output = *num_silent_frames == self.delay_frames
126 && (info.frames <= self.delay_frames || is_input_silent);
127
128 if clear_output {
129 if !info.out_silence_mask.is_channel_silent(ch_i) {
130 out_buf[..info.frames].fill(0.0);
131 }
132
133 out_silence_mask.set_channel(ch_i, true);
134 } else {
135 out_buf[..first_copy_frames]
136 .copy_from_slice(&delay_buf[self.ptr..self.ptr + first_copy_frames]);
137
138 if second_copy_frames > 0 {
139 out_buf[first_copy_frames..first_copy_frames + second_copy_frames]
140 .copy_from_slice(&delay_buf[..second_copy_frames]);
141 }
142
143 if extra_input_frames > 0 {
144 if is_input_silent {
145 out_buf[self.delay_frames..info.frames].fill(0.0);
146 } else {
147 out_buf[self.delay_frames..info.frames]
148 .copy_from_slice(&in_buf[..extra_input_frames]);
149 }
150 }
151 }
152
153 if !is_input_silent || *num_silent_frames < self.delay_frames {
154 delay_buf[self.ptr..self.ptr + first_copy_frames].copy_from_slice(
155 &in_buf[extra_input_frames..extra_input_frames + first_copy_frames],
156 );
157
158 if second_copy_frames > 0 {
159 delay_buf[..second_copy_frames].copy_from_slice(
160 &in_buf[extra_input_frames + first_copy_frames..info.frames],
161 );
162 }
163 }
164
165 *num_silent_frames = if is_input_silent {
166 (*num_silent_frames + info.frames).min(self.delay_frames)
167 } else {
168 0
169 };
170 }
171
172 if info.frames < self.delay_frames {
173 self.ptr += info.frames;
174 if self.ptr >= self.delay_frames {
175 self.ptr -= self.delay_frames;
176 }
177 }
178
179 ProcessStatus::OutputsModifiedWithMask(MaskType::Silence(out_silence_mask))
180 }
181
182 fn new_stream(
183 &mut self,
184 _stream_info: &firewheel_core::StreamInfo,
185 _context: &mut ProcStreamCtx,
186 ) {
187 self.buffer.fill(0.0);
188 self.num_silent_frames_per_channel.fill(self.delay_frames);
189 }
190}