<!doctype html>
<html>
<head>
<title>Test onended with Different Playback Rates</title>
<style type="text/css">
header {
margin: 20px 0;
}
#results {
white-space: pre;
font-family: monospace;
}
</style>
</head>
<body>
<h1>Test onended with Different Playback Rates</h1>
<p>
Run the following tests in order. In all cases the onended event should be fired.
See <a href="crbug.com/484935">crbug.com/484935</a>.
</p>
<ol>
<li>
Press "Rate 1" button to test onended with a playback rate of 1. You should hear a sound and
the onended event should be fired, which should print a message to the console and the
Result are below. This audio is the reference.
<p>
The onended event should occur after about <span id="rate-1-duration"></span> sec.
</p>
</li>
<li>
Press "Rate 2" button to do the same test but with the playback rate set to 2. This should
sound somewhat like 1, but have a higher pitch and finish sooner.
<p>
The onended event should occur after about <span id="rate-2-duration"></span> sec.
</p>
</li>
<li>
Press "Rate 1/2" button to do the same test but with the playback rate set to 1/2. This
should sound like 1, but last twice as long with a lower pitch. The entire audio sample
should be played. (Compare with Rate 1.)
<p>
The onended event should occur after about <span id="rate-half-duration"></span> sec.
</p>
</li>
<li>
Press "Rate variable" button to test playback with a variable playback rate. The entire
sample should be played. Make sure this is distantly different from the other rates
above. If not, then playback automation did not work and a new bug should be filed.
<p>
The onended event should occur after approximately <span
id="rate-var-min-duration"></span> sec, but may take longer.
</p>
</li>
</ol>
<button id="rate-1" disabled onclick="test1()">Rate 1</button>
<button id="rate-2" disabled onclick="test2()">Rate 2</button>
<button id="rate-half" disabled onclick="testHalf()">Rate 1/2</button>
<button id="rate-var" disabled onclick="testVariable()">Rate variable</button>
<header>Results</header>
<div id="results"></div>
<script>
var context = new AudioContext();
var src;
var buffer;
// Duration (in sec) of the sine source to be used as the test signal.
var duration = 1;
function generateTestSignal () {
// Create a new test signal. A tone of nominal length |duration|. Near the end, we
// increase the amplitude and then finally fade out the signal. We do this so that we can
// hear when the tone should be ending in case a test fails and the output is clipped
// permaturely.
// Time from the nominal end where we increase the amplitude
var changeTime = 0.2;
// How fast to fade out the signal.
var timeConstant = 0.2;
// Create an offline context long enough to hold the nominal tone plus the fade out.
// Somewhat arbitrarily use 5 time constants as the duration of the fade. The signal should
// be small enough that there's not large glitch at the end, but short enough that we don't
// have a long silence at the end.
var contextDuration = duration + timeConstant * 5;
var offline = new OfflineAudioContext(1, contextDuration * context.sampleRate, context.sampleRate);
var osc = offline.createOscillator();
var gain = offline.createGain();
osc.connect(gain);
gain.connect(offline.destination);
// Start the tone at amplitude 0.75.
gain.gain.setValueAtTime(.75, 0);
// Gradually increase the gain to 1, a little before the nominal end of the tone.
gain.gain.setTargetAtTime(1, duration - changeTime, timeConstant);
// Now fade out the signal.
gain.gain.setTargetAtTime(0, duration, timeConstant);
osc.start();
offline.startRendering().then(function (b) {
buffer = context.createBuffer(1, b.length, context.sampleRate);
buffer.copyToChannel(b.getChannelData(0), 0);
// Inform user how long the done is.
log("Test signal duration = " + buffer.duration + " sec");
// Update the text with the actual lengths so we can compare the expected onended time and
// the actual.
document.getElementById("rate-1-duration").textContent = contextDuration;
document.getElementById("rate-2-duration").textContent = contextDuration / 2;
document.getElementById("rate-half-duration").textContent = contextDuration / 0.5;
// The factor 1.13 is an approximation of where the onended event should occur. This was
// determined by experimentation because it's pretty hard to calculate the actual duration
// when we automate the playback rate in complicated ways.
document.getElementById("rate-var-min-duration").textContent = 1.13 * contextDuration;
// Signal generated so we can enable the buttons now.
enableButtons();
});
}
function enableButtons () {
document.getElementById("rate-1").disabled = false;
document.getElementById("rate-2").disabled = false;
document.getElementById("rate-half").disabled = false;
document.getElementById("rate-var").disabled = false;
}
window.onload = generateTestSignal;
function createGraph(rate) {
// Create a simple graph with the source connected to the destination and set up the
// playback rate according to |rate|.
src = context.createBufferSource();
src.buffer = buffer;
src.playbackRate.value = rate;
src.connect(context.destination);
}
function test1() {
// Rate 1 test.
createGraph(1);
var startTime = context.currentTime;
src.onended = function () {
log("Rate 1 test ended at " + (context.currentTime - startTime) + " sec");
}
src.start();
}
function test2() {
// Rate 2 test
createGraph(2);
var startTime = context.currentTime;
src.onended = function () {
log("Rate 2 test ended at " + (context.currentTime - startTime) + " sec");
}
src.start();
}
function testHalf() {
// Rate 1/2 test
createGraph(0.5);
var startTime = context.currentTime;
src.onended = function () {
log("Rate 0.5 test ended at " + (context.currentTime - startTime) + " sec");
}
src.start();
}
function testVariable() {
// Variable rate test. Set the nominal playback rate to 0 so that the automation completely
// determines the playback rate. (Otherwise it gets added to the intrinsic playback rate.)
createGraph(0);
// Create a constant buffer of value 1 that we will automate to generate the desired
// playback rate.
var gainSrc = context.createBufferSource();
var gainSrcBuffer = context.createBuffer(1, 1, context.sampleRate);
var d = gainSrcBuffer.getChannelData(0);
d[0] = 1;
gainSrc.buffer = gainSrcBuffer;
gainSrc.loop = true;
// Automate this gain node to produce the desired playback rate. What we want is to start
// the playback rate at 2, exponentially ramp down to 0.1 at |duration|/2. Then linear ramp
// back up to 1. This will cause the output signal to change pitch and duration. The exact
// modulation is not important except that we want the minimum playback rate to be less than
// 1 to make sure we don't prematurely end the sample causing the onended event not to be
// fired.
var playback = context.createGain();
playback.gain.setValueAtTime(2, context.currentTime);
playback.gain.exponentialRampToValueAtTime(0.1, context.currentTime + duration / 2);
playback.gain.linearRampToValueAtTime(1, context.currentTime + duration);
gainSrc.connect(playback);
playback.connect(src.playbackRate);
gainSrc.start();
var startTime = context.currentTime;
src.onended = function () {
log("Rate variable test ended at " + (context.currentTime - startTime) + " sec");
}
src.start();
}
function log(message) {
console.log(message);
var results = document.getElementById("results");
results.textContent += message + "\n";
}
</script>
</body>
</html>