summaryrefslogtreecommitdiffstats
path: root/apps/pcmbuf.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2011-08-23 01:37:59 +0000
committerMichael Sevakis <jethead71@rockbox.org>2011-08-23 01:37:59 +0000
commit5078d460c678705aa3226fd2768bc85d7b3008ad (patch)
treeb32f638f99de904c4b5b4aee2b1e1b50b8bd77eb /apps/pcmbuf.c
parentfbde6baab59d7c8d047ef267884373d6d9f9e59d (diff)
downloadrockbox-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.c112
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 */