| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| Test Constructor: PeriodicWave |
| </title> |
| <script src="../../imported/w3c/web-platform-tests/resources/testharness.js"></script> |
| <script src="../../resources/testharnessreport.js"></script> |
| <script src="../resources/audit-util.js"></script> |
| <script src="../resources/audit.js"></script> |
| <script src="../resources/audionodeoptions.js"></script> |
| </head> |
| <body> |
| <script id="layout-test-code"> |
| let context; |
| |
| let audit = Audit.createTaskRunner(); |
| |
| audit.define('initialize', (task, should) => { |
| context = initializeContext(should); |
| task.done(); |
| }); |
| |
| audit.define('invalid constructor', (task, should) => { |
| testInvalidConstructor(should, 'PeriodicWave', context); |
| task.done(); |
| }); |
| |
| audit.define('default constructor', (task, should) => { |
| should(() => { |
| node = new PeriodicWave(context); |
| }, 'node = new PeriodicWave(context)').notThrow(); |
| |
| task.done(); |
| }); |
| |
| audit.define('constructor with options', (task, should) => { |
| let node1; |
| let options = {real: [1, 1]}; |
| should( |
| () => { |
| node1 = new PeriodicWave(context, options); |
| }, |
| 'node = new PeriodicWave(context, ' + JSON.stringify(options) + ')') |
| .notThrow(); |
| should(node1 instanceof PeriodicWave, 'node1 instanceof PeriodicWave') |
| .beEqualTo(true); |
| |
| let node2; |
| options = {imag: [1, 1]}; |
| should( |
| () => { |
| node2 = new PeriodicWave(context, options); |
| }, |
| 'node2 = new PeriodicWave(context, ' + JSON.stringify(options) + |
| ')') |
| .notThrow(); |
| should(node2 instanceof PeriodicWave, 'node2 instanceof PeriodicWave') |
| .beEqualTo(true); |
| |
| let node3; |
| options = {real: [1, 2], imag: [1, 1]}; |
| should( |
| () => { |
| node3 = new PeriodicWave(context, options); |
| }, |
| 'node3 = new PeriodicWave(context, ' + JSON.stringify(options) + |
| ')') |
| .notThrow(); |
| should(node3 instanceof PeriodicWave, 'node3 instanceof PeriodicWave') |
| .beEqualTo(true); |
| |
| task.done(); |
| }); |
| |
| // The following test that the correct waveforms are produced when various |
| // possible PeriodicWave options are used. These are needed because it's |
| // the only way to tell if the various options were correctly applied. |
| |
| // TODO(rtoy): These functionality tests should be moved out to a separate |
| // file. |
| audit.define('1: real periodicwave test', (task, should) => { |
| verifyPeriodicWaveOutput( |
| should, {real: [0, 2]}, generateReference(Math.cos), 2.7143e-5) |
| .then(() => task.done()); |
| }); |
| |
| audit.define('2: real periodicwave test', (task, should) => { |
| verifyPeriodicWaveOutput( |
| should, {real: [0, 2], disableNormalization: false}, |
| generateReference(Math.cos), 2.7143e-5) |
| .then(() => task.done()); |
| }); |
| |
| audit.define('3: real periodicwave test', (task, should) => { |
| verifyPeriodicWaveOutput( |
| should, {real: [0, 2], disableNormalization: true}, |
| generateReference(x => 2 * Math.cos(x)), 5.4285e-5) |
| .then(() => task.done()); |
| }); |
| |
| audit.define('1: imag periodicwave test', (task, should) => { |
| verifyPeriodicWaveOutput( |
| should, {imag: [0, 2]}, generateReference(Math.sin), 2.7262e-5) |
| .then(() => task.done()); |
| }); |
| |
| audit.define('2: imag periodicwave test', (task, should) => { |
| verifyPeriodicWaveOutput( |
| should, {imag: [0, 2], disableNormalization: false}, |
| generateReference(Math.sin), 2.7262e-5) |
| .then(() => task.done()); |
| }); |
| |
| audit.define('3: imag periodicwave test', (task, should) => { |
| verifyPeriodicWaveOutput( |
| should, {imag: [0, 2], disableNormalization: true}, |
| generateReference(x => 2 * Math.sin(x)), 5.4524-5) |
| .then(() => task.done()); |
| }); |
| |
| audit.define('1: real/imag periodicwave test', (task, should) => { |
| verifyPeriodicWaveOutput( |
| should, { |
| real: [0, 1], |
| imag: [0, 1], |
| }, |
| generateReference(x => Math.SQRT1_2 * (Math.sin(x) + Math.cos(x))), |
| 3.8371e-5) |
| .then(() => task.done()); |
| }); |
| |
| audit.define('2: real/imag periodicwave test', (task, should) => { |
| verifyPeriodicWaveOutput( |
| should, {real: [0, 1], imag: [0, 1], disableNormalization: false}, |
| generateReference(x => Math.SQRT1_2 * (Math.sin(x) + Math.cos(x))), |
| 2.7225e-5) |
| .then(() => task.done()); |
| }); |
| |
| audit.define('3: real/imag periodicwave test', (task, should) => { |
| verifyPeriodicWaveOutput( |
| should, {real: [0, 1], imag: [0, 1], disableNormalization: true}, |
| generateReference(x => Math.sin(x) + Math.cos(x)), 3.8501e-5) |
| .then(() => task.done()); |
| }); |
| |
| // Returns a function that generates the expected reference array where |
| // the samples are generated by the function |gen|. |
| function generateReference(gen) { |
| return (length, freq, sampleRate) => { |
| let expected = new Float32Array(length); |
| let omega = 2 * Math.PI * freq / sampleRate; |
| for (let k = 0; k < length; ++k) { |
| expected[k] = gen(omega * k); |
| } |
| return expected; |
| }; |
| } |
| |
| // Verify that an oscillator constructed from the given periodic wave |
| // produces the expected result. |
| function verifyPeriodicWaveOutput( |
| should, waveOptions, expectedFunction, threshold) { |
| let node; |
| // Rather arbitrary sample rate and render length. Length doesn't have |
| // to be very long. |
| let sampleRate = 48000; |
| let renderLength = 0.25; |
| let testContext = |
| new OfflineAudioContext(1, renderLength * sampleRate, sampleRate); |
| |
| let options = { |
| periodicWave: new PeriodicWave(testContext, waveOptions) |
| }; |
| node = new OscillatorNode(testContext, options); |
| |
| // Create the graph |
| node.connect(testContext.destination); |
| node.start(); |
| |
| return testContext.startRendering().then(function(resultBuffer) { |
| let actual = resultBuffer.getChannelData(0); |
| let expected = expectedFunction( |
| actual.length, node.frequency.value, testContext.sampleRate); |
| // Actual must match expected to within the (experimentally) |
| // determined threshold. |
| let message = ''; |
| if (waveOptions.disableNormalization != undefined) |
| message = |
| 'disableNormalization: ' + waveOptions.disableNormalization; |
| if (waveOptions.real) { |
| if (message.length > 0) |
| message += ', ' |
| message += 'real: [' + waveOptions.real + ']'; |
| } |
| if (waveOptions.imag) { |
| if (message.length > 0) |
| message += ', ' |
| message += 'imag: [' + waveOptions.imag + ']'; |
| } |
| should(actual, 'Oscillator with periodicWave {' + message + '}') |
| .beCloseToArray(expected, {absoluteThreshold: threshold}); |
| }); |
| } |
| |
| // Verify that the default PeriodicWave produces a sine wave. Use a |
| // 2-channel context to verify this. |
| function sineWaveTest(should, waveFun, message) { |
| // Channel 0 is the output from the PeriodicWave, and channel 1 is the |
| // reference oscillator output. |
| let context = new OfflineAudioContext(2, 40000, 40000); |
| let oscRef = |
| new OscillatorNode(context, {type: 'sine', frequency: 440}); |
| let wave = waveFun(context); |
| let oscTest = |
| new OscillatorNode(context, {frequency: 440, periodicWave: wave}); |
| |
| let merger = new ChannelMergerNode(context, {numberOfInputs: 2}); |
| |
| oscRef.connect(merger, 0, 1); |
| oscTest.connect(merger, 0, 0); |
| |
| merger.connect(context.destination); |
| |
| oscRef.start(); |
| oscTest.start(); |
| |
| return context.startRendering().then(output => { |
| // The output from the two channels MUST match exactly. |
| let actual = output.getChannelData(0); |
| let ref = output.getChannelData(1); |
| |
| should(actual, message).beEqualToArray(ref); |
| }); |
| } |
| |
| audit.define('default wave', (task, should) => { |
| // Verify that the default PeriodicWave produces a sine wave. |
| sineWaveTest( |
| should, (context) => new PeriodicWave(context), |
| 'new PeriodicWave(context) output') |
| .then(() => task.done()); |
| }); |
| |
| audit.define('default wave (with dict)', (task, should) => { |
| // Verify that the default PeriodicWave produces a sine wave when the |
| // PeriodicWaveOptions dictionary is given, but real and imag members |
| // are not set. |
| sineWaveTest( |
| should, (context) => new PeriodicWave(context, {foo: 42}), |
| 'new PeriodicWave(context, {foo: 42}) output') |
| .then(() => task.done()); |
| }); |
| |
| audit.run(); |
| </script> |
| </body> |
| </html> |