diff options
author | Thomas Martitz <kugel@rockbox.org> | 2013-05-30 11:24:16 +0200 |
---|---|---|
committer | Thomas Martitz <kugel@rockbox.org> | 2013-12-23 12:17:38 +0100 |
commit | 22e802e80048defd401462e062afcb10093ac793 (patch) | |
tree | 09d24f7eb2a3b18e6563e838398b2715394f7c4c | |
parent | 64b9e1fa7b645daa36ca0018dc168d4f871fd538 (diff) | |
download | rockbox-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.c | 2 | ||||
-rw-r--r-- | apps/mpeg.c | 72 | ||||
-rw-r--r-- | apps/playback.c | 176 | ||||
-rw-r--r-- | apps/playback.h | 6 | ||||
-rwxr-xr-x | apps/playlist.c | 87 | ||||
-rw-r--r-- | apps/plugin.c | 19 | ||||
-rw-r--r-- | apps/plugin.h | 1 | ||||
-rw-r--r-- | apps/radio/radioart.c | 40 | ||||
-rw-r--r-- | apps/recorder/pcm_record.c | 20 | ||||
-rw-r--r-- | apps/recorder/recording.c | 10 | ||||
-rw-r--r-- | apps/talk.c | 761 | ||||
-rw-r--r-- | apps/talk.h | 21 | ||||
-rw-r--r-- | firmware/export/audio.h | 4 | ||||
-rw-r--r-- | firmware/target/arm/pp/usb-fw-pp502x.c | 15 | ||||
-rw-r--r-- | firmware/usbstack/usb_storage.c | 17 |
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 | ||
568 | static 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 | |||
579 | unsigned 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. */ |
609 | static void generate_unbuffer_events(void) | 581 | static void generate_unbuffer_events(void) |
@@ -2755,7 +2727,6 @@ size_t audio_buffer_available(void) | |||
2755 | 2727 | ||
2756 | static void audio_reset_buffer_noalloc(void* buf, size_t bufsize) | 2728 | static 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 | ||
2770 | static void audio_reset_buffer(void) | 2740 | static 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 | ||
2819 | void audio_stop(void) | 2794 | void 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 | ||
2841 | void audio_hard_stop(void) | 2816 | void 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 */ |
731 | static void audio_reset_buffer_noalloc( | 730 | static 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 | ||
885 | static void audio_reset_buffer(enum audio_buffer_state state) | 881 | static 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 */ | ||
3516 | unsigned 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) */ | ||
3578 | bool 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); | |||
80 | void audio_set_crossfade(int enable); | 80 | void audio_set_crossfade(int enable); |
81 | #endif | 81 | #endif |
82 | 82 | ||
83 | enum | ||
84 | { | ||
85 | AUDIO_WANT_PLAYBACK = 0, | ||
86 | AUDIO_WANT_VOICE, | ||
87 | }; | ||
88 | bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */ | ||
89 | size_t audio_get_filebuflen(void); | 83 | size_t audio_get_filebuflen(void); |
90 | 84 | ||
91 | unsigned int playback_status(void); | 85 | unsigned 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 = ¤t_playlist; | 2106 | struct playlist_info* playlist = ¤t_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; | 2504 | out: |
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 | ||
804 | static int plugin_buffer_handle; | ||
805 | |||
804 | int plugin_load(const char* plugin, const void* parameter) | 806 | int 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 | */ |
985 | void* plugin_get_audio_buffer(size_t *buffer_size) | 996 | void* 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 |
37 | struct radioart { | 38 | struct radioart { |
@@ -158,14 +159,49 @@ static void buffer_reset_handler(void *data) | |||
158 | (void)data; | 159 | (void)data; |
159 | } | 160 | } |
160 | 161 | ||
162 | static 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 | |||
192 | static struct buflib_callbacks radioart_ops = { | ||
193 | .shrink_callback = shrink_callback, | ||
194 | }; | ||
195 | |||
161 | void radioart_init(bool entering_screen) | 196 | void 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 | ||
1408 | static int pcmrec_handle; | ||
1405 | /* Q_AUDIO_INIT_RECORDING */ | 1409 | /* Q_AUDIO_INIT_RECORDING */ |
1406 | static void on_init_recording(void) | 1410 | static 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 | ||
692 | void rec_set_recording_options(struct audio_recording_options *options) | 692 | void 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 | ||
98 | struct voicefile /* file format of our voice file */ | 98 | struct 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 | ||
110 | struct 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 |
128 | static unsigned char *clip_buffer; | ||
129 | static long max_clipsize; /* size of the biggest clip */ | 120 | static long max_clipsize; /* size of the biggest clip */ |
130 | static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */ | 121 | static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */ |
131 | static uint8_t clip_age[QUEUE_SIZE]; | 122 | static 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 | ||
137 | static char* voicebuf; /* root pointer to our buffer */ | ||
138 | static 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. */ |
140 | static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in | 129 | static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in |
141 | thumbnail buffer */ | 130 | thumbnail buffer */ |
142 | static long size_for_thumbnail; /* total thumbnail buffer size */ | 131 | static long size_for_thumbnail; /* total thumbnail buffer size */ |
143 | static struct voicefile* p_voicefile; /* loaded voicefile */ | 132 | static struct voicefile_header voicefile; /* loaded voicefile */ |
144 | static bool has_voicefile; /* a voicefile file is present */ | 133 | static bool has_voicefile; /* a voicefile file is present */ |
145 | static bool need_shutup; /* is there possibly any voice playing to be shutup */ | 134 | static bool need_shutup; /* is there possibly any voice playing to be shutup */ |
146 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | ||
147 | static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */ | 135 | static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */ |
148 | static int queue_write; /* write index of queue, by application */ | 136 | static int queue_write; /* write index of queue, by application */ |
149 | static int queue_read; /* read index of queue, by ISR context */ | 137 | static 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 */ |
159 | static int sent; /* how many bytes handed over to playback, owned by ISR */ | 147 | static int sent; /* how many bytes handed over to playback, owned by ISR */ |
160 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ | 148 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ |
161 | static int filehandle = -1; /* global, so we can keep the file open if needed */ | 149 | static int silence_offset; /* VOICE_PAUSE clip, used for termination */ |
162 | static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */ | 150 | static long silence_length; /* length of the VOICE_PAUSE clip */ |
163 | static long silence_len; /* length of the VOICE_PAUSE clip */ | 151 | static unsigned long lastclip_offset; /* address of latest clip, for silence add */ |
164 | static unsigned char* p_lastclip; /* address of latest clip, for silence add */ | ||
165 | static unsigned long voicefile_size = 0; /* size of the loaded voice file */ | ||
166 | static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ | 152 | static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ |
167 | static bool talk_initialized; /* true if talk_init has been called */ | 153 | static bool talk_initialized; /* true if talk_init has been called */ |
154 | static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */ | ||
168 | static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */ | 155 | static 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 */ | ||
159 | static unsigned long voicefile_size; | ||
160 | |||
161 | struct queue_entry /* one entry of the internal queue */ | ||
162 | { | ||
163 | int offset, length; | ||
164 | }; | ||
165 | |||
166 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | ||
169 | 167 | ||
170 | 168 | ||
171 | /***************** Private implementation *****************/ | 169 | /***************** Private implementation *****************/ |
172 | 170 | ||
171 | static int thumb_handle; | ||
172 | static 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 */ | ||
177 | static 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 */ | ||
188 | static 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 | |||
203 | static 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 | |||
211 | static 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 | |||
230 | static 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 | |||
239 | static int index_handle, index_handle_locked; | ||
240 | static 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 | |||
248 | static 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 | |||
264 | static struct buflib_callbacks index_ops = { | ||
265 | .move_callback = index_move_callback, | ||
266 | .shrink_callback = index_shrink_callback, | ||
267 | }; | ||
268 | |||
173 | static int open_voicefile(void) | 269 | static 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 */ |
191 | static unsigned char* get_clip(long id, long* p_size) | 287 | static 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 | 394 | static bool load_index_table(int fd, const struct voicefile_header *hdr) | |
286 | /* load the voice file into the mp3 buffer */ | ||
287 | static 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 | |||
439 | static 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)) | 453 | static 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 | 477 | static 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 */ | ||
504 | static 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 | |||
528 | static 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; |
397 | load_err: | 554 | |
398 | p_voicefile = NULL; | 555 | load_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 */ |
410 | static void mp3_callback(const void** start, size_t* size) | 562 | static 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 */ |
544 | static void queue_clip(unsigned char* buf, long size, bool enqueue) | 715 | static 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 | ||
597 | static 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() */ | ||
608 | static 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 | ||
625 | static 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 | ||
642 | void talk_init(void) | 776 | void 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 | |||
859 | out: | ||
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 */ |
727 | bool talk_voice_required(void) | 866 | bool 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 */ | ||
736 | static 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 */ | ||
747 | size_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 */ |
761 | void talk_buffer_steal(void) | 875 | void 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 */ |
777 | int talk_id(int32_t id, bool enqueue) | 887 | int 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 | */ | ||
84 | enum 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 */ | ||
92 | void 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) */ |
75 | extern const char* const dir_thumbnail_name; /* "_dirname.talk" */ | 95 | extern const char* const dir_thumbnail_name; /* "_dirname.talk" */ |
76 | extern const char* const file_thumbnail_ext; /* ".talk" for file voicing */ | 96 | extern 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 |
82 | int talk_get_bufsize(void); /* get the loaded voice file size */ | 102 | int talk_get_bufsize(void); /* get the loaded voice file size */ |
83 | size_t talkbuf_init(char* bufstart); | 103 | size_t talkbuf_init(char* bufstart); |
84 | void talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */ | ||
85 | bool is_voice_queued(void); /* Are there more voice clips to be spoken? */ | 104 | bool is_voice_queued(void); /* Are there more voice clips to be spoken? */ |
86 | int talk_id(int32_t id, bool enqueue); /* play a voice ID from voicefont */ | 105 | int 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); | |||
75 | int audio_get_file_pos(void); | 75 | int audio_get_file_pos(void); |
76 | void audio_beep(int duration); | 76 | void 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(!) */ | ||
80 | unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size); | ||
81 | |||
82 | #if CONFIG_CODEC == SWCODEC | 78 | #if CONFIG_CODEC == SWCODEC |
83 | void audio_next_dir(void); | 79 | void audio_next_dir(void); |
84 | void audio_prev_dir(void); | 80 | void 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 */ | ||
236 | extern unsigned char freebuffer[]; | ||
237 | extern unsigned char freebufferend[]; | ||
238 | unsigned 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 | |||
248 | void usb_drv_int_enable(bool enable) | 233 | void 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 | ||
434 | static int usb_handle; | ||
433 | void usb_storage_init_connection(void) | 435 | void 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 | ||
479 | void usb_storage_disconnect(void) | 485 | void 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() */ |