大家好,我正在播放音频文件。我把它读作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));
}
答案 0 :(得分:15)
在评论中,你说“我正在播放音频文件......我把它读作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.
注意以下几点非常重要:
short
(2个字节),而不是int
(4个字节),其值在范围内 - 32768至32767。BitConverter
类进行转换。在我们进入实际规范化之前,让我们通过编写一些辅助函数来使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;
}
这里应该做一个重要的区别:audio normalization 不与statistical 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;
}
最后一步是将样本从浮点值转换为整数值,并将它们存储为little-endian short
。
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[]
辅助函数以将字节数组作为参数和将值直接复制到数组中。
我没有做任何这样的事情,以便更容易看到发生了什么。
正如我之前提到的,你应该绝对读取抖动,因为它会极大地提高音频输出的质量。
我自己一直在做一个音频项目,所以我通过一些反复试验来解决这个问题。我希望它可以帮助某个人。
答案 1 :(得分:2)
这有效:
float number = 0.43f;
byte[] array = BitConverter.GetBytes(number);
什么对你不起作用?
答案 2 :(得分:0)
您可以像这样使用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);
答案 3 :(得分:0)
您可以将temp
更改为字节数组列表,以避免一直覆盖它。
byte[] data = new byte[] { 1, 3, 5, 7, 9 }; // sample data
IList<byte[]> temp = new List<byte[]>(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)));
}
答案 4 :(得分:0)
if (Math.Abs(sample) > biggest) biggest = sample;
我会将其更改为:
if (Math.Abs(sample) > biggest) biggest = Math.Abs(sample);
因为如果最大值为负值,则将所有值乘以负数。