Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
868 lines (781 sloc) 25.4 KB
/*
* $Id$
*/
//<copyright info
/*
* Tcl ViaVoiceOutloud Interface program (c) Copyright 1999 by
* Paige Phault
*
* The author hereby grants permission to use, copy, modify,
* distribute, and license this software for any purpose,
* provided that existing copyright notices are retained in all
* copies and that this notice is included verbatim in any
* distributions. No written agreement, license, or royalty fee
* is required for any of the authorized uses. Modifications to
* this software may be copyrighted by their authors and need
* not follow the licensing terms described here, provided that
* the new terms are clearly indicated on the first page of each
* file where they apply.
*
* IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
* CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE,
* ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE
* AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
* AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
* ENHANCEMENTS, OR MODIFICATIONS.
*
* dynamic loading of eci library contributed by Jeffrey Sorensen
* --this allows a compiled version of this speech server to be
* distributed without violating the IBM Viavoice license. This
* means that end-users only need install the Viavoice RTK
* (Runtime toolkit) to use Emacspeak with the ViaVoice TTS
* engine.
*
* February 2005 TVR: Updating to use alsalib output routines
*/
//>
//<Usage:
/*
* TCL usage package require tts
*
* proc index x { puts "I just played index $x" }
*
* synth "Hello world" synth -index 0 "This is some" -index 1
* "really wierd" say -index 2 "text" say -reset
*
* The only difference bewtween say and synth is that synth
* calls eciSynthesize and say doesn't. You can put as many
* text blocks as you like after a command.
*/
//>
//<includes
#include <alloca.h>
#include <dlfcn.h>
#include <sys/time.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
#include <tcl.h>
#include "langswitch.h"
#define PACKAGENAME "tts"
#define PACKAGEVERSION "1.0"
#define ECILIBRARYNAME "libibmeci.so"
//>
//< alsa: globals and defines
#define DEFAULT_FORMAT SND_PCM_FORMAT_S16
#define DEFAULT_SPEED 11025
/*
* globals
*/
static snd_pcm_t *AHandle = NULL;
static snd_output_t *Log = NULL;
short *waveBuffer = NULL;
int waveBufferBytes = 0;
//>
//<decls and function prototypes
/*
* The following declarations are derived from the publically
* available documentation for ViaVoice TTS outloud. --they are
* placed here to obviate the need for having the ViaVoice SDK
* installed.
*/
typedef enum { eciDataNotProcessed, eciDataProcessed } ECICallbackReturn;
typedef enum {
eciWaveformBuffer,
eciPhonemeBuffer,
eciIndexReply,
eciPhonemeIndexReply
} ECIMessage;
typedef enum {
eciSynthMode,
eciInputType,
eciTextMode,
eciDictionary,
eciSampleRate = 5,
eciWantPhonemeIndices = 7,
eciRealWorldUnits,
eciLanguageDialect,
eciNumberMode,
eciPhrasePrediction,
eciNumParams
} ECIParam;
static void (*_eciVersion)(char *);
static void *(*_eciNewEx)(enum ECILanguageDialect);
static int (*_eciGetAvailableLanguages)(enum ECILanguageDialect *, int *);
static void (*_eciDelete)(void *);
static int (*_eciReset)(void *);
static int (*_eciStop)(void *);
static int (*_eciClearInput)(void *);
static int (*_eciPause)(void *, int);
static int (*_eciSynthesize)(void *);
static int (*_eciSynchronize)(void *);
static int (*_eciSpeaking)(void *);
static int (*_eciAddText)(void *, char *);
static int (*_eciInsertIndex)(void *, int);
static int (*_eciSetParam)(void *, int, int);
static int (*_eciGetVoiceParam)(void *, int, int);
static int (*_eciSetVoiceParam)(void *, int, int, int);
static int (*_eciSetOutputBuffer)(void *, int, short *);
static int (*_eciSetOutputDevice)(void *, int);
static void (*_eciRegisterCallback)(void *, int (*)(void *, int, long, void *),
void *);
static size_t alsa_init();
static void alsa_reset(); // drop handle and reset
static size_t alsa_configure(void);
extern "C" int Atcleci_Init(Tcl_Interp *interp);
int SetRate(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int GetRate(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int getTTSVersion(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int showAlsaState(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int Say(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int Stop(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int SpeakingP(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int Synchronize(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int Pause(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int Resume(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int SetLanguage(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int alsa_close();
int alsa_retry();
int eciCallback(void *, int, long, void *);
//>
//<alsa: set hw and sw params
static size_t alsa_configure(void) {
//<init:
size_t chunk_bytes, bits_per_sample, bits_per_frame = 0;
snd_pcm_uframes_t period_size, buffer_size = 0;
snd_pcm_hw_params_t *params;
unsigned int rate = DEFAULT_SPEED;
int err;
snd_pcm_hw_params_alloca(&params);
//>
//<defaults:
err = snd_pcm_hw_params_any(AHandle, params);
if (err < 0) {
fprintf(stderr, "PCM: Broken configuration: no configurations available");
exit(EXIT_FAILURE);
}
//>
//<Format:
err = snd_pcm_hw_params_set_format(AHandle, params, DEFAULT_FORMAT);
if (err < 0) {
fprintf(stderr, "Sample format non available");
exit(EXIT_FAILURE);
}
//>
//<Channels:
err = snd_pcm_hw_params_set_channels(AHandle, params, 1);
if (err < 0) {
fprintf(stderr, "Channels count non available");
exit(EXIT_FAILURE);
}
//>
//<Rate:
err = snd_pcm_hw_params_set_rate_resample(AHandle, params, 1);
assert(err >= 0);
err = snd_pcm_hw_params_set_rate(AHandle, params, rate, 0);
assert(err >= 0);
//>
//<Access Mode:
err = snd_pcm_hw_params_set_access(AHandle, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) {
fprintf(stderr, "Access type not available");
exit(EXIT_FAILURE);
}
//>
//<Commit hw params:
err = snd_pcm_hw_params(AHandle, params);
if (err < 0) {
fprintf(stderr, "Unable to install hw params:");
exit(EXIT_FAILURE);
}
//>
//<finalize period_size and buffer_size:
snd_pcm_hw_params_get_period_size(params, &period_size, 0);
snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
if (period_size == buffer_size) {
fprintf(stderr, "Can't use period equal to buffer size (%lu == %lu)",
period_size, buffer_size);
exit(EXIT_FAILURE);
}
//>
bits_per_sample = snd_pcm_format_physical_width(DEFAULT_FORMAT);
bits_per_frame = bits_per_sample * 1; // mono
chunk_bytes = period_size * bits_per_frame / 8;
return chunk_bytes;
}
//>
//<xrun and suspend
#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
// static void xrun(void) {
// snd_pcm_status_t *status;
// int res;
// snd_pcm_status_alloca(&status);
// if ((res = snd_pcm_status(AHandle, status)) < 0) {
// fprintf(stderr, "status error: %s", snd_strerror(res));
// alsa_close();
// exit(EXIT_FAILURE);
// }
// if (snd_pcm_status_get_state(status) == SND_PCM_STATE_RUNNING) {
// // DMIX appears to be in a confused state, attempt to restore sanity.
// if ((res = snd_pcm_prepare(AHandle)) < 0) {
// // Attempt to fix failed!
// fprintf(stderr, "XRUN: prepare error: %s", snd_strerror(res));
// alsa_close();
// exit(EXIT_FAILURE);
// }
// return; // ready to continue
// }
// 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);
// fprintf(stderr, "Underrun!!! (at least %.3f ms long)\n",
// diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
// if ((res = snd_pcm_prepare(AHandle)) < 0) {
// fprintf(stderr, "xrun: prepare error: %s", snd_strerror(res));
// alsa_close();
// exit(EXIT_FAILURE);
// }
// return; // ok, data should be accepted again
// }
// fprintf(stderr, "read/write error, state = %s\n",
// snd_pcm_state_name(snd_pcm_status_get_state(status)));
// // DMIX leaves device in a strange state, so retry.
// alsa_retry();
// }
static void suspend(void) {
int res;
fprintf(stderr, "Suspended. Trying resume. ");
fflush(stderr);
while ((res = snd_pcm_resume(AHandle)) == -EAGAIN)
sleep(1); /* wait until suspend flag is released */
if (res < 0) {
fprintf(stderr, "Failed. Restarting stream. ");
fflush(stderr);
if ((res = snd_pcm_prepare(AHandle)) < 0) {
fprintf(stderr, "suspend: prepare error: %s", snd_strerror(res));
alsa_close();
exit(EXIT_FAILURE);
}
}
fprintf(stderr, "Done.\n");
}
//>
//<alsa: pcm_write
static ssize_t pcm_write(short *data, size_t count) {
ssize_t r;
ssize_t result = 0;
int res;
while (count > 0) {
r = snd_pcm_writei(AHandle, data, count);
if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
snd_pcm_wait(AHandle, 100);
} else if (r == -EPIPE) {
if ((res = snd_pcm_prepare(AHandle)) < 0) {
fprintf(stderr, "Write/Retry: prepare error: %s", snd_strerror(res));
alsa_close();
exit(EXIT_FAILURE);
}
} else if (r == -ESTRPIPE) {
suspend();
} else if (r < 0) {
fprintf(stderr, "write error: %s", snd_strerror(r));
alsa_close();
exit(EXIT_FAILURE);
}
if (r > 0) {
result += r;
count -= r;
data += r;
}
}
return result;
}
//>
//<alsa_reset
void alsa_reset() {
snd_pcm_drop(AHandle); // flush all frames
snd_pcm_prepare(AHandle);
}
//>
//<alsa_init
static size_t alsa_init() {
int err;
const char *device = getenv("ALSA_DEFAULT");
if (device == NULL) {
device = "default";
}
size_t chunk_bytes = 0;
if ((err = snd_pcm_open(&AHandle, device, SND_PCM_STREAM_PLAYBACK,
0 /* blocking */)) < 0) {
fprintf(stderr, "Playback open error: %s\n", snd_strerror(err));
exit(1);
}
err = snd_output_stdio_attach(&Log, stderr, 0);
assert(err >= 0);
chunk_bytes = alsa_configure();
return chunk_bytes;
}
//>
//<alsa_close
int alsa_close() {
// shut down alsa
if (AHandle) {
snd_pcm_close(AHandle);
}
free(waveBuffer);
return TCL_OK;
}
int alsa_retry() {
fprintf(stderr, "re-initializing ALSA\n");
int res;
if ((res = snd_pcm_prepare(AHandle)) < 0) {
fprintf(stderr, "Retry: prepare error: %s", snd_strerror(res));
alsa_close();
exit(EXIT_FAILURE);
}
return TCL_OK;
}
//>
//<eciFree
void TclEciFree(ClientData eciHandle) { _eciDelete(eciHandle); }
//>
//<tcleci_init
int Atcleci_Init(Tcl_Interp *interp) {
int rc;
size_t chunk_bytes = 0;
void *eciHandle;
void *eciLib;
//< configure shared library symbols
eciLib = dlopen(ECILIBRARYNAME, RTLD_LAZY);
if (eciLib == NULL) {
Tcl_AppendResult(interp, "Could not load ", ECILIBRARYNAME, "\n", dlerror(),
"\nPlease install the IBM ViaVoice Outloud RTK", NULL);
return TCL_ERROR;
}
_eciVersion = (void (*)(char *))(unsigned long)dlsym(eciLib, "eciVersion");
_eciGetAvailableLanguages = (int (*)(enum ECILanguageDialect *, int *))(
unsigned long)dlsym(eciLib, "eciGetAvailableLanguages");
_eciNewEx = (void *(*)(enum ECILanguageDialect))(unsigned long)dlsym(
eciLib, "eciNewEx");
_eciDelete = (void (*)(void *))(unsigned long)dlsym(eciLib, "eciDelete");
_eciReset = (int (*)(void *))(unsigned long)dlsym(eciLib, "eciReset");
_eciStop = (int (*)(void *))(unsigned long)dlsym(eciLib, "eciStop");
_eciClearInput =
(int (*)(void *))(unsigned long)dlsym(eciLib, "eciClearInput");
_eciPause = (int (*)(void *, int))(unsigned long)dlsym(eciLib, "eciPause");
_eciSynthesize =
(int (*)(void *))(unsigned long)dlsym(eciLib, "eciSynthesize");
_eciSynchronize =
(int (*)(void *))(unsigned long)dlsym(eciLib, "eciSynchronize");
_eciSpeaking = (int (*)(void *))(unsigned long)dlsym(eciLib, "eciSpeaking");
_eciInsertIndex =
(int (*)(void *, int))(unsigned long)dlsym(eciLib, "eciInsertIndex");
_eciAddText =
(int (*)(void *, char *))(unsigned long)dlsym(eciLib, "eciAddText");
_eciSetParam =
(int (*)(void *, int, int))(unsigned long)dlsym(eciLib, "eciSetParam");
_eciGetVoiceParam = (int (*)(void *, int, int))(unsigned long)dlsym(
eciLib, "eciGetVoiceParam");
_eciSetVoiceParam = (int (*)(void *, int, int, int))(unsigned long)dlsym(
eciLib, "eciSetVoiceParam");
_eciRegisterCallback =
(void (*)(void *, int (*)(void *, int, long, void *), void *))(
unsigned long)dlsym(eciLib, "eciRegisterCallback");
_eciSetOutputBuffer = (int (*)(void *, int, short *))(unsigned long)dlsym(
eciLib, "eciSetOutputBuffer");
_eciSetOutputDevice =
(int (*)(void *, int))(unsigned long)dlsym(eciLib, "eciSetOutputDevice");
//>
//< check for needed symbols
int okay = 1;
if (!_eciNewEx) {
okay = 0;
Tcl_AppendResult(interp, "eciNewEx undef\n", NULL);
}
if (!_eciDelete) {
okay = 0;
Tcl_AppendResult(interp, "eciDelete undef\n", NULL);
}
if (!_eciReset) {
okay = 0;
Tcl_AppendResult(interp, "eciReset undef\n", NULL);
}
if (!_eciStop) {
okay = 0;
Tcl_AppendResult(interp, "eciStop undef\n", NULL);
}
if (!_eciClearInput) {
okay = 0;
Tcl_AppendResult(interp, "eciClearInput undef\n", NULL);
}
if (!_eciPause) {
okay = 0;
Tcl_AppendResult(interp, "eciPause undef\n", NULL);
}
if (!_eciSynthesize) {
okay = 0;
Tcl_AppendResult(interp, "eciSynthesize undef\n", NULL);
}
if (!_eciSpeaking) {
okay = 0;
Tcl_AppendResult(interp, "eciSpeaking undef\n", NULL);
}
if (!_eciInsertIndex) {
okay = 0;
Tcl_AppendResult(interp, "eciInsertIndex undef\n", NULL);
}
if (!_eciAddText) {
okay = 0;
Tcl_AppendResult(interp, "eciAddText undef\n", NULL);
}
if (!_eciSetParam) {
okay = 0;
Tcl_AppendResult(interp, "eciSetParam undef\n", NULL);
}
if (!_eciSetParam) {
okay = 0;
Tcl_AppendResult(interp, "eciSetParam undef\n", NULL);
}
if (!_eciGetVoiceParam) {
okay = 0;
Tcl_AppendResult(interp, "eciGetVoiceParam undef\n", NULL);
}
if (!_eciSetVoiceParam) {
okay = 0;
Tcl_AppendResult(interp, "eciSetVoiceParam undef\n", NULL);
}
if (!_eciRegisterCallback) {
okay = 0;
Tcl_AppendResult(interp, "eciRegisterCallback undef\n", NULL);
}
if (!_eciSetOutputBuffer) {
okay = 0;
Tcl_AppendResult(interp, "eciSetOutputBuffer undef\n", NULL);
}
if (!_eciSetOutputDevice) {
okay = 0;
Tcl_AppendResult(interp, "eciSetOutputDevice undef\n", NULL);
}
if (!_eciGetAvailableLanguages) {
okay = 0;
Tcl_AppendResult(interp, "_eciGetAvailableLanguages undef\n", NULL);
}
if (!okay) {
Tcl_AppendResult(interp, "Missing symbols from ", ECILIBRARYNAME, NULL);
return TCL_ERROR;
}
//>
//<setup package, create tts handle
if (Tcl_PkgProvide(interp, PACKAGENAME, PACKAGEVERSION) != TCL_OK) {
Tcl_AppendResult(interp, "Error loading ", PACKAGENAME, NULL);
return TCL_ERROR;
}
static enum ECILanguageDialect aLanguages[LANG_INFO_MAX];
int nLanguages = LANG_INFO_MAX;
_eciGetAvailableLanguages(aLanguages, &nLanguages);
enum ECILanguageDialect aDefaultLanguage =
initLanguage(interp, aLanguages, nLanguages);
if (aDefaultLanguage == NODEFINEDCODESET) {
Tcl_AppendResult(interp, "No language found", PACKAGENAME, NULL);
return TCL_ERROR;
}
fprintf(stderr, "Found %d languages.\n", nLanguages);
eciHandle = _eciNewEx(aDefaultLanguage);
if (eciHandle == 0) {
Tcl_AppendResult(interp, "Could not open text-to-speech engine", NULL);
return TCL_ERROR;
}
//>
//<initialize alsa
chunk_bytes = alsa_init();
//<Finally, allocate waveBuffer
fprintf(stderr, "allocating %d 16 bit samples, %f seconds of audio.\n",
(int)chunk_bytes, (chunk_bytes / (float)DEFAULT_SPEED));
//waveBuffer = (short *)malloc(chunk_bytes * sizeof(short));
waveBuffer = (short *)calloc(chunk_bytes, sizeof(short));
waveBufferBytes = (chunk_bytes * sizeof(short));
if (waveBuffer == NULL) {
fprintf(stderr, "not enough memory");
alsa_close();
exit(EXIT_FAILURE);
}
//>
//>
//<initialize TTS
if ((_eciSetParam(eciHandle, eciInputType, 1) == -1) ||
(_eciSetParam(eciHandle, eciSynthMode, 1) == -1) ||
(_eciSetParam(eciHandle, eciSampleRate, 1) == -1)) {
Tcl_AppendResult(interp, "Could not initialized tts", NULL);
_eciDelete(eciHandle);
return TCL_ERROR;
}
_eciRegisterCallback(eciHandle, eciCallback, interp);
//>
//<set output to buffer
rc = _eciSynchronize(eciHandle);
if (!rc) {
Tcl_AppendResult(interp, "Error resetting TTS engine.\n", NULL);
return TCL_ERROR;
}
rc = _eciSetOutputBuffer(eciHandle, chunk_bytes, waveBuffer);
if (!rc) {
Tcl_AppendResult(interp, "Error setting output buffer.\n", NULL);
return TCL_ERROR;
}
fprintf(stderr, "output buffered to waveBuffer with size %d\n",
(int)chunk_bytes);
//>
//<register tcl commands
Tcl_CreateObjCommand(interp, "setRate", SetRate, (ClientData)eciHandle,
TclEciFree);
Tcl_CreateObjCommand(interp, "getRate", GetRate, (ClientData)eciHandle,
TclEciFree);
Tcl_CreateObjCommand(interp, "ttsVersion", getTTSVersion,
(ClientData)eciHandle, TclEciFree);
Tcl_CreateObjCommand(interp, "alsaState", showAlsaState, (ClientData)NULL,
TclEciFree);
Tcl_CreateObjCommand(interp, "say", Say, (ClientData)eciHandle, TclEciFree);
Tcl_CreateObjCommand(interp, "synth", Say, (ClientData)eciHandle, NULL);
Tcl_CreateObjCommand(interp, "synchronize", Synchronize,
(ClientData)eciHandle, TclEciFree);
Tcl_CreateObjCommand(interp, "stop", Stop, (ClientData)eciHandle, TclEciFree);
Tcl_CreateObjCommand(interp, "speakingP", SpeakingP, (ClientData)eciHandle,
TclEciFree);
Tcl_CreateObjCommand(interp, "pause", Pause, (ClientData)eciHandle,
TclEciFree);
Tcl_CreateObjCommand(interp, "resume", Resume, (ClientData)eciHandle,
TclEciFree);
Tcl_CreateObjCommand(interp, "setLanguage", SetLanguage,
(ClientData)eciHandle, TclEciFree);
//>
//<set up index processing
rc = Tcl_Eval(interp,
"proc index x {global tts; \
set tts(last_index) $x}");
//>
return TCL_OK;
}
//>
//<playTTS
int playTTS(size_t count) {
pcm_write(waveBuffer, count);
return eciDataProcessed;
}
//>
//<eciCallBack
int eciCallback(void *eciHandle, int msg, long lparam, void *data) {
Tcl_Interp *interp = (Tcl_Interp *)data;
if (msg == eciIndexReply) {
char buffer[128];
snprintf(buffer, 128, "index %ld", lparam);
int rc = Tcl_Eval(interp, buffer);
if (rc != TCL_OK) Tcl_BackgroundError(interp);
} else if ((msg == eciWaveformBuffer) && (lparam > 0)) {
playTTS(lparam);
}
return 1;
}
//>
//<getRate, setRate
int GetRate(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
int rc, rate, voice;
if (objc != 2) {
Tcl_AppendResult(interp, "Usage: getRate voiceCode ", TCL_STATIC);
return TCL_ERROR;
}
rc = Tcl_GetIntFromObj(interp, objv[1], &voice);
if (rc != TCL_OK) return rc;
rate = _eciGetVoiceParam(eciHandle, voice, 6 /* eciSpeed */);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rate));
return TCL_OK;
}
int SetRate(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
int rc, rate, voice;
if (objc != 3) {
Tcl_AppendResult(interp, "Usage: setRate voiceCode speechRate ",
TCL_STATIC);
return TCL_ERROR;
}
rc = Tcl_GetIntFromObj(interp, objv[1], &voice);
if (rc != TCL_OK) return rc;
rc = Tcl_GetIntFromObj(interp, objv[2], &rate);
if (rc != TCL_OK) return rc;
rc = _eciSetVoiceParam(eciHandle, voice, 6 /* eciSpeed */, rate);
if (rc == -1) {
Tcl_AppendResult(interp, "Could not set rate", TCL_STATIC);
return TCL_ERROR;
}
// fprintf(stderr, "setRate returned %d\n", rc);
rate = _eciGetVoiceParam(eciHandle, voice, 6 /* eciSpeed */);
fprintf(stderr, "eciGetVoiceParam returned %d for voice %d \n", rate, voice);
return TCL_OK;
}
//>
//<say
int Say(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
int i, rc, index, length;
for (i = 1; i < objc; i++) {
// if string begins with -, assume it is an index value
char *txt = Tcl_GetStringFromObj(objv[i], &length);
if (Tcl_StringMatch(txt, "-reset")) {
_eciReset(eciHandle);
if ((_eciSetParam(eciHandle, eciInputType, 1) == -1) ||
(_eciSetParam(eciHandle, eciSynthMode, 1) == -1) ||
(_eciSetParam(eciHandle, eciSampleRate, 1) == -1)) {
Tcl_AppendResult(interp, "Could not re-initialized tts", NULL);
return TCL_ERROR;
}
} else if (Tcl_StringMatch(txt, "-index")) {
i++;
if (i == objc) {
Tcl_AppendResult(interp, "missing index parameter", TCL_STATIC);
return TCL_ERROR;
}
rc = Tcl_GetIntFromObj(interp, objv[i], &index);
if (rc != TCL_OK) return rc;
rc = _eciInsertIndex(eciHandle, index);
if (!rc) {
Tcl_AppendResult(interp, "Could not insert index", TCL_STATIC);
return TCL_ERROR;
}
} else {
char *dest = convertFromUTF8(interp, Tcl_GetStringFromObj(objv[i], NULL));
if (dest) {
rc = _eciAddText(eciHandle, dest);
free(dest);
if (!rc) {
Tcl_SetResult(interp, const_cast<char *>("Internal tts error"),
TCL_STATIC);
return TCL_ERROR;
}
}
}
}
if (Tcl_StringMatch(Tcl_GetStringFromObj(objv[0], NULL), "synth")) {
rc = _eciSynthesize(eciHandle);
if (!rc) {
Tcl_SetResult(interp, const_cast<char *>("Internal tts synth error"),
TCL_STATIC);
return TCL_ERROR;
}
}
return TCL_OK;
}
//>
//<stop, pause, resume
//<synchronize, stop
int Synchronize(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
int rc = _eciSynchronize(eciHandle);
if (!rc) {
Tcl_SetResult(interp, const_cast<char *>("Internal tts synth error"),
TCL_STATIC);
return TCL_ERROR;
}
return TCL_OK;
}
int Stop(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
if (_eciStop(eciHandle)) {
alsa_reset();
memset(waveBuffer, 0,waveBufferBytes);
usleep(10);
return TCL_OK;
}
Tcl_SetResult(interp, const_cast<char *>("Could not stop synthesis"),
TCL_STATIC);
return TCL_ERROR;
}
//>
int SpeakingP(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
if (_eciSpeaking(eciHandle)) {
Tcl_SetObjResult(interp, Tcl_NewIntObj(1));
} else {
Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
}
return TCL_OK;
}
int Pause(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
if (_eciPause(eciHandle, 1)) return TCL_OK;
Tcl_SetResult(interp, const_cast<char *>("Could not pause synthesis"),
TCL_STATIC);
return TCL_ERROR;
}
int Resume(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
if (_eciPause(eciHandle, 0)) return TCL_OK;
Tcl_SetResult(interp, const_cast<char *>("Could not resume synthesis"),
TCL_STATIC);
return TCL_ERROR;
}
//>
//<getVersion
int getTTSVersion(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
if (objc != 1) {
Tcl_AppendResult(interp, "Usage: ttsVersion ", TCL_STATIC);
return TCL_ERROR;
}
char *version = (char *)alloca(16);
_eciVersion(version);
Tcl_SetResult(interp, version, TCL_STATIC);
return TCL_OK;
}
//>
//<show alsa state
int showAlsaState(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
if (objc != 1) {
Tcl_AppendResult(interp, "Usage: alsaState ", TCL_STATIC);
return TCL_ERROR;
}
fprintf(stderr, "PCM name: '%s'\n", snd_pcm_name(AHandle));
fprintf(stderr, "PCM state: %s\n",
snd_pcm_state_name(snd_pcm_state(AHandle)));
snd_pcm_dump(AHandle, Log);
return TCL_OK;
}
//>
//<SetLanguage
int SetLanguage(ClientData eciHandle, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]) {
int aIndex;
const char *code = getAnnotation(interp, &aIndex);
if (code) {
char buffer[ANNOTATION_MAX_SIZE];
snprintf(buffer, ANNOTATION_MAX_SIZE, "`l%s", code);
_eciAddText(eciHandle, buffer);
}
return TCL_OK;
}
//>
//<end of file
// local variables:
// folded-file: t
// end:
//>