diff options
Diffstat (limited to 'apps/talk.c')
-rw-r--r-- | apps/talk.c | 194 |
1 files changed, 88 insertions, 106 deletions
diff --git a/apps/talk.c b/apps/talk.c index 47410121ba..ede0aff66b 100644 --- a/apps/talk.c +++ b/apps/talk.c | |||
@@ -80,10 +80,6 @@ const char* const file_thumbnail_ext = ".talk"; | |||
80 | #define MAX_THUMBNAIL_BUFSIZE 0x10000 | 80 | #define MAX_THUMBNAIL_BUFSIZE 0x10000 |
81 | #endif | 81 | #endif |
82 | 82 | ||
83 | #ifndef SIMULATOR | ||
84 | extern bool audio_is_initialized; | ||
85 | #endif | ||
86 | |||
87 | /***************** Data types *****************/ | 83 | /***************** Data types *****************/ |
88 | 84 | ||
89 | struct clip_entry /* one entry of the index table */ | 85 | struct clip_entry /* one entry of the index table */ |
@@ -118,8 +114,7 @@ static long size_for_thumbnail; /* leftover buffer size for it */ | |||
118 | static struct voicefile* p_voicefile; /* loaded voicefile */ | 114 | static struct voicefile* p_voicefile; /* loaded voicefile */ |
119 | static bool has_voicefile; /* a voicefile file is present */ | 115 | static bool has_voicefile; /* a voicefile file is present */ |
120 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | 116 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ |
121 | /* enqueue next utterance even if enqueue is false. */ | 117 | static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */ |
122 | static bool force_enqueue_next; | ||
123 | static int queue_write; /* write index of queue, by application */ | 118 | static int queue_write; /* write index of queue, by application */ |
124 | static int queue_read; /* read index of queue, by ISR context */ | 119 | static int queue_read; /* read index of queue, by ISR context */ |
125 | static int sent; /* how many bytes handed over to playback, owned by ISR */ | 120 | static int sent; /* how many bytes handed over to playback, owned by ISR */ |
@@ -131,16 +126,8 @@ static unsigned char* p_lastclip; /* address of latest clip, for silence add */ | |||
131 | static unsigned long voicefile_size = 0; /* size of the loaded voice file */ | 126 | static unsigned long voicefile_size = 0; /* size of the loaded voice file */ |
132 | static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ | 127 | static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ |
133 | static bool talk_initialized; /* true if talk_init has been called */ | 128 | static bool talk_initialized; /* true if talk_init has been called */ |
134 | static int talk_menu_disable; /* if non-zero, temporarily disable voice UI (not saved) */ | 129 | static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */ |
135 | 130 | ||
136 | /***************** Private prototypes *****************/ | ||
137 | |||
138 | static void load_voicefile(void); | ||
139 | static void mp3_callback(unsigned char** start, size_t* size); | ||
140 | static int queue_clip(unsigned char* buf, long size, bool enqueue); | ||
141 | static int open_voicefile(void); | ||
142 | static unsigned char* get_clip(long id, long* p_size); | ||
143 | int shutup(void); /* Interrupt voice, as when enqueue is false */ | ||
144 | 131 | ||
145 | /***************** Private implementation *****************/ | 132 | /***************** Private implementation *****************/ |
146 | 133 | ||
@@ -161,6 +148,50 @@ static int open_voicefile(void) | |||
161 | } | 148 | } |
162 | 149 | ||
163 | 150 | ||
151 | /* fetch a clip from the voice file */ | ||
152 | static unsigned char* get_clip(long id, long* p_size) | ||
153 | { | ||
154 | long clipsize; | ||
155 | unsigned char* clipbuf; | ||
156 | |||
157 | if (id > VOICEONLY_DELIMITER) | ||
158 | { /* voice-only entries use the second part of the table */ | ||
159 | id -= VOICEONLY_DELIMITER + 1; | ||
160 | if (id >= p_voicefile->id2_max) | ||
161 | return NULL; /* must be newer than we have */ | ||
162 | id += p_voicefile->id1_max; /* table 2 is behind table 1 */ | ||
163 | } | ||
164 | else | ||
165 | { /* normal use of the first table */ | ||
166 | if (id >= p_voicefile->id1_max) | ||
167 | return NULL; /* must be newer than we have */ | ||
168 | } | ||
169 | |||
170 | clipsize = p_voicefile->index[id].size; | ||
171 | if (clipsize == 0) /* clip not included in voicefile */ | ||
172 | return NULL; | ||
173 | clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset; | ||
174 | |||
175 | #ifdef HAVE_MMC /* dynamic loading, on demand */ | ||
176 | if (!(clipsize & LOADED_MASK)) | ||
177 | { /* clip used for the first time, needs loading */ | ||
178 | lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET); | ||
179 | if (read(filehandle, clipbuf, clipsize) != clipsize) | ||
180 | return NULL; /* read error */ | ||
181 | |||
182 | p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */ | ||
183 | } | ||
184 | else | ||
185 | { /* clip is in memory already */ | ||
186 | clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */ | ||
187 | } | ||
188 | #endif | ||
189 | |||
190 | *p_size = clipsize; | ||
191 | return clipbuf; | ||
192 | } | ||
193 | |||
194 | |||
164 | /* load the voice file into the mp3 buffer */ | 195 | /* load the voice file into the mp3 buffer */ |
165 | static void load_voicefile(void) | 196 | static void load_voicefile(void) |
166 | { | 197 | { |
@@ -252,7 +283,7 @@ load_err: | |||
252 | /* Are more voice clips queued and waiting? */ | 283 | /* Are more voice clips queued and waiting? */ |
253 | bool is_voice_queued() | 284 | bool is_voice_queued() |
254 | { | 285 | { |
255 | return !!QUEUE_LEVEL; | 286 | return (QUEUE_LEVEL != 0); |
256 | } | 287 | } |
257 | 288 | ||
258 | 289 | ||
@@ -280,7 +311,7 @@ static void mp3_callback(unsigned char** start, size_t* size) | |||
280 | 311 | ||
281 | re_check: | 312 | re_check: |
282 | 313 | ||
283 | if (QUEUE_LEVEL) /* queue is not empty? */ | 314 | if (QUEUE_LEVEL != 0) /* queue is not empty? */ |
284 | { /* start next clip */ | 315 | { /* start next clip */ |
285 | #if CONFIG_CODEC != SWCODEC | 316 | #if CONFIG_CODEC != SWCODEC |
286 | sent = MIN(queue[queue_read].len, 0xFFFF); | 317 | sent = MIN(queue[queue_read].len, 0xFFFF); |
@@ -309,26 +340,22 @@ re_check: | |||
309 | } | 340 | } |
310 | } | 341 | } |
311 | 342 | ||
343 | /***************** Public routines *****************/ | ||
344 | |||
312 | /* stop the playback and the pending clips */ | 345 | /* stop the playback and the pending clips */ |
313 | int do_shutup(void) | 346 | void talk_force_shutup(void) |
314 | { | 347 | { |
348 | /* Most of this is MAS only */ | ||
315 | #if CONFIG_CODEC != SWCODEC | 349 | #if CONFIG_CODEC != SWCODEC |
316 | unsigned char* pos; | 350 | unsigned char* pos; |
317 | unsigned char* search; | 351 | unsigned char* search; |
318 | unsigned char* end; | 352 | unsigned char* end; |
319 | #endif | ||
320 | |||
321 | if (QUEUE_LEVEL == 0) /* has ended anyway */ | 353 | if (QUEUE_LEVEL == 0) /* has ended anyway */ |
322 | { | 354 | return; |
323 | #if CONFIG_CODEC == SWCODEC | 355 | |
324 | mp3_play_stop(); | ||
325 | #endif | ||
326 | return 0; | ||
327 | } | ||
328 | #if CONFIG_CODEC != SWCODEC | ||
329 | #if CONFIG_CPU == SH7034 | 356 | #if CONFIG_CPU == SH7034 |
330 | CHCR3 &= ~0x0001; /* disable the DMA (and therefore the interrupt also) */ | 357 | CHCR3 &= ~0x0001; /* disable the DMA (and therefore the interrupt also) */ |
331 | #endif | 358 | #endif /* CONFIG_CPU == SH7034 */ |
332 | /* search next frame boundary and continue up to there */ | 359 | /* search next frame boundary and continue up to there */ |
333 | pos = search = mp3_get_pos(); | 360 | pos = search = mp3_get_pos(); |
334 | end = queue[queue_read].buf + queue[queue_read].len; | 361 | end = queue[queue_read].buf + queue[queue_read].len; |
@@ -362,41 +389,38 @@ int do_shutup(void) | |||
362 | #if CONFIG_CPU == SH7034 | 389 | #if CONFIG_CPU == SH7034 |
363 | DTCR3 = sent; /* let the DMA finish this frame */ | 390 | DTCR3 = sent; /* let the DMA finish this frame */ |
364 | CHCR3 |= 0x0001; /* re-enable DMA */ | 391 | CHCR3 |= 0x0001; /* re-enable DMA */ |
365 | #endif | 392 | #endif /* CONFIG_CPU == SH7034 */ |
366 | return 0; | 393 | return; |
367 | } | 394 | } |
368 | } | 395 | } |
369 | #endif | 396 | #endif /* CONFIG_CODEC != SWCODEC */ |
370 | 397 | ||
371 | /* nothing to do, was frame boundary or not our clip */ | 398 | /* Either SWCODEC, or MAS had nothing to do (was frame boundary or not our clip) */ |
372 | mp3_play_stop(); | 399 | mp3_play_stop(); |
373 | |||
374 | queue_write = queue_read = 0; /* reset the queue */ | 400 | queue_write = queue_read = 0; /* reset the queue */ |
375 | 401 | return; | |
376 | return 0; | ||
377 | } | 402 | } |
378 | 403 | ||
379 | /* Shutup the voice, except if force_enqueue_next is set. */ | 404 | /* Shutup the voice, except if force_enqueue_next is set. */ |
380 | int shutup(void) | 405 | void talk_shutup(void) |
381 | { | 406 | { |
382 | if (!force_enqueue_next) | 407 | if (!force_enqueue_next) |
383 | return do_shutup(); | 408 | talk_force_shutup(); |
384 | return 0; | ||
385 | } | 409 | } |
386 | 410 | ||
387 | /* schedule a clip, at the end or discard the existing queue */ | 411 | /* schedule a clip, at the end or discard the existing queue */ |
388 | static int queue_clip(unsigned char* buf, long size, bool enqueue) | 412 | static void queue_clip(unsigned char* buf, long size, bool enqueue) |
389 | { | 413 | { |
390 | int queue_level; | 414 | int queue_level; |
391 | 415 | ||
392 | if (!enqueue) | 416 | if (!enqueue) |
393 | shutup(); /* cut off all the pending stuff */ | 417 | talk_shutup(); /* cut off all the pending stuff */ |
394 | /* Something is being enqueued, force_enqueue_next override is no | 418 | /* Something is being enqueued, force_enqueue_next override is no |
395 | longer in effect. */ | 419 | longer in effect. */ |
396 | force_enqueue_next = false; | 420 | force_enqueue_next = false; |
397 | 421 | ||
398 | if (!size) | 422 | if (!size) |
399 | return 0; /* safety check */ | 423 | return; /* safety check */ |
400 | #if CONFIG_CPU == SH7034 | 424 | #if CONFIG_CPU == SH7034 |
401 | /* disable the DMA temporarily, to be safe of race condition */ | 425 | /* disable the DMA temporarily, to be safe of race condition */ |
402 | CHCR3 &= ~0x0001; | 426 | CHCR3 &= ~0x0001; |
@@ -431,50 +455,7 @@ static int queue_clip(unsigned char* buf, long size, bool enqueue) | |||
431 | #endif | 455 | #endif |
432 | } | 456 | } |
433 | 457 | ||
434 | return 0; | 458 | return; |
435 | } | ||
436 | |||
437 | /* fetch a clip from the voice file */ | ||
438 | static unsigned char* get_clip(long id, long* p_size) | ||
439 | { | ||
440 | long clipsize; | ||
441 | unsigned char* clipbuf; | ||
442 | |||
443 | if (id > VOICEONLY_DELIMITER) | ||
444 | { /* voice-only entries use the second part of the table */ | ||
445 | id -= VOICEONLY_DELIMITER + 1; | ||
446 | if (id >= p_voicefile->id2_max) | ||
447 | return NULL; /* must be newer than we have */ | ||
448 | id += p_voicefile->id1_max; /* table 2 is behind table 1 */ | ||
449 | } | ||
450 | else | ||
451 | { /* normal use of the first table */ | ||
452 | if (id >= p_voicefile->id1_max) | ||
453 | return NULL; /* must be newer than we have */ | ||
454 | } | ||
455 | |||
456 | clipsize = p_voicefile->index[id].size; | ||
457 | if (clipsize == 0) /* clip not included in voicefile */ | ||
458 | return NULL; | ||
459 | clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset; | ||
460 | |||
461 | #ifdef HAVE_MMC /* dynamic loading, on demand */ | ||
462 | if (!(clipsize & LOADED_MASK)) | ||
463 | { /* clip used for the first time, needs loading */ | ||
464 | lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET); | ||
465 | if (read(filehandle, clipbuf, clipsize) != clipsize) | ||
466 | return NULL; /* read error */ | ||
467 | |||
468 | p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */ | ||
469 | } | ||
470 | else | ||
471 | { /* clip is in memory already */ | ||
472 | clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */ | ||
473 | } | ||
474 | #endif | ||
475 | |||
476 | *p_size = clipsize; | ||
477 | return clipbuf; | ||
478 | } | 459 | } |
479 | 460 | ||
480 | 461 | ||
@@ -500,11 +481,12 @@ static void reset_state(void) | |||
500 | p_silence = NULL; /* pause clip not accessible */ | 481 | p_silence = NULL; /* pause clip not accessible */ |
501 | } | 482 | } |
502 | 483 | ||
484 | |||
503 | /***************** Public implementation *****************/ | 485 | /***************** Public implementation *****************/ |
504 | 486 | ||
505 | void talk_init(void) | 487 | void talk_init(void) |
506 | { | 488 | { |
507 | talk_menu_disable = 0; | 489 | talk_temp_disable_count = 0; |
508 | if (talk_initialized && !strcasecmp(last_lang, global_settings.lang_file)) | 490 | if (talk_initialized && !strcasecmp(last_lang, global_settings.lang_file)) |
509 | { | 491 | { |
510 | /* not a new file, nothing to do */ | 492 | /* not a new file, nothing to do */ |
@@ -558,7 +540,7 @@ int talk_get_bufsize(void) | |||
558 | } | 540 | } |
559 | 541 | ||
560 | /* somebody else claims the mp3 buffer, e.g. for regular play/record */ | 542 | /* somebody else claims the mp3 buffer, e.g. for regular play/record */ |
561 | int talk_buffer_steal(void) | 543 | void talk_buffer_steal(void) |
562 | { | 544 | { |
563 | #if CONFIG_CODEC != SWCODEC | 545 | #if CONFIG_CODEC != SWCODEC |
564 | mp3_play_stop(); | 546 | mp3_play_stop(); |
@@ -570,9 +552,7 @@ int talk_buffer_steal(void) | |||
570 | filehandle = -1; | 552 | filehandle = -1; |
571 | } | 553 | } |
572 | #endif | 554 | #endif |
573 | reset_state(); | 555 | reset_state();; |
574 | |||
575 | return 0; | ||
576 | } | 556 | } |
577 | 557 | ||
578 | 558 | ||
@@ -583,6 +563,8 @@ int talk_id(long id, bool enqueue) | |||
583 | unsigned char* clipbuf; | 563 | unsigned char* clipbuf; |
584 | int unit; | 564 | int unit; |
585 | 565 | ||
566 | if (talk_temp_disable_count > 0) | ||
567 | return -1; /* talking has been disabled */ | ||
586 | #if CONFIG_CODEC != SWCODEC | 568 | #if CONFIG_CODEC != SWCODEC |
587 | if (audio_status()) /* busy, buffer in use */ | 569 | if (audio_status()) /* busy, buffer in use */ |
588 | return -1; | 570 | return -1; |
@@ -644,6 +626,8 @@ int talk_file(const char* filename, bool enqueue) | |||
644 | int size; | 626 | int size; |
645 | struct mp3entry info; | 627 | struct mp3entry info; |
646 | 628 | ||
629 | if (talk_temp_disable_count > 0) | ||
630 | return -1; /* talking has been disabled */ | ||
647 | #if CONFIG_CODEC != SWCODEC | 631 | #if CONFIG_CODEC != SWCODEC |
648 | if (audio_status()) /* busy, buffer in use */ | 632 | if (audio_status()) /* busy, buffer in use */ |
649 | return -1; | 633 | return -1; |
@@ -689,13 +673,15 @@ int talk_number(long n, bool enqueue) | |||
689 | int level = 2; /* mille count */ | 673 | int level = 2; /* mille count */ |
690 | long mil = 1000000000; /* highest possible "-illion" */ | 674 | long mil = 1000000000; /* highest possible "-illion" */ |
691 | 675 | ||
676 | if (talk_temp_disable_count > 0) | ||
677 | return -1; /* talking has been disabled */ | ||
692 | #if CONFIG_CODEC != SWCODEC | 678 | #if CONFIG_CODEC != SWCODEC |
693 | if (audio_status()) /* busy, buffer in use */ | 679 | if (audio_status()) /* busy, buffer in use */ |
694 | return -1; | 680 | return -1; |
695 | #endif | 681 | #endif |
696 | 682 | ||
697 | if (!enqueue) | 683 | if (!enqueue) |
698 | shutup(); /* cut off all the pending stuff */ | 684 | talk_shutup(); /* cut off all the pending stuff */ |
699 | 685 | ||
700 | if (n==0) | 686 | if (n==0) |
701 | { /* special case */ | 687 | { /* special case */ |
@@ -785,6 +771,8 @@ int talk_value(long n, int unit, bool enqueue) | |||
785 | = VOICE_PM_UNITS_PER_TICK, | 771 | = VOICE_PM_UNITS_PER_TICK, |
786 | }; | 772 | }; |
787 | 773 | ||
774 | if (talk_temp_disable_count > 0) | ||
775 | return -1; /* talking has been disabled */ | ||
788 | #if CONFIG_CODEC != SWCODEC | 776 | #if CONFIG_CODEC != SWCODEC |
789 | if (audio_status()) /* busy, buffer in use */ | 777 | if (audio_status()) /* busy, buffer in use */ |
790 | return -1; | 778 | return -1; |
@@ -819,13 +807,15 @@ int talk_spell(const char* spell, bool enqueue) | |||
819 | { | 807 | { |
820 | char c; /* currently processed char */ | 808 | char c; /* currently processed char */ |
821 | 809 | ||
810 | if (talk_temp_disable_count > 0) | ||
811 | return -1; /* talking has been disabled */ | ||
822 | #if CONFIG_CODEC != SWCODEC | 812 | #if CONFIG_CODEC != SWCODEC |
823 | if (audio_status()) /* busy, buffer in use */ | 813 | if (audio_status()) /* busy, buffer in use */ |
824 | return -1; | 814 | return -1; |
825 | #endif | 815 | #endif |
826 | 816 | ||
827 | if (!enqueue) | 817 | if (!enqueue) |
828 | shutup(); /* cut off all the pending stuff */ | 818 | talk_shutup(); /* cut off all the pending stuff */ |
829 | 819 | ||
830 | while ((c = *spell++) != '\0') | 820 | while ((c = *spell++) != '\0') |
831 | { | 821 | { |
@@ -849,26 +839,18 @@ int talk_spell(const char* spell, bool enqueue) | |||
849 | return 0; | 839 | return 0; |
850 | } | 840 | } |
851 | 841 | ||
852 | bool talk_menus_enabled(void) | 842 | void talk_disable(bool disable) |
853 | { | 843 | { |
854 | return (global_settings.talk_menu && talk_menu_disable == 0); | 844 | if (disable) |
855 | } | 845 | talk_temp_disable_count++; |
856 | 846 | else | |
857 | 847 | talk_temp_disable_count--; | |
858 | void talk_disable_menus(void) | ||
859 | { | ||
860 | talk_menu_disable++; | ||
861 | } | ||
862 | |||
863 | void talk_enable_menus(void) | ||
864 | { | ||
865 | talk_menu_disable--; | ||
866 | } | 848 | } |
867 | 849 | ||
868 | #if CONFIG_RTC | 850 | #if CONFIG_RTC |
869 | void talk_date_time(struct tm *tm, bool speak_current_time_string) | 851 | void talk_date_time(struct tm *tm, bool speak_current_time_string) |
870 | { | 852 | { |
871 | if(talk_menus_enabled ()) | 853 | if(global_settings.talk_menu) |
872 | { | 854 | { |
873 | if(speak_current_time_string) | 855 | if(speak_current_time_string) |
874 | talk_id(VOICE_CURRENT_TIME, true); | 856 | talk_id(VOICE_CURRENT_TIME, true); |