将音频信号从内存流拆分为帧。字节数组 [C#]
Splitting audio signal to frames from memory stream resp. byte array [C#]
我正在尝试在 c# 中实现 VAD(语音 Activity 检测)算法,因为我没有找到任何适合此目的的库。
我不使用波形文件,而只使用像这样的内存流:
NAudio.Wave.WaveFileWriter waveWriter;
Stream s1 = new MemoryStream();
WaveInEvent waveSource = new WaveInEvent();
waveSource.WaveFormat = new NAudio.Wave.WaveFormat(16000, 16, 1);
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveWriter = new NAudio.Wave.WaveFileWriter(s1, waveSource.WaveFormat);
waveSource.StartRecording();
Console.ReadLine();
waveSource.StopRecording();
s1.Position = 0;
var bytes = streamToArray(s1);
我将遵循此 tutorial,其中第一步是将输入信号拆分为 10 毫秒帧。我知道如何从文件输入中执行此操作,但是如何对字节数组执行类似的操作?
感谢您的回答!
更新:
我测试了这些方法:
1.
short[] sdata = new short[(int)Math.Ceiling(bytes.Length / 2.0)];
Buffer.BlockCopy(bytes, 0, sdata, 0, bytes.Length);
2.
for (var i = 0; i < bytes.Length; i += 2)
{
var b1 = (short)bytes[i];
var b2 = (short)bytes[i + 1];
sListData.Add((short)(b1 | b2 << 8));
}
当我将输出数组与使用 Big Endian 的方法进行比较时,它们都是相等的。
所以 BlockCopy
可以完成这项工作,但只有在 BE 合适的情况下。
不能 100% 确定这是否是您的意思,但是:
假设音频流参数为 16kbp,每个样本 16 位,单通道,这是一个非常快速的计算,如果 16000 个样本是一秒,那么 10ms 就是 16000/100 = 160 个样本。现在 16 位是 2 个字节,这给了我们 160 * 2 = 320 个字节。
所以每 320 个字节是 10ms。
您可以使用 Array.Copy - https://msdn.microsoft.com/en-us/library/z50k9bft(v=vs.110).aspx 将块 od 数据复制到单独的数组。
下面是一些代码:
var int8Array = new byte[] { 0x04, 0x02, 0x08, 0xA1 };
var int16ArrayLE = new List<short>();
var int16ArrayBE = new List<short>();
for(var i=0;i<int8Array.Length;i+=2)
{
var b1 = (short)int8Array[i];
var b2 = (short)int8Array[i+1];
int16ArrayLE.Add((short)((b1 << 8) + b2));
int16ArrayBE.Add((short)(b1 + (b2 << 8)));
}
在.net中short类型是16位整数,byte是8位整数。为了可读性,代码有点明确。我展示了 2 种可能的转换 - Little Endian 和 Big Endian。在前者中第一个字节在后者中第二个字节更重要。字节顺序取决于流格式,可以是任何一种。
我正在尝试在 c# 中实现 VAD(语音 Activity 检测)算法,因为我没有找到任何适合此目的的库。 我不使用波形文件,而只使用像这样的内存流:
NAudio.Wave.WaveFileWriter waveWriter;
Stream s1 = new MemoryStream();
WaveInEvent waveSource = new WaveInEvent();
waveSource.WaveFormat = new NAudio.Wave.WaveFormat(16000, 16, 1);
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveWriter = new NAudio.Wave.WaveFileWriter(s1, waveSource.WaveFormat);
waveSource.StartRecording();
Console.ReadLine();
waveSource.StopRecording();
s1.Position = 0;
var bytes = streamToArray(s1);
我将遵循此 tutorial,其中第一步是将输入信号拆分为 10 毫秒帧。我知道如何从文件输入中执行此操作,但是如何对字节数组执行类似的操作? 感谢您的回答!
更新:
我测试了这些方法:
1.
short[] sdata = new short[(int)Math.Ceiling(bytes.Length / 2.0)];
Buffer.BlockCopy(bytes, 0, sdata, 0, bytes.Length);
2.
for (var i = 0; i < bytes.Length; i += 2)
{
var b1 = (short)bytes[i];
var b2 = (short)bytes[i + 1];
sListData.Add((short)(b1 | b2 << 8));
}
当我将输出数组与使用 Big Endian 的方法进行比较时,它们都是相等的。
所以 BlockCopy
可以完成这项工作,但只有在 BE 合适的情况下。
不能 100% 确定这是否是您的意思,但是: 假设音频流参数为 16kbp,每个样本 16 位,单通道,这是一个非常快速的计算,如果 16000 个样本是一秒,那么 10ms 就是 16000/100 = 160 个样本。现在 16 位是 2 个字节,这给了我们 160 * 2 = 320 个字节。 所以每 320 个字节是 10ms。 您可以使用 Array.Copy - https://msdn.microsoft.com/en-us/library/z50k9bft(v=vs.110).aspx 将块 od 数据复制到单独的数组。
下面是一些代码:
var int8Array = new byte[] { 0x04, 0x02, 0x08, 0xA1 };
var int16ArrayLE = new List<short>();
var int16ArrayBE = new List<short>();
for(var i=0;i<int8Array.Length;i+=2)
{
var b1 = (short)int8Array[i];
var b2 = (short)int8Array[i+1];
int16ArrayLE.Add((short)((b1 << 8) + b2));
int16ArrayBE.Add((short)(b1 + (b2 << 8)));
}
在.net中short类型是16位整数,byte是8位整数。为了可读性,代码有点明确。我展示了 2 种可能的转换 - Little Endian 和 Big Endian。在前者中第一个字节在后者中第二个字节更重要。字节顺序取决于流格式,可以是任何一种。