Search code examples
caudiobufferpcmalsa

Storing captured audio in PCM_FORMAT_S16_LE into a char buffer and converting into useful data


So I'm using alsa to capture audio from a microphone and eventually I want to process samples from each channel. This type of low level programming is quite new to me.

However I'm a little confused, most of the examples using alsa store the samples for audio captured in format PCM_FORMAT_S16_LE in a char buffer. I understand it's not really a char buffer but a byte buffer. The device I'm using has four channels so from what I understand I have a 16 bit number stored in two bytes in the char buffer. Where 2 bytes/sample, and then 4 channels are interleaved.

Here's my code for capturing, I've omitted the hw setup as it's not important to my question.

char *buffer;

further down the program...

snd_pcm_params_get_period_size(params, &frames, &dir);

size = frames *8; /* 2 bytes/sample, 4 channels */

buffer = (char *) malloc(size);

    snd_pcm_hw_params_get_period_time(params, &val, &dir);

    loops = 5000000 / val;

    while (loops > 0)
    {
        loops--;
        capture = send_pcm_readi(handle, buffer, frames);

        if (capture == -EPIPE)
        {
            fprintf(stderr, "overrun occured");
            snd_pcm_prepare(handle);
        }
        else if (capture <0)
        {
            fprintf(stderr, "error from read: %s\n", snd_strerror(capture));
        }
        else if(capture != (int)frames)
        {
            fprintf(stderr, "short read, read %d frames\n", capture);
        }

        /*  

        Process values

        */


    }

What I would like to do is be able to convert each sample to a voltage or a dB value in order to do some further processing. I know the data is correct as I can write the samples into a text file and for example audacity can interpret the raw data to be a four channel audio file.

However, I'm confused as to how I get this information directly from the char buffer?


Solution

  • When you have signed 16-bit samples, you should use a signed 16-bit data type for your buffer:

    typedef short int s16;
    
    s16 *buffer = malloc(size_in_bytes);
    

    (You should use SND_PCM_FORMAT_S16 to get the endianness correct.)

    In the buffer, every four values are one frame.

    for (i = 0; i < capture; i++) {
        ch1 = buffer[i * 4 + 0];
        ch2 = buffer[i * 4 + 1];
        ch3 = buffer[i * 4 + 2];
        ch4 = buffer[i * 4 + 3];
        // or use a loop over 0..3
        ...
    }
    

    Alternatively, if you want to access all the samples of one specific channel, go over the buffer in steps of four:

    // for the first channel
    for (i = 0; i < capture; i++) {
        sample = buffer[i * 4 + 0];
        ...
    }