Advertisement
Guest User

Untitled

a guest
Oct 18th, 2017
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.54 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Concurrent;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Numerics;
  7. using System.Threading.Tasks;
  8. using System.Threading;
  9. using System.Diagnostics;
  10.  
  11. namespace WpfApplication1
  12. {
  13.     public class timefreq
  14.     {
  15.         public float[][] timeFreqData;
  16.         public int wSamp;
  17.         public Complex[] twiddles;
  18.  
  19.         public timefreq(float[] x, int windowSamp, bool parallelFlag)
  20.         {
  21.             int ii;
  22.             double pi = 3.14159265;
  23.             Complex i = Complex.ImaginaryOne;
  24.             this.wSamp = windowSamp;
  25.             twiddles = new Complex[wSamp];
  26.  
  27.             //Calculate twiddle factors, calculated once based on N aka wSamp
  28.             //TDoesn't take too much time, lots of pow and float math
  29.             //Should be easily parallelised, but unnecessary
  30.             for (ii = 0; ii < wSamp; ii++)
  31.             {
  32.                 double a = 2 * pi * ii / (double)wSamp;
  33.                 twiddles[ii] = Complex.Pow(Complex.Exp(-i), (float)a);
  34.             }
  35.  
  36.             timeFreqData = new float[wSamp/2][];
  37.  
  38.             //Quantizes length and samples
  39.             int nearest = (int)Math.Ceiling((double)x.Length / (double)wSamp);
  40.             nearest = nearest * wSamp;
  41.  
  42.             Complex[] compX = new Complex[nearest];
  43.             for (int kk = 0; kk < nearest; kk++)
  44.             {
  45.                 if (kk < x.Length)
  46.                 {
  47.                     compX[kk] = x[kk];
  48.                 }
  49.                 else
  50.                 {
  51.                     compX[kk] = Complex.Zero;
  52.                 }
  53.             }
  54.  
  55.             //Wait this squares the data off
  56.             //When there are double the samples there are half the columns
  57.             //Sum total always equals nearest (2383872)
  58.             int cols = 2 * nearest /wSamp;
  59.  
  60.             for (int jj = 0; jj < wSamp / 2; jj++)
  61.             {
  62.                 timeFreqData[jj] = new float[cols];
  63.             }
  64.  
  65.             //Also need to check to apply parallel to everything before this
  66.             if (parallelFlag)
  67.             {
  68.                 timeFreqData = stftPar(compX, wSamp);
  69.             }
  70.             else
  71.             {
  72.                 timeFreqData = stftSeq(compX, wSamp);
  73.             }
  74.            
  75.    
  76.         }
  77.  
  78.         //Short-time Fourier Transform
  79.         //Performs FFT on short, overlapping time segments
  80.         float[][] stftSeq(Complex[] x, int wSamp)
  81.         {
  82.             int ii = 0;
  83.             int jj = 0;
  84.             int kk = 0;
  85.             int ll = 0;
  86.             int N = x.Length;
  87.  
  88.             //Stores the absolute maximum intensity of any frequency
  89.             //Presumably to determine a colour scale
  90.             float fftMax = 0;
  91.            
  92.             //Array of arrays to store the maximum frequency intensities at each time slice
  93.             float[][] Y = new float[wSamp / 2][];
  94.             for (ll = 0; ll < wSamp / 2; ll++)
  95.             {
  96.                 //Each row is a frequency
  97.                 Y[ll] = new float[2 * (int)Math.Floor((double)N / (double)wSamp)];
  98.             }
  99.            
  100.             Complex[] temp = new Complex[wSamp];
  101.             Complex[] tempFFT = new Complex[wSamp];
  102.  
  103.             //Investigate this loop
  104.             //This is where most time is spent
  105.             //Takes a slice of the wave file "temp" (array of doubles)
  106.             //Runs FFT on it and returns the intensities of each frequency "tempFFT" (array of complexes)
  107.             //Stores the absolute intensity values of each frequency [kk] into slice [ii] of "Y" (array of float arrays)
  108.  
  109.             for (ii = 0; ii < 2 * Math.Floor((double)N / (double)wSamp) - 1; ii++)
  110.             //Parallel.For(0, Convert.ToInt64(2 * Math.Floor((double)N / (double)wSamp) - 1),
  111.             //ii =>
  112.             {
  113.                 //Take a slice of the wave file
  114.                 //Overlaps by 50%?
  115.                 //Misses the very start?
  116.                 for (jj = 0; jj < wSamp; jj++)
  117.                 {
  118.                     temp[jj] = x[ii * (wSamp / 2) + jj];
  119.                 }
  120.  
  121.                 //Recursive FFT algorithm, takes up all the time
  122.                 tempFFT = fft2(temp);
  123.  
  124.                 //Stores the values and checks to see if any are higher than the max already encountered
  125.                 for (kk = 0; kk < wSamp / 2; kk++)
  126.                 {
  127.                     Y[kk][ii] = (float)Complex.Abs(tempFFT[kk]);
  128.  
  129.                     //Needs to be broken out in the final version, fftMax is a shared variable
  130.                     //Possibly keep locally within the thread and find max later
  131.                     if (Y[kk][ii] > fftMax)
  132.                     {
  133.                         fftMax = Y[kk][ii];
  134.                     }
  135.                 }
  136.                
  137.             }
  138.  
  139.             for (ii = 0; ii < 2 * Math.Floor((double)N / (double)wSamp) - 1; ii++)
  140.             {
  141.                 for (kk = 0; kk < wSamp / 2; kk++)
  142.                 {
  143.                     Y[kk][ii] /= fftMax;
  144.                 }
  145.             }
  146.  
  147.             return Y;
  148.         }
  149.  
  150.         //Short-time Fourier Transform
  151.         //Performs FFT on short, overlapping time segments
  152.         float[][] stftPar(Complex[] x, int wSamp)
  153.         {
  154.             int N = x.Length;
  155.             int timeSlices = (2 * (int)Math.Floor((double)N / (double)wSamp));
  156.             int frequencies = wSamp / 2;
  157.  
  158.             //Stores the absolute maximum intensity of any frequency
  159.             //Presumably to determine a colour scale
  160.             float fftMax = 0;
  161.  
  162.             //Array of arrays to store the maximum frequency intensities at each time slice
  163.             float[][] Y = new float[timeSlices][];
  164.             for (int ii = 0; ii < Y.Length; ii++)
  165.             {
  166.                 //Each row is the frequency values at that timeslice
  167.                 Y[ii] = new float[frequencies];
  168.             }
  169.  
  170.             //Investigate this loop
  171.             //This is where most time is spent
  172.             //Takes a slice of the wave file "temp" (array of doubles)
  173.             //Runs FFT on it and returns the intensities of each frequency "tempFFT" (array of complexes)
  174.             //Stores the absolute intensity values of each frequency [kk] into slice [ii] of "Y" (array of float arrays)
  175.  
  176.             Object lockObject = new Object();
  177.             ParallelOptions pOptions = new ParallelOptions();
  178.             pOptions.MaxDegreeOfParallelism = -1;
  179.  
  180.             Parallel.For(0, timeSlices - 1, pOptions, () => new float(),
  181.             (index, loop, localMax) =>
  182.             {
  183.                 long ii = index;
  184.  
  185.                 Complex[] timeSlice = new Complex[wSamp];
  186.                 Complex[] tempFFT = new Complex[wSamp];
  187.  
  188.                 var fftMag = Y[ii];
  189.  
  190.                 //Take a slice of the wave file
  191.                 //Overlaps by 50%?
  192.                 //Misses the very start?
  193.                 for (int jj = 0; jj < wSamp; jj++)
  194.                 {
  195.                     timeSlice[jj] = x[ii * (frequencies) + jj];
  196.                 }
  197.  
  198.                 //Recursive FFT algorithm, takes up all the time
  199.                 tempFFT = fft2(timeSlice);
  200.  
  201.                 //Stores the values and checks to see if any are higher than the max already encountered
  202.                 for (int kk = 0; kk < frequencies; kk++)
  203.                 {
  204.                     fftMag[kk] = (float)Complex.Abs(tempFFT[kk]);
  205.  
  206.                     //Needs to be broken out in the final version, fftMax is a shared variable
  207.                     //Possibly keep locally within the thread and find max later
  208.                     if (fftMag[kk] > localMax)
  209.                     {
  210.                         localMax = fftMag[kk];
  211.                     }
  212.  
  213.                 }
  214.                 Buffer.BlockCopy(fftMag, 0, Y[ii], 0, fftMag.Length);
  215.                 return localMax;
  216.             },
  217.                 (lMax) =>
  218.                 {
  219.                     lock (lockObject)
  220.                     {
  221.                         if (lMax > fftMax)
  222.                         {
  223.                             fftMax = lMax;
  224.                         }
  225.                     }
  226.                 }
  227.             );
  228.  
  229.             var Z = new float[Y[0].Length][];
  230.             for (int ii = 0; ii < Z.Length; ii++) Z[ii] = new float[Y.Length];
  231.  
  232.             //Normalise all to fftMax
  233.             //for (int ii = 0; ii < 2 * Math.Floor((double)N / (double)wSamp) - 1; ii++)
  234.             Parallel.For(0, Y.Length - 1,
  235.                 index =>
  236.             {
  237.                 long ii = index;
  238.                 for (int kk = 0; kk < Y[0].Length; kk++)
  239.                 {
  240.                     Z[kk][ii] = Y[ii][kk] / fftMax;
  241.                 }
  242.             });
  243.             return Z;
  244.         }
  245.  
  246.  
  247.         //Could increase the radix from 2 to explore caching optimisations
  248.         Complex[] fft(Complex[] x)
  249.         {
  250.             int ii = 0;
  251.             int kk = 0;
  252.             int N = x.Length;
  253.  
  254.             Complex[] Y = new Complex[N];
  255.  
  256.             // NEED TO MEMSET TO ZERO?
  257.  
  258.             if (N == 1)
  259.             {
  260.                 Y[0] = x[0]; return Y;
  261.             }
  262.  
  263.             //So much memory access, could reduce by simply calling fft with O(x) or E(X) inline
  264.             //Maybe, need to profile though
  265.             Complex[] E = new Complex[N/2];
  266.             Complex[] O = new Complex[N/2];
  267.             Complex[] even = new Complex[N/2];
  268.             Complex[] odd = new Complex[N/2];
  269.  
  270.             //Splits N and x into even and odd sets
  271.             //Could be optimised by replacing % with bit shift hack
  272.             //Obviously parallel
  273.             //Or could just eliminated completely by calculating in the call to fft() later
  274.             for (ii = 0; ii < N; ii++)
  275.             {
  276.  
  277.                 if (ii % 2 == 0)
  278.                 {
  279.                     even[ii / 2] = x[ii];
  280.                 }
  281.                 if (ii % 2 == 1)
  282.                 {
  283.                     odd[(ii - 1) / 2] = x[ii];
  284.                 }
  285.             }
  286.  
  287.             //RECURSION
  288.             //Algorithm splits the sample space in half and abuses the properties of
  289.             //the DFT to use intermediate results in later calculations (called a butterfly)
  290.             //This allows recursion up to Log2(N) (e.g. 11 for 2048) along with the final loop for N = 1
  291.             //Do this for N samples and you have a depth first graph search N wide and Log2(N+1) deep
  292.             //Results in O(N log N) complexity
  293.             //This could possibly be done in distinct threads, absolutely hilariously
  294.             //Apparently this isn't hilarious and is actually possible
  295.             E = fft(even);
  296.             O = fft(odd);
  297.  
  298.             //Should be able to be parallelised
  299.             //Might not be worth the overhead depending on N
  300.             //Could also compute O*twiddles in a single loop for SSE reasons (lots of float multiplication)
  301.             for (kk = 0; kk < N; kk++)
  302.             {
  303.                 //Might be able to fix up % with bit shifting
  304.                 Y[kk] = E[(kk % (N / 2))] + O[(kk % (N / 2))] * twiddles[kk * wSamp / N];
  305.             }
  306.            
  307.  
  308.            return Y;
  309.         }
  310.  
  311.  
  312.         Complex[] fft2(Complex[] x)
  313.         {
  314.             int bits = (int)Math.Log(x.Length, 2);
  315.             for(int i = 1; i < x.Length; i++)
  316.             {
  317.                 int swapPosition = bitReversal(i, bits);
  318.                 if(swapPosition > i)
  319.                 {
  320.                     var temp = x[i];
  321.                     x[i] = x[swapPosition];
  322.                     x[i] = temp;
  323.                 }
  324.             }
  325.  
  326.             for(int n = 2; n <= x.Length; n = n << 1)
  327.             {
  328.                 for(int i = 0; i < x.Length; i += n)
  329.                 {
  330.                     for(int j = 0; j < (n/2); j++)
  331.                     {
  332.                         int evenIndex = i + j;
  333.                         int oddIndex = i + j + (n/2);
  334.                         var even = x[evenIndex];
  335.                         var odd = x[oddIndex];
  336.  
  337.                         Complex exp = twiddles[j * wSamp / n] * odd;
  338.  
  339.                         x[evenIndex] = even + exp;
  340.                         x[oddIndex] = even - exp;
  341.                     }
  342.                 }
  343.             }
  344.  
  345.             return x;
  346.         }
  347.  
  348.         static int bitReversal(int n, int bits)
  349.         {
  350.             int reversed = n;
  351.             int count = bits - 1;
  352.  
  353.             n = n >> 1;
  354.             while(n > 0)
  355.             {
  356.                 reversed = (reversed << 1) | (n & 1);
  357.                 count--;
  358.                 n = n >> 1;
  359.             }
  360.  
  361.             return (reversed << count) & (1 << bits - 1);
  362.         }
  363.     }
  364. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement