diff --git a/cli.c b/cli.c index 1ddd34b..033de09 100644 --- a/cli.c +++ b/cli.c @@ -31,10 +31,11 @@ extern char *pidPath; extern char *defaultVoice; /* command line options */ -const char *shortOptions = "P:V:dhv"; +const char *shortOptions = "P:V:adhv"; const struct option longOptions[] = { {"pid-path", required_argument, NULL, 'P'}, {"default-voice", required_argument, NULL, 'V'}, + {"acsint", no_argument, NULL, 'a'}, {"debug", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, @@ -79,6 +80,9 @@ void process_cli(int argc, char **argv) case 'V': defaultVoice = strdup(optarg); break; + case 'a': + espeakup_mode = ESPEAKUP_MODE_ACSINT; + break; case 'd': debug = 1; break; diff --git a/espeak.c b/espeak.c index 6c1bec5..510a44c 100644 --- a/espeak.c +++ b/espeak.c @@ -40,6 +40,21 @@ const int volumeMultiplier = 22; volatile int stop_requested = 0; +static int acsint_callback(short *wav, int numsamples, espeak_EVENT * events) +{ + int i; + for (i = 0; events[i].type != espeakEVENT_LIST_TERMINATED; i++) { + if (events[i].type == espeakEVENT_MARK) { + int mark = atoi(events[i].id.name); + if ((mark < 0) || (mark > 255)) + continue; + putchar(mark); + fflush(stdout); + } + } + return 0; +} + static espeak_ERROR set_frequency(struct synth_t *s, int freq, enum adjust_t adj) { @@ -139,9 +154,13 @@ static espeak_ERROR stop_speech(void) static espeak_ERROR speak_text(struct synth_t *s) { espeak_ERROR rc; + int synth_mode = 0; - rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, 0, 0, NULL, - NULL); + if (espeakup_mode == ESPEAKUP_MODE_ACSINT) + synth_mode |= espeakSSML; + + rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, 0, synth_mode, + NULL, NULL); return rc; } @@ -219,6 +238,10 @@ int initialize_espeak(struct synth_t *s) return -1; } + /* We need a callback in acsint mode, but not in speakup mode. */ + if (espeakup_mode == ESPEAKUP_MODE_ACSINT) + espeak_SetSynthCallback(acsint_callback); + /* Setup initial voice parameters */ if (defaultVoice) { set_voice(s, defaultVoice); diff --git a/espeakup.c b/espeakup.c index 40b4a64..124fcc3 100644 --- a/espeakup.c +++ b/espeakup.c @@ -30,6 +30,7 @@ char *pidPath = "/var/run/espeakup.pid"; int debug = 0; +enum espeakup_mode_t espeakup_mode = ESPEAKUP_MODE_SPEAKUP; struct queue_t *synth_queue = NULL; int self_pipe_fds[2]; @@ -99,7 +100,7 @@ int main(int argc, char **argv) /* * If we are not in debug mode, become a daemon and store the pid. */ - if (!debug) { + if (espeakup_mode != ESPEAKUP_MODE_ACSINT && !debug) { daemon(0, 1); if (create_pid_file() < 0) { perror("Unable to create pid file"); diff --git a/espeakup.h b/espeakup.h index 8cd318a..57a3f29 100644 --- a/espeakup.h +++ b/espeakup.h @@ -31,6 +31,11 @@ #define PACKAGE_VERSION "0.80-dev" #define PACKAGE_BUGREPORT "http://github.com/williamh/espeakup/issues" +enum espeakup_mode_t { + ESPEAKUP_MODE_SPEAKUP, + ESPEAKUP_MODE_ACSINT +}; + enum command_t { CMD_SET_FREQUENCY, CMD_SET_PITCH, @@ -70,6 +75,7 @@ struct synth_t { extern struct queue_t *synth_queue; extern int debug; +extern enum espeakup_mode_t espeakup_mode; extern void process_cli(int argc, char **argv); extern void *signal_thread(void *arg); diff --git a/softsynth.c b/softsynth.c index 9747963..d70c381 100644 --- a/softsynth.c +++ b/softsynth.c @@ -28,13 +28,103 @@ #include "espeakup.h" /* max buffer size */ -static const size_t maxBufferSize = 1025; +/* A big fat buffer. */ +static const size_t maxBufferSize = 16 * 1024 + 1; /* synth flush character */ static const int synthFlushChar = 0x18; + static int softFD = 0; +/* Text accumulator: */ +char *textAccumulator; +int textAccumulator_l; + +/* String routines, borrowed from edbrowse: */ +char *EMPTYSTRING = ""; + +void *allocMem(size_t n) +{ + void *s; + if (!n) + return EMPTYSTRING; + if (!(s = malloc(n))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + return s; +} /* allocMem */ + +void *reallocMem(void *p, size_t n) +{ + void *s; + if (!n) { + fprintf(stderr, "Trying to reallocate memory with size of 0.\n"); + exit(1); + } + if (!p) { + fprintf(stderr, "realloc called with a NULL pointer!\n"); + exit(1); + } + if (p == EMPTYSTRING) + return allocMem(n); + if (!(s = realloc(p, n))) { + fprintf(stderr, "Failed to allocate memory.\n"); + exit(1); + } + return s; +} /* reallocMem */ + +char *initString(int *l) +{ + *l = 0; + return EMPTYSTRING; +} + +void stringAndString(char **s, int *l, const char *t) +{ + char *p = *s; + int oldlen, newlen, x; + oldlen = *l; + newlen = oldlen + strlen(t); + *l = newlen; + ++newlen; /* room for the 0 */ + x = oldlen ^ newlen; + if (x > oldlen) { /* must realloc */ + newlen |= (newlen >> 1); + newlen |= (newlen >> 2); + newlen |= (newlen >> 4); + newlen |= (newlen >> 8); + newlen |= (newlen >> 16); + p = reallocMem(p, newlen); + *s = p; + } + strcpy(p + oldlen, t); +} /* stringAndString */ + +void stringAndBytes(char **s, int *l, const char *t, int cnt) +{ + char *p = *s; + int oldlen, newlen, x; + oldlen = *l; + newlen = oldlen + cnt; + *l = newlen; + ++newlen; + x = oldlen ^ newlen; + if (x > oldlen) { /* must realloc */ + newlen |= (newlen >> 1); + newlen |= (newlen >> 2); + newlen |= (newlen >> 4); + newlen |= (newlen >> 8); + newlen |= (newlen >> 16); + p = reallocMem(p, newlen); + *s = p; + } + memcpy(p + oldlen, t, cnt); + p[oldlen + cnt] = 0; +} /* stringAndBytes */ + static void queue_add_cmd(enum command_t cmd, enum adjust_t adj, int value) { struct espeak_entry_t *entry; @@ -147,8 +237,15 @@ static int process_command(struct synth_t *s, char *buf, int start) break; } - if (cmd != CMD_FLUSH && cmd != CMD_UNKNOWN) + if (cmd != CMD_FLUSH && cmd != CMD_UNKNOWN) { + if (espeakup_mode == ESPEAKUP_MODE_ACSINT + && textAccumulator_l != 0) { + queue_add_text(textAccumulator, textAccumulator_l); + free(textAccumulator); + textAccumulator = initString(&textAccumulator_l); + } queue_add_cmd(cmd, adj, value); + } return cp - (buf + start); } @@ -178,6 +275,38 @@ static void process_buffer(struct synth_t *s, char *buf, ssize_t length) } } +static void process_buffer_acsint(struct synth_t *s, char *buf, + ssize_t length) +{ + int start = 0; + int i; + int flushIt = 0; + + while (start < length) { + for (i = start; i < length; i++) { + if (buf[i] == '\r' || buf[i] == '\n') + flushIt = 1; + if (buf[i] >= 0 && buf[i] < ' ') + break; + } + if (i > start) + stringAndBytes(&textAccumulator, &textAccumulator_l, + buf + start, i - start); + if (flushIt) { + if (textAccumulator != EMPTYSTRING) { + queue_add_text(textAccumulator, textAccumulator_l); + free(textAccumulator); + textAccumulator = initString(&textAccumulator_l); + } + flushIt = 0; + } + if (i < length) + start = i = i + process_command(s, buf, i); + else + start = length; + } +} + static void request_espeak_stop(void) { pthread_mutex_lock(&queue_guard); @@ -191,6 +320,12 @@ static void request_espeak_stop(void) int open_softsynth(void) { int rc = 0; + /* If we're in acsint mode, we read from stdin. No need to open. */ + if (espeakup_mode == ESPEAKUP_MODE_ACSINT) { + softFD = STDIN_FILENO; + return 0; + } + /* open the softsynth. */ softFD = open("/dev/softsynth", O_RDWR | O_NONBLOCK); if (softFD < 0) { @@ -216,6 +351,8 @@ void *softsynth_thread(void *arg) int terminalFD = PIPE_READ_FD; int greatestFD; + textAccumulator = initString(&textAccumulator_l); + if (terminalFD > softFD) greatestFD = terminalFD; else @@ -264,7 +401,10 @@ void *softsynth_thread(void *arg) memmove(buf, cp + 1, strlen(cp + 1) + 1); length = strlen(buf); } - process_buffer(s, buf, length); + if (espeakup_mode == ESPEAKUP_MODE_SPEAKUP) + process_buffer(s, buf, length); + else + process_buffer_acsint(s, buf, length); pthread_mutex_lock(&queue_guard); } pthread_cond_signal(&runner_awake);