diff options
-rw-r--r-- | apps/playback.c | 10 | ||||
-rw-r--r-- | apps/playback.h | 2 | ||||
-rw-r--r-- | apps/talk.c | 70 | ||||
-rw-r--r-- | apps/voice_thread.c | 21 |
4 files changed, 76 insertions, 27 deletions
diff --git a/apps/playback.c b/apps/playback.c index 937bf05394..daa9ab3f2e 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -2599,13 +2599,7 @@ void audio_init(void) | |||
2599 | 2599 | ||
2600 | } /* audio_init */ | 2600 | } /* audio_init */ |
2601 | 2601 | ||
2602 | void audio_wait_for_init(void) | 2602 | bool audio_is_thread_ready(void) |
2603 | { | 2603 | { |
2604 | /* audio thread will only set this once after it finished the final | 2604 | return audio_thread_ready; |
2605 | * audio hardware init so this little construct is safe - even | ||
2606 | * cross-core. */ | ||
2607 | while (!audio_thread_ready) | ||
2608 | { | ||
2609 | sleep(0); | ||
2610 | } | ||
2611 | } | 2605 | } |
diff --git a/apps/playback.h b/apps/playback.h index 66d96b4084..d9f29cc56a 100644 --- a/apps/playback.h +++ b/apps/playback.h | |||
@@ -28,7 +28,7 @@ | |||
28 | /* Functions */ | 28 | /* Functions */ |
29 | const char *get_codec_filename(int cod_spec); | 29 | const char *get_codec_filename(int cod_spec); |
30 | void voice_wait(void); | 30 | void voice_wait(void); |
31 | void audio_wait_for_init(void); | 31 | bool audio_is_thread_ready(void); |
32 | int audio_track_count(void); | 32 | int audio_track_count(void); |
33 | long audio_filebufused(void); | 33 | long audio_filebufused(void); |
34 | void audio_pre_ff_rewind(void); | 34 | void audio_pre_ff_rewind(void); |
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; |
diff --git a/apps/voice_thread.c b/apps/voice_thread.c index 8d08e7744b..aeffa5bd7c 100644 --- a/apps/voice_thread.c +++ b/apps/voice_thread.c | |||
@@ -136,11 +136,12 @@ void mp3_play_data(const unsigned char* start, int size, | |||
136 | /* Stop current voice clip from playing */ | 136 | /* Stop current voice clip from playing */ |
137 | void mp3_play_stop(void) | 137 | void mp3_play_stop(void) |
138 | { | 138 | { |
139 | mutex_lock(&voice_mutex); /* Sync against voice_stop */ | 139 | if(!audio_is_thread_ready()) |
140 | return; | ||
140 | 141 | ||
141 | LOGFQUEUE("mp3 > voice Q_VOICE_STOP: 1"); | 142 | mutex_lock(&voice_mutex); /* Sync against voice_stop */ |
142 | queue_remove_from_head(&voice_queue, Q_VOICE_STOP); | 143 | LOGFQUEUE("mp3 >| voice Q_VOICE_STOP: 1"); |
143 | queue_post(&voice_queue, Q_VOICE_STOP, 1); | 144 | queue_send(&voice_queue, Q_VOICE_STOP, 1); |
144 | 145 | ||
145 | mutex_unlock(&voice_mutex); | 146 | mutex_unlock(&voice_mutex); |
146 | } | 147 | } |
@@ -167,8 +168,7 @@ void voice_stop(void) | |||
167 | mutex_lock(&voice_mutex); | 168 | mutex_lock(&voice_mutex); |
168 | 169 | ||
169 | /* Stop the output and current clip */ | 170 | /* Stop the output and current clip */ |
170 | LOGFQUEUE("mp3 >| voice Q_VOICE_STOP: 1"); | 171 | mp3_play_stop(); |
171 | queue_send(&voice_queue, Q_VOICE_STOP, 1); | ||
172 | 172 | ||
173 | /* Careful if using sync objects in talk.c - make sure locking order is | 173 | /* Careful if using sync objects in talk.c - make sure locking order is |
174 | * observed with one or the other always granted first */ | 174 | * observed with one or the other always granted first */ |
@@ -298,8 +298,13 @@ static void voice_thread(void) | |||
298 | struct voice_thread_data td; | 298 | struct voice_thread_data td; |
299 | 299 | ||
300 | voice_data_init(&td); | 300 | voice_data_init(&td); |
301 | audio_wait_for_init(); | 301 | |
302 | 302 | /* audio thread will only set this once after it finished the final | |
303 | * audio hardware init so this little construct is safe - even | ||
304 | * cross-core. */ | ||
305 | while (!audio_is_thread_ready()) | ||
306 | sleep(0); | ||
307 | |||
303 | goto message_wait; | 308 | goto message_wait; |
304 | 309 | ||
305 | while (1) | 310 | while (1) |