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 "core/platform/audio/AudioBus.h" 32 #include "core/platform/audio/AudioUtilities.h" 33 #include "core/platform/audio/FFTFrame.h" 34 #include "core/platform/audio/VectorMath.h" 35 36 #include <algorithm> 37 #include <limits.h> 38 #include "wtf/Complex.h" 39 #include "wtf/Float32Array.h" 40 #include "wtf/MainThread.h" 41 #include "wtf/MathExtras.h" 42 #include "wtf/Uint8Array.h" 43 44 using namespace std; 45 46 namespace WebCore { 47 48 const double RealtimeAnalyser::DefaultSmoothingTimeConstant = 0.8; 49 const double RealtimeAnalyser::DefaultMinDecibels = -100; 50 const double RealtimeAnalyser::DefaultMaxDecibels = -30; 51 52 const unsigned RealtimeAnalyser::DefaultFFTSize = 2048; 53 // All FFT implementations are expected to handle power-of-two sizes MinFFTSize <= size <= MaxFFTSize. 54 const unsigned RealtimeAnalyser::MinFFTSize = 32; 55 const unsigned RealtimeAnalyser::MaxFFTSize = 2048; 56 const unsigned RealtimeAnalyser::InputBufferSize = RealtimeAnalyser::MaxFFTSize * 2; 57 58 RealtimeAnalyser::RealtimeAnalyser() 59 : m_inputBuffer(InputBufferSize) 60 , m_writeIndex(0) 61 , m_fftSize(DefaultFFTSize) 62 , m_magnitudeBuffer(DefaultFFTSize / 2) 63 , m_smoothingTimeConstant(DefaultSmoothingTimeConstant) 64 , m_minDecibels(DefaultMinDecibels) 65 , m_maxDecibels(DefaultMaxDecibels) 66 { 67 m_analysisFrame = adoptPtr(new FFTFrame(DefaultFFTSize)); 68 } 69 70 RealtimeAnalyser::~RealtimeAnalyser() 71 { 72 } 73 74 void RealtimeAnalyser::reset() 75 { 76 m_writeIndex = 0; 77 m_inputBuffer.zero(); 78 m_magnitudeBuffer.zero(); 79 } 80 81 bool RealtimeAnalyser::setFftSize(size_t size) 82 { 83 ASSERT(isMainThread()); 84 85 // Only allow powers of two. 86 unsigned log2size = static_cast<unsigned>(log2(size)); 87 bool isPOT(1UL << log2size == size); 88 89 if (!isPOT || size > MaxFFTSize || size < MinFFTSize) 90 return false; 91 92 if (m_fftSize != size) { 93 m_analysisFrame = adoptPtr(new FFTFrame(size)); 94 // m_magnitudeBuffer has size = fftSize / 2 because it contains floats reduced from complex values in m_analysisFrame. 95 m_magnitudeBuffer.allocate(size / 2); 96 m_fftSize = size; 97 } 98 99 return true; 100 } 101 102 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) 103 { 104 bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->length() >= framesToProcess; 105 ASSERT(isBusGood); 106 if (!isBusGood) 107 return; 108 109 // FIXME : allow to work with non-FFTSize divisible chunking 110 bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex + framesToProcess <= m_inputBuffer.size(); 111 ASSERT(isDestinationGood); 112 if (!isDestinationGood) 113 return; 114 115 // Perform real-time analysis 116 const float* source = bus->channel(0)->data(); 117 float* dest = m_inputBuffer.data() + m_writeIndex; 118 119 // The source has already been sanity checked with isBusGood above. 120 memcpy(dest, source, sizeof(float) * framesToProcess); 121 122 // Sum all channels in one if numberOfChannels > 1. 123 unsigned numberOfChannels = bus->numberOfChannels(); 124 if (numberOfChannels > 1) { 125 for (unsigned i = 1; i < numberOfChannels; i++) { 126 source = bus->channel(i)->data(); 127 VectorMath::vadd(dest, 1, source, 1, dest, 1, framesToProcess); 128 } 129 const float scale = 1.0 / numberOfChannels; 130 VectorMath::vsmul(dest, 1, &scale, dest, 1, framesToProcess); 131 } 132 133 m_writeIndex += framesToProcess; 134 if (m_writeIndex >= InputBufferSize) 135 m_writeIndex = 0; 136 } 137 138 namespace { 139 140 void applyWindow(float* p, size_t n) 141 { 142 ASSERT(isMainThread()); 143 144 // Blackman window 145 double alpha = 0.16; 146 double a0 = 0.5 * (1 - alpha); 147 double a1 = 0.5; 148 double a2 = 0.5 * alpha; 149 150 for (unsigned i = 0; i < n; ++i) { 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); 153 p[i] *= float(window); 154 } 155 } 156 157 } // namespace 158 159 void RealtimeAnalyser::doFFTAnalysis() 160 { 161 ASSERT(isMainThread()); 162 163 // Unroll the input buffer into a temporary buffer, where we'll apply an analysis window followed by an FFT. 164 size_t fftSize = this->fftSize(); 165 166 AudioFloatArray temporaryBuffer(fftSize); 167 float* inputBuffer = m_inputBuffer.data(); 168 float* tempP = temporaryBuffer.data(); 169 170 // Take the previous fftSize values from the input buffer and copy into the temporary buffer. 171 unsigned writeIndex = m_writeIndex; 172 if (writeIndex < fftSize) { 173 memcpy(tempP, inputBuffer + writeIndex - fftSize + InputBufferSize, sizeof(*tempP) * (fftSize - writeIndex)); 174 memcpy(tempP + fftSize - writeIndex, inputBuffer, sizeof(*tempP) * writeIndex); 175 } else 176 memcpy(tempP, inputBuffer + writeIndex - fftSize, sizeof(*tempP) * fftSize); 177 178 179 // Window the input samples. 180 applyWindow(tempP, fftSize); 181 182 // Do the analysis. 183 m_analysisFrame->doFFT(tempP); 184 185 float* realP = m_analysisFrame->realData(); 186 float* imagP = m_analysisFrame->imagData(); 187 188 // Blow away the packed nyquist component. 189 imagP[0] = 0; 190 191 // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor). 192 const double magnitudeScale = 1.0 / DefaultFFTSize; 193 194 // A value of 0 does no averaging with the previous result. Larger values produce slower, but smoother changes. 195 double k = m_smoothingTimeConstant; 196 k = max(0.0, k); 197 k = min(1.0, k); 198 199 // Convert the analysis data from complex to magnitude and average with the previous result. 200 float* destination = magnitudeBuffer().data(); 201 size_t n = magnitudeBuffer().size(); 202 for (size_t i = 0; i < n; ++i) { 203 Complex c(realP[i], imagP[i]); 204 double scalarMagnitude = abs(c) * magnitudeScale; 205 destination[i] = float(k * destination[i] + (1 - k) * scalarMagnitude); 206 } 207 } 208 209 void RealtimeAnalyser::getFloatFrequencyData(Float32Array* destinationArray) 210 { 211 ASSERT(isMainThread()); 212 213 if (!destinationArray) 214 return; 215 216 doFFTAnalysis(); 217 218 // Convert from linear magnitude to floating-point decibels. 219 const double minDecibels = m_minDecibels; 220 unsigned sourceLength = magnitudeBuffer().size(); 221 size_t len = min(sourceLength, destinationArray->length()); 222 if (len > 0) { 223 const float* source = magnitudeBuffer().data(); 224 float* destination = destinationArray->data(); 225 226 for (unsigned i = 0; i < len; ++i) { 227 float linearValue = source[i]; 228 double dbMag = !linearValue ? minDecibels : AudioUtilities::linearToDecibels(linearValue); 229 destination[i] = float(dbMag); 230 } 231 } 232 } 233 234 void RealtimeAnalyser::getByteFrequencyData(Uint8Array* destinationArray) 235 { 236 ASSERT(isMainThread()); 237 238 if (!destinationArray) 239 return; 240 241 doFFTAnalysis(); 242 243 // Convert from linear magnitude to unsigned-byte decibels. 244 unsigned sourceLength = magnitudeBuffer().size(); 245 size_t len = min(sourceLength, destinationArray->length()); 246 if (len > 0) { 247 const double rangeScaleFactor = m_maxDecibels == m_minDecibels ? 1 : 1 / (m_maxDecibels - m_minDecibels); 248 const double minDecibels = m_minDecibels; 249 250 const float* source = magnitudeBuffer().data(); 251 unsigned char* destination = destinationArray->data(); 252 253 for (unsigned i = 0; i < len; ++i) { 254 float linearValue = source[i]; 255 double dbMag = !linearValue ? minDecibels : AudioUtilities::linearToDecibels(linearValue); 256 257 // The range m_minDecibels to m_maxDecibels will be scaled to byte values from 0 to UCHAR_MAX. 258 double scaledValue = UCHAR_MAX * (dbMag - minDecibels) * rangeScaleFactor; 259 260 // Clip to valid range. 261 if (scaledValue < 0) 262 scaledValue = 0; 263 if (scaledValue > UCHAR_MAX) 264 scaledValue = UCHAR_MAX; 265 266 destination[i] = static_cast<unsigned char>(scaledValue); 267 } 268 } 269 } 270 271 void RealtimeAnalyser::getByteTimeDomainData(Uint8Array* destinationArray) 272 { 273 ASSERT(isMainThread()); 274 275 if (!destinationArray) 276 return; 277 278 unsigned fftSize = this->fftSize(); 279 size_t len = min(fftSize, destinationArray->length()); 280 if (len > 0) { 281 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_inputBuffer.size() > fftSize; 282 ASSERT(isInputBufferGood); 283 if (!isInputBufferGood) 284 return; 285 286 float* inputBuffer = m_inputBuffer.data(); 287 unsigned char* destination = destinationArray->data(); 288 289 unsigned writeIndex = m_writeIndex; 290 291 for (unsigned i = 0; i < len; ++i) { 292 // Buffer access is protected due to modulo operation. 293 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSize) % InputBufferSize]; 294 295 // Scale from nominal -1 -> +1 to unsigned byte. 296 double scaledValue = 128 * (value + 1); 297 298 // Clip to valid range. 299 if (scaledValue < 0) 300 scaledValue = 0; 301 if (scaledValue > UCHAR_MAX) 302 scaledValue = UCHAR_MAX; 303 304 destination[i] = static_cast<unsigned char>(scaledValue); 305 } 306 } 307 } 308 309 } // namespace WebCore 310 311 #endif // ENABLE(WEB_AUDIO) 312