POV-Ray : Newsgroups : povray.off-topic : FFT Filter... easy. : FFT Filter... easy. Server Time
3 Sep 2024 19:21:01 EDT (-0400)
  FFT Filter... easy.  
From: Mike Raiford
Date: 27 Aug 2010 11:24:39
Message: <4c77d8b7$1@news.povray.org>
Sooooo ... I wanted to try to make an FFT filter.

Actually quite easy:

There's a couple tricks, of course. One being how the file is being 
read. I read chunks 2*FFT Len... then seek back by FFT Len. This is so I 
have enough extra data to do the overlap later.

The code that does the filtering isn't actually using the generated 
filter (I was experimenting, and rather than mess with creating a filter 
with a nice rolloff Like I set out to do, I just did a sharp cut at a 
certain frequency, in this case its a high-pass filter...) The filter is 
reflected from FFT_LEN to 2*FFT_LEN. This may not be necessary as I 
believe the only thing it will affect is the resulting imaginary numbers 
when the ifft is computed. Once the ifft is performed, the blocks are 
blended using a Gaussian curve to eliminate any pops caused by abrupt 
sample value changes. (This is also the reason the data is read in the 
way it is, otherwise half the signal would be empty)

There is probably A LOT of improvement this could take, but its a neat 
start. :D

Code follows (C#)



         const int FFT_LEN = 4096;

         static void Main(string[] args)
         {

             double[] dr = new double[FFT_LEN*2];
             double[] di = new double[FFT_LEN * 2];
             double[] fr = new double[FFT_LEN * 2];


             FileStream fs = 
System.IO.File.OpenRead(@"D:\Sample\sound.raw");

             for (int i = 0; i < FFT_LEN; i++)
             {
                 fr[i] = 1/Math.Pow(1+(i)/(double)FFT_LEN, 2);
             }

             int bytes = 0;
             byte[] samples = new byte[FFT_LEN*2];

             List<double> output = new List<double>();

             int time = 0;
             int ch = 0;

             int bl = (int)((fs.Length/FFT_LEN) / 40) / 4;

             while ((bytes = fs.Read(samples, 0, FFT_LEN*2)) > 0)
             {
                 if(fs.Position != fs.Length)
                     fs.Seek(-FFT_LEN, SeekOrigin.Current);
                 time++;
                 if (time % bl == 0)
                 {
                     switch (ch)
                     {
                         case 0:
                             Console.Write(".");
                             break;
                         case 1:
                             Console.Write("\bo");
                             break;
                         case 2:
                             Console.Write("\bO");
                             break;
                         case 3:
                             Console.Write("\bX");
                             break;
                     }

                     ch = (ch + 1) % 4;
                 }
                 for (int i = 0; i < dr.Length; i++)
                 {
                     di[i] = 0;
                     if (i < bytes)
                         dr[i] = samples[i] - 128;
                     else
                         dr[i] = 0;
                 }
                 fft(FFT_LEN*2, dr, di, false);

                 for (int i = 0; i < FFT_LEN*2; i++)
                 {
                     // Convert to polar coordinates:
                     double m = Math.Sqrt(Math.Pow(dr[i], 2) + 
Math.Pow(di[i], 2));
                     double p = Math.Atan2(di[i],dr[i]);


                     int n = i;
                     if (n >= FFT_LEN) n = -(FFT_LEN - (n + 1));

                     if (i < FFT_LEN-3000 || i > FFT_LEN+3000) m = 0;


                     dr[i] = m * Math.Cos(p);
                     di[i] = m * Math.Sin(p);
                 }
                 fft(FFT_LEN*2, dr, di, true);

                 if (output.Count > 0)
                 {
                     int start = output.Count - FFT_LEN;
                     for (int i = 0; i < FFT_LEN; i++)
                     {
                         double fn = Math.Exp(-Math.Pow(i, 2) / (2 * 
Math.Pow(FFT_LEN/4, 2)));
                         //dr[i] += output[i + start];
                         dr[i] = dr[i]*(1-fn) + fn*output[i + start];
                     }
                 }

                 // Replace the last FFT_LEN samples with the samples in dr.
                 if(output.Count > 0) output.RemoveRange(output.Count - 
FFT_LEN, FFT_LEN);
                 output.AddRange(dr);

             }

             fs.Close();

             FileStream outF = File.OpenWrite(@"d:\sample\out.au");

             BinaryWriter bw = new BinaryWriter(outF);

             bw.Write(new byte[] { (byte)'.', (byte)'s', (byte)'n', 
(byte)'d' });

             bw.Write(BE(24));
             bw.Write(BE(output.Count));
             bw.Write(BE(2));
             bw.Write(BE(44100));
             bw.Write(BE(1));

             for (int i = 0; i < output.Count; i++)
             {
                 sbyte out_byte = (sbyte)Math.Min(Math.Max(output[i], 
-127), 127);
                 //System.Diagnostics.Debug.Print("{0} = {1}", 
output[i], out_byte);
                 //sbyte out_byte = (sbyte)(Math.Sin(i/100.0)*127);
                 bw.Write(out_byte);
             }

             outF.Close();



             //fft(16, r, i, false);
             //System.Console.ReadKey();




         }

         static byte[] BE(int n)
         {
             byte[] bytes = BitConverter.GetBytes(n);
             return bytes.Reverse().ToArray();
         }

         static void fft(int n, double[] rx, double[] ix, bool inverse)
         {


             if (Math.Abs(Math.Truncate(Math.Log((double)n, 2)) - 
Math.Log((double)n, 2)) > 0.00001)
             {
                 throw new ArgumentException("n must be a power of 2");
             }

             int hn = n / 2;
             int j = 0;

             // Reverse bit sort
             // Essentially, the indices bit order is reversed.
             for (int i = 0; i < n - 1; i++)
             {
                 if (j > i)
                 {
                     double tr = rx[j];
                     double ti = ix[j];

                     rx[j] = rx[i];
                     ix[j] = ix[i];

                     rx[i] = tr;
                     ix[i] = ti;

                 }

                 int k = hn;

                 while (k <= j)
                 {
                     j -= k;
                     k >>= 1;
                 }

                 j += k;

             }



             // FFT
             //

             int m = (int)Math.Log(n, 2);

             int l1 = 1;

             // Each stage in the recomposition
             for (int l = 0; l < m; l++)
             {
                 int l2 = l1;
                 l1 <<= 1;

                 double ur = 1;
                 double ui = 0;

                 double sr = (double)Math.Cos(Math.PI / l2);
                 double si = (double)Math.Sin(Math.PI / l2);

                 if (inverse) si = -si;

                 double tr;
                 double ti;

                 // Each Value in that state
                 for (j = 0; j < l2; j++)
                 {
                     for (int i = j; i < n; i += l1)
                     {
                         int i1 = i + l2;
                         tr = rx[i1] * ur - ix[i1] * ui;
                         ti = rx[i1] * ui + ix[i1] * ur;
                         rx[i1] = rx[i] - tr;
                         ix[i1] = ix[i] - ti;
                         rx[i] = rx[i] + tr;
                         ix[i] = ix[i] + ti;

                     }

                     tr = ur;
                     ur = tr * sr - ui * si;
                     ui = tr * si + ui * sr;
                 }

             }

             if (inverse)
             {
                 for (int i = 0; i < n; i++)
                 {
                     rx[i] /= n;
                     ix[i] /= n;
                 }
             }

         }
-- 
~Mike


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.