OLD | NEW |
1 // For the record, these distance formulas were taken from the OpenAL | 1 // For the record, these distance formulas were taken from the OpenAL |
2 // spec | 2 // spec |
3 // (http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specific
ation.pdf), | 3 // (http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specific
ation.pdf), |
4 // not the code. The Web Audio spec follows the OpenAL formulas. | 4 // not the code. The Web Audio spec follows the OpenAL formulas. |
5 | 5 |
6 function linearDistance(panner, x, y, z) { | 6 function linearDistance(panner, x, y, z) { |
7 var distance = Math.sqrt(x * x + y * y + z * z); | 7 let distance = Math.sqrt(x * x + y * y + z * z); |
8 var dref = Math.min(panner.refDistance, panner.maxDistance); | 8 let dref = Math.min(panner.refDistance, panner.maxDistance); |
9 var dmax = Math.max(panner.refDistance, panner.maxDistance); | 9 let dmax = Math.max(panner.refDistance, panner.maxDistance); |
10 distance = Math.max(Math.min(distance, dmax), dref); | 10 distance = Math.max(Math.min(distance, dmax), dref); |
11 var rolloff = Math.max(Math.min(panner.rolloffFactor, 1), 0); | 11 let rolloff = Math.max(Math.min(panner.rolloffFactor, 1), 0); |
12 if (dref === dmax) | 12 if (dref === dmax) |
13 return 1 - rolloff; | 13 return 1 - rolloff; |
14 | 14 |
15 var gain = (1 - rolloff * (distance - dref) / (dmax - dref)); | 15 let gain = (1 - rolloff * (distance - dref) / (dmax - dref)); |
16 | 16 |
17 return gain; | 17 return gain; |
18 } | 18 } |
19 | 19 |
20 function inverseDistance(panner, x, y, z) { | 20 function inverseDistance(panner, x, y, z) { |
21 var distance = Math.sqrt(x * x + y * y + z * z); | 21 let distance = Math.sqrt(x * x + y * y + z * z); |
22 distance = Math.max(distance, panner.refDistance); | 22 distance = Math.max(distance, panner.refDistance); |
23 var rolloff = panner.rolloffFactor; | 23 let rolloff = panner.rolloffFactor; |
24 var gain = panner.refDistance / (panner.refDistance + rolloff * (Math.max(di
stance, panner.refDistance) - panner.refDistance)); | 24 let gain = panner.refDistance / |
| 25 (panner.refDistance + |
| 26 rolloff * (Math.max(distance, panner.refDistance) - panner.refDistance)); |
25 | 27 |
26 return gain; | 28 return gain; |
27 } | 29 } |
28 | 30 |
29 function exponentialDistance(panner, x, y, z) { | 31 function exponentialDistance(panner, x, y, z) { |
30 var distance = Math.sqrt(x * x + y * y + z * z); | 32 let distance = Math.sqrt(x * x + y * y + z * z); |
31 distance = Math.max(distance, panner.refDistance); | 33 distance = Math.max(distance, panner.refDistance); |
32 var rolloff = panner.rolloffFactor; | 34 let rolloff = panner.rolloffFactor; |
33 var gain = Math.pow(distance / panner.refDistance, -rolloff); | 35 let gain = Math.pow(distance / panner.refDistance, -rolloff); |
34 | 36 |
35 return gain; | 37 return gain; |
36 } | 38 } |
37 | 39 |
38 // Simple implementations of 3D vectors implemented as a 3-element array. | 40 // Simple implementations of 3D vectors implemented as a 3-element array. |
39 | 41 |
40 // x - y | 42 // x - y |
41 function vec3Sub(x, y) { | 43 function vec3Sub(x, y) { |
42 var z = new Float32Array(3); | 44 let z = new Float32Array(3); |
43 z[0] = x[0] - y[0]; | 45 z[0] = x[0] - y[0]; |
44 z[1] = x[1] - y[1]; | 46 z[1] = x[1] - y[1]; |
45 z[2] = x[2] - y[2]; | 47 z[2] = x[2] - y[2]; |
46 | 48 |
47 return z; | 49 return z; |
48 } | 50 } |
49 | 51 |
50 // x/|x| | 52 // x/|x| |
51 function vec3Normalize(x) { | 53 function vec3Normalize(x) { |
52 var mag = Math.hypot(...x); | 54 let mag = Math.hypot(...x); |
53 return x.map(function (c) { return c / mag; }); | 55 return x.map(function(c) { |
| 56 return c / mag; |
| 57 }); |
54 } | 58 } |
55 | 59 |
56 // x == 0? | 60 // x == 0? |
57 function vec3IsZero(x) { | 61 function vec3IsZero(x) { |
58 return x[0] === 0 && x[1] === 0 && x[2] === 0; | 62 return x[0] === 0 && x[1] === 0 && x[2] === 0; |
59 } | 63 } |
60 | 64 |
61 // Vector cross product | 65 // Vector cross product |
62 function vec3Cross(u, v) { | 66 function vec3Cross(u, v) { |
63 var cross = new Float32Array(3); | 67 let cross = new Float32Array(3); |
64 cross[0] = u[1] * v[2] - u[2] * v[1]; | 68 cross[0] = u[1] * v[2] - u[2] * v[1]; |
65 cross[1] = u[2] * v[0] - u[0] * v[2]; | 69 cross[1] = u[2] * v[0] - u[0] * v[2]; |
66 cross[2] = u[0] * v[1] - u[1] * v[0]; | 70 cross[2] = u[0] * v[1] - u[1] * v[0]; |
67 return cross; | 71 return cross; |
68 } | 72 } |
69 | 73 |
70 // Dot product | 74 // Dot product |
71 function vec3Dot(x, y) { | 75 function vec3Dot(x, y) { |
72 return x[0] * y[0] + x[1] * y[1] + x[2] * y[2]; | 76 return x[0] * y[0] + x[1] * y[1] + x[2] * y[2]; |
73 } | 77 } |
74 | 78 |
75 // a*x, for scalar a | 79 // a*x, for scalar a |
76 function vec3Scale(a, x) { | 80 function vec3Scale(a, x) { |
77 return x.map(function (c) { return a * c; }); | 81 return x.map(function(c) { |
| 82 return a * c; |
| 83 }); |
78 } | 84 } |
79 | 85 |
80 function calculateAzimuth(source, listener, listenerForward, listenerUp) { | 86 function calculateAzimuth(source, listener, listenerForward, listenerUp) { |
81 var sourceListener = vec3Sub(source, listener); | 87 let sourceListener = vec3Sub(source, listener); |
82 | 88 |
83 if (vec3IsZero(sourceListener)) | 89 if (vec3IsZero(sourceListener)) |
84 return 0; | 90 return 0; |
85 | 91 |
86 sourceListener = vec3Normalize(sourceListener); | 92 sourceListener = vec3Normalize(sourceListener); |
87 | 93 |
88 var listenerRight = vec3Normalize(vec3Cross(listenerForward, listenerUp)); | 94 let listenerRight = vec3Normalize(vec3Cross(listenerForward, listenerUp)); |
89 var listenerForwardNorm = vec3Normalize(listenerForward); | 95 let listenerForwardNorm = vec3Normalize(listenerForward); |
90 | 96 |
91 var up = vec3Cross(listenerRight, listenerForwardNorm); | 97 let up = vec3Cross(listenerRight, listenerForwardNorm); |
92 var upProjection = vec3Dot(sourceListener, up); | 98 let upProjection = vec3Dot(sourceListener, up); |
93 | 99 |
94 var projectedSource = vec3Normalize(vec3Sub(sourceListener, vec3Scale(upProj
ection, up))); | 100 let projectedSource = |
| 101 vec3Normalize(vec3Sub(sourceListener, vec3Scale(upProjection, up))); |
95 | 102 |
96 var azimuth = 180 / Math.PI * Math.acos(vec3Dot(projectedSource, listenerRig
ht)); | 103 let azimuth = |
| 104 180 / Math.PI * Math.acos(vec3Dot(projectedSource, listenerRight)); |
97 | 105 |
98 // Source in front or behind the listener | 106 // Source in front or behind the listener |
99 var frontBack = vec3Dot(projectedSource, listenerForwardNorm); | 107 let frontBack = vec3Dot(projectedSource, listenerForwardNorm); |
100 if (frontBack < 0) | 108 if (frontBack < 0) |
101 azimuth = 360 - azimuth; | 109 azimuth = 360 - azimuth; |
102 | 110 |
103 // Make azimuth relative to "front" and not "right" listener vector. | 111 // Make azimuth relative to "front" and not "right" listener vector. |
104 if (azimuth >= 0 && azimuth <= 270) | 112 if (azimuth >= 0 && azimuth <= 270) |
105 azimuth = 90 - azimuth; | 113 azimuth = 90 - azimuth; |
106 else | 114 else |
107 azimuth = 450 - azimuth; | 115 azimuth = 450 - azimuth; |
108 | 116 |
109 // We don't need elevation, so we're skipping that computation. | 117 // We don't need elevation, so we're skipping that computation. |
110 return azimuth; | 118 return azimuth; |
111 } | 119 } |
112 | 120 |
113 // Map our position angle to the azimuth angle (in degrees). | 121 // Map our position angle to the azimuth angle (in degrees). |
114 // | 122 // |
115 // An angle of 0 corresponds to an azimuth of 90 deg; pi, to -90 deg. | 123 // An angle of 0 corresponds to an azimuth of 90 deg; pi, to -90 deg. |
116 function angleToAzimuth(angle) { | 124 function angleToAzimuth(angle) { |
117 return 90 - angle * 180 / Math.PI; | 125 return 90 - angle * 180 / Math.PI; |
118 } | 126 } |
119 | 127 |
120 // The gain caused by the EQUALPOWER panning model | 128 // The gain caused by the EQUALPOWER panning model |
121 function equalPowerGain(azimuth, numberOfChannels) { | 129 function equalPowerGain(azimuth, numberOfChannels) { |
122 var halfPi = Math.PI / 2; | 130 let halfPi = Math.PI / 2; |
123 | 131 |
124 if (azimuth < -90) | 132 if (azimuth < -90) |
125 azimuth = -180 - azimuth; | 133 azimuth = -180 - azimuth; |
126 else | 134 else |
127 azimuth = 180 - azimuth; | 135 azimuth = 180 - azimuth; |
128 | 136 |
129 if (numberOfChannels == 1) { | 137 if (numberOfChannels == 1) { |
130 var panPosition = (azimuth + 90) / 180; | 138 let panPosition = (azimuth + 90) / 180; |
131 | 139 |
132 var gainL = Math.cos(halfPi * panPosition); | 140 let gainL = Math.cos(halfPi * panPosition); |
133 var gainR = Math.sin(halfPi * panPosition); | 141 let gainR = Math.sin(halfPi * panPosition); |
134 | 142 |
135 return { left : gainL, right : gainR }; | 143 return {left: gainL, right: gainR}; |
| 144 } else { |
| 145 if (azimuth <= 0) { |
| 146 let panPosition = (azimuth + 90) / 90; |
| 147 |
| 148 let gainL = Math.cos(halfPi * panPosition); |
| 149 let gainR = Math.sin(halfPi * panPosition); |
| 150 |
| 151 return {left: gainL, right: gainR}; |
136 } else { | 152 } else { |
137 if (azimuth <= 0) { | 153 let panPosition = azimuth / 90; |
138 var panPosition = (azimuth + 90) / 90; | |
139 | 154 |
140 var gainL = Math.cos(halfPi * panPosition); | 155 let gainL = Math.cos(halfPi * panPosition); |
141 var gainR = Math.sin(halfPi * panPosition); | 156 let gainR = Math.sin(halfPi * panPosition); |
142 | 157 |
143 return { left : gainL, right : gainR }; | 158 return {left: gainL, right: gainR}; |
144 } else { | |
145 var panPosition = azimuth / 90; | |
146 | |
147 var gainL = Math.cos(halfPi * panPosition); | |
148 var gainR = Math.sin(halfPi * panPosition); | |
149 | |
150 return { left : gainL, right : gainR }; | |
151 } | |
152 } | 159 } |
| 160 } |
153 } | 161 } |
154 | 162 |
155 function applyPanner(azimuth, srcL, srcR, numberOfChannels) { | 163 function applyPanner(azimuth, srcL, srcR, numberOfChannels) { |
156 var length = srcL.length; | 164 let length = srcL.length; |
157 var outL = new Float32Array(length); | 165 let outL = new Float32Array(length); |
158 var outR = new Float32Array(length); | 166 let outR = new Float32Array(length); |
159 | 167 |
160 if (numberOfChannels == 1) { | 168 if (numberOfChannels == 1) { |
161 for (var k = 0; k < length; ++k) { | 169 for (let k = 0; k < length; ++k) { |
162 var gains = equalPowerGain(azimuth[k], numberOfChannels); | 170 let gains = equalPowerGain(azimuth[k], numberOfChannels); |
163 | 171 |
164 outL[k] = srcL[k] * gains.left; | 172 outL[k] = srcL[k] * gains.left; |
165 outR[k] = srcR[k] * gains.right; | 173 outR[k] = srcR[k] * gains.right; |
166 } | 174 } |
167 } else { | 175 } else { |
168 for (var k = 0; k < length; ++k) { | 176 for (let k = 0; k < length; ++k) { |
169 var gains = equalPowerGain(azimuth[k], numberOfChannels); | 177 let gains = equalPowerGain(azimuth[k], numberOfChannels); |
170 | 178 |
171 if (azimuth[k] <= 0) { | 179 if (azimuth[k] <= 0) { |
172 outL[k] = srcL[k] + srcR[k] * gains.left; | 180 outL[k] = srcL[k] + srcR[k] * gains.left; |
173 outR[k] = srcR[k] * gains.right; | 181 outR[k] = srcR[k] * gains.right; |
174 } else { | 182 } else { |
175 outL[k] = srcL[k] * gains.left; | 183 outL[k] = srcL[k] * gains.left; |
176 outR[k] = srcR[k] + srcL[k] * gains.right; | 184 outR[k] = srcR[k] + srcL[k] * gains.right; |
177 } | 185 } |
178 } | |
179 } | 186 } |
| 187 } |
180 | 188 |
181 return { left: outL, right: outR }; | 189 return {left: outL, right: outR}; |
182 } | 190 } |
OLD | NEW |