From 16c053863297b05dc0f258177841f6cb85085afd Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Sat, 9 Nov 2013 14:07:50 +1300 Subject: [PATCH] b=931311 wait for HRTF panner to load impulse database before doing test --HG-- extra : transplant_source : %2A%1F%D0kKSr%AA%8F%C1%26%E5%E6%EC%D5%CF%8C%A9%D11 --- .../webaudio/test/test_pannerNodeTail.html | 88 ++++++++++++------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/content/media/webaudio/test/test_pannerNodeTail.html b/content/media/webaudio/test/test_pannerNodeTail.html index c60fbc0cab86..12efbae4d6f4 100644 --- a/content/media/webaudio/test/test_pannerNodeTail.html +++ b/content/media/webaudio/test/test_pannerNodeTail.html @@ -27,7 +27,7 @@ // // Web Audio doesn't provide a means to precisely time connect()s but we can // test that the output of delay nodes matches the output from a reference -// PannerNodes that we know will not be GCed. +// PannerNode that we know will not be GCed. // // Another set of delay nodes is added upstream to ensure that the source node // has removed its self-reference after dispatching its "ended" event. @@ -41,15 +41,14 @@ const blockSize = 128; const bufferSize = 4096; const pannerCount = bufferSize / blockSize; // sourceDelayBufferCount should be long enough to allow the source node -// onended to finish. Because of the way blocks are processed in sets on -// the graph thread, this also affects when the graph thread receives the -// disconnect. +// onended to finish and remove the source self-reference. const sourceDelayBufferCount = 3; var gotEnded = false; // ccDelayLength should be long enough to allow CC to run var ccDelayBufferCount = 20; const ccDelayLength = ccDelayBufferCount * bufferSize; +var ctx; var testPanners = []; var referencePanner; var referenceProcessCount = 0; @@ -58,18 +57,27 @@ var referenceOutput = [new Float32Array(bufferSize), var testProcessor; var testProcessCount = 0; +function isChannelSilent(channel) { + for (var i = 0; i < channel.length; ++i) { + if (channel[i] != 0.0) { + dump("input at " + i + "\n"); + return false; + } + } + return true; +} + function onReferenceOutput(e) { switch(referenceProcessCount) { case sourceDelayBufferCount - 1: // The panners are about to finish. if (!gotEnded) { - todo(false, "Oscillator hasn't ended. Increase sourceDelayBufferCount?"); + todo(false, "Source hasn't ended. Increase sourceDelayBufferCount?"); } // Connect each PannerNode output to a downstream DelayNode, // and connect ScriptProcessors to compare test and reference panners. - var ctx = e.target.context; var delayDuration = ccDelayLength / ctx.sampleRate; for (var i = 0; i < pannerCount; ++i) { var delay = ctx.createDelay(delayDuration); @@ -80,6 +88,9 @@ function onReferenceOutput(e) { testProcessor = null; testPanners = null; + // The panning effect is linear so only one reference panner is required. + // This also checks that the individual panners don't chop their output + // too soon. referencePanner.connect(e.target); // Assuming the above operations have already scheduled an event to run in @@ -101,16 +112,13 @@ function onReferenceOutput(e) { e.target.onaudioprocess = null; e.target.disconnect(); - for (var i = 0; i < referenceOutput[0].length; ++i) { - if (referenceOutput[0][i] != 0.0) { - return; // good - a connection must have been received by the graph - } - } // If the buffer is silent, there is probably not much point just // increasing the buffer size, because, with the buffer size already // significantly larger than panner tail time, it demonstrates that the // lag between threads is much greater than the tail time. - todo(false, "Connections not detected."); + if (isChannelSilent(referenceOutput[0])) { + todo(false, "Connections not detected."); + } } referenceProcessCount++; @@ -131,36 +139,30 @@ function onTestOutput(e) { } function startTest() { - var ctx = new AudioContext(); - // Place the listener to the side of the origin, where the panners are - // positioned, to maximize delay in one ear. - ctx.listener.setPosition(1,0,0); - // 0.002 is MaxDelayTimeSeconds in HRTFpanner.cpp // and 512 is fftSize() at 48 kHz. const expectedPannerTailTime = 0.002 * ctx.sampleRate + 512; // Create some PannerNodes downstream from DelayNodes with delays long - // enough for their source oscillator to finish, dispatch its "ended" event + // enough for their source to finish, dispatch its "ended" event // and release its playing reference. The DelayNodes should expire their // tail-time references before the PannerNodes and so only the PannerNode // lifetimes depends on their tail-time references. Many DelayNodes are // created and timed to finish at different times so that one PannerNode // will be finishing the block processed immediately after the connect is // received. - var oscillator = ctx.createOscillator(); - oscillator.start(0); + var source = ctx.createBufferSource(); // Just short of blockSize here to avoid rounding into the next block - oscillator.stop((blockSize - 1) / ctx.sampleRate); - oscillator.onended = function(e) { + var buffer = ctx.createBuffer(1, blockSize - 1, ctx.sampleRate); + for (var i = 0; i < buffer.length; ++i) { + buffer.getChannelData(0)[i] = Math.cos(Math.PI * i / buffer.length); + } + source.buffer = buffer; + source.start(0); + source.onended = function(e) { gotEnded = true; }; - // The panner effect is linear so only one reference panner is required. - // This also checks that the individual panners don't chop their output too - // soon. - referencePanner = ctx.createPanner(); - // Time the first test panner to finish just before downstream DelayNodes // are about the be connected. Note that DelayNode lifetime depends on // maxDelayTime so set that equal to the delay. @@ -171,11 +173,11 @@ function startTest() { for (var i = 0; i < pannerCount; ++i) { var delay = ctx.createDelay(delayDuration); delay.delayTime.value = delayDuration; - oscillator.connect(delay); + source.connect(delay); delay.connect(referencePanner) var panner = ctx.createPanner(); - delay.connect(panner) + delay.connect(panner); testPanners[i] = panner; delayDuration += blockSize / ctx.sampleRate; @@ -196,7 +198,33 @@ function startTest() { testProcessor.connect(ctx.destination); } -startTest(); +function prepareTest() { + ctx = new AudioContext(); + // Place the listener to the side of the origin, where the panners are + // positioned, to maximize delay in one ear. + ctx.listener.setPosition(1,0,0); + + // A PannerNode will produce no output until it has loaded its HRIR + // database. Wait for this to load before starting the test. + var processor = ctx.createScriptProcessor(bufferSize, 2, 0); + referencePanner = ctx.createPanner(); + referencePanner.connect(processor); + var oscillator = ctx.createOscillator(); + oscillator.connect(referencePanner); + oscillator.start(0); + + processor.onaudioprocess = function(e) { + if (isChannelSilent(e.inputBuffer.getChannelData(0))) + return; + + oscillator.stop(0); + oscillator.disconnect(); + referencePanner.disconnect(); + e.target.onaudioprocess = null; + SimpleTest.executeSoon(startTest); + }; +} +prepareTest();