author | Ehsan Akhgari <ehsan@mozilla.com> |
Mon, 10 Jun 2013 16:08:21 -0400 | |
changeset 134586 | dc80f47a7123800dfe27cb40492b8f48ec5bc217 |
parent 134585 | 6058da103d4574b99f460f65b044400635ac15ba |
child 134587 | 6bed30223d8f63012b88f9ae9faaf44dd88d0911 |
push id | 24805 |
push user | emorley@mozilla.com |
push date | Tue, 11 Jun 2013 08:32:39 +0000 |
treeherder | mozilla-central@81b227f1a522 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | roc |
bugs | 815643 |
milestone | 24.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/media/AudioNodeEngine.cpp +++ b/content/media/AudioNodeEngine.cpp @@ -65,16 +65,34 @@ AudioBlockCopyChannelWithScale(const flo } else { for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { aOutput[i] = aInput[i]*aScale; } } } 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], float aOutput[WEBAUDIO_BLOCK_SIZE]) { for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { aOutput[i] = aInput[i]*aScale[i]; } }
--- a/content/media/AudioNodeEngine.h +++ b/content/media/AudioNodeEngine.h @@ -106,16 +106,24 @@ void AudioBlockCopyChannelWithScale(cons /** * Vector copy-scaled operation. */ 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. */ void AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], uint32_t aChannelCount, float aScale); /** * Upmix a mono input to a stereo output, scaling the two output channels by two
--- a/content/media/webaudio/AnalyserNode.cpp +++ b/content/media/webaudio/AnalyserNode.cpp @@ -5,17 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/AnalyserNode.h" #include "mozilla/dom/AnalyserNodeBinding.h" #include "AudioNodeEngine.h" #include "AudioNodeStream.h" #include "mozilla/Mutex.h" #include "mozilla/PodOperations.h" -#include "kiss_fft/kiss_fftr.h" namespace mozilla { namespace dom { NS_IMPL_ISUPPORTS_INHERITED0(AnalyserNode, AudioNode) class AnalyserNodeEngine : public AudioNodeEngine { @@ -75,17 +74,17 @@ public: } }; AnalyserNode::AnalyserNode(AudioContext* aContext) : AudioNode(aContext, 1, ChannelCountMode::Explicit, ChannelInterpretation::Speakers) - , mFFTSize(2048) + , mAnalysisBlock(2048) , mMinDecibels(-100.) , mMaxDecibels(-30.) , mSmoothingTimeConstant(.8) , mWriteIndex(0) { mStream = aContext->Graph()->CreateAudioNodeStream(new AnalyserNodeEngine(this), MediaStreamGraph::INTERNAL_STREAM); AllocateBuffer(); @@ -102,18 +101,18 @@ AnalyserNode::SetFftSize(uint32_t aValue { // Disallow values that are not a power of 2 and outside the [32,2048] range if (aValue < 32 || aValue > 2048 || (aValue & (aValue - 1)) != 0) { aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return; } - if (mFFTSize != aValue) { - mFFTSize = aValue; + if (FftSize() != aValue) { + mAnalysisBlock.SetFFTSize(aValue); AllocateBuffer(); } } void AnalyserNode::SetMinDecibels(double aValue, ErrorResult& aRv) { if (aValue >= mMaxDecibels) { @@ -199,38 +198,35 @@ AnalyserNode::GetByteTimeDomainData(Uint bool AnalyserNode::FFTAnalysis() { float* inputBuffer; bool allocated = false; 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); - kiss_fftr_cfg fft = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr); - kiss_fftr(fft, inputBuffer, outputBuffer); - free(fft); + ApplyBlackmanWindow(inputBuffer, FftSize()); + + 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; } if (allocated) { moz_free(inputBuffer); } @@ -251,20 +247,20 @@ AnalyserNode::ApplyBlackmanWindow(float* aBuffer[i] *= window; } } 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()); if (result) { memset(mOutputBuffer.Elements(), 0, sizeof(float) * FrequencyBinCount()); } } }
--- a/content/media/webaudio/AnalyserNode.h +++ b/content/media/webaudio/AnalyserNode.h @@ -3,16 +3,17 @@ /* 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 AnalyserNode_h_ #define AnalyserNode_h_ #include "AudioNode.h" +#include "FFTBlock.h" namespace mozilla { namespace dom { class AudioContext; class AnalyserNode : public AudioNode { @@ -24,17 +25,17 @@ public: virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE; void GetFloatFrequencyData(Float32Array& aArray); void GetByteFrequencyData(Uint8Array& aArray); void GetByteTimeDomainData(Uint8Array& aArray); uint32_t FftSize() const { - return mFFTSize; + return mAnalysisBlock.FFTSize(); } void SetFftSize(uint32_t aValue, ErrorResult& aRv); uint32_t FrequencyBinCount() const { return FftSize() / 2; } double MinDecibels() const { @@ -55,17 +56,17 @@ public: private: friend class AnalyserNodeEngine; void AppendChunk(const AudioChunk& aChunk); bool AllocateBuffer(); bool FFTAnalysis(); void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize); private: - uint32_t mFFTSize; + FFTBlock mAnalysisBlock; double mMinDecibels; double mMaxDecibels; double mSmoothingTimeConstant; uint32_t mWriteIndex; FallibleTArray<float> mBuffer; FallibleTArray<float> mOutputBuffer; };
new file mode 100644 --- /dev/null +++ b/content/media/webaudio/FFTBlock.h @@ -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 +