#include "config.h"
#if ENABLE(WEB_AUDIO)
#if !OS(DARWIN) && USE(WEBAUDIO_FFTW)
#include "FFTFrame.h"
#include <wtf/MathExtras.h>
namespace WebCore {
const int kMaxFFTPow2Size = 24;
fftwf_plan* FFTFrame::fftwForwardPlans = 0;
fftwf_plan* FFTFrame::fftwBackwardPlans = 0;
Mutex* FFTFrame::s_planLock = 0;
namespace {
unsigned unpackedFFTWDataSize(unsigned fftSize)
{
return fftSize / 2 + 1;
}
}
FFTFrame::FFTFrame(unsigned fftSize)
: m_FFTSize(fftSize)
, m_log2FFTSize(static_cast<unsigned>(log2(fftSize)))
, m_forwardPlan(0)
, m_backwardPlan(0)
, m_data(2 * (3 + unpackedFFTWDataSize(fftSize))) {
ASSERT(1UL << m_log2FFTSize == m_FFTSize);
float temporary;
m_forwardPlan = fftwPlanForSize(fftSize, Forward,
&temporary, realData(), imagData());
m_backwardPlan = fftwPlanForSize(fftSize, Backward,
realData(), imagData(), &temporary);
}
FFTFrame::FFTFrame()
: m_FFTSize(0)
, m_log2FFTSize(0)
, m_forwardPlan(0)
, m_backwardPlan(0)
{
}
FFTFrame::FFTFrame(const FFTFrame& frame)
: m_FFTSize(frame.m_FFTSize)
, m_log2FFTSize(frame.m_log2FFTSize)
, m_forwardPlan(0)
, m_backwardPlan(0)
, m_data(2 * (3 + unpackedFFTWDataSize(fftSize()))) {
float temporary;
m_forwardPlan = fftwPlanForSize(m_FFTSize, Forward,
&temporary, realData(), imagData());
m_backwardPlan = fftwPlanForSize(m_FFTSize, Backward,
realData(), imagData(), &temporary);
size_t nbytes = sizeof(float) * unpackedFFTWDataSize(fftSize());
memcpy(realData(), frame.realData(), nbytes);
memcpy(imagData(), frame.imagData(), nbytes);
}
FFTFrame::~FFTFrame()
{
}
void FFTFrame::multiply(const FFTFrame& frame)
{
FFTFrame& frame1 = *this;
FFTFrame& frame2 = const_cast<FFTFrame&>(frame);
float* realP1 = frame1.realData();
float* imagP1 = frame1.imagData();
const float* realP2 = frame2.realData();
const float* imagP2 = frame2.imagData();
float scale = 0.5f;
realP1[0] *= scale * realP2[0];
imagP1[0] *= scale * imagP2[0];
unsigned halfSize = fftSize() / 2;
for (unsigned i = 1; i < halfSize; ++i) {
float realResult = realP1[i] * realP2[i] - imagP1[i] * imagP2[i];
float imagResult = realP1[i] * imagP2[i] + imagP1[i] * realP2[i];
realP1[i] = scale * realResult;
imagP1[i] = scale * imagResult;
}
}
void FFTFrame::doFFT(float* data)
{
fftwf_execute_split_dft_r2c(m_forwardPlan, data, realData(), imagData());
float scaleFactor = 2;
unsigned length = unpackedFFTWDataSize(fftSize());
float* realData = this->realData();
float* imagData = this->imagData();
for (unsigned i = 0; i < length; ++i) {
realData[i] = realData[i] * scaleFactor;
imagData[i] = imagData[i] * scaleFactor;
}
imagData[0] = realData[length - 1];
}
void FFTFrame::doInverseFFT(float* data)
{
unsigned length = unpackedFFTWDataSize(fftSize());
float* realData = this->realData();
float* imagData = this->imagData();
realData[length - 1] = imagData[0];
imagData[length - 1] = 0;
imagData[0] = 0;
fftwf_execute_split_dft_c2r(m_backwardPlan, realData, imagData, data);
float scaleFactor = 1.0 / (2.0 * fftSize());
unsigned n = fftSize();
for (unsigned i = 0; i < n; ++i)
data[i] *= scaleFactor;
imagData[0] = realData[length - 1];
}
void FFTFrame::initialize()
{
if (!fftwForwardPlans) {
fftwForwardPlans = new fftwf_plan[kMaxFFTPow2Size];
fftwBackwardPlans = new fftwf_plan[kMaxFFTPow2Size];
for (int i = 0; i < kMaxFFTPow2Size; ++i) {
fftwForwardPlans[i] = 0;
fftwBackwardPlans[i] = 0;
}
}
if (!s_planLock)
s_planLock = new Mutex();
}
void FFTFrame::cleanup()
{
if (!fftwForwardPlans)
return;
for (int i = 0; i < kMaxFFTPow2Size; ++i) {
if (fftwForwardPlans[i])
fftwf_destroy_plan(fftwForwardPlans[i]);
if (fftwBackwardPlans[i])
fftwf_destroy_plan(fftwBackwardPlans[i]);
}
delete[] fftwForwardPlans;
delete[] fftwBackwardPlans;
fftwForwardPlans = 0;
fftwBackwardPlans = 0;
delete s_planLock;
s_planLock = 0;
}
float* FFTFrame::realData() const
{
return const_cast<float*>(m_data.data());
}
float* FFTFrame::imagData() const
{
return const_cast<float*>(realData() + unpackedFFTWDataSize(fftSize()) + 3);
}
fftwf_plan FFTFrame::fftwPlanForSize(unsigned fftSize, Direction direction,
float* data1, float* data2, float* data3)
{
ASSERT(fftwForwardPlans);
if (!fftwForwardPlans)
return 0;
ASSERT(s_planLock);
if (!s_planLock)
return 0;
MutexLocker locker(*s_planLock);
ASSERT(fftSize);
int pow2size = static_cast<int>(log2(fftSize));
ASSERT(pow2size < kMaxFFTPow2Size);
fftwf_plan* plans = (direction == Forward) ? fftwForwardPlans : fftwBackwardPlans;
if (!plans[pow2size]) {
fftwf_iodim dimension;
dimension.n = fftSize;
dimension.is = 1;
dimension.os = 1;
unsigned flags = FFTW_ESTIMATE | FFTW_PRESERVE_INPUT | FFTW_UNALIGNED;
switch (direction) {
case Forward:
plans[pow2size] = fftwf_plan_guru_split_dft_r2c(1, &dimension, 0, 0,
data1, data2, data3,
flags);
break;
case Backward:
plans[pow2size] = fftwf_plan_guru_split_dft_c2r(1, &dimension, 0, 0,
data1, data2, data3,
flags);
break;
}
}
ASSERT(plans[pow2size]);
return plans[pow2size];
}
}
#endif // !OS(DARWIN) && USE(WEBAUDIO_FFTW)
#endif // ENABLE(WEB_AUDIO)