Bug 931311 - Wait for HRTF panner to load impulse database before doing test. a=test-only
authorKarl Tomlinson <karlt+@karlt.net>
Sat, 09 Nov 2013 14:07:50 +1300
changeset 166457 0939430ea588ba0b6ddb00b4249ad0986a8934c7
parent 166456 856ca571aa89dab408220de4213f745a0e6c5245
child 166458 773937451ebee0e5b093f5e8c99efa208867955e
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstest-only
bugs931311
milestone27.0a2
Bug 931311 - Wait for HRTF panner to load impulse database before doing test. a=test-only
content/media/webaudio/test/test_pannerNodeTail.html
--- a/content/media/webaudio/test/test_pannerNodeTail.html
+++ b/content/media/webaudio/test/test_pannerNodeTail.html
@@ -22,69 +22,80 @@
 // In the current implementation, DelayNodes will take a tail-time reference
 // immediately when they receive the first block of sound from an upstream
 // node, so this test connects the downstream DelayNodes while the upstream
 // nodes are finishing, and then runs GC (on the main thread) before the
 // DelayNodes receive any input (on the graph thread).
 //
 // 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.
 
 SimpleTest.waitForExplicitFinish();
 
 const blockSize = 128;
 // bufferSize should be long enough that to allow an audioprocess event to be
 // sent to the main thread and a connect message to return to the graph
 // thread.
 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;
 var referenceOutput = [new Float32Array(bufferSize),
                        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);
       delay.delayTime.value = delayDuration;
       delay.connect(testProcessor);
       testPanners[i].connect(delay);
     }
     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
     // stable state and ask the graph thread to make connections, schedule a
     // subsequent event to run cycle collection, which should not collect
     // panners that are still producing sound.
     SimpleTest.executeSoon(function() {
       SpecialPowers.forceGC();
@@ -96,26 +107,23 @@ function onReferenceOutput(e) {
   case sourceDelayBufferCount:
     // Record this buffer during which PannerNode outputs were connected.
     for (var i = 0; i < 2; ++i) {
       e.inputBuffer.copyFromChannel(referenceOutput[i], i);
     }
     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++;
 }
 
 function onTestOutput(e) {
   if (testProcessCount < sourceDelayBufferCount + ccDelayBufferCount) {
     testProcessCount++;
@@ -126,61 +134,55 @@ function onTestOutput(e) {
     compareBuffers(e.inputBuffer.getChannelData(i), referenceOutput[i]);
   }
   e.target.onaudioprocess = null;
   e.target.disconnect();
   SimpleTest.finish();
 }
 
 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.
   var delayDuration =
     (sourceDelayBufferCount * bufferSize
      - expectedPannerTailTime - 2 * blockSize) / ctx.sampleRate;
 
   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;
   }
 
   // Create a ScriptProcessor now to use as a timer to trigger connection of
   // downstream nodes.  It will also be used to record reference output.
   var referenceProcessor = ctx.createScriptProcessor(bufferSize, 2, 0);
@@ -191,13 +193,39 @@ function startTest() {
   // The test ScriptProcessor will record output of testPanners. 
   // Create it now so that it is synchronized with the referenceProcessor.
   testProcessor = ctx.createScriptProcessor(bufferSize, 2, 0);
   testProcessor.onaudioprocess = onTestOutput;
   // Start audioprocess events before source delays are connected.
   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();
 </script>
 </pre>
 </body>
 </html>