[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 2/2] move src/audio to src/modules/audio
From: |
William Hubbs |
Subject: |
[PATCH 2/2] move src/audio to src/modules/audio |
Date: |
Fri, 17 Sep 2010 11:09:45 -0500 |
The audio subsystem is only used in the modules, so the source for it is
being moved into the modules directory to reflect this.
---
configure.ac | 4 +-
src/Makefile.am | 2 +-
src/audio/Makefile.am | 37 --
src/audio/alsa.c | 834 ---------------------------------
src/audio/libao.c | 241 ----------
src/audio/nas.c | 252 ----------
src/audio/oss.c | 505 --------------------
src/audio/pulse.c | 291 ------------
src/audio/spd_audio.c | 263 -----------
src/audio/spd_audio.h | 44 --
src/audio/spd_audio_plugin.h | 69 ---
src/audio/static_plugins.c.in | 50 --
src/modules/Makefile.am | 4 +-
src/modules/audio/Makefile.am | 37 ++
src/modules/audio/alsa.c | 834 +++++++++++++++++++++++++++++++++
src/modules/audio/libao.c | 241 ++++++++++
src/modules/audio/nas.c | 252 ++++++++++
src/modules/audio/oss.c | 505 ++++++++++++++++++++
src/modules/audio/pulse.c | 291 ++++++++++++
src/modules/audio/spd_audio.c | 263 +++++++++++
src/modules/audio/spd_audio.h | 44 ++
src/modules/audio/spd_audio_plugin.h | 69 +++
src/modules/audio/static_plugins.c.in | 50 ++
23 files changed, 2592 insertions(+), 2590 deletions(-)
delete mode 100644 src/audio/Makefile.am
delete mode 100644 src/audio/alsa.c
delete mode 100644 src/audio/libao.c
delete mode 100644 src/audio/nas.c
delete mode 100644 src/audio/oss.c
delete mode 100644 src/audio/pulse.c
delete mode 100644 src/audio/spd_audio.c
delete mode 100644 src/audio/spd_audio.h
delete mode 100644 src/audio/spd_audio_plugin.h
delete mode 100644 src/audio/static_plugins.c.in
create mode 100644 src/modules/audio/Makefile.am
create mode 100644 src/modules/audio/alsa.c
create mode 100644 src/modules/audio/libao.c
create mode 100644 src/modules/audio/nas.c
create mode 100644 src/modules/audio/oss.c
create mode 100644 src/modules/audio/pulse.c
create mode 100644 src/modules/audio/spd_audio.c
create mode 100644 src/modules/audio/spd_audio.h
create mode 100644 src/modules/audio/spd_audio_plugin.h
create mode 100644 src/modules/audio/static_plugins.c.in
diff --git a/configure.ac b/configure.ac
index 61b00c4..2441474 100644
--- a/configure.ac
+++ b/configure.ac
@@ -431,13 +431,13 @@ AC_CONFIG_FILES([Makefile
src/api/python/Makefile
src/api/python/speechd/Makefile
src/api/python/speechd_config/Makefile
- src/audio/Makefile
- src/audio/static_plugins.c
src/clients/Makefile
src/clients/say/Makefile
src/clients/spdsend/Makefile
src/common/Makefile
src/modules/Makefile
+ src/modules/audio/Makefile
+ src/modules/audio/static_plugins.c
src/server/Makefile
src/tests/Makefile])
AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index 81d0690..f2da76d 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 audio modules api clients tests
+SUBDIRS=common server modules api clients tests
diff --git a/src/audio/Makefile.am b/src/audio/Makefile.am
deleted file mode 100644
index 0d67162..0000000
--- a/src/audio/Makefile.am
+++ /dev/null
@@ -1,37 +0,0 @@
-
-localedir = $(datadir)/locale
-inc_local = "-I$(top_srcdir)/include/"
-
-include_HEADERS = spd_audio_plugin.h
-
-am_cflags = -DLOCALEDIR=\"$(localedir)\" $(inc_local)
-
-spdlib_LTLIBRARIES = libsdaudio.la
-libsdaudio_la_CPPFLAGS = $(GLIB_CFLAGS)
-libsdaudio_la_SOURCES = spd_audio.c spd_audio.h spd_audio_plugin.h
static_plugins.c
-
-if nas_support
-libsdaudio_la_SOURCES += nas.c
-endif
-
-if pulse_support
-libsdaudio_la_SOURCES += pulse.c
-endif
-
-if alsa_support
-libsdaudio_la_SOURCES += alsa.c
-endif
-
-if libao_support
-libsdaudio_la_SOURCES += libao.c
-endif
-
-if oss_support
-libsdaudio_la_SOURCES += oss.c
-endif
-
-EXTRA_DIST = alsa.c libao.c oss.c nas.c pulse.c static_plugins.c.in
-
-AM_CFLAGS = -DLOCALEDIR=\"$(localedir)\" $(inc_local)
-libsdaudio_la_LDFLAGS = -version-info
@LIB_SDAUDIO_CURRENT@:@LIB_SDAUDIO_REVISION@:@LIB_SDAUDIO_AGE@ -lpthread
$(SPD_AUDIO_LIBS)
-libsdaudio_la_LIBADD = $(GLIB_LIBS)
diff --git a/src/audio/alsa.c b/src/audio/alsa.c
deleted file mode 100644
index f381ccf..0000000
--- a/src/audio/alsa.c
+++ /dev/null
@@ -1,834 +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.*/
-
-#include <stdio.h>
-#include <sys/time.h>
-#include <time.h>
-#include <pthread.h>
-
-#include <alsa/asoundlib.h>
-
-#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 = 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); \
- xfree(tstr); \
- }
-
-#define ERR(arg...) \
- { \
- time_t t; \
- struct timeval tv; \
- char *tstr; \
- t = time(NULL); \
- tstr = 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); \
- xfree(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);
-
- 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 *) 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 = strdup(pars[1]);
-
- ret = _alsa_open(alsa_id);
- if (ret){
- ERR("Cannot initialize Alsa device '%s': Can't open.",
alsa_id->alsa_device_name);
- 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.");
-
- free (alsa_id->alsa_device_name);
- 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()\
- 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 = 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*) 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)
- 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]);
-
- xfree(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;}
-#undef MSG
-#undef ERR
diff --git a/src/audio/libao.c b/src/audio/libao.c
deleted file mode 100644
index 0b4734c..0000000
--- a/src/audio/libao.c
+++ /dev/null
@@ -1,241 +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.
- *
- */
-
-#include <sys/time.h>
-#include <time.h>
-#include <string.h>
-#include <ao/ao.h>
-
-#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 = 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); \
- xfree(tstr); \
- }
-
-#define ERR(arg...) \
- { \
- time_t t; \
- struct timeval tv; \
- char *tstr; \
- t = time(NULL); \
- tstr = 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); \
- xfree(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 *) 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 ();
-
- 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;}
-
-#undef MSG
-#undef ERR
diff --git a/src/audio/nas.c b/src/audio/nas.c
deleted file mode 100644
index 8910c9b..0000000
--- a/src/audio/nas.c
+++ /dev/null
@@ -1,252 +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 $
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <audio/audiolib.h>
-#include <audio/soundlib.h>
-
-#include <pthread.h>
-
-#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 *) 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);
-
- 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;}
\ No newline at end of file
diff --git a/src/audio/oss.c b/src/audio/oss.c
deleted file mode 100644
index 4254b60..0000000
--- a/src/audio/oss.c
+++ /dev/null
@@ -1,505 +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 $
- */
-
-#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 <sys/soundcard.h>
-
-#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 = 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); \
- xfree(tstr); \
- }
-
-#define ERR(arg...) \
- { \
- time_t t; \
- struct timeval tv; \
- char *tstr; \
- t = time(NULL); \
- tstr = 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); \
- xfree(tstr); \
- }
-
-static int oss_log_level;
-static char const * oss_play_cmd="play";
-
-void
-xfree(void* p)
-{
- if (p != NULL) free(p);
-}
-
-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 *) malloc(sizeof(spd_oss_id_t));
-
- oss_id->device_name = (char*) 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) {
- free (oss_id->device_name);
- free (oss_id);
- return NULL;
- }
- ret = _oss_close(oss_id);
- if (ret) {
- free (oss_id->device_name);
- 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*) 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) 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. */
-
- free(oss_id->device_name);
- 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;}
-#undef MSG
-#undef ERR
diff --git a/src/audio/pulse.c b/src/audio/pulse.c
deleted file mode 100644
index f4d4c12..0000000
--- a/src/audio/pulse.c
+++ /dev/null
@@ -1,291 +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.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <time.h>
-#include <string.h>
-#include <stdarg.h>
-
-#include <pulse/simple.h>
-#include <pulse/error.h>
-
-#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 *) 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) {
- 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);
- 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;}
diff --git a/src/audio/spd_audio.c b/src/audio/spd_audio.c
deleted file mode 100644
index 9cf0a27..0000000
--- a/src/audio/spd_audio.c
+++ /dev/null
@@ -1,263 +0,0 @@
-
-/*
- * spd_audio.c -- Spd Audio Output 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: spd_audio.c,v 1.21 2008-06-09 10:29:12 hanke Exp $
- */
-
-/*
- * spd_audio is a simple realtime audio output library with the capability of
- * playing 8 or 16 bit data, immediate stop and synchronization. This library
- * currently provides OSS, NAS, ALSA and PulseAudio backend. The available
backends are
- * specified at compile-time using the directives WITH_OSS, WITH_NAS,
WITH_ALSA,
- * WITH_PULSE, WITH_LIBAO but the user program is allowed to switch between
them at run-time.
- */
-
-#include "spd_audio.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <time.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <pthread.h>
-
-#include <glib.h>
-
-static int spd_audio_log_level;
-extern spd_audio_plugin_t * spd_audio_static_plugin_get (char * name);
-
-/* Open the audio device.
-
- Arguments:
- type -- The requested device. Currently AudioOSS or AudioNAS.
- pars -- and array of pointers to parameters to pass to
- the device backend, terminated by a NULL pointer.
- See the source/documentation of each specific backend.
- error -- a pointer to the string where error description is
- stored in case of failure (returned AudioID == NULL).
- Otherwise will contain NULL.
-
- Return value:
- Newly allocated AudioID structure that can be passed to
- all other spd_audio functions, or NULL in case of failure.
-
-*/
-AudioID*
-spd_audio_open(char *name, void **pars, char **error)
-{
- AudioID *id;
- struct spd_audio_plugin *function;
- spd_audio_plugin_t const *p;
-
- /* check whether static plugin is available */
- p = spd_audio_static_plugin_get (name);
- if (p == NULL || p->name == NULL) {
- *error = (char*)g_strdup_printf("Unknown plugin %s ", name);
- return (AudioID *)NULL;
- }
-
- id = p->open (pars);
- if (id == NULL) {
- *error = (char*) g_strdup_printf("Couldn't open %s plugin", name);
- return (AudioID *)NULL;
- }
-
- id->function = p;
-#if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
- id->format = SPD_AUDIO_BE;
-#else
- id->format = SPD_AUDIO_LE;
-#endif
-
- *error = NULL;
-
- return id;
-}
-
-/* Play a track on the audio device (blocking).
-
- Arguments:
- id -- the AudioID* of the device returned by spd_audio_open
- track -- a track to play (see spd_audio.h)
-
- Return value:
- 0 if everything is ok, a non-zero value in case of failure.
- See the particular backend documentation or source for the
- meaning of these non-zero values.
-
- Comment:
- spd_audio_play() is a blocking function. It returns exactly
- when the given track stopped playing. However, it's possible
- to safely interrupt it using spd_audio_stop() described bellow.
- (spd_audio_stop() needs to be called from another thread, obviously.)
-
-*/
-int
-spd_audio_play(AudioID *id, AudioTrack track, AudioFormat format)
-{
- int ret;
-
- if (id && id->function->play){
- /* Only perform byte swapping if the driver in use has given us audio
in
- an endian format other than what the running CPU supports. */
- if (format != id->format){
- unsigned char *out_ptr, *out_end, c;
- out_ptr = (unsigned char *) track.samples;
- out_end = out_ptr + track.num_samples*2 * track.num_channels;
- while(out_ptr < out_end){
- c = out_ptr[0];
- out_ptr[0] = out_ptr[1];
- out_ptr[1] = c;
- out_ptr += 2;
- }
- }
- ret = id->function->play(id, track);
- }
- else{
- fprintf(stderr, "Play not supported on this device\n");
- return -1;
- }
-
- return ret;
-}
-
-/* Stop playing the current track on device id
-
-Arguments:
- id -- the AudioID* of the device returned by spd_audio_open
-
-Return value:
- 0 if everything is ok, a non-zero value in case of failure.
- See the particular backend documentation or source for the
- meaning of these non-zero values.
-
-Comment:
- spd_audio_stop() safely interrupts spd_audio_play() when called
- from another thread. It shouldn't cause any clicks or unwanted
- effects in the sound output.
-
- It's safe to call spd_audio_stop() even if the device isn't playing
- any track. In that case, it does nothing. However, there is a danger
- when using spd_audio_stop(). Since you must obviously do it from
- another thread than where spd_audio_play is running, you must make
- yourself sure that the device is still open and the id you pass it
- is valid and will be valid until spd_audio_stop returns. In other words,
- you should use some mutex or other synchronization device to be sure
- spd_audio_close isn't called before or during spd_audio_stop execution.
-*/
-
-int
-spd_audio_stop(AudioID *id)
-{
- int ret;
- if (id && id->function->stop){
- ret = id->function->stop(id);
- }
- else{
- fprintf(stderr, "Stop not supported on this device\n");
- return -1;
- }
- return ret;
-}
-
-/* Close the audio device id
-
-Arguments:
- id -- the AudioID* of the device returned by spd_audio_open
-
-Return value:
- 0 if everything is ok, a non-zero value in case of failure.
-
-Comments:
-
- Please make sure no other spd_audio function with this device id
- is running in another threads. See spd_audio_stop() for detailed
- description of possible problems.
-*/
-int
-spd_audio_close(AudioID *id)
-{
- int ret = 0;
- if (id && id->function->close){
- return (id->function->close(id));
- }
- return -1;
-}
-
-/* Set volume for playing tracks on the device id
-
-Arguments:
- id -- the AudioID* of the device returned by spd_audio_open
- volume -- a value in the range <-100:100> where -100 means the
- least volume (probably silence), 0 the default volume
- and +100 the highest volume possible to make on that
- device for a single flow (i.e. not using mixer).
-
-Return value:
- 0 if everything is ok, a non-zero value in case of failure.
- See the particular backend documentation or source for the
- meaning of these non-zero values.
-
-Comments:
-
- In case of /dev/dsp, it's not possible to set volume for
- the particular flow. For that reason, the value 0 means
- the volume the track was recorded on and each smaller value
- means less volume (since this works by deviding the samples
- in the track by a constant).
-*/
-int
-spd_audio_set_volume(AudioID *id, int volume)
-{
- if ((volume > 100) || (volume < -100)){
- fprintf(stderr, "Requested volume out of range");
- return -1;
- }
- if(id == NULL){
- fprintf(stderr, "audio id is NULL in spd_audio_set_volume\n");
- return -1;
- }
- id->volume = volume;
- return 0;
-}
-
-void
-spd_audio_set_loglevel(AudioID *id, int level)
-{
- if (level){
- spd_audio_log_level = level;
- if (id != 0 && id->function != 0)
- id->function->set_loglevel(level);
- }
-}
-
-char const *
-spd_audio_get_playcmd(AudioID *id)
-{
- if (id != 0 && id->function != 0) {
- return id->function->get_playcmd();
- }
- return NULL;
-}
diff --git a/src/audio/spd_audio.h b/src/audio/spd_audio.h
deleted file mode 100644
index 90aa95f..0000000
--- a/src/audio/spd_audio.h
+++ /dev/null
@@ -1,44 +0,0 @@
-
-/*
- * spd_audio.h -- The SPD Audio Library Header
- *
- * Copyright (C) 2004 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: spd_audio.h,v 1.21 2008-10-15 17:28:17 hanke Exp $
- */
-
-#ifndef __SPD_AUDIO_H
-#define __SPD_AUDIO_H
-
-#include "spd_audio_plugin.h"
-
-AudioID* spd_audio_open(char *name, void **pars, char **error);
-
-int spd_audio_play(AudioID *id, AudioTrack track, AudioFormat format);
-
-int spd_audio_stop(AudioID *id);
-
-int spd_audio_close(AudioID *id);
-
-int spd_audio_set_volume(AudioID *id, int volume);
-
-void spd_audio_set_loglevel(AudioID *id, int level);
-
-char const * spd_audio_get_playcmd (AudioID *id);
-
-#endif /* ifndef #__SPD_AUDIO_H */
diff --git a/src/audio/spd_audio_plugin.h b/src/audio/spd_audio_plugin.h
deleted file mode 100644
index 6fae68a..0000000
--- a/src/audio/spd_audio_plugin.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * spd_audio_plugin.h -- The SPD Audio Plugin Header
- *
- * Copyright (C) 2004 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.
- *
- */
-
-#ifndef __SPD_AUDIO_PLUGIN_H
-#define __SPD_AUDIO_PLUGIN_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum{SPD_AUDIO_LE, SPD_AUDIO_BE} AudioFormat;
-
-typedef struct{
- int bits;
- int num_channels;
- int sample_rate;
-
- int num_samples;
- signed short *samples;
-}AudioTrack;
-
-struct spd_audio_plugin;
-
-typedef struct{
-
- int volume;
- AudioFormat format;
-
- struct spd_audio_plugin const *function;
- void *private_data;
-
- int working;
-}AudioID;
-
-typedef struct spd_audio_plugin {
- const char * name;
- AudioID * (* open) (void** pars);
- int (* play) (AudioID *id, AudioTrack track);
- int (* stop) (AudioID *id);
- int (* close) (AudioID *id);
- int (* set_volume) (AudioID *id, int);
- void (* set_loglevel) (int level);
- char const * (* get_playcmd) (void);
-} spd_audio_plugin_t;
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* ifndef #__SPD_AUDIO_PLUGIN_H */
diff --git a/src/audio/static_plugins.c.in b/src/audio/static_plugins.c.in
deleted file mode 100644
index eca2983..0000000
--- a/src/audio/static_plugins.c.in
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * static_plugins.c -- spd audio library static plugins
- *
- * Copyright (C) 2010 Andrei Kholodnyi
- *
- * 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.
- *
- */
-
-#include <string.h>
-
-#include "spd_audio_plugin.h"
-
- at STATIC_AUDIO_PLUGINS_EXTERN@
-
-static spd_audio_plugin_t const * (* spd_audio_static_plugins[]) (void) =
-{
- @STATIC_AUDIO_PLUGINS_GET@
- 0
-};
-
-spd_audio_plugin_t const * spd_audio_static_plugin_get (char * name)
-{
- int i;
- spd_audio_plugin_t const * plugin;
-
- for (i=0; spd_audio_static_plugins[i]; i++)
- {
- plugin = spd_audio_static_plugins[i]();
- if (plugin != NULL && 0 == strcmp(plugin->name, name))
- {
- return plugin;
- }
- }
-
- return (spd_audio_plugin_t *)NULL;
-}
diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am
index 1979084..06e33bd 100644
--- a/src/modules/Makefile.am
+++ b/src/modules/Makefile.am
@@ -4,9 +4,11 @@ inc_local = "-I$(top_srcdir)/include/"
SNDFILE_CFLAGS = @SNDFILE_CFLAGS@
SNDFILE_LIBS = @SNDFILE_LIBS@
+SUBDIRS = audio
+
EXTRA_DIST = module_main.c module_utils_addvoice.c festival_client.c
festival_client.h ivona_client.c dummy.c dummy-message.wav
-AM_CFLAGS = @ERROR_CFLAGS@ -DLOCALEDIR=\"$(localedir)\"
-DDATADIR=\"$(snddatadir)\" $(inc_local) $(DOTCONF_CFLAGS) $(GLIB_CFLAGS)
$(GTHREAD_CFLAGS) -L$(top_builddir)/src/audio -I$(top_srcdir)/src/audio
$(ibmtts_include) @SNDFILE_CFLAGS@ -D_GNU_SOURCE
+AM_CFLAGS = @ERROR_CFLAGS@ -DLOCALEDIR=\"$(localedir)\"
-DDATADIR=\"$(snddatadir)\" $(inc_local) $(DOTCONF_CFLAGS) $(GLIB_CFLAGS)
$(GTHREAD_CFLAGS) -L$(top_builddir)/src/modules/audio
-I$(top_srcdir)/src/modules/audio $(ibmtts_include) @SNDFILE_CFLAGS@
-D_GNU_SOURCE
modulebin_PROGRAMS = sd_dummy sd_generic sd_festival sd_cicero
diff --git a/src/modules/audio/Makefile.am b/src/modules/audio/Makefile.am
new file mode 100644
index 0000000..0d67162
--- /dev/null
+++ b/src/modules/audio/Makefile.am
@@ -0,0 +1,37 @@
+
+localedir = $(datadir)/locale
+inc_local = "-I$(top_srcdir)/include/"
+
+include_HEADERS = spd_audio_plugin.h
+
+am_cflags = -DLOCALEDIR=\"$(localedir)\" $(inc_local)
+
+spdlib_LTLIBRARIES = libsdaudio.la
+libsdaudio_la_CPPFLAGS = $(GLIB_CFLAGS)
+libsdaudio_la_SOURCES = spd_audio.c spd_audio.h spd_audio_plugin.h
static_plugins.c
+
+if nas_support
+libsdaudio_la_SOURCES += nas.c
+endif
+
+if pulse_support
+libsdaudio_la_SOURCES += pulse.c
+endif
+
+if alsa_support
+libsdaudio_la_SOURCES += alsa.c
+endif
+
+if libao_support
+libsdaudio_la_SOURCES += libao.c
+endif
+
+if oss_support
+libsdaudio_la_SOURCES += oss.c
+endif
+
+EXTRA_DIST = alsa.c libao.c oss.c nas.c pulse.c static_plugins.c.in
+
+AM_CFLAGS = -DLOCALEDIR=\"$(localedir)\" $(inc_local)
+libsdaudio_la_LDFLAGS = -version-info
@LIB_SDAUDIO_CURRENT@:@LIB_SDAUDIO_REVISION@:@LIB_SDAUDIO_AGE@ -lpthread
$(SPD_AUDIO_LIBS)
+libsdaudio_la_LIBADD = $(GLIB_LIBS)
diff --git a/src/modules/audio/alsa.c b/src/modules/audio/alsa.c
new file mode 100644
index 0000000..f381ccf
--- /dev/null
+++ b/src/modules/audio/alsa.c
@@ -0,0 +1,834 @@
+
+/*
+ * 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.*/
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <pthread.h>
+
+#include <alsa/asoundlib.h>
+
+#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 = 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); \
+ xfree(tstr); \
+ }
+
+#define ERR(arg...) \
+ { \
+ time_t t; \
+ struct timeval tv; \
+ char *tstr; \
+ t = time(NULL); \
+ tstr = 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); \
+ xfree(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);
+
+ 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 *) 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 = strdup(pars[1]);
+
+ ret = _alsa_open(alsa_id);
+ if (ret){
+ ERR("Cannot initialize Alsa device '%s': Can't open.",
alsa_id->alsa_device_name);
+ 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.");
+
+ free (alsa_id->alsa_device_name);
+ 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()\
+ 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 = 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*) 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)
+ 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]);
+
+ xfree(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;}
+#undef MSG
+#undef ERR
diff --git a/src/modules/audio/libao.c b/src/modules/audio/libao.c
new file mode 100644
index 0000000..0b4734c
--- /dev/null
+++ b/src/modules/audio/libao.c
@@ -0,0 +1,241 @@
+/*
+ * 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.
+ *
+ */
+
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <ao/ao.h>
+
+#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 = 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); \
+ xfree(tstr); \
+ }
+
+#define ERR(arg...) \
+ { \
+ time_t t; \
+ struct timeval tv; \
+ char *tstr; \
+ t = time(NULL); \
+ tstr = 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); \
+ xfree(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 *) 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 ();
+
+ 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;}
+
+#undef MSG
+#undef ERR
diff --git a/src/modules/audio/nas.c b/src/modules/audio/nas.c
new file mode 100644
index 0000000..8910c9b
--- /dev/null
+++ b/src/modules/audio/nas.c
@@ -0,0 +1,252 @@
+/*
+ * 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 $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <audio/audiolib.h>
+#include <audio/soundlib.h>
+
+#include <pthread.h>
+
+#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 *) 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);
+
+ 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;}
\ No newline at end of file
diff --git a/src/modules/audio/oss.c b/src/modules/audio/oss.c
new file mode 100644
index 0000000..4254b60
--- /dev/null
+++ b/src/modules/audio/oss.c
@@ -0,0 +1,505 @@
+
+/*
+ * 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 $
+ */
+
+#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 <sys/soundcard.h>
+
+#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 = 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); \
+ xfree(tstr); \
+ }
+
+#define ERR(arg...) \
+ { \
+ time_t t; \
+ struct timeval tv; \
+ char *tstr; \
+ t = time(NULL); \
+ tstr = 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); \
+ xfree(tstr); \
+ }
+
+static int oss_log_level;
+static char const * oss_play_cmd="play";
+
+void
+xfree(void* p)
+{
+ if (p != NULL) free(p);
+}
+
+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 *) malloc(sizeof(spd_oss_id_t));
+
+ oss_id->device_name = (char*) 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) {
+ free (oss_id->device_name);
+ free (oss_id);
+ return NULL;
+ }
+ ret = _oss_close(oss_id);
+ if (ret) {
+ free (oss_id->device_name);
+ 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*) 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) 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. */
+
+ free(oss_id->device_name);
+ 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;}
+#undef MSG
+#undef ERR
diff --git a/src/modules/audio/pulse.c b/src/modules/audio/pulse.c
new file mode 100644
index 0000000..f4d4c12
--- /dev/null
+++ b/src/modules/audio/pulse.c
@@ -0,0 +1,291 @@
+
+/*
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <pulse/simple.h>
+#include <pulse/error.h>
+
+#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 *) 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) {
+ 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);
+ 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;}
diff --git a/src/modules/audio/spd_audio.c b/src/modules/audio/spd_audio.c
new file mode 100644
index 0000000..9cf0a27
--- /dev/null
+++ b/src/modules/audio/spd_audio.c
@@ -0,0 +1,263 @@
+
+/*
+ * spd_audio.c -- Spd Audio Output 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: spd_audio.c,v 1.21 2008-06-09 10:29:12 hanke Exp $
+ */
+
+/*
+ * spd_audio is a simple realtime audio output library with the capability of
+ * playing 8 or 16 bit data, immediate stop and synchronization. This library
+ * currently provides OSS, NAS, ALSA and PulseAudio backend. The available
backends are
+ * specified at compile-time using the directives WITH_OSS, WITH_NAS,
WITH_ALSA,
+ * WITH_PULSE, WITH_LIBAO but the user program is allowed to switch between
them at run-time.
+ */
+
+#include "spd_audio.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include <glib.h>
+
+static int spd_audio_log_level;
+extern spd_audio_plugin_t * spd_audio_static_plugin_get (char * name);
+
+/* Open the audio device.
+
+ Arguments:
+ type -- The requested device. Currently AudioOSS or AudioNAS.
+ pars -- and array of pointers to parameters to pass to
+ the device backend, terminated by a NULL pointer.
+ See the source/documentation of each specific backend.
+ error -- a pointer to the string where error description is
+ stored in case of failure (returned AudioID == NULL).
+ Otherwise will contain NULL.
+
+ Return value:
+ Newly allocated AudioID structure that can be passed to
+ all other spd_audio functions, or NULL in case of failure.
+
+*/
+AudioID*
+spd_audio_open(char *name, void **pars, char **error)
+{
+ AudioID *id;
+ struct spd_audio_plugin *function;
+ spd_audio_plugin_t const *p;
+
+ /* check whether static plugin is available */
+ p = spd_audio_static_plugin_get (name);
+ if (p == NULL || p->name == NULL) {
+ *error = (char*)g_strdup_printf("Unknown plugin %s ", name);
+ return (AudioID *)NULL;
+ }
+
+ id = p->open (pars);
+ if (id == NULL) {
+ *error = (char*) g_strdup_printf("Couldn't open %s plugin", name);
+ return (AudioID *)NULL;
+ }
+
+ id->function = p;
+#if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
+ id->format = SPD_AUDIO_BE;
+#else
+ id->format = SPD_AUDIO_LE;
+#endif
+
+ *error = NULL;
+
+ return id;
+}
+
+/* Play a track on the audio device (blocking).
+
+ Arguments:
+ id -- the AudioID* of the device returned by spd_audio_open
+ track -- a track to play (see spd_audio.h)
+
+ Return value:
+ 0 if everything is ok, a non-zero value in case of failure.
+ See the particular backend documentation or source for the
+ meaning of these non-zero values.
+
+ Comment:
+ spd_audio_play() is a blocking function. It returns exactly
+ when the given track stopped playing. However, it's possible
+ to safely interrupt it using spd_audio_stop() described bellow.
+ (spd_audio_stop() needs to be called from another thread, obviously.)
+
+*/
+int
+spd_audio_play(AudioID *id, AudioTrack track, AudioFormat format)
+{
+ int ret;
+
+ if (id && id->function->play){
+ /* Only perform byte swapping if the driver in use has given us audio
in
+ an endian format other than what the running CPU supports. */
+ if (format != id->format){
+ unsigned char *out_ptr, *out_end, c;
+ out_ptr = (unsigned char *) track.samples;
+ out_end = out_ptr + track.num_samples*2 * track.num_channels;
+ while(out_ptr < out_end){
+ c = out_ptr[0];
+ out_ptr[0] = out_ptr[1];
+ out_ptr[1] = c;
+ out_ptr += 2;
+ }
+ }
+ ret = id->function->play(id, track);
+ }
+ else{
+ fprintf(stderr, "Play not supported on this device\n");
+ return -1;
+ }
+
+ return ret;
+}
+
+/* Stop playing the current track on device id
+
+Arguments:
+ id -- the AudioID* of the device returned by spd_audio_open
+
+Return value:
+ 0 if everything is ok, a non-zero value in case of failure.
+ See the particular backend documentation or source for the
+ meaning of these non-zero values.
+
+Comment:
+ spd_audio_stop() safely interrupts spd_audio_play() when called
+ from another thread. It shouldn't cause any clicks or unwanted
+ effects in the sound output.
+
+ It's safe to call spd_audio_stop() even if the device isn't playing
+ any track. In that case, it does nothing. However, there is a danger
+ when using spd_audio_stop(). Since you must obviously do it from
+ another thread than where spd_audio_play is running, you must make
+ yourself sure that the device is still open and the id you pass it
+ is valid and will be valid until spd_audio_stop returns. In other words,
+ you should use some mutex or other synchronization device to be sure
+ spd_audio_close isn't called before or during spd_audio_stop execution.
+*/
+
+int
+spd_audio_stop(AudioID *id)
+{
+ int ret;
+ if (id && id->function->stop){
+ ret = id->function->stop(id);
+ }
+ else{
+ fprintf(stderr, "Stop not supported on this device\n");
+ return -1;
+ }
+ return ret;
+}
+
+/* Close the audio device id
+
+Arguments:
+ id -- the AudioID* of the device returned by spd_audio_open
+
+Return value:
+ 0 if everything is ok, a non-zero value in case of failure.
+
+Comments:
+
+ Please make sure no other spd_audio function with this device id
+ is running in another threads. See spd_audio_stop() for detailed
+ description of possible problems.
+*/
+int
+spd_audio_close(AudioID *id)
+{
+ int ret = 0;
+ if (id && id->function->close){
+ return (id->function->close(id));
+ }
+ return -1;
+}
+
+/* Set volume for playing tracks on the device id
+
+Arguments:
+ id -- the AudioID* of the device returned by spd_audio_open
+ volume -- a value in the range <-100:100> where -100 means the
+ least volume (probably silence), 0 the default volume
+ and +100 the highest volume possible to make on that
+ device for a single flow (i.e. not using mixer).
+
+Return value:
+ 0 if everything is ok, a non-zero value in case of failure.
+ See the particular backend documentation or source for the
+ meaning of these non-zero values.
+
+Comments:
+
+ In case of /dev/dsp, it's not possible to set volume for
+ the particular flow. For that reason, the value 0 means
+ the volume the track was recorded on and each smaller value
+ means less volume (since this works by deviding the samples
+ in the track by a constant).
+*/
+int
+spd_audio_set_volume(AudioID *id, int volume)
+{
+ if ((volume > 100) || (volume < -100)){
+ fprintf(stderr, "Requested volume out of range");
+ return -1;
+ }
+ if(id == NULL){
+ fprintf(stderr, "audio id is NULL in spd_audio_set_volume\n");
+ return -1;
+ }
+ id->volume = volume;
+ return 0;
+}
+
+void
+spd_audio_set_loglevel(AudioID *id, int level)
+{
+ if (level){
+ spd_audio_log_level = level;
+ if (id != 0 && id->function != 0)
+ id->function->set_loglevel(level);
+ }
+}
+
+char const *
+spd_audio_get_playcmd(AudioID *id)
+{
+ if (id != 0 && id->function != 0) {
+ return id->function->get_playcmd();
+ }
+ return NULL;
+}
diff --git a/src/modules/audio/spd_audio.h b/src/modules/audio/spd_audio.h
new file mode 100644
index 0000000..90aa95f
--- /dev/null
+++ b/src/modules/audio/spd_audio.h
@@ -0,0 +1,44 @@
+
+/*
+ * spd_audio.h -- The SPD Audio Library Header
+ *
+ * Copyright (C) 2004 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: spd_audio.h,v 1.21 2008-10-15 17:28:17 hanke Exp $
+ */
+
+#ifndef __SPD_AUDIO_H
+#define __SPD_AUDIO_H
+
+#include "spd_audio_plugin.h"
+
+AudioID* spd_audio_open(char *name, void **pars, char **error);
+
+int spd_audio_play(AudioID *id, AudioTrack track, AudioFormat format);
+
+int spd_audio_stop(AudioID *id);
+
+int spd_audio_close(AudioID *id);
+
+int spd_audio_set_volume(AudioID *id, int volume);
+
+void spd_audio_set_loglevel(AudioID *id, int level);
+
+char const * spd_audio_get_playcmd (AudioID *id);
+
+#endif /* ifndef #__SPD_AUDIO_H */
diff --git a/src/modules/audio/spd_audio_plugin.h
b/src/modules/audio/spd_audio_plugin.h
new file mode 100644
index 0000000..6fae68a
--- /dev/null
+++ b/src/modules/audio/spd_audio_plugin.h
@@ -0,0 +1,69 @@
+/*
+ * spd_audio_plugin.h -- The SPD Audio Plugin Header
+ *
+ * Copyright (C) 2004 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.
+ *
+ */
+
+#ifndef __SPD_AUDIO_PLUGIN_H
+#define __SPD_AUDIO_PLUGIN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum{SPD_AUDIO_LE, SPD_AUDIO_BE} AudioFormat;
+
+typedef struct{
+ int bits;
+ int num_channels;
+ int sample_rate;
+
+ int num_samples;
+ signed short *samples;
+}AudioTrack;
+
+struct spd_audio_plugin;
+
+typedef struct{
+
+ int volume;
+ AudioFormat format;
+
+ struct spd_audio_plugin const *function;
+ void *private_data;
+
+ int working;
+}AudioID;
+
+typedef struct spd_audio_plugin {
+ const char * name;
+ AudioID * (* open) (void** pars);
+ int (* play) (AudioID *id, AudioTrack track);
+ int (* stop) (AudioID *id);
+ int (* close) (AudioID *id);
+ int (* set_volume) (AudioID *id, int);
+ void (* set_loglevel) (int level);
+ char const * (* get_playcmd) (void);
+} spd_audio_plugin_t;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ifndef #__SPD_AUDIO_PLUGIN_H */
diff --git a/src/modules/audio/static_plugins.c.in
b/src/modules/audio/static_plugins.c.in
new file mode 100644
index 0000000..eca2983
--- /dev/null
+++ b/src/modules/audio/static_plugins.c.in
@@ -0,0 +1,50 @@
+/*
+ * static_plugins.c -- spd audio library static plugins
+ *
+ * Copyright (C) 2010 Andrei Kholodnyi
+ *
+ * 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.
+ *
+ */
+
+#include <string.h>
+
+#include "spd_audio_plugin.h"
+
+ at STATIC_AUDIO_PLUGINS_EXTERN@
+
+static spd_audio_plugin_t const * (* spd_audio_static_plugins[]) (void) =
+{
+ @STATIC_AUDIO_PLUGINS_GET@
+ 0
+};
+
+spd_audio_plugin_t const * spd_audio_static_plugin_get (char * name)
+{
+ int i;
+ spd_audio_plugin_t const * plugin;
+
+ for (i=0; spd_audio_static_plugins[i]; i++)
+ {
+ plugin = spd_audio_static_plugins[i]();
+ if (plugin != NULL && 0 == strcmp(plugin->name, name))
+ {
+ return plugin;
+ }
+ }
+
+ return (spd_audio_plugin_t *)NULL;
+}
--
1.7.2.2
- [PATCH 1/2] remove audio include path from tests Makefile, William Hubbs, 2010/09/17
- [PATCH 2/2] move src/audio to src/modules/audio,
William Hubbs <=
- [PATCH 1/2] remove audio include path from tests Makefile, William Hubbs, 2010/09/17
- [PATCH 2/2] move src/audio to src/modules/audio, William Hubbs, 2010/09/17
- [PATCH 2/2] move src/audio to src/modules/audio, William Hubbs, 2010/09/18
- [PATCH 2/2] move src/audio to src/modules/audio, Andrei Kholodnyi, 2010/09/18
- [PATCH 2/2] move src/audio to src/modules/audio, William Hubbs, 2010/09/18
- [PATCH 2/2] move src/audio to src/modules/audio, Andrei Kholodnyi, 2010/09/18