pcmbuf: clarify and simplify crossfade code, etc.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23538 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index fbcb231..e3f190e 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -416,47 +416,7 @@
 }
 
 
-/** track change functions */
-
-static inline void codec_crossfade_track_change(void)
-{
-    /* Initiate automatic crossfade mode */
-    pcmbuf_crossfade_init(false);
-    /* Notify the wps that the track change starts now */
-    LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
-    queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
-}
-
-static void codec_track_skip_done(bool was_manual)
-{
-    /* Manual track change (always crossfade or flush audio). */
-    if (was_manual)
-    {
-        pcmbuf_crossfade_init(true);
-        LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
-        queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
-    }
-    /* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
-    else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
-             && global_settings.crossfade != CROSSFADE_ENABLE_TRACKSKIP)
-    {
-        if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP)
-        {
-            if (global_settings.playlist_shuffle)
-                /* shuffle mode is on, so crossfade: */
-                codec_crossfade_track_change();
-            else
-                /* shuffle mode is off, so normal gapless playback */
-                pcmbuf_start_track_change();
-        }
-        else
-            /* normal crossfade:  */
-            codec_crossfade_track_change();
-    }
-    else
-        /* normal gapless playback. */
-        pcmbuf_start_track_change();
-}
+/* track change */
 
 static bool codec_load_next_track(void)
 {
@@ -487,7 +447,7 @@
     {
         case Q_CODEC_REQUEST_COMPLETE:
             LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
-            codec_track_skip_done(!automatic_skip);
+            pcmbuf_start_track_change(!automatic_skip);
             return true;
 
         case Q_CODEC_REQUEST_FAILED:
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index c8f89d9..1e286fa 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -49,6 +49,17 @@
     return sample;
 }
 
+#define PCMBUF_TARGET_CHUNK 32768 /* This is the target fill size of chunks
+                                     on the pcm buffer */
+#define PCMBUF_MINAVG_CHUNK 24576 /* This is the minimum average size of
+                                     chunks on the pcm buffer (or we run out
+                                     of buffer descriptors, which is
+                                     non-fatal) */
+#define PCMBUF_MIN_CHUNK     4096 /* We try to never feed a chunk smaller than
+                                     this to the DMA */
+#define PCMBUF_MIX_CHUNK     8192 /* This is the maximum size of one packet
+                                     for mixing (crossfade or voice) */
+
 #if MEMORYSIZE > 2
 /* Keep watermark high for iPods at least (2s) */
 #define PCMBUF_WATERMARK     (NATIVE_FREQUENCY * 4 * 2)
@@ -135,6 +146,10 @@
 static void pcmbuf_under_watermark(bool under);
 static bool pcmbuf_flush_fillpos(void);
 
+static bool pcmbuf_crossfade_init(bool manual_skip);
+static bool pcmbuf_is_crossfade_enabled(void);
+static void pcmbuf_finish_crossfade_enable(void);
+
 
 /**************************************/
 
@@ -181,7 +196,7 @@
  * still playing.  Set flags to make sure the elapsed time of the current
  * track is updated properly, and mark the currently written chunk as the
  * last one in the track. */
-void pcmbuf_start_track_change(void)
+static void pcmbuf_gapless_track_change(void)
 {
     /* we're starting a track transition */
     track_transition = true;
@@ -190,6 +205,44 @@
     end_of_track = true;
 }
 
+static void pcmbuf_crossfade_track_change(void)
+{
+    /* Initiate automatic crossfade mode */
+    pcmbuf_crossfade_init(false);
+    /* Notify the wps that the track change starts now */
+    audio_post_track_change(false);
+}
+
+void pcmbuf_start_track_change(bool manual_skip)
+{
+    /* Manual track change (always crossfade or flush audio). */
+    if (manual_skip)
+    {
+        pcmbuf_crossfade_init(true);
+        audio_post_track_change(false);
+    }
+    /* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
+    else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
+             && global_settings.crossfade != CROSSFADE_ENABLE_TRACKSKIP)
+    {
+        if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP)
+        {
+            if (global_settings.playlist_shuffle)
+                /* shuffle mode is on, so crossfade: */
+                pcmbuf_crossfade_track_change();
+            else
+                /* shuffle mode is off, so normal gapless playback */
+                pcmbuf_gapless_track_change();
+        }
+        else
+            /* normal crossfade:  */
+            pcmbuf_crossfade_track_change();
+    }
+    else
+        /* normal gapless playback. */
+        pcmbuf_gapless_track_change();
+}
+
 /* Called when the last chunk in the track has been played */
 static void pcmbuf_finish_track_change(void)
 {
@@ -198,7 +251,7 @@
     track_transition = false;
     
     /* notify playback that the track has just finished */
-    audio_post_track_change();
+    audio_post_track_change(true);
 }
 
 
@@ -274,15 +327,6 @@
     DISPLAY_DESC("callback");
 }
 
-static void pcmbuf_set_watermark_bytes(void)
-{
-    pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ?
-        /* If crossfading, try to keep the buffer full other than 1 second */
-        (pcmbuf_size - (NATIVE_FREQUENCY * 4 * 1)) :
-        /* Otherwise, just use the default */
-        PCMBUF_WATERMARK;
-}
-
 /* This is really just part of pcmbuf_flush_fillpos, but is easier to keep
  * in a separate function for the moment */
 static inline void pcmbuf_add_chunk(void)
@@ -330,6 +374,28 @@
     DISPLAY_DESC("add_chunk");
 }
 
+/**
+ * Commit samples waiting to the pcm buffer.
+ */
+static bool pcmbuf_flush_fillpos(void)
+{
+    if (audiobuffer_fillpos) {
+        /* Never use the last buffer descriptor */
+        while (pcmbuf_write == pcmbuf_write_end) {
+            /* If this happens, something is being stupid */
+            if (!pcm_is_playing()) {
+                logf("pcmbuf_flush_fillpos error");
+                pcmbuf_play_start();
+            }
+            /* Let approximately one chunk of data playback */
+            sleep(HZ*PCMBUF_TARGET_CHUNK/(NATIVE_FREQUENCY*4));
+        }
+        pcmbuf_add_chunk();
+        return true;
+    }
+    return false;
+}
+
 #ifdef HAVE_PRIORITY_SCHEDULING
 static void boost_codec_thread(bool boost)
 {
@@ -431,7 +497,7 @@
     return pcmbuf_size;
 }
 
-bool pcmbuf_crossfade_init(bool manual_skip)
+static bool pcmbuf_crossfade_init(bool manual_skip)
 {
     /* Can't do two crossfades at once and, no fade if pcm is off now */
     if (crossfade_init || crossfade_active || !pcm_is_playing())
@@ -542,11 +608,20 @@
 
 bool pcmbuf_is_same_size(void)
 {
+    bool same_size;
+    
     if (audiobuffer == NULL)
-        return true; /* Not set up yet even once so always */
-
-    size_t bufsize = pcmbuf_get_next_required_pcmbuf_size();
-    return pcmbuf_calc_audiobuffer_ptr(bufsize) == audiobuffer;
+        same_size = true;   /* Not set up yet even once so always */
+    else
+    {
+        size_t bufsize = pcmbuf_get_next_required_pcmbuf_size();
+        same_size = pcmbuf_calc_audiobuffer_ptr(bufsize) == audiobuffer;
+    }
+    
+    if (same_size)
+        pcmbuf_finish_crossfade_enable();
+    
+    return same_size;
 }
 
 /* Initialize the pcmbuffer the structure looks like this:
@@ -566,7 +641,7 @@
     end_of_track = false;
     track_transition = false;
 
-    pcmbuf_crossfade_enable_finished();
+    pcmbuf_finish_crossfade_enable();
 
     pcmbuf_play_stop();
 
@@ -606,28 +681,6 @@
     }
 }
 
-/**
- * Commit samples waiting to the pcm buffer.
- */
-static bool pcmbuf_flush_fillpos(void)
-{
-    if (audiobuffer_fillpos) {
-        /* Never use the last buffer descriptor */
-        while (pcmbuf_write == pcmbuf_write_end) {
-            /* If this happens, something is being stupid */
-            if (!pcm_is_playing()) {
-                logf("pcmbuf_flush_fillpos error");
-                pcmbuf_play_start();
-            }
-            /* Let approximately one chunk of data playback */
-            sleep(HZ*PCMBUF_TARGET_CHUNK/(NATIVE_FREQUENCY*4));
-        }
-        pcmbuf_add_chunk();
-        return true;
-    }
-    return false;
-}
-
 /** 
  * Low memory targets don't have crossfade, so don't compile crossfade
  * specific code in order to save some memory.                         */
@@ -907,7 +960,7 @@
     return true;
 }
 
-void* pcmbuf_request_buffer(int *count)
+void *pcmbuf_request_buffer(int *count)
 {
 #ifdef HAVE_CROSSFADE
     if (crossfade_init)
@@ -943,38 +996,6 @@
     }
 }
 
-void * pcmbuf_request_voice_buffer(int *count)
-{
-    /* A get-it-to-work-for-now hack (audio status could change by
-       completion) */
-    if (audio_status() & AUDIO_STATUS_PLAY)
-    {
-        if (pcmbuf_read == NULL)
-        {
-            return NULL;
-        }
-        else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 &&
-                 (pcmbuf_mix_chunk || pcmbuf_read->link))
-        {
-            *count = MIN(*count, PCMBUF_MIX_CHUNK/4);
-            return voicebuf;
-        }
-        else
-        {
-            return NULL;
-        }
-    }
-    else
-    {
-        return pcmbuf_request_buffer(count);
-    }
-}
-
-bool pcmbuf_is_crossfade_active(void)
-{
-    return crossfade_active || crossfade_init;
-}
-
 void pcmbuf_write_complete(int count)
 {
     size_t length = (size_t)(unsigned int)count << 2;
@@ -1098,12 +1119,12 @@
 #endif /* HAVE_HARDWARE_BEEP */
 
 /* Returns pcm buffer usage in percents (0 to 100). */
-int pcmbuf_usage(void)
+static int pcmbuf_usage(void)
 {
     return pcmbuf_unplayed_bytes * 100 / pcmbuf_size;
 }
 
-int pcmbuf_mix_free(void)
+static int pcmbuf_mix_free(void)
 {
     if (pcmbuf_mix_chunk)
     {
@@ -1117,6 +1138,33 @@
     return 100;
 }
 
+void *pcmbuf_request_voice_buffer(int *count)
+{
+    /* A get-it-to-work-for-now hack (audio status could change by
+       completion) */
+    if (audio_status() & AUDIO_STATUS_PLAY)
+    {
+        if (pcmbuf_read == NULL)
+        {
+            return NULL;
+        }
+        else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 &&
+                 (pcmbuf_mix_chunk || pcmbuf_read->link))
+        {
+            *count = MIN(*count, PCMBUF_MIX_CHUNK/4);
+            return voicebuf;
+        }
+        else
+        {
+            return NULL;
+        }
+    }
+    else
+    {
+        return pcmbuf_request_buffer(count);
+    }
+}
+
 void pcmbuf_write_voice_complete(int count)
 {
     /* A get-it-to-work-for-now hack (audio status could have changed) */
@@ -1163,23 +1211,33 @@
     }
 }
 
-void pcmbuf_crossfade_enable(bool on_off)
+void pcmbuf_request_crossfade_enable(bool on_off)
 {
     /* Next setting to be used, not applied now */
     crossfade_enabled_pending = on_off;
 }
 
-void pcmbuf_crossfade_enable_finished(void)
+static void pcmbuf_finish_crossfade_enable(void)
 {
     /* Copy the pending setting over now */
     crossfade_enabled = crossfade_enabled_pending;
-    pcmbuf_set_watermark_bytes();
+    
+    pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ?
+        /* If crossfading, try to keep the buffer full other than 1 second */
+        (pcmbuf_size - (NATIVE_FREQUENCY * 4 * 1)) :
+        /* Otherwise, just use the default */
+        PCMBUF_WATERMARK;
 }
 
-bool pcmbuf_is_crossfade_enabled(void)
+static bool pcmbuf_is_crossfade_enabled(void)
 {
     if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE)
         return global_settings.playlist_shuffle;
 
     return crossfade_enabled;
 }
+
+bool pcmbuf_is_crossfade_active(void)
+{
+    return crossfade_active || crossfade_init;
+}
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index 1d893f9..f8e6905 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -21,58 +21,37 @@
 #ifndef PCMBUF_H
 #define PCMBUF_H
 
-#define PCMBUF_TARGET_CHUNK 32768 /* This is the target fill size of chunks
-                                     on the pcm buffer */
-#define PCMBUF_MINAVG_CHUNK 24576 /* This is the minimum average size of
-                                     chunks on the pcm buffer (or we run out
-                                     of buffer descriptors, which is
-                                     non-fatal) */
-#define PCMBUF_MIN_CHUNK     4096 /* We try to never feed a chunk smaller than
-                                     this to the DMA */
-#define PCMBUF_MIX_CHUNK     8192 /* This is the maximum size of one packet
-                                     for mixing (crossfade or voice) */
-
-/* Returns true if the buffer needs to change size */
-bool pcmbuf_is_same_size(void);
+/* playback */
 size_t pcmbuf_init(unsigned char *bufend);
-/* Size in bytes used by the pcmbuffer */
-size_t pcmbuf_get_bufsize(void);
-#ifdef ROCKBOX_HAS_LOGF
-/* just used for logging for now */
-unsigned char * pcmbuf_get_meminfo(size_t *length);
-#endif
-
-void pcmbuf_pause(bool pause);
-void pcmbuf_play_stop(void);
-bool pcmbuf_is_crossfade_active(void);
-
-/* These functions are for playing chained buffers of PCM data */
-#if defined(HAVE_ADJUSTABLE_CPU_FREQ)
-void pcmbuf_boost(bool state);
-void pcmbuf_set_boost_mode(bool state);
-#else
-#define pcmbuf_boost(state) do { } while(0)
-#define pcmbuf_set_boost_mode(state) do { } while(0)
-#endif
-bool pcmbuf_is_lowdata(void);
 void pcmbuf_play_start(void);
-bool pcmbuf_crossfade_init(bool manual_skip);
-void pcmbuf_start_track_change(void);
-size_t pcmbuf_free(void);
-unsigned long pcmbuf_get_latency(void);
-void pcmbuf_set_low_latency(bool state);
-void * pcmbuf_request_buffer(int *count);
+void pcmbuf_play_stop(void);
+void pcmbuf_pause(bool pause);
+void pcmbuf_start_track_change(bool manual_skip);
+void *pcmbuf_request_buffer(int *count);
 void pcmbuf_write_complete(int count);
-void * pcmbuf_request_voice_buffer(int *count);
-void pcmbuf_write_voice_complete(int count);
-bool pcmbuf_is_crossfade_enabled(void);
-void pcmbuf_crossfade_enable(bool on_off);
-void pcmbuf_crossfade_enable_finished(void);
-int pcmbuf_usage(void);
-int pcmbuf_mix_free(void);
-void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
 
-int pcmbuf_used_descs(void);
+/* crossfade */
+bool pcmbuf_is_crossfade_active(void);
+void pcmbuf_request_crossfade_enable(bool on_off);
+bool pcmbuf_is_same_size(void);
+
+/* voice */
+void *pcmbuf_request_voice_buffer(int *count);
+void pcmbuf_write_voice_complete(int count);
+
+/* debug menu, other metrics */
+size_t pcmbuf_free(void);
+size_t pcmbuf_get_bufsize(void);
 int pcmbuf_descs(void);
+int pcmbuf_used_descs(void);
+#ifdef ROCKBOX_HAS_LOGF
+unsigned char *pcmbuf_get_meminfo(size_t *length);
+#endif
+
+/* misc */
+void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
+bool pcmbuf_is_lowdata(void);
+void pcmbuf_set_low_latency(bool state);
+unsigned long pcmbuf_get_latency(void);
 
 #endif
diff --git a/apps/playback.c b/apps/playback.c
index d3c4b46..fbaaa5c 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -246,10 +246,18 @@
 
 /* Post message from pcmbuf that the end of the previous track
  * has just been played. */
-void audio_post_track_change(void)
+void audio_post_track_change(bool pcmbuf)
 {
-    LOGFQUEUE("pcmbuf > pcmbuf Q_AUDIO_TRACK_CHANGED");
-    queue_post(&pcmbuf_queue, Q_AUDIO_TRACK_CHANGED, 0);
+    if (pcmbuf)
+    {
+        LOGFQUEUE("pcmbuf > pcmbuf Q_AUDIO_TRACK_CHANGED");
+        queue_post(&pcmbuf_queue, Q_AUDIO_TRACK_CHANGED, 0);
+    }
+    else
+    {
+        LOGFQUEUE("pcmbuf > audio Q_AUDIO_TRACK_CHANGED");
+        queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
+    }
 }
 
 /* Scan the pcmbuf queue and return true if a message pulled.
@@ -814,18 +822,12 @@
     size_t size;
 
     /* Tell it the next setting to use */
-    pcmbuf_crossfade_enable(enable);
+    pcmbuf_request_crossfade_enable(enable);
 
     /* Return if size hasn't changed or this is too early to determine
        which in the second case there's no way we could be playing
        anything at all */
-    if (pcmbuf_is_same_size())
-    {
-        /* This function is a copout and just syncs some variables -
-           to be removed at a later date */
-        pcmbuf_crossfade_enable_finished();
-        return;
-    }
+    if (pcmbuf_is_same_size()) return;
 
     offset = 0;
     was_playing = playing;
@@ -2058,7 +2060,7 @@
 #endif
 
     /* Set crossfade setting for next buffer init which should be about... */
-    pcmbuf_crossfade_enable(global_settings.crossfade);
+    pcmbuf_request_crossfade_enable(global_settings.crossfade);
 
     /* initialize the buffering system */
 
diff --git a/apps/playback.h b/apps/playback.h
index b168770..c0fd15e 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -70,7 +70,7 @@
 bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
 size_t audio_get_filebuflen(void);
 void audio_pcmbuf_position_callback(size_t size) ICODE_ATTR;
-void audio_post_track_change(void);
+void audio_post_track_change(bool pcmbuf);
 int get_audio_hid(void);
 int *get_codec_hid(void);
 void audio_set_prev_elapsed(unsigned long setting);