1 var sampleRate = 44100.0;
3 // HRTF extra frames. This is a magic constant currently in
4 // AudioBufferSourceNode::process that always extends the
5 // duration by this number of samples. See bug 77224
6 // (https://bugs.webkit.org/show_bug.cgi?id=77224).
7 var extraFramesHRTF = 512;
9 // How many grains to play.
10 var numberOfTests = 100;
12 // Duration of each grain to be played
15 // Time step between the start of each grain. We need to add a little
16 // bit of silence so we can detect grain boundaries and also account
17 // for the extra frames for HRTF.
18 var timeStep = duration + .005 + extraFramesHRTF / sampleRate;
20 // Time step between the start for each grain.
21 var grainOffsetStep = 0.001;
23 // How long to render to cover all of the grains.
24 var renderTime = (numberOfTests + 1) * timeStep;
29 // Create a buffer containing the data that we want. The function f
30 // returns the desired value at sample frame k.
31 function createSignalBuffer(context, f) {
33 // Make sure the buffer has enough data for all of the possible
34 // grain offsets and durations. Need to include the extra frames
35 // for HRTF. The additional 1 is for any round-off errors.
36 var signalLength = Math.floor(1 + extraFramesHRTF + sampleRate * (numberOfTests * grainOffsetStep + duration));
38 var buffer = context.createBuffer(2, signalLength, sampleRate);
39 var data = buffer.getChannelData(0);
41 for (var k = 0; k < signalLength; ++k) {
48 // From the data array, find the start and end sample frame for each
49 // grain. This depends on the data having 0's between grain, and
50 // that the grain is always strictly non-zero.
51 function findStartAndEndSamples(data) {
52 var nSamples = data.length;
56 var lookForStart = true;
58 // Look through the rendered data to find the start and stop
59 // times of each grain.
60 for (var k = 0; k < nSamples; ++k) {
62 // Find a non-zero point and record the start. We're not
63 // concerned with the value in this test, only that the
64 // grain started here.
65 if (renderedData[k]) {
70 // Find a zero and record the end of the grain.
71 if (!renderedData[k]) {
78 return {start : startTime, end : endTime};
81 function playGrain(context, source, time, offset, duration) {
82 var bufferSource = context.createBufferSource();
84 bufferSource.buffer = source;
85 bufferSource.connect(context.destination);
86 bufferSource.start(time, offset, duration);
89 // Play out all grains. Returns a object containing two arrays, one
90 // for the start time and one for the grain offset time.
91 function playAllGrains(context, source, numberOfNotes) {
92 var startTimes = new Array(numberOfNotes);
93 var offsets = new Array(numberOfNotes);
95 for (var k = 0; k < numberOfNotes; ++k) {
96 var timeOffset = k * timeStep;
97 var grainOffset = k * grainOffsetStep;
99 playGrain(context, source, timeOffset, grainOffset, duration);
100 startTimes[k] = timeOffset;
101 offsets[k] = grainOffset;
104 return { startTimes : startTimes, grainOffsetTimes : offsets };
107 // Verify that the start and end frames for each grain match our
108 // expected start and end frames.
109 function verifyStartAndEndFrames(startEndFrames) {
110 var startFrames = startEndFrames.start;
111 var endFrames = startEndFrames.end;
115 // Count of how many grains started at the incorrect time.
116 var errorCountStart = 0;
118 // Count of how many grains ended at the incorrect time.
119 var errorCountEnd = 0;
121 if (startFrames.length != endFrames.length) {
122 testFailed("Could not find the beginning or end of a grain.");
126 if (startFrames.length == numberOfTests && endFrames.length == numberOfTests) {
127 testPassed("Found all " + numberOfTests + " grains.");
129 testFailed("Did not find all " + numberOfTests + " grains.");
132 // Examine the start and stop times to see if they match our
134 for (var k = 0; k < startFrames.length; ++k) {
135 var expectedStart = timeToSampleFrame(k * timeStep, sampleRate);
136 // The end point is the duration, plus the extra frames
138 var expectedEnd = extraFramesHRTF + expectedStart + grainLengthInSampleFrames(k * grainOffsetStep, duration, sampleRate);
140 if (startFrames[k] != expectedStart) {
141 testFailed("Pulse " + k + " started at " + startFrames[k] + " but expected at " + expectedStart);
146 if (endFrames[k] != expectedEnd) {
147 testFailed("Pulse " + k + " ended at " + endFrames[k] + " but expected at " + expectedEnd);
153 // Check that all the grains started or ended at the correct time.
154 if (!errorCountStart) {
155 if (startFrames.length == numberOfTests) {
156 testPassed("All " + numberOfTests + " grains started at the correct time.");
158 testFailed("All grains started at the correct time, but only " + startFrames.length + " grains found.");
162 testFailed(errorCountStart + " out of " + numberOfTests + " grains started at the wrong time.");
166 if (!errorCountEnd) {
167 if (endFrames.length == numberOfTests) {
168 testPassed("All " + numberOfTests + " grains ended at the correct time.");
170 testFailed("All grains ended at the correct time, but only " + endFrames.length + " grains found.");
174 testFailed(errorCountEnd + " out of " + numberOfTests + " grains ended at the wrong time.");