diff --git a/Makefile b/Makefile index 61faf5e..931d0ed 100644 --- a/Makefile +++ b/Makefile @@ -2,26 +2,19 @@ CC = cc INSTALL = /usr/bin/install RM = /bin/rm -f -PREFIX = /usr -BINDIR = $(PREFIX)/bin - -LDLIBS = -lespeak - SRCS = \ espeakup.c \ + queue.c \ softsynth.c \ synth.c OBJS = $(SRCS:.c=.o) -ifeq ("$(origin TAG)", "command line") -TIMESTAMP := _p$(shell date +%Y%m%d) -endif +CFLAGS = -ggdb +LDLIBS = -lespeak -VERSION := $(shell grep 'Version.*=' espeakup.c | sed 's/.*"\(.*\)";/\1/') -TAG := v$(VERSION) -TARPREFIX := espeakup-$(VERSION) -TARFILE := $(TARPREFIX)$(TIMESTAMP).tar +PREFIX = /usr +BINDIR = $(PREFIX)/bin all: espeakup @@ -37,6 +30,15 @@ distclean: clean espeakup: $(OBJS) +ifeq ("$(origin TAG)", "command line") +TIMESTAMP := _p$(shell date +%Y%m%d) +endif + +VERSION := $(shell grep 'Version.*=' espeakup.c | sed 's/.*"\(.*\)";/\1/') +TAG := v$(VERSION) +TARPREFIX := espeakup-$(VERSION) +TARFILE := $(TARPREFIX)$(TIMESTAMP).tar + tarball: git archive --format=tar --prefix=$(TARPREFIX)/ $(TAG) > $(TARFILE) tar f $(TARFILE) --delete $(TARPREFIX)/.gitignore diff --git a/espeakup.c b/espeakup.c index a37e214..70f337a 100644 --- a/espeakup.c +++ b/espeakup.c @@ -35,6 +35,12 @@ const char *shortOptions = "dhv"; /* path to our pid file */ const char *pidPath = "/var/run/espeakup.pid"; +/* default voice settings */ +const int defaultFrequency = 5; +const int defaultPitch = 5; +const int defaultRate = 5; +const int defaultVolume = 5; + int debug = 0; int espeakup_is_running(void) @@ -123,13 +129,7 @@ void process_cli(int argc, char **argv) int main(int argc, char **argv) { - struct synth_t s = { - .frequency = 5, - .pitch = 5, - .rate = 5, - .voice = "default", - .volume = 5, - }; + struct synth_t s; /* process command line options */ process_cli(argc, argv); @@ -158,11 +158,10 @@ int main(int argc, char **argv) espeak_SetSynthCallback(SynthCallback); /* Setup initial voice parameters */ - set_voice(&s); - set_frequency (&s); - set_pitch (&s); - set_rate (&s); - set_volume(&s); + set_frequency (&s, defaultFrequency, ADJ_SET); + set_pitch (&s, defaultPitch, ADJ_SET); + set_rate (&s, defaultRate, ADJ_SET); + set_volume(&s, defaultVolume, ADJ_SET); /* register signal handler */ signal(SIGINT, espeakup_sighandler); diff --git a/espeakup.h b/espeakup.h index f0a0de1..90efc9a 100644 --- a/espeakup.h +++ b/espeakup.h @@ -22,6 +22,23 @@ #include +enum command_t { + CMD_SET_FREQUENCY, + CMD_SET_PITCH, + CMD_SET_RATE, + CMD_SET_VOICE, + CMD_SET_VOLUME, + CMD_SPEAK_TEXT, + CMD_FLUSH, + CMD_UNKNOWN, +}; + +enum adjust_t { + ADJ_DEC, + ADJ_SET, + ADJ_INC, +}; + struct synth_t { int frequency; int pitch; @@ -35,11 +52,11 @@ struct synth_t { extern int debug; extern int SynthCallback(short *wav, int numsamples, espeak_EVENT *events); -extern espeak_ERROR set_frequency (struct synth_t *s); -extern espeak_ERROR set_pitch (struct synth_t *s); -extern espeak_ERROR set_rate (struct synth_t *s); +extern espeak_ERROR set_frequency(struct synth_t *s, int freq, enum adjust_t adj); +extern espeak_ERROR set_pitch (struct synth_t *s, int pitch, enum adjust_t adj); +extern espeak_ERROR set_rate (struct synth_t *s, int rate, enum adjust_t adj); extern espeak_ERROR set_voice(struct synth_t *s); -extern espeak_ERROR set_volume (struct synth_t *s); +extern espeak_ERROR set_volume (struct synth_t *s, int vol, enum adjust_t adj); extern espeak_ERROR stop_speech(void); extern espeak_ERROR speak_text(struct synth_t *s); extern void open_softsynth(void); diff --git a/queue.c b/queue.c new file mode 100644 index 0000000..70d3a33 --- /dev/null +++ b/queue.c @@ -0,0 +1,139 @@ +/* + * 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 . + */ + +#include +#include +#include +#include + +#include "espeakup.h" + +struct queue_entry_t { + enum command_t cmd; + enum adjust_t adjust; + int value; + char *buf; + int len; + struct queue_entry_t *next; +}; + +static struct queue_entry_t *first = NULL; +static struct queue_entry_t *last = NULL; + +static void queue_add(struct queue_entry_t *entry) +{ + assert(entry); + entry->next = NULL; + if (! last) + last = entry; + if (! first) { + first = entry; + } else { + first->next = entry; + first = first->next; + } +} + +static void queue_remove(void) +{ + struct queue_entry_t *temp; + + assert(last); + temp = last; + last = temp->next; + if (temp->cmd == CMD_SPEAK_TEXT) + free(temp->buf); + free(temp); + if (! last) + first = last; +} + +void queue_clear(void) +{ + while(last) + queue_remove(); +} + +void queue_add_cmd(enum command_t cmd, enum adjust_t adj, int value) +{ + struct queue_entry_t *entry; + + entry = malloc(sizeof(struct queue_entry_t)); + if (! entry) { + perror("unable to allocate memory for queue entry"); + return; + } + entry->cmd = cmd; + entry->adjust = adj; + entry->value = value; + queue_add(entry); +} + +void queue_add_text(char *txt, size_t length) +{ + struct queue_entry_t *entry; + + entry = malloc(sizeof(struct queue_entry_t)); + if (! entry) { + perror("unable to allocate memory for queue entry"); + return; + } + entry->cmd = CMD_SPEAK_TEXT; + entry->adjust = ADJ_SET; + entry->buf = strdup(txt); + if (! entry->buf) { + perror("unable to allocate space for text"); + free(entry); + return; + } + entry->len = length; + queue_add(entry); +} + +void queue_process_entry(struct synth_t *s) +{ + espeak_ERROR error; + + if(! last) + return; + + switch (last->cmd) { + case CMD_SET_FREQUENCY: + error = set_frequency(s, last->value, last->adjust); + break; + case CMD_SET_PITCH: + error = set_pitch(s, last->value, last->adjust); + break; + case CMD_SET_RATE: + error = set_rate(s, last->value, last->adjust); + break; + case CMD_SET_VOICE: + break; + case CMD_SET_VOLUME: + error = set_volume(s, last->value, last->adjust); + break; + case CMD_SPEAK_TEXT: + s->buf = last->buf; + s->len = last->len; + error = speak_text(s); + break; + } + if (error == EE_OK) + queue_remove(); +} diff --git a/softsynth.c b/softsynth.c index 55ae171..50ec96b 100644 --- a/softsynth.c +++ b/softsynth.c @@ -26,72 +26,74 @@ #include "espeakup.h" /* max buffer size */ -const int maxBufferSize = 1025; +const size_t maxBufferSize = 1025; static int softFD = 0; static int process_command(struct synth_t *s, char *buf, int start) { - int rc; - char value; - char param; + char *cp; + int value; + enum adjust_t adj; + enum command_t cmd; - rc = 1; - switch (buf[start]) { + cp = buf+start; + switch (*cp) { case 1: - if (buf[start+1] == '+' || buf[start+1] == '-') { - value = buf[start+2]-'0'; - param = buf[start+3]; - if (buf[start+1] == '-') - value = -value; - switch (param) { - case 'f': - s->frequency += value; - set_frequency (s); - break; - case 'p': - s->pitch += value; - set_pitch (s); - break; - case 's': - s->rate += value; - set_rate (s); - break; - case 'v': - s->volume += value; - set_volume (s); - break; - } - rc = 4; - } else if (buf[start+1] >= '0' && buf[start+1] <= '9') { - value = buf[start+1]-'0'; - param = buf[start+2]; - switch (param) { - case 'f': - s->frequency = value; - set_frequency (s); - break; - case 'p': - s->pitch = value; - set_pitch (s); - break; - case 's': - s->rate = value; - set_rate (s); - break; - case 'v': - s->volume = value; - set_volume (s); - break; - } + cp++; + switch (*cp) { + case '+': + adj = ADJ_INC; + cp++; + break; + case '-': + adj = ADJ_DEC; + cp++; + break; + default: + adj = ADJ_SET; + break; + } + + value = 0; + while(isdigit(*cp)) { + value = value * 10 + (*cp - '0'); + cp++; + } + + switch (*cp) { + case 'f': + cmd = CMD_SET_FREQUENCY; + cp++; + break; + case 'p': + cmd = CMD_SET_PITCH; + cp++; + break; + case 's': + cmd = CMD_SET_RATE; + cp++; + break; + case 'v': + cmd = CMD_SET_VOLUME; + cp++; + break; } - rc = 3; break; case 24: + cmd = CMD_FLUSH; stop_speech(); + queue_clear(); + cp++; + break; + default: + cmd = CMD_UNKNOWN; + cp++; break; } - return rc; + if (cmd != CMD_FLUSH && cmd != CMD_UNKNOWN) + queue_add_cmd(cmd, adj, value); + return cp - (buf + start); } static void process_buffer (struct synth_t *s, char *buf, size_t length) @@ -110,9 +112,7 @@ static void process_buffer (struct synth_t *s, char *buf, size_t length) txtLen = end-start; strncpy (txtBuf, buf + start, txtLen); *(txtBuf+txtLen) = 0; - s->buf = txtBuf; - s->len = txtLen; - speak_text(s); + queue_add_text(txtBuf, txtLen); } if (end < length) start = end = end+process_command (s, buf, end); @@ -141,11 +141,13 @@ void main_loop (struct synth_t *s) int i; size_t length; char buf[maxBufferSize]; + struct timeval tv = {0, 0}; while (1) { + queue_process_entry(s); FD_ZERO (&set); FD_SET (softFD, &set); - i = select (softFD+1, &set, NULL, NULL, NULL); + i = select (softFD+1, &set, NULL, NULL, &tv); if (i < 0) { if (errno == EINTR) continue; @@ -153,8 +155,12 @@ void main_loop (struct synth_t *s) break; } + if (! FD_ISSET(softFD, &set)) + continue; length = read (softFD, buf, maxBufferSize - 1); if (length < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; perror("Read from softsynth failed"); break; } diff --git a/synth.c b/synth.c index 7e34a63..1a83405 100644 --- a/synth.c +++ b/synth.c @@ -31,19 +31,46 @@ int SynthCallback(short *wav, int numsamples, espeak_EVENT *events) return 0; } -espeak_ERROR set_frequency (struct synth_t *s) +espeak_ERROR set_frequency (struct synth_t *s, int freq, enum adjust_t adj) { - return (espeak_SetParameter(espeakRANGE, s->frequency * frequencyMultiplier, 0)); + espeak_ERROR rc; + + if (adj == ADJ_DEC) + freq = -freq; + if (adj != ADJ_SET) + freq += s->frequency; + rc = espeak_SetParameter(espeakRANGE, freq * frequencyMultiplier, 0); + if (rc == EE_OK) + s->frequency = freq; + return rc; } -espeak_ERROR set_pitch (struct synth_t *s) +espeak_ERROR set_pitch (struct synth_t *s, int pitch, enum adjust_t adj) { - return (espeak_SetParameter(espeakPITCH, s->pitch * pitchMultiplier, 0)); + espeak_ERROR rc; + + if (adj == ADJ_DEC) + pitch = -pitch; + if (adj != ADJ_SET) + pitch += s->pitch; + rc = espeak_SetParameter(espeakPITCH, pitch * pitchMultiplier, 0); + if (rc == EE_OK) + s->pitch = pitch; + return rc; } -espeak_ERROR set_rate (struct synth_t *s) +espeak_ERROR set_rate (struct synth_t *s, int rate, enum adjust_t adj) { - return (espeak_SetParameter(espeakRATE, s->rate * rateMultiplier + rateOffset, 0)); + espeak_ERROR rc; + + if (adj == ADJ_DEC) + rate = -rate; + if (adj != ADJ_SET) + rate += s->rate; + rc = espeak_SetParameter(espeakRATE, rate * rateMultiplier + rateOffset, 0); + if (rc == EE_OK) + s->rate = rate; + return rc; } espeak_ERROR set_voice(struct synth_t *s) @@ -51,9 +78,14 @@ espeak_ERROR set_voice(struct synth_t *s) return (espeak_SetVoiceByName(s->voice)); } -espeak_ERROR set_volume (struct synth_t *s) +espeak_ERROR set_volume (struct synth_t *s, int vol, enum adjust_t adj) { - return (espeak_SetParameter(espeakVOLUME, (s->volume+1) * volumeMultiplier, 0)); + espeak_ERROR rc; + + + rc = espeak_SetParameter(espeakVOLUME, (vol+1) * volumeMultiplier, 0); + if (rc == EE_OK) + s->volume = vol; } espeak_ERROR stop_speech(void)