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 |