diff options
Diffstat (limited to 'apps')
-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 | } |