diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-08-23 01:37:59 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-08-23 01:37:59 +0000 |
commit | 5078d460c678705aa3226fd2768bc85d7b3008ad (patch) | |
tree | b32f638f99de904c4b5b4aee2b1e1b50b8bd77eb /apps/pcmbuf.c | |
parent | fbde6baab59d7c8d047ef267884373d6d9f9e59d (diff) | |
download | rockbox-5078d460c678705aa3226fd2768bc85d7b3008ad.tar.gz rockbox-5078d460c678705aa3226fd2768bc85d7b3008ad.tar.bz2 rockbox-5078d460c678705aa3226fd2768bc85d7b3008ad.zip |
Fix FS#12238 - WPS delay on pause introduced by r30097 which was the excuse I wanted anyway to do a better PCM fade on stop/pause implementation. New fade is asynchronous tick-based. Restores skin update points in the WPS that were removed when fading mechanism was changed.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30340 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r-- | apps/pcmbuf.c | 112 |
1 files changed, 70 insertions, 42 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index 2e8bc3f47c..2ba6b6f154 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c @@ -91,6 +91,13 @@ static bool track_transition; /* Fade effect */ static unsigned int fade_vol = MIX_AMP_UNITY; +static enum +{ + PCM_NOT_FADING = 0, + PCM_FADING_IN, + PCM_FADING_OUT, +} fade_state = PCM_NOT_FADING; +static bool fade_out_complete = false; /* Voice */ static bool soft_mode = false; @@ -628,8 +635,9 @@ bool pcmbuf_start_track_change(bool auto_skip) * operations involved in sending a new chunk to the DMA. */ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) { + struct chunkdesc *pcmbuf_current = read_chunk; + { - struct chunkdesc *pcmbuf_current = read_chunk; /* Take the finished chunk out of circulation */ read_chunk = pcmbuf_current->link; @@ -657,25 +665,28 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) { /* Commit last samples at end of playlist */ - if (pcmbuffer_fillpos && !read_chunk) + if (pcmbuffer_fillpos && !pcmbuf_current) { logf("pcmbuf_pcm_callback: commit last samples"); commit_chunk(false); } } + /* Stop at this frame */ + pcmbuf_current = fade_out_complete ? NULL : read_chunk; + { /* Send the new chunk to the DMA */ - if(read_chunk) + if(pcmbuf_current) { - last_chunksize = read_chunk->size; + last_chunksize = pcmbuf_current->size; pcmbuf_unplayed_bytes -= last_chunksize; *size = last_chunksize; - *start = read_chunk->addr; + *start = pcmbuf_current->addr; } else { - /* No more chunks */ + /* No more chunks or pause indicated */ logf("pcmbuf_pcm_callback: no more chunks"); last_chunksize = 0; *size = 0; @@ -694,8 +705,8 @@ void pcmbuf_play_start(void) logf("pcmbuf_play_start"); last_chunksize = read_chunk->size; pcmbuf_unplayed_bytes -= last_chunksize; - mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, - pcmbuf_pcm_callback, NULL, 0); + mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback, + read_chunk->addr, last_chunksize); } } @@ -1087,58 +1098,75 @@ static void pcmbuf_update_volume(void) /* Quiet-down the channel if 'shhh' is true or else play at normal level */ void pcmbuf_soft_mode(bool shhh) { + /* "Hate this" alert (messing with IRQ in app code): Have to block + the tick or improper order could leave volume in soft mode if + fading reads the old value first but updates after us. */ + int oldlevel = disable_irq_save(); soft_mode = shhh; pcmbuf_update_volume(); + restore_irq(oldlevel); } -/* Fade channel in or out */ -void pcmbuf_fade(bool fade, bool in) +/* Tick that does the fade for the playback channel */ +static void pcmbuf_fade_tick(void) { - if (!fade) - { - /* Simply set the level */ - fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE; - } - else - { - /* Start from the opposing end */ - fade_vol = in ? MIX_AMP_MUTE : MIX_AMP_UNITY; - } + /* ~1/3 second for full range fade */ + const unsigned int fade_step = MIX_AMP_UNITY / (HZ / 3); + + if (fade_state == PCM_FADING_IN) + fade_vol += MIN(fade_step, MIX_AMP_UNITY - fade_vol); + else if (fade_state == PCM_FADING_OUT) + fade_vol -= MIN(fade_step, fade_vol - MIX_AMP_MUTE); pcmbuf_update_volume(); - if (fade) + if (fade_vol == MIX_AMP_MUTE || fade_vol == MIX_AMP_UNITY) { - /* Do this on thread for now */ -#ifdef HAVE_PRIORITY_SCHEDULING - int old_prio = thread_set_priority(thread_self(), PRIORITY_REALTIME); -#endif + /* Fade is complete */ + tick_remove_task(pcmbuf_fade_tick); - while (1) + if (fade_state == PCM_FADING_OUT) { - /* Linear fade actually sounds better */ - if (in) - fade_vol += MIN(MIX_AMP_UNITY/16, MIX_AMP_UNITY - fade_vol); - else - fade_vol -= MIN(MIX_AMP_UNITY/16, fade_vol - MIX_AMP_MUTE); + /* Tell PCM to stop at its earliest convenience */ + fade_out_complete = true; + } - pcmbuf_update_volume(); + fade_state = PCM_NOT_FADING; + } +} - if (fade_vol > MIX_AMP_MUTE && fade_vol < MIX_AMP_UNITY) - { - sleep(0); - continue; - } +/* Fade channel in or out in the background - must pause it first */ +void pcmbuf_fade(bool fade, bool in) +{ + pcm_play_lock(); - break; - } + if (fade_state != PCM_NOT_FADING) + tick_remove_task(pcmbuf_fade_tick); -#ifdef HAVE_PRIORITY_SCHEDULING - thread_set_priority(thread_self(), old_prio); -#endif + fade_out_complete = false; + + pcm_play_unlock(); + + if (!fade) + { + /* Simply set the level */ + fade_state = PCM_NOT_FADING; + fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE; + pcmbuf_update_volume(); + } + else + { + /* Set direction and resume fade from current point */ + fade_state = in ? PCM_FADING_IN : PCM_FADING_OUT; + tick_add_task(pcmbuf_fade_tick); } } +/* Return 'true' if fade is in progress */ +bool pcmbuf_fading(void) +{ + return fade_state != PCM_NOT_FADING; +} /** Misc */ |