/*************************************************************************** * Copyright (C) 2007, Gilles Casse * * * * 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "speech.h" #ifdef USE_ASYNC // This source file is only used for asynchronious modes // #include #include #include #include #include #include #include #include "speak_lib.h" #include "event.h" #include "wave.h" #include "debug.h" //> // // // event=%s\n","NULL"); } else { static const char* label[] = { "LIST_TERMINATED", "WORD", "SENTENCE", "MARK", "PLAY", "END", "MSG_TERMINATED" }; SHOW("event_display > event=0x%x\n",event); SHOW("event_display > type=%s\n",label[event->type]); SHOW("event_display > uid=%d\n",event->unique_identifier); SHOW("event_display > text_position=%d\n",event->text_position); SHOW("event_display > length=%d\n",event->length); SHOW("event_display > audio_position=%d\n",event->audio_position); SHOW("event_display > sample=%d\n",event->sample); SHOW("event_display > user_data=0x%x\n",event->user_data); } #endif } //> //type) { case espeakEVENT_MARK: case espeakEVENT_PLAY: if (event->id.name) { a_event->id.name = strdup(event->id.name); } break; default: break; } } event_display(a_event); return a_event; } //> //type = espeakEVENT_SENTENCE // * 0, 1 or several calls: event->type = espeakEVENT_WORD // * Last call: event->type = espeakEVENT_MSG_TERMINATED // static void event_notify(espeak_EVENT* event) { ENTER("event_notify"); static unsigned int a_old_uid = 0; espeak_EVENT events[2]; memcpy(&events[0],event,sizeof(espeak_EVENT)); // the event parameter in the callback function should be an array of eventd memcpy(&events[1],event,sizeof(espeak_EVENT)); events[1].type = espeakEVENT_LIST_TERMINATED; // ... terminated by an event type=0 if (event && my_callback) { event_display(event); switch(event->type) { case espeakEVENT_SENTENCE: my_callback(NULL, 0, events); a_old_uid = event->unique_identifier; break; case espeakEVENT_MSG_TERMINATED: case espeakEVENT_MARK: case espeakEVENT_WORD: case espeakEVENT_END: case espeakEVENT_PHONEME: { // jonsd - I'm not sure what this is for. gilles says it's for when Gnome Speech reads a file of blank lines if (a_old_uid != event->unique_identifier) { espeak_EVENT_TYPE a_new_type = events[0].type; events[0].type = espeakEVENT_SENTENCE; my_callback(NULL, 0, events); events[0].type = a_new_type; usleep(50000); } my_callback(NULL, 0, events); a_old_uid = event->unique_identifier; } break; default: case espeakEVENT_LIST_TERMINATED: case espeakEVENT_PLAY: break; } } } //> //type) { case espeakEVENT_MSG_TERMINATED: event_notify(event); break; case espeakEVENT_MARK: case espeakEVENT_PLAY: if(event->id.name) { free((void*)(event->id.name)); } break; default: break; } free(event); return 1; } //> // locked\n"); espeak_EVENT* a_event = event_copy(event); a_error = push(a_event); if (a_error != EE_OK) { event_delete(a_event); } SHOW_TIME("event_declare > unlocking\n"); a_status = pthread_mutex_unlock(&my_mutex); } // TBD: remove the comment // reminder: code in comment. // This wait can lead to an underrun // // if (!a_status && !my_event_is_running && (a_error == EE_OK)) // { // // quit when command is actually started // // (for possible forthcoming 'end of command' checks) SHOW_TIME("event_declare > post my_sem_start_is_required\n"); sem_post(&my_sem_start_is_required); // int val=1; // while (val) // { // usleep(50000); // TBD: event? // sem_getvalue(&my_sem_start_is_required, &val); // } // } if (a_status != 0) { a_error = EE_INTERNAL_ERROR; } return a_error; } //> // locked\n"); if (a_status != 0) { return EE_INTERNAL_ERROR; } if (my_event_is_running) { SHOW_TIME("event_stop > post my_sem_stop_is_required\n"); sem_post(&my_sem_stop_is_required); a_event_is_running = 1; } else { init(); // clear pending events } SHOW_TIME("event_stop > unlocking\n"); a_status = pthread_mutex_unlock(&my_mutex); if (a_status != 0) { return EE_INTERNAL_ERROR; } if (a_event_is_running) { SHOW_TIME("event_stop > wait for my_sem_stop_is_acknowledged\n"); while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) { continue; // Restart when interrupted by handler } SHOW_TIME("event_stop > get my_sem_stop_is_acknowledged\n"); } SHOW_TIME("LEAVE event_stop\n"); return EE_OK; } //> // sleep_until_timeout_or_stop_request > start sem_timedwait from %d.%09lu to %d.%09lu \n", to.tv_sec, to.tv_nsec, ts.tv_sec, ts.tv_nsec); while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1 && errno == EINTR) { continue; // Restart when interrupted by handler } assert (gettimeofday(&tv, NULL) != -1); SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n", tv.tv_sec, tv.tv_usec*1000); if (err == 0) { SHOW("polling_thread > sleep_until_timeout_or_stop_request > %s\n","stop required!"); a_stop_is_required=1; // stop required } return a_stop_is_required; } //> // // locking\n"); int a_status = pthread_mutex_lock(&my_mutex); SHOW_TIME("polling_thread > locked (my_event_is_running = 0)\n"); my_event_is_running = 0; pthread_mutex_unlock(&my_mutex); SHOW_TIME("polling_thread > unlocked\n"); SHOW_TIME("polling_thread > wait for my_sem_start_is_required\n"); while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) { continue; // Restart when interrupted by handler } SHOW_TIME("polling_thread > get my_sem_start_is_required\n"); a_status = pthread_mutex_lock(&my_mutex); SHOW_TIME("polling_thread > locked (my_event_is_running = 1)\n"); my_event_is_running = 1; pthread_mutex_unlock(&my_mutex); SHOW_TIME("polling_thread > unlocked\n"); a_stop_is_required=0; a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); if ((a_status==0) && a_stop_is_required) { SHOW("polling_thread > stop required (%d)\n", __LINE__); while(0 == sem_trywait(&my_sem_stop_is_required)) { }; } else { a_stop_is_required=0; } // In this loop, my_event_is_running = 1 while (head && !a_stop_is_required) { SHOW_TIME("polling_thread > check head\n"); while(0 == sem_trywait(&my_sem_start_is_required)) { }; espeak_EVENT* event = (espeak_EVENT*)(head->data); assert(event); uint32_t time_in_ms = 0; int err = get_remaining_time((uint32_t)event->sample, &time_in_ms, &a_stop_is_required); if (a_stop_is_required) { break; } else if (err != 0) { // No available time: the event is deleted. SHOW("polling_thread > %s\n","audio device down"); a_status = pthread_mutex_lock(&my_mutex); SHOW_TIME("polling_thread > locked\n"); event_delete( (espeak_EVENT*)pop()); a_status = pthread_mutex_unlock(&my_mutex); SHOW_TIME("polling_thread > unlocked\n"); } else if (time_in_ms==0) { // the event is already reached. if (my_callback) { event_notify(event); // the user_data (and the type) are cleaned to be sure // that MSG_TERMINATED is called twice (at delete time too). event->type=espeakEVENT_LIST_TERMINATED; event->user_data=NULL; } a_status = pthread_mutex_lock(&my_mutex); SHOW_TIME("polling_thread > locked\n"); event_delete( (espeak_EVENT*)pop()); a_status = pthread_mutex_unlock(&my_mutex); SHOW_TIME("polling_thread > unlocked\n"); a_stop_is_required=0; a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); if ((a_status==0) && a_stop_is_required) { SHOW("polling_thread > stop required (%d)\n", __LINE__); while(0 == sem_trywait(&my_sem_stop_is_required)) { }; } else { a_stop_is_required=0; } } else { // The event will be notified soon: sleep until timeout or stop request a_stop_is_required = sleep_until_timeout_or_stop_request(time_in_ms); } } a_status = pthread_mutex_lock(&my_mutex); SHOW_TIME("polling_thread > locked\n"); SHOW_TIME("polling_thread > my_event_is_running = 0\n"); my_event_is_running = 0; if(!a_stop_is_required) { a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); if ((a_status==0) && a_stop_is_required) { SHOW("polling_thread > stop required (%d)\n", __LINE__); while(0 == sem_trywait(&my_sem_stop_is_required)) { }; } else { a_stop_is_required=0; } } a_status = pthread_mutex_unlock(&my_mutex); SHOW_TIME("polling_thread > unlocked\n"); if (a_stop_is_required) { SHOW("polling_thread > %s\n","stop required!"); // no mutex required since the stop command is synchronous // and waiting for my_sem_stop_is_acknowledged init(); // acknowledge the stop request SHOW_TIME("polling_thread > post my_sem_stop_is_acknowledged\n"); a_status = sem_post(&my_sem_stop_is_acknowledged); } } return NULL; } //> // push"); assert((!head && !tail) || (head && tail)); if (the_data == NULL) { SHOW("event > push > event=0x%x\n", NULL); return EE_INTERNAL_ERROR; } if (node_counter >= MAX_NODE_COUNTER) { SHOW("event > push > %s\n", "EE_BUFFER_FULL"); return EE_BUFFER_FULL; } node *n = (node *)malloc(sizeof(node)); if (n == NULL) { return EE_INTERNAL_ERROR; } if (head == NULL) { head = n; tail = n; } else { tail->next = n; tail = n; } tail->next = NULL; tail->data = the_data; node_counter++; SHOW("event > push > counter=%d (uid=%d)\n",node_counter,((espeak_EVENT*)the_data)->unique_identifier); return EE_OK; } static void* pop() { ENTER("event > pop"); void* the_data = NULL; assert((!head && !tail) || (head && tail)); if (head != NULL) { node* n = head; the_data = n->data; head = n->next; free(n); node_counter--; SHOW("event > pop > event=0x%x (counter=%d, uid=%d)\n",the_data, node_counter,((espeak_EVENT*)the_data)->unique_identifier); } if(head == NULL) { tail = NULL; } return the_data; } static void init() { ENTER("event > init"); while (event_delete( (espeak_EVENT*)pop() )) {} node_counter = 0; } //> //