diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/media/webaudio/test/blink/biquad-filters.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/webaudio/test/blink/biquad-filters.js')
-rw-r--r-- | dom/media/webaudio/test/blink/biquad-filters.js | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/dom/media/webaudio/test/blink/biquad-filters.js b/dom/media/webaudio/test/blink/biquad-filters.js new file mode 100644 index 0000000000..06fff98b18 --- /dev/null +++ b/dom/media/webaudio/test/blink/biquad-filters.js @@ -0,0 +1,368 @@ +// Taken from WebKit/LayoutTests/webaudio/resources/biquad-filters.js + +// A biquad filter has a z-transform of +// H(z) = (b0 + b1 / z + b2 / z^2) / (1 + a1 / z + a2 / z^2) +// +// The formulas for the various filters were taken from +// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt. + + +// Lowpass filter. +function createLowpassFilter(freq, q, gain) { + var b0; + var b1; + var b2; + var a0; + var a1; + var a2; + + if (freq == 1) { + // The formula below works, except for roundoff. When freq = 1, + // the filter is just a wire, so hardwire the coefficients. + b0 = 1; + b1 = 0; + b2 = 0; + a0 = 1; + a1 = 0; + a2 = 0; + } else { + var w0 = Math.PI * freq; + var alpha = 0.5 * Math.sin(w0) / Math.pow(10, q / 20); + var cos_w0 = Math.cos(w0); + + b0 = 0.5 * (1 - cos_w0); + b1 = 1 - cos_w0; + b2 = b0; + a0 = 1 + alpha; + a1 = -2.0 * cos_w0; + a2 = 1 - alpha; + } + + return normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2); +} + +function createHighpassFilter(freq, q, gain) { + var b0; + var b1; + var b2; + var a1; + var a2; + + if (freq == 1) { + // The filter is 0 + b0 = 0; + b1 = 0; + b2 = 0; + a0 = 1; + a1 = 0; + a2 = 0; + } else if (freq == 0) { + // The filter is 1. Computation of coefficients below is ok, but + // there's a pole at 1 and a zero at 1, so round-off could make + // the filter unstable. + b0 = 1; + b1 = 0; + b2 = 0; + a0 = 1; + a1 = 0; + a2 = 0; + } else { + var w0 = Math.PI * freq; + var alpha = 0.5 * Math.sin(w0) / Math.pow(10, q / 20); + var cos_w0 = Math.cos(w0); + + b0 = 0.5 * (1 + cos_w0); + b1 = -1 - cos_w0; + b2 = b0; + a0 = 1 + alpha; + a1 = -2.0 * cos_w0; + a2 = 1 - alpha; + } + + return normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2); +} + +function normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2) { + var scale = 1 / a0; + + return {b0 : b0 * scale, + b1 : b1 * scale, + b2 : b2 * scale, + a1 : a1 * scale, + a2 : a2 * scale}; +} + +function createBandpassFilter(freq, q, gain) { + var b0; + var b1; + var b2; + var a0; + var a1; + var a2; + var coef; + + if (freq > 0 && freq < 1) { + var w0 = Math.PI * freq; + if (q > 0) { + var alpha = Math.sin(w0) / (2 * q); + var k = Math.cos(w0); + + b0 = alpha; + b1 = 0; + b2 = -alpha; + a0 = 1 + alpha; + a1 = -2 * k; + a2 = 1 - alpha; + + coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2); + } else { + // q = 0, and frequency is not 0 or 1. The above formula has a + // divide by zero problem. The limit of the z-transform as q + // approaches 0 is 1, so set the filter that way. + coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } + } else { + // When freq = 0 or 1, the z-transform is identically 0, + // independent of q. + coef = {b0 : 0, b1 : 0, b2 : 0, a1 : 0, a2 : 0} + } + + return coef; +} + +function createLowShelfFilter(freq, q, gain) { + // q not used + var b0; + var b1; + var b2; + var a0; + var a1; + var a2; + var coef; + + var S = 1; + var A = Math.pow(10, gain / 40); + + if (freq == 1) { + // The filter is just a constant gain + coef = {b0 : A * A, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } else if (freq == 0) { + // The filter is 1 + coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } else { + var w0 = Math.PI * freq; + var alpha = 1 / 2 * Math.sin(w0) * Math.sqrt((A + 1 / A) * (1 / S - 1) + 2); + var k = Math.cos(w0); + var k2 = 2 * Math.sqrt(A) * alpha; + var Ap1 = A + 1; + var Am1 = A - 1; + + b0 = A * (Ap1 - Am1 * k + k2); + b1 = 2 * A * (Am1 - Ap1 * k); + b2 = A * (Ap1 - Am1 * k - k2); + a0 = Ap1 + Am1 * k + k2; + a1 = -2 * (Am1 + Ap1 * k); + a2 = Ap1 + Am1 * k - k2; + coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2); + } + + return coef; +} + +function createHighShelfFilter(freq, q, gain) { + // q not used + var b0; + var b1; + var b2; + var a0; + var a1; + var a2; + var coef; + + var A = Math.pow(10, gain / 40); + + if (freq == 1) { + // When freq = 1, the z-transform is 1 + coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } else if (freq > 0) { + var w0 = Math.PI * freq; + var S = 1; + var alpha = 0.5 * Math.sin(w0) * Math.sqrt((A + 1 / A) * (1 / S - 1) + 2); + var k = Math.cos(w0); + var k2 = 2 * Math.sqrt(A) * alpha; + var Ap1 = A + 1; + var Am1 = A - 1; + + b0 = A * (Ap1 + Am1 * k + k2); + b1 = -2 * A * (Am1 + Ap1 * k); + b2 = A * (Ap1 + Am1 * k - k2); + a0 = Ap1 - Am1 * k + k2; + a1 = 2 * (Am1 - Ap1*k); + a2 = Ap1 - Am1 * k-k2; + + coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2); + } else { + // When freq = 0, the filter is just a gain + coef = {b0 : A * A, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } + + return coef; +} + +function createPeakingFilter(freq, q, gain) { + var b0; + var b1; + var b2; + var a0; + var a1; + var a2; + var coef; + + var A = Math.pow(10, gain / 40); + + if (freq > 0 && freq < 1) { + if (q > 0) { + var w0 = Math.PI * freq; + var alpha = Math.sin(w0) / (2 * q); + var k = Math.cos(w0); + + b0 = 1 + alpha * A; + b1 = -2 * k; + b2 = 1 - alpha * A; + a0 = 1 + alpha / A; + a1 = -2 * k; + a2 = 1 - alpha / A; + + coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2); + } else { + // q = 0, we have a divide by zero problem in the formulas + // above. But if we look at the z-transform, we see that the + // limit as q approaches 0 is A^2. + coef = {b0 : A * A, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } + } else { + // freq = 0 or 1, the z-transform is 1 + coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } + + return coef; +} + +function createNotchFilter(freq, q, gain) { + var b0; + var b1; + var b2; + var a0; + var a1; + var a2; + var coef; + + if (freq > 0 && freq < 1) { + if (q > 0) { + var w0 = Math.PI * freq; + var alpha = Math.sin(w0) / (2 * q); + var k = Math.cos(w0); + + b0 = 1; + b1 = -2 * k; + b2 = 1; + a0 = 1 + alpha; + a1 = -2 * k; + a2 = 1 - alpha; + coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2); + } else { + // When q = 0, we get a divide by zero above. The limit of the + // z-transform as q approaches 0 is 0, so set the coefficients + // appropriately. + coef = {b0 : 0, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } + } else { + // When freq = 0 or 1, the z-transform is 1 + coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } + + return coef; +} + +function createAllpassFilter(freq, q, gain) { + var b0; + var b1; + var b2; + var a0; + var a1; + var a2; + var coef; + + if (freq > 0 && freq < 1) { + if (q > 0) { + var w0 = Math.PI * freq; + var alpha = Math.sin(w0) / (2 * q); + var k = Math.cos(w0); + + b0 = 1 - alpha; + b1 = -2 * k; + b2 = 1 + alpha; + a0 = 1 + alpha; + a1 = -2 * k; + a2 = 1 - alpha; + coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2); + } else { + // q = 0 + coef = {b0 : -1, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } + } else { + coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; + } + + return coef; +} + +function filterData(filterCoef, signal, len) { + var y = new Array(len); + var b0 = filterCoef.b0; + var b1 = filterCoef.b1; + var b2 = filterCoef.b2; + var a1 = filterCoef.a1; + var a2 = filterCoef.a2; + + // Prime the pump. (Assumes the signal has length >= 2!) + y[0] = b0 * signal[0]; + y[1] = b0 * signal[1] + b1 * signal[0] - a1 * y[0]; + + // Filter all of the signal that we have. + for (var k = 2; k < Math.min(signal.length, len); ++k) { + y[k] = b0 * signal[k] + b1 * signal[k-1] + b2 * signal[k-2] - a1 * y[k-1] - a2 * y[k-2]; + } + + // If we need to filter more, but don't have any signal left, + // assume the signal is zero. + for (var k = signal.length; k < len; ++k) { + y[k] = - a1 * y[k-1] - a2 * y[k-2]; + } + + return y; +} + +// Map the filter type name to a function that computes the filter coefficents for the given filter +// type. +var filterCreatorFunction = {"lowpass": createLowpassFilter, + "highpass": createHighpassFilter, + "bandpass": createBandpassFilter, + "lowshelf": createLowShelfFilter, + "highshelf": createHighShelfFilter, + "peaking": createPeakingFilter, + "notch": createNotchFilter, + "allpass": createAllpassFilter}; + +var filterTypeName = {"lowpass": "Lowpass filter", + "highpass": "Highpass filter", + "bandpass": "Bandpass filter", + "lowshelf": "Lowshelf filter", + "highshelf": "Highshelf filter", + "peaking": "Peaking filter", + "notch": "Notch filter", + "allpass": "Allpass filter"}; + +function createFilter(filterType, freq, q, gain) { + return filterCreatorFunction[filterType](freq, q, gain); +} |