POV-Ray : Newsgroups : povray.off-topic : FFT Filter... easy. Server Time
3 Sep 2024 17:14:54 EDT (-0400)
  FFT Filter... easy. (Message 1 to 6 of 6)  
From: Mike Raiford
Subject: FFT Filter... easy.
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

From: Mike Raiford
Subject: FFT Vocoder = Not as easy [~150K]
Date: 27 Aug 2010 12:47:30
Message: <4c77ec22@news.povray.org>
Well.. Maybe... , but here's the output from a 512 channel vocoder using 
a modified version of the posted code ;)

There's definitely room for improvement. One to keep the drone down 
somehow, and two, to allow for higher resolution filters based by 
overlapping slices.

Apologies for the sample used. I needed a quick spoken-word sound byte, 
and ... well.. it was convenient.
-- 
~Mike


Post a reply to this message


Attachments:
Download 'calvary2.mp3.mpg' (33 KB) Download 'out.mp3.dat' (135 KB)

From: Orchid XP v8
Subject: Re: FFT Filter... easy.
Date: 27 Aug 2010 13:02:59
Message: <4c77efc3$1@news.povray.org>
Mike Raiford wrote:
> Sooooo ... I wanted to try to make an FFT filter.

You're aware that you can perform linear filtering operations without 
using an FFT at all, right?

An FFT convolution is faster when the filter kernel is large, however.

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

I sat here and thought about posting the 8 lines or so of Haskell it 
would take to do the same task. And then I realised that Warp would 
complain that it's not processing the data in-place, so it has different 
performance characteristics, and then I'd have to build both your 
version and my version and do detailed timing statistics, which would 
require me to install the MS .NET runtime and some sort of C# 
compiler... Nah. I have better things to do with my Friday afternoon. ;-)

-- 
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*


Post a reply to this message

From: Mike Raiford
Subject: Re: FFT Filter... easy.
Date: 27 Aug 2010 13:24:12
Message: <4c77f4bc@news.povray.org>
On 8/27/2010 12:02 PM, Orchid XP v8 wrote:
> Mike Raiford wrote:
>> Sooooo ... I wanted to try to make an FFT filter.
>
> You're aware that you can perform linear filtering operations without
> using an FFT at all, right?
>

Well, yeah... of course. There's dozens of polynomials out there to do 
it, too. But try designing an arbitrary filter with that. :D

The kicker is, all of those filter functions can be built using 
capacitors, resistors and/or inductors.

> An FFT convolution is faster when the filter kernel is large, however.

Indeed. But using FFT makes filter design easier.

... And using FFT also makes doing a vocoder rather simple.

> I sat here and thought about posting the 8 lines or so of Haskell it
> would take to do the same task. And then I realised that Warp would
> complain that it's not processing the data in-place, so it has different
> performance characteristics, and then I'd have to build both your
> version and my version and do detailed timing statistics, which would
> require me to install the MS .NET runtime and some sort of C#
> compiler... Nah. I have better things to do with my Friday afternoon. ;-)


Thbbbbbbt.

-- 
~Mike


Post a reply to this message

From: Mike Raiford
Subject: Re: FFT Vocoder = Not as easy [~150K]
Date: 27 Aug 2010 13:25:24
Message: <4c77f504@news.povray.org>
Another one ... same sample ... this sound rather ominous. Using a 
square wave as the basis.




-- 
~Mike


Post a reply to this message


Attachments:
Download 'out.mp3.dat' (102 KB)

From: Warp
Subject: Re: FFT Filter... easy.
Date: 27 Aug 2010 14:40:11
Message: <4c78068b@news.povray.org>
Orchid XP v8 <voi### [at] devnull> wrote:
> And then I realised that Warp would 
> complain that it's not processing the data in-place

  I don't understand why.

-- 
                                                          - Warp


Post a reply to this message

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