diff options
author | Thomas Martitz <kugel@rockbox.org> | 2014-02-02 14:48:32 +0100 |
---|---|---|
committer | Thomas Martitz <kugel@rockbox.org> | 2014-02-02 19:40:39 +0100 |
commit | 03f373c9cd370e77766a511c574b3cf7bf8d9226 (patch) | |
tree | 0a460efd9f33e29c1480df3652fab35aa0e4fa7c /apps/talk.c | |
parent | c46f9be10a9b3f34f804583d8cdc980ab62c58bd (diff) | |
download | rockbox-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.c | 372 |
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 ( | |||
144 | static bool talk_initialized; /* true if talk_init has been called */ | 152 | static bool talk_initialized; /* true if talk_init has been called */ |
145 | static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */ | 153 | static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */ |
146 | static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */ | 154 | static 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 */ | 157 | static unsigned long voicefile_size, voicebuf_size; |
150 | static unsigned long voicefile_size; | ||
151 | 158 | ||
152 | struct queue_entry /* one entry of the internal queue */ | 159 | struct 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 | ||
170 | static struct buflib_context clip_ctx; | 173 | static struct buflib_context clip_ctx; |
171 | 174 | ||
172 | struct clip_cache_metadata { | 175 | struct clip_cache_metadata { |
@@ -177,7 +180,6 @@ struct clip_cache_metadata { | |||
177 | static int metadata_table_handle; | 180 | static int metadata_table_handle; |
178 | static unsigned max_clips; | 181 | static unsigned max_clips; |
179 | static int cache_hits, cache_misses; | 182 | static int cache_hits, cache_misses; |
180 | #endif | ||
181 | 183 | ||
182 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | 184 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ |
183 | static struct queue_entry silence, *last_clip; | 185 | static struct queue_entry silence, *last_clip; |
@@ -189,11 +191,8 @@ static int index_handle, talk_handle, thumb_handle; | |||
189 | static int move_callback(int handle, void *current, void *new) | 191 | static 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 | 351 | static 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 | |||
353 | static int free_oldest_clip(void) | 369 | static 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() */ | ||
396 | static 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 | |||
419 | static 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 | |||
449 | static 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 */ |
379 | static int get_clip(long id, struct queue_entry *q) | 477 | static 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 | 578 | static bool create_clip_buffer(size_t max_size) |
529 | static 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 | ||
595 | alloc_err: | ||
596 | index_handle = core_free(index_handle); | ||
597 | return false; | ||
598 | } | ||
599 | |||
549 | static bool alloc_thumbnail_buf(void) | 600 | static 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 | ||
600 | static 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 */ | ||
656 | static 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 | |||
638 | load_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 | } |