| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| Test Different PeriodicWave Lengths at Different Sample Rates |
| </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> |
| </head> |
| <body> |
| <script id="layout-test-code"> |
| // Test PeriodicWave objects with varying number of coefficients at |
| // different sample rates. Basically, verify that the coefficients are |
| // used at the appropriate sample rates. This is done by comparing the |
| // outputs of two periodic waves used in oscillators. The output should |
| // either be exactly zero or not depending on whether the coefficients |
| // were used. |
| |
| let renderLength = 1; |
| let context; |
| |
| let audit = Audit.createTaskRunner(); |
| |
| // The set of Audit tests to be run to verify that PeriodicWave is using |
| // the correct number of coefficients. The name does not have to be |
| // unique; the index of the entry is appended to the test. Every entry |
| // (except the last) needs sampleRate, bigWave, smallWave, and verifier |
| // values. |
| |
| let testSet = [ |
| // Tests for contexts at 192 kHz. |
| |
| // Test that we use more than 2048 Fourier coefficients at 192 kHz |
| // sample rate. Basically verifies that 8192 is acceptable. |
| { |
| name: '192khz-test-1', |
| sampleRate: 192000, |
| bigWave: 8192, |
| smallWave: 2048, |
| verifier: resultShouldBeNonZero |
| }, |
| |
| // Test that we use at least 2049 Fourier coefficients at 192 kHz sample |
| // rate. |
| { |
| name: '192khz-test-2', |
| sampleRate: 192000, |
| bigWave: 2049, |
| smallWave: 2048, |
| verifier: resultShouldBeNonZero |
| }, |
| |
| // Test that we use all 8192 Fourier coefficients at 192 kHz sample |
| // rate. |
| { |
| name: '192khz-test-3', |
| sampleRate: 192000, |
| bigWave: 8192, |
| smallWave: 8191, |
| verifier: resultShouldBeNonZero |
| }, |
| |
| // Tests for contexts at 48 kHz. |
| |
| // Test that we do not use more than 2048 Fourier coefficients at 48 |
| // kHz. This depends on the internal implementation where, for backward |
| // compatibility and speed, we only use 2048 coefficients at 48 kHz. |
| // (This is also true for rates below 88.2 kHz.) Also tests that 8192 |
| // coefficients are allowed (but not all coefficients are used, of |
| // course). |
| { |
| name: '48khz-test-1', |
| sampleRate: 48000, |
| bigWave: 8192, |
| smallWave: 2048, |
| verifier: resultShouldBeZero |
| }, |
| |
| // Test that we do not use more than 2048 Fourier coefficients. |
| { |
| name: '48khz-test-2', |
| sampleRate: 48000, |
| bigWave: 2049, |
| smallWave: 2048, |
| verifier: resultShouldBeZero |
| }, |
| |
| // It's not immediately clear with single-preicison arithmetic that we |
| // can distinguish between 2049 and 2048 coefficients, so do one more |
| // test with slightly more coefficients. |
| { |
| name: '48khz-test-3', |
| sampleRate: 48000, |
| bigWave: (2 + 1 / 64) * 1024, |
| smallWave: 2048, |
| verifier: resultShouldBeZero |
| }, |
| |
| // Test that we use at least 2048 Fourier coefficients at 48 kHz. |
| // Ideally we want to compare 2047 and 2048 coefficients, but |
| // single-precision arithmetic makes the resulting waveforms the same. |
| // Hence use a smaller value that produces different waveforms. |
| { |
| name: '48khz-test-4', |
| sampleRate: 48000, |
| bigWave: 2048, |
| smallWave: 2046, |
| verifier: resultShouldBeNonZero |
| }, |
| |
| // Tests for contexts at 24 kHz. |
| |
| // Test that we do not use more than 1024 Fourier coefficients at 24 |
| // kHz. |
| { |
| name: '24khz-test-1', |
| sampleRate: 24000, |
| bigWave: 8192, |
| smallWave: 1024, |
| verifier: resultShouldBeZero |
| }, |
| |
| // Test that we do not use more than 1024 Fourier coefficients at 24 |
| // kHz. |
| { |
| name: '24khz-test-2', |
| sampleRate: 24000, |
| bigWave: 1025, |
| smallWave: 1024, |
| verifier: resultShouldBeZero |
| }, |
| |
| // Test that we use at least 1024 Fourier coefficients at 24 kHz. |
| // Again, 1023 and 1024 produce the same waveforms in single-precisiion |
| // so use a smaller wave table size. |
| { |
| name: '24khz-test-3', |
| sampleRate: 24000, |
| bigWave: 1024, |
| smallWave: 1022, |
| verifier: resultShouldBeNonZero |
| }, |
| ]; |
| |
| function generatePrefix(sampleRate, bigLength, smallLength) { |
| return 'At ' + (sampleRate / 1000) + ' kHz, PeriodicWave with ' + |
| bigLength + ' coefficients vs ' + smallLength + ': '; |
| } |
| |
| // Returns a function the verifies that the result is zero. The |
| // parameters control what is printed in the messages. |
| function resultShouldBeZero(should, sampleRate, bigLength, smallLength) { |
| return function(buffer) { |
| let prefix = generatePrefix(sampleRate, bigLength, smallLength); |
| should(isBufferZero(buffer), prefix + 'are identical') |
| .beEqualTo(true); |
| } |
| } |
| |
| // Returns a function the verifies that the result is non-zero. The |
| // parameters control what is printed in the messages. |
| function resultShouldBeNonZero( |
| should, sampleRate, bigLength, smallLength) { |
| return function(buffer) { |
| let prefix = generatePrefix(sampleRate, bigLength, smallLength); |
| should(!isBufferZero(buffer), prefix + 'are different') |
| .beEqualTo(true); |
| } |
| } |
| |
| // Creates a function that is used to run an Audit test for a given sample |
| // rate, periodic wave sizes, and verifier. |
| function createAuditTestFunction( |
| sampleRate, bigLength, smallLength, verifier) { |
| return (task, should) => { |
| // Create the audio graph, render it, and then verify that the output |
| // is the expected result. |
| createAudioGraph(sampleRate, bigLength, smallLength); |
| |
| return context.startRendering() |
| .then(verifier(should, sampleRate, bigLength, smallLength)) |
| .then(() => task.done()); |
| } |
| } |
| |
| // Create the audio graph for the test. |
| function createAudioGraph( |
| sampleRate, bigPeriodicWaveLength, smallPeriodicWaveLength) { |
| context = |
| new OfflineAudioContext(1, renderLength * sampleRate, sampleRate); |
| |
| // Two PeriodicWave objects are created with different sizes (small and |
| // big). The contents are the same except that the samll sized |
| // PeriodicWave has fewer coefficients. |
| let smallWaveRealCoef = new Float32Array(smallPeriodicWaveLength); |
| let smallWaveImagCoef = new Float32Array(smallPeriodicWaveLength); |
| let bigWaveRealCoef = new Float32Array(bigPeriodicWaveLength); |
| let bigWaveImagCoef = new Float32Array(bigPeriodicWaveLength); |
| |
| // Set up the Fourier coefficients for a sawtooth wave. We use |
| // sawtooth because all coefficients are non-zero |
| bigWaveImagCoef[0] = 0; |
| for (let k = 1; k < bigPeriodicWaveLength; k++) { |
| let piFactor = 2 / (Math.PI * k); |
| bigWaveImagCoef[k] = piFactor * ((k % 2 === 0) ? -1 : 1); |
| if (k < smallPeriodicWaveLength) |
| smallWaveImagCoef[k] = bigWaveImagCoef[k]; |
| } |
| |
| let smallPeriodicWave = |
| context.createPeriodicWave(smallWaveRealCoef, smallWaveImagCoef); |
| let bigPeriodicWave = |
| context.createPeriodicWave(bigWaveRealCoef, bigWaveImagCoef); |
| |
| // Create oscillators using these PeriodicWave's. |
| let smallOscillator = context.createOscillator(); |
| let bigOscillator = context.createOscillator(); |
| |
| smallOscillator.setPeriodicWave(smallPeriodicWave); |
| bigOscillator.setPeriodicWave(bigPeriodicWave); |
| |
| // Use a frequency of 1 Hz to make the distinction easier. Can't tell |
| // from this test, but if you plot the signals from these oscillators, |
| // it's very clear that they are different. |
| smallOscillator.frequency.value = 1; |
| bigOscillator.frequency.value = 1; |
| |
| // The desired output is the difference between these oscillators. |
| let gain = context.createGain(); |
| gain.gain.value = -1; |
| smallOscillator.connect(gain); |
| |
| gain.connect(context.destination); |
| bigOscillator.connect(context.destination); |
| |
| // Start the oscillators. |
| smallOscillator.start(); |
| bigOscillator.start(); |
| } |
| |
| // Return true if the buffer is exactly zero. |
| function isBufferZero(buffer) { |
| if (buffer.getChannelData(0).find(function(x) { |
| return x != 0; |
| })) |
| return false; |
| return true; |
| } |
| |
| // Ensure the actual Audit test name is unique by prepending an index to |
| // the provided test name. |
| function actualTestName(name, index) { |
| return index + ':' + name; |
| } |
| |
| // Define the tasks based on the entries in testSet. |
| function defineAuditTests() { |
| for (let k = 0; k < testSet.length; ++k) { |
| let {name, sampleRate, bigWave, smallWave, verifier} = testSet[k]; |
| let actualName = actualTestName(name, k); |
| audit.define( |
| actualName, |
| createAuditTestFunction( |
| sampleRate, bigWave, smallWave, verifier)); |
| } |
| } |
| |
| defineAuditTests(); |
| audit.run(); |
| </script> |
| </body> |
| </html> |