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

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

我试过这个:

 byte[] data = ar.ReadData(); byte[] temp=new byte[data.Length]; float biggest= 0; ; for (int i = 0; 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]的范围来规范化音频然后我需要放置该字节[ ]回到播放音频播放器

我在这里做了一个很大的假设,但我猜你从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. 此数据采用little-endian表示,除非您的体系结构也是little-endian,否则不能使用.NET BitConverter类进行转换。
  3. 我们不必将数据拆分为每个通道流,因为我们基于来自任一通道的单个最高值来规范化两个通道。
  4. 将浮点值转换为整数值将导致量化错误,因此您可能希望使用某种抖动 (这本身就是一个完整的主题)。

助手function

在我们进入实际规范化之前,让我们通过编写几个辅助函数来从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; } 

正常化

这里应该做一个重要的区别: 音频标准化与统计标准化不同 。 在这里,我们将对音频数据执行峰值归一化,将信号放大一定量,使其峰值处于上限。 为了使音频数据的峰值标准化,我们首先找到最大值,从上限(对于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; } 

此时,最大值包含音频数据中的最大值。 现在要执行实际归一化,我们从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; } 

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

 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 to byte[]辅助函数以将字节数组作为参数并将值直接复制到arrays。 我没有做任何这样的事情,以便更容易看到发生了什么。

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

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

这有效:

 float number = 0.43f; byte[] array = BitConverter.GetBytes(number); 

什么对你不起作用?

您可以像这样使用Buffer.BlockCopy

 float[] floats = new float[] { 0.43f, 0.45f, 0.47f }; byte[] result = new byte[sizeof(float) * floats.Length]; Buffer.BlockCopy(floats, 0, result, 0, result.Length); 

您可以将temp更改为字节数组列表,以避免一直覆盖它。

  byte[] data = new byte[] { 1, 3, 5, 7, 9 }; // sample data IList temp = new List(data.Length); float biggest = 0; ; for (int i = 0; i < data.Length; i++) { if (data[i] > biggest) biggest = data[i]; } for (int i = 0; i < data.Length; i++) { temp.Add(BitConverter.GetBytes(data[i] * (1 / biggest))); } 
 if (Math.Abs(sample) > biggest) biggest = sample; 

我会改为:

 if (Math.Abs(sample) > biggest) biggest = Math.Abs(sample); 

因为如果最大值为负值,则将所有值乘以负数。