Clarify track transition code in pcmbuf and playback. No functional changes yet.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23506 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index d4217c3..affb560 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -416,46 +416,8 @@
 }
 
 
-/** pcmbuf track change callbacks */
-
-/* Between the codec and PCM track change, we need to keep updating the
-   "elapsed" value of the previous (to the codec, but current to the
-   user/PCM/WPS) track, so that the progressbar reaches the end.
-   During that transition, the WPS will display prevtrack_id3. */
-static void codec_pcmbuf_position_callback(size_t size) ICODE_ATTR;
-static void codec_pcmbuf_position_callback(size_t size)
-{
-    /* This is called from an ISR, so be quick */
-    unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
-        othertrack_id3->elapsed;
-
-    if (time >= othertrack_id3->length)
-    {
-        pcmbuf_set_position_callback(NULL);
-        othertrack_id3->elapsed = othertrack_id3->length;
-    }
-    else
-        othertrack_id3->elapsed = time;
-}
-
-static void codec_pcmbuf_track_changed_callback(void)
-{
-    LOGFQUEUE("codec > pcmbuf/audio Q_AUDIO_TRACK_CHANGED");
-    pcmbuf_set_position_callback(NULL);
-    audio_post_track_change();
-}
-
-
 /** track change functions */
 
-static inline void codec_gapless_track_change(void)
-{
-    /* callback keeps the progress bar moving while the pcmbuf empties */
-    pcmbuf_set_position_callback(codec_pcmbuf_position_callback);
-    /* set the pcmbuf callback for when the track really changes */
-    pcmbuf_set_event_handler(codec_pcmbuf_track_changed_callback);
-}
-
 static inline void codec_crossfade_track_change(void)
 {
     /* Initiate automatic crossfade mode */
@@ -484,8 +446,8 @@
                 /* shuffle mode is on, so crossfade: */
                 codec_crossfade_track_change();
             else
-                /* shuffle mode is off, so do a gapless track change */
-                codec_gapless_track_change();
+                /* shuffle mode is off, so normal gapless playback */
+                pcmbuf_start_track_change();
         }
         else
             /* normal crossfade:  */
@@ -493,7 +455,7 @@
     }
     else
         /* normal gapless playback. */
-        codec_gapless_track_change();
+        pcmbuf_start_track_change();
 }
 
 static bool codec_load_next_track(void)
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 3ec0c11..3b461ec 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -25,6 +25,7 @@
 #include <kernel.h>
 #include "pcmbuf.h"
 #include "pcm.h"
+#include "playback.h"
 
 /* Define LOGF_ENABLE to enable logf output in this file */
 /*#define LOGF_ENABLE*/
@@ -62,8 +63,8 @@
     void *addr;
     size_t size;
     struct pcmbufdesc* link;
-    /* Call this when the buffer has been played */
-    void (*callback)(void);
+    /* true if last chunk in the track */
+    bool end_of_track;
 };
 
 #define PCMBUF_DESCS(bufsize) \
@@ -82,8 +83,8 @@
 static char *fadebuf IDATA_ATTR;
 static char *voicebuf IDATA_ATTR;
 
-static void (*pcmbuf_event_handler)(void) IDATA_ATTR;
-static void (*position_callback)(size_t size) IDATA_ATTR;
+static bool end_of_track IDATA_ATTR;
+bool track_transition IDATA_ATTR;
 
 /* Crossfade related state */
 static bool crossfade_enabled;
@@ -134,25 +135,57 @@
 static void pcmbuf_under_watermark(bool under);
 static bool pcmbuf_flush_fillpos(void);
 
-#define CALL_IF_EXISTS(function, args...) if (function) function(args)
-/* This function has 3 major logical parts (separated by brackets both for
+
+/* Track change functions */
+
+/* The codec is moving on to the next track, but the current track is
+ * 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)
+{
+    /* we're starting a track transition */
+    track_transition = true;
+    
+    /* mark the last chunk in the track */
+    end_of_track = true;
+}
+
+/* Called when the last chunk in the track has been played */
+static void pcmbuf_finish_track_change(void)
+{
+    /* not in a track transition anymore */
+    if(track_transition){logf("pcmbuf: (finish change) track transition false");}
+    track_transition = false;
+    
+    /* notify playback that the track has just finished */
+    audio_post_track_change();
+}
+
+
+/** PCM driver callback
+ * This function has 3 major logical parts (separated by brackets both for
  * readability and variable scoping).  The first part performs the
  * operations related to finishing off the last buffer we fed to the DMA.
  * The second part detects the end of playlist condition when the pcm
  * buffer is empty except for uncommitted samples.  Then they are committed.
  * The third part performs the operations involved in sending a new buffer
  * to the DMA. */
-static void pcmbuf_callback(unsigned char** start, size_t* size) ICODE_ATTR;
-static void pcmbuf_callback(unsigned char** start, size_t* size)
+static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) ICODE_ATTR;
+static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
 {
     {
         struct pcmbufdesc *pcmbuf_current = pcmbuf_read;
         /* Take the finished buffer out of circulation */
         pcmbuf_read = pcmbuf_current->link;
 
-        /* The buffer is finished, call the callback functions */
-        CALL_IF_EXISTS(position_callback, last_chunksize);
-        CALL_IF_EXISTS(pcmbuf_current->callback);
+        /* if during a track transition, update the elapsed time */
+        if (track_transition)
+            audio_pcmbuf_position_callback(last_chunksize);
+        
+        /* if last buffer in the track, let the audio thread know */
+        if (pcmbuf_current->end_of_track)
+            pcmbuf_finish_track_change();
 
         /* Put the finished buffer back into circulation */
         pcmbuf_write_end->link = pcmbuf_current;
@@ -170,7 +203,7 @@
         /* Commit last samples at end of playlist */
         if (audiobuffer_fillpos && !pcmbuf_read)
         {
-            logf("pcmbuf callback: commit last samples");
+            logf("pcmbuf_pcm_callback: commit last samples");
             pcmbuf_flush_fillpos();
         }
     }
@@ -195,16 +228,12 @@
             last_chunksize = 0;
             *realsize = 0;
             *realstart = NULL;
-            CALL_IF_EXISTS(pcmbuf_event_handler);
+            if (end_of_track)
+                pcmbuf_finish_track_change();
         }
     }
 }
 
-void pcmbuf_set_position_callback(void (*callback)(size_t size))
-{
-    position_callback = callback;
-}
-
 static void pcmbuf_set_watermark_bytes(void)
 {
     pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ?
@@ -225,10 +254,9 @@
     /* Fill in the values in the new buffer chunk */
     pcmbuf_current->addr = &audiobuffer[audiobuffer_pos];
     pcmbuf_current->size = size;
-    pcmbuf_current->callback = pcmbuf_event_handler;
+    pcmbuf_current->end_of_track = end_of_track;
     pcmbuf_current->link = NULL;
-    /* This is single use only */
-    pcmbuf_event_handler = NULL;
+    end_of_track = false;   /* This is single use only */
     if (pcmbuf_read != NULL) {
         if (pcmbuf_flush)
         {
@@ -320,11 +348,6 @@
     }
 }
 
-void pcmbuf_set_event_handler(void (*event_handler)(void))
-{
-    pcmbuf_event_handler = event_handler;
-}
-
 unsigned int pcmbuf_get_latency(void)
 {
     /* Be careful how this calculation is rearranged, it's easy to overflow */
@@ -493,8 +516,9 @@
 
     pcmbuf_init_pcmbuffers();
 
-    position_callback = NULL;
-    pcmbuf_event_handler = NULL;
+    if(track_transition){logf("pcmbuf: (init) track transition false");}
+    end_of_track = false;
+    track_transition = false;
 
     pcmbuf_crossfade_enable_finished();
 
@@ -531,7 +555,7 @@
     {
         last_chunksize = pcmbuf_read->size;
         pcmbuf_unplayed_bytes -= last_chunksize;
-        pcm_play_data(pcmbuf_callback,
+        pcm_play_data(pcmbuf_pcm_callback,
             (unsigned char *)pcmbuf_read->addr, last_chunksize);
     }
 }
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index b4e551f..b6f0c76 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -57,8 +57,7 @@
 bool pcmbuf_is_lowdata(void);
 void pcmbuf_play_start(void);
 bool pcmbuf_crossfade_init(bool manual_skip);
-void pcmbuf_set_event_handler(void (*callback)(void));
-void pcmbuf_set_position_callback(void (*callback)(size_t size));
+void pcmbuf_start_track_change(void);
 size_t pcmbuf_free(void);
 unsigned int pcmbuf_get_latency(void);
 void pcmbuf_set_low_latency(bool state);
diff --git a/apps/playback.c b/apps/playback.c
index bebbfb1..d3c4b46 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -179,6 +179,7 @@
 
 /* Track change controls */
 bool automatic_skip = false;        /* Who initiated in-progress skip? (C/A-) */
+extern bool track_transition;       /* Are we in a track transition? */
 static bool dir_skip = false;       /* Is a directory skip pending? (A) */
 static bool new_playlist = false;   /* Are we starting a new playlist? (A) */
 static int wps_offset = 0;          /* Pending track change offset, to keep WPS responsive (A) */
@@ -223,10 +224,31 @@
 
 /**************************************/
 
-/* Post message from pcmbuf callback in the codec thread that
- * the end of the previous track has just been played. */
+/* Between the codec and PCM track change, we need to keep updating the
+   "elapsed" value of the previous (to the codec, but current to the
+   user/PCM/WPS) track, so that the progressbar reaches the end.
+   During that transition, the WPS will display othertrack_id3. */
+void audio_pcmbuf_position_callback(size_t size)
+{
+    /* This is called from an ISR, so be quick */
+    unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
+        othertrack_id3->elapsed;
+
+    if (time >= othertrack_id3->length)
+    {
+        if(track_transition){logf("playback: (callback) track transition false");}
+        track_transition = false;
+        othertrack_id3->elapsed = othertrack_id3->length;
+    }
+    else
+        othertrack_id3->elapsed = time;
+}
+
+/* Post message from pcmbuf that the end of the previous track
+ * has just been played. */
 void audio_post_track_change(void)
 {
+    LOGFQUEUE("pcmbuf > pcmbuf Q_AUDIO_TRACK_CHANGED");
     queue_post(&pcmbuf_queue, Q_AUDIO_TRACK_CHANGED, 0);
 }
 
@@ -248,16 +270,6 @@
     return false;
 }
 
-/* Clear the pcmbuf queue of messages
- * Permissible Context(s): Thread
- */
-static void pcmbuf_queue_clear(void)
-{
-    pcm_play_lock();
-    queue_clear(&pcmbuf_queue);
-    pcm_play_unlock();
-}
-
 /* --- Helper functions --- */
 
 static struct mp3entry *bufgetid3(int handle_id)
@@ -1617,7 +1629,9 @@
     if (pcm_is_playing())
     {
         pcmbuf_play_stop();
-        pcmbuf_queue_clear();
+        pcm_play_lock();
+        queue_clear(&pcmbuf_queue);
+        pcm_play_unlock();
     }
     pcmbuf_pause(paused);
 }
diff --git a/apps/playback.h b/apps/playback.h
index 95b0bef..b168770 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -69,6 +69,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);
 int get_audio_hid(void);
 int *get_codec_hid(void);