0
votes

I want to separate low, mid and high frequencies from .wav file. for that i have used FFT to convert data from time domain to frequency domain.

code for reading file and applying Fast Fourier Transform with help of NAudio is like

OpenFileDialog file = new OpenFileDialog();
        file.ShowDialog();
        WaveFileReader reader = new WaveFileReader(file.FileName);
        int samepleRate = reader.WaveFormat.SampleRate;
        double ts = 1.0 / samepleRate;
        int _fftLength = 4096;
        double time = reader.TotalTime.TotalSeconds;
        int channels = reader.WaveFormat.Channels;
        int _m = (int)Math.Log(_fftLength, 2.0);
        float fileSize = (float)reader.Length / 1048576;
        if (fileSize < 2)
            window = 8;
        else if (fileSize > 2 && fileSize < 4)
            window = 16;
        else if (fileSize > 4 && fileSize < 8)
            window = 32;
        else if (fileSize > 8 && fileSize < 12)
            window = 128;
        else if (fileSize > 12 && fileSize < 20)
            window = 256;
        else if (fileSize > 20 && fileSize < 30)
            window = 512;
        else
            window = 2048;


        byte[] readBuffer = new byte[reader.Length];

        reader.Read(readBuffer,0,readBuffer.Length);
        float[] data = ConvertByteToFloat(readBuffer,readBuffer.Length);            

        Complex[] fftBuffer= new Complex[_fftLength];
        int fftPos = 0;
        for (int i = 0; i < _fftLength; i++)
        {
            fftBuffer[fftPos].X = (float)(data[i] * NAudio.Dsp.FastFourierTransform.HammingWindow(i,_fftLength));
            fftBuffer[fftPos].Y = 0;
            fftPos++;
        }
        NAudio.Dsp.FastFourierTransform.FFT(true, _m, fftBuffer);

private float[] ConvertByteToFloat(byte[] array, int length)
    {
        int samplesNeeded = length / 4;
        float[] floatArr = new float[samplesNeeded];

        for (int i = 0; i < samplesNeeded; i++)
        {
            floatArr[i] = (float)BitConverter.ToInt32(array, i * 4);
        }

        return floatArr;
    }

 //ZedGraph code 
        GraphPane myPane = zedGraphControl1.GraphPane;
        myPane.Title.Text = "Frequency domain output";
        PointPairList list1 = new PointPairList();
        PointPairList list2 = new PointPairList();
        for (int i = 0; i < fftBuffer.Length; i++)
        {
            list1.Add(i, fftBuffer[i].Y);
        }
        list2.Add(0, 0);
        //list2.Add(time, 0);uncomment this and remove below to plot time domain graph
        var maxIndex = -1;
        var maxValue = 0f;
        for (var j = 0; j < _fftLength / 2; j++)
        {
            var value = fftBuffer[j].X * fftBuffer[j].X
                + fftBuffer[j].Y * fftBuffer[j].Y;

            if (value > maxValue)
            {
                maxIndex = j;
                maxValue = value;
            }
            var freq = maxIndex == -1 ? 0
           : (ushort)Math.Round((_fftLength - maxIndex) / (_fftLength * ts));
            list2.Add(freq, 0);
        }
        if (myCurve1 != null && myCurve2 != null)
        {
            myCurve1.Clear();
            myCurve2.Clear();
        }

        myCurve1 = myPane.AddCurve(null, list1, Color.Blue, SymbolType.None);
        myCurve1.IsX2Axis = true;
        myCurve2 = myPane.AddCurve(null, list2, Color.Black, SymbolType.None);
        myPane.XAxis.Scale.MaxAuto = true;
        myPane.XAxis.Scale.MinAuto = true;
        myPane.YAxis.Title.Text = "Amplitude";
        myPane.XAxis.Title.Text = "Frequency";
        zedGraphControl1.AxisChange();
        zedGraphControl1.Invalidate();

Now I got the frequency domain data and I have plotted it on ZedGraph with Amplitude on Y-axis and Frequency on X-axis. FFT output on ZedGraph

Now I have complex data as FFT out with me, but how to separate below listed frequencies from given data and how to generate or play file of that particular frequency.

  1. Low - 20Hz to 500Hz
  2. Mid - 500Hz to 4KHz
  3. High- 4KHz to 20KHz

Any advise or guidance would be greatly appreciated..!!

1

1 Answers

0
votes

If you just want to filter an audio stream, you are heading down a computationally expensive approach. FFTs are great for identifying spectral signatures. But if you want to go form time to frequency and back to time, that's a lot of number crunching (and you will most likely not like what you hear). If you keep the currently stated path, you will need to do some form of masking in the frequency domain, then convert back to time. When you convert back to time (a real product) it's going to sound "dirty" (you will need to implement some form of an imaginary to real merge). In your code, you form a magnitude squared to get back to a real time series (note - this will give you a rectified signal - which should sound hissy).

If you want to isolate a particular band, start with some form of a FIR band pass filter. If you want to hear the result as you are developing, check out examples of how to use the Android AudioTrack class.