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

StackOverflow https://stackoverflow.com/questions/22179261

  •  05-06-2023
  •  | 
  •  

Question

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?

Was it helpful?

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];
    ...
}

OTHER TIPS

short int *outBuffer = malloc(size);

Either outBuffer[i] = (buffer[i*2] << 8) | buffer[i*2+1];

or outBuffer[i] = buffer[i*2] | (buffer[i*2+1] << 8);

Depending on which direction you're going. If you still need to deinterleave the channels, then simply copy out every nth 16-bit sample - or you can do it in place.

If the endianness of the audio and the computer match then you can just cast as in @CL's answer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top