Package: flite Version: 1.2-release-2.2 Severity: wishlist Tags: patch
Attached is a patch I developed in early 2006 that adds Alsa audio playback support to flite. By now, the functunality is well tested mostly via eflite. Alsa support allows TTS users to play other sounds wile flite is talking. Flite currently uses OSS for audio playback which is generally deprecated. The patch should apply cleanly to the Debian modified flite sources. It also selects Alsa as audio playback method in the debian/rules script. -- System Information: Debian Release: lenny/sid APT prefers unstable APT policy: (800, 'unstable'), (750, 'experimental') Architecture: i386 (i686) Kernel: Linux 2.6.20.suspend2 Locale: LANG=en_US.iso88591, LC_CTYPE=en_US.iso88591 (charmap=ISO-8859-1) Shell: /bin/sh linked to /bin/bash Versions of packages flite depends on: ii libasound2 1.0.14a-2 ALSA library ii libc6 2.6.1-5 GNU C Library: Shared libraries ii libflite1 1.2-release-3 a small run-time speech synthesis flite recommends no packages. -- no debconf information
diff -uNr flite-1.2-release.orig/config/common_make_rules flite-1.2-release/config/common_make_rules --- flite-1.2-release.orig/config/common_make_rules 2007-09-08 12:31:31.000000000 +0200 +++ flite-1.2-release/config/common_make_rules 2007-09-08 10:41:25.000000000 +0200 @@ -88,7 +88,7 @@ @ rm -rf shared_os && mkdir shared_os @ rm -f $@ $(LIBDIR)/[EMAIL PROTECTED] $(LIBDIR)/[EMAIL PROTECTED] @ (cd shared_os && ar x ../$<) - @ (cd shared_os && $(CC) -shared -Wl,-soname,[EMAIL PROTECTED] -o ../[EMAIL PROTECTED] *.os) + @ (cd shared_os && $(CC) -shared -Wl,-soname,[EMAIL PROTECTED] -o ../[EMAIL PROTECTED] *.os $(AUDIOLIBS)) @(cd $(LIBDIR); ln -s [EMAIL PROTECTED] [EMAIL PROTECTED]; \ ln -s [EMAIL PROTECTED] $@) @ rm -rf shared_os diff -uNr flite-1.2-release.orig/configure flite-1.2-release/configure --- flite-1.2-release.orig/configure 2003-02-18 17:18:20.000000000 +0100 +++ flite-1.2-release/configure 2007-09-08 10:41:25.000000000 +0200 @@ -1415,16 +1415,16 @@ echo "$ac_t""no" 1>&6 fi -ac_safe=`echo "sys/asoundlib.h" | sed 'y%./+-%__p_%'` -echo $ac_n "checking for sys/asoundlib.h""... $ac_c" 1>&6 -echo "configure:1421: checking for sys/asoundlib.h" >&5 +ac_safe=`echo "alsa/asoundlib.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for alsa/asoundlib.h""... $ac_c" 1>&6 +echo "configure:1421: checking for alsa/asoundlib.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF #line 1426 "configure" #include "confdefs.h" -#include <sys/asoundlib.h> +#include <alsa/asoundlib.h> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1431: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } @@ -1445,23 +1445,24 @@ echo "$ac_t""yes" 1>&6 AUDIODRIVER="alsa" AUDIODEFS=-DCST_AUDIO_ALSA + AUDIOLIBS=-lasound else echo "$ac_t""no" 1>&6 fi ac_safe=`echo "mmsystem.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for mmsystem.h""... $ac_c" 1>&6 -echo "configure:1455: checking for mmsystem.h" >&5 +echo "configure:1456: checking for mmsystem.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1460 "configure" +#line 1461 "configure" #include "confdefs.h" #include <mmsystem.h> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1465: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1466: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* diff -uNr flite-1.2-release.orig/configure.in flite-1.2-release/configure.in --- flite-1.2-release.orig/configure.in 2003-02-18 01:45:45.000000000 +0100 +++ flite-1.2-release/configure.in 2007-09-08 10:41:25.000000000 +0200 @@ -131,9 +131,10 @@ AC_CHECK_HEADER(sys/audioio.h, [AUDIODRIVER="sun" AUDIODEFS=-DCST_AUDIO_SUNOS]) -AC_CHECK_HEADER(sys/asoundlib.h, +AC_CHECK_HEADER(alsa/asoundlib.h, [AUDIODRIVER="alsa" - AUDIODEFS=-DCST_AUDIO_ALSA]) + AUDIODEFS=-DCST_AUDIO_ALSA + AUDIOLIBS=-lasound]) AC_CHECK_HEADER(mmsystem.h, [AUDIODRIVER="wince" AUDIODEFS=-DCST_AUDIO_WINCE diff -uNr flite-1.2-release.orig/debian/rules flite-1.2-release/debian/rules --- flite-1.2-release.orig/debian/rules 2007-09-08 12:31:31.000000000 +0200 +++ flite-1.2-release/debian/rules 2007-09-08 10:41:38.000000000 +0200 @@ -22,7 +22,7 @@ dh_testdir ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) \ --prefix=/usr --mandir=\$${prefix}/share/man \ - --infodir=\$${prefix}/share/info --with-audio=oss --with-vox=cmu_us_kal16 --enable-shared + --infodir=\$${prefix}/share/info --with-audio=alsa --with-vox=cmu_us_kal16 --enable-shared build: build-stamp diff -uNr flite-1.2-release.orig/src/audio/au_alsa.c flite-1.2-release/src/audio/au_alsa.c --- flite-1.2-release.orig/src/audio/au_alsa.c 2001-12-04 19:05:52.000000000 +0100 +++ flite-1.2-release/src/audio/au_alsa.c 2007-09-08 10:41:25.000000000 +0200 @@ -2,7 +2,7 @@ /* */ /* Language Technologies Institute */ /* Carnegie Mellon University */ -/* Copyright (c) 2001 */ +/* Copyright (c) 2000 */ /* All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to use and distribute */ @@ -29,158 +29,283 @@ /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */ /* THIS SOFTWARE. */ /* */ +/*********************************************************************** */ +/* Author: Lukas Loehrer ( */ +/* Date: January 2005 */ /*************************************************************************/ -/* Author: Geoff Harrison ([EMAIL PROTECTED]) */ -/* Date: Sepetember 2001 */ -/*************************************************************************/ -/* */ -/* Access to ALSA audio devices */ /* */ +/* Native access to alsa audio devices on Linux */ +/* Tested with libasound version 1.0.10 */ /*************************************************************************/ -#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> +#include <assert.h> +#include <errno.h> + #include "cst_string.h" #include "cst_wave.h" #include "cst_audio.h" -#include <sys/asoundlib.h> +#include <alsa/asoundlib.h> -#include <sys/stat.h> -#include <fcntl.h> -static int alsa_card = 0, alsa_device = 0; +/*static char *pcm_dev_name = "hw:0,0"; */ +static char *pcm_dev_name ="default"; + +static inline void print_pcm_state(snd_pcm_t *handle, char *msg) +{ + fprintf(stderr, "PCM state at %s = %s\n", msg, + snd_pcm_state_name(snd_pcm_state(handle))); +} cst_audiodev *audio_open_alsa(int sps, int channels, cst_audiofmt fmt) { - snd_pcm_channel_info_t pinfo; - snd_pcm_channel_params_t params; - snd_pcm_channel_setup_t setup; - snd_pcm_t *pcm; - cst_audiodev *ad; - int err; - -#ifdef __QNXNTO__ - if (snd_pcm_open_preferred(&pcm,&alsa_card,&alsa_device,SND_PCM_OPEN_PLAYBACK) < 0) - { - cst_errmsg("alsa_audio: failed to open audio device\n"); - cst_error(); - } - if (snd_pcm_plugin_set_disable(pcm,PLUGIN_DISABLE_MMAP) < 0) - { - cst_errmsg("alsa_audio: failed to disable mmap\n"); - snd_pcm_close(pcm); - cst_error(); - } -#else - if (snd_pcm_open(&pcm,alsa_card,alsa_device,SND_PCM_OPEN_PLAYBACK) < 0) - { - cst_errmsg("alsa_audio: failed to open audio device\n"); - cst_error(); - } -#endif - - - memset(&pinfo, 0, sizeof(pinfo)); - memset(¶ms, 0, sizeof(params)); - memset(&setup, 0, sizeof(setup)); - - pinfo.channel = SND_PCM_CHANNEL_PLAYBACK; - snd_pcm_plugin_info(pcm,&pinfo); - - params.mode = SND_PCM_MODE_BLOCK; - params.channel = SND_PCM_CHANNEL_PLAYBACK; - params.start_mode = SND_PCM_START_DATA; - params.stop_mode = SND_PCM_STOP_STOP; - - params.buf.block.frag_size = pinfo.max_fragment_size; - params.buf.block.frags_max = 1; - params.buf.block.frags_min = 1; - - params.format.interleave = 1; - params.format.rate = sps; - params.format.voices = channels; - - switch (fmt) - { - case CST_AUDIO_LINEAR16: + cst_audiodev *ad; + unsigned int real_rate; + int err; + + /* alsa specific stuff */ + snd_pcm_t *pcm_handle; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + snd_pcm_hw_params_t *hwparams; + snd_pcm_format_t format; + snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED; + + /* Allocate the snd_pcm_hw_params_t structure on the stack. */ + snd_pcm_hw_params_alloca(&hwparams); + + /* Open pcm device */ + err = snd_pcm_open(&pcm_handle, pcm_dev_name, stream, 0); + if (err < 0) + { + cst_errmsg("audio_open_alsa: failed to open audio device %s. %s\n", + pcm_dev_name, snd_strerror(err)); + return NULL; + } + + /* Init hwparams with full configuration space */ + err = snd_pcm_hw_params_any(pcm_handle, hwparams); + if (err < 0) + { + snd_pcm_close(pcm_handle); + cst_errmsg("audio_open_alsa: failed to get hardware parameters from audio device. %s\n", snd_strerror(err)); + return NULL; + } + + /* Set access mode */ + err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, access); + if (err < 0) + { + snd_pcm_close(pcm_handle); + cst_errmsg("audio_open_alsa: failed to set access mode. %s.\n", snd_strerror(err)); + return NULL; + } + + /* Determine matching alsa sample format */ + /* This could be implemented in a more */ + /* flexible way (byte order conversion). */ + switch (fmt) + { + case CST_AUDIO_LINEAR16: if (CST_LITTLE_ENDIAN) - params.format.format = SND_PCM_SFMT_S16_LE; + format = SND_PCM_FORMAT_S16_LE; else - params.format.format = SND_PCM_SFMT_S16_BE; + format = SND_PCM_FORMAT_S16_BE; + break; + case CST_AUDIO_LINEAR8: + format = SND_PCM_FORMAT_U8; break; - case CST_AUDIO_LINEAR8: - params.format.format = SND_PCM_SFMT_U8; + case CST_AUDIO_MULAW: + format = SND_PCM_FORMAT_MU_LAW; break; - case CST_AUDIO_MULAW: - params.format.format = SND_PCM_SFMT_MU_LAW; + default: + snd_pcm_close(pcm_handle); + cst_errmsg("audio_open_alsa: failed to find suitable format.\n"); + return NULL; break; - } + } - if((err = snd_pcm_plugin_params(pcm,¶ms)) < 0) - { - cst_errmsg("alsa_audio params setting failed: %s\n",snd_strerror(err)); - snd_pcm_close(pcm); - cst_error(); - } - if((err = snd_pcm_plugin_setup(pcm,SND_PCM_CHANNEL_PLAYBACK)) > 0) { - cst_errmsg("alsa_audio sound prepare setting failed: %s\n",snd_strerror(err)); - snd_pcm_close(pcm); - cst_error(); - } - if((err = snd_pcm_plugin_prepare(pcm,SND_PCM_CHANNEL_PLAYBACK)) > 0) { - cst_errmsg("alsa_audio sound prepare setting failed: %s\n",snd_strerror(err)); - snd_pcm_close(pcm); - cst_error(); - } - - pinfo.channel = SND_PCM_CHANNEL_PLAYBACK; - snd_pcm_plugin_info(pcm,&pinfo); - - ad = cst_alloc(cst_audiodev, 1); - ad->platform_data = pcm; - ad->sps = ad->real_sps = sps; - ad->channels = ad->real_channels = channels; - ad->fmt = ad->real_fmt = fmt; + /* Set samble format */ + err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format); + if (err <0) + { + snd_pcm_close(pcm_handle); + cst_errmsg("audio_open_alsa: failed to set format. %s.\n", snd_strerror(err)); + return NULL; + } + + /* Set sample rate near the disired rate */ + real_rate = sps; + err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &real_rate, 0); + if (err < 0) + { + snd_pcm_close(pcm_handle); + cst_errmsg("audio_open_alsa: failed to set sample rate near %d. %s.\n", sps, snd_strerror(err)); + return NULL; + } + /*FIXME: This is probably too strict */ + assert(sps == real_rate); + + /* Set number of channels */ + assert(channels >0); + err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels); + if (err < 0) + { + snd_pcm_close(pcm_handle); + cst_errmsg("audio_open_alsa: failed to set number of channels to %d. %s.\n", channels, snd_strerror(err)); + return NULL; + } + + /* Commit hardware parameters */ + err = snd_pcm_hw_params(pcm_handle, hwparams); + if (err < 0) + { + snd_pcm_close(pcm_handle); + cst_errmsg("audio_open_alsa: failed to set hw parameters. %s.\n", snd_strerror(err)); + return NULL; + } + + /* Make sure the device is ready to accept data */ + assert(snd_pcm_state(pcm_handle) == SND_PCM_STATE_PREPARED); + + /* Write hardware parameters to flite audio device data structure */ + ad = cst_alloc(cst_audiodev, 1); + assert(ad != NULL); + ad->real_sps = ad->sps = sps; + ad->real_channels = ad->channels = channels; + ad->real_fmt = ad->fmt = fmt; + ad->platform_data = (void *) pcm_handle; - return ad; + return ad; } int audio_close_alsa(cst_audiodev *ad) { - snd_pcm_t *pcm; + int result; + snd_pcm_t *pcm_handle; - if (ad == NULL) - return 0; + if (ad == NULL) + return 0; - pcm = ad->platform_data; - snd_pcm_plugin_flush(pcm,0); - snd_pcm_close(pcm); - cst_free(ad); + pcm_handle = (snd_pcm_t *) ad->platform_data; + result = snd_pcm_close(pcm_handle); + if (result < 0) + { + cst_errmsg("audio_close_alsa: Error: %s.\n", snd_strerror(result)); + } + cst_free(ad); + return result; +} - return 0; +/* Returns zero if recovery was successful. */ +static int recover_from_error(snd_pcm_t *pcm_handle, ssize_t res) +{ + if (res == -EPIPE) /* xrun */ + { + res = snd_pcm_prepare(pcm_handle); + if (res < 0) + { + /* Failed to recover from xrun */ + cst_errmsg("recover_from_write_error: failed to recover from xrun. %s\n.", snd_strerror(res)); + return res; + } + } + else if (res == -ESTRPIPE) /* Suspend */ + { + while ((res = snd_pcm_resume(pcm_handle)) == -EAGAIN) + { + snd_pcm_wait(pcm_handle, 1000); + } + if (res < 0) + { + res = snd_pcm_prepare(pcm_handle); + if (res <0) + { + /* Resume failed */ + cst_errmsg("audio_recover_from_write_error: failed to resume after suspend. %s\n.", snd_strerror(res)); + return res; + } + } + } + else if (res < 0) + { + /* Unknown failure */ + cst_errmsg("audio_recover_from_write_error: %s.\n", snd_strerror(res)); + return res; + } + return 0; } int audio_write_alsa(cst_audiodev *ad, void *samples, int num_bytes) { - snd_pcm_t *pcm = ad->platform_data; - - return snd_pcm_plugin_write(pcm,samples,num_bytes); + size_t frame_size; + ssize_t num_frames, res; + snd_pcm_t *pcm_handle; + char *buf = (char *) samples; + + /* Determine frame size in bytes */ + frame_size = audio_bps(ad->real_fmt) * ad->real_channels; + /* Require that only complete frames are handed in */ + assert((num_bytes % frame_size) == 0); + num_frames = num_bytes / frame_size; + pcm_handle = (snd_pcm_t *) ad->platform_data; + + while (num_frames > 0) + { + res = snd_pcm_writei(pcm_handle, buf, num_frames); + if (res != num_frames) + { + if (res == -EAGAIN || (res > 0 && res < num_frames)) + { + snd_pcm_wait(pcm_handle, 100); + } + else if (recover_from_error(pcm_handle, res) < 0) + { + return -1; + } + } + + if (res >0) + { + num_frames -= res; + buf += res * frame_size; + } + } + return num_bytes; } int audio_flush_alsa(cst_audiodev *ad) { - snd_pcm_t *pcm = ad->platform_data; - - return snd_pcm_plugin_flush(pcm,0); + int result; + result = snd_pcm_drain((snd_pcm_t *) ad->platform_data); + if (result < 0) + { + cst_errmsg("audio_flush_alsa: Error: %s.\n", snd_strerror(result)); + } + /* Prepare device for more data */ + result = snd_pcm_prepare((snd_pcm_t *) ad->platform_data); +if (result < 0) + { + cst_errmsg("audio_flush_alsa: Error: %s.\n", snd_strerror(result)); + } + return result; } int audio_drain_alsa(cst_audiodev *ad) { - snd_pcm_t *pcm = ad->platform_data; - - return snd_pcm_plugin_playback_drain(pcm); + int result; + result = snd_pcm_drop((snd_pcm_t *) ad->platform_data); + if (result < 0) + { + cst_errmsg("audio_drain_alsa: Error: %s.\n", snd_strerror(result)); + } +/* Prepare device for more data */ + result = snd_pcm_prepare((snd_pcm_t *) ad->platform_data); +if (result < 0) + { + cst_errmsg("audio_drain_alsa: Error: %s.\n", snd_strerror(result)); + } + return result; } -