зеркало из https://github.com/mozilla/gecko-dev.git
Bug 815643 - Part 2: Refactor our Web Audio FFT code into the FFTBlock class; r=roc
This is useful in order for us to be able to borrow code from Blink with fewer changes, and also to explore faster platform dependent FFT implementations in the future if needed. --HG-- extra : rebase_source : 34bbded736e1b385754756513c59ee27ce7552a5
This commit is contained in:
Родитель
e71db503bf
Коммит
675e417f91
|
@ -69,6 +69,24 @@ AudioBlockCopyChannelWithScale(const float* aInput,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
BufferComplexMultiply(const float* aInput,
|
||||
const float* aScale,
|
||||
float* aOutput,
|
||||
uint32_t aSize)
|
||||
{
|
||||
for (uint32_t i = 0; i < aSize * 2; i += 2) {
|
||||
float real1 = aInput[i];
|
||||
float imag1 = aInput[i + 1];
|
||||
float real2 = aScale[i];
|
||||
float imag2 = aScale[i + 1];
|
||||
float realResult = real1 * real2 - imag1 * imag2;
|
||||
float imagResult = real1 * imag2 + imag1 * real2;
|
||||
aOutput[i] = realResult;
|
||||
aOutput[i + 1] = imagResult;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
|
||||
const float aScale[WEBAUDIO_BLOCK_SIZE],
|
||||
|
|
|
@ -110,6 +110,14 @@ void AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
|
|||
const float aScale[WEBAUDIO_BLOCK_SIZE],
|
||||
float aOutput[WEBAUDIO_BLOCK_SIZE]);
|
||||
|
||||
/**
|
||||
* Vector complex multiplication on arbitrary sized buffers.
|
||||
*/
|
||||
void BufferComplexMultiply(const float* aInput,
|
||||
const float* aScale,
|
||||
float* aOutput,
|
||||
uint32_t aSize);
|
||||
|
||||
/**
|
||||
* In place gain. aScale == 1.0f should be optimized.
|
||||
*/
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "AudioNodeStream.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "kiss_fft/kiss_fftr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -80,7 +79,7 @@ AnalyserNode::AnalyserNode(AudioContext* aContext)
|
|||
1,
|
||||
ChannelCountMode::Explicit,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mFFTSize(2048)
|
||||
, mAnalysisBlock(2048)
|
||||
, mMinDecibels(-100.)
|
||||
, mMaxDecibels(-30.)
|
||||
, mSmoothingTimeConstant(.8)
|
||||
|
@ -107,8 +106,8 @@ AnalyserNode::SetFftSize(uint32_t aValue, ErrorResult& aRv)
|
|||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return;
|
||||
}
|
||||
if (mFFTSize != aValue) {
|
||||
mFFTSize = aValue;
|
||||
if (FftSize() != aValue) {
|
||||
mAnalysisBlock.SetFFTSize(aValue);
|
||||
AllocateBuffer();
|
||||
}
|
||||
}
|
||||
|
@ -204,28 +203,25 @@ AnalyserNode::FFTAnalysis()
|
|||
if (mWriteIndex == 0) {
|
||||
inputBuffer = mBuffer.Elements();
|
||||
} else {
|
||||
inputBuffer = static_cast<float*>(moz_malloc(mFFTSize * sizeof(float)));
|
||||
inputBuffer = static_cast<float*>(moz_malloc(FftSize() * sizeof(float)));
|
||||
if (!inputBuffer) {
|
||||
return false;
|
||||
}
|
||||
memcpy(inputBuffer, mBuffer.Elements() + mWriteIndex, sizeof(float) * (mFFTSize - mWriteIndex));
|
||||
memcpy(inputBuffer + mFFTSize - mWriteIndex, mBuffer.Elements(), sizeof(float) * mWriteIndex);
|
||||
memcpy(inputBuffer, mBuffer.Elements() + mWriteIndex, sizeof(float) * (FftSize() - mWriteIndex));
|
||||
memcpy(inputBuffer + FftSize() - mWriteIndex, mBuffer.Elements(), sizeof(float) * mWriteIndex);
|
||||
allocated = true;
|
||||
}
|
||||
nsAutoArrayPtr<kiss_fft_cpx> outputBuffer(new kiss_fft_cpx[FrequencyBinCount() + 1]);
|
||||
|
||||
ApplyBlackmanWindow(inputBuffer, mFFTSize);
|
||||
ApplyBlackmanWindow(inputBuffer, FftSize());
|
||||
|
||||
kiss_fftr_cfg fft = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
|
||||
kiss_fftr(fft, inputBuffer, outputBuffer);
|
||||
free(fft);
|
||||
mAnalysisBlock.PerformFFT(inputBuffer);
|
||||
|
||||
// Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor).
|
||||
const double magnitudeScale = 1.0 / mFFTSize;
|
||||
const double magnitudeScale = 1.0 / FftSize();
|
||||
|
||||
for (uint32_t i = 0; i < mOutputBuffer.Length(); ++i) {
|
||||
double scalarMagnitude = sqrt(outputBuffer[i].r * outputBuffer[i].r +
|
||||
outputBuffer[i].i * outputBuffer[i].i) *
|
||||
double scalarMagnitude = NS_hypot(mAnalysisBlock.RealData(i),
|
||||
mAnalysisBlock.ImagData(i)) *
|
||||
magnitudeScale;
|
||||
mOutputBuffer[i] = mSmoothingTimeConstant * mOutputBuffer[i] +
|
||||
(1.0 - mSmoothingTimeConstant) * scalarMagnitude;
|
||||
|
@ -256,10 +252,10 @@ bool
|
|||
AnalyserNode::AllocateBuffer()
|
||||
{
|
||||
bool result = true;
|
||||
if (mBuffer.Length() != mFFTSize) {
|
||||
result = mBuffer.SetLength(mFFTSize);
|
||||
if (mBuffer.Length() != FftSize()) {
|
||||
result = mBuffer.SetLength(FftSize());
|
||||
if (result) {
|
||||
memset(mBuffer.Elements(), 0, sizeof(float) * mFFTSize);
|
||||
memset(mBuffer.Elements(), 0, sizeof(float) * FftSize());
|
||||
mWriteIndex = 0;
|
||||
|
||||
result = mOutputBuffer.SetLength(FrequencyBinCount());
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define AnalyserNode_h_
|
||||
|
||||
#include "AudioNode.h"
|
||||
#include "FFTBlock.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -29,7 +30,7 @@ public:
|
|||
void GetByteTimeDomainData(Uint8Array& aArray);
|
||||
uint32_t FftSize() const
|
||||
{
|
||||
return mFFTSize;
|
||||
return mAnalysisBlock.FFTSize();
|
||||
}
|
||||
void SetFftSize(uint32_t aValue, ErrorResult& aRv);
|
||||
uint32_t FrequencyBinCount() const
|
||||
|
@ -60,7 +61,7 @@ private:
|
|||
void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize);
|
||||
|
||||
private:
|
||||
uint32_t mFFTSize;
|
||||
FFTBlock mAnalysisBlock;
|
||||
double mMinDecibels;
|
||||
double mMaxDecibels;
|
||||
double mSmoothingTimeConstant;
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef FFTBlock_h_
|
||||
#define FFTBlock_h_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "AudioNodeEngine.h"
|
||||
#include "kiss_fft/kiss_fftr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// This class defines an FFT block, loosely modeled after Blink's FFTFrame
|
||||
// class to make sharing code with Blink easy.
|
||||
// Currently it's implemented on top of KissFFT on all platforms.
|
||||
class FFTBlock {
|
||||
public:
|
||||
explicit FFTBlock(uint32_t aFFTSize)
|
||||
: mFFTSize(aFFTSize)
|
||||
{
|
||||
mOutputBuffer.SetLength(aFFTSize / 2 + 1);
|
||||
PodZero(mOutputBuffer.Elements(), aFFTSize / 2 + 1);
|
||||
}
|
||||
|
||||
void PerformFFT(const float* aData)
|
||||
{
|
||||
kiss_fftr_cfg fft = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
|
||||
kiss_fftr(fft, aData, mOutputBuffer.Elements());
|
||||
free(fft);
|
||||
}
|
||||
void PerformInverseFFT(float* aData) const
|
||||
{
|
||||
kiss_fftr_cfg fft = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr);
|
||||
kiss_fftri(fft, mOutputBuffer.Elements(), aData);
|
||||
free(fft);
|
||||
for (uint32_t i = 0; i < mFFTSize; ++i) {
|
||||
aData[i] /= mFFTSize;
|
||||
}
|
||||
}
|
||||
void Multiply(const FFTBlock& aFrame)
|
||||
{
|
||||
BufferComplexMultiply(reinterpret_cast<const float*>(mOutputBuffer.Elements()),
|
||||
reinterpret_cast<const float*>(aFrame.mOutputBuffer.Elements()),
|
||||
reinterpret_cast<float*>(mOutputBuffer.Elements()),
|
||||
mFFTSize / 2 + 1);
|
||||
}
|
||||
|
||||
void SetFFTSize(uint32_t aSize)
|
||||
{
|
||||
mFFTSize = aSize;
|
||||
mOutputBuffer.SetLength(aSize / 2 + 1);
|
||||
PodZero(mOutputBuffer.Elements(), aSize / 2 + 1);
|
||||
}
|
||||
|
||||
float FFTSize() const
|
||||
{
|
||||
return mFFTSize;
|
||||
}
|
||||
float RealData(uint32_t aIndex) const
|
||||
{
|
||||
return mOutputBuffer[aIndex].r;
|
||||
}
|
||||
float ImagData(uint32_t aIndex) const
|
||||
{
|
||||
return mOutputBuffer[aIndex].i;
|
||||
}
|
||||
|
||||
private:
|
||||
nsTArray<kiss_fft_cpx> mOutputBuffer;
|
||||
uint32_t mFFTSize;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Загрузка…
Ссылка в новой задаче