| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 | 5 |
| 6 /** | 6 /** |
| 7 * @fileOverview This file includes legacy utility functions for the layout | 7 * @fileOverview This file includes legacy utility functions for the layout |
| 8 * test. | 8 * test. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 | 11 |
| 12 function writeString(s, a, offset) { | 12 function writeString(s, a, offset) { |
| 13 for (var i = 0; i < s.length; ++i) { | 13 for (let i = 0; i < s.length; ++i) { |
| 14 a[offset + i] = s.charCodeAt(i); | 14 a[offset + i] = s.charCodeAt(i); |
| 15 } | 15 } |
| 16 } | 16 } |
| 17 | 17 |
| 18 function writeInt16(n, a, offset) { | 18 function writeInt16(n, a, offset) { |
| 19 n = Math.floor(n); | 19 n = Math.floor(n); |
| 20 | 20 |
| 21 var b1 = n & 255; | 21 let b1 = n & 255; |
| 22 var b2 = (n >> 8) & 255; | 22 let b2 = (n >> 8) & 255; |
| 23 | 23 |
| 24 a[offset + 0] = b1; | 24 a[offset + 0] = b1; |
| 25 a[offset + 1] = b2; | 25 a[offset + 1] = b2; |
| 26 } | 26 } |
| 27 | 27 |
| 28 function writeInt32(n, a, offset) { | 28 function writeInt32(n, a, offset) { |
| 29 n = Math.floor(n); | 29 n = Math.floor(n); |
| 30 var b1 = n & 255; | 30 let b1 = n & 255; |
| 31 var b2 = (n >> 8) & 255; | 31 let b2 = (n >> 8) & 255; |
| 32 var b3 = (n >> 16) & 255; | 32 let b3 = (n >> 16) & 255; |
| 33 var b4 = (n >> 24) & 255; | 33 let b4 = (n >> 24) & 255; |
| 34 | 34 |
| 35 a[offset + 0] = b1; | 35 a[offset + 0] = b1; |
| 36 a[offset + 1] = b2; | 36 a[offset + 1] = b2; |
| 37 a[offset + 2] = b3; | 37 a[offset + 2] = b3; |
| 38 a[offset + 3] = b4; | 38 a[offset + 3] = b4; |
| 39 } | 39 } |
| 40 | 40 |
| 41 // Return the bits of the float as a 32-bit integer value. This | 41 // Return the bits of the float as a 32-bit integer value. This |
| 42 // produces the raw bits; no intepretation of the value is done. | 42 // produces the raw bits; no intepretation of the value is done. |
| 43 function floatBits(f) { | 43 function floatBits(f) { |
| 44 var buf = new ArrayBuffer(4); | 44 let buf = new ArrayBuffer(4); |
| 45 (new Float32Array(buf))[0] = f; | 45 (new Float32Array(buf))[0] = f; |
| 46 var bits = (new Uint32Array(buf))[0]; | 46 let bits = (new Uint32Array(buf))[0]; |
| 47 // Return as a signed integer. | 47 // Return as a signed integer. |
| 48 return bits | 0; | 48 return bits | 0; |
| 49 } | 49 } |
| 50 | 50 |
| 51 function writeAudioBuffer(audioBuffer, a, offset, asFloat) { | 51 function writeAudioBuffer(audioBuffer, a, offset, asFloat) { |
| 52 var n = audioBuffer.length; | 52 let n = audioBuffer.length; |
| 53 var channels = audioBuffer.numberOfChannels; | 53 let channels = audioBuffer.numberOfChannels; |
| 54 | 54 |
| 55 for (var i = 0; i < n; ++i) { | 55 for (let i = 0; i < n; ++i) { |
| 56 for (var k = 0; k < channels; ++k) { | 56 for (let k = 0; k < channels; ++k) { |
| 57 var buffer = audioBuffer.getChannelData(k); | 57 let buffer = audioBuffer.getChannelData(k); |
| 58 if (asFloat) { | 58 if (asFloat) { |
| 59 var sample = floatBits(buffer[i]); | 59 let sample = floatBits(buffer[i]); |
| 60 writeInt32(sample, a, offset); | 60 writeInt32(sample, a, offset); |
| 61 offset += 4; | 61 offset += 4; |
| 62 } else { | 62 } else { |
| 63 var sample = buffer[i] * 32768.0; | 63 let sample = buffer[i] * 32768.0; |
| 64 | 64 |
| 65 // Clip samples to the limitations of 16-bit. | 65 // Clip samples to the limitations of 16-bit. |
| 66 // If we don't do this then we'll get nasty wrap-around distortion
. | 66 // If we don't do this then we'll get nasty wrap-around distortion. |
| 67 if (sample < -32768) | 67 if (sample < -32768) |
| 68 sample = -32768; | 68 sample = -32768; |
| 69 if (sample > 32767) | 69 if (sample > 32767) |
| 70 sample = 32767; | 70 sample = 32767; |
| 71 | 71 |
| 72 writeInt16(sample, a, offset); | 72 writeInt16(sample, a, offset); |
| 73 offset += 2; | 73 offset += 2; |
| 74 } | 74 } |
| 75 } | |
| 76 } | 75 } |
| 76 } |
| 77 } | 77 } |
| 78 | 78 |
| 79 // See http://soundfile.sapp.org/doc/WaveFormat/ and | 79 // See http://soundfile.sapp.org/doc/WaveFormat/ and |
| 80 // http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html | 80 // http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html |
| 81 // for a quick introduction to the WAVE PCM format. | 81 // for a quick introduction to the WAVE PCM format. |
| 82 function createWaveFileData(audioBuffer, asFloat) { | 82 function createWaveFileData(audioBuffer, asFloat) { |
| 83 var bytesPerSample = asFloat ? 4 : 2; | 83 let bytesPerSample = asFloat ? 4 : 2; |
| 84 var frameLength = audioBuffer.length; | 84 let frameLength = audioBuffer.length; |
| 85 var numberOfChannels = audioBuffer.numberOfChannels; | 85 let numberOfChannels = audioBuffer.numberOfChannels; |
| 86 var sampleRate = audioBuffer.sampleRate; | 86 let sampleRate = audioBuffer.sampleRate; |
| 87 var bitsPerSample = 8 * bytesPerSample; | 87 let bitsPerSample = 8 * bytesPerSample; |
| 88 var byteRate = sampleRate * numberOfChannels * bitsPerSample/8; | 88 let byteRate = sampleRate * numberOfChannels * bitsPerSample / 8; |
| 89 var blockAlign = numberOfChannels * bitsPerSample/8; | 89 let blockAlign = numberOfChannels * bitsPerSample / 8; |
| 90 var wavDataByteLength = frameLength * numberOfChannels * bytesPerSample; | 90 let wavDataByteLength = frameLength * numberOfChannels * bytesPerSample; |
| 91 var headerByteLength = 44; | 91 let headerByteLength = 44; |
| 92 var totalLength = headerByteLength + wavDataByteLength; | 92 let totalLength = headerByteLength + wavDataByteLength; |
| 93 | 93 |
| 94 var waveFileData = new Uint8Array(totalLength); | 94 let waveFileData = new Uint8Array(totalLength); |
| 95 | 95 |
| 96 var subChunk1Size = 16; // for linear PCM | 96 let subChunk1Size = 16; // for linear PCM |
| 97 var subChunk2Size = wavDataByteLength; | 97 let subChunk2Size = wavDataByteLength; |
| 98 var chunkSize = 4 + (8 + subChunk1Size) + (8 + subChunk2Size); | 98 let chunkSize = 4 + (8 + subChunk1Size) + (8 + subChunk2Size); |
| 99 | 99 |
| 100 writeString("RIFF", waveFileData, 0); | 100 writeString('RIFF', waveFileData, 0); |
| 101 writeInt32(chunkSize, waveFileData, 4); | 101 writeInt32(chunkSize, waveFileData, 4); |
| 102 writeString("WAVE", waveFileData, 8); | 102 writeString('WAVE', waveFileData, 8); |
| 103 writeString("fmt ", waveFileData, 12); | 103 writeString('fmt ', waveFileData, 12); |
| 104 | 104 |
| 105 writeInt32(subChunk1Size, waveFileData, 16); // SubChunk1Size (4) | 105 writeInt32(subChunk1Size, waveFileData, 16); // SubChunk1Size (4) |
| 106 // The format tag value is 1 for integer PCM data and 3 for IEEE | 106 // The format tag value is 1 for integer PCM data and 3 for IEEE |
| 107 // float data. | 107 // float data. |
| 108 writeInt16(asFloat ? 3 : 1, waveFileData, 20); // AudioFormat (2) | 108 writeInt16(asFloat ? 3 : 1, waveFileData, 20); // AudioFormat (2) |
| 109 writeInt16(numberOfChannels, waveFileData, 22); // NumChannels (2) | 109 writeInt16(numberOfChannels, waveFileData, 22); // NumChannels (2) |
| 110 writeInt32(sampleRate, waveFileData, 24); // SampleRate (4) | 110 writeInt32(sampleRate, waveFileData, 24); // SampleRate (4) |
| 111 writeInt32(byteRate, waveFileData, 28); // ByteRate (4) | 111 writeInt32(byteRate, waveFileData, 28); // ByteRate (4) |
| 112 writeInt16(blockAlign, waveFileData, 32); // BlockAlign (2) | 112 writeInt16(blockAlign, waveFileData, 32); // BlockAlign (2) |
| 113 writeInt32(bitsPerSample, waveFileData, 34); // BitsPerSample (4) | 113 writeInt32(bitsPerSample, waveFileData, 34); // BitsPerSample (4) |
| 114 | 114 |
| 115 writeString("data", waveFileData, 36); | 115 writeString('data', waveFileData, 36); |
| 116 writeInt32(subChunk2Size, waveFileData, 40); // SubChunk2Size (4) | 116 writeInt32(subChunk2Size, waveFileData, 40); // SubChunk2Size (4) |
| 117 | 117 |
| 118 // Write actual audio data starting at offset 44. | 118 // Write actual audio data starting at offset 44. |
| 119 writeAudioBuffer(audioBuffer, waveFileData, 44, asFloat); | 119 writeAudioBuffer(audioBuffer, waveFileData, 44, asFloat); |
| 120 | 120 |
| 121 return waveFileData; | 121 return waveFileData; |
| 122 } | 122 } |
| 123 | 123 |
| 124 function createAudioData(audioBuffer, asFloat) { | 124 function createAudioData(audioBuffer, asFloat) { |
| 125 return createWaveFileData(audioBuffer, asFloat); | 125 return createWaveFileData(audioBuffer, asFloat); |
| 126 } | 126 } |
| 127 | 127 |
| 128 function finishAudioTest(event) { | 128 function finishAudioTest(event) { |
| 129 var audioData = createAudioData(event.renderedBuffer); | 129 let audioData = createAudioData(event.renderedBuffer); |
| 130 testRunner.setAudioData(audioData); | 130 testRunner.setAudioData(audioData); |
| 131 testRunner.notifyDone(); | 131 testRunner.notifyDone(); |
| 132 } | 132 } |
| 133 | 133 |
| 134 // Save the given |audioBuffer| to a WAV file using the name given by | 134 // Save the given |audioBuffer| to a WAV file using the name given by |
| 135 // |filename|. This is intended to be run from a browser. The | 135 // |filename|. This is intended to be run from a browser. The |
| 136 // developer is expected to use the console to run downloadAudioBuffer | 136 // developer is expected to use the console to run downloadAudioBuffer |
| 137 // when necessary to create a new reference file for a test. If | 137 // when necessary to create a new reference file for a test. If |
| 138 // |asFloat| is given and is true, the WAV file produced uses 32-bit | 138 // |asFloat| is given and is true, the WAV file produced uses 32-bit |
| 139 // float format (full WebAudio resolution). Otherwise a 16-bit PCM | 139 // float format (full WebAudio resolution). Otherwise a 16-bit PCM |
| 140 // WAV file is produced. | 140 // WAV file is produced. |
| 141 function downloadAudioBuffer(audioBuffer, filename, asFloat) { | 141 function downloadAudioBuffer(audioBuffer, filename, asFloat) { |
| 142 // Don't download if testRunner is defined; we're running a layout | 142 // Don't download if testRunner is defined; we're running a layout |
| 143 // test where this won't be useful in general. | 143 // test where this won't be useful in general. |
| 144 if (window.testRunner) | 144 if (window.testRunner) |
| 145 return false; | 145 return false; |
| 146 // Convert the audio buffer to an array containing the WAV file | 146 // Convert the audio buffer to an array containing the WAV file |
| 147 // contents. Then convert it to a blob that can be saved as a WAV | 147 // contents. Then convert it to a blob that can be saved as a WAV |
| 148 // file. | 148 // file. |
| 149 let wavData = createAudioData(audioBuffer, asFloat); | 149 let wavData = createAudioData(audioBuffer, asFloat); |
| 150 let blob = new Blob([wavData], {type: 'audio/wav'}); | 150 let blob = new Blob([wavData], {type: 'audio/wav'}); |
| 151 // Manually create html tags for downloading, and simulate a click | 151 // Manually create html tags for downloading, and simulate a click |
| 152 // to download the file to the given file name. | 152 // to download the file to the given file name. |
| 153 let a = document.createElement('a'); | 153 let a = document.createElement('a'); |
| 154 a.style.display = 'none'; | 154 a.style.display = 'none'; |
| 155 a.download = filename; | 155 a.download = filename; |
| 156 let audioURL = window.URL.createObjectURL(blob); | 156 let audioURL = window.URL.createObjectURL(blob); |
| 157 let audio = new Audio(); | 157 let audio = new Audio(); |
| 158 audio.src = audioURL; | 158 audio.src = audioURL; |
| 159 a.href = audioURL; | 159 a.href = audioURL; |
| 160 document.body.appendChild(a); | 160 document.body.appendChild(a); |
| 161 a.click(); | 161 a.click(); |
| 162 return true; | 162 return true; |
| 163 } | 163 } |
| 164 | 164 |
| 165 // Compare two arrays (commonly extracted from buffer.getChannelData()) with | 165 // Compare two arrays (commonly extracted from buffer.getChannelData()) with |
| 166 // constraints: | 166 // constraints: |
| 167 // options.thresholdSNR: Minimum allowed SNR between the actual and expected | 167 // options.thresholdSNR: Minimum allowed SNR between the actual and expected |
| 168 // signal. The default value is 10000. | 168 // signal. The default value is 10000. |
| 169 // options.thresholdDiffULP: Maximum allowed difference between the actual | 169 // options.thresholdDiffULP: Maximum allowed difference between the actual |
| 170 // and expected signal in ULP(Unit in the last place). The default is 0. | 170 // and expected signal in ULP(Unit in the last place). The default is 0. |
| 171 // options.thresholdDiffCount: Maximum allowed number of sample differences | 171 // options.thresholdDiffCount: Maximum allowed number of sample differences |
| 172 // which exceeds the threshold. The default is 0. | 172 // which exceeds the threshold. The default is 0. |
| 173 // options.bitDepth: The expected result is assumed to come from an audio | 173 // options.bitDepth: The expected result is assumed to come from an audio |
| 174 // file with this number of bits of precision. The default is 16. | 174 // file with this number of bits of precision. The default is 16. |
| 175 function compareBuffersWithConstraints(should, actual, expected, options) { | 175 function compareBuffersWithConstraints(should, actual, expected, options) { |
| 176 if (!options) | 176 if (!options) |
| 177 options = {}; | 177 options = {}; |
| 178 | 178 |
| 179 // Only print out the message if the lengths are different; the | 179 // Only print out the message if the lengths are different; the |
| 180 // expectation is that they are the same, so don't clutter up the | 180 // expectation is that they are the same, so don't clutter up the |
| 181 // output. | 181 // output. |
| 182 if (actual.length !== expected.length) { | 182 if (actual.length !== expected.length) { |
| 183 should(actual.length === expected.length, | 183 should( |
| 184 "Length of actual and expected buffers should match") | 184 actual.length === expected.length, |
| 185 .beTrue(); | 185 'Length of actual and expected buffers should match') |
| 186 .beTrue(); |
| 187 } |
| 188 |
| 189 let maxError = -1; |
| 190 let diffCount = 0; |
| 191 let errorPosition = -1; |
| 192 let thresholdSNR = (options.thresholdSNR || 10000); |
| 193 |
| 194 let thresholdDiffULP = (options.thresholdDiffULP || 0); |
| 195 let thresholdDiffCount = (options.thresholdDiffCount || 0); |
| 196 |
| 197 // By default, the bit depth is 16. |
| 198 let bitDepth = (options.bitDepth || 16); |
| 199 let scaleFactor = Math.pow(2, bitDepth - 1); |
| 200 |
| 201 let noisePower = 0, signalPower = 0; |
| 202 |
| 203 for (let i = 0; i < actual.length; i++) { |
| 204 let diff = actual[i] - expected[i]; |
| 205 noisePower += diff * diff; |
| 206 signalPower += expected[i] * expected[i]; |
| 207 |
| 208 if (Math.abs(diff) > maxError) { |
| 209 maxError = Math.abs(diff); |
| 210 errorPosition = i; |
| 186 } | 211 } |
| 187 | 212 |
| 188 var maxError = -1; | 213 // The reference file is a 16-bit WAV file, so we will almost never get |
| 189 var diffCount = 0; | 214 // an exact match between it and the actual floating-point result. |
| 190 var errorPosition = -1; | 215 if (Math.abs(diff) > scaleFactor) |
| 191 var thresholdSNR = (options.thresholdSNR || 10000); | 216 diffCount++; |
| 217 } |
| 192 | 218 |
| 193 var thresholdDiffULP = (options.thresholdDiffULP || 0); | 219 let snr = 10 * Math.log10(signalPower / noisePower); |
| 194 var thresholdDiffCount = (options.thresholdDiffCount || 0); | 220 let maxErrorULP = maxError * scaleFactor; |
| 195 | 221 |
| 196 // By default, the bit depth is 16. | 222 should(snr, 'SNR').beGreaterThanOrEqualTo(thresholdSNR); |
| 197 var bitDepth = (options.bitDepth || 16); | |
| 198 var scaleFactor = Math.pow(2, bitDepth - 1); | |
| 199 | 223 |
| 200 var noisePower = 0, signalPower = 0; | 224 should( |
| 225 maxErrorULP, |
| 226 options.prefix + ': Maximum difference (in ulp units (' + bitDepth + |
| 227 '-bits))') |
| 228 .beLessThanOrEqualTo(thresholdDiffULP); |
| 201 | 229 |
| 202 for (var i = 0; i < actual.length; i++) { | 230 should(diffCount, options.prefix + ': Number of differences between results') |
| 203 var diff = actual[i] - expected[i]; | 231 .beLessThanOrEqualTo(thresholdDiffCount); |
| 204 noisePower += diff * diff; | |
| 205 signalPower += expected[i] * expected[i]; | |
| 206 | |
| 207 if (Math.abs(diff) > maxError) { | |
| 208 maxError = Math.abs(diff); | |
| 209 errorPosition = i; | |
| 210 } | |
| 211 | |
| 212 // The reference file is a 16-bit WAV file, so we will almost never get | |
| 213 // an exact match between it and the actual floating-point result. | |
| 214 if (Math.abs(diff) > scaleFactor) | |
| 215 diffCount++; | |
| 216 } | |
| 217 | |
| 218 var snr = 10 * Math.log10(signalPower / noisePower); | |
| 219 var maxErrorULP = maxError * scaleFactor; | |
| 220 | |
| 221 should(snr, "SNR").beGreaterThanOrEqualTo(thresholdSNR); | |
| 222 | |
| 223 should(maxErrorULP, | |
| 224 options.prefix + ': Maximum difference (in ulp units (' + bitDepth + | |
| 225 '-bits))' | |
| 226 ).beLessThanOrEqualTo(thresholdDiffULP); | |
| 227 | |
| 228 should(diffCount, options.prefix + | |
| 229 ': Number of differences between results').beLessThanOrEqualTo( | |
| 230 thresholdDiffCount); | |
| 231 } | 232 } |
| 232 | 233 |
| 233 // Create an impulse in a buffer of length sampleFrameLength | 234 // Create an impulse in a buffer of length sampleFrameLength |
| 234 function createImpulseBuffer(context, sampleFrameLength) { | 235 function createImpulseBuffer(context, sampleFrameLength) { |
| 235 var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleR
ate); | 236 let audioBuffer = |
| 236 var n = audioBuffer.length; | 237 context.createBuffer(1, sampleFrameLength, context.sampleRate); |
| 237 var dataL = audioBuffer.getChannelData(0); | 238 let n = audioBuffer.length; |
| 239 let dataL = audioBuffer.getChannelData(0); |
| 238 | 240 |
| 239 for (var k = 0; k < n; ++k) { | 241 for (let k = 0; k < n; ++k) { |
| 240 dataL[k] = 0; | 242 dataL[k] = 0; |
| 241 } | 243 } |
| 242 dataL[0] = 1; | 244 dataL[0] = 1; |
| 243 | 245 |
| 244 return audioBuffer; | 246 return audioBuffer; |
| 245 } | 247 } |
| 246 | 248 |
| 247 // Create a buffer of the given length with a linear ramp having values 0 <= x <
1. | 249 // Create a buffer of the given length with a linear ramp having values 0 <= x < |
| 250 // 1. |
| 248 function createLinearRampBuffer(context, sampleFrameLength) { | 251 function createLinearRampBuffer(context, sampleFrameLength) { |
| 249 var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleR
ate); | 252 let audioBuffer = |
| 250 var n = audioBuffer.length; | 253 context.createBuffer(1, sampleFrameLength, context.sampleRate); |
| 251 var dataL = audioBuffer.getChannelData(0); | 254 let n = audioBuffer.length; |
| 255 let dataL = audioBuffer.getChannelData(0); |
| 252 | 256 |
| 253 for (var i = 0; i < n; ++i) | 257 for (let i = 0; i < n; ++i) |
| 254 dataL[i] = i / n; | 258 dataL[i] = i / n; |
| 255 | 259 |
| 256 return audioBuffer; | 260 return audioBuffer; |
| 257 } | 261 } |
| 258 | 262 |
| 259 // Create an AudioBuffer of length |sampleFrameLength| having a constant value |
constantValue|. If | 263 // Create an AudioBuffer of length |sampleFrameLength| having a constant value |
| 260 // |constantValue| is a number, the buffer has one channel filled with that valu
e. If | 264 // |constantValue|. If |constantValue| is a number, the buffer has one channel |
| 261 // |constantValue| is an array, the buffer is created wit a number of channels e
qual to the length | 265 // filled with that value. If |constantValue| is an array, the buffer is created |
| 262 // of the array, and channel k is filled with the k'th element of the |constantV
alue| array. | 266 // wit a number of channels equal to the length of the array, and channel k is |
| 267 // filled with the k'th element of the |constantValue| array. |
| 263 function createConstantBuffer(context, sampleFrameLength, constantValue) { | 268 function createConstantBuffer(context, sampleFrameLength, constantValue) { |
| 264 var channels; | 269 let channels; |
| 265 var values; | 270 let values; |
| 266 | 271 |
| 267 if (typeof constantValue === "number") { | 272 if (typeof constantValue === 'number') { |
| 268 channels = 1; | 273 channels = 1; |
| 269 values = [constantValue]; | 274 values = [constantValue]; |
| 270 } else { | 275 } else { |
| 271 channels = constantValue.length; | 276 channels = constantValue.length; |
| 272 values = constantValue; | 277 values = constantValue; |
| 273 } | 278 } |
| 274 | 279 |
| 275 var audioBuffer = context.createBuffer(channels, sampleFrameLength, context.
sampleRate); | 280 let audioBuffer = |
| 276 var n = audioBuffer.length; | 281 context.createBuffer(channels, sampleFrameLength, context.sampleRate); |
| 282 let n = audioBuffer.length; |
| 277 | 283 |
| 278 for (var c = 0; c < channels; ++c) { | 284 for (let c = 0; c < channels; ++c) { |
| 279 var data = audioBuffer.getChannelData(c); | 285 let data = audioBuffer.getChannelData(c); |
| 280 for (var i = 0; i < n; ++i) | 286 for (let i = 0; i < n; ++i) |
| 281 data[i] = values[c]; | 287 data[i] = values[c]; |
| 282 } | 288 } |
| 283 | 289 |
| 284 return audioBuffer; | 290 return audioBuffer; |
| 285 } | 291 } |
| 286 | 292 |
| 287 // Create a stereo impulse in a buffer of length sampleFrameLength | 293 // Create a stereo impulse in a buffer of length sampleFrameLength |
| 288 function createStereoImpulseBuffer(context, sampleFrameLength) { | 294 function createStereoImpulseBuffer(context, sampleFrameLength) { |
| 289 var audioBuffer = context.createBuffer(2, sampleFrameLength, context.sampleR
ate); | 295 let audioBuffer = |
| 290 var n = audioBuffer.length; | 296 context.createBuffer(2, sampleFrameLength, context.sampleRate); |
| 291 var dataL = audioBuffer.getChannelData(0); | 297 let n = audioBuffer.length; |
| 292 var dataR = audioBuffer.getChannelData(1); | 298 let dataL = audioBuffer.getChannelData(0); |
| 299 let dataR = audioBuffer.getChannelData(1); |
| 293 | 300 |
| 294 for (var k = 0; k < n; ++k) { | 301 for (let k = 0; k < n; ++k) { |
| 295 dataL[k] = 0; | 302 dataL[k] = 0; |
| 296 dataR[k] = 0; | 303 dataR[k] = 0; |
| 297 } | 304 } |
| 298 dataL[0] = 1; | 305 dataL[0] = 1; |
| 299 dataR[0] = 1; | 306 dataR[0] = 1; |
| 300 | 307 |
| 301 return audioBuffer; | 308 return audioBuffer; |
| 302 } | 309 } |
| 303 | 310 |
| 304 // Convert time (in seconds) to sample frames. | 311 // Convert time (in seconds) to sample frames. |
| 305 function timeToSampleFrame(time, sampleRate) { | 312 function timeToSampleFrame(time, sampleRate) { |
| 306 return Math.floor(0.5 + time * sampleRate); | 313 return Math.floor(0.5 + time * sampleRate); |
| 307 } | 314 } |
| 308 | 315 |
| 309 // Compute the number of sample frames consumed by noteGrainOn with | 316 // Compute the number of sample frames consumed by noteGrainOn with |
| 310 // the specified |grainOffset|, |duration|, and |sampleRate|. | 317 // the specified |grainOffset|, |duration|, and |sampleRate|. |
| 311 function grainLengthInSampleFrames(grainOffset, duration, sampleRate) { | 318 function grainLengthInSampleFrames(grainOffset, duration, sampleRate) { |
| 312 var startFrame = timeToSampleFrame(grainOffset, sampleRate); | 319 let startFrame = timeToSampleFrame(grainOffset, sampleRate); |
| 313 var endFrame = timeToSampleFrame(grainOffset + duration, sampleRate); | 320 let endFrame = timeToSampleFrame(grainOffset + duration, sampleRate); |
| 314 | 321 |
| 315 return endFrame - startFrame; | 322 return endFrame - startFrame; |
| 316 } | 323 } |
| 317 | 324 |
| 318 // True if the number is not an infinity or NaN | 325 // True if the number is not an infinity or NaN |
| 319 function isValidNumber(x) { | 326 function isValidNumber(x) { |
| 320 return !isNaN(x) && (x != Infinity) && (x != -Infinity); | 327 return !isNaN(x) && (x != Infinity) && (x != -Infinity); |
| 321 } | 328 } |
| 322 | 329 |
| 323 // Compute the (linear) signal-to-noise ratio between |actual| and | 330 // Compute the (linear) signal-to-noise ratio between |actual| and |
| 324 // |expected|. The result is NOT in dB! If the |actual| and | 331 // |expected|. The result is NOT in dB! If the |actual| and |
| 325 // |expected| have different lengths, the shorter length is used. | 332 // |expected| have different lengths, the shorter length is used. |
| 326 function computeSNR(actual, expected) { | 333 function computeSNR(actual, expected) { |
| 327 var signalPower = 0; | 334 let signalPower = 0; |
| 328 var noisePower = 0; | 335 let noisePower = 0; |
| 329 | 336 |
| 330 var length = Math.min(actual.length, expected.length); | 337 let length = Math.min(actual.length, expected.length); |
| 331 | 338 |
| 332 for (var k = 0; k < length; ++k) { | 339 for (let k = 0; k < length; ++k) { |
| 333 var diff = actual[k] - expected[k]; | 340 let diff = actual[k] - expected[k]; |
| 334 signalPower += expected[k] * expected[k]; | 341 signalPower += expected[k] * expected[k]; |
| 335 noisePower += diff * diff; | 342 noisePower += diff * diff; |
| 336 } | 343 } |
| 337 | 344 |
| 338 return signalPower / noisePower; | 345 return signalPower / noisePower; |
| 339 } | 346 } |
| OLD | NEW |