clinuxalsaoggvorbis

Implementing sound player with ALSA and Ogg vorbis in C


I am trying to implement a very simple ogg player. It basically opens an ogg file, verifies it and runs thru libvorbisfile decoder to ALSA PCM. The problem is that sound output is very distorted, laggy and it requires quite a lot of imagination to hear the original music file. Both alsa and ogg file is encoded in 44100Hz samplerate, two channels and 16bit sample size.

Here is the source code:

#include <stdio.h>
#include <stdlib.h>
#include <vorbis/vorbisfile.h>
#include <alsa/asoundlib.h>

char *buffer;
static char *device = "default";

int main(int argc, char **argv)
{
    buffer = (char *) malloc(4096);
    int err;
    snd_pcm_t *handle;
    snd_pcm_sframes_t frames;

    if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0))
            < 0) {
        printf("Playback open error: %s\n", snd_strerror(err));
        exit(EXIT_FAILURE);
    }
    if((err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,
            SND_PCM_ACCESS_RW_INTERLEAVED, 2, 44100, 1, 500000))
            < 0) { /* 0.5sec */
        printf("Playback open error: %s\n", snd_strerror(err));
        exit(EXIT_FAILURE);
    }

    OggVorbis_File vf;
    int eof = 0;
    int current_section;

    err = ov_fopen("zedd.ogg", &vf);
    if(err != 0) {
        perror("Error opening file");
    } else {
        vorbis_info *vi = ov_info(&vf, -1);
        fprintf(stderr, "Bitstream is %d channel, %ldHz\n",
                vi->channels, vi->rate);
        fprintf(stderr, "Encoded by: %s\n\n",
                ov_comment(&vf, -1)->vendor);

        while(!eof) {
            long ret = ov_read(&vf, buffer, 4096, 0, 2, 1,
                    &current_section);
            if(ret == 0) {
                /* EOF */
                eof = 1;
            } else if(ret < 0) {
                /* error in the stream. */
            } else {
                frames = snd_pcm_writei(handle, buffer, ret);
                snd_pcm_wait(handle, 20000);
                if(frames < 0)
                    frames = snd_pcm_recover(handle, frames,
                            0);
                if(frames < 0) {
                    printf("snd_pcm_writei failed: %s\n",
                            snd_strerror(err));
                    break;
                }
                if(frames > 0 && frames < 4096)
                    printf("Short write (expected %li, wrote %li)\n",
                            ret, frames);
            }
        }
        ov_clear(&vf);
    }

    snd_pcm_close(handle);

    return (0);
}

Any help would be really helpfull.


Solution

  • The answer is actually really simple. In function snd_pcm_writei i was passing not the number of frames, but number of bytes in buffer. For 16bit 2 channel audio is one frame four bytes (two bytes per sample and we have two channels). Changing call to frames = snd_pcm_writei(handle, buffer, ret/4); and deleting snd_pcm_wait (redundant in blocking mode) fixes all issues. Many thans for guys in #xiph on irc.freenode.net.