summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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() */