【问题标题】:Splitting audio signal to frames from memory stream resp. byte array [C#]将音频信号从内存流分别拆分为帧。字节数组 [C#]
【发布时间】:2017-03-21 18:03:42
【问题描述】:

我正在尝试在 c# 中实现 VAD(语音活动检测)算法,因为我没有找到任何适合此目的的库。 我不使用波形文件,而只使用内存流,就像这样:

        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 合适时。

【问题讨论】:

    标签: c# audio stream memorystream


    【解决方案1】:

    不是 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 中短类型是 16 位整数,字节是 8 位整数。为了可读性,代码有点明确。我展示了 2 种可能的转换 - Little Endian 和 Big Endian。前者的第一个字节在后者的第二个字节中更重要。字节顺序取决于流格式,两者都可以。

    【讨论】:

    • 感谢您的回答。我读到要在特定帧上应用 FFT,它的大小应该是 2 的幂。所以我假设我可以将 320 字节更改为 256 对吗?
    • 不,你弄错了。在您的情况下,您得到一个包含 160 个样本的帧,每个样本 2 个字节。因此,首先您应该将每个单字节样本转换为 2 字节值,例如 (x[i] + ((int)x[i+1])
    • 我没有清楚地理解你的第一句话是什么意思。简单地说,我需要将输出的字节数组拆分为大小为 2 的帧。例如 256。我只是想知道将例如 byte[8500] 拆分为多个 byte[256] 是否是正确的方法,然后忽略如果基本数组不能被 2 整除,则余数。顺便说一句,感谢您的时间。真的很感激。
    • 好的,让我澄清一下: 1. 如果您有一个代表 16 位音频样本的字节数组,首先您需要将其转换为 2 个字节数的数组。要做到这一点,您需要遍历 2*N 字节并将每个奇数和偶数字节组合成一个数字。非常重要的是哪个字节是最重要的——奇数还是偶数?让我举个例子 - 假设你有 4 字节数组:0x04 0x02 0x08 0xA1 这实际上使样本为 0x0402 0x08A1 或 0x0204 0xA108。
    • 2.在你拥有一组实际的 16 位样本后,你可以取其中的 256 个并计算 FFT。我从 160 个样本开始,因为您提到了 10ms 块,在 16kbps 采样的情况下,160 个样本;)我想对于如此高的采样,265 个样本是可以的,因为它代表 > 10ms。如果采样率较低 - 8kbps 可能还不够。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多