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/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