[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] move audio source out of the modules directory
From: |
William Hubbs |
Subject: |
[PATCH] move audio source out of the modules directory |
Date: |
Wed, 23 Feb 2011 10:46:52 -0600 |
Since the audio plugins are independent of the modules now, we can move
the audio source directory outside of the modules directory.
---
configure.ac | 12 +-
src/Makefile.am | 2 +-
src/audio/.gitignore | 1 +
src/audio/Makefile.am | 45 ++
src/audio/alsa.c | 883 +++++++++++++++++++++++++++++++++++++++++
src/audio/libao.c | 246 ++++++++++++
src/audio/nas.c | 263 ++++++++++++
src/audio/oss.c | 523 ++++++++++++++++++++++++
src/audio/pulse.c | 310 +++++++++++++++
src/modules/Makefile.am | 2 -
src/modules/audio/.gitignore | 1 -
src/modules/audio/Makefile.am | 45 --
src/modules/audio/alsa.c | 883 -----------------------------------------
src/modules/audio/libao.c | 246 ------------
src/modules/audio/nas.c | 263 ------------
src/modules/audio/oss.c | 523 ------------------------
src/modules/audio/pulse.c | 310 ---------------
17 files changed, 2278 insertions(+), 2280 deletions(-)
create mode 100644 src/audio/.gitignore
create mode 100644 src/audio/Makefile.am
create mode 100644 src/audio/alsa.c
create mode 100644 src/audio/libao.c
create mode 100644 src/audio/nas.c
create mode 100644 src/audio/oss.c
create mode 100644 src/audio/pulse.c
delete mode 100644 src/modules/audio/.gitignore
delete mode 100644 src/modules/audio/Makefile.am
delete mode 100644 src/modules/audio/alsa.c
delete mode 100644 src/modules/audio/libao.c
delete mode 100644 src/modules/audio/nas.c
delete mode 100644 src/modules/audio/oss.c
delete mode 100644 src/modules/audio/pulse.c
diff --git a/configure.ac b/configure.ac
index 079de31..7c67ef3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -212,7 +212,7 @@ AS_IF([test $with_pulse != "no"],
[with_pulse=yes
AS_IF([test -z "$default_audio_method"],
[default_audio_method=pulse])
- audio_dlopen_modules="$audio_dlopen_modules -dlopen
audio/spd_pulse.la"],
+ audio_dlopen_modules="$audio_dlopen_modules -dlopen
../audio/spd_pulse.la"],
[AS_IF([test $with_pulse = "yes"],
[AC_MSG_FAILURE([pulseaudio is not available])])])])
AM_CONDITIONAL([pulse_support], [test $with_pulse = "yes"])
@@ -229,7 +229,7 @@ AS_IF([test $with_alsa != "no"],
[with_alsa=yes
AS_IF([test -z "$default_audio_method"],
[default_audio_method=alsa])
- audio_dlopen_modules="$audio_dlopen_modules -dlopen
audio/spd_alsa.la"],
+ audio_dlopen_modules="$audio_dlopen_modules -dlopen
../audio/spd_alsa.la"],
[AS_IF([test $with_alsa = "yes"],
[AC_MSG_FAILURE([ALSA is not available])])])])
AM_CONDITIONAL([alsa_support], [test $with_alsa = "yes"])
@@ -246,7 +246,7 @@ AS_IF([test $with_libao != "no"],
[with_libao=yes
AS_IF([test -z "$default_audio_method"],
[default_audio_method=libao])
- audio_dlopen_modules="$audio_dlopen_modules -dlopen
audio/spd_libao.la"],
+ audio_dlopen_modules="$audio_dlopen_modules -dlopen
../audio/spd_libao.la"],
[AS_IF([test $with_libao = yes],
[AC_MSG_FAILURE([libao is not available])])])])
AM_CONDITIONAL([libao_support], [test $with_libao = "yes"])
@@ -263,7 +263,7 @@ AS_IF([test $with_oss != "no"],
[with_oss=yes
AS_IF([test -z "$default_audio_method"],
[default_audio_method=oss])
- audio_dlopen_modules="$audio_dlopen_modules -dlopen
audio/spd_oss.la"],
+ audio_dlopen_modules="$audio_dlopen_modules -dlopen
../audio/spd_oss.la"],
[AS_IF([test $with_oss = "yes"],
[AC_MSG_FAILURE([oss is not available])])])])
AM_CONDITIONAL([oss_support], [test $with_oss = "yes"])
@@ -278,7 +278,7 @@ AS_IF([test $with_nas != "no"],
[with_nas=yes
AS_IF([test -z "$default_audio_method"],
[default_audio_method=nas])
- audio_dlopen_modules="$audio_dlopen_modules -dlopen
audio/spd_nas.la";
+ audio_dlopen_modules="$audio_dlopen_modules -dlopen
../../audio/spd_nas.la";
NAS_LIBS="-L/usr/X11R6/lib -lXau -laudio"],
[AS_IF([test $with_nas = "yes"],
[AC_MSG_FAILURE([nas is not available])])],
@@ -361,12 +361,12 @@ AC_CONFIG_FILES([Makefile
src/api/python/Makefile
src/api/python/speechd/Makefile
src/api/python/speechd_config/Makefile
+ src/audio/Makefile
src/clients/Makefile
src/clients/say/Makefile
src/clients/spdsend/Makefile
src/common/Makefile
src/modules/Makefile
- src/modules/audio/Makefile
src/server/Makefile
src/tests/Makefile])
AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index f2da76d..81d0690 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,4 +1,4 @@
## Process this file with automake to produce Makefile.in
-SUBDIRS=common server modules api clients tests
+SUBDIRS=common server audio modules api clients tests
diff --git a/src/audio/.gitignore b/src/audio/.gitignore
new file mode 100644
index 0000000..f75f98e
--- /dev/null
+++ b/src/audio/.gitignore
@@ -0,0 +1 @@
+static_plugins.c
diff --git a/src/audio/Makefile.am b/src/audio/Makefile.am
new file mode 100644
index 0000000..d84fcdb
--- /dev/null
+++ b/src/audio/Makefile.am
@@ -0,0 +1,45 @@
+## Process this file with automake to produce Makefile.in
+
+inc_local = -I$(top_srcdir)/include/
+
+audio_LTLIBRARIES =
+
+if alsa_support
+audio_LTLIBRARIES += spd_alsa.la
+spd_alsa_la_SOURCES = alsa.c
+spd_alsa_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local) $(ALSA_CFLAGS)
+spd_alsa_la_LIBADD = $(ALSA_LIBS) $(GLIB_LIBS)
+spd_alsa_la_LDFLAGS = -module -avoid-version
+endif
+
+if libao_support
+audio_LTLIBRARIES += spd_libao.la
+spd_libao_la_SOURCES = libao.c
+spd_libao_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local) $(LIBAO_CFLAGS)
+spd_libao_la_LIBADD = $(LIBAO_LIBS) $(GLIB_LIBS)
+spd_libao_la_LDFLAGS = -module -avoid-version
+endif
+
+if nas_support
+audio_LTLIBRARIES += spd_nas.la
+spd_nas_la_SOURCES = nas.c
+spd_nas_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local)
+spd_nas_la_LIBADD = $(NAS_LIBS) $(GLIB_LIBS)
+spd_nas_la_LDFLAGS = -module -avoid-version
+endif
+
+if oss_support
+audio_LTLIBRARIES += spd_oss.la
+spd_oss_la_SOURCES = oss.c
+spd_oss_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local)
+spd_oss_la_LIBADD = $(GLIB_LIBS)
+spd_oss_la_LDFLAGS = -module -avoid-version
+endif
+
+if pulse_support
+audio_LTLIBRARIES += spd_pulse.la
+spd_pulse_la_SOURCES = pulse.c
+spd_pulse_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local) $(PULSE_CFLAGS)
+spd_pulse_la_LIBADD = $(PULSE_LIBS) $(GLIB_LIBS)
+spd_pulse_la_LDFLAGS = -module -avoid-version
+endif
diff --git a/src/audio/alsa.c b/src/audio/alsa.c
new file mode 100644
index 0000000..4e20a54
--- /dev/null
+++ b/src/audio/alsa.c
@@ -0,0 +1,883 @@
+
+/*
+ * alsa.c -- The Advanced Linux Sound System backend for Speech Dispatcher
+ *
+ * Copyright (C) 2005,2006 Brailcom, o.p.s.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1, or (at your option) any later
+ * version.
+ *
+ * This software 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 Lesser General Public License
+ * along with this package; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * $Id: alsa.c,v 1.30 2008-10-15 17:27:32 hanke Exp $
+ */
+
+/* NOTE: This module uses the non-blocking write() / poll() approach to
+ alsa-lib functions.*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <pthread.h>
+#include <glib.h>
+
+#include <alsa/asoundlib.h>
+
+#define SPD_AUDIO_PLUGIN_ENTRY spd_alsa_LTX_spd_audio_plugin_get
+#include <spd_audio_plugin.h>
+
+typedef struct {
+ AudioID id;
+ snd_pcm_t *alsa_pcm; /* identifier of the ALSA device */
+ snd_pcm_hw_params_t *alsa_hw_params; /* parameters of sound */
+ snd_pcm_sw_params_t *alsa_sw_params; /* parameters of playback */
+ snd_pcm_uframes_t alsa_buffer_size;
+ pthread_mutex_t alsa_pcm_mutex; /* mutex to guard the state of the
device */
+ pthread_mutex_t alsa_pipe_mutex; /* mutex to guard the stop
pipes */
+ int alsa_stop_pipe[2]; /* Pipe for communication about stop requests */
+ int alsa_fd_count; /* Counter of descriptors to poll */
+ struct pollfd *alsa_poll_fds; /* Descriptors to poll */
+ int alsa_opened; /* 1 between snd_pcm_open and _close, 0
otherwise */
+ char *alsa_device_name; /* the name of the device to open */
+} spd_alsa_id_t;
+
+static int _alsa_close(spd_alsa_id_t * id);
+static int _alsa_open(spd_alsa_id_t * id);
+
+static int xrun(spd_alsa_id_t * id);
+static int suspend(spd_alsa_id_t * id);
+
+static int wait_for_poll(spd_alsa_id_t * id, struct pollfd *alsa_poll_fds,
+ unsigned int count, int draining);
+
+#ifndef timersub
+#define timersub(a, b, result) \
+do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif
+
+/* Put a message into the logfile (stderr) */
+#define MSG(level, arg...) \
+ if(level <= alsa_log_level){ \
+ time_t t; \
+ struct timeval tv; \
+ char *tstr; \
+ t = time(NULL); \
+ tstr = g_strdup(ctime(&t)); \
+ tstr[strlen(tstr)-1] = 0; \
+ gettimeofday(&tv,NULL); \
+ fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
+ fprintf(stderr," ALSA: "); \
+ fprintf(stderr,arg); \
+ fprintf(stderr,"\n"); \
+ fflush(stderr); \
+ g_free(tstr); \
+ }
+
+#define ERR(arg...) \
+ { \
+ time_t t; \
+ struct timeval tv; \
+ char *tstr; \
+ t = time(NULL); \
+ tstr = g_strdup(ctime(&t)); \
+ tstr[strlen(tstr)-1] = 0; \
+ gettimeofday(&tv,NULL); \
+ fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
+ fprintf(stderr," ALSA ERROR: "); \
+ fprintf(stderr,arg); \
+ fprintf(stderr,"\n"); \
+ fflush(stderr); \
+ g_free(tstr); \
+ }
+
+static int alsa_log_level;
+static char const *alsa_play_cmd = "aplay";
+
+/* I/O error handler */
+static int xrun(spd_alsa_id_t * id)
+{
+ snd_pcm_status_t *status;
+ int res;
+
+ if (id == NULL)
+ return -1;
+
+ MSG(1, "WARNING: Entering XRUN handler");
+
+ snd_pcm_status_alloca(&status);
+ if ((res = snd_pcm_status(id->alsa_pcm, status)) < 0) {
+ ERR("status error: %s", snd_strerror(res));
+
+ return -1;
+ }
+ if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
+ struct timeval now, diff, tstamp;
+ gettimeofday(&now, 0);
+ snd_pcm_status_get_trigger_tstamp(status, &tstamp);
+ timersub(&now, &tstamp, &diff);
+ MSG(1, "underrun!!! (at least %.3f ms long)",
+ diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
+ if ((res = snd_pcm_prepare(id->alsa_pcm)) < 0) {
+ ERR("xrun: prepare error: %s", snd_strerror(res));
+
+ return -1;
+ }
+
+ return 0; /* ok, data should be accepted again */
+ }
+ ERR("read/write error, state = %s",
+ snd_pcm_state_name(snd_pcm_status_get_state(status)));
+
+ return -1;
+}
+
+/* I/O suspend handler */
+static int suspend(spd_alsa_id_t * id)
+{
+ int res;
+
+ MSG(1, "WARNING: Entering SUSPEND handler.");
+
+ if (id == NULL)
+ return -1;
+
+ while ((res = snd_pcm_resume(id->alsa_pcm)) == -EAGAIN)
+ sleep(1); /* wait until suspend flag is released */
+
+ if (res < 0) {
+ if ((res = snd_pcm_prepare(id->alsa_pcm)) < 0) {
+ ERR("suspend: prepare error: %s", snd_strerror(res));
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Open the device so that it's ready for playing on the default
+ device. Internal function used by the public alsa_open. */
+static int _alsa_open(spd_alsa_id_t * id)
+{
+ int err;
+
+ MSG(1, "Opening ALSA device");
+ fflush(stderr);
+
+ /* Open the device */
+ if ((err = snd_pcm_open(&id->alsa_pcm, id->alsa_device_name,
+ SND_PCM_STREAM_PLAYBACK,
+ SND_PCM_NONBLOCK)) < 0) {
+ ERR("Cannot open audio device %s (%s)", id->alsa_device_name,
+ snd_strerror(err));
+ return -1;
+ }
+
+ /* Allocate space for hw_params (description of the sound parameters) */
+ /* Allocate space for sw_params (description of the sound parameters) */
+ MSG(2, "Allocating new sw_params structure");
+ if ((err = snd_pcm_sw_params_malloc(&id->alsa_sw_params)) < 0) {
+ ERR("Cannot allocate hardware parameter structure (%s)",
+ snd_strerror(err));
+ return -1;
+ }
+
+ MSG(1, "Opening ALSA device ... success");
+
+ return 0;
+}
+
+/*
+ Close the device. Internal function used by public alsa_close.
+*/
+
+static int _alsa_close(spd_alsa_id_t * id)
+{
+ int err;
+
+ MSG(1, "Closing ALSA device");
+
+ if (id->alsa_opened == 0)
+ return 0;
+
+ pthread_mutex_lock(&id->alsa_pipe_mutex);
+ id->alsa_opened = 0;
+
+ if ((err = snd_pcm_close(id->alsa_pcm)) < 0) {
+ MSG(2, "Cannot close ALSA device (%s)", snd_strerror(err));
+ return -1;
+ }
+
+ snd_pcm_sw_params_free(id->alsa_sw_params);
+
+ g_free(id->alsa_poll_fds);
+ pthread_mutex_unlock(&id->alsa_pipe_mutex);
+
+ MSG(1, "Closing ALSA device ... success");
+
+ return 0;
+}
+
+/* Open ALSA for playback.
+
+ These parameters are passed in pars:
+ (char*) pars[0] ... null-terminated string containing the name
+ of the device to be used for sound output
+ on ALSA
+ (void*) pars[1] ... =NULL
+*/
+static AudioID *alsa_open(void **pars)
+{
+ spd_alsa_id_t *alsa_id;
+ int ret;
+
+ if (pars[1] == NULL) {
+ ERR("Can't open ALSA sound output, missing parameters in
argument.");
+ return NULL;
+ }
+
+ alsa_id = (spd_alsa_id_t *) g_malloc(sizeof(spd_alsa_id_t));
+
+ pthread_mutex_init(&alsa_id->alsa_pipe_mutex, NULL);
+
+ alsa_id->alsa_opened = 0;
+
+ MSG(1, "Opening ALSA sound output");
+
+ alsa_id->alsa_device_name = g_strdup(pars[1]);
+
+ ret = _alsa_open(alsa_id);
+ if (ret) {
+ ERR("Cannot initialize Alsa device '%s': Can't open.",
+ alsa_id->alsa_device_name);
+ g_free(alsa_id);
+ return NULL;
+ }
+
+ MSG(1, "Device '%s' initialized succesfully.",
+ alsa_id->alsa_device_name);
+
+ return (AudioID *) alsa_id;
+}
+
+/* Close ALSA */
+static int alsa_close(AudioID * id)
+{
+ int err;
+ spd_alsa_id_t *alsa_id = (spd_alsa_id_t *) id;
+
+ /* Close device */
+ if ((err = _alsa_close(alsa_id)) < 0) {
+ ERR("Cannot close audio device");
+ return -1;
+ }
+ MSG(1, "ALSA closed.");
+
+ g_free(alsa_id->alsa_device_name);
+ g_free(alsa_id);
+ id = NULL;
+
+ return 0;
+}
+
+/* Wait until ALSA is readdy for more samples or alsa_stop() was called.
+
+Returns 0 if ALSA is ready for more input, +1 if a request to stop
+the sound output was received and a negative value on error. */
+
+int wait_for_poll(spd_alsa_id_t * id, struct pollfd *alsa_poll_fds,
+ unsigned int count, int draining)
+{
+ unsigned short revents;
+ snd_pcm_state_t state;
+ int ret;
+
+ // MSG("Waiting for poll");
+
+ /* Wait for certain events */
+ while (1) {
+ ret = poll(id->alsa_poll_fds, count, -1);
+ // MSG("wait_for_poll: activity on %d descriptors", ret);
+
+ /* Check for stop request from alsa_stop on the last file
+ descriptors */
+ revents = id->alsa_poll_fds[count - 1].revents;
+ if (0 != revents) {
+ if (revents & POLLIN) {
+ MSG(4, "wait_for_poll: stop requested");
+ return 1;
+ }
+ }
+
+ /* Check the first count-1 descriptors for ALSA events */
+ snd_pcm_poll_descriptors_revents(id->alsa_pcm,
+ id->alsa_poll_fds, count - 1,
+ &revents);
+
+ /* Ensure we are in the right state */
+ state = snd_pcm_state(id->alsa_pcm);
+ // MSG("State after poll returned is %s",
snd_pcm_state_name(state));
+
+ if (SND_PCM_STATE_XRUN == state) {
+ if (!draining) {
+ MSG(1, "WARNING: Buffer underrun detected!");
+ if (xrun(id) != 0)
+ return -1;
+ return 0;
+ } else {
+ MSG(4, "Poll: Playback terminated");
+ return 0;
+ }
+ }
+
+ if (SND_PCM_STATE_SUSPENDED == state) {
+ MSG(1, "WARNING: Suspend detected!");
+ if (suspend(id) != 0)
+ return -1;
+ return 0;
+ }
+
+ /* Check for errors */
+ if (revents & POLLERR) {
+ MSG(4, "wait_for_poll: poll revents says POLLERR");
+ return -EIO;
+ }
+
+ /* Is ALSA ready for more input? */
+ if ((revents & POLLOUT)) {
+ // MSG("Poll: Ready for more input");
+ return 0;
+ }
+ }
+}
+
+#define ERROR_EXIT()\
+ g_free(track_volume.samples); \
+ ERR("alsa_play() abnormal exit"); \
+ _alsa_close(alsa_id); \
+ return -1;
+
+/* Play the track _track_ (see spd_audio.h) using the id->alsa_pcm device and
+ id-hw_params parameters. This is a blocking function, however, it's possible
+ to interrupt playing from a different thread with alsa_stop(). alsa_play
+ returns after and immediatelly after the whole sound was played on the
+ speakers.
+
+ The idea is that we get the ALSA file descriptors and we will poll() to see
+ when alsa is ready for more input while sleeping in the meantime. We will
+ additionally poll() for one more descriptor used by alsa_stop() to notify the
+ thread with alsa_play() that the stop of the playback is requested. The
+ variable can_be_stopped is used for very simple synchronization between the
+ two threads. */
+static int alsa_play(AudioID * id, AudioTrack track)
+{
+ snd_pcm_format_t format;
+ int bytes_per_sample;
+ int num_bytes;
+ spd_alsa_id_t *alsa_id = (spd_alsa_id_t *) id;
+
+ signed short *output_samples;
+
+ AudioTrack track_volume;
+ float real_volume;
+ int i;
+
+ int err;
+ int ret;
+
+ snd_pcm_uframes_t framecount;
+ snd_pcm_uframes_t period_size;
+ size_t samples_per_period;
+ size_t silent_samples;
+ size_t volume_size;
+ unsigned int sr;
+
+ snd_pcm_state_t state;
+
+ struct pollfd alsa_stop_pipe_pfd;
+
+ if (alsa_id == NULL) {
+ ERR("Invalid device passed to alsa_play()");
+ return -1;
+ }
+
+ pthread_mutex_lock(&alsa_id->alsa_pipe_mutex);
+
+ MSG(2, "Start of playback on ALSA");
+
+ /* Is it not an empty track? */
+ /* Passing an empty track is not an error */
+ if (track.samples == NULL) {
+ pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
+ return 0;
+ }
+ /* Allocate space for hw_params (description of the sound parameters) */
+ MSG(2, "Allocating new hw_params structure");
+ if ((err = snd_pcm_hw_params_malloc(&alsa_id->alsa_hw_params)) < 0) {
+ ERR("Cannot allocate hardware parameter structure (%s)",
+ snd_strerror(err));
+ pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
+ return -1;
+ }
+
+ /* Initialize hw_params on our pcm */
+ if ((err =
+ snd_pcm_hw_params_any(alsa_id->alsa_pcm,
+ alsa_id->alsa_hw_params)) < 0) {
+ ERR("Cannot initialize hardware parameter structure (%s)",
+ snd_strerror(err));
+ pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
+ return -1;
+ }
+
+ /* Create the pipe for communication about stop requests */
+ if (pipe(alsa_id->alsa_stop_pipe)) {
+ ERR("Stop pipe creation failed (%s)", strerror(errno));
+ pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
+ return -1;
+ }
+
+ /* Find how many descriptors we will get for poll() */
+ alsa_id->alsa_fd_count =
+ snd_pcm_poll_descriptors_count(alsa_id->alsa_pcm);
+ if (alsa_id->alsa_fd_count <= 0) {
+ ERR("Invalid poll descriptors count returned from ALSA.");
+ pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
+ return -1;
+ }
+
+ /* Create and fill in struct pollfd *alsa_poll_fds with ALSA
descriptors */
+ alsa_id->alsa_poll_fds =
+ g_malloc((alsa_id->alsa_fd_count + 1) * sizeof(struct pollfd));
+ assert(alsa_id->alsa_poll_fds);
+ if ((err =
+ snd_pcm_poll_descriptors(alsa_id->alsa_pcm, alsa_id->alsa_poll_fds,
+ alsa_id->alsa_fd_count)) < 0) {
+ ERR("Unable to obtain poll descriptors for playback: %s\n",
+ snd_strerror(err));
+ pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
+ return -1;
+ }
+
+ /* Create a new pollfd structure for requests by alsa_stop() */
+ alsa_stop_pipe_pfd.fd = alsa_id->alsa_stop_pipe[0];
+ alsa_stop_pipe_pfd.events = POLLIN;
+ alsa_stop_pipe_pfd.revents = 0;
+
+ /* Join this our own pollfd to the ALSAs ones */
+ alsa_id->alsa_poll_fds[alsa_id->alsa_fd_count] = alsa_stop_pipe_pfd;
+ alsa_id->alsa_fd_count++;
+
+ alsa_id->alsa_opened = 1;
+ pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
+
+ /* Report current state */
+ state = snd_pcm_state(alsa_id->alsa_pcm);
+ MSG(4, "PCM state before setting audio parameters: %s",
+ snd_pcm_state_name(state));
+
+ /* Choose the correct format */
+ if (track.bits == 16) {
+ switch (alsa_id->id.format) {
+ case SPD_AUDIO_LE:
+ format = SND_PCM_FORMAT_S16_LE;
+ break;
+ case SPD_AUDIO_BE:
+ format = SND_PCM_FORMAT_S16_BE;
+ break;
+ default:
+ ERR("unknown audio format (%d)", alsa_id->id.format);
+ return -1;
+ }
+ bytes_per_sample = 2;
+ } else if (track.bits == 8) {
+ bytes_per_sample = 1;
+ format = SND_PCM_FORMAT_S8;
+ } else {
+ ERR("Unsupported sound data format, track.bits = %d",
+ track.bits);
+ return -1;
+ }
+
+ /* Set access mode, bitrate, sample rate and channels */
+ MSG(4, "Setting access type to INTERLEAVED");
+ if ((err = snd_pcm_hw_params_set_access(alsa_id->alsa_pcm,
+ alsa_id->alsa_hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED)
+ ) < 0) {
+ ERR("Cannot set access type (%s)", snd_strerror(err));
+ return -1;
+ }
+
+ MSG(4, "Setting sample format to %s", snd_pcm_format_name(format));
+ if ((err =
+ snd_pcm_hw_params_set_format(alsa_id->alsa_pcm,
+ alsa_id->alsa_hw_params,
+ format)) < 0) {
+ ERR("Cannot set sample format (%s)", snd_strerror(err));
+ return -1;
+ }
+
+ MSG(4, "Setting sample rate to %i", track.sample_rate);
+ sr = track.sample_rate;
+ if ((err =
+ snd_pcm_hw_params_set_rate_near(alsa_id->alsa_pcm,
+ alsa_id->alsa_hw_params, &sr,
+ 0)) < 0) {
+ ERR("Cannot set sample rate (%s)", snd_strerror(err));
+
+ return -1;
+ }
+
+ MSG(4, "Setting channel count to %i", track.num_channels);
+ if ((err =
+ snd_pcm_hw_params_set_channels(alsa_id->alsa_pcm,
+ alsa_id->alsa_hw_params,
+ track.num_channels)) < 0) {
+ MSG(4, "cannot set channel count (%s)", snd_strerror(err));
+ return -1;
+ }
+
+ MSG(4, "Setting hardware parameters on the ALSA device");
+ if ((err =
+ snd_pcm_hw_params(alsa_id->alsa_pcm,
+ alsa_id->alsa_hw_params)) < 0) {
+ MSG(4, "cannot set parameters (%s) state=%s", snd_strerror(err),
+ snd_pcm_state_name(snd_pcm_state(alsa_id->alsa_pcm)));
+ return -1;
+ }
+
+ /* Get the current swparams */
+ if ((err =
+ snd_pcm_sw_params_current(alsa_id->alsa_pcm,
+ alsa_id->alsa_sw_params)) < 0) {
+ ERR("Unable to determine current swparams for playback: %s\n",
+ snd_strerror(err));
+ return -1;
+ }
+ // MSG("Checking buffer size");
+ if ((err =
+ snd_pcm_hw_params_get_buffer_size(alsa_id->alsa_hw_params,
+ &(alsa_id->alsa_buffer_size))) <
+ 0) {
+ ERR("Unable to get buffer size for playback: %s\n",
+ snd_strerror(err));
+ return -1;
+ }
+ MSG(4, "Buffer size on ALSA device is %d bytes",
+ (int)alsa_id->alsa_buffer_size);
+
+ /* This is probably better left for the device driver to decide */
+ /* allow the transfer when at least period_size samples can be
processed */
+ /* err = snd_pcm_sw_params_set_avail_min(id->alsa_pcm,
id->alsa_sw_params, id->alsa_buffer_size/4);
+ if (err < 0) {
+ ERR("Unable to set avail min for playback: %s\n", snd_strerror(err));
+ return err;
+ } */
+
+ /* Get period size. */
+ snd_pcm_hw_params_get_period_size(alsa_id->alsa_hw_params, &period_size,
+ 0);
+
+ /* Calculate size of silence at end of buffer. */
+ samples_per_period = period_size * track.num_channels;
+ // MSG("samples per period = %i", samples_per_period);
+ // MSG("num_samples = %i", track.num_samples);
+ silent_samples =
+ samples_per_period - (track.num_samples % samples_per_period);
+ // MSG("silent samples = %i", silent_samples);
+
+ MSG(4, "Preparing device for playback");
+ if ((err = snd_pcm_prepare(alsa_id->alsa_pcm)) < 0) {
+ ERR("Cannot prepare audio interface for playback (%s)",
+ snd_strerror(err));
+
+ return -1;
+ }
+
+ /* Calculate space needed to round up to nearest period size. */
+ volume_size = bytes_per_sample * (track.num_samples + silent_samples);
+ MSG(4, "volume size = %i", (int)volume_size);
+
+ /* Create a copy of track with adjusted volume. */
+ MSG(4, "Making copy of track and adjusting volume");
+ track_volume = track;
+ track_volume.samples = (short *)g_malloc(volume_size);
+ real_volume = ((float)alsa_id->id.volume + 100) / (float)200;
+ for (i = 0; i <= track.num_samples - 1; i++)
+ track_volume.samples[i] = track.samples[i] * real_volume;
+
+ if (silent_samples > 0) {
+ u_int16_t silent16;
+ u_int8_t silent8;
+
+ /* Fill remaining space with silence */
+ MSG(4,
+ "Filling with silence up to the period size,
silent_samples=%d",
+ (int)silent_samples);
+ /* TODO: This hangs. Why?
+ snd_pcm_format_set_silence(format,
+ track_volume.samples + (track.num_samples *
bytes_per_sample), silent_samples);
+ */
+ switch (bytes_per_sample) {
+ case 2:
+ silent16 = snd_pcm_format_silence_16(format);
+ for (i = 0; i < silent_samples; i++)
+ track_volume.samples[track.num_samples + i] =
+ silent16;
+ break;
+ case 1:
+ silent8 = snd_pcm_format_silence(format);
+ for (i = 0; i < silent_samples; i++)
+ track_volume.samples[track.num_samples + i] =
+ silent8;
+ break;
+ }
+ }
+
+ /* Loop until all samples are played on the device. */
+ output_samples = track_volume.samples;
+ num_bytes = (track.num_samples + silent_samples) * bytes_per_sample;
+ // MSG("Still %d bytes left to be played", num_bytes);
+ while (num_bytes > 0) {
+
+ /* Write as much samples as possible */
+ framecount = num_bytes / bytes_per_sample / track.num_channels;
+ if (framecount < period_size)
+ framecount = period_size;
+
+ /* Report current state state */
+ state = snd_pcm_state(alsa_id->alsa_pcm);
+ // MSG("PCM state before writei: %s",
+ // snd_pcm_state_name(state));
+
+ /* MSG("snd_pcm_writei() called") */
+ ret =
+ snd_pcm_writei(alsa_id->alsa_pcm, output_samples,
+ framecount);
+ // MSG("Sent %d of %d remaining bytes",
ret*bytes_per_sample, num_bytes);
+
+ if (ret == -EAGAIN) {
+ MSG(4, "Warning: Forced wait!");
+ snd_pcm_wait(alsa_id->alsa_pcm, 100);
+ } else if (ret == -EPIPE) {
+ if (xrun(alsa_id) != 0)
+ ERROR_EXIT();
+ } else if (ret == -ESTRPIPE) {
+ if (suspend(alsa_id) != 0)
+ ERROR_EXIT();
+ } else if (ret == -EBUSY) {
+ MSG(4, "WARNING: sleeping while PCM BUSY");
+ usleep(100);
+ continue;
+ } else if (ret < 0) {
+ ERR("Write to audio interface failed (%s)",
+ snd_strerror(ret));
+ ERROR_EXIT();
+ }
+
+ if (ret > 0) {
+ /* Update counter of bytes left and move the data
pointer */
+ num_bytes -=
+ ret * bytes_per_sample * track.num_channels;
+ output_samples +=
+ ret * bytes_per_sample * track.num_channels / 2;
+ }
+
+ /* Report current state */
+ state = snd_pcm_state(alsa_id->alsa_pcm);
+ // MSG("PCM state before polling: %s",
+ // snd_pcm_state_name(state));
+
+ err =
+ wait_for_poll(alsa_id, alsa_id->alsa_poll_fds,
+ alsa_id->alsa_fd_count, 0);
+ if (err < 0) {
+ ERR("Wait for poll() failed\n");
+ ERROR_EXIT();
+ } else if (err == 1) {
+ MSG(4, "Playback stopped");
+
+ /* Drop the playback on the sound device (probably
+ still in progress up till now) */
+ err = snd_pcm_drop(alsa_id->alsa_pcm);
+ if (err < 0) {
+ ERR("snd_pcm_drop() failed: %s",
+ snd_strerror(err));
+ return -1;
+ }
+
+ goto terminate;
+ }
+
+ if (num_bytes <= 0)
+ break;
+// MSG("ALSA ready for more samples");
+
+ /* Stop requests can be issued again */
+ }
+
+ MSG(4, "Draining...");
+
+ /* We want to next "device ready" notification only after the buffer is
+ already empty */
+ err =
+ snd_pcm_sw_params_set_avail_min(alsa_id->alsa_pcm,
+ alsa_id->alsa_sw_params,
+ alsa_id->alsa_buffer_size);
+ if (err < 0) {
+ ERR("Unable to set avail min for playback: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+ /* write the parameters to the playback device */
+ err = snd_pcm_sw_params(alsa_id->alsa_pcm, alsa_id->alsa_sw_params);
+ if (err < 0) {
+ ERR("Unable to set sw params for playback: %s\n",
+ snd_strerror(err));
+ return -1;
+ }
+
+ err =
+ wait_for_poll(alsa_id, alsa_id->alsa_poll_fds,
+ alsa_id->alsa_fd_count, 1);
+ if (err < 0) {
+ ERR("Wait for poll() failed\n");
+ return -1;
+ } else if (err == 1) {
+ MSG(4, "Playback stopped while draining");
+
+ /* Drop the playback on the sound device (probably
+ still in progress up till now) */
+ err = snd_pcm_drop(alsa_id->alsa_pcm);
+ if (err < 0) {
+ ERR("snd_pcm_drop() failed: %s", snd_strerror(err));
+ return -1;
+ }
+ }
+ MSG(4, "Draining terminated");
+
+terminate:
+ /* Terminating (successfully or after a stop) */
+ if (track_volume.samples != NULL)
+ g_free(track_volume.samples);
+
+ err = snd_pcm_drop(alsa_id->alsa_pcm);
+ if (err < 0) {
+ ERR("snd_pcm_drop() failed: %s", snd_strerror(err));
+ return -1;
+ }
+
+ MSG(2, "Freeing HW parameters");
+ snd_pcm_hw_params_free(alsa_id->alsa_hw_params);
+
+ pthread_mutex_lock(&alsa_id->alsa_pipe_mutex);
+ alsa_id->alsa_opened = 0;
+ close(alsa_id->alsa_stop_pipe[0]);
+ close(alsa_id->alsa_stop_pipe[1]);
+
+ g_free(alsa_id->alsa_poll_fds);
+ pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
+
+ MSG(1, "End of playback on ALSA");
+
+ return 0;
+}
+
+#undef ERROR_EXIT
+
+/*
+ Stop the playback on the device and interrupt alsa_play()
+*/
+static int alsa_stop(AudioID * id)
+{
+ char buf;
+ int ret;
+ spd_alsa_id_t *alsa_id = (spd_alsa_id_t *) id;
+
+ MSG(1, "STOP!");
+
+ if (alsa_id == NULL)
+ return 0;
+
+ pthread_mutex_lock(&alsa_id->alsa_pipe_mutex);
+ if (alsa_id->alsa_opened) {
+ /* This constant is arbitrary */
+ buf = 42;
+
+ ret = write(alsa_id->alsa_stop_pipe[1], &buf, 1);
+ if (ret <= 0) {
+ ERR("Can't write stop request to pipe, err %d: %s",
+ errno, strerror(errno));
+ }
+ }
+ pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
+
+ return 0;
+}
+
+/*
+ Set volume
+
+ Comments: It's not possible to set individual track volume with Alsa, so we
+ handle volume in alsa_play() by multiplication of each sample.
+*/
+static int alsa_set_volume(AudioID * id, int volume)
+{
+ return 0;
+}
+
+static void alsa_set_loglevel(int level)
+{
+ if (level) {
+ alsa_log_level = level;
+ }
+}
+
+static char const *alsa_get_playcmd(void)
+{
+ return alsa_play_cmd;
+}
+
+/* Provide the Alsa backend. */
+static spd_audio_plugin_t alsa_functions = {
+ "alsa",
+ alsa_open,
+ alsa_play,
+ alsa_stop,
+ alsa_close,
+ alsa_set_volume,
+ alsa_set_loglevel,
+ alsa_get_playcmd
+};
+
+spd_audio_plugin_t *alsa_plugin_get(void)
+{
+ return &alsa_functions;
+}
+
+spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
+ __attribute__ ((weak, alias("alsa_plugin_get")));
+#undef MSG
+#undef ERR
diff --git a/src/audio/libao.c b/src/audio/libao.c
new file mode 100644
index 0000000..e82be2b
--- /dev/null
+++ b/src/audio/libao.c
@@ -0,0 +1,246 @@
+/*
+ * libao.c -- The libao backend for the spd_audio library.
+ *
+ * Author: Marco Skambraks <marco at openblinux.de>
+ * Date: 2009-12-15
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Leser General Public License as published by the Free
+ * Software Foundation; either version 2.1, or (at your option) any later
+ * version.
+ *
+ * This software 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 Lesser General Public License
+ * along with this package; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <glib.h>
+#include <ao/ao.h>
+
+#define SPD_AUDIO_PLUGIN_ENTRY spd_libao_LTX_spd_audio_plugin_get
+#include <spd_audio_plugin.h>
+
+/* send a packet of XXX bytes to the sound device */
+#define AO_SEND_BYTES 256
+/* Put a message into the logfile (stderr) */
+#define MSG(level, arg...) \
+ if(level <= libao_log_level){ \
+ time_t t; \
+ struct timeval tv; \
+ char *tstr; \
+ t = time(NULL); \
+ tstr = g_strdup(ctime(&t)); \
+ tstr[strlen(tstr)-1] = 0; \
+ gettimeofday(&tv,NULL); \
+ fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
+ fprintf(stderr," libao:: "); \
+ fprintf(stderr,arg); \
+ fprintf(stderr,"\n"); \
+ fflush(stderr); \
+ g_free(tstr); \
+ }
+
+#define ERR(arg...) \
+ { \
+ time_t t; \
+ struct timeval tv; \
+ char *tstr; \
+ t = time(NULL); \
+ tstr = g_strdup(ctime(&t)); \
+ tstr[strlen(tstr)-1] = 0; \
+ gettimeofday(&tv,NULL); \
+ fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
+ fprintf(stderr," libao ERROR: "); \
+ fprintf(stderr,arg); \
+ fprintf(stderr,"\n"); \
+ fflush(stderr); \
+ g_free(tstr); \
+ }
+
+/* AO_FORMAT_INITIALIZER is an ao_sample_format structure with zero values
+ in all of its fields. We can guarantee that the fields of a
+ stack-allocated ao_sample_format are zeroed by assigning
+ AO_FORMAT_INITIALIZER to it.
+ This is the most portable way to initialize a stack-allocated struct to
+ zero. */
+static ao_sample_format AO_FORMAT_INITIALIZER;
+static ao_sample_format current_ao_parameters;
+
+static volatile int ao_stop_playback = 0;
+
+static int default_driver;
+static int libao_log_level;
+
+ao_device *device = NULL;
+
+static inline void libao_open_handle(int rate, int channels, int bits)
+{
+ ao_sample_format format = AO_FORMAT_INITIALIZER;
+
+ format.channels = channels;
+ format.rate = rate;
+ format.bits = bits;
+ format.byte_format = AO_FMT_NATIVE;
+ device = ao_open_live(default_driver, &format, NULL);
+
+ if (device != NULL)
+ current_ao_parameters = format;
+}
+
+static inline void libao_close_handle(void)
+{
+ if (device != NULL) {
+ ao_close(device);
+ device = NULL;
+ }
+}
+
+static AudioID *libao_open(void **pars)
+{
+ AudioID *id;
+
+ id = (AudioID *) g_malloc(sizeof(AudioID));
+
+ ao_initialize();
+ default_driver = ao_default_driver_id();
+ return id;
+}
+
+static int libao_play(AudioID * id, AudioTrack track)
+{
+ int bytes_per_sample;
+
+ int num_bytes;
+
+ int outcnt = 0;
+
+ signed short *output_samples;
+
+ int i;
+
+ if (id == NULL)
+ return -1;
+ if (track.samples == NULL || track.num_samples <= 0)
+ return 0;
+
+ /* Choose the correct format */
+ if (track.bits == 16)
+ bytes_per_sample = 2;
+ else if (track.bits == 8)
+ bytes_per_sample = 1;
+ else {
+ ERR("Audio: Unrecognized sound data format.\n");
+ return -10;
+ }
+ MSG(3, "Starting playback");
+ output_samples = track.samples;
+ num_bytes = track.num_samples * bytes_per_sample;
+
+ if ((device == NULL)
+ || (track.num_channels != current_ao_parameters.channels)
+ || (track.sample_rate != current_ao_parameters.rate)
+ || (track.bits != current_ao_parameters.bits)) {
+ libao_close_handle();
+ libao_open_handle(track.sample_rate, track.num_channels,
+ track.bits);
+ }
+
+ if (device == NULL) {
+ ERR("error opening libao dev");
+ return -2;
+ }
+ MSG(3, "bytes to play: %d, (%f secs)", num_bytes,
+ (((float)(num_bytes) / 2) / (float)track.sample_rate));
+
+ ao_stop_playback = 0;
+ outcnt = 0;
+ i = 0;
+
+ while ((outcnt < num_bytes) && !ao_stop_playback) {
+ if ((num_bytes - outcnt) > AO_SEND_BYTES)
+ i = AO_SEND_BYTES;
+ else
+ i = (num_bytes - outcnt);
+
+ if (!ao_play(device, (char *)output_samples + outcnt, i)) {
+ libao_close_handle();
+ ERR("Audio: ao_play() - closing device - re-open it in
next run\n");
+ return -1;
+ }
+ outcnt += i;
+ }
+
+ return 0;
+
+}
+
+/* stop the libao_play() loop */
+static int libao_stop(AudioID * id)
+{
+
+ ao_stop_playback = 1;
+ return 0;
+}
+
+static int libao_close(AudioID * id)
+{
+ libao_close_handle();
+ ao_shutdown();
+
+ g_free(id);
+ id = NULL;
+ return 0;
+}
+
+static int libao_set_volume(AudioID * id, int volume)
+{
+ return 0;
+}
+
+static void libao_set_loglevel(int level)
+{
+ if (level) {
+ libao_log_level = level;
+ }
+}
+
+static char const *libao_get_playcmd(void)
+{
+ return NULL;
+}
+
+/* Provide the libao backend. */
+static spd_audio_plugin_t libao_functions = {
+ "libao",
+ libao_open,
+ libao_play,
+ libao_stop,
+ libao_close,
+ libao_set_volume,
+ libao_set_loglevel,
+ libao_get_playcmd
+};
+
+spd_audio_plugin_t *libao_plugin_get(void)
+{
+ return &libao_functions;
+}
+
+spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
+ __attribute__ ((weak, alias("libao_plugin_get")));
+#undef MSG
+#undef ERR
diff --git a/src/audio/nas.c b/src/audio/nas.c
new file mode 100644
index 0000000..ca76127
--- /dev/null
+++ b/src/audio/nas.c
@@ -0,0 +1,263 @@
+/*
+ * nas.c -- The Network Audio System backend for the spd_audio library.
+ *
+ * Copyright (C) 2004,2006 Brailcom, o.p.s.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1, or (at your option) any later
+ * version.
+ *
+ * This software 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 Lesser General Public License
+ * along with this package; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * $Id: nas.c,v 1.8 2006-07-11 16:12:26 hanke Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <audio/audiolib.h>
+#include <audio/soundlib.h>
+
+#include <pthread.h>
+
+#define SPD_AUDIO_PLUGIN_ENTRY spd_nas_LTX_spd_audio_plugin_get
+#include <spd_audio_plugin.h>
+
+typedef struct {
+ AudioID id;
+ AuServer *aud;
+ AuFlowID flow;
+ pthread_mutex_t flow_mutex;
+ pthread_t nas_event_handler;
+ pthread_cond_t pt_cond;
+ pthread_mutex_t pt_mutex;
+} spd_nas_id_t;
+
+static int nas_log_level;
+
+/* Internal event handler */
+static void *_nas_handle_events(void *par)
+{
+ spd_nas_id_t *nas_id = (spd_nas_id_t *) par;
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+
+ while (1)
+ AuHandleEvents(nas_id->aud);
+
+}
+
+/* NAS Server error handler */
+/* Unfortunatelly we can't return these errors to the caller
+ since this handler gets called in the event handler thread. */
+static AuBool _nas_handle_server_error(AuServer * server, AuErrorEvent * event)
+{
+ fprintf(stderr, "ERROR: Non-fatal server error in NAS\n");
+
+ if (event->type != 0) {
+ fprintf(stderr,
+ "Event of a different type received in NAS error
handler.");
+ return -1;
+ }
+
+ /* It's a pain but we can't ask for string return code
+ since it's not allowed to talk to the server inside error handlers
+ because of possible deadlocks. */
+ fprintf(stderr, "NAS: Serial number of failed request: %d\n",
+ event->serial);
+ fprintf(stderr, "NAS: Error code: %d\n", event->error_code);
+ fprintf(stderr, "NAS: Resource id: %d\n", event->resourceid);
+ fprintf(stderr, "NAS: Request code: %d\n", event->request_code);
+ fprintf(stderr, "NAS: Minor code: %d\n\n", event->minor_code);
+
+ return 0;
+}
+
+static AudioID *nas_open(void **pars)
+{
+ spd_nas_id_t *nas_id;
+ int ret;
+ AuBool r;
+
+ nas_id = (spd_nas_id_t *) g_malloc(sizeof(spd_nas_id_t));
+
+ nas_id->aud = AuOpenServer(pars[2], 0, NULL, 0, NULL, NULL);
+ if (!nas_id->aud) {
+ fprintf(stderr, "Can't connect to NAS audio server\n");
+ return NULL;
+ }
+
+ AuSetErrorHandler(nas_id->aud, _nas_handle_server_error);
+ /* return value incompatible with documentation here */
+ /* if (!r){
+ fprintf(stderr, "Can't set default NAS event handler\n");
+ return -1;
+ } */
+
+ nas_id->flow = 0;
+
+ pthread_cond_init(&nas_id->pt_cond, NULL);
+ pthread_mutex_init(&nas_id->pt_mutex, NULL);
+ pthread_mutex_init(&nas_id->flow_mutex, NULL);
+
+ ret =
+ pthread_create(&nas_id->nas_event_handler, NULL, _nas_handle_events,
+ (void *)nas_id);
+ if (ret != 0) {
+ fprintf(stderr,
+ "ERROR: NAS Audio module: thread creation failed\n");
+ return NULL;
+ }
+
+ return (AudioID *) nas_id;
+}
+
+static int nas_play(AudioID * id, AudioTrack track)
+{
+ char *buf;
+ Sound s;
+ AuEventHandlerRec *event_handler;
+ int ret;
+ float lenght;
+ struct timeval now;
+ struct timespec timeout;
+ spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
+
+ if (nas_id == NULL)
+ return -2;
+
+ s = SoundCreate(SoundFileFormatNone,
+ AuFormatLinearSigned16LSB,
+ track.num_channels,
+ track.sample_rate, track.num_samples, NULL);
+
+ buf = (char *)track.samples;
+
+ pthread_mutex_lock(&nas_id->flow_mutex);
+
+ event_handler = AuSoundPlayFromData(nas_id->aud,
+ s,
+ buf,
+ AuNone,
+ ((nas_id->id.volume +
+ 100) / 2) * 1500, NULL, NULL,
+ &nas_id->flow, NULL, NULL, NULL);
+
+ if (event_handler == NULL) {
+ fprintf(stderr,
+ "AuSoundPlayFromData failed for unknown resons.\n");
+ return -1;
+ }
+
+ if (nas_id->flow == 0) {
+ fprintf(stderr, "Couldn't start data flow");
+ }
+ pthread_mutex_unlock(&nas_id->flow_mutex);
+
+ /* Another timing magic */
+ pthread_mutex_lock(&nas_id->pt_mutex);
+ lenght = (((float)track.num_samples) / (float)track.sample_rate);
+ gettimeofday(&now, NULL);
+ timeout.tv_sec = now.tv_sec + (int)lenght;
+ timeout.tv_nsec =
+ now.tv_usec * 1000 + (lenght - (int)lenght) * 1000000000;
+ pthread_cond_timedwait(&nas_id->pt_cond, &nas_id->pt_mutex, &timeout);
+ pthread_mutex_unlock(&nas_id->pt_mutex);
+
+ pthread_mutex_lock(&nas_id->flow_mutex);
+ nas_id->flow = 0;
+ pthread_mutex_unlock(&nas_id->flow_mutex);
+
+ return 0;
+}
+
+static int nas_stop(AudioID * id)
+{
+ int ret;
+ spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
+
+ if (nas_id == NULL)
+ return -2;
+
+ pthread_mutex_lock(&nas_id->flow_mutex);
+ if (nas_id->flow != 0)
+ AuStopFlow(nas_id->aud, nas_id->flow, NULL);
+ nas_id->flow = 0;
+ pthread_mutex_unlock(&nas_id->flow_mutex);
+
+ pthread_mutex_lock(&nas_id->pt_mutex);
+ pthread_cond_signal(&nas_id->pt_cond);
+ pthread_mutex_unlock(&nas_id->pt_mutex);
+
+ return 0;
+}
+
+static int nas_close(AudioID * id)
+{
+ spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
+
+ if (nas_id == NULL)
+ return -2;
+
+ pthread_cancel(nas_id->nas_event_handler);
+ pthread_join(nas_id->nas_event_handler, NULL);
+
+ pthread_mutex_destroy(&nas_id->pt_mutex);
+ pthread_mutex_destroy(&nas_id->flow_mutex);
+
+ AuCloseServer(nas_id->aud);
+
+ g_free(nas_id);
+ id = NULL;
+
+ return 0;
+}
+
+static int nas_set_volume(AudioID * id, int volume)
+{
+ return 0;
+}
+
+static void nas_set_loglevel(int level)
+{
+ if (level) {
+ nas_log_level = level;
+ }
+}
+
+static char const *nas_get_playcmd(void)
+{
+ return NULL;
+}
+
+/* Provide the NAS backend */
+static spd_audio_plugin_t nas_functions = {
+ "nas",
+ nas_open,
+ nas_play,
+ nas_stop,
+ nas_close,
+ nas_set_volume,
+ nas_set_loglevel,
+ nas_get_playcmd
+};
+
+spd_audio_plugin_t *nas_plugin_get(void)
+{
+ return &nas_functions;
+}
+
+spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
+ __attribute__ ((weak, alias("nas_plugin_get")));
diff --git a/src/audio/oss.c b/src/audio/oss.c
new file mode 100644
index 0000000..b7232c3
--- /dev/null
+++ b/src/audio/oss.c
@@ -0,0 +1,523 @@
+
+/*
+ * oss.c -- The Open Sound System backend for the spd_audio library.
+ *
+ * Copyright (C) 2004,2006 Brailcom, o.p.s.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Leser General Public License as published by the Free
+ * Software Foundation; either version 2.1, or (at your option) any later
+ * version.
+ *
+ * This software 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 Lesser General Public License
+ * along with this package; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * $Id: oss.c,v 1.13 2006-07-11 16:12:26 hanke Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/soundcard.h>
+#include <errno.h>
+#include <unistd.h> /* for open, close */
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <glib.h>
+
+#include <sys/soundcard.h>
+
+#define SPD_AUDIO_PLUGIN_ENTRY spd_oss_LTX_spd_audio_plugin_get
+#include <spd_audio_plugin.h>
+
+typedef struct {
+ AudioID id;
+ int fd;
+ char *device_name;
+ pthread_mutex_t fd_mutex;
+ pthread_cond_t pt_cond;
+ pthread_mutex_t pt_mutex;
+} spd_oss_id_t;
+
+static int _oss_open(spd_oss_id_t * id);
+static int _oss_close(spd_oss_id_t * id);
+static int _oss_sync(spd_oss_id_t * id);
+
+/* Put a message into the logfile (stderr) */
+#define MSG(level, arg...) \
+ if(level <= oss_log_level){ \
+ time_t t; \
+ struct timeval tv; \
+ char *tstr; \
+ t = time(NULL); \
+ tstr = g_strdup(ctime(&t)); \
+ tstr[strlen(tstr)-1] = 0; \
+ gettimeofday(&tv,NULL); \
+ fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
+ fprintf(stderr," OSS: "); \
+ fprintf(stderr,arg); \
+ fprintf(stderr,"\n"); \
+ fflush(stderr); \
+ g_free(tstr); \
+ }
+
+#define ERR(arg...) \
+ { \
+ time_t t; \
+ struct timeval tv; \
+ char *tstr; \
+ t = time(NULL); \
+ tstr = g_strdup(ctime(&t)); \
+ tstr[strlen(tstr)-1] = 0; \
+ gettimeofday(&tv,NULL); \
+ fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
+ fprintf(stderr," OSS ERROR: "); \
+ fprintf(stderr,arg); \
+ fprintf(stderr,"\n"); \
+ fflush(stderr); \
+ g_free(tstr); \
+ }
+
+static int oss_log_level;
+static char const *oss_play_cmd = "play";
+
+static int _oss_open(spd_oss_id_t * id)
+{
+ MSG(1, "_oss_open()")
+ pthread_mutex_lock(&id->fd_mutex);
+
+ id->fd = open(id->device_name, O_WRONLY, 0);
+ if (id->fd < 0) {
+ perror(id->device_name);
+ pthread_mutex_unlock(&id->fd_mutex);
+ id = NULL;
+ return -1;
+ }
+
+ pthread_mutex_unlock(&id->fd_mutex);
+
+ return 0;
+}
+
+static int _oss_close(spd_oss_id_t * id)
+{
+ MSG(1, "_oss_close()")
+ if (id == NULL)
+ return 0;
+ if (id->fd < 0)
+ return 0;
+
+ pthread_mutex_lock(&id->fd_mutex);
+ close(id->fd);
+ id->fd = -1;
+ pthread_mutex_unlock(&id->fd_mutex);
+ return 0;
+}
+
+/* Open OSS device
+ Arguments:
+ **pars:
+ (char*) pars[0] -- the name of the device (e.g. "/dev/dsp")
+ (void*) pars[1] = NULL
+*/
+static AudioID *oss_open(void **pars)
+{
+ spd_oss_id_t *oss_id;
+ int ret;
+
+ if (pars[0] == NULL)
+ return NULL;
+
+ oss_id = (spd_oss_id_t *) g_malloc(sizeof(spd_oss_id_t));
+
+ oss_id->device_name = g_strdup((char *)pars[0]);
+
+ pthread_mutex_init(&oss_id->fd_mutex, NULL);
+
+ pthread_cond_init(&oss_id->pt_cond, NULL);
+ pthread_mutex_init(&oss_id->pt_mutex, NULL);
+
+ /* Test if it's possible to access the device */
+ ret = _oss_open(oss_id);
+ if (ret) {
+ g_free(oss_id->device_name);
+ g_free(oss_id);
+ return NULL;
+ }
+ ret = _oss_close(oss_id);
+ if (ret) {
+ g_free(oss_id->device_name);
+ g_free(oss_id);
+ return NULL;
+ }
+
+ return (AudioID *) oss_id;
+}
+
+/* Internal function. */
+static int _oss_sync(spd_oss_id_t * id)
+{
+ int ret;
+
+ ret = ioctl(id->fd, SNDCTL_DSP_POST, 0);
+ if (ret == -1) {
+ perror("reset");
+ return -1;
+ }
+ return 0;
+}
+
+static int oss_play(AudioID * id, AudioTrack track)
+{
+ int ret, ret2;
+ struct timeval now;
+ struct timespec timeout;
+ float lenght;
+ int r = 0;
+ int format, oformat, channels, speed;
+ int bytes_per_sample;
+ int num_bytes;
+ signed short *output_samples;
+ float delay = 0;
+ float DELAY = 0.1; /* in seconds */
+ audio_buf_info info;
+ int bytes;
+ float real_volume;
+ int i;
+ int re;
+ spd_oss_id_t *oss_id = (spd_oss_id_t *) id;
+
+ AudioTrack track_volume;
+
+ if (oss_id == NULL)
+ return -1;
+
+ /* Open the sound device. This is necessary for OSS so that the
+ application doesn't prevent others from accessing /dev/dsp when
+ it doesn't play anything. */
+ ret = _oss_open(oss_id);
+ if (ret)
+ return -2;
+
+ /* Create a copy of track with the adjusted volume */
+ track_volume = track;
+ track_volume.samples =
+ (short *)g_malloc(sizeof(short) * track.num_samples);
+ real_volume = ((float)id->volume + 100) / (float)200;
+ for (i = 0; i <= track.num_samples - 1; i++)
+ track_volume.samples[i] = track.samples[i] * real_volume;
+
+ /* Choose the correct format */
+ if (track.bits == 16) {
+ format = AFMT_S16_NE;
+ bytes_per_sample = 2;
+ } else if (track.bits == 8) {
+ bytes_per_sample = 1;
+ format = AFMT_S8;
+ } else {
+ ERR("Audio: Unrecognized sound data format.\n");
+ _oss_close(oss_id);
+ return -10;
+ }
+
+ oformat = format;
+ ret = ioctl(oss_id->fd, SNDCTL_DSP_SETFMT, &format);
+ if (ret == -1) {
+ perror("OSS ERROR: format");
+ _oss_close(oss_id);
+ return -1;
+ }
+ if (format != oformat) {
+ ERR("Device doesn't support 16-bit sound format.\n");
+ _oss_close(oss_id);
+ return -2;
+ }
+
+ /* Choose the correct number of channels */
+ channels = track.num_channels;
+ ret = ioctl(oss_id->fd, SNDCTL_DSP_CHANNELS, &channels);
+ if (ret == -1) {
+ perror("OSS ERROR: channels");
+ _oss_close(oss_id);
+ return -3;
+ }
+ if (channels != track.num_channels) {
+ MSG(1, "Device doesn't support stereo sound.\n");
+ _oss_close(oss_id);
+ return -4;
+ }
+
+ /* Choose the correct sample rate */
+ speed = track.sample_rate;
+ ret = ioctl(oss_id->fd, SNDCTL_DSP_SPEED, &speed);
+ if (ret == -1) {
+ ERR("OSS ERROR: Can't set sample rate %d nor any similar.",
+ track.sample_rate);
+ _oss_close(oss_id);
+ return -5;
+ }
+ if (speed != track.sample_rate) {
+ ERR("Device doesn't support bitrate %d, using %d instead.\n",
+ track.sample_rate, speed);
+ }
+
+ /* Is it not an empty track? */
+ if (track.samples == NULL) {
+ _oss_close(oss_id);
+ return 0;
+ }
+
+ /* Loop until all samples are played on the device.
+ In the meantime, wait in pthread_cond_timedwait for more data
+ or for interruption. */
+ MSG(4, "Starting playback");
+ output_samples = track_volume.samples;
+ num_bytes = track.num_samples * bytes_per_sample;
+ MSG(4, "bytes to play: %d, (%f secs)", num_bytes,
+ (((float)(num_bytes) / 2) / (float)track.sample_rate));
+ while (num_bytes > 0) {
+
+ /* OSS doesn't support non-blocking write, so lets check how
much data
+ can we write so that write() returns immediatelly */
+ re = ioctl(oss_id->fd, SNDCTL_DSP_GETOSPACE, &info);
+ if (re == -1) {
+ perror("OSS ERROR: GETOSPACE");
+ _oss_close(oss_id);
+ return -5;
+ }
+
+ /* If there is not enough space for a single fragment, try
later.
+ (This shouldn't happen, it has very bad effect on
synchronization!) */
+ if (info.fragments == 0) {
+ MSG(4,
+ "WARNING: There is not enough space for a single
fragment, looping");
+ usleep(100);
+ continue;
+ }
+
+ MSG(4,
+ "There is space for %d more fragments, fragment size is %d
bytes",
+ info.fragments, info.fragsize);
+
+ bytes = info.fragments * info.fragsize;
+ ret =
+ write(oss_id->fd, output_samples,
+ num_bytes > bytes ? bytes : num_bytes);
+
+ /* Handle write() errors */
+ if (ret <= 0) {
+ perror("audio");
+ _oss_close(oss_id);
+ return -6;
+ }
+
+ num_bytes -= ret;
+ output_samples += ret / 2;
+
+ MSG(4, "%d bytes written to OSS, %d remaining", ret, num_bytes);
+
+ /* If there is some more data that is less than a
+ full fragment, we need to write it immediatelly so
+ that it doesn't cause buffer underruns later. */
+ if ((num_bytes > 0)
+ && (num_bytes < info.fragsize)
+ && (bytes + num_bytes < info.bytes)) {
+
+ MSG(4,
+ "Writing the rest of the data (%d bytes) to OSS,
not a full fragment",
+ num_bytes);
+
+ ret2 = write(oss_id->fd, output_samples, num_bytes);
+ num_bytes -= ret2;
+ output_samples += ret2 / 2;
+ ret += ret2;
+ }
+
+ /* Handle write() errors */
+ if (ret <= 0) {
+ perror("audio");
+ _oss_close(oss_id);
+ return -6;
+ }
+
+ /* Some timing magic...
+ We need to wait for the time computed from the number of
+ samples written. But this wait needs to be interruptible
+ by oss_stop(). Furthermore, there need to be no buffer
+ underrruns, so we actually wait a bit (DELAY) less
+ in the first pass through the while() loop. Then our timer
+ will be DELAY nsecs backwards.
+ */
+ MSG(4, "Now we will try to wait");
+ pthread_mutex_lock(&oss_id->pt_mutex);
+ lenght = (((float)(ret) / 2) / (float)track.sample_rate);
+ if (!delay) {
+ delay = lenght > DELAY ? DELAY : lenght;
+ lenght -= delay;
+ }
+ MSG(4, "Wait for %f secs (begin: %f, delay: %f)", lenght,
+ lenght + delay, delay)
+ gettimeofday(&now, NULL);
+ timeout.tv_sec = now.tv_sec + (int)lenght;
+ timeout.tv_nsec =
+ now.tv_usec * 1000 + (lenght - (int)lenght) * 1000000000;
+ //MSG("5, waiting till %d:%d (%d:%d | %d:%d)", timeout.tv_sec,
timeout.tv_nsec,
+ // now.tv_sec, now.tv_usec*1000, timeout.tv_sec -
now.tv_sec, timeout.tv_nsec-now.tv_usec*1000);
+
+ timeout.tv_sec += timeout.tv_nsec / 1000000000;
+ timeout.tv_nsec = timeout.tv_nsec % 1000000000;
+ // MSG("6, waiting till %d:%d (%d:%d | %d:%d)",
timeout.tv_sec, timeout.tv_nsec,
+ // now.tv_sec, now.tv_usec*1000, timeout.tv_sec - now.tv_sec,
timeout.tv_nsec-now.tv_usec*1000);
+ r = pthread_cond_timedwait(&oss_id->pt_cond, &oss_id->pt_mutex,
+ &timeout);
+ pthread_mutex_unlock(&oss_id->pt_mutex);
+ MSG(4, "End of wait");
+
+ /* The pthread_cond_timedwait was interrupted by change in the
+ condition variable? if so, terminate. */
+ if (r != ETIMEDOUT) {
+ MSG(4, "Playback stopped, %d", r);
+ break;
+ }
+ }
+
+ /* ...one more excersise in timing magic.
+ Wait for the resting delay secs. */
+
+ /* Ugly hack: correct for the time we spend outside timing segments */
+ delay -= 0.05;
+
+ MSG(4, "Wait for the resting delay = %f secs", delay)
+ if ((delay > 0) && (r == ETIMEDOUT)) {
+ pthread_mutex_lock(&oss_id->pt_mutex);
+ gettimeofday(&now, NULL);
+ timeout.tv_sec = now.tv_sec;
+ timeout.tv_nsec = now.tv_usec * 1000 + delay * 1000000000;
+ // MSG("6, waiting till %d:%d (%d:%d | %d:%d)", timeout.tv_sec,
timeout.tv_nsec,
+ // now.tv_sec, now.tv_usec*1000, timeout.tv_sec -
now.tv_sec, timeout.tv_nsec-now.tv_usec*1000);
+ timeout.tv_sec += timeout.tv_nsec / 1000000000;
+ timeout.tv_nsec = timeout.tv_nsec % 1000000000;
+ // MSG("6, waiting till %d:%d (%d:%d | %d:%d)", timeout.tv_sec,
timeout.tv_nsec,
+ // now.tv_sec, now.tv_usec*1000, timeout.tv_sec -
now.tv_sec, timeout.tv_nsec-now.tv_usec*1000);
+ r = pthread_cond_timedwait(&oss_id->pt_cond, &oss_id->pt_mutex,
+ &timeout);
+ pthread_mutex_unlock(&oss_id->pt_mutex);
+ }
+ MSG(4, "End of wait");
+
+ if (track_volume.samples != NULL)
+ g_free(track_volume.samples);
+
+ /* Flush all the buffers */
+ _oss_sync(oss_id);
+
+ /* Close the device so that we don't block other apps trying to
+ access the device. */
+ _oss_close(oss_id);
+
+ MSG(4, "Device closed");
+
+ return 0;
+}
+
+/* Stop the playback on the device and interrupt oss_play */
+static int oss_stop(AudioID * id)
+{
+ int ret = 0;
+ spd_oss_id_t *oss_id = (spd_oss_id_t *) id;
+
+ if (oss_id == NULL)
+ return 0;
+
+ MSG(4, "stop() called");
+
+ /* Stop the playback on /dev/dsp */
+ pthread_mutex_lock(&oss_id->fd_mutex);
+ if (oss_id->fd >= 0)
+ ret = ioctl(oss_id->fd, SNDCTL_DSP_RESET, 0);
+ pthread_mutex_unlock(&oss_id->fd_mutex);
+ if (ret == -1) {
+ perror("reset");
+ return -1;
+ }
+
+ /* Interrupt oss_play by setting the condition variable */
+ pthread_mutex_lock(&oss_id->pt_mutex);
+ pthread_cond_signal(&oss_id->pt_cond);
+ pthread_mutex_unlock(&oss_id->pt_mutex);
+ return 0;
+}
+
+/* Close the device */
+static int oss_close(AudioID * id)
+{
+ spd_oss_id_t *oss_id = (spd_oss_id_t *) id;
+
+ /* Does nothing because the device is being automatically openned and
+ closed in oss_play before and after playing each sample. */
+
+ g_free(oss_id->device_name);
+ g_free(oss_id);
+ id = NULL;
+
+ return 0;
+}
+
+/* Set volume
+
+Comments:
+ /dev/dsp can't set volume. We just multiply the track samples by
+ a constant in oss_play (see oss_play() for more information).
+*/
+static int oss_set_volume(AudioID * id, int volume)
+{
+ return 0;
+}
+
+static void oss_set_loglevel(int level)
+{
+ if (level) {
+ oss_log_level = level;
+ }
+}
+
+static char const *oss_get_playcmd(void)
+{
+ return oss_play_cmd;
+}
+
+/* Provide the OSS backend. */
+static spd_audio_plugin_t oss_functions = {
+ "oss",
+ oss_open,
+ oss_play,
+ oss_stop,
+ oss_close,
+ oss_set_volume,
+ oss_set_loglevel,
+ oss_get_playcmd
+};
+
+spd_audio_plugin_t *oss_plugin_get(void)
+{
+ return &oss_functions;
+}
+
+spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
+ __attribute__ ((weak, alias("oss_plugin_get")));
+#undef MSG
+#undef ERR
diff --git a/src/audio/pulse.c b/src/audio/pulse.c
new file mode 100644
index 0000000..a4137e5
--- /dev/null
+++ b/src/audio/pulse.c
@@ -0,0 +1,310 @@
+
+/*
+ * pulse.c -- The simple pulseaudio backend for the spd_audio library.
+ *
+ * Based on libao.c from Marco Skambraks <marco at openblinux.de>
+ * Date: 2009-12-15
+ *
+ * Copied from Luke Yelavich's libao.c driver, and merged with code from
+ * Marco's ao_pulse.c driver, by Bill Cox, Dec 21, 2009.
+ *
+ * Minor changes be Rui Batista <rui.batista at ist.utl.pt> to configure
settings through speech-dispatcher configuration files
+ * Date: Dec 22, 2009
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Leser General Public License as published by the Free
+ * Software Foundation; either version 2.1, or (at your option) any later
+ * version.
+ *
+ * This software 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 Lesser General Public License
+ * along with this package; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <stdarg.h>
+#include <glib.h>
+
+#include <pulse/simple.h>
+#include <pulse/error.h>
+
+#define SPD_AUDIO_PLUGIN_ENTRY spd_pulse_LTX_spd_audio_plugin_get
+#include <spd_audio_plugin.h>
+
+/* Switch this on to debug, see output log location in MSG() */
+//#define DEBUG_PULSE
+typedef struct {
+ AudioID id;
+ pa_simple *pa_simple;
+ char *pa_server;
+ int pa_min_audio_length;
+ volatile int pa_stop_playback;
+ int pa_current_rate; // Sample rate for currently PA connection
+ int pa_current_bps; // Bits per sample rate for currently PA
connection
+ int pa_current_channels; // Number of channels for currently PA
connection
+} spd_pulse_id_t;
+
+/* send a packet of XXX bytes to the sound device */
+#define PULSE_SEND_BYTES 256
+
+/* This is the smallest audio sound we are expected to play immediately
without buffering. */
+/* Changed to define on config file. Default is the same. */
+#define DEFAULT_PA_MIN_AUDIO_LENgTH 100
+
+static int pulse_log_level;
+static char const *pulse_play_cmd = "paplay";
+
+/* Write to /tmp/speech-dispatcher-pulse.log */
+#ifdef DEBUG_PULSE
+static FILE *pulseDebugFile = NULL;
+static void MSG(char *message, ...)
+{
+ va_list ap;
+
+ if (pulseDebugFile == NULL) {
+ pulseDebugFile = fopen("/tmp/speech-dispatcher-pulse.log", "w");
+ }
+ va_start(ap, message);
+ vfprintf(pulseDebugFile, message, ap);
+ va_end(ap);
+ fflush(pulseDebugFile);
+}
+#else
+static void MSG(char *message, ...)
+{
+}
+#endif
+
+static int _pulse_open(spd_pulse_id_t * id, int sample_rate,
+ int num_channels, int bytes_per_sample)
+{
+ pa_buffer_attr buffAttr;
+ pa_sample_spec ss;
+ int error;
+
+ ss.rate = sample_rate;
+ ss.channels = num_channels;
+ if (bytes_per_sample == 2) {
+ switch (id->id.format) {
+ case SPD_AUDIO_LE:
+ ss.format = PA_SAMPLE_S16LE;
+ break;
+ case SPD_AUDIO_BE:
+ ss.format = PA_SAMPLE_S16BE;
+ break;
+ }
+ } else {
+ ss.format = PA_SAMPLE_U8;
+ }
+
+ /* Set prebuf to one sample so that keys are spoken as soon as typed
rather than delayed until the next key pressed */
+ buffAttr.maxlength = (uint32_t) - 1;
+ //buffAttr.tlength = (uint32_t)-1; - this is the default, which causes
key echo to not work properly.
+ buffAttr.tlength = id->pa_min_audio_length;
+ buffAttr.prebuf = (uint32_t) - 1;
+ buffAttr.minreq = (uint32_t) - 1;
+ buffAttr.fragsize = (uint32_t) - 1;
+ /* Open new connection */
+ if (!
+ (id->pa_simple =
+ pa_simple_new(id->pa_server, "speech-dispatcher",
+ PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL,
+ &buffAttr, &error))) {
+ fprintf(stderr, __FILE__ ": pa_simple_new() failed: %s\n",
+ pa_strerror(error));
+ return 1;
+ }
+ return 0;
+}
+
+/* Close the connection to the server. Does not free the AudioID struct. */
+/* Usable in pulse_play, which closes connections on failure or */
+/* changes in audio parameters. */
+static void pulse_connection_close(spd_pulse_id_t * pulse_id)
+{
+ if (pulse_id->pa_simple != NULL) {
+ pa_simple_free(pulse_id->pa_simple);
+ pulse_id->pa_simple = NULL;
+ }
+}
+
+static AudioID *pulse_open(void **pars)
+{
+ spd_pulse_id_t *pulse_id;
+ int ret;
+
+ pulse_id = (spd_pulse_id_t *) g_malloc(sizeof(spd_pulse_id_t));
+
+ /* Select an Endianness for the initial connection. */
+#if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
+ pulse_id->id.format = SPD_AUDIO_BE;
+#else
+ pulse_id->id.format = SPD_AUDIO_LE;
+#endif
+ pulse_id->pa_simple = NULL;
+ pulse_id->pa_server = (char *)pars[3];
+ pulse_id->pa_min_audio_length = DEFAULT_PA_MIN_AUDIO_LENgTH;
+
+ pulse_id->pa_current_rate = -1;
+ pulse_id->pa_current_bps = -1;
+ pulse_id->pa_current_channels = -1;
+
+ if (!strcmp(pulse_id->pa_server, "default")) {
+ pulse_id->pa_server = NULL;
+ }
+
+ if (pars[4] != NULL && atoi(pars[4]) != 0)
+ pulse_id->pa_min_audio_length = atoi(pars[4]);
+
+ pulse_id->pa_stop_playback = 0;
+
+ ret = _pulse_open(pulse_id, 44100, 1, 2);
+ if (ret) {
+ g_free(pulse_id);
+ pulse_id = NULL;
+ }
+
+ return (AudioID *) pulse_id;
+}
+
+static int pulse_play(AudioID * id, AudioTrack track)
+{
+ int bytes_per_sample;
+ int num_bytes;
+ int outcnt = 0;
+ signed short *output_samples;
+ int i;
+ int error;
+ spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
+
+ if (id == NULL) {
+ return -1;
+ }
+ if (track.samples == NULL || track.num_samples <= 0) {
+ return 0;
+ }
+ MSG("Starting playback\n");
+ /* Choose the correct format */
+ if (track.bits == 16) {
+ bytes_per_sample = 2;
+ } else if (track.bits == 8) {
+ bytes_per_sample = 1;
+ } else {
+ MSG("ERROR: Unsupported sound data format, track.bits = %d\n",
+ track.bits);
+ return -1;
+ }
+ output_samples = track.samples;
+ num_bytes = track.num_samples * bytes_per_sample;
+
+ /* Check if the current connection has suitable parameters for this
track */
+ if (pulse_id->pa_current_rate != track.sample_rate
+ || pulse_id->pa_current_bps != track.bits
+ || pulse_id->pa_current_channels != track.num_channels) {
+ MSG("Reopenning connection due to change in track parameters
sample_rate:%d bps:%d channels:%d\n", track.sample_rate, track.bits,
track.num_channels);
+ /* Close old connection if any */
+ pulse_connection_close(pulse_id);
+ /* Open a new connection */
+ _pulse_open(pulse_id, track.sample_rate, track.num_channels,
+ bytes_per_sample);
+ /* Keep track of current connection parameters */
+ pulse_id->pa_current_rate = track.sample_rate;
+ pulse_id->pa_current_bps = track.bits;
+ pulse_id->pa_current_channels = track.num_channels;
+ }
+ MSG("bytes to play: %d, (%f secs)\n", num_bytes,
+ (((float)(num_bytes) / 2) / (float)track.sample_rate));
+ pulse_id->pa_stop_playback = 0;
+ outcnt = 0;
+ i = 0;
+ while ((outcnt < num_bytes) && !pulse_id->pa_stop_playback) {
+ if ((num_bytes - outcnt) > PULSE_SEND_BYTES) {
+ i = PULSE_SEND_BYTES;
+ } else {
+ i = (num_bytes - outcnt);
+ }
+ if (pa_simple_write
+ (pulse_id->pa_simple, ((char *)output_samples) + outcnt, i,
+ &error) < 0) {
+ pa_simple_drain(pulse_id->pa_simple, NULL);
+ pulse_connection_close(pulse_id);
+ MSG("ERROR: Audio: pulse_play(): %s - closing device -
re-open it in next run\n", pa_strerror(error));
+ break;
+ } else {
+ MSG("Pulse: wrote %u bytes\n", i);
+ }
+ outcnt += i;
+ }
+ return 0;
+}
+
+/* stop the pulse_play() loop */
+static int pulse_stop(AudioID * id)
+{
+ spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
+
+ pulse_id->pa_stop_playback = 1;
+ return 0;
+}
+
+static int pulse_close(AudioID * id)
+{
+ spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
+ pulse_connection_close(pulse_id);
+ g_free(pulse_id);
+ id = NULL;
+
+ return 0;
+}
+
+static int pulse_set_volume(AudioID * id, int volume)
+{
+ return 0;
+}
+
+static void pulse_set_loglevel(int level)
+{
+ if (level) {
+ pulse_log_level = level;
+ }
+}
+
+static char const *pulse_get_playcmd(void)
+{
+ return pulse_play_cmd;
+}
+
+/* Provide the pulse backend. */
+static spd_audio_plugin_t pulse_functions = {
+ "pulse",
+ pulse_open,
+ pulse_play,
+ pulse_stop,
+ pulse_close,
+ pulse_set_volume,
+ pulse_set_loglevel,
+ pulse_get_playcmd
+};
+
+spd_audio_plugin_t *pulse_plugin_get(void)
+{
+ return &pulse_functions;
+}
+
+spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
+ __attribute__ ((weak, alias("pulse_plugin_get")));
diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am
index f74b0a4..3588e53 100644
--- a/src/modules/Makefile.am
+++ b/src/modules/Makefile.am
@@ -1,7 +1,5 @@
## Process this file with automake to produce Makefile.in
-SUBDIRS = audio
-
inc_local = -I$(top_srcdir)/include
audio_SOURCES = spd_audio.c spd_audio.h
common_SOURCES = module_main.c module_utils.c module_utils.h
diff --git a/src/modules/audio/.gitignore b/src/modules/audio/.gitignore
deleted file mode 100644
index f75f98e..0000000
--- a/src/modules/audio/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-static_plugins.c
diff --git a/src/modules/audio/Makefile.am b/src/modules/audio/Makefile.am
deleted file mode 100644
index d84fcdb..0000000
--- a/src/modules/audio/Makefile.am
+++ /dev/null
@@ -1,45 +0,0 @@
-## Process this file with automake to produce Makefile.in
-
-inc_local = -I$(top_srcdir)/include/
-
-audio_LTLIBRARIES =
-
-if alsa_support
-audio_LTLIBRARIES += spd_alsa.la
-spd_alsa_la_SOURCES = alsa.c
-spd_alsa_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local) $(ALSA_CFLAGS)
-spd_alsa_la_LIBADD = $(ALSA_LIBS) $(GLIB_LIBS)
-spd_alsa_la_LDFLAGS = -module -avoid-version
-endif
-
-if libao_support
-audio_LTLIBRARIES += spd_libao.la
-spd_libao_la_SOURCES = libao.c
-spd_libao_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local) $(LIBAO_CFLAGS)
-spd_libao_la_LIBADD = $(LIBAO_LIBS) $(GLIB_LIBS)
-spd_libao_la_LDFLAGS = -module -avoid-version
-endif
-
-if nas_support
-audio_LTLIBRARIES += spd_nas.la
-spd_nas_la_SOURCES = nas.c
-spd_nas_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local)
-spd_nas_la_LIBADD = $(NAS_LIBS) $(GLIB_LIBS)
-spd_nas_la_LDFLAGS = -module -avoid-version
-endif
-
-if oss_support
-audio_LTLIBRARIES += spd_oss.la
-spd_oss_la_SOURCES = oss.c
-spd_oss_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local)
-spd_oss_la_LIBADD = $(GLIB_LIBS)
-spd_oss_la_LDFLAGS = -module -avoid-version
-endif
-
-if pulse_support
-audio_LTLIBRARIES += spd_pulse.la
-spd_pulse_la_SOURCES = pulse.c
-spd_pulse_la_CPPFLAGS = $(GLIB_CFLAGS) $(inc_local) $(PULSE_CFLAGS)
-spd_pulse_la_LIBADD = $(PULSE_LIBS) $(GLIB_LIBS)
-spd_pulse_la_LDFLAGS = -module -avoid-version
-endif
diff --git a/src/modules/audio/alsa.c b/src/modules/audio/alsa.c
deleted file mode 100644
index 4e20a54..0000000
--- a/src/modules/audio/alsa.c
+++ /dev/null
@@ -1,883 +0,0 @@
-
-/*
- * alsa.c -- The Advanced Linux Sound System backend for Speech Dispatcher
- *
- * Copyright (C) 2005,2006 Brailcom, o.p.s.
- *
- * This is free software; you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1, or (at your option) any later
- * version.
- *
- * This software 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 Lesser General Public License
- * along with this package; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * $Id: alsa.c,v 1.30 2008-10-15 17:27:32 hanke Exp $
- */
-
-/* NOTE: This module uses the non-blocking write() / poll() approach to
- alsa-lib functions.*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <sys/time.h>
-#include <time.h>
-#include <pthread.h>
-#include <glib.h>
-
-#include <alsa/asoundlib.h>
-
-#define SPD_AUDIO_PLUGIN_ENTRY spd_alsa_LTX_spd_audio_plugin_get
-#include <spd_audio_plugin.h>
-
-typedef struct {
- AudioID id;
- snd_pcm_t *alsa_pcm; /* identifier of the ALSA device */
- snd_pcm_hw_params_t *alsa_hw_params; /* parameters of sound */
- snd_pcm_sw_params_t *alsa_sw_params; /* parameters of playback */
- snd_pcm_uframes_t alsa_buffer_size;
- pthread_mutex_t alsa_pcm_mutex; /* mutex to guard the state of the
device */
- pthread_mutex_t alsa_pipe_mutex; /* mutex to guard the stop
pipes */
- int alsa_stop_pipe[2]; /* Pipe for communication about stop requests */
- int alsa_fd_count; /* Counter of descriptors to poll */
- struct pollfd *alsa_poll_fds; /* Descriptors to poll */
- int alsa_opened; /* 1 between snd_pcm_open and _close, 0
otherwise */
- char *alsa_device_name; /* the name of the device to open */
-} spd_alsa_id_t;
-
-static int _alsa_close(spd_alsa_id_t * id);
-static int _alsa_open(spd_alsa_id_t * id);
-
-static int xrun(spd_alsa_id_t * id);
-static int suspend(spd_alsa_id_t * id);
-
-static int wait_for_poll(spd_alsa_id_t * id, struct pollfd *alsa_poll_fds,
- unsigned int count, int draining);
-
-#ifndef timersub
-#define timersub(a, b, result) \
-do { \
- (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
- (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
- if ((result)->tv_usec < 0) { \
- --(result)->tv_sec; \
- (result)->tv_usec += 1000000; \
- } \
- } while (0)
-#endif
-
-/* Put a message into the logfile (stderr) */
-#define MSG(level, arg...) \
- if(level <= alsa_log_level){ \
- time_t t; \
- struct timeval tv; \
- char *tstr; \
- t = time(NULL); \
- tstr = g_strdup(ctime(&t)); \
- tstr[strlen(tstr)-1] = 0; \
- gettimeofday(&tv,NULL); \
- fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
- fprintf(stderr," ALSA: "); \
- fprintf(stderr,arg); \
- fprintf(stderr,"\n"); \
- fflush(stderr); \
- g_free(tstr); \
- }
-
-#define ERR(arg...) \
- { \
- time_t t; \
- struct timeval tv; \
- char *tstr; \
- t = time(NULL); \
- tstr = g_strdup(ctime(&t)); \
- tstr[strlen(tstr)-1] = 0; \
- gettimeofday(&tv,NULL); \
- fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
- fprintf(stderr," ALSA ERROR: "); \
- fprintf(stderr,arg); \
- fprintf(stderr,"\n"); \
- fflush(stderr); \
- g_free(tstr); \
- }
-
-static int alsa_log_level;
-static char const *alsa_play_cmd = "aplay";
-
-/* I/O error handler */
-static int xrun(spd_alsa_id_t * id)
-{
- snd_pcm_status_t *status;
- int res;
-
- if (id == NULL)
- return -1;
-
- MSG(1, "WARNING: Entering XRUN handler");
-
- snd_pcm_status_alloca(&status);
- if ((res = snd_pcm_status(id->alsa_pcm, status)) < 0) {
- ERR("status error: %s", snd_strerror(res));
-
- return -1;
- }
- if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
- struct timeval now, diff, tstamp;
- gettimeofday(&now, 0);
- snd_pcm_status_get_trigger_tstamp(status, &tstamp);
- timersub(&now, &tstamp, &diff);
- MSG(1, "underrun!!! (at least %.3f ms long)",
- diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
- if ((res = snd_pcm_prepare(id->alsa_pcm)) < 0) {
- ERR("xrun: prepare error: %s", snd_strerror(res));
-
- return -1;
- }
-
- return 0; /* ok, data should be accepted again */
- }
- ERR("read/write error, state = %s",
- snd_pcm_state_name(snd_pcm_status_get_state(status)));
-
- return -1;
-}
-
-/* I/O suspend handler */
-static int suspend(spd_alsa_id_t * id)
-{
- int res;
-
- MSG(1, "WARNING: Entering SUSPEND handler.");
-
- if (id == NULL)
- return -1;
-
- while ((res = snd_pcm_resume(id->alsa_pcm)) == -EAGAIN)
- sleep(1); /* wait until suspend flag is released */
-
- if (res < 0) {
- if ((res = snd_pcm_prepare(id->alsa_pcm)) < 0) {
- ERR("suspend: prepare error: %s", snd_strerror(res));
-
- return -1;
- }
- }
-
- return 0;
-}
-
-/* Open the device so that it's ready for playing on the default
- device. Internal function used by the public alsa_open. */
-static int _alsa_open(spd_alsa_id_t * id)
-{
- int err;
-
- MSG(1, "Opening ALSA device");
- fflush(stderr);
-
- /* Open the device */
- if ((err = snd_pcm_open(&id->alsa_pcm, id->alsa_device_name,
- SND_PCM_STREAM_PLAYBACK,
- SND_PCM_NONBLOCK)) < 0) {
- ERR("Cannot open audio device %s (%s)", id->alsa_device_name,
- snd_strerror(err));
- return -1;
- }
-
- /* Allocate space for hw_params (description of the sound parameters) */
- /* Allocate space for sw_params (description of the sound parameters) */
- MSG(2, "Allocating new sw_params structure");
- if ((err = snd_pcm_sw_params_malloc(&id->alsa_sw_params)) < 0) {
- ERR("Cannot allocate hardware parameter structure (%s)",
- snd_strerror(err));
- return -1;
- }
-
- MSG(1, "Opening ALSA device ... success");
-
- return 0;
-}
-
-/*
- Close the device. Internal function used by public alsa_close.
-*/
-
-static int _alsa_close(spd_alsa_id_t * id)
-{
- int err;
-
- MSG(1, "Closing ALSA device");
-
- if (id->alsa_opened == 0)
- return 0;
-
- pthread_mutex_lock(&id->alsa_pipe_mutex);
- id->alsa_opened = 0;
-
- if ((err = snd_pcm_close(id->alsa_pcm)) < 0) {
- MSG(2, "Cannot close ALSA device (%s)", snd_strerror(err));
- return -1;
- }
-
- snd_pcm_sw_params_free(id->alsa_sw_params);
-
- g_free(id->alsa_poll_fds);
- pthread_mutex_unlock(&id->alsa_pipe_mutex);
-
- MSG(1, "Closing ALSA device ... success");
-
- return 0;
-}
-
-/* Open ALSA for playback.
-
- These parameters are passed in pars:
- (char*) pars[0] ... null-terminated string containing the name
- of the device to be used for sound output
- on ALSA
- (void*) pars[1] ... =NULL
-*/
-static AudioID *alsa_open(void **pars)
-{
- spd_alsa_id_t *alsa_id;
- int ret;
-
- if (pars[1] == NULL) {
- ERR("Can't open ALSA sound output, missing parameters in
argument.");
- return NULL;
- }
-
- alsa_id = (spd_alsa_id_t *) g_malloc(sizeof(spd_alsa_id_t));
-
- pthread_mutex_init(&alsa_id->alsa_pipe_mutex, NULL);
-
- alsa_id->alsa_opened = 0;
-
- MSG(1, "Opening ALSA sound output");
-
- alsa_id->alsa_device_name = g_strdup(pars[1]);
-
- ret = _alsa_open(alsa_id);
- if (ret) {
- ERR("Cannot initialize Alsa device '%s': Can't open.",
- alsa_id->alsa_device_name);
- g_free(alsa_id);
- return NULL;
- }
-
- MSG(1, "Device '%s' initialized succesfully.",
- alsa_id->alsa_device_name);
-
- return (AudioID *) alsa_id;
-}
-
-/* Close ALSA */
-static int alsa_close(AudioID * id)
-{
- int err;
- spd_alsa_id_t *alsa_id = (spd_alsa_id_t *) id;
-
- /* Close device */
- if ((err = _alsa_close(alsa_id)) < 0) {
- ERR("Cannot close audio device");
- return -1;
- }
- MSG(1, "ALSA closed.");
-
- g_free(alsa_id->alsa_device_name);
- g_free(alsa_id);
- id = NULL;
-
- return 0;
-}
-
-/* Wait until ALSA is readdy for more samples or alsa_stop() was called.
-
-Returns 0 if ALSA is ready for more input, +1 if a request to stop
-the sound output was received and a negative value on error. */
-
-int wait_for_poll(spd_alsa_id_t * id, struct pollfd *alsa_poll_fds,
- unsigned int count, int draining)
-{
- unsigned short revents;
- snd_pcm_state_t state;
- int ret;
-
- // MSG("Waiting for poll");
-
- /* Wait for certain events */
- while (1) {
- ret = poll(id->alsa_poll_fds, count, -1);
- // MSG("wait_for_poll: activity on %d descriptors", ret);
-
- /* Check for stop request from alsa_stop on the last file
- descriptors */
- revents = id->alsa_poll_fds[count - 1].revents;
- if (0 != revents) {
- if (revents & POLLIN) {
- MSG(4, "wait_for_poll: stop requested");
- return 1;
- }
- }
-
- /* Check the first count-1 descriptors for ALSA events */
- snd_pcm_poll_descriptors_revents(id->alsa_pcm,
- id->alsa_poll_fds, count - 1,
- &revents);
-
- /* Ensure we are in the right state */
- state = snd_pcm_state(id->alsa_pcm);
- // MSG("State after poll returned is %s",
snd_pcm_state_name(state));
-
- if (SND_PCM_STATE_XRUN == state) {
- if (!draining) {
- MSG(1, "WARNING: Buffer underrun detected!");
- if (xrun(id) != 0)
- return -1;
- return 0;
- } else {
- MSG(4, "Poll: Playback terminated");
- return 0;
- }
- }
-
- if (SND_PCM_STATE_SUSPENDED == state) {
- MSG(1, "WARNING: Suspend detected!");
- if (suspend(id) != 0)
- return -1;
- return 0;
- }
-
- /* Check for errors */
- if (revents & POLLERR) {
- MSG(4, "wait_for_poll: poll revents says POLLERR");
- return -EIO;
- }
-
- /* Is ALSA ready for more input? */
- if ((revents & POLLOUT)) {
- // MSG("Poll: Ready for more input");
- return 0;
- }
- }
-}
-
-#define ERROR_EXIT()\
- g_free(track_volume.samples); \
- ERR("alsa_play() abnormal exit"); \
- _alsa_close(alsa_id); \
- return -1;
-
-/* Play the track _track_ (see spd_audio.h) using the id->alsa_pcm device and
- id-hw_params parameters. This is a blocking function, however, it's possible
- to interrupt playing from a different thread with alsa_stop(). alsa_play
- returns after and immediatelly after the whole sound was played on the
- speakers.
-
- The idea is that we get the ALSA file descriptors and we will poll() to see
- when alsa is ready for more input while sleeping in the meantime. We will
- additionally poll() for one more descriptor used by alsa_stop() to notify the
- thread with alsa_play() that the stop of the playback is requested. The
- variable can_be_stopped is used for very simple synchronization between the
- two threads. */
-static int alsa_play(AudioID * id, AudioTrack track)
-{
- snd_pcm_format_t format;
- int bytes_per_sample;
- int num_bytes;
- spd_alsa_id_t *alsa_id = (spd_alsa_id_t *) id;
-
- signed short *output_samples;
-
- AudioTrack track_volume;
- float real_volume;
- int i;
-
- int err;
- int ret;
-
- snd_pcm_uframes_t framecount;
- snd_pcm_uframes_t period_size;
- size_t samples_per_period;
- size_t silent_samples;
- size_t volume_size;
- unsigned int sr;
-
- snd_pcm_state_t state;
-
- struct pollfd alsa_stop_pipe_pfd;
-
- if (alsa_id == NULL) {
- ERR("Invalid device passed to alsa_play()");
- return -1;
- }
-
- pthread_mutex_lock(&alsa_id->alsa_pipe_mutex);
-
- MSG(2, "Start of playback on ALSA");
-
- /* Is it not an empty track? */
- /* Passing an empty track is not an error */
- if (track.samples == NULL) {
- pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
- return 0;
- }
- /* Allocate space for hw_params (description of the sound parameters) */
- MSG(2, "Allocating new hw_params structure");
- if ((err = snd_pcm_hw_params_malloc(&alsa_id->alsa_hw_params)) < 0) {
- ERR("Cannot allocate hardware parameter structure (%s)",
- snd_strerror(err));
- pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
- return -1;
- }
-
- /* Initialize hw_params on our pcm */
- if ((err =
- snd_pcm_hw_params_any(alsa_id->alsa_pcm,
- alsa_id->alsa_hw_params)) < 0) {
- ERR("Cannot initialize hardware parameter structure (%s)",
- snd_strerror(err));
- pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
- return -1;
- }
-
- /* Create the pipe for communication about stop requests */
- if (pipe(alsa_id->alsa_stop_pipe)) {
- ERR("Stop pipe creation failed (%s)", strerror(errno));
- pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
- return -1;
- }
-
- /* Find how many descriptors we will get for poll() */
- alsa_id->alsa_fd_count =
- snd_pcm_poll_descriptors_count(alsa_id->alsa_pcm);
- if (alsa_id->alsa_fd_count <= 0) {
- ERR("Invalid poll descriptors count returned from ALSA.");
- pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
- return -1;
- }
-
- /* Create and fill in struct pollfd *alsa_poll_fds with ALSA
descriptors */
- alsa_id->alsa_poll_fds =
- g_malloc((alsa_id->alsa_fd_count + 1) * sizeof(struct pollfd));
- assert(alsa_id->alsa_poll_fds);
- if ((err =
- snd_pcm_poll_descriptors(alsa_id->alsa_pcm, alsa_id->alsa_poll_fds,
- alsa_id->alsa_fd_count)) < 0) {
- ERR("Unable to obtain poll descriptors for playback: %s\n",
- snd_strerror(err));
- pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
- return -1;
- }
-
- /* Create a new pollfd structure for requests by alsa_stop() */
- alsa_stop_pipe_pfd.fd = alsa_id->alsa_stop_pipe[0];
- alsa_stop_pipe_pfd.events = POLLIN;
- alsa_stop_pipe_pfd.revents = 0;
-
- /* Join this our own pollfd to the ALSAs ones */
- alsa_id->alsa_poll_fds[alsa_id->alsa_fd_count] = alsa_stop_pipe_pfd;
- alsa_id->alsa_fd_count++;
-
- alsa_id->alsa_opened = 1;
- pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
-
- /* Report current state */
- state = snd_pcm_state(alsa_id->alsa_pcm);
- MSG(4, "PCM state before setting audio parameters: %s",
- snd_pcm_state_name(state));
-
- /* Choose the correct format */
- if (track.bits == 16) {
- switch (alsa_id->id.format) {
- case SPD_AUDIO_LE:
- format = SND_PCM_FORMAT_S16_LE;
- break;
- case SPD_AUDIO_BE:
- format = SND_PCM_FORMAT_S16_BE;
- break;
- default:
- ERR("unknown audio format (%d)", alsa_id->id.format);
- return -1;
- }
- bytes_per_sample = 2;
- } else if (track.bits == 8) {
- bytes_per_sample = 1;
- format = SND_PCM_FORMAT_S8;
- } else {
- ERR("Unsupported sound data format, track.bits = %d",
- track.bits);
- return -1;
- }
-
- /* Set access mode, bitrate, sample rate and channels */
- MSG(4, "Setting access type to INTERLEAVED");
- if ((err = snd_pcm_hw_params_set_access(alsa_id->alsa_pcm,
- alsa_id->alsa_hw_params,
- SND_PCM_ACCESS_RW_INTERLEAVED)
- ) < 0) {
- ERR("Cannot set access type (%s)", snd_strerror(err));
- return -1;
- }
-
- MSG(4, "Setting sample format to %s", snd_pcm_format_name(format));
- if ((err =
- snd_pcm_hw_params_set_format(alsa_id->alsa_pcm,
- alsa_id->alsa_hw_params,
- format)) < 0) {
- ERR("Cannot set sample format (%s)", snd_strerror(err));
- return -1;
- }
-
- MSG(4, "Setting sample rate to %i", track.sample_rate);
- sr = track.sample_rate;
- if ((err =
- snd_pcm_hw_params_set_rate_near(alsa_id->alsa_pcm,
- alsa_id->alsa_hw_params, &sr,
- 0)) < 0) {
- ERR("Cannot set sample rate (%s)", snd_strerror(err));
-
- return -1;
- }
-
- MSG(4, "Setting channel count to %i", track.num_channels);
- if ((err =
- snd_pcm_hw_params_set_channels(alsa_id->alsa_pcm,
- alsa_id->alsa_hw_params,
- track.num_channels)) < 0) {
- MSG(4, "cannot set channel count (%s)", snd_strerror(err));
- return -1;
- }
-
- MSG(4, "Setting hardware parameters on the ALSA device");
- if ((err =
- snd_pcm_hw_params(alsa_id->alsa_pcm,
- alsa_id->alsa_hw_params)) < 0) {
- MSG(4, "cannot set parameters (%s) state=%s", snd_strerror(err),
- snd_pcm_state_name(snd_pcm_state(alsa_id->alsa_pcm)));
- return -1;
- }
-
- /* Get the current swparams */
- if ((err =
- snd_pcm_sw_params_current(alsa_id->alsa_pcm,
- alsa_id->alsa_sw_params)) < 0) {
- ERR("Unable to determine current swparams for playback: %s\n",
- snd_strerror(err));
- return -1;
- }
- // MSG("Checking buffer size");
- if ((err =
- snd_pcm_hw_params_get_buffer_size(alsa_id->alsa_hw_params,
- &(alsa_id->alsa_buffer_size))) <
- 0) {
- ERR("Unable to get buffer size for playback: %s\n",
- snd_strerror(err));
- return -1;
- }
- MSG(4, "Buffer size on ALSA device is %d bytes",
- (int)alsa_id->alsa_buffer_size);
-
- /* This is probably better left for the device driver to decide */
- /* allow the transfer when at least period_size samples can be
processed */
- /* err = snd_pcm_sw_params_set_avail_min(id->alsa_pcm,
id->alsa_sw_params, id->alsa_buffer_size/4);
- if (err < 0) {
- ERR("Unable to set avail min for playback: %s\n", snd_strerror(err));
- return err;
- } */
-
- /* Get period size. */
- snd_pcm_hw_params_get_period_size(alsa_id->alsa_hw_params, &period_size,
- 0);
-
- /* Calculate size of silence at end of buffer. */
- samples_per_period = period_size * track.num_channels;
- // MSG("samples per period = %i", samples_per_period);
- // MSG("num_samples = %i", track.num_samples);
- silent_samples =
- samples_per_period - (track.num_samples % samples_per_period);
- // MSG("silent samples = %i", silent_samples);
-
- MSG(4, "Preparing device for playback");
- if ((err = snd_pcm_prepare(alsa_id->alsa_pcm)) < 0) {
- ERR("Cannot prepare audio interface for playback (%s)",
- snd_strerror(err));
-
- return -1;
- }
-
- /* Calculate space needed to round up to nearest period size. */
- volume_size = bytes_per_sample * (track.num_samples + silent_samples);
- MSG(4, "volume size = %i", (int)volume_size);
-
- /* Create a copy of track with adjusted volume. */
- MSG(4, "Making copy of track and adjusting volume");
- track_volume = track;
- track_volume.samples = (short *)g_malloc(volume_size);
- real_volume = ((float)alsa_id->id.volume + 100) / (float)200;
- for (i = 0; i <= track.num_samples - 1; i++)
- track_volume.samples[i] = track.samples[i] * real_volume;
-
- if (silent_samples > 0) {
- u_int16_t silent16;
- u_int8_t silent8;
-
- /* Fill remaining space with silence */
- MSG(4,
- "Filling with silence up to the period size,
silent_samples=%d",
- (int)silent_samples);
- /* TODO: This hangs. Why?
- snd_pcm_format_set_silence(format,
- track_volume.samples + (track.num_samples *
bytes_per_sample), silent_samples);
- */
- switch (bytes_per_sample) {
- case 2:
- silent16 = snd_pcm_format_silence_16(format);
- for (i = 0; i < silent_samples; i++)
- track_volume.samples[track.num_samples + i] =
- silent16;
- break;
- case 1:
- silent8 = snd_pcm_format_silence(format);
- for (i = 0; i < silent_samples; i++)
- track_volume.samples[track.num_samples + i] =
- silent8;
- break;
- }
- }
-
- /* Loop until all samples are played on the device. */
- output_samples = track_volume.samples;
- num_bytes = (track.num_samples + silent_samples) * bytes_per_sample;
- // MSG("Still %d bytes left to be played", num_bytes);
- while (num_bytes > 0) {
-
- /* Write as much samples as possible */
- framecount = num_bytes / bytes_per_sample / track.num_channels;
- if (framecount < period_size)
- framecount = period_size;
-
- /* Report current state state */
- state = snd_pcm_state(alsa_id->alsa_pcm);
- // MSG("PCM state before writei: %s",
- // snd_pcm_state_name(state));
-
- /* MSG("snd_pcm_writei() called") */
- ret =
- snd_pcm_writei(alsa_id->alsa_pcm, output_samples,
- framecount);
- // MSG("Sent %d of %d remaining bytes",
ret*bytes_per_sample, num_bytes);
-
- if (ret == -EAGAIN) {
- MSG(4, "Warning: Forced wait!");
- snd_pcm_wait(alsa_id->alsa_pcm, 100);
- } else if (ret == -EPIPE) {
- if (xrun(alsa_id) != 0)
- ERROR_EXIT();
- } else if (ret == -ESTRPIPE) {
- if (suspend(alsa_id) != 0)
- ERROR_EXIT();
- } else if (ret == -EBUSY) {
- MSG(4, "WARNING: sleeping while PCM BUSY");
- usleep(100);
- continue;
- } else if (ret < 0) {
- ERR("Write to audio interface failed (%s)",
- snd_strerror(ret));
- ERROR_EXIT();
- }
-
- if (ret > 0) {
- /* Update counter of bytes left and move the data
pointer */
- num_bytes -=
- ret * bytes_per_sample * track.num_channels;
- output_samples +=
- ret * bytes_per_sample * track.num_channels / 2;
- }
-
- /* Report current state */
- state = snd_pcm_state(alsa_id->alsa_pcm);
- // MSG("PCM state before polling: %s",
- // snd_pcm_state_name(state));
-
- err =
- wait_for_poll(alsa_id, alsa_id->alsa_poll_fds,
- alsa_id->alsa_fd_count, 0);
- if (err < 0) {
- ERR("Wait for poll() failed\n");
- ERROR_EXIT();
- } else if (err == 1) {
- MSG(4, "Playback stopped");
-
- /* Drop the playback on the sound device (probably
- still in progress up till now) */
- err = snd_pcm_drop(alsa_id->alsa_pcm);
- if (err < 0) {
- ERR("snd_pcm_drop() failed: %s",
- snd_strerror(err));
- return -1;
- }
-
- goto terminate;
- }
-
- if (num_bytes <= 0)
- break;
-// MSG("ALSA ready for more samples");
-
- /* Stop requests can be issued again */
- }
-
- MSG(4, "Draining...");
-
- /* We want to next "device ready" notification only after the buffer is
- already empty */
- err =
- snd_pcm_sw_params_set_avail_min(alsa_id->alsa_pcm,
- alsa_id->alsa_sw_params,
- alsa_id->alsa_buffer_size);
- if (err < 0) {
- ERR("Unable to set avail min for playback: %s\n",
- snd_strerror(err));
- return err;
- }
- /* write the parameters to the playback device */
- err = snd_pcm_sw_params(alsa_id->alsa_pcm, alsa_id->alsa_sw_params);
- if (err < 0) {
- ERR("Unable to set sw params for playback: %s\n",
- snd_strerror(err));
- return -1;
- }
-
- err =
- wait_for_poll(alsa_id, alsa_id->alsa_poll_fds,
- alsa_id->alsa_fd_count, 1);
- if (err < 0) {
- ERR("Wait for poll() failed\n");
- return -1;
- } else if (err == 1) {
- MSG(4, "Playback stopped while draining");
-
- /* Drop the playback on the sound device (probably
- still in progress up till now) */
- err = snd_pcm_drop(alsa_id->alsa_pcm);
- if (err < 0) {
- ERR("snd_pcm_drop() failed: %s", snd_strerror(err));
- return -1;
- }
- }
- MSG(4, "Draining terminated");
-
-terminate:
- /* Terminating (successfully or after a stop) */
- if (track_volume.samples != NULL)
- g_free(track_volume.samples);
-
- err = snd_pcm_drop(alsa_id->alsa_pcm);
- if (err < 0) {
- ERR("snd_pcm_drop() failed: %s", snd_strerror(err));
- return -1;
- }
-
- MSG(2, "Freeing HW parameters");
- snd_pcm_hw_params_free(alsa_id->alsa_hw_params);
-
- pthread_mutex_lock(&alsa_id->alsa_pipe_mutex);
- alsa_id->alsa_opened = 0;
- close(alsa_id->alsa_stop_pipe[0]);
- close(alsa_id->alsa_stop_pipe[1]);
-
- g_free(alsa_id->alsa_poll_fds);
- pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
-
- MSG(1, "End of playback on ALSA");
-
- return 0;
-}
-
-#undef ERROR_EXIT
-
-/*
- Stop the playback on the device and interrupt alsa_play()
-*/
-static int alsa_stop(AudioID * id)
-{
- char buf;
- int ret;
- spd_alsa_id_t *alsa_id = (spd_alsa_id_t *) id;
-
- MSG(1, "STOP!");
-
- if (alsa_id == NULL)
- return 0;
-
- pthread_mutex_lock(&alsa_id->alsa_pipe_mutex);
- if (alsa_id->alsa_opened) {
- /* This constant is arbitrary */
- buf = 42;
-
- ret = write(alsa_id->alsa_stop_pipe[1], &buf, 1);
- if (ret <= 0) {
- ERR("Can't write stop request to pipe, err %d: %s",
- errno, strerror(errno));
- }
- }
- pthread_mutex_unlock(&alsa_id->alsa_pipe_mutex);
-
- return 0;
-}
-
-/*
- Set volume
-
- Comments: It's not possible to set individual track volume with Alsa, so we
- handle volume in alsa_play() by multiplication of each sample.
-*/
-static int alsa_set_volume(AudioID * id, int volume)
-{
- return 0;
-}
-
-static void alsa_set_loglevel(int level)
-{
- if (level) {
- alsa_log_level = level;
- }
-}
-
-static char const *alsa_get_playcmd(void)
-{
- return alsa_play_cmd;
-}
-
-/* Provide the Alsa backend. */
-static spd_audio_plugin_t alsa_functions = {
- "alsa",
- alsa_open,
- alsa_play,
- alsa_stop,
- alsa_close,
- alsa_set_volume,
- alsa_set_loglevel,
- alsa_get_playcmd
-};
-
-spd_audio_plugin_t *alsa_plugin_get(void)
-{
- return &alsa_functions;
-}
-
-spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
- __attribute__ ((weak, alias("alsa_plugin_get")));
-#undef MSG
-#undef ERR
diff --git a/src/modules/audio/libao.c b/src/modules/audio/libao.c
deleted file mode 100644
index e82be2b..0000000
--- a/src/modules/audio/libao.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * libao.c -- The libao backend for the spd_audio library.
- *
- * Author: Marco Skambraks <marco at openblinux.de>
- * Date: 2009-12-15
- *
- * This is free software; you can redistribute it and/or modify it under the
- * terms of the GNU Leser General Public License as published by the Free
- * Software Foundation; either version 2.1, or (at your option) any later
- * version.
- *
- * This software 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 Lesser General Public License
- * along with this package; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <sys/time.h>
-#include <time.h>
-#include <string.h>
-#include <glib.h>
-#include <ao/ao.h>
-
-#define SPD_AUDIO_PLUGIN_ENTRY spd_libao_LTX_spd_audio_plugin_get
-#include <spd_audio_plugin.h>
-
-/* send a packet of XXX bytes to the sound device */
-#define AO_SEND_BYTES 256
-/* Put a message into the logfile (stderr) */
-#define MSG(level, arg...) \
- if(level <= libao_log_level){ \
- time_t t; \
- struct timeval tv; \
- char *tstr; \
- t = time(NULL); \
- tstr = g_strdup(ctime(&t)); \
- tstr[strlen(tstr)-1] = 0; \
- gettimeofday(&tv,NULL); \
- fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
- fprintf(stderr," libao:: "); \
- fprintf(stderr,arg); \
- fprintf(stderr,"\n"); \
- fflush(stderr); \
- g_free(tstr); \
- }
-
-#define ERR(arg...) \
- { \
- time_t t; \
- struct timeval tv; \
- char *tstr; \
- t = time(NULL); \
- tstr = g_strdup(ctime(&t)); \
- tstr[strlen(tstr)-1] = 0; \
- gettimeofday(&tv,NULL); \
- fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
- fprintf(stderr," libao ERROR: "); \
- fprintf(stderr,arg); \
- fprintf(stderr,"\n"); \
- fflush(stderr); \
- g_free(tstr); \
- }
-
-/* AO_FORMAT_INITIALIZER is an ao_sample_format structure with zero values
- in all of its fields. We can guarantee that the fields of a
- stack-allocated ao_sample_format are zeroed by assigning
- AO_FORMAT_INITIALIZER to it.
- This is the most portable way to initialize a stack-allocated struct to
- zero. */
-static ao_sample_format AO_FORMAT_INITIALIZER;
-static ao_sample_format current_ao_parameters;
-
-static volatile int ao_stop_playback = 0;
-
-static int default_driver;
-static int libao_log_level;
-
-ao_device *device = NULL;
-
-static inline void libao_open_handle(int rate, int channels, int bits)
-{
- ao_sample_format format = AO_FORMAT_INITIALIZER;
-
- format.channels = channels;
- format.rate = rate;
- format.bits = bits;
- format.byte_format = AO_FMT_NATIVE;
- device = ao_open_live(default_driver, &format, NULL);
-
- if (device != NULL)
- current_ao_parameters = format;
-}
-
-static inline void libao_close_handle(void)
-{
- if (device != NULL) {
- ao_close(device);
- device = NULL;
- }
-}
-
-static AudioID *libao_open(void **pars)
-{
- AudioID *id;
-
- id = (AudioID *) g_malloc(sizeof(AudioID));
-
- ao_initialize();
- default_driver = ao_default_driver_id();
- return id;
-}
-
-static int libao_play(AudioID * id, AudioTrack track)
-{
- int bytes_per_sample;
-
- int num_bytes;
-
- int outcnt = 0;
-
- signed short *output_samples;
-
- int i;
-
- if (id == NULL)
- return -1;
- if (track.samples == NULL || track.num_samples <= 0)
- return 0;
-
- /* Choose the correct format */
- if (track.bits == 16)
- bytes_per_sample = 2;
- else if (track.bits == 8)
- bytes_per_sample = 1;
- else {
- ERR("Audio: Unrecognized sound data format.\n");
- return -10;
- }
- MSG(3, "Starting playback");
- output_samples = track.samples;
- num_bytes = track.num_samples * bytes_per_sample;
-
- if ((device == NULL)
- || (track.num_channels != current_ao_parameters.channels)
- || (track.sample_rate != current_ao_parameters.rate)
- || (track.bits != current_ao_parameters.bits)) {
- libao_close_handle();
- libao_open_handle(track.sample_rate, track.num_channels,
- track.bits);
- }
-
- if (device == NULL) {
- ERR("error opening libao dev");
- return -2;
- }
- MSG(3, "bytes to play: %d, (%f secs)", num_bytes,
- (((float)(num_bytes) / 2) / (float)track.sample_rate));
-
- ao_stop_playback = 0;
- outcnt = 0;
- i = 0;
-
- while ((outcnt < num_bytes) && !ao_stop_playback) {
- if ((num_bytes - outcnt) > AO_SEND_BYTES)
- i = AO_SEND_BYTES;
- else
- i = (num_bytes - outcnt);
-
- if (!ao_play(device, (char *)output_samples + outcnt, i)) {
- libao_close_handle();
- ERR("Audio: ao_play() - closing device - re-open it in
next run\n");
- return -1;
- }
- outcnt += i;
- }
-
- return 0;
-
-}
-
-/* stop the libao_play() loop */
-static int libao_stop(AudioID * id)
-{
-
- ao_stop_playback = 1;
- return 0;
-}
-
-static int libao_close(AudioID * id)
-{
- libao_close_handle();
- ao_shutdown();
-
- g_free(id);
- id = NULL;
- return 0;
-}
-
-static int libao_set_volume(AudioID * id, int volume)
-{
- return 0;
-}
-
-static void libao_set_loglevel(int level)
-{
- if (level) {
- libao_log_level = level;
- }
-}
-
-static char const *libao_get_playcmd(void)
-{
- return NULL;
-}
-
-/* Provide the libao backend. */
-static spd_audio_plugin_t libao_functions = {
- "libao",
- libao_open,
- libao_play,
- libao_stop,
- libao_close,
- libao_set_volume,
- libao_set_loglevel,
- libao_get_playcmd
-};
-
-spd_audio_plugin_t *libao_plugin_get(void)
-{
- return &libao_functions;
-}
-
-spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
- __attribute__ ((weak, alias("libao_plugin_get")));
-#undef MSG
-#undef ERR
diff --git a/src/modules/audio/nas.c b/src/modules/audio/nas.c
deleted file mode 100644
index ca76127..0000000
--- a/src/modules/audio/nas.c
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * nas.c -- The Network Audio System backend for the spd_audio library.
- *
- * Copyright (C) 2004,2006 Brailcom, o.p.s.
- *
- * This is free software; you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1, or (at your option) any later
- * version.
- *
- * This software 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 Lesser General Public License
- * along with this package; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * $Id: nas.c,v 1.8 2006-07-11 16:12:26 hanke Exp $
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <audio/audiolib.h>
-#include <audio/soundlib.h>
-
-#include <pthread.h>
-
-#define SPD_AUDIO_PLUGIN_ENTRY spd_nas_LTX_spd_audio_plugin_get
-#include <spd_audio_plugin.h>
-
-typedef struct {
- AudioID id;
- AuServer *aud;
- AuFlowID flow;
- pthread_mutex_t flow_mutex;
- pthread_t nas_event_handler;
- pthread_cond_t pt_cond;
- pthread_mutex_t pt_mutex;
-} spd_nas_id_t;
-
-static int nas_log_level;
-
-/* Internal event handler */
-static void *_nas_handle_events(void *par)
-{
- spd_nas_id_t *nas_id = (spd_nas_id_t *) par;
- pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
-
- while (1)
- AuHandleEvents(nas_id->aud);
-
-}
-
-/* NAS Server error handler */
-/* Unfortunatelly we can't return these errors to the caller
- since this handler gets called in the event handler thread. */
-static AuBool _nas_handle_server_error(AuServer * server, AuErrorEvent * event)
-{
- fprintf(stderr, "ERROR: Non-fatal server error in NAS\n");
-
- if (event->type != 0) {
- fprintf(stderr,
- "Event of a different type received in NAS error
handler.");
- return -1;
- }
-
- /* It's a pain but we can't ask for string return code
- since it's not allowed to talk to the server inside error handlers
- because of possible deadlocks. */
- fprintf(stderr, "NAS: Serial number of failed request: %d\n",
- event->serial);
- fprintf(stderr, "NAS: Error code: %d\n", event->error_code);
- fprintf(stderr, "NAS: Resource id: %d\n", event->resourceid);
- fprintf(stderr, "NAS: Request code: %d\n", event->request_code);
- fprintf(stderr, "NAS: Minor code: %d\n\n", event->minor_code);
-
- return 0;
-}
-
-static AudioID *nas_open(void **pars)
-{
- spd_nas_id_t *nas_id;
- int ret;
- AuBool r;
-
- nas_id = (spd_nas_id_t *) g_malloc(sizeof(spd_nas_id_t));
-
- nas_id->aud = AuOpenServer(pars[2], 0, NULL, 0, NULL, NULL);
- if (!nas_id->aud) {
- fprintf(stderr, "Can't connect to NAS audio server\n");
- return NULL;
- }
-
- AuSetErrorHandler(nas_id->aud, _nas_handle_server_error);
- /* return value incompatible with documentation here */
- /* if (!r){
- fprintf(stderr, "Can't set default NAS event handler\n");
- return -1;
- } */
-
- nas_id->flow = 0;
-
- pthread_cond_init(&nas_id->pt_cond, NULL);
- pthread_mutex_init(&nas_id->pt_mutex, NULL);
- pthread_mutex_init(&nas_id->flow_mutex, NULL);
-
- ret =
- pthread_create(&nas_id->nas_event_handler, NULL, _nas_handle_events,
- (void *)nas_id);
- if (ret != 0) {
- fprintf(stderr,
- "ERROR: NAS Audio module: thread creation failed\n");
- return NULL;
- }
-
- return (AudioID *) nas_id;
-}
-
-static int nas_play(AudioID * id, AudioTrack track)
-{
- char *buf;
- Sound s;
- AuEventHandlerRec *event_handler;
- int ret;
- float lenght;
- struct timeval now;
- struct timespec timeout;
- spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
-
- if (nas_id == NULL)
- return -2;
-
- s = SoundCreate(SoundFileFormatNone,
- AuFormatLinearSigned16LSB,
- track.num_channels,
- track.sample_rate, track.num_samples, NULL);
-
- buf = (char *)track.samples;
-
- pthread_mutex_lock(&nas_id->flow_mutex);
-
- event_handler = AuSoundPlayFromData(nas_id->aud,
- s,
- buf,
- AuNone,
- ((nas_id->id.volume +
- 100) / 2) * 1500, NULL, NULL,
- &nas_id->flow, NULL, NULL, NULL);
-
- if (event_handler == NULL) {
- fprintf(stderr,
- "AuSoundPlayFromData failed for unknown resons.\n");
- return -1;
- }
-
- if (nas_id->flow == 0) {
- fprintf(stderr, "Couldn't start data flow");
- }
- pthread_mutex_unlock(&nas_id->flow_mutex);
-
- /* Another timing magic */
- pthread_mutex_lock(&nas_id->pt_mutex);
- lenght = (((float)track.num_samples) / (float)track.sample_rate);
- gettimeofday(&now, NULL);
- timeout.tv_sec = now.tv_sec + (int)lenght;
- timeout.tv_nsec =
- now.tv_usec * 1000 + (lenght - (int)lenght) * 1000000000;
- pthread_cond_timedwait(&nas_id->pt_cond, &nas_id->pt_mutex, &timeout);
- pthread_mutex_unlock(&nas_id->pt_mutex);
-
- pthread_mutex_lock(&nas_id->flow_mutex);
- nas_id->flow = 0;
- pthread_mutex_unlock(&nas_id->flow_mutex);
-
- return 0;
-}
-
-static int nas_stop(AudioID * id)
-{
- int ret;
- spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
-
- if (nas_id == NULL)
- return -2;
-
- pthread_mutex_lock(&nas_id->flow_mutex);
- if (nas_id->flow != 0)
- AuStopFlow(nas_id->aud, nas_id->flow, NULL);
- nas_id->flow = 0;
- pthread_mutex_unlock(&nas_id->flow_mutex);
-
- pthread_mutex_lock(&nas_id->pt_mutex);
- pthread_cond_signal(&nas_id->pt_cond);
- pthread_mutex_unlock(&nas_id->pt_mutex);
-
- return 0;
-}
-
-static int nas_close(AudioID * id)
-{
- spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
-
- if (nas_id == NULL)
- return -2;
-
- pthread_cancel(nas_id->nas_event_handler);
- pthread_join(nas_id->nas_event_handler, NULL);
-
- pthread_mutex_destroy(&nas_id->pt_mutex);
- pthread_mutex_destroy(&nas_id->flow_mutex);
-
- AuCloseServer(nas_id->aud);
-
- g_free(nas_id);
- id = NULL;
-
- return 0;
-}
-
-static int nas_set_volume(AudioID * id, int volume)
-{
- return 0;
-}
-
-static void nas_set_loglevel(int level)
-{
- if (level) {
- nas_log_level = level;
- }
-}
-
-static char const *nas_get_playcmd(void)
-{
- return NULL;
-}
-
-/* Provide the NAS backend */
-static spd_audio_plugin_t nas_functions = {
- "nas",
- nas_open,
- nas_play,
- nas_stop,
- nas_close,
- nas_set_volume,
- nas_set_loglevel,
- nas_get_playcmd
-};
-
-spd_audio_plugin_t *nas_plugin_get(void)
-{
- return &nas_functions;
-}
-
-spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
- __attribute__ ((weak, alias("nas_plugin_get")));
diff --git a/src/modules/audio/oss.c b/src/modules/audio/oss.c
deleted file mode 100644
index b7232c3..0000000
--- a/src/modules/audio/oss.c
+++ /dev/null
@@ -1,523 +0,0 @@
-
-/*
- * oss.c -- The Open Sound System backend for the spd_audio library.
- *
- * Copyright (C) 2004,2006 Brailcom, o.p.s.
- *
- * This is free software; you can redistribute it and/or modify it under the
- * terms of the GNU Leser General Public License as published by the Free
- * Software Foundation; either version 2.1, or (at your option) any later
- * version.
- *
- * This software 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 Lesser General Public License
- * along with this package; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * $Id: oss.c,v 1.13 2006-07-11 16:12:26 hanke Exp $
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <sys/time.h>
-#include <time.h>
-#include <string.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/soundcard.h>
-#include <errno.h>
-#include <unistd.h> /* for open, close */
-#include <sys/ioctl.h>
-#include <pthread.h>
-#include <glib.h>
-
-#include <sys/soundcard.h>
-
-#define SPD_AUDIO_PLUGIN_ENTRY spd_oss_LTX_spd_audio_plugin_get
-#include <spd_audio_plugin.h>
-
-typedef struct {
- AudioID id;
- int fd;
- char *device_name;
- pthread_mutex_t fd_mutex;
- pthread_cond_t pt_cond;
- pthread_mutex_t pt_mutex;
-} spd_oss_id_t;
-
-static int _oss_open(spd_oss_id_t * id);
-static int _oss_close(spd_oss_id_t * id);
-static int _oss_sync(spd_oss_id_t * id);
-
-/* Put a message into the logfile (stderr) */
-#define MSG(level, arg...) \
- if(level <= oss_log_level){ \
- time_t t; \
- struct timeval tv; \
- char *tstr; \
- t = time(NULL); \
- tstr = g_strdup(ctime(&t)); \
- tstr[strlen(tstr)-1] = 0; \
- gettimeofday(&tv,NULL); \
- fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
- fprintf(stderr," OSS: "); \
- fprintf(stderr,arg); \
- fprintf(stderr,"\n"); \
- fflush(stderr); \
- g_free(tstr); \
- }
-
-#define ERR(arg...) \
- { \
- time_t t; \
- struct timeval tv; \
- char *tstr; \
- t = time(NULL); \
- tstr = g_strdup(ctime(&t)); \
- tstr[strlen(tstr)-1] = 0; \
- gettimeofday(&tv,NULL); \
- fprintf(stderr," %s [%d]",tstr, (int) tv.tv_usec); \
- fprintf(stderr," OSS ERROR: "); \
- fprintf(stderr,arg); \
- fprintf(stderr,"\n"); \
- fflush(stderr); \
- g_free(tstr); \
- }
-
-static int oss_log_level;
-static char const *oss_play_cmd = "play";
-
-static int _oss_open(spd_oss_id_t * id)
-{
- MSG(1, "_oss_open()")
- pthread_mutex_lock(&id->fd_mutex);
-
- id->fd = open(id->device_name, O_WRONLY, 0);
- if (id->fd < 0) {
- perror(id->device_name);
- pthread_mutex_unlock(&id->fd_mutex);
- id = NULL;
- return -1;
- }
-
- pthread_mutex_unlock(&id->fd_mutex);
-
- return 0;
-}
-
-static int _oss_close(spd_oss_id_t * id)
-{
- MSG(1, "_oss_close()")
- if (id == NULL)
- return 0;
- if (id->fd < 0)
- return 0;
-
- pthread_mutex_lock(&id->fd_mutex);
- close(id->fd);
- id->fd = -1;
- pthread_mutex_unlock(&id->fd_mutex);
- return 0;
-}
-
-/* Open OSS device
- Arguments:
- **pars:
- (char*) pars[0] -- the name of the device (e.g. "/dev/dsp")
- (void*) pars[1] = NULL
-*/
-static AudioID *oss_open(void **pars)
-{
- spd_oss_id_t *oss_id;
- int ret;
-
- if (pars[0] == NULL)
- return NULL;
-
- oss_id = (spd_oss_id_t *) g_malloc(sizeof(spd_oss_id_t));
-
- oss_id->device_name = g_strdup((char *)pars[0]);
-
- pthread_mutex_init(&oss_id->fd_mutex, NULL);
-
- pthread_cond_init(&oss_id->pt_cond, NULL);
- pthread_mutex_init(&oss_id->pt_mutex, NULL);
-
- /* Test if it's possible to access the device */
- ret = _oss_open(oss_id);
- if (ret) {
- g_free(oss_id->device_name);
- g_free(oss_id);
- return NULL;
- }
- ret = _oss_close(oss_id);
- if (ret) {
- g_free(oss_id->device_name);
- g_free(oss_id);
- return NULL;
- }
-
- return (AudioID *) oss_id;
-}
-
-/* Internal function. */
-static int _oss_sync(spd_oss_id_t * id)
-{
- int ret;
-
- ret = ioctl(id->fd, SNDCTL_DSP_POST, 0);
- if (ret == -1) {
- perror("reset");
- return -1;
- }
- return 0;
-}
-
-static int oss_play(AudioID * id, AudioTrack track)
-{
- int ret, ret2;
- struct timeval now;
- struct timespec timeout;
- float lenght;
- int r = 0;
- int format, oformat, channels, speed;
- int bytes_per_sample;
- int num_bytes;
- signed short *output_samples;
- float delay = 0;
- float DELAY = 0.1; /* in seconds */
- audio_buf_info info;
- int bytes;
- float real_volume;
- int i;
- int re;
- spd_oss_id_t *oss_id = (spd_oss_id_t *) id;
-
- AudioTrack track_volume;
-
- if (oss_id == NULL)
- return -1;
-
- /* Open the sound device. This is necessary for OSS so that the
- application doesn't prevent others from accessing /dev/dsp when
- it doesn't play anything. */
- ret = _oss_open(oss_id);
- if (ret)
- return -2;
-
- /* Create a copy of track with the adjusted volume */
- track_volume = track;
- track_volume.samples =
- (short *)g_malloc(sizeof(short) * track.num_samples);
- real_volume = ((float)id->volume + 100) / (float)200;
- for (i = 0; i <= track.num_samples - 1; i++)
- track_volume.samples[i] = track.samples[i] * real_volume;
-
- /* Choose the correct format */
- if (track.bits == 16) {
- format = AFMT_S16_NE;
- bytes_per_sample = 2;
- } else if (track.bits == 8) {
- bytes_per_sample = 1;
- format = AFMT_S8;
- } else {
- ERR("Audio: Unrecognized sound data format.\n");
- _oss_close(oss_id);
- return -10;
- }
-
- oformat = format;
- ret = ioctl(oss_id->fd, SNDCTL_DSP_SETFMT, &format);
- if (ret == -1) {
- perror("OSS ERROR: format");
- _oss_close(oss_id);
- return -1;
- }
- if (format != oformat) {
- ERR("Device doesn't support 16-bit sound format.\n");
- _oss_close(oss_id);
- return -2;
- }
-
- /* Choose the correct number of channels */
- channels = track.num_channels;
- ret = ioctl(oss_id->fd, SNDCTL_DSP_CHANNELS, &channels);
- if (ret == -1) {
- perror("OSS ERROR: channels");
- _oss_close(oss_id);
- return -3;
- }
- if (channels != track.num_channels) {
- MSG(1, "Device doesn't support stereo sound.\n");
- _oss_close(oss_id);
- return -4;
- }
-
- /* Choose the correct sample rate */
- speed = track.sample_rate;
- ret = ioctl(oss_id->fd, SNDCTL_DSP_SPEED, &speed);
- if (ret == -1) {
- ERR("OSS ERROR: Can't set sample rate %d nor any similar.",
- track.sample_rate);
- _oss_close(oss_id);
- return -5;
- }
- if (speed != track.sample_rate) {
- ERR("Device doesn't support bitrate %d, using %d instead.\n",
- track.sample_rate, speed);
- }
-
- /* Is it not an empty track? */
- if (track.samples == NULL) {
- _oss_close(oss_id);
- return 0;
- }
-
- /* Loop until all samples are played on the device.
- In the meantime, wait in pthread_cond_timedwait for more data
- or for interruption. */
- MSG(4, "Starting playback");
- output_samples = track_volume.samples;
- num_bytes = track.num_samples * bytes_per_sample;
- MSG(4, "bytes to play: %d, (%f secs)", num_bytes,
- (((float)(num_bytes) / 2) / (float)track.sample_rate));
- while (num_bytes > 0) {
-
- /* OSS doesn't support non-blocking write, so lets check how
much data
- can we write so that write() returns immediatelly */
- re = ioctl(oss_id->fd, SNDCTL_DSP_GETOSPACE, &info);
- if (re == -1) {
- perror("OSS ERROR: GETOSPACE");
- _oss_close(oss_id);
- return -5;
- }
-
- /* If there is not enough space for a single fragment, try
later.
- (This shouldn't happen, it has very bad effect on
synchronization!) */
- if (info.fragments == 0) {
- MSG(4,
- "WARNING: There is not enough space for a single
fragment, looping");
- usleep(100);
- continue;
- }
-
- MSG(4,
- "There is space for %d more fragments, fragment size is %d
bytes",
- info.fragments, info.fragsize);
-
- bytes = info.fragments * info.fragsize;
- ret =
- write(oss_id->fd, output_samples,
- num_bytes > bytes ? bytes : num_bytes);
-
- /* Handle write() errors */
- if (ret <= 0) {
- perror("audio");
- _oss_close(oss_id);
- return -6;
- }
-
- num_bytes -= ret;
- output_samples += ret / 2;
-
- MSG(4, "%d bytes written to OSS, %d remaining", ret, num_bytes);
-
- /* If there is some more data that is less than a
- full fragment, we need to write it immediatelly so
- that it doesn't cause buffer underruns later. */
- if ((num_bytes > 0)
- && (num_bytes < info.fragsize)
- && (bytes + num_bytes < info.bytes)) {
-
- MSG(4,
- "Writing the rest of the data (%d bytes) to OSS,
not a full fragment",
- num_bytes);
-
- ret2 = write(oss_id->fd, output_samples, num_bytes);
- num_bytes -= ret2;
- output_samples += ret2 / 2;
- ret += ret2;
- }
-
- /* Handle write() errors */
- if (ret <= 0) {
- perror("audio");
- _oss_close(oss_id);
- return -6;
- }
-
- /* Some timing magic...
- We need to wait for the time computed from the number of
- samples written. But this wait needs to be interruptible
- by oss_stop(). Furthermore, there need to be no buffer
- underrruns, so we actually wait a bit (DELAY) less
- in the first pass through the while() loop. Then our timer
- will be DELAY nsecs backwards.
- */
- MSG(4, "Now we will try to wait");
- pthread_mutex_lock(&oss_id->pt_mutex);
- lenght = (((float)(ret) / 2) / (float)track.sample_rate);
- if (!delay) {
- delay = lenght > DELAY ? DELAY : lenght;
- lenght -= delay;
- }
- MSG(4, "Wait for %f secs (begin: %f, delay: %f)", lenght,
- lenght + delay, delay)
- gettimeofday(&now, NULL);
- timeout.tv_sec = now.tv_sec + (int)lenght;
- timeout.tv_nsec =
- now.tv_usec * 1000 + (lenght - (int)lenght) * 1000000000;
- //MSG("5, waiting till %d:%d (%d:%d | %d:%d)", timeout.tv_sec,
timeout.tv_nsec,
- // now.tv_sec, now.tv_usec*1000, timeout.tv_sec -
now.tv_sec, timeout.tv_nsec-now.tv_usec*1000);
-
- timeout.tv_sec += timeout.tv_nsec / 1000000000;
- timeout.tv_nsec = timeout.tv_nsec % 1000000000;
- // MSG("6, waiting till %d:%d (%d:%d | %d:%d)",
timeout.tv_sec, timeout.tv_nsec,
- // now.tv_sec, now.tv_usec*1000, timeout.tv_sec - now.tv_sec,
timeout.tv_nsec-now.tv_usec*1000);
- r = pthread_cond_timedwait(&oss_id->pt_cond, &oss_id->pt_mutex,
- &timeout);
- pthread_mutex_unlock(&oss_id->pt_mutex);
- MSG(4, "End of wait");
-
- /* The pthread_cond_timedwait was interrupted by change in the
- condition variable? if so, terminate. */
- if (r != ETIMEDOUT) {
- MSG(4, "Playback stopped, %d", r);
- break;
- }
- }
-
- /* ...one more excersise in timing magic.
- Wait for the resting delay secs. */
-
- /* Ugly hack: correct for the time we spend outside timing segments */
- delay -= 0.05;
-
- MSG(4, "Wait for the resting delay = %f secs", delay)
- if ((delay > 0) && (r == ETIMEDOUT)) {
- pthread_mutex_lock(&oss_id->pt_mutex);
- gettimeofday(&now, NULL);
- timeout.tv_sec = now.tv_sec;
- timeout.tv_nsec = now.tv_usec * 1000 + delay * 1000000000;
- // MSG("6, waiting till %d:%d (%d:%d | %d:%d)", timeout.tv_sec,
timeout.tv_nsec,
- // now.tv_sec, now.tv_usec*1000, timeout.tv_sec -
now.tv_sec, timeout.tv_nsec-now.tv_usec*1000);
- timeout.tv_sec += timeout.tv_nsec / 1000000000;
- timeout.tv_nsec = timeout.tv_nsec % 1000000000;
- // MSG("6, waiting till %d:%d (%d:%d | %d:%d)", timeout.tv_sec,
timeout.tv_nsec,
- // now.tv_sec, now.tv_usec*1000, timeout.tv_sec -
now.tv_sec, timeout.tv_nsec-now.tv_usec*1000);
- r = pthread_cond_timedwait(&oss_id->pt_cond, &oss_id->pt_mutex,
- &timeout);
- pthread_mutex_unlock(&oss_id->pt_mutex);
- }
- MSG(4, "End of wait");
-
- if (track_volume.samples != NULL)
- g_free(track_volume.samples);
-
- /* Flush all the buffers */
- _oss_sync(oss_id);
-
- /* Close the device so that we don't block other apps trying to
- access the device. */
- _oss_close(oss_id);
-
- MSG(4, "Device closed");
-
- return 0;
-}
-
-/* Stop the playback on the device and interrupt oss_play */
-static int oss_stop(AudioID * id)
-{
- int ret = 0;
- spd_oss_id_t *oss_id = (spd_oss_id_t *) id;
-
- if (oss_id == NULL)
- return 0;
-
- MSG(4, "stop() called");
-
- /* Stop the playback on /dev/dsp */
- pthread_mutex_lock(&oss_id->fd_mutex);
- if (oss_id->fd >= 0)
- ret = ioctl(oss_id->fd, SNDCTL_DSP_RESET, 0);
- pthread_mutex_unlock(&oss_id->fd_mutex);
- if (ret == -1) {
- perror("reset");
- return -1;
- }
-
- /* Interrupt oss_play by setting the condition variable */
- pthread_mutex_lock(&oss_id->pt_mutex);
- pthread_cond_signal(&oss_id->pt_cond);
- pthread_mutex_unlock(&oss_id->pt_mutex);
- return 0;
-}
-
-/* Close the device */
-static int oss_close(AudioID * id)
-{
- spd_oss_id_t *oss_id = (spd_oss_id_t *) id;
-
- /* Does nothing because the device is being automatically openned and
- closed in oss_play before and after playing each sample. */
-
- g_free(oss_id->device_name);
- g_free(oss_id);
- id = NULL;
-
- return 0;
-}
-
-/* Set volume
-
-Comments:
- /dev/dsp can't set volume. We just multiply the track samples by
- a constant in oss_play (see oss_play() for more information).
-*/
-static int oss_set_volume(AudioID * id, int volume)
-{
- return 0;
-}
-
-static void oss_set_loglevel(int level)
-{
- if (level) {
- oss_log_level = level;
- }
-}
-
-static char const *oss_get_playcmd(void)
-{
- return oss_play_cmd;
-}
-
-/* Provide the OSS backend. */
-static spd_audio_plugin_t oss_functions = {
- "oss",
- oss_open,
- oss_play,
- oss_stop,
- oss_close,
- oss_set_volume,
- oss_set_loglevel,
- oss_get_playcmd
-};
-
-spd_audio_plugin_t *oss_plugin_get(void)
-{
- return &oss_functions;
-}
-
-spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
- __attribute__ ((weak, alias("oss_plugin_get")));
-#undef MSG
-#undef ERR
diff --git a/src/modules/audio/pulse.c b/src/modules/audio/pulse.c
deleted file mode 100644
index a4137e5..0000000
--- a/src/modules/audio/pulse.c
+++ /dev/null
@@ -1,310 +0,0 @@
-
-/*
- * pulse.c -- The simple pulseaudio backend for the spd_audio library.
- *
- * Based on libao.c from Marco Skambraks <marco at openblinux.de>
- * Date: 2009-12-15
- *
- * Copied from Luke Yelavich's libao.c driver, and merged with code from
- * Marco's ao_pulse.c driver, by Bill Cox, Dec 21, 2009.
- *
- * Minor changes be Rui Batista <rui.batista at ist.utl.pt> to configure
settings through speech-dispatcher configuration files
- * Date: Dec 22, 2009
- *
- * This is free software; you can redistribute it and/or modify it under the
- * terms of the GNU Leser General Public License as published by the Free
- * Software Foundation; either version 2.1, or (at your option) any later
- * version.
- *
- * This software 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 Lesser General Public License
- * along with this package; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <time.h>
-#include <string.h>
-#include <stdarg.h>
-#include <glib.h>
-
-#include <pulse/simple.h>
-#include <pulse/error.h>
-
-#define SPD_AUDIO_PLUGIN_ENTRY spd_pulse_LTX_spd_audio_plugin_get
-#include <spd_audio_plugin.h>
-
-/* Switch this on to debug, see output log location in MSG() */
-//#define DEBUG_PULSE
-typedef struct {
- AudioID id;
- pa_simple *pa_simple;
- char *pa_server;
- int pa_min_audio_length;
- volatile int pa_stop_playback;
- int pa_current_rate; // Sample rate for currently PA connection
- int pa_current_bps; // Bits per sample rate for currently PA
connection
- int pa_current_channels; // Number of channels for currently PA
connection
-} spd_pulse_id_t;
-
-/* send a packet of XXX bytes to the sound device */
-#define PULSE_SEND_BYTES 256
-
-/* This is the smallest audio sound we are expected to play immediately
without buffering. */
-/* Changed to define on config file. Default is the same. */
-#define DEFAULT_PA_MIN_AUDIO_LENgTH 100
-
-static int pulse_log_level;
-static char const *pulse_play_cmd = "paplay";
-
-/* Write to /tmp/speech-dispatcher-pulse.log */
-#ifdef DEBUG_PULSE
-static FILE *pulseDebugFile = NULL;
-static void MSG(char *message, ...)
-{
- va_list ap;
-
- if (pulseDebugFile == NULL) {
- pulseDebugFile = fopen("/tmp/speech-dispatcher-pulse.log", "w");
- }
- va_start(ap, message);
- vfprintf(pulseDebugFile, message, ap);
- va_end(ap);
- fflush(pulseDebugFile);
-}
-#else
-static void MSG(char *message, ...)
-{
-}
-#endif
-
-static int _pulse_open(spd_pulse_id_t * id, int sample_rate,
- int num_channels, int bytes_per_sample)
-{
- pa_buffer_attr buffAttr;
- pa_sample_spec ss;
- int error;
-
- ss.rate = sample_rate;
- ss.channels = num_channels;
- if (bytes_per_sample == 2) {
- switch (id->id.format) {
- case SPD_AUDIO_LE:
- ss.format = PA_SAMPLE_S16LE;
- break;
- case SPD_AUDIO_BE:
- ss.format = PA_SAMPLE_S16BE;
- break;
- }
- } else {
- ss.format = PA_SAMPLE_U8;
- }
-
- /* Set prebuf to one sample so that keys are spoken as soon as typed
rather than delayed until the next key pressed */
- buffAttr.maxlength = (uint32_t) - 1;
- //buffAttr.tlength = (uint32_t)-1; - this is the default, which causes
key echo to not work properly.
- buffAttr.tlength = id->pa_min_audio_length;
- buffAttr.prebuf = (uint32_t) - 1;
- buffAttr.minreq = (uint32_t) - 1;
- buffAttr.fragsize = (uint32_t) - 1;
- /* Open new connection */
- if (!
- (id->pa_simple =
- pa_simple_new(id->pa_server, "speech-dispatcher",
- PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL,
- &buffAttr, &error))) {
- fprintf(stderr, __FILE__ ": pa_simple_new() failed: %s\n",
- pa_strerror(error));
- return 1;
- }
- return 0;
-}
-
-/* Close the connection to the server. Does not free the AudioID struct. */
-/* Usable in pulse_play, which closes connections on failure or */
-/* changes in audio parameters. */
-static void pulse_connection_close(spd_pulse_id_t * pulse_id)
-{
- if (pulse_id->pa_simple != NULL) {
- pa_simple_free(pulse_id->pa_simple);
- pulse_id->pa_simple = NULL;
- }
-}
-
-static AudioID *pulse_open(void **pars)
-{
- spd_pulse_id_t *pulse_id;
- int ret;
-
- pulse_id = (spd_pulse_id_t *) g_malloc(sizeof(spd_pulse_id_t));
-
- /* Select an Endianness for the initial connection. */
-#if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
- pulse_id->id.format = SPD_AUDIO_BE;
-#else
- pulse_id->id.format = SPD_AUDIO_LE;
-#endif
- pulse_id->pa_simple = NULL;
- pulse_id->pa_server = (char *)pars[3];
- pulse_id->pa_min_audio_length = DEFAULT_PA_MIN_AUDIO_LENgTH;
-
- pulse_id->pa_current_rate = -1;
- pulse_id->pa_current_bps = -1;
- pulse_id->pa_current_channels = -1;
-
- if (!strcmp(pulse_id->pa_server, "default")) {
- pulse_id->pa_server = NULL;
- }
-
- if (pars[4] != NULL && atoi(pars[4]) != 0)
- pulse_id->pa_min_audio_length = atoi(pars[4]);
-
- pulse_id->pa_stop_playback = 0;
-
- ret = _pulse_open(pulse_id, 44100, 1, 2);
- if (ret) {
- g_free(pulse_id);
- pulse_id = NULL;
- }
-
- return (AudioID *) pulse_id;
-}
-
-static int pulse_play(AudioID * id, AudioTrack track)
-{
- int bytes_per_sample;
- int num_bytes;
- int outcnt = 0;
- signed short *output_samples;
- int i;
- int error;
- spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
-
- if (id == NULL) {
- return -1;
- }
- if (track.samples == NULL || track.num_samples <= 0) {
- return 0;
- }
- MSG("Starting playback\n");
- /* Choose the correct format */
- if (track.bits == 16) {
- bytes_per_sample = 2;
- } else if (track.bits == 8) {
- bytes_per_sample = 1;
- } else {
- MSG("ERROR: Unsupported sound data format, track.bits = %d\n",
- track.bits);
- return -1;
- }
- output_samples = track.samples;
- num_bytes = track.num_samples * bytes_per_sample;
-
- /* Check if the current connection has suitable parameters for this
track */
- if (pulse_id->pa_current_rate != track.sample_rate
- || pulse_id->pa_current_bps != track.bits
- || pulse_id->pa_current_channels != track.num_channels) {
- MSG("Reopenning connection due to change in track parameters
sample_rate:%d bps:%d channels:%d\n", track.sample_rate, track.bits,
track.num_channels);
- /* Close old connection if any */
- pulse_connection_close(pulse_id);
- /* Open a new connection */
- _pulse_open(pulse_id, track.sample_rate, track.num_channels,
- bytes_per_sample);
- /* Keep track of current connection parameters */
- pulse_id->pa_current_rate = track.sample_rate;
- pulse_id->pa_current_bps = track.bits;
- pulse_id->pa_current_channels = track.num_channels;
- }
- MSG("bytes to play: %d, (%f secs)\n", num_bytes,
- (((float)(num_bytes) / 2) / (float)track.sample_rate));
- pulse_id->pa_stop_playback = 0;
- outcnt = 0;
- i = 0;
- while ((outcnt < num_bytes) && !pulse_id->pa_stop_playback) {
- if ((num_bytes - outcnt) > PULSE_SEND_BYTES) {
- i = PULSE_SEND_BYTES;
- } else {
- i = (num_bytes - outcnt);
- }
- if (pa_simple_write
- (pulse_id->pa_simple, ((char *)output_samples) + outcnt, i,
- &error) < 0) {
- pa_simple_drain(pulse_id->pa_simple, NULL);
- pulse_connection_close(pulse_id);
- MSG("ERROR: Audio: pulse_play(): %s - closing device -
re-open it in next run\n", pa_strerror(error));
- break;
- } else {
- MSG("Pulse: wrote %u bytes\n", i);
- }
- outcnt += i;
- }
- return 0;
-}
-
-/* stop the pulse_play() loop */
-static int pulse_stop(AudioID * id)
-{
- spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
-
- pulse_id->pa_stop_playback = 1;
- return 0;
-}
-
-static int pulse_close(AudioID * id)
-{
- spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
- pulse_connection_close(pulse_id);
- g_free(pulse_id);
- id = NULL;
-
- return 0;
-}
-
-static int pulse_set_volume(AudioID * id, int volume)
-{
- return 0;
-}
-
-static void pulse_set_loglevel(int level)
-{
- if (level) {
- pulse_log_level = level;
- }
-}
-
-static char const *pulse_get_playcmd(void)
-{
- return pulse_play_cmd;
-}
-
-/* Provide the pulse backend. */
-static spd_audio_plugin_t pulse_functions = {
- "pulse",
- pulse_open,
- pulse_play,
- pulse_stop,
- pulse_close,
- pulse_set_volume,
- pulse_set_loglevel,
- pulse_get_playcmd
-};
-
-spd_audio_plugin_t *pulse_plugin_get(void)
-{
- return &pulse_functions;
-}
-
-spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
- __attribute__ ((weak, alias("pulse_plugin_get")));
--
1.7.3.4
- [PATCH] move audio source out of the modules directory,
William Hubbs <=