summaryrefslogtreecommitdiff
path: root/apps/playback.c
diff options
context:
space:
mode:
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