1 /*
2 * module_utils_speak_queue.c - Speak queue helper for Speech Dispatcher modules
3 *
4 * Copyright (C) 2007 Brailcom, o.p.s.
5 * Copyright (C) 2019-2020 Samuel Thibault <samuel.thibault@ens-lyon.org>
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 *
20 * Based on espeak.c
21 *
22 * @author Lukas Loehrer
23 * Based on ibmtts.c.
24 */
25
26 #include "module_utils_speak_queue.h"
27
28 #define DBG_MODNAME "speak_queue"
29
30 #include "module_utils.h"
31
32 typedef enum {
33 IDLE,
34 BEFORE_SYNTH,
35 BEFORE_PLAY,
36 SPEAKING
37 } speak_queue_state_t;
38
39 typedef enum {
40 SPEAK_QUEUE_PAUSE_OFF,
41 SPEAK_QUEUE_PAUSE_REQUESTED,
42 SPEAK_QUEUE_PAUSE_MARK_REPORTED
43 } speak_queue_pause_state_t;
44
45 /* Thread and process control. */
46 /* Global mutex for the whole speak queue mechanism */
47 static pthread_mutex_t speak_queue_mutex;
48
49 static speak_queue_state_t speak_queue_state = IDLE;
50 static gboolean speak_queue_configured = FALSE; /* Whether we have configured audio */
51
52 static pthread_t speak_queue_play_thread;
53 static pthread_t speak_queue_stop_or_pause_thread;
54
55 /* Used to wake the stop_or_pause thread from main */
56 static pthread_cond_t speak_queue_stop_or_pause_cond;
57
58 static pthread_cond_t speak_queue_stop_or_pause_sleeping_cond;
59 static int speak_queue_stop_or_pause_sleeping;
60
61 /* Used to wake the play thread from main */
62 static pthread_cond_t speak_queue_play_cond;
63 /* Used to wait for the play thread to go sleeping */
64 static pthread_cond_t speak_queue_play_sleeping_cond;
65 static int speak_queue_play_sleeping;
66
67 static gboolean speak_queue_close_requested = FALSE;
68 static speak_queue_pause_state_t speak_queue_pause_state = SPEAK_QUEUE_PAUSE_OFF;
69 static gboolean speak_queue_stop_requested = FALSE;
70
71 static void module_speak_queue_reset(void);
72
73 /* The playback queue. */
74
75 static int speak_queue_maxsize;
76
77 typedef enum {
78 SPEAK_QUEUE_QET_AUDIO, /* Chunk of audio. */
79 SPEAK_QUEUE_QET_INDEX_MARK, /* Index mark event. */
80 SPEAK_QUEUE_QET_SOUND_ICON, /* A Sound Icon */
81 SPEAK_QUEUE_QET_BEGIN, /* Beginning of speech. */
82 SPEAK_QUEUE_QET_END /* Speech completed. */
83 } speak_queue_entry_type;
84
85 typedef struct {
86 AudioTrack track;
87 AudioFormat format;
88 } speak_queue_audio_chunk;
89
90 typedef struct {
91 speak_queue_entry_type type;
92 union {
93 char *markId;
94 speak_queue_audio_chunk audio;
95 char *sound_icon_filename;
96 } data;
97 } speak_queue_entry;
98
99 static GSList *playback_queue = NULL;
100 static int playback_queue_size = 0; /* Number of audio frames currently in queue */
101
102 /* Use to wait for queue room availability. Theoretically several threads might
103 * be wanting to push, so use broadcast. */
104 static pthread_cond_t playback_queue_room_condition;
105 /* Use to wait for queue data availability */
106 static pthread_cond_t playback_queue_data_condition;
107
108 /* Internal function prototypes for playback thread. */
109 static gboolean speak_queue_add_flag_to_playback_queue(speak_queue_entry_type type);
110 static void speak_queue_delete_playback_queue_entry(speak_queue_entry *
111 playback_queue_entry);
112 static gboolean speak_queue_send_to_audio(speak_queue_entry *
113 playback_queue_entry);
114
115 /* Miscellaneous internal function prototypes. */
116 static void speak_queue_clear_playback_queue();
117
118 /* The playback thread start routine. */
119 static void *speak_queue_play(void *);
120 /* The stop_or_pause start routine. */
121 static void *speak_queue_stop_or_pause(void *);
122
module_speak_queue_init(int maxsize,char ** status_info)123 int module_speak_queue_init(int maxsize, char **status_info)
124 {
125 int ret;
126
127 speak_queue_maxsize = maxsize;
128
129 /* Reset global state */
130 module_speak_queue_reset();
131
132 /* This mutex mediates all accesses */
133 pthread_mutex_init(&speak_queue_mutex, NULL);
134
135 pthread_cond_init(&playback_queue_room_condition, NULL);
136 pthread_cond_init(&playback_queue_data_condition, NULL);
137
138 DBG(DBG_MODNAME " Creating new thread for stop or pause.");
139 pthread_cond_init(&speak_queue_stop_or_pause_cond, NULL);
140 pthread_cond_init(&speak_queue_stop_or_pause_sleeping_cond, NULL);
141 speak_queue_stop_or_pause_sleeping = 0;
142
143 ret =
144 pthread_create(&speak_queue_stop_or_pause_thread, NULL,
145 speak_queue_stop_or_pause, NULL);
146 if (0 != ret) {
147 DBG("Failed to create stop-or-pause thread.");
148 *status_info =
149 g_strdup("Failed to create stop-or-pause thread.");
150 return -1;
151 }
152
153 pthread_cond_init(&speak_queue_play_cond, NULL);
154 pthread_cond_init(&speak_queue_play_sleeping_cond, NULL);
155 speak_queue_play_sleeping = 0;
156
157 DBG(DBG_MODNAME " Creating new thread for playback.");
158 ret = pthread_create(&speak_queue_play_thread, NULL, speak_queue_play, NULL);
159 if (ret != 0) {
160 DBG("Failed to create playback thread.");
161 *status_info = g_strdup("Failed to create playback thread.");
162 return -1;
163 }
164
165 return 0;
166 }
167
module_speak_queue_reset(void)168 void module_speak_queue_reset(void)
169 {
170 speak_queue_state = IDLE;
171 speak_queue_pause_state = SPEAK_QUEUE_PAUSE_OFF;
172 speak_queue_stop_requested = FALSE;
173 }
174
module_speak_queue_before_synth(void)175 int module_speak_queue_before_synth(void)
176 {
177 pthread_mutex_lock(&speak_queue_mutex);
178 if (speak_queue_state != IDLE) {
179 DBG(DBG_MODNAME " Warning, module_speak called when not ready.");
180 pthread_mutex_unlock(&speak_queue_mutex);
181 return FALSE;
182 }
183
184 module_speak_queue_reset();
185 speak_queue_state = BEFORE_SYNTH;
186 pthread_mutex_unlock(&speak_queue_mutex);
187 return TRUE;
188 }
189
module_speak_queue_before_play(void)190 int module_speak_queue_before_play(void)
191 {
192 int ret = 0;
193 pthread_mutex_lock(&speak_queue_mutex);
194 if (speak_queue_state == BEFORE_SYNTH) {
195 ret = 1;
196 speak_queue_state = BEFORE_PLAY;
197 speak_queue_add_flag_to_playback_queue(SPEAK_QUEUE_QET_BEGIN);
198 /* Wake up playback thread */
199 pthread_cond_signal(&speak_queue_play_cond);
200 }
201 pthread_mutex_unlock(&speak_queue_mutex);
202 return ret;
203 }
204
module_speak_queue_add_end(void)205 gboolean module_speak_queue_add_end(void)
206 {
207 pthread_mutex_lock(&speak_queue_mutex);
208 gboolean ret = speak_queue_add_flag_to_playback_queue(SPEAK_QUEUE_QET_END);
209 pthread_mutex_unlock(&speak_queue_mutex);
210 return ret;
211 }
212
playback_queue_pop()213 static speak_queue_entry *playback_queue_pop()
214 {
215 speak_queue_entry *result = NULL;
216 pthread_mutex_lock(&speak_queue_mutex);
217 while (!speak_queue_stop_requested && playback_queue == NULL) {
218 pthread_cond_wait(&playback_queue_data_condition,
219 &speak_queue_mutex);
220 }
221 if (!speak_queue_stop_requested) {
222 result = (speak_queue_entry *) playback_queue->data;
223 playback_queue =
224 g_slist_remove(playback_queue, playback_queue->data);
225 if (result->type == SPEAK_QUEUE_QET_AUDIO) {
226 playback_queue_size -= result->data.audio.track.num_samples;
227 pthread_cond_broadcast(&playback_queue_room_condition);
228 }
229 }
230 pthread_mutex_unlock(&speak_queue_mutex);
231 return result;
232 }
233
playback_queue_push(speak_queue_entry * entry)234 static gboolean playback_queue_push(speak_queue_entry * entry)
235 {
236 playback_queue = g_slist_append(playback_queue, entry);
237 if (entry->type == SPEAK_QUEUE_QET_AUDIO) {
238 playback_queue_size += entry->data.audio.track.num_samples;
239 }
240 pthread_cond_signal(&playback_queue_data_condition);
241 return TRUE;
242 }
243
244 /* Adds a chunk of pcm audio to the audio playback queue.
245 Waits until there is enough space in the queue. */
246 gboolean
module_speak_queue_add_audio(const AudioTrack * track,AudioFormat format)247 module_speak_queue_add_audio(const AudioTrack *track, AudioFormat format)
248 {
249 pthread_mutex_lock(&speak_queue_mutex);
250 while (playback_queue_size > speak_queue_maxsize) {
251 if (speak_queue_state == IDLE || speak_queue_stop_requested) {
252 pthread_mutex_unlock(&speak_queue_mutex);
253 return FALSE;
254 }
255 pthread_cond_wait(&playback_queue_room_condition,
256 &speak_queue_mutex);
257 }
258 if (speak_queue_state == IDLE || speak_queue_stop_requested) {
259 pthread_mutex_unlock(&speak_queue_mutex);
260 return FALSE;
261 }
262
263 speak_queue_entry *playback_queue_entry =
264 g_new(speak_queue_entry, 1);
265
266 playback_queue_entry->type = SPEAK_QUEUE_QET_AUDIO;
267 playback_queue_entry->data.audio.track = *track;
268 gint nbytes = track->bits / 8 * track->num_samples;
269 playback_queue_entry->data.audio.track.samples = g_memdup(track->samples, nbytes);
270 playback_queue_entry->data.audio.format = format;
271
272 playback_queue_push(playback_queue_entry);
273 pthread_mutex_unlock(&speak_queue_mutex);
274 return TRUE;
275 }
276
277 /* Adds an Index Mark to the audio playback queue. */
module_speak_queue_add_mark(const char * markId)278 gboolean module_speak_queue_add_mark(const char *markId)
279 {
280 speak_queue_entry *playback_queue_entry =
281 (speak_queue_entry *) g_malloc(sizeof(speak_queue_entry));
282
283 playback_queue_entry->type = SPEAK_QUEUE_QET_INDEX_MARK;
284 playback_queue_entry->data.markId = g_strdup(markId);
285 pthread_mutex_lock(&speak_queue_mutex);
286 gboolean ret = playback_queue_push(playback_queue_entry);
287 pthread_mutex_unlock(&speak_queue_mutex);
288 return ret;
289 }
290
291 /* Adds a begin or end flag to the playback queue. */
speak_queue_add_flag_to_playback_queue(speak_queue_entry_type type)292 static gboolean speak_queue_add_flag_to_playback_queue(speak_queue_entry_type type)
293 {
294 speak_queue_entry *playback_queue_entry =
295 (speak_queue_entry *) g_malloc(sizeof(speak_queue_entry));
296
297 playback_queue_entry->type = type;
298 return playback_queue_push(playback_queue_entry);
299 }
300
301 /* Add a sound icon to the playback queue. */
module_speak_queue_add_sound_icon(const char * filename)302 gboolean module_speak_queue_add_sound_icon(const char *filename)
303 {
304 speak_queue_entry *playback_queue_entry =
305 (speak_queue_entry *) g_malloc(sizeof(speak_queue_entry));
306
307 playback_queue_entry->type = SPEAK_QUEUE_QET_SOUND_ICON;
308 playback_queue_entry->data.sound_icon_filename = g_strdup(filename);
309 pthread_mutex_lock(&speak_queue_mutex);
310 gboolean ret = playback_queue_push(playback_queue_entry);
311 pthread_mutex_unlock(&speak_queue_mutex);
312 return ret;
313 }
314
315 /* Deletes an entry from the playback audio queue, freeing memory. */
316 static void
speak_queue_delete_playback_queue_entry(speak_queue_entry * playback_queue_entry)317 speak_queue_delete_playback_queue_entry(speak_queue_entry * playback_queue_entry)
318 {
319 switch (playback_queue_entry->type) {
320 case SPEAK_QUEUE_QET_AUDIO:
321 g_free(playback_queue_entry->data.audio.track.samples);
322 break;
323 case SPEAK_QUEUE_QET_INDEX_MARK:
324 g_free(playback_queue_entry->data.markId);
325 break;
326 case SPEAK_QUEUE_QET_SOUND_ICON:
327 g_free(playback_queue_entry->data.sound_icon_filename);
328 break;
329 default:
330 break;
331 }
332 g_free(playback_queue_entry);
333 }
334
335 /* Erases the entire playback queue, freeing memory. */
speak_queue_clear_playback_queue()336 static void speak_queue_clear_playback_queue()
337 {
338 pthread_mutex_lock(&speak_queue_mutex);
339
340 while (NULL != playback_queue) {
341 speak_queue_entry *playback_queue_entry =
342 playback_queue->data;
343 speak_queue_delete_playback_queue_entry(playback_queue_entry);
344 playback_queue =
345 g_slist_remove(playback_queue, playback_queue->data);
346 }
347 playback_queue = NULL;
348 playback_queue_size = 0;
349 pthread_cond_broadcast(&playback_queue_room_condition);
350 pthread_mutex_unlock(&speak_queue_mutex);
351 }
352
353 /* Sends a chunk of audio to the audio player and waits for completion or error. */
speak_queue_send_to_audio(speak_queue_entry * playback_queue_entry)354 static gboolean speak_queue_send_to_audio(speak_queue_entry * playback_queue_entry)
355 {
356 int ret = 0;
357 DBG(DBG_MODNAME " Sending %i samples to audio.",
358 playback_queue_entry->data.audio.track.num_samples);
359 if (!speak_queue_configured)
360 {
361 spd_audio_begin(module_audio_id,
362 playback_queue_entry->data.audio.track,
363 playback_queue_entry->data.audio.format);
364 speak_queue_configured = TRUE;
365 }
366 ret = spd_audio_feed_sync_overlap(module_audio_id,
367 playback_queue_entry->data.audio.track,
368 playback_queue_entry->data.audio.format);
369 if (ret < 0) {
370 DBG("ERROR: Can't play track for unknown reason.");
371 return FALSE;
372 }
373 DBG(DBG_MODNAME " Sent to audio.");
374 return TRUE;
375 }
376
377 /* Playback thread. */
speak_queue_play(void * nothing)378 static void *speak_queue_play(void *nothing)
379 {
380 char *markId;
381 speak_queue_entry *playback_queue_entry = NULL;
382
383 DBG(DBG_MODNAME " Playback thread starting.......");
384
385 /* Block all signals to this thread. */
386 set_speaking_thread_parameters();
387
388 pthread_mutex_lock(&speak_queue_mutex);
389 while (!speak_queue_close_requested) {
390 speak_queue_play_sleeping = 1;
391 pthread_cond_signal(&speak_queue_play_sleeping_cond);
392 while (speak_queue_state < BEFORE_PLAY && !speak_queue_close_requested) {
393 pthread_cond_wait(&speak_queue_play_cond, &speak_queue_mutex);
394 }
395 speak_queue_play_sleeping = 0;
396 pthread_cond_signal(&speak_queue_play_sleeping_cond);
397 DBG(DBG_MODNAME " Playback.");
398 if (speak_queue_close_requested)
399 break;
400 pthread_mutex_unlock(&speak_queue_mutex);
401
402 while (1) {
403 gboolean finished = FALSE;
404 playback_queue_entry = playback_queue_pop();
405 if (playback_queue_entry == NULL) {
406 DBG(DBG_MODNAME " playback thread detected stop.");
407 break;
408 }
409
410 switch (playback_queue_entry->type) {
411 case SPEAK_QUEUE_QET_AUDIO:
412 speak_queue_send_to_audio(playback_queue_entry);
413 break;
414 case SPEAK_QUEUE_QET_INDEX_MARK:
415 markId = playback_queue_entry->data.markId;
416 DBG(DBG_MODNAME " reporting index mark |%s|.",
417 markId);
418 module_report_index_mark(markId);
419 DBG(DBG_MODNAME " index mark reported.");
420 pthread_mutex_lock(&speak_queue_mutex);
421 if (speak_queue_state == SPEAKING
422 && speak_queue_pause_state ==
423 SPEAK_QUEUE_PAUSE_REQUESTED
424 && speak_queue_stop_or_pause_sleeping
425 && g_str_has_prefix(markId, "__spd_")) {
426 DBG(DBG_MODNAME " Pause requested in playback thread. Stopping.");
427 speak_queue_stop_requested = TRUE;
428 speak_queue_pause_state =
429 SPEAK_QUEUE_PAUSE_MARK_REPORTED;
430 pthread_cond_signal
431 (&speak_queue_stop_or_pause_cond);
432 finished = TRUE;
433 }
434 pthread_mutex_unlock(&speak_queue_mutex);
435 break;
436 case SPEAK_QUEUE_QET_SOUND_ICON:
437 if (speak_queue_configured) {
438 spd_audio_end(module_audio_id);
439 speak_queue_configured = FALSE;
440 }
441 module_play_file(playback_queue_entry->
442 data.sound_icon_filename);
443 break;
444 case SPEAK_QUEUE_QET_BEGIN:{
445 gboolean report_begin = FALSE;
446 pthread_mutex_lock(&speak_queue_mutex);
447 if (speak_queue_state == BEFORE_PLAY) {
448 speak_queue_state = SPEAKING;
449 report_begin = TRUE;
450 }
451 pthread_mutex_unlock
452 (&speak_queue_mutex);
453 if (report_begin)
454 module_report_event_begin();
455 break;
456 }
457 case SPEAK_QUEUE_QET_END:
458 if (speak_queue_configured) {
459 spd_audio_end(module_audio_id);
460 speak_queue_configured = FALSE;
461 }
462 pthread_mutex_lock(&speak_queue_mutex);
463 DBG(DBG_MODNAME " playback thread got END from queue.");
464 if (speak_queue_state == SPEAKING) {
465 if (!speak_queue_stop_requested) {
466 DBG(DBG_MODNAME " playback thread reporting end.");
467 speak_queue_state = IDLE;
468 speak_queue_pause_state =
469 SPEAK_QUEUE_PAUSE_OFF;
470 }
471 finished = TRUE;
472 }
473 pthread_mutex_unlock(&speak_queue_mutex);
474 if (finished)
475 module_report_event_end();
476 break;
477 }
478
479 speak_queue_delete_playback_queue_entry
480 (playback_queue_entry);
481 if (finished)
482 break;
483 }
484 if (speak_queue_configured) {
485 spd_audio_end(module_audio_id);
486 speak_queue_configured = FALSE;
487 }
488 pthread_mutex_lock(&speak_queue_mutex);
489 }
490 speak_queue_play_sleeping = 1;
491 pthread_mutex_unlock(&speak_queue_mutex);
492 DBG(DBG_MODNAME " Playback thread ended.......");
493 return 0;
494 }
495
module_speak_queue_stop_requested(void)496 int module_speak_queue_stop_requested(void)
497 {
498 return speak_queue_stop_requested;
499 }
500
module_speak_queue_stop(void)501 void module_speak_queue_stop(void)
502 {
503 pthread_mutex_lock(&speak_queue_mutex);
504 if (speak_queue_state != IDLE &&
505 !speak_queue_stop_requested &&
506 speak_queue_stop_or_pause_sleeping) {
507 DBG(DBG_MODNAME " stopping...");
508 speak_queue_stop_requested = TRUE;
509 /* Wake the stop_or_pause thread. */
510 pthread_cond_signal(&speak_queue_stop_or_pause_cond);
511 } else {
512 DBG(DBG_MODNAME " Cannot stop now.");
513 }
514 pthread_mutex_unlock(&speak_queue_mutex);
515 }
516
module_speak_queue_pause(void)517 void module_speak_queue_pause(void)
518 {
519 pthread_mutex_lock(&speak_queue_mutex);
520 if (speak_queue_pause_state == SPEAK_QUEUE_PAUSE_OFF && !speak_queue_stop_requested) {
521 speak_queue_pause_state = SPEAK_QUEUE_PAUSE_REQUESTED;
522 }
523 pthread_mutex_unlock(&speak_queue_mutex);
524 }
525
module_speak_queue_terminate(void)526 void module_speak_queue_terminate(void)
527 {
528 pthread_mutex_lock(&speak_queue_mutex);
529 speak_queue_stop_requested = TRUE;
530 speak_queue_close_requested = TRUE;
531
532 pthread_cond_broadcast(&playback_queue_room_condition);
533 pthread_cond_signal(&playback_queue_data_condition);
534
535 pthread_cond_signal(&speak_queue_play_cond);
536 pthread_cond_signal(&speak_queue_stop_or_pause_cond);
537 pthread_mutex_unlock(&speak_queue_mutex);
538
539 DBG(DBG_MODNAME " Joining play thread.");
540 pthread_join(speak_queue_play_thread, NULL);
541 DBG(DBG_MODNAME " Joining stop thread.");
542 pthread_join(speak_queue_stop_or_pause_thread, NULL);
543 }
544
module_speak_queue_free(void)545 void module_speak_queue_free(void)
546 {
547 DBG(DBG_MODNAME " Freeing resources.");
548 speak_queue_clear_playback_queue();
549
550 pthread_mutex_destroy(&speak_queue_mutex);
551 pthread_cond_destroy(&playback_queue_room_condition);
552 pthread_cond_destroy(&playback_queue_data_condition);
553 pthread_cond_destroy(&speak_queue_play_cond);
554 pthread_cond_destroy(&speak_queue_play_sleeping_cond);
555 pthread_cond_destroy(&speak_queue_stop_or_pause_cond);
556 pthread_cond_destroy(&speak_queue_stop_or_pause_sleeping_cond);
557 }
558
559 /* Stop or Pause thread. */
speak_queue_stop_or_pause(void * nothing)560 static void *speak_queue_stop_or_pause(void *nothing)
561 {
562 int ret;
563
564 DBG(DBG_MODNAME " Stop or pause thread starting.......");
565
566 /* Block all signals to this thread. */
567 set_speaking_thread_parameters();
568
569 pthread_mutex_lock(&speak_queue_mutex);
570 while (!speak_queue_close_requested) {
571 speak_queue_stop_or_pause_sleeping = 1;
572 pthread_cond_signal(&speak_queue_stop_or_pause_sleeping_cond);
573 while (!speak_queue_stop_requested)
574 pthread_cond_wait(&speak_queue_stop_or_pause_cond, &speak_queue_mutex);
575 speak_queue_stop_or_pause_sleeping = 0;
576 pthread_cond_signal(&speak_queue_stop_or_pause_sleeping_cond);
577
578 DBG(DBG_MODNAME " Stop or pause.");
579 if (speak_queue_close_requested)
580 break;
581
582 pthread_cond_signal(&playback_queue_data_condition);
583 pthread_cond_broadcast(&playback_queue_room_condition);
584 pthread_mutex_unlock(&speak_queue_mutex);
585
586 if (module_audio_id) {
587 pthread_mutex_lock(&speak_queue_mutex);
588 speak_queue_state = IDLE;
589 pthread_mutex_unlock(&speak_queue_mutex);
590 DBG(DBG_MODNAME " Stopping audio.");
591 ret = spd_audio_stop(module_audio_id);
592 if (ret != 0)
593 DBG("spd_audio_stop returned non-zero value.");
594 pthread_mutex_lock(&speak_queue_mutex);
595 while (!speak_queue_play_sleeping) {
596 ret = spd_audio_stop(module_audio_id);
597 if (ret != 0)
598 DBG("spd_audio_stop returned non-zero value.");
599 pthread_mutex_unlock(&speak_queue_mutex);
600 g_usleep(5000);
601 pthread_mutex_lock(&speak_queue_mutex);
602 }
603 pthread_mutex_unlock(&speak_queue_mutex);
604 } else {
605 pthread_mutex_lock(&speak_queue_mutex);
606 while (!speak_queue_play_sleeping)
607 pthread_cond_wait(&speak_queue_play_sleeping_cond, &speak_queue_mutex);
608 pthread_mutex_unlock(&speak_queue_mutex);
609 }
610
611 DBG(DBG_MODNAME " Waiting for synthesis to stop.");
612
613 module_speak_queue_cancel();
614
615 DBG(DBG_MODNAME " Clearing playback queue.");
616 speak_queue_clear_playback_queue();
617
618 int save_pause_state = speak_queue_pause_state;
619 pthread_mutex_lock(&speak_queue_mutex);
620 module_speak_queue_reset();
621 pthread_mutex_unlock(&speak_queue_mutex);
622
623 if (save_pause_state == SPEAK_QUEUE_PAUSE_MARK_REPORTED) {
624 module_report_event_pause();
625 } else {
626 module_report_event_stop();
627 }
628
629 DBG(DBG_MODNAME " Stop or pause thread ended.......\n");
630 pthread_mutex_lock(&speak_queue_mutex);
631 }
632 pthread_mutex_unlock(&speak_queue_mutex);
633 pthread_exit(NULL);
634 }
635