summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/resources/note-grain-on-testing.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webaudio/resources/note-grain-on-testing.js')
-rw-r--r--testing/web-platform/tests/webaudio/resources/note-grain-on-testing.js165
1 files changed, 165 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/resources/note-grain-on-testing.js b/testing/web-platform/tests/webaudio/resources/note-grain-on-testing.js
new file mode 100644
index 0000000000..ad0631670d
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/resources/note-grain-on-testing.js
@@ -0,0 +1,165 @@
+// Use a power of two to eliminate round-off converting from frames to time.
+let sampleRate = 32768;
+
+// How many grains to play.
+let numberOfTests = 100;
+
+// Duration of each grain to be played. Make a whole number of frames
+let duration = Math.floor(0.01 * sampleRate) / sampleRate;
+
+// A little extra bit of silence between grain boundaries. Must be a whole
+// number of frames.
+let grainGap = Math.floor(0.005 * sampleRate) / sampleRate;
+
+// Time step between the start of each grain. We need to add a little
+// bit of silence so we can detect grain boundaries
+let timeStep = duration + grainGap;
+
+// Time step between the start for each grain. Must be a whole number of
+// frames.
+let grainOffsetStep = Math.floor(0.001 * sampleRate) / sampleRate;
+
+// How long to render to cover all of the grains.
+let renderTime = (numberOfTests + 1) * timeStep;
+
+let context;
+let 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. The additional 1 is for any
+ // round-off errors.
+ let signalLength =
+ Math.floor(1 + sampleRate * (numberOfTests * grainOffsetStep + duration));
+
+ let buffer = context.createBuffer(2, signalLength, sampleRate);
+ let data = buffer.getChannelData(0);
+
+ for (let 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) {
+ let nSamples = data.length;
+
+ let startTime = [];
+ let endTime = [];
+ let lookForStart = true;
+
+ // Look through the rendered data to find the start and stop
+ // times of each grain.
+ for (let 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) {
+ let bufferSource = context.createBufferSource();
+
+ bufferSource.buffer = source;
+ bufferSource.connect(context.destination);
+ bufferSource.start(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) {
+ let startTimes = new Array(numberOfNotes);
+ let offsets = new Array(numberOfNotes);
+
+ for (let k = 0; k < numberOfNotes; ++k) {
+ let timeOffset = k * timeStep;
+ let 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, should) {
+ let startFrames = startEndFrames.start;
+ let endFrames = startEndFrames.end;
+
+ // Count of how many grains started at the incorrect time.
+ let errorCountStart = 0;
+
+ // Count of how many grains ended at the incorrect time.
+ let errorCountEnd = 0;
+
+ should(
+ startFrames.length == endFrames.length, 'Found all grain starts and ends')
+ .beTrue();
+
+ should(startFrames.length, 'Number of start frames').beEqualTo(numberOfTests);
+ should(endFrames.length, 'Number of end frames').beEqualTo(numberOfTests);
+
+ // Examine the start and stop times to see if they match our
+ // expectations.
+ for (let k = 0; k < startFrames.length; ++k) {
+ let expectedStart = timeToSampleFrame(k * timeStep, sampleRate);
+ // The end point is the duration.
+ let expectedEnd = expectedStart +
+ grainLengthInSampleFrames(k * grainOffsetStep, duration, sampleRate);
+
+ if (startFrames[k] != expectedStart)
+ ++errorCountStart;
+ if (endFrames[k] != expectedEnd)
+ ++errorCountEnd;
+
+ should([startFrames[k], endFrames[k]], 'Pulse ' + k + ' boundary')
+ .beEqualToArray([expectedStart, expectedEnd]);
+ }
+
+ // Check that all the grains started or ended at the correct time.
+ if (!errorCountStart) {
+ should(
+ startFrames.length, 'Number of grains that started at the correct time')
+ .beEqualTo(numberOfTests);
+ } else {
+ should(
+ errorCountStart,
+ 'Number of grains out of ' + numberOfTests +
+ 'that started at the wrong time')
+ .beEqualTo(0);
+ }
+
+ if (!errorCountEnd) {
+ should(endFrames.length, 'Number of grains that ended at the correct time')
+ .beEqualTo(numberOfTests);
+ } else {
+ should(
+ errorCountEnd,
+ 'Number of grains out of ' + numberOfTests +
+ ' that ended at the wrong time')
+ .beEqualTo(0);
+ }
+}