summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2013-05-30 11:24:16 +0200
committerThomas Martitz <kugel@rockbox.org>2013-12-23 12:17:38 +0100
commit22e802e80048defd401462e062afcb10093ac793 (patch)
tree09d24f7eb2a3b18e6563e838398b2715394f7c4c
parent64b9e1fa7b645daa36ca0018dc168d4f871fd538 (diff)
downloadrockbox-22e802e80048defd401462e062afcb10093ac793.tar.gz
rockbox-22e802e80048defd401462e062afcb10093ac793.zip
playback,talk: Share audiobuffer via core_alloc_maximum().
This fixes the radioart crash that was the result of buffering.c working on a freed buffer at the same time as buflib (radioart uses buffering.c for the images). With this change the buffer is owned by buflib exclusively so this cannot happen. As a result, audio_get_buffer() doesn't exist anymore. Callers should call core_alloc_maximum() directly. This buffer needs to be protected as usual against movement if necessary (previously it was not protected at all which cased the radioart crash), To get most of it they can adjust the willingness of the talk engine to give its buffer away (at the expense of disabling voice interface) with the new talk_buffer_set_policy() function. Change-Id: I52123012208d04967876a304451d634e2bef3a33
-rw-r--r--apps/gui/skin_engine/skin_render.c2
-rw-r--r--apps/mpeg.c72
-rw-r--r--apps/playback.c176
-rw-r--r--apps/playback.h6
-rwxr-xr-xapps/playlist.c87
-rw-r--r--apps/plugin.c19
-rw-r--r--apps/plugin.h1
-rw-r--r--apps/radio/radioart.c40
-rw-r--r--apps/recorder/pcm_record.c20
-rw-r--r--apps/recorder/recording.c10
-rw-r--r--apps/talk.c761
-rw-r--r--apps/talk.h21
-rw-r--r--firmware/export/audio.h4
-rw-r--r--firmware/target/arm/pp/usb-fw-pp502x.c15
-rw-r--r--firmware/usbstack/usb_storage.c17
15 files changed, 670 insertions, 581 deletions
diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c
index 28483cbc49..0d36711e91 100644
--- a/apps/gui/skin_engine/skin_render.c
+++ b/apps/gui/skin_engine/skin_render.c
@@ -280,13 +280,11 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
280 if (do_refresh && aa) 280 if (do_refresh && aa)
281 { 281 {
282 int handle = playback_current_aa_hid(data->playback_aa_slot); 282 int handle = playback_current_aa_hid(data->playback_aa_slot);
283#if 0 /* FIXME: FS#12797*/
284 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF)) 283 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
285 { 284 {
286 struct dim dim = {aa->width, aa->height}; 285 struct dim dim = {aa->width, aa->height};
287 handle = radio_get_art_hid(&dim); 286 handle = radio_get_art_hid(&dim);
288 } 287 }
289#endif
290 aa->draw_handle = handle; 288 aa->draw_handle = handle;
291 } 289 }
292 break; 290 break;
diff --git a/apps/mpeg.c b/apps/mpeg.c
index 2783a24085..5fd1bfdb82 100644
--- a/apps/mpeg.c
+++ b/apps/mpeg.c
@@ -527,7 +527,13 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
527 ssize_t size = (ssize_t)old_size - wanted_size; 527 ssize_t size = (ssize_t)old_size - wanted_size;
528 /* keep at least 256K for the buffering */ 528 /* keep at least 256K for the buffering */
529 if ((size - extradata_size) < AUDIO_BUFFER_RESERVE) 529 if ((size - extradata_size) < AUDIO_BUFFER_RESERVE)
530 return BUFLIB_CB_CANNOT_SHRINK; 530 {
531 /* check if buflib needs the memory really hard. if yes we give
532 * up playback for now, otherwise refuse to shrink to keep at least
533 * 256K for the buffering */
534 if ((hints & BUFLIB_SHRINK_POS_MASK) != BUFLIB_SHRINK_POS_MASK)
535 return BUFLIB_CB_CANNOT_SHRINK;
536 }
531 /* TODO: Do it without stopping playback, if possible */ 537 /* TODO: Do it without stopping playback, if possible */
532 bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY; 538 bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY;
533 long offset = audio_current_track()->offset; 539 long offset = audio_current_track()->offset;
@@ -539,7 +545,6 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
539 } 545 }
540 else 546 else
541 audio_stop(); 547 audio_stop();
542 talk_buffer_steal(); /* we obtain control over the buffer */
543 548
544 switch (hints & BUFLIB_SHRINK_POS_MASK) 549 switch (hints & BUFLIB_SHRINK_POS_MASK)
545 { 550 {
@@ -551,6 +556,12 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
551 core_shrink(handle, start + wanted_size, size); 556 core_shrink(handle, start + wanted_size, size);
552 audio_reset_buffer_noalloc(start + wanted_size, size); 557 audio_reset_buffer_noalloc(start + wanted_size, size);
553 break; 558 break;
559 case BUFLIB_SHRINK_POS_MASK:
560 audiobuf_handle = core_free(audiobuf_handle);
561 mpeg_audiobuf = NULL;
562 talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
563 playing = false;
564 break;
554 } 565 }
555 if (playing) 566 if (playing)
556 { /* safe to call even from the audio thread (due to queue_post()) */ 567 { /* safe to call even from the audio thread (due to queue_post()) */
@@ -565,45 +576,6 @@ static struct buflib_callbacks ops = {
565 .shrink_callback = shrink_callback, 576 .shrink_callback = shrink_callback,
566}; 577};
567 578
568static size_t audio_talkbuf_init(char *bufstart)
569{
570 size_t ret = talkbuf_init(bufstart);
571 if (ret > (size_t)audiobuflen) /* does the voice even fit? */
572 {
573 talk_buffer_steal();
574 return 0;
575 }
576 return ret;
577}
578
579unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
580{
581 (void)talk_buf; /* always grab the voice buffer for now */
582
583 if (audio_is_initialized)
584 audio_hard_stop();
585
586 if (!buffer_size) /* special case for talk_init() */
587 return NULL;
588
589 if (!audiobuf_handle)
590 {
591 size_t bufsize;
592 /* audio_hard_stop() frees audiobuf, so re-aquire */
593 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
594 audiobuflen = bufsize;
595 if (buffer_size)
596 *buffer_size = audiobuflen;
597 }
598 mpeg_audiobuf = core_get_data(audiobuf_handle);
599 /* tell talk about the new buffer, don't re-enable just yet because the
600 * buffer is stolen */
601 audio_talkbuf_init(mpeg_audiobuf);
602
603 return mpeg_audiobuf;
604}
605
606
607#ifndef SIMULATOR 579#ifndef SIMULATOR
608/* Send callback events to notify about removing old tracks. */ 580/* Send callback events to notify about removing old tracks. */
609static void generate_unbuffer_events(void) 581static void generate_unbuffer_events(void)
@@ -2755,7 +2727,6 @@ size_t audio_buffer_available(void)
2755 2727
2756static void audio_reset_buffer_noalloc(void* buf, size_t bufsize) 2728static void audio_reset_buffer_noalloc(void* buf, size_t bufsize)
2757{ 2729{
2758 talk_buffer_steal(); /* will use the mp3 buffer */
2759 mpeg_audiobuf = buf; 2730 mpeg_audiobuf = buf;
2760 audiobuflen = bufsize; 2731 audiobuflen = bufsize;
2761 if (global_settings.cuesheet) 2732 if (global_settings.cuesheet)
@@ -2764,16 +2735,20 @@ static void audio_reset_buffer_noalloc(void* buf, size_t bufsize)
2764 mpeg_audiobuf = SKIPBYTES(mpeg_audiobuf, sizeof(struct cuesheet)); 2735 mpeg_audiobuf = SKIPBYTES(mpeg_audiobuf, sizeof(struct cuesheet));
2765 audiobuflen -= sizeof(struct cuesheet); 2736 audiobuflen -= sizeof(struct cuesheet);
2766 } 2737 }
2767 audio_talkbuf_init(mpeg_audiobuf);
2768} 2738}
2769 2739
2770static void audio_reset_buffer(void) 2740static void audio_reset_buffer(void)
2771{ 2741{
2772 size_t bufsize = audiobuflen; 2742 size_t bufsize = audiobuflen;
2773 2743
2774 /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */ 2744 /* alloc buffer if it's was never allocated or freed by audio_hard_stop()
2745 * because voice cannot be played during audio playback make
2746 * talk.c give up all buffers and disable itself */
2775 if (!audiobuf_handle) 2747 if (!audiobuf_handle)
2748 {
2749 talk_buffer_set_policy(TALK_BUFFER_LOOSE);
2776 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); 2750 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
2751 }
2777 2752
2778 audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize); 2753 audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize);
2779} 2754}
@@ -2818,6 +2793,8 @@ void audio_play(long offset)
2818 2793
2819void audio_stop(void) 2794void audio_stop(void)
2820{ 2795{
2796 if (audiobuf_handle <= 0)
2797 return; /* nothing to do, must be hard-stopped already */
2821#ifndef SIMULATOR 2798#ifndef SIMULATOR
2822 mpeg_stop_done = false; 2799 mpeg_stop_done = false;
2823 queue_post(&mpeg_queue, MPEG_STOP, 0); 2800 queue_post(&mpeg_queue, MPEG_STOP, 0);
@@ -2828,8 +2805,6 @@ void audio_stop(void)
2828 is_playing = false; 2805 is_playing = false;
2829 playing = false; 2806 playing = false;
2830#endif /* SIMULATOR */ 2807#endif /* SIMULATOR */
2831 /* give voice our entire buffer */
2832 audio_talkbuf_init(mpeg_audiobuf);
2833} 2808}
2834 2809
2835/* dummy */ 2810/* dummy */
@@ -2840,13 +2815,12 @@ void audio_stop_recording(void)
2840 2815
2841void audio_hard_stop(void) 2816void audio_hard_stop(void)
2842{ 2817{
2843 audio_stop();
2844 /* tell voice we obtain the buffer before freeing */
2845 talk_buffer_steal();
2846 if (audiobuf_handle > 0) 2818 if (audiobuf_handle > 0)
2847 { 2819 {
2820 audio_stop();
2848 audiobuf_handle = core_free(audiobuf_handle); 2821 audiobuf_handle = core_free(audiobuf_handle);
2849 mpeg_audiobuf = NULL; 2822 mpeg_audiobuf = NULL;
2823 talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
2850 } 2824 }
2851} 2825}
2852 2826
diff --git a/apps/playback.c b/apps/playback.c
index b240e95acd..5e234beb36 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -110,7 +110,6 @@ static enum audio_buffer_state
110{ 110{
111 AUDIOBUF_STATE_TRASHED = -1, /* trashed; must be reset */ 111 AUDIOBUF_STATE_TRASHED = -1, /* trashed; must be reset */
112 AUDIOBUF_STATE_INITIALIZED = 0, /* voice+audio OR audio-only */ 112 AUDIOBUF_STATE_INITIALIZED = 0, /* voice+audio OR audio-only */
113 AUDIOBUF_STATE_VOICED_ONLY = 1, /* voice-only */
114} buffer_state = AUDIOBUF_STATE_TRASHED; /* (A,O) */ 113} buffer_state = AUDIOBUF_STATE_TRASHED; /* (A,O) */
115 114
116/** Main state control **/ 115/** Main state control **/
@@ -729,60 +728,42 @@ size_t audio_buffer_available(void)
729/* Set up the audio buffer for playback 728/* Set up the audio buffer for playback
730 * filebuflen must be pre-initialized with the maximum size */ 729 * filebuflen must be pre-initialized with the maximum size */
731static void audio_reset_buffer_noalloc( 730static void audio_reset_buffer_noalloc(
732 void *filebuf, enum audio_buffer_state state) 731 void *filebuf)
733{ 732{
734 /* 733 /*
735 * Layout audio buffer as follows: 734 * Layout audio buffer as follows:
736 * [[|TALK]|SCRATCH|BUFFERING|PCM] 735 * [|SCRATCH|BUFFERING|PCM]
737 */ 736 */
738
739 /* see audio_get_recording_buffer if this is modified */
740 logf("%s()", __func__); 737 logf("%s()", __func__);
741 738
742 /* If the setup of anything allocated before the file buffer is 739 /* If the setup of anything allocated before the file buffer is
743 changed, do check the adjustments after the buffer_alloc call 740 changed, do check the adjustments after the buffer_alloc call
744 as it will likely be affected and need sliding over */ 741 as it will likely be affected and need sliding over */
745
746 /* Initially set up file buffer as all space available */
747 size_t allocsize; 742 size_t allocsize;
743 /* Subtract whatever the pcm buffer says it used plus the guard
744 buffer */
745 allocsize = pcmbuf_init(filebuf + filebuflen);
748 746
749 /* Subtract whatever voice needs (we're called when promoting 747 /* Make sure filebuflen is a pointer sized multiple after
750 the state only) */ 748 adjustment */
751 allocsize = talkbuf_init(filebuf);
752 allocsize = ALIGN_UP(allocsize, sizeof (intptr_t)); 749 allocsize = ALIGN_UP(allocsize, sizeof (intptr_t));
753 if (allocsize > filebuflen) 750 if (allocsize > filebuflen)
754 goto bufpanic; 751 goto bufpanic;
755 752
756 filebuf += allocsize;
757 filebuflen -= allocsize; 753 filebuflen -= allocsize;
758 754
759 if (state == AUDIOBUF_STATE_INITIALIZED) 755 /* Scratch memory */
760 { 756 allocsize = scratch_mem_size();
761 /* Subtract whatever the pcm buffer says it used plus the guard 757 if (allocsize > filebuflen)
762 buffer */ 758 goto bufpanic;
763 allocsize = pcmbuf_init(filebuf + filebuflen);
764
765 /* Make sure filebuflen is a pointer sized multiple after
766 adjustment */
767 allocsize = ALIGN_UP(allocsize, sizeof (intptr_t));
768 if (allocsize > filebuflen)
769 goto bufpanic;
770
771 filebuflen -= allocsize;
772
773 /* Scratch memory */
774 allocsize = scratch_mem_size();
775 if (allocsize > filebuflen)
776 goto bufpanic;
777 759
778 scratch_mem_init(filebuf); 760 scratch_mem_init(filebuf);
779 filebuf += allocsize; 761 filebuf += allocsize;
780 filebuflen -= allocsize; 762 filebuflen -= allocsize;
781 763
782 buffering_reset(filebuf, filebuflen); 764 buffering_reset(filebuf, filebuflen);
783 }
784 765
785 buffer_state = state; 766 buffer_state = AUDIOBUF_STATE_INITIALIZED;
786 767
787#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) 768#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
788 /* Make sure everything adds up - yes, some info is a bit redundant but 769 /* Make sure everything adds up - yes, some info is a bit redundant but
@@ -811,15 +792,24 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
811 /* codec messages */ 792 /* codec messages */
812 { Q_AUDIO_PLAY, Q_AUDIO_PLAY }, 793 { Q_AUDIO_PLAY, Q_AUDIO_PLAY },
813 }; 794 };
795 bool give_up = false;
814 /* filebuflen is, at this point, the buffering.c buffer size, 796 /* filebuflen is, at this point, the buffering.c buffer size,
815 * i.e. the audiobuf except voice, scratch mem, pcm, ... */ 797 * i.e. the audiobuf except voice, scratch mem, pcm, ... */
816 ssize_t extradata_size = old_size - filebuflen; 798 ssize_t extradata_size = old_size - filebuflen;
817 /* check what buflib requests */ 799 /* check what buflib requests */
818 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); 800 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
819 ssize_t size = (ssize_t)old_size - wanted_size; 801 ssize_t size = (ssize_t)old_size - wanted_size;
820 /* keep at least 256K for the buffering */ 802
821 if ((size - extradata_size) < AUDIO_BUFFER_RESERVE) 803 if ((size - extradata_size) < AUDIO_BUFFER_RESERVE)
822 return BUFLIB_CB_CANNOT_SHRINK; 804 {
805 /* check if buflib needs the memory really hard. if yes we give
806 * up playback for now, otherwise refuse to shrink to keep at least
807 * 256K for the buffering */
808 if ((hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK)
809 give_up = true;
810 else
811 return BUFLIB_CB_CANNOT_SHRINK;
812 }
823 813
824 814
825 /* TODO: Do it without stopping playback, if possible */ 815 /* TODO: Do it without stopping playback, if possible */
@@ -852,20 +842,26 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
852#ifdef PLAYBACK_VOICE 842#ifdef PLAYBACK_VOICE
853 voice_stop(); 843 voice_stop();
854#endif 844#endif
855 /* we should be free to change the buffer now 845
856 * set final buffer size before calling audio_reset_buffer_noalloc() 846 /* we should be free to change the buffer now */
847 if (give_up)
848 {
849 buffer_state = AUDIOBUF_STATE_TRASHED;
850 audiobuf_handle = core_free(audiobuf_handle);
851 return BUFLIB_CB_OK;
852 }
853 /* set final buffer size before calling audio_reset_buffer_noalloc()
857 * (now it's the total size, the call will subtract voice etc) */ 854 * (now it's the total size, the call will subtract voice etc) */
858 filebuflen = size; 855 filebuflen = size;
859 switch (hints & BUFLIB_SHRINK_POS_MASK) 856 switch (hints & BUFLIB_SHRINK_POS_MASK)
860 { 857 {
861 case BUFLIB_SHRINK_POS_BACK: 858 case BUFLIB_SHRINK_POS_BACK:
862 core_shrink(handle, start, size); 859 core_shrink(handle, start, size);
863 audio_reset_buffer_noalloc(start, buffer_state); 860 audio_reset_buffer_noalloc(start);
864 break; 861 break;
865 case BUFLIB_SHRINK_POS_FRONT: 862 case BUFLIB_SHRINK_POS_FRONT:
866 core_shrink(handle, start + wanted_size, size); 863 core_shrink(handle, start + wanted_size, size);
867 audio_reset_buffer_noalloc(start + wanted_size, 864 audio_reset_buffer_noalloc(start + wanted_size);
868 buffer_state);
869 break; 865 break;
870 } 866 }
871 if (playing || play_queued) 867 if (playing || play_queued)
@@ -882,7 +878,7 @@ static struct buflib_callbacks ops = {
882 .shrink_callback = shrink_callback, 878 .shrink_callback = shrink_callback,
883}; 879};
884 880
885static void audio_reset_buffer(enum audio_buffer_state state) 881static void audio_reset_buffer(void)
886{ 882{
887 if (audiobuf_handle > 0) 883 if (audiobuf_handle > 0)
888 { 884 {
@@ -890,9 +886,13 @@ static void audio_reset_buffer(enum audio_buffer_state state)
890 audiobuf_handle = 0; 886 audiobuf_handle = 0;
891 } 887 }
892 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); 888 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
893 unsigned char *filebuf = core_get_data(audiobuf_handle);
894 889
895 audio_reset_buffer_noalloc(filebuf, state); 890 if (audiobuf_handle > 0)
891 audio_reset_buffer_noalloc(core_get_data(audiobuf_handle));
892 else
893 /* someone is abusing core_alloc_maximum(). Fix this evil guy instead of
894 * trying to handle OOM without hope */
895 panicf("%s(): OOM!\n", __func__);
896} 896}
897 897
898/* Set the buffer margin to begin rebuffering when 'seconds' from empty */ 898/* Set the buffer margin to begin rebuffering when 'seconds' from empty */
@@ -2033,7 +2033,7 @@ static int audio_fill_file_buffer(void)
2033 if (buffer_state != AUDIOBUF_STATE_INITIALIZED || 2033 if (buffer_state != AUDIOBUF_STATE_INITIALIZED ||
2034 !pcmbuf_is_same_size()) 2034 !pcmbuf_is_same_size())
2035 { 2035 {
2036 audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED); 2036 audio_reset_buffer();
2037 } 2037 }
2038 2038
2039 logf("Starting buffer fill"); 2039 logf("Starting buffer fill");
@@ -2464,7 +2464,7 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2464 /* Mark the buffer dirty - if not playing, it will be reset next 2464 /* Mark the buffer dirty - if not playing, it will be reset next
2465 time */ 2465 time */
2466 if (buffer_state == AUDIOBUF_STATE_INITIALIZED) 2466 if (buffer_state == AUDIOBUF_STATE_INITIALIZED)
2467 buffer_state = AUDIOBUF_STATE_VOICED_ONLY; 2467 buffer_state = AUDIOBUF_STATE_TRASHED;
2468 } 2468 }
2469 2469
2470 if (old_status != PLAY_STOPPED) 2470 if (old_status != PLAY_STOPPED)
@@ -3511,88 +3511,6 @@ void audio_flush_and_reload_tracks(void)
3511 audio_queue_post(Q_AUDIO_FLUSH, 0); 3511 audio_queue_post(Q_AUDIO_FLUSH, 0);
3512} 3512}
3513 3513
3514/* Return the pointer to the main audio buffer, optionally preserving
3515 voicing */
3516unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
3517{
3518 unsigned char *buf;
3519
3520 if (audio_is_initialized && thread_self() != audio_thread_id)
3521 {
3522 audio_hard_stop();
3523 }
3524 /* else buffer_state will be AUDIOBUF_STATE_TRASHED at this point */
3525
3526 if (buffer_size == NULL)
3527 {
3528 /* Special case for talk_init to use since it already knows it's
3529 trashed */
3530 buffer_state = AUDIOBUF_STATE_TRASHED;
3531 return NULL;
3532 }
3533
3534 /* make sure buffer is freed and re-allocated to simplify code below
3535 * (audio_hard_stop() likely has done that already) */
3536 if (audiobuf_handle > 0)
3537 audiobuf_handle = core_free(audiobuf_handle);
3538
3539 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
3540 buf = core_get_data(audiobuf_handle);
3541
3542 if (buffer_state == AUDIOBUF_STATE_INITIALIZED)
3543 buffering_reset(NULL, 0); /* mark buffer invalid */
3544
3545 if (talk_buf || !talk_voice_required())
3546 {
3547 logf("get buffer: talk, audio");
3548 /* Ok to use everything from audiobuf - voice is loaded,
3549 the talk buffer is not needed because voice isn't being used, or
3550 could be AUDIOBUF_STATE_TRASHED already. If state is
3551 AUDIOBUF_STATE_VOICED_ONLY, no problem as long as memory isn't
3552 written without the caller knowing what's going on. Changing certain
3553 settings may move it to a worse condition but the memory in use by
3554 something else will remain undisturbed.
3555 */
3556 if (buffer_state != AUDIOBUF_STATE_TRASHED)
3557 {
3558 talk_buffer_steal();
3559 buffer_state = AUDIOBUF_STATE_TRASHED;
3560 }
3561 }
3562 else
3563 {
3564 logf("get buffer: audio");
3565 /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or
3566 still AUDIOBUF_STATE_INITIALIZED */
3567 size_t talkbuf_size = talkbuf_init(buf);
3568 buf += talkbuf_size; /* Skip talk buffer */
3569 filebuflen -= talkbuf_size;
3570 buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
3571 }
3572
3573 *buffer_size = filebuflen;
3574 return buf;
3575}
3576
3577/* Restore audio buffer to a particular state (promoting status) */
3578bool audio_restore_playback(int type)
3579{
3580 switch (type)
3581 {
3582 case AUDIO_WANT_PLAYBACK:
3583 if (buffer_state != AUDIOBUF_STATE_INITIALIZED)
3584 audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED);
3585 return true;
3586 case AUDIO_WANT_VOICE:
3587 if (buffer_state == AUDIOBUF_STATE_TRASHED)
3588 audio_reset_buffer(AUDIOBUF_STATE_VOICED_ONLY);
3589 return true;
3590 default:
3591 return false;
3592 }
3593}
3594
3595
3596/** --- Miscellaneous public interfaces --- **/ 3514/** --- Miscellaneous public interfaces --- **/
3597 3515
3598#ifdef HAVE_ALBUMART 3516#ifdef HAVE_ALBUMART
diff --git a/apps/playback.h b/apps/playback.h
index f56bbfdff0..177768ded3 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -80,12 +80,6 @@ void audio_set_cuesheet(bool enable);
80void audio_set_crossfade(int enable); 80void audio_set_crossfade(int enable);
81#endif 81#endif
82 82
83enum
84{
85 AUDIO_WANT_PLAYBACK = 0,
86 AUDIO_WANT_VOICE,
87};
88bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
89size_t audio_get_filebuflen(void); 83size_t audio_get_filebuflen(void);
90 84
91unsigned int playback_status(void); 85unsigned int playback_status(void);
diff --git a/apps/playlist.c b/apps/playlist.c
index 35b7e35baa..3d930cf3f9 100755
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -104,6 +104,7 @@
104#include "rbunicode.h" 104#include "rbunicode.h"
105#include "root_menu.h" 105#include "root_menu.h"
106#include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */ 106#include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */
107#include "panic.h"
107 108
108#define PLAYLIST_CONTROL_FILE_VERSION 2 109#define PLAYLIST_CONTROL_FILE_VERSION 2
109 110
@@ -532,13 +533,6 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
532 533
533 splash(0, ID2P(LANG_WAIT)); 534 splash(0, ID2P(LANG_WAIT));
534 535
535 if (!buffer)
536 {
537 /* use mp3 buffer for maximum load speed */
538 audio_stop();
539 buffer = audio_get_buffer(false, &buflen);
540 }
541
542 store_index = true; 536 store_index = true;
543 537
544 while(1) 538 while(1)
@@ -2077,8 +2071,26 @@ int playlist_create(const char *dir, const char *file)
2077 new_playlist(playlist, dir, file); 2071 new_playlist(playlist, dir, file);
2078 2072
2079 if (file) 2073 if (file)
2080 /* load the playlist file */ 2074 {
2081 add_indices_to_playlist(playlist, NULL, 0); 2075 /* dummy ops with no callbacks, needed because by
2076 * default buflib buffers can be moved around which must be avoided */
2077 static struct buflib_callbacks dummy_ops;
2078 int handle;
2079 size_t buflen;
2080 /* use mp3 buffer for maximum load speed */
2081 handle = core_alloc_maximum("temp", &buflen, &dummy_ops);
2082 if (handle > 0)
2083 {
2084 /* load the playlist file */
2085 add_indices_to_playlist(playlist, core_get_data(handle), buflen);
2086 core_free(handle);
2087 }
2088 else
2089 {
2090 /* should not happen */
2091 panicf("%s(): OOM", __func__);
2092 }
2093 }
2082 2094
2083 return 0; 2095 return 0;
2084} 2096}
@@ -2094,14 +2106,22 @@ int playlist_resume(void)
2094 struct playlist_info* playlist = &current_playlist; 2106 struct playlist_info* playlist = &current_playlist;
2095 char *buffer; 2107 char *buffer;
2096 size_t buflen; 2108 size_t buflen;
2109 int handle;
2097 int nread; 2110 int nread;
2098 int total_read = 0; 2111 int total_read = 0;
2099 int control_file_size = 0; 2112 int control_file_size = 0;
2100 bool first = true; 2113 bool first = true;
2101 bool sorted = true; 2114 bool sorted = true;
2115 int result = -1;
2102 2116
2117 /* dummy ops with no callbacks, needed because by
2118 * default buflib buffers can be moved around which must be avoided */
2119 static struct buflib_callbacks dummy_ops;
2103 /* use mp3 buffer for maximum load speed */ 2120 /* use mp3 buffer for maximum load speed */
2104 buffer = (char *)audio_get_buffer(false, &buflen); 2121 handle = core_alloc_maximum("temp", &buflen, &dummy_ops);
2122 if (handle < 0)
2123 panicf("%s(): OOM", __func__);
2124 buffer = core_get_data(handle);
2105 2125
2106 empty_playlist(playlist, true); 2126 empty_playlist(playlist, true);
2107 2127
@@ -2110,7 +2130,7 @@ int playlist_resume(void)
2110 if (playlist->control_fd < 0) 2130 if (playlist->control_fd < 0)
2111 { 2131 {
2112 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); 2132 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2113 return -1; 2133 goto out;
2114 } 2134 }
2115 playlist->control_created = true; 2135 playlist->control_created = true;
2116 2136
@@ -2118,7 +2138,7 @@ int playlist_resume(void)
2118 if (control_file_size <= 0) 2138 if (control_file_size <= 0)
2119 { 2139 {
2120 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); 2140 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2121 return -1; 2141 goto out;
2122 } 2142 }
2123 2143
2124 /* read a small amount first to get the header */ 2144 /* read a small amount first to get the header */
@@ -2127,14 +2147,14 @@ int playlist_resume(void)
2127 if(nread <= 0) 2147 if(nread <= 0)
2128 { 2148 {
2129 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); 2149 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2130 return -1; 2150 goto out;
2131 } 2151 }
2132 2152
2133 playlist->started = true; 2153 playlist->started = true;
2134 2154
2135 while (1) 2155 while (1)
2136 { 2156 {
2137 int result = 0; 2157 result = 0;
2138 int count; 2158 int count;
2139 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT; 2159 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
2140 int last_newline = 0; 2160 int last_newline = 0;
@@ -2195,7 +2215,10 @@ int playlist_resume(void)
2195 version = atoi(str1); 2215 version = atoi(str1);
2196 2216
2197 if (version != PLAYLIST_CONTROL_FILE_VERSION) 2217 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2198 return -1; 2218 {
2219 result = -1;
2220 goto out;
2221 }
2199 2222
2200 update_playlist_filename(playlist, str2, str3); 2223 update_playlist_filename(playlist, str2, str3);
2201 2224
@@ -2204,7 +2227,7 @@ int playlist_resume(void)
2204 /* NOTE: add_indices_to_playlist() overwrites the 2227 /* NOTE: add_indices_to_playlist() overwrites the
2205 audiobuf so we need to reload control file 2228 audiobuf so we need to reload control file
2206 data */ 2229 data */
2207 add_indices_to_playlist(playlist, NULL, 0); 2230 add_indices_to_playlist(playlist, buffer, buflen);
2208 } 2231 }
2209 else if (str2[0] != '\0') 2232 else if (str2[0] != '\0')
2210 { 2233 {
@@ -2242,7 +2265,10 @@ int playlist_resume(void)
2242 buffer */ 2265 buffer */
2243 if (add_track_to_playlist(playlist, str3, position, 2266 if (add_track_to_playlist(playlist, str3, position,
2244 queue, total_read+(str3-buffer)) < 0) 2267 queue, total_read+(str3-buffer)) < 0)
2245 return -1; 2268 {
2269 result = -1;
2270 goto out;
2271 }
2246 2272
2247 playlist->last_insert_pos = last_position; 2273 playlist->last_insert_pos = last_position;
2248 2274
@@ -2264,7 +2290,10 @@ int playlist_resume(void)
2264 2290
2265 if (remove_track_from_playlist(playlist, position, 2291 if (remove_track_from_playlist(playlist, position,
2266 false) < 0) 2292 false) < 0)
2267 return -1; 2293 {
2294 result = -1;
2295 goto out;
2296 }
2268 2297
2269 break; 2298 break;
2270 } 2299 }
@@ -2291,7 +2320,10 @@ int playlist_resume(void)
2291 2320
2292 if (randomise_playlist(playlist, seed, false, 2321 if (randomise_playlist(playlist, seed, false,
2293 false) < 0) 2322 false) < 0)
2294 return -1; 2323 {
2324 result = -1;
2325 goto out;
2326 }
2295 sorted = false; 2327 sorted = false;
2296 break; 2328 break;
2297 } 2329 }
@@ -2308,7 +2340,10 @@ int playlist_resume(void)
2308 playlist->first_index = atoi(str1); 2340 playlist->first_index = atoi(str1);
2309 2341
2310 if (sort_playlist(playlist, false, false) < 0) 2342 if (sort_playlist(playlist, false, false) < 0)
2311 return -1; 2343 {
2344 result = -1;
2345 goto out;
2346 }
2312 2347
2313 sorted = true; 2348 sorted = true;
2314 break; 2349 break;
@@ -2421,13 +2456,14 @@ int playlist_resume(void)
2421 if (result < 0) 2456 if (result < 0)
2422 { 2457 {
2423 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID)); 2458 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2424 return result; 2459 goto out;
2425 } 2460 }
2426 2461
2427 if (useraborted) 2462 if (useraborted)
2428 { 2463 {
2429 splash(HZ*2, ID2P(LANG_CANCEL)); 2464 splash(HZ*2, ID2P(LANG_CANCEL));
2430 return -1; 2465 result = -1;
2466 goto out;
2431 } 2467 }
2432 if (!newline || (exit_loop && count<nread)) 2468 if (!newline || (exit_loop && count<nread))
2433 { 2469 {
@@ -2435,7 +2471,8 @@ int playlist_resume(void)
2435 { 2471 {
2436 /* no newline at end of control file */ 2472 /* no newline at end of control file */
2437 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID)); 2473 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2438 return -1; 2474 result = -1;
2475 goto out;
2439 } 2476 }
2440 2477
2441 /* We didn't end on a newline or we exited loop prematurely. 2478 /* We didn't end on a newline or we exited loop prematurely.
@@ -2464,7 +2501,9 @@ int playlist_resume(void)
2464 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); 2501 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2465#endif 2502#endif
2466 2503
2467 return 0; 2504out:
2505 core_free(handle);
2506 return result;
2468} 2507}
2469 2508
2470/* 2509/*
diff --git a/apps/plugin.c b/apps/plugin.c
index 18c49f4c27..d80e6a1e0e 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -801,6 +801,8 @@ static const struct plugin_api rockbox_api = {
801 the API gets incompatible */ 801 the API gets incompatible */
802}; 802};
803 803
804static int plugin_buffer_handle;
805
804int plugin_load(const char* plugin, const void* parameter) 806int plugin_load(const char* plugin, const void* parameter)
805{ 807{
806 struct plugin_header *p_hdr; 808 struct plugin_header *p_hdr;
@@ -815,6 +817,8 @@ int plugin_load(const char* plugin, const void* parameter)
815 } 817 }
816 lc_close(current_plugin_handle); 818 lc_close(current_plugin_handle);
817 current_plugin_handle = pfn_tsr_exit = NULL; 819 current_plugin_handle = pfn_tsr_exit = NULL;
820 if (plugin_buffer_handle > 0)
821 plugin_buffer_handle = core_free(plugin_buffer_handle);
818 } 822 }
819 823
820 splash(0, ID2P(LANG_WAIT)); 824 splash(0, ID2P(LANG_WAIT));
@@ -878,6 +882,9 @@ int plugin_load(const char* plugin, const void* parameter)
878 touchscreen_set_mode(TOUCHSCREEN_BUTTON); 882 touchscreen_set_mode(TOUCHSCREEN_BUTTON);
879#endif 883#endif
880 884
885 /* allow voice to back off if the plugin needs lots of memory */
886 talk_buffer_set_policy(TALK_BUFFER_LOOSE);
887
881#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE 888#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
882 open_files = 0; 889 open_files = 0;
883#endif 890#endif
@@ -891,8 +898,12 @@ int plugin_load(const char* plugin, const void* parameter)
891 { /* close handle if plugin is no tsr one */ 898 { /* close handle if plugin is no tsr one */
892 lc_close(current_plugin_handle); 899 lc_close(current_plugin_handle);
893 current_plugin_handle = NULL; 900 current_plugin_handle = NULL;
901 if (plugin_buffer_handle > 0)
902 plugin_buffer_handle = core_free(plugin_buffer_handle);
894 } 903 }
895 904
905 talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
906
896 /* Go back to the global setting in case the plugin changed it */ 907 /* Go back to the global setting in case the plugin changed it */
897#ifdef HAVE_TOUCHSCREEN 908#ifdef HAVE_TOUCHSCREEN
898 touchscreen_set_mode(global_settings.touch_mode); 909 touchscreen_set_mode(global_settings.touch_mode);
@@ -984,8 +995,12 @@ void* plugin_get_buffer(size_t *buffer_size)
984 */ 995 */
985void* plugin_get_audio_buffer(size_t *buffer_size) 996void* plugin_get_audio_buffer(size_t *buffer_size)
986{ 997{
987 audio_stop(); 998 /* dummy ops with no callbacks, needed because by
988 return audio_get_buffer(true, buffer_size); 999 * default buflib buffers can be moved around which must be avoided */
1000 static struct buflib_callbacks dummy_ops;
1001 plugin_buffer_handle = core_alloc_maximum("plugin audio buf", buffer_size,
1002 &dummy_ops);
1003 return core_get_data(plugin_buffer_handle);
989} 1004}
990 1005
991/* The plugin wants to stay resident after leaving its main function, e.g. 1006/* The plugin wants to stay resident after leaving its main function, e.g.
diff --git a/apps/plugin.h b/apps/plugin.h
index af673b3afe..38d8889d9e 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -112,6 +112,7 @@ void* plugin_get_buffer(size_t *buffer_size);
112#include "timefuncs.h" 112#include "timefuncs.h"
113#include "crc32.h" 113#include "crc32.h"
114#include "rbpaths.h" 114#include "rbpaths.h"
115#include "core_alloc.h"
115 116
116#ifdef HAVE_ALBUMART 117#ifdef HAVE_ALBUMART
117#include "albumart.h" 118#include "albumart.h"
diff --git a/apps/radio/radioart.c b/apps/radio/radioart.c
index 76697c6017..cab6aa29ce 100644
--- a/apps/radio/radioart.c
+++ b/apps/radio/radioart.c
@@ -32,6 +32,7 @@
32#include "kernel.h" 32#include "kernel.h"
33#include "string-extra.h" 33#include "string-extra.h"
34#include "filefuncs.h" 34#include "filefuncs.h"
35#include "core_alloc.h"
35 36
36#define MAX_RADIOART_IMAGES 10 37#define MAX_RADIOART_IMAGES 10
37struct radioart { 38struct radioart {
@@ -158,14 +159,49 @@ static void buffer_reset_handler(void *data)
158 (void)data; 159 (void)data;
159} 160}
160 161
162static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
163{
164 (void)start;
165 (void)old_size;
166
167 ssize_t old_size_s = old_size;
168 size_t size_hint = (hints & BUFLIB_SHRINK_SIZE_MASK);
169 ssize_t wanted_size = old_size_s - size_hint;
170
171 if (wanted_size <= 0)
172 {
173 core_free(handle);
174 buffering_reset(NULL, 0);
175 }
176 else
177 {
178 if (hints & BUFLIB_SHRINK_POS_FRONT)
179 start += size_hint;
180
181 buffering_reset(start, wanted_size);
182 core_shrink(handle, start, wanted_size);
183 buf = start;
184
185 /* one-shot */
186 add_event(BUFFER_EVENT_BUFFER_RESET, true, buffer_reset_handler);
187 }
188
189 return BUFLIB_CB_OK;
190}
191
192static struct buflib_callbacks radioart_ops = {
193 .shrink_callback = shrink_callback,
194};
195
161void radioart_init(bool entering_screen) 196void radioart_init(bool entering_screen)
162{ 197{
163 if (entering_screen) 198 if (entering_screen)
164 { 199 {
165 /* grab control over buffering */ 200 /* grab control over buffering */
166 size_t bufsize; 201 size_t bufsize;
167 buf = audio_get_buffer(false, &bufsize); 202 int handle = core_alloc_maximum("radioart", &bufsize, &radioart_ops);
168 buffering_reset(buf, bufsize); 203 buffering_reset(core_get_data(handle), bufsize);
204 buf = core_get_data(handle);
169 /* one-shot */ 205 /* one-shot */
170 add_event(BUFFER_EVENT_BUFFER_RESET, true, buffer_reset_handler); 206 add_event(BUFFER_EVENT_BUFFER_RESET, true, buffer_reset_handler);
171 } 207 }
diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c
index 2170e473bc..799c733948 100644
--- a/apps/recorder/pcm_record.c
+++ b/apps/recorder/pcm_record.c
@@ -24,6 +24,7 @@
24#include "config.h" 24#include "config.h"
25#include "system.h" 25#include "system.h"
26#include "kernel.h" 26#include "kernel.h"
27#include "panic.h"
27#include "string-extra.h" 28#include "string-extra.h"
28#include "pcm_record.h" 29#include "pcm_record.h"
29#include "codecs.h" 30#include "codecs.h"
@@ -40,6 +41,8 @@
40#include "spdif.h" 41#include "spdif.h"
41#endif 42#endif
42#include "audio_thread.h" 43#include "audio_thread.h"
44#include "core_alloc.h"
45#include "talk.h"
43 46
44/* Macros to enable logf for queues 47/* Macros to enable logf for queues
45 logging on SYS_TIMEOUT can be disabled */ 48 logging on SYS_TIMEOUT can be disabled */
@@ -1402,11 +1405,22 @@ static void tally_prerecord_data(void)
1402 1405
1403/** Event handlers for recording thread **/ 1406/** Event handlers for recording thread **/
1404 1407
1408static int pcmrec_handle;
1405/* Q_AUDIO_INIT_RECORDING */ 1409/* Q_AUDIO_INIT_RECORDING */
1406static void on_init_recording(void) 1410static void on_init_recording(void)
1407{ 1411{
1408 send_event(RECORDING_EVENT_START, NULL); 1412 send_event(RECORDING_EVENT_START, NULL);
1409 rec_buffer = audio_get_buffer(true, &rec_buffer_size); 1413 /* dummy ops with no callbacks, needed because by
1414 * default buflib buffers can be moved around which must be avoided
1415 * FIXME: This buffer should play nicer and be shrinkable/movable */
1416 static struct buflib_callbacks dummy_ops;
1417 talk_buffer_set_policy(TALK_BUFFER_LOOSE);
1418 pcmrec_handle = core_alloc_maximum("pcmrec", &rec_buffer_size, &dummy_ops);
1419 if (pcmrec_handle)
1420 /* someone is abusing core_alloc_maximum(). Fix this evil guy instead of
1421 * trying to handle OOM without hope */
1422 panicf("%s(): OOM\n", __func__);
1423 rec_buffer = core_get_data(pcmrec_handle);
1410 init_rec_buffers(); 1424 init_rec_buffers();
1411 init_state(); 1425 init_state();
1412 pcm_init_recording(); 1426 pcm_init_recording();
@@ -1430,6 +1444,10 @@ static void on_close_recording(void)
1430 audio_set_output_source(AUDIO_SRC_PLAYBACK); 1444 audio_set_output_source(AUDIO_SRC_PLAYBACK);
1431 pcm_apply_settings(); 1445 pcm_apply_settings();
1432 1446
1447 if (pcmrec_handle > 0)
1448 pcmrec_handle = core_free(pcmrec_handle);
1449 talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
1450
1433 send_event(RECORDING_EVENT_STOP, NULL); 1451 send_event(RECORDING_EVENT_STOP, NULL);
1434} 1452}
1435 1453
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 1c3460fbd0..4a7399b01e 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -691,15 +691,8 @@ void rec_set_source(int source, unsigned flags)
691 691
692void rec_set_recording_options(struct audio_recording_options *options) 692void rec_set_recording_options(struct audio_recording_options *options)
693{ 693{
694#if CONFIG_CODEC != SWCODEC
695 if (global_settings.rec_prerecord_time)
696 {
697 talk_buffer_steal(); /* will use the mp3 buffer */
698 }
699#else /* == SWCODEC */
700 rec_set_source(options->rec_source, 694 rec_set_source(options->rec_source,
701 options->rec_source_flags | SRCF_RECORDING); 695 options->rec_source_flags | SRCF_RECORDING);
702#endif /* CONFIG_CODEC != SWCODEC */
703 696
704 audio_set_recording_options(options); 697 audio_set_recording_options(options);
705} 698}
@@ -724,9 +717,6 @@ void rec_command(enum recording_command cmd)
724 /* steal mp3 buffer, create unique filename and start recording */ 717 /* steal mp3 buffer, create unique filename and start recording */
725 pm_reset_clipcount(); 718 pm_reset_clipcount();
726 pm_activate_clipcount(true); 719 pm_activate_clipcount(true);
727#if CONFIG_CODEC != SWCODEC
728 talk_buffer_steal(); /* we use the mp3 buffer */
729#endif
730 audio_record(rec_create_filename(path_buffer)); 720 audio_record(rec_create_filename(path_buffer));
731 break; 721 break;
732 case RECORDING_CMD_START_NEWFILE: 722 case RECORDING_CMD_START_NEWFILE:
diff --git a/apps/talk.c b/apps/talk.c
index b94dcf18ee..baf854fce3 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -79,8 +79,8 @@ const char* const file_thumbnail_ext = ".talk";
79 79
80#define LOADED_MASK 0x80000000 /* MSB */ 80#define LOADED_MASK 0x80000000 /* MSB */
81 81
82/* swcodec: cap p_thumnail to MAX_THUMNAIL_BUFSIZE since audio keeps playing 82/* swcodec: cap thumbnail buffer to MAX_THUMNAIL_BUFSIZE since audio keeps
83 * while voice 83 * playing while voice
84 * hwcodec: just use whatever is left in the audiobuffer, music 84 * hwcodec: just use whatever is left in the audiobuffer, music
85 * playback is impossible => no cap */ 85 * playback is impossible => no cap */
86#if CONFIG_CODEC == SWCODEC 86#if CONFIG_CODEC == SWCODEC
@@ -95,25 +95,17 @@ struct clip_entry /* one entry of the index table */
95 int size; /* size of the clip */ 95 int size; /* size of the clip */
96}; 96};
97 97
98struct voicefile /* file format of our voice file */ 98struct voicefile_header /* file format of our voice file */
99{ 99{
100 int version; /* version of the voicefile */ 100 int version; /* version of the voicefile */
101 int target_id; /* the rockbox target the file was made for */ 101 int target_id; /* the rockbox target the file was made for */
102 int table; /* offset to index table, (=header size) */ 102 int table; /* offset to index table, (=header size) */
103 int id1_max; /* number of "normal" clips contained in above index */ 103 int id1_max; /* number of "normal" clips contained in above index */
104 int id2_max; /* number of "voice only" clips contained in above index */ 104 int id2_max; /* number of "voice only" clips contained in above index */
105 struct clip_entry index[]; /* followed by the index tables */ 105 /* The header is folled by the index tables (n*struct clip_entry),
106 /* and finally the mp3 clips, not visible here, bitswapped 106 * which is followed by the mp3/speex encoded clip data */
107 for SH based players */
108}; 107};
109 108
110struct queue_entry /* one entry of the internal queue */
111{
112 unsigned char* buf;
113 long len;
114};
115
116
117/***************** Globals *****************/ 109/***************** Globals *****************/
118 110
119#if (CONFIG_CODEC == SWCODEC && MEMORYSIZE <= 2) || defined(ONDIO_SERIES) 111#if (CONFIG_CODEC == SWCODEC && MEMORYSIZE <= 2) || defined(ONDIO_SERIES)
@@ -125,7 +117,6 @@ struct queue_entry /* one entry of the internal queue */
125#endif 117#endif
126 118
127#ifdef TALK_PARTIAL_LOAD 119#ifdef TALK_PARTIAL_LOAD
128static unsigned char *clip_buffer;
129static long max_clipsize; /* size of the biggest clip */ 120static long max_clipsize; /* size of the biggest clip */
130static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */ 121static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */
131static uint8_t clip_age[QUEUE_SIZE]; 122static uint8_t clip_age[QUEUE_SIZE];
@@ -134,16 +125,13 @@ static uint8_t clip_age[QUEUE_SIZE];
134#endif 125#endif
135#endif 126#endif
136 127
137static char* voicebuf; /* root pointer to our buffer */
138static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */
139/* Multiple thumbnails can be loaded back-to-back in this buffer. */ 128/* Multiple thumbnails can be loaded back-to-back in this buffer. */
140static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in 129static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in
141 thumbnail buffer */ 130 thumbnail buffer */
142static long size_for_thumbnail; /* total thumbnail buffer size */ 131static long size_for_thumbnail; /* total thumbnail buffer size */
143static struct voicefile* p_voicefile; /* loaded voicefile */ 132static struct voicefile_header voicefile; /* loaded voicefile */
144static bool has_voicefile; /* a voicefile file is present */ 133static bool has_voicefile; /* a voicefile file is present */
145static bool need_shutup; /* is there possibly any voice playing to be shutup */ 134static bool need_shutup; /* is there possibly any voice playing to be shutup */
146static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */
147static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */ 135static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */
148static int queue_write; /* write index of queue, by application */ 136static int queue_write; /* write index of queue, by application */
149static int queue_read; /* read index of queue, by ISR context */ 137static int queue_read; /* read index of queue, by ISR context */
@@ -158,18 +146,126 @@ static struct mutex queue_mutex SHAREDBSS_ATTR;
158#endif /* CONFIG_CODEC */ 146#endif /* CONFIG_CODEC */
159static int sent; /* how many bytes handed over to playback, owned by ISR */ 147static int sent; /* how many bytes handed over to playback, owned by ISR */
160static unsigned char curr_hd[3]; /* current frame header, for re-sync */ 148static unsigned char curr_hd[3]; /* current frame header, for re-sync */
161static int filehandle = -1; /* global, so we can keep the file open if needed */ 149static int silence_offset; /* VOICE_PAUSE clip, used for termination */
162static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */ 150static long silence_length; /* length of the VOICE_PAUSE clip */
163static long silence_len; /* length of the VOICE_PAUSE clip */ 151static unsigned long lastclip_offset; /* address of latest clip, for silence add */
164static unsigned char* p_lastclip; /* address of latest clip, for silence add */
165static unsigned long voicefile_size = 0; /* size of the loaded voice file */
166static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ 152static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */
167static bool talk_initialized; /* true if talk_init has been called */ 153static bool talk_initialized; /* true if talk_init has been called */
154static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */
168static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */ 155static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */
156 /* size of the loaded voice file
157 * offsets smaller than this denote a clip from teh voice file,
158 * offsets larger than this denote a thumbnail clip */
159static unsigned long voicefile_size;
160
161struct queue_entry /* one entry of the internal queue */
162{
163 int offset, length;
164};
165
166static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */
169 167
170 168
171/***************** Private implementation *****************/ 169/***************** Private implementation *****************/
172 170
171static int thumb_handle;
172static int talk_handle, talk_handle_locked;
173
174#if CONFIG_CODEC != SWCODEC
175
176/* on HWCODEC only voice xor audio can be active at a time */
177static bool check_audio_status(void)
178{
179 if (audio_status()) /* busy, buffer in use */
180 return false;
181 /* ensure playback is given up on the buffer */
182 audio_hard_stop();
183 return true;
184}
185
186/* ISR (mp3_callback()) must not run during moving of the clip buffer,
187 * because the MAS may get out-of-sync */
188static void sync_callback(int handle, bool sync_on)
189{
190 (void) handle;
191 (void) sync_on;
192#if CONFIG_CPU == SH7034
193 if (sync_on)
194 CHCR3 &= ~0x0001; /* disable the DMA (and therefore the interrupt also) */
195 else
196 CHCR3 |= 0x0001; /* re-enable the DMA */
197#endif
198}
199#else
200#define check_audio_status() (true)
201#endif
202
203static int move_callback(int handle, void *current, void *new)
204{
205 (void)handle;(void)current;(void)new;
206 if (UNLIKELY(talk_handle_locked))
207 return BUFLIB_CB_CANNOT_MOVE;
208 return BUFLIB_CB_OK;
209}
210
211static int shrink_callback(int handle, unsigned hints, void *start, size_t old_size)
212{
213 (void)start;(void)old_size;
214 int *h;
215 if (handle == talk_handle)
216 h = &talk_handle;
217 else // if (handle == thumb_handle)
218 h = &thumb_handle;
219
220 if (LIKELY(!talk_handle_locked)
221 && give_buffer_away
222 && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK)
223 {
224 *h = core_free(handle);
225 return BUFLIB_CB_OK;
226 }
227 return BUFLIB_CB_CANNOT_SHRINK;
228}
229
230static struct buflib_callbacks talk_ops = {
231 .move_callback = move_callback,
232#if CONFIG_CODEC != SWCODEC
233 .sync_callback = sync_callback,
234#endif
235 .shrink_callback = shrink_callback,
236};
237
238
239static int index_handle, index_handle_locked;
240static int index_move_callback(int handle, void *current, void *new)
241{
242 (void)handle;(void)current;(void)new;
243 if (UNLIKELY(index_handle_locked))
244 return BUFLIB_CB_CANNOT_MOVE;
245 return BUFLIB_CB_OK;
246}
247
248static int index_shrink_callback(int handle, unsigned hints, void *start, size_t old_size)
249{
250 (void)start;(void)old_size;
251 if (LIKELY(!index_handle_locked)
252 && give_buffer_away
253 && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK)
254 {
255 index_handle = core_free(handle);
256 /* the clip buffer isn't usable without index table */
257 if (LIKELY(!talk_handle_locked))
258 talk_handle = core_free(talk_handle);
259 return BUFLIB_CB_OK;
260 }
261 return BUFLIB_CB_CANNOT_SHRINK;
262}
263
264static struct buflib_callbacks index_ops = {
265 .move_callback = index_move_callback,
266 .shrink_callback = index_shrink_callback,
267};
268
173static int open_voicefile(void) 269static int open_voicefile(void)
174{ 270{
175 char buf[64]; 271 char buf[64];
@@ -188,38 +284,41 @@ static int open_voicefile(void)
188 284
189 285
190/* fetch a clip from the voice file */ 286/* fetch a clip from the voice file */
191static unsigned char* get_clip(long id, long* p_size) 287static int get_clip(long id, long* p_size)
192{ 288{
193 long clipsize; 289 int retval = -1;
194 unsigned char* clipbuf; 290 struct clip_entry* clipbuf;
195 291 size_t clipsize;
292
196 if (id > VOICEONLY_DELIMITER) 293 if (id > VOICEONLY_DELIMITER)
197 { /* voice-only entries use the second part of the table. 294 { /* voice-only entries use the second part of the table.
198 The first string comes after VOICEONLY_DELIMITER so we need to 295 The first string comes after VOICEONLY_DELIMITER so we need to
199 substract VOICEONLY_DELIMITER + 1 */ 296 substract VOICEONLY_DELIMITER + 1 */
200 id -= VOICEONLY_DELIMITER + 1; 297 id -= VOICEONLY_DELIMITER + 1;
201 if (id >= p_voicefile->id2_max) 298 if (id >= voicefile.id2_max)
202 return NULL; /* must be newer than we have */ 299 return -1; /* must be newer than we have */
203 id += p_voicefile->id1_max; /* table 2 is behind table 1 */ 300 id += voicefile.id1_max; /* table 2 is behind table 1 */
204 } 301 }
205 else 302 else
206 { /* normal use of the first table */ 303 { /* normal use of the first table */
207 if (id >= p_voicefile->id1_max) 304 if (id >= voicefile.id1_max)
208 return NULL; /* must be newer than we have */ 305 return -1; /* must be newer than we have */
209 } 306 }
210 307
211 clipsize = p_voicefile->index[id].size; 308 clipbuf = core_get_data(index_handle);
309 clipsize = clipbuf[id].size;
212 if (clipsize == 0) /* clip not included in voicefile */ 310 if (clipsize == 0) /* clip not included in voicefile */
213 return NULL; 311 return -1;
214 312
215#ifndef TALK_PARTIAL_LOAD 313#ifndef TALK_PARTIAL_LOAD
216 clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset; 314 retval = clipbuf[id].offset;
217#endif
218 315
219#ifdef TALK_PARTIAL_LOAD 316#else
220 if (!(clipsize & LOADED_MASK)) 317 if (!(clipsize & LOADED_MASK))
221 { /* clip needs loading */ 318 { /* clip needs loading */
222 int idx = 0; 319 ssize_t ret;
320 int fd, idx = 0;
321 unsigned char *voicebuf;
223 if (id == VOICE_PAUSE) { 322 if (id == VOICE_PAUSE) {
224 idx = QUEUE_SIZE; /* we keep VOICE_PAUSE loaded */ 323 idx = QUEUE_SIZE; /* we keep VOICE_PAUSE loaded */
225 } else { 324 } else {
@@ -243,18 +342,29 @@ static unsigned char* get_clip(long id, long* p_size)
243 } 342 }
244 clip_age[idx] = 0; /* reset clip's age */ 343 clip_age[idx] = 0; /* reset clip's age */
245 } 344 }
246 clipbuf = clip_buffer + idx * max_clipsize; 345 retval = idx * max_clipsize;
346 fd = open_voicefile();
347 if (fd < 0)
348 return -1; /* open error */
349
350 talk_handle_locked++;
351 voicebuf = core_get_data(talk_handle);
352 clipbuf = core_get_data(index_handle);
353 lseek(fd, clipbuf[id].offset, SEEK_SET);
354 ret = read(fd, &voicebuf[retval], clipsize);
355 close(fd);
356 talk_handle_locked--;
247 357
248 lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET); 358 if (ret < 0 || clipsize != (size_t)ret)
249 if (read(filehandle, clipbuf, clipsize) != clipsize) 359 return -1; /* read error */
250 return NULL; /* read error */
251 360
252 p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */ 361 clipbuf = core_get_data(index_handle);
362 clipbuf[id].size |= LOADED_MASK; /* mark as loaded */
253 363
254 if (id != VOICE_PAUSE) { 364 if (id != VOICE_PAUSE) {
255 if (buffered_id[idx] >= 0) { 365 if (buffered_id[idx] >= 0) {
256 /* mark previously loaded clip as unloaded */ 366 /* mark previously loaded clip as unloaded */
257 p_voicefile->index[buffered_id[idx]].size &= ~LOADED_MASK; 367 clipbuf[buffered_id[idx]].size &= ~LOADED_MASK;
258 } 368 }
259 buffered_id[idx] = id; 369 buffered_id[idx] = id;
260 } 370 }
@@ -262,14 +372,13 @@ static unsigned char* get_clip(long id, long* p_size)
262 else 372 else
263 { /* clip is in memory already */ 373 { /* clip is in memory already */
264 /* Find where it was loaded */ 374 /* Find where it was loaded */
265 clipbuf = clip_buffer;
266 if (id == VOICE_PAUSE) { 375 if (id == VOICE_PAUSE) {
267 clipbuf += QUEUE_SIZE * max_clipsize; 376 retval = QUEUE_SIZE * max_clipsize;
268 } else { 377 } else {
269 int idx; 378 int idx;
270 for (idx=0; idx<QUEUE_SIZE; idx++) 379 for (idx=0; idx<QUEUE_SIZE; idx++)
271 if (buffered_id[idx] == id) { 380 if (buffered_id[idx] == id) {
272 clipbuf += idx * max_clipsize; 381 retval = idx * max_clipsize;
273 clip_age[idx] = 0; /* reset clip's age */ 382 clip_age[idx] = 0; /* reset clip's age */
274 break; 383 break;
275 } 384 }
@@ -279,153 +388,200 @@ static unsigned char* get_clip(long id, long* p_size)
279#endif /* TALK_PARTIAL_LOAD */ 388#endif /* TALK_PARTIAL_LOAD */
280 389
281 *p_size = clipsize; 390 *p_size = clipsize;
282 return clipbuf; 391 return retval;
283} 392}
284 393
285 394static bool load_index_table(int fd, const struct voicefile_header *hdr)
286/* load the voice file into the mp3 buffer */
287static void load_voicefile(bool probe, char* buf, size_t bufsize)
288{ 395{
289 union voicebuf { 396 ssize_t ret;
290 unsigned char* buf; 397 struct clip_entry *buf;
291 struct voicefile* file;
292 };
293 union voicebuf voicebuf;
294 398
295 size_t load_size, alloc_size; 399 if (index_handle > 0) /* nothing to do? */
296 ssize_t got_size; 400 return true;
297#ifdef ROCKBOX_LITTLE_ENDIAN
298 int i;
299#endif
300 401
301 if (!probe) 402 ssize_t alloc_size = (hdr->id1_max + hdr->id2_max) * sizeof(struct clip_entry);
302 filehandle = open_voicefile(); 403 index_handle = core_alloc_ex("voice index", alloc_size, &index_ops);
303 if (filehandle < 0) /* failed to open */ 404 if (index_handle < 0)
304 goto load_err; 405 return false;
305 406
306 voicebuf.buf = buf; 407 index_handle_locked++;
307 if (!voicebuf.buf) 408 buf = core_get_data(index_handle);
308 goto load_err; 409 ret = read(fd, buf, alloc_size);
309 410
310#ifdef TALK_PARTIAL_LOAD 411#ifndef TALK_PARTIAL_LOAD
311 /* load only the header for now */ 412 int clips_offset, num_clips;
312 load_size = sizeof(struct voicefile); 413 /* adjust the offsets of the clips, they are relative to the file
313#else 414 * TALK_PARTUAL_LOAD needs the file offset instead as it loads
314 /* load the entire file */ 415 * the clips later */
315 load_size = filesize(filehandle); 416 clips_offset = hdr->table;
417 num_clips = hdr->id1_max + hdr->id2_max;
418 clips_offset += num_clips * sizeof(struct clip_entry); /* skip index */
419#endif
420 if (ret == alloc_size)
421 for (int i = 0; i < hdr->id1_max + hdr->id2_max; i++)
422 {
423#ifdef ROCKBOX_LITTLE_ENDIAN
424 structec_convert(&buf[i], "ll", 1, true);
316#endif 425#endif
317 if (load_size > bufsize) /* won't fit? */ 426#ifndef TALK_PARTIAL_LOAD
318 goto load_err; 427 buf[i].offset -= clips_offset;
428#endif
429 }
430
431 index_handle_locked--;
319 432
320 got_size = read(filehandle, voicebuf.buf, load_size); 433 if (ret != alloc_size)
321 if (got_size != (ssize_t)load_size /* failure */) 434 index_handle = core_free(index_handle);
322 goto load_err;
323 435
324 alloc_size = load_size; 436 return ret == alloc_size;
437}
438
439static bool load_header(int fd, struct voicefile_header *hdr)
440{
441 ssize_t got_size = read(fd, hdr, sizeof(*hdr));
442 if (got_size != sizeof(*hdr))
443 return false;
325 444
326#ifdef ROCKBOX_LITTLE_ENDIAN 445#ifdef ROCKBOX_LITTLE_ENDIAN
327 logf("Byte swapping voice file"); 446 logf("Byte swapping voice file");
328 structec_convert(voicebuf.buf, "lllll", 1, true); 447 structec_convert(&voicefile, "lllll", 1, true);
329#endif 448#endif
449 return true;
450}
330 451
331 /* format check */ 452#ifndef TALK_PARTIAL_LOAD
332 if (voicebuf.file->table == sizeof(struct voicefile)) 453static bool load_data(int fd, ssize_t size_to_read)
333 { 454{
334 p_voicefile = voicebuf.file; 455 unsigned char *buf;
335 456 ssize_t ret;
336 if (p_voicefile->version != VOICE_VERSION ||
337 p_voicefile->target_id != TARGET_ID)
338 {
339 logf("Incompatible voice file");
340 goto load_err;
341 }
342 }
343 else
344 goto load_err;
345 457
346#ifdef TALK_PARTIAL_LOAD 458 if (size_to_read < 0)
347 /* load the index table, now that we know its size from the header */ 459 return false;
348 load_size = (p_voicefile->id1_max + p_voicefile->id2_max)
349 * sizeof(struct clip_entry);
350 460
351 if (load_size > bufsize) /* won't fit? */ 461 talk_handle = core_alloc_ex("voice data", size_to_read, &talk_ops);
352 goto load_err; 462 if (talk_handle < 0)
463 return false;
353 464
354 got_size = read(filehandle, &p_voicefile->index[0], load_size); 465 talk_handle_locked++;
355 if (got_size != (ssize_t)load_size) /* read error */ 466 buf = core_get_data(talk_handle);
356 goto load_err; 467 ret = read(fd, buf, size_to_read);
468 talk_handle_locked--;
357 469
358 alloc_size += load_size; 470 if (ret != size_to_read)
359#else 471 talk_handle = core_free(talk_handle);
360 close(filehandle);
361 filehandle = -1;
362#endif /* TALK_PARTIAL_LOAD */
363 472
364#ifdef ROCKBOX_LITTLE_ENDIAN 473 return ret == size_to_read;
365 for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++) 474}
366 structec_convert(&p_voicefile->index[i], "ll", 1, true);
367#endif 475#endif
368 476
369#ifdef TALK_PARTIAL_LOAD 477static bool alloc_thumbnail_buf(void)
370 clip_buffer = (unsigned char *) p_voicefile + p_voicefile->table; 478{
371 unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max; 479 int handle;
372 clip_buffer += clips * sizeof(struct clip_entry); /* skip index */ 480 size_t size;
481 if (thumb_handle > 0)
482 return true; /* nothing to do? */
483#if CONFIG_CODEC == SWCODEC
484 /* try to allocate the max. first, and take whatever we can get if that
485 * fails */
486 size = MAX_THUMBNAIL_BUFSIZE;
487 handle = core_alloc_ex("voice thumb", MAX_THUMBNAIL_BUFSIZE, &talk_ops);
488 if (handle < 0)
489 {
490 size = core_allocatable();
491 handle = core_alloc_ex("voice thumb", size, &talk_ops);
492 }
493#else
494 /* on HWCODEC, just use the rest of the remaining buffer,
495 * normal playback cannot happen anyway */
496 handle = core_alloc_maximum("voice thumb", &size, &talk_ops);
373#endif 497#endif
374 if (!probe) { 498 thumb_handle = handle;
375 /* make sure to have the silence clip, if available */ 499 size_for_thumbnail = (handle > 0) ? size : 0;
376 p_silence = get_clip(VOICE_PAUSE, &silence_len); 500 return handle > 0;
501}
502
503/* load the voice file into the mp3 buffer */
504static bool load_voicefile_index(int fd)
505{
506 if (fd < 0) /* failed to open */
507 return false;
508
509 /* load the header first */
510 if (!load_header(fd, &voicefile))
511 return false;
512
513 /* format check */
514 if (voicefile.table == sizeof(struct voicefile_header))
515 {
516 if (voicefile.version == VOICE_VERSION &&
517 voicefile.target_id == TARGET_ID)
518 {
519 if (load_index_table(fd, &voicefile))
520 return true;
521 }
377 } 522 }
378 523
524 logf("Incompatible voice file");
525 return false;
526}
527
528static bool load_voicefile_data(int fd, size_t max_size)
529{
379#ifdef TALK_PARTIAL_LOAD 530#ifdef TALK_PARTIAL_LOAD
380 alloc_size += silence_len + QUEUE_SIZE; 531 /* just allocate, populate on an as-needed basis later */
532 talk_handle = core_alloc_ex("voice data", max_size, &talk_ops);
533 if (talk_handle < 0)
534 goto load_err_free;
535#else
536 size_t load_size, clips_size;
537 /* load the entire file into memory */
538 clips_size = (voicefile.id1_max+voicefile.id2_max) * sizeof(struct clip_entry);
539 load_size = max_size - voicefile.table - clips_size;
540 if (!load_data(fd, load_size))
541 goto load_err_free;
381#endif 542#endif
382 543
383 if (alloc_size > bufsize) 544 /* make sure to have the silence clip, if available
384 goto load_err; 545 * return value can be cached globally even for TALK_PARTIAL_LOAD because
546 * the VOICE_PAUSE clip is specially handled */
547 silence_offset = get_clip(VOICE_PAUSE, &silence_length);
385 548
386 /* now move p_thumbnail behind the voice clip buffer */ 549 /* not an error if this fails here, might try again when the
387 p_thumbnail = voicebuf.buf + alloc_size; 550 * actual thumbnails are attempted to be played back */
388 p_thumbnail += (long)p_thumbnail % 2; /* 16-bit align */ 551 alloc_thumbnail_buf();
389 size_for_thumbnail = voicebuf.buf + bufsize - p_thumbnail;
390#if CONFIG_CODEC == SWCODEC
391 size_for_thumbnail = MIN(size_for_thumbnail, MAX_THUMBNAIL_BUFSIZE);
392#endif
393 if (size_for_thumbnail <= 0)
394 p_thumbnail = NULL;
395 552
396 return; 553 return true;
397load_err: 554
398 p_voicefile = NULL; 555load_err_free:
399 has_voicefile = false; /* don't try again */ 556 index_handle = core_free(index_handle);
400 if (filehandle >= 0) 557 return false;
401 {
402 close(filehandle);
403 filehandle = -1;
404 }
405 return;
406} 558}
407 559
408 560
409/* called in ISR context (on HWCODEC) if mp3 data got consumed */ 561/* called in ISR context (on HWCODEC) if mp3 data got consumed */
410static void mp3_callback(const void** start, size_t* size) 562static void mp3_callback(const void** start, size_t* size)
411{ 563{
412 queue[queue_read].len -= sent; /* we completed this */ 564 queue[queue_read].length -= sent; /* we completed this */
413 queue[queue_read].buf += sent; 565 queue[queue_read].offset += sent;
414 566
415 if (queue[queue_read].len > 0) /* current clip not finished? */ 567 if (queue[queue_read].length > 0) /* current clip not finished? */
416 { /* feed the next 64K-1 chunk */ 568 { /* feed the next 64K-1 chunk */
569 int offset;
417#if CONFIG_CODEC != SWCODEC 570#if CONFIG_CODEC != SWCODEC
418 sent = MIN(queue[queue_read].len, 0xFFFF); 571 sent = MIN(queue[queue_read].length, 0xFFFF);
419#else 572#else
420 sent = queue[queue_read].len; 573 sent = queue[queue_read].length;
421#endif 574#endif
422 *start = queue[queue_read].buf; 575 offset = queue[queue_read].offset;
576 if ((unsigned long)offset >= voicefile_size)
577 *start = core_get_data(thumb_handle) + offset - voicefile_size;
578 else
579 *start = core_get_data(talk_handle) + offset;
423 *size = sent; 580 *size = sent;
424 return; 581 return;
425 } 582 }
426 talk_queue_lock(); 583 talk_queue_lock();
427 if(p_thumbnail 584 if(thumb_handle && (unsigned long)queue[queue_read].offset == voicefile_size+thumbnail_buf_used)
428 && queue[queue_read].buf == p_thumbnail +thumbnail_buf_used)
429 thumbnail_buf_used = 0; 585 thumbnail_buf_used = 0;
430 if (sent > 0) /* go to next entry */ 586 if (sent > 0) /* go to next entry */
431 { 587 {
@@ -436,24 +592,31 @@ re_check:
436 592
437 if (QUEUE_LEVEL != 0) /* queue is not empty? */ 593 if (QUEUE_LEVEL != 0) /* queue is not empty? */
438 { /* start next clip */ 594 { /* start next clip */
595 unsigned char *buf;
439#if CONFIG_CODEC != SWCODEC 596#if CONFIG_CODEC != SWCODEC
440 sent = MIN(queue[queue_read].len, 0xFFFF); 597 sent = MIN(queue[queue_read].length, 0xFFFF);
441#else 598#else
442 sent = queue[queue_read].len; 599 sent = queue[queue_read].length;
443#endif 600#endif
444 *start = p_lastclip = queue[queue_read].buf; 601 lastclip_offset = queue[queue_read].offset;
602 /* offsets larger than voicefile_size denote thumbnail clips */
603 if (lastclip_offset >= voicefile_size)
604 buf = core_get_data(thumb_handle) + lastclip_offset - voicefile_size;
605 else
606 buf = core_get_data(talk_handle) + lastclip_offset;
607 *start = buf;
445 *size = sent; 608 *size = sent;
446 curr_hd[0] = p_lastclip[1]; 609 curr_hd[0] = buf[1];
447 curr_hd[1] = p_lastclip[2]; 610 curr_hd[1] = buf[2];
448 curr_hd[2] = p_lastclip[3]; 611 curr_hd[2] = buf[3];
449 } 612 }
450 else if (p_silence != NULL /* silence clip available */ 613 else if (silence_offset > 0 /* silence clip available */
451 && p_lastclip != p_silence /* previous clip wasn't silence */ 614 && lastclip_offset != (unsigned long)silence_offset /* previous clip wasn't silence */
452 && !(p_lastclip >= p_thumbnail /* ..or thumbnail */ 615 && !(lastclip_offset >= voicefile_size /* ..or thumbnail */
453 && p_lastclip < p_thumbnail +size_for_thumbnail)) 616 && lastclip_offset < voicefile_size +size_for_thumbnail))
454 { /* add silence clip when queue runs empty playing a voice clip */ 617 { /* add silence clip when queue runs empty playing a voice clip */
455 queue[queue_write].buf = p_silence; 618 queue[queue_write].offset = silence_offset;
456 queue[queue_write].len = silence_len; 619 queue[queue_write].length = silence_length;
457 queue_write = (queue_write + 1) & QUEUE_MASK; 620 queue_write = (queue_write + 1) & QUEUE_MASK;
458 621
459 goto re_check; 622 goto re_check;
@@ -461,6 +624,7 @@ re_check:
461 else 624 else
462 { 625 {
463 *size = 0; /* end of data */ 626 *size = 0; /* end of data */
627 talk_handle_locked--;
464 } 628 }
465 talk_queue_unlock(); 629 talk_queue_unlock();
466} 630}
@@ -478,6 +642,8 @@ void talk_force_shutup(void)
478 unsigned char* pos; 642 unsigned char* pos;
479 unsigned char* search; 643 unsigned char* search;
480 unsigned char* end; 644 unsigned char* end;
645 int len;
646 unsigned clip_offset;
481 if (QUEUE_LEVEL == 0) /* has ended anyway */ 647 if (QUEUE_LEVEL == 0) /* has ended anyway */
482 return; 648 return;
483 649
@@ -486,13 +652,17 @@ void talk_force_shutup(void)
486#endif /* CONFIG_CPU == SH7034 */ 652#endif /* CONFIG_CPU == SH7034 */
487 /* search next frame boundary and continue up to there */ 653 /* search next frame boundary and continue up to there */
488 pos = search = mp3_get_pos(); 654 pos = search = mp3_get_pos();
489 end = queue[queue_read].buf + queue[queue_read].len; 655 clip_offset = queue[queue_read].offset;
656 if (clip_offset >= voicefile_size)
657 end = core_get_data(thumb_handle) + clip_offset - voicefile_size;
658 else
659 end = core_get_data(talk_handle) + clip_offset;
660 len = queue[queue_read].length;
490 661
491 if (pos >= queue[queue_read].buf 662 if (pos >= end && pos <= (end+len)) /* really our clip? */
492 && pos <= end) /* really our clip? */
493 { /* (for strange reasons this isn't nesessarily the case) */ 663 { /* (for strange reasons this isn't nesessarily the case) */
494 /* find the next frame boundary */ 664 /* find the next frame boundary */
495 while (search < end) /* search the remaining data */ 665 while (search < (end+len)) /* search the remaining data */
496 { 666 {
497 if (*search++ != 0xFF) /* quick search for frame sync byte */ 667 if (*search++ != 0xFF) /* quick search for frame sync byte */
498 continue; /* (this does the majority of the job) */ 668 continue; /* (this does the majority of the job) */
@@ -512,7 +682,7 @@ void talk_force_shutup(void)
512 sent = search-pos; 682 sent = search-pos;
513 683
514 queue_write = (queue_read + 1) & QUEUE_MASK; /* will be empty after next callback */ 684 queue_write = (queue_read + 1) & QUEUE_MASK; /* will be empty after next callback */
515 queue[queue_read].len = sent; /* current one ends after this */ 685 queue[queue_read].length = sent; /* current one ends after this */
516 686
517#if CONFIG_CPU == SH7034 687#if CONFIG_CPU == SH7034
518 DTCR3 = sent; /* let the DMA finish this frame */ 688 DTCR3 = sent; /* let the DMA finish this frame */
@@ -528,6 +698,7 @@ void talk_force_shutup(void)
528 mp3_play_stop(); 698 mp3_play_stop();
529 talk_queue_lock(); 699 talk_queue_lock();
530 queue_write = queue_read = 0; /* reset the queue */ 700 queue_write = queue_read = 0; /* reset the queue */
701 talk_handle_locked = MAX(talk_handle_locked-1, 0);
531 thumbnail_buf_used = 0; 702 thumbnail_buf_used = 0;
532 talk_queue_unlock(); 703 talk_queue_unlock();
533 need_shutup = false; 704 need_shutup = false;
@@ -541,8 +712,9 @@ void talk_shutup(void)
541} 712}
542 713
543/* schedule a clip, at the end or discard the existing queue */ 714/* schedule a clip, at the end or discard the existing queue */
544static void queue_clip(unsigned char* buf, long size, bool enqueue) 715static void queue_clip(unsigned long clip_offset, long size, bool enqueue)
545{ 716{
717 unsigned char *buf;
546 int queue_level; 718 int queue_level;
547 719
548 if (!enqueue) 720 if (!enqueue)
@@ -562,20 +734,25 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue)
562 734
563 if (queue_level < QUEUE_SIZE - 1) /* space left? */ 735 if (queue_level < QUEUE_SIZE - 1) /* space left? */
564 { 736 {
565 queue[queue_write].buf = buf; /* populate an entry */ 737 queue[queue_write].offset = clip_offset; /* populate an entry */
566 queue[queue_write].len = size; 738 queue[queue_write].length = size;
567 queue_write = (queue_write + 1) & QUEUE_MASK; 739 queue_write = (queue_write + 1) & QUEUE_MASK;
568 } 740 }
569 talk_queue_unlock(); 741 talk_queue_unlock();
570 742
571 if (queue_level == 0) 743 if (queue_level == 0)
572 { /* queue was empty, we have to do the initial start */ 744 { /* queue was empty, we have to do the initial start */
573 p_lastclip = buf; 745 lastclip_offset = clip_offset;
574#if CONFIG_CODEC != SWCODEC 746#if CONFIG_CODEC != SWCODEC
575 sent = MIN(size, 0xFFFF); /* DMA can do no more */ 747 sent = MIN(size, 0xFFFF); /* DMA can do no more */
576#else 748#else
577 sent = size; 749 sent = size;
578#endif 750#endif
751 talk_handle_locked++;
752 if (clip_offset >= voicefile_size)
753 buf = core_get_data(thumb_handle) + clip_offset - voicefile_size;
754 else
755 buf = core_get_data(talk_handle) + clip_offset;
579 mp3_play_data(buf, sent, mp3_callback); 756 mp3_play_data(buf, sent, mp3_callback);
580 curr_hd[0] = buf[1]; 757 curr_hd[0] = buf[1];
581 curr_hd[1] = buf[2]; 758 curr_hd[1] = buf[2];
@@ -594,53 +771,11 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue)
594 return; 771 return;
595} 772}
596 773
597static void alloc_thumbnail_buf(void)
598{
599 /* use the audio buffer now, need to release before loading a voice */
600 p_thumbnail = voicebuf;
601#if CONFIG_CODEC == SWCODEC
602 size_for_thumbnail = MAX_THUMBNAIL_BUFSIZE;
603#endif
604 thumbnail_buf_used = 0;
605}
606
607/* common code for talk_init() and talk_buffer_steal() */
608static void reset_state(void)
609{
610 queue_write = queue_read = 0; /* reset the queue */
611 p_voicefile = NULL; /* indicate no voicefile (trashed) */
612 p_thumbnail = NULL; /* no thumbnails either */
613
614#ifdef TALK_PARTIAL_LOAD
615 int i;
616 for(i=0; i<QUEUE_SIZE; i++)
617 buffered_id[i] = -1;
618#endif
619
620 p_silence = NULL; /* pause clip not accessible */
621 voicebuf = NULL; /* voice buffer is gone */
622}
623
624#if CONFIG_CODEC == SWCODEC
625static bool restore_state(void)
626{
627 if (!voicebuf)
628 {
629 size_t size;
630 audio_restore_playback(AUDIO_WANT_VOICE);
631 voicebuf = audio_get_buffer(true, &size);
632 audio_get_buffer(false, &size);
633 }
634
635 return !!voicebuf;
636}
637#endif /* CONFIG_CODEC == SWCODEC */
638
639
640/***************** Public implementation *****************/ 774/***************** Public implementation *****************/
641 775
642void talk_init(void) 776void talk_init(void)
643{ 777{
778 int filehandle;
644 talk_temp_disable_count = 0; 779 talk_temp_disable_count = 0;
645 if (talk_initialized && !strcasecmp(last_lang, global_settings.lang_file)) 780 if (talk_initialized && !strcasecmp(last_lang, global_settings.lang_file))
646 { 781 {
@@ -648,14 +783,6 @@ void talk_init(void)
648 return; 783 return;
649 } 784 }
650 785
651#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
652 if (filehandle >= 0)
653 {
654 close(filehandle);
655 filehandle = -1;
656 }
657#endif
658
659#if CONFIG_CODEC == SWCODEC 786#if CONFIG_CODEC == SWCODEC
660 if(!talk_initialized) 787 if(!talk_initialized)
661 mutex_init(&queue_mutex); 788 mutex_init(&queue_mutex);
@@ -665,138 +792,118 @@ void talk_init(void)
665 strlcpy((char *)last_lang, (char *)global_settings.lang_file, 792 strlcpy((char *)last_lang, (char *)global_settings.lang_file,
666 MAX_FILENAME); 793 MAX_FILENAME);
667 794
795 /* reset some states */
796 queue_write = queue_read = 0; /* reset the queue */
797 memset(&voicefile, 0, sizeof(voicefile));
798
799#ifdef TALK_PARTIAL_LOAD
800 for(int i=0; i<QUEUE_SIZE; i++)
801 buffered_id[i] = -1;
802#endif
803
804 silence_offset = -1; /* pause clip not accessible */
805 voicefile_size = has_voicefile = 0;
806 /* need to free these as their size depends on the voice file, and
807 * this function is called when the talk voice file changes */
808 if (index_handle > 0) index_handle = core_free(index_handle);
809 if (talk_handle > 0) talk_handle = core_free(talk_handle);
810 /* don't free thumb handle, it doesn't depend on the actual voice file
811 * and so we can re-use it if it's already allocated in any event */
812
668 filehandle = open_voicefile(); 813 filehandle = open_voicefile();
669 if (filehandle < 0) { 814 if (filehandle < 0)
670 has_voicefile = false;
671 voicefile_size = 0;
672 return; 815 return;
673 }
674 816
675 voicefile_size = filesize(filehandle); 817 if (!load_voicefile_index(filehandle))
676 818 goto out;
677 audio_get_buffer(false, NULL); /* Must tell audio to reinitialize */
678 reset_state(); /* use this for most of our inits */
679 819
680#ifdef TALK_PARTIAL_LOAD 820#ifdef TALK_PARTIAL_LOAD
681 size_t bufsize; 821 /* TALK_PARTIAL_LOAD loads the actual clip data later, and not all
682 char* buf = plugin_get_buffer(&bufsize); 822 * at once */
683 /* we won't load the full file, we only need the index */ 823 unsigned num_clips = voicefile.id1_max + voicefile.id2_max;
684 load_voicefile(true, buf, bufsize); 824 struct clip_entry *clips = core_get_data(index_handle);
685 if (!p_voicefile)
686 return;
687
688 unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
689 unsigned i;
690 int silence_size = 0; 825 int silence_size = 0;
691 826
692 for(i=0; i<clips; i++) { 827 for(unsigned i=0; i<num_clips; i++) {
693 int size = p_voicefile->index[i].size; 828 int size = clips[i].size;
694 if (size > max_clipsize) 829 if (size > max_clipsize)
695 max_clipsize = size; 830 max_clipsize = size;
696 if (i == VOICE_PAUSE) 831 if (i == VOICE_PAUSE)
697 silence_size = size; 832 silence_size = size;
698 } 833 }
699 834
700 voicefile_size = p_voicefile->table + clips * sizeof(struct clip_entry); 835 voicefile_size = voicefile.table + num_clips * sizeof(struct clip_entry);
701 voicefile_size += max_clipsize * QUEUE_SIZE + silence_size; 836 voicefile_size += max_clipsize * QUEUE_SIZE + silence_size;
702 p_voicefile = NULL; /* Don't pretend we can load talk clips just yet */
703#endif
704
705 837
706 /* test if we can open and if it fits in the audiobuffer */ 838 /* test if we can open and if it fits in the audiobuffer */
707 size_t audiobufsz = audio_buffer_available(); 839 size_t audiobufsz = audio_buffer_available();
708 if (voicefile_size <= audiobufsz) { 840 has_voicefile = audiobufsz >= voicefile_size;
709 has_voicefile = true; 841
710 } else { 842#else
711 has_voicefile = false; 843 /* load the compressed clip data into memory, in its entirety */
844 voicefile_size = filesize(filehandle);
845 if (!load_voicefile_data(filehandle, voicefile_size))
846 {
712 voicefile_size = 0; 847 voicefile_size = 0;
848 goto out;
713 } 849 }
714 850 has_voicefile = true;
715 close(filehandle); /* close again, this was just to detect presence */ 851#endif
716 filehandle = -1;
717 852
718#if CONFIG_CODEC == SWCODEC 853#if CONFIG_CODEC == SWCODEC
719 /* Safe to init voice playback engine now since we now know if talk is 854 /* Safe to init voice playback engine now since we now know if talk is
720 required or not */ 855 required or not */
721 voice_thread_init(); 856 voice_thread_init();
722#endif 857#endif
858
859out:
860 close(filehandle); /* close again, this was just to detect presence */
861 filehandle = -1;
723} 862}
724 863
725#if CONFIG_CODEC == SWCODEC 864#if CONFIG_CODEC == SWCODEC
726/* return if a voice codec is required or not */ 865/* return if a voice codec is required or not */
727bool talk_voice_required(void) 866bool talk_voice_required(void)
728{ 867{
729 return (voicefile_size != 0) /* Voice file is available */ 868 return (has_voicefile) /* Voice file is available */
730 || (global_settings.talk_dir_clip) /* Thumbnail clips are required */ 869 || (global_settings.talk_dir_clip) /* Thumbnail clips are required */
731 || (global_settings.talk_file_clip); 870 || (global_settings.talk_file_clip);
732} 871}
733#endif 872#endif
734 873
735/* return size of voice file */
736static size_t talk_get_buffer_size(void)
737{
738#if CONFIG_CODEC == SWCODEC
739 return voicefile_size + MAX_THUMBNAIL_BUFSIZE;
740#else
741 return audio_buffer_available();
742#endif
743}
744
745/* Sets the buffer for the voicefile and returns how many bytes of this
746 * buffer we will use for the voicefile */
747size_t talkbuf_init(char *bufstart)
748{
749 bool changed = voicebuf != bufstart;
750
751 if (changed) /* must reload voice file */
752 reset_state();
753
754 if (bufstart)
755 voicebuf = bufstart;
756
757 return talk_get_buffer_size();
758}
759
760/* somebody else claims the mp3 buffer, e.g. for regular play/record */ 874/* somebody else claims the mp3 buffer, e.g. for regular play/record */
761void talk_buffer_steal(void) 875void talk_buffer_set_policy(int policy)
762{ 876{
763#if CONFIG_CODEC != SWCODEC 877 switch(policy)
764 mp3_play_stop();
765#endif
766#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
767 if (filehandle >= 0)
768 { 878 {
769 close(filehandle); 879 case TALK_BUFFER_DEFAULT:
770 filehandle = -1; 880 case TALK_BUFFER_HOLD: give_buffer_away = false; break;
881 case TALK_BUFFER_LOOSE: give_buffer_away = true; break;
882 default: DEBUGF("Ignoring unknown policy\n"); break;
771 } 883 }
772#endif
773 reset_state();
774} 884}
775 885
776/* play a voice ID from voicefile */ 886/* play a voice ID from voicefile */
777int talk_id(int32_t id, bool enqueue) 887int talk_id(int32_t id, bool enqueue)
778{ 888{
889 int clip;
779 long clipsize; 890 long clipsize;
780 unsigned char* clipbuf;
781 int32_t unit; 891 int32_t unit;
782 int decimals; 892 int decimals;
783 893
784 if (talk_temp_disable_count > 0) 894 if (talk_temp_disable_count > 0)
785 return -1; /* talking has been disabled */ 895 return -1; /* talking has been disabled */
786#if CONFIG_CODEC == SWCODEC 896 if (!check_audio_status())
787 /* If talk buffer was stolen, it must be restored for voicefile's sake */
788 if (!restore_state())
789 return -1; /* cannot get any space */
790#else
791 if (audio_status()) /* busy, buffer in use */
792 return -1; 897 return -1;
793#endif
794
795 if (p_voicefile == NULL && has_voicefile) /* reload needed? */
796 load_voicefile(false, voicebuf, talk_get_buffer_size());
797 898
798 if (p_voicefile == NULL) /* still no voices? */ 899 if (has_voicefile && (talk_handle <= 0 || index_handle <= 0)) /* reload needed? */
799 return -1; 900 {
901 int fd = open_voicefile();
902 if (fd < 0
903 || !load_voicefile_index(fd)
904 || !load_voicefile_data(fd, voicefile_size))
905 return -1;
906 }
800 907
801 if (id == -1) /* -1 is an indication for silence */ 908 if (id == -1) /* -1 is an indication for silence */
802 return -1; 909 return -1;
@@ -814,8 +921,8 @@ int talk_id(int32_t id, bool enqueue)
814 return 0; /* and stop, end of special case */ 921 return 0; /* and stop, end of special case */
815 } 922 }
816 923
817 clipbuf = get_clip(id, &clipsize); 924 clip = get_clip(id, &clipsize);
818 if (clipbuf == NULL) 925 if (clip < 0)
819 return -1; /* not present */ 926 return -1; /* not present */
820 927
821#ifdef LOGF_ENABLE 928#ifdef LOGF_ENABLE
@@ -825,7 +932,7 @@ int talk_id(int32_t id, bool enqueue)
825 logf("\ntalk_id: Say '%s'\n", str(id)); 932 logf("\ntalk_id: Say '%s'\n", str(id));
826#endif 933#endif
827 934
828 queue_clip(clipbuf, clipsize, enqueue); 935 queue_clip(clip, clipsize, enqueue);
829 936
830 return 0; 937 return 0;
831} 938}
@@ -859,23 +966,18 @@ static int _talk_file(const char* filename,
859 int fd; 966 int fd;
860 int size; 967 int size;
861 int thumb_used; 968 int thumb_used;
969 char *buf;
862#if CONFIG_CODEC != SWCODEC 970#if CONFIG_CODEC != SWCODEC
863 struct mp3entry info; 971 struct mp3entry info;
864#endif 972#endif
865 973
866 if (talk_temp_disable_count > 0) 974 if (talk_temp_disable_count > 0)
867 return -1; /* talking has been disabled */ 975 return -1; /* talking has been disabled */
868#if CONFIG_CODEC == SWCODEC 976 if (!check_audio_status())
869 /* If talk buffer was stolen, it must be restored for thumbnail's sake */ 977 return -1;
870 if (!restore_state())
871 return -1; /* cannot get any space */
872#else
873 if (audio_status()) /* busy, buffer in use */
874 return -1;
875#endif
876 978
877 if (p_thumbnail == NULL || size_for_thumbnail <= 0) 979 if (!alloc_thumbnail_buf())
878 alloc_thumbnail_buf(); 980 return -1;
879 981
880#if CONFIG_CODEC != SWCODEC 982#if CONFIG_CODEC != SWCODEC
881 if(mp3info(&info, filename)) /* use this to find real start */ 983 if(mp3info(&info, filename)) /* use this to find real start */
@@ -905,8 +1007,10 @@ static int _talk_file(const char* filename,
905 lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ 1007 lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */
906#endif 1008#endif
907 1009
908 size = read(fd, p_thumbnail +thumb_used, 1010 talk_handle_locked++;
909 size_for_thumbnail -thumb_used); 1011 buf = core_get_data(thumb_handle);
1012 size = read(fd, buf+thumb_used, size_for_thumbnail - thumb_used);
1013 talk_handle_locked--;
910 close(fd); 1014 close(fd);
911 1015
912 /* ToDo: find audio, skip ID headers and trailers */ 1016 /* ToDo: find audio, skip ID headers and trailers */
@@ -914,7 +1018,8 @@ static int _talk_file(const char* filename,
914 if (size > 0) /* Don't play missing clips */ 1018 if (size > 0) /* Don't play missing clips */
915 { 1019 {
916#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) 1020#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
917 bitswap(p_thumbnail, size); 1021 /* bitswap doesnt yield() */
1022 bitswap(core_get_data(thumb_handle), size);
918#endif 1023#endif
919 if(prefix_ids) 1024 if(prefix_ids)
920 /* prefix thumbnail by speaking these ids, but only now 1025 /* prefix thumbnail by speaking these ids, but only now
@@ -922,9 +1027,9 @@ static int _talk_file(const char* filename,
922 spoken. */ 1027 spoken. */
923 talk_idarray(prefix_ids, true); 1028 talk_idarray(prefix_ids, true);
924 talk_queue_lock(); 1029 talk_queue_lock();
925 thumbnail_buf_used = thumb_used +size; 1030 thumbnail_buf_used = thumb_used + size;
926 talk_queue_unlock(); 1031 talk_queue_unlock();
927 queue_clip(p_thumbnail +thumb_used, size, true); 1032 queue_clip(voicefile_size + thumb_used, size, true);
928 } 1033 }
929 1034
930 return size; 1035 return size;
@@ -1012,10 +1117,8 @@ int talk_number(long n, bool enqueue)
1012 1117
1013 if (talk_temp_disable_count > 0) 1118 if (talk_temp_disable_count > 0)
1014 return -1; /* talking has been disabled */ 1119 return -1; /* talking has been disabled */
1015#if CONFIG_CODEC != SWCODEC 1120 if (!check_audio_status())
1016 if (audio_status()) /* busy, buffer in use */ 1121 return -1;
1017 return -1;
1018#endif
1019 1122
1020 if (!enqueue) 1123 if (!enqueue)
1021 talk_shutup(); /* cut off all the pending stuff */ 1124 talk_shutup(); /* cut off all the pending stuff */
@@ -1160,10 +1263,8 @@ int talk_value_decimal(long n, int unit, int decimals, bool enqueue)
1160 1263
1161 if (talk_temp_disable_count > 0) 1264 if (talk_temp_disable_count > 0)
1162 return -1; /* talking has been disabled */ 1265 return -1; /* talking has been disabled */
1163#if CONFIG_CODEC != SWCODEC 1266 if (!check_audio_status())
1164 if (audio_status()) /* busy, buffer in use */ 1267 return -1;
1165 return -1;
1166#endif
1167 1268
1168 /* special case for time duration */ 1269 /* special case for time duration */
1169 if (unit == UNIT_TIME) 1270 if (unit == UNIT_TIME)
@@ -1217,10 +1318,8 @@ int talk_spell(const char* spell, bool enqueue)
1217 1318
1218 if (talk_temp_disable_count > 0) 1319 if (talk_temp_disable_count > 0)
1219 return -1; /* talking has been disabled */ 1320 return -1; /* talking has been disabled */
1220#if CONFIG_CODEC != SWCODEC 1321 if (!check_audio_status())
1221 if (audio_status()) /* busy, buffer in use */ 1322 return -1;
1222 return -1;
1223#endif
1224 1323
1225 if (!enqueue) 1324 if (!enqueue)
1226 talk_shutup(); /* cut off all the pending stuff */ 1325 talk_shutup(); /* cut off all the pending stuff */
diff --git a/apps/talk.h b/apps/talk.h
index 55e7208f1d..28be71884f 100644
--- a/apps/talk.h
+++ b/apps/talk.h
@@ -71,6 +71,26 @@ enum {
71/* convenience macro to have both virtual pointer and ID as arguments */ 71/* convenience macro to have both virtual pointer and ID as arguments */
72#define STR(id) ID2P(id), id 72#define STR(id) ID2P(id), id
73 73
74/* Policy values for how hard to try to keep the talk/voice buffers.
75 * Affects how genereous talk.c is when it's asked for memory in
76 * shrink_callbacks().
77 *
78 * I.e. setting the policy to TALK_BUFFER_LOOSE, it will happily give its
79 * entire bufer away if asked for, e.g. due to a another module
80 * calling core_alloc_maximum(), TALK_BUFFER_HOLD on the other hand will
81 * make it keep the buffers so that a call to core_alloc_maximum() does not
82 * stop the speech-interface.
83 */
84enum talk_buffer_policies {
85 TALK_BUFFER_DEFAULT,
86 TALK_BUFFER_LOOSE,
87 TALK_BUFFER_HOLD,
88};
89
90/* This sets the actual policy. Call this before core_alloc_maximum() to
91 * get the desired outcome */
92void talk_buffer_set_policy(int policy);
93
74/* publish these strings, so they're stored only once (better than #define) */ 94/* publish these strings, so they're stored only once (better than #define) */
75extern const char* const dir_thumbnail_name; /* "_dirname.talk" */ 95extern const char* const dir_thumbnail_name; /* "_dirname.talk" */
76extern const char* const file_thumbnail_ext; /* ".talk" for file voicing */ 96extern const char* const file_thumbnail_ext; /* ".talk" for file voicing */
@@ -81,7 +101,6 @@ bool talk_voice_required(void); /* returns true if voice codec required */
81#endif 101#endif
82int talk_get_bufsize(void); /* get the loaded voice file size */ 102int talk_get_bufsize(void); /* get the loaded voice file size */
83size_t talkbuf_init(char* bufstart); 103size_t talkbuf_init(char* bufstart);
84void talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */
85bool is_voice_queued(void); /* Are there more voice clips to be spoken? */ 104bool is_voice_queued(void); /* Are there more voice clips to be spoken? */
86int talk_id(int32_t id, bool enqueue); /* play a voice ID from voicefont */ 105int talk_id(int32_t id, bool enqueue); /* play a voice ID from voicefont */
87/* play a thumbnail from file */ 106/* play a thumbnail from file */
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 8108f50939..57f7981b34 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -75,10 +75,6 @@ void audio_error_clear(void);
75int audio_get_file_pos(void); 75int audio_get_file_pos(void);
76void audio_beep(int duration); 76void audio_beep(int duration);
77 77
78/* Required call when audio buffer is required for some other purpose */
79/* implemented in apps but called from firmware(!) */
80unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size);
81
82#if CONFIG_CODEC == SWCODEC 78#if CONFIG_CODEC == SWCODEC
83void audio_next_dir(void); 79void audio_next_dir(void);
84void audio_prev_dir(void); 80void audio_prev_dir(void);
diff --git a/firmware/target/arm/pp/usb-fw-pp502x.c b/firmware/target/arm/pp/usb-fw-pp502x.c
index 44cce14389..acbb221cfd 100644
--- a/firmware/target/arm/pp/usb-fw-pp502x.c
+++ b/firmware/target/arm/pp/usb-fw-pp502x.c
@@ -230,21 +230,6 @@ void usb_insert_int(void)
230} 230}
231#endif /* USB_STATUS_BY_EVENT */ 231#endif /* USB_STATUS_BY_EVENT */
232 232
233#ifdef HAVE_BOOTLOADER_USB_MODE
234/* Replacement function that returns all unused memory after the bootloader
235 * because the storage driver uses the audio buffer */
236extern unsigned char freebuffer[];
237extern unsigned char freebufferend[];
238unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
239{
240 if (buffer_size)
241 *buffer_size = freebufferend - freebuffer + 1;
242
243 return freebuffer;
244 (void)talk_buf;
245}
246#endif /* HAVE_BOOTLOADER_USB_MODE */
247
248void usb_drv_int_enable(bool enable) 233void usb_drv_int_enable(bool enable)
249{ 234{
250 /* enable/disable USB IRQ in CPU */ 235 /* enable/disable USB IRQ in CPU */
diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c
index d1279d0ee1..a3b867319d 100644
--- a/firmware/usbstack/usb_storage.c
+++ b/firmware/usbstack/usb_storage.c
@@ -34,6 +34,7 @@
34#if CONFIG_RTC 34#if CONFIG_RTC
35#include "timefuncs.h" 35#include "timefuncs.h"
36#endif 36#endif
37#include "core_alloc.h"
37 38
38#ifdef USB_USE_RAMDISK 39#ifdef USB_USE_RAMDISK
39#define RAMDISK_SIZE 2048 40#define RAMDISK_SIZE 2048
@@ -430,6 +431,7 @@ int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size)
430 return (dest - orig_dest); 431 return (dest - orig_dest);
431} 432}
432 433
434static int usb_handle;
433void usb_storage_init_connection(void) 435void usb_storage_init_connection(void)
434{ 436{
435 logf("ums: set config"); 437 logf("ums: set config");
@@ -452,13 +454,17 @@ void usb_storage_init_connection(void)
452#else 454#else
453 /* TODO : check if bufsize is at least 32K ? */ 455 /* TODO : check if bufsize is at least 32K ? */
454 size_t bufsize; 456 size_t bufsize;
455 unsigned char * audio_buffer; 457 unsigned char * buffer;
458 /* dummy ops with no callbacks, needed because by
459 * default buflib buffers can be moved around which must be avoided */
460 static struct buflib_callbacks dummy_ops;
456 461
457 audio_buffer = audio_get_buffer(false,&bufsize); 462 usb_handle = core_alloc_maximum("usb storage", &bufsize, &dummy_ops);
463 buffer = core_get_data(usb_handle);
458#if defined(UNCACHED_ADDR) && CONFIG_CPU != AS3525 464#if defined(UNCACHED_ADDR) && CONFIG_CPU != AS3525
459 cbw_buffer = (void *)UNCACHED_ADDR((unsigned int)(audio_buffer+31) & 0xffffffe0); 465 cbw_buffer = (void *)UNCACHED_ADDR((unsigned int)(buffer+31) & 0xffffffe0);
460#else 466#else
461 cbw_buffer = (void *)((unsigned int)(audio_buffer+31) & 0xffffffe0); 467 cbw_buffer = (void *)((unsigned int)(buffer+31) & 0xffffffe0);
462#endif 468#endif
463 tb.transfer_buffer = cbw_buffer + MAX_CBW_SIZE; 469 tb.transfer_buffer = cbw_buffer + MAX_CBW_SIZE;
464 commit_discard_dcache(); 470 commit_discard_dcache();
@@ -478,7 +484,8 @@ void usb_storage_init_connection(void)
478 484
479void usb_storage_disconnect(void) 485void usb_storage_disconnect(void)
480{ 486{
481 /* Empty for now */ 487 if (usb_handle > 0)
488 usb_handle = core_free(usb_handle);
482} 489}
483 490
484/* called by usb_core_transfer_complete() */ 491/* called by usb_core_transfer_complete() */