【问题标题】:How to separate frequency from audio file (.wav)如何从音频文件(.wav)中分离频率
【发布时间】:2017-11-03 16:10:01
【问题描述】:

我想从 .wav 文件中分离低频、中频和高频。 为此,我使用 FFT 将数据从时域转换为频域。

借助 NAudio 读取文件和应用快速傅里叶变换的代码类似于

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();

现在我得到了频域数据,并将其绘制在 ZedGraph 上,Y 轴为振幅,X 轴为频率。 FFT output on ZedGraph

现在我有复杂的数据作为 FFT 输出,但是如何将下面列出的频率与给定的数据分开,以及如何生成或播放该特定频率的文件。

  1. 低 - 20Hz 到 500Hz
  2. 中 - 500Hz 到 4KHz
  3. 高 - 4KHz 到 20KHz

任何建议或指导将不胜感激..!!

【问题讨论】:

    标签: c# audio signal-processing fft naudio


    【解决方案1】:

    如果您只想过滤音频流,您将采用一种计算成本高昂的方法。 FFT 非常适合识别光谱特征。但是如果你想从时间到频率并回到时间,那就是大量的数字运算(你很可能不喜欢你听到的)。如果您保留当前规定的路径,则需要在频域中进行某种形式的屏蔽,然后转换回时间。当您转换回时间(真实产品)时,它会听起来“肮脏”(您将需要实现某种形式的想象到真实的合并)。在您的代码中,您形成一个幅度平方以返回实时序列(注意 - 这会给您一个校正信号 - 这听起来应该很嘶嘶声)。

    如果您想隔离特定波段,请从某种形式的 FIR 带通滤波器开始。如果您想在开发过程中听到结果,请查看如何使用 Android AudioTrack 类的示例。

    【讨论】:

    • 我的目的是将这些频率分开以用于混音器应用程序(进行 dj 混音)。我在 c# 中尝试了一些低通和高通过滤器功能,但不确定我在此过滤器功能后获得的数据是否有效与任何音频播放器。保存 .wav 并播放音频不是我的目标,但需要验证数据是否有效。我只需要带有播放位置(基本上是时域)的过滤数据数组,这样我就可以混合两首歌曲的低=>低、中=>中和高=>高。
    猜你喜欢
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 2021-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多