| 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 |