view libao2/ao_alsa5.c @ 2077:85683535e2b7

Similar to 1.15
author jaf
date Thu, 04 Oct 2001 17:30:46 +0000
parents a3c3e1de6f02
children 08947e067d80
line wrap: on
line source

/*
  ao_alsa5 - ALSA-0.5.x output plugin for MPlayer

  (C) Alex Beregszaszi <alex@naxine.org>

  Thanks to Arpi for helping me ;)
*/

#include <errno.h>
#include <sys/asoundlib.h>

#include "../config.h"

#include "audio_out.h"
#include "audio_out_internal.h"
#include "afmt.h"

extern int verbose;

static ao_info_t info = 
{
    "ALSA-0.5.x audio output",
    "alsa5",
    "Alex Beregszaszi <alex@naxine.org>",
    ""
};

LIBAO_EXTERN(alsa5)

/* global variables:
    ao_samplerate
    ao_channels
    ao_format
    ao_bps
    ao_outburst
    ao_buffersize
*/

static snd_pcm_t *alsa_handler;
static snd_pcm_format_t alsa_format;
static int alsa_rate = SND_PCM_RATE_CONTINUOUS;

/* to set/get/query special features/parameters */
static int control(int cmd, int arg)
{
    return(CONTROL_UNKNOWN);
}

/*
    open & setup audio device
    return: 1=success 0=fail
*/
static int init(int rate_hz, int channels, int format, int flags)
{
    int err;
    int cards = -1;
    snd_pcm_channel_params_t params;
    snd_pcm_channel_setup_t setup;
    snd_pcm_info_t info;
    snd_pcm_channel_info_t chninfo;

    printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz,
	channels, audio_out_format_name(format));

    alsa_handler = NULL;

    if (verbose)
	printf("alsa-init: compiled for ALSA-%s (%d)\n", SND_LIB_VERSION_STR,
	    SND_LIB_VERSION);

    if ((cards = snd_cards()) < 0)
    {
	printf("alsa-init: no soundcards found\n");
	return(0);
    }

    ao_format = format;
    ao_channels = channels - 1;
    ao_samplerate = rate_hz;
    ao_bps = ao_samplerate*(ao_channels+1);
    ao_outburst = OUTBURST;
    ao_buffersize = 16384;

    memset(&alsa_format, 0, sizeof(alsa_format));
    switch (format)
    {
	case AFMT_S8:
	    alsa_format.format = SND_PCM_SFMT_S8;
	    break;
	case AFMT_U8:
	    alsa_format.format = SND_PCM_SFMT_U8;
	    break;
	case AFMT_U16_LE:
	    alsa_format.format = SND_PCM_SFMT_U16_LE;
	    break;
	case AFMT_U16_BE:
	    alsa_format.format = SND_PCM_SFMT_U16_BE;
	    break;
	case AFMT_S16_LE:
	    alsa_format.format = SND_PCM_SFMT_S16_LE;
	    break;
	case AFMT_S16_BE:
	    alsa_format.format = SND_PCM_SFMT_S16_BE;
	    break;
	default:
	    alsa_format.format = SND_PCM_SFMT_MPEG;
	    break;
    }
    
    switch(alsa_format.format)
    {
	case SND_PCM_SFMT_S16_LE:
	case SND_PCM_SFMT_U16_LE:
	    ao_bps *= 2;
	    break;
	case -1:
	    printf("alsa-init: invalid format (%s) requested - output disabled\n",
		audio_out_format_name(format));
	    return(0);
	default:
	    break;
    }

    switch(rate_hz)
    {
	case 8000:
	    alsa_rate = SND_PCM_RATE_8000;
	    break;
	case 11025:
	    alsa_rate = SND_PCM_RATE_11025;
	    break;
	case 16000:
	    alsa_rate = SND_PCM_RATE_16000;
	    break;
	case 22050:
	    alsa_rate = SND_PCM_RATE_22050;
	    break;
	case 32000:
	    alsa_rate = SND_PCM_RATE_32000;
	    break;
	case 44100:
	    alsa_rate = SND_PCM_RATE_44100;
	    break;
	case 48000:
	    alsa_rate = SND_PCM_RATE_48000;
	    break;
	case 88200:
	    alsa_rate = SND_PCM_RATE_88200;
	    break;
	case 96000:
	    alsa_rate = SND_PCM_RATE_96000;
	    break;
	case 176400:
	    alsa_rate = SND_PCM_RATE_176400;
	    break;
	case 192000:
	    alsa_rate = SND_PCM_RATE_192000;
	    break;
	default:
	    alsa_rate = SND_PCM_RATE_CONTINUOUS;
	    break;
    }

    alsa_format.rate = ao_samplerate;
    alsa_format.voices = ao_channels*2;
    alsa_format.interleave = 1;

    if ((err = snd_pcm_open(&alsa_handler, 0, 0, SND_PCM_OPEN_PLAYBACK)) < 0)
    {
	printf("alsa-init: playback open error: %s\n", snd_strerror(err));
	return(0);
    }

    if ((err = snd_pcm_info(alsa_handler, &info)) < 0)
    {
	printf("alsa-init: pcm info error: %s\n", snd_strerror(err));
	return(0);
    }

    printf("alsa-init: %d soundcard%s found, using: %s\n", cards,
	(cards == 1) ? "" : "s", info.name);

    if (info.flags & SND_PCM_INFO_PLAYBACK)
    {
	bzero(&chninfo, sizeof(chninfo));
	chninfo.channel = SND_PCM_CHANNEL_PLAYBACK;
	if ((err = snd_pcm_channel_info(alsa_handler, &chninfo)) < 0)
	{
	    printf("alsa-init: pcm channel info error: %s\n", snd_strerror(err));
	    return(0);
	}
	if (chninfo.buffer_size)
	    ao_buffersize = chninfo.buffer_size;
	if (verbose)
	    printf("alsa-init: setting preferred buffer size from driver: %d bytes\n",
		ao_buffersize);
    }

    memset(&params, 0, sizeof(params));
    params.channel = SND_PCM_CHANNEL_PLAYBACK;
    params.mode = SND_PCM_MODE_STREAM;
    params.format = alsa_format;
    params.start_mode = SND_PCM_START_DATA;
    params.stop_mode = SND_PCM_STOP_ROLLOVER;
    params.buf.stream.queue_size = ao_buffersize;
    params.buf.stream.fill = SND_PCM_FILL_NONE;

    if ((err = snd_pcm_channel_params(alsa_handler, &params)) < 0)
    {
	printf("alsa-init: error setting parameters: %s\n", snd_strerror(err));
	return(0);
    }

    memset(&setup, 0, sizeof(setup));
    setup.channel = SND_PCM_CHANNEL_PLAYBACK;
    setup.mode = SND_PCM_MODE_STREAM;
    setup.format = alsa_format;
    setup.buf.stream.queue_size = ao_buffersize;
    setup.msbits_per_sample = ao_bps;
    
    if ((err = snd_pcm_channel_setup(alsa_handler, &setup)) < 0)
    {
	printf("alsa-init: error setting up channel: %s\n", snd_strerror(err));
	return(0);
    }

    if ((err = snd_pcm_channel_prepare(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
    {
	printf("alsa-init: channel prepare error: %s\n", snd_strerror(err));
	return(0);
    }

    printf("AUDIO: %d Hz/%d channels/%d bps/%d bytes buffer/%s\n",
	ao_samplerate, ao_channels+1, ao_bps, ao_buffersize,
	snd_pcm_get_format_name(alsa_format.format));
    return(1);
}

/* close audio device */
static void uninit()
{
    int err;

    if ((err = snd_pcm_playback_drain(alsa_handler)) < 0)
    {
	printf("alsa-uninit: playback drain error: %s\n", snd_strerror(err));
	return;
    }

    if ((err = snd_pcm_channel_flush(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
    {
	printf("alsa-uninit: playback flush error: %s\n", snd_strerror(err));
	return;
    }

    if ((err = snd_pcm_close(alsa_handler)) < 0)
    {
	printf("alsa-uninit: pcm close error: %s\n", snd_strerror(err));
	return;
    }
}

/* stop playing and empty buffers (for seeking/pause) */
static void reset()
{
    int err;

    if ((err = snd_pcm_playback_drain(alsa_handler)) < 0)
    {
	printf("alsa-reset: playback drain error: %s\n", snd_strerror(err));
	return;
    }

    if ((err = snd_pcm_channel_flush(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
    {
	printf("alsa-reset: playback flush error: %s\n", snd_strerror(err));
	return;
    }

    if ((err = snd_pcm_channel_prepare(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
    {
	printf("alsa-reset: channel prepare error: %s\n", snd_strerror(err));
	return;
    }
}

/* stop playing, keep buffers (for pause) */
static void audio_pause()
{
    int err;

    if ((err = snd_pcm_playback_drain(alsa_handler)) < 0)
    {
	printf("alsa-pause: playback drain error: %s\n", snd_strerror(err));
	return;
    }

    if ((err = snd_pcm_channel_flush(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
    {
	printf("alsa-pause: playback flush error: %s\n", snd_strerror(err));
	return;
    }
}

/* resume playing, after audio_pause() */
static void audio_resume()
{
    int err;
    if ((err = snd_pcm_channel_prepare(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
    {
	printf("alsa-resume: channel prepare error: %s\n", snd_strerror(err));
	return;
    }
}

/*
    plays 'len' bytes of 'data'
    returns: number of bytes played
*/
static int play(void* data, int len, int flags)
{
    if ((len = snd_pcm_write(alsa_handler, data, len)) != len)
    {
	if (len == -EPIPE) /* underrun? */
	{
	    printf("alsa-play: alsa underrun, resetting stream\n");
	    if ((len = snd_pcm_channel_prepare(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
	    {
		printf("alsa-play: playback prepare error: %s\n", snd_strerror(len));
		return(0);
	    }
	    if ((len = snd_pcm_write(alsa_handler, data, len)) != len)
	    {
		printf("alsa-play: write error after reset: %s - giving up\n",
		    snd_strerror(len));
		return(0);
	    }
	    return(len); /* 2nd write was ok */
	}
	printf("alsa-play: output error: %s\n", snd_strerror(len));
    }
    return(len);
}

/* how many byes are free in the buffer */
static int get_space()
{
    snd_pcm_channel_status_t ch_stat;
    
    ch_stat.channel = SND_PCM_CHANNEL_PLAYBACK;

    if (snd_pcm_channel_status(alsa_handler, &ch_stat) < 0)
	return(0); /* error occured */
    else
	return(ch_stat.free);
}

/* how many unplayed bytes are in the buffer */
static int get_delay()
{
    snd_pcm_channel_status_t ch_stat;
    
    ch_stat.channel = SND_PCM_CHANNEL_PLAYBACK;
    
    if (snd_pcm_channel_status(alsa_handler, &ch_stat) < 0)
	return(ao_buffersize); /* error occured */
    else
	return(ch_stat.count);
}