| var sampleRate = 44100.0; |
| |
| // 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. |
| var timeStep = duration + .005; |
| |
| // 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 + sampleRate * (numberOfTests * grainOffsetStep + duration)); |
| |
| var buffer = context.createBuffer(2, signalLength, sampleRate); |
| var data = buffer.getChannelData(0); |
| |
| 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.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) { |
| 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 = 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; |
| } |