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

2024-01-28

大家好,我正在播放音频文件。我把它读作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];
    }
}

这部分代码应该放置例如 0.43 int byte[] 如果可能的话我尝试了这个但它不起作用:

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

您在评论中表示“我正在播放音频文件...我将其读取为 byte[],然后我需要通过将值放入 [-1,1] 范围内来标准化音频,然后我需要将该 byte[] 放回到播放音频播放器中"

我在这里做了一个很大的假设,但我猜测您收到的数据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.

这里重要的是要注意以下几点:

  1. 由于音频数据是 16 位,因此来自单个通道的单个样本是short(2 个字节),不是int(4 个字节),值范围为 -32768 到 32767。
  2. 该数据位于小尾数法 http://en.wikipedia.org/wiki/Endianness表示,除非您的架构也是小端,否则您不能使用 .NETBitConverter http://msdn.microsoft.com/en-us/library/system.bitconverter.aspx用于转换的类。
  3. 我们不必将数据拆分为每个通道的流,因为我们根据任一通道的单个最高值对两个通道进行标准化。
  4. 将浮点值转换为整数值将导致量化错误,因此您可能需要使用某种抖动 http://en.wikipedia.org/wiki/Dither(这本身就是一个完整的主题)。

辅助函数

在我们开始实际的标准化之前,让我们通过编写几个辅助函数来使这变得更容易short from a 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;
}

正常化

这里应该做出一个重要的区别:音频标准化 http://en.wikipedia.org/wiki/Audio_normalization is not统计标准化 http://en.wikipedia.org/wiki/Normalization_(statistics)。在这里,我们将对音频数据进行峰值归一化,将信号放大恒定量,使其峰值处于上限。为了对音频数据进行峰值归一化,我们首先找到最大值,从上限中减去它(对于 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;
}

在此刻,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;
}

最后一步是将样本从浮点转换为整数值,并将它们存储为小尾数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];
}

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

最后一点,请记住,此代码并不是最有效的;它是最有效的。您可以组合其中几个循环,并且您可能可以使用Buffer.BlockCopy()用于数组复制,以及修改您的short to byte[]辅助函数将字节数组作为参数并将值直接复制到数组中。 我没有做任何这些事情,以便更容易地了解发生了什么。

正如我之前提到的,你应该绝对地阅读有关抖动的内容,因为它将极大地提高音频输出的质量。

我自己一直在从事一个音频项目,所以我通过一些尝试和错误弄清楚了这一切;我希望它能帮助某人。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

规范化音频,如何将浮点数组转换为字节数组? 的相关文章