diff options
-rw-r--r-- | apps/talk.c | 269 |
1 files changed, 104 insertions, 165 deletions
diff --git a/apps/talk.c b/apps/talk.c index 820cb75c8d..a49de09e84 100644 --- a/apps/talk.c +++ b/apps/talk.c | |||
@@ -80,14 +80,6 @@ const char* const file_thumbnail_ext = ".talk"; | |||
80 | 80 | ||
81 | #define LOADED_MASK 0x80000000 /* MSB */ | 81 | #define LOADED_MASK 0x80000000 /* MSB */ |
82 | 82 | ||
83 | /* swcodec: cap thumbnail buffer to MAX_THUMNAIL_BUFSIZE since audio keeps | ||
84 | * playing while voice | ||
85 | * hwcodec: just use whatever is left in the audiobuffer, music | ||
86 | * playback is impossible => no cap */ | ||
87 | #if CONFIG_CODEC == SWCODEC | ||
88 | #define MAX_THUMBNAIL_BUFSIZE 0x10000 | ||
89 | #endif | ||
90 | |||
91 | #define DEFAULT_VOICE_LANG "english" | 83 | #define DEFAULT_VOICE_LANG "english" |
92 | 84 | ||
93 | /***************** Data types *****************/ | 85 | /***************** Data types *****************/ |
@@ -124,13 +116,14 @@ struct voicefile_header /* file format of our voice file */ | |||
124 | #endif | 116 | #endif |
125 | 117 | ||
126 | #ifndef MAX_CLIP_BUFFER_SIZE | 118 | #ifndef MAX_CLIP_BUFFER_SIZE |
127 | #define MAX_CLIP_BUFFER_SIZE (1<<30) /* 1GB should be enough for everybody */ | 119 | /* 1GB should be enough for everybody; will be capped to voicefile size */ |
120 | #define MAX_CLIP_BUFFER_SIZE (1<<30) | ||
128 | #endif | 121 | #endif |
122 | #define THUMBNAIL_RESERVE (50000) | ||
129 | 123 | ||
130 | /* Multiple thumbnails can be loaded back-to-back in this buffer. */ | 124 | /* Multiple thumbnails can be loaded back-to-back in this buffer. */ |
131 | static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in | 125 | static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in |
132 | thumbnail buffer */ | 126 | thumbnail buffer */ |
133 | static long size_for_thumbnail; /* total thumbnail buffer size */ | ||
134 | static struct voicefile_header voicefile; /* loaded voicefile */ | 127 | static struct voicefile_header voicefile; /* loaded voicefile */ |
135 | static bool has_voicefile; /* a voicefile file is present */ | 128 | static bool has_voicefile; /* a voicefile file is present */ |
136 | static bool need_shutup; /* is there possibly any voice playing to be shutup */ | 129 | static bool need_shutup; /* is there possibly any voice playing to be shutup */ |
@@ -158,16 +151,9 @@ static unsigned long voicefile_size, voicebuf_size; | |||
158 | 151 | ||
159 | struct queue_entry /* one entry of the internal queue */ | 152 | struct queue_entry /* one entry of the internal queue */ |
160 | { | 153 | { |
161 | int offset; /* actually a buflib handle if type == HANDLE */ | 154 | int handle; /* buflib handle to the clip data */ |
162 | int length; /* total length of the clip */ | 155 | int length; /* total length of the clip */ |
163 | int remaining; /* bytes that still need to be deoded */ | 156 | int remaining; /* bytes that still need to be deoded */ |
164 | /* for large QUEUE_SIZE values it might be worthwhile to merge the type | ||
165 | * into the bits of the above members (as a space saver). For small values | ||
166 | * the required extra code outweights this so it's not done here */ | ||
167 | enum offset_type { | ||
168 | TALK_HANDLE, | ||
169 | THUMB_OFFSET, | ||
170 | } type; | ||
171 | }; | 157 | }; |
172 | 158 | ||
173 | static struct buflib_context clip_ctx; | 159 | static struct buflib_context clip_ctx; |
@@ -186,7 +172,7 @@ static struct queue_entry silence, *last_clip; | |||
186 | 172 | ||
187 | /***************** Private implementation *****************/ | 173 | /***************** Private implementation *****************/ |
188 | 174 | ||
189 | static int index_handle, talk_handle, thumb_handle; | 175 | static int index_handle, talk_handle; |
190 | 176 | ||
191 | static int move_callback(int handle, void *current, void *new) | 177 | static int move_callback(int handle, void *current, void *new) |
192 | { | 178 | { |
@@ -282,34 +268,12 @@ static int shrink_callback(int handle, unsigned hints, void *start, size_t old_s | |||
282 | return BUFLIB_CB_CANNOT_SHRINK; | 268 | return BUFLIB_CB_CANNOT_SHRINK; |
283 | } | 269 | } |
284 | 270 | ||
285 | static int thumb_shrink_callback(int handle, unsigned hints, void *start, size_t old_size) | ||
286 | { | ||
287 | (void)start;(void)old_size;(void)hints; | ||
288 | |||
289 | if (handle == thumb_handle && thumbnail_buf_used == 0) | ||
290 | { | ||
291 | mutex_lock(&read_buffer_mutex); | ||
292 | thumb_handle = core_free(handle); | ||
293 | mutex_unlock(&read_buffer_mutex); | ||
294 | return BUFLIB_CB_OK; | ||
295 | } | ||
296 | |||
297 | return BUFLIB_CB_CANNOT_SHRINK; | ||
298 | } | ||
299 | |||
300 | |||
301 | |||
302 | static struct buflib_callbacks talk_ops = { | 271 | static struct buflib_callbacks talk_ops = { |
303 | .move_callback = move_callback, | 272 | .move_callback = move_callback, |
304 | .sync_callback = sync_callback, | 273 | .sync_callback = sync_callback, |
305 | .shrink_callback = shrink_callback, | 274 | .shrink_callback = shrink_callback, |
306 | }; | 275 | }; |
307 | 276 | ||
308 | static struct buflib_callbacks thumb_ops = { | ||
309 | .move_callback = move_callback, | ||
310 | .sync_callback = sync_callback, | ||
311 | .shrink_callback = thumb_shrink_callback, | ||
312 | }; | ||
313 | 277 | ||
314 | static int open_voicefile(void) | 278 | static int open_voicefile(void) |
315 | { | 279 | { |
@@ -375,19 +339,31 @@ static int free_oldest_clip(void) | |||
375 | struct clip_cache_metadata *cc = buflib_get_data(&clip_ctx, metadata_table_handle); | 339 | struct clip_cache_metadata *cc = buflib_get_data(&clip_ctx, metadata_table_handle); |
376 | for(age = i = 0, now = current_tick; i < max_clips; i++) | 340 | for(age = i = 0, now = current_tick; i < max_clips; i++) |
377 | { | 341 | { |
378 | if (cc[i].handle && (now - cc[i].tick) > age | 342 | if (cc[i].handle) |
379 | && cc[i].voice_id != VOICE_PAUSE) /* never consider silence */ | ||
380 | { | 343 | { |
381 | age = now - cc[i].tick; | 344 | if ((now - cc[i].tick) > age && cc[i].voice_id != VOICE_PAUSE) |
382 | oldest = i; | 345 | { |
346 | /* find the last-used clip but never consider silence */ | ||
347 | age = now - cc[i].tick; | ||
348 | oldest = i; | ||
349 | } | ||
350 | else if (cc[i].voice_id == VOICEONLY_DELIMITER) | ||
351 | { | ||
352 | /* thumb clips are freed immediately */ | ||
353 | oldest = i; | ||
354 | break; | ||
355 | } | ||
383 | } | 356 | } |
384 | } | 357 | } |
358 | /* free the last one if no oldest one could be determined */ | ||
385 | cc = &cc[oldest]; | 359 | cc = &cc[oldest]; |
386 | cc->handle = buflib_free(&clip_ctx, cc->handle); | 360 | cc->handle = buflib_free(&clip_ctx, cc->handle); |
387 | /* need to clear the LOADED bit too */ | 361 | /* need to clear the LOADED bit too (not for thumb clips) */ |
388 | clipbuf = core_get_data(index_handle); | 362 | if (cc->voice_id != VOICEONLY_DELIMITER) |
389 | clipbuf[id2index(cc->voice_id)].size &= ~LOADED_MASK; | 363 | { |
390 | 364 | clipbuf = core_get_data(index_handle); | |
365 | clipbuf[id2index(cc->voice_id)].size &= ~LOADED_MASK; | ||
366 | } | ||
391 | return oldest; | 367 | return oldest; |
392 | } | 368 | } |
393 | 369 | ||
@@ -458,6 +434,8 @@ static void load_initial_clips(int fd) | |||
458 | int handle; | 434 | int handle; |
459 | struct clip_entry* clipbuf = core_get_data(index_handle); | 435 | struct clip_entry* clipbuf = core_get_data(index_handle); |
460 | size_t clipsize = clipbuf[index].size; | 436 | size_t clipsize = clipbuf[index].size; |
437 | ssize_t ret; | ||
438 | |||
461 | if (clipsize == 0) /* clip not included in voicefile */ | 439 | if (clipsize == 0) /* clip not included in voicefile */ |
462 | continue; | 440 | continue; |
463 | 441 | ||
@@ -465,7 +443,8 @@ static void load_initial_clips(int fd) | |||
465 | if (handle < 0) | 443 | if (handle < 0) |
466 | break; | 444 | break; |
467 | 445 | ||
468 | if (read_clip_data(fd, index, handle) < 0) | 446 | ret = read_clip_data(fd, index, handle); |
447 | if (ret < 0) | ||
469 | break; | 448 | break; |
470 | 449 | ||
471 | add_cache_entry(handle, i++, index2id(index)); | 450 | add_cache_entry(handle, i++, index2id(index)); |
@@ -480,7 +459,6 @@ static int get_clip(long id, struct queue_entry *q) | |||
480 | int retval = -1; | 459 | int retval = -1; |
481 | struct clip_entry* clipbuf; | 460 | struct clip_entry* clipbuf; |
482 | size_t clipsize; | 461 | size_t clipsize; |
483 | enum offset_type type; | ||
484 | 462 | ||
485 | index = id2index(id); | 463 | index = id2index(id); |
486 | if (index == -1) | 464 | if (index == -1) |
@@ -521,12 +499,10 @@ static int get_clip(long id, struct queue_entry *q) | |||
521 | clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */ | 499 | clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */ |
522 | retval = cc[i].handle; | 500 | retval = cc[i].handle; |
523 | } | 501 | } |
524 | type = TALK_HANDLE; | ||
525 | 502 | ||
526 | q->offset = retval; | 503 | q->handle = retval; |
527 | q->length = clipsize; | 504 | q->length = clipsize; |
528 | q->remaining = clipsize; | 505 | q->remaining = clipsize; |
529 | q->type = type; | ||
530 | return 0; | 506 | return 0; |
531 | } | 507 | } |
532 | 508 | ||
@@ -596,33 +572,6 @@ alloc_err: | |||
596 | index_handle = core_free(index_handle); | 572 | index_handle = core_free(index_handle); |
597 | return false; | 573 | return false; |
598 | } | 574 | } |
599 | |||
600 | static bool alloc_thumbnail_buf(void) | ||
601 | { | ||
602 | int handle; | ||
603 | size_t size; | ||
604 | if (thumb_handle > 0) | ||
605 | return true; /* nothing to do? */ | ||
606 | #if CONFIG_CODEC == SWCODEC | ||
607 | /* try to allocate the max. first, and take whatever we can get if that | ||
608 | * fails */ | ||
609 | size = MAX_THUMBNAIL_BUFSIZE; | ||
610 | handle = core_alloc_ex("voice thumb", MAX_THUMBNAIL_BUFSIZE, &thumb_ops); | ||
611 | if (handle < 0) | ||
612 | { | ||
613 | size = core_allocatable(); | ||
614 | handle = core_alloc_ex("voice thumb", size, &thumb_ops); | ||
615 | } | ||
616 | #else | ||
617 | /* on HWCODEC, just use the rest of the remaining buffer, | ||
618 | * normal playback cannot happen anyway */ | ||
619 | size = audio_buffer_available(); | ||
620 | handle = core_alloc_ex("voice thumb", size, &thumb_ops); | ||
621 | #endif | ||
622 | thumb_handle = handle; | ||
623 | size_for_thumbnail = (handle > 0) ? size : 0; | ||
624 | return handle > 0; | ||
625 | } | ||
626 | 575 | ||
627 | /* load the voice file into the mp3 buffer */ | 576 | /* load the voice file into the mp3 buffer */ |
628 | static bool load_voicefile_index(int fd) | 577 | static bool load_voicefile_index(int fd) |
@@ -684,10 +633,6 @@ static bool load_voicefile_data(int fd) | |||
684 | * VOICE_PAUSE clip is specially handled */ | 633 | * VOICE_PAUSE clip is specially handled */ |
685 | get_clip(VOICE_PAUSE, &silence); | 634 | get_clip(VOICE_PAUSE, &silence); |
686 | 635 | ||
687 | /* not an error if this fails here, might try again when the | ||
688 | * actual thumbnails are attempted to be played back */ | ||
689 | alloc_thumbnail_buf(); | ||
690 | |||
691 | return true; | 636 | return true; |
692 | } | 637 | } |
693 | 638 | ||
@@ -701,18 +646,13 @@ static void* commit_transfer(struct queue_entry *qe, size_t *size) | |||
701 | { | 646 | { |
702 | void *buf = NULL; /* shut up gcc */ | 647 | void *buf = NULL; /* shut up gcc */ |
703 | static unsigned char *bufpos = commit_buffer; | 648 | static unsigned char *bufpos = commit_buffer; |
704 | int offset = qe->offset; | ||
705 | #if CONFIG_CODEC != SWCODEC | 649 | #if CONFIG_CODEC != SWCODEC |
706 | sent = MIN(qe->remaining, 0xFFFF); | 650 | sent = MIN(qe->remaining, 0xFFFF); |
707 | #else | 651 | #else |
708 | sent = qe->remaining; | 652 | sent = qe->remaining; |
709 | #endif | 653 | #endif |
710 | sent = MIN((size_t)sent, sizeof(commit_buffer)); | 654 | sent = MIN((size_t)sent, sizeof(commit_buffer)); |
711 | switch (qe->type) | 655 | buf = buflib_get_data(&clip_ctx, qe->handle); |
712 | { | ||
713 | case TALK_HANDLE: buf = buflib_get_data(&clip_ctx, offset); break; | ||
714 | case THUMB_OFFSET: buf = core_get_data(thumb_handle) + offset; break; | ||
715 | } | ||
716 | /* adjust buffer position to what has been played already */ | 656 | /* adjust buffer position to what has been played already */ |
717 | buf += (qe->length - qe->remaining); | 657 | buf += (qe->length - qe->remaining); |
718 | memcpy(bufpos, buf, sent); | 658 | memcpy(bufpos, buf, sent); |
@@ -724,8 +664,8 @@ static void* commit_transfer(struct queue_entry *qe, size_t *size) | |||
724 | 664 | ||
725 | static inline bool is_silence(struct queue_entry *qe) | 665 | static inline bool is_silence(struct queue_entry *qe) |
726 | { | 666 | { |
727 | if (silence.length > 0) /* silence clip available? */ | 667 | if (silence.handle > 0) /* silence clip available? */ |
728 | return (qe->offset == silence.offset && qe->type == silence.type); | 668 | return (qe->handle == silence.handle); |
729 | else | 669 | else |
730 | return false; | 670 | return false; |
731 | } | 671 | } |
@@ -743,21 +683,15 @@ static void mp3_callback(const void** start, size_t* size) | |||
743 | } | 683 | } |
744 | 684 | ||
745 | talk_queue_lock(); | 685 | talk_queue_lock(); |
746 | /* check if thumbnails have been played */ | ||
747 | if (qe->type == THUMB_OFFSET) | ||
748 | { | ||
749 | if (qe->remaining == 0 && (qe->length + qe->offset) == thumbnail_buf_used) | ||
750 | thumbnail_buf_used = 0; | ||
751 | } | ||
752 | 686 | ||
753 | /* increment read position for the just played clip */ | 687 | /* increment read position for the just played clip */ |
754 | queue_read = (queue_read + 1) & QUEUE_MASK; | 688 | queue_read = (queue_read + 1) & QUEUE_MASK; |
755 | 689 | ||
756 | if (QUEUE_LEVEL == 0) | 690 | if (QUEUE_LEVEL == 0) |
757 | { | 691 | { |
758 | if (!is_silence(last_clip) && last_clip->type != THUMB_OFFSET) | 692 | if (!is_silence(last_clip)) |
759 | { /* add silence clip when queue runs empty playing a voice clip, | 693 | { /* add silence clip when queue runs empty playing a voice clip, |
760 | * only if the previous clip wasn't silence or thumbnail */ | 694 | * only if the previous clip wasn't already silence */ |
761 | queue[queue_write] = silence; | 695 | queue[queue_write] = silence; |
762 | queue_write = (queue_write + 1) & QUEUE_MASK; | 696 | queue_write = (queue_write + 1) & QUEUE_MASK; |
763 | } | 697 | } |
@@ -793,7 +727,6 @@ void talk_force_shutup(void) | |||
793 | unsigned char* search; | 727 | unsigned char* search; |
794 | unsigned char* end; | 728 | unsigned char* end; |
795 | int len; | 729 | int len; |
796 | unsigned offset; | ||
797 | if (QUEUE_LEVEL == 0) /* has ended anyway */ | 730 | if (QUEUE_LEVEL == 0) /* has ended anyway */ |
798 | return; | 731 | return; |
799 | 732 | ||
@@ -802,13 +735,7 @@ void talk_force_shutup(void) | |||
802 | #endif /* CONFIG_CPU == SH7034 */ | 735 | #endif /* CONFIG_CPU == SH7034 */ |
803 | /* search next frame boundary and continue up to there */ | 736 | /* search next frame boundary and continue up to there */ |
804 | pos = search = mp3_get_pos(); | 737 | pos = search = mp3_get_pos(); |
805 | offset = queue[queue_read].offset; | 738 | end = buflib_get_data(&clip_ctx, queue[queue_read].handle); |
806 | switch (queue[queue_read].type) | ||
807 | { | ||
808 | case THUMB_OFFSET: end = core_get_data(thumb_handle) + offset; break; | ||
809 | case TALK_HANDLE: end = buflib_get_data(&clip_ctx, offset); break; | ||
810 | default: end = NULL; /* shut up gcc */ | ||
811 | } | ||
812 | len = queue[queue_read].length; | 739 | len = queue[queue_read].length; |
813 | 740 | ||
814 | if (pos >= end && pos <= (end+len)) /* really our clip? */ | 741 | if (pos >= end && pos <= (end+len)) /* really our clip? */ |
@@ -914,7 +841,6 @@ static void queue_clip(struct queue_entry *clip, bool enqueue) | |||
914 | return; | 841 | return; |
915 | } | 842 | } |
916 | 843 | ||
917 | #if CONFIG_CODEC == SWCODEC | ||
918 | /* return if a voice codec is required or not */ | 844 | /* return if a voice codec is required or not */ |
919 | static bool talk_voice_required(void) | 845 | static bool talk_voice_required(void) |
920 | { | 846 | { |
@@ -922,7 +848,6 @@ static bool talk_voice_required(void) | |||
922 | || (global_settings.talk_dir_clip) /* Thumbnail clips are required */ | 848 | || (global_settings.talk_dir_clip) /* Thumbnail clips are required */ |
923 | || (global_settings.talk_file_clip); | 849 | || (global_settings.talk_file_clip); |
924 | } | 850 | } |
925 | #endif | ||
926 | 851 | ||
927 | /***************** Public implementation *****************/ | 852 | /***************** Public implementation *****************/ |
928 | 853 | ||
@@ -951,7 +876,7 @@ void talk_init(void) | |||
951 | queue_write = queue_read = 0; /* reset the queue */ | 876 | queue_write = queue_read = 0; /* reset the queue */ |
952 | memset(&voicefile, 0, sizeof(voicefile)); | 877 | memset(&voicefile, 0, sizeof(voicefile)); |
953 | 878 | ||
954 | silence.offset = -1; /* pause clip not accessible */ | 879 | silence.handle = -1; /* pause clip not accessible */ |
955 | voicefile_size = has_voicefile = 0; | 880 | voicefile_size = has_voicefile = 0; |
956 | /* need to free these as their size depends on the voice file, and | 881 | /* need to free these as their size depends on the voice file, and |
957 | * this function is called when the talk voice file changes */ | 882 | * this function is called when the talk voice file changes */ |
@@ -961,37 +886,46 @@ void talk_init(void) | |||
961 | * and so we can re-use it if it's already allocated in any event */ | 886 | * and so we can re-use it if it's already allocated in any event */ |
962 | 887 | ||
963 | filehandle = open_voicefile(); | 888 | filehandle = open_voicefile(); |
964 | if (filehandle < 0) | 889 | if (filehandle > 0) |
965 | return; | ||
966 | |||
967 | if (!load_voicefile_index(filehandle)) | ||
968 | goto out; | ||
969 | |||
970 | /* Now determine the maximum buffer size needed for the voicefile. | ||
971 | * The below pretends the entire voicefile would be loaded. The buffer | ||
972 | * size is eventually capped later on in load_voicefile_data() */ | ||
973 | int num_clips = voicefile.id1_max + voicefile.id2_max; | ||
974 | int non_empty = num_clips; | ||
975 | int total_size = 0, avg_size; | ||
976 | struct clip_entry *clips = core_get_data(index_handle); | ||
977 | /* check for the average clip size to estimate the maximum number of | ||
978 | * clips the buffer can hold */ | ||
979 | for (int i = 0; i<num_clips; i++) | ||
980 | { | 890 | { |
981 | if (clips[i].size) | 891 | if (!load_voicefile_index(filehandle)) |
982 | total_size += ALIGN_UP(clips[i].size, sizeof(void *)); | 892 | goto out; |
983 | else | 893 | |
984 | non_empty -= 1; | 894 | /* Now determine the maximum buffer size needed for the voicefile. |
895 | * The below pretends the entire voicefile would be loaded. The buffer | ||
896 | * size is eventually capped later on in load_voicefile_data() */ | ||
897 | int num_clips = voicefile.id1_max + voicefile.id2_max; | ||
898 | int non_empty = num_clips; | ||
899 | int total_size = 0, avg_size; | ||
900 | struct clip_entry *clips = core_get_data(index_handle); | ||
901 | /* check for the average clip size to estimate the maximum number of | ||
902 | * clips the buffer can hold */ | ||
903 | for (int i = 0; i<num_clips; i++) | ||
904 | { | ||
905 | if (clips[i].size) | ||
906 | total_size += ALIGN_UP(clips[i].size, sizeof(void *)); | ||
907 | else | ||
908 | non_empty -= 1; | ||
909 | } | ||
910 | avg_size = total_size / non_empty; | ||
911 | max_clips = MIN((int)(MAX_CLIP_BUFFER_SIZE/avg_size) + 1, non_empty); | ||
912 | /* account for possible thumb clips */ | ||
913 | total_size += THUMBNAIL_RESERVE; | ||
914 | max_clips += 16; | ||
915 | voicefile_size = total_size; | ||
916 | has_voicefile = true; | ||
985 | } | 917 | } |
986 | avg_size = total_size / non_empty; | 918 | else if (talk_voice_required()) |
987 | max_clips = MIN((int)(MAX_CLIP_BUFFER_SIZE/avg_size) + 1, non_empty); | 919 | { |
988 | voicefile_size = total_size; | 920 | /* create buffer just for thumb clips */ |
989 | /* additionally to the clips we need a table to record the age of the clips | 921 | max_clips = 16; |
922 | voicefile_size = THUMBNAIL_RESERVE; | ||
923 | } | ||
924 | /* additionally to the clip we need a table to record the age of the clips | ||
990 | * so that, when memory is tight, only the most recently used ones are kept */ | 925 | * so that, when memory is tight, only the most recently used ones are kept */ |
991 | voicefile_size += sizeof(struct clip_cache_metadata) * max_clips; | 926 | voicefile_size += sizeof(struct clip_cache_metadata) * max_clips; |
992 | /* compensate a bit for buflib alloc overhead. */ | 927 | /* compensate a bit for buflib alloc overhead. */ |
993 | voicefile_size += BUFLIB_ALLOC_OVERHEAD * max_clips + 64; | 928 | voicefile_size += BUFLIB_ALLOC_OVERHEAD * max_clips + 64; |
994 | has_voicefile = true; | ||
995 | 929 | ||
996 | load_voicefile_data(filehandle); | 930 | load_voicefile_data(filehandle); |
997 | 931 | ||
@@ -1003,7 +937,6 @@ void talk_init(void) | |||
1003 | 937 | ||
1004 | out: | 938 | out: |
1005 | close(filehandle); /* close again, this was just to detect presence */ | 939 | close(filehandle); /* close again, this was just to detect presence */ |
1006 | filehandle = -1; | ||
1007 | } | 940 | } |
1008 | 941 | ||
1009 | /* somebody else claims the mp3 buffer, e.g. for regular play/record */ | 942 | /* somebody else claims the mp3 buffer, e.g. for regular play/record */ |
@@ -1035,10 +968,10 @@ int talk_id(int32_t id, bool enqueue) | |||
1035 | if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */ | 968 | if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */ |
1036 | { | 969 | { |
1037 | int fd = open_voicefile(); | 970 | int fd = open_voicefile(); |
1038 | if (fd < 0 | 971 | if (fd < 0 || !load_voicefile_index(fd)) |
1039 | || !load_voicefile_index(fd) | ||
1040 | || !load_voicefile_data(fd)) | ||
1041 | return -1; | 972 | return -1; |
973 | load_voicefile_data(fd); | ||
974 | close(fd); | ||
1042 | } | 975 | } |
1043 | 976 | ||
1044 | if (id == -1) /* -1 is an indication for silence */ | 977 | if (id == -1) /* -1 is an indication for silence */ |
@@ -1100,7 +1033,7 @@ static int _talk_file(const char* filename, | |||
1100 | { | 1033 | { |
1101 | int fd; | 1034 | int fd; |
1102 | int size; | 1035 | int size; |
1103 | int thumb_used; | 1036 | int handle, oldest = -1; |
1104 | #if CONFIG_CODEC != SWCODEC | 1037 | #if CONFIG_CODEC != SWCODEC |
1105 | struct mp3entry info; | 1038 | struct mp3entry info; |
1106 | #endif | 1039 | #endif |
@@ -1110,9 +1043,8 @@ static int _talk_file(const char* filename, | |||
1110 | return -1; /* talking has been disabled */ | 1043 | return -1; /* talking has been disabled */ |
1111 | if (!check_audio_status()) | 1044 | if (!check_audio_status()) |
1112 | return -1; | 1045 | return -1; |
1113 | 1046 | if (talk_handle <= 0) | |
1114 | if (!alloc_thumbnail_buf()) | 1047 | load_voicefile_data(-1); |
1115 | return -1; | ||
1116 | 1048 | ||
1117 | #if CONFIG_CODEC != SWCODEC | 1049 | #if CONFIG_CODEC != SWCODEC |
1118 | if(mp3info(&info, filename)) /* use this to find real start */ | 1050 | if(mp3info(&info, filename)) /* use this to find real start */ |
@@ -1130,19 +1062,17 @@ static int _talk_file(const char* filename, | |||
1130 | { | 1062 | { |
1131 | return 0; | 1063 | return 0; |
1132 | } | 1064 | } |
1133 | 1065 | size = filesize(fd); | |
1134 | thumb_used = thumbnail_buf_used; | ||
1135 | if(filesize(fd) > size_for_thumbnail -thumb_used) | ||
1136 | { /* Don't play truncated clips */ | ||
1137 | close(fd); | ||
1138 | return 0; | ||
1139 | } | ||
1140 | 1066 | ||
1141 | #if CONFIG_CODEC != SWCODEC | 1067 | #if CONFIG_CODEC != SWCODEC |
1142 | lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ | 1068 | size -= lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ |
1143 | #endif | 1069 | #endif |
1144 | 1070 | ||
1145 | size = read_to_handle(fd, thumb_handle, thumb_used, size_for_thumbnail - thumb_used); | 1071 | /* free clips from cache until this one succeeds to allocate */ |
1072 | while ((handle = buflib_alloc(&clip_ctx, size)) < 0) | ||
1073 | oldest = free_oldest_clip(); | ||
1074 | |||
1075 | size = read_to_handle_ex(fd, &clip_ctx, handle, 0, size); | ||
1146 | close(fd); | 1076 | close(fd); |
1147 | 1077 | ||
1148 | /* ToDo: find audio, skip ID headers and trailers */ | 1078 | /* ToDo: find audio, skip ID headers and trailers */ |
@@ -1150,21 +1080,25 @@ static int _talk_file(const char* filename, | |||
1150 | if (size > 0) /* Don't play missing clips */ | 1080 | if (size > 0) /* Don't play missing clips */ |
1151 | { | 1081 | { |
1152 | struct queue_entry clip; | 1082 | struct queue_entry clip; |
1083 | clip.handle = handle; | ||
1084 | clip.length = clip.remaining = size; | ||
1153 | #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) | 1085 | #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) |
1154 | /* bitswap doesnt yield() */ | 1086 | /* bitswap doesnt yield() */ |
1155 | bitswap(core_get_data(thumb_handle), size); | 1087 | bitswap(buflib_get_data(&clip_ctx, handle), size); |
1156 | #endif | 1088 | #endif |
1157 | if(prefix_ids) | 1089 | if(prefix_ids) |
1158 | /* prefix thumbnail by speaking these ids, but only now | 1090 | /* prefix thumbnail by speaking these ids, but only now |
1159 | that we know there's actually a thumbnail to be | 1091 | that we know there's actually a thumbnail to be |
1160 | spoken. */ | 1092 | spoken. */ |
1161 | talk_idarray(prefix_ids, true); | 1093 | talk_idarray(prefix_ids, true); |
1162 | talk_queue_lock(); | 1094 | /* finally insert into metadata table. thumb clips go under the |
1163 | thumbnail_buf_used = thumb_used + size; | 1095 | * VOICEONLY_DELIMITER id so the cache can distinguish them from |
1164 | talk_queue_unlock(); | 1096 | * normal clips */ |
1165 | clip = (struct queue_entry){ .offset = thumb_used, .length = size, .remaining = size, .type = THUMB_OFFSET }; | 1097 | add_cache_entry(handle, oldest, VOICEONLY_DELIMITER); |
1166 | queue_clip(&clip, true); | 1098 | queue_clip(&clip, true); |
1167 | } | 1099 | } |
1100 | else | ||
1101 | buflib_free(&clip_ctx, handle); | ||
1168 | 1102 | ||
1169 | return size; | 1103 | return size; |
1170 | } | 1104 | } |
@@ -1566,6 +1500,7 @@ void talk_time(const struct tm *tm, bool enqueue) | |||
1566 | bool talk_get_debug_data(struct talk_debug_data *data) | 1500 | bool talk_get_debug_data(struct talk_debug_data *data) |
1567 | { | 1501 | { |
1568 | char* p_lang = DEFAULT_VOICE_LANG; /* default */ | 1502 | char* p_lang = DEFAULT_VOICE_LANG; /* default */ |
1503 | struct clip_cache_metadata *cc; | ||
1569 | 1504 | ||
1570 | memset(data, 0, sizeof(*data)); | 1505 | memset(data, 0, sizeof(*data)); |
1571 | 1506 | ||
@@ -1593,17 +1528,21 @@ bool talk_get_debug_data(struct talk_debug_data *data) | |||
1593 | if (size > data->max_clipsize) | 1528 | if (size > data->max_clipsize) |
1594 | data->max_clipsize = size; | 1529 | data->max_clipsize = size; |
1595 | data->avg_clipsize += size; | 1530 | data->avg_clipsize += size; |
1596 | if (clips[i].size & LOADED_MASK) | 1531 | } |
1597 | cached++; | 1532 | cc = buflib_get_data(&clip_ctx, metadata_table_handle); |
1533 | for (int i = 0; i < (int) max_clips; i++) | ||
1534 | { | ||
1535 | if (cc[i].handle > 0) | ||
1536 | cached += 1; | ||
1598 | } | 1537 | } |
1599 | data->avg_clipsize /= real_clips; | 1538 | data->avg_clipsize /= real_clips; |
1600 | data->num_empty_clips = data->num_clips - real_clips; | 1539 | data->num_empty_clips = data->num_clips - real_clips; |
1601 | data->memory_allocated = sizeof(commit_buffer) + sizeof(voicefile) | 1540 | data->memory_allocated = sizeof(commit_buffer) + sizeof(voicefile) |
1602 | + data->num_clips * sizeof(struct clip_entry) | 1541 | + data->num_clips * sizeof(struct clip_entry) |
1603 | + voicebuf_size + size_for_thumbnail; | 1542 | + voicebuf_size; |
1604 | data->memory_used = data->memory_allocated - size_for_thumbnail + thumbnail_buf_used; | 1543 | data->memory_used = 0; |
1605 | if (talk_handle > 0) | 1544 | if (talk_handle > 0) |
1606 | data->memory_used -= buflib_available(&clip_ctx); | 1545 | data->memory_used = data->memory_allocated - buflib_available(&clip_ctx); |
1607 | data->cached_clips = cached; | 1546 | data->cached_clips = cached; |
1608 | data->cache_hits = cache_hits; | 1547 | data->cache_hits = cache_hits; |
1609 | data->cache_misses = cache_misses; | 1548 | data->cache_misses = cache_misses; |