<!DOCTYPE html>
<html>
<head>
<title>
Test Custom Oscillator at Very Low Frequency
</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">
// Create a custom oscillator and verify that the parts of a periodic wave
// that should be ignored really are ignored.
let sampleRate = 48000;
// The desired frequency of the oscillator. The value to be used depends
// on the implementation of the PeriodicWave and should be less than then
// lowest fundamental frequency. The lowest frequency is the Nyquist
// frequency divided by the max number of coefficients used for the FFT.
// In the current implementation, the max number of coefficients is 2048
// (for a sample rate of 48 kHz) so the lowest frequency is 24000/2048 =
// 11.78 Hz.
let desiredFrequencyHz = 1;
// Minimum allowed SNR between the actual oscillator and the expected
// result. Experimentally determined.
let snrThreshold = 130;
let context;
let osc;
let actual;
let audit = Audit.createTaskRunner();
// Compute the SNR between the actual result and expected cosine wave
function checkCosineResult(should, result, freq, sampleRate) {
let signal = 0;
let noise = 0;
let omega = 2 * Math.PI * freq / sampleRate;
actual = result.getChannelData(0);
for (let k = 0; k < actual.length; ++k) {
let x = Math.cos(omega * k);
let diff = x - actual[k];
signal += x * x;
noise += diff * diff;
}
let snr = 10 * Math.log10(signal / noise);
should(snr, 'SNR of ' + desiredFrequencyHz + ' Hz sine wave')
.beGreaterThanOrEqualTo(snrThreshold);
}
audit.define('low-freq-oscillator', (task, should) => {
context = new OfflineAudioContext(1, sampleRate, sampleRate);
osc = context.createOscillator();
// Create the custom oscillator. For simplicity of testing, we use just
// a cosine wave, but the initial elements of the real and imaginary
// parts are explicitly set to non-zero to test that they are ignored.
let r = new Float32Array(2);
let i = new Float32Array(2);
r[0] = 1; // DC component to be ignored
r[1] = 1; // Fundamental
i[0] = 1; // Sine term that doesn't actually exist in a Fourier series
i[1] = 0;
let wave = context.createPeriodicWave(r, i);
osc.setPeriodicWave(wave);
osc.frequency.value = desiredFrequencyHz;
osc.connect(context.destination);
osc.start();
context.startRendering()
.then(function(buffer) {
checkCosineResult(should, buffer, desiredFrequencyHz, sampleRate);
})
.then(() => task.done());
});
audit.run();
</script>
</body>
</html>