| <!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> |