mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-03 22:07:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
BIN
dom/media/webaudio/test/audio-expected.wav
Normal file
BIN
dom/media/webaudio/test/audio-expected.wav
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/audio-mono-expected-2.wav
Normal file
BIN
dom/media/webaudio/test/audio-mono-expected-2.wav
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/audio-mono-expected.wav
Normal file
BIN
dom/media/webaudio/test/audio-mono-expected.wav
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/audio-quad.wav
Normal file
BIN
dom/media/webaudio/test/audio-quad.wav
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/audio.ogv
Normal file
BIN
dom/media/webaudio/test/audio.ogv
Normal file
Binary file not shown.
|
|
@ -0,0 +1,3 @@
|
|||
onmessage = function (event) {
|
||||
postMessage("Pong");
|
||||
};
|
||||
BIN
dom/media/webaudio/test/audiovideo.mp4
Normal file
BIN
dom/media/webaudio/test/audiovideo.mp4
Normal file
Binary file not shown.
9
dom/media/webaudio/test/blink/README
Normal file
9
dom/media/webaudio/test/blink/README
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
This directory contains tests originally borrowed from the Blink Web Audio test
|
||||
suite.
|
||||
|
||||
The process of borrowing tests from Blink is as follows:
|
||||
|
||||
* Import the pristine file from the Blink repo, noting the revision in the
|
||||
commit message.
|
||||
* Modify the test files to turn the LayoutTest into a mochitest-plain and add
|
||||
* them to the test suite in a separate commit.
|
||||
192
dom/media/webaudio/test/blink/audio-testing.js
Normal file
192
dom/media/webaudio/test/blink/audio-testing.js
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
if (window.testRunner)
|
||||
testRunner.overridePreference("WebKitWebAudioEnabled", "1");
|
||||
|
||||
function writeString(s, a, offset) {
|
||||
for (var i = 0; i < s.length; ++i) {
|
||||
a[offset + i] = s.charCodeAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
function writeInt16(n, a, offset) {
|
||||
n = Math.floor(n);
|
||||
|
||||
var b1 = n & 255;
|
||||
var b2 = (n >> 8) & 255;
|
||||
|
||||
a[offset + 0] = b1;
|
||||
a[offset + 1] = b2;
|
||||
}
|
||||
|
||||
function writeInt32(n, a, offset) {
|
||||
n = Math.floor(n);
|
||||
var b1 = n & 255;
|
||||
var b2 = (n >> 8) & 255;
|
||||
var b3 = (n >> 16) & 255;
|
||||
var b4 = (n >> 24) & 255;
|
||||
|
||||
a[offset + 0] = b1;
|
||||
a[offset + 1] = b2;
|
||||
a[offset + 2] = b3;
|
||||
a[offset + 3] = b4;
|
||||
}
|
||||
|
||||
function writeAudioBuffer(audioBuffer, a, offset) {
|
||||
var n = audioBuffer.length;
|
||||
var channels = audioBuffer.numberOfChannels;
|
||||
|
||||
for (var i = 0; i < n; ++i) {
|
||||
for (var k = 0; k < channels; ++k) {
|
||||
var buffer = audioBuffer.getChannelData(k);
|
||||
var sample = buffer[i] * 32768.0;
|
||||
|
||||
// Clip samples to the limitations of 16-bit.
|
||||
// If we don't do this then we'll get nasty wrap-around distortion.
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
if (sample > 32767)
|
||||
sample = 32767;
|
||||
|
||||
writeInt16(sample, a, offset);
|
||||
offset += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createWaveFileData(audioBuffer) {
|
||||
var frameLength = audioBuffer.length;
|
||||
var numberOfChannels = audioBuffer.numberOfChannels;
|
||||
var sampleRate = audioBuffer.sampleRate;
|
||||
var bitsPerSample = 16;
|
||||
var byteRate = sampleRate * numberOfChannels * bitsPerSample/8;
|
||||
var blockAlign = numberOfChannels * bitsPerSample/8;
|
||||
var wavDataByteLength = frameLength * numberOfChannels * 2; // 16-bit audio
|
||||
var headerByteLength = 44;
|
||||
var totalLength = headerByteLength + wavDataByteLength;
|
||||
|
||||
var waveFileData = new Uint8Array(totalLength);
|
||||
|
||||
var subChunk1Size = 16; // for linear PCM
|
||||
var subChunk2Size = wavDataByteLength;
|
||||
var chunkSize = 4 + (8 + subChunk1Size) + (8 + subChunk2Size);
|
||||
|
||||
writeString("RIFF", waveFileData, 0);
|
||||
writeInt32(chunkSize, waveFileData, 4);
|
||||
writeString("WAVE", waveFileData, 8);
|
||||
writeString("fmt ", waveFileData, 12);
|
||||
|
||||
writeInt32(subChunk1Size, waveFileData, 16); // SubChunk1Size (4)
|
||||
writeInt16(1, waveFileData, 20); // AudioFormat (2)
|
||||
writeInt16(numberOfChannels, waveFileData, 22); // NumChannels (2)
|
||||
writeInt32(sampleRate, waveFileData, 24); // SampleRate (4)
|
||||
writeInt32(byteRate, waveFileData, 28); // ByteRate (4)
|
||||
writeInt16(blockAlign, waveFileData, 32); // BlockAlign (2)
|
||||
writeInt32(bitsPerSample, waveFileData, 34); // BitsPerSample (4)
|
||||
|
||||
writeString("data", waveFileData, 36);
|
||||
writeInt32(subChunk2Size, waveFileData, 40); // SubChunk2Size (4)
|
||||
|
||||
// Write actual audio data starting at offset 44.
|
||||
writeAudioBuffer(audioBuffer, waveFileData, 44);
|
||||
|
||||
return waveFileData;
|
||||
}
|
||||
|
||||
function createAudioData(audioBuffer) {
|
||||
return createWaveFileData(audioBuffer);
|
||||
}
|
||||
|
||||
function finishAudioTest(event) {
|
||||
var audioData = createAudioData(event.renderedBuffer);
|
||||
testRunner.setAudioData(audioData);
|
||||
testRunner.notifyDone();
|
||||
}
|
||||
|
||||
// Create an impulse in a buffer of length sampleFrameLength
|
||||
function createImpulseBuffer(context, sampleFrameLength) {
|
||||
var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
|
||||
var n = audioBuffer.length;
|
||||
var dataL = audioBuffer.getChannelData(0);
|
||||
|
||||
for (var k = 0; k < n; ++k) {
|
||||
dataL[k] = 0;
|
||||
}
|
||||
dataL[0] = 1;
|
||||
|
||||
return audioBuffer;
|
||||
}
|
||||
|
||||
// Create a buffer of the given length with a linear ramp having values 0 <= x < 1.
|
||||
function createLinearRampBuffer(context, sampleFrameLength) {
|
||||
var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
|
||||
var n = audioBuffer.length;
|
||||
var dataL = audioBuffer.getChannelData(0);
|
||||
|
||||
for (var i = 0; i < n; ++i)
|
||||
dataL[i] = i / n;
|
||||
|
||||
return audioBuffer;
|
||||
}
|
||||
|
||||
// Create a buffer of the given length having a constant value.
|
||||
function createConstantBuffer(context, sampleFrameLength, constantValue) {
|
||||
var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
|
||||
var n = audioBuffer.length;
|
||||
var dataL = audioBuffer.getChannelData(0);
|
||||
|
||||
for (var i = 0; i < n; ++i)
|
||||
dataL[i] = constantValue;
|
||||
|
||||
return audioBuffer;
|
||||
}
|
||||
|
||||
// Create a stereo impulse in a buffer of length sampleFrameLength
|
||||
function createStereoImpulseBuffer(context, sampleFrameLength) {
|
||||
var audioBuffer = context.createBuffer(2, sampleFrameLength, context.sampleRate);
|
||||
var n = audioBuffer.length;
|
||||
var dataL = audioBuffer.getChannelData(0);
|
||||
var dataR = audioBuffer.getChannelData(1);
|
||||
|
||||
for (var k = 0; k < n; ++k) {
|
||||
dataL[k] = 0;
|
||||
dataR[k] = 0;
|
||||
}
|
||||
dataL[0] = 1;
|
||||
dataR[0] = 1;
|
||||
|
||||
return audioBuffer;
|
||||
}
|
||||
|
||||
// Convert time (in seconds) to sample frames.
|
||||
function timeToSampleFrame(time, sampleRate) {
|
||||
return Math.floor(0.5 + time * sampleRate);
|
||||
}
|
||||
|
||||
// Compute the number of sample frames consumed by start with
|
||||
// the specified |grainOffset|, |duration|, and |sampleRate|.
|
||||
function grainLengthInSampleFrames(grainOffset, duration, sampleRate) {
|
||||
var startFrame = timeToSampleFrame(grainOffset, sampleRate);
|
||||
var endFrame = timeToSampleFrame(grainOffset + duration, sampleRate);
|
||||
|
||||
return endFrame - startFrame;
|
||||
}
|
||||
|
||||
// True if the number is not an infinity or NaN
|
||||
function isValidNumber(x) {
|
||||
return !isNaN(x) && (x != Infinity) && (x != -Infinity);
|
||||
}
|
||||
|
||||
function shouldThrowTypeError(func, text) {
|
||||
var ok = false;
|
||||
try {
|
||||
func();
|
||||
} catch (e) {
|
||||
if (e instanceof TypeError) {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
testPassed(text + " threw TypeError.");
|
||||
} else {
|
||||
testFailed(text + " should throw TypeError.");
|
||||
}
|
||||
}
|
||||
368
dom/media/webaudio/test/blink/biquad-filters.js
Normal file
368
dom/media/webaudio/test/blink/biquad-filters.js
Normal file
|
|
@ -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);
|
||||
}
|
||||
153
dom/media/webaudio/test/blink/biquad-testing.js
Normal file
153
dom/media/webaudio/test/blink/biquad-testing.js
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
// Globals, to make testing and debugging easier.
|
||||
var context;
|
||||
var filter;
|
||||
var signal;
|
||||
var renderedBuffer;
|
||||
var renderedData;
|
||||
|
||||
var sampleRate = 44100.0;
|
||||
var pulseLengthFrames = .1 * sampleRate;
|
||||
|
||||
// Maximum allowed error for the test to succeed. Experimentally determined.
|
||||
var maxAllowedError = 5.9e-8;
|
||||
|
||||
// This must be large enough so that the filtered result is
|
||||
// essentially zero. See comments for createTestAndRun.
|
||||
var timeStep = .1;
|
||||
|
||||
// Maximum number of filters we can process (mostly for setting the
|
||||
// render length correctly.)
|
||||
var maxFilters = 5;
|
||||
|
||||
// How long to render. Must be long enough for all of the filters we
|
||||
// want to test.
|
||||
var renderLengthSeconds = timeStep * (maxFilters + 1) ;
|
||||
|
||||
var renderLengthSamples = Math.round(renderLengthSeconds * sampleRate);
|
||||
|
||||
// Number of filters that will be processed.
|
||||
var nFilters;
|
||||
|
||||
function createImpulseBuffer(context, length) {
|
||||
var impulse = context.createBuffer(1, length, context.sampleRate);
|
||||
var data = impulse.getChannelData(0);
|
||||
for (var k = 1; k < data.length; ++k) {
|
||||
data[k] = 0;
|
||||
}
|
||||
data[0] = 1;
|
||||
|
||||
return impulse;
|
||||
}
|
||||
|
||||
|
||||
function createTestAndRun(context, filterType, filterParameters) {
|
||||
// To test the filters, we apply a signal (an impulse) to each of
|
||||
// the specified filters, with each signal starting at a different
|
||||
// time. The output of the filters is summed together at the
|
||||
// output. Thus for filter k, the signal input to the filter
|
||||
// starts at time k * timeStep. For this to work well, timeStep
|
||||
// must be large enough for the output of each filter to have
|
||||
// decayed to zero with timeStep seconds. That way the filter
|
||||
// outputs don't interfere with each other.
|
||||
|
||||
nFilters = Math.min(filterParameters.length, maxFilters);
|
||||
|
||||
signal = new Array(nFilters);
|
||||
filter = new Array(nFilters);
|
||||
|
||||
impulse = createImpulseBuffer(context, pulseLengthFrames);
|
||||
|
||||
// Create all of the signal sources and filters that we need.
|
||||
for (var k = 0; k < nFilters; ++k) {
|
||||
signal[k] = context.createBufferSource();
|
||||
signal[k].buffer = impulse;
|
||||
|
||||
filter[k] = context.createBiquadFilter();
|
||||
filter[k].type = filterType;
|
||||
filter[k].frequency.value = context.sampleRate / 2 * filterParameters[k].cutoff;
|
||||
filter[k].detune.value = (filterParameters[k].detune === undefined) ? 0 : filterParameters[k].detune;
|
||||
filter[k].Q.value = filterParameters[k].q;
|
||||
filter[k].gain.value = filterParameters[k].gain;
|
||||
|
||||
signal[k].connect(filter[k]);
|
||||
filter[k].connect(context.destination);
|
||||
|
||||
signal[k].start(timeStep * k);
|
||||
}
|
||||
|
||||
context.oncomplete = checkFilterResponse(filterType, filterParameters);
|
||||
context.startRendering();
|
||||
}
|
||||
|
||||
function addSignal(dest, src, destOffset) {
|
||||
// Add src to dest at the given dest offset.
|
||||
for (var k = destOffset, j = 0; k < dest.length, j < src.length; ++k, ++j) {
|
||||
dest[k] += src[j];
|
||||
}
|
||||
}
|
||||
|
||||
function generateReference(filterType, filterParameters) {
|
||||
var result = new Array(renderLengthSamples);
|
||||
var data = new Array(renderLengthSamples);
|
||||
// Initialize the result array and data.
|
||||
for (var k = 0; k < result.length; ++k) {
|
||||
result[k] = 0;
|
||||
data[k] = 0;
|
||||
}
|
||||
// Make data an impulse.
|
||||
data[0] = 1;
|
||||
|
||||
for (var k = 0; k < nFilters; ++k) {
|
||||
// Filter an impulse
|
||||
var detune = (filterParameters[k].detune === undefined) ? 0 : filterParameters[k].detune;
|
||||
var frequency = filterParameters[k].cutoff * Math.pow(2, detune / 1200); // Apply detune, converting from Cents.
|
||||
|
||||
var filterCoef = createFilter(filterType,
|
||||
frequency,
|
||||
filterParameters[k].q,
|
||||
filterParameters[k].gain);
|
||||
var y = filterData(filterCoef, data, renderLengthSamples);
|
||||
|
||||
// Accumulate this filtered data into the final output at the desired offset.
|
||||
addSignal(result, y, timeToSampleFrame(timeStep * k, sampleRate));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function checkFilterResponse(filterType, filterParameters) {
|
||||
return function(event) {
|
||||
renderedBuffer = event.renderedBuffer;
|
||||
renderedData = renderedBuffer.getChannelData(0);
|
||||
|
||||
reference = generateReference(filterType, filterParameters);
|
||||
|
||||
var len = Math.min(renderedData.length, reference.length);
|
||||
|
||||
var success = true;
|
||||
|
||||
// Maximum error between rendered data and expected data
|
||||
var maxError = 0;
|
||||
|
||||
// Sample offset where the maximum error occurred.
|
||||
var maxPosition = 0;
|
||||
|
||||
// Number of infinities or NaNs that occurred in the rendered data.
|
||||
var invalidNumberCount = 0;
|
||||
|
||||
ok(nFilters == filterParameters.length, "Test wanted " + filterParameters.length + " filters but only " + maxFilters + " allowed.");
|
||||
|
||||
compareChannels(renderedData, reference, len, 0, 0, true);
|
||||
|
||||
// Check for bad numbers in the rendered output too.
|
||||
// There shouldn't be any.
|
||||
for (var k = 0; k < len; ++k) {
|
||||
if (!isValidNumber(renderedData[k])) {
|
||||
++invalidNumberCount;
|
||||
}
|
||||
}
|
||||
|
||||
ok(invalidNumberCount == 0, "Rendered output has " + invalidNumberCount + " infinities or NaNs.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
182
dom/media/webaudio/test/blink/convolution-testing.js
Normal file
182
dom/media/webaudio/test/blink/convolution-testing.js
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
var sampleRate = 44100.0;
|
||||
|
||||
var renderLengthSeconds = 8;
|
||||
var pulseLengthSeconds = 1;
|
||||
var pulseLengthFrames = pulseLengthSeconds * sampleRate;
|
||||
|
||||
function createSquarePulseBuffer(context, sampleFrameLength) {
|
||||
var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
|
||||
|
||||
var n = audioBuffer.length;
|
||||
var data = audioBuffer.getChannelData(0);
|
||||
|
||||
for (var i = 0; i < n; ++i)
|
||||
data[i] = 1;
|
||||
|
||||
return audioBuffer;
|
||||
}
|
||||
|
||||
// The triangle buffer holds the expected result of the convolution.
|
||||
// It linearly ramps up from 0 to its maximum value (at the center)
|
||||
// then linearly ramps down to 0. The center value corresponds to the
|
||||
// point where the two square pulses overlap the most.
|
||||
function createTrianglePulseBuffer(context, sampleFrameLength) {
|
||||
var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
|
||||
|
||||
var n = audioBuffer.length;
|
||||
var halfLength = n / 2;
|
||||
var data = audioBuffer.getChannelData(0);
|
||||
|
||||
for (var i = 0; i < halfLength; ++i)
|
||||
data[i] = i + 1;
|
||||
|
||||
for (var i = halfLength; i < n; ++i)
|
||||
data[i] = n - i - 1;
|
||||
|
||||
return audioBuffer;
|
||||
}
|
||||
|
||||
function log10(x) {
|
||||
return Math.log(x)/Math.LN10;
|
||||
}
|
||||
|
||||
function linearToDecibel(x) {
|
||||
return 20*log10(x);
|
||||
}
|
||||
|
||||
// Verify that the rendered result is very close to the reference
|
||||
// triangular pulse.
|
||||
function checkTriangularPulse(rendered, reference) {
|
||||
var match = true;
|
||||
var maxDelta = 0;
|
||||
var valueAtMaxDelta = 0;
|
||||
var maxDeltaIndex = 0;
|
||||
|
||||
for (var i = 0; i < reference.length; ++i) {
|
||||
var diff = rendered[i] - reference[i];
|
||||
var x = Math.abs(diff);
|
||||
if (x > maxDelta) {
|
||||
maxDelta = x;
|
||||
valueAtMaxDelta = reference[i];
|
||||
maxDeltaIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// allowedDeviationFraction was determined experimentally. It
|
||||
// is the threshold of the relative error at the maximum
|
||||
// difference between the true triangular pulse and the
|
||||
// rendered pulse.
|
||||
var allowedDeviationDecibels = -129.4;
|
||||
var maxDeviationDecibels = linearToDecibel(maxDelta / valueAtMaxDelta);
|
||||
|
||||
if (maxDeviationDecibels <= allowedDeviationDecibels) {
|
||||
testPassed("Triangular portion of convolution is correct.");
|
||||
} else {
|
||||
testFailed("Triangular portion of convolution is not correct. Max deviation = " + maxDeviationDecibels + " dB at " + maxDeltaIndex);
|
||||
match = false;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
// Verify that the rendered data is close to zero for the first part
|
||||
// of the tail.
|
||||
function checkTail1(data, reference, breakpoint) {
|
||||
var isZero = true;
|
||||
var tail1Max = 0;
|
||||
|
||||
for (var i = reference.length; i < reference.length + breakpoint; ++i) {
|
||||
var mag = Math.abs(data[i]);
|
||||
if (mag > tail1Max) {
|
||||
tail1Max = mag;
|
||||
}
|
||||
}
|
||||
|
||||
// Let's find the peak of the reference (even though we know a
|
||||
// priori what it is).
|
||||
var refMax = 0;
|
||||
for (var i = 0; i < reference.length; ++i) {
|
||||
refMax = Math.max(refMax, Math.abs(reference[i]));
|
||||
}
|
||||
|
||||
// This threshold is experimentally determined by examining the
|
||||
// value of tail1MaxDecibels.
|
||||
var threshold1 = -129.7;
|
||||
|
||||
var tail1MaxDecibels = linearToDecibel(tail1Max/refMax);
|
||||
if (tail1MaxDecibels <= threshold1) {
|
||||
testPassed("First part of tail of convolution is sufficiently small.");
|
||||
} else {
|
||||
testFailed("First part of tail of convolution is not sufficiently small: " + tail1MaxDecibels + " dB");
|
||||
isZero = false;
|
||||
}
|
||||
|
||||
return isZero;
|
||||
}
|
||||
|
||||
// Verify that the second part of the tail of the convolution is
|
||||
// exactly zero.
|
||||
function checkTail2(data, reference, breakpoint) {
|
||||
var isZero = true;
|
||||
var tail2Max = 0;
|
||||
// For the second part of the tail, the maximum value should be
|
||||
// exactly zero.
|
||||
var threshold2 = 0;
|
||||
for (var i = reference.length + breakpoint; i < data.length; ++i) {
|
||||
if (Math.abs(data[i]) > 0) {
|
||||
isZero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isZero) {
|
||||
testPassed("Rendered signal after tail of convolution is silent.");
|
||||
} else {
|
||||
testFailed("Rendered signal after tail of convolution should be silent.");
|
||||
}
|
||||
|
||||
return isZero;
|
||||
}
|
||||
|
||||
function checkConvolvedResult(trianglePulse) {
|
||||
return function(event) {
|
||||
var renderedBuffer = event.renderedBuffer;
|
||||
|
||||
var referenceData = trianglePulse.getChannelData(0);
|
||||
var renderedData = renderedBuffer.getChannelData(0);
|
||||
|
||||
var success = true;
|
||||
|
||||
// Verify the triangular pulse is actually triangular.
|
||||
|
||||
success = success && checkTriangularPulse(renderedData, referenceData);
|
||||
|
||||
// Make sure that portion after convolved portion is totally
|
||||
// silent. But round-off prevents this from being completely
|
||||
// true. At the end of the triangle, it should be close to
|
||||
// zero. If we go farther out, it should be even closer and
|
||||
// eventually zero.
|
||||
|
||||
// For the tail of the convolution (where the result would be
|
||||
// theoretically zero), we partition the tail into two
|
||||
// parts. The first is the at the beginning of the tail,
|
||||
// where we tolerate a small but non-zero value. The second part is
|
||||
// farther along the tail where the result should be zero.
|
||||
|
||||
// breakpoint is the point dividing the first two tail parts
|
||||
// we're looking at. Experimentally determined.
|
||||
var breakpoint = 12800;
|
||||
|
||||
success = success && checkTail1(renderedData, referenceData, breakpoint);
|
||||
|
||||
success = success && checkTail2(renderedData, referenceData, breakpoint);
|
||||
|
||||
if (success) {
|
||||
testPassed("Test signal was correctly convolved.");
|
||||
} else {
|
||||
testFailed("Test signal was not correctly convolved.");
|
||||
}
|
||||
|
||||
finishJSTest();
|
||||
}
|
||||
}
|
||||
23
dom/media/webaudio/test/blink/mochitest.ini
Normal file
23
dom/media/webaudio/test/blink/mochitest.ini
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
[DEFAULT]
|
||||
tags=msg
|
||||
tags = webaudio
|
||||
subsuite = media
|
||||
support-files =
|
||||
biquad-filters.js
|
||||
biquad-testing.js
|
||||
../webaudio.js
|
||||
|
||||
[test_biquadFilterNodeAllPass.html]
|
||||
[test_biquadFilterNodeAutomation.html]
|
||||
skip-if = true # Known problems with Biquad automation, e.g. Bug 1155709
|
||||
[test_biquadFilterNodeBandPass.html]
|
||||
[test_biquadFilterNodeGetFrequencyResponse.html]
|
||||
[test_biquadFilterNodeHighPass.html]
|
||||
[test_biquadFilterNodeHighShelf.html]
|
||||
[test_biquadFilterNodeLowPass.html]
|
||||
[test_biquadFilterNodeLowShelf.html]
|
||||
[test_biquadFilterNodeNotch.html]
|
||||
[test_biquadFilterNodePeaking.html]
|
||||
[test_biquadFilterNodeTail.html]
|
||||
[test_iirFilterNode.html]
|
||||
[test_iirFilterNodeGetFrequencyResponse.html]
|
||||
210
dom/media/webaudio/test/blink/panner-model-testing.js
Normal file
210
dom/media/webaudio/test/blink/panner-model-testing.js
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
var sampleRate = 48000.0;
|
||||
|
||||
var numberOfChannels = 1;
|
||||
|
||||
// Time step when each panner node starts.
|
||||
var timeStep = 0.001;
|
||||
|
||||
// Length of the impulse signal.
|
||||
var pulseLengthFrames = Math.round(timeStep * sampleRate);
|
||||
|
||||
// How many panner nodes to create for the test
|
||||
var nodesToCreate = 100;
|
||||
|
||||
// Be sure we render long enough for all of our nodes.
|
||||
var renderLengthSeconds = timeStep * (nodesToCreate + 1);
|
||||
|
||||
// These are global mostly for debugging.
|
||||
var context;
|
||||
var impulse;
|
||||
var bufferSource;
|
||||
var panner;
|
||||
var position;
|
||||
var time;
|
||||
|
||||
var renderedBuffer;
|
||||
var renderedLeft;
|
||||
var renderedRight;
|
||||
|
||||
function createGraph(context, nodeCount) {
|
||||
bufferSource = new Array(nodeCount);
|
||||
panner = new Array(nodeCount);
|
||||
position = new Array(nodeCount);
|
||||
time = new Array(nodeCount);
|
||||
// Angle between panner locations. (nodeCount - 1 because we want
|
||||
// to include both 0 and 180 deg.
|
||||
var angleStep = Math.PI / (nodeCount - 1);
|
||||
|
||||
if (numberOfChannels == 2) {
|
||||
impulse = createStereoImpulseBuffer(context, pulseLengthFrames);
|
||||
}
|
||||
else
|
||||
impulse = createImpulseBuffer(context, pulseLengthFrames);
|
||||
|
||||
for (var k = 0; k < nodeCount; ++k) {
|
||||
bufferSource[k] = context.createBufferSource();
|
||||
bufferSource[k].buffer = impulse;
|
||||
|
||||
panner[k] = context.createPanner();
|
||||
panner[k].panningModel = "equalpower";
|
||||
panner[k].distanceModel = "linear";
|
||||
|
||||
var angle = angleStep * k;
|
||||
position[k] = {angle : angle, x : Math.cos(angle), z : Math.sin(angle)};
|
||||
panner[k].positionX.value = position[k].x;
|
||||
panner[k].positionZ.value = position[k].z;
|
||||
|
||||
bufferSource[k].connect(panner[k]);
|
||||
panner[k].connect(context.destination);
|
||||
|
||||
// Start the source
|
||||
time[k] = k * timeStep;
|
||||
bufferSource[k].start(time[k]);
|
||||
}
|
||||
}
|
||||
|
||||
function createTestAndRun(context, nodeCount, numberOfSourceChannels) {
|
||||
numberOfChannels = numberOfSourceChannels;
|
||||
|
||||
createGraph(context, nodeCount);
|
||||
|
||||
context.oncomplete = checkResult;
|
||||
context.startRendering();
|
||||
}
|
||||
|
||||
// Map our position angle to the azimuth angle (in degrees).
|
||||
//
|
||||
// An angle of 0 corresponds to an azimuth of 90 deg; pi, to -90 deg.
|
||||
function angleToAzimuth(angle) {
|
||||
return 90 - angle * 180 / Math.PI;
|
||||
}
|
||||
|
||||
// The gain caused by the EQUALPOWER panning model
|
||||
function equalPowerGain(angle) {
|
||||
var azimuth = angleToAzimuth(angle);
|
||||
|
||||
if (numberOfChannels == 1) {
|
||||
var panPosition = (azimuth + 90) / 180;
|
||||
|
||||
var gainL = Math.cos(0.5 * Math.PI * panPosition);
|
||||
var gainR = Math.sin(0.5 * Math.PI * panPosition);
|
||||
|
||||
return { left : gainL, right : gainR };
|
||||
} else {
|
||||
if (azimuth <= 0) {
|
||||
var panPosition = (azimuth + 90) / 90;
|
||||
|
||||
var gainL = 1 + Math.cos(0.5 * Math.PI * panPosition);
|
||||
var gainR = Math.sin(0.5 * Math.PI * panPosition);
|
||||
|
||||
return { left : gainL, right : gainR };
|
||||
} else {
|
||||
var panPosition = azimuth / 90;
|
||||
|
||||
var gainL = Math.cos(0.5 * Math.PI * panPosition);
|
||||
var gainR = 1 + Math.sin(0.5 * Math.PI * panPosition);
|
||||
|
||||
return { left : gainL, right : gainR };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkResult(event) {
|
||||
renderedBuffer = event.renderedBuffer;
|
||||
renderedLeft = renderedBuffer.getChannelData(0);
|
||||
renderedRight = renderedBuffer.getChannelData(1);
|
||||
|
||||
// The max error we allow between the rendered impulse and the
|
||||
// expected value. This value is experimentally determined. Set
|
||||
// to 0 to make the test fail to see what the actual error is.
|
||||
var maxAllowedError = 1.3e-6;
|
||||
|
||||
var success = true;
|
||||
|
||||
// Number of impulses found in the rendered result.
|
||||
var impulseCount = 0;
|
||||
|
||||
// Max (relative) error and the index of the maxima for the left
|
||||
// and right channels.
|
||||
var maxErrorL = 0;
|
||||
var maxErrorIndexL = 0;
|
||||
var maxErrorR = 0;
|
||||
var maxErrorIndexR = 0;
|
||||
|
||||
// Number of impulses that don't match our expected locations.
|
||||
var timeCount = 0;
|
||||
|
||||
// Locations of where the impulses aren't at the expected locations.
|
||||
var timeErrors = new Array();
|
||||
|
||||
for (var k = 0; k < renderedLeft.length; ++k) {
|
||||
// We assume that the left and right channels start at the same instant.
|
||||
if (renderedLeft[k] != 0 || renderedRight[k] != 0) {
|
||||
// The expected gain for the left and right channels.
|
||||
var pannerGain = equalPowerGain(position[impulseCount].angle);
|
||||
var expectedL = pannerGain.left;
|
||||
var expectedR = pannerGain.right;
|
||||
|
||||
// Absolute error in the gain.
|
||||
var errorL = Math.abs(renderedLeft[k] - expectedL);
|
||||
var errorR = Math.abs(renderedRight[k] - expectedR);
|
||||
|
||||
if (Math.abs(errorL) > maxErrorL) {
|
||||
maxErrorL = Math.abs(errorL);
|
||||
maxErrorIndexL = impulseCount;
|
||||
}
|
||||
if (Math.abs(errorR) > maxErrorR) {
|
||||
maxErrorR = Math.abs(errorR);
|
||||
maxErrorIndexR = impulseCount;
|
||||
}
|
||||
|
||||
// Keep track of the impulses that didn't show up where we
|
||||
// expected them to be.
|
||||
var expectedOffset = timeToSampleFrame(time[impulseCount], sampleRate);
|
||||
if (k != expectedOffset) {
|
||||
timeErrors[timeCount] = { actual : k, expected : expectedOffset};
|
||||
++timeCount;
|
||||
}
|
||||
++impulseCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (impulseCount == nodesToCreate) {
|
||||
testPassed("Number of impulses matches the number of panner nodes.");
|
||||
} else {
|
||||
testFailed("Number of impulses is incorrect. (Found " + impulseCount + " but expected " + nodesToCreate + ")");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (timeErrors.length > 0) {
|
||||
success = false;
|
||||
testFailed(timeErrors.length + " timing errors found in " + nodesToCreate + " panner nodes.");
|
||||
for (var k = 0; k < timeErrors.length; ++k) {
|
||||
testFailed("Impulse at sample " + timeErrors[k].actual + " but expected " + timeErrors[k].expected);
|
||||
}
|
||||
} else {
|
||||
testPassed("All impulses at expected offsets.");
|
||||
}
|
||||
|
||||
if (maxErrorL <= maxAllowedError) {
|
||||
testPassed("Left channel gain values are correct.");
|
||||
} else {
|
||||
testFailed("Left channel gain values are incorrect. Max error = " + maxErrorL + " at time " + time[maxErrorIndexL] + " (threshold = " + maxAllowedError + ")");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (maxErrorR <= maxAllowedError) {
|
||||
testPassed("Right channel gain values are correct.");
|
||||
} else {
|
||||
testFailed("Right channel gain values are incorrect. Max error = " + maxErrorR + " at time " + time[maxErrorIndexR] + " (threshold = " + maxAllowedError + ")");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
testPassed("EqualPower panner test passed");
|
||||
} else {
|
||||
testFailed("EqualPower panner test failed");
|
||||
}
|
||||
|
||||
finishJSTest();
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode All Pass Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// Create offline audio context.
|
||||
var context = new OfflineAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
|
||||
|
||||
var filterParameters = [{cutoff : 0, q : 10, gain : 1 },
|
||||
{cutoff : 1, q : 10, gain : 1 },
|
||||
{cutoff : .5, q : 0, gain : 1 },
|
||||
{cutoff : 0.25, q : 10, gain : 1 },
|
||||
];
|
||||
createTestAndRun(context, "allpass", filterParameters);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode All Pass Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// Don't need to run these tests at high sampling rate, so just use a low one to reduce memory
|
||||
// usage and complexity.
|
||||
var sampleRate = 16000;
|
||||
|
||||
// How long to render for each test.
|
||||
var renderDuration = 1;
|
||||
|
||||
// The definition of the linear ramp automation function.
|
||||
function linearRamp(t, v0, v1, t0, t1) {
|
||||
return v0 + (v1 - v0) * (t - t0) / (t1 - t0);
|
||||
}
|
||||
|
||||
// Generate the filter coefficients for the specified filter using the given parameters for
|
||||
// the given duration. |filterTypeFunction| is a function that returns the filter
|
||||
// coefficients for one set of parameters. |parameters| is a property bag that contains the
|
||||
// start and end values (as an array) for each of the biquad attributes. The properties are
|
||||
// |freq|, |Q|, |gain|, and |detune|. |duration| is the number of seconds for which the
|
||||
// coefficients are generated.
|
||||
//
|
||||
// A property bag with properties |b0|, |b1|, |b2|, |a1|, |a2|. Each propery is an array
|
||||
// consisting of the coefficients for the time-varying biquad filter.
|
||||
function generateFilterCoefficients(filterTypeFunction, parameters, duration) {
|
||||
var endFrame = Math.ceil(duration * sampleRate);
|
||||
var nCoef = endFrame;
|
||||
var b0 = new Float64Array(nCoef);
|
||||
var b1 = new Float64Array(nCoef);
|
||||
var b2 = new Float64Array(nCoef);
|
||||
var a1 = new Float64Array(nCoef);
|
||||
var a2 = new Float64Array(nCoef);
|
||||
|
||||
var k = 0;
|
||||
// If the property is not given, use the defaults.
|
||||
var freqs = parameters.freq || [350, 350];
|
||||
var qs = parameters.Q || [1, 1];
|
||||
var gains = parameters.gain || [0, 0];
|
||||
var detunes = parameters.detune || [0, 0];
|
||||
|
||||
for (var frame = 0; frame < endFrame; ++frame) {
|
||||
// Apply linear ramp at frame |frame|.
|
||||
var f = linearRamp(frame / sampleRate, freqs[0], freqs[1], 0, duration);
|
||||
var q = linearRamp(frame / sampleRate, qs[0], qs[1], 0, duration);
|
||||
var g = linearRamp(frame / sampleRate, gains[0], gains[1], 0, duration);
|
||||
var d = linearRamp(frame / sampleRate, detunes[0], detunes[1], 0, duration);
|
||||
|
||||
// Compute actual frequency parameter
|
||||
f = f * Math.pow(2, d / 1200);
|
||||
|
||||
// Compute filter coefficients
|
||||
var coef = filterTypeFunction(f / (sampleRate / 2), q, g);
|
||||
b0[k] = coef.b0;
|
||||
b1[k] = coef.b1;
|
||||
b2[k] = coef.b2;
|
||||
a1[k] = coef.a1;
|
||||
a2[k] = coef.a2;
|
||||
++k;
|
||||
}
|
||||
|
||||
return {b0: b0, b1: b1, b2: b2, a1: a1, a2: a2};
|
||||
}
|
||||
|
||||
// Apply the given time-varying biquad filter to the given signal, |signal|. |coef| should be
|
||||
// the time-varying coefficients of the filter, as returned by |generateFilterCoefficients|.
|
||||
function timeVaryingFilter(signal, coef) {
|
||||
var length = signal.length;
|
||||
// Use double precision for the internal computations.
|
||||
var y = new Float64Array(length);
|
||||
|
||||
// Prime the pump. (Assumes the signal has length >= 2!)
|
||||
y[0] = coef.b0[0] * signal[0];
|
||||
y[1] = coef.b0[1] * signal[1] + coef.b1[1] * signal[0] - coef.a1[1] * y[0];
|
||||
|
||||
for (var n = 2; n < length; ++n) {
|
||||
y[n] = coef.b0[n] * signal[n] + coef.b1[n] * signal[n-1] + coef.b2[n] * signal[n-2];
|
||||
y[n] -= coef.a1[n] * y[n-1] + coef.a2[n] * y[n-2];
|
||||
}
|
||||
|
||||
// But convert the result to single precision for comparison.
|
||||
return y.map(Math.fround);
|
||||
}
|
||||
|
||||
// Configure the audio graph using |context|. Returns the biquad filter node and the
|
||||
// AudioBuffer used for the source.
|
||||
function configureGraph(context, toneFrequency) {
|
||||
// The source is just a simple sine wave.
|
||||
var src = context.createBufferSource();
|
||||
var b = context.createBuffer(1, renderDuration * sampleRate, sampleRate);
|
||||
var data = b.getChannelData(0);
|
||||
var omega = 2 * Math.PI * toneFrequency / sampleRate;
|
||||
for (var k = 0; k < data.length; ++k) {
|
||||
data[k] = Math.sin(omega * k);
|
||||
}
|
||||
src.buffer = b;
|
||||
var f = context.createBiquadFilter();
|
||||
src.connect(f);
|
||||
f.connect(context.destination);
|
||||
|
||||
src.start();
|
||||
|
||||
return {filter: f, source: b};
|
||||
}
|
||||
|
||||
function createFilterVerifier(filterCreator, threshold, parameters, input, message) {
|
||||
return function (resultBuffer) {
|
||||
var actual = resultBuffer.getChannelData(0);
|
||||
var coefs = generateFilterCoefficients(filterCreator, parameters, renderDuration);
|
||||
|
||||
reference = timeVaryingFilter(input, coefs);
|
||||
|
||||
compareChannels(actual, reference);
|
||||
};
|
||||
}
|
||||
|
||||
var testPromises = [];
|
||||
|
||||
// Automate just the frequency parameter. A bandpass filter is used where the center
|
||||
// frequency is swept across the source (which is a simple tone).
|
||||
testPromises.push(function () {
|
||||
var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
|
||||
|
||||
// Center frequency of bandpass filter and also the frequency of the test tone.
|
||||
var centerFreq = 10*440;
|
||||
|
||||
// Sweep the frequency +/- 9*440 Hz from the center. This should cause the output to low at
|
||||
// the beginning and end of the test where the done is outside the pass band of the filter,
|
||||
// but high in the center where the tone is near the center of the pass band.
|
||||
var parameters = {
|
||||
freq: [centerFreq - 9*440, centerFreq + 9*440]
|
||||
}
|
||||
var graph = configureGraph(context, centerFreq);
|
||||
var f = graph.filter;
|
||||
var b = graph.source;
|
||||
|
||||
f.type = "bandpass";
|
||||
f.frequency.setValueAtTime(parameters.freq[0], 0);
|
||||
f.frequency.linearRampToValueAtTime(parameters.freq[1], renderDuration);
|
||||
|
||||
return context.startRendering()
|
||||
.then(createFilterVerifier(createBandpassFilter, 5e-5, parameters, b.getChannelData(0),
|
||||
"Output of bandpass filter with frequency automation"));
|
||||
}());
|
||||
|
||||
// Automate just the Q parameter. A bandpass filter is used where the Q of the filter is
|
||||
// swept.
|
||||
testPromises.push(function() {
|
||||
var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
|
||||
|
||||
// The frequency of the test tone.
|
||||
var centerFreq = 440;
|
||||
|
||||
// Sweep the Q paramter between 1 and 200. This will cause the output of the filter to pass
|
||||
// most of the tone at the beginning to passing less of the tone at the end. This is
|
||||
// because we set center frequency of the bandpass filter to be slightly off from the actual
|
||||
// tone.
|
||||
var parameters = {
|
||||
Q: [1, 200],
|
||||
// Center frequency of the bandpass filter is just 25 Hz above the tone frequency.
|
||||
freq: [centerFreq + 25, centerFreq + 25]
|
||||
};
|
||||
var graph = configureGraph(context, centerFreq);
|
||||
var f = graph.filter;
|
||||
var b = graph.source;
|
||||
|
||||
f.type = "bandpass";
|
||||
f.frequency.value = parameters.freq[0];
|
||||
f.Q.setValueAtTime(parameters.Q[0], 0);
|
||||
f.Q.linearRampToValueAtTime(parameters.Q[1], renderDuration);
|
||||
|
||||
return context.startRendering()
|
||||
.then(createFilterVerifier(createBandpassFilter, 1.4e-6, parameters, b.getChannelData(0),
|
||||
"Output of bandpass filter with Q automation"));
|
||||
}());
|
||||
|
||||
// Automate just the gain of the lowshelf filter. A test tone will be in the lowshelf part of
|
||||
// the filter. The output will vary as the gain of the lowshelf is changed.
|
||||
testPromises.push(function() {
|
||||
var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
|
||||
|
||||
// Frequency of the test tone.
|
||||
var centerFreq = 440;
|
||||
|
||||
// Set the cutoff frequency of the lowshelf to be significantly higher than the test tone.
|
||||
// Sweep the gain from 20 dB to -20 dB. (We go from 20 to -20 to easily verify that the
|
||||
// filter didn't go unstable.)
|
||||
var parameters = {
|
||||
freq: [3500, 3500],
|
||||
gain: [20, -20]
|
||||
}
|
||||
var graph = configureGraph(context, centerFreq);
|
||||
var f = graph.filter;
|
||||
var b = graph.source;
|
||||
|
||||
f.type = "lowshelf";
|
||||
f.frequency.value = parameters.freq[0];
|
||||
f.gain.setValueAtTime(parameters.gain[0], 0);
|
||||
f.gain.linearRampToValueAtTime(parameters.gain[1], renderDuration);
|
||||
|
||||
context.startRendering()
|
||||
.then(createFilterVerifier(createLowShelfFilter, 8e-6, parameters, b.getChannelData(0),
|
||||
"Output of lowshelf filter with gain automation"));
|
||||
}());
|
||||
|
||||
// Automate just the detune parameter. Basically the same test as for the frequncy parameter
|
||||
// but we just use the detune parameter to modulate the frequency parameter.
|
||||
testPromises.push(function() {
|
||||
var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
|
||||
var centerFreq = 10*440;
|
||||
var parameters = {
|
||||
freq: [centerFreq, centerFreq],
|
||||
detune: [-10*1200, 10*1200]
|
||||
};
|
||||
var graph = configureGraph(context, centerFreq);
|
||||
var f = graph.filter;
|
||||
var b = graph.source;
|
||||
|
||||
f.type = "bandpass";
|
||||
f.frequency.value = parameters.freq[0];
|
||||
f.detune.setValueAtTime(parameters.detune[0], 0);
|
||||
f.detune.linearRampToValueAtTime(parameters.detune[1], renderDuration);
|
||||
|
||||
context.startRendering()
|
||||
.then(createFilterVerifier(createBandpassFilter, 5e-6, parameters, b.getChannelData(0),
|
||||
"Output of bandpass filter with detune automation"));
|
||||
}());
|
||||
|
||||
// Automate all of the filter parameters at once. This is a basic check that everything is
|
||||
// working. A peaking filter is used because it uses all of the parameters.
|
||||
testPromises.push(function() {
|
||||
var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
|
||||
var graph = configureGraph(context, 10*440);
|
||||
var f = graph.filter;
|
||||
var b = graph.source;
|
||||
|
||||
// Sweep all of the filter parameters. These are pretty much arbitrary.
|
||||
var parameters = {
|
||||
freq: [10000, 100],
|
||||
Q: [f.Q.value, .0001],
|
||||
gain: [f.gain.value, 20],
|
||||
detune: [2400, -2400]
|
||||
};
|
||||
|
||||
f.type = "peaking";
|
||||
// Set starting points for all parameters of the filter. Start at 10 kHz for the center
|
||||
// frequency, and the defaults for Q and gain.
|
||||
f.frequency.setValueAtTime(parameters.freq[0], 0);
|
||||
f.Q.setValueAtTime(parameters.Q[0], 0);
|
||||
f.gain.setValueAtTime(parameters.gain[0], 0);
|
||||
f.detune.setValueAtTime(parameters.detune[0], 0);
|
||||
|
||||
// Linear ramp each parameter
|
||||
f.frequency.linearRampToValueAtTime(parameters.freq[1], renderDuration);
|
||||
f.Q.linearRampToValueAtTime(parameters.Q[1], renderDuration);
|
||||
f.gain.linearRampToValueAtTime(parameters.gain[1], renderDuration);
|
||||
f.detune.linearRampToValueAtTime(parameters.detune[1], renderDuration);
|
||||
|
||||
context.startRendering()
|
||||
.then(createFilterVerifier(createPeakingFilter, 3.3e-4, parameters, b.getChannelData(0),
|
||||
"Output of peaking filter with automation of all parameters"));
|
||||
}());
|
||||
|
||||
// Test that modulation of the frequency parameter of the filter works. A sinusoid of 440 Hz
|
||||
// is the test signal that is applied to a bandpass biquad filter. The frequency parameter of
|
||||
// the filter is modulated by a sinusoid at 103 Hz, and the frequency modulation varies from
|
||||
// 116 to 412 Hz. (This test was taken from the description in
|
||||
// https://github.com/WebAudio/web-audio-api/issues/509#issuecomment-94731355)
|
||||
testPromises.push(function() {
|
||||
var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
|
||||
|
||||
// Create a graph with the sinusoidal source at 440 Hz as the input to a biquad filter.
|
||||
var graph = configureGraph(context, 440);
|
||||
var f = graph.filter;
|
||||
var b = graph.source;
|
||||
|
||||
f.type = "bandpass";
|
||||
f.Q.value = 5;
|
||||
f.frequency.value = 264;
|
||||
|
||||
// Create the modulation source, a sinusoid with frequency 103 Hz and amplitude 148. (The
|
||||
// amplitude of 148 is added to the filter's frequency value of 264 to produce a sinusoidal
|
||||
// modulation of the frequency parameter from 116 to 412 Hz.)
|
||||
var mod = context.createBufferSource();
|
||||
var mbuffer = context.createBuffer(1, renderDuration * sampleRate, sampleRate);
|
||||
var d = mbuffer.getChannelData(0);
|
||||
var omega = 2 * Math.PI * 103 / sampleRate;
|
||||
for (var k = 0; k < d.length; ++k) {
|
||||
d[k] = 148 * Math.sin(omega * k);
|
||||
}
|
||||
mod.buffer = mbuffer;
|
||||
|
||||
mod.connect(f.frequency);
|
||||
|
||||
mod.start();
|
||||
return context.startRendering()
|
||||
.then(function (resultBuffer) {
|
||||
var actual = resultBuffer.getChannelData(0);
|
||||
// Compute the filter coefficients using the mod sine wave
|
||||
|
||||
var endFrame = Math.ceil(renderDuration * sampleRate);
|
||||
var nCoef = endFrame;
|
||||
var b0 = new Float64Array(nCoef);
|
||||
var b1 = new Float64Array(nCoef);
|
||||
var b2 = new Float64Array(nCoef);
|
||||
var a1 = new Float64Array(nCoef);
|
||||
var a2 = new Float64Array(nCoef);
|
||||
|
||||
// Generate the filter coefficients when the frequency varies from 116 to 248 Hz using
|
||||
// the 103 Hz sinusoid.
|
||||
for (var k = 0; k < nCoef; ++k) {
|
||||
var freq = f.frequency.value + d[k];
|
||||
var c = createBandpassFilter(freq / (sampleRate / 2), f.Q.value, f.gain.value);
|
||||
b0[k] = c.b0;
|
||||
b1[k] = c.b1;
|
||||
b2[k] = c.b2;
|
||||
a1[k] = c.a1;
|
||||
a2[k] = c.a2;
|
||||
}
|
||||
reference = timeVaryingFilter(b.getChannelData(0),
|
||||
{b0: b0, b1: b1, b2: b2, a1: a1, a2: a2});
|
||||
|
||||
compareChannels(actual, reference);
|
||||
});
|
||||
}());
|
||||
|
||||
// Wait for all tests
|
||||
Promise.all(testPromises).then(function () {
|
||||
SimpleTest.finish();
|
||||
}, function () {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode Band Pass Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// Create offline audio context.
|
||||
var context = new OfflineAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
|
||||
|
||||
// The filters we want to test.
|
||||
var filterParameters = [{cutoff : 0, q : 0, gain : 1 },
|
||||
{cutoff : 1, q : 0, gain : 1 },
|
||||
{cutoff : 0.5, q : 0, gain : 1 },
|
||||
{cutoff : 0.25, q : 1, gain : 1 },
|
||||
];
|
||||
|
||||
createTestAndRun(context, "bandpass", filterParameters);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode All Pass Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// Test the frequency response of a biquad filter. We compute the frequency response for a simple
|
||||
// peaking biquad filter and compare it with the expected frequency response. The actual filter
|
||||
// used doesn't matter since we're testing getFrequencyResponse and not the actual filter output.
|
||||
// The filters are extensively tested in other biquad tests.
|
||||
|
||||
var context;
|
||||
|
||||
// The biquad filter node.
|
||||
var filter;
|
||||
|
||||
// The magnitude response of the biquad filter.
|
||||
var magResponse;
|
||||
|
||||
// The phase response of the biquad filter.
|
||||
var phaseResponse;
|
||||
|
||||
// Number of frequency samples to take.
|
||||
var numberOfFrequencies = 1000;
|
||||
|
||||
// The filter parameters.
|
||||
var filterCutoff = 1000; // Hz.
|
||||
var filterQ = 1;
|
||||
var filterGain = 5; // Decibels.
|
||||
|
||||
// The maximum allowed error in the magnitude response.
|
||||
var maxAllowedMagError = 5.7e-7;
|
||||
|
||||
// The maximum allowed error in the phase response.
|
||||
var maxAllowedPhaseError = 4.7e-8;
|
||||
|
||||
// The magnitudes and phases of the reference frequency response.
|
||||
var magResponse;
|
||||
var phaseResponse;
|
||||
|
||||
// The magnitudes and phases of the reference frequency response.
|
||||
var expectedMagnitudes;
|
||||
var expectedPhases;
|
||||
|
||||
// Convert frequency in Hz to a normalized frequency between 0 to 1 with 1 corresponding to the
|
||||
// Nyquist frequency.
|
||||
function normalizedFrequency(freqHz, sampleRate)
|
||||
{
|
||||
var nyquist = sampleRate / 2;
|
||||
return freqHz / nyquist;
|
||||
}
|
||||
|
||||
// Get the filter response at a (normalized) frequency |f| for the filter with coefficients |coef|.
|
||||
function getResponseAt(coef, f)
|
||||
{
|
||||
var b0 = coef.b0;
|
||||
var b1 = coef.b1;
|
||||
var b2 = coef.b2;
|
||||
var a1 = coef.a1;
|
||||
var a2 = coef.a2;
|
||||
|
||||
// H(z) = (b0 + b1 / z + b2 / z^2) / (1 + a1 / z + a2 / z^2)
|
||||
//
|
||||
// Compute H(exp(i * pi * f)). No native complex numbers in javascript, so break H(exp(i * pi * // f))
|
||||
// in to the real and imaginary parts of the numerator and denominator. Let omega = pi * f.
|
||||
// Then the numerator is
|
||||
//
|
||||
// b0 + b1 * cos(omega) + b2 * cos(2 * omega) - i * (b1 * sin(omega) + b2 * sin(2 * omega))
|
||||
//
|
||||
// and the denominator is
|
||||
//
|
||||
// 1 + a1 * cos(omega) + a2 * cos(2 * omega) - i * (a1 * sin(omega) + a2 * sin(2 * omega))
|
||||
//
|
||||
// Compute the magnitude and phase from the real and imaginary parts.
|
||||
|
||||
var omega = Math.PI * f;
|
||||
var numeratorReal = b0 + b1 * Math.cos(omega) + b2 * Math.cos(2 * omega);
|
||||
var numeratorImag = -(b1 * Math.sin(omega) + b2 * Math.sin(2 * omega));
|
||||
var denominatorReal = 1 + a1 * Math.cos(omega) + a2 * Math.cos(2 * omega);
|
||||
var denominatorImag = -(a1 * Math.sin(omega) + a2 * Math.sin(2 * omega));
|
||||
|
||||
var magnitude = Math.sqrt((numeratorReal * numeratorReal + numeratorImag * numeratorImag)
|
||||
/ (denominatorReal * denominatorReal + denominatorImag * denominatorImag));
|
||||
var phase = Math.atan2(numeratorImag, numeratorReal) - Math.atan2(denominatorImag, denominatorReal);
|
||||
|
||||
if (phase >= Math.PI) {
|
||||
phase -= 2 * Math.PI;
|
||||
} else if (phase <= -Math.PI) {
|
||||
phase += 2 * Math.PI;
|
||||
}
|
||||
|
||||
return {magnitude : magnitude, phase : phase};
|
||||
}
|
||||
|
||||
// Compute the reference frequency response for the biquad filter |filter| at the frequency samples
|
||||
// given by |frequencies|.
|
||||
function frequencyResponseReference(filter, frequencies)
|
||||
{
|
||||
var sampleRate = filter.context.sampleRate;
|
||||
var normalizedFreq = normalizedFrequency(filter.frequency.value, sampleRate);
|
||||
var filterCoefficients = createFilter(filter.type, normalizedFreq, filter.Q.value, filter.gain.value);
|
||||
|
||||
var magnitudes = [];
|
||||
var phases = [];
|
||||
|
||||
for (var k = 0; k < frequencies.length; ++k) {
|
||||
var response = getResponseAt(filterCoefficients, normalizedFrequency(frequencies[k], sampleRate));
|
||||
magnitudes.push(response.magnitude);
|
||||
phases.push(response.phase);
|
||||
}
|
||||
|
||||
return {magnitudes : magnitudes, phases : phases};
|
||||
}
|
||||
|
||||
// Compute a set of linearly spaced frequencies.
|
||||
function createFrequencies(nFrequencies, sampleRate)
|
||||
{
|
||||
var frequencies = new Float32Array(nFrequencies);
|
||||
var nyquist = sampleRate / 2;
|
||||
var freqDelta = nyquist / nFrequencies;
|
||||
|
||||
for (var k = 0; k < nFrequencies; ++k) {
|
||||
frequencies[k] = k * freqDelta;
|
||||
}
|
||||
|
||||
return frequencies;
|
||||
}
|
||||
|
||||
function linearToDecibels(x)
|
||||
{
|
||||
if (x) {
|
||||
return 20 * Math.log(x) / Math.LN10;
|
||||
} else {
|
||||
return -1000;
|
||||
}
|
||||
}
|
||||
|
||||
// Look through the array and find any NaN or infinity. Returns the index of the first occurence or
|
||||
// -1 if none.
|
||||
function findBadNumber(signal)
|
||||
{
|
||||
for (var k = 0; k < signal.length; ++k) {
|
||||
if (!isValidNumber(signal[k])) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Compute absolute value of the difference between phase angles, taking into account the wrapping
|
||||
// of phases.
|
||||
function absolutePhaseDifference(x, y)
|
||||
{
|
||||
var diff = Math.abs(x - y);
|
||||
|
||||
if (diff > Math.PI) {
|
||||
diff = 2 * Math.PI - diff;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Compare the frequency response with our expected response.
|
||||
function compareResponses(filter, frequencies, magResponse, phaseResponse)
|
||||
{
|
||||
var expectedResponse = frequencyResponseReference(filter, frequencies);
|
||||
|
||||
expectedMagnitudes = expectedResponse.magnitudes;
|
||||
expectedPhases = expectedResponse.phases;
|
||||
|
||||
var n = magResponse.length;
|
||||
var success = true;
|
||||
var badResponse = false;
|
||||
|
||||
var maxMagError = -1;
|
||||
var maxMagErrorIndex = -1;
|
||||
|
||||
var k;
|
||||
var hasBadNumber;
|
||||
|
||||
hasBadNumber = findBadNumber(magResponse);
|
||||
ok (hasBadNumber < 0, "Magnitude response has NaN or infinity at " + hasBadNumber);
|
||||
|
||||
hasBadNumber = findBadNumber(phaseResponse);
|
||||
ok (hasBadNumber < 0, "Phase response has NaN or infinity at " + hasBadNumber);
|
||||
|
||||
// These aren't testing the implementation itself. Instead, these are sanity checks on the
|
||||
// reference. Failure here does not imply an error in the implementation.
|
||||
hasBadNumber = findBadNumber(expectedMagnitudes);
|
||||
ok (hasBadNumber < 0, "Expected magnitude response has NaN or infinity at " + hasBadNumber);
|
||||
|
||||
hasBadNumber = findBadNumber(expectedPhases);
|
||||
ok (hasBadNumber < 0, "Expected phase response has NaN or infinity at " + hasBadNumber);
|
||||
|
||||
for (k = 0; k < n; ++k) {
|
||||
var error = Math.abs(linearToDecibels(magResponse[k]) - linearToDecibels(expectedMagnitudes[k]));
|
||||
if (error > maxMagError) {
|
||||
maxMagError = error;
|
||||
maxMagErrorIndex = k;
|
||||
}
|
||||
}
|
||||
|
||||
var message = "Magnitude error (" + maxMagError + " dB)";
|
||||
message += " exceeded threshold at " + frequencies[maxMagErrorIndex];
|
||||
message += " Hz. Actual: " + linearToDecibels(magResponse[maxMagErrorIndex]);
|
||||
message += " dB, expected: " + linearToDecibels(expectedMagnitudes[maxMagErrorIndex]) + " dB.";
|
||||
ok(maxMagError < maxAllowedMagError, message);
|
||||
|
||||
var maxPhaseError = -1;
|
||||
var maxPhaseErrorIndex = -1;
|
||||
|
||||
for (k = 0; k < n; ++k) {
|
||||
var error = absolutePhaseDifference(phaseResponse[k], expectedPhases[k]);
|
||||
if (error > maxPhaseError) {
|
||||
maxPhaseError = error;
|
||||
maxPhaseErrorIndex = k;
|
||||
}
|
||||
}
|
||||
|
||||
message = "Phase error (radians) (" + maxPhaseError;
|
||||
message += ") exceeded threshold at " + frequencies[maxPhaseErrorIndex];
|
||||
message += " Hz. Actual: " + phaseResponse[maxPhaseErrorIndex];
|
||||
message += " expected: " + expectedPhases[maxPhaseErrorIndex];
|
||||
|
||||
ok(maxPhaseError < maxAllowedPhaseError, message);
|
||||
}
|
||||
|
||||
context = new AudioContext();
|
||||
|
||||
filter = context.createBiquadFilter();
|
||||
|
||||
// Arbitrarily test a peaking filter, but any kind of filter can be tested.
|
||||
filter.type = "peaking";
|
||||
filter.frequency.value = filterCutoff;
|
||||
filter.Q.value = filterQ;
|
||||
filter.gain.value = filterGain;
|
||||
|
||||
var frequencies = createFrequencies(numberOfFrequencies, context.sampleRate);
|
||||
magResponse = new Float32Array(numberOfFrequencies);
|
||||
phaseResponse = new Float32Array(numberOfFrequencies);
|
||||
|
||||
filter.getFrequencyResponse(frequencies, magResponse, phaseResponse);
|
||||
compareResponses(filter, frequencies, magResponse, phaseResponse);
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode High Pass Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// Create offline audio context.
|
||||
var context = new OfflineAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
|
||||
|
||||
// The filters we want to test.
|
||||
var filterParameters = [{cutoff : 0, q : 1, gain : 1 },
|
||||
{cutoff : 1, q : 1, gain : 1 },
|
||||
{cutoff : 0.25, q : 1, gain : 1 },
|
||||
];
|
||||
|
||||
createTestAndRun(context, "highpass", filterParameters);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode High Shelf Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// Create offline audio context.
|
||||
var context = new OfflineAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
|
||||
|
||||
// The filters we want to test.
|
||||
var filterParameters = [{cutoff : 0, q : 10, gain : 10 },
|
||||
{cutoff : 1, q : 10, gain : 10 },
|
||||
{cutoff : 0.25, q : 10, gain : 10 },
|
||||
];
|
||||
|
||||
createTestAndRun(context, "highshelf", filterParameters);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode Low Pass Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// Create offline audio context.
|
||||
var context = new OfflineAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
|
||||
|
||||
// The filters we want to test.
|
||||
var filterParameters = [{cutoff : 0, q : 1, gain : 1 },
|
||||
{cutoff : 1, q : 1, gain : 1 },
|
||||
{cutoff : 0.25, q : 1, gain : 1 },
|
||||
{cutoff : 0.25, q : 1, gain : 1, detune : 100 },
|
||||
{cutoff : 0.01, q : 1, gain : 1, detune : -200 },
|
||||
];
|
||||
createTestAndRun(context, "lowpass", filterParameters);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode Low Shelf Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
|
||||
// Create offline audio context.
|
||||
var context = new OfflineAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
|
||||
|
||||
// The filters we want to test.
|
||||
var filterParameters = [{cutoff : 0, q : 10, gain : 10 },
|
||||
{cutoff : 1, q : 10, gain : 10 },
|
||||
{cutoff : 0.25, q : 10, gain : 10 },
|
||||
];
|
||||
|
||||
createTestAndRun(context, "lowshelf", filterParameters);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode Notch Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// Create offline audio context.
|
||||
var context = new OfflineAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
|
||||
|
||||
var filterParameters = [{cutoff : 0, q : 10, gain : 1 },
|
||||
{cutoff : 1, q : 10, gain : 1 },
|
||||
{cutoff : .5, q : 0, gain : 1 },
|
||||
{cutoff : 0.25, q : 10, gain : 1 },
|
||||
];
|
||||
|
||||
createTestAndRun(context, "notch", filterParameters);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode Low Pass Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// Create offline audio context.
|
||||
var context = new OfflineAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
|
||||
|
||||
// The filters we want to test.
|
||||
var filterParameters = [{cutoff : 0, q : 10, gain : 10 },
|
||||
{cutoff : 1, q : 10, gain : 10 },
|
||||
{cutoff : .5, q : 0, gain : 10 },
|
||||
{cutoff : 0.25, q : 10, gain : 10 },
|
||||
];
|
||||
|
||||
createTestAndRun(context, "peaking", filterParameters);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
76
dom/media/webaudio/test/blink/test_biquadFilterNodeTail.html
Normal file
76
dom/media/webaudio/test/blink/test_biquadFilterNodeTail.html
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode All Pass Filter</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="audio-testing.js"></script>
|
||||
<script src="biquad-filters.js"></script>
|
||||
<script src="biquad-testing.js"></script>
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
// A high sample rate shows the issue more clearly.
|
||||
var sampleRate = 192000;
|
||||
// Some short duration because we don't need to run the test for very long.
|
||||
var testDurationSec = 0.5;
|
||||
var testDurationFrames = testDurationSec * sampleRate;
|
||||
|
||||
// Amplitude experimentally determined to give a biquad output close to 1. (No attempt was
|
||||
// made to produce exactly 1; it's not needed.)
|
||||
var sourceAmplitude = 100;
|
||||
|
||||
// The output of the biquad filter should not change by more than this much between output
|
||||
// samples. Threshold was determined experimentally.
|
||||
var glitchThreshold = 0.01292;
|
||||
|
||||
// Test that a Biquad filter doesn't have it's output terminated because the input has gone
|
||||
// away. Generally, when a source node is finished, it disconnects itself from any downstream
|
||||
// nodes. This is the correct behavior. Nodes that have no inputs (disconnected) are
|
||||
// generally assumed to output zeroes. This is also desired behavior. However, biquad
|
||||
// filters have memory so they should not suddenly output zeroes when the input is
|
||||
// disconnected. This test checks to see if the output doesn't suddenly change to zero.
|
||||
var context = new OfflineAudioContext(1, testDurationFrames, sampleRate);
|
||||
|
||||
// Create an impulse source.
|
||||
var buffer = context.createBuffer(1, 1, context.sampleRate);
|
||||
buffer.getChannelData(0)[0] = sourceAmplitude;
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
// Create the biquad filter. It doesn't really matter what kind, so the default filter type
|
||||
// and parameters is fine. Connect the source to it.
|
||||
var biquad = context.createBiquadFilter();
|
||||
source.connect(biquad);
|
||||
biquad.connect(context.destination);
|
||||
|
||||
source.start();
|
||||
|
||||
context.startRendering().then(function(result) {
|
||||
// There should be no large discontinuities in the output
|
||||
var buffer = result.getChannelData(0);
|
||||
var maxGlitchIndex = 0;
|
||||
var maxGlitchValue = 0.0;
|
||||
for (var i = 1; i < buffer.length; i++) {
|
||||
var diff = Math.abs(buffer[i-1] - buffer[i]);
|
||||
if (diff >= glitchThreshold) {
|
||||
if (diff > maxGlitchValue) {
|
||||
maxGlitchIndex = i;
|
||||
maxGlitchValue = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
ok(maxGlitchIndex == 0, 'glitches detected in biquad output: maximum glitch at ' + maxGlitchIndex + ' with diff of ' + maxGlitchValue);
|
||||
SimpleTest.finish();
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
467
dom/media/webaudio/test/blink/test_iirFilterNode.html
Normal file
467
dom/media/webaudio/test/blink/test_iirFilterNode.html
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test IIRFilterNode GetFrequencyResponse</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<script type="text/javascript" src="biquad-filters.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
var sampleRate = 48000;
|
||||
var testDurationSec = 1;
|
||||
var testFrames = testDurationSec * sampleRate;
|
||||
|
||||
var testPromises = []
|
||||
testPromises.push(function () {
|
||||
// Test that the feedback coefficients are normalized. Do this be creating two
|
||||
// IIRFilterNodes. One has normalized coefficients, and one doesn't. Compute the
|
||||
// difference and make sure they're the same.
|
||||
var context = new OfflineAudioContext(2, testFrames, sampleRate);
|
||||
|
||||
// Use a simple impulse as the source.
|
||||
var buffer = context.createBuffer(1, 1, sampleRate);
|
||||
buffer.getChannelData(0)[0] = 1;
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
// Gain node for computing the difference between the filters.
|
||||
var gain = context.createGain();
|
||||
gain.gain.value = -1;
|
||||
|
||||
// The IIR filters. Use a common feedforward array.
|
||||
var ff = [1];
|
||||
|
||||
var fb1 = [1, .9];
|
||||
|
||||
var fb2 = new Float64Array(2);
|
||||
// Scale the feedback coefficients by an arbitrary factor.
|
||||
var coefScaleFactor = 2;
|
||||
for (var k = 0; k < fb2.length; ++k) {
|
||||
fb2[k] = coefScaleFactor * fb1[k];
|
||||
}
|
||||
|
||||
var iir1 = context.createIIRFilter(ff, fb1);
|
||||
var iir2 = context.createIIRFilter(ff, fb2);
|
||||
|
||||
// Create the graph. The output of iir1 (normalized coefficients) is channel 0, and the
|
||||
// output of iir2 (unnormalized coefficients), with appropriate scaling, is channel 1.
|
||||
var merger = context.createChannelMerger(2);
|
||||
source.connect(iir1);
|
||||
source.connect(iir2);
|
||||
iir1.connect(merger, 0, 0);
|
||||
iir2.connect(gain);
|
||||
|
||||
// The gain for the gain node should be set to compensate for the scaling of the
|
||||
// coefficients. Since iir2 has scaled the coefficients by coefScaleFactor, the output is
|
||||
// reduced by the same factor, so adjust the gain to scale the output of iir2 back up.
|
||||
gain.gain.value = coefScaleFactor;
|
||||
gain.connect(merger, 0, 1);
|
||||
|
||||
merger.connect(context.destination);
|
||||
|
||||
source.start();
|
||||
|
||||
// Rock and roll!
|
||||
|
||||
return context.startRendering().then(function (result) {
|
||||
// Find the max amplitude of the result, which should be near zero.
|
||||
var iir1Data = result.getChannelData(0);
|
||||
var iir2Data = result.getChannelData(1);
|
||||
|
||||
// Threshold isn't exactly zero because the arithmetic is done differently between the
|
||||
// IIRFilterNode and the BiquadFilterNode.
|
||||
compareChannels(iir1Data, iir2Data);
|
||||
});
|
||||
}());
|
||||
|
||||
testPromises.push(function () {
|
||||
// Create a simple 1-zero filter and compare with the expected output.
|
||||
var context = new OfflineAudioContext(1, testFrames, sampleRate);
|
||||
|
||||
// Use a simple impulse as the source
|
||||
var buffer = context.createBuffer(1, 1, sampleRate);
|
||||
buffer.getChannelData(0)[0] = 1;
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
// The filter is y(n) = 0.5*(x(n) + x(n-1)), a simple 2-point moving average. This is
|
||||
// rather arbitrary; keep it simple.
|
||||
|
||||
var iir = context.createIIRFilter([0.5, 0.5], [1]);
|
||||
|
||||
// Create the graph
|
||||
source.connect(iir);
|
||||
iir.connect(context.destination);
|
||||
|
||||
// Rock and roll!
|
||||
source.start();
|
||||
|
||||
return context.startRendering().then(function (result) {
|
||||
var actual = result.getChannelData(0);
|
||||
var expected = new Float64Array(testFrames);
|
||||
// The filter is a simple 2-point moving average of an impulse, so the first two values
|
||||
// are non-zero and the rest are zero.
|
||||
expected[0] = 0.5;
|
||||
expected[1] = 0.5;
|
||||
compareChannels(actual, expected);
|
||||
});
|
||||
}());
|
||||
|
||||
testPromises.push(function () {
|
||||
// Create a simple 1-pole filter and compare with the expected output.
|
||||
|
||||
// The filter is y(n) + c*y(n-1)= x(n). The analytical response is (-c)^n, so choose a
|
||||
// suitable number of frames to run the test for where the output isn't flushed to zero.
|
||||
var c = 0.9;
|
||||
var eps = 1e-20;
|
||||
var duration = Math.floor(Math.log(eps) / Math.log(Math.abs(c)));
|
||||
var context = new OfflineAudioContext(1, duration, sampleRate);
|
||||
|
||||
// Use a simple impulse as the source
|
||||
var buffer = context.createBuffer(1, 1, sampleRate);
|
||||
buffer.getChannelData(0)[0] = 1;
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
var iir = context.createIIRFilter([1], [1, c]);
|
||||
|
||||
// Create the graph
|
||||
source.connect(iir);
|
||||
iir.connect(context.destination);
|
||||
|
||||
// Rock and roll!
|
||||
source.start();
|
||||
|
||||
return context.startRendering().then(function (result) {
|
||||
var actual = result.getChannelData(0);
|
||||
var expected = new Float64Array(actual.length);
|
||||
|
||||
// The filter is a simple 1-pole filter: y(n) = -c*y(n-k)+x(n), with an impulse as the
|
||||
// input.
|
||||
expected[0] = 1;
|
||||
for (k = 1; k < testFrames; ++k) {
|
||||
expected[k] = -c * expected[k-1];
|
||||
}
|
||||
|
||||
compareChannels(actual, expected);
|
||||
});
|
||||
}());
|
||||
|
||||
// This function creates an IIRFilterNode equivalent to the specified
|
||||
// BiquadFilterNode and compares the outputs. The
|
||||
// outputs from the two filters should be virtually identical.
|
||||
function testWithBiquadFilter(filterType) {
|
||||
var context = new OfflineAudioContext(2, testFrames, sampleRate);
|
||||
|
||||
// Use a constant (step function) as the source
|
||||
var buffer = context.createBuffer(1, testFrames, context.sampleRate);
|
||||
for (var i = 0; i < testFrames; ++i) {
|
||||
buffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
// Create the biquad. Choose some rather arbitrary values for Q and gain for the biquad
|
||||
// so that the shelf filters aren't identical.
|
||||
var biquad = context.createBiquadFilter();
|
||||
biquad.type = filterType;
|
||||
biquad.Q.value = 10;
|
||||
biquad.gain.value = 10;
|
||||
|
||||
// Create the equivalent IIR Filter node by computing the coefficients of the given biquad
|
||||
// filter type.
|
||||
var nyquist = sampleRate / 2;
|
||||
var coef = createFilter(filterType,
|
||||
biquad.frequency.value / nyquist,
|
||||
biquad.Q.value,
|
||||
biquad.gain.value);
|
||||
|
||||
var iir = context.createIIRFilter([coef.b0, coef.b1, coef.b2], [1, coef.a1, coef.a2]);
|
||||
|
||||
var merger = context.createChannelMerger(2);
|
||||
// Create the graph
|
||||
source.connect(biquad);
|
||||
source.connect(iir);
|
||||
|
||||
biquad.connect(merger, 0, 0);
|
||||
iir.connect(merger, 0, 1);
|
||||
|
||||
merger.connect(context.destination);
|
||||
|
||||
// Rock and roll!
|
||||
source.start();
|
||||
|
||||
return context.startRendering().then(function (result) {
|
||||
// Find the max amplitude of the result, which should be near zero.
|
||||
var expected = result.getChannelData(0);
|
||||
var actual = result.getChannelData(1);
|
||||
compareChannels(actual, expected);
|
||||
});
|
||||
}
|
||||
|
||||
biquadFilterTypes = ["lowpass", "highpass", "bandpass", "notch",
|
||||
"allpass", "lowshelf", "highshelf", "peaking"];
|
||||
|
||||
// Create a set of tasks based on biquadTestConfigs.
|
||||
for (var i = 0; i < biquadFilterTypes.length; ++i) {
|
||||
testPromises.push(testWithBiquadFilter(biquadFilterTypes[i]));
|
||||
}
|
||||
|
||||
testPromises.push(function () {
|
||||
// Multi-channel test. Create a biquad filter and the equivalent IIR filter. Filter the
|
||||
// same multichannel signal and compare the results.
|
||||
var nChannels = 3;
|
||||
var context = new OfflineAudioContext(nChannels, testFrames, sampleRate);
|
||||
|
||||
// Create a set of oscillators as the multi-channel source.
|
||||
var source = [];
|
||||
|
||||
for (k = 0; k < nChannels; ++k) {
|
||||
source[k] = context.createOscillator();
|
||||
source[k].type = "sawtooth";
|
||||
// The frequency of the oscillator is pretty arbitrary, but each oscillator should have a
|
||||
// different frequency.
|
||||
source[k].frequency.value = 100 + k * 100;
|
||||
}
|
||||
|
||||
var merger = context.createChannelMerger(3);
|
||||
|
||||
var biquad = context.createBiquadFilter();
|
||||
|
||||
// Create the equivalent IIR Filter node.
|
||||
var nyquist = sampleRate / 2;
|
||||
var coef = createFilter(biquad.type,
|
||||
biquad.frequency.value / nyquist,
|
||||
biquad.Q.value,
|
||||
biquad.gain.value);
|
||||
var fb = [1, coef.a1, coef.a2];
|
||||
var ff = [coef.b0, coef.b1, coef.b2];
|
||||
|
||||
var iir = context.createIIRFilter(ff, fb);
|
||||
// Gain node to compute the difference between the IIR and biquad filter.
|
||||
var gain = context.createGain();
|
||||
gain.gain.value = -1;
|
||||
|
||||
// Create the graph.
|
||||
for (k = 0; k < nChannels; ++k)
|
||||
source[k].connect(merger, 0, k);
|
||||
|
||||
merger.connect(biquad);
|
||||
merger.connect(iir);
|
||||
iir.connect(gain);
|
||||
biquad.connect(context.destination);
|
||||
gain.connect(context.destination);
|
||||
|
||||
for (k = 0; k < nChannels; ++k)
|
||||
source[k].start();
|
||||
|
||||
return context.startRendering().then(function (result) {
|
||||
var errorThresholds = [3.7671e-5, 3.0071e-5, 2.6241e-5];
|
||||
|
||||
// Check the difference signal on each channel
|
||||
for (channel = 0; channel < result.numberOfChannels; ++channel) {
|
||||
// Find the max amplitude of the result, which should be near zero.
|
||||
var data = result.getChannelData(channel);
|
||||
var maxError = data.reduce(function(reducedValue, currentValue) {
|
||||
return Math.max(reducedValue, Math.abs(currentValue));
|
||||
});
|
||||
|
||||
ok(maxError <= errorThresholds[channel], "Max difference between IIR and Biquad on channel " + channel);
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
testPromises.push(function () {
|
||||
// Apply an IIRFilter to the given input signal.
|
||||
//
|
||||
// IIR filter in the time domain is
|
||||
//
|
||||
// y[n] = sum(ff[k]*x[n-k], k, 0, M) - sum(fb[k]*y[n-k], k, 1, N)
|
||||
//
|
||||
function iirFilter(input, feedforward, feedback) {
|
||||
// For simplicity, create an x buffer that contains the input, and a y buffer that contains
|
||||
// the output. Both of these buffers have an initial work space to implement the initial
|
||||
// memory of the filter.
|
||||
var workSize = Math.max(feedforward.length, feedback.length);
|
||||
var x = new Float32Array(input.length + workSize);
|
||||
|
||||
// Float64 because we want to match the implementation that uses doubles to minimize
|
||||
// roundoff.
|
||||
var y = new Float64Array(input.length + workSize);
|
||||
|
||||
// Copy the input over.
|
||||
for (var k = 0; k < input.length; ++k)
|
||||
x[k + feedforward.length] = input[k];
|
||||
|
||||
// Run the filter
|
||||
for (var n = 0; n < input.length; ++n) {
|
||||
var index = n + workSize;
|
||||
var yn = 0;
|
||||
for (var k = 0; k < feedforward.length; ++k)
|
||||
yn += feedforward[k] * x[index - k];
|
||||
for (var k = 0; k < feedback.length; ++k)
|
||||
yn -= feedback[k] * y[index - k];
|
||||
|
||||
y[index] = yn;
|
||||
}
|
||||
|
||||
return y.slice(workSize).map(Math.fround);
|
||||
}
|
||||
|
||||
// Cascade the two given biquad filters to create one IIR filter.
|
||||
function cascadeBiquads(f1Coef, f2Coef) {
|
||||
// The biquad filters are:
|
||||
//
|
||||
// f1 = (b10 + b11/z + b12/z^2)/(1 + a11/z + a12/z^2);
|
||||
// f2 = (b20 + b21/z + b22/z^2)/(1 + a21/z + a22/z^2);
|
||||
//
|
||||
// To cascade them, multiply the two transforms together to get a fourth order IIR filter.
|
||||
|
||||
var numProduct = [f1Coef.b0 * f2Coef.b0,
|
||||
f1Coef.b0 * f2Coef.b1 + f1Coef.b1 * f2Coef.b0,
|
||||
f1Coef.b0 * f2Coef.b2 + f1Coef.b1 * f2Coef.b1 + f1Coef.b2 * f2Coef.b0,
|
||||
f1Coef.b1 * f2Coef.b2 + f1Coef.b2 * f2Coef.b1,
|
||||
f1Coef.b2 * f2Coef.b2
|
||||
];
|
||||
|
||||
var denProduct = [1,
|
||||
f2Coef.a1 + f1Coef.a1,
|
||||
f2Coef.a2 + f1Coef.a1 * f2Coef.a1 + f1Coef.a2,
|
||||
f1Coef.a1 * f2Coef.a2 + f1Coef.a2 * f2Coef.a1,
|
||||
f1Coef.a2 * f2Coef.a2
|
||||
];
|
||||
|
||||
return {
|
||||
ff: numProduct,
|
||||
fb: denProduct
|
||||
}
|
||||
}
|
||||
|
||||
// Find the magnitude of the root of the quadratic that has the maximum magnitude.
|
||||
//
|
||||
// The quadratic is z^2 + a1 * z + a2 and we want the root z that has the largest magnitude.
|
||||
function largestRootMagnitude(a1, a2) {
|
||||
var discriminant = a1 * a1 - 4 * a2;
|
||||
if (discriminant < 0) {
|
||||
// Complex roots: -a1/2 +/- i*sqrt(-d)/2. Thus the magnitude of each root is the same
|
||||
// and is sqrt(a1^2/4 + |d|/4)
|
||||
var d = Math.sqrt(-discriminant);
|
||||
return Math.hypot(a1 / 2, d / 2);
|
||||
} else {
|
||||
// Real roots
|
||||
var d = Math.sqrt(discriminant);
|
||||
return Math.max(Math.abs((-a1 + d) / 2), Math.abs((-a1 - d) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
// Cascade 2 lowpass biquad filters and compare that with the equivalent 4th order IIR
|
||||
// filter.
|
||||
|
||||
var nyquist = sampleRate / 2;
|
||||
// Compute the coefficients of a lowpass filter.
|
||||
|
||||
// First some preliminary stuff. Compute the coefficients of the biquad. This is used to
|
||||
// figure out how frames to use in the test.
|
||||
var biquadType = "lowpass";
|
||||
var biquadCutoff = 350;
|
||||
var biquadQ = 5;
|
||||
var biquadGain = 1;
|
||||
|
||||
var coef = createFilter(biquadType,
|
||||
biquadCutoff / nyquist,
|
||||
biquadQ,
|
||||
biquadGain);
|
||||
|
||||
// Cascade the biquads together to create an equivalent IIR filter.
|
||||
var cascade = cascadeBiquads(coef, coef);
|
||||
|
||||
// Since we're cascading two identical biquads, the root of denominator of the IIR filter is
|
||||
// repeated, so the root of the denominator with the largest magnitude occurs twice. The
|
||||
// impulse response of the IIR filter will be roughly c*(r*r)^n at time n, where r is the
|
||||
// root of largest magnitude. This approximation gets better as n increases. We can use
|
||||
// this to get a rough idea of when the response has died down to a small value.
|
||||
|
||||
// This is the value we will use to determine how many frames to render. Rendering too many
|
||||
// is a waste of time and also makes it hard to compare the actual result to the expected
|
||||
// because the magnitudes are so small that they could be mostly round-off noise.
|
||||
//
|
||||
// Find magnitude of the root with largest magnitude
|
||||
var rootMagnitude = largestRootMagnitude(coef.a1, coef.a2);
|
||||
|
||||
// Find n such that |r|^(2*n) <= eps. That is, n = log(eps)/(2*log(r)). Somewhat
|
||||
// arbitrarily choose eps = 1e-20;
|
||||
var eps = 1e-20;
|
||||
var framesForTest = Math.floor(Math.log(eps) / (2 * Math.log(rootMagnitude)));
|
||||
|
||||
// We're ready to create the graph for the test. The offline context has two channels:
|
||||
// channel 0 is the expected (cascaded biquad) result and channel 1 is the actual IIR filter
|
||||
// result.
|
||||
var context = new OfflineAudioContext(2, framesForTest, sampleRate);
|
||||
|
||||
// Use a simple impulse with a large (arbitrary) amplitude as the source
|
||||
var amplitude = 1;
|
||||
var buffer = context.createBuffer(1, testFrames, sampleRate);
|
||||
buffer.getChannelData(0)[0] = amplitude;
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
// Create the two biquad filters. Doesn't really matter what, but for simplicity we choose
|
||||
// identical lowpass filters with the same parameters.
|
||||
var biquad1 = context.createBiquadFilter();
|
||||
biquad1.type = biquadType;
|
||||
biquad1.frequency.value = biquadCutoff;
|
||||
biquad1.Q.value = biquadQ;
|
||||
|
||||
var biquad2 = context.createBiquadFilter();
|
||||
biquad2.type = biquadType;
|
||||
biquad2.frequency.value = biquadCutoff;
|
||||
biquad2.Q.value = biquadQ;
|
||||
|
||||
var iir = context.createIIRFilter(cascade.ff, cascade.fb);
|
||||
|
||||
// Create the merger to get the signals into multiple channels
|
||||
var merger = context.createChannelMerger(2);
|
||||
|
||||
// Create the graph, filtering the source through two biquads.
|
||||
source.connect(biquad1);
|
||||
biquad1.connect(biquad2);
|
||||
biquad2.connect(merger, 0, 0);
|
||||
|
||||
source.connect(iir);
|
||||
iir.connect(merger, 0, 1);
|
||||
|
||||
merger.connect(context.destination);
|
||||
|
||||
// Now filter the source through the IIR filter.
|
||||
var y = iirFilter(buffer.getChannelData(0), cascade.ff, cascade.fb);
|
||||
|
||||
// Rock and roll!
|
||||
source.start();
|
||||
|
||||
return context.startRendering().then(function(result) {
|
||||
var expected = result.getChannelData(0);
|
||||
var actual = result.getChannelData(1);
|
||||
|
||||
compareChannels(actual, expected);
|
||||
|
||||
});
|
||||
}());
|
||||
|
||||
// Wait for all tests
|
||||
Promise.all(testPromises).then(function () {
|
||||
SimpleTest.finish();
|
||||
}, function () {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test IIRFilterNode GetFrequencyResponse</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<script type="text/javascript" src="biquad-filters.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
// Modified from WebKit/LayoutTests/webaudio/iirfilter-getFrequencyResponse.html
|
||||
var sampleRate = 48000;
|
||||
var testDurationSec = 0.01;
|
||||
|
||||
// Compute a set of linearly spaced frequencies.
|
||||
function createFrequencies(nFrequencies, sampleRate)
|
||||
{
|
||||
var frequencies = new Float32Array(nFrequencies);
|
||||
var nyquist = sampleRate / 2;
|
||||
var freqDelta = nyquist / nFrequencies;
|
||||
|
||||
for (var k = 0; k < nFrequencies; ++k) {
|
||||
frequencies[k] = k * freqDelta;
|
||||
}
|
||||
|
||||
return frequencies;
|
||||
}
|
||||
|
||||
// Number of frequency samples to take.
|
||||
var numberOfFrequencies = 1000;
|
||||
|
||||
var context = new OfflineAudioContext(1, testDurationSec * sampleRate, sampleRate);
|
||||
|
||||
var frequencies = createFrequencies(numberOfFrequencies, context.sampleRate);
|
||||
|
||||
// 1-Pole IIR Filter
|
||||
var iir = context.createIIRFilter([1], [1, -0.9]);
|
||||
|
||||
var iirMag = new Float32Array(numberOfFrequencies);
|
||||
var iirPhase = new Float32Array(numberOfFrequencies);
|
||||
var trueMag = new Float32Array(numberOfFrequencies);
|
||||
var truePhase = new Float32Array(numberOfFrequencies);
|
||||
|
||||
// The IIR filter is
|
||||
// H(z) = 1/(1 - 0.9*z^(-1)).
|
||||
//
|
||||
// The frequency response is
|
||||
// H(exp(j*w)) = 1/(1 - 0.9*exp(-j*w)).
|
||||
//
|
||||
// Thus, the magnitude is
|
||||
// |H(exp(j*w))| = 1/sqrt(1.81-1.8*cos(w)).
|
||||
//
|
||||
// The phase is
|
||||
// arg(H(exp(j*w)) = atan(0.9*sin(w)/(.9*cos(w)-1))
|
||||
|
||||
var frequencyScale = Math.PI / (sampleRate / 2);
|
||||
|
||||
for (var k = 0; k < frequencies.length; ++k) {
|
||||
var omega = frequencyScale * frequencies[k];
|
||||
trueMag[k] = 1/Math.sqrt(1.81-1.8*Math.cos(omega));
|
||||
truePhase[k] = Math.atan(0.9 * Math.sin(omega) / (0.9 * Math.cos(omega) - 1));
|
||||
}
|
||||
|
||||
iir.getFrequencyResponse(frequencies, iirMag, iirPhase);
|
||||
compareChannels(iirMag, trueMag);
|
||||
compareChannels(iirPhase, truePhase);
|
||||
|
||||
// Compare IIR and Biquad Filter
|
||||
// Create an IIR filter equivalent to the biquad filter. Compute the frequency response for both and verify that they are the same.
|
||||
var biquad = context.createBiquadFilter();
|
||||
var coef = createFilter(biquad.type,
|
||||
biquad.frequency.value / (context.sampleRate / 2),
|
||||
biquad.Q.value,
|
||||
biquad.gain.value);
|
||||
|
||||
var iir = context.createIIRFilter([coef.b0, coef.b1, coef.b2], [1, coef.a1, coef.a2]);
|
||||
|
||||
var biquadMag = new Float32Array(numberOfFrequencies);
|
||||
var biquadPhase = new Float32Array(numberOfFrequencies);
|
||||
var iirMag = new Float32Array(numberOfFrequencies);
|
||||
var iirPhase = new Float32Array(numberOfFrequencies);
|
||||
|
||||
biquad.getFrequencyResponse(frequencies, biquadMag, biquadPhase);
|
||||
iir.getFrequencyResponse(frequencies, iirMag, iirPhase);
|
||||
compareChannels(iirMag, biquadMag);
|
||||
compareChannels(iirPhase, biquadPhase);
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
1
dom/media/webaudio/test/browser.ini
Normal file
1
dom/media/webaudio/test/browser.ini
Normal file
|
|
@ -0,0 +1 @@
|
|||
[browser_bug1181073.js]
|
||||
40
dom/media/webaudio/test/browser_bug1181073.js
Normal file
40
dom/media/webaudio/test/browser_bug1181073.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
add_task(function*() {
|
||||
// Make the min_background_timeout_value very high to avoid problems on slow machines
|
||||
yield new Promise(resolve => SpecialPowers.pushPrefEnv({
|
||||
'set': [['dom.min_background_timeout_value', 3000]]
|
||||
}, resolve));
|
||||
|
||||
// Make a new tab, and put it in the background
|
||||
yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
|
||||
yield BrowserTestUtils.withNewTab("about:blank", function*() {
|
||||
let time = yield ContentTask.spawn(browser, null, function () {
|
||||
return new Promise(resolve => {
|
||||
let start = content.performance.now();
|
||||
let id = content.window.setInterval(function() {
|
||||
let end = content.performance.now();
|
||||
content.window.clearInterval(id);
|
||||
resolve(end - start);
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
ok(time > 2000, "Interval is throttled with no webaudio (" + time + " ms)");
|
||||
|
||||
time = yield ContentTask.spawn(browser, null, function () {
|
||||
return new Promise(resolve => {
|
||||
// Create an audio context, and save it on the window so it doesn't get GCed
|
||||
content.window._audioCtx = new content.window.AudioContext();
|
||||
|
||||
let start = content.performance.now();
|
||||
let id = content.window.setInterval(function() {
|
||||
let end = content.performance.now();
|
||||
content.window.clearInterval(id);
|
||||
resolve(end - start);
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
ok(time < 1000, "Interval is not throttled with an audio context present (" + time + " ms)");
|
||||
});
|
||||
});
|
||||
});
|
||||
25
dom/media/webaudio/test/corsServer.sjs
Normal file
25
dom/media/webaudio/test/corsServer.sjs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
var file = Components.classes["@mozilla.org/file/directory_service;1"].
|
||||
getService(Components.interfaces.nsIProperties).
|
||||
get("CurWorkD", Components.interfaces.nsILocalFile);
|
||||
var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
|
||||
createInstance(Components.interfaces.nsIFileInputStream);
|
||||
var bis = Components.classes["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Components.interfaces.nsIBinaryInputStream);
|
||||
var paths = "tests/dom/media/webaudio/test/small-shot.ogg";
|
||||
var split = paths.split("/");
|
||||
for(var i = 0; i < split.length; ++i) {
|
||||
file.append(split[i]);
|
||||
}
|
||||
fis.init(file, -1, -1, false);
|
||||
bis.setInputStream(fis);
|
||||
var bytes = bis.readBytes(bis.available());
|
||||
response.setHeader("Content-Type", "video/ogg", false);
|
||||
response.setHeader("Content-Length", ""+ bytes.length, false);
|
||||
response.setHeader("Access-Control-Allow-Origin", "*", false);
|
||||
response.write(bytes, bytes.length);
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
bis.close();
|
||||
}
|
||||
1
dom/media/webaudio/test/invalid.txt
Normal file
1
dom/media/webaudio/test/invalid.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Surely this is not an audio file!
|
||||
19
dom/media/webaudio/test/layouttest-glue.js
Normal file
19
dom/media/webaudio/test/layouttest-glue.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Reimplementation of the LayoutTest API from Blink so we can easily port
|
||||
// WebAudio tests to Simpletest, without touching the internals of the test.
|
||||
|
||||
function testFailed(msg) {
|
||||
ok(false, msg);
|
||||
}
|
||||
|
||||
function testPassed(msg) {
|
||||
ok(true, msg);
|
||||
}
|
||||
|
||||
function finishJSTest() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function description(str) {
|
||||
info(str);
|
||||
}
|
||||
|
||||
212
dom/media/webaudio/test/mochitest.ini
Normal file
212
dom/media/webaudio/test/mochitest.ini
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
[DEFAULT]
|
||||
tags=msg
|
||||
tags = webaudio
|
||||
subsuite = media
|
||||
support-files =
|
||||
audio-expected.wav
|
||||
audio-mono-expected-2.wav
|
||||
audio-mono-expected.wav
|
||||
audio-quad.wav
|
||||
audio.ogv
|
||||
audiovideo.mp4
|
||||
audioBufferSourceNodeDetached_worker.js
|
||||
corsServer.sjs
|
||||
invalid.txt
|
||||
layouttest-glue.js
|
||||
noaudio.webm
|
||||
small-shot-expected.wav
|
||||
small-shot-mono-expected.wav
|
||||
small-shot.ogg
|
||||
small-shot.mp3
|
||||
sweep-300-330-1sec.opus
|
||||
ting-44.1k-1ch.ogg
|
||||
ting-44.1k-2ch.ogg
|
||||
ting-48k-1ch.ogg
|
||||
ting-48k-2ch.ogg
|
||||
ting-44.1k-1ch.wav
|
||||
ting-44.1k-2ch.wav
|
||||
ting-48k-1ch.wav
|
||||
ting-48k-2ch.wav
|
||||
sine-440-10s.opus
|
||||
webaudio.js
|
||||
|
||||
[test_analyserNode.html]
|
||||
[test_analyserScale.html]
|
||||
[test_analyserNodeOutput.html]
|
||||
[test_analyserNodePassThrough.html]
|
||||
[test_analyserNodeWithGain.html]
|
||||
[test_AudioBuffer.html]
|
||||
[test_audioBufferSourceNode.html]
|
||||
[test_audioBufferSourceNodeEnded.html]
|
||||
[test_audioBufferSourceNodeLazyLoopParam.html]
|
||||
[test_audioBufferSourceNodeLoop.html]
|
||||
[test_audioBufferSourceNodeLoopStartEnd.html]
|
||||
[test_audioBufferSourceNodeLoopStartEndSame.html]
|
||||
[test_audioBufferSourceNodeDetached.html]
|
||||
skip-if = (toolkit == 'android' && debug) || os == 'win' # bug 1127845, bug 1138468
|
||||
[test_audioBufferSourceNodeNoStart.html]
|
||||
[test_audioBufferSourceNodeNullBuffer.html]
|
||||
[test_audioBufferSourceNodeOffset.html]
|
||||
skip-if = (toolkit == 'android') || debug #bug 906752
|
||||
[test_audioBufferSourceNodePassThrough.html]
|
||||
[test_audioBufferSourceNodeRate.html]
|
||||
[test_AudioContext.html]
|
||||
[test_AudioContext_disabled.html]
|
||||
[test_audioContextSuspendResumeClose.html]
|
||||
tags=capturestream
|
||||
[test_audioDestinationNode.html]
|
||||
[test_AudioListener.html]
|
||||
[test_AudioNodeDevtoolsAPI.html]
|
||||
[test_audioParamChaining.html]
|
||||
[test_AudioParamDevtoolsAPI.html]
|
||||
[test_audioParamExponentialRamp.html]
|
||||
[test_audioParamGain.html]
|
||||
[test_audioParamLinearRamp.html]
|
||||
[test_audioParamSetCurveAtTime.html]
|
||||
[test_audioParamSetCurveAtTimeTwice.html]
|
||||
[test_audioParamSetCurveAtTimeZeroDuration.html]
|
||||
[test_audioParamSetTargetAtTime.html]
|
||||
[test_audioParamSetTargetAtTimeZeroTimeConstant.html]
|
||||
[test_audioParamSetValueAtTime.html]
|
||||
[test_audioParamTimelineDestinationOffset.html]
|
||||
[test_badConnect.html]
|
||||
[test_biquadFilterNode.html]
|
||||
[test_biquadFilterNodePassThrough.html]
|
||||
[test_biquadFilterNodeWithGain.html]
|
||||
[test_bug808374.html]
|
||||
[test_bug827541.html]
|
||||
[test_bug839753.html]
|
||||
[test_bug845960.html]
|
||||
[test_bug856771.html]
|
||||
[test_bug866570.html]
|
||||
[test_bug866737.html]
|
||||
[test_bug867089.html]
|
||||
[test_bug867104.html]
|
||||
[test_bug867174.html]
|
||||
[test_bug867203.html]
|
||||
[test_bug875221.html]
|
||||
[test_bug875402.html]
|
||||
[test_bug894150.html]
|
||||
[test_bug956489.html]
|
||||
[test_bug964376.html]
|
||||
[test_bug966247.html]
|
||||
tags=capturestream
|
||||
[test_bug972678.html]
|
||||
[test_bug1113634.html]
|
||||
[test_bug1118372.html]
|
||||
[test_bug1027864.html]
|
||||
[test_bug1056032.html]
|
||||
skip-if = toolkit == 'android' # bug 1056706
|
||||
[test_bug1255618.html]
|
||||
[test_bug1267579.html]
|
||||
[test_channelMergerNode.html]
|
||||
[test_channelMergerNodeWithVolume.html]
|
||||
[test_channelSplitterNode.html]
|
||||
[test_channelSplitterNodeWithVolume.html]
|
||||
skip-if = (android_version == '18' && debug) # bug 1158417
|
||||
[test_convolverNode.html]
|
||||
[test_convolverNode_mono_mono.html]
|
||||
[test_convolverNodeChannelCount.html]
|
||||
[test_convolverNodeDelay.html]
|
||||
[test_convolverNodeFiniteInfluence.html]
|
||||
[test_convolverNodePassThrough.html]
|
||||
[test_convolverNodeWithGain.html]
|
||||
[test_currentTime.html]
|
||||
[test_decodeMultichannel.html]
|
||||
[test_decodeAudioDataPromise.html]
|
||||
[test_decodeOpusTail.html]
|
||||
[test_delayNode.html]
|
||||
[test_delayNodeAtMax.html]
|
||||
[test_delayNodeChannelChanges.html]
|
||||
skip-if = toolkit == 'android' # bug 1056706
|
||||
[test_delayNodeCycles.html]
|
||||
[test_delayNodePassThrough.html]
|
||||
[test_delayNodeSmallMaxDelay.html]
|
||||
[test_delayNodeTailIncrease.html]
|
||||
[test_delayNodeTailWithDisconnect.html]
|
||||
[test_delayNodeTailWithGain.html]
|
||||
[test_delayNodeTailWithReconnect.html]
|
||||
[test_delayNodeWithGain.html]
|
||||
[test_disconnectAll.html]
|
||||
[test_disconnectAudioParam.html]
|
||||
[test_disconnectAudioParamFromOutput.html]
|
||||
[test_disconnectExceptions.html]
|
||||
[test_disconnectFromAudioNode.html]
|
||||
[test_disconnectFromAudioNodeAndOutput.html]
|
||||
[test_disconnectFromAudioNodeAndOutputAndInput.html]
|
||||
[test_disconnectFromAudioNodeMultipleConnection.html]
|
||||
[test_disconnectFromOutput.html]
|
||||
[test_dynamicsCompressorNode.html]
|
||||
[test_dynamicsCompressorNodePassThrough.html]
|
||||
[test_dynamicsCompressorNodeWithGain.html]
|
||||
[test_gainNode.html]
|
||||
[test_gainNodeInLoop.html]
|
||||
[test_gainNodePassThrough.html]
|
||||
[test_iirFilterNodePassThrough.html]
|
||||
[test_maxChannelCount.html]
|
||||
[test_mediaDecoding.html]
|
||||
[test_mediaElementAudioSourceNode.html]
|
||||
tags=capturestream
|
||||
[test_mediaElementAudioSourceNodeFidelity.html]
|
||||
tags=capturestream
|
||||
[test_mediaElementAudioSourceNodePassThrough.html]
|
||||
tags=capturestream
|
||||
skip-if = toolkit == 'android' # bug 1145816
|
||||
[test_mediaElementAudioSourceNodeVideo.html]
|
||||
tags=capturestream
|
||||
[test_mediaElementAudioSourceNodeCrossOrigin.html]
|
||||
tags=capturestream
|
||||
skip-if = toolkit == 'android' # bug 1145816
|
||||
[test_mediaStreamAudioDestinationNode.html]
|
||||
[test_mediaStreamAudioSourceNode.html]
|
||||
[test_mediaStreamAudioSourceNodeCrossOrigin.html]
|
||||
tags=capturestream
|
||||
[test_mediaStreamAudioSourceNodeNoGC.html]
|
||||
[test_mediaStreamAudioSourceNodePassThrough.html]
|
||||
[test_mediaStreamAudioSourceNodeResampling.html]
|
||||
tags=capturestream
|
||||
[test_mixingRules.html]
|
||||
skip-if = toolkit == 'android' # bug 1091965
|
||||
[test_mozaudiochannel.html]
|
||||
# Android: bug 1061675; OSX 10.6: bug 1097721
|
||||
skip-if = (toolkit == 'android') || (os == 'mac' && os_version == '10.6')
|
||||
[test_nodeToParamConnection.html]
|
||||
[test_OfflineAudioContext.html]
|
||||
[test_offlineDestinationChannelCountLess.html]
|
||||
[test_offlineDestinationChannelCountMore.html]
|
||||
[test_oscillatorNode.html]
|
||||
[test_oscillatorNode2.html]
|
||||
[test_oscillatorNodeNegativeFrequency.html]
|
||||
[test_oscillatorNodePassThrough.html]
|
||||
[test_oscillatorNodeStart.html]
|
||||
[test_oscillatorTypeChange.html]
|
||||
[test_pannerNode.html]
|
||||
[test_pannerNode_equalPower.html]
|
||||
[test_pannerNodeAbove.html]
|
||||
[test_pannerNodeAtZeroDistance.html]
|
||||
[test_pannerNodeChannelCount.html]
|
||||
[test_pannerNodeHRTFSymmetry.html]
|
||||
[test_pannerNodeTail.html]
|
||||
[test_pannerNode_maxDistance.html]
|
||||
[test_stereoPannerNode.html]
|
||||
[test_stereoPannerNodePassThrough.html]
|
||||
[test_periodicWave.html]
|
||||
[test_periodicWaveDisableNormalization.html]
|
||||
[test_periodicWaveBandLimiting.html]
|
||||
[test_ScriptProcessorCollected1.html]
|
||||
[test_scriptProcessorNode.html]
|
||||
[test_scriptProcessorNodeChannelCount.html]
|
||||
[test_scriptProcessorNodePassThrough.html]
|
||||
[test_scriptProcessorNode_playbackTime1.html]
|
||||
[test_scriptProcessorNodeZeroInputOutput.html]
|
||||
[test_scriptProcessorNodeNotConnected.html]
|
||||
[test_sequentialBufferSourceWithResampling.html]
|
||||
[test_singleSourceDest.html]
|
||||
[test_stereoPanningWithGain.html]
|
||||
[test_waveDecoder.html]
|
||||
[test_waveShaper.html]
|
||||
[test_waveShaperGain.html]
|
||||
[test_waveShaperNoCurve.html]
|
||||
[test_waveShaperPassThrough.html]
|
||||
[test_waveShaperInvalidLengthCurve.html]
|
||||
[test_WebAudioMemoryReporting.html]
|
||||
BIN
dom/media/webaudio/test/noaudio.webm
Normal file
BIN
dom/media/webaudio/test/noaudio.webm
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/sine-440-10s.opus
Normal file
BIN
dom/media/webaudio/test/sine-440-10s.opus
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/small-shot-expected.wav
Normal file
BIN
dom/media/webaudio/test/small-shot-expected.wav
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/small-shot-mono-expected.wav
Normal file
BIN
dom/media/webaudio/test/small-shot-mono-expected.wav
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/small-shot.mp3
Normal file
BIN
dom/media/webaudio/test/small-shot.mp3
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/small-shot.ogg
Normal file
BIN
dom/media/webaudio/test/small-shot.ogg
Normal file
Binary file not shown.
BIN
dom/media/webaudio/test/sweep-300-330-1sec.opus
Normal file
BIN
dom/media/webaudio/test/sweep-300-330-1sec.opus
Normal file
Binary file not shown.
105
dom/media/webaudio/test/test_AudioBuffer.html
Normal file
105
dom/media/webaudio/test/test_AudioBuffer.html
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test whether we can create an AudioContext interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var context = new AudioContext();
|
||||
var buffer = context.createBuffer(2, 2048, context.sampleRate);
|
||||
SpecialPowers.gc(); // Make sure that our channels are accessible after GC
|
||||
ok(buffer, "Buffer was allocated successfully");
|
||||
is(buffer.sampleRate, context.sampleRate, "Correct sample rate");
|
||||
is(buffer.length, 2048, "Correct length");
|
||||
ok(Math.abs(buffer.duration - 2048 / context.sampleRate) < 0.0001, "Correct duration");
|
||||
is(buffer.numberOfChannels, 2, "Correct number of channels");
|
||||
for (var i = 0; i < buffer.numberOfChannels; ++i) {
|
||||
var buf = buffer.getChannelData(i);
|
||||
ok(buf, "Buffer index " + i + " exists");
|
||||
ok(buf instanceof Float32Array, "Result is a typed array");
|
||||
is(buf.length, buffer.length, "Correct length");
|
||||
var foundNonZero = false;
|
||||
for (var j = 0; j < buf.length; ++j) {
|
||||
if (buf[j] != 0) {
|
||||
foundNonZero = true;
|
||||
break;
|
||||
}
|
||||
buf[j] = j;
|
||||
}
|
||||
ok(!foundNonZero, "Buffer " + i + " should be initialized to 0");
|
||||
}
|
||||
|
||||
// Now test copying the channel data out of a normal buffer
|
||||
var copy = new Float32Array(100);
|
||||
buffer.copyFromChannel(copy, 0, 1024);
|
||||
for (var i = 0; i < copy.length; ++i) {
|
||||
is(copy[i], 1024 + i, "Correct sample");
|
||||
}
|
||||
|
||||
// Test copying the channel data out of a playing buffer
|
||||
var srcNode = context.createBufferSource();
|
||||
srcNode.buffer = buffer;
|
||||
srcNode.start(0);
|
||||
copy = new Float32Array(100);
|
||||
buffer.copyFromChannel(copy, 0, 1024);
|
||||
for (var i = 0; i < copy.length; ++i) {
|
||||
is(copy[i], 1024 + i, "Correct sample");
|
||||
}
|
||||
|
||||
// Test copying to the channel data
|
||||
var newData = new Float32Array(200);
|
||||
buffer.copyToChannel(newData, 0, 100);
|
||||
var changedData = buffer.getChannelData(0);
|
||||
for (var i = 0; i < changedData.length; ++i) {
|
||||
if (i < 100 || i >= 300) {
|
||||
is(changedData[i], i, "Untouched sample");
|
||||
} else {
|
||||
is(changedData[i], 0, "Correct sample");
|
||||
}
|
||||
}
|
||||
|
||||
// Now, detach the array buffer
|
||||
var worker = new Worker("audioBufferSourceNodeDetached_worker.js");
|
||||
var data = buffer.getChannelData(0).buffer;
|
||||
worker.postMessage(data, [data]);
|
||||
SpecialPowers.gc();
|
||||
|
||||
expectException(function() {
|
||||
buffer.copyFromChannel(copy, 0, 1024);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
|
||||
expectException(function() {
|
||||
buffer.copyToChannel(newData, 0, 100);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
|
||||
expectException(function() {
|
||||
context.createBuffer(2, 2048, 7999);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
context.createBuffer(2, 2048, 192001);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
context.createBuffer(2, 2048, 8000); // no exception
|
||||
context.createBuffer(2, 2048, 192000); // no exception
|
||||
context.createBuffer(32, 2048, 48000); // no exception
|
||||
// Null length
|
||||
expectException(function() {
|
||||
context.createBuffer(2, 0, 48000);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
// Null number of channels
|
||||
expectException(function() {
|
||||
context.createBuffer(0, 2048, 48000);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
23
dom/media/webaudio/test/test_AudioContext.html
Normal file
23
dom/media/webaudio/test/test_AudioContext.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test whether we can create an AudioContext interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var ac = new AudioContext();
|
||||
ok(ac, "Create a AudioContext object");
|
||||
ok(ac instanceof EventTarget, "AudioContexts must be EventTargets");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
56
dom/media/webaudio/test/test_AudioContext_disabled.html
Normal file
56
dom/media/webaudio/test/test_AudioContext_disabled.html
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test whether we can disable the AudioContext interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
const webaudio_interfaces = [
|
||||
"AudioContext",
|
||||
"OfflineAudioContext",
|
||||
"AudioContext",
|
||||
"OfflineAudioCompletionEvent",
|
||||
"AudioNode",
|
||||
"AudioDestinationNode",
|
||||
"AudioParam",
|
||||
"GainNode",
|
||||
"DelayNode",
|
||||
"AudioBuffer",
|
||||
"AudioBufferSourceNode",
|
||||
"MediaElementAudioSourceNode",
|
||||
"ScriptProcessorNode",
|
||||
"AudioProcessingEvent",
|
||||
"PannerNode",
|
||||
"AudioListener",
|
||||
"StereoPannerNode",
|
||||
"ConvolverNode",
|
||||
"AnalyserNode",
|
||||
"ChannelSplitterNode",
|
||||
"ChannelMergerNode",
|
||||
"DynamicsCompressorNode",
|
||||
"BiquadFilterNode",
|
||||
"IIRFilterNode",
|
||||
"WaveShaperNode",
|
||||
"OscillatorNode",
|
||||
"PeriodicWave",
|
||||
"MediaStreamAudioSourceNode",
|
||||
"MediaStreamAudioDestinationNode"
|
||||
];
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.webaudio.enabled", false]]}, function() {
|
||||
webaudio_interfaces.forEach((e) => ok(!window[e], e + " must be disabled when the Web Audio API is disabled"));
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
35
dom/media/webaudio/test/test_AudioListener.html
Normal file
35
dom/media/webaudio/test/test_AudioListener.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioContext.listener and the AudioListener interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var context = new AudioContext();
|
||||
ok("listener" in context, "AudioContext.listener should exist");
|
||||
ok(Math.abs(context.listener.dopplerFactor - 1.0) < 1e-4, "Correct default doppler factor");
|
||||
ok(Math.abs(context.listener.speedOfSound - 343.3) < 1e-4, "Correct default speed of sound value");
|
||||
context.listener.dopplerFactor = 0.5;
|
||||
ok(Math.abs(context.listener.dopplerFactor - 0.5) < 1e-4, "The doppler factor value can be changed");
|
||||
context.listener.speedOfSound = 400;
|
||||
ok(Math.abs(context.listener.speedOfSound - 400) < 1e-4, "The speed of sound can be changed");
|
||||
// The values set by the following cannot be read from script, but the
|
||||
// implementation is simple enough, so we just make sure that nothing throws.
|
||||
with (context.listener) {
|
||||
setPosition(1.0, 1.0, 1.0);
|
||||
setOrientation(1.0, 1.0, 1.0, 1.0, 1.0, 1.0);
|
||||
setVelocity(0.5, 1.0, 1.5);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
59
dom/media/webaudio/test/test_AudioNodeDevtoolsAPI.html
Normal file
59
dom/media/webaudio/test/test_AudioNodeDevtoolsAPI.html
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the devtool AudioNode API</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function id(node) {
|
||||
return SpecialPowers.getPrivilegedProps(node, "id");
|
||||
}
|
||||
|
||||
var ac = new AudioContext();
|
||||
var ids;
|
||||
var weak;
|
||||
(function() {
|
||||
var src1 = ac.createBufferSource();
|
||||
var src2 = ac.createBufferSource();
|
||||
ok(id(src2) > id(src1), "The ID should be monotonic");
|
||||
ok(id(src1) > id(ac.destination), "The ID of the destination node should be the lowest");
|
||||
ids = [id(src1), id(src2)];
|
||||
weak = SpecialPowers.Cu.getWeakReference(src1);
|
||||
is(SpecialPowers.unwrap(weak.get()), src1, "The node should support a weak reference");
|
||||
})();
|
||||
function observer(subject, topic, data) {
|
||||
var id = parseInt(data);
|
||||
var index = ids.indexOf(id);
|
||||
if (index != -1) {
|
||||
info("Dropping id " + id + " at index " + index);
|
||||
ids.splice(index, 1);
|
||||
if (ids.length == 0) {
|
||||
SimpleTest.executeSoon(function() {
|
||||
is(weak.get(), null, "The weak reference must be dropped now");
|
||||
SpecialPowers.removeObserver(observer, "webaudio-node-demise");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
SpecialPowers.addObserver(observer, "webaudio-node-demise", false);
|
||||
|
||||
forceCC();
|
||||
forceCC();
|
||||
|
||||
function forceCC() {
|
||||
SpecialPowers.DOMWindowUtils.cycleCollect();
|
||||
SpecialPowers.DOMWindowUtils.garbageCollect();
|
||||
SpecialPowers.DOMWindowUtils.garbageCollect();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
49
dom/media/webaudio/test/test_AudioParamDevtoolsAPI.html
Normal file
49
dom/media/webaudio/test/test_AudioParamDevtoolsAPI.html
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the devtool AudioParam API</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function checkIdAndName(node, name) {
|
||||
is(SpecialPowers.getPrivilegedProps(node, "id"),
|
||||
SpecialPowers.getPrivilegedProps(node[name], "parentNodeId"),
|
||||
"The parent id should be correct");
|
||||
is(SpecialPowers.getPrivilegedProps(node[name], "name"), name,
|
||||
"The name of the AudioParam should be correct.");
|
||||
}
|
||||
|
||||
var ac = new AudioContext(),
|
||||
gain = ac.createGain(),
|
||||
osc = ac.createOscillator(),
|
||||
del = ac.createDelay(),
|
||||
source = ac.createBufferSource(),
|
||||
stereoPanner = ac.createStereoPanner(),
|
||||
comp = ac.createDynamicsCompressor(),
|
||||
biquad = ac.createBiquadFilter();
|
||||
|
||||
checkIdAndName(gain, "gain");
|
||||
checkIdAndName(osc, "frequency");
|
||||
checkIdAndName(osc, "detune");
|
||||
checkIdAndName(del, "delayTime");
|
||||
checkIdAndName(source, "playbackRate");
|
||||
checkIdAndName(source, "detune");
|
||||
checkIdAndName(stereoPanner, "pan");
|
||||
checkIdAndName(comp, "threshold");
|
||||
checkIdAndName(comp, "knee");
|
||||
checkIdAndName(comp, "ratio");
|
||||
checkIdAndName(comp, "attack");
|
||||
checkIdAndName(comp, "release");
|
||||
checkIdAndName(biquad, "frequency");
|
||||
checkIdAndName(biquad, "detune");
|
||||
checkIdAndName(biquad, "Q");
|
||||
checkIdAndName(biquad, "gain");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
102
dom/media/webaudio/test/test_OfflineAudioContext.html
Normal file
102
dom/media/webaudio/test/test_OfflineAudioContext.html
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test OfflineAudioContext</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var renderedBuffer = null;
|
||||
var finished = 0;
|
||||
|
||||
function finish() {
|
||||
finished++;
|
||||
if (finished == 2) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function setOrCompareRenderedBuffer(aRenderedBuffer) {
|
||||
if (renderedBuffer) {
|
||||
is(renderedBuffer, aRenderedBuffer, "Rendered buffers from the event and the promise should be the same");
|
||||
finish();
|
||||
} else {
|
||||
renderedBuffer = aRenderedBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var ctx = new OfflineAudioContext(2, 100, 22050);
|
||||
ok(ctx instanceof EventTarget, "OfflineAudioContexts must be EventTargets");
|
||||
is(ctx.length, 100, "OfflineAudioContext.length is equal to the value passed to the ctor.");
|
||||
|
||||
var buf = ctx.createBuffer(2, 100, ctx.sampleRate);
|
||||
for (var i = 0; i < 2; ++i) {
|
||||
for (var j = 0; j < 100; ++j) {
|
||||
buf.getChannelData(i)[j] = Math.sin(2 * Math.PI * 200 * j / ctx.sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
expectException(function() {
|
||||
ctx.createMediaStreamDestination();
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
|
||||
expectException(function() {
|
||||
new OfflineAudioContext(2, 100, 0);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
expectException(function() {
|
||||
new OfflineAudioContext(2, 100, -1);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
expectException(function() {
|
||||
new OfflineAudioContext(0, 100, 44100);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
new OfflineAudioContext(32, 100, 44100);
|
||||
expectException(function() {
|
||||
new OfflineAudioContext(33, 100, 44100);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
expectException(function() {
|
||||
new OfflineAudioContext(2, 0, 44100);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
|
||||
var src = ctx.createBufferSource();
|
||||
src.buffer = buf;
|
||||
src.start(0);
|
||||
src.connect(ctx.destination);
|
||||
|
||||
ctx.addEventListener("complete", function(e) {
|
||||
ok(e instanceof OfflineAudioCompletionEvent, "Correct event received");
|
||||
is(e.renderedBuffer.numberOfChannels, 2, "Correct expected number of buffers");
|
||||
ok(renderedBuffer != null, "The event should be fired after the promise callback.");
|
||||
expectNoException(function() {
|
||||
ctx.startRendering().then(function() {
|
||||
ok(false, "Promise should not resolve when startRendering is called a second time on an OfflineAudioContext")
|
||||
finish();
|
||||
}).catch(function(err) {
|
||||
ok(true, "Promise should reject when startRendering is called a second time on an OfflineAudioContext")
|
||||
finish();
|
||||
});
|
||||
});
|
||||
compareBuffers(e.renderedBuffer, buf);
|
||||
setOrCompareRenderedBuffer(e.renderedBuffer);
|
||||
|
||||
}, false);
|
||||
|
||||
expectNoException(function() {
|
||||
ctx.startRendering().then(function(b) {
|
||||
is(renderedBuffer, null, "The promise callback should be called first.");
|
||||
setOrCompareRenderedBuffer(b);
|
||||
}).catch(function (error) {
|
||||
ok(false, "The promise from OfflineAudioContext.startRendering should never be rejected");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
84
dom/media/webaudio/test/test_ScriptProcessorCollected1.html
Normal file
84
dom/media/webaudio/test/test_ScriptProcessorCollected1.html
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test ScriptProcessorNode in cycle with no listener is collected</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var observer = function(subject, topic, data) {
|
||||
var id = parseInt(data);
|
||||
var index = ids.indexOf(id);
|
||||
if (index != -1) {
|
||||
ok(true, "Collected AudioNode id " + id + " at index " + index);
|
||||
ids.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.addObserver(observer, "webaudio-node-demise", false);
|
||||
|
||||
SimpleTest.registerCleanupFunction(function() {
|
||||
if (observer) {
|
||||
SpecialPowers.removeObserver(observer, "webaudio-node-demise");
|
||||
}
|
||||
});
|
||||
|
||||
var ac = new AudioContext();
|
||||
|
||||
var testProcessor = ac.createScriptProcessor(256, 1, 0);
|
||||
var delay = ac.createDelay();
|
||||
testProcessor.connect(delay);
|
||||
delay.connect(testProcessor);
|
||||
|
||||
var referenceProcessor = ac.createScriptProcessor(256, 1, 0);
|
||||
var gain = ac.createGain();
|
||||
gain.connect(referenceProcessor);
|
||||
|
||||
var processCount = 0;
|
||||
testProcessor.onaudioprocess = function(event) {
|
||||
++processCount;
|
||||
switch (processCount) {
|
||||
case 1:
|
||||
// Switch to listening to referenceProcessor;
|
||||
referenceProcessor.onaudioprocess = event.target.onaudioprocess;
|
||||
referenceProcessor = null;
|
||||
event.target.onaudioprocess = null;
|
||||
case 2:
|
||||
// There are no references to testProcessor and so GC can begin.
|
||||
SpecialPowers.forceGC();
|
||||
break;
|
||||
case 3:
|
||||
// Another GC should not be required after testProcessor would have
|
||||
// received another audioprocess event.
|
||||
SpecialPowers.forceCC();
|
||||
// Expect that webaudio-demise has been queued.
|
||||
// Queue another event to check.
|
||||
SimpleTest.executeSoon(function() {
|
||||
SpecialPowers.removeObserver(observer, "webaudio-node-demise");
|
||||
observer = null;
|
||||
event.target.onaudioprocess = null;
|
||||
ok(ids.length == 0, "All expected nodes should be collected");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
function id(node) {
|
||||
return SpecialPowers.getPrivilegedProps(node, "id");
|
||||
}
|
||||
|
||||
// Nodes with these ids should be collected.
|
||||
var ids = [ id(testProcessor), id(delay), id(gain) ];
|
||||
testProcessor = null;
|
||||
delay = null;
|
||||
gain = null;
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
54
dom/media/webaudio/test/test_WebAudioMemoryReporting.html
Normal file
54
dom/media/webaudio/test/test_WebAudioMemoryReporting.html
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web Audio memory reporting</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var ac = new AudioContext();
|
||||
var sp = ac.createScriptProcessor(4096, 1, 1);
|
||||
sp.connect(ac.destination);
|
||||
|
||||
// Not started so as to test
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1225003#c2
|
||||
var oac = new OfflineAudioContext(1, 1, 48000);
|
||||
|
||||
var nodeTypes = ["ScriptProcessorNode", "AudioDestinationNode"];
|
||||
var objectTypes = ["dom-nodes", "engine-objects", "stream-objects"];
|
||||
|
||||
var usages = { "explicit/webaudio/audiocontext": 0 };
|
||||
|
||||
for (var i = 0; i < nodeTypes.length; ++i) {
|
||||
for (var j = 0; j < objectTypes.length; ++j) {
|
||||
usages["explicit/webaudio/audio-node/" +
|
||||
nodeTypes[i] + "/" + objectTypes[j]] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var handleReport = function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) {
|
||||
if (aPath in usages) {
|
||||
usages[aPath] += aAmount;
|
||||
}
|
||||
}
|
||||
|
||||
var finished = function () {
|
||||
ok(true, "Yay didn't crash!");
|
||||
for (var resource in usages) {
|
||||
ok(usages[resource] > 0, "Non-zero usage for " + resource);
|
||||
};
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(SpecialPowers.Ci.nsIMemoryReporterManager).
|
||||
getReports(handleReport, null, finished, null, /* anonymized = */ false);
|
||||
|
||||
// To test bug 1225003, run a failing decodeAudioData() job over a time when
|
||||
// the tasks from getReports() are expected to run.
|
||||
ac.decodeAudioData(new ArrayBuffer(4), function(){}, function(){});
|
||||
</script>
|
||||
</html>
|
||||
103
dom/media/webaudio/test/test_analyserNode.html
Normal file
103
dom/media/webaudio/test/test_analyserNode.html
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AnalyserNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
|
||||
var context = new AudioContext();
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var destination = context.destination;
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var analyser = context.createAnalyser();
|
||||
|
||||
source.buffer = buffer;
|
||||
|
||||
source.connect(analyser);
|
||||
analyser.connect(destination);
|
||||
|
||||
is(analyser.channelCount, 1, "analyser node has 1 input channels by default");
|
||||
is(analyser.channelCountMode, "max", "Correct channelCountMode for the analyser node");
|
||||
is(analyser.channelInterpretation, "speakers", "Correct channelCountInterpretation for the analyser node");
|
||||
|
||||
is(analyser.fftSize, 2048, "Correct default value for fftSize");
|
||||
is(analyser.frequencyBinCount, 1024, "Correct default value for frequencyBinCount");
|
||||
expectException(function() {
|
||||
analyser.fftSize = 0;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.fftSize = 1;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.fftSize = 8;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.fftSize = 100; // non-power of two
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.fftSize = 2049;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.fftSize = 4097;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.fftSize = 8193;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.fftSize = 16385;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.fftSize = 32769;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.fftSize = 65536;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
analyser.fftSize = 1024;
|
||||
is(analyser.frequencyBinCount, 512, "Correct new value for frequencyBinCount");
|
||||
|
||||
is(analyser.minDecibels, -100, "Correct default value for minDecibels");
|
||||
is(analyser.maxDecibels, -30, "Correct default value for maxDecibels");
|
||||
expectException(function() {
|
||||
analyser.minDecibels = -30;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.minDecibels = -29;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.maxDecibels = -100;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.maxDecibels = -101;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
|
||||
is(analyser.smoothingTimeConstant, 0.8, "Correct default value for smoothingTimeConstant");
|
||||
expectException(function() {
|
||||
analyser.smoothingTimeConstant = -0.1;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser.smoothingTimeConstant = 1.1;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
analyser.smoothingTimeConstant = 0;
|
||||
analyser.smoothingTimeConstant = 1;
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
43
dom/media/webaudio/test/test_analyserNodeOutput.html
Normal file
43
dom/media/webaudio/test/test_analyserNodeOutput.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AnalyserNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var analyser = context.createAnalyser();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(analyser);
|
||||
|
||||
source.start(0);
|
||||
return analyser;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
47
dom/media/webaudio/test/test_analyserNodePassThrough.html
Normal file
47
dom/media/webaudio/test/test_analyserNodePassThrough.html
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AnalyserNode with passthrough</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var analyser = context.createAnalyser();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(analyser);
|
||||
|
||||
var analyserWrapped = SpecialPowers.wrap(analyser);
|
||||
ok("passThrough" in analyserWrapped, "AnalyserNode should support the passThrough API");
|
||||
analyserWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return analyser;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
47
dom/media/webaudio/test/test_analyserNodeWithGain.html
Normal file
47
dom/media/webaudio/test/test_analyserNodeWithGain.html
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Test effect of AnalyserNode on GainNode output</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
promise_test(function() {
|
||||
// fftSize <= bufferSize so that the time domain data is full of input after
|
||||
// processing the buffer.
|
||||
const fftSize = 32;
|
||||
const bufferSize = 128;
|
||||
|
||||
var context = new OfflineAudioContext(1, bufferSize, 48000);
|
||||
|
||||
var analyser1 = context.createAnalyser();
|
||||
analyser1.fftSize = fftSize;
|
||||
analyser1.connect(context.destination);
|
||||
var analyser2 = context.createAnalyser();
|
||||
analyser2.fftSize = fftSize;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.value = 2.0;
|
||||
gain.connect(analyser1);
|
||||
gain.connect(analyser2);
|
||||
|
||||
// Create a DC input to make getFloatTimeDomainData() output consistent at
|
||||
// any time.
|
||||
var buffer = context.createBuffer(1, 1, context.sampleRate);
|
||||
buffer.getChannelData(0)[0] = 1.0 / gain.gain.value;
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.loop = true;
|
||||
source.connect(gain);
|
||||
source.start();
|
||||
|
||||
return context.startRendering().
|
||||
then(function(buffer) {
|
||||
assert_equals(buffer.getChannelData(0)[0], 1.0,
|
||||
"analyser1 output");
|
||||
|
||||
var data = new Float32Array(1);
|
||||
analyser1.getFloatTimeDomainData(data);
|
||||
assert_equals(data[0], 1.0, "analyser1 time domain data");
|
||||
analyser2.getFloatTimeDomainData(data);
|
||||
assert_equals(data[0], 1.0, "analyser2 time domain data");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
59
dom/media/webaudio/test/test_analyserScale.html
Normal file
59
dom/media/webaudio/test/test_analyserScale.html
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AnalyserNode when the input is scaled </title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
|
||||
var context = new AudioContext();
|
||||
|
||||
var gain = context.createGain();
|
||||
var analyser = context.createAnalyser();
|
||||
var osc = context.createOscillator();
|
||||
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(analyser);
|
||||
|
||||
osc.start();
|
||||
|
||||
var array = new Uint8Array(analyser.frequencyBinCount);
|
||||
|
||||
function getAnalyserData() {
|
||||
gain.gain.setValueAtTime(currentGain, context.currentTime);
|
||||
analyser.getByteTimeDomainData(array);
|
||||
var inrange = true;
|
||||
var max = -1;
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
if (array[i] > max) {
|
||||
max = Math.abs(array[i] - 128);
|
||||
}
|
||||
}
|
||||
if (max <= currentGain * 128) {
|
||||
ok(true, "Analyser got scaled data for " + currentGain);
|
||||
currentGain = tests.shift();
|
||||
if (currentGain == undefined) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(getAnalyserData);
|
||||
}
|
||||
|
||||
var tests = [1.0, 0.5, 0.0];
|
||||
var currentGain = tests.shift();
|
||||
requestAnimationFrame(getAnalyserData);
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
44
dom/media/webaudio/test/test_audioBufferSourceNode.html
Normal file
44
dom/media/webaudio/test/test_audioBufferSourceNode.html
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 4096,
|
||||
createGraph: function(context) {
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.start(0);
|
||||
source.buffer = buffer;
|
||||
return source;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var buffers = [];
|
||||
var buffer = context.createBuffer(2, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
buffer.getChannelData(1)[i] = buffer.getChannelData(0)[i];
|
||||
}
|
||||
buffers.push(buffer);
|
||||
buffers.push(getEmptyBuffer(context, 2048));
|
||||
return buffers;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode when an AudioBuffer's getChanneData buffer is detached</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function createGarbage() {
|
||||
var s = [];
|
||||
for (var i = 0; i < 10000000; ++i) {
|
||||
s.push(i);
|
||||
}
|
||||
var sum = 0;
|
||||
for (var i = 0; i < s.length; ++i) {
|
||||
sum += s[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
var worker = new Worker("audioBufferSourceNodeDetached_worker.js");
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var buffer = context.createBuffer(1, 10000000, context.sampleRate);
|
||||
var data = buffer.getChannelData(0);
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
data[i] = (i%100)/100 - 0.5;
|
||||
}
|
||||
|
||||
// Detach the buffer now
|
||||
var data = buffer.getChannelData(0).buffer;
|
||||
worker.postMessage(data, [data]);
|
||||
// Create garbage and GC to replace the buffer data with garbage
|
||||
SpecialPowers.gc();
|
||||
createGarbage();
|
||||
SpecialPowers.gc();
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.start();
|
||||
// This should play silence
|
||||
return source;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
36
dom/media/webaudio/test/test_audioBufferSourceNodeEnded.html
Normal file
36
dom/media/webaudio/test/test_audioBufferSourceNodeEnded.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test ended event on AudioBufferSourceNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var context = new AudioContext();
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
source.onended = function(e) {
|
||||
is(e.target, source, "Correct target for the ended event");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
source.start(0);
|
||||
source.buffer = buffer;
|
||||
source.connect(context.destination);
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 4096,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
// silence for half of the buffer, ones after that.
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 1024; i < 2048; i++) {
|
||||
buffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
// we start at the 1024 frames, we should only have ones.
|
||||
source.loop = true;
|
||||
source.loopStart = 1024 / context.sampleRate;
|
||||
source.loopEnd = 2048 / context.sampleRate;
|
||||
source.buffer = buffer;
|
||||
source.start(0, 1024 / context.sampleRate, 1024 / context.sampleRate);
|
||||
return source;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var expectedBuffer = context.createBuffer(1, 4096, context.sampleRate);
|
||||
for (var i = 0; i < 4096; i++) {
|
||||
expectedBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
45
dom/media/webaudio/test/test_audioBufferSourceNodeLoop.html
Normal file
45
dom/media/webaudio/test/test_audioBufferSourceNodeLoop.html
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode looping</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 2048 * 4,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
source.start(0);
|
||||
source.loop = true;
|
||||
return source;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var expectedBuffer = context.createBuffer(1, 2048 * 4, context.sampleRate);
|
||||
for (var i = 0; i < 4; ++i) {
|
||||
for (var j = 0; j < 2048; ++j) {
|
||||
expectedBuffer.getChannelData(0)[i * 2048 + j] = Math.sin(440 * 2 * Math.PI * j / context.sampleRate);
|
||||
}
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode looping</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 2048 * 4,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.start(0);
|
||||
source.loop = true;
|
||||
source.loopStart = buffer.duration * 0.25;
|
||||
source.loopEnd = buffer.duration * 0.75;
|
||||
return source;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var expectedBuffer = context.createBuffer(1, 2048 * 4, context.sampleRate);
|
||||
for (var i = 0; i < 1536; ++i) {
|
||||
expectedBuffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
for (var i = 0; i < 6; ++i) {
|
||||
for (var j = 512; j < 1536; ++j) {
|
||||
expectedBuffer.getChannelData(0)[1536 + i * 1024 + j - 512] = Math.sin(440 * 2 * Math.PI * j / context.sampleRate);
|
||||
}
|
||||
}
|
||||
for (var j = 7680; j < 2048 * 4; ++j) {
|
||||
expectedBuffer.getChannelData(0)[j] = Math.sin(440 * 2 * Math.PI * (j - 7168) / context.sampleRate);
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode looping</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
source.loop = true;
|
||||
source.loopStart = source.loopEnd = 1 / context.sampleRate;
|
||||
source.start(0);
|
||||
return source;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
return buffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode when start() is not called</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
var data = buffer.getChannelData(0);
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
data[i] = (i%100)/100 - 0.5;
|
||||
}
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
return source;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
source.start(0);
|
||||
source.buffer = null;
|
||||
is(source.buffer, null, "Try playing back a null buffer");
|
||||
return source;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the offset property on AudioBufferSourceNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var fuzz = 0.3;
|
||||
|
||||
if (navigator.platform.startsWith("Mac")) {
|
||||
// bug 895720
|
||||
fuzz = 0.6;
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var samplesFromSource = 0;
|
||||
var context = new AudioContext();
|
||||
var sp = context.createScriptProcessor(256);
|
||||
|
||||
sp.onaudioprocess = function(e) {
|
||||
samplesFromSource += e.inputBuffer.length;
|
||||
}
|
||||
|
||||
var buffer = context.createBuffer(1, context.sampleRate, context.sampleRate);
|
||||
for (var i = 0; i < context.sampleRate; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
source.onended = function(e) {
|
||||
// The timing at which the audioprocess and ended listeners are called can
|
||||
// change, hence the fuzzy equal here.
|
||||
var errorRatio = samplesFromSource / (0.5 * context.sampleRate);
|
||||
ok(errorRatio > (1.0 - fuzz) && errorRatio < (1.0 + fuzz),
|
||||
"Correct number of samples received (expected: " +
|
||||
(0.5 * context.sampleRate) + ", actual: " + samplesFromSource + ").");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
source.buffer = buffer;
|
||||
source.connect(sp);
|
||||
source.start(0, 0.5);
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode with passthrough</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
source.buffer = buffer;
|
||||
|
||||
var srcWrapped = SpecialPowers.wrap(source);
|
||||
ok("passThrough" in srcWrapped, "AudioBufferSourceNode should support the passThrough API");
|
||||
srcWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return source;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
|
||||
return [expectedBuffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
66
dom/media/webaudio/test/test_audioBufferSourceNodeRate.html
Normal file
66
dom/media/webaudio/test/test_audioBufferSourceNodeRate.html
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var rate = 44100;
|
||||
var off = new OfflineAudioContext(1, rate, rate);
|
||||
var off2 = new OfflineAudioContext(1, rate, rate);
|
||||
|
||||
var source = off.createBufferSource();
|
||||
var source2 = off2.createBufferSource();
|
||||
|
||||
// a buffer of a 440Hz at half the length. If we detune by -1200 or set the
|
||||
// playbackRate to 0.5, we should get 44100 samples back with a sine at 220Hz.
|
||||
var buf = off.createBuffer(1, rate / 2, rate);
|
||||
var bufarray = buf.getChannelData(0);
|
||||
for (var i = 0; i < bufarray.length; i++) {
|
||||
bufarray[i] = Math.sin(i * 440 * 2 * Math.PI / rate);
|
||||
}
|
||||
|
||||
source.buffer = buf;
|
||||
source.playbackRate.value = 0.5; // 50% slowdown
|
||||
source.connect(off.destination);
|
||||
source.start(0);
|
||||
|
||||
source2.buffer = buf;
|
||||
source2.detune.value = -1200; // one octave -> 50% slowdown
|
||||
source2.connect(off2.destination);
|
||||
source2.start(0);
|
||||
|
||||
var finished = 0;
|
||||
function finish() {
|
||||
finished++;
|
||||
if (finished == 2) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
off.startRendering().then((renderedPlaybackRate) => {
|
||||
// we don't care about comparing the value here, we just want to know whether
|
||||
// the second part is noisy.
|
||||
var rmsValue = rms(renderedPlaybackRate, 0, 22050);
|
||||
ok(rmsValue != 0, "Resampling happened (rms of the second part " + rmsValue + ")");
|
||||
|
||||
off2.startRendering().then((renderedDetune) => {
|
||||
var rmsValue = rms(renderedDetune, 0, 22050);
|
||||
ok(rmsValue != 0, "Resampling happened (rms of the second part " + rmsValue + ")");
|
||||
// The two buffers should be the same: detune of -1200 is a 50% slowdown
|
||||
compareBuffers(renderedPlaybackRate, renderedDetune);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
410
dom/media/webaudio/test/test_audioContextSuspendResumeClose.html
Normal file
410
dom/media/webaudio/test/test_audioContextSuspendResumeClose.html
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test suspend, resume and close method of the AudioContext</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function tryToCreateNodeOnClosedContext(ctx) {
|
||||
ok(ctx.state, "closed", "The context is in closed state");
|
||||
|
||||
[ { name: "createBufferSource" },
|
||||
{ name: "createMediaStreamDestination",
|
||||
onOfflineAudioContext: false},
|
||||
{ name: "createScriptProcessor" },
|
||||
{ name: "createStereoPanner" },
|
||||
{ name: "createAnalyser" },
|
||||
{ name: "createGain" },
|
||||
{ name: "createDelay" },
|
||||
{ name: "createBiquadFilter" },
|
||||
{ name: "createWaveShaper" },
|
||||
{ name: "createPanner" },
|
||||
{ name: "createConvolver" },
|
||||
{ name: "createChannelSplitter" },
|
||||
{ name: "createChannelMerger" },
|
||||
{ name: "createDynamicsCompressor" },
|
||||
{ name: "createOscillator" },
|
||||
{ name: "createMediaElementSource",
|
||||
args: [new Audio()],
|
||||
onOfflineAudioContext: false },
|
||||
{ name: "createMediaStreamSource",
|
||||
args: [new Audio().mozCaptureStream()],
|
||||
onOfflineAudioContext: false } ].forEach(function(e) {
|
||||
|
||||
if (e.onOfflineAudioContext == false &&
|
||||
ctx instanceof OfflineAudioContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
expectException(function() {
|
||||
ctx[e.name].apply(ctx, e.args);
|
||||
}, DOMException.INVALID_STATE_ERR);
|
||||
});
|
||||
}
|
||||
|
||||
function loadFile(url, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", url, true);
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.onload = function() {
|
||||
callback(xhr.response);
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
// createBuffer, createPeriodicWave and decodeAudioData should work on a context
|
||||
// that has `state` == "closed"
|
||||
function tryLegalOpeerationsOnClosedContext(ctx) {
|
||||
ok(ctx.state, "closed", "The context is in closed state");
|
||||
|
||||
[ { name: "createBuffer",
|
||||
args: [1, 44100, 44100] },
|
||||
{ name: "createPeriodicWave",
|
||||
args: [new Float32Array(10), new Float32Array(10)] }
|
||||
].forEach(function(e) {
|
||||
expectNoException(function() {
|
||||
ctx[e.name].apply(ctx, e.args);
|
||||
});
|
||||
});
|
||||
loadFile("ting-44.1k-1ch.ogg", function(buf) {
|
||||
ctx.decodeAudioData(buf).then(function(decodedBuf) {
|
||||
ok(true, "decodeAudioData on a closed context should work, it did.")
|
||||
todo(false, "0 " + (ctx instanceof OfflineAudioContext ? "Offline" : "Realtime"));
|
||||
finish();
|
||||
}).catch(function(e){
|
||||
ok(false, "decodeAudioData on a closed context should work, it did not");
|
||||
finish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Test that MediaStreams that are the output of a suspended AudioContext are
|
||||
// producing silence
|
||||
// ac1 produce a sine fed to a MediaStreamAudioDestinationNode
|
||||
// ac2 is connected to ac1 with a MediaStreamAudioSourceNode, and check that
|
||||
// there is silence when ac1 is suspended
|
||||
function testMultiContextOutput() {
|
||||
var ac1 = new AudioContext(),
|
||||
ac2 = new AudioContext();
|
||||
|
||||
ac1.onstatechange = function() {
|
||||
ac1.onstatechange = null;
|
||||
|
||||
var osc1 = ac1.createOscillator(),
|
||||
mediaStreamDestination1 = ac1.createMediaStreamDestination();
|
||||
|
||||
var mediaStreamAudioSourceNode2 =
|
||||
ac2.createMediaStreamSource(mediaStreamDestination1.stream),
|
||||
sp2 = ac2.createScriptProcessor(),
|
||||
silentBuffersInARow = 0;
|
||||
|
||||
|
||||
sp2.onaudioprocess = function(e) {
|
||||
ac1.suspend().then(function() {
|
||||
is(ac1.state, "suspended", "ac1 is suspended");
|
||||
sp2.onaudioprocess = checkSilence;
|
||||
});
|
||||
sp2.onaudioprocess = null;
|
||||
}
|
||||
|
||||
function checkSilence(e) {
|
||||
var input = e.inputBuffer.getChannelData(0);
|
||||
var silent = true;
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
if (input[i] != 0.0) {
|
||||
silent = false;
|
||||
}
|
||||
}
|
||||
|
||||
todo(false, "input buffer is " + (silent ? "silent" : "noisy"));
|
||||
|
||||
if (silent) {
|
||||
silentBuffersInARow++;
|
||||
if (silentBuffersInARow == 10) {
|
||||
ok(true,
|
||||
"MediaStreams produce silence when their input is blocked.");
|
||||
sp2.onaudioprocess = null;
|
||||
ac1.close();
|
||||
ac2.close();
|
||||
todo(false,"1");
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
is(silentBuffersInARow, 0,
|
||||
"No non silent buffer inbetween silent buffers.");
|
||||
}
|
||||
}
|
||||
|
||||
osc1.connect(mediaStreamDestination1);
|
||||
|
||||
mediaStreamAudioSourceNode2.connect(sp2);
|
||||
osc1.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Test that there is no buffering between contexts when connecting a running
|
||||
// AudioContext to a suspended AudioContext. Our ScriptProcessorNode does some
|
||||
// buffering internally, so we ensure this by using a very very low frequency
|
||||
// on a sine, and oberve that the phase has changed by a big enough margin.
|
||||
function testMultiContextInput() {
|
||||
var ac1 = new AudioContext(),
|
||||
ac2 = new AudioContext();
|
||||
|
||||
ac1.onstatechange = function() {
|
||||
ac1.onstatechange = null;
|
||||
|
||||
var osc1 = ac1.createOscillator(),
|
||||
mediaStreamDestination1 = ac1.createMediaStreamDestination(),
|
||||
sp1 = ac1.createScriptProcessor();
|
||||
|
||||
var mediaStreamAudioSourceNode2 =
|
||||
ac2.createMediaStreamSource(mediaStreamDestination1.stream),
|
||||
sp2 = ac2.createScriptProcessor(),
|
||||
eventReceived = 0;
|
||||
|
||||
|
||||
osc1.frequency.value = 0.0001;
|
||||
|
||||
function checkDiscontinuity(e) {
|
||||
var inputBuffer = e.inputBuffer.getChannelData(0);
|
||||
if (eventReceived++ == 3) {
|
||||
var delta = Math.abs(inputBuffer[1] - sp2.value),
|
||||
theoreticalIncrement = 2048 * 3 * Math.PI * 2 * osc1.frequency.value / ac1.sampleRate;
|
||||
ok(delta >= theoreticalIncrement,
|
||||
"Buffering did not occur when the context was suspended (delta:" + delta + " increment: " + theoreticalIncrement+")");
|
||||
ac1.close();
|
||||
ac2.close();
|
||||
sp1.onaudioprocess = null;
|
||||
sp2.onaudioprocess = null;
|
||||
todo(false, "2");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
sp2.onaudioprocess = function(e) {
|
||||
var inputBuffer = e.inputBuffer.getChannelData(0);
|
||||
sp2.value = inputBuffer[inputBuffer.length - 1];
|
||||
ac2.suspend().then(function() {
|
||||
ac2.resume().then(function() {
|
||||
sp2.onaudioprocess = checkDiscontinuity;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
osc1.connect(mediaStreamDestination1);
|
||||
osc1.connect(sp1);
|
||||
|
||||
mediaStreamAudioSourceNode2.connect(sp2);
|
||||
osc1.start();
|
||||
}
|
||||
}
|
||||
|
||||
// Test that ScriptProcessorNode's onaudioprocess don't get called while the
|
||||
// context is suspended/closed. It is possible that we get the handler called
|
||||
// exactly once after suspend, because the event has already been sent to the
|
||||
// event loop.
|
||||
function testScriptProcessNodeSuspended() {
|
||||
var ac = new AudioContext();
|
||||
var sp = ac.createScriptProcessor();
|
||||
var remainingIterations = 30;
|
||||
var afterResume = false;
|
||||
ac.onstatechange = function() {
|
||||
ac.onstatechange = null;
|
||||
sp.onaudioprocess = function() {
|
||||
ok(ac.state == "running", "If onaudioprocess is called, the context" +
|
||||
" must be running (was " + ac.state + ", remainingIterations:" + remainingIterations +")");
|
||||
remainingIterations--;
|
||||
if (!afterResume) {
|
||||
if (remainingIterations == 0) {
|
||||
ac.suspend().then(function() {
|
||||
ac.resume().then(function() {
|
||||
remainingIterations = 30;
|
||||
afterResume = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
sp.onaudioprocess = null;
|
||||
todo(false,"3");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
sp.connect(ac.destination);
|
||||
}
|
||||
|
||||
// Take an AudioContext, make sure it switches to running when the audio starts
|
||||
// flowing, and then, call suspend, resume and close on it, tracking its state.
|
||||
function testAudioContext() {
|
||||
var ac = new AudioContext();
|
||||
is(ac.state, "suspended", "AudioContext should start in suspended state.");
|
||||
var stateTracker = {
|
||||
previous: ac.state,
|
||||
// no promise for the initial suspended -> running
|
||||
initial: { handler: false },
|
||||
suspend: { promise: false, handler: false },
|
||||
resume: { promise: false, handler: false },
|
||||
close: { promise: false, handler: false }
|
||||
};
|
||||
|
||||
function initialSuspendToRunning() {
|
||||
ok(stateTracker.previous == "suspended" &&
|
||||
ac.state == "running",
|
||||
"AudioContext should switch to \"running\" when the audio hardware is" +
|
||||
" ready.");
|
||||
|
||||
stateTracker.previous = ac.state;
|
||||
ac.onstatechange = afterSuspend;
|
||||
stateTracker.initial.handler = true;
|
||||
|
||||
ac.suspend().then(function() {
|
||||
ok(!stateTracker.suspend.promise && !stateTracker.suspend.handler,
|
||||
"Promise should be resolved before the callback, and only once.")
|
||||
stateTracker.suspend.promise = true;
|
||||
});
|
||||
}
|
||||
|
||||
function afterSuspend() {
|
||||
ok(stateTracker.previous == "running" &&
|
||||
ac.state == "suspended",
|
||||
"AudioContext should switch to \"suspend\" when the audio stream is" +
|
||||
"suspended.");
|
||||
ok(stateTracker.suspend.promise && !stateTracker.suspend.handler,
|
||||
"Handler should be called after the callback, and only once");
|
||||
|
||||
stateTracker.suspend.handler = true;
|
||||
stateTracker.previous = ac.state;
|
||||
ac.onstatechange = afterResume;
|
||||
|
||||
ac.resume().then(function() {
|
||||
ok(!stateTracker.resume.promise && !stateTracker.resume.handler,
|
||||
"Promise should be called before the callback, and only once");
|
||||
stateTracker.resume.promise = true;
|
||||
});
|
||||
}
|
||||
|
||||
function afterResume() {
|
||||
ok(stateTracker.previous == "suspended" &&
|
||||
ac.state == "running",
|
||||
"AudioContext should switch to \"running\" when the audio stream resumes.");
|
||||
|
||||
ok(stateTracker.resume.promise && !stateTracker.resume.handler,
|
||||
"Handler should be called after the callback, and only once");
|
||||
|
||||
stateTracker.resume.handler = true;
|
||||
stateTracker.previous = ac.state;
|
||||
ac.onstatechange = afterClose;
|
||||
|
||||
ac.close().then(function() {
|
||||
ok(!stateTracker.close.promise && !stateTracker.close.handler,
|
||||
"Promise should be called before the callback, and only once");
|
||||
stateTracker.close.promise = true;
|
||||
tryToCreateNodeOnClosedContext(ac);
|
||||
tryLegalOpeerationsOnClosedContext(ac);
|
||||
});
|
||||
}
|
||||
|
||||
function afterClose() {
|
||||
ok(stateTracker.previous == "running" &&
|
||||
ac.state == "closed",
|
||||
"AudioContext should switch to \"closed\" when the audio stream is" +
|
||||
" closed.");
|
||||
ok(stateTracker.close.promise && !stateTracker.close.handler,
|
||||
"Handler should be called after the callback, and only once");
|
||||
}
|
||||
|
||||
ac.onstatechange = initialSuspendToRunning;
|
||||
}
|
||||
|
||||
function testOfflineAudioContext() {
|
||||
var o = new OfflineAudioContext(1, 44100, 44100);
|
||||
is(o.state, "suspended", "OfflineAudioContext should start in suspended state.");
|
||||
|
||||
expectRejectedPromise(o, "suspend", "NotSupportedError");
|
||||
expectRejectedPromise(o, "resume", "NotSupportedError");
|
||||
expectRejectedPromise(o, "close", "NotSupportedError");
|
||||
|
||||
var previousState = o.state,
|
||||
finishedRendering = false;
|
||||
function beforeStartRendering() {
|
||||
ok(previousState == "suspended" && o.state == "running", "onstatechanged" +
|
||||
"handler is called on state changed, and the new state is running");
|
||||
previousState = o.state;
|
||||
o.onstatechange = onRenderingFinished;
|
||||
}
|
||||
|
||||
function onRenderingFinished() {
|
||||
ok(previousState == "running" && o.state == "closed",
|
||||
"onstatechanged handler is called when rendering finishes, " +
|
||||
"and the new state is closed");
|
||||
ok(finishedRendering, "The Promise that is resolved when the rendering is" +
|
||||
"done should be resolved earlier than the state change.");
|
||||
previousState = o.state;
|
||||
o.onstatechange = afterRenderingFinished;
|
||||
|
||||
tryToCreateNodeOnClosedContext(o);
|
||||
tryLegalOpeerationsOnClosedContext(o);
|
||||
}
|
||||
|
||||
function afterRenderingFinished() {
|
||||
ok(false, "There should be no transition out of the closed state.");
|
||||
}
|
||||
|
||||
o.onstatechange = beforeStartRendering;
|
||||
|
||||
o.startRendering().then(function(buffer) {
|
||||
finishedRendering = true;
|
||||
});
|
||||
}
|
||||
|
||||
function testSuspendResumeEventLoop() {
|
||||
var ac = new AudioContext();
|
||||
var source = ac.createBufferSource();
|
||||
source.buffer = ac.createBuffer(1, 44100, 44100);
|
||||
source.onended = function() {
|
||||
ok(true, "The AudioContext did resume.");
|
||||
finish();
|
||||
}
|
||||
ac.onstatechange = function() {
|
||||
ac.onstatechange = null;
|
||||
|
||||
ok(ac.state == "running", "initial state is running");
|
||||
ac.suspend();
|
||||
source.start();
|
||||
ac.resume();
|
||||
}
|
||||
}
|
||||
|
||||
var remaining = 0;
|
||||
function finish() {
|
||||
remaining--;
|
||||
if (remaining == 0) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var tests = [
|
||||
testAudioContext,
|
||||
testOfflineAudioContext,
|
||||
testScriptProcessNodeSuspended,
|
||||
testMultiContextOutput,
|
||||
testMultiContextInput,
|
||||
testSuspendResumeEventLoop
|
||||
];
|
||||
remaining = tests.length;
|
||||
tests.forEach(function(f) { f() });
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
26
dom/media/webaudio/test/test_audioDestinationNode.html
Normal file
26
dom/media/webaudio/test/test_audioDestinationNode.html
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioDestinationNode as EventTarget</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var ac = new AudioContext()
|
||||
ac.destination.addEventListener("foo", function() {
|
||||
ok(true, "Event received!");
|
||||
SimpleTest.finish();
|
||||
}, false);
|
||||
ac.destination.dispatchEvent(new CustomEvent("foo"));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
77
dom/media/webaudio/test/test_audioParamChaining.html
Normal file
77
dom/media/webaudio/test/test_audioParamChaining.html
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test whether we can create an AudioContext interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish()
|
||||
|
||||
function frameToTime(frame, rate)
|
||||
{
|
||||
return frame / rate;
|
||||
}
|
||||
|
||||
const RATE = 44100;
|
||||
|
||||
var oc = new OfflineAudioContext(1, 44100, RATE);
|
||||
// This allows us to have a source that is simply a DC offset.
|
||||
var source = oc.createBufferSource();
|
||||
var buf = oc.createBuffer(1, 1, RATE);
|
||||
buf.getChannelData(0)[0] = 1;
|
||||
source.loop = true;
|
||||
source.buffer = buf;
|
||||
|
||||
source.start(0);
|
||||
|
||||
var gain = oc.createGain();
|
||||
|
||||
source.connect(gain).connect(oc.destination);
|
||||
|
||||
var gain2 = oc.createGain();
|
||||
var rv2 = gain2.gain.linearRampToValueAtTime(0.1, 0.5);
|
||||
ok(rv2 instanceof AudioParam, "linearRampToValueAtTime returns an AudioParam.");
|
||||
ok(rv2 == gain2.gain, "linearRampToValueAtTime returns the right AudioParam.");
|
||||
|
||||
rv2 = gain2.gain.exponentialRampToValueAtTime(0.01, 1.0);
|
||||
ok(rv2 instanceof AudioParam,
|
||||
"exponentialRampToValueAtTime returns an AudioParam.");
|
||||
ok(rv2 == gain2.gain,
|
||||
"exponentialRampToValueAtTime returns the right AudioParam.");
|
||||
|
||||
rv2 = gain2.gain.setTargetAtTime(1.0, 2.0, 0.1);
|
||||
ok(rv2 instanceof AudioParam, "setTargetAtTime returns an AudioParam.");
|
||||
ok(rv2 == gain2.gain, "setTargetAtTime returns the right AudioParam.");
|
||||
|
||||
var array = new Float32Array(10);
|
||||
rv2 = gain2.gain.setValueCurveAtTime(array, 10, 11);
|
||||
ok(rv2 instanceof AudioParam, "setValueCurveAtTime returns an AudioParam.");
|
||||
ok(rv2 == gain2.gain, "setValueCurveAtTime returns the right AudioParam.");
|
||||
|
||||
// We chain three automation methods, making a gain step.
|
||||
var rv = gain.gain.setValueAtTime(0, frameToTime(0, RATE))
|
||||
.setValueAtTime(0.5, frameToTime(22000, RATE))
|
||||
.setValueAtTime(1, frameToTime(44000, RATE));
|
||||
|
||||
ok(rv instanceof AudioParam, "setValueAtTime returns an AudioParam.");
|
||||
ok(rv == gain.gain, "setValueAtTime returns the right AudioParam.");
|
||||
|
||||
oc.startRendering().then(function(rendered) {
|
||||
console.log(rendered.getChannelData(0));
|
||||
is(rendered.getChannelData(0)[0], 0,
|
||||
"The value of the first step is correct.");
|
||||
is(rendered.getChannelData(0)[22050], 0.5,
|
||||
"The value of the second step is correct");
|
||||
is(rendered.getChannelData(0)[44099], 1,
|
||||
"The value of the third step is correct.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
54
dom/media/webaudio/test/test_audioParamExponentialRamp.html
Normal file
54
dom/media/webaudio/test/test_audioParamExponentialRamp.html
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.exponentialRampToValue</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var V0 = 0.1;
|
||||
var V1 = 0.9;
|
||||
var T0 = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueAtTime(V0, 0);
|
||||
gain.gain.exponentialRampToValueAtTime(V1, 2048/context.sampleRate);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var T1 = 2048 / context.sampleRate;
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = V0 * Math.pow(V1 / V0, (t - T0) / (T1 - T0));
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
61
dom/media/webaudio/test/test_audioParamGain.html
Normal file
61
dom/media/webaudio/test/test_audioParamGain.html
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam with pre-gain </title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var ctx = new AudioContext();
|
||||
var source = ctx.createOscillator();
|
||||
var lfo = ctx.createOscillator();
|
||||
var lfoIntensity = ctx.createGain();
|
||||
var effect = ctx.createGain();
|
||||
var sp = ctx.createScriptProcessor(2048, 1);
|
||||
|
||||
source.frequency.value = 440;
|
||||
lfo.frequency.value = 2;
|
||||
// Very low gain, so the LFO should have very little influence
|
||||
// on the source, its RMS value should be close to the nominal value
|
||||
// for a sine wave.
|
||||
lfoIntensity.gain.value = 0.0001;
|
||||
|
||||
lfo.connect(lfoIntensity);
|
||||
lfoIntensity.connect(effect.gain);
|
||||
source.connect(effect);
|
||||
effect.connect(sp);
|
||||
|
||||
sp.onaudioprocess = function(e) {
|
||||
var buffer = e.inputBuffer.getChannelData(0);
|
||||
var rms = 0;
|
||||
for (var i = 0; i < buffer.length; i++) {
|
||||
rms += buffer[i] * buffer[i];
|
||||
}
|
||||
|
||||
rms /= buffer.length;
|
||||
rms = Math.sqrt(rms);
|
||||
|
||||
// 1 / Math.sqrt(2) is the theoretical RMS value for a sine wave.
|
||||
ok(fuzzyCompare(rms, 1 / Math.sqrt(2)),
|
||||
"Gain correctly applied to the AudioParam.");
|
||||
|
||||
ctx = null;
|
||||
sp.onaudioprocess = null;
|
||||
lfo.stop(0);
|
||||
source.stop(0);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
lfo.start(0);
|
||||
source.start(0);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
54
dom/media/webaudio/test/test_audioParamLinearRamp.html
Normal file
54
dom/media/webaudio/test/test_audioParamLinearRamp.html
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.linearRampToValue</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var V0 = 0.1;
|
||||
var V1 = 0.9;
|
||||
var T0 = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueAtTime(V0, 0);
|
||||
gain.gain.linearRampToValueAtTime(V1, 2048/context.sampleRate);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var T1 = 2048 / context.sampleRate;
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = V0 + (V1 - V0) * ((t - T0) / (T1 - T0));
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
54
dom/media/webaudio/test/test_audioParamSetCurveAtTime.html
Normal file
54
dom/media/webaudio/test/test_audioParamSetCurveAtTime.html
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.linearRampToValue</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var T0 = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createConstantSource();
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueCurveAtTime(this.curve, T0, this.duration);
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.duration = 1024 / context.sampleRate;
|
||||
this.curve = new Float32Array([1.0, 0.5, 0.75, 0.25]);
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
var data = expectedBuffer.getChannelData(0);
|
||||
var step = 1024 / 3;
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
if (i < step) {
|
||||
data[i] = 1.0 - 0.5*i/step;
|
||||
} else if (i < 2*step) {
|
||||
data[i] = 0.5 + 0.25*(i - step)/step;
|
||||
} else if (i < 3*step) {
|
||||
data[i] = 0.75 - 0.5*(i - 2*step)/step;
|
||||
} else {
|
||||
data[i] = 0.25;
|
||||
}
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.setValueCurveAtTime twice</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
|
||||
function linearInterpolate(t0, v0, t1, v1, t)
|
||||
{
|
||||
return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
|
||||
}
|
||||
|
||||
var T0 = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var curve2 = new Float32Array(100);
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
curve2[i] = Math.sin(220 * 6 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createConstantSource();
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueCurveAtTime(curve2, T0, this.duration/2);
|
||||
//Set a different curve from the first one
|
||||
gain.gain.setValueCurveAtTime(this.curve, T0, this.duration);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.duration = 1024 / context.sampleRate;
|
||||
this.curve = new Float32Array(100);
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
this.curve[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
step = 1024.0/99.0;
|
||||
var current = Math.floor(i / step);
|
||||
var next = current + 1;
|
||||
if (next < this.curve.length) {
|
||||
expectedBuffer.getChannelData(0)[i] = linearInterpolate(current*step, this.curve[current], next*step, this.curve[next], i);
|
||||
} else {
|
||||
expectedBuffer.getChannelData(0)[i] = this.curve[99];
|
||||
}
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.linearRampToValue</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var T0 = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueCurveAtTime(this.curve, this.T0, 0);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.T0 = 1024 / context.sampleRate;
|
||||
this.curve = new Float32Array(100);
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
this.curve[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 1024; ++i) {
|
||||
expectedBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
for (var i = 1024; i < 2048; ++i) {
|
||||
expectedBuffer.getChannelData(0)[i] = this.curve[99];
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
55
dom/media/webaudio/test/test_audioParamSetTargetAtTime.html
Normal file
55
dom/media/webaudio/test/test_audioParamSetTargetAtTime.html
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.setTargetAtTime</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var V0 = 0.9;
|
||||
var V1 = 0.1;
|
||||
var T0 = 0;
|
||||
var TimeConstant = 10;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.value = V0;
|
||||
gain.gain.setTargetAtTime(V1, T0, TimeConstant);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var T1 = 2048 / context.sampleRate;
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = V1 + (V0 - V1) * Math.exp(-(t - T0) / TimeConstant);
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.setTargetAtTime with zero time constant</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var V0 = 0.9;
|
||||
var V1 = 0.1;
|
||||
var T0 = 0;
|
||||
var TimeConstant = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.value = V0;
|
||||
gain.gain.setTargetAtTime(V1, T0, TimeConstant);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var T1 = 2048 / context.sampleRate;
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = V1;
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
52
dom/media/webaudio/test/test_audioParamSetValueAtTime.html
Normal file
52
dom/media/webaudio/test/test_audioParamSetValueAtTime.html
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.linearRampToValue</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var V0 = 0.1;
|
||||
var V1 = 0.9;
|
||||
var T0 = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.value = 0;
|
||||
gain.gain.setValueAtTime(V0, 1024/context.sampleRate);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 1024; i < 2048; ++i) {
|
||||
expectedBuffer.getChannelData(0)[i] = 0.1;
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam timeline events scheduled after the destination stream has started playback</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.requestFlakyTimeout("This test needs to wait until the AudioDestinationNode's stream's timer starts.");
|
||||
|
||||
var gTest = {
|
||||
length: 16384,
|
||||
numberOfChannels: 1,
|
||||
createGraphAsync: function(context, callback) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
source.start(context.currentTime);
|
||||
source.stop(context.currentTime + sourceBuffer.duration);
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueAtTime(0, context.currentTime);
|
||||
gain.gain.setTargetAtTime(0, context.currentTime + sourceBuffer.duration, 1);
|
||||
source.connect(gain);
|
||||
|
||||
callback(gain);
|
||||
}, 100);
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
48
dom/media/webaudio/test/test_badConnect.html
Normal file
48
dom/media/webaudio/test/test_badConnect.html
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test whether we can create an AudioContext interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var context1 = new AudioContext();
|
||||
var context2 = new AudioContext();
|
||||
|
||||
var destination1 = context1.destination;
|
||||
var destination2 = context2.destination;
|
||||
|
||||
isnot(destination1, destination2, "Destination nodes should not be the same");
|
||||
isnot(destination1.context, destination2.context, "Destination nodes should not have the same context");
|
||||
|
||||
var source1 = context1.createBufferSource();
|
||||
|
||||
expectException(function() {
|
||||
source1.connect(destination1, 1);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
source1.connect(destination1, 0, 1);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
source1.connect(destination2);
|
||||
}, DOMException.SYNTAX_ERR);
|
||||
|
||||
source1.connect(destination1);
|
||||
|
||||
expectException(function() {
|
||||
source1.disconnect(1);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
86
dom/media/webaudio/test/test_biquadFilterNode.html
Normal file
86
dom/media/webaudio/test/test_biquadFilterNode.html
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function near(a, b, msg) {
|
||||
ok(Math.abs(a - b) < 1e-3, msg);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var context = new AudioContext();
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var destination = context.destination;
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var filter = context.createBiquadFilter();
|
||||
|
||||
source.buffer = buffer;
|
||||
|
||||
source.connect(filter);
|
||||
filter.connect(destination);
|
||||
|
||||
// Verify default values
|
||||
is(filter.type, "lowpass", "Correct default value for type");
|
||||
near(filter.frequency.defaultValue, 350, "Correct default value for filter frequency");
|
||||
near(filter.detune.defaultValue, 0, "Correct default value for filter detune");
|
||||
near(filter.Q.defaultValue, 1, "Correct default value for filter Q");
|
||||
near(filter.gain.defaultValue, 0, "Correct default value for filter gain");
|
||||
is(filter.channelCount, 2, "Biquad filter node has 2 input channels by default");
|
||||
is(filter.channelCountMode, "max", "Correct channelCountMode for the biquad filter node");
|
||||
is(filter.channelInterpretation, "speakers", "Correct channelCountInterpretation for the biquad filter node");
|
||||
|
||||
// Make sure that we can set all of the valid type values
|
||||
var types = [
|
||||
"lowpass",
|
||||
"highpass",
|
||||
"bandpass",
|
||||
"lowshelf",
|
||||
"highshelf",
|
||||
"peaking",
|
||||
"notch",
|
||||
"allpass",
|
||||
];
|
||||
for (var i = 0; i < types.length; ++i) {
|
||||
filter.type = types[i];
|
||||
}
|
||||
|
||||
// Make sure getFrequencyResponse handles invalid frequencies properly
|
||||
var frequencies = new Float32Array([-1.0, context.sampleRate*0.5 - 1.0, context.sampleRate]);
|
||||
var magResults = new Float32Array(3);
|
||||
var phaseResults = new Float32Array(3);
|
||||
filter.getFrequencyResponse(frequencies, magResults, phaseResults);
|
||||
ok(isNaN(magResults[0]), "Invalid input frequency should give NaN magnitude response");
|
||||
ok(!isNaN(magResults[1]), "Valid input frequency should not give NaN magnitude response");
|
||||
ok(isNaN(magResults[2]), "Invalid input frequency should give NaN magnitude response");
|
||||
ok(isNaN(phaseResults[0]), "Invalid input frquency should give NaN phase response");
|
||||
ok(!isNaN(phaseResults[1]), "Valid input frquency should not give NaN phase response");
|
||||
ok(isNaN(phaseResults[2]), "Invalid input frquency should give NaN phase response");
|
||||
|
||||
source.start(0);
|
||||
SimpleTest.executeSoon(function() {
|
||||
source.stop(0);
|
||||
source.disconnect();
|
||||
filter.disconnect();
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode with passthrough</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var filter = context.createBiquadFilter();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(filter);
|
||||
|
||||
var filterWrapped = SpecialPowers.wrap(filter);
|
||||
ok("passThrough" in filterWrapped, "BiquadFilterNode should support the passThrough API");
|
||||
filterWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return filter;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
61
dom/media/webaudio/test/test_biquadFilterNodeWithGain.html
Normal file
61
dom/media/webaudio/test/test_biquadFilterNodeWithGain.html
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode after a GainNode and tail - Bugs 924286 and 924288</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
const signalLength = 2048;
|
||||
|
||||
var gTest = {
|
||||
length: signalLength,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
// Two oscillators scheduled sequentially
|
||||
var signalDuration = signalLength / context.sampleRate;
|
||||
var osc1 = context.createOscillator();
|
||||
osc1.type = "square";
|
||||
osc1.start(0);
|
||||
osc1.stop(signalDuration / 2);
|
||||
var osc2 = context.createOscillator();
|
||||
osc2.start(signalDuration / 2);
|
||||
osc2.stop(signalDuration);
|
||||
|
||||
// Comparing a biquad on each source with one on both sources checks that
|
||||
// the biquad on the first source doesn't shut down early.
|
||||
var biquad1 = context.createBiquadFilter();
|
||||
osc1.connect(biquad1);
|
||||
var biquad2 = context.createBiquadFilter();
|
||||
osc2.connect(biquad2);
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.value = -1;
|
||||
osc1.connect(gain);
|
||||
osc2.connect(gain);
|
||||
|
||||
var biquadWithGain = context.createBiquadFilter();
|
||||
gain.connect(biquadWithGain);
|
||||
|
||||
// The output of biquadWithGain should be the inverse of the sum of the
|
||||
// outputs of biquad1 and biquad2, so blend them together and expect
|
||||
// silence.
|
||||
var blend = context.createGain();
|
||||
biquad1.connect(blend);
|
||||
biquad2.connect(blend);
|
||||
biquadWithGain.connect(blend);
|
||||
|
||||
return blend;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
74
dom/media/webaudio/test/test_bug1027864.html
Normal file
74
dom/media/webaudio/test/test_bug1027864.html
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test bug 1027864</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function observer(subject, topic, data) {
|
||||
var id = parseInt(data);
|
||||
var index = ids.indexOf(id);
|
||||
if (index != -1) {
|
||||
ok(true, "Dropping id " + id + " at index " + index);
|
||||
ids.splice(index, 1);
|
||||
if (ids.length == 0) {
|
||||
SimpleTest.executeSoon(function() {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function id(node) {
|
||||
return SpecialPowers.getPrivilegedProps(node, "id");
|
||||
}
|
||||
|
||||
SpecialPowers.addObserver(observer, "webaudio-node-demise", false);
|
||||
|
||||
SimpleTest.registerCleanupFunction(function() {
|
||||
SpecialPowers.removeObserver(observer, "webaudio-node-demise");
|
||||
});
|
||||
|
||||
var ac = new AudioContext();
|
||||
var ids;
|
||||
|
||||
(function() {
|
||||
var delay = ac.createDelay();
|
||||
delay.delayTime.value = 0.03;
|
||||
|
||||
var gain = ac.createGain();
|
||||
gain.gain.value = 0.6;
|
||||
|
||||
delay.connect(gain);
|
||||
gain.connect(delay);
|
||||
|
||||
gain.connect(ac.destination);
|
||||
|
||||
var source = ac.createOscillator();
|
||||
|
||||
source.connect(gain);
|
||||
source.start(ac.currentTime);
|
||||
source.stop(ac.currentTime + 0.1);
|
||||
|
||||
ids = [ id(delay), id(gain), id(source) ];
|
||||
})();
|
||||
|
||||
setInterval(function() {
|
||||
forceCC();
|
||||
}, 200);
|
||||
|
||||
function forceCC() {
|
||||
SpecialPowers.DOMWindowUtils.cycleCollect();
|
||||
SpecialPowers.DOMWindowUtils.garbageCollect();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
35
dom/media/webaudio/test/test_bug1056032.html
Normal file
35
dom/media/webaudio/test/test_bug1056032.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<meta charset=utf-8>
|
||||
<head>
|
||||
<title>Test that we can decode an mp3 (bug 1056032)</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var filename = "small-shot.mp3";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", filename);
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.onload = function() {
|
||||
var context = new AudioContext();
|
||||
context.decodeAudioData(xhr.response, function(b) {
|
||||
ok(true, "We can decode an mp3 using decodeAudioData");
|
||||
SimpleTest.finish();
|
||||
}, function() {
|
||||
ok(false, "We should be able to decode an mp3 using decodeAudioData but couldn't");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
};
|
||||
xhr.send(null);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
54
dom/media/webaudio/test/test_bug1113634.html
Normal file
54
dom/media/webaudio/test/test_bug1113634.html
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.setTargetAtTime where the target time is the same as the time of a previous event</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var V0 = 0.9;
|
||||
var V1 = 0.1;
|
||||
var T0 = 0;
|
||||
var TimeConstant = 0.1;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueAtTime(V0, T0);
|
||||
gain.gain.setTargetAtTime(V1, T0, TimeConstant);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = V1 + (V0 - V1) * Math.exp(-(t - T0) / TimeConstant);
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
46
dom/media/webaudio/test/test_bug1118372.html
Normal file
46
dom/media/webaudio/test/test_bug1118372.html
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test WaveShaperNode with no curve</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
var context = new OfflineAudioContext(1, 2048, 44100);
|
||||
|
||||
var osc=context.createOscillator();
|
||||
var gain=context.createGain();
|
||||
var shaper=context.createWaveShaper();
|
||||
gain.gain.value=0.1;
|
||||
shaper.curve=new Float32Array([-0.5,-0.5,1,1]);
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(shaper);
|
||||
shaper.connect(context.destination);
|
||||
osc.start(0);
|
||||
|
||||
context.startRendering().then(function(buffer) {
|
||||
var samples = buffer.getChannelData(0);
|
||||
// the signal should be scaled
|
||||
var failures = 0;
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
if (samples[i] > 0.5) {
|
||||
failures = failures + 1;
|
||||
}
|
||||
}
|
||||
ok(failures == 0, "signal should have been rescaled by gain: found " + failures + " points too loud.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
41
dom/media/webaudio/test/test_bug1255618.html
Normal file
41
dom/media/webaudio/test/test_bug1255618.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test sync XHR does not crash unlinked AudioContext</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const filename = "test_bug1255618.html";
|
||||
|
||||
function collect_and_fetch() {
|
||||
SpecialPowers.forceGC();
|
||||
SpecialPowers.forceCC();
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", filename, false);
|
||||
var ended = false;
|
||||
xhr.onloadend = function() { ended = true; }
|
||||
// Sync XHR will suspend timeouts, which involves any AudioContexts still
|
||||
// registered with the window.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1255618#c0
|
||||
xhr.send(null);
|
||||
|
||||
ok(ended, "No crash during fetch");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var ac = new AudioContext();
|
||||
|
||||
ac.onstatechange = function () {
|
||||
ac.onstatechange = null;
|
||||
is(ac.state, "running", "statechange to running");
|
||||
ac = null;
|
||||
SimpleTest.executeSoon(collect_and_fetch);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
46
dom/media/webaudio/test/test_bug1267579.html
Normal file
46
dom/media/webaudio/test/test_bug1267579.html
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test that PeriodicWave handles fundamental fequency of zero</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
// This is the smallest value that the test framework will accept
|
||||
const testLength = 256;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
runTest();
|
||||
});
|
||||
|
||||
var gTest = {
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var osc = context.createOscillator();
|
||||
osc.setPeriodicWave(context.
|
||||
createPeriodicWave(new Float32Array([0.0, 1.0]),
|
||||
new Float32Array(2)));
|
||||
osc.frequency.value = 0.0;
|
||||
osc.start();
|
||||
return osc;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var buffer = context.createBuffer(1, testLength, context.sampleRate);
|
||||
|
||||
for (var i = 0; i < buffer.length; ++i) {
|
||||
buffer.getChannelData(0)[i] = 1.0;
|
||||
}
|
||||
return buffer;
|
||||
},
|
||||
};
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
22
dom/media/webaudio/test/test_bug808374.html
Normal file
22
dom/media/webaudio/test/test_bug808374.html
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Crashtest for bug 808374</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
try {
|
||||
var ctx = new AudioContext();
|
||||
ctx.createBuffer(0, 1, ctx.sampleRate);
|
||||
} catch (e) {
|
||||
ok(true, "The test should not crash during CC");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
22
dom/media/webaudio/test/test_bug827541.html
Normal file
22
dom/media/webaudio/test/test_bug827541.html
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tell the cycle collector about the audio contexts owned by nsGlobalWindow</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var iframe = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
|
||||
document.body.appendChild(iframe);
|
||||
var frameWin = iframe.contentWindow;
|
||||
new frameWin.AudioContext();
|
||||
document.body.removeChild(iframe);
|
||||
new frameWin.AudioContext();
|
||||
|
||||
ok(true, "This test should not leak");
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
18
dom/media/webaudio/test/test_bug839753.html
Normal file
18
dom/media/webaudio/test/test_bug839753.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Crashtest for bug 839753</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
(new AudioContext()).destination.expando = null;
|
||||
ok(true, "The test should not trigger wrapper cache assertions");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
18
dom/media/webaudio/test/test_bug845960.html
Normal file
18
dom/media/webaudio/test/test_bug845960.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Crashtest for bug 845960</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
(new AudioContext()).decodeAudioData(new ArrayBuffer(0), function() {});
|
||||
ok(true, "Should not crash when the optional failure callback is not specified");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
26
dom/media/webaudio/test/test_bug856771.html
Normal file
26
dom/media/webaudio/test/test_bug856771.html
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 856771</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var context = new AudioContext();
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.connect(context.destination);
|
||||
ok(true, "Nothing should leak");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
18
dom/media/webaudio/test/test_bug866570.html
Normal file
18
dom/media/webaudio/test/test_bug866570.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Crashtest for bug 859600</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
(new AudioContext()).foo = null;
|
||||
ok(true, "The test should not fatally assert");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
36
dom/media/webaudio/test/test_bug866737.html
Normal file
36
dom/media/webaudio/test/test_bug866737.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 866737</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var context = new AudioContext();
|
||||
|
||||
(function() {
|
||||
var d = context.createDelay();
|
||||
var panner = context.createPanner();
|
||||
d.connect(panner);
|
||||
var gain = context.createGain();
|
||||
panner.connect(gain);
|
||||
gain.connect(context.destination);
|
||||
gain.disconnect(0);
|
||||
})();
|
||||
|
||||
SpecialPowers.forceGC();
|
||||
SpecialPowers.forceCC();
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.connect(context.destination);
|
||||
gain.disconnect(0);
|
||||
|
||||
ok(true, "No crashes should happen!");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
43
dom/media/webaudio/test/test_bug867089.html
Normal file
43
dom/media/webaudio/test/test_bug867089.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Crashtest for bug 867089</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var ctx = new AudioContext();
|
||||
|
||||
// Test invalid playbackRate values for AudioBufferSourceNode.
|
||||
var source = ctx.createBufferSource();
|
||||
var buffer = ctx.createBuffer(2, 2048, 8000);
|
||||
source.buffer = buffer;
|
||||
source.playbackRate.value = 0.0;
|
||||
source.connect(ctx.destination);
|
||||
source.start(0);
|
||||
|
||||
var source2 = ctx.createBufferSource();
|
||||
source2.buffer = buffer;
|
||||
source2.playbackRate.value = -1.0;
|
||||
source2.connect(ctx.destination);
|
||||
source2.start(0);
|
||||
|
||||
var source3 = ctx.createBufferSource();
|
||||
source3.buffer = buffer;
|
||||
source3.playbackRate.value = 3000000.0;
|
||||
source3.connect(ctx.destination);
|
||||
source3.start(0);
|
||||
ok(true, "We did not crash.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
34
dom/media/webaudio/test/test_bug867104.html
Normal file
34
dom/media/webaudio/test/test_bug867104.html
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Crashtest for bug 867104</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var ctx = new AudioContext();
|
||||
var source = ctx.createBufferSource();
|
||||
var b0 = ctx.createBuffer(32,798,22050);
|
||||
var b1 = ctx.createBuffer(32,28,22050);
|
||||
var sp = ctx.createScriptProcessor(0, 2, 0);
|
||||
source.buffer = b0;
|
||||
source.connect(sp);
|
||||
source.start(0);
|
||||
source.buffer = b1;
|
||||
sp.onaudioprocess = function() {
|
||||
ok(true, "We did not crash.");
|
||||
sp.onaudioprocess = null;
|
||||
SimpleTest.finish();
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
38
dom/media/webaudio/test/test_bug867174.html
Normal file
38
dom/media/webaudio/test/test_bug867174.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Crashtest for bug 867174</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var ctx = new AudioContext();
|
||||
|
||||
var source = ctx.createBufferSource();
|
||||
var buffer = ctx.createBuffer(2, 2048, 8000);
|
||||
source.playbackRate.setTargetAtTime(0, 2, 3);
|
||||
var sp = ctx.createScriptProcessor();
|
||||
source.connect(sp);
|
||||
sp.connect(ctx.destination);
|
||||
source.start(0);
|
||||
|
||||
sp.onaudioprocess = function(e) {
|
||||
// Now set the buffer
|
||||
source.buffer = buffer;
|
||||
|
||||
ok(true, "We did not crash.");
|
||||
sp.onaudioprocess = null;
|
||||
SimpleTest.finish();
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
34
dom/media/webaudio/test/test_bug867203.html
Normal file
34
dom/media/webaudio/test/test_bug867203.html
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Crashtest for bug 867203</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var ctx = new AudioContext();
|
||||
|
||||
var panner1 = ctx.createPanner();
|
||||
panner1.setVelocity(1, 1, 1);
|
||||
ctx.listener.setVelocity(1, 1, 1);
|
||||
(function() {
|
||||
ctx.createBufferSource().connect(panner1);
|
||||
})();
|
||||
SpecialPowers.forceGC();
|
||||
SpecialPowers.forceCC();
|
||||
ctx.createPanner();
|
||||
|
||||
ok(true, "We did not crash.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue