diff --git a/configure.in b/configure.in index b5a6d3f9384649e730cdc3b891db0ada6ac9fede..beaca639f106d514c0155f128689167198ddb315 100644 --- a/configure.in +++ b/configure.in @@ -149,7 +149,7 @@ AC_CHECK_HEADERS(sys/soundcard.h) AC_CHECK_HEADERS(machine/soundcard.h) AM_CONDITIONAL(HAVE_OSS,test "${ac_cv_header_sys_soundcard_h}" = "yes" || test "${ac_cv_header_machine_soundcard_h}" = "yes") -dnl Check for ALSA +dnl Check for ALSA 0.5.x AC_ARG_ENABLE(alsa, [ --enable-alsa include alsa 0.5 output plugin ], [ BUILD_ALSA="$enableval" ],[ BUILD_ALSA="yes" ]) @@ -167,6 +167,24 @@ else fi 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" ]) + +if test "$BUILD_ALSA" = "yes"; then + AC_CHECK_LIB(asound, snd_pcm_open, have_alsa09=yes, have_alsa09=no) + AC_CHECK_HEADER(sys/asoundlib.h, , have_alsa09=no) + AM_CONDITIONAL(HAVE_ALSA09,test "x$have_alsa09" = xyes) +fi + +if test "x$have_alsa09" = xyes; then + ALSA09_LIBS="-lasound" +else + ALSA09_LIBS="" +fi +AC_SUBST(ALSA09_LIBS) + dnl Check for Sun audio AC_CHECK_HEADERS(sys/audioio.h) @@ -202,4 +220,5 @@ AM_CONDITIONAL(HAVE_IRIX,test "x$have_irix" = xyes) AM_CONDITIONAL(HAVE_SOLARIS,test "x$have_solaris" = xyes) -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 debian/Makefile) +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/alsa09/Makefile src/plugins/sun/Makefile src/plugins/irix/Makefile src/plugins/arts/Makefile debian/Makefile) + diff --git a/src/ao_private.h b/src/ao_private.h index b26e72dcce09e1610849148163b3ab895bfbc6d2..591f81557c31f3737121a1bfd5cc1d8a034d5b31 100644 --- a/src/ao_private.h +++ b/src/ao_private.h @@ -42,7 +42,7 @@ #if defined(__OpenBSD__) #define DLOPEN_FLAG RTLD_LAZY #else -#define DLOPEN_FLAG RTLD_NOW +#define DLOPEN_FLAG (RTLD_NOW | RTLD_GLOBAL) #endif /* --- Constants --- */ diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 3b9cd9e1a1ae118098fe46bf1ab284a06152982b..a38c85ee16694af083e99f83e3d2f6738700ac39 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -1,4 +1,4 @@ ## Process this file with automake to produce Makefile.in AUTOMAKE_OPTIONS = foreign -SUBDIRS = oss esd arts alsa sun irix # macosx +SUBDIRS = oss esd arts alsa alsa09 sun irix # macosx diff --git a/src/plugins/alsa09/Makefile.am b/src/plugins/alsa09/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..c1e19f003884406b152e3e4513e294d8bac2b79d --- /dev/null +++ b/src/plugins/alsa09/Makefile.am @@ -0,0 +1,28 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +if HAVE_ALSA09 + +alsa09ltlibs = libalsa09.la +alsa09ldflags = -export-dynamic -avoid-version +alsa09sources = ao_alsa09.c + +else + +alsa09ltlibs = +alsa09ldflags = +alsa09sources = + +endif + +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/include + +libdir = $(plugindir) +lib_LTLIBRARIES = $(alsa09ltlibs) + +libalsa09_la_LDFLAGS = $(alsa09ldflags) +libalsa09_la_LIBADD = @ALSA09_LIBS@ +libalsa09_la_SOURCES = $(alsa09sources) + +EXTRA_DIST = ao_alsa09.c diff --git a/src/plugins/alsa09/ao_alsa09.c b/src/plugins/alsa09/ao_alsa09.c new file mode 100644 index 0000000000000000000000000000000000000000..cc454cbe42d74a10eb2b323395d29403d3a4a2b5 --- /dev/null +++ b/src/plugins/alsa09/ao_alsa09.c @@ -0,0 +1,309 @@ +/* + * + * 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. + * + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/stat.h> +#include <string.h> + +#include <sys/asoundlib.h> +#include <ao/ao.h> +#include <ao/plugin.h> + +#define AO_ALSA_BUF_SIZE 32768 + +static char *ao_alsa_options[] = { + "dev", + "buf_size" +}; +static ao_info ao_alsa_info = +{ + AO_TYPE_LIVE, + "Advanced Linux Sound Architecture (ALSA) output", + "alsa09", + "Bill Currie <bill@taniwha.org>", + "Outputs to the Advanced Linux Sound Architecture version 0.9.x.", + AO_FMT_NATIVE, + 30, + ao_alsa_options, + 2 +}; + + +typedef struct ao_alsa_internal +{ + snd_pcm_t *pcm_handle; + char *buf; + int buf_size; + int buf_end; + int sample_size; + char *dev; +} ao_alsa_internal; + + +int ao_plugin_test() +{ + snd_pcm_t *handle; + int err; + + 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 */ + } +} + + +ao_info *ao_plugin_driver_info(void) +{ + return &ao_alsa_info; +} + + +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; /* Could not initialize device memory */ + + internal->buf_size = AO_ALSA_BUF_SIZE; + internal->dev = strdup ("default"); + if (!internal->dev) { + free (internal); + return 0; + } + + device->internal = internal; + + return 1; /* Memory alloc successful */ +} + + +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); + internal->dev = strdup (value); + if (!internal->dev) + return 0; + } + else if (!strcmp(key, "buf_size")) + internal->buf_size = atoi(value); + + return 1; +} + +int ao_plugin_open(ao_device *device, ao_sample_format *format) +{ + ao_alsa_internal *internal = (ao_alsa_internal *) device->internal; + snd_pcm_hw_params_t *hwparams; + + int err; + int fmt; + char *cmd; + + internal->buf = malloc(internal->buf_size); + internal->buf_end = 0; + if (internal->buf == NULL) + return 0; /* Could not alloc swap buffer */ + + + /* Open the ALSA device */ + err = snd_pcm_open(&(internal->pcm_handle), internal->dev, + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if (err < 0) { + free (internal->buf); + return 0; + } + + snd_pcm_hw_params_alloca(&hwparams); + + cmd = "snd_pcm_hw_params_any"; + err = snd_pcm_hw_params_any(internal->pcm_handle, hwparams); + if (err < 0) + goto error; + + cmd = "snd_pcm_hw_params_set_access"; + err = snd_pcm_hw_params_set_access(internal->pcm_handle, hwparams, + SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) + goto error; + + switch (format->bits) { + case 8 : fmt = SND_PCM_FORMAT_S8; + break; + case 16 : fmt = + device->client_byte_format == AO_FMT_BIG ? + SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE; + device->driver_byte_format = device->client_byte_format; + break; + default : return 0; + } + cmd = "snd_pcm_hw_params_set_format"; + err = snd_pcm_hw_params_set_format(internal->pcm_handle, hwparams, fmt); + if (err < 0) + goto error; + + cmd = "snd_pcm_hw_params_set_channels"; + if (format->channels == 1 || format->channels == 2) + err = snd_pcm_hw_params_set_channels(internal->pcm_handle, + hwparams, format->channels); + else + return 0; + if (err < 0) + goto error; + internal->sample_size = format->bits * format->channels / 8; + + cmd = "snd_pcm_hw_params_set_rate"; + err = snd_pcm_hw_params_set_rate_near(internal->pcm_handle, hwparams, + format->rate, 0); + if (err < 0) + goto error; + + cmd = "snd_pcm_hw_params_set_period_size"; + err = snd_pcm_hw_params_set_period_size(internal->pcm_handle, hwparams, + internal->buf_size / internal->sample_size, 0); + if (err < 0) + goto error; + + cmd = "snd_pcm_hw_params_set_periods"; + err = snd_pcm_hw_params_set_periods(internal->pcm_handle, hwparams, + 2, 0); + if (err < 0) + goto error; + + cmd = "snd_pcm_hw_params"; + err = snd_pcm_hw_params(internal->pcm_handle, hwparams); + if (err < 0) + goto error; + + cmd = "snd_pcm_prepare"; + err = snd_pcm_prepare(internal->pcm_handle); + if (err < 0) + goto error; + + return 1; +error: + fprintf(stderr, "ALSA %s error: %s\n", cmd, snd_strerror(err)); + snd_pcm_close(internal->pcm_handle); + free(internal->buf); + return 0; +} + + +int _alsa_write_buffer(ao_alsa_internal *s) +{ + snd_pcm_t *pcm_handle = s->pcm_handle; + int len = s->buf_end / s->sample_size; + int err; + char *buf = s->buf; + + s->buf_end = 0; + + do { + err = snd_pcm_writei (pcm_handle, buf, len); + if (err == -EAGAIN) + snd_pcm_wait (pcm_handle, 1000); + } while (err == -EAGAIN); + if (err == -EPIPE) { + /* fprintf(stderr, "ALSA: underrun. resetting stream\n"); */ + snd_pcm_prepare(pcm_handle); + err = snd_pcm_writei(pcm_handle, buf, len); + if (err != len) { + fprintf(stderr, "ALSA write error: %s\n", snd_strerror(err)); + return 0; + } else if (err < 0) { + fprintf(stderr, "ALSA write error: %s\n", snd_strerror(err)); + return 0; + } + } + + return 1; +} + + +int ao_plugin_play(ao_device *device, const char *output_samples, + uint_32 num_bytes) +{ + ao_alsa_internal *internal = (ao_alsa_internal *) device->internal; + + int packed = 0; + int copy_len; + char *samples = (char *) output_samples; + int ok = 1; + + while (packed < num_bytes && ok) { + /* Pack the buffer */ + if (num_bytes-packed < internal->buf_size - internal->buf_end) + copy_len = num_bytes - packed; + else + copy_len = internal->buf_size - internal->buf_end; + + memcpy(internal->buf + internal->buf_end, samples + packed, + copy_len); + packed += copy_len; + internal->buf_end += copy_len; + + if(internal->buf_end == internal->buf_size) + ok = _alsa_write_buffer(internal); + } + + return ok; +} + + +int ao_plugin_close(ao_device *device) +{ + ao_alsa_internal *internal = (ao_alsa_internal *) device->internal; + int result; + + /* Clear buffer */ + result = _alsa_write_buffer(internal); + snd_pcm_close(internal->pcm_handle); + free(internal->buf); + + return result; +} + + +void ao_plugin_device_clear(ao_device *device) +{ + ao_alsa_internal *internal = (ao_alsa_internal *) device->internal; + if (internal->dev) + free (internal->dev); + free(internal); +}