| OLD | NEW |
| 1 <!doctype html> | 1 <!DOCTYPE html> |
| 2 <html> | 2 <html> |
| 3 <head> | 3 <head> |
| 4 <title>Test Sampling of Oscillator Start Times</title> | 4 <title> |
| 5 Test Sampling of Oscillator Start Times |
| 6 </title> |
| 5 <script src="../../resources/testharness.js"></script> | 7 <script src="../../resources/testharness.js"></script> |
| 6 <script src="../../resources/testharnessreport.js"></script> | 8 <script src="../../resources/testharnessreport.js"></script> |
| 7 <script src="../resources/audit-util.js"></script> | 9 <script src="../resources/audit-util.js"></script> |
| 8 <script src="../resources/audit.js"></script> | 10 <script src="../resources/audit.js"></script> |
| 9 </head> | 11 </head> |
| 10 | |
| 11 <body> | 12 <body> |
| 12 <script> | 13 <script id="layout-test-code"> |
| 13 // Experimentation indicates that this sample rate with a 440 Hz | 14 // Experimentation indicates that this sample rate with a 440 Hz |
| 14 // oscillator makes for a large difference in the difference signal if the | 15 // oscillator makes for a large difference in the difference signal if the |
| 15 // oscillator start isn't sampled correctly. | 16 // oscillator start isn't sampled correctly. |
| 16 let defaultSampleRate = 24000; | 17 let defaultSampleRate = 24000; |
| 17 let renderDuration = 1; | 18 let renderDuration = 1; |
| 18 let renderFrames = renderDuration * defaultSampleRate; | 19 let renderFrames = renderDuration * defaultSampleRate; |
| 19 | 20 |
| 20 let audit = Audit.createTaskRunner(); | 21 let audit = Audit.createTaskRunner(); |
| 21 | 22 |
| 22 audit.define( | 23 audit.define( |
| 23 { | 24 { |
| 24 label: 'basic test small', | 25 label: 'basic test small', |
| 25 description: 'Start oscillator slightly past a sample frame' | 26 description: 'Start oscillator slightly past a sample frame' |
| 26 }, | 27 }, |
| 27 function(task, should) { | 28 function(task, should) { |
| 28 testStartSampling(should, 1.25, { | 29 testStartSampling(should, 1.25, { |
| 29 error: 1.0842e-4, | 30 error: 1.0842e-4, |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 // the internal implementation. | 76 // the internal implementation. |
| 76 testStartWithGain(should, 48000, { | 77 testStartWithGain(should, 48000, { |
| 77 error: 4.1724e-7, | 78 error: 4.1724e-7, |
| 78 snrThreshold: 137.536 | 79 snrThreshold: 137.536 |
| 79 }).then(task.done.bind(task)); | 80 }).then(task.done.bind(task)); |
| 80 }); | 81 }); |
| 81 | 82 |
| 82 function testStartSampling(should, startFrame, thresholds) { | 83 function testStartSampling(should, startFrame, thresholds) { |
| 83 // Start the oscillator in the middle of a sample frame and compare | 84 // Start the oscillator in the middle of a sample frame and compare |
| 84 // against the theoretical result. | 85 // against the theoretical result. |
| 85 let context = new OfflineAudioContext(1, renderFrames, | 86 let context = |
| 86 defaultSampleRate); | 87 new OfflineAudioContext(1, renderFrames, defaultSampleRate); |
| 87 let osc = context.createOscillator(); | 88 let osc = context.createOscillator(); |
| 88 osc.connect(context.destination); | 89 osc.connect(context.destination); |
| 89 osc.start(startFrame / context.sampleRate); | 90 osc.start(startFrame / context.sampleRate); |
| 90 | 91 |
| 91 return context.startRendering().then(function (result) { | 92 return context.startRendering().then(function(result) { |
| 92 let actual = result.getChannelData(0); | 93 let actual = result.getChannelData(0); |
| 93 let expected = new Array(actual.length); | 94 let expected = new Array(actual.length); |
| 94 expected.fill(0); | 95 expected.fill(0); |
| 95 | 96 |
| 96 // The expected curve is | 97 // The expected curve is |
| 97 // | 98 // |
| 98 // sin(2*pi*f*(t-t0)) | 99 // sin(2*pi*f*(t-t0)) |
| 99 // | 100 // |
| 100 // where f is the oscillator frequency and t0 is the start time. | 101 // where f is the oscillator frequency and t0 is the start time. |
| 101 let actualStart = Math.ceil(startFrame); | 102 let actualStart = Math.ceil(startFrame); |
| 102 let omega = 2 * Math.PI * osc.frequency.value / context.sampleRate; | 103 let omega = 2 * Math.PI * osc.frequency.value / context.sampleRate; |
| 103 for (let k = actualStart; k < actual.length; ++k) { | 104 for (let k = actualStart; k < actual.length; ++k) { |
| 104 expected[k] = Math.sin(omega * (k - startFrame)); | 105 expected[k] = Math.sin(omega * (k - startFrame)); |
| 105 } | 106 } |
| 106 | 107 |
| 107 let prefix = "Oscillator.start(" + startFrame + " frames)"; | 108 let prefix = 'Oscillator.start(' + startFrame + ' frames)'; |
| 108 should(actual, prefix) | 109 should(actual, prefix).beCloseToArray(expected, { |
| 109 .beCloseToArray(expected, { | 110 absoluteThreshold: thresholds.error |
| 110 absoluteThreshold: thresholds.error | 111 }); |
| 111 }); | |
| 112 let snr = 10 * Math.log10(computeSNR(actual, expected)); | 112 let snr = 10 * Math.log10(computeSNR(actual, expected)); |
| 113 should(snr, prefix + ": SNR (dB)") | 113 should(snr, prefix + ': SNR (dB)') |
| 114 .beGreaterThanOrEqualTo(thresholds.snrThreshold); | 114 .beGreaterThanOrEqualTo(thresholds.snrThreshold); |
| 115 }) | 115 }) |
| 116 } | 116 } |
| 117 | 117 |
| 118 function testStartWithGain(should, sampleRate, thresholds) { | 118 function testStartWithGain(should, sampleRate, thresholds) { |
| 119 // Test consists of starting a cosine wave with a quarter wavelength | 119 // Test consists of starting a cosine wave with a quarter wavelength |
| 120 // delay and comparing that with a sine wave that has the initial | 120 // delay and comparing that with a sine wave that has the initial |
| 121 // quarter wavelength zeroed out. These should be equal. | 121 // quarter wavelength zeroed out. These should be equal. |
| 122 | 122 |
| 123 let context = new OfflineAudioContext(3, renderFrames, sampleRate); | 123 let context = new OfflineAudioContext(3, renderFrames, sampleRate); |
| 124 let osc = context.createOscillator(); | 124 let osc = context.createOscillator(); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 136 g.gain.setValueAtTime(0, 0); | 136 g.gain.setValueAtTime(0, 0); |
| 137 g.gain.setValueAtTime(1, quarterWaveTime); | 137 g.gain.setValueAtTime(1, quarterWaveTime); |
| 138 osc.connect(g); | 138 osc.connect(g); |
| 139 g.connect(merger, 0, 2); | 139 g.connect(merger, 0, 2); |
| 140 g.connect(merger, 0, 0); | 140 g.connect(merger, 0, 0); |
| 141 | 141 |
| 142 // Cosine wave oscillator with starting after a quarter wave length. | 142 // Cosine wave oscillator with starting after a quarter wave length. |
| 143 let osc2 = context.createOscillator(); | 143 let osc2 = context.createOscillator(); |
| 144 // Creates a cosine wave. | 144 // Creates a cosine wave. |
| 145 let wave = context.createPeriodicWave( | 145 let wave = context.createPeriodicWave( |
| 146 Float32Array.from([0, 1]), | 146 Float32Array.from([0, 1]), Float32Array.from([0, 0])); |
| 147 Float32Array.from([0, 0])); | |
| 148 osc2.setPeriodicWave(wave); | 147 osc2.setPeriodicWave(wave); |
| 149 | 148 |
| 150 osc2.connect(merger, 0, 1); | 149 osc2.connect(merger, 0, 1); |
| 151 | 150 |
| 152 // A gain inverter so subtract the two waveforms. | 151 // A gain inverter so subtract the two waveforms. |
| 153 let inverter = context.createGain(); | 152 let inverter = context.createGain(); |
| 154 inverter.gain.value = -1; | 153 inverter.gain.value = -1; |
| 155 osc2.connect(inverter); | 154 osc2.connect(inverter); |
| 156 inverter.connect(merger, 0, 0); | 155 inverter.connect(merger, 0, 0); |
| 157 | 156 |
| 158 osc.start(); | 157 osc.start(); |
| 159 osc2.start(quarterWaveTime); | 158 osc2.start(quarterWaveTime); |
| 160 | 159 |
| 161 return context.startRendering().then(function (result) { | 160 return context.startRendering().then(function(result) { |
| 162 // Channel 0 = diff | 161 // Channel 0 = diff |
| 163 // Channel 1 = osc with start | 162 // Channel 1 = osc with start |
| 164 // Channel 2 = osc with gain | 163 // Channel 2 = osc with gain |
| 165 | 164 |
| 166 // Channel 0 should be very close to 0. | 165 // Channel 0 should be very close to 0. |
| 167 // Channel 1 should match channel 2 very closely. | 166 // Channel 1 should match channel 2 very closely. |
| 168 let diff = result.getChannelData(0); | 167 let diff = result.getChannelData(0); |
| 169 let oscStart = result.getChannelData(1); | 168 let oscStart = result.getChannelData(1); |
| 170 let oscGain = result.getChannelData(2); | 169 let oscGain = result.getChannelData(2); |
| 171 let snr = 10 * Math.log10(computeSNR(oscStart, oscGain)); | 170 let snr = 10 * Math.log10(computeSNR(oscStart, oscGain)); |
| 172 | 171 |
| 173 let prefix = "Sample rate " + sampleRate + ": Delayed cosine oscillato
r"; | 172 let prefix = |
| 174 should(oscStart, prefix) | 173 'Sample rate ' + sampleRate + ': Delayed cosine oscillator'; |
| 175 .beCloseToArray(oscGain, { | 174 should(oscStart, prefix).beCloseToArray(oscGain, { |
| 176 absoluteThreshold: thresholds.error | 175 absoluteThreshold: thresholds.error |
| 177 }); | 176 }); |
| 178 should(snr, prefix + ": SNR (dB)") | 177 should(snr, prefix + ': SNR (dB)') |
| 179 .beGreaterThanOrEqualTo(thresholds.snrThreshold); | 178 .beGreaterThanOrEqualTo(thresholds.snrThreshold); |
| 180 }); | 179 }); |
| 181 } | 180 } |
| 182 | 181 |
| 183 audit.run(); | 182 audit.run(); |
| 184 </script> | 183 </script> |
| 185 </body> | 184 </body> |
| 186 </html> | 185 </html> |
| OLD | NEW |