Home | History | Annotate | Download | only in webaudio
      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