diff options
Diffstat (limited to 'apps/playback.c')
-rw-r--r-- | apps/playback.c | 176 |
1 files changed, 47 insertions, 129 deletions
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 |