从数组中的音频字节获取音高或幅度等信息
Posted
技术标签:
【中文标题】从数组中的音频字节获取音高或幅度等信息【英文标题】:Get information like pitch or amplitude from audio byte in an array 【发布时间】:2017-09-01 08:58:03 【问题描述】:我想获取字节数组中音频字节的音高(Hz)。 这是我现在的代码:
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 这样的值
【问题讨论】:
你是什么意思“音频字节的音高”?该数组是以特定采样率对音频流进行的数字捕获——每个字节代表采样时刻的幅度——因此该字节没有音高。您需要对整个阵列(或其中的一部分)进行某种音频分析,以获得该时间段的音高。 您需要阅读和分析标题。 @PaulF 我可以分析并得到它的音调的最短时间是多少?我该怎么做? @Eldar:要做到这一点,您需要对音频流的不同时间间隔进行一系列 FFT 变换。对于每个间隔,您可以从频谱中确定音高并进一步处理它们。当然,这里的假设是您的音频流一次只包含一个小频带,而音乐、语音等情况并非如此...... 您可能想尝试Signal Processing Stack Exchange 来寻求资源 - 但正如 Marko 所说,“对所涉及的数学有基本的了解对于任何类型的音频分析都是必不可少的” - 任何体面的书都会涉及一些非常复杂的数学。我在 35 多年前学习了基础知识,所以我怀疑我用过的任何书仍然可用 - 所以我只能建议谷歌。快速搜索一下这个页面 - dspguru.com/dsp/books/favorites - 我不能保证提到的任何书籍。 【参考方案1】:恐怕您的代码(和问题)过于幼稚。
Wav 文件不仅仅是音频样本的集合。查看(例如)http://soundfile.sapp.org/doc/WaveFormat/ 了解文件格式及其结构的说明。
如果您想读取、处理、写入音频文件,有不同的库(例如 NAudio)会很有帮助。
从音频流中的 1 个样本,您永远无法计算音高。为此,您需要(相对较大)数量的样本并使用 FFT 变换计算频谱。
【讨论】:
不错的链接,我正在寻找这样的标题描述。 :-) 在发布这个问题之前,我只是在阅读,这个数字非常好,它对我理解 WAV 文件的结构有很大帮助。【参考方案2】:WAV
是数据只是脉冲编码调制 (PCM)。这意味着每个值都代表音频信号的一个实际点。
Wav 文件有一个标题,你可以找到一些关于它的信息here。它描述了文件的结构。
如果您的意思是“调整”样本的基频,请尝试FFT
幅度是某个点的值,但要注意,您需要考虑这些变量:
比特误码样本 字节顺序 块对齐 频道数【讨论】:
查看@JohanDonne 的答案以获得更好的标题结构链接:soundfile.sapp.org/doc/WaveFormat 在分析数字音频时——音高通常定义为样本的基频(即最高幅度频率)——“音高是播放和原始采样率的差异” 听起来更像是音高转换。 我的意思是频率分析,我只是不知道如何实现FFT @PaulF:是的,我确实误解了问题的那一部分,我会更新它。【参考方案3】:单个 FFT 幅度峰值是衡量音乐音高的一种较差且通常不准确的方法,因为音高是一种更复杂的心理声学现象。
在估计频率时存在时频权衡,通常与 sampleRate/blockLength 成正比。因此以 44100 的采样率使用 44 个采样块,频率估计误差将在 44100/44 或 +-1000 Hz 左右(可能取决于平稳性和信噪比)。
【讨论】:
你能定义blockLength
吗?
DFT 的长度。以上是关于从数组中的音频字节获取音高或幅度等信息的主要内容,如果未能解决你的问题,请参考以下文章