From 5226157402a706302f6e37f5496347fb8cecca47 Mon Sep 17 00:00:00 2001 From: Aleksey Lim Date: Mon, 09 Mar 2009 18:04:59 +0000 Subject: Simplify scheme by processing only one string per STATE_PLAYING session; do not rely on espeak while processing marks --- diff --git a/src/espeak.c b/src/espeak.c index 9331fb5..6cce18b 100644 --- a/src/espeak.c +++ b/src/espeak.c @@ -28,15 +28,12 @@ #define SPIN_FRAME_SIZE 255 #include "espeak.h" -#include "text.h" -#include "slist.h" typedef enum { IN = 1, - PROCESS = 2, - OUT = 4, - PLAY = 8 + OUT = 2, + PLAY = 4 } SpinState; typedef enum @@ -46,16 +43,17 @@ typedef enum typedef struct { - volatile SpinState state; + Econtext *context; - Text text; - goffset last_word; + volatile SpinState state; GMemoryOutputStream *sound; goffset sound_offset; GArray *events; goffset events_pos; + + goffset last_word; goffset mark_offset; const gchar *mark_name; } Espin; @@ -64,13 +62,14 @@ struct _Econtext { volatile ContextState state; + gchar *text; + goffset text_offset; + gsize text_len; + Espin queue[SPIN_QUEUE_SIZE]; Espin *in; - Espin *process; Espin *out; - SList in_queue; - GSList *process_chunk; volatile gint rate; @@ -127,8 +126,6 @@ static GSList *process_queue = NULL; static gint espeak_sample_rate = 0; static GValueArray *espeak_voices = NULL; -static GOutputStream *espeak_buffer = NULL; -static GArray *espeak_events = NULL; // ----------------------------------------------------------------------------- @@ -142,18 +139,18 @@ espeak_new(GstElement *emitter) for (i = SPIN_QUEUE_SIZE; i--;) { - self->queue[i].state = IN; - self->queue[i].sound = G_MEMORY_OUTPUT_STREAM( - g_memory_output_stream_new(NULL, 0, realloc, free)); - self->queue[i].events = g_array_new(FALSE, FALSE, sizeof(espeak_EVENT)); + Espin *spin = &self->queue[i]; + + spin->context = self; + spin->state = IN; + spin->sound = G_MEMORY_OUTPUT_STREAM(g_memory_output_stream_new( + NULL, 0, realloc, free)); + spin->events = g_array_new(FALSE, FALSE, sizeof(espeak_EVENT)); } self->in = self->queue; - self->process = self->queue; self->out = self->queue; - slist_new(&self->in_queue); - self->process_chunk = g_slist_alloc(); self->process_chunk->data = self; @@ -186,11 +183,9 @@ espeak_unref(Econtext *self) g_output_stream_close(G_OUTPUT_STREAM(self->queue[i].sound), NULL, NULL); g_object_unref(self->queue[i].sound); - text_unref(&self->queue[i].text); g_array_free(self->queue[i].events, TRUE); } - slist_free(&self->in_queue); g_slist_free(self->process_chunk); gst_object_unref(self->bus); @@ -202,58 +197,19 @@ espeak_unref(Econtext *self) // in/out ---------------------------------------------------------------------- -static void -in_spinning(Econtext *self, Espin **spin, Text *text) -{ - GST_DEBUG("[%p] text.body=%s text.offset=%ld text.frame_len=%ld", - self, text->body, text->offset, text->frame_len); - - gboolean chunked = FALSE; - - while (!text_eot(text)) - { - text_unref(&(*spin)->text); - - text_chunk(text, &(*spin)->text, SPIN_FRAME_SIZE); - g_atomic_int_set(&(*spin)->state, PROCESS); - spinning(self->queue, spin); - chunked = TRUE; - - if (g_atomic_int_get(&(*spin)->state) != IN) - break; - } - - if (chunked) - process_push(self); - - GST_DEBUG("[%p] text.body=%s text.offset=%ld text.frame_len=%ld", - self, text->body, text->offset, text->frame_len); -} - void -espeak_in(Econtext *self, const gchar *str_) +espeak_in(Econtext *self, const gchar *text) { - GST_DEBUG("[%p] str=%s", self, str_); + GST_DEBUG("[%p] text=%s", self, text); - if (str_ == NULL || *str_ == 0) + if (text == NULL || *text == 0) return; - Text *text = text_new(str_); + self->text = g_strdup(text); + self->text_offset = 0; + self->text_len = strlen(text); - if (g_atomic_int_get(&self->in->state) != IN) - { - // in_queue should be not empty at this point - slist_push(&self->in_queue, text); - return; - } - - in_spinning(self, &self->in, text); - - if (!text_eot(text)) - { - GST_DEBUG("[%p] text_len=%d", self, text_len(text)); - slist_push(&self->in_queue, text); - } + process_push(self); } GstBuffer* @@ -292,12 +248,12 @@ play(Econtext *self, Espin *spin, gsize size_to_play) else if (i->type == espeakEVENT_WORD) { sample_offset = i[1].sample*2; - text_offset = spin->text.offset + i->text_position - 1; + text_offset = i->text_position; text_len = i->length; GST_DEBUG("sample_offset=%d txt_offset=%d txt_len=%d, txt=%s", sample_offset, text_offset, text_len, - spin->text.body + text_offset); + self->text + text_offset); break; } } @@ -356,11 +312,11 @@ play(Econtext *self, Espin *spin, gsize size_to_play) if (i->sample == 0) { if (spin->sound_offset == 0) - emit_mark(self, i->text_position - 1, i->id.name); + emit_mark(self, i->text_position, i->id.name); continue; } - mark_offset = i->text_position - 1; + mark_offset = i->text_position; mark_name = i->id.name; sample_offset = i->sample*2; break; @@ -403,7 +359,8 @@ play(Econtext *self, Espin *spin, gsize size_to_play) spin->sound_offset += size_to_play; - GST_DEBUG("size_to_play=%ld tell=%ld", size_to_play, spin->sound_offset); + GST_DEBUG("out=%p size_to_play=%ld tell=%ld", GST_BUFFER_DATA(out), + size_to_play, spin->sound_offset); return out; } @@ -418,8 +375,7 @@ espeak_out(Econtext *self, gsize size_to_play) g_mutex_lock(process_lock); while ((g_atomic_int_get(&self->out->state) & (PLAY|OUT)) == 0) { - if ((self->state & INPROCESS) == 0 && - slist_empty(&self->in_queue)) + if ((self->state & INPROCESS) == 0) { GST_DEBUG("[%p]", self); g_mutex_unlock(process_lock); @@ -433,47 +389,28 @@ espeak_out(Econtext *self, gsize size_to_play) Espin *spin = self->out; gsize spin_size = g_memory_output_stream_get_data_size(spin->sound); - GST_DEBUG("[%p] spin->sound_offset=%ld spin_size=%ld spin->body=%s", - self, spin->sound_offset, spin_size, - spin->text.body + spin->text.frame_len); + GST_DEBUG("[%p] spin->sound_offset=%ld spin_size=%ld", + self, spin->sound_offset, spin_size); if (g_atomic_int_get(&spin->state) == PLAY && spin->sound_offset >= spin_size) { - GSList *text_link = slist_pop_link(&self->in_queue); - - if (text_link) - { - Text *text = text_link->data; - in_spinning(self, &self->out, text); - - GST_DEBUG("[%p] text_eot=%d", self, text_eot(text)); - - if (text_eot(text)) - g_slist_free(text_link); - else - slist_push_link(&self->in_queue, text_link); - } - else - { - GST_DEBUG("[%p]", self); - g_atomic_int_set(&spin->state, IN); - spinning(self->queue, &self->out); - } - + g_atomic_int_set(&spin->state, IN); + process_push(self); + spinning(self->queue, &self->out); continue; } return play(self, spin, size_to_play); } + GST_DEBUG("[%p]", self); return NULL; } void espeak_reset(Econtext *self) { - slist_clean(&self->in_queue); process_pop(self); GstBuffer *buf; @@ -483,6 +420,12 @@ espeak_reset(Econtext *self) int i; for (i = SPIN_QUEUE_SIZE; i--;) g_atomic_int_set(&self->queue[i].state, IN); + + if (self->text) + { + g_free(self->text); + self->text = NULL; + } } // espeak ---------------------------------------------------------------------- @@ -493,26 +436,49 @@ synth_cb(short *data, int numsamples, espeak_EVENT *events) if (data == NULL) return 0; + Espin *spin = events->user_data; + if (numsamples > 0) { - g_output_stream_write(espeak_buffer, data, numsamples*2, NULL, NULL); + g_output_stream_write(G_OUTPUT_STREAM(spin->sound), data, numsamples*2, + NULL, NULL); + + goffset last_offset = spin->context->text_offset; - if (espeak_events) + espeak_EVENT *i; + for (i = events; i->type != espeakEVENT_LIST_TERMINATED; ++i) { - for (; events->type != espeakEVENT_LIST_TERMINATED; ++events) + last_offset = i->text_position = MAX(last_offset, i->text_position + + spin->context->text_offset - 1); + + if (i->type == espeakEVENT_MARK) { - GST_DEBUG("type=%d text_position=%d length=%d " - "audio_position=%d sample=%d", - events->type, events->text_position, events->length, - events->audio_position, events->sample*2); - g_array_append_val(espeak_events, *events); + gchar *pos = spin->context->text + i->text_position; + gint turn = 0; + + for (; pos > spin->context->text; --pos) + if (*pos == '"') + { + if (turn++ == 0) + *pos = 0; + else + { + i->id.name = pos + 1; + break; + } + } } + + GST_DEBUG("type=%d text_position=%d length=%d " + "audio_position=%d sample=%d", + i->type, i->text_position, i->length, + i->audio_position, i->sample*2); + + g_array_append_val(spin->events, *i); } } - GST_DEBUG("numsamples=%d data_size=%ld", numsamples*2, - g_memory_output_stream_get_data_size(G_MEMORY_OUTPUT_STREAM( - espeak_buffer))); + GST_DEBUG("numsamples=%d", numsamples*2); return 0; } @@ -520,14 +486,6 @@ synth_cb(short *data, int numsamples, espeak_EVENT *events) static void synth(Econtext *self, Espin *spin) { - gchar *text = text_first(&spin->text); - gchar *last = text_last(&spin->text); - - gchar old_last_char = *last; - *last = 0; - - GST_DEBUG("[%p] text='%s' last=%d", self, text, last-text); - g_seekable_seek(G_SEEKABLE(spin->sound), 0, G_SEEK_SET, NULL, NULL); g_array_set_size(spin->events, 0); @@ -544,20 +502,62 @@ synth(Econtext *self, Espin *spin) gint track = g_atomic_int_get(&self->track); - espeak_buffer = G_OUTPUT_STREAM(spin->sound); - espeak_events = track == ESPEAK_TRACK_NONE ? NULL : spin->events; - gint flags = espeakCHARS_UTF8; if (track == ESPEAK_TRACK_MARK) flags |= espeakSSML; - espeak_Synth(text, text_len(&spin->text), 0, POS_WORD, 0, flags, - NULL, NULL); + gchar *text = self->text + self->text_offset; + gsize last = SPIN_FRAME_SIZE; + + while (last < SPIN_FRAME_SIZE*2 && text[last]) + if (g_ascii_isspace(text[last])) + break; + else + ++last; + + if (track == ESPEAK_TRACK_MARK) + { + goffset last_mark = SPIN_FRAME_SIZE; + + for (; last_mark < SPIN_FRAME_SIZE*2 && text[last_mark]; ++last_mark) + { + if (strncmp("", text + last_mark, 2) != 0) + continue; + for (last_mark = SPIN_FRAME_SIZE; last_mark > 0; --last_mark) + if (strncmp("text_offset, last); + + espeak_Synth(self->text + self->text_offset, + last + 1, 0, POS_CHARACTER, 0, flags, NULL, spin); + + text[last] = old_last; + + if (spin->events->len) + { + goffset i = self->text_offset = g_array_index(spin->events, + espeak_EVENT, spin->events->len-1).text_position + 1; + self->text_offset = i + (g_ascii_isspace(self->text[i]) ? 1 : 0); + } espeak_EVENT last_event = { espeakEVENT_LIST_TERMINATED }; last_event.sample = g_memory_output_stream_get_data_size(spin->sound) / 2; g_array_append_val(spin->events, last_event); - *last = old_last_char; } gint @@ -631,25 +631,32 @@ process(gpointer data) while (process_queue) { Econtext *context = (Econtext*)process_queue->data; - Espin *spin = context->process; + Espin *spin = context->in; process_queue = g_slist_remove_link(process_queue, process_queue); - synth(context, spin); - - g_atomic_int_set(&spin->state, OUT); - spinning(context->queue, &context->process); - - if (g_atomic_int_get(&context->process->state) == PROCESS) + if (context->text_offset >= context->text_len) { GST_DEBUG("[%p]", context); - process_queue = g_slist_concat(process_queue, - context->process_chunk); + context->state &= ~INPROCESS; } else { - GST_DEBUG("[%p]", context); - context->state &= ~INPROCESS; + synth(context, spin); + g_atomic_int_set(&spin->state, OUT); + spinning(context->queue, &context->in); + + if (g_atomic_int_get(&context->in->state) == IN) + { + GST_DEBUG("[%p]", context); + process_queue = g_slist_concat(process_queue, + context->process_chunk); + } + else + { + GST_DEBUG("[%p]", context); + context->state &= ~INPROCESS; + } } } diff --git a/src/slist.h b/src/slist.h deleted file mode 100644 index 8c80fa8..0000000 --- a/src/slist.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef SLIST_H -#define SLIST_H - -typedef struct -{ - GSList *list; - GMutex *lock; -} SList; - -inline void -slist_new(SList *self) -{ - self->list = NULL; - self->lock = g_mutex_new(); -} - -inline void -slist_clean(SList *self) -{ - GSList *i; - g_mutex_lock(self->lock); - for (i = self->list; i; i = g_slist_next(i)) - text_unref(i->data); - g_mutex_unlock(self->lock); -} - -inline void -slist_free(SList *self) -{ - slist_clean(self); - g_slist_free(self->list); - g_mutex_free(self->lock); -} - -inline gboolean -slist_empty(SList *self) -{ - g_mutex_lock(self->lock); - gboolean out = self->list == NULL; - g_mutex_unlock(self->lock); - return out; -} - -inline GSList* -slist_pop_link(SList *self) -{ - GSList *out = NULL; - - g_mutex_lock(self->lock); - out = self->list; - self->list = g_slist_remove_link(self->list, self->list); - g_mutex_unlock(self->lock); - - return out; -} - -inline void -slist_push(SList *self, Text *data) -{ - g_mutex_lock(self->lock); - self->list = g_slist_append(self->list, data); - g_mutex_unlock(self->lock); -} - -inline void -slist_push_link(SList *self, GSList *link) -{ - g_mutex_lock(self->lock); - self->list = g_slist_concat(link, self->list); - g_mutex_unlock(self->lock); -} - -#endif diff --git a/src/text.h b/src/text.h deleted file mode 100644 index 0e65e18..0000000 --- a/src/text.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef TEXT_H -#define TEXT_H - -#include - -typedef struct -{ - goffset offset; - gsize frame_len; - gsize len; - gchar *body; -} Text; - -inline Text* -text_new(const gchar *src) -{ - if (!src) - return NULL; - - gsize len = strlen(src); - - Text *out = (Text*)g_malloc(sizeof(Text) + len + 1); - gchar *body = (gchar*)out + sizeof(Text); - - out->offset = 0; - out->frame_len = len; - out->len = len; - out->body = body; - - memcpy(body, src, len + 1); - - GST_DEBUG("[%p] len=%ld", out, len); - - return out; -} - -inline void -text_chunk(Text *src, Text *dst, gsize len) -{ - memcpy(dst, src, sizeof(Text)); - - gsize dst_len = MIN(len, src->frame_len); - gchar *dst_last = dst->body + dst->offset + dst_len; - gchar *i; - - if (dst_len < src->frame_len) - for (i = dst_last; dst_len; --dst_len, --i) - if (g_ascii_isspace(*i)) - break; - - if (dst_len) - dst->frame_len = dst_len; - else - { - dst_last = g_utf8_prev_char(dst_last + 1); - dst->frame_len = dst_last - (dst->body + dst->offset); - } - - src->offset += dst->frame_len; - src->frame_len -= dst->frame_len; - - GST_DEBUG("[%p] dst_len=%ld dst_last=%ld " - "src->offset=%ld src->frame_len=%ld dst->body=%s", src, dst_len, - dst_last-dst->body, src->offset, src->frame_len, - dst->body + dst->offset); -} - -inline gchar* -text_first(Text *self) -{ - return self->body + self->offset; -} - -inline gchar* -text_last(Text *self) -{ - return text_first(self) + self->frame_len; -} - -inline gboolean -text_eot(Text *self) -{ - return self->frame_len == 0; -} - -inline void -text_unref(Text *self) -{ - if (text_eot(self)) - return; - - gpointer data = NULL; - - if (self->offset + self->frame_len >= self->len) - data = self->body - sizeof(Text); - - memset(self, 0, sizeof(Text)); - - GST_DEBUG("[%p]", data); - - g_free(data); -} - -inline gsize -text_len(Text *self) -{ - return self->frame_len; -} - -#endif -- cgit v0.9.1