| OLD | NEW |
| 1 var sampleRate = 44100.0; | 1 var sampleRate = 44100.0; |
| 2 | 2 |
| 3 var renderLengthSeconds = 8; | 3 var renderLengthSeconds = 8; |
| 4 var pulseLengthSeconds = 1; | 4 var pulseLengthSeconds = 1; |
| 5 var pulseLengthFrames = pulseLengthSeconds * sampleRate; | 5 var pulseLengthFrames = pulseLengthSeconds * sampleRate; |
| 6 | 6 |
| 7 function createSquarePulseBuffer(context, sampleFrameLength) { | 7 function createSquarePulseBuffer(context, sampleFrameLength) { |
| 8 var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleR
ate); | 8 let audioBuffer = |
| 9 context.createBuffer(1, sampleFrameLength, context.sampleRate); |
| 9 | 10 |
| 10 var n = audioBuffer.length; | 11 let n = audioBuffer.length; |
| 11 var data = audioBuffer.getChannelData(0); | 12 let data = audioBuffer.getChannelData(0); |
| 12 | 13 |
| 13 for (var i = 0; i < n; ++i) | 14 for (let i = 0; i < n; ++i) |
| 14 data[i] = 1; | 15 data[i] = 1; |
| 15 | 16 |
| 16 return audioBuffer; | 17 return audioBuffer; |
| 17 } | 18 } |
| 18 | 19 |
| 19 // The triangle buffer holds the expected result of the convolution. | 20 // The triangle buffer holds the expected result of the convolution. |
| 20 // It linearly ramps up from 0 to its maximum value (at the center) | 21 // It linearly ramps up from 0 to its maximum value (at the center) |
| 21 // then linearly ramps down to 0. The center value corresponds to the | 22 // then linearly ramps down to 0. The center value corresponds to the |
| 22 // point where the two square pulses overlap the most. | 23 // point where the two square pulses overlap the most. |
| 23 function createTrianglePulseBuffer(context, sampleFrameLength) { | 24 function createTrianglePulseBuffer(context, sampleFrameLength) { |
| 24 var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleR
ate); | 25 let audioBuffer = |
| 26 context.createBuffer(1, sampleFrameLength, context.sampleRate); |
| 25 | 27 |
| 26 var n = audioBuffer.length; | 28 let n = audioBuffer.length; |
| 27 var halfLength = n / 2; | 29 let halfLength = n / 2; |
| 28 var data = audioBuffer.getChannelData(0); | 30 let data = audioBuffer.getChannelData(0); |
| 29 | |
| 30 for (var i = 0; i < halfLength; ++i) | |
| 31 data[i] = i + 1; | |
| 32 | 31 |
| 33 for (var i = halfLength; i < n; ++i) | 32 for (let i = 0; i < halfLength; ++i) |
| 34 data[i] = n - i - 1; | 33 data[i] = i + 1; |
| 35 | 34 |
| 36 return audioBuffer; | 35 for (let i = halfLength; i < n; ++i) |
| 36 data[i] = n - i - 1; |
| 37 |
| 38 return audioBuffer; |
| 37 } | 39 } |
| 38 | 40 |
| 39 function log10(x) { | 41 function log10(x) { |
| 40 return Math.log(x)/Math.LN10; | 42 return Math.log(x) / Math.LN10; |
| 41 } | 43 } |
| 42 | 44 |
| 43 function linearToDecibel(x) { | 45 function linearToDecibel(x) { |
| 44 return 20*log10(x); | 46 return 20 * log10(x); |
| 45 } | 47 } |
| 46 | 48 |
| 47 // Verify that the rendered result is very close to the reference | 49 // Verify that the rendered result is very close to the reference |
| 48 // triangular pulse. | 50 // triangular pulse. |
| 49 function checkTriangularPulse(rendered, reference, should) { | 51 function checkTriangularPulse(rendered, reference, should) { |
| 50 var match = true; | 52 let match = true; |
| 51 var maxDelta = 0; | 53 let maxDelta = 0; |
| 52 var valueAtMaxDelta = 0; | 54 let valueAtMaxDelta = 0; |
| 53 var maxDeltaIndex = 0; | 55 let maxDeltaIndex = 0; |
| 54 | 56 |
| 55 for (var i = 0; i < reference.length; ++i) { | 57 for (let i = 0; i < reference.length; ++i) { |
| 56 var diff = rendered[i] - reference[i]; | 58 let diff = rendered[i] - reference[i]; |
| 57 var x = Math.abs(diff); | 59 let x = Math.abs(diff); |
| 58 if (x > maxDelta) { | 60 if (x > maxDelta) { |
| 59 maxDelta = x; | 61 maxDelta = x; |
| 60 valueAtMaxDelta = reference[i]; | 62 valueAtMaxDelta = reference[i]; |
| 61 maxDeltaIndex = i; | 63 maxDeltaIndex = i; |
| 62 } | |
| 63 } | 64 } |
| 65 } |
| 64 | 66 |
| 65 // allowedDeviationFraction was determined experimentally. It | 67 // allowedDeviationFraction was determined experimentally. It |
| 66 // is the threshold of the relative error at the maximum | 68 // is the threshold of the relative error at the maximum |
| 67 // difference between the true triangular pulse and the | 69 // difference between the true triangular pulse and the |
| 68 // rendered pulse. | 70 // rendered pulse. |
| 69 var allowedDeviationDecibels = -124.41; | 71 let allowedDeviationDecibels = -124.41; |
| 70 var maxDeviationDecibels = linearToDecibel(maxDelta / valueAtMaxDelta); | 72 let maxDeviationDecibels = linearToDecibel(maxDelta / valueAtMaxDelta); |
| 71 | 73 |
| 72 should(maxDeviationDecibels, | 74 should( |
| 73 "Deviation (in dB) of triangular portion of convolution") | 75 maxDeviationDecibels, |
| 74 .beLessThanOrEqualTo(allowedDeviationDecibels); | 76 'Deviation (in dB) of triangular portion of convolution') |
| 77 .beLessThanOrEqualTo(allowedDeviationDecibels); |
| 75 | 78 |
| 76 return match; | 79 return match; |
| 77 } | 80 } |
| 78 | 81 |
| 79 // Verify that the rendered data is close to zero for the first part | 82 // Verify that the rendered data is close to zero for the first part |
| 80 // of the tail. | 83 // of the tail. |
| 81 function checkTail1(data, reference, breakpoint, should) { | 84 function checkTail1(data, reference, breakpoint, should) { |
| 82 var isZero = true; | 85 let isZero = true; |
| 83 var tail1Max = 0; | 86 let tail1Max = 0; |
| 84 | 87 |
| 85 for (var i = reference.length; i < reference.length + breakpoint; ++i) { | 88 for (let i = reference.length; i < reference.length + breakpoint; ++i) { |
| 86 var mag = Math.abs(data[i]); | 89 let mag = Math.abs(data[i]); |
| 87 if (mag > tail1Max) { | 90 if (mag > tail1Max) { |
| 88 tail1Max = mag; | 91 tail1Max = mag; |
| 89 } | |
| 90 } | 92 } |
| 93 } |
| 91 | 94 |
| 92 // Let's find the peak of the reference (even though we know a | 95 // Let's find the peak of the reference (even though we know a |
| 93 // priori what it is). | 96 // priori what it is). |
| 94 var refMax = 0; | 97 let refMax = 0; |
| 95 for (var i = 0; i < reference.length; ++i) { | 98 for (let i = 0; i < reference.length; ++i) { |
| 96 refMax = Math.max(refMax, Math.abs(reference[i])); | 99 refMax = Math.max(refMax, Math.abs(reference[i])); |
| 97 } | 100 } |
| 98 | 101 |
| 99 // This threshold is experimentally determined by examining the | 102 // This threshold is experimentally determined by examining the |
| 100 // value of tail1MaxDecibels. | 103 // value of tail1MaxDecibels. |
| 101 var threshold1 = -129.7; | 104 let threshold1 = -129.7; |
| 102 | 105 |
| 103 var tail1MaxDecibels = linearToDecibel(tail1Max/refMax); | 106 let tail1MaxDecibels = linearToDecibel(tail1Max / refMax); |
| 104 should(tail1MaxDecibels, | 107 should(tail1MaxDecibels, 'Deviation in first part of tail of convolutions') |
| 105 "Deviation in first part of tail of convolutions") | 108 .beLessThanOrEqualTo(threshold1); |
| 106 .beLessThanOrEqualTo(threshold1); | |
| 107 | 109 |
| 108 return isZero; | 110 return isZero; |
| 109 } | 111 } |
| 110 | 112 |
| 111 // Verify that the second part of the tail of the convolution is | 113 // Verify that the second part of the tail of the convolution is |
| 112 // exactly zero. | 114 // exactly zero. |
| 113 function checkTail2(data, reference, breakpoint, should) { | 115 function checkTail2(data, reference, breakpoint, should) { |
| 114 var isZero = true; | 116 let isZero = true; |
| 115 var tail2Max = 0; | 117 let tail2Max = 0; |
| 116 // For the second part of the tail, the maximum value should be | 118 // For the second part of the tail, the maximum value should be |
| 117 // exactly zero. | 119 // exactly zero. |
| 118 var threshold2 = 0; | 120 let threshold2 = 0; |
| 119 for (var i = reference.length + breakpoint; i < data.length; ++i) { | 121 for (let i = reference.length + breakpoint; i < data.length; ++i) { |
| 120 if (Math.abs(data[i]) > 0) { | 122 if (Math.abs(data[i]) > 0) { |
| 121 isZero = false; | 123 isZero = false; |
| 122 break; | 124 break; |
| 123 } | |
| 124 } | 125 } |
| 126 } |
| 125 | 127 |
| 126 should(isZero, | 128 should(isZero, 'Rendered signal after tail of convolution is silent') |
| 127 "Rendered signal after tail of convolution is silent") | 129 .beTrue(); |
| 128 .beTrue(); | |
| 129 | 130 |
| 130 return isZero; | 131 return isZero; |
| 131 } | 132 } |
| 132 | 133 |
| 133 function checkConvolvedResult(renderedBuffer, trianglePulse, should) { | 134 function checkConvolvedResult(renderedBuffer, trianglePulse, should) { |
| 134 var referenceData = trianglePulse.getChannelData(0); | 135 let referenceData = trianglePulse.getChannelData(0); |
| 135 var renderedData = renderedBuffer.getChannelData(0); | 136 let renderedData = renderedBuffer.getChannelData(0); |
| 136 | 137 |
| 137 var success = true; | 138 let success = true; |
| 138 | 139 |
| 139 // Verify the triangular pulse is actually triangular. | 140 // Verify the triangular pulse is actually triangular. |
| 140 | 141 |
| 141 success = success && checkTriangularPulse(renderedData, referenceData, | 142 success = |
| 142 should); | 143 success && checkTriangularPulse(renderedData, referenceData, should); |
| 143 | 144 |
| 144 // Make sure that portion after convolved portion is totally | 145 // Make sure that portion after convolved portion is totally |
| 145 // silent. But round-off prevents this from being completely | 146 // silent. But round-off prevents this from being completely |
| 146 // true. At the end of the triangle, it should be close to | 147 // true. At the end of the triangle, it should be close to |
| 147 // zero. If we go farther out, it should be even closer and | 148 // zero. If we go farther out, it should be even closer and |
| 148 // eventually zero. | 149 // eventually zero. |
| 149 | 150 |
| 150 // For the tail of the convolution (where the result would be | 151 // For the tail of the convolution (where the result would be |
| 151 // theoretically zero), we partition the tail into two | 152 // theoretically zero), we partition the tail into two |
| 152 // parts. The first is the at the beginning of the tail, | 153 // parts. The first is the at the beginning of the tail, |
| 153 // where we tolerate a small but non-zero value. The second part is | 154 // where we tolerate a small but non-zero value. The second part is |
| 154 // farther along the tail where the result should be zero. | 155 // farther along the tail where the result should be zero. |
| 155 | 156 |
| 156 // breakpoint is the point dividing the first two tail parts | 157 // breakpoint is the point dividing the first two tail parts |
| 157 // we're looking at. Experimentally determined. | 158 // we're looking at. Experimentally determined. |
| 158 var breakpoint = 12800; | 159 let breakpoint = 12800; |
| 159 | 160 |
| 160 success = success && checkTail1(renderedData, referenceData, breakpoint, | 161 success = |
| 161 should); | 162 success && checkTail1(renderedData, referenceData, breakpoint, should); |
| 162 | 163 |
| 163 success = success && checkTail2(renderedData, referenceData, breakpoint, | 164 success = |
| 164 should); | 165 success && checkTail2(renderedData, referenceData, breakpoint, should); |
| 165 | 166 |
| 166 should(success, "Test signal convolved") | 167 should(success, 'Test signal convolved').message('correctly', 'incorrectly'); |
| 167 .message("correctly", "incorrectly"); | |
| 168 } | 168 } |
| OLD | NEW |