1 /* 2 * Copyright (C) 2010, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include "config.h" 26 27 #if ENABLE(WEB_AUDIO) 28 29 #include "modules/webaudio/RealtimeAnalyser.h" 30 31 #include "platform/audio/AudioBus.h" 32 #include "platform/audio/AudioUtilities.h" 33 #include "platform/audio/VectorMath.h" 34 35 #include <algorithm> 36 #include <limits.h> 37 #include "wtf/Complex.h" 38 #include "wtf/Float32Array.h" 39 #include "wtf/MainThread.h" 40 #include "wtf/MathExtras.h" 41 #include "wtf/Uint8Array.h" 42 43 namespace blink { 44 45 const double RealtimeAnalyser::DefaultSmoothingTimeConstant = 0.8; 46 const double RealtimeAnalyser::DefaultMinDecibels = -100; 47 const double RealtimeAnalyser::DefaultMaxDecibels = -30; 48 49 const unsigned RealtimeAnalyser::DefaultFFTSize = 2048; 50 // All FFT implementations are expected to handle power-of-two sizes MinFFTSize <= size <= MaxFFTSize. 51 const unsigned RealtimeAnalyser::MinFFTSize = 32; 52 const unsigned RealtimeAnalyser::MaxFFTSize = 2048; 53 const unsigned RealtimeAnalyser::InputBufferSize = RealtimeAnalyser::MaxFFTSize * 2; 54 55 RealtimeAnalyser::RealtimeAnalyser() 56 : m_inputBuffer(InputBufferSize) 57 , m_writeIndex(0) 58 , m_fftSize(DefaultFFTSize) 59 , m_magnitudeBuffer(DefaultFFTSize / 2) 60 , m_smoothingTimeConstant(DefaultSmoothingTimeConstant) 61 , m_minDecibels(DefaultMinDecibels) 62 , m_maxDecibels(DefaultMaxDecibels) 63 { 64 m_analysisFrame = adoptPtr(new FFTFrame(DefaultFFTSize)); 65 } 66 67 bool RealtimeAnalyser::setFftSize(size_t size) 68 { 69 ASSERT(isMainThread()); 70 71 // Only allow powers of two. 72 unsigned log2size = static_cast<unsigned>(log2(size)); 73 bool isPOT(1UL << log2size == size); 74 75 if (!isPOT || size > MaxFFTSize || size < MinFFTSize) 76 return false; 77 78 if (m_fftSize != size) { 79 m_analysisFrame = adoptPtr(new FFTFrame(size)); 80 // m_magnitudeBuffer has size = fftSize / 2 because it contains floats reduced from complex values in m_analysisFrame. 81 m_magnitudeBuffer.allocate(size / 2); 82 m_fftSize = size; 83 } 84 85 return true; 86 } 87 88 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) 89 { 90 bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->length() >= framesToProcess; 91 ASSERT(isBusGood); 92 if (!isBusGood) 93 return; 94 95 // FIXME : allow to work with non-FFTSize divisible chunking 96 bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex + framesToProcess <= m_inputBuffer.size(); 97 ASSERT(isDestinationGood); 98 if (!isDestinationGood) 99 return; 100 101 // Perform real-time analysis 102 const float* source = bus->channel(0)->data(); 103 float* dest = m_inputBuffer.data() + m_writeIndex; 104 105 // The source has already been sanity checked with isBusGood above. 106 memcpy(dest, source, sizeof(float) * framesToProcess); 107 108 // Sum all channels in one if numberOfChannels > 1. 109 unsigned numberOfChannels = bus->numberOfChannels(); 110 if (numberOfChannels > 1) { 111 for (unsigned i = 1; i < numberOfChannels; i++) { 112 source = bus->channel(i)->data(); 113 VectorMath::vadd(dest, 1, source, 1, dest, 1, framesToProcess); 114 } 115 const float scale = 1.0 / numberOfChannels; 116 VectorMath::vsmul(dest, 1, &scale, dest, 1, framesToProcess); 117 } 118 119 m_writeIndex += framesToProcess; 120 if (m_writeIndex >= InputBufferSize) 121 m_writeIndex = 0; 122 } 123 124 namespace { 125 126 void applyWindow(float* p, size_t n) 127 { 128 ASSERT(isMainThread()); 129 130 // Blackman window 131 double alpha = 0.16; 132 double a0 = 0.5 * (1 - alpha); 133 double a1 = 0.5; 134 double a2 = 0.5 * alpha; 135 136 for (unsigned i = 0; i < n; ++i) { 137 double x = static_cast<double>(i) / static_cast<double>(n); 138 double window = a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble * 2.0 * x); 139 p[i] *= float(window); 140 } 141 } 142 143 } // namespace 144 145 void RealtimeAnalyser::doFFTAnalysis() 146 { 147 ASSERT(isMainThread()); 148 149 // Unroll the input buffer into a temporary buffer, where we'll apply an analysis window followed by an FFT. 150 size_t fftSize = this->fftSize(); 151 152 AudioFloatArray temporaryBuffer(fftSize); 153 float* inputBuffer = m_inputBuffer.data(); 154 float* tempP = temporaryBuffer.data(); 155 156 // Take the previous fftSize values from the input buffer and copy into the temporary buffer. 157 unsigned writeIndex = m_writeIndex; 158 if (writeIndex < fftSize) { 159 memcpy(tempP, inputBuffer + writeIndex - fftSize + InputBufferSize, sizeof(*tempP) * (fftSize - writeIndex)); 160 memcpy(tempP + fftSize - writeIndex, inputBuffer, sizeof(*tempP) * writeIndex); 161 } else 162 memcpy(tempP, inputBuffer + writeIndex - fftSize, sizeof(*tempP) * fftSize); 163 164 165 // Window the input samples. 166 applyWindow(tempP, fftSize); 167 168 // Do the analysis. 169 m_analysisFrame->doFFT(tempP); 170 171 float* realP = m_analysisFrame->realData(); 172 float* imagP = m_analysisFrame->imagData(); 173 174 // Blow away the packed nyquist component. 175 imagP[0] = 0; 176 177 // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor). 178 const double magnitudeScale = 1.0 / fftSize; 179 180 // A value of 0 does no averaging with the previous result. Larger values produce slower, but smoother changes. 181 double k = m_smoothingTimeConstant; 182 k = std::max(0.0, k); 183 k = std::min(1.0, k); 184 185 // Convert the analysis data from complex to magnitude and average with the previous result. 186 float* destination = magnitudeBuffer().data(); 187 size_t n = magnitudeBuffer().size(); 188 for (size_t i = 0; i < n; ++i) { 189 Complex c(realP[i], imagP[i]); 190 double scalarMagnitude = abs(c) * magnitudeScale; 191 destination[i] = float(k * destination[i] + (1 - k) * scalarMagnitude); 192 } 193 } 194 195 void RealtimeAnalyser::getFloatFrequencyData(Float32Array* destinationArray) 196 { 197 ASSERT(isMainThread()); 198 199 if (!destinationArray) 200 return; 201 202 doFFTAnalysis(); 203 204 // Convert from linear magnitude to floating-point decibels. 205 const double minDecibels = m_minDecibels; 206 unsigned sourceLength = magnitudeBuffer().size(); 207 size_t len = std::min(sourceLength, destinationArray->length()); 208 if (len > 0) { 209 const float* source = magnitudeBuffer().data(); 210 float* destination = destinationArray->data(); 211 212 for (unsigned i = 0; i < len; ++i) { 213 float linearValue = source[i]; 214 double dbMag = !linearValue ? minDecibels : AudioUtilities::linearToDecibels(linearValue); 215 destination[i] = float(dbMag); 216 } 217 } 218 } 219 220 void RealtimeAnalyser::getByteFrequencyData(Uint8Array* destinationArray) 221 { 222 ASSERT(isMainThread()); 223 224 if (!destinationArray) 225 return; 226 227 doFFTAnalysis(); 228 229 // Convert from linear magnitude to unsigned-byte decibels. 230 unsigned sourceLength = magnitudeBuffer().size(); 231 size_t len = std::min(sourceLength, destinationArray->length()); 232 if (len > 0) { 233 const double rangeScaleFactor = m_maxDecibels == m_minDecibels ? 1 : 1 / (m_maxDecibels - m_minDecibels); 234 const double minDecibels = m_minDecibels; 235 236 const float* source = magnitudeBuffer().data(); 237 unsigned char* destination = destinationArray->data(); 238 239 for (unsigned i = 0; i < len; ++i) { 240 float linearValue = source[i]; 241 double dbMag = !linearValue ? minDecibels : AudioUtilities::linearToDecibels(linearValue); 242 243 // The range m_minDecibels to m_maxDecibels will be scaled to byte values from 0 to UCHAR_MAX. 244 double scaledValue = UCHAR_MAX * (dbMag - minDecibels) * rangeScaleFactor; 245 246 // Clip to valid range. 247 if (scaledValue < 0) 248 scaledValue = 0; 249 if (scaledValue > UCHAR_MAX) 250 scaledValue = UCHAR_MAX; 251 252 destination[i] = static_cast<unsigned char>(scaledValue); 253 } 254 } 255 } 256 257 void RealtimeAnalyser::getFloatTimeDomainData(Float32Array* destinationArray) 258 { 259 ASSERT(isMainThread()); 260 261 if (!destinationArray) 262 return; 263 264 unsigned fftSize = this->fftSize(); 265 size_t len = std::min(fftSize, destinationArray->length()); 266 if (len > 0) { 267 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_inputBuffer.size() > fftSize; 268 ASSERT(isInputBufferGood); 269 if (!isInputBufferGood) 270 return; 271 272 float* inputBuffer = m_inputBuffer.data(); 273 float* destination = destinationArray->data(); 274 275 unsigned writeIndex = m_writeIndex; 276 277 for (unsigned i = 0; i < len; ++i) { 278 // Buffer access is protected due to modulo operation. 279 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSize) % InputBufferSize]; 280 281 destination[i] = value; 282 } 283 } 284 } 285 286 void RealtimeAnalyser::getByteTimeDomainData(Uint8Array* destinationArray) 287 { 288 ASSERT(isMainThread()); 289 290 if (!destinationArray) 291 return; 292 293 unsigned fftSize = this->fftSize(); 294 size_t len = std::min(fftSize, destinationArray->length()); 295 if (len > 0) { 296 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_inputBuffer.size() > fftSize; 297 ASSERT(isInputBufferGood); 298 if (!isInputBufferGood) 299 return; 300 301 float* inputBuffer = m_inputBuffer.data(); 302 unsigned char* destination = destinationArray->data(); 303 304 unsigned writeIndex = m_writeIndex; 305 306 for (unsigned i = 0; i < len; ++i) { 307 // Buffer access is protected due to modulo operation. 308 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSize) % InputBufferSize]; 309 310 // Scale from nominal -1 -> +1 to unsigned byte. 311 double scaledValue = 128 * (value + 1); 312 313 // Clip to valid range. 314 if (scaledValue < 0) 315 scaledValue = 0; 316 if (scaledValue > UCHAR_MAX) 317 scaledValue = UCHAR_MAX; 318 319 destination[i] = static_cast<unsigned char>(scaledValue); 320 } 321 } 322 } 323 324 } // namespace blink 325 326 #endif // ENABLE(WEB_AUDIO) 327