c# - 标准化音频,如何将 float 组转换为字节数组?

标签 c# audio floating-point arrays normalization

大家好,我正在播放音频文件。我将其作为 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 channel 16 位/44.1kHz PCM 数据的字节数组. (旁注:您使用的是 Alvas.Audio 库吗?)如果是这种情况,请按以下步骤操作。

背景

首先,介绍一下背景。 2 channel 、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 位,来自单个 channel 的单个样本是 short(2 字节),而不是 int(4 字节),其中-32768 到 32767 范围内的值。
  2. 此数据在 little-endian 中表示,除非您的体系结构也是小端,否则您不能使用 .NET BitConverter转换类。
  3. 我们不必将数据拆分为每个 channel 的流,因为我们正在根据任一 channel 的单个最高值对两个 channel 进行归一化。
  4. 将浮点值转换为整数值会导致量化错误,因此您可能希望使用某种 dithering (这本身就是一个完整的主题)。

辅助函数

在我们进入实际的规范化之前,让我们通过编写一些辅助函数来从 byte[] 中获取 short 并副-相反:

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;
}

规范化

这里应该做一个重要的区分:audio normalizationstatistical normalization相同.在这里,我们将对音频数据执行峰值归一化,将信号放大一个常数,使其峰值位于上限。峰值标准化音频数据,我们首先找到最大值,从上限(对于 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 包含音频数据中的最大值。现在要执行实际的归一化,我们从 32767 中减去 biggest 以获得一个值,该值对应于音频数据中最响亮样本的峰值偏移量。接下来,我们将此偏移量添加到每个音频样本,有效地增加每个样本的音量,直到我们最响亮的样本处于峰值。

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];
}

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

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

正如我之前提到的,您应该绝对阅读抖动,因为它会极大地提高音频输出的质量。

我自己一直在做一个音频项目,所以我通过反复试验弄清楚了所有这些;我希望它能对某个地方的人有所帮助。

关于c# - 标准化音频,如何将 float 组转换为字节数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9805407/

相关文章:

c# - 发布 JSON 数据 WebAPI

c# - 是否可以在 C# 中生成复杂的音调?

c - 将单精度除法实现为 double 乘法

c# - iTextSharp 从现有的 PDF 模板生成 PDF

c# - 在 C# 中处理多参数查询的模式或最佳实践

c# - asp.net 控件何时被处置

audio - 在iOS6中检测到静音模式?

c# - 我正在使用nAudio库从MIC录音,如何增加MIC的增益?

objective-c - 如何在 objective-c 中的双操作数中获取cos90的值

javascript - 测试 JavaScript 中接受数字文字的函数的上限