[xiph-commits] r16832 - in trunk/ao: . src/plugins src/plugins/alsa
xiphmont at svn.xiph.org
xiphmont at svn.xiph.org
Tue Jan 26 23:40:50 PST 2010
Author: xiphmont
Date: 2010-01-26 23:40:50 -0800 (Tue, 26 Jan 2010)
New Revision: 16832
Added:
trunk/ao/src/plugins/alsa/
trunk/ao/src/plugins/alsa/ao_alsa.c
Removed:
trunk/ao/src/plugins/alsa/ao_alsa09.c
trunk/ao/src/plugins/alsa05/
trunk/ao/src/plugins/alsa09/
Modified:
trunk/ao/configure.ac
trunk/ao/src/plugins/Makefile.am
trunk/ao/src/plugins/alsa/Makefile.am
Log:
Remove old alsa05 driver
rename alsa09 to alsa
update alsa option list
Modified: trunk/ao/configure.ac
===================================================================
--- trunk/ao/configure.ac 2010-01-27 07:25:29 UTC (rev 16831)
+++ trunk/ao/configure.ac 2010-01-27 07:40:50 UTC (rev 16832)
@@ -241,58 +241,39 @@
AM_CONDITIONAL(HAVE_OSS,test "${ac_cv_header_sys_soundcard_h}" = "yes" || test "${ac_cv_header_machine_soundcard_h}" = "yes")
-dnl Check for ALSA 0.5.x
+dnl Check for ALSA 0.9/1.0
-AC_ARG_ENABLE(alsa, [ --enable-alsa include alsa 0.5 output plugin ],
+AC_ARG_ENABLE(alsa, [ --enable-alsa include alsa 0.9/1.0 output plugin ],
[ BUILD_ALSA="$enableval" ],[ BUILD_ALSA="yes" ])
+AC_ARG_ENABLE(alsa-mmap,
+ [ --enable-alsa-mmap use mmio with alsa ],
+ [ BUILD_ALSAMMIO="$enableval" ],[ BUILD_ALSAMMIO="no" ])
if test "$BUILD_ALSA" = "yes"; then
- AC_CHECK_LIB(asound, snd_pcm_channel_params, have_alsa=yes, have_alsa=no)
- AC_CHECK_HEADER(sys/asoundlib.h, , have_alsa=no)
+ AC_CHECK_LIB(asound, snd_pcm_open, have_alsa=yes, have_alsa=no)
+ AC_CHECK_HEADER(alsa/asoundlib.h, , have_alsa=no)
+ if test "$BUILD_ALSAMMIO" = "yes" ; then
+ AC_CHECK_HEADER(sys/mman.h, have_alsammio=yes, have_alsammio=no)
+ fi
+
fi
if test "x$have_alsa" = xyes; then
ALSA_LIBS="-lasound"
+ if test "x$have_alsammio" = xyes; then
+ AC_DEFINE(USE_ALSA_MMIO)
+ fi
else
ALSA_LIBS=""
fi
AM_CONDITIONAL(HAVE_ALSA,test "x$have_alsa" = xyes)
AC_SUBST(ALSA_LIBS)
-
-dnl Check for ALSA 0.9.x
-
-AC_ARG_ENABLE(alsa09, [ --enable-alsa09 include alsa 0.9 output plugin ],
-[ BUILD_ALSA09="$enableval" ],[ BUILD_ALSA09="yes" ])
-AC_ARG_ENABLE(alsa09-mmap,
- [ --enable-alsa09-mmap use mmio with alsa 0.9 (experimental!) ],
- [ BUILD_ALSA09MMIO="$enableval" ],[ BUILD_ALSA09MMIO="no" ])
-
-if test "$BUILD_ALSA09" = "yes"; then
- AC_CHECK_LIB(asound, snd_pcm_open, have_alsa09=yes, have_alsa09=no)
- AC_CHECK_HEADER(alsa/asoundlib.h, , have_alsa09=no)
- if test "$BUILD_ALSA09MMIO" = "yes" ; then
- AC_CHECK_HEADER(sys/mman.h, have_alsa09mmio=yes, have_alsa09mmio=no)
- fi
-
-fi
-
-if test "x$have_alsa09" = xyes; then
- ALSA09_LIBS="-lasound"
- if test "x$have_alsa09mmio" = xyes; then
- AC_DEFINE(USE_ALSA_MMIO)
- fi
-else
- ALSA09_LIBS=""
-fi
-AM_CONDITIONAL(HAVE_ALSA09,test "x$have_alsa09" = xyes)
-AC_SUBST(ALSA09_LIBS)
-
dnl Decide whether we need to enable the workaround for broken OSS APIs
dnl such as the OSS emulation in ALSA.
AC_ARG_ENABLE(broken-oss, [ --enable-broken-oss workaround for some OSS drivers (see README for details)],,
-if test "x$have_alsa" = "xyes" -o "x$have_alsa09" = "xyes"; then
+if test "x$have_alsa" = "xyes" -o "x$have_alsa" = "xyes"; then
enable_broken_oss="yes"
fi)
@@ -421,4 +402,4 @@
AC_SUBST(PLUGIN_LDFLAGS)
-AC_OUTPUT(Makefile src/Makefile doc/Makefile include/Makefile include/ao/Makefile include/ao/os_types.h src/plugins/Makefile src/plugins/esd/Makefile src/plugins/oss/Makefile src/plugins/alsa05/Makefile src/plugins/alsa09/Makefile src/plugins/sun/Makefile src/plugins/irix/Makefile src/plugins/arts/Makefile src/plugins/macosx/Makefile src/plugins/nas/Makefile src/plugins/pulse/Makefile ao.pc)
+AC_OUTPUT(Makefile src/Makefile doc/Makefile include/Makefile include/ao/Makefile include/ao/os_types.h src/plugins/Makefile src/plugins/esd/Makefile src/plugins/oss/Makefile src/plugins/alsa/Makefile src/plugins/sun/Makefile src/plugins/irix/Makefile src/plugins/arts/Makefile src/plugins/macosx/Makefile src/plugins/nas/Makefile src/plugins/pulse/Makefile ao.pc)
Modified: trunk/ao/src/plugins/Makefile.am
===================================================================
--- trunk/ao/src/plugins/Makefile.am 2010-01-27 07:25:29 UTC (rev 16831)
+++ trunk/ao/src/plugins/Makefile.am 2010-01-27 07:40:50 UTC (rev 16832)
@@ -1,4 +1,4 @@
## Process this file with automake to produce Makefile.in
AUTOMAKE_OPTIONS = foreign
-SUBDIRS = oss esd arts alsa05 alsa09 sun irix macosx nas pulse
+SUBDIRS = oss esd arts alsa sun irix macosx nas pulse
Modified: trunk/ao/src/plugins/alsa/Makefile.am
===================================================================
--- trunk/ao/src/plugins/alsa09/Makefile.am 2010-01-24 10:12:03 UTC (rev 16797)
+++ trunk/ao/src/plugins/alsa/Makefile.am 2010-01-27 07:40:50 UTC (rev 16832)
@@ -2,25 +2,25 @@
AUTOMAKE_OPTIONS = foreign
-if HAVE_ALSA09
+if HAVE_ALSA
-alsa09ltlibs = libalsa09.la
-alsa09sources = ao_alsa09.c
+alsaltlibs = libalsa.la
+alsasources = ao_alsa.c
else
-alsa09ltlibs =
-alsa09sources =
+alsaltlibs =
+alsasources =
endif
INCLUDES = -I$(top_builddir)/include/ao -I$(top_srcdir)/include
libdir = $(plugindir)
-lib_LTLIBRARIES = $(alsa09ltlibs)
+lib_LTLIBRARIES = $(alsaltlibs)
-libalsa09_la_LDFLAGS = @PLUGIN_LDFLAGS@
-libalsa09_la_LIBADD = @ALSA09_LIBS@
-libalsa09_la_SOURCES = $(alsa09sources)
+libalsa_la_LDFLAGS = @PLUGIN_LDFLAGS@
+libalsa_la_LIBADD = @ALSA_LIBS@
+libalsa_la_SOURCES = $(alsasources)
-EXTRA_DIST = ao_alsa09.c
+EXTRA_DIST = ao_alsa.c
Copied: trunk/ao/src/plugins/alsa/ao_alsa.c (from rev 16797, trunk/ao/src/plugins/alsa09/ao_alsa09.c)
===================================================================
--- trunk/ao/src/plugins/alsa/ao_alsa.c (rev 0)
+++ trunk/ao/src/plugins/alsa/ao_alsa.c 2010-01-27 07:40:50 UTC (rev 16832)
@@ -0,0 +1,568 @@
+/*
+ *
+ * ao_alsa.c
+ *
+ * Copyright (C) Stan Seibert - July 2000, July 2001
+ * Modifications Copyright (C) Monty - January 2010
+ *
+ * This file is part of libao, a cross-platform library. See
+ * README for a history of this source code.
+ *
+ * libao is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * libao is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Largely rewritten 2/18/2002 Kevin Cody Jr <kevinc at wuff.dhs.org>
+ *
+ */
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include <alsa/asoundlib.h>
+#include <ao/ao.h>
+#include <ao/plugin.h>
+
+/* default 500 millisecond buffer */
+#define AO_ALSA_BUFFER_TIME 500000
+
+/* the period time is calculated if not given as an option */
+#define AO_ALSA_PERIOD_TIME 0
+
+/* number of samples between interrupts
+ * supplying a period_time to ao overrides the use of this */
+#define AO_ALSA_SAMPLE_XFER 256
+
+/* set mmap to default if enabled at compile time, otherwise, mmap isn't
+ the default */
+#ifdef USE_ALSA_MMIO
+#define AO_ALSA_WRITEI snd_pcm_mmap_writei
+#define AO_ALSA_ACCESS_MASK SND_PCM_ACCESS_MMAP_INTERLEAVED
+#else
+#define AO_ALSA_WRITEI snd_pcm_writei
+#define AO_ALSA_ACCESS_MASK SND_PCM_ACCESS_RW_INTERLEAVED
+#endif
+
+typedef snd_pcm_sframes_t ao_alsa_writei_t(snd_pcm_t *pcm, const void *buffer,
+ snd_pcm_uframes_t size);
+
+static char *ao_alsa_options[] = {
+ "dev",
+ "buffer_time",
+ "period_time",
+ "use_mmap",
+ "matrix",
+ "verbose",
+ "quiet"
+};
+
+
+static ao_info ao_alsa_info =
+{
+ AO_TYPE_LIVE,
+ "Advanced Linux Sound Architecture (ALSA) output",
+ "alsa",
+ "Bill Currie <bill at taniwha.org>/Kevin Cody, Jr. <kevinc at wuff.dhs.org>",
+ "Outputs to the Advanced Linux Sound Architecture version 0.9.x/1.x.x.",
+ AO_FMT_NATIVE,
+ 35,
+ ao_alsa_options,
+ 7
+};
+
+
+typedef struct ao_alsa_internal
+{
+ snd_pcm_t *pcm_handle;
+ unsigned int buffer_time;
+ unsigned int period_time;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t period_size;
+ int sample_size;
+ snd_pcm_format_t bitformat;
+ char *dev;
+ char *cmd;
+ ao_alsa_writei_t * writei;
+ snd_pcm_access_t access_mask;
+} ao_alsa_internal;
+
+
+/* determine if parameters are requires for this particular plugin */
+int ao_plugin_test()
+{
+ snd_pcm_t *handle;
+ int err;
+
+ /* Use nonblock flag when testing to avoid getting stuck if the device
+ is in use. Try several devices, as 'default' usually means 'stereo only'. */
+ err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK,
+ SND_PCM_NONBLOCK);
+ if (err != 0)
+ return 0; /* Cannot use this plugin with default parameters */
+ else {
+ snd_pcm_close(handle);
+ return 1; /* This plugin works in default mode */
+ }
+}
+
+
+/* return the address of the driver info structure */
+ao_info *ao_plugin_driver_info(void)
+{
+ return &ao_alsa_info;
+}
+
+
+/* initialize internal data structures */
+int ao_plugin_device_init(ao_device *device)
+{
+ ao_alsa_internal *internal;
+
+ internal = (ao_alsa_internal *) calloc(1,sizeof(ao_alsa_internal));
+
+ if (internal == NULL)
+ return 0;
+
+ internal->buffer_time = AO_ALSA_BUFFER_TIME;
+ internal->period_time = AO_ALSA_PERIOD_TIME;
+ internal->writei = AO_ALSA_WRITEI;
+ internal->access_mask = AO_ALSA_ACCESS_MASK;
+
+ device->internal = internal;
+
+ return 1;
+}
+
+
+/* pass application parameters regarding the sound device */
+int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
+{
+ ao_alsa_internal *internal = (ao_alsa_internal *) device->internal;
+
+ if (!strcmp(key, "dev")) {
+ if (internal->dev)
+ free (internal->dev);
+ if (!(internal->dev = strdup(value)))
+ return 0;
+ }
+ else if (!strcmp(key, "buffer_time"))
+ internal->buffer_time = atoi(value) * 1000;
+ else if (!strcmp(key, "period_time"))
+ internal->period_time = atoi(value);
+ else if (!strcmp(key,"use_mmap")) {
+ if(!strcmp(value,"yes") || !strcmp(value,"y") ||
+ !strcmp(value,"true") || !strcmp(value,"t") ||
+ !strcmp(value,"1"))
+ {
+ internal->writei = snd_pcm_mmap_writei;
+ internal->access_mask = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ }
+ else {
+ internal->writei = snd_pcm_writei;
+ internal->access_mask = SND_PCM_ACCESS_RW_INTERLEAVED;
+ }
+ }
+
+ return 1;
+}
+
+
+/* determine the alsa bitformat for a given bitwidth and endianness */
+static inline int alsa_get_sample_bitformat(int bitwidth, int bigendian)
+{
+ int ret;
+
+ switch (bitwidth) {
+ case 8 : ret = SND_PCM_FORMAT_S8;
+ break;
+ case 16 : ret = SND_PCM_FORMAT_S16;
+ break;
+ case 24 : ret = SND_PCM_FORMAT_S24;
+ break;
+ case 32 : ret = SND_PCM_FORMAT_S32;
+ break;
+ default : fprintf(stderr,"ALSA: invalid bitwidth %d\n", bitwidth);
+ return -1;
+ }
+
+ return ret;
+}
+
+
+/* setup alsa data format and buffer geometry */
+static inline int alsa_set_hwparams(ao_alsa_internal *internal,
+ ao_sample_format *format)
+{
+ snd_pcm_hw_params_t *params;
+ int err;
+ unsigned int rate = format->rate;
+
+ /* allocate the hardware parameter structure */
+ snd_pcm_hw_params_alloca(¶ms);
+
+ /* fetch all possible hardware parameters */
+ internal->cmd = "snd_pcm_hw_params_any";
+ err = snd_pcm_hw_params_any(internal->pcm_handle, params);
+ if (err < 0)
+ return err;
+
+ /* set the access type */
+ internal->cmd = "snd_pcm_hw_params_set_access";
+ err = snd_pcm_hw_params_set_access(internal->pcm_handle,
+ params, internal->access_mask);
+ if (err < 0)
+ return err;
+
+ /* set the sample bitformat */
+ internal->cmd = "snd_pcm_hw_params_set_format";
+ err = snd_pcm_hw_params_set_format(internal->pcm_handle,
+ params, internal->bitformat);
+ if (err < 0)
+ return err;
+
+ /* set the number of channels */
+ internal->cmd = "snd_pcm_hw_params_set_channels";
+ err = snd_pcm_hw_params_set_channels(internal->pcm_handle,
+ params, (unsigned int)format->channels);
+ if (err < 0)
+ return err;
+
+ /* save the sample size in bytes for posterity */
+ internal->sample_size = format->bits * format->channels / 8;
+
+ /* set the sample rate */
+ internal->cmd = "snd_pcm_hw_params_set_rate_near";
+ err = snd_pcm_hw_params_set_rate_near(internal->pcm_handle,
+ params, &rate, 0);
+ if (err < 0)
+ return err;
+ if (rate > 1.05 * format->rate || rate < 0.95 * format->rate) {
+ fprintf(stderr, "warning: sample rate %i not supported "
+ "by the hardware, using %u\n", format->rate, rate);
+ }
+
+ /* set the length of the hardware sample buffer in microseconds */
+ internal->cmd = "snd_pcm_hw_params_set_buffer_time_near";
+ err = snd_pcm_hw_params_set_buffer_time_near(internal->pcm_handle,
+ params, &(internal->buffer_time), 0);
+ if (err < 0)
+ return err;
+
+ /* calculate a period time of one half sample time */
+ if ((internal->period_time == 0) && (rate > 0))
+ internal->period_time =
+ 1000000 * AO_ALSA_SAMPLE_XFER / rate;
+
+ /* set the time per hardware sample transfer */
+ internal->cmd = "snd_pcm_hw_params_set_period_time_near";
+ err = snd_pcm_hw_params_set_period_time_near(internal->pcm_handle,
+ params, &(internal->period_time), 0);
+ if (err < 0)
+ return err;
+
+ /* commit the params structure to the hardware via ALSA */
+ internal->cmd = "snd_pcm_hw_params";
+ err = snd_pcm_hw_params(internal->pcm_handle, params);
+ if (err < 0)
+ return err;
+
+ /* save the period size in frames for posterity */
+ internal->cmd = "snd_pcm_hw_get_period_size";
+ err = snd_pcm_hw_params_get_period_size(params,
+ &(internal->period_size), 0);
+ if (err < 0)
+ return err;
+
+ /* save the buffer size in frames for posterity */
+ internal->cmd = "snd_pcm_hw_get_buffer_size";
+ err = snd_pcm_hw_params_get_buffer_size(params,
+ &(internal->buffer_size));
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+
+/* setup alsa data transfer behavior */
+static inline int alsa_set_swparams(ao_alsa_internal *internal)
+{
+ snd_pcm_sw_params_t *params;
+ int err;
+
+ /* allocate the software parameter structure */
+ snd_pcm_sw_params_alloca(¶ms);
+
+ /* fetch the current software parameters */
+ internal->cmd = "snd_pcm_sw_params_current";
+ err = snd_pcm_sw_params_current(internal->pcm_handle, params);
+ if (err < 0)
+ return err;
+
+ /* allow transfers to start when there is one period */
+ internal->cmd = "snd_pcm_sw_params_set_start_threshold";
+ err = snd_pcm_sw_params_set_start_threshold(internal->pcm_handle,
+ params, internal->period_size);
+ if (err < 0)
+ return err;
+
+ /* require a minimum of one full transfer in the buffer */
+ internal->cmd = "snd_pcm_sw_params_set_avail_min";
+ err = snd_pcm_sw_params_set_avail_min(internal->pcm_handle, params,
+ internal->period_size);
+ if (err < 0)
+ return err;
+
+ /* do not align transfers */
+ internal->cmd = "snd_pcm_sw_params_set_xfer_align";
+ err = snd_pcm_sw_params_set_xfer_align(internal->pcm_handle, params, 1);
+ if (err < 0)
+ return err;
+
+ /* commit the params structure to ALSA */
+ internal->cmd = "snd_pcm_sw_params";
+ err = snd_pcm_sw_params(internal->pcm_handle, params);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+
+/* prepare the audio device for playback */
+int ao_plugin_open(ao_device *device, ao_sample_format *format)
+{
+ ao_alsa_internal *internal = (ao_alsa_internal *) device->internal;
+ int err;
+
+ /* Get the ALSA bitformat first to make sure it's valid */
+ err = alsa_get_sample_bitformat(format->bits,
+ device->client_byte_format == AO_FMT_BIG);
+ if (err < 0)
+ goto error;
+
+ internal->bitformat = err;
+
+ /* Open the ALSA device */
+ internal->cmd = "snd_pcm_open";
+ err=0;
+ if(!internal->dev){
+ char *tmp=NULL;
+ /* we don't try just 'default' as it's a plug device that
+ will accept any number of channels but usually plays back
+ everything as stereo. */
+ switch(format->channels){
+ default:
+ case 8:
+ case 7:
+ err = snd_pcm_open(&(internal->pcm_handle), tmp="surround71",
+ SND_PCM_STREAM_PLAYBACK, 0);
+ break;
+ case 4:
+ case 3:
+ err = snd_pcm_open(&(internal->pcm_handle), tmp="surround40",
+ SND_PCM_STREAM_PLAYBACK, 0);
+ if(err==0)break;
+ case 6:
+ case 5:
+ err = snd_pcm_open(&(internal->pcm_handle), tmp="surround51",
+ SND_PCM_STREAM_PLAYBACK, 0);
+ case 1:
+ case 2:
+ break;
+ }
+
+ if(err){
+ fprintf(stderr,"ERROR: Unable to open ALSA surround device '%s'\n"
+ " trying default device...\n",tmp);
+ tmp=NULL;
+ }
+ if(!tmp)
+ err = snd_pcm_open(&(internal->pcm_handle), tmp="default",
+ SND_PCM_STREAM_PLAYBACK, 0);
+ internal->dev=strdup(tmp);
+
+ }else
+ err = snd_pcm_open(&(internal->pcm_handle), internal->dev,
+ SND_PCM_STREAM_PLAYBACK, 0);
+ if (err < 0) {
+ internal->pcm_handle = NULL;
+ goto error;
+ }
+
+ /* Set up the hardware parameters, ie sample and buffer specs */
+ err = alsa_set_hwparams(internal, format);
+ if (err < 0)
+ goto error;
+
+ /* Set up the software parameters, ie de-buffering specs */
+ err = alsa_set_swparams(internal);
+ if (err < 0)
+ goto error;
+
+ /* alsa's endinness will be the same as the application's */
+ if (format->bits > 8)
+ device->driver_byte_format = device->client_byte_format;
+
+ if(device->verbose>0)
+ fprintf(stderr,"Using alsa device '%s'\n", internal->dev);
+
+ if(!device->output_matrix){
+ if(!strncasecmp(internal->dev,"plug:",5))
+ if(format->channels>2 && device->verbose>=0)
+ fprintf(stderr,"\nWARNING: No way to determine hardware channel mapping of\n"
+ "ALSA 'plug:' devices.\n");
+ if(!strcasecmp(internal->dev,"default")){
+ if(format->channels>2 && device->verbose>=0)
+ fprintf(stderr,"\nWARNING: ALSA 'default' device plays only L,R channels.\n");
+ device->output_matrix=strdup("L,R");
+ }else
+ device->output_matrix=strdup("L,R,BL,BR,C,LFE,SL,SR");
+ }
+
+ return 1;
+
+error:
+ fprintf(stderr, "ALSA %s error: %s\n",
+ internal->cmd, snd_strerror(err));
+ if (internal->pcm_handle) {
+ snd_pcm_close(internal->pcm_handle);
+ internal->pcm_handle = NULL;
+ }
+ return 0;
+}
+
+
+/* recover from an alsa exception */
+static inline int alsa_error_recovery(ao_alsa_internal *internal, int err)
+{
+ if (err == -EPIPE) {
+ /* FIXME: underrun length detection */
+ //fprintf(stderr,"ALSA: underrun, at least %dms.\n", 0);
+ /* output buffer underrun */
+ internal->cmd = "underrun recovery: snd_pcm_prepare";
+ err = snd_pcm_prepare(internal->pcm_handle);
+ if (err < 0)
+ return err;
+ } else if (err == -ESTRPIPE) {
+ /* application was suspended, wait until suspend flag clears */
+ internal->cmd = "suspend recovery: snd_pcm_prepare";
+ while ((err = snd_pcm_resume(internal->pcm_handle)) == -EAGAIN)
+ sleep (1);
+
+ if (err < 0) {
+ /* unable to wake up pcm device, restart it */
+ internal->cmd = "suspend recovery: snd_pcm_prepare";
+ err = snd_pcm_prepare(internal->pcm_handle);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+ }
+
+ /* error isn't recoverable */
+ return err;
+}
+
+
+/* play num_bytes of audio data */
+int ao_plugin_play(ao_device *device, const char *output_samples,
+ uint_32 num_bytes)
+{
+ ao_alsa_internal *internal = (ao_alsa_internal *) device->internal;
+ uint_32 len = num_bytes / internal->sample_size;
+ char *ptr = (char *) output_samples;
+ int err;
+
+ /* the entire buffer might not transfer at once */
+ while (len > 0) {
+ /* try to write the entire buffer at once */
+ err = internal->writei(internal->pcm_handle, ptr, len);
+
+ /* no data transferred or interrupt signal */
+ if (err == -EAGAIN || err == -EINTR) {
+ continue;
+ }
+
+ if (err < 0) {
+ /* this might be an error, or an exception */
+ err = alsa_error_recovery(internal, err);
+ if (err < 0) {
+ fprintf(stderr,"ALSA write error: %s\n",
+ snd_strerror(err));
+ return 0;
+ }else /* recovered, continue */
+ continue;
+ }
+
+ /* decrement the sample counter */
+ len -= err;
+
+ /* adjust the start pointer */
+ ptr += err * internal->sample_size;
+ }
+
+ return 1;
+}
+
+
+/* close the audio device */
+int ao_plugin_close(ao_device *device)
+{
+ ao_alsa_internal *internal;
+
+ if (device) {
+ if ((internal = (ao_alsa_internal *) device->internal)) {
+ if (internal->pcm_handle) {
+ snd_pcm_drain(internal->pcm_handle);
+ snd_pcm_close(internal->pcm_handle);
+ internal->pcm_handle=NULL;
+ } else
+ fprintf(stderr,"ao_plugin_close called with uninitialized ao_device->internal->pcm_handle\n");
+ } else
+ fprintf(stderr,"ao_plugin_close called with uninitialized ao_device->internal\n");
+ } else
+ fprintf(stderr,"ao_plugin_close called with uninitialized ao_device\n");
+
+ return 1;
+}
+
+
+/* free the internal data structures */
+void ao_plugin_device_clear(ao_device *device)
+{
+ ao_alsa_internal *internal;
+
+ if (device) {
+ if ((internal = (ao_alsa_internal *) device->internal)) {
+ if (internal->dev)
+ free (internal->dev);
+ else
+ fprintf(stderr,"ao_plugin_device_clear called with uninitialized ao_device->internal->dev\n");
+ if (internal->cmd)
+ internal->cmd = NULL;
+
+ free(device->internal);
+ } else
+ fprintf(stderr,"ao_plugin_device_clear called with uninitialized ao_device->internal\n");
+ } else
+ fprintf(stderr,"ao_plugin_device_clear called with uninitialized ao_device\n");
+}
+
Deleted: trunk/ao/src/plugins/alsa/ao_alsa09.c
===================================================================
--- trunk/ao/src/plugins/alsa09/ao_alsa09.c 2010-01-24 10:12:03 UTC (rev 16797)
+++ trunk/ao/src/plugins/alsa/ao_alsa09.c 2010-01-27 07:40:50 UTC (rev 16832)
@@ -1,518 +0,0 @@
-/*
- *
- * ao_alsa09.c
- *
- * Copyright (C) Stan Seibert - July 2000, July 2001
- *
- * This file is part of libao, a cross-platform library. See
- * README for a history of this source code.
- *
- * libao is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * libao is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Make; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Largely rewritten 2/18/2002 Kevin Cody Jr <kevinc at wuff.dhs.org>
- *
- */
-
-#define ALSA_PCM_NEW_HW_PARAMS_API
-#define ALSA_PCM_NEW_SW_PARAMS_API
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <string.h>
-
-#include <alsa/asoundlib.h>
-#include <ao/ao.h>
-#include <ao/plugin.h>
-
-/* default 500 millisecond buffer */
-#define AO_ALSA_BUFFER_TIME 500000
-
-/* the period time is calculated if not given as an option */
-#define AO_ALSA_PERIOD_TIME 0
-
-/* number of samples between interrupts
- * supplying a period_time to ao overrides the use of this */
-#define AO_ALSA_SAMPLE_XFER 256
-
-/* set mmap to default if enabled at compile time, otherwise, mmap isn't
- the default */
-#ifdef USE_ALSA_MMIO
-#define AO_ALSA_WRITEI snd_pcm_mmap_writei
-#define AO_ALSA_ACCESS_MASK SND_PCM_ACCESS_MMAP_INTERLEAVED
-#else
-#define AO_ALSA_WRITEI snd_pcm_writei
-#define AO_ALSA_ACCESS_MASK SND_PCM_ACCESS_RW_INTERLEAVED
-#endif
-
-typedef snd_pcm_sframes_t ao_alsa_writei_t(snd_pcm_t *pcm, const void *buffer,
- snd_pcm_uframes_t size);
-
-static char *ao_alsa_options[] = {
- "dev",
- "buffer_time",
- "period_time",
- "use_mmap"
-};
-
-
-static ao_info ao_alsa_info =
-{
- AO_TYPE_LIVE,
- "Advanced Linux Sound Architecture (ALSA) output",
- "alsa",
- "Bill Currie <bill at taniwha.org>/Kevin Cody, Jr. <kevinc at wuff.dhs.org>",
- "Outputs to the Advanced Linux Sound Architecture version 0.9.x.",
- AO_FMT_NATIVE,
- 35,
- ao_alsa_options,
- 3
-};
-
-
-typedef struct ao_alsa_internal
-{
- snd_pcm_t *pcm_handle;
- unsigned int buffer_time;
- unsigned int period_time;
- snd_pcm_uframes_t buffer_size;
- snd_pcm_uframes_t period_size;
- int sample_size;
- snd_pcm_format_t bitformat;
- char *dev;
- char *cmd;
- ao_alsa_writei_t * writei;
- snd_pcm_access_t access_mask;
-} ao_alsa_internal;
-
-
-/* determine if parameters are requires for this particular plugin */
-int ao_plugin_test()
-{
- snd_pcm_t *handle;
- int err;
-
- /* Use nonblock flag when testing to avoid getting stuck if the device
- is in use. */
- err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK,
- SND_PCM_NONBLOCK);
-
- if (err != 0)
- return 0; /* Cannot use this plugin with default parameters */
- else {
- snd_pcm_close(handle);
- return 1; /* This plugin works in default mode */
- }
-}
-
-
-/* return the address of the driver info structure */
-ao_info *ao_plugin_driver_info(void)
-{
- return &ao_alsa_info;
-}
-
-
-/* initialize internal data structures */
-int ao_plugin_device_init(ao_device *device)
-{
- ao_alsa_internal *internal;
-
- internal = (ao_alsa_internal *) malloc(sizeof(ao_alsa_internal));
-
- if (internal == NULL)
- return 0;
-
- internal->buffer_time = AO_ALSA_BUFFER_TIME;
- internal->period_time = AO_ALSA_PERIOD_TIME;
- internal->writei = AO_ALSA_WRITEI;
- internal->access_mask = AO_ALSA_ACCESS_MASK;
-
- if (!(internal->dev = strdup("default"))) {
- free (internal);
- return 0;
- }
-
- device->internal = internal;
-
- return 1;
-}
-
-
-/* pass application parameters regarding the sound device */
-int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
-{
- ao_alsa_internal *internal = (ao_alsa_internal *) device->internal;
-
- if (!strcmp(key, "dev")) {
- if (internal->dev)
- free (internal->dev);
- if (!(internal->dev = strdup(value)))
- return 0;
- }
- else if (!strcmp(key, "buffer_time"))
- internal->buffer_time = atoi(value) * 1000;
- else if (!strcmp(key, "period_time"))
- internal->period_time = atoi(value);
- else if (!strcmp(key,"use_mmap")) {
- if(!strcmp(value,"yes") || !strcmp(value,"y") ||
- !strcmp(value,"true") || !strcmp(value,"t") ||
- !strcmp(value,"1"))
- {
- internal->writei = snd_pcm_mmap_writei;
- internal->access_mask = SND_PCM_ACCESS_MMAP_INTERLEAVED;
- }
- else {
- internal->writei = snd_pcm_writei;
- internal->access_mask = SND_PCM_ACCESS_RW_INTERLEAVED;
- }
- }
-
- return 1;
-}
-
-
-/* determine the alsa bitformat for a given bitwidth and endianness */
-static inline int alsa_get_sample_bitformat(int bitwidth, int bigendian)
-{
- int ret;
-
- switch (bitwidth) {
- case 8 : ret = SND_PCM_FORMAT_S8;
- break;
- case 16 : ret = SND_PCM_FORMAT_S16;
- break;
- case 24 : ret = SND_PCM_FORMAT_S24;
- break;
- case 32 : ret = SND_PCM_FORMAT_S32;
- break;
- default : fprintf(stderr,"ALSA: invalid bitwidth %d\n", bitwidth);
- return -1;
- }
-
- return ret;
-}
-
-
-/* setup alsa data format and buffer geometry */
-static inline int alsa_set_hwparams(ao_alsa_internal *internal,
- ao_sample_format *format)
-{
- snd_pcm_hw_params_t *params;
- int err;
- unsigned int rate = format->rate;
-
- /* allocate the hardware parameter structure */
- snd_pcm_hw_params_alloca(¶ms);
-
- /* fetch all possible hardware parameters */
- internal->cmd = "snd_pcm_hw_params_any";
- err = snd_pcm_hw_params_any(internal->pcm_handle, params);
- if (err < 0)
- return err;
-
- /* set the access type */
- internal->cmd = "snd_pcm_hw_params_set_access";
- err = snd_pcm_hw_params_set_access(internal->pcm_handle,
- params, internal->access_mask);
- if (err < 0)
- return err;
-
- /* set the sample bitformat */
- internal->cmd = "snd_pcm_hw_params_set_format";
- err = snd_pcm_hw_params_set_format(internal->pcm_handle,
- params, internal->bitformat);
- if (err < 0)
- return err;
-
- /* set the number of channels */
- internal->cmd = "snd_pcm_hw_params_set_channels";
- err = snd_pcm_hw_params_set_channels(internal->pcm_handle,
- params, (unsigned int)format->channels);
- if (err < 0)
- return err;
-
- /* save the sample size in bytes for posterity */
- internal->sample_size = format->bits * format->channels / 8;
-
- /* set the sample rate */
- internal->cmd = "snd_pcm_hw_params_set_rate_near";
- err = snd_pcm_hw_params_set_rate_near(internal->pcm_handle,
- params, &rate, 0);
- if (err < 0)
- return err;
- if (rate > 1.05 * format->rate || rate < 0.95 * format->rate) {
- fprintf(stderr, "warning: sample rate %i not supported "
- "by the hardware, using %u\n", format->rate, rate);
- }
-
- /* set the length of the hardware sample buffer in microseconds */
- internal->cmd = "snd_pcm_hw_params_set_buffer_time_near";
- err = snd_pcm_hw_params_set_buffer_time_near(internal->pcm_handle,
- params, &(internal->buffer_time), 0);
- if (err < 0)
- return err;
-
- /* calculate a period time of one half sample time */
- if ((internal->period_time == 0) && (rate > 0))
- internal->period_time =
- 1000000 * AO_ALSA_SAMPLE_XFER / rate;
-
- /* set the time per hardware sample transfer */
- internal->cmd = "snd_pcm_hw_params_set_period_time_near";
- err = snd_pcm_hw_params_set_period_time_near(internal->pcm_handle,
- params, &(internal->period_time), 0);
- if (err < 0)
- return err;
-
- /* commit the params structure to the hardware via ALSA */
- internal->cmd = "snd_pcm_hw_params";
- err = snd_pcm_hw_params(internal->pcm_handle, params);
- if (err < 0)
- return err;
-
- /* save the period size in frames for posterity */
- internal->cmd = "snd_pcm_hw_get_period_size";
- err = snd_pcm_hw_params_get_period_size(params,
- &(internal->period_size), 0);
- if (err < 0)
- return err;
-
- /* save the buffer size in frames for posterity */
- internal->cmd = "snd_pcm_hw_get_buffer_size";
- err = snd_pcm_hw_params_get_buffer_size(params,
- &(internal->buffer_size));
- if (err < 0)
- return err;
-
- return 1;
-}
-
-
-/* setup alsa data transfer behavior */
-static inline int alsa_set_swparams(ao_alsa_internal *internal)
-{
- snd_pcm_sw_params_t *params;
- int err;
-
- /* allocate the software parameter structure */
- snd_pcm_sw_params_alloca(¶ms);
-
- /* fetch the current software parameters */
- internal->cmd = "snd_pcm_sw_params_current";
- err = snd_pcm_sw_params_current(internal->pcm_handle, params);
- if (err < 0)
- return err;
-
- /* allow transfers to start when there is one period */
- internal->cmd = "snd_pcm_sw_params_set_start_threshold";
- err = snd_pcm_sw_params_set_start_threshold(internal->pcm_handle,
- params, internal->period_size);
- if (err < 0)
- return err;
-
- /* require a minimum of one full transfer in the buffer */
- internal->cmd = "snd_pcm_sw_params_set_avail_min";
- err = snd_pcm_sw_params_set_avail_min(internal->pcm_handle, params,
- internal->period_size);
- if (err < 0)
- return err;
-
- /* do not align transfers */
- internal->cmd = "snd_pcm_sw_params_set_xfer_align";
- err = snd_pcm_sw_params_set_xfer_align(internal->pcm_handle, params, 1);
- if (err < 0)
- return err;
-
- /* commit the params structure to ALSA */
- internal->cmd = "snd_pcm_sw_params";
- err = snd_pcm_sw_params(internal->pcm_handle, params);
- if (err < 0)
- return err;
-
- return 1;
-}
-
-
-/* prepare the audio device for playback */
-int ao_plugin_open(ao_device *device, ao_sample_format *format)
-{
- ao_alsa_internal *internal = (ao_alsa_internal *) device->internal;
- int err;
-
- /* Get the ALSA bitformat first to make sure it's valid */
- err = alsa_get_sample_bitformat(format->bits,
- device->client_byte_format == AO_FMT_BIG);
- if (err < 0)
- goto error;
-
- internal->bitformat = err;
-
- /* Open the ALSA device */
- internal->cmd = "snd_pcm_open";
- err = snd_pcm_open(&(internal->pcm_handle), internal->dev,
- SND_PCM_STREAM_PLAYBACK, 0);
- if (err < 0) {
- internal->pcm_handle = NULL;
- goto error;
- }
-
- /* Set up the hardware parameters, ie sample and buffer specs */
- err = alsa_set_hwparams(internal, format);
- if (err < 0)
- goto error;
-
- /* Set up the software parameters, ie de-buffering specs */
- err = alsa_set_swparams(internal);
- if (err < 0)
- goto error;
-
- /* alsa's endinness will be the same as the application's */
- if (format->bits > 8)
- device->driver_byte_format = device->client_byte_format;
-
- return 1;
-
-error:
- fprintf(stderr, "ALSA %s error: %s\n",
- internal->cmd, snd_strerror(err));
- if (internal->pcm_handle) {
- snd_pcm_close(internal->pcm_handle);
- internal->pcm_handle = NULL;
- }
- return 0;
-}
-
-
-/* recover from an alsa exception */
-static inline int alsa_error_recovery(ao_alsa_internal *internal, int err)
-{
- if (err == -EPIPE) {
- /* FIXME: underrun length detection */
- fprintf(stderr,"ALSA: underrun, at least %dms.\n", 0);
- /* output buffer underrun */
- internal->cmd = "underrun recovery: snd_pcm_prepare";
- err = snd_pcm_prepare(internal->pcm_handle);
- if (err < 0)
- return err;
- } else if (err == -ESTRPIPE) {
- /* application was suspended, wait until suspend flag clears */
- internal->cmd = "suspend recovery: snd_pcm_prepare";
- while ((err = snd_pcm_resume(internal->pcm_handle)) == -EAGAIN)
- sleep (1);
-
- if (err < 0) {
- /* unable to wake up pcm device, restart it */
- internal->cmd = "suspend recovery: snd_pcm_prepare";
- err = snd_pcm_prepare(internal->pcm_handle);
- if (err < 0)
- return err;
- }
- return 0;
- }
-
- /* error isn't recoverable */
- return err;
-}
-
-
-/* play num_bytes of audio data */
-int ao_plugin_play(ao_device *device, const char *output_samples,
- uint_32 num_bytes)
-{
- ao_alsa_internal *internal = (ao_alsa_internal *) device->internal;
- uint_32 len = num_bytes / internal->sample_size;
- char *ptr = (char *) output_samples;
- int err;
-
- /* the entire buffer might not transfer at once */
- while (len > 0) {
- /* try to write the entire buffer at once */
- err = internal->writei(internal->pcm_handle, ptr, len);
-
- /* no data transferred or interrupt signal */
- if (err == -EAGAIN || err == -EINTR) {
- continue;
- }
-
- if (err < 0) {
- /* this might be an error, or an exception */
- err = alsa_error_recovery(internal, err);
- if (err < 0) {
- fprintf(stderr,"ALSA write error: %s\n",
- snd_strerror(err));
- return 0;
- }
-
- /* abandon the rest of the buffer */
- break;
- }
-
- /* decrement the sample counter */
- len -= err;
-
- /* adjust the start pointer */
- ptr += err * internal->sample_size;
- }
-
- return 1;
-}
-
-
-/* close the audio device */
-int ao_plugin_close(ao_device *device)
-{
- ao_alsa_internal *internal;
-
- if (device) {
- if ((internal = (ao_alsa_internal *) device->internal)) {
- if (internal->pcm_handle) {
- snd_pcm_drain(internal->pcm_handle);
- snd_pcm_close(internal->pcm_handle);
- internal->pcm_handle=NULL;
- } else
- fprintf(stderr,"ao_plugin_close called with uninitialized ao_device->internal->pcm_handle\n");
- } else
- fprintf(stderr,"ao_plugin_close called with uninitialized ao_device->internal\n");
- } else
- fprintf(stderr,"ao_plugin_close called with uninitialized ao_device\n");
-
- return 1;
-}
-
-
-/* free the internal data structures */
-void ao_plugin_device_clear(ao_device *device)
-{
- ao_alsa_internal *internal;
-
- if (device) {
- if ((internal = (ao_alsa_internal *) device->internal)) {
- if (internal->dev)
- free (internal->dev);
- else
- fprintf(stderr,"ao_plugin_device_clear called with uninitialized ao_device->internal->dev\n");
- if (internal->cmd)
- internal->cmd = NULL;
-
- free(device->internal);
- } else
- fprintf(stderr,"ao_plugin_device_clear called with uninitialized ao_device->internal\n");
- } else
- fprintf(stderr,"ao_plugin_device_clear called with uninitialized ao_device\n");
-}
-
More information about the commits
mailing list