Search code examples
c#.netcompressionclrnaudio

How would I use NAudio to compress incoming PCM data from the microphone?


I'm working on a VOIP application that will be transmitting audio data across a network using NAudio. I don't want to stream raw PCM because of bandwidth concerns so I'm trying to encode the samples in μ-law. So far, I've been unable to find any explanations on how exactly this is done. I assume that I'm supposed to use NAudio.Codecs.MuLawEncoder.LinearToMuLawSample(short s), but the problem is that I'm given a byte array in the DataAvailable event.

I've tried iterating through the DataAvailable buffer, calling LinearToMuLawSample on every byte. However, the actual size of the array obviously isn't changing and thus I'm not getting any size reduction.

Here's the code responsible for encoding:

byte[] sample = _rawSamples.Take(); //take from sample buffer

for (int i = 0; i < sample.Length; i++) //actual conversion code
    sample[i] = MuLawEncoder.LinearToMuLawSample(sample[i]);

_encodedSamples.Add(sample); //add to network buffer

Here's the contents of my DataAvailable handler:

private void _input_DataAvailable(object sender, WaveInEventArgs e)
{
    byte[] buffer = e.Buffer;
    Array.Resize(ref buffer, e.BytesRecorded);

    _rawSamples.Add(audioData);
}

And finally, here's how I'm declaring my WaveIn:

_input = new WaveIn();
_input.BufferMilliseconds = 100;
_input.DataAvailable += _input_DataAvailable;
_input.RecordingStopped += _input_InputRecordingStopped;

Essentially, what I'm looking for is a way to take PCM data in the form of a byte array and convert it to a format suitable for sending over a network. I don't quite understand how the MuLawEncoder class can actually encode/compress audio data in this manner.


Solution

  • Update

    I was able to get it working using the examples at https://github.com/naudio/NAudio/blob/master/NAudioDemo/NetworkChatDemo/. Here are the key functions I ended up using:

    private byte[] EncodeSamples(byte[] data)
    {
        byte[] encoded = new byte[data.Length / 2];
        int outIndex = 0;
    
        for (int n = 0; n < data.Length; n += 2)
            encoded[outIndex++] = MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(data, n));
    
        return encoded;
    }
    
    private byte[] DecodeSamples(byte[] data)
    {
        byte[] decoded = new byte[data.Length * 2];
        int outIndex = 0;
        for (int n = 0; n < data.Length; n++)
        {
            short decodedSample = MuLawDecoder.MuLawToLinearSample(data[n]);
            decoded[outIndex++] = (byte)(decodedSample & 0xFF);
            decoded[outIndex++] = (byte)(decodedSample >> 8);
        }
        return decoded;
    }
    

    The key was stepping through the data two bytes at a time, converting each pair into an Int16, passing that into LinearToMuLawSample, and packing the return values into a new array of half the size.