Search code examples
c#audiostreammemorystream

Splitting audio signal to frames from memory stream resp. byte array [C#]


I am trying to implement VAD (Voice Activity Detection) algorithm in c# because I did not find any suitable library for this purpose. Im not working with wave files but only with memory streams just like this:

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

Im gonna follow this tutorial where the first step is to split input signal to 10ms frames. I know how to do this from file input, but how can I perform similar action with array of bytes? Thanks for your answer!

Update:

I tested these methods:

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

And when I compared output arrays with the method using Big Endian, then they are all equal. So BlockCopydoes the job but only when BE is appropiate.


Solution

  • Not 100% sure if this it what you mean but: Assuming audio stream parameters of 16kbp, 16bit per sample, single channel, it's a very quick calculation that if 16000 samples is one second than 10ms is 16000/100 = 160 samples. Now 16bit is 2 bytes which gives us 160 * 2 = 320 bytes. So each 320 bytes is 10ms. You can use Array.Copy - https://msdn.microsoft.com/en-us/library/z50k9bft(v=vs.110).aspx to copy chunk od data to separate array.

    Just a bit of code below:

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

    In .net short type is 16bit integer and byte is 8bit integer. Code is a bit explicit for readability. I showed 2 possible conversions - Little Endian and Big Endian. In former the first byte is more significant in latter the second byte. Byte order depends on the stream format, can be either.