This source file includes following definitions.
- m_maxDecibels
- setFftSize
- writeInput
- applyWindow
- doFFTAnalysis
- getFloatFrequencyData
- getByteFrequencyData
- getFloatTimeDomainData
- getByteTimeDomainData
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "modules/webaudio/RealtimeAnalyser.h"
#include "platform/audio/AudioBus.h"
#include "platform/audio/AudioUtilities.h"
#include "platform/audio/VectorMath.h"
#include <algorithm>
#include <limits.h>
#include "wtf/Complex.h"
#include "wtf/Float32Array.h"
#include "wtf/MainThread.h"
#include "wtf/MathExtras.h"
#include "wtf/Uint8Array.h"
using namespace std;
namespace WebCore {
const double RealtimeAnalyser::DefaultSmoothingTimeConstant = 0.8;
const double RealtimeAnalyser::DefaultMinDecibels = -100;
const double RealtimeAnalyser::DefaultMaxDecibels = -30;
const unsigned RealtimeAnalyser::DefaultFFTSize = 2048;
const unsigned RealtimeAnalyser::MinFFTSize = 32;
const unsigned RealtimeAnalyser::MaxFFTSize = 2048;
const unsigned RealtimeAnalyser::InputBufferSize = RealtimeAnalyser::MaxFFTSize * 2;
RealtimeAnalyser::RealtimeAnalyser()
: m_inputBuffer(InputBufferSize)
, m_writeIndex(0)
, m_fftSize(DefaultFFTSize)
, m_magnitudeBuffer(DefaultFFTSize / 2)
, m_smoothingTimeConstant(DefaultSmoothingTimeConstant)
, m_minDecibels(DefaultMinDecibels)
, m_maxDecibels(DefaultMaxDecibels)
{
m_analysisFrame = adoptPtr(new FFTFrame(DefaultFFTSize));
}
bool RealtimeAnalyser::setFftSize(size_t size)
{
ASSERT(isMainThread());
unsigned log2size = static_cast<unsigned>(log2(size));
bool isPOT(1UL << log2size == size);
if (!isPOT || size > MaxFFTSize || size < MinFFTSize)
return false;
if (m_fftSize != size) {
m_analysisFrame = adoptPtr(new FFTFrame(size));
m_magnitudeBuffer.allocate(size / 2);
m_fftSize = size;
}
return true;
}
void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess)
{
bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->length() >= framesToProcess;
ASSERT(isBusGood);
if (!isBusGood)
return;
bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex + framesToProcess <= m_inputBuffer.size();
ASSERT(isDestinationGood);
if (!isDestinationGood)
return;
const float* source = bus->channel(0)->data();
float* dest = m_inputBuffer.data() + m_writeIndex;
memcpy(dest, source, sizeof(float) * framesToProcess);
unsigned numberOfChannels = bus->numberOfChannels();
if (numberOfChannels > 1) {
for (unsigned i = 1; i < numberOfChannels; i++) {
source = bus->channel(i)->data();
VectorMath::vadd(dest, 1, source, 1, dest, 1, framesToProcess);
}
const float scale = 1.0 / numberOfChannels;
VectorMath::vsmul(dest, 1, &scale, dest, 1, framesToProcess);
}
m_writeIndex += framesToProcess;
if (m_writeIndex >= InputBufferSize)
m_writeIndex = 0;
}
namespace {
void applyWindow(float* p, size_t n)
{
ASSERT(isMainThread());
double alpha = 0.16;
double a0 = 0.5 * (1 - alpha);
double a1 = 0.5;
double a2 = 0.5 * alpha;
for (unsigned i = 0; i < n; ++i) {
double x = static_cast<double>(i) / static_cast<double>(n);
double window = a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble * 2.0 * x);
p[i] *= float(window);
}
}
}
void RealtimeAnalyser::doFFTAnalysis()
{
ASSERT(isMainThread());
size_t fftSize = this->fftSize();
AudioFloatArray temporaryBuffer(fftSize);
float* inputBuffer = m_inputBuffer.data();
float* tempP = temporaryBuffer.data();
unsigned writeIndex = m_writeIndex;
if (writeIndex < fftSize) {
memcpy(tempP, inputBuffer + writeIndex - fftSize + InputBufferSize, sizeof(*tempP) * (fftSize - writeIndex));
memcpy(tempP + fftSize - writeIndex, inputBuffer, sizeof(*tempP) * writeIndex);
} else
memcpy(tempP, inputBuffer + writeIndex - fftSize, sizeof(*tempP) * fftSize);
applyWindow(tempP, fftSize);
m_analysisFrame->doFFT(tempP);
float* realP = m_analysisFrame->realData();
float* imagP = m_analysisFrame->imagData();
imagP[0] = 0;
const double magnitudeScale = 1.0 / fftSize;
double k = m_smoothingTimeConstant;
k = max(0.0, k);
k = min(1.0, k);
float* destination = magnitudeBuffer().data();
size_t n = magnitudeBuffer().size();
for (size_t i = 0; i < n; ++i) {
Complex c(realP[i], imagP[i]);
double scalarMagnitude = abs(c) * magnitudeScale;
destination[i] = float(k * destination[i] + (1 - k) * scalarMagnitude);
}
}
void RealtimeAnalyser::getFloatFrequencyData(Float32Array* destinationArray)
{
ASSERT(isMainThread());
if (!destinationArray)
return;
doFFTAnalysis();
const double minDecibels = m_minDecibels;
unsigned sourceLength = magnitudeBuffer().size();
size_t len = min(sourceLength, destinationArray->length());
if (len > 0) {
const float* source = magnitudeBuffer().data();
float* destination = destinationArray->data();
for (unsigned i = 0; i < len; ++i) {
float linearValue = source[i];
double dbMag = !linearValue ? minDecibels : AudioUtilities::linearToDecibels(linearValue);
destination[i] = float(dbMag);
}
}
}
void RealtimeAnalyser::getByteFrequencyData(Uint8Array* destinationArray)
{
ASSERT(isMainThread());
if (!destinationArray)
return;
doFFTAnalysis();
unsigned sourceLength = magnitudeBuffer().size();
size_t len = min(sourceLength, destinationArray->length());
if (len > 0) {
const double rangeScaleFactor = m_maxDecibels == m_minDecibels ? 1 : 1 / (m_maxDecibels - m_minDecibels);
const double minDecibels = m_minDecibels;
const float* source = magnitudeBuffer().data();
unsigned char* destination = destinationArray->data();
for (unsigned i = 0; i < len; ++i) {
float linearValue = source[i];
double dbMag = !linearValue ? minDecibels : AudioUtilities::linearToDecibels(linearValue);
double scaledValue = UCHAR_MAX * (dbMag - minDecibels) * rangeScaleFactor;
if (scaledValue < 0)
scaledValue = 0;
if (scaledValue > UCHAR_MAX)
scaledValue = UCHAR_MAX;
destination[i] = static_cast<unsigned char>(scaledValue);
}
}
}
void RealtimeAnalyser::getFloatTimeDomainData(Float32Array* destinationArray)
{
ASSERT(isMainThread());
if (!destinationArray)
return;
unsigned fftSize = this->fftSize();
size_t len = min(fftSize, destinationArray->length());
if (len > 0) {
bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_inputBuffer.size() > fftSize;
ASSERT(isInputBufferGood);
if (!isInputBufferGood)
return;
float* inputBuffer = m_inputBuffer.data();
float* destination = destinationArray->data();
unsigned writeIndex = m_writeIndex;
for (unsigned i = 0; i < len; ++i) {
float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSize) % InputBufferSize];
destination[i] = value;
}
}
}
void RealtimeAnalyser::getByteTimeDomainData(Uint8Array* destinationArray)
{
ASSERT(isMainThread());
if (!destinationArray)
return;
unsigned fftSize = this->fftSize();
size_t len = min(fftSize, destinationArray->length());
if (len > 0) {
bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_inputBuffer.size() > fftSize;
ASSERT(isInputBufferGood);
if (!isInputBufferGood)
return;
float* inputBuffer = m_inputBuffer.data();
unsigned char* destination = destinationArray->data();
unsigned writeIndex = m_writeIndex;
for (unsigned i = 0; i < len; ++i) {
float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSize) % InputBufferSize];
double scaledValue = 128 * (value + 1);
if (scaledValue < 0)
scaledValue = 0;
if (scaledValue > UCHAR_MAX)
scaledValue = UCHAR_MAX;
destination[i] = static_cast<unsigned char>(scaledValue);
}
}
}
}
#endif