michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef FFTBlock_h_ michael@0: #define FFTBlock_h_ michael@0: michael@0: #include "nsTArray.h" michael@0: #include "AudioNodeEngine.h" michael@0: #include "kiss_fft/kiss_fftr.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: // This class defines an FFT block, loosely modeled after Blink's FFTFrame michael@0: // class to make sharing code with Blink easy. michael@0: // Currently it's implemented on top of KissFFT on all platforms. michael@0: class FFTBlock { michael@0: public: michael@0: explicit FFTBlock(uint32_t aFFTSize) michael@0: : mFFT(nullptr) michael@0: , mIFFT(nullptr) michael@0: , mFFTSize(aFFTSize) michael@0: { michael@0: MOZ_COUNT_CTOR(FFTBlock); michael@0: mOutputBuffer.SetLength(aFFTSize / 2 + 1); michael@0: PodZero(mOutputBuffer.Elements(), aFFTSize / 2 + 1); michael@0: } michael@0: ~FFTBlock() michael@0: { michael@0: MOZ_COUNT_DTOR(FFTBlock); michael@0: Clear(); michael@0: } michael@0: michael@0: // Return a new FFTBlock with frequency components interpolated between michael@0: // |block0| and |block1| with |interp| between 0.0 and 1.0. michael@0: static FFTBlock* michael@0: CreateInterpolatedBlock(const FFTBlock& block0, michael@0: const FFTBlock& block1, double interp); michael@0: michael@0: // Transform FFTSize() points of aData and store the result internally. michael@0: void PerformFFT(const float* aData) michael@0: { michael@0: EnsureFFT(); michael@0: kiss_fftr(mFFT, aData, mOutputBuffer.Elements()); michael@0: } michael@0: // Inverse-transform internal data and store the resulting FFTSize() michael@0: // points in aData. michael@0: void GetInverse(float* aDataOut) michael@0: { michael@0: GetInverseWithoutScaling(aDataOut); michael@0: AudioBufferInPlaceScale(aDataOut, 1.0f / mFFTSize, mFFTSize); michael@0: } michael@0: // Inverse-transform internal frequency data and store the resulting michael@0: // FFTSize() points in |aDataOut|. If frequency data has not already been michael@0: // scaled, then the output will need scaling by 1/FFTSize(). michael@0: void GetInverseWithoutScaling(float* aDataOut) michael@0: { michael@0: EnsureIFFT(); michael@0: kiss_fftri(mIFFT, mOutputBuffer.Elements(), aDataOut); michael@0: } michael@0: // Inverse-transform the FFTSize()/2+1 points of data in each michael@0: // of aRealDataIn and aImagDataIn and store the resulting michael@0: // FFTSize() points in aRealDataOut. michael@0: void PerformInverseFFT(float* aRealDataIn, michael@0: float *aImagDataIn, michael@0: float *aRealDataOut) michael@0: { michael@0: EnsureIFFT(); michael@0: const uint32_t inputSize = mFFTSize / 2 + 1; michael@0: nsTArray inputBuffer; michael@0: inputBuffer.SetLength(inputSize); michael@0: for (uint32_t i = 0; i < inputSize; ++i) { michael@0: inputBuffer[i].r = aRealDataIn[i]; michael@0: inputBuffer[i].i = aImagDataIn[i]; michael@0: } michael@0: kiss_fftri(mIFFT, inputBuffer.Elements(), aRealDataOut); michael@0: for (uint32_t i = 0; i < mFFTSize; ++i) { michael@0: aRealDataOut[i] /= mFFTSize; michael@0: } michael@0: } michael@0: michael@0: void Multiply(const FFTBlock& aFrame) michael@0: { michael@0: BufferComplexMultiply(reinterpret_cast(mOutputBuffer.Elements()), michael@0: reinterpret_cast(aFrame.mOutputBuffer.Elements()), michael@0: reinterpret_cast(mOutputBuffer.Elements()), michael@0: mFFTSize / 2 + 1); michael@0: } michael@0: michael@0: // Perform a forward FFT on |aData|, assuming zeros after dataSize samples, michael@0: // and pre-scale the generated internal frequency domain coefficients so michael@0: // that GetInverseWithoutScaling() can be used to transform to the time michael@0: // domain. This is useful for convolution kernels. michael@0: void PadAndMakeScaledDFT(const float* aData, size_t dataSize) michael@0: { michael@0: MOZ_ASSERT(dataSize <= FFTSize()); michael@0: nsTArray paddedData; michael@0: paddedData.SetLength(FFTSize()); michael@0: AudioBufferCopyWithScale(aData, 1.0f / FFTSize(), michael@0: paddedData.Elements(), dataSize); michael@0: PodZero(paddedData.Elements() + dataSize, mFFTSize - dataSize); michael@0: PerformFFT(paddedData.Elements()); michael@0: } michael@0: michael@0: void SetFFTSize(uint32_t aSize) michael@0: { michael@0: mFFTSize = aSize; michael@0: mOutputBuffer.SetLength(aSize / 2 + 1); michael@0: PodZero(mOutputBuffer.Elements(), aSize / 2 + 1); michael@0: Clear(); michael@0: } michael@0: michael@0: // Return the average group delay and removes this from the frequency data. michael@0: double ExtractAverageGroupDelay(); michael@0: michael@0: uint32_t FFTSize() const michael@0: { michael@0: return mFFTSize; michael@0: } michael@0: float RealData(uint32_t aIndex) const michael@0: { michael@0: return mOutputBuffer[aIndex].r; michael@0: } michael@0: float ImagData(uint32_t aIndex) const michael@0: { michael@0: return mOutputBuffer[aIndex].i; michael@0: } michael@0: michael@0: size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t amount = 0; michael@0: amount += aMallocSizeOf(mFFT); michael@0: amount += aMallocSizeOf(mIFFT); michael@0: amount += mOutputBuffer.SizeOfExcludingThis(aMallocSizeOf); michael@0: return amount; michael@0: } michael@0: michael@0: size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: private: michael@0: FFTBlock(const FFTBlock& other) MOZ_DELETE; michael@0: void operator=(const FFTBlock& other) MOZ_DELETE; michael@0: michael@0: void EnsureFFT() michael@0: { michael@0: if (!mFFT) { michael@0: mFFT = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr); michael@0: } michael@0: } michael@0: void EnsureIFFT() michael@0: { michael@0: if (!mIFFT) { michael@0: mIFFT = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr); michael@0: } michael@0: } michael@0: void Clear() michael@0: { michael@0: free(mFFT); michael@0: free(mIFFT); michael@0: mFFT = mIFFT = nullptr; michael@0: } michael@0: void AddConstantGroupDelay(double sampleFrameDelay); michael@0: void InterpolateFrequencyComponents(const FFTBlock& block0, michael@0: const FFTBlock& block1, double interp); michael@0: michael@0: kiss_fftr_cfg mFFT, mIFFT; michael@0: nsTArray mOutputBuffer; michael@0: uint32_t mFFTSize; michael@0: }; michael@0: michael@0: } michael@0: michael@0: #endif michael@0: