From 5078d460c678705aa3226fd2768bc85d7b3008ad Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Tue, 23 Aug 2011 01:37:59 +0000 Subject: 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 --- apps/gui/wps.c | 49 +++++++++++++++++-------- apps/pcmbuf.c | 112 +++++++++++++++++++++++++++++++++++--------------------- apps/pcmbuf.h | 1 + apps/playback.c | 40 ++++++++++++-------- 4 files changed, 130 insertions(+), 72 deletions(-) diff --git a/apps/gui/wps.c b/apps/gui/wps.c index 80522d04bd..48d9e0912e 100644 --- a/apps/gui/wps.c +++ b/apps/gui/wps.c @@ -119,14 +119,28 @@ char* wps_default_skin(enum screen_type screen) return skin_buf[screen]; } +static void update_non_static(void) +{ + int i; + FOR_NB_SCREENS(i) + skin_update(WPS, i, SKIN_REFRESH_NON_STATIC); +} + void pause_action(bool may_fade, bool updatewps) { -#if CONFIG_CODEC != SWCODEC +#if CONFIG_CODEC == SWCODEC + /* Do audio first, then update, unless skin were to use its local + status in which case, reverse it */ + audio_pause(); + + if (updatewps) + update_non_static(); +#else if (may_fade && global_settings.fade_on_stop) fade(false, updatewps); else -#endif audio_pause(); +#endif if (global_settings.pause_rewind) { long newpos; @@ -139,18 +153,26 @@ void pause_action(bool may_fade, bool updatewps) audio_ff_rewind(newpos > 0 ? newpos : 0); } - (void)may_fade; (void)updatewps; + (void)may_fade; } void unpause_action(bool may_fade, bool updatewps) { -#if CONFIG_CODEC != SWCODEC +#if CONFIG_CODEC == SWCODEC + /* Do audio first, then update, unless skin were to use its local + status in which case, reverse it */ + audio_resume(); + + if (updatewps) + update_non_static(); +#else if (may_fade && global_settings.fade_on_stop) fade(true, updatewps); else -#endif audio_resume(); - (void)may_fade; (void)updatewps; +#endif + + (void)may_fade; } #if CONFIG_CODEC != SWCODEC @@ -159,7 +181,7 @@ void fade(bool fade_in, bool updatewps) int fp_global_vol = global_settings.volume << 8; int fp_min_vol = sound_min(SOUND_VOLUME) << 8; int fp_step = (fp_global_vol - fp_min_vol) / 10; - int i; + skin_get_global_state()->is_fading = !fade_in; if (fade_in) { /* fade in */ @@ -171,10 +193,8 @@ void fade(bool fade_in, bool updatewps) sleep(HZ/10); /* let audio thread run */ audio_resume(); - if (updatewps) { - FOR_NB_SCREENS(i) - skin_update(WPS, i, SKIN_REFRESH_NON_STATIC); - } + if (updatewps) + update_non_static(); while (fp_volume < fp_global_vol - fp_step) { fp_volume += fp_step; @@ -187,10 +207,8 @@ void fade(bool fade_in, bool updatewps) /* fade out */ int fp_volume = fp_global_vol; - if (updatewps) { - FOR_NB_SCREENS(i) - skin_update(WPS, i, SKIN_REFRESH_NON_STATIC); - } + if (updatewps) + update_non_static(); while (fp_volume > fp_min_vol + fp_step) { fp_volume -= fp_step; @@ -1140,6 +1158,7 @@ long gui_wps_show(void) fade(false, true); #else audio_pause(); + update_non_static(); #endif if (bookmark) bookmark_autobookmark(true); 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 */ diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h index b7f5a3c2d6..a5cd3163e1 100644 --- a/apps/pcmbuf.h +++ b/apps/pcmbuf.h @@ -65,6 +65,7 @@ unsigned char *pcmbuf_get_meminfo(size_t *length); /* Misc */ void pcmbuf_fade(bool fade, bool in); +bool pcmbuf_fading(void); void pcmbuf_soft_mode(bool shhh); bool pcmbuf_is_lowdata(void); void pcmbuf_set_low_latency(bool state); diff --git a/apps/playback.c b/apps/playback.c index a38534a2da..3adf6f6a49 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -1100,6 +1100,14 @@ static void audio_clear_paused_pcm(void) pcmbuf_play_stop(); } +/* Wait for any in-progress fade to complete */ +static void audio_wait_fade_complete(void) +{ + /* Just loop until it's done */ + while (pcmbuf_fading()) + sleep(0); +} + /* End the ff/rw mode */ static void audio_ff_rewind_end(void) { @@ -2439,7 +2447,12 @@ static void audio_stop_playback(void) if (play_status == PLAY_STOPPED) return; - pcmbuf_fade(global_settings.fade_on_stop, false); + bool do_fade = global_settings.fade_on_stop && filling != STATE_ENDED; + + pcmbuf_fade(do_fade, false); + + /* Wait for fade-out */ + audio_wait_fade_complete(); /* Stop the codec and unload it */ halt_decoding_track(true); @@ -2480,27 +2493,22 @@ static void audio_on_pause(bool pause) if (play_status == PLAY_STOPPED || pause == (play_status == PLAY_PAUSED)) return; - bool const do_fade = global_settings.fade_on_stop; - - if (pause) - pcmbuf_fade(do_fade, false); + play_status = pause ? PLAY_PAUSED : PLAY_PLAYING; - if (!ff_rw_mode) + if (!pause && codec_skip_pending) { - /* Not in ff/rw mode - may set the state (otherwise this could make - old data play because seek hasn't completed and cleared it) */ - pcmbuf_pause(pause); + /* Actually do the skip that is due - resets the status flag */ + audio_on_codec_complete(codec_skip_status); } - if (!pause) - pcmbuf_fade(do_fade, true); + bool do_fade = global_settings.fade_on_stop; - play_status = pause ? PLAY_PAUSED : PLAY_PLAYING; + pcmbuf_fade(do_fade, !pause); - if (!pause && codec_skip_pending) + if (!ff_rw_mode && !(do_fade && pause)) { - /* Actually do the skip that is due - resets the status flag */ - audio_on_codec_complete(codec_skip_status); + /* Not in ff/rw mode - can actually change the audio state now */ + pcmbuf_pause(pause); } } @@ -2644,6 +2652,8 @@ static void audio_on_pre_ff_rewind(void) ff_rw_mode = true; + audio_wait_fade_complete(); + if (play_status == PLAY_PAUSED) return; -- cgit v1.2.3