summaryrefslogtreecommitdiff
path: root/apps/talk.c
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2014-02-02 14:48:32 +0100
committerThomas Martitz <kugel@rockbox.org>2014-02-02 19:40:39 +0100
commit03f373c9cd370e77766a511c574b3cf7bf8d9226 (patch)
tree0a460efd9f33e29c1480df3652fab35aa0e4fa7c /apps/talk.c
parentc46f9be10a9b3f34f804583d8cdc980ab62c58bd (diff)
downloadrockbox-03f373c9cd370e77766a511c574b3cf7bf8d9226.tar.gz
rockbox-03f373c9cd370e77766a511c574b3cf7bf8d9226.zip
talk.c: The new cache management is good enough to use it on all targets.
This unifies the talk.c for all targets. The only separation is left is TALK_PROGRESSIVE_LOAD: When this is defined the talk buffer will not be initially prefilled. This is useful for super slow storage or when the buffer is not large enough to prefill it with useful clips (the prefill code could be made smarter too). The buffer size can be adjusted. By default lowmem uses 100k while other targets load the entire file. The bigger the more clips can be cached but with diminishing returns. Change-Id: Ife38fb101c53093117e6638c40c65f7d177a31d4
Diffstat (limited to 'apps/talk.c')
-rw-r--r--apps/talk.c372
1 files changed, 189 insertions, 183 deletions
diff --git a/apps/talk.c b/apps/talk.c
index 4c4e6184b8..820cb75c8d 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -46,6 +46,7 @@
46#include "structec.h" 46#include "structec.h"
47#include "plugin.h" /* plugin_get_buffer() */ 47#include "plugin.h" /* plugin_get_buffer() */
48#include "debug.h" 48#include "debug.h"
49#include "panic.h"
49 50
50/* Memory layout varies between targets because the 51/* Memory layout varies between targets because the
51 Archos (MASCODEC) devices cannot mix voice and audio playback 52 Archos (MASCODEC) devices cannot mix voice and audio playback
@@ -115,8 +116,15 @@ struct voicefile_header /* file format of our voice file */
115 * together with codecs, so we load clips each time they are accessed. 116 * together with codecs, so we load clips each time they are accessed.
116 * The Ondios have slow storage access and loading the entire voice file would 117 * The Ondios have slow storage access and loading the entire voice file would
117 * take several seconds, so we use the same mechanism. */ 118 * take several seconds, so we use the same mechanism. */
118#define TALK_PARTIAL_LOAD 119#define TALK_PROGRESSIVE_LOAD
119#define MAX_CLIP_BUFFER_SIZE 100000 /* 70+ clips should fit into 100k */ 120#if !defined(ONDIO_SERIES)
121/* 70+ clips should fit into 100k */
122#define MAX_CLIP_BUFFER_SIZE (100000)
123#endif
124#endif
125
126#ifndef MAX_CLIP_BUFFER_SIZE
127#define MAX_CLIP_BUFFER_SIZE (1<<30) /* 1GB should be enough for everybody */
120#endif 128#endif
121 129
122/* Multiple thumbnails can be loaded back-to-back in this buffer. */ 130/* Multiple thumbnails can be loaded back-to-back in this buffer. */
@@ -144,10 +152,9 @@ static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (
144static bool talk_initialized; /* true if talk_init has been called */ 152static bool talk_initialized; /* true if talk_init has been called */
145static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */ 153static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */
146static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */ 154static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */
147 /* size of the loaded voice file 155 /* size of the voice data in the voice file and the actually allocated buffer
148 * offsets smaller than this denote a clip from teh voice file, 156 * for it. voicebuf_size is always smaller or equal to voicefile_size */
149 * offsets larger than this denote a thumbnail clip */ 157static unsigned long voicefile_size, voicebuf_size;
150static unsigned long voicefile_size;
151 158
152struct queue_entry /* one entry of the internal queue */ 159struct queue_entry /* one entry of the internal queue */
153{ 160{
@@ -158,15 +165,11 @@ struct queue_entry /* one entry of the internal queue */
158 * into the bits of the above members (as a space saver). For small values 165 * into the bits of the above members (as a space saver). For small values
159 * the required extra code outweights this so it's not done here */ 166 * the required extra code outweights this so it's not done here */
160 enum offset_type { 167 enum offset_type {
161 TALK_OFFSET,
162 THUMB_OFFSET,
163#ifdef TALK_PARTIAL_LOAD
164 TALK_HANDLE, 168 TALK_HANDLE,
165#endif 169 THUMB_OFFSET,
166 } type; 170 } type;
167}; 171};
168 172
169#ifdef TALK_PARTIAL_LOAD
170static struct buflib_context clip_ctx; 173static struct buflib_context clip_ctx;
171 174
172struct clip_cache_metadata { 175struct clip_cache_metadata {
@@ -177,7 +180,6 @@ struct clip_cache_metadata {
177static int metadata_table_handle; 180static int metadata_table_handle;
178static unsigned max_clips; 181static unsigned max_clips;
179static int cache_hits, cache_misses; 182static int cache_hits, cache_misses;
180#endif
181 183
182static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ 184static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */
183static struct queue_entry silence, *last_clip; 185static struct queue_entry silence, *last_clip;
@@ -189,11 +191,8 @@ static int index_handle, talk_handle, thumb_handle;
189static int move_callback(int handle, void *current, void *new) 191static int move_callback(int handle, void *current, void *new)
190{ 192{
191 (void)handle; (void)current; (void)new; 193 (void)handle; (void)current; (void)new;
192#ifdef TALK_PARTIAL_LOAD 194 if (handle == talk_handle && !buflib_context_relocate(&clip_ctx, new))
193 if (handle == talk_handle) 195 return BUFLIB_CB_CANNOT_MOVE;
194 if (!buflib_context_relocate(&clip_ctx, new))
195 return BUFLIB_CB_CANNOT_MOVE;
196#endif
197 return BUFLIB_CB_OK; 196 return BUFLIB_CB_OK;
198} 197}
199 198
@@ -349,7 +348,24 @@ static int id2index(int id)
349 return index; 348 return index;
350} 349}
351 350
352#ifdef TALK_PARTIAL_LOAD 351static int index2id(int index)
352{
353 int id = index;
354
355 if (index >= voicefile.id2_max + voicefile.id1_max)
356 return -1;
357
358 if (index >= voicefile.id1_max)
359 { /* must be voice-only if it exceeds table 1 */
360 id -= voicefile.id1_max;
361 /* The first string comes after VOICEONLY_DELIMITER so we need to
362 add VOICEONLY_DELIMITER + 1 */
363 id += VOICEONLY_DELIMITER + 1;
364 }
365
366 return id;
367}
368
353static int free_oldest_clip(void) 369static int free_oldest_clip(void)
354{ 370{
355 unsigned i; 371 unsigned i;
@@ -374,7 +390,89 @@ static int free_oldest_clip(void)
374 390
375 return oldest; 391 return oldest;
376} 392}
393
394
395/* common code for load_initial_clips() and get_clip() */
396static void add_cache_entry(int clip_handle, int table_index, int id)
397{
398 unsigned i;
399 struct clip_cache_metadata *cc = buflib_get_data(&clip_ctx, metadata_table_handle);
400
401 if (table_index != -1)
402 {
403 /* explicit slot; use that */
404 cc = &cc[table_index];
405 if (cc->handle > 0) panicf("%s(): Slot already used", __func__);
406 }
407 else
408 { /* find an empty slot */
409 for(i = 0; cc[i].handle && i < max_clips; i++) ;
410 if (i == max_clips) /* no free slot in the cache table? */
411 i = free_oldest_clip();
412 cc = &cc[i];
413 }
414 cc->handle = clip_handle;
415 cc->tick = current_tick;
416 cc->voice_id = id;
417}
418
419static ssize_t read_clip_data(int fd, int index, int clip_handle)
420{
421 struct clip_entry* clipbuf;
422 size_t clipsize;
423 ssize_t ret;
424
425 if (fd < 0)
426 {
427 buflib_free(&clip_ctx, clip_handle);
428 return -1; /* open error */
429 }
430
431 clipbuf = core_get_data(index_handle);
432 /* this must not be called with LOADED_MASK set in clipsize */
433 clipsize = clipbuf[index].size;
434 lseek(fd, clipbuf[index].offset, SEEK_SET);
435 ret = read_to_handle_ex(fd, &clip_ctx, clip_handle, 0, clipsize);
436
437 if (ret < 0 || clipsize != (size_t)ret)
438 {
439 buflib_free(&clip_ctx, clip_handle);
440 return -2; /* read error */
441 }
442
443 clipbuf = core_get_data(index_handle);
444 clipbuf[index].size |= LOADED_MASK; /* mark as loaded */
445
446 return ret;
447}
448
449static void load_initial_clips(int fd)
450{
451 (void) fd;
452#ifndef TALK_PROGRESSIVE_LOAD
453 unsigned index, i;
454 unsigned num_clips = voicefile.id1_max + voicefile.id2_max;
455
456 for(index = i = 0; index < num_clips && i < max_clips; index++)
457 {
458 int handle;
459 struct clip_entry* clipbuf = core_get_data(index_handle);
460 size_t clipsize = clipbuf[index].size;
461 if (clipsize == 0) /* clip not included in voicefile */
462 continue;
463
464 handle = buflib_alloc(&clip_ctx, clipsize);
465 if (handle < 0)
466 break;
467
468 if (read_clip_data(fd, index, handle) < 0)
469 break;
470
471 add_cache_entry(handle, i++, index2id(index));
472 }
377#endif 473#endif
474}
475
378/* fetch a clip from the voice file */ 476/* fetch a clip from the voice file */
379static int get_clip(long id, struct queue_entry *q) 477static int get_clip(long id, struct queue_entry *q)
380{ 478{
@@ -393,16 +491,9 @@ static int get_clip(long id, struct queue_entry *q)
393 if (clipsize == 0) /* clip not included in voicefile */ 491 if (clipsize == 0) /* clip not included in voicefile */
394 return -1; 492 return -1;
395 493
396#ifndef TALK_PARTIAL_LOAD
397 retval = clipbuf[index].offset;
398 type = TALK_OFFSET;
399
400#else
401 if (!(clipsize & LOADED_MASK)) 494 if (!(clipsize & LOADED_MASK))
402 { /* clip needs loading */ 495 { /* clip needs loading */
403 struct clip_cache_metadata *cc;
404 int fd, handle, oldest = -1; 496 int fd, handle, oldest = -1;
405 unsigned i;
406 ssize_t ret; 497 ssize_t ret;
407 cache_misses++; 498 cache_misses++;
408 /* free clips from cache until this one succeeds to allocate */ 499 /* free clips from cache until this one succeeds to allocate */
@@ -411,40 +502,12 @@ static int get_clip(long id, struct queue_entry *q)
411 /* handle should now hold a valid alloc. Load from disk 502 /* handle should now hold a valid alloc. Load from disk
412 * and insert into cache */ 503 * and insert into cache */
413 fd = open_voicefile(); 504 fd = open_voicefile();
414 if (fd < 0) 505 ret = read_clip_data(fd, index, handle);
415 {
416 buflib_free(&clip_ctx, handle);
417 return -1; /* open error */
418 }
419 clipbuf = core_get_data(index_handle);
420 lseek(fd, clipbuf[index].offset, SEEK_SET);
421 ret = read_to_handle_ex(fd, &clip_ctx, handle, 0, clipsize);
422 close(fd); 506 close(fd);
423 507 if (ret < 0)
424 if (ret < 0 || clipsize != (size_t)ret) 508 return ret;
425 {
426 buflib_free(&clip_ctx, handle);
427 return -1; /* read error */
428 }
429
430 clipbuf = core_get_data(index_handle);
431 clipbuf[index].size |= LOADED_MASK; /* mark as loaded */
432
433 /* finally insert into metadata table */ 509 /* finally insert into metadata table */
434 cc = buflib_get_data(&clip_ctx, metadata_table_handle); 510 add_cache_entry(handle, oldest, id);
435 if (oldest != -1)
436 /* went through the cache in the above loop already, re-use the slot */
437 cc = &cc[oldest];
438 else
439 { /* find an empty slot */
440 for(i = 0; cc[i].handle && i < max_clips; i++) ;
441 if (i == max_clips) /* no free slot in the cache table? */
442 i = free_oldest_clip();
443 cc = &cc[i];
444 }
445 cc->handle = handle;
446 cc->tick = current_tick;
447 cc->voice_id = id;
448 retval = handle; 511 retval = handle;
449 } 512 }
450 else 513 else
@@ -459,7 +522,6 @@ static int get_clip(long id, struct queue_entry *q)
459 retval = cc[i].handle; 522 retval = cc[i].handle;
460 } 523 }
461 type = TALK_HANDLE; 524 type = TALK_HANDLE;
462#endif /* TALK_PARTIAL_LOAD */
463 525
464 q->offset = retval; 526 q->offset = retval;
465 q->length = clipsize; 527 q->length = clipsize;
@@ -483,15 +545,6 @@ static bool load_index_table(int fd, const struct voicefile_header *hdr)
483 545
484 ret = read_to_handle(fd, index_handle, 0, alloc_size); 546 ret = read_to_handle(fd, index_handle, 0, alloc_size);
485 547
486#ifndef TALK_PARTIAL_LOAD
487 int clips_offset, num_clips;
488 /* adjust the offsets of the clips, they are relative to the file
489 * TALK_PARTUAL_LOAD needs the file offset instead as it loads
490 * the clips later */
491 clips_offset = hdr->table;
492 num_clips = hdr->id1_max + hdr->id2_max;
493 clips_offset += num_clips * sizeof(struct clip_entry); /* skip index */
494#endif
495 if (ret == alloc_size) 548 if (ret == alloc_size)
496 { 549 {
497 buf = core_get_data(index_handle); 550 buf = core_get_data(index_handle);
@@ -501,9 +554,6 @@ static bool load_index_table(int fd, const struct voicefile_header *hdr)
501 /* doesn't yield() */ 554 /* doesn't yield() */
502 structec_convert(&buf[i], "ll", 1, true); 555 structec_convert(&buf[i], "ll", 1, true);
503#endif 556#endif
504#ifndef TALK_PARTIAL_LOAD
505 buf[i].offset -= clips_offset;
506#endif
507 } 557 }
508 } 558 }
509 else 559 else
@@ -525,27 +575,28 @@ static bool load_header(int fd, struct voicefile_header *hdr)
525 return true; 575 return true;
526} 576}
527 577
528#ifndef TALK_PARTIAL_LOAD 578static bool create_clip_buffer(size_t max_size)
529static bool load_data(int fd, ssize_t size_to_read)
530{ 579{
531 ssize_t ret; 580 size_t alloc_size;
532 581 /* just allocate, populate on an as-needed basis later */
533 if (size_to_read < 0) 582 talk_handle = core_alloc_ex("voice data", max_size, &talk_ops);
534 return false;
535
536 talk_handle = core_alloc_ex("voice data", size_to_read, &talk_ops);
537 if (talk_handle < 0) 583 if (talk_handle < 0)
538 return false; 584 goto alloc_err;
539 585
540 ret = read_to_handle(fd, talk_handle, 0, size_to_read); 586 buflib_init(&clip_ctx, core_get_data(talk_handle), max_size);
541 587
542 if (ret != size_to_read) 588 /* the first alloc is the clip metadata table */
543 talk_handle = core_free(talk_handle); 589 alloc_size = max_clips * sizeof(struct clip_cache_metadata);
590 metadata_table_handle = buflib_alloc(&clip_ctx, alloc_size);
591 memset(buflib_get_data(&clip_ctx, metadata_table_handle), 0, alloc_size);
544 592
545 return ret == size_to_read; 593 return true;
546}
547#endif
548 594
595alloc_err:
596 index_handle = core_free(index_handle);
597 return false;
598}
599
549static bool alloc_thumbnail_buf(void) 600static bool alloc_thumbnail_buf(void)
550{ 601{
551 int handle; 602 int handle;
@@ -565,7 +616,8 @@ static bool alloc_thumbnail_buf(void)
565#else 616#else
566 /* on HWCODEC, just use the rest of the remaining buffer, 617 /* on HWCODEC, just use the rest of the remaining buffer,
567 * normal playback cannot happen anyway */ 618 * normal playback cannot happen anyway */
568 handle = core_alloc_maximum("voice thumb", &size, &thumb_ops); 619 size = audio_buffer_available();
620 handle = core_alloc_ex("voice thumb", size, &thumb_ops);
569#endif 621#endif
570 thumb_handle = handle; 622 thumb_handle = handle;
571 size_for_thumbnail = (handle > 0) ? size : 0; 623 size_for_thumbnail = (handle > 0) ? size : 0;
@@ -597,47 +649,46 @@ static bool load_voicefile_index(int fd)
597 return false; 649 return false;
598} 650}
599 651
600static bool load_voicefile_data(int fd, size_t max_size) 652/* this function caps the voicefile buffer and allocates it. It can
653 * be called after talk_init(), e.g. when the voice was temporarily disabled.
654 * The buffer size has to be capped again each time because the available
655 * audio buffer changes over time */
656static bool load_voicefile_data(int fd)
601{ 657{
602#ifdef TALK_PARTIAL_LOAD 658 voicebuf_size = voicefile_size;
603 (void)fd; 659 /* cap to the max. number of clips or the size of the available audio
604 size_t alloc_size; 660 * buffer which we grab. We leave some to the rest of the system.
661 * While that reduces our buffer size it improves the chance that
662 * other allocs succeed without disabling voice which would require
663 * reloading the voice from disk (as we do not shrink our buffer when
664 * other code attempts new allocs these would fail) */
665 ssize_t cap = MIN(MAX_CLIP_BUFFER_SIZE, audio_buffer_available() - (64<<10));
666 if (UNLIKELY(cap < 0))
667 {
668 logf("Not enough memory for voice. Disabling...\n");
669 return false;
670 }
671 else if (voicebuf_size > (size_t)cap)
672 voicebuf_size = cap;
673
605 /* just allocate, populate on an as-needed basis later 674 /* just allocate, populate on an as-needed basis later
606 * re-create the clip buffer to ensure clip_ctx is up-to-date */ 675 * re-create the clip buffer to ensure clip_ctx is up-to-date */
607 if (talk_handle > 0) 676 if (talk_handle > 0)
608 talk_handle = core_free(talk_handle); 677 talk_handle = core_free(talk_handle);
609 talk_handle = core_alloc_ex("voice data", max_size, &talk_ops); 678 if (!create_clip_buffer(voicebuf_size))
610 if (talk_handle < 0) 679 return false;
611 goto load_err_free;
612
613 buflib_init(&clip_ctx, core_get_data(talk_handle), max_size);
614
615 alloc_size = max_clips * sizeof(struct clip_cache_metadata);
616 /* the first alloc is the clip metadata table */
617 metadata_table_handle = buflib_alloc(&clip_ctx, alloc_size);
618 memset(buflib_get_data(&clip_ctx, metadata_table_handle), 0, alloc_size);
619
620#else
621 /* load the entire file into memory */
622 if (!load_data(fd, max_size))
623 goto load_err_free;
624#endif
625 680
626 /* make sure to have the silence clip, if available 681 load_initial_clips(fd);
627 * return value can be cached globally even for TALK_PARTIAL_LOAD because 682 /* make sure to have the silence clip, if available return value can
628 * the VOICE_PAUSE clip is specially handled */ 683 * be cached globally even for TALK_PROGRESSIVE_LOAD because the
629 if (get_clip(VOICE_PAUSE, &silence)) 684 * VOICE_PAUSE clip is specially handled */
630 goto load_err_free; 685 get_clip(VOICE_PAUSE, &silence);
631 686
632 /* not an error if this fails here, might try again when the 687 /* not an error if this fails here, might try again when the
633 * actual thumbnails are attempted to be played back */ 688 * actual thumbnails are attempted to be played back */
634 alloc_thumbnail_buf(); 689 alloc_thumbnail_buf();
635 690
636 return true; 691 return true;
637
638load_err_free:
639 index_handle = core_free(index_handle);
640 return false;
641} 692}
642 693
643/* most, if not all, clips should be well below 32k (largest in english.lang is 694/* most, if not all, clips should be well below 32k (largest in english.lang is
@@ -659,11 +710,8 @@ static void* commit_transfer(struct queue_entry *qe, size_t *size)
659 sent = MIN((size_t)sent, sizeof(commit_buffer)); 710 sent = MIN((size_t)sent, sizeof(commit_buffer));
660 switch (qe->type) 711 switch (qe->type)
661 { 712 {
662 case TALK_OFFSET: buf = core_get_data(talk_handle) + offset; break;
663 case THUMB_OFFSET: buf = core_get_data(thumb_handle) + offset; break;
664#ifdef TALK_PARTIAL_LOAD
665 case TALK_HANDLE: buf = buflib_get_data(&clip_ctx, offset); break; 713 case TALK_HANDLE: buf = buflib_get_data(&clip_ctx, offset); break;
666#endif 714 case THUMB_OFFSET: buf = core_get_data(thumb_handle) + offset; break;
667 } 715 }
668 /* adjust buffer position to what has been played already */ 716 /* adjust buffer position to what has been played already */
669 buf += (qe->length - qe->remaining); 717 buf += (qe->length - qe->remaining);
@@ -757,11 +805,8 @@ void talk_force_shutup(void)
757 offset = queue[queue_read].offset; 805 offset = queue[queue_read].offset;
758 switch (queue[queue_read].type) 806 switch (queue[queue_read].type)
759 { 807 {
760 case TALK_OFFSET: end = core_get_data(talk_handle) + offset; break;
761 case THUMB_OFFSET: end = core_get_data(thumb_handle) + offset; break; 808 case THUMB_OFFSET: end = core_get_data(thumb_handle) + offset; break;
762#ifdef TALK_PARTIAL_LOAD
763 case TALK_HANDLE: end = buflib_get_data(&clip_ctx, offset); break; 809 case TALK_HANDLE: end = buflib_get_data(&clip_ctx, offset); break;
764#endif
765 default: end = NULL; /* shut up gcc */ 810 default: end = NULL; /* shut up gcc */
766 } 811 }
767 len = queue[queue_read].length; 812 len = queue[queue_read].length;
@@ -922,64 +967,34 @@ void talk_init(void)
922 if (!load_voicefile_index(filehandle)) 967 if (!load_voicefile_index(filehandle))
923 goto out; 968 goto out;
924 969
925#ifdef TALK_PARTIAL_LOAD 970 /* Now determine the maximum buffer size needed for the voicefile.
926 /* TALK_PARTIAL_LOAD loads the actual clip data later, and not all 971 * The below pretends the entire voicefile would be loaded. The buffer
927 * at once */ 972 * size is eventually capped later on in load_voicefile_data() */
928 unsigned num_clips = voicefile.id1_max + voicefile.id2_max; 973 int num_clips = voicefile.id1_max + voicefile.id2_max;
974 int non_empty = num_clips;
975 int total_size = 0, avg_size;
929 struct clip_entry *clips = core_get_data(index_handle); 976 struct clip_entry *clips = core_get_data(index_handle);
930 int avg_size = clips[0].size; 977 /* check for the average clip size to estimate the maximum number of
931 int real_clips = 1; /* shut up gcc */ 978 * clips the buffer can hold */
932 /* check for the smallest clip size to estimate the max. number of clips 979 for (int i = 0; i<num_clips; i++)
933 * the buffer has to hold */
934 for(unsigned i=1; i<num_clips; i++)
935 { 980 {
936 if (clips[i].size) 981 if (clips[i].size)
937 { /* don't consider empty clips, they are not stored anyway */ 982 total_size += ALIGN_UP(clips[i].size, sizeof(void *));
938 real_clips += 1; 983 else
939 avg_size += clips[i].size; 984 non_empty -= 1;
940 }
941 } 985 }
942 avg_size /= real_clips; 986 avg_size = total_size / non_empty;
943 987 max_clips = MIN((int)(MAX_CLIP_BUFFER_SIZE/avg_size) + 1, non_empty);
944 max_clips = MIN((int)(MAX_CLIP_BUFFER_SIZE/avg_size) + 1, real_clips); 988 voicefile_size = total_size;
945 voicefile_size = MAX_CLIP_BUFFER_SIZE; 989 /* additionally to the clips we need a table to record the age of the clips
946 /* additionally to the clip we need a table to record the age of the clips
947 * so that, when memory is tight, only the most recently used ones are kept */ 990 * so that, when memory is tight, only the most recently used ones are kept */
948 voicefile_size += sizeof(struct clip_cache_metadata) * max_clips; 991 voicefile_size += sizeof(struct clip_cache_metadata) * max_clips;
949 /* compensate a bit for buflib per-alloc overhead */ 992 /* compensate a bit for buflib alloc overhead. */
950 voicefile_size += BUFLIB_ALLOC_OVERHEAD * max_clips; 993 voicefile_size += BUFLIB_ALLOC_OVERHEAD * max_clips + 64;
951 /* cap to the max. number of clips or the size of the available audio
952 * buffer which we grab. We leave some to the rest of the system.
953 * While that reduces our buffer size it improves the chance that
954 * other allocs succeed without disabling voice which would require
955 * reloading the voice from disk (as we do not shrink our buffer when
956 * other code attempts new allocs these would fail) */
957 ssize_t cap = MIN(MAX_CLIP_BUFFER_SIZE, audio_buffer_available() - (64<<10));
958 if (UNLIKELY(cap < 0))
959 {
960 logf("Not enough memory for voice. Disabling...\n");
961 if (index_handle > 0)
962 index_handle = core_free(index_handle);
963 voicefile_size = 0;
964 goto out;
965 }
966 else if (voicefile_size > (size_t)cap)
967 voicefile_size = cap;
968
969#else
970 size_t clips_size;
971 clips_size = (voicefile.id1_max+voicefile.id2_max) * sizeof(struct clip_entry);
972 voicefile_size = filesize(filehandle) - voicefile.table - clips_size;
973 /* load the compressed clip data into memory */
974 if (!load_voicefile_data(filehandle, voicefile_size))
975 {
976 voicefile_size = 0;
977 goto out;
978 }
979#endif
980
981 has_voicefile = true; 994 has_voicefile = true;
982 995
996 load_voicefile_data(filehandle);
997
983#if CONFIG_CODEC == SWCODEC 998#if CONFIG_CODEC == SWCODEC
984 /* Initialize the actual voice clip playback engine as well */ 999 /* Initialize the actual voice clip playback engine as well */
985 if (talk_voice_required()) 1000 if (talk_voice_required())
@@ -1022,7 +1037,7 @@ int talk_id(int32_t id, bool enqueue)
1022 int fd = open_voicefile(); 1037 int fd = open_voicefile();
1023 if (fd < 0 1038 if (fd < 0
1024 || !load_voicefile_index(fd) 1039 || !load_voicefile_index(fd)
1025 || !load_voicefile_data(fd, voicefile_size)) 1040 || !load_voicefile_data(fd))
1026 return -1; 1041 return -1;
1027 } 1042 }
1028 1043
@@ -1561,9 +1576,7 @@ bool talk_get_debug_data(struct talk_debug_data *data)
1561 p_lang = (char *)global_settings.lang_file; 1576 p_lang = (char *)global_settings.lang_file;
1562 1577
1563 struct clip_entry *clips = core_get_data(index_handle); 1578 struct clip_entry *clips = core_get_data(index_handle);
1564#ifdef TALK_PARTIAL_LOAD
1565 int cached = 0; 1579 int cached = 0;
1566#endif
1567 int real_clips = 0; 1580 int real_clips = 0;
1568 1581
1569 strlcpy(data->voicefile, p_lang, sizeof(data->voicefile)); 1582 strlcpy(data->voicefile, p_lang, sizeof(data->voicefile));
@@ -1580,27 +1593,20 @@ bool talk_get_debug_data(struct talk_debug_data *data)
1580 if (size > data->max_clipsize) 1593 if (size > data->max_clipsize)
1581 data->max_clipsize = size; 1594 data->max_clipsize = size;
1582 data->avg_clipsize += size; 1595 data->avg_clipsize += size;
1583#ifdef TALK_PARTIAL_LOAD
1584 if (clips[i].size & LOADED_MASK) 1596 if (clips[i].size & LOADED_MASK)
1585 cached++; 1597 cached++;
1586#endif
1587 } 1598 }
1588 data->avg_clipsize /= real_clips; 1599 data->avg_clipsize /= real_clips;
1589 data->num_empty_clips = data->num_clips - real_clips; 1600 data->num_empty_clips = data->num_clips - real_clips;
1590 data->memory_allocated = sizeof(commit_buffer) + sizeof(voicefile) 1601 data->memory_allocated = sizeof(commit_buffer) + sizeof(voicefile)
1591 + data->num_clips * sizeof(struct clip_entry) 1602 + data->num_clips * sizeof(struct clip_entry)
1592 + voicefile_size + size_for_thumbnail; 1603 + voicebuf_size + size_for_thumbnail;
1593 data->memory_used = data->memory_allocated - size_for_thumbnail + thumbnail_buf_used; 1604 data->memory_used = data->memory_allocated - size_for_thumbnail + thumbnail_buf_used;
1594#ifdef TALK_PARTIAL_LOAD
1595 if (talk_handle > 0) 1605 if (talk_handle > 0)
1596 data->memory_used -= buflib_available(&clip_ctx); 1606 data->memory_used -= buflib_available(&clip_ctx);
1597 data->cached_clips = cached; 1607 data->cached_clips = cached;
1598 data->cache_hits = cache_hits; 1608 data->cache_hits = cache_hits;
1599 data->cache_misses = cache_misses; 1609 data->cache_misses = cache_misses;
1600#else
1601 data->cached_clips = real_clips;
1602 data->cache_hits = data->cache_misses = -1;
1603#endif
1604 1610
1605 return true; 1611 return true;
1606} 1612}