web_audio_api/node/
channel_merger.rs1use std::fmt::Debug;
2
3use crate::context::{AudioContextRegistration, BaseAudioContext};
4use crate::render::{
5 AudioParamValues, AudioProcessor, AudioRenderQuantum, AudioWorkletGlobalScope,
6};
7use crate::MAX_CHANNELS;
8
9use super::{AudioNode, AudioNodeOptions, ChannelConfig, ChannelCountMode, ChannelInterpretation};
10
11#[track_caller]
20#[inline(always)]
21pub(crate) fn assert_valid_number_of_channels(number_of_channels: usize) {
22 assert!(
23 number_of_channels > 0 && number_of_channels <= MAX_CHANNELS,
24 "IndexSizeError - Invalid number of channels: {:?} is outside range [1, {:?}]",
25 number_of_channels,
26 MAX_CHANNELS
27 );
28}
29
30#[track_caller]
38#[inline(always)]
39fn assert_valid_channel_count(count: usize) {
40 assert!(
41 count == 1,
42 "InvalidStateError - channel count of ChannelMergerNode must be equal to 1"
43 );
44}
45
46#[track_caller]
54#[inline(always)]
55fn assert_valid_channel_count_mode(mode: ChannelCountMode) {
56 assert!(
57 mode == ChannelCountMode::Explicit,
58 "InvalidStateError - channel count of ChannelMergerNode must be set to Explicit"
59 );
60}
61
62#[derive(Clone, Debug)]
67pub struct ChannelMergerOptions {
68 pub number_of_inputs: usize,
69 pub audio_node_options: AudioNodeOptions,
70}
71
72impl Default for ChannelMergerOptions {
73 fn default() -> Self {
74 Self {
75 number_of_inputs: 6,
76 audio_node_options: AudioNodeOptions {
77 channel_count: 1,
78 channel_count_mode: ChannelCountMode::Explicit,
79 channel_interpretation: ChannelInterpretation::Speakers,
80 },
81 }
82 }
83}
84
85#[derive(Debug)]
87pub struct ChannelMergerNode {
88 registration: AudioContextRegistration,
89 channel_config: ChannelConfig,
90 number_of_inputs: usize,
91}
92
93impl AudioNode for ChannelMergerNode {
94 fn registration(&self) -> &AudioContextRegistration {
95 &self.registration
96 }
97
98 fn channel_config(&self) -> &ChannelConfig {
99 &self.channel_config
100 }
101
102 fn set_channel_count(&self, count: usize) {
103 assert_valid_channel_count(count);
104 }
105
106 fn set_channel_count_mode(&self, mode: ChannelCountMode) {
107 assert_valid_channel_count_mode(mode);
108 self.channel_config
109 .set_count_mode(mode, self.registration());
110 }
111
112 fn number_of_inputs(&self) -> usize {
113 self.number_of_inputs
114 }
115
116 fn number_of_outputs(&self) -> usize {
117 1
118 }
119}
120
121impl ChannelMergerNode {
122 pub fn new<C: BaseAudioContext>(context: &C, options: ChannelMergerOptions) -> Self {
123 context.base().register(move |registration| {
124 assert_valid_number_of_channels(options.number_of_inputs);
125
126 assert_valid_channel_count(options.audio_node_options.channel_count);
127 assert_valid_channel_count_mode(options.audio_node_options.channel_count_mode);
128
129 let node = ChannelMergerNode {
130 registration,
131 channel_config: options.audio_node_options.into(),
132 number_of_inputs: options.number_of_inputs,
133 };
134
135 let render = ChannelMergerRenderer {};
136
137 (node, Box::new(render))
138 })
139 }
140}
141
142#[derive(Debug)]
143struct ChannelMergerRenderer {}
144
145impl AudioProcessor for ChannelMergerRenderer {
146 fn process(
147 &mut self,
148 inputs: &[AudioRenderQuantum],
149 outputs: &mut [AudioRenderQuantum],
150 _params: AudioParamValues<'_>,
151 _scope: &AudioWorkletGlobalScope,
152 ) -> bool {
153 let output = &mut outputs[0];
155
156 if inputs.iter().any(|input| !input.is_silent()) {
161 output.set_number_of_channels(inputs.len());
162
163 inputs.iter().enumerate().for_each(|(i, input)| {
164 *output.channel_data_mut(i) = input.channel_data(0).clone();
165 });
166 } else {
167 output.make_silent();
168 }
169
170 false
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use float_eq::assert_float_eq;
177
178 use crate::context::{BaseAudioContext, OfflineAudioContext};
179 use crate::node::{AudioNode, AudioScheduledSourceNode};
180
181 use super::*;
182
183 #[test]
184 #[should_panic]
185 fn test_invalid_constructor_options() {
186 let sample_rate = 48000.;
187 let context = OfflineAudioContext::new(1, 128, sample_rate);
188
189 let mut options = ChannelMergerOptions::default();
190 options.audio_node_options.channel_count = 2;
191
192 let _merger = ChannelMergerNode::new(&context, options);
193 }
194
195 #[test]
196 #[should_panic]
197 fn test_invalid_set_channel_count() {
198 let sample_rate = 48000.;
199 let context = OfflineAudioContext::new(1, 128, sample_rate);
200
201 let options = ChannelMergerOptions::default();
202 let merger = ChannelMergerNode::new(&context, options);
203 merger.set_channel_count(3);
204 }
205
206 #[test]
207 fn test_merge() {
208 let sample_rate = 48000.;
209 let mut context = OfflineAudioContext::new(2, 128, sample_rate);
210
211 let merger = context.create_channel_merger(2);
212 merger.connect(&context.destination());
213
214 let mut src1 = context.create_constant_source();
215 src1.offset().set_value(2.);
216 src1.connect_from_output_to_input(&merger, 0, 0);
217 src1.start();
218
219 let mut src2 = context.create_constant_source();
220 src2.offset().set_value(3.);
221 src2.connect_from_output_to_input(&merger, 0, 1);
222 src2.start();
223
224 let buffer = context.start_rendering_sync();
225
226 let left = buffer.get_channel_data(0);
227 assert_float_eq!(left, &[2.; 128][..], abs_all <= 0.);
228
229 let right = buffer.get_channel_data(1);
230 assert_float_eq!(right, &[3.; 128][..], abs_all <= 0.);
231 }
232
233 #[test]
234 fn test_merge_disconnect() {
235 let sample_rate = 48000.;
236 let length = 4 * 128;
237 let disconnect_at = length as f64 / sample_rate as f64 / 2.;
238 let mut context = OfflineAudioContext::new(2, length, sample_rate);
239
240 let merger = context.create_channel_merger(2);
241 merger.connect(&context.destination());
242
243 let mut src1 = context.create_constant_source();
244 src1.offset().set_value(2.);
245 src1.connect_from_output_to_input(&merger, 0, 0);
246 src1.start();
247
248 let mut src2 = context.create_constant_source();
249 src2.offset().set_value(3.);
250 src2.connect_from_output_to_input(&merger, 0, 1);
251 src2.start();
252
253 context.suspend_sync(disconnect_at, move |_| src2.disconnect());
254
255 let buffer = context.start_rendering_sync();
256
257 let left = buffer.get_channel_data(0);
258 assert_float_eq!(left, &vec![2.; length][..], abs_all <= 0.);
259
260 let right = buffer.get_channel_data(1);
261 assert_float_eq!(
262 &right[0..length / 2],
263 &vec![3.; length / 2][..],
264 abs_all <= 0.
265 );
266 assert_float_eq!(
267 &right[length / 2..],
268 &vec![0.; length / 2][..],
269 abs_all <= 0.
270 );
271 }
272}