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;
+}