OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2010, Google Inc. All rights reserved. | 2 * Copyright (C) 2010, Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 | 98 |
99 return true; | 99 return true; |
100 } | 100 } |
101 | 101 |
102 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) | 102 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) |
103 { | 103 { |
104 bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->leng
th() >= framesToProcess; | 104 bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->leng
th() >= framesToProcess; |
105 ASSERT(isBusGood); | 105 ASSERT(isBusGood); |
106 if (!isBusGood) | 106 if (!isBusGood) |
107 return; | 107 return; |
108 | 108 |
109 // FIXME : allow to work with non-FFTSize divisible chunking | 109 // FIXME : allow to work with non-FFTSize divisible chunking |
110 bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex
+ framesToProcess <= m_inputBuffer.size(); | 110 bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex
+ framesToProcess <= m_inputBuffer.size(); |
111 ASSERT(isDestinationGood); | 111 ASSERT(isDestinationGood); |
112 if (!isDestinationGood) | 112 if (!isDestinationGood) |
113 return; | 113 return; |
114 | 114 |
115 // Perform real-time analysis | 115 // Perform real-time analysis |
116 const float* source = bus->channel(0)->data(); | 116 const float* source = bus->channel(0)->data(); |
117 float* dest = m_inputBuffer.data() + m_writeIndex; | 117 float* dest = m_inputBuffer.data() + m_writeIndex; |
118 | 118 |
119 // The source has already been sanity checked with isBusGood above. | 119 // The source has already been sanity checked with isBusGood above. |
120 memcpy(dest, source, sizeof(float) * framesToProcess); | 120 memcpy(dest, source, sizeof(float) * framesToProcess); |
121 | 121 |
122 // Sum all channels in one if numberOfChannels > 1. | 122 // Sum all channels in one if numberOfChannels > 1. |
123 unsigned numberOfChannels = bus->numberOfChannels(); | 123 unsigned numberOfChannels = bus->numberOfChannels(); |
124 if (numberOfChannels > 1) { | 124 if (numberOfChannels > 1) { |
125 for (unsigned i = 1; i < numberOfChannels; i++) { | 125 for (unsigned i = 1; i < numberOfChannels; i++) { |
126 source = bus->channel(i)->data(); | 126 source = bus->channel(i)->data(); |
127 VectorMath::vadd(dest, 1, source, 1, dest, 1, framesToProcess); | 127 VectorMath::vadd(dest, 1, source, 1, dest, 1, framesToProcess); |
128 } | 128 } |
129 const float scale = 1.0 / numberOfChannels; | 129 const float scale = 1.0 / numberOfChannels; |
130 VectorMath::vsmul(dest, 1, &scale, dest, 1, framesToProcess); | 130 VectorMath::vsmul(dest, 1, &scale, dest, 1, framesToProcess); |
131 } | 131 } |
132 | 132 |
133 m_writeIndex += framesToProcess; | 133 m_writeIndex += framesToProcess; |
134 if (m_writeIndex >= InputBufferSize) | 134 if (m_writeIndex >= InputBufferSize) |
135 m_writeIndex = 0; | 135 m_writeIndex = 0; |
136 } | 136 } |
137 | 137 |
138 namespace { | 138 namespace { |
139 | 139 |
140 void applyWindow(float* p, size_t n) | 140 void applyWindow(float* p, size_t n) |
141 { | 141 { |
142 ASSERT(isMainThread()); | 142 ASSERT(isMainThread()); |
143 | 143 |
144 // Blackman window | 144 // Blackman window |
145 double alpha = 0.16; | 145 double alpha = 0.16; |
146 double a0 = 0.5 * (1 - alpha); | 146 double a0 = 0.5 * (1 - alpha); |
147 double a1 = 0.5; | 147 double a1 = 0.5; |
148 double a2 = 0.5 * alpha; | 148 double a2 = 0.5 * alpha; |
149 | 149 |
150 for (unsigned i = 0; i < n; ++i) { | 150 for (unsigned i = 0; i < n; ++i) { |
151 double x = static_cast<double>(i) / static_cast<double>(n); | 151 double x = static_cast<double>(i) / static_cast<double>(n); |
152 double window = a0 - a1 * cos(2 * piDouble * x) + a2 * cos(4 * piDouble
* x); | 152 double window = a0 - a1 * cos(2 * piDouble * x) + a2 * cos(4 * piDouble
* x); |
153 p[i] *= float(window); | 153 p[i] *= float(window); |
154 } | 154 } |
155 } | 155 } |
156 | 156 |
157 } // namespace | 157 } // namespace |
158 | 158 |
159 void RealtimeAnalyser::doFFTAnalysis() | 159 void RealtimeAnalyser::doFFTAnalysis() |
160 { | 160 { |
161 ASSERT(isMainThread()); | 161 ASSERT(isMainThread()); |
162 | 162 |
163 // Unroll the input buffer into a temporary buffer, where we'll apply an ana
lysis window followed by an FFT. | 163 // Unroll the input buffer into a temporary buffer, where we'll apply an ana
lysis window followed by an FFT. |
164 size_t fftSize = this->fftSize(); | 164 size_t fftSize = this->fftSize(); |
165 | 165 |
166 AudioFloatArray temporaryBuffer(fftSize); | 166 AudioFloatArray temporaryBuffer(fftSize); |
167 float* inputBuffer = m_inputBuffer.data(); | 167 float* inputBuffer = m_inputBuffer.data(); |
168 float* tempP = temporaryBuffer.data(); | 168 float* tempP = temporaryBuffer.data(); |
169 | 169 |
170 // Take the previous fftSize values from the input buffer and copy into the
temporary buffer. | 170 // Take the previous fftSize values from the input buffer and copy into the
temporary buffer. |
171 unsigned writeIndex = m_writeIndex; | 171 unsigned writeIndex = m_writeIndex; |
172 if (writeIndex < fftSize) { | 172 if (writeIndex < fftSize) { |
173 memcpy(tempP, inputBuffer + writeIndex - fftSize + InputBufferSize, size
of(*tempP) * (fftSize - writeIndex)); | 173 memcpy(tempP, inputBuffer + writeIndex - fftSize + InputBufferSize, size
of(*tempP) * (fftSize - writeIndex)); |
174 memcpy(tempP + fftSize - writeIndex, inputBuffer, sizeof(*tempP) * write
Index); | 174 memcpy(tempP + fftSize - writeIndex, inputBuffer, sizeof(*tempP) * write
Index); |
175 } else | 175 } else |
176 memcpy(tempP, inputBuffer + writeIndex - fftSize, sizeof(*tempP) * fftSi
ze); | 176 memcpy(tempP, inputBuffer + writeIndex - fftSize, sizeof(*tempP) * fftSi
ze); |
177 | 177 |
178 | 178 |
179 // Window the input samples. | 179 // Window the input samples. |
180 applyWindow(tempP, fftSize); | 180 applyWindow(tempP, fftSize); |
181 | 181 |
182 // Do the analysis. | 182 // Do the analysis. |
183 m_analysisFrame->doFFT(tempP); | 183 m_analysisFrame->doFFT(tempP); |
184 | 184 |
185 float* realP = m_analysisFrame->realData(); | 185 float* realP = m_analysisFrame->realData(); |
186 float* imagP = m_analysisFrame->imagData(); | 186 float* imagP = m_analysisFrame->imagData(); |
187 | 187 |
188 // Blow away the packed nyquist component. | 188 // Blow away the packed nyquist component. |
189 imagP[0] = 0; | 189 imagP[0] = 0; |
190 | 190 |
191 // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FF
T scaling factor). | 191 // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FF
T scaling factor). |
192 const double magnitudeScale = 1.0 / DefaultFFTSize; | 192 const double magnitudeScale = 1.0 / DefaultFFTSize; |
193 | 193 |
194 // A value of 0 does no averaging with the previous result. Larger values p
roduce slower, but smoother changes. | 194 // A value of 0 does no averaging with the previous result. Larger values p
roduce slower, but smoother changes. |
195 double k = m_smoothingTimeConstant; | 195 double k = m_smoothingTimeConstant; |
196 k = max(0.0, k); | 196 k = max(0.0, k); |
197 k = min(1.0, k); | 197 k = min(1.0, k); |
198 | 198 |
199 // Convert the analysis data from complex to magnitude and average with the
previous result. | 199 // Convert the analysis data from complex to magnitude and average with the
previous result. |
200 float* destination = magnitudeBuffer().data(); | 200 float* destination = magnitudeBuffer().data(); |
201 size_t n = magnitudeBuffer().size(); | 201 size_t n = magnitudeBuffer().size(); |
202 for (size_t i = 0; i < n; ++i) { | 202 for (size_t i = 0; i < n; ++i) { |
203 Complex c(realP[i], imagP[i]); | 203 Complex c(realP[i], imagP[i]); |
204 double scalarMagnitude = abs(c) * magnitudeScale; | 204 double scalarMagnitude = abs(c) * magnitudeScale; |
205 destination[i] = float(k * destination[i] + (1 - k) * scalarMagnitude); | 205 destination[i] = float(k * destination[i] + (1 - k) * scalarMagnitude); |
206 } | 206 } |
207 } | 207 } |
208 | 208 |
209 void RealtimeAnalyser::getFloatFrequencyData(Float32Array* destinationArray) | 209 void RealtimeAnalyser::getFloatFrequencyData(Float32Array* destinationArray) |
210 { | 210 { |
211 ASSERT(isMainThread()); | 211 ASSERT(isMainThread()); |
212 | 212 |
213 if (!destinationArray) | 213 if (!destinationArray) |
214 return; | 214 return; |
215 | 215 |
216 doFFTAnalysis(); | 216 doFFTAnalysis(); |
217 | 217 |
218 // Convert from linear magnitude to floating-point decibels. | 218 // Convert from linear magnitude to floating-point decibels. |
219 const double minDecibels = m_minDecibels; | 219 const double minDecibels = m_minDecibels; |
220 unsigned sourceLength = magnitudeBuffer().size(); | 220 unsigned sourceLength = magnitudeBuffer().size(); |
221 size_t len = min(sourceLength, destinationArray->length()); | 221 size_t len = min(sourceLength, destinationArray->length()); |
222 if (len > 0) { | 222 if (len > 0) { |
223 const float* source = magnitudeBuffer().data(); | 223 const float* source = magnitudeBuffer().data(); |
224 float* destination = destinationArray->data(); | 224 float* destination = destinationArray->data(); |
225 | 225 |
226 for (unsigned i = 0; i < len; ++i) { | 226 for (unsigned i = 0; i < len; ++i) { |
227 float linearValue = source[i]; | 227 float linearValue = source[i]; |
228 double dbMag = !linearValue ? minDecibels : AudioUtilities::linearTo
Decibels(linearValue); | 228 double dbMag = !linearValue ? minDecibels : AudioUtilities::linearTo
Decibels(linearValue); |
229 destination[i] = float(dbMag); | 229 destination[i] = float(dbMag); |
230 } | 230 } |
231 } | 231 } |
232 } | 232 } |
233 | 233 |
234 void RealtimeAnalyser::getByteFrequencyData(Uint8Array* destinationArray) | 234 void RealtimeAnalyser::getByteFrequencyData(Uint8Array* destinationArray) |
235 { | 235 { |
236 ASSERT(isMainThread()); | 236 ASSERT(isMainThread()); |
237 | 237 |
238 if (!destinationArray) | 238 if (!destinationArray) |
239 return; | 239 return; |
240 | 240 |
241 doFFTAnalysis(); | 241 doFFTAnalysis(); |
242 | 242 |
243 // Convert from linear magnitude to unsigned-byte decibels. | 243 // Convert from linear magnitude to unsigned-byte decibels. |
244 unsigned sourceLength = magnitudeBuffer().size(); | 244 unsigned sourceLength = magnitudeBuffer().size(); |
245 size_t len = min(sourceLength, destinationArray->length()); | 245 size_t len = min(sourceLength, destinationArray->length()); |
246 if (len > 0) { | 246 if (len > 0) { |
247 const double rangeScaleFactor = m_maxDecibels == m_minDecibels ? 1 : 1 /
(m_maxDecibels - m_minDecibels); | 247 const double rangeScaleFactor = m_maxDecibels == m_minDecibels ? 1 : 1 /
(m_maxDecibels - m_minDecibels); |
248 const double minDecibels = m_minDecibels; | 248 const double minDecibels = m_minDecibels; |
249 | 249 |
250 const float* source = magnitudeBuffer().data(); | 250 const float* source = magnitudeBuffer().data(); |
251 unsigned char* destination = destinationArray->data(); | 251 unsigned char* destination = destinationArray->data(); |
252 | 252 |
253 for (unsigned i = 0; i < len; ++i) { | 253 for (unsigned i = 0; i < len; ++i) { |
254 float linearValue = source[i]; | 254 float linearValue = source[i]; |
255 double dbMag = !linearValue ? minDecibels : AudioUtilities::linearTo
Decibels(linearValue); | 255 double dbMag = !linearValue ? minDecibels : AudioUtilities::linearTo
Decibels(linearValue); |
256 | 256 |
257 // The range m_minDecibels to m_maxDecibels will be scaled to byte v
alues from 0 to UCHAR_MAX. | 257 // The range m_minDecibels to m_maxDecibels will be scaled to byte v
alues from 0 to UCHAR_MAX. |
258 double scaledValue = UCHAR_MAX * (dbMag - minDecibels) * rangeScaleF
actor; | 258 double scaledValue = UCHAR_MAX * (dbMag - minDecibels) * rangeScaleF
actor; |
259 | 259 |
260 // Clip to valid range. | 260 // Clip to valid range. |
261 if (scaledValue < 0) | 261 if (scaledValue < 0) |
262 scaledValue = 0; | 262 scaledValue = 0; |
263 if (scaledValue > UCHAR_MAX) | 263 if (scaledValue > UCHAR_MAX) |
264 scaledValue = UCHAR_MAX; | 264 scaledValue = UCHAR_MAX; |
265 | 265 |
266 destination[i] = static_cast<unsigned char>(scaledValue); | 266 destination[i] = static_cast<unsigned char>(scaledValue); |
267 } | 267 } |
268 } | 268 } |
269 } | 269 } |
270 | 270 |
271 void RealtimeAnalyser::getByteTimeDomainData(Uint8Array* destinationArray) | 271 void RealtimeAnalyser::getByteTimeDomainData(Uint8Array* destinationArray) |
272 { | 272 { |
273 ASSERT(isMainThread()); | 273 ASSERT(isMainThread()); |
274 | 274 |
275 if (!destinationArray) | 275 if (!destinationArray) |
276 return; | 276 return; |
277 | 277 |
278 unsigned fftSize = this->fftSize(); | 278 unsigned fftSize = this->fftSize(); |
279 size_t len = min(fftSize, destinationArray->length()); | 279 size_t len = min(fftSize, destinationArray->length()); |
280 if (len > 0) { | 280 if (len > 0) { |
281 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; | 281 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; |
282 ASSERT(isInputBufferGood); | 282 ASSERT(isInputBufferGood); |
283 if (!isInputBufferGood) | 283 if (!isInputBufferGood) |
284 return; | 284 return; |
285 | 285 |
286 float* inputBuffer = m_inputBuffer.data(); | 286 float* inputBuffer = m_inputBuffer.data(); |
287 unsigned char* destination = destinationArray->data(); | 287 unsigned char* destination = destinationArray->data(); |
288 | 288 |
289 unsigned writeIndex = m_writeIndex; | 289 unsigned writeIndex = m_writeIndex; |
290 | 290 |
291 for (unsigned i = 0; i < len; ++i) { | 291 for (unsigned i = 0; i < len; ++i) { |
292 // Buffer access is protected due to modulo operation. | 292 // Buffer access is protected due to modulo operation. |
293 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; | 293 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; |
294 | 294 |
295 // Scale from nominal -1 -> +1 to unsigned byte. | 295 // Scale from nominal -1 -> +1 to unsigned byte. |
296 double scaledValue = 128 * (value + 1); | 296 double scaledValue = 128 * (value + 1); |
297 | 297 |
298 // Clip to valid range. | 298 // Clip to valid range. |
299 if (scaledValue < 0) | 299 if (scaledValue < 0) |
300 scaledValue = 0; | 300 scaledValue = 0; |
301 if (scaledValue > UCHAR_MAX) | 301 if (scaledValue > UCHAR_MAX) |
302 scaledValue = UCHAR_MAX; | 302 scaledValue = UCHAR_MAX; |
303 | 303 |
304 destination[i] = static_cast<unsigned char>(scaledValue); | 304 destination[i] = static_cast<unsigned char>(scaledValue); |
305 } | 305 } |
306 } | 306 } |
307 } | 307 } |
308 | 308 |
309 } // namespace WebCore | 309 } // namespace WebCore |
310 | 310 |
311 #endif // ENABLE(WEB_AUDIO) | 311 #endif // ENABLE(WEB_AUDIO) |
OLD | NEW |