diff options
author | Stéphane Doyon <s.doyon@videotron.ca> | 2008-07-15 14:06:11 +0000 |
---|---|---|
committer | Stéphane Doyon <s.doyon@videotron.ca> | 2008-07-15 14:06:11 +0000 |
commit | c893affeefa35975c916a222d20a989f31555646 (patch) | |
tree | 72db68190ff77e70e0d9ffc0df4753a24b2261e1 /apps/talk.c | |
parent | 4aafed43d40d72315ad314b71737b169f8dbdf22 (diff) | |
download | rockbox-c893affeefa35975c916a222d20a989f31555646.tar.gz rockbox-c893affeefa35975c916a222d20a989f31555646.zip |
Accept FS#8918: Voice multiple thumbnails and talk race fixes.
-Allows loading multiple thumbnails back-to-back in the one thumbnail buffer.
-Mutex to prevent race conditions with talk queue indices and
thumbnail buffer state.
-Synchronous shutup.
-Shutup is a noop if no voice is queued.
-mp3_play_stop() does nothing until the audio thread is ready.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18046 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/talk.c')
-rw-r--r-- | apps/talk.c | 70 |
1 files changed, 60 insertions, 10 deletions
diff --git a/apps/talk.c b/apps/talk.c index 61dafb0c6c..f84ecd0ef5 100644 --- a/apps/talk.c +++ b/apps/talk.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include "playback.h" | 44 | #include "playback.h" |
45 | #endif | 45 | #endif |
46 | #include "debug.h" | 46 | #include "debug.h" |
47 | #include "kernel.h" | ||
47 | 48 | ||
48 | 49 | ||
49 | /* Memory layout varies between targets because the | 50 | /* Memory layout varies between targets because the |
@@ -110,14 +111,27 @@ struct queue_entry /* one entry of the internal queue */ | |||
110 | 111 | ||
111 | /***************** Globals *****************/ | 112 | /***************** Globals *****************/ |
112 | 113 | ||
113 | static unsigned char* p_thumbnail = NULL; /* buffer for thumbnail */ | 114 | static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */ |
114 | static long size_for_thumbnail; /* leftover buffer size for it */ | 115 | /* Multiple thumbnails can be loaded back-to-back in this buffer. */ |
116 | static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in | ||
117 | thumbnail buffer */ | ||
118 | static long size_for_thumbnail; /* total thumbnail buffer size */ | ||
115 | static struct voicefile* p_voicefile; /* loaded voicefile */ | 119 | static struct voicefile* p_voicefile; /* loaded voicefile */ |
116 | static bool has_voicefile; /* a voicefile file is present */ | 120 | static bool has_voicefile; /* a voicefile file is present */ |
121 | static bool need_shutup; /* is there possibly any voice playing to be shutup */ | ||
117 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | 122 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ |
118 | static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */ | 123 | static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */ |
119 | static int queue_write; /* write index of queue, by application */ | 124 | static int queue_write; /* write index of queue, by application */ |
120 | static int queue_read; /* read index of queue, by ISR context */ | 125 | static int queue_read; /* read index of queue, by ISR context */ |
126 | #if CONFIG_CODEC == SWCODEC | ||
127 | struct mutex queue_mutex SHAREDBSS_ATTR; /* protects queue_read, queue_write | ||
128 | and thumbnail_buf_used */ | ||
129 | #define talk_queue_lock() ({ mutex_lock(&queue_mutex); }) | ||
130 | #define talk_queue_unlock() ({ mutex_unlock(&queue_mutex); }) | ||
131 | #else | ||
132 | #define talk_queue_lock() ({ }) | ||
133 | #define talk_queue_unlock() ({ }) | ||
134 | #endif /* CONFIG_CODEC */ | ||
121 | static int sent; /* how many bytes handed over to playback, owned by ISR */ | 135 | static int sent; /* how many bytes handed over to playback, owned by ISR */ |
122 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ | 136 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ |
123 | static int filehandle = -1; /* global, so the MMC variant can keep the file open */ | 137 | static int filehandle = -1; /* global, so the MMC variant can keep the file open */ |
@@ -299,7 +313,11 @@ static void mp3_callback(unsigned char** start, size_t* size) | |||
299 | *size = sent; | 313 | *size = sent; |
300 | return; | 314 | return; |
301 | } | 315 | } |
302 | else if (sent > 0) /* go to next entry */ | 316 | talk_queue_lock(); |
317 | if(p_thumbnail | ||
318 | && queue[queue_read].buf == p_thumbnail +thumbnail_buf_used) | ||
319 | thumbnail_buf_used = 0; | ||
320 | if (sent > 0) /* go to next entry */ | ||
303 | { | 321 | { |
304 | queue_read = (queue_read + 1) & QUEUE_MASK; | 322 | queue_read = (queue_read + 1) & QUEUE_MASK; |
305 | } | 323 | } |
@@ -321,7 +339,8 @@ re_check: | |||
321 | } | 339 | } |
322 | else if (p_silence != NULL /* silence clip available */ | 340 | else if (p_silence != NULL /* silence clip available */ |
323 | && p_lastclip != p_silence /* previous clip wasn't silence */ | 341 | && p_lastclip != p_silence /* previous clip wasn't silence */ |
324 | && p_lastclip != p_thumbnail) /* ..or thumbnail */ | 342 | && !(p_lastclip >= p_thumbnail /* ..or thumbnail */ |
343 | && p_lastclip < p_thumbnail +size_for_thumbnail)) | ||
325 | { /* add silence clip when queue runs empty playing a voice clip */ | 344 | { /* add silence clip when queue runs empty playing a voice clip */ |
326 | queue[queue_write].buf = p_silence; | 345 | queue[queue_write].buf = p_silence; |
327 | queue[queue_write].len = silence_len; | 346 | queue[queue_write].len = silence_len; |
@@ -333,6 +352,7 @@ re_check: | |||
333 | { | 352 | { |
334 | *size = 0; /* end of data */ | 353 | *size = 0; /* end of data */ |
335 | } | 354 | } |
355 | talk_queue_unlock(); | ||
336 | } | 356 | } |
337 | 357 | ||
338 | /***************** Public routines *****************/ | 358 | /***************** Public routines *****************/ |
@@ -388,6 +408,7 @@ void talk_force_shutup(void) | |||
388 | DTCR3 = sent; /* let the DMA finish this frame */ | 408 | DTCR3 = sent; /* let the DMA finish this frame */ |
389 | CHCR3 |= 0x0001; /* re-enable DMA */ | 409 | CHCR3 |= 0x0001; /* re-enable DMA */ |
390 | #endif /* CONFIG_CPU == SH7034 */ | 410 | #endif /* CONFIG_CPU == SH7034 */ |
411 | thumbnail_buf_used = 0; | ||
391 | return; | 412 | return; |
392 | } | 413 | } |
393 | } | 414 | } |
@@ -395,14 +416,17 @@ void talk_force_shutup(void) | |||
395 | 416 | ||
396 | /* Either SWCODEC, or MAS had nothing to do (was frame boundary or not our clip) */ | 417 | /* Either SWCODEC, or MAS had nothing to do (was frame boundary or not our clip) */ |
397 | mp3_play_stop(); | 418 | mp3_play_stop(); |
419 | talk_queue_lock(); | ||
398 | queue_write = queue_read = 0; /* reset the queue */ | 420 | queue_write = queue_read = 0; /* reset the queue */ |
399 | return; | 421 | thumbnail_buf_used = 0; |
422 | talk_queue_unlock(); | ||
423 | need_shutup = false; | ||
400 | } | 424 | } |
401 | 425 | ||
402 | /* Shutup the voice, except if force_enqueue_next is set. */ | 426 | /* Shutup the voice, except if force_enqueue_next is set. */ |
403 | void talk_shutup(void) | 427 | void talk_shutup(void) |
404 | { | 428 | { |
405 | if (!force_enqueue_next) | 429 | if (need_shutup && !force_enqueue_next) |
406 | talk_force_shutup(); | 430 | talk_force_shutup(); |
407 | } | 431 | } |
408 | 432 | ||
@@ -423,6 +447,7 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue) | |||
423 | /* disable the DMA temporarily, to be safe of race condition */ | 447 | /* disable the DMA temporarily, to be safe of race condition */ |
424 | CHCR3 &= ~0x0001; | 448 | CHCR3 &= ~0x0001; |
425 | #endif | 449 | #endif |
450 | talk_queue_lock(); | ||
426 | queue_level = QUEUE_LEVEL; /* check old level */ | 451 | queue_level = QUEUE_LEVEL; /* check old level */ |
427 | 452 | ||
428 | if (queue_level < QUEUE_SIZE - 1) /* space left? */ | 453 | if (queue_level < QUEUE_SIZE - 1) /* space left? */ |
@@ -431,7 +456,8 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue) | |||
431 | queue[queue_write].len = size; | 456 | queue[queue_write].len = size; |
432 | queue_write = (queue_write + 1) & QUEUE_MASK; | 457 | queue_write = (queue_write + 1) & QUEUE_MASK; |
433 | } | 458 | } |
434 | 459 | talk_queue_unlock(); | |
460 | |||
435 | if (queue_level == 0) | 461 | if (queue_level == 0) |
436 | { /* queue was empty, we have to do the initial start */ | 462 | { /* queue was empty, we have to do the initial start */ |
437 | p_lastclip = buf; | 463 | p_lastclip = buf; |
@@ -453,6 +479,8 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue) | |||
453 | #endif | 479 | #endif |
454 | } | 480 | } |
455 | 481 | ||
482 | need_shutup = true; | ||
483 | |||
456 | return; | 484 | return; |
457 | } | 485 | } |
458 | 486 | ||
@@ -476,6 +504,7 @@ static void reset_state(void) | |||
476 | p_thumbnail = audiobuf; | 504 | p_thumbnail = audiobuf; |
477 | size_for_thumbnail = audiobufend - audiobuf; | 505 | size_for_thumbnail = audiobufend - audiobuf; |
478 | #endif | 506 | #endif |
507 | thumbnail_buf_used = 0; | ||
479 | p_silence = NULL; /* pause clip not accessible */ | 508 | p_silence = NULL; /* pause clip not accessible */ |
480 | } | 509 | } |
481 | 510 | ||
@@ -499,6 +528,11 @@ void talk_init(void) | |||
499 | } | 528 | } |
500 | #endif | 529 | #endif |
501 | 530 | ||
531 | #if CONFIG_CODEC == SWCODEC | ||
532 | if(!talk_initialized) | ||
533 | mutex_init(&queue_mutex); | ||
534 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
535 | |||
502 | talk_initialized = true; | 536 | talk_initialized = true; |
503 | strncpy((char *) last_lang, (char *)global_settings.lang_file, | 537 | strncpy((char *) last_lang, (char *)global_settings.lang_file, |
504 | MAX_FILENAME); | 538 | MAX_FILENAME); |
@@ -625,6 +659,7 @@ int talk_file(const char* filename, bool enqueue) | |||
625 | { | 659 | { |
626 | int fd; | 660 | int fd; |
627 | int size; | 661 | int size; |
662 | int thumb_used; | ||
628 | #if CONFIG_CODEC != SWCODEC | 663 | #if CONFIG_CODEC != SWCODEC |
629 | struct mp3entry info; | 664 | struct mp3entry info; |
630 | #endif | 665 | #endif |
@@ -646,27 +681,42 @@ int talk_file(const char* filename, bool enqueue) | |||
646 | } | 681 | } |
647 | #endif | 682 | #endif |
648 | 683 | ||
684 | if (!enqueue) | ||
685 | /* shutup now to free the thumbnail buffer */ | ||
686 | talk_shutup(); | ||
687 | |||
649 | fd = open(filename, O_RDONLY); | 688 | fd = open(filename, O_RDONLY); |
650 | if (fd < 0) /* failed to open */ | 689 | if (fd < 0) /* failed to open */ |
651 | { | 690 | { |
652 | return 0; | 691 | return 0; |
653 | } | 692 | } |
654 | 693 | ||
694 | thumb_used = thumbnail_buf_used; | ||
695 | if(filesize(fd) > size_for_thumbnail -thumb_used) | ||
696 | { /* Don't play truncated clips */ | ||
697 | close(fd); | ||
698 | return 0; | ||
699 | } | ||
700 | |||
655 | #if CONFIG_CODEC != SWCODEC | 701 | #if CONFIG_CODEC != SWCODEC |
656 | lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ | 702 | lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ |
657 | #endif | 703 | #endif |
658 | 704 | ||
659 | size = read(fd, p_thumbnail, size_for_thumbnail); | 705 | size = read(fd, p_thumbnail +thumb_used, |
706 | size_for_thumbnail -thumb_used); | ||
660 | close(fd); | 707 | close(fd); |
661 | 708 | ||
662 | /* ToDo: find audio, skip ID headers and trailers */ | 709 | /* ToDo: find audio, skip ID headers and trailers */ |
663 | 710 | ||
664 | if (size > 0 && size != size_for_thumbnail) /* Don't play missing or truncated clips */ | 711 | if (size > 0) /* Don't play missing clips */ |
665 | { | 712 | { |
666 | #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) | 713 | #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) |
667 | bitswap(p_thumbnail, size); | 714 | bitswap(p_thumbnail, size); |
668 | #endif | 715 | #endif |
669 | queue_clip(p_thumbnail, size, enqueue); | 716 | talk_queue_lock(); |
717 | thumbnail_buf_used = thumb_used +size; | ||
718 | talk_queue_unlock(); | ||
719 | queue_clip(p_thumbnail +thumb_used, size, true); | ||
670 | } | 720 | } |
671 | 721 | ||
672 | return size; | 722 | return size; |