Bug 1220037 - pack and unpack Nyquist for MOZ_LIBAV_FFT. r=padenot, a=sylvestre
authorKarl Tomlinson <karlt+@karlt.net>
Fri, 30 Oct 2015 14:48:08 +1300
changeset 296665 ab27c2db7e68
parent 296664 1a56a93a6292
child 296666 d3b021968c63
push id5278
push usercbook@mozilla.com
push date2015-11-09 10:38 +0000
treeherdermozilla-beta@d3b021968c63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot, sylvestre
bugs1220037
milestone43.0
Bug 1220037 - pack and unpack Nyquist for MOZ_LIBAV_FFT. r=padenot, a=sylvestre BufferComplexMultiply knows nothing about this format and so ends up corrupting the DC coefficient if packed Nyquists are multiplied.
dom/media/webaudio/FFTBlock.h
--- a/dom/media/webaudio/FFTBlock.h
+++ b/dom/media/webaudio/FFTBlock.h
@@ -78,16 +78,19 @@ public:
   void PerformFFT(const float* aData)
   {
     EnsureFFT();
 #if defined(MOZ_LIBAV_FFT)
     AlignedTArray<FFTSample> complex(mFFTSize);
     PodCopy(complex.Elements(), aData, mFFTSize);
     av_rdft_calc(mAvRDFT, complex.Elements());
     PodCopy((FFTSample*)mOutputBuffer.Elements(), complex.Elements(), mFFTSize);
+    // Recover packed Nyquist.
+    mOutputBuffer[mFFTSize / 2].r = mOutputBuffer[0].i;
+    mOutputBuffer[0].i = 0.0f;
 #else
 #ifdef BUILD_ARM_NEON
     if (mozilla::supports_neon()) {
       omxSP_FFTFwd_RToCCS_F32_Sfs(aData, mOutputBuffer.Elements()->f, mOmxFFT);
     } else
 #endif
     {
       kiss_fftr(mKissFFT, aData, &(mOutputBuffer.Elements()->c));
@@ -105,16 +108,17 @@ public:
   // FFTSize() points in |aDataOut|.  If frequency data has not already been
   // scaled, then the output will need scaling by 1/FFTSize().
   void GetInverseWithoutScaling(float* aDataOut)
   {
     EnsureIFFT();
 #if defined(MOZ_LIBAV_FFT)
     {
       PodCopy(aDataOut, (float*)mOutputBuffer.Elements(), mFFTSize);
+      aDataOut[1] = mOutputBuffer[mFFTSize/2].r; // Packed Nyquist
       av_rdft_calc(mAvIRDFT, aDataOut);
       // TODO: Once bug 877662 lands, change this to use SSE.
       // Even though this function doesn't scale, the libav forward transform
       // gives a value that needs scaling by 2 in order for things to turn out
       // similar to how we expect from kissfft/openmax.
       for (uint32_t i = 0; i < mFFTSize; ++i) {
         aDataOut[i] *= 2.0;
       }
@@ -131,20 +135,27 @@ public:
     {
       kiss_fftri(mKissIFFT, &(mOutputBuffer.Elements()->c), aDataOut);
     }
 #endif
   }
 
   void Multiply(const FFTBlock& aFrame)
   {
+    uint32_t halfSize = mFFTSize / 2;
+    // DFTs are not packed.
+    MOZ_ASSERT(mOutputBuffer[0].i == 0);
+    MOZ_ASSERT(mOutputBuffer[halfSize].i == 0);
+    MOZ_ASSERT(aFrame.mOutputBuffer[0].i == 0);
+    MOZ_ASSERT(aFrame.mOutputBuffer[halfSize].i == 0);
+
     BufferComplexMultiply(mOutputBuffer.Elements()->f,
                           aFrame.mOutputBuffer.Elements()->f,
                           mOutputBuffer.Elements()->f,
-                          mFFTSize / 2 + 1);
+                          halfSize + 1);
   }
 
   // Perform a forward FFT on |aData|, assuming zeros after dataSize samples,
   // and pre-scale the generated internal frequency domain coefficients so
   // that GetInverseWithoutScaling() can be used to transform to the time
   // domain.  This is useful for convolution kernels.
   void PadAndMakeScaledDFT(const float* aData, size_t dataSize)
   {