stare
stare

Reputation: 150

Why does the sound skip in ALSA?

I am trying to play this sound using the program below, but the sound is a little fast and it skips. The sound file's sample rate is 11025 Hz, stereo, the sample size is 16 bit. The issue appears to be snd_pcm_hw_params_set_buffer_size() and snd_pcm_hw_params_set_period_size() (both are functions I am studying) due to invalid arguments, but I don't know why they are invalid and commenting them out doesn't address the skipping effect. What am I doing wrong?

#include <stdlib.h>
#include <stdint.h>
#include <alsa/asoundlib.h>

#define STEREO              2
#define BITS            16 / 8
#define FRAMEBUFFERSIZE   512 // in samples
#define PERIODS             2
#define SAMPLERATE      11025

void snd_error_checker(int error, char *function_name)
{
    if (error)
    {
        printf("Error (%s): %s\n", function_name, snd_strerror(error));
        // exit(EXIT_FAILURE);
    }
}

int main(void)
{
    snd_pcm_t *handle;
    uint32_t channels                   = STEREO;
    uint32_t sample_size                = BITS; // 16 bit
    uint32_t frame_size                 = sample_size * channels;
    snd_pcm_uframes_t frames            = FRAMEBUFFERSIZE / frame_size;
    snd_pcm_uframes_t frames_per_period = frames / PERIODS;
    int32_t dir = 0; // No idea what this does.
    snd_pcm_hw_params_t *params;
    int16_t *buffer;
    FILE *wav;
    int32_t size;
    int error;

    error = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    snd_error_checker(error, "snd_pcm_open()");
    snd_pcm_hw_params_alloca(&params);
    error = snd_pcm_hw_params_any(handle, params);
    snd_error_checker(error, "snd_pcm_hw_params_any()");
    error = snd_pcm_hw_params_set_buffer_size(handle, params, frames);
    snd_error_checker(error, "snd_pcm_hw_params_set_buffer_size()");
    error = snd_pcm_hw_params_set_period_size(handle, params, frames_per_period, dir);
    snd_error_checker(error, "snd_pcm_hw_params_set_period_size()");
    error = snd_pcm_hw_params_set_rate(handle, params, SAMPLERATE, dir);
    snd_error_checker(error, "snd_pcm_hw_params_set_rate()");
    error = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_error_checker(error, "snd_pcm_hw_params_set_access()");
    error = snd_pcm_hw_params_set_channels(handle, params, STEREO);
    snd_error_checker(error, "snd_pcm_hw_params_set_channels()");
    error = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
    snd_error_checker(error, "snd_pcm_hw_params_set_format()");
    error = snd_pcm_hw_params(handle, params);
    snd_error_checker(error, "snd_pcm_hw_params()");

    // Insert samples to framebuffer.
    wav = fopen("pcm1611s.wav", "r");
    fseek(wav, 0L, SEEK_END);
    size = ftell(wav);
    fseek(wav, 0L, SEEK_SET);
    buffer = malloc(size);
    fread(buffer, sizeof(int16_t), size, wav);
    fclose(wav);
    size /= sizeof(int16_t);

    // Set ptrbuffer 46 bytes ahead to skip the header.
    for (int16_t *ptrbuffer = buffer + 46; size > ptrbuffer - buffer; ptrbuffer += FRAMEBUFFERSIZE * STEREO * BITS)
    {
        error = snd_pcm_writei(handle, ptrbuffer, frames);
        if (error)
        {
            snd_pcm_recover(handle, error, 0);
        }
    }
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    exit(EXIT_SUCCESS);
}

Upvotes: 0

Views: 331

Answers (2)

CL.
CL.

Reputation: 180020

#define FRAMEBUFFERSIZE   512 // in samples

The comment is lying; the value is used as a byte count.

uint32_t sample_size                = BITS; // 16 bit

The comment is lying; the value is actualy measured in bytes. (And BITS is named wrong.)

fread(buffer, sizeof(int16_t), size, wav);

size is measured in bytes, but you tell fread() to read 16-bit words. (This does not actually hurt because it stops reading at the end of the file. But you should have checked for errors.)

int16_t *ptrbuffer = ...

You are telling the compiler that ptrbuffer points to 16-bit values.

ptrbuffer += FRAMEBUFFERSIZE * STEREO * BITS

You are telling the compiler to step over 2048 16-bit values, i.e., over 4096 bytes. You actually want to step over all the sample in a buffer, that is, frames * STEREO.

error = snd_pcm_writei(...);
if (error)

snd_pcm_writei() returns a positive number on success; errors are negative.

Upvotes: 1

jjm
jjm

Reputation: 461

Three points need to be consider for proper playback

  1. Check return value of snd_pcm_writei , will give a clear idea , whats going wrong if any error. Referalsa-project

  2. Reading file in while() loop and giving to snd_pcm_writei can cause skip in audio with under run (-EPIPE error), since fread is time consuming call.

  3. Check return values of all of your library calls and make sure all are success. Also make sure the values are setting properly in driver . For example, after snd_pcm_hw_params_set_rate verify it with snd_pcm_hw_params_get_rate, if your driver is not supporting sampling rate 11025KHz, we can find out it with return values and rechecking with snd_pcm_hw_params_get_rate

Upvotes: 1

Related Questions