summaryrefslogtreecommitdiff
path: root/apps/playback.c
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2013-05-30 11:24:16 +0200
committerThomas Martitz <kugel@rockbox.org>2013-12-23 12:17:38 +0100
commit22e802e80048defd401462e062afcb10093ac793 (patch)
tree09d24f7eb2a3b18e6563e838398b2715394f7c4c /apps/playback.c
parent64b9e1fa7b645daa36ca0018dc168d4f871fd538 (diff)
downloadrockbox-22e802e80048defd401462e062afcb10093ac793.tar.gz
rockbox-22e802e80048defd401462e062afcb10093ac793.zip
playback,talk: Share audiobuffer via core_alloc_maximum().
This fixes the radioart crash that was the result of buffering.c working on a freed buffer at the same time as buflib (radioart uses buffering.c for the images). With this change the buffer is owned by buflib exclusively so this cannot happen. As a result, audio_get_buffer() doesn't exist anymore. Callers should call core_alloc_maximum() directly. This buffer needs to be protected as usual against movement if necessary (previously it was not protected at all which cased the radioart crash), To get most of it they can adjust the willingness of the talk engine to give its buffer away (at the expense of disabling voice interface) with the new talk_buffer_set_policy() function. Change-Id: I52123012208d04967876a304451d634e2bef3a33
Diffstat (limited to 'apps/playback.c')
-rw-r--r--apps/playback.c176
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 */
731static void audio_reset_buffer_noalloc( 730static void audio_reset_buffer_noalloc(
732 void *filebuf, enum audio_buffer_state state) 731 void *filebuf)
733{ 732{
734 /* 733 /*
735 * Layout audio buffer as follows: 734 * Layout audio buffer as follows:
736 * [[|TALK]|SCRATCH|BUFFERING|PCM] 735 * [|SCRATCH|BUFFERING|PCM]
737 */ 736 */
738
739 /* see audio_get_recording_buffer if this is modified */
740 logf("%s()", __func__); 737 logf("%s()", __func__);
741 738
742 /* If the setup of anything allocated before the file buffer is 739 /* If the setup of anything allocated before the file buffer is
743 changed, do check the adjustments after the buffer_alloc call 740 changed, do check the adjustments after the buffer_alloc call
744 as it will likely be affected and need sliding over */ 741 as it will likely be affected and need sliding over */
745
746 /* Initially set up file buffer as all space available */
747 size_t allocsize; 742 size_t allocsize;
743 /* Subtract whatever the pcm buffer says it used plus the guard
744 buffer */
745 allocsize = pcmbuf_init(filebuf + filebuflen);
748 746
749 /* Subtract whatever voice needs (we're called when promoting 747 /* Make sure filebuflen is a pointer sized multiple after
750 the state only) */ 748 adjustment */
751 allocsize = talkbuf_init(filebuf);
752 allocsize = ALIGN_UP(allocsize, sizeof (intptr_t)); 749 allocsize = ALIGN_UP(allocsize, sizeof (intptr_t));
753 if (allocsize > filebuflen) 750 if (allocsize > filebuflen)
754 goto bufpanic; 751 goto bufpanic;
755 752
756 filebuf += allocsize;
757 filebuflen -= allocsize; 753 filebuflen -= allocsize;
758 754
759 if (state == AUDIOBUF_STATE_INITIALIZED) 755 /* Scratch memory */
760 { 756 allocsize = scratch_mem_size();
761 /* Subtract whatever the pcm buffer says it used plus the guard 757 if (allocsize > filebuflen)
762 buffer */ 758 goto bufpanic;
763 allocsize = pcmbuf_init(filebuf + filebuflen);
764
765 /* Make sure filebuflen is a pointer sized multiple after
766 adjustment */
767 allocsize = ALIGN_UP(allocsize, sizeof (intptr_t));
768 if (allocsize > filebuflen)
769 goto bufpanic;
770
771 filebuflen -= allocsize;
772
773 /* Scratch memory */
774 allocsize = scratch_mem_size();
775 if (allocsize > filebuflen)
776 goto bufpanic;
777 759
778 scratch_mem_init(filebuf); 760 scratch_mem_init(filebuf);
779 filebuf += allocsize; 761 filebuf += allocsize;
780 filebuflen -= allocsize; 762 filebuflen -= allocsize;
781 763
782 buffering_reset(filebuf, filebuflen); 764 buffering_reset(filebuf, filebuflen);
783 }
784 765
785 buffer_state = state; 766 buffer_state = AUDIOBUF_STATE_INITIALIZED;
786 767
787#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) 768#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
788 /* Make sure everything adds up - yes, some info is a bit redundant but 769 /* Make sure everything adds up - yes, some info is a bit redundant but
@@ -811,15 +792,24 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
811 /* codec messages */ 792 /* codec messages */
812 { Q_AUDIO_PLAY, Q_AUDIO_PLAY }, 793 { Q_AUDIO_PLAY, Q_AUDIO_PLAY },
813 }; 794 };
795 bool give_up = false;
814 /* filebuflen is, at this point, the buffering.c buffer size, 796 /* filebuflen is, at this point, the buffering.c buffer size,
815 * i.e. the audiobuf except voice, scratch mem, pcm, ... */ 797 * i.e. the audiobuf except voice, scratch mem, pcm, ... */
816 ssize_t extradata_size = old_size - filebuflen; 798 ssize_t extradata_size = old_size - filebuflen;
817 /* check what buflib requests */ 799 /* check what buflib requests */
818 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); 800 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
819 ssize_t size = (ssize_t)old_size - wanted_size; 801 ssize_t size = (ssize_t)old_size - wanted_size;
820 /* keep at least 256K for the buffering */ 802
821 if ((size - extradata_size) < AUDIO_BUFFER_RESERVE) 803 if ((size - extradata_size) < AUDIO_BUFFER_RESERVE)
822 return BUFLIB_CB_CANNOT_SHRINK; 804 {
805 /* check if buflib needs the memory really hard. if yes we give
806 * up playback for now, otherwise refuse to shrink to keep at least
807 * 256K for the buffering */
808 if ((hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK)
809 give_up = true;
810 else
811 return BUFLIB_CB_CANNOT_SHRINK;
812 }
823 813
824 814
825 /* TODO: Do it without stopping playback, if possible */ 815 /* TODO: Do it without stopping playback, if possible */
@@ -852,20 +842,26 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
852#ifdef PLAYBACK_VOICE 842#ifdef PLAYBACK_VOICE
853 voice_stop(); 843 voice_stop();
854#endif 844#endif
855 /* we should be free to change the buffer now 845
856 * set final buffer size before calling audio_reset_buffer_noalloc() 846 /* we should be free to change the buffer now */
847 if (give_up)
848 {
849 buffer_state = AUDIOBUF_STATE_TRASHED;
850 audiobuf_handle = core_free(audiobuf_handle);
851 return BUFLIB_CB_OK;
852 }
853 /* set final buffer size before calling audio_reset_buffer_noalloc()
857 * (now it's the total size, the call will subtract voice etc) */ 854 * (now it's the total size, the call will subtract voice etc) */
858 filebuflen = size; 855 filebuflen = size;
859 switch (hints & BUFLIB_SHRINK_POS_MASK) 856 switch (hints & BUFLIB_SHRINK_POS_MASK)
860 { 857 {
861 case BUFLIB_SHRINK_POS_BACK: 858 case BUFLIB_SHRINK_POS_BACK:
862 core_shrink(handle, start, size); 859 core_shrink(handle, start, size);
863 audio_reset_buffer_noalloc(start, buffer_state); 860 audio_reset_buffer_noalloc(start);
864 break; 861 break;
865 case BUFLIB_SHRINK_POS_FRONT: 862 case BUFLIB_SHRINK_POS_FRONT:
866 core_shrink(handle, start + wanted_size, size); 863 core_shrink(handle, start + wanted_size, size);
867 audio_reset_buffer_noalloc(start + wanted_size, 864 audio_reset_buffer_noalloc(start + wanted_size);
868 buffer_state);
869 break; 865 break;
870 } 866 }
871 if (playing || play_queued) 867 if (playing || play_queued)
@@ -882,7 +878,7 @@ static struct buflib_callbacks ops = {
882 .shrink_callback = shrink_callback, 878 .shrink_callback = shrink_callback,
883}; 879};
884 880
885static void audio_reset_buffer(enum audio_buffer_state state) 881static void audio_reset_buffer(void)
886{ 882{
887 if (audiobuf_handle > 0) 883 if (audiobuf_handle > 0)
888 { 884 {
@@ -890,9 +886,13 @@ static void audio_reset_buffer(enum audio_buffer_state state)
890 audiobuf_handle = 0; 886 audiobuf_handle = 0;
891 } 887 }
892 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); 888 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
893 unsigned char *filebuf = core_get_data(audiobuf_handle);
894 889
895 audio_reset_buffer_noalloc(filebuf, state); 890 if (audiobuf_handle > 0)
891 audio_reset_buffer_noalloc(core_get_data(audiobuf_handle));
892 else
893 /* someone is abusing core_alloc_maximum(). Fix this evil guy instead of
894 * trying to handle OOM without hope */
895 panicf("%s(): OOM!\n", __func__);
896} 896}
897 897
898/* Set the buffer margin to begin rebuffering when 'seconds' from empty */ 898/* Set the buffer margin to begin rebuffering when 'seconds' from empty */
@@ -2033,7 +2033,7 @@ static int audio_fill_file_buffer(void)
2033 if (buffer_state != AUDIOBUF_STATE_INITIALIZED || 2033 if (buffer_state != AUDIOBUF_STATE_INITIALIZED ||
2034 !pcmbuf_is_same_size()) 2034 !pcmbuf_is_same_size())
2035 { 2035 {
2036 audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED); 2036 audio_reset_buffer();
2037 } 2037 }
2038 2038
2039 logf("Starting buffer fill"); 2039 logf("Starting buffer fill");
@@ -2464,7 +2464,7 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2464 /* Mark the buffer dirty - if not playing, it will be reset next 2464 /* Mark the buffer dirty - if not playing, it will be reset next
2465 time */ 2465 time */
2466 if (buffer_state == AUDIOBUF_STATE_INITIALIZED) 2466 if (buffer_state == AUDIOBUF_STATE_INITIALIZED)
2467 buffer_state = AUDIOBUF_STATE_VOICED_ONLY; 2467 buffer_state = AUDIOBUF_STATE_TRASHED;
2468 } 2468 }
2469 2469
2470 if (old_status != PLAY_STOPPED) 2470 if (old_status != PLAY_STOPPED)
@@ -3511,88 +3511,6 @@ void audio_flush_and_reload_tracks(void)
3511 audio_queue_post(Q_AUDIO_FLUSH, 0); 3511 audio_queue_post(Q_AUDIO_FLUSH, 0);
3512} 3512}
3513 3513
3514/* Return the pointer to the main audio buffer, optionally preserving
3515 voicing */
3516unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
3517{
3518 unsigned char *buf;
3519
3520 if (audio_is_initialized && thread_self() != audio_thread_id)
3521 {
3522 audio_hard_stop();
3523 }
3524 /* else buffer_state will be AUDIOBUF_STATE_TRASHED at this point */
3525
3526 if (buffer_size == NULL)
3527 {
3528 /* Special case for talk_init to use since it already knows it's
3529 trashed */
3530 buffer_state = AUDIOBUF_STATE_TRASHED;
3531 return NULL;
3532 }
3533
3534 /* make sure buffer is freed and re-allocated to simplify code below
3535 * (audio_hard_stop() likely has done that already) */
3536 if (audiobuf_handle > 0)
3537 audiobuf_handle = core_free(audiobuf_handle);
3538
3539 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
3540 buf = core_get_data(audiobuf_handle);
3541
3542 if (buffer_state == AUDIOBUF_STATE_INITIALIZED)
3543 buffering_reset(NULL, 0); /* mark buffer invalid */
3544
3545 if (talk_buf || !talk_voice_required())
3546 {
3547 logf("get buffer: talk, audio");
3548 /* Ok to use everything from audiobuf - voice is loaded,
3549 the talk buffer is not needed because voice isn't being used, or
3550 could be AUDIOBUF_STATE_TRASHED already. If state is
3551 AUDIOBUF_STATE_VOICED_ONLY, no problem as long as memory isn't
3552 written without the caller knowing what's going on. Changing certain
3553 settings may move it to a worse condition but the memory in use by
3554 something else will remain undisturbed.
3555 */
3556 if (buffer_state != AUDIOBUF_STATE_TRASHED)
3557 {
3558 talk_buffer_steal();
3559 buffer_state = AUDIOBUF_STATE_TRASHED;
3560 }
3561 }
3562 else
3563 {
3564 logf("get buffer: audio");
3565 /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or
3566 still AUDIOBUF_STATE_INITIALIZED */
3567 size_t talkbuf_size = talkbuf_init(buf);
3568 buf += talkbuf_size; /* Skip talk buffer */
3569 filebuflen -= talkbuf_size;
3570 buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
3571 }
3572
3573 *buffer_size = filebuflen;
3574 return buf;
3575}
3576
3577/* Restore audio buffer to a particular state (promoting status) */
3578bool audio_restore_playback(int type)
3579{
3580 switch (type)
3581 {
3582 case AUDIO_WANT_PLAYBACK:
3583 if (buffer_state != AUDIOBUF_STATE_INITIALIZED)
3584 audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED);
3585 return true;
3586 case AUDIO_WANT_VOICE:
3587 if (buffer_state == AUDIOBUF_STATE_TRASHED)
3588 audio_reset_buffer(AUDIOBUF_STATE_VOICED_ONLY);
3589 return true;
3590 default:
3591 return false;
3592 }
3593}
3594
3595
3596/** --- Miscellaneous public interfaces --- **/ 3514/** --- Miscellaneous public interfaces --- **/
3597 3515
3598#ifdef HAVE_ALBUMART 3516#ifdef HAVE_ALBUMART