Title: [106612] trunk/LayoutTests
Revision
106612
Author
[email protected]
Date
2012-02-02 18:40:49 -0800 (Thu, 02 Feb 2012)

Log Message

noteGrainOn needs more tests
https://bugs.webkit.org/show_bug.cgi?id=77225

Patch by Raymond Toy <[email protected]> on 2012-02-02
Reviewed by Kenneth Russell.

* webaudio/note-grain-on-play.html: Added.
* webaudio/note-grain-on-play-expected.txt: Added.
* webaudio/note-grain-on-timing.html: Refactored to use new
functions in note-grain-on-testing.js.
* webaudio/note-grain-on-timing-expected.txt: Updated.
* webaudio/resources/audio-testing.js:
(grainLengthInSampleFrames):  Utility to compute length of a grain
in samples.
* webaudio/resources/note-grain-on-testing.js: Added.
(createSignalBuffer):
(findStartAndEndSamples):
(playGrain):
(playAllGrains):
(verifyStarAndtEndTimes): Common functions for note-grain-on-play and
note-grain-on-timing tests to use.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (106611 => 106612)


--- trunk/LayoutTests/ChangeLog	2012-02-03 02:28:17 UTC (rev 106611)
+++ trunk/LayoutTests/ChangeLog	2012-02-03 02:40:49 UTC (rev 106612)
@@ -1,5 +1,28 @@
 2012-02-02  Raymond Toy  <[email protected]>
 
+        noteGrainOn needs more tests
+        https://bugs.webkit.org/show_bug.cgi?id=77225
+
+        Reviewed by Kenneth Russell.
+
+        * webaudio/note-grain-on-play.html: Added.
+        * webaudio/note-grain-on-play-expected.txt: Added.
+        * webaudio/note-grain-on-timing.html: Refactored to use new
+        functions in note-grain-on-testing.js.
+        * webaudio/note-grain-on-timing-expected.txt: Updated.
+        * webaudio/resources/audio-testing.js:
+        (grainLengthInSampleFrames):  Utility to compute length of a grain
+        in samples.
+        * webaudio/resources/note-grain-on-testing.js: Added.
+        (createSignalBuffer):
+        (findStartAndEndSamples):
+        (playGrain):
+        (playAllGrains):
+        (verifyStarAndtEndTimes): Common functions for note-grain-on-play and
+        note-grain-on-timing tests to use.
+
+2012-02-02  Raymond Toy  <[email protected]>
+
         Typo in sample-accurate-scheduling layout test?
         https://bugs.webkit.org/show_bug.cgi?id=75996
 

Added: trunk/LayoutTests/webaudio/note-grain-on-play-expected.txt (0 => 106612)


--- trunk/LayoutTests/webaudio/note-grain-on-play-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webaudio/note-grain-on-play-expected.txt	2012-02-03 02:40:49 UTC (rev 106612)
@@ -0,0 +1,13 @@
+Test noteGrainOn offset rendering.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS Found all 100 grains.
+PASS All 100 grains started at the correct time.
+PASS All 100 grains ended at the correct time.
+PASS All 100 grains contained the correct data.
+PASS noteGrainOn offset rendering tests passed.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/webaudio/note-grain-on-play.html (0 => 106612)


--- trunk/LayoutTests/webaudio/note-grain-on-play.html	                        (rev 0)
+++ trunk/LayoutTests/webaudio/note-grain-on-play.html	2012-02-03 02:40:49 UTC (rev 106612)
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <script src=""
+    <script src=""
+    <script src=""
+  </head>
+
+  <body>
+    <div id="description"></div>
+    <div id="console"></div>
+
+    <script>
+      description("Test noteGrainOn offset rendering.");
+
+      // To test noteGrainOn, a single ramp signal is created.
+      // Various sections of the ramp are rendered by noteGrainOn() at
+      // different times, and we verify that the actual output
+      // consists of the correct section of the ramp at the correct
+      // time.
+      
+      var linearRampBuffer;
+
+      // Array of the grain offset used for each ramp played.
+      var grainOffsetTime = [];
+
+      // Verify the received signal is a ramp from the correct section
+      // of our ramp signal.
+      function verifyGrain(renderedData, startFrame, endFrame, grainIndex) {
+          var grainOffsetFrame = timeToSampleFrame(grainOffsetTime[grainIndex], sampleRate);
+          var grainFrameLength = endFrame - startFrame;
+          var ramp = linearRampBuffer.getChannelData(0);
+          var isCorrect = true;
+
+          var expected;
+          var actual;
+          var frame;
+      
+          for (var k = 0; k < grainFrameLength; ++k) {
+              if (renderedData[startFrame + k] != ramp[grainOffsetFrame + k]) {
+                  expected = ramp[grainOffsetFrame + k];
+                  actual = renderedData[startFrame + k];
+                  frame = startFrame + k;
+                  isCorrect = false;
+                  break;
+              }
+          }
+          return { verified: isCorrect,
+                   expected : expected ,
+                   actual : actual,
+                   frame : frame };
+      }
+      
+      function checkResult(event) {
+          var buffer = event.renderedBuffer;
+          renderedData = buffer.getChannelData(0);
+          var nSamples = renderedData.length;
+
+          var success = true;
+
+          // Number of grains that we found that have incorrect data.
+          var invalidGrainDataCount = 0;
+      
+          var startEndFrames = findStartAndEndSamples(renderedData);
+
+          // Verify the start and stop times.  Not strictly needed for
+          // this test, but it's useful to know that if the ramp data
+          // appears to be incorrect.
+          success = success && verifyStartAndEndFrames(startEndFrames);
+
+          // Loop through each of the rendered grains and check that
+          // each grain contains our expected ramp.
+          for (var k = 0; k < startEndFrames.start.length; ++k) {
+              // Verify that the rendered data matches the expected
+              // section of our ramp signal.
+              var result = verifyGrain(renderedData, startEndFrames.start[k], startEndFrames.end[k], k);
+
+              if (!result.verified) {
+                  testFailed("Grain " + k + " incorrect.  Expected " + result.expected + " but received " + result.actual + " at sample frame " + result.frame);
+                  ++invalidGrainDataCount;
+                  success = false;
+              }
+          }
+          
+          if (!invalidGrainDataCount) {
+              testPassed("All " + numberOfTests + " grains contained the correct data.");
+          } else {
+              testFailed(invalidGrainDataCount + " grains out of " + numberOfTests + " did not contain the expected data.");
+              success = false;
+          }
+      
+          if (success) {
+              testPassed("noteGrainOn offset rendering tests passed.");
+          } else {
+              testFailed("noteGrainOn offset rendering tests failed.");
+          }
+
+          finishJSTest();
+      }
+
+      function runTest() {
+          if (window.layoutTestController) {
+              layoutTestController.dumpAsText();
+              layoutTestController.waitUntilDone();
+          }
+
+          window.jsTestIsAsync = true;
+
+          // Create offline audio context.
+          context = new webkitAudioContext(2, sampleRate * renderTime, sampleRate);
+
+          // Create a linear ramp for testing noteGrainOn.
+          linearRampBuffer = createSignalBuffer(context,
+                                                function(k) {
+                                                    // Want the ramp to start
+                                                    // with 1, not 0.
+                                                    return k + 1;
+                                                });    
+
+          var grainInfo = playAllGrains(context, linearRampBuffer, numberOfTests);
+
+          grainOffsetTime = grainInfo.grainOffsetTimes;
+
+          context._oncomplete_ = checkResult;
+          context.startRendering();
+      }
+      
+      runTest();
+      successfullyParsed = true;
+
+    </script>
+
+    <script src=""
+  </body>
+</html>

Modified: trunk/LayoutTests/webaudio/note-grain-on-timing-expected.txt (106611 => 106612)


--- trunk/LayoutTests/webaudio/note-grain-on-timing-expected.txt	2012-02-03 02:28:17 UTC (rev 106611)
+++ trunk/LayoutTests/webaudio/note-grain-on-timing-expected.txt	2012-02-03 02:40:49 UTC (rev 106612)
@@ -2,9 +2,9 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-PASS Found all 100 pulses.
-PASS All 100 square pulses started at the correct time.
-PASS All 100 square pulses ended at the correct time.
+PASS Found all 100 grains.
+PASS All 100 grains started at the correct time.
+PASS All 100 grains ended at the correct time.
 PASS noteGrainOn timing tests passed.
 PASS successfullyParsed is true
 

Modified: trunk/LayoutTests/webaudio/note-grain-on-timing.html (106611 => 106612)


--- trunk/LayoutTests/webaudio/note-grain-on-timing.html	2012-02-03 02:28:17 UTC (rev 106611)
+++ trunk/LayoutTests/webaudio/note-grain-on-timing.html	2012-02-03 02:40:49 UTC (rev 106612)
@@ -2,6 +2,7 @@
 <html>
   <head>
     <script src=""
+    <script src=""
     <script src=""
   </head>
 
@@ -12,148 +13,19 @@
     <script>
       description("Test timing of noteGrainOn.");
 
-      var sampleRate = 44100.0;
-
-      // HRTF extra frames.  This is a magic constant currently in
-      // AudioBufferSourceNode::process that always extends the
-      // duration by this number of samples.  See bug 77224
-      // (https://bugs.webkit.org/show_bug.cgi?id=77224).
-      var extraFramesHRTF = 512;
-      
-      // How many square pulses to play.
-      var numberOfTests = 100;
-
-      // Duration of the square pulse to be played
-      var duration = 0.01;
-
-      // Time step between the start of each square pulse.  We need to
-      // add a little bit of silence so we can detect pulse boundaries
-      // and also account for the extra frames for HRTF.
-      var timeStep = duration + .005 + extraFramesHRTF / sampleRate;
-
-      // Time step between the grain start for each square pulse.
-      var grainOffsetStep = 0.005;
-
-      // How long to render to cover all of the pulses.
-      var renderTime = (numberOfTests + 1) * timeStep;
-
-      var context;
       var squarePulseBuffer;
-      var renderedData;
 
-      function createSquarePulse(context) {
-          // Create a square pulse that is long enough so that all the
-          // possible grain offsets still results in a square pulse of
-          // of the requested duration.  (The extra 1 is for any
-          // round-off.) 
-
-          var pulseLength = Math.floor(1 + extraFramesHRTF + sampleRate * (numberOfTests * grainOffsetStep + duration));
-
-          squarePulseBuffer = context.createBuffer(2, pulseLength, sampleRate);
-          var data = ""
-          for (var k = 0; k < pulseLength; ++k) {
-              data[k] = 1;
-          }
-      }
-
-      function trueGrainLength(grainOffset, duration) {
-          var startFrame = timeToSampleFrame(grainOffset, sampleRate);
-          var endFrame = timeToSampleFrame(grainOffset + duration, sampleRate);
-
-          return endFrame - startFrame;
-      }
-      
       function checkResult(event) {
           var buffer = event.renderedBuffer;
           renderedData = buffer.getChannelData(0);
           var nSamples = renderedData.length;
 
           var success = true;
-          var errorCountStart = 0;
-          var errorCountEnd = 0;
       
-          var startTime = [];
-          var endTime = [];
-          var lookForStart = true;
-      
-          // Look through the rendered data to find the start and stop
-          // times of each pulse.
-          for (var k = 0; k < nSamples; ++k) {
-              if (lookForStart) {
-                  // Find a non-zero point and record it.  We're not
-                  // concerned with the value in this test, only that
-                  // the pulse started here.  Other tests should cover
-                  // this case.
-                  if (renderedData[k] > 0) {
-                      startTime.push(k);
-                      lookForStart = false;
-                  }
-              } else {
-                  // Find a zero and record it.
-                  if (renderedData[k] == 0) {
-                      endTime.push(k);
-                      lookForStart = true;
-                  }
-              }
-          }
+          var startEndFrames = findStartAndEndSamples(renderedData);
 
-          if (startTime.length != endTime.length) {
-              testFailed("Could not find the beginning or end of a square pulse.");
-              success = false;
-          }
+          success = success && verifyStartAndEndFrames(startEndFrames);
 
-          if (startTime.length == numberOfTests && endTime.length == numberOfTests) {
-              testPassed("Found all " + numberOfTests + " pulses.");
-          } else {
-              testFailed("Did not find all " + numberOfTests + " pulses.");
-          }
-
-          // Examine the start and stop times to see if they match our
-          // expectations.
-          for (var k = 0; k < startTime.length; ++k) {
-              var expectedStart = timeToSampleFrame(k * timeStep, sampleRate);
-              // The end point is the duration, plus the extra frames
-              // for HRTF.
-              var expectedEnd = extraFramesHRTF + expectedStart + trueGrainLength(k * grainOffsetStep, duration);
-
-              if (startTime[k] != expectedStart) {
-                  testFailed("Pulse " + k + " started at " + startTime[k] + " but expected at " + expectedStart);
-                  ++errorCountStart;
-                  success = false;
-              }
-
-              if (endTime[k] != expectedEnd) {
-                  testFailed("Pulse " + k + " ended at " + endTime[k] + " but expected at " + expectedEnd);
-                  ++errorCountEnd;
-                  success = false;
-              }
-          }
-
-          if (!errorCountStart) {
-              if (startTime.length == numberOfTests) {
-                  testPassed("All " + numberOfTests + " square pulses started at the correct time.");
-              } else {
-                  testFailed("All pulses started at the correct time, but only " + startTime.length + " pulses found.");
-                  success = false;
-              }
-          } else {
-              testFailed(errorCountStart + " out of " + numberOfTests + " square pulses started at the wrong time.");
-              success = false;
-          }
-
-          if (!errorCountEnd) {
-              if (endTime.length == numberOfTests) {
-                  testPassed("All " + numberOfTests + " square pulses ended at the correct time.");
-              } else {
-                  testFailed("All pulses ended at the correct time, but only " + endTime.length + " pulses found.");
-                  success = false;
-              }
-          } else {
-              testFailed(errorCountEnd + " out of " + numberOfTests + " square pulses ended at the wrong time.");
-              success = false;
-          }
-
-      
           if (success) {
               testPassed("noteGrainOn timing tests passed.");
           } else {
@@ -163,16 +35,6 @@
           finishJSTest();
       }
 
-      function playNote(time, grainOffset, duration) {
-          var bufferSource = context.createBufferSource();
-          bufferSource.buffer = squarePulseBuffer;
-          bufferSource.connect(context.destination);
-          // We're only testing that the source starts and ends at a
-          // particular time.  See bug 77225
-          // (https://bugs.webkit.org/show_bug.cgi?id=77225).
-          bufferSource.noteGrainOn(time, grainOffset, duration);
-      }
-
       function runTest() {
           if (window.layoutTestController) {
               layoutTestController.dumpAsText();
@@ -183,13 +45,11 @@
 
           // Create offline audio context.
           context = new webkitAudioContext(2, sampleRate * renderTime, sampleRate);
-          createSquarePulse(context);    
 
-          for (var i = 0; i < numberOfTests; ++i) {
-              var timeOffset = timeStep * i;
-              playNote(timeOffset, i * grainOffsetStep, duration);
-          }
+          squarePulseBuffer = createSignalBuffer(context, function (k) { return 1 });    
 
+          playAllGrains(context, squarePulseBuffer, numberOfTests);
+
           context._oncomplete_ = checkResult;
           context.startRendering();
       }

Modified: trunk/LayoutTests/webaudio/resources/audio-testing.js (106611 => 106612)


--- trunk/LayoutTests/webaudio/resources/audio-testing.js	2012-02-03 02:28:17 UTC (rev 106611)
+++ trunk/LayoutTests/webaudio/resources/audio-testing.js	2012-02-03 02:40:49 UTC (rev 106612)
@@ -125,3 +125,11 @@
     return Math.floor(0.5 + time * sampleRate);
 }
 
+// Compute the number of sample frames consumed by noteGrainOn with
+// the specified |grainOffset|, |duration|, and |sampleRate|.
+function grainLengthInSampleFrames(grainOffset, duration, sampleRate) {
+    var startFrame = timeToSampleFrame(grainOffset, sampleRate);
+    var endFrame = timeToSampleFrame(grainOffset + duration, sampleRate);
+
+    return endFrame - startFrame;
+}

Added: trunk/LayoutTests/webaudio/resources/note-grain-on-testing.js (0 => 106612)


--- trunk/LayoutTests/webaudio/resources/note-grain-on-testing.js	                        (rev 0)
+++ trunk/LayoutTests/webaudio/resources/note-grain-on-testing.js	2012-02-03 02:40:49 UTC (rev 106612)
@@ -0,0 +1,179 @@
+var sampleRate = 44100.0;
+
+// HRTF extra frames.  This is a magic constant currently in
+// AudioBufferSourceNode::process that always extends the
+// duration by this number of samples.  See bug 77224
+// (https://bugs.webkit.org/show_bug.cgi?id=77224).
+var extraFramesHRTF = 512;
+
+// How many grains to play.
+var numberOfTests = 100;
+
+// Duration of each grain to be played
+var duration = 0.01;
+
+// Time step between the start of each grain.  We need to add a little
+// bit of silence so we can detect grain boundaries and also account
+// for the extra frames for HRTF.
+var timeStep = duration + .005 + extraFramesHRTF / sampleRate;
+
+// Time step between the start for each grain.
+var grainOffsetStep = 0.001;
+
+// How long to render to cover all of the grains.
+var renderTime = (numberOfTests + 1) * timeStep;
+
+var context;
+var renderedData;
+
+// Create a buffer containing the data that we want.  The function f
+// returns the desired value at sample frame k.
+function createSignalBuffer(context, f) {
+
+    // Make sure the buffer has enough data for all of the possible
+    // grain offsets and durations.  Need to include the extra frames
+    // for HRTF.  The additional 1 is for any round-off errors.
+    var signalLength = Math.floor(1 + extraFramesHRTF + sampleRate * (numberOfTests * grainOffsetStep + duration));
+
+    var buffer = context.createBuffer(2, signalLength, sampleRate);
+    var data = ""
+
+    for (var k = 0; k < signalLength; ++k) {
+        data[k] = f(k);
+    }
+
+    return buffer;
+}
+
+// From the data array, find the start and end sample frame for each
+// grain.  This depends on the data having 0's between grain, and
+// that the grain is always strictly non-zero.
+function findStartAndEndSamples(data) {
+    var nSamples = data.length;
+
+    var startTime = [];
+    var endTime = [];
+    var lookForStart = true;
+      
+    // Look through the rendered data to find the start and stop
+    // times of each grain.
+    for (var k = 0; k < nSamples; ++k) {
+        if (lookForStart) {
+            // Find a non-zero point and record the start.  We're not
+            // concerned with the value in this test, only that the
+            // grain started here.
+            if (renderedData[k]) {
+                startTime.push(k);
+                lookForStart = false;
+            }
+        } else {
+            // Find a zero and record the end of the grain.
+            if (!renderedData[k]) {
+                endTime.push(k);
+                lookForStart = true;
+            }
+        }
+    }
+
+    return {start : startTime, end : endTime};
+}
+
+function playGrain(context, source, time, offset, duration) {
+    var bufferSource = context.createBufferSource();
+
+    bufferSource.buffer = source;
+    bufferSource.connect(context.destination);
+    bufferSource.noteGrainOn(time, offset, duration);
+}
+
+// Play out all grains.  Returns a object containing two arrays, one
+// for the start time and one for the grain offset time.
+function playAllGrains(context, source, numberOfNotes) {
+    var startTimes = new Array(numberOfNotes);
+    var offsets = new Array(numberOfNotes);
+
+    for (var k = 0; k < numberOfNotes; ++k) {
+        var timeOffset = k * timeStep;
+        var grainOffset = k * grainOffsetStep;
+
+        playGrain(context, source, timeOffset, grainOffset, duration);
+        startTimes[k] = timeOffset;
+        offsets[k] = grainOffset;
+    }
+
+    return { startTimes : startTimes, grainOffsetTimes : offsets };
+}
+
+// Verify that the start and end frames for each grain match our
+// expected start and end frames.
+function verifyStartAndEndFrames(startEndFrames) {
+    var startFrames = startEndFrames.start;
+    var endFrames = startEndFrames.end;
+      
+    var success = true;
+
+    // Count of how many grains started at the incorrect time.
+    var errorCountStart = 0;
+
+    // Count of how many grains ended at the incorrect time.
+    var errorCountEnd = 0;
+
+    if (startFrames.length != endFrames.length) {
+        testFailed("Could not find the beginning or end of a grain.");
+        success = false;
+    }
+
+    if (startFrames.length == numberOfTests && endFrames.length == numberOfTests) {
+        testPassed("Found all " + numberOfTests + " grains.");
+    } else {
+        testFailed("Did not find all " + numberOfTests + " grains.");
+    }
+
+    // Examine the start and stop times to see if they match our
+    // expectations.
+    for (var k = 0; k < startFrames.length; ++k) {
+        var expectedStart = timeToSampleFrame(k * timeStep, sampleRate);
+        // The end point is the duration, plus the extra frames
+        // for HRTF.
+        var expectedEnd = extraFramesHRTF + expectedStart + grainLengthInSampleFrames(k * grainOffsetStep, duration, sampleRate);
+
+        if (startFrames[k] != expectedStart) {
+            testFailed("Pulse " + k + " started at " + startFrames[k] + " but expected at " + expectedStart);
+            ++errorCountStart;
+            success = false;
+        }
+
+        if (endFrames[k] != expectedEnd) {
+            testFailed("Pulse " + k + " ended at " + endFrames[k] + " but expected at " + expectedEnd);
+            ++errorCountEnd;
+            success = false;
+        }
+    }
+
+    // Check that all the grains started or ended at the correct time.
+    if (!errorCountStart) {
+        if (startFrames.length == numberOfTests) {
+            testPassed("All " + numberOfTests + " grains started at the correct time.");
+        } else {
+            testFailed("All grains started at the correct time, but only " + startFrames.length + " grains found.");
+            success = false;
+        }
+    } else {
+        testFailed(errorCountStart + " out of " + numberOfTests + " grains started at the wrong time.");
+        success = false;
+    }
+
+    if (!errorCountEnd) {
+        if (endFrames.length == numberOfTests) {
+            testPassed("All " + numberOfTests + " grains ended at the correct time.");
+        } else {
+            testFailed("All grains ended at the correct time, but only " + endFrames.length + " grains found.");
+            success = false;
+        }
+    } else {
+        testFailed(errorCountEnd + " out of " + numberOfTests + " grains ended at the wrong time.");
+        success = false;
+    }
+
+    return success;
+}
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to