diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2012-05-02 17:22:28 -0400 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2012-05-02 17:22:28 -0400 |
commit | da6cebb6b0b17b4a75a2bd4f51b7cf70b5dafe40 (patch) | |
tree | df0eb18120c38ec7b08d3ae1e0837f0781065e87 | |
parent | 3d3a144cf68186fd34f7bf11181b7757c7a6018d (diff) | |
download | rockbox-da6cebb6b0b17b4a75a2bd4f51b7cf70b5dafe40.tar.gz rockbox-da6cebb6b0b17b4a75a2bd4f51b7cf70b5dafe40.zip |
Use buflib for the allocation of voice PCM resources.
Buffers are not allocated and thread is not created until the first
call where voice is required.
Adds a different callback (sync_callback) to buflib so that other
sorts of synchonization are possible, such as briefly locking-out the
PCM callback for a buffer move. It's sort of a messy addition but it
is needed so voice decoding won't have to be stopped when its buffer
is moved.
Change-Id: I4d4d8c35eed5dd15fb7ee7df9323af3d036e92b3
-rw-r--r-- | apps/gui/icon.c | 2 | ||||
-rw-r--r-- | apps/gui/skin_engine/skin_backdrops.c | 2 | ||||
-rw-r--r-- | apps/gui/skin_engine/skin_parser.c | 2 | ||||
-rw-r--r-- | apps/playback.c | 95 | ||||
-rw-r--r-- | apps/talk.c | 58 | ||||
-rw-r--r-- | apps/voice_thread.c | 153 | ||||
-rw-r--r-- | apps/voice_thread.h | 5 | ||||
-rw-r--r-- | firmware/buflib.c | 28 | ||||
-rw-r--r-- | firmware/export/mp3_playback.h | 3 | ||||
-rw-r--r-- | firmware/export/pcm_mixer.h | 6 | ||||
-rw-r--r-- | firmware/font.c | 2 | ||||
-rw-r--r-- | firmware/include/buflib.h | 6 | ||||
-rw-r--r-- | firmware/pcm_mixer.c | 10 |
13 files changed, 242 insertions, 130 deletions
diff --git a/apps/gui/icon.c b/apps/gui/icon.c index f11de7fff1..46da38b765 100644 --- a/apps/gui/icon.c +++ b/apps/gui/icon.c | |||
@@ -175,7 +175,7 @@ static int buflib_move_callback(int handle, void* current, void* new) | |||
175 | } | 175 | } |
176 | return BUFLIB_CB_OK; | 176 | return BUFLIB_CB_OK; |
177 | } | 177 | } |
178 | static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL}; | 178 | static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL}; |
179 | 179 | ||
180 | static void load_icons(const char* filename, enum Iconset iconset, | 180 | static void load_icons(const char* filename, enum Iconset iconset, |
181 | enum screen_type screen) | 181 | enum screen_type screen) |
diff --git a/apps/gui/skin_engine/skin_backdrops.c b/apps/gui/skin_engine/skin_backdrops.c index 15b68589a9..edb895c56f 100644 --- a/apps/gui/skin_engine/skin_backdrops.c +++ b/apps/gui/skin_engine/skin_backdrops.c | |||
@@ -60,7 +60,7 @@ static int buflib_move_callback(int handle, void* current, void* new) | |||
60 | skin_backdrop_show(current_lcd_backdrop[i]); | 60 | skin_backdrop_show(current_lcd_backdrop[i]); |
61 | return BUFLIB_CB_OK; | 61 | return BUFLIB_CB_OK; |
62 | } | 62 | } |
63 | static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL}; | 63 | static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL}; |
64 | static bool first_go = true; | 64 | static bool first_go = true; |
65 | void skin_backdrop_init(void) | 65 | void skin_backdrop_init(void) |
66 | { | 66 | { |
diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c index 0ebea67188..a2a360de0e 100644 --- a/apps/gui/skin_engine/skin_parser.c +++ b/apps/gui/skin_engine/skin_parser.c | |||
@@ -1655,7 +1655,7 @@ static int buflib_move_callback(int handle, void* current, void* new) | |||
1655 | return BUFLIB_CB_CANNOT_MOVE; | 1655 | return BUFLIB_CB_CANNOT_MOVE; |
1656 | return BUFLIB_CB_OK; | 1656 | return BUFLIB_CB_OK; |
1657 | } | 1657 | } |
1658 | static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL}; | 1658 | static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL}; |
1659 | static void lock_handle(int handle) | 1659 | static void lock_handle(int handle) |
1660 | { | 1660 | { |
1661 | currently_loading_handle = handle; | 1661 | currently_loading_handle = handle; |
diff --git a/apps/playback.c b/apps/playback.c index 997a374410..bd7dd813c9 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -753,11 +753,12 @@ size_t audio_buffer_available(void) | |||
753 | 753 | ||
754 | /* Set up the audio buffer for playback | 754 | /* Set up the audio buffer for playback |
755 | * filebuflen must be pre-initialized with the maximum size */ | 755 | * filebuflen must be pre-initialized with the maximum size */ |
756 | static void audio_reset_buffer_noalloc(void* filebuf) | 756 | static void audio_reset_buffer_noalloc( |
757 | void *filebuf, enum audio_buffer_state state) | ||
757 | { | 758 | { |
758 | /* | 759 | /* |
759 | * Layout audio buffer as follows: | 760 | * Layout audio buffer as follows: |
760 | * [[|TALK]|SCRATCH|BUFFERING|PCM|[VOICE|]] | 761 | * [[|TALK]|SCRATCH|BUFFERING|PCM] |
761 | */ | 762 | */ |
762 | 763 | ||
763 | /* see audio_get_recording_buffer if this is modified */ | 764 | /* see audio_get_recording_buffer if this is modified */ |
@@ -770,7 +771,8 @@ static void audio_reset_buffer_noalloc(void* filebuf) | |||
770 | /* Initially set up file buffer as all space available */ | 771 | /* Initially set up file buffer as all space available */ |
771 | size_t allocsize; | 772 | size_t allocsize; |
772 | 773 | ||
773 | /* Subtract whatever voice needs */ | 774 | /* Subtract whatever voice needs (we're called when promoting |
775 | the state only) */ | ||
774 | allocsize = talkbuf_init(filebuf); | 776 | allocsize = talkbuf_init(filebuf); |
775 | allocsize = ALIGN_UP(allocsize, sizeof (intptr_t)); | 777 | allocsize = ALIGN_UP(allocsize, sizeof (intptr_t)); |
776 | if (allocsize > filebuflen) | 778 | if (allocsize > filebuflen) |
@@ -779,41 +781,33 @@ static void audio_reset_buffer_noalloc(void* filebuf) | |||
779 | filebuf += allocsize; | 781 | filebuf += allocsize; |
780 | filebuflen -= allocsize; | 782 | filebuflen -= allocsize; |
781 | 783 | ||
782 | if (talk_voice_required()) | 784 | if (state == AUDIOBUF_STATE_INITIALIZED) |
783 | { | 785 | { |
784 | /* Need a space for voice PCM output */ | 786 | /* Subtract whatever the pcm buffer says it used plus the guard |
785 | allocsize = voicebuf_init(filebuf + filebuflen); | 787 | buffer */ |
788 | allocsize = pcmbuf_init(filebuf + filebuflen); | ||
786 | 789 | ||
790 | /* Make sure filebuflen is a pointer sized multiple after | ||
791 | adjustment */ | ||
787 | allocsize = ALIGN_UP(allocsize, sizeof (intptr_t)); | 792 | allocsize = ALIGN_UP(allocsize, sizeof (intptr_t)); |
788 | if (allocsize > filebuflen) | 793 | if (allocsize > filebuflen) |
789 | goto bufpanic; | 794 | goto bufpanic; |
790 | 795 | ||
791 | filebuflen -= allocsize; | 796 | filebuflen -= allocsize; |
792 | } | ||
793 | |||
794 | /* Subtract whatever the pcm buffer says it used plus the guard buffer */ | ||
795 | allocsize = pcmbuf_init(filebuf + filebuflen); | ||
796 | |||
797 | /* Make sure filebuflen is a pointer sized multiple after adjustment */ | ||
798 | allocsize = ALIGN_UP(allocsize, sizeof (intptr_t)); | ||
799 | if (allocsize > filebuflen) | ||
800 | goto bufpanic; | ||
801 | 797 | ||
802 | filebuflen -= allocsize; | 798 | /* Scratch memory */ |
803 | 799 | allocsize = scratch_mem_size(); | |
804 | /* Scratch memory */ | 800 | if (allocsize > filebuflen) |
805 | allocsize = scratch_mem_size(); | 801 | goto bufpanic; |
806 | if (allocsize > filebuflen) | ||
807 | goto bufpanic; | ||
808 | 802 | ||
809 | scratch_mem_init(filebuf); | 803 | scratch_mem_init(filebuf); |
810 | filebuf += allocsize; | 804 | filebuf += allocsize; |
811 | filebuflen -= allocsize; | 805 | filebuflen -= allocsize; |
812 | 806 | ||
813 | buffering_reset(filebuf, filebuflen); | 807 | buffering_reset(filebuf, filebuflen); |
808 | } | ||
814 | 809 | ||
815 | /* Clear any references to the file buffer */ | 810 | buffer_state = state; |
816 | buffer_state = AUDIOBUF_STATE_INITIALIZED; | ||
817 | 811 | ||
818 | #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) | 812 | #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) |
819 | /* Make sure everything adds up - yes, some info is a bit redundant but | 813 | /* Make sure everything adds up - yes, some info is a bit redundant but |
@@ -891,11 +885,12 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s | |||
891 | { | 885 | { |
892 | case BUFLIB_SHRINK_POS_BACK: | 886 | case BUFLIB_SHRINK_POS_BACK: |
893 | core_shrink(handle, start, size); | 887 | core_shrink(handle, start, size); |
894 | audio_reset_buffer_noalloc(start); | 888 | audio_reset_buffer_noalloc(start, buffer_state); |
895 | break; | 889 | break; |
896 | case BUFLIB_SHRINK_POS_FRONT: | 890 | case BUFLIB_SHRINK_POS_FRONT: |
897 | core_shrink(handle, start + wanted_size, size); | 891 | core_shrink(handle, start + wanted_size, size); |
898 | audio_reset_buffer_noalloc(start + wanted_size); | 892 | audio_reset_buffer_noalloc(start + wanted_size, |
893 | buffer_state); | ||
899 | break; | 894 | break; |
900 | } | 895 | } |
901 | if (playing || play_queued) | 896 | if (playing || play_queued) |
@@ -912,7 +907,7 @@ static struct buflib_callbacks ops = { | |||
912 | .shrink_callback = shrink_callback, | 907 | .shrink_callback = shrink_callback, |
913 | }; | 908 | }; |
914 | 909 | ||
915 | static void audio_reset_buffer(void) | 910 | static void audio_reset_buffer(enum audio_buffer_state state) |
916 | { | 911 | { |
917 | if (audiobuf_handle > 0) | 912 | if (audiobuf_handle > 0) |
918 | { | 913 | { |
@@ -922,7 +917,7 @@ static void audio_reset_buffer(void) | |||
922 | audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); | 917 | audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); |
923 | unsigned char *filebuf = core_get_data(audiobuf_handle); | 918 | unsigned char *filebuf = core_get_data(audiobuf_handle); |
924 | 919 | ||
925 | audio_reset_buffer_noalloc(filebuf); | 920 | audio_reset_buffer_noalloc(filebuf, state); |
926 | } | 921 | } |
927 | 922 | ||
928 | /* Set the buffer margin to begin rebuffering when 'seconds' from empty */ | 923 | /* Set the buffer margin to begin rebuffering when 'seconds' from empty */ |
@@ -2051,7 +2046,7 @@ static int audio_fill_file_buffer(void) | |||
2051 | file size shouldn't have changed so we can go straight from | 2046 | file size shouldn't have changed so we can go straight from |
2052 | AUDIOBUF_STATE_VOICED_ONLY to AUDIOBUF_STATE_INITIALIZED */ | 2047 | AUDIOBUF_STATE_VOICED_ONLY to AUDIOBUF_STATE_INITIALIZED */ |
2053 | if (buffer_state != AUDIOBUF_STATE_INITIALIZED) | 2048 | if (buffer_state != AUDIOBUF_STATE_INITIALIZED) |
2054 | audio_reset_buffer(); | 2049 | audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED); |
2055 | 2050 | ||
2056 | logf("Starting buffer fill"); | 2051 | logf("Starting buffer fill"); |
2057 | 2052 | ||
@@ -3649,14 +3644,9 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) | |||
3649 | logf("get buffer: audio"); | 3644 | logf("get buffer: audio"); |
3650 | /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or | 3645 | /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or |
3651 | still AUDIOBUF_STATE_INITIALIZED */ | 3646 | still AUDIOBUF_STATE_INITIALIZED */ |
3652 | /* Skip talk buffer and move pcm buffer to end to maximize available | 3647 | size_t talkbuf_size = talkbuf_init(buf); |
3653 | contiguous memory - no audio running means voice will not need the | 3648 | buf += talkbuf_size; /* Skip talk buffer */ |
3654 | swap space */ | ||
3655 | size_t talkbuf_size; | ||
3656 | buf += talkbuf_size = talkbuf_init(buf); | ||
3657 | filebuflen -= talkbuf_size; | 3649 | filebuflen -= talkbuf_size; |
3658 | filebuflen -= voicebuf_init(buf + filebuflen); | ||
3659 | |||
3660 | buffer_state = AUDIOBUF_STATE_VOICED_ONLY; | 3650 | buffer_state = AUDIOBUF_STATE_VOICED_ONLY; |
3661 | } | 3651 | } |
3662 | 3652 | ||
@@ -3673,19 +3663,18 @@ unsigned char * audio_get_recording_buffer(size_t *buffer_size) | |||
3673 | } | 3663 | } |
3674 | #endif /* HAVE_RECORDING */ | 3664 | #endif /* HAVE_RECORDING */ |
3675 | 3665 | ||
3676 | /* Restore audio buffer to a particular state (one more valid than the current | 3666 | /* Restore audio buffer to a particular state (promoting status) */ |
3677 | state) */ | ||
3678 | bool audio_restore_playback(int type) | 3667 | bool audio_restore_playback(int type) |
3679 | { | 3668 | { |
3680 | switch (type) | 3669 | switch (type) |
3681 | { | 3670 | { |
3682 | case AUDIO_WANT_PLAYBACK: | 3671 | case AUDIO_WANT_PLAYBACK: |
3683 | if (buffer_state != AUDIOBUF_STATE_INITIALIZED) | 3672 | if (buffer_state != AUDIOBUF_STATE_INITIALIZED) |
3684 | audio_reset_buffer(); | 3673 | audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED); |
3685 | return true; | 3674 | return true; |
3686 | case AUDIO_WANT_VOICE: | 3675 | case AUDIO_WANT_VOICE: |
3687 | if (buffer_state == AUDIOBUF_STATE_TRASHED) | 3676 | if (buffer_state == AUDIOBUF_STATE_TRASHED) |
3688 | audio_reset_buffer(); | 3677 | audio_reset_buffer(AUDIOBUF_STATE_VOICED_ONLY); |
3689 | return true; | 3678 | return true; |
3690 | default: | 3679 | default: |
3691 | return false; | 3680 | return false; |
@@ -3910,24 +3899,18 @@ void audio_init(void) | |||
3910 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list, | 3899 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list, |
3911 | audio_thread_id); | 3900 | audio_thread_id); |
3912 | 3901 | ||
3913 | #ifdef PLAYBACK_VOICE | 3902 | /* Initialize the track buffering system */ |
3914 | voice_thread_init(); | 3903 | track_list_init(); |
3915 | #endif | 3904 | buffering_init(); |
3916 | |||
3917 | /* audio_reset_buffer must know the size of voice buffer so init | ||
3918 | talk first */ | ||
3919 | talk_init(); | ||
3920 | 3905 | ||
3921 | #ifdef HAVE_CROSSFADE | 3906 | #ifdef HAVE_CROSSFADE |
3922 | /* Set crossfade setting for next buffer init which should be about... */ | 3907 | /* Set crossfade setting for next buffer init which should be about... */ |
3923 | pcmbuf_request_crossfade_enable(global_settings.crossfade); | 3908 | pcmbuf_request_crossfade_enable(global_settings.crossfade); |
3924 | #endif | 3909 | #endif |
3925 | 3910 | ||
3926 | /* Initialize the buffering system */ | 3911 | /* ...now...audio_reset_buffer must know the size of voicefile buffer so |
3927 | track_list_init(); | 3912 | init talk first which will init the buffers */ |
3928 | buffering_init(); | 3913 | talk_init(); |
3929 | /* ...now! Set up the buffers */ | ||
3930 | audio_reset_buffer(); | ||
3931 | 3914 | ||
3932 | /* Probably safe to say */ | 3915 | /* Probably safe to say */ |
3933 | audio_is_initialized = true; | 3916 | audio_is_initialized = true; |
diff --git a/apps/talk.c b/apps/talk.c index 4b0975083d..f3ca967d3a 100644 --- a/apps/talk.c +++ b/apps/talk.c | |||
@@ -31,7 +31,11 @@ | |||
31 | #include "kernel.h" | 31 | #include "kernel.h" |
32 | #include "settings.h" | 32 | #include "settings.h" |
33 | #include "settings_list.h" | 33 | #include "settings_list.h" |
34 | #if CONFIG_CODEC == SWCODEC | ||
35 | #include "voice_thread.h" | ||
36 | #else | ||
34 | #include "mp3_playback.h" | 37 | #include "mp3_playback.h" |
38 | #endif | ||
35 | #include "audio.h" | 39 | #include "audio.h" |
36 | #include "lang.h" | 40 | #include "lang.h" |
37 | #include "talk.h" | 41 | #include "talk.h" |
@@ -43,7 +47,6 @@ | |||
43 | #include "plugin.h" /* plugin_get_buffer() */ | 47 | #include "plugin.h" /* plugin_get_buffer() */ |
44 | #include "debug.h" | 48 | #include "debug.h" |
45 | 49 | ||
46 | |||
47 | /* Memory layout varies between targets because the | 50 | /* Memory layout varies between targets because the |
48 | Archos (MASCODEC) devices cannot mix voice and audio playback | 51 | Archos (MASCODEC) devices cannot mix voice and audio playback |
49 | 52 | ||
@@ -589,7 +592,6 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue) | |||
589 | return; | 592 | return; |
590 | } | 593 | } |
591 | 594 | ||
592 | |||
593 | static void alloc_thumbnail_buf(void) | 595 | static void alloc_thumbnail_buf(void) |
594 | { | 596 | { |
595 | /* use the audio buffer now, need to release before loading a voice */ | 597 | /* use the audio buffer now, need to release before loading a voice */ |
@@ -614,8 +616,22 @@ static void reset_state(void) | |||
614 | #endif | 616 | #endif |
615 | 617 | ||
616 | p_silence = NULL; /* pause clip not accessible */ | 618 | p_silence = NULL; /* pause clip not accessible */ |
617 | voicebuf = NULL; | 619 | voicebuf = NULL; /* voice buffer is gone */ |
620 | } | ||
621 | |||
622 | #if CONFIG_CODEC == SWCODEC | ||
623 | static bool restore_state(void) | ||
624 | { | ||
625 | if (!voicebuf) | ||
626 | { | ||
627 | size_t size; | ||
628 | audio_restore_playback(AUDIO_WANT_VOICE); | ||
629 | voicebuf = audio_get_buffer(false, &size); | ||
630 | } | ||
631 | |||
632 | return !!voicebuf; | ||
618 | } | 633 | } |
634 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
619 | 635 | ||
620 | 636 | ||
621 | /***************** Public implementation *****************/ | 637 | /***************** Public implementation *****************/ |
@@ -655,7 +671,7 @@ void talk_init(void) | |||
655 | 671 | ||
656 | voicefile_size = filesize(filehandle); | 672 | voicefile_size = filesize(filehandle); |
657 | 673 | ||
658 | audio_get_buffer(false, NULL); /* Must tell audio to reinitialize */ | 674 | audio_get_buffer(false, NULL); /* Must tell audio to reinitialize */ |
659 | reset_state(); /* use this for most of our inits */ | 675 | reset_state(); /* use this for most of our inits */ |
660 | 676 | ||
661 | #ifdef TALK_PARTIAL_LOAD | 677 | #ifdef TALK_PARTIAL_LOAD |
@@ -693,9 +709,14 @@ void talk_init(void) | |||
693 | voicefile_size = 0; | 709 | voicefile_size = 0; |
694 | } | 710 | } |
695 | 711 | ||
696 | alloc_thumbnail_buf(); | ||
697 | close(filehandle); /* close again, this was just to detect presence */ | 712 | close(filehandle); /* close again, this was just to detect presence */ |
698 | filehandle = -1; | 713 | filehandle = -1; |
714 | |||
715 | #if CONFIG_CODEC == SWCODEC | ||
716 | /* Safe to init voice playback engine now since we now know if talk is | ||
717 | required or not */ | ||
718 | voice_thread_init(); | ||
719 | #endif | ||
699 | } | 720 | } |
700 | 721 | ||
701 | #if CONFIG_CODEC == SWCODEC | 722 | #if CONFIG_CODEC == SWCODEC |
@@ -709,7 +730,7 @@ bool talk_voice_required(void) | |||
709 | #endif | 730 | #endif |
710 | 731 | ||
711 | /* return size of voice file */ | 732 | /* return size of voice file */ |
712 | static int talk_get_buffer(void) | 733 | static size_t talk_get_buffer_size(void) |
713 | { | 734 | { |
714 | #if CONFIG_CODEC == SWCODEC | 735 | #if CONFIG_CODEC == SWCODEC |
715 | return voicefile_size + MAX_THUMBNAIL_BUFSIZE; | 736 | return voicefile_size + MAX_THUMBNAIL_BUFSIZE; |
@@ -726,10 +747,11 @@ size_t talkbuf_init(char *bufstart) | |||
726 | 747 | ||
727 | if (changed) /* must reload voice file */ | 748 | if (changed) /* must reload voice file */ |
728 | reset_state(); | 749 | reset_state(); |
750 | |||
729 | if (bufstart) | 751 | if (bufstart) |
730 | voicebuf = bufstart; | 752 | voicebuf = bufstart; |
731 | 753 | ||
732 | return talk_get_buffer(); | 754 | return talk_get_buffer_size(); |
733 | } | 755 | } |
734 | 756 | ||
735 | /* somebody else claims the mp3 buffer, e.g. for regular play/record */ | 757 | /* somebody else claims the mp3 buffer, e.g. for regular play/record */ |
@@ -748,29 +770,27 @@ void talk_buffer_steal(void) | |||
748 | reset_state(); | 770 | reset_state(); |
749 | } | 771 | } |
750 | 772 | ||
751 | |||
752 | /* play a voice ID from voicefile */ | 773 | /* play a voice ID from voicefile */ |
753 | int talk_id(int32_t id, bool enqueue) | 774 | int talk_id(int32_t id, bool enqueue) |
754 | { | 775 | { |
755 | long clipsize; | 776 | long clipsize; |
756 | int temp = talk_get_buffer(); | ||
757 | unsigned char* clipbuf; | 777 | unsigned char* clipbuf; |
758 | int32_t unit; | 778 | int32_t unit; |
759 | int decimals; | 779 | int decimals; |
760 | 780 | ||
761 | if (talk_temp_disable_count > 0) | 781 | if (talk_temp_disable_count > 0) |
762 | return -1; /* talking has been disabled */ | 782 | return -1; /* talking has been disabled */ |
763 | #if CONFIG_CODEC != SWCODEC | 783 | #if CONFIG_CODEC == SWCODEC |
784 | /* If talk buffer was stolen, it must be restored for voicefile's sake */ | ||
785 | if (!restore_state()) | ||
786 | return -1; /* cannot get any space */ | ||
787 | #else | ||
764 | if (audio_status()) /* busy, buffer in use */ | 788 | if (audio_status()) /* busy, buffer in use */ |
765 | return -1; | 789 | return -1; |
766 | #endif | 790 | #endif |
767 | 791 | ||
768 | /* try to get audio buffer until talkbuf_init() is called */ | 792 | if (p_voicefile == NULL && has_voicefile) /* reload needed? */ |
769 | if (!voicebuf) | 793 | load_voicefile(false, voicebuf, talk_get_buffer_size()); |
770 | voicebuf = audio_get_buffer(true, (size_t*)&temp); | ||
771 | |||
772 | if (p_voicefile == NULL && has_voicefile) | ||
773 | load_voicefile(false, voicebuf, MIN(talk_get_buffer(),temp)); /* reload needed */ | ||
774 | 794 | ||
775 | if (p_voicefile == NULL) /* still no voices? */ | 795 | if (p_voicefile == NULL) /* still no voices? */ |
776 | return -1; | 796 | return -1; |
@@ -842,7 +862,11 @@ static int _talk_file(const char* filename, | |||
842 | 862 | ||
843 | if (talk_temp_disable_count > 0) | 863 | if (talk_temp_disable_count > 0) |
844 | return -1; /* talking has been disabled */ | 864 | return -1; /* talking has been disabled */ |
845 | #if CONFIG_CODEC != SWCODEC | 865 | #if CONFIG_CODEC == SWCODEC |
866 | /* If talk buffer was stolen, it must be restored for thumbnail's sake */ | ||
867 | if (!restore_state()) | ||
868 | return -1; /* cannot get any space */ | ||
869 | #else | ||
846 | if (audio_status()) /* busy, buffer in use */ | 870 | if (audio_status()) /* busy, buffer in use */ |
847 | return -1; | 871 | return -1; |
848 | #endif | 872 | #endif |
diff --git a/apps/voice_thread.c b/apps/voice_thread.c index 48c6f4845d..d0d36b4064 100644 --- a/apps/voice_thread.c +++ b/apps/voice_thread.c | |||
@@ -20,6 +20,7 @@ | |||
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include <sys/types.h> | 21 | #include <sys/types.h> |
22 | #include "system.h" | 22 | #include "system.h" |
23 | #include "core_alloc.h" | ||
23 | #include "thread.h" | 24 | #include "thread.h" |
24 | #include "voice_thread.h" | 25 | #include "voice_thread.h" |
25 | #include "talk.h" | 26 | #include "talk.h" |
@@ -31,6 +32,10 @@ | |||
31 | #include "pcm_mixer.h" | 32 | #include "pcm_mixer.h" |
32 | #include "codecs/libspeex/speex/speex.h" | 33 | #include "codecs/libspeex/speex/speex.h" |
33 | 34 | ||
35 | /* Default number of native-frequency PCM frames to queue - adjust as | ||
36 | necessary per-target */ | ||
37 | #define VOICE_FRAMES 4 | ||
38 | |||
34 | /* Define any of these as "1" and uncomment the LOGF_ENABLE line to log | 39 | /* Define any of these as "1" and uncomment the LOGF_ENABLE line to log |
35 | regular and/or timeout messages */ | 40 | regular and/or timeout messages */ |
36 | #define VOICE_LOGQUEUES 0 | 41 | #define VOICE_LOGQUEUES 0 |
@@ -80,22 +85,10 @@ static struct event_queue voice_queue SHAREDBSS_ATTR; | |||
80 | static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR; | 85 | static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR; |
81 | static int quiet_counter SHAREDDATA_ATTR = 0; | 86 | static int quiet_counter SHAREDDATA_ATTR = 0; |
82 | 87 | ||
83 | /* Buffer for decoded samples */ | ||
84 | static spx_int16_t voice_output_buf[VOICE_FRAME_COUNT] MEM_ALIGN_ATTR; | ||
85 | |||
86 | #define VOICE_PCM_FRAME_COUNT ((NATIVE_FREQUENCY*VOICE_FRAME_COUNT + \ | 88 | #define VOICE_PCM_FRAME_COUNT ((NATIVE_FREQUENCY*VOICE_FRAME_COUNT + \ |
87 | VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE) | 89 | VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE) |
88 | #define VOICE_PCM_FRAME_SIZE (VOICE_PCM_FRAME_COUNT*2*sizeof (int16_t)) | 90 | #define VOICE_PCM_FRAME_SIZE (VOICE_PCM_FRAME_COUNT*2*sizeof (int16_t)) |
89 | 91 | ||
90 | /* Default number of native-frequency PCM frames to queue - adjust as | ||
91 | necessary per-target */ | ||
92 | #define VOICE_FRAMES 3 | ||
93 | |||
94 | /* Might have lookahead and be skipping samples, so size is needed */ | ||
95 | static size_t voicebuf_sizes[VOICE_FRAMES]; | ||
96 | static int16_t (* voicebuf)[2*VOICE_PCM_FRAME_COUNT]; | ||
97 | static unsigned int cur_buf_in, cur_buf_out; | ||
98 | |||
99 | /* Voice processing states */ | 92 | /* Voice processing states */ |
100 | enum voice_state | 93 | enum voice_state |
101 | { | 94 | { |
@@ -135,6 +128,7 @@ struct voice_thread_data | |||
135 | struct voice_info vi; /* Copy of clip data */ | 128 | struct voice_info vi; /* Copy of clip data */ |
136 | int lookahead; /* Number of samples to drop at start of clip */ | 129 | int lookahead; /* Number of samples to drop at start of clip */ |
137 | struct dsp_buffer src; /* Speex output buffer/input to DSP */ | 130 | struct dsp_buffer src; /* Speex output buffer/input to DSP */ |
131 | struct dsp_buffer *dst; /* Pointer to DSP output buffer for PCM */ | ||
138 | }; | 132 | }; |
139 | 133 | ||
140 | /* Functions called in their repective state that return the next state to | 134 | /* Functions called in their repective state that return the next state to |
@@ -143,10 +137,62 @@ static enum voice_state voice_message(struct voice_thread_data *td); | |||
143 | static enum voice_state voice_decode(struct voice_thread_data *td); | 137 | static enum voice_state voice_decode(struct voice_thread_data *td); |
144 | static enum voice_state voice_buffer_insert(struct voice_thread_data *td); | 138 | static enum voice_state voice_buffer_insert(struct voice_thread_data *td); |
145 | 139 | ||
140 | /* Might have lookahead and be skipping samples, so size is needed */ | ||
141 | static struct voice_buf | ||
142 | { | ||
143 | /* Buffer for decoded samples */ | ||
144 | spx_int16_t spx_outbuf[VOICE_FRAME_COUNT]; | ||
145 | /* Queue frame indexes */ | ||
146 | unsigned int frame_in, frame_out; | ||
147 | /* For PCM pointer adjustment */ | ||
148 | struct voice_thread_data *td; | ||
149 | /* Buffers for mixing voice */ | ||
150 | struct voice_pcm_frame | ||
151 | { | ||
152 | size_t size; | ||
153 | int16_t pcm[2*VOICE_PCM_FRAME_COUNT]; | ||
154 | } frames[VOICE_FRAMES]; | ||
155 | } *voice_buf = NULL; | ||
156 | |||
157 | static int voice_buf_hid = 0; | ||
158 | |||
159 | static int move_callback(int handle, void *current, void *new) | ||
160 | { | ||
161 | /* Have to adjust the pointers that point into things in voice_buf */ | ||
162 | off_t diff = new - current; | ||
163 | struct voice_thread_data *td = voice_buf->td; | ||
164 | td->src.p32[0] = SKIPBYTES(td->src.p32[0], diff); | ||
165 | td->src.p32[1] = SKIPBYTES(td->src.p32[1], diff); | ||
166 | if (td->dst != NULL) /* Only when calling dsp_process */ | ||
167 | td->dst->p16out = SKIPBYTES(td->dst->p16out, diff); | ||
168 | mixer_adjust_channel_address(PCM_MIXER_CHAN_VOICE, diff); | ||
169 | voice_buf = new; | ||
170 | |||
171 | return BUFLIB_CB_OK; | ||
172 | (void)handle; | ||
173 | }; | ||
174 | |||
175 | static void sync_callback(int handle, bool sync_on) | ||
176 | { | ||
177 | /* A move must not allow PCM to access the channel */ | ||
178 | if (sync_on) | ||
179 | pcm_play_lock(); | ||
180 | else | ||
181 | pcm_play_unlock(); | ||
182 | |||
183 | (void)handle; | ||
184 | } | ||
185 | |||
186 | static struct buflib_callbacks ops = | ||
187 | { | ||
188 | .move_callback = move_callback, | ||
189 | .sync_callback = sync_callback, | ||
190 | }; | ||
191 | |||
146 | /* Number of frames in queue */ | 192 | /* Number of frames in queue */ |
147 | static inline int voice_unplayed_frames(void) | 193 | static inline int voice_unplayed_frames(void) |
148 | { | 194 | { |
149 | return cur_buf_in - cur_buf_out; | 195 | return voice_buf->frame_in - voice_buf->frame_out; |
150 | } | 196 | } |
151 | 197 | ||
152 | /* Mixer channel callback */ | 198 | /* Mixer channel callback */ |
@@ -155,10 +201,11 @@ static void voice_pcm_callback(const void **start, size_t *size) | |||
155 | if (voice_unplayed_frames() == 0) | 201 | if (voice_unplayed_frames() == 0) |
156 | return; /* Done! */ | 202 | return; /* Done! */ |
157 | 203 | ||
158 | unsigned int i = ++cur_buf_out % VOICE_FRAMES; | 204 | struct voice_pcm_frame *frame = |
205 | &voice_buf->frames[++voice_buf->frame_out % VOICE_FRAMES]; | ||
159 | 206 | ||
160 | *start = voicebuf[i]; | 207 | *start = frame->pcm; |
161 | *size = voicebuf_sizes[i]; | 208 | *size = frame->size; |
162 | } | 209 | } |
163 | 210 | ||
164 | /* Start playback of voice channel if not already playing */ | 211 | /* Start playback of voice channel if not already playing */ |
@@ -168,16 +215,18 @@ static void voice_start_playback(void) | |||
168 | voice_unplayed_frames() <= 0) | 215 | voice_unplayed_frames() <= 0) |
169 | return; | 216 | return; |
170 | 217 | ||
171 | unsigned int i = cur_buf_out % VOICE_FRAMES; | 218 | struct voice_pcm_frame *frame = |
219 | &voice_buf->frames[voice_buf->frame_out % VOICE_FRAMES]; | ||
220 | |||
172 | mixer_channel_play_data(PCM_MIXER_CHAN_VOICE, voice_pcm_callback, | 221 | mixer_channel_play_data(PCM_MIXER_CHAN_VOICE, voice_pcm_callback, |
173 | voicebuf[i], voicebuf_sizes[i]); | 222 | frame->pcm, frame->size); |
174 | } | 223 | } |
175 | 224 | ||
176 | /* Stop the voice channel */ | 225 | /* Stop the voice channel */ |
177 | static void voice_stop_playback(void) | 226 | static void voice_stop_playback(void) |
178 | { | 227 | { |
179 | mixer_channel_stop(PCM_MIXER_CHAN_VOICE); | 228 | mixer_channel_stop(PCM_MIXER_CHAN_VOICE); |
180 | cur_buf_in = cur_buf_out = 0; | 229 | voice_buf->frame_in = voice_buf->frame_out = 0; |
181 | } | 230 | } |
182 | 231 | ||
183 | /* Grab a free PCM frame */ | 232 | /* Grab a free PCM frame */ |
@@ -190,7 +239,7 @@ static int16_t * voice_buf_get(void) | |||
190 | return NULL; | 239 | return NULL; |
191 | } | 240 | } |
192 | 241 | ||
193 | return voicebuf[cur_buf_in % VOICE_FRAMES]; | 242 | return voice_buf->frames[voice_buf->frame_in % VOICE_FRAMES].pcm; |
194 | } | 243 | } |
195 | 244 | ||
196 | /* Commit a frame returned by voice_buf_get and set the actual size */ | 245 | /* Commit a frame returned by voice_buf_get and set the actual size */ |
@@ -198,7 +247,7 @@ static void voice_buf_commit(int count) | |||
198 | { | 247 | { |
199 | if (count > 0) | 248 | if (count > 0) |
200 | { | 249 | { |
201 | voicebuf_sizes[cur_buf_in++ % VOICE_FRAMES] = | 250 | voice_buf->frames[voice_buf->frame_in++ % VOICE_FRAMES].size = |
202 | count * 2 * sizeof (int16_t); | 251 | count * 2 * sizeof (int16_t); |
203 | } | 252 | } |
204 | } | 253 | } |
@@ -207,7 +256,7 @@ static void voice_buf_commit(int count) | |||
207 | void mp3_play_data(const void *start, size_t size, | 256 | void mp3_play_data(const void *start, size_t size, |
208 | mp3_play_callback_t get_more) | 257 | mp3_play_callback_t get_more) |
209 | { | 258 | { |
210 | if (get_more != NULL && start != NULL && size > 0) | 259 | if (voice_thread_id && start && size && get_more) |
211 | { | 260 | { |
212 | struct voice_info voice_clip = | 261 | struct voice_info voice_clip = |
213 | { | 262 | { |
@@ -224,8 +273,11 @@ void mp3_play_data(const void *start, size_t size, | |||
224 | /* Stop current voice clip from playing */ | 273 | /* Stop current voice clip from playing */ |
225 | void mp3_play_stop(void) | 274 | void mp3_play_stop(void) |
226 | { | 275 | { |
227 | LOGFQUEUE("mp3 >| voice Q_VOICE_STOP"); | 276 | if (voice_thread_id != 0) |
228 | queue_send(&voice_queue, Q_VOICE_STOP, 0); | 277 | { |
278 | LOGFQUEUE("mp3 >| voice Q_VOICE_STOP"); | ||
279 | queue_send(&voice_queue, Q_VOICE_STOP, 0); | ||
280 | } | ||
229 | } | 281 | } |
230 | 282 | ||
231 | void mp3_play_pause(bool play) | 283 | void mp3_play_pause(bool play) |
@@ -270,6 +322,9 @@ static void voice_data_init(struct voice_thread_data *td) | |||
270 | dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO); | 322 | dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO); |
271 | 323 | ||
272 | mixer_channel_set_amplitude(PCM_MIXER_CHAN_VOICE, MIX_AMP_UNITY); | 324 | mixer_channel_set_amplitude(PCM_MIXER_CHAN_VOICE, MIX_AMP_UNITY); |
325 | |||
326 | voice_buf->td = td; | ||
327 | td->dst = NULL; | ||
273 | } | 328 | } |
274 | 329 | ||
275 | /* Voice thread message processing */ | 330 | /* Voice thread message processing */ |
@@ -300,9 +355,6 @@ static enum voice_state voice_message(struct voice_thread_data *td) | |||
300 | /* Copy the clip info */ | 355 | /* Copy the clip info */ |
301 | td->vi = *(struct voice_info *)td->ev.data; | 356 | td->vi = *(struct voice_info *)td->ev.data; |
302 | 357 | ||
303 | /* Be sure audio buffer is initialized */ | ||
304 | audio_restore_playback(AUDIO_WANT_VOICE); | ||
305 | |||
306 | /* We need nothing more from the sending thread - let it run */ | 358 | /* We need nothing more from the sending thread - let it run */ |
307 | queue_reply(&voice_queue, 1); | 359 | queue_reply(&voice_queue, 1); |
308 | 360 | ||
@@ -356,7 +408,7 @@ static enum voice_state voice_decode(struct voice_thread_data *td) | |||
356 | return VOICE_STATE_MESSAGE; | 408 | return VOICE_STATE_MESSAGE; |
357 | 409 | ||
358 | /* Decode the data */ | 410 | /* Decode the data */ |
359 | if (speex_decode_int(td->st, &td->bits, voice_output_buf) < 0) | 411 | if (speex_decode_int(td->st, &td->bits, voice_buf->spx_outbuf) < 0) |
360 | { | 412 | { |
361 | /* End of stream or error - get next clip */ | 413 | /* End of stream or error - get next clip */ |
362 | td->vi.size = 0; | 414 | td->vi.size = 0; |
@@ -386,7 +438,7 @@ static enum voice_state voice_decode(struct voice_thread_data *td) | |||
386 | 438 | ||
387 | /* Output the decoded frame */ | 439 | /* Output the decoded frame */ |
388 | td->src.remcount = VOICE_FRAME_COUNT - td->lookahead; | 440 | td->src.remcount = VOICE_FRAME_COUNT - td->lookahead; |
389 | td->src.pin[0] = &voice_output_buf[td->lookahead]; | 441 | td->src.pin[0] = &voice_buf->spx_outbuf[td->lookahead]; |
390 | td->src.pin[1] = NULL; | 442 | td->src.pin[1] = NULL; |
391 | td->src.proc_mask = 0; | 443 | td->src.proc_mask = 0; |
392 | 444 | ||
@@ -412,7 +464,9 @@ static enum voice_state voice_buffer_insert(struct voice_thread_data *td) | |||
412 | dst.remcount = 0; | 464 | dst.remcount = 0; |
413 | dst.bufcount = VOICE_PCM_FRAME_COUNT; | 465 | dst.bufcount = VOICE_PCM_FRAME_COUNT; |
414 | 466 | ||
467 | td->dst = &dst; | ||
415 | dsp_process(td->dsp, &td->src, &dst); | 468 | dsp_process(td->dsp, &td->src, &dst); |
469 | td->dst = NULL; | ||
416 | 470 | ||
417 | voice_buf_commit(dst.remcount); | 471 | voice_buf_commit(dst.remcount); |
418 | 472 | ||
@@ -451,9 +505,36 @@ static void NORETURN_ATTR voice_thread(void) | |||
451 | } | 505 | } |
452 | } | 506 | } |
453 | 507 | ||
454 | /* Initialize all synchronization objects create the thread */ | 508 | /* Initialize buffers, all synchronization objects and create the thread */ |
455 | void voice_thread_init(void) | 509 | void voice_thread_init(void) |
456 | { | 510 | { |
511 | if (voice_thread_id != 0) | ||
512 | return; /* Already did an init and succeeded at it */ | ||
513 | |||
514 | if (!talk_voice_required()) | ||
515 | { | ||
516 | logf("No voice required"); | ||
517 | return; | ||
518 | } | ||
519 | |||
520 | voice_buf_hid = core_alloc_ex("voice buf", sizeof (*voice_buf), &ops); | ||
521 | |||
522 | if (voice_buf_hid <= 0) | ||
523 | { | ||
524 | logf("voice: core_alloc_ex failed"); | ||
525 | return; | ||
526 | } | ||
527 | |||
528 | voice_buf = core_get_data(voice_buf_hid); | ||
529 | |||
530 | if (voice_buf == NULL) | ||
531 | { | ||
532 | logf("voice: core_get_data failed"); | ||
533 | core_free(voice_buf_hid); | ||
534 | voice_buf_hid = 0; | ||
535 | return; | ||
536 | } | ||
537 | |||
457 | logf("Starting voice thread"); | 538 | logf("Starting voice thread"); |
458 | queue_init(&voice_queue, false); | 539 | queue_init(&voice_queue, false); |
459 | 540 | ||
@@ -469,18 +550,12 @@ void voice_thread_init(void) | |||
469 | /* Set the voice thread priority */ | 550 | /* Set the voice thread priority */ |
470 | void voice_thread_set_priority(int priority) | 551 | void voice_thread_set_priority(int priority) |
471 | { | 552 | { |
553 | if (voice_thread_id == 0) | ||
554 | return; | ||
555 | |||
472 | if (priority > PRIORITY_VOICE) | 556 | if (priority > PRIORITY_VOICE) |
473 | priority = PRIORITY_VOICE; | 557 | priority = PRIORITY_VOICE; |
474 | 558 | ||
475 | thread_set_priority(voice_thread_id, priority); | 559 | thread_set_priority(voice_thread_id, priority); |
476 | } | 560 | } |
477 | #endif | 561 | #endif |
478 | |||
479 | /* Initialize voice PCM buffer and return size, allocated from the end */ | ||
480 | size_t voicebuf_init(void *bufend) | ||
481 | { | ||
482 | size_t size = VOICE_FRAMES * sizeof (voicebuf[0]); | ||
483 | cur_buf_out = cur_buf_in = 0; | ||
484 | voicebuf = bufend - size; | ||
485 | return size; | ||
486 | } | ||
diff --git a/apps/voice_thread.h b/apps/voice_thread.h index e88630439b..64c20c1e8d 100644 --- a/apps/voice_thread.h +++ b/apps/voice_thread.h | |||
@@ -23,7 +23,10 @@ | |||
23 | 23 | ||
24 | #include "config.h" | 24 | #include "config.h" |
25 | 25 | ||
26 | #ifndef MP3_PLAY_CALLBACK_DEFINED | ||
27 | #define MP3_PLAY_CALLBACK_DEFINED | ||
26 | typedef void (*mp3_play_callback_t)(const void **start, size_t *size); | 28 | typedef void (*mp3_play_callback_t)(const void **start, size_t *size); |
29 | #endif | ||
27 | 30 | ||
28 | void mp3_play_data(const void *start, size_t size, | 31 | void mp3_play_data(const void *start, size_t size, |
29 | mp3_play_callback_t get_more); | 32 | mp3_play_callback_t get_more); |
@@ -39,6 +42,4 @@ void voice_thread_init(void) INIT_ATTR; | |||
39 | void voice_thread_set_priority(int priority); | 42 | void voice_thread_set_priority(int priority); |
40 | #endif | 43 | #endif |
41 | 44 | ||
42 | size_t voicebuf_init(void *bufend); | ||
43 | |||
44 | #endif /* VOICE_THREAD_H */ | 45 | #endif /* VOICE_THREAD_H */ |
diff --git a/firmware/buflib.c b/firmware/buflib.c index 2d4b4b8bb2..f73c8ce4fb 100644 --- a/firmware/buflib.c +++ b/firmware/buflib.c | |||
@@ -210,23 +210,27 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift) | |||
210 | /* disable IRQs to make accessing the buffer from interrupt context safe. */ | 210 | /* disable IRQs to make accessing the buffer from interrupt context safe. */ |
211 | /* protect the move callback, as a cached global pointer might be updated | 211 | /* protect the move callback, as a cached global pointer might be updated |
212 | * in it. and protect "tmp->alloc = new_start" for buflib_get_data() */ | 212 | * in it. and protect "tmp->alloc = new_start" for buflib_get_data() */ |
213 | disable_irq(); | ||
214 | /* call the callback before moving */ | 213 | /* call the callback before moving */ |
215 | if (ops) | 214 | if (ops && ops->sync_callback) |
215 | ops->sync_callback(handle, true); | ||
216 | else | ||
217 | disable_irq(); | ||
218 | |||
219 | bool retval = false; | ||
220 | if (!ops || ops->move_callback(handle, tmp->alloc, new_start) | ||
221 | != BUFLIB_CB_CANNOT_MOVE) | ||
216 | { | 222 | { |
217 | if (ops->move_callback(handle, tmp->alloc, new_start) | 223 | tmp->alloc = new_start; /* update handle table */ |
218 | == BUFLIB_CB_CANNOT_MOVE) | 224 | memmove(new_block, block, block->val * sizeof(union buflib_data)); |
219 | { | 225 | retval = true; |
220 | enable_irq(); | ||
221 | return false; | ||
222 | } | ||
223 | } | 226 | } |
224 | 227 | ||
225 | tmp->alloc = new_start; /* update handle table */ | 228 | if (ops && ops->sync_callback) |
226 | memmove(new_block, block, block->val * sizeof(union buflib_data)); | 229 | ops->sync_callback(handle, false); |
230 | else | ||
231 | enable_irq(); | ||
227 | 232 | ||
228 | enable_irq(); | 233 | return retval; |
229 | return true; | ||
230 | } | 234 | } |
231 | 235 | ||
232 | /* Compact allocations and handle table, adjusting handle pointers as needed. | 236 | /* Compact allocations and handle table, adjusting handle pointers as needed. |
diff --git a/firmware/export/mp3_playback.h b/firmware/export/mp3_playback.h index de27a2a46d..7434021611 100644 --- a/firmware/export/mp3_playback.h +++ b/firmware/export/mp3_playback.h | |||
@@ -27,7 +27,10 @@ | |||
27 | #include <stdbool.h> | 27 | #include <stdbool.h> |
28 | 28 | ||
29 | /* callback fn */ | 29 | /* callback fn */ |
30 | #ifndef MP3_PLAY_CALLBACK_DEFINED | ||
31 | #define MP3_PLAY_CALLBACK_DEFINED | ||
30 | typedef void (*mp3_play_callback_t)(const void **start, size_t* size); | 32 | typedef void (*mp3_play_callback_t)(const void **start, size_t* size); |
33 | #endif | ||
31 | 34 | ||
32 | /* functions formerly in mpeg.c */ | 35 | /* functions formerly in mpeg.c */ |
33 | void mp3_init(int volume, int bass, int treble, int balance, int loudness, | 36 | void mp3_init(int volume, int bass, int treble, int balance, int loudness, |
diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h index 6e1632d5ae..5d8deb2bbf 100644 --- a/firmware/export/pcm_mixer.h +++ b/firmware/export/pcm_mixer.h | |||
@@ -22,6 +22,8 @@ | |||
22 | #ifndef PCM_MIXER_H | 22 | #ifndef PCM_MIXER_H |
23 | #define PCM_MIXER_H | 23 | #define PCM_MIXER_H |
24 | 24 | ||
25 | #include <sys/types.h> | ||
26 | |||
25 | /** Simple config **/ | 27 | /** Simple config **/ |
26 | 28 | ||
27 | /* Length of PCM frames (always) */ | 29 | /* Length of PCM frames (always) */ |
@@ -111,6 +113,10 @@ const void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count | |||
111 | void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel, | 113 | void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel, |
112 | int *left, int *right); | 114 | int *left, int *right); |
113 | 115 | ||
116 | /* Adjust channel pointer by a given offset to support movable buffers */ | ||
117 | void mixer_adjust_channel_address(enum pcm_mixer_channel channel, | ||
118 | off_t offset); | ||
119 | |||
114 | /* Stop ALL channels and PCM and reset state */ | 120 | /* Stop ALL channels and PCM and reset state */ |
115 | void mixer_reset(void); | 121 | void mixer_reset(void); |
116 | 122 | ||
diff --git a/firmware/font.c b/firmware/font.c index 895ce15446..4b21f0ffe3 100644 --- a/firmware/font.c +++ b/firmware/font.c | |||
@@ -141,7 +141,7 @@ void font_lock(int font_id, bool lock) | |||
141 | lock_font_handle(buflib_allocations[font_id], lock); | 141 | lock_font_handle(buflib_allocations[font_id], lock); |
142 | } | 142 | } |
143 | 143 | ||
144 | static struct buflib_callbacks buflibops = {buflibmove_callback, NULL }; | 144 | static struct buflib_callbacks buflibops = {buflibmove_callback, NULL, NULL }; |
145 | 145 | ||
146 | static inline struct font *pf_from_handle(int handle) | 146 | static inline struct font *pf_from_handle(int handle) |
147 | { | 147 | { |
diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h index 9cd7c0b2e0..6c9ccf7402 100644 --- a/firmware/include/buflib.h +++ b/firmware/include/buflib.h | |||
@@ -103,6 +103,12 @@ struct buflib_callbacks { | |||
103 | * at least shrinkable | 103 | * at least shrinkable |
104 | */ | 104 | */ |
105 | int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size); | 105 | int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size); |
106 | /** | ||
107 | * This is called when special steps must be taken for synchronization | ||
108 | * both before the move_callback is called and after the data has been | ||
109 | * moved. | ||
110 | */ | ||
111 | void (*sync_callback)(int handle, bool sync_on); | ||
106 | }; | 112 | }; |
107 | 113 | ||
108 | #define BUFLIB_SHRINK_POS_MASK ((1<<0|1<<1)<<30) | 114 | #define BUFLIB_SHRINK_POS_MASK ((1<<0|1<<1)<<30) |
diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c index c3e7ffa478..85d31ade70 100644 --- a/firmware/pcm_mixer.c +++ b/firmware/pcm_mixer.c | |||
@@ -406,6 +406,16 @@ void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel, | |||
406 | *right = peaks->val[1]; | 406 | *right = peaks->val[1]; |
407 | } | 407 | } |
408 | 408 | ||
409 | /* Adjust channel pointer by a given offset to support movable buffers */ | ||
410 | void mixer_adjust_channel_address(enum pcm_mixer_channel channel, | ||
411 | off_t offset) | ||
412 | { | ||
413 | pcm_play_lock(); | ||
414 | /* Makes no difference if it's stopped */ | ||
415 | channels[channel].start += offset; | ||
416 | pcm_play_unlock(); | ||
417 | } | ||
418 | |||
409 | /* Stop ALL channels and PCM and reset state */ | 419 | /* Stop ALL channels and PCM and reset state */ |
410 | void mixer_reset(void) | 420 | void mixer_reset(void) |
411 | { | 421 | { |