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