Bug 1447273 - Add test to check that different GainNode configuraitons produce correct output. r?pehrsons draft
authorBryce Van Dyk <bvandyk@mozilla.com>
Thu, 05 Apr 2018 14:58:08 -0400
changeset 778639 cfb06a18ba6f94e184d90feb17491534548979f6
parent 778638 95d69312a8c73ca68e9d84d702ecdc6498ba6fde
push id105544
push userbvandyk@mozilla.com
push dateFri, 06 Apr 2018 15:56:09 +0000
reviewerspehrsons
bugs1447273
milestone61.0a1
Bug 1447273 - Add test to check that different GainNode configuraitons produce correct output. r?pehrsons MozReview-Commit-ID: 6iaCqotLmug
dom/media/webaudio/test/mochitest.ini
dom/media/webaudio/test/test_bug1447273.html
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -102,16 +102,17 @@ tags=capturestream
 [test_bug1113634.html]
 [test_bug1118372.html]
 [test_bug1027864.html]
 [test_bug1056032.html]
 skip-if = toolkit == 'android' # bug 1056706
 [test_bug1255618.html]
 [test_bug1267579.html]
 [test_bug1355798.html]
+[test_bug1447273.html]
 [test_channelMergerNode.html]
 [test_channelMergerNodeWithVolume.html]
 [test_channelSplitterNode.html]
 [test_channelSplitterNodeWithVolume.html]
 skip-if = (android_version == '18' && debug) # bug 1158417
 [test_convolverNode.html]
 [test_convolverNode_mono_mono.html]
 [test_convolverNodeChannelCount.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_bug1447273.html
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test bug 1447273 - GainNode with a stereo input and changing volume</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">
+/**
+ * Sets up a stereo BufferSource and plumbs it through different gain node
+ * configurations. A control gain path with no changes to gain is used along
+ * with 2 other paths which should increase their gain. The result should be
+ * that audio travelling along the increased gain paths is louder than the
+ * control path.
+ */
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout(
+  "This test uses a live audio context and uses a setTimeout to schedule a " +
+  "change to the graph.");
+addLoadEvent(function() {
+  let context = new AudioContext();
+
+  let numChannels = 2;
+  let sampleRate = context.sampleRate;
+  // 8 seconds to mitigate timing issues on slow test machines
+  let recordingLength = 8;
+  let bufferLength = sampleRate * recordingLength;
+  let gainExplicitlyIncreased = false;
+  let sourceFinished = false;
+
+  // Create source buffer
+  let sourceBuffer = context.createBuffer(numChannels, bufferLength, sampleRate);
+  for (let i = 0; i < bufferLength; ++i) {
+    sourceBuffer.getChannelData(0)[i] = 1;
+    sourceBuffer.getChannelData(1)[i] = 1;
+  }
+  let source = context.createBufferSource();
+  source.buffer = sourceBuffer;
+
+  let gainNoChange = context.createGain();
+  let gainExplicitAssignment = context.createGain();
+  let gainSetValueAtTime = context.createGain();
+
+  // All gain nodes start of with the same gain
+  gainNoChange.gain.value = 0.25;
+  gainExplicitAssignment.gain.value = 0.25;
+  gainSetValueAtTime.gain.value = 0.25;
+
+  // Connect source to gain nodes:
+  // source--> gainNoChange
+  //       |-> gainExplicitAssignment
+  //       \-> gainSetValueAtTime
+  source.connect(gainNoChange);
+  source.connect(gainExplicitAssignment);
+  source.connect(gainSetValueAtTime);
+
+  // Create intermediate media streams (required to repro bug 1447273)
+  let destNoChange = context.createMediaStreamDestination();
+  let destExplicitAssignement = context.createMediaStreamDestination();
+  let destSetValueAtTime = context.createMediaStreamDestination();
+
+  let sourceNoChange = context.createMediaStreamSource(destNoChange.stream);
+  let sourceExplicitAssignement = context.createMediaStreamSource(destExplicitAssignement.stream);
+  let sourceSetValueAtTime = context.createMediaStreamSource(destSetValueAtTime.stream);
+
+  // Connect gain nodes to our intermediate streams:
+  // source--> gainNoChange           -> destNoChange            -> sourceNoChange
+  //       |-> gainExplicitAssignment -> destExplicitAssignement -> sourceExplicitAssignement
+  //       \-> gainSetValueAtTime     -> destSetValueAtTime      -> sourceSetValueAtTime
+  gainNoChange.connect(destNoChange);
+  gainExplicitAssignment.connect(destExplicitAssignement);
+  gainSetValueAtTime.connect(destSetValueAtTime);
+
+  // Create analysers for each path
+  let analyserNoChange = context.createAnalyser();
+  let analyserExplicitAssignment = context.createAnalyser();
+  let analyserSetValueAtTime = context.createAnalyser();
+
+  // Connect our intermediate media streams to analysers:
+  // source--> gainNoChange           -> destNoChange            -> sourceNoChange            -> analyserNoChange
+  //       |-> gainExplicitAssignment -> destExplicitAssignement -> sourceExplicitAssignement -> analyserExplicitAssignment
+  //       \-> gainSetValueAtTime     -> destSetValueAtTime      -> sourceSetValueAtTime      -> analyserSetValueAtTime
+  sourceNoChange.connect(analyserNoChange);
+  sourceExplicitAssignement.connect(analyserExplicitAssignment);
+  sourceSetValueAtTime.connect(analyserSetValueAtTime);
+
+  // Half way through our source buffer increase our gain on
+  // gainSetValueAtTime path.
+  gainSetValueAtTime.gain.setValueAtTime(0.5, recordingLength / 2);
+
+  // Maximum values seen at each analyser node, will be updated by
+  // checkAnalysersForMaxValues() during test.
+  let maxNoGainChange = 0;
+  let maxExplicitAssignment = 0;
+  let maxSetValueAtTime = 0;
+
+  // Poll analysers and check for max values
+  function checkAnalysersForMaxValues() {
+    let findMaxValue =
+      (array) => array.reduce((a, b) => Math.max(Math.abs(a), Math.abs(b)));
+
+    let dataArray = new Float32Array(analyserNoChange.fftSize);
+    analyserNoChange.getFloatTimeDomainData(dataArray);
+    maxNoGainChange = Math.max(maxNoGainChange, findMaxValue(dataArray));
+
+    analyserExplicitAssignment.getFloatTimeDomainData(dataArray);
+    maxExplicitAssignment = Math.max(maxExplicitAssignment, findMaxValue(dataArray));
+
+    analyserSetValueAtTime.getFloatTimeDomainData(dataArray);
+    maxSetValueAtTime = Math.max(maxSetValueAtTime, findMaxValue(dataArray));
+  }
+
+  // Setup our end condition
+  source.onended = () => {
+    sourceFinished = true;
+    info(`maxNoGainChange: ${maxNoGainChange}`);
+    info(`maxExplicitAssignment: ${maxExplicitAssignment}`);
+    info(`maxSetValueAtTime: ${maxSetValueAtTime}`);
+    ok(gainExplicitlyIncreased,
+       "Gain should be explicitly assinged during test!");
+    ok(maxExplicitAssignment > maxNoGainChange,
+       "Volume should increase due to explicit assignment to gain.value");
+    ok(maxSetValueAtTime > maxNoGainChange,
+       "Volume should increase due to setValueAtTime on gain.value");
+    SimpleTest.finish();
+  };
+
+  // Start the graph
+  source.start(0);
+
+  // We'll use this callback to check our analysers for gain
+  function animationFrameCb() {
+    if (sourceFinished) {
+      return;
+    }
+    requestAnimationFrame(animationFrameCb);
+    checkAnalysersForMaxValues();
+  }
+
+  // Using timers is gross, but as of writing there doesn't appear to be a
+  // nicer way to perform gain.value = 0.5 on our node. When/if we support
+  // suspend(time) on offline contexts we could potentially use that instead.
+
+  // Roughly half way through our source buffer (setTimeout flakiness) increase
+  // our gain on gainExplicitAssignment path.
+  window.setTimeout(() => {
+    gainExplicitAssignment.gain.value = 0.5;
+    // Make debugging flaky timeouts in test easier
+    info("Gain explicitly set!")
+    gainExplicitlyIncreased = true;
+    // Start checking analysers, we do this only after changing volume to avoid
+    // possible starvation of this timeout from requestAnimationFrame calls.
+    animationFrameCb();
+  }, recordingLength * 1000 / 2);
+});
+
+</script>
+</pre>
+</body>
+</html>