blob: b3b531da2a326f3e13664975c5dcd515854cc3f9 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>
AudioBufferSourceNode - playbackRate test
</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../resources/audit-util.js"></script>
<script src="../resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
let audit = Audit.createTaskRunner();
// Any sample rate mutiple of 128 is valid for this test, but here it uses
// 48000Hz because it is a commonly used number that happens to be
// multiple of 128.
let sampleRate = 32768;
// The test iterates over 60 pitches starting from 36. (MIDI pitch of C2)
let fundamentalPitch = 36;
let numberOfPitches = 60;
let noteDuration = Math.floor(0.025 * sampleRate) / sampleRate;
let totalDuration = noteDuration * numberOfPitches;
// Test constraints for each octave.
let testConstraints = [
{thresholdSNR: 97.215, thresholdDiffULP: 0.6446},
{thresholdSNR: 97.212, thresholdDiffULP: 0.6446},
{thresholdSNR: 97.217, thresholdDiffULP: 0.6446},
{thresholdSNR: 97.196, thresholdDiffULP: 0.6485},
{thresholdSNR: 97.074, thresholdDiffULP: 0.6915}
];
function pitchToFrequency(midiPitch) {
return 440 * Math.pow(2, (Math.floor(midiPitch) - 69) / 12);
}
function pitchDiffToPlaybackRate(midiPitchDiff) {
return Math.pow(2, midiPitchDiff / 12);
}
function createSineWaveBuffer(context, frequency, duration) {
let buffer = context.createBuffer(1, duration * sampleRate, sampleRate);
let data = buffer.getChannelData(0);
let omega = 2 * Math.PI * frequency / sampleRate;
for (let i = 0; i < data.length; i++)
data[i] = Math.sin(omega * i);
return buffer;
}
// This is the fundamental buffer for playbackRate modulation. The
// duration of this buffer is arbitrary but long enough to produce the
// sound without running short.
let fundamentalBuffer;
// A unit test consists of 2 sources: the 'actual' source runs a buffer
// with the playback rate modulated and the 'expected' source runs a
// mathmatically generated sound buffer.
function runUnitTest(context, noteStart, notePitch) {
let actualSrc = context.createBufferSource();
let expectedSrc = context.createBufferSource();
let merger = context.createChannelMerger(2);
actualSrc.buffer = fundamentalBuffer;
expectedSrc.buffer = createSineWaveBuffer(
context, pitchToFrequency(notePitch), noteDuration);
actualSrc.playbackRate.value =
pitchDiffToPlaybackRate(notePitch - fundamentalPitch);
actualSrc.connect(merger, 0, 0);
expectedSrc.connect(merger, 0, 1);
merger.connect(context.destination);
actualSrc.start(noteStart);
actualSrc.stop(noteStart + noteDuration);
expectedSrc.start(noteStart);
expectedSrc.stop(noteStart + noteDuration);
}
// Test if AudioBufferSourceNode.playbackRate can playback at different
// rates properly.
audit.define('playbackrate-test', (task, should) => {
let context =
new OfflineAudioContext(2, totalDuration * sampleRate, sampleRate);
fundamentalBuffer = createSineWaveBuffer(
context, pitchToFrequency(fundamentalPitch), totalDuration);
// Schedule tests up to 60 pitches above from the fundamental pitch.
for (let iteration = 0; iteration < numberOfPitches; iteration++)
runUnitTest(
context, noteDuration * iteration, fundamentalPitch + iteration);
// Once the rendering is complete, split the buffer into 5 octaves. Then
// perform the SNR and the maximum difference ULP check for each octave
// with different constraints.
context.startRendering()
.then(function(renderedBuffer) {
let actual = renderedBuffer.getChannelData(0);
let expected = renderedBuffer.getChannelData(1);
let octaveLength = Math.floor(noteDuration * 12 * sampleRate);
for (let i = 0; i < numberOfPitches / 12; i++) {
let start = i * octaveLength;
let end = (i + 1) * octaveLength;
let octaveActual = actual.subarray(start, end);
let octaveExpected = expected.subarray(start, end);
compareBuffersWithConstraints(
should, octaveActual, octaveExpected, {
prefix: i,
thresholdSNR: testConstraints[i].thresholdSNR,
thresholdDiffULP: testConstraints[i].thresholdDiffULP
});
}
})
.then(() => task.done());
});
audit.run();
</script>
</body>
</html>