Advertisement
Guest User

Windowed FFT analysis

a guest
Sep 29th, 2013
155
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.31 KB | None | 0 0
  1. #include <complex>
  2. #include <vector>
  3. #include <algorithm>
  4. #include <assert.h>
  5. #include <cstring>
  6.  
  7. #define _USE_MATH_DEFINES
  8. #include <math.h>
  9.  
  10. // Return true if power of 2.
  11. inline bool isPowerOf2(size_t i)
  12. {
  13.     assert(i >= 0);
  14.     return (i != 0) && ((i & (i - 1)) == 0);
  15. }
  16.  
  17. // return x so that (1 << x) >= i
  18. int iFloorLog2(size_t i)
  19. {
  20.     assert(i > 0);
  21.    
  22.     #if defined(_WIN32)
  23.         unsigned long y;
  24.         _BitScanReverse(&y, i);
  25.         return y;
  26.     #else            
  27.         int result = 0;
  28.         while (i > 1)
  29.         {
  30.             i = i / 2;
  31.             result = result + 1;
  32.         }
  33.         return result;
  34.     #endif      
  35. }
  36.  
  37. class Window
  38. {
  39. public:
  40.     enum Type
  41.     {
  42.         TYPE_RECT,
  43.         TYPE_HANN
  44.     };
  45.  
  46.     template<typename T>
  47.     static void generate(Type type, size_t N, T out[])
  48.     {
  49.         for (size_t i = 0; i < N; ++i)
  50.         {
  51.             out[i] = static_cast<T>(eval(type, i, N));
  52.         }
  53.     }
  54.  
  55.     static double eval(Type type, size_t n, size_t N)
  56.     {
  57.         switch(type)
  58.         {
  59.         case TYPE_RECT:
  60.             return 1.0;
  61.  
  62.         case TYPE_HANN:
  63.             return 0.5 - 0.5 * cos((2 * M_PI * n) / (N - 1));
  64.  
  65.         default:
  66.             assert(false);
  67.         }
  68.     }
  69. };
  70.  
  71.  
  72. enum FFTDirection
  73. {
  74.     FFT_FORWARD = 0,
  75.     FFT_REVERSE = 1
  76. };
  77.  
  78. template<typename T>
  79. void FFT(std::complex<T>* inout, size_t size, FFTDirection direction)
  80. {
  81.     assert(isPowerOf2(size));
  82.     int m = iFloorLog2(size);
  83.  
  84.     // do the bit reversal
  85.     int i2 = size / 2;
  86.     int j = 0;
  87.     for (int i = 0; i < (int)size - 1; ++i)
  88.     {
  89.         if (i < j)
  90.             std::swap(inout[i], inout[j]);
  91.  
  92.         int k = i2;
  93.         while(k <= j)
  94.         {
  95.             j = j - k;
  96.             k = k / 2;
  97.         }
  98.         j += k;
  99.     }
  100.  
  101.     // compute the FFT
  102.     std::complex<T> c = -1;
  103.     int l2 = 1;
  104.     for (int l = 0; l < m; ++l)
  105.     {
  106.         int l1 = l2;
  107.         l2 = l2 * 2;
  108.         std::complex<T> u = 1;
  109.         for (int j2 = 0; j2 < l1; ++j2)
  110.         {
  111.             int i = j2;
  112.             while (i < (int)size)
  113.             {
  114.                 int i1 = i + l1;
  115.                 std::complex<T> t1 = u * inout[i1];
  116.                 inout[i1] = inout[i] - t1;
  117.                 inout[i] += t1;
  118.                 i += l2;
  119.             }
  120.             u = u * c;
  121.         }
  122.  
  123.         T newImag = sqrt((1 - real(c)) / 2);
  124.         if (direction == FFT_FORWARD)
  125.             newImag = -newImag;
  126.         T newReal = sqrt((1 + real(c)) / 2);
  127.         c = std::complex<T>(newReal, newImag);
  128.     }
  129.  
  130.     // scaling for forward transformation
  131.     if (direction == FFT_FORWARD)
  132.     {
  133.         for (int i = 0; i < (int)size; ++i)
  134.             inout[i] = inout[i] / std::complex<T>((T)size, 0);
  135.     }
  136. }
  137.  
  138. // Send Short term FFT data to a listener.
  139. // Variable overlap.
  140. // Introduced approx. windowSize/2 samples delay.
  141. class FFTAnalyzer
  142. {
  143. public:
  144.  
  145.     // if zeroPhaseWindowing = true, "zero phase" windowing is used
  146.     // (center of window is at first sample, zero-padding happen at center)
  147.     FFTAnalyzer(Window::Type windowType, bool zeroPhaseWindowing, bool correctWindowLoss)
  148.     {
  149.         _windowType = windowType;
  150.         _zeroPhaseWindowing = zeroPhaseWindowing;
  151.         _setupWasCalled = false;
  152.         _correctWindowLoss = correctWindowLoss;
  153.     }
  154.  
  155.     size_t windowSize() const
  156.     {
  157.         return _windowSize;
  158.     }
  159.  
  160.     size_t analysisPeriod() const
  161.     {
  162.         return _analysisPeriod;
  163.     }
  164.  
  165.     // To call at initialization and whenever samplerate changes
  166.     // windowSize = size of analysis window, expressed in samples
  167.     // fftSize = size of FFT. Must be power-of-two and >= windowSize. Missing samples are zero-padded in time domain.
  168.     // analysisPeriod = period of analysis results, allow to be more precise frequentially, expressed in samples
  169.     // Basic overlap is achieved with windowSize = 2 * analysisPeriod
  170.     void setup(size_t windowSize, size_t fftSize, size_t analysisPeriod)
  171.     {
  172.         assert(isPowerOf2(fftSize));
  173.         assert(fftSize >= windowSize);
  174.         _setupWasCalled = true;
  175.  
  176.         assert(windowSize != 1);
  177.         assert(analysisPeriod <= windowSize); // no support for zero overlap
  178.  
  179.         // 1-sized FFT support
  180.         if (analysisPeriod == 0)
  181.             analysisPeriod = 1;
  182.  
  183.         _windowSize = windowSize;
  184.         _fftSize = fftSize;
  185.         _analysisPeriod = analysisPeriod;
  186.  
  187.         // clear input delay
  188.         _audioBuffer.resize(_windowSize);
  189.         _index = 0;
  190.  
  191.         _windowBuffer.resize(_windowSize);
  192.         Window::generate(_windowType, _windowSize, _windowBuffer.data());
  193.  
  194.         _windowGainCorrFactor = 0;
  195.         for (size_t i = 0; i < _windowSize; ++i)
  196.             _windowGainCorrFactor += _windowBuffer[i];
  197.         _windowGainCorrFactor = _windowSize / _windowGainCorrFactor;
  198.  
  199.         if (_correctWindowLoss)
  200.         {
  201.             for (size_t i = 0; i < _windowSize; ++i)
  202.                 _windowBuffer[i] *= _windowGainCorrFactor;
  203.         }
  204.            
  205.     }
  206.  
  207.     // Process one sample, eventually return the result of short-term FFT
  208.     // in a given Buffer
  209.     bool feed(float x, std::vector<std::complex<float>>* fftData)
  210.     {
  211.         assert(_setupWasCalled);
  212.         _audioBuffer[_index] = x;
  213.         _index = _index + 1;
  214.         if (_index >= _windowSize)
  215.         {
  216.             fftData->resize(_fftSize);
  217.  
  218.             if (_zeroPhaseWindowing)
  219.             {
  220.                 // "Zero Phase" windowing
  221.                 // Through clever reordering, phase of ouput coefficients will relate to the
  222.                 // center of the window
  223.                 //_
  224.                 // \_                   _/
  225.                 //   \                 /
  226.                 //    \               /
  227.                 //     \_____________/____
  228.                 size_t center = (_windowSize - 1) / 2; // position of center bin
  229.                 size_t nLeft = _windowSize - center;
  230.                 for (size_t i = 0; i < nLeft; ++i)
  231.                     (*fftData)[i] = _audioBuffer[center + i] * _windowBuffer[center + i];
  232.  
  233.                 size_t nPadding = _fftSize - _windowSize;
  234.                 for (size_t i = 0; i < nPadding; ++i)
  235.                     (*fftData)[nLeft + i] = 0.0f;
  236.  
  237.                 for (size_t i = 0; i < center; ++i)
  238.                     (*fftData)[nLeft + nPadding + i] = _audioBuffer[i] * _windowBuffer[i];
  239.             }
  240.             else
  241.             {
  242.                 // "Normal" windowing
  243.                 // Phase of ouput coefficient will relate to the start of the buffer
  244.                 //      _
  245.                 //    _/ \_          
  246.                 //   /     \              
  247.                 //  /       \              
  248.                 //_/         \____________
  249.  
  250.                 // fill FFT buffer and multiply by window
  251.                 for (size_t i = 0; i < _windowSize; ++i)
  252.                     (*fftData)[i] = _audioBuffer[i] * _windowBuffer[i];
  253.  
  254.                 // zero-padding
  255.                 for (size_t i = _windowSize; i < _fftSize; ++i)
  256.                     (*fftData)[i] = 0.0f;
  257.             }
  258.  
  259.             // perform forward FFT on this slice
  260.             FFT(fftData->data(), _fftSize, FFT_FORWARD);
  261.  
  262.             // rotate buffer
  263.             {
  264.                 size_t const samplesToDrop = _analysisPeriod;
  265.                 assert(0 < samplesToDrop && samplesToDrop <= _windowSize);
  266.                 size_t remainingSamples = _windowSize - samplesToDrop;
  267.  
  268.                 // TODO: use ring buffer instead of copy
  269.                 memmove(_audioBuffer.data(), _audioBuffer.data() + samplesToDrop, sizeof(float) * remainingSamples);
  270.                 _index = remainingSamples;
  271.  
  272.             }
  273.             return true;
  274.         }
  275.         else
  276.         {
  277.             return false;
  278.         }
  279.     }
  280.  
  281. private:
  282.     std::vector<float> _audioBuffer;
  283.     std::vector<float> _windowBuffer;
  284.  
  285.     size_t _fftSize;        // in samples
  286.     size_t _windowSize;     // in samples
  287.     size_t _analysisPeriod; // in samples
  288.  
  289.     Window::Type _windowType;
  290.     bool _zeroPhaseWindowing;
  291.  
  292.     size_t _index;
  293.     bool _setupWasCalled;
  294.  
  295.     // should we multiply by _windowGainCorrFactor?
  296.     bool _correctWindowLoss;
  297.     // the factor by which to multiply transformed data to get in range results
  298.     float _windowGainCorrFactor;
  299. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement