From d373fb2aa7fbf673594cd98225877c6998dc99ad Mon Sep 17 00:00:00 2001 From: Christopher Brannon Date: Fri, 19 Jun 2009 14:37:38 -0500 Subject: [PATCH] An initial stab at ALSA support. It's very raw right now. --- Makefile | 5 +- alsa.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ espeak_sound.c | 9 +++ espeakup.c | 11 +++- espeakup.h | 4 ++ synth.c | 2 + 6 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 alsa.c create mode 100644 espeak_sound.c diff --git a/Makefile b/Makefile index b636930..c45f437 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,18 @@ INSTALL = install +CFLAGS ?= -DUSE_ALSA SRCS = \ cli.c \ + alsa.c \ espeakup.c \ + espeak_sound.c \ queue.c \ softsynth.c \ synth.c OBJS = $(SRCS:.c=.o) -LDLIBS = -lespeak -lpthread +LDLIBS = -lespeak -lasound PREFIX = /usr MANDIR = $(PREFIX)/share/man/man8 diff --git a/alsa.c b/alsa.c new file mode 100644 index 0000000..88af42f --- /dev/null +++ b/alsa.c @@ -0,0 +1,149 @@ +/* + * espeakup - interface which allows speakup to use espeak + * + * Copyright (C) 2008 William Hubbs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * File: alsa.c + * Description: Produce audio by calling the ALSA library directly. +*/ + +#ifdef USE_ALSA +#include +#include +#include +#define ALSA_PCM_NEW_HW_PARAMS_API +#include +#include "espeakup.h" + +int minimum(int x, int y) +{ + if (x <= y) + return x; + else + return y; +} + +static snd_pcm_t *handle; +static snd_pcm_hw_params_t *params; +snd_pcm_status_t *status; +static unsigned int rate = 22050; /* sample rate */ +static int dir = 0; +static snd_pcm_uframes_t frames; + +static int alsa_play_callback(short *audio, int numsamples, + espeak_EVENT * events) +{ + int samples_written = 0; + int avail; + int to_write; + snd_pcm_state_t state; + snd_pcm_status(handle, status); + state = snd_pcm_status_get_state(status); + if (state != SND_PCM_STATE_RUNNING) + snd_pcm_prepare(handle); + + while (numsamples > 0) { + if (stopped) { + snd_pcm_drop(handle); + stopped = 0; + return 1; + } + avail = snd_pcm_avail_update(handle); + if (avail <= 0) + continue; + avail = minimum(avail, 32 * 2); + to_write = minimum(avail, numsamples); + samples_written = snd_pcm_writei(handle, audio, to_write); + if (samples_written == -EPIPE) + snd_pcm_prepare(handle); + else { + numsamples -= samples_written; + audio += samples_written; + } + } + + return 0; +} + +int init_audio(void) +{ + int rc; + unsigned saved_rate; + + rate = 22050; + + /* Open PCM device for playback. */ + rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); + if (rc < 0) { + fprintf(stderr, + "unable to open pcm device: %s\n", snd_strerror(rc)); + return rc; + } + + /* Allocate a hardware parameters object. */ + rc = snd_pcm_hw_params_malloc(¶ms); + if (rc < 0) { + fprintf(stderr, + "Unable to allocate memory to store audio parameters: %s\n", + snd_strerror(rc)); + return rc; + } + rc = snd_pcm_status_malloc(&status); + if (rc < 0) { + fprintf(stderr, + "Unable to allocate memory to store PCM status: %s\n", + snd_strerror(rc)); + return rc; + } + + + /* Fill it in with default values. */ + snd_pcm_hw_params_any(handle, params); + + /* Set the desired hardware parameters. */ + + /* Interleaved mode */ + snd_pcm_hw_params_set_access(handle, params, + SND_PCM_ACCESS_RW_INTERLEAVED); + + /* Signed 16-bit little-endian format */ + snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); + + /* One channel */ + snd_pcm_hw_params_set_channels(handle, params, 1); + + saved_rate = rate; + snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir); + + /* Set period size to 32 frames. */ + frames = 32; + snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); + + /* Write the parameters to the driver */ + rc = snd_pcm_hw_params(handle, params); + if (rc < 0) { + fprintf(stderr, + "unable to set hw parameters: %s\n", snd_strerror(rc)); + return rc; + } + + audio_mode = AUDIO_OUTPUT_RETRIEVAL; + audio_callback = alsa_play_callback; + return 0; +} +#endif diff --git a/espeak_sound.c b/espeak_sound.c new file mode 100644 index 0000000..e8a3a03 --- /dev/null +++ b/espeak_sound.c @@ -0,0 +1,9 @@ +#ifndef USE_ALSA +#include "espeakup.h" +int init_audio(void) +{ + audio_mode = AUDIO_OUTPUT_PLAYBACK; + audio_callback = NULL; + return 0; +} +#endif diff --git a/espeakup.c b/espeakup.c index 7157b0a..5aaf818 100644 --- a/espeakup.c +++ b/espeakup.c @@ -42,6 +42,10 @@ const int defaultVolume = 5; char *defaultVoice = NULL; int debug = 0; +volatile int stopped = 0; +espeak_AUDIO_OUTPUT audio_mode; +t_espeak_callback *audio_callback = NULL; + int espeakup_is_running(void) { int rc; @@ -115,6 +119,10 @@ int main(int argc, char **argv) signal(SIGINT, espeakup_sighandler); signal(SIGTERM, espeakup_sighandler); + if (init_audio() < 0) { + return 5; + } + if (!debug) { /* become a daemon */ daemon(0, 1); @@ -127,7 +135,8 @@ int main(int argc, char **argv) } /* initialize espeak */ - espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, NULL, 0); + espeak_Initialize(audio_mode, 0, NULL, 0); + espeak_SetSynthCallback(audio_callback); /* Setup initial voice parameters */ if (defaultVoice) { diff --git a/espeakup.h b/espeakup.h index 75292c8..eb67e3d 100644 --- a/espeakup.h +++ b/espeakup.h @@ -78,5 +78,9 @@ extern int open_softsynth(void); extern void close_softsynth(void); extern void main_loop(struct synth_t *s); extern void * queue_runner(void *arg); +extern int init_audio(void); +extern volatile int stopped; +extern espeak_AUDIO_OUTPUT audio_mode; +extern t_espeak_callback *audio_callback; #endif diff --git a/synth.c b/synth.c index fe6ca15..f7f13c3 100644 --- a/synth.c +++ b/synth.c @@ -114,6 +114,7 @@ espeak_ERROR set_volume(struct synth_t * s, int vol, enum adjust_t adj) espeak_ERROR stop_speech(void) { + stopped = 1; return (espeak_Cancel()); } @@ -121,6 +122,7 @@ espeak_ERROR speak_text(struct synth_t * s) { espeak_ERROR rc; + stopped = 0; rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, 0, 0, NULL, NULL); return rc;