从阵列中的音频字节获取音高或幅度等信息

时间:2017-09-01 08:58:03

标签: c# audio fft

我想得到一个字节数组中音频字节的音高。 这是我现在的代码:

        byte[] wav = File.ReadAllBytes("test.wav");
        for (int i = 44; i<wav.Length; i++)
        {
            // wav[i] is an audio byte, channel shifts every 2 bytes (I think)
        }

起初我认为wav文件是用数百或数千个块构建的,每个块都包含一个采样率,所以我试图扫描整个数组中的另一个字节序列,代表单词“WAVE”,这是一个块的一部分,但采样率仅在数组的开头,而在44位之后,所有数组都只是音频数据本身。
音频字节只是一个十六进制值,我无法理解如何从该值中获取任何信息。

更新:我已经下载了具有FFT算法的Math.NET库。 这是FFT的文档:https://numerics.mathdotnet.com/api/MathNet.Numerics.IntegralTransforms/Fourier.htm 我已经阅读了那里的所有方法,但我不知道什么方法可以做我想要的(给它几个字节的wav文件并获得它们的频率)。

更新2: 现在我使用Accord库进行FFT,我在youtube上找到了一个教程。 这是我将音频字节转换为双数组的代码:

            for (int i = 44; i<wav.Length; i+=BufferSize)
        {
            float currentSec = (float) audioLength / wav.Length * i;

            byte[] buffer = new byte[BufferSize];

            for (int j = 0; j < buffer.Length; j++)
            {
                if ((i + j + 1) < wav.Length)
                    buffer[j] = wav[i + j];
            }

            int SAMPLE_RESOLUTION = 16;
            int BYTES_PER_POINT = SAMPLE_RESOLUTION / 8;

            Int32[] vals = new Int32[buffer.Length / BYTES_PER_POINT];
            double[] Ys = new double[buffer.Length / BYTES_PER_POINT];
            double[] Ys2 = new double[buffer.Length / BYTES_PER_POINT];

            for (int k = 0; k < Ys.Length; k++)
            {
                byte hByte = buffer[k * 2 + 1];
                byte lByte = buffer[k * 2 + 0];

                vals[k] = (int)(short)((hByte << 8) | lByte);
                Ys[k] = vals[k];
            }
            Ys2 = FFT(Ys);

            double avgFrq = AverageFromArray(Ys2);

            if(lastSecond < (int) currentSec)
                lastSecond = (int) currentSec;

        }

FFT功能:

       private double[] FFT(double[] data)
   {
        double[] fft = new double[data.Length];
        System.Numerics.Complex[] fftComplex = new System.Numerics.Complex[data.Length];
        for (int i = 0; i < data.Length; i++)
        {
            fftComplex[i] = new System.Numerics.Complex(data[i], 0);
        }

        Accord.Math.FourierTransform.FFT(fftComplex, Accord.Math.FourierTransform.Direction.Forward);
        for (int i = 0; i < data.Length; i++)
        {
            fft[i] = fftComplex[i].Magnitude;
        }

        return fft;
    }

因此要检查它是否有效我制作的wav文件只是5000Hz频率的白噪声,但这些是我从FFT得到的结果(2048字节数组的值): https://pastebin.com/PUq5bQTn 整个音频文件具有相同的5000Hz频率但我的代码给我的值为605.80502914453746和4401.1090268930584

3 个答案:

答案 0 :(得分:6)

我担心你的代码(和问题)过于天真。

  1. Wav文件不仅仅是音频样本的集合。有关文件格式及其结构的说明,请查看(例如)this

  2. 如果您想阅读,处理,编写音频文件,那里有不同的库(例如NAudio)可以提供很多帮助。

  3. 从音频流中的1个样本中,您永远无法计算音高。为此,您需要一个(相对较大的)样本数,并使用FFT变换计算频谱。

答案 1 :(得分:4)

iframe.unbind( "load" ); 数据只是脉冲编码调制(PCM)。这意味着每个值都代表音频信号的实际点。

Wav文件有标题,您可以找到有关它的一些信息here。它描述了文件的结构。

如果你的意思是&#34; pitch&#34;样本的基本频率,请尝试FFT

幅度是某一点的值,但要注意,您需要考虑这些变量:

  • bits ber sample
  • 字节顺序
  • block align
  • 频道数

答案 2 :(得分:2)

  1. 单个FFT幅度峰值是衡量音高的一种较差且通常不准确的方式,因为音调是一种更复杂的心理声学现象。

  2. 估算频率时存在时频折衷,通常与sampleRate / blockLength成正比。因此,以44100的采样率使用44个采样块,频率估计误差将在44100/44或大约+ -1000 Hz(可能取决于平稳性和信噪比)。