| 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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 , m_smoothingTimeConstant(DefaultSmoothingTimeConstant) | 53 , m_smoothingTimeConstant(DefaultSmoothingTimeConstant) |
| 54 , m_minDecibels(DefaultMinDecibels) | 54 , m_minDecibels(DefaultMinDecibels) |
| 55 , m_maxDecibels(DefaultMaxDecibels) | 55 , m_maxDecibels(DefaultMaxDecibels) |
| 56 , m_lastAnalysisTime(-1) | 56 , m_lastAnalysisTime(-1) |
| 57 { | 57 { |
| 58 m_analysisFrame = wrapUnique(new FFTFrame(DefaultFFTSize)); | 58 m_analysisFrame = wrapUnique(new FFTFrame(DefaultFFTSize)); |
| 59 } | 59 } |
| 60 | 60 |
| 61 bool RealtimeAnalyser::setFftSize(size_t size) | 61 bool RealtimeAnalyser::setFftSize(size_t size) |
| 62 { | 62 { |
| 63 ASSERT(isMainThread()); | 63 DCHECK(isMainThread()); |
| 64 | 64 |
| 65 // Only allow powers of two. | 65 // Only allow powers of two. |
| 66 unsigned log2size = static_cast<unsigned>(log2(size)); | 66 unsigned log2size = static_cast<unsigned>(log2(size)); |
| 67 bool isPOT(1UL << log2size == size); | 67 bool isPOT(1UL << log2size == size); |
| 68 | 68 |
| 69 if (!isPOT || size > MaxFFTSize || size < MinFFTSize) | 69 if (!isPOT || size > MaxFFTSize || size < MinFFTSize) |
| 70 return false; | 70 return false; |
| 71 | 71 |
| 72 if (m_fftSize != size) { | 72 if (m_fftSize != size) { |
| 73 m_analysisFrame = wrapUnique(new FFTFrame(size)); | 73 m_analysisFrame = wrapUnique(new FFTFrame(size)); |
| 74 // m_magnitudeBuffer has size = fftSize / 2 because it contains floats r
educed from complex values in m_analysisFrame. | 74 // m_magnitudeBuffer has size = fftSize / 2 because it contains floats r
educed from complex values in m_analysisFrame. |
| 75 m_magnitudeBuffer.allocate(size / 2); | 75 m_magnitudeBuffer.allocate(size / 2); |
| 76 m_fftSize = size; | 76 m_fftSize = size; |
| 77 } | 77 } |
| 78 | 78 |
| 79 return true; | 79 return true; |
| 80 } | 80 } |
| 81 | 81 |
| 82 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) | 82 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) |
| 83 { | 83 { |
| 84 bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->leng
th() >= framesToProcess; | 84 bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->leng
th() >= framesToProcess; |
| 85 ASSERT(isBusGood); | 85 DCHECK(isBusGood); |
| 86 if (!isBusGood) | 86 if (!isBusGood) |
| 87 return; | 87 return; |
| 88 | 88 |
| 89 // FIXME : allow to work with non-FFTSize divisible chunking | 89 // FIXME : allow to work with non-FFTSize divisible chunking |
| 90 bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex
+ framesToProcess <= m_inputBuffer.size(); | 90 bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex
+ framesToProcess <= m_inputBuffer.size(); |
| 91 ASSERT(isDestinationGood); | 91 DCHECK(isDestinationGood); |
| 92 if (!isDestinationGood) | 92 if (!isDestinationGood) |
| 93 return; | 93 return; |
| 94 | 94 |
| 95 // Perform real-time analysis | 95 // Perform real-time analysis |
| 96 float* dest = m_inputBuffer.data() + m_writeIndex; | 96 float* dest = m_inputBuffer.data() + m_writeIndex; |
| 97 | 97 |
| 98 // Clear the bus and downmix the input according to the down mixing rules.
Then save the result | 98 // Clear the bus and downmix the input according to the down mixing rules.
Then save the result |
| 99 // in the m_inputBuffer at the appropriate place. | 99 // in the m_inputBuffer at the appropriate place. |
| 100 m_downMixBus->zero(); | 100 m_downMixBus->zero(); |
| 101 m_downMixBus->sumFrom(*bus); | 101 m_downMixBus->sumFrom(*bus); |
| 102 memcpy(dest, m_downMixBus->channel(0)->data(), framesToProcess * sizeof(*des
t)); | 102 memcpy(dest, m_downMixBus->channel(0)->data(), framesToProcess * sizeof(*des
t)); |
| 103 | 103 |
| 104 m_writeIndex += framesToProcess; | 104 m_writeIndex += framesToProcess; |
| 105 if (m_writeIndex >= InputBufferSize) | 105 if (m_writeIndex >= InputBufferSize) |
| 106 m_writeIndex = 0; | 106 m_writeIndex = 0; |
| 107 } | 107 } |
| 108 | 108 |
| 109 namespace { | 109 namespace { |
| 110 | 110 |
| 111 void applyWindow(float* p, size_t n) | 111 void applyWindow(float* p, size_t n) |
| 112 { | 112 { |
| 113 ASSERT(isMainThread()); | 113 DCHECK(isMainThread()); |
| 114 | 114 |
| 115 // Blackman window | 115 // Blackman window |
| 116 double alpha = 0.16; | 116 double alpha = 0.16; |
| 117 double a0 = 0.5 * (1 - alpha); | 117 double a0 = 0.5 * (1 - alpha); |
| 118 double a1 = 0.5; | 118 double a1 = 0.5; |
| 119 double a2 = 0.5 * alpha; | 119 double a2 = 0.5 * alpha; |
| 120 | 120 |
| 121 for (unsigned i = 0; i < n; ++i) { | 121 for (unsigned i = 0; i < n; ++i) { |
| 122 double x = static_cast<double>(i) / static_cast<double>(n); | 122 double x = static_cast<double>(i) / static_cast<double>(n); |
| 123 double window = a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble *
2.0 * x); | 123 double window = a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble *
2.0 * x); |
| 124 p[i] *= float(window); | 124 p[i] *= float(window); |
| 125 } | 125 } |
| 126 } | 126 } |
| 127 | 127 |
| 128 } // namespace | 128 } // namespace |
| 129 | 129 |
| 130 void RealtimeAnalyser::doFFTAnalysis() | 130 void RealtimeAnalyser::doFFTAnalysis() |
| 131 { | 131 { |
| 132 ASSERT(isMainThread()); | 132 DCHECK(isMainThread()); |
| 133 | 133 |
| 134 // Unroll the input buffer into a temporary buffer, where we'll apply an ana
lysis window followed by an FFT. | 134 // Unroll the input buffer into a temporary buffer, where we'll apply an ana
lysis window followed by an FFT. |
| 135 size_t fftSize = this->fftSize(); | 135 size_t fftSize = this->fftSize(); |
| 136 | 136 |
| 137 AudioFloatArray temporaryBuffer(fftSize); | 137 AudioFloatArray temporaryBuffer(fftSize); |
| 138 float* inputBuffer = m_inputBuffer.data(); | 138 float* inputBuffer = m_inputBuffer.data(); |
| 139 float* tempP = temporaryBuffer.data(); | 139 float* tempP = temporaryBuffer.data(); |
| 140 | 140 |
| 141 // Take the previous fftSize values from the input buffer and copy into the
temporary buffer. | 141 // Take the previous fftSize values from the input buffer and copy into the
temporary buffer. |
| 142 unsigned writeIndex = m_writeIndex; | 142 unsigned writeIndex = m_writeIndex; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 for (unsigned i = 0; i < len; ++i) { | 189 for (unsigned i = 0; i < len; ++i) { |
| 190 float linearValue = source[i]; | 190 float linearValue = source[i]; |
| 191 double dbMag = AudioUtilities::linearToDecibels(linearValue); | 191 double dbMag = AudioUtilities::linearToDecibels(linearValue); |
| 192 destination[i] = float(dbMag); | 192 destination[i] = float(dbMag); |
| 193 } | 193 } |
| 194 } | 194 } |
| 195 } | 195 } |
| 196 | 196 |
| 197 void RealtimeAnalyser::getFloatFrequencyData(DOMFloat32Array* destinationArray,
double currentTime) | 197 void RealtimeAnalyser::getFloatFrequencyData(DOMFloat32Array* destinationArray,
double currentTime) |
| 198 { | 198 { |
| 199 ASSERT(isMainThread()); | 199 DCHECK(isMainThread()); |
| 200 ASSERT(destinationArray); | 200 DCHECK(destinationArray); |
| 201 | 201 |
| 202 if (currentTime <= m_lastAnalysisTime) { | 202 if (currentTime <= m_lastAnalysisTime) { |
| 203 convertFloatToDb(destinationArray); | 203 convertFloatToDb(destinationArray); |
| 204 return; | 204 return; |
| 205 } | 205 } |
| 206 | 206 |
| 207 // Time has advanced since the last call; update the FFT data. | 207 // Time has advanced since the last call; update the FFT data. |
| 208 m_lastAnalysisTime = currentTime; | 208 m_lastAnalysisTime = currentTime; |
| 209 doFFTAnalysis(); | 209 doFFTAnalysis(); |
| 210 | 210 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 236 if (scaledValue > UCHAR_MAX) | 236 if (scaledValue > UCHAR_MAX) |
| 237 scaledValue = UCHAR_MAX; | 237 scaledValue = UCHAR_MAX; |
| 238 | 238 |
| 239 destination[i] = static_cast<unsigned char>(scaledValue); | 239 destination[i] = static_cast<unsigned char>(scaledValue); |
| 240 } | 240 } |
| 241 } | 241 } |
| 242 } | 242 } |
| 243 | 243 |
| 244 void RealtimeAnalyser::getByteFrequencyData(DOMUint8Array* destinationArray, dou
ble currentTime) | 244 void RealtimeAnalyser::getByteFrequencyData(DOMUint8Array* destinationArray, dou
ble currentTime) |
| 245 { | 245 { |
| 246 ASSERT(isMainThread()); | 246 DCHECK(isMainThread()); |
| 247 ASSERT(destinationArray); | 247 DCHECK(destinationArray); |
| 248 | 248 |
| 249 if (currentTime <= m_lastAnalysisTime) { | 249 if (currentTime <= m_lastAnalysisTime) { |
| 250 // FIXME: Is it worth caching the data so we don't have to do the conver
sion every time? | 250 // FIXME: Is it worth caching the data so we don't have to do the conver
sion every time? |
| 251 // Perhaps not, since we expect many calls in the same rendering quantum
. | 251 // Perhaps not, since we expect many calls in the same rendering quantum
. |
| 252 convertToByteData(destinationArray); | 252 convertToByteData(destinationArray); |
| 253 return; | 253 return; |
| 254 } | 254 } |
| 255 | 255 |
| 256 // Time has advanced since the last call; update the FFT data. | 256 // Time has advanced since the last call; update the FFT data. |
| 257 m_lastAnalysisTime = currentTime; | 257 m_lastAnalysisTime = currentTime; |
| 258 doFFTAnalysis(); | 258 doFFTAnalysis(); |
| 259 | 259 |
| 260 convertToByteData(destinationArray); | 260 convertToByteData(destinationArray); |
| 261 } | 261 } |
| 262 | 262 |
| 263 void RealtimeAnalyser::getFloatTimeDomainData(DOMFloat32Array* destinationArray) | 263 void RealtimeAnalyser::getFloatTimeDomainData(DOMFloat32Array* destinationArray) |
| 264 { | 264 { |
| 265 ASSERT(isMainThread()); | 265 DCHECK(isMainThread()); |
| 266 ASSERT(destinationArray); | 266 DCHECK(destinationArray); |
| 267 | 267 |
| 268 unsigned fftSize = this->fftSize(); | 268 unsigned fftSize = this->fftSize(); |
| 269 size_t len = std::min(fftSize, destinationArray->length()); | 269 size_t len = std::min(fftSize, destinationArray->length()); |
| 270 if (len > 0) { | 270 if (len > 0) { |
| 271 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; | 271 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; |
| 272 ASSERT(isInputBufferGood); | 272 DCHECK(isInputBufferGood); |
| 273 if (!isInputBufferGood) | 273 if (!isInputBufferGood) |
| 274 return; | 274 return; |
| 275 | 275 |
| 276 float* inputBuffer = m_inputBuffer.data(); | 276 float* inputBuffer = m_inputBuffer.data(); |
| 277 float* destination = destinationArray->data(); | 277 float* destination = destinationArray->data(); |
| 278 | 278 |
| 279 unsigned writeIndex = m_writeIndex; | 279 unsigned writeIndex = m_writeIndex; |
| 280 | 280 |
| 281 for (unsigned i = 0; i < len; ++i) { | 281 for (unsigned i = 0; i < len; ++i) { |
| 282 // Buffer access is protected due to modulo operation. | 282 // Buffer access is protected due to modulo operation. |
| 283 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; | 283 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; |
| 284 | 284 |
| 285 destination[i] = value; | 285 destination[i] = value; |
| 286 } | 286 } |
| 287 } | 287 } |
| 288 } | 288 } |
| 289 | 289 |
| 290 void RealtimeAnalyser::getByteTimeDomainData(DOMUint8Array* destinationArray) | 290 void RealtimeAnalyser::getByteTimeDomainData(DOMUint8Array* destinationArray) |
| 291 { | 291 { |
| 292 ASSERT(isMainThread()); | 292 DCHECK(isMainThread()); |
| 293 ASSERT(destinationArray); | 293 DCHECK(destinationArray); |
| 294 | 294 |
| 295 unsigned fftSize = this->fftSize(); | 295 unsigned fftSize = this->fftSize(); |
| 296 size_t len = std::min(fftSize, destinationArray->length()); | 296 size_t len = std::min(fftSize, destinationArray->length()); |
| 297 if (len > 0) { | 297 if (len > 0) { |
| 298 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; | 298 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; |
| 299 ASSERT(isInputBufferGood); | 299 DCHECK(isInputBufferGood); |
| 300 if (!isInputBufferGood) | 300 if (!isInputBufferGood) |
| 301 return; | 301 return; |
| 302 | 302 |
| 303 float* inputBuffer = m_inputBuffer.data(); | 303 float* inputBuffer = m_inputBuffer.data(); |
| 304 unsigned char* destination = destinationArray->data(); | 304 unsigned char* destination = destinationArray->data(); |
| 305 | 305 |
| 306 unsigned writeIndex = m_writeIndex; | 306 unsigned writeIndex = m_writeIndex; |
| 307 | 307 |
| 308 for (unsigned i = 0; i < len; ++i) { | 308 for (unsigned i = 0; i < len; ++i) { |
| 309 // Buffer access is protected due to modulo operation. | 309 // Buffer access is protected due to modulo operation. |
| 310 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; | 310 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; |
| 311 | 311 |
| 312 // Scale from nominal -1 -> +1 to unsigned byte. | 312 // Scale from nominal -1 -> +1 to unsigned byte. |
| 313 double scaledValue = 128 * (value + 1); | 313 double scaledValue = 128 * (value + 1); |
| 314 | 314 |
| 315 // Clip to valid range. | 315 // Clip to valid range. |
| 316 if (scaledValue < 0) | 316 if (scaledValue < 0) |
| 317 scaledValue = 0; | 317 scaledValue = 0; |
| 318 if (scaledValue > UCHAR_MAX) | 318 if (scaledValue > UCHAR_MAX) |
| 319 scaledValue = UCHAR_MAX; | 319 scaledValue = UCHAR_MAX; |
| 320 | 320 |
| 321 destination[i] = static_cast<unsigned char>(scaledValue); | 321 destination[i] = static_cast<unsigned char>(scaledValue); |
| 322 } | 322 } |
| 323 } | 323 } |
| 324 } | 324 } |
| 325 | 325 |
| 326 } // namespace blink | 326 } // namespace blink |
| 327 | 327 |
| OLD | NEW |