规范化音频,如何将float数组转换为字节数组?

use*_*456 3 c# floating-point audio bytearray normalization

大家好,我正在播放音频文件.我把它读作a byte[],然后我需要通过将值放入[-1,1]的范围来规范化音频.我想将每个浮点值放入一个byte[i]数组,然后将其byte[]放回播放音频播放器.

我试过这个:

byte[] data = ar.ReadData();
byte[] temp=new byte[data.Length];
float biggest= 0; ;
for (int i = 0; i < data.Length; i++)
{
    if (data[i] > biggest)
    {
        biggest= data[i];
    }
}
Run Code Online (Sandbox Code Playgroud)

这段代码应该举例如0.43 int byte []如果可能的话我试过这个但是它不起作用:

for (int i = 0; i < data.Length; i++)
{
    temp = BitConverter.GetBytes(data[i] * (1 / biggest));
}
Run Code Online (Sandbox Code Playgroud)

all*_*mix 16

在评论中,你说" 我正在播放音频文件...我把它读作byte []然后我需要通过将值放入[-1,1]的范围来规范化音频然后我需要把那个字节[ ]回到播放音频播放器 "

我在这里做了一个很大的假设,但我猜测你收到的数据ar.ReadData()是一个2通道16位/ 44.1kHz PCM数据的字节数组.(旁注:您使用的是Alvas.Audio库吗?)如果是这种情况,请按照以下步骤操作.

背景

首先,一点背景.2通道,16位PCM数据流如下所示:

   byte | 01 02 | 03 04 | 05 06 | 07 08 | 09 10 | 11 12 | ...
channel |  Left | Right | Left  | Right | Left |  Right | ...
  frame |     First     |    Second     |     Third     | ...
 sample | 1st L | 1st R | 2nd L | 2nd R | 3rd L | 3rd R | ... etc.
Run Code Online (Sandbox Code Playgroud)

在这里注意一些事情很重要:

  1. 由于音频数据是16位,因此来自单个通道的单个样本是short(2个字节),而不是int(4个字节),其值在-32768到32767范围内.
  2. 此数据采用little-endian表示,除非您的体系结构也是little-endian,否则不能使用.NET BitConverter类进行转换.
  3. 我们不必将数据拆分为每个通道流,因为我们基于来自任一通道的单个最高值来规范化两个通道.
  4. 将浮点值转换为整数值将导致量化错误,因此您可能希望使用某种抖动(这本身就是一个完整的主题).

助手功能

在我们进入实际规范化之前,让我们通过编写几个辅助函数来使这更容易,从而获得a short,byte[]反之亦然:

short GetShortFromLittleEndianBytes(byte[] data, int startIndex)
{
    return (short)((data[startIndex + 1] << 8)
         | data[startIndex]);
}

byte[] GetLittleEndianBytesFromShort(short data)
{
    byte[] b = new byte[2];
    b[0] = (byte)data;
    b[1] = (byte)(data >> 8 & 0xFF);
    return b;
}
Run Code Online (Sandbox Code Playgroud)

正常化

一个重要的区别应该在这里提出:音频正常化一样的统计正常化.在这里,我们将对音频数据执行峰值归一化,将信号放大一定量,使其峰值处于上限.要对音频数据进行峰值归一化,我们首先找到最大值,从上限中减去它(对于16位PCM数据,这是32767)以获得偏移量,然后通过该偏移量增加每个值.

因此,要标准化我们的音频数据,首先扫描它以找到峰值幅度:

byte[] input = ar.ReadData();  // the function you used above
float biggest = -32768F;
float sample;
for (int i = 0; i < input.Length; i += 2)
{
    sample = (float)GetShortFromLittleEndianBytes(input, i);
    if (sample > biggest) biggest = sample;
}
Run Code Online (Sandbox Code Playgroud)

此时,biggest包含音频数据中的最大值.现在要执行实际归一化,我们biggest从32767中减去一个值,该值对应于音频数据中最响亮样本的峰值偏移量.接下来,我们将此偏移添加到每个音频样本,有效地增加每个样本的音量,直到我们最大的样本处于峰值.

float offset = 32767 - biggest;

float[] data = new float[input.length / 2];
for (int i = 0; i < input.Length; i += 2)
{
    data[i / 2] = (float)GetShortFromLittleEndianBytes(input, i) + offset;
}
Run Code Online (Sandbox Code Playgroud)

最后一步是将样本从浮点值转换为整数值,并将它们存储为little-endian shorts.

byte[] output = new byte[input.Length];
for (int i = 0; i < output.Length; i += 2)
{
    byte[] tmp = GetLittleEndianBytesFromShort(Convert.ToInt16(data[i / 2]));
    output[i] = tmp[0];
    output[i + 1] = tmp[1];
}
Run Code Online (Sandbox Code Playgroud)

我们完成了!现在,您可以将output包含规范化PCM数据的字节数组发送到音频播放器.

最后要注意的是,请记住,此代码不是最有效的; 你可以组合其中几个循环,你可以Buffer.BlockCopy()用于数组复制,以及修改你shortbyte[]辅助函数,把一个字节数组作为参数,并将值直接复制到数组中.我没有做任何这样的事情,以便更容易看到发生了什么.

正如我之前提到的,你应该完全阅读抖动,因为它将极大地提高音频输出的质量.

我自己一直在做一个音频项目,所以我通过一些反复试验来解决这个问题.我希望它可以帮助某个人.