Skip to content

Commit 3cc5c38

Browse files
committed
fix: IIRFilter getFrequencyResponse return NAN for invalid frequencies
1 parent 2b9866d commit 3cc5c38

File tree

2 files changed

+44
-16
lines changed

2 files changed

+44
-16
lines changed

src/node/biquad_filter.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,8 +1209,7 @@ mod tests {
12091209
}
12101210

12111211
#[test]
1212-
#[allow(clippy::excessive_precision)]
1213-
fn test_frequency_responses_invalid_frequencies() {
1212+
fn test_frequency_response_invalid_frequencies() {
12141213
let context = OfflineAudioContext::new(1, 128, 44_100.);
12151214

12161215
let frequency = 2000.;

src/node/iir_filter.rs

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -233,24 +233,34 @@ impl IIRFilterNode {
233233
let nquist = sample_rate / 2.;
234234

235235
for (i, &f) in frequency_hz.iter().enumerate() {
236-
let freq = f64::from(f).clamp(0., nquist);
237-
let z = -2.0 * PI * freq / sample_rate;
238-
let mut num: Complex<f64> = Complex::new(0., 0.);
239-
let mut denom: Complex<f64> = Complex::new(0., 0.);
236+
let freq = f64::from(f);
237+
// <https://webaudio.github.io/web-audio-api/#dom-iirfilternode-getfrequencyresponse>
238+
// > If a value in the frequencyHz parameter is not within [0, sampleRate/2],
239+
// > where sampleRate is the value of the sampleRate property of the AudioContext,
240+
// > the corresponding value at the same index of the magResponse/phaseResponse
241+
// > array MUST be NaN.
242+
if freq < 0. || freq > nquist {
243+
mag_response[i] = f32::NAN;
244+
phase_response[i] = f32::NAN;
245+
} else {
246+
let z = -2.0 * PI * freq / sample_rate;
247+
let mut num: Complex<f64> = Complex::new(0., 0.);
248+
let mut denom: Complex<f64> = Complex::new(0., 0.);
240249

241-
for (idx, &b) in self.feedforward.iter().enumerate() {
242-
num += Complex::from_polar(b, idx as f64 * z);
243-
}
250+
for (idx, &b) in self.feedforward.iter().enumerate() {
251+
num += Complex::from_polar(b, idx as f64 * z);
252+
}
244253

245-
for (idx, &a) in self.feedback.iter().enumerate() {
246-
denom += Complex::from_polar(a, idx as f64 * z);
247-
}
254+
for (idx, &a) in self.feedback.iter().enumerate() {
255+
denom += Complex::from_polar(a, idx as f64 * z);
256+
}
248257

249-
let response = num / denom;
258+
let response = num / denom;
250259

251-
let (mag, phase) = response.to_polar();
252-
mag_response[i] = mag as f32;
253-
phase_response[i] = phase as f32;
260+
let (mag, phase) = response.to_polar();
261+
mag_response[i] = mag as f32;
262+
phase_response[i] = phase as f32;
263+
}
254264
}
255265
}
256266
}
@@ -897,4 +907,23 @@ mod tests {
897907
let feedforward = vec![b0, b1, b2];
898908
compare_frequency_response(BiquadFilterType::Highshelf, feedback, feedforward);
899909
}
910+
911+
#[test]
912+
fn test_frequency_response_invalid_frequencies() {
913+
let context = OfflineAudioContext::new(2, 555, 44_100.);
914+
let options = IIRFilterOptions {
915+
feedback: vec![1.; 10],
916+
feedforward: vec![1.; 10],
917+
audio_node_options: AudioNodeOptions::default(),
918+
};
919+
let iir = IIRFilterNode::new(&context, options);
920+
921+
let frequency_hz = [-1., 22_051.];
922+
let mut mags = [0.; 2];
923+
let mut phases = [0.; 2];
924+
925+
iir.get_frequency_response(&frequency_hz, &mut mags, &mut phases);
926+
mags.iter().for_each(|v| assert!(v.is_nan()));
927+
phases.iter().for_each(|v| assert!(v.is_nan()));
928+
}
900929
}

0 commit comments

Comments
 (0)