1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
<!doctype html>
<html>
<head>
<title>
Test Basic Oscillator Sine Wave Test
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>
<body>
<script>
// Don't change the sample rate. The tests below depend on this sample
// rate to cover all the cases in Chrome's implementation. But the tests
// are general and apply to any browser.
const sampleRate = 44100;
// Only need a few samples for testing, so just use two renders.
const durationFrames = 2 * RENDER_QUANTUM_FRAMES;
let audit = Audit.createTaskRunner();
// The following tests verify that the oscillator produces the same
// results as the mathematical oscillators. We choose sine wave and a
// custom wave because we know they're bandlimited and won't change with
// the frequency.
//
// The tests for 1 and 2 Hz are intended to test Chrome's interpolation
// algorithm, but are still generally applicable to any browser.
audit.define(
{label: 'Test 0', description: 'Sine wave: 100 Hz'},
async (task, should) => {
let context = new OfflineAudioContext(
{length: durationFrames, sampleRate: sampleRate});
const freqHz = 100;
let src =
new OscillatorNode(context, {type: 'sine', frequency: freqHz});
src.connect(context.destination);
src.start();
let renderedBuffer = await context.startRendering();
checkResult(should, renderedBuffer, context, {
freqHz: freqHz,
a1: 0,
b1: 1,
prefix: 'Sine',
threshold: 1.8045e-6,
snrThreshold: 118.91
});
task.done();
});
audit.define(
{label: 'Test 1', description: 'Sine wave: -100 Hz'},
async (task, should) => {
let context = new OfflineAudioContext(
{length: durationFrames, sampleRate: sampleRate});
const freqHz = -100;
let src =
new OscillatorNode(context, {type: 'sine', frequency: freqHz});
src.connect(context.destination);
src.start();
let renderedBuffer = await context.startRendering();
checkResult(should, renderedBuffer, context, {
freqHz: freqHz,
a1: 0,
b1: 1,
prefix: 'Sine',
threshold: 4.7684e-7,
snrThreshold: 130.95
});
task.done();
});
audit.define(
{label: 'Test 2', description: 'Sine wave: 2 Hz'},
async (task, should) => {
let context = new OfflineAudioContext(
{length: durationFrames, sampleRate: sampleRate});
const freqHz = 2;
let src =
new OscillatorNode(context, {type: 'sine', frequency: freqHz});
src.connect(context.destination);
src.start();
let renderedBuffer = await context.startRendering();
checkResult(should, renderedBuffer, context, {
freqHz: freqHz,
a1: 0,
b1: 1,
prefix: 'Sine',
threshold: 1.4516e-7,
snrThreshold: 119.93
});
task.done();
});
audit.define(
{label: 'Test 3', description: 'Sine wave: 1 Hz'},
async (task, should) => {
let context = new OfflineAudioContext(
{length: durationFrames, sampleRate: sampleRate});
const freqHz = 1;
let src =
new OscillatorNode(context, {type: 'sine', frequency: freqHz});
src.connect(context.destination);
src.start();
let renderedBuffer = await context.startRendering();
checkResult(should, renderedBuffer, context, {
freqHz: freqHz,
a1: 0,
b1: 1,
prefix: 'Sine',
threshold: 1.4157e-7,
snrThreshold: 112.22
});
task.done();
});
audit.define(
{label: 'Test 4', description: 'Custom wave: 100 Hz'},
async (task, should) => {
let context = new OfflineAudioContext(
{length: durationFrames, sampleRate: sampleRate});
const freqHz = 100;
let wave = new PeriodicWave(
context,
{real: [0, 1], imag: [0, 1], disableNormalization: true});
let src = new OscillatorNode(
context,
{type: 'custom', frequency: freqHz, periodicWave: wave});
src.connect(context.destination);
src.start();
let renderedBuffer = await context.startRendering();
checkResult(should, renderedBuffer, context, {
freqHz: freqHz,
a1: 1,
b1: 1,
prefix: 'Custom',
threshold: 1.8478e-6,
snrThreshold: 122.43
});
task.done();
});
audit.define(
{label: 'Test 5', description: 'Custom wave: 1 Hz'},
async (task, should) => {
let context = new OfflineAudioContext(
{length: durationFrames, sampleRate: sampleRate});
const freqHz = 1;
let wave = new PeriodicWave(
context,
{real: [0, 1], imag: [0, 1], disableNormalization: true});
let src = new OscillatorNode(
context,
{type: 'custom', frequency: freqHz, periodicWave: wave});
src.connect(context.destination);
src.start();
let renderedBuffer = await context.startRendering();
checkResult(should, renderedBuffer, context, {
freqHz: freqHz,
a1: 1,
b1: 1,
prefix: 'Custom',
threshold: 4.7684e-7,
snrThreshold: 138.76
});
task.done();
});
audit.run();
function waveForm(context, freqHz, a1, b1, nsamples) {
let buffer =
new AudioBuffer({length: nsamples, sampleRate: context.sampleRate});
let signal = buffer.getChannelData(0);
const omega = 2 * Math.PI * freqHz / context.sampleRate;
for (let k = 0; k < nsamples; ++k) {
signal[k] = a1 * Math.cos(omega * k) + b1 * Math.sin(omega * k);
}
return buffer;
}
function checkResult(should, renderedBuffer, context, options) {
let {freqHz, a1, b1, prefix, threshold, snrThreshold} = options;
let actual = renderedBuffer.getChannelData(0);
let expected =
waveForm(context, freqHz, a1, b1, actual.length).getChannelData(0);
should(actual, `${prefix}: ${freqHz} Hz`).beCloseToArray(expected, {
absoluteThreshold: threshold
});
let snr = 10 * Math.log10(computeSNR(actual, expected));
should(snr, `${prefix}: SNR (db)`).beGreaterThanOrEqualTo(snrThreshold);
}
</script>
</body>
</html>
|