Samy Massoud
Samy Massoud

Reputation: 4385

Resample loopback capture

I successfully captured sound from Wasapi using the following code:

IWaveIn waveIn = new WasapiLoopbackCapture();
waveIn.DataAvailable += OnDataReceivedFromWaveOut;

What I need to do now, is to resample the in-memory data to pcm with a sample rate of 8000 and 16 bits per sample mono.

I can't use ACMStream to resample the example, because the recorded audio is 32 bits per second.

I have tried this code to convert bytes from 32 to 16 bits, but all I get every time is just blank audio.

byte[] newArray16Bit = new byte[e.BytesRecorded / 2];
short two;
float value;
for (int i = 0, j = 0; i < e.BytesRecorded; i += 4, j += 2)
{
    value = (BitConverter.ToSingle(e.Buffer, i));
    two = (short)(value * short.MaxValue);

    newArray16Bit[j] = (byte)(two & 0xFF);
    newArray16Bit[j + 1] = (byte)((two >> 8) & 0xFF);
}

source = newArray16Bit;

Upvotes: 3

Views: 984

Answers (1)

Joshua Frank
Joshua Frank

Reputation: 13828

I use this routine to resample on the fly from WASAPI IeeeFloat to the format I need in my app, which is 16kHz, 16 bit, 1 channel. My formats are fixed, so I'm hardcoding the conversions I need, but it can be adapted as needed.

private void ResampleWasapi(object sender, WaveInEventArgs e)
{
    //the result of downsampling
    var resampled = new byte[e.BytesRecorded / 12];
    var indexResampled = 0;

    //a variable to flag the mod 3-ness of the current sample
    var arity = -1;

    var runningSamples = new short[3];
    for(var offset = 0; offset < e.BytesRecorded; offset += 8)
    {
        var float1 = BitConverter.ToSingle(e.Buffer, offset);
        var float2 = BitConverter.ToSingle(e.Buffer, offset + 4);

        //simple average to collapse 2 channels into 1
        float mono = (float)((double)float1 + (double)float2) / 2;

        //convert (-1, 1) range int to short
        short sixteenbit = (short)(mono * 32767);

        //the input is 48000Hz and the output is 16000Hz, so we need 1/3rd of the data points
        //so save up 3 running samples and then mix and write to the file
        arity = (arity + 1) % 3;

        //record the value
        runningSamples[arity] = sixteenbit;

        //if we've hit the third one
        if (arity == 2)
        {
            //simple average of the 3 and put in the 0th position
            runningSamples[0] = (short)(((int)runningSamples[0] + (int)runningSamples[1] + (int)runningSamples[2]) / 3);

            //copy that short (2 bytes) into the result array at the current location
            Buffer.BlockCopy(runningSamples, 0, resampled, indexResampled, 2);

            //for next pass
            indexResampled += 2;
        }
    }

    //and tell listeners that we've got data
    RaiseDataEvent(resampled, resampled.Length);
}

Upvotes: 2

Related Questions