mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-27 02:47:31 +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
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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue