Skip to content

Commit 0185ed8

Browse files
authored
Merge pull request #443 from orottier/bugfix/channel-count-issue
Fix: do not upmix inputs to the AudioNode channelCount beforehand
2 parents 81144bf + b3739dd commit 0185ed8

File tree

5 files changed

+53
-55
lines changed

5 files changed

+53
-55
lines changed

src/node/biquad_filter.rs

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use std::any::Any;
33
use std::f64::consts::{PI, SQRT_2};
44

5+
use arrayvec::ArrayVec;
56
use num_complex::Complex;
67

78
use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
@@ -396,10 +397,7 @@ impl BiquadFilterNode {
396397
frequency: f_proc,
397398
q: q_proc,
398399
type_,
399-
x1: Vec::with_capacity(MAX_CHANNELS),
400-
x2: Vec::with_capacity(MAX_CHANNELS),
401-
y1: Vec::with_capacity(MAX_CHANNELS),
402-
y2: Vec::with_capacity(MAX_CHANNELS),
400+
xy: ArrayVec::new(),
403401
};
404402

405403
let node = Self {
@@ -549,10 +547,7 @@ struct BiquadFilterRenderer {
549547
/// `BiquadFilterType`
550548
type_: BiquadFilterType,
551549
// keep filter state for each channel
552-
x1: Vec<f64>,
553-
x2: Vec<f64>,
554-
y1: Vec<f64>,
555-
y2: Vec<f64>,
550+
xy: ArrayVec<[f64; 4], MAX_CHANNELS>,
556551
}
557552

558553
impl AudioProcessor for BiquadFilterRenderer {
@@ -572,10 +567,10 @@ impl AudioProcessor for BiquadFilterRenderer {
572567
if input.is_silent() {
573568
let mut ended = true;
574569

575-
if self.x1.iter().any(|&v| v.is_normal())
576-
|| self.x2.iter().any(|&v| v.is_normal())
577-
|| self.y1.iter().any(|&v| v.is_normal())
578-
|| self.y2.iter().any(|&v| v.is_normal())
570+
if self
571+
.xy
572+
.iter()
573+
.any(|v| v.iter().copied().any(f64::is_normal))
579574
{
580575
ended = false;
581576
}
@@ -595,16 +590,16 @@ impl AudioProcessor for BiquadFilterRenderer {
595590
// see https://webaudio.github.io/web-audio-api/#channels-tail-time
596591
let num_channels = input.number_of_channels();
597592

598-
if num_channels != self.x1.len() {
599-
self.x1.resize(num_channels, 0.);
600-
self.x2.resize(num_channels, 0.);
601-
self.y1.resize(num_channels, 0.);
602-
self.y2.resize(num_channels, 0.);
593+
if num_channels != self.xy.len() {
594+
self.xy.truncate(num_channels);
595+
for _ in self.xy.len()..num_channels {
596+
self.xy.push([0.; 4]);
597+
}
603598
}
604599

605600
output.set_number_of_channels(num_channels);
606601
} else {
607-
let num_channels = self.x1.len();
602+
let num_channels = self.xy.len();
608603
output.set_number_of_channels(num_channels);
609604
}
610605

@@ -649,12 +644,15 @@ impl AudioProcessor for BiquadFilterRenderer {
649644
};
650645

651646
for (channel_number, output_channel) in output.channels_mut().iter_mut().enumerate() {
652-
let input_channel = input.channel_data(channel_number);
647+
let input_channel = if input.is_silent() {
648+
input.channel_data(0)
649+
} else {
650+
input.channel_data(channel_number)
651+
};
653652
// retrieve state from previous block
654-
let mut x1 = self.x1[channel_number];
655-
let mut x2 = self.x2[channel_number];
656-
let mut y1 = self.y1[channel_number];
657-
let mut y2 = self.y2[channel_number];
653+
let (mut x1, mut x2, mut y1, mut y2) = match self.xy[channel_number] {
654+
[x1, x2, y1, y2] => (x1, x2, y1, y2),
655+
};
658656

659657
output_channel
660658
.iter_mut()
@@ -676,10 +674,7 @@ impl AudioProcessor for BiquadFilterRenderer {
676674
});
677675

678676
// store channel state for next block
679-
self.x1[channel_number] = x1;
680-
self.x2[channel_number] = x2;
681-
self.y1[channel_number] = y1;
682-
self.y2[channel_number] = y2;
677+
self.xy[channel_number] = [x1, x2, y1, y2];
683678
}
684679

685680
true

src/node/iir_filter.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! The IIR filter control and renderer parts
2+
use arrayvec::ArrayVec;
23
use num_complex::Complex;
4+
35
use std::f64::consts::PI;
46

57
use crate::context::{AudioContextRegistration, BaseAudioContext};
@@ -254,7 +256,7 @@ struct IirFilterRenderer {
254256
/// Normalized filter's coeffs -- `(b[n], a[n])`
255257
norm_coeffs: Vec<(f64, f64)>,
256258
/// filter's states
257-
states: Vec<Vec<f64>>,
259+
states: ArrayVec<Vec<f64>, MAX_CHANNELS>,
258260
}
259261

260262
impl IirFilterRenderer {
@@ -292,7 +294,11 @@ impl IirFilterRenderer {
292294
});
293295

294296
let coeffs_len = norm_coeffs.len();
295-
let states = vec![Vec::<f64>::with_capacity(MAX_CHANNELS); coeffs_len];
297+
298+
// eagerly assume stereo input, will adjust during rendering if needed
299+
let mut states = ArrayVec::new();
300+
states.push(vec![0.; coeffs_len]);
301+
states.push(vec![0.; coeffs_len]);
296302

297303
Self {
298304
norm_coeffs,
@@ -340,35 +346,38 @@ impl AudioProcessor for IirFilterRenderer {
340346
// see https://webaudio.github.io/web-audio-api/#channels-tail-time
341347
let num_channels = input.number_of_channels();
342348

343-
if num_channels != self.states[0].len() {
344-
self.states
345-
.iter_mut()
346-
.for_each(|state| state.resize(num_channels, 0.));
349+
if num_channels != self.states.len() {
350+
self.states.truncate(num_channels);
351+
for _ in self.states.len()..num_channels {
352+
self.states.push(vec![0.; self.norm_coeffs.len()]);
353+
}
347354
}
348355

349356
output.set_number_of_channels(num_channels);
350357
} else {
351-
let num_channels = self.states[0].len();
358+
let num_channels = self.states.len();
352359
output.set_number_of_channels(num_channels);
353360
}
354361

355362
// apply filter
356-
for (channel_number, (input_channel, output_channel)) in input
357-
.channels()
358-
.iter()
359-
.zip(output.channels_mut())
360-
.enumerate()
361-
{
363+
for (channel_number, output_channel) in output.channels_mut().iter_mut().enumerate() {
364+
let input_channel = if input.is_silent() {
365+
input.channel_data(0)
366+
} else {
367+
input.channel_data(channel_number)
368+
};
369+
let channel_state = &mut self.states[channel_number];
370+
362371
for (&i, o) in input_channel.iter().zip(output_channel.iter_mut()) {
363372
let input = f64::from(i);
364373
let b0 = self.norm_coeffs[0].0;
365-
let last_state = self.states[0][channel_number];
374+
let last_state = channel_state[0];
366375
let output = b0.mul_add(input, last_state);
367376

368377
// update states for next call
369378
for (i, (b, a)) in self.norm_coeffs.iter().skip(1).enumerate() {
370-
let state = self.states[i + 1][channel_number];
371-
self.states[i][channel_number] = b * input - a * output + state;
379+
let state = channel_state[i + 1];
380+
channel_state[i] = b * input - a * output + state;
372381
}
373382

374383
#[cfg(debug_assertions)]

src/node/panner.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,6 @@ mod tests {
11421142
let panner = PannerNode::new(&context, options);
11431143
assert_eq!(panner.panning_model(), PanningModelType::EqualPower);
11441144
panner.position_y().set_value(1.); // sound comes from above
1145-
panner.set_channel_count(1);
11461145

11471146
src.connect(&panner);
11481147
panner.connect(&context.destination());
@@ -1181,8 +1180,11 @@ mod tests {
11811180
listener.up_y().set_value(0.);
11821181
listener.up_z().set_value(1.);
11831182

1184-
// 128 input samples of value 1.
1185-
let input = AudioBuffer::from(vec![vec![1.; RENDER_QUANTUM_SIZE]], sample_rate);
1183+
// 128 input samples of value 1, stereo
1184+
let input = AudioBuffer::from(
1185+
vec![vec![1.; RENDER_QUANTUM_SIZE], vec![1.; RENDER_QUANTUM_SIZE]],
1186+
sample_rate,
1187+
);
11861188
let mut src = AudioBufferSourceNode::new(&context, AudioBufferSourceOptions::default());
11871189
src.set_buffer(input);
11881190
src.start();

src/render/graph.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -417,14 +417,6 @@ impl Graph {
417417
// acquire a mutable borrow of the current processing node
418418
let mut node = nodes[*index].borrow_mut();
419419

420-
// make sure all input buffers have the correct number of channels, this might not be
421-
// the case if the node has no inputs connected or the channel count has just changed
422-
let interpretation = node.channel_config.interpretation();
423-
let count = node.channel_config.count();
424-
node.inputs
425-
.iter_mut()
426-
.for_each(|i| i.mix(count, interpretation));
427-
428420
// let the current node process (catch any panics that may occur)
429421
let params = AudioParamValues::from(nodes);
430422
scope.node_id.set(*index);

tests/mixing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ fn test_stereo_to_discrete_stereo() {
9393

9494
assert_eq!(output.number_of_channels(), 2);
9595
assert_float_eq!(output.get_channel_data(0), ONES, abs_all <= 0.);
96-
assert_float_eq!(output.get_channel_data(1), ONES, abs_all <= 0.);
96+
assert_float_eq!(output.get_channel_data(1), ZEROES, abs_all <= 0.);
9797
}
9898

9999
#[test]

0 commit comments

Comments
 (0)