Skip to content

Commit 9a365d4

Browse files
committed
Ensure k-rate with connected inputs deliver a constant value per quantum
1 parent 9a4f295 commit 9a365d4

File tree

1 file changed

+64
-13
lines changed

1 file changed

+64
-13
lines changed

src/param.rs

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -740,30 +740,46 @@ impl AudioParamProcessor {
740740
#[cfg(test)]
741741
assert!(self.buffer.len() == 1 || self.buffer.len() == RENDER_QUANTUM_SIZE);
742742

743-
if self.buffer.len() == 1 && input.is_silent() {
743+
// handle all k-rate and inactive a-rate processing
744+
if self.buffer.len() == 1 || !self.automation_rate.is_a_rate() {
744745
let mut value = self.buffer[0];
745746

746-
if value.is_nan() {
747-
value = self.default_value;
748-
}
747+
// we can return a 1-sized buffer if either
748+
// - the input signal is zero, or
749+
// - if this is k-rate processing
750+
if input.is_silent() || !self.automation_rate.is_a_rate() {
751+
output.set_single_valued(true);
752+
753+
value += input.channel_data(0)[0];
754+
755+
if value.is_nan() {
756+
value = self.default_value;
757+
}
758+
759+
output.channel_data_mut(0)[0] = value.clamp(self.min_value, self.max_value);
760+
} else {
761+
// a-rate processing and input non-zero
762+
output.set_single_valued(false);
763+
*output = input.clone();
764+
output.channel_data_mut(0).iter_mut().for_each(|o| {
765+
*o += value;
749766

750-
output.set_single_valued(true);
767+
if o.is_nan() {
768+
*o = self.default_value;
769+
}
751770

752-
let output_channel = output.channel_data_mut(0);
753-
output_channel[0] = value.clamp(self.min_value, self.max_value);
771+
*o = o.clamp(self.min_value, self.max_value)
772+
});
773+
}
754774
} else {
755-
// @note: we could add two other optimizations here:
756-
// - when buffer.len() == 1 and buffer[0] == 0., then we don't need to
757-
// zip and add, but we still need to clamp
758-
// - when input.is_silent(), then we can copy_from_slice the buffer into
759-
// output and then just clamp
775+
// a-rate processing
760776
*output = input.clone();
761777
output.set_single_valued(false);
762778

763779
output
764780
.channel_data_mut(0)
765781
.iter_mut()
766-
.zip(self.buffer.iter().cycle())
782+
.zip(self.buffer.iter())
767783
.for_each(|(o, p)| {
768784
*o += p;
769785

@@ -3433,6 +3449,41 @@ mod tests {
34333449
}
34343450
}
34353451

3452+
#[test]
3453+
fn test_k_rate_makes_input_single_valued() {
3454+
let alloc = Alloc::with_capacity(1);
3455+
let context = OfflineAudioContext::new(1, 0, 48000.);
3456+
3457+
let opts = AudioParamDescriptor {
3458+
name: String::new(),
3459+
automation_rate: AutomationRate::K,
3460+
default_value: 0.,
3461+
min_value: 0.,
3462+
max_value: 10.,
3463+
};
3464+
let (_param, mut render) = audio_param_pair(opts, context.mock_registration());
3465+
3466+
// no event in timeline, buffer length is 1
3467+
let vs = render.compute_intrinsic_values(0., 1., 128);
3468+
assert_float_eq!(vs, &[0.; 1][..], abs_all <= 0.);
3469+
3470+
// mix to output step, input is not silence
3471+
let signal = alloc.silence();
3472+
let mut input = AudioRenderQuantum::from(signal);
3473+
input.channel_data_mut(0)[0] = 1.;
3474+
input.channel_data_mut(0)[1] = 2.;
3475+
input.channel_data_mut(0)[2] = 3.;
3476+
3477+
let signal = alloc.silence();
3478+
let mut output = AudioRenderQuantum::from(signal);
3479+
3480+
render.mix_to_output(&input, &mut output);
3481+
3482+
// expect only 1, not the other values
3483+
assert!(output.single_valued());
3484+
assert_float_eq!(output.channel_data(0)[0], 1., abs <= 0.);
3485+
}
3486+
34363487
#[test]
34373488
fn test_full_render_chain() {
34383489
let alloc = Alloc::with_capacity(1);

0 commit comments

Comments
 (0)