Index: apps/playlist.c
===================================================================
--- apps/playlist.c	(revision 29752)
+++ apps/playlist.c	(working copy)
@@ -822,9 +822,6 @@
     playlist->amount++;
     playlist->num_inserted_tracks++;
     
-    /* Update index for resume. */
-    playlist_update_resume_index();
-
     return insert_position;
 }
 
@@ -925,9 +922,6 @@
         sync_control(playlist, false);
     }
     
-    /* Update index for resume. */
-    playlist_update_resume_index();
-
     return 0;
 }
 
@@ -987,9 +981,6 @@
             playlist->first_index, NULL, NULL, NULL);
     }
     
-    /* Update index for resume. */
-    playlist_update_resume_index();
-
     return 0;
 }
 
@@ -1030,9 +1021,6 @@
             playlist->first_index, -1, NULL, NULL, NULL);
     }
     
-    /* Update index for resume. */
-    playlist_update_resume_index();
-
     return 0;
 }
 
@@ -1205,9 +1193,6 @@
             break;
         }
     }
-    
-    /* Update index for resume. */
-    playlist_update_resume_index();
 }
 
 /*
@@ -2486,6 +2471,12 @@
     if (index < 0)
         return NULL;
 
+#if CONFIG_CODEC == SWCODEC
+    /* Just testing - don't care about the file name */
+    if (!buf || !buf_size)
+        return "";
+#endif
+
     control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
     seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
 
@@ -2632,30 +2623,17 @@
     return 0;
 }
 
-/* Get current playlist index. */
-int playlist_get_index(void)
-{
-    return current_playlist.index;
-}
-
-/* Update resume index within playlist_info structure. */
-void playlist_update_resume_index(void)
-{
-    struct playlist_info* playlist = &current_playlist;
-    playlist->resume_index = playlist->index;
-}
-
 /* Update resume info for current playing song.  Returns -1 on error. */
 int playlist_update_resume_info(const struct mp3entry* id3)
 {
     struct playlist_info* playlist = &current_playlist;
-    
+
     if (id3)
     {
-        if (global_status.resume_index  != playlist->resume_index ||
+        if (global_status.resume_index  != playlist->index ||
             global_status.resume_offset != id3->offset)
         {
-            global_status.resume_index  = playlist->resume_index;
+            global_status.resume_index  = playlist->index;
             global_status.resume_offset = id3->offset;
             status_save();
         }
@@ -3203,9 +3181,6 @@
     queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
 #endif
 
-    /* Update index for resume. */
-    playlist_update_resume_index();
-
     return result;
 }
 
Index: apps/playlist.h
===================================================================
--- apps/playlist.h	(revision 29752)
+++ apps/playlist.h	(working copy)
@@ -90,7 +90,6 @@
     int  buffer_end_pos; /* last position where buffer was written  */
     int  index;          /* index of current playing track          */
     int  first_index;    /* index of first song in playlist         */
-    int  resume_index;   /* index of playing track to resume        */
     int  amount;         /* number of tracks in the index           */
     int  last_insert_pos; /* last position we inserted a track      */
     int  seed;           /* shuffle seed                            */
@@ -132,7 +131,6 @@
 int playlist_next(int steps);
 bool playlist_next_dir(int direction);
 int playlist_get_resume_info(int *resume_index);
-int playlist_get_index(void);
 int playlist_update_resume_info(const struct mp3entry* id3);
 int playlist_get_display_index(void);
 int playlist_amount(void);
@@ -176,6 +174,5 @@
                                    int (*callback)(char*, void*),
                                    void* context);
 int playlist_remove_all_tracks(struct playlist_info *playlist);
-void playlist_update_resume_index(void);
 
 #endif /* __PLAYLIST_H__ */
Index: apps/pcmbuf.c
===================================================================
--- apps/pcmbuf.c	(revision 29752)
+++ apps/pcmbuf.c	(working copy)
@@ -86,6 +86,8 @@
 /* Amount pcmbuffer_pos will be increased.*/
 static size_t pcmbuffer_fillpos IDATA_ATTR;
 
+static struct chunkdesc *first_desc;
+
 /* Gapless playback */
 static bool track_transition IDATA_ATTR;
 
@@ -144,7 +146,12 @@
 static void pcmbuf_finish_crossfade_enable(void);
 #endif
 
+/* Callbacks into playback.c */
+extern void audio_pcmbuf_position_callback(unsigned int time);
+extern void audio_pcmbuf_track_change(bool pcmbuf);
+extern bool audio_pcmbuf_may_play(void);
 
+
 /**************************************/
 
 /* define this to show detailed chunkdesc usage information on the sim console */
@@ -153,9 +160,8 @@
 #ifndef SIMULATOR
 #undef DESC_DEBUG
 #endif
+
 #ifdef DESC_DEBUG
-static struct chunkdesc *first_desc;
-static bool show_desc_in_use = false;
 #define DISPLAY_DESC(caller) while(!show_desc(caller))
 #define DESC_IDX(desc)       (desc ? desc - first_desc : -1)
 #define SHOW_DESC(desc)      if(DESC_IDX(desc)==-1) DEBUGF("--"); \
@@ -231,6 +237,7 @@
             /* Flush! Discard all data after the currently playing chunk,
                and make the current chunk play next */
             logf("commit_chunk: flush");
+            pcm_play_lock();
             write_end_chunk->link = read_chunk->link;
             read_chunk->link = pcmbuf_current;
             while (write_end_chunk->link)
@@ -238,6 +245,9 @@
                 write_end_chunk = write_end_chunk->link;
                 pcmbuf_unplayed_bytes -= write_end_chunk->size;
             }
+
+            read_chunk->end_of_track = track_transition;
+            pcm_play_unlock();
         }
         /* If there is already a read buffer setup, add to it */
         else
@@ -248,7 +258,7 @@
         /* Otherwise create the buffer */
         read_chunk = pcmbuf_current;
     }
-    
+
     /* If flush_next_time is true, then the current chunk will be thrown out
      * and the next chunk to be committed will be the next to be played.
      * This is used to empty the PCM buffer for a track change. */
@@ -354,7 +364,7 @@
 #endif
         {
             logf("pcm starting");
-            if (!(audio_status() & AUDIO_STATUS_PAUSE))
+            if (audio_pcmbuf_may_play())
                 pcmbuf_play_start();
         }
     }
@@ -373,8 +383,12 @@
     /* crossfade has begun, put the new track samples in fadebuf */
     if (crossfade_active)
     {
-        *count = MIN(*count, CROSSFADE_BUFSIZE/4);
-        return fadebuf;
+        int cnt = MIN(*count, CROSSFADE_BUFSIZE/4);
+        if (prepare_insert(cnt << 2))
+        {
+            *count = cnt;
+            return fadebuf;
+        }
     }
     else
 #endif
@@ -421,9 +435,7 @@
 
 static inline void init_pcmbuffers(void)
 {
-#ifdef DESC_DEBUG
     first_desc = write_chunk;
-#endif
     struct chunkdesc *next = write_chunk;
     next++;
     write_end_chunk = write_chunk;
@@ -494,19 +506,27 @@
            currently playing chunk. If not, cancel notification. */
         track_transition = monitor;
         read_end_chunk->end_of_track = monitor;
+        if (!monitor)
+        {
+            /* Clear all notifications */
+            struct chunkdesc *desc = first_desc;
+            struct chunkdesc *end = desc + pcmbuf_descs();
+            while (desc < end)
+                desc++->end_of_track = false;
+        }
     }
     else
     {
         /* Post now if PCM stopped and last buffer was sent. */
         track_transition = false;
         if (monitor)
-            audio_post_track_change(false);
+            audio_pcmbuf_track_change(false);
     }
 
     pcm_play_unlock();
 }
 
-void pcmbuf_start_track_change(bool auto_skip)
+bool pcmbuf_start_track_change(bool auto_skip)
 {
     bool crossfade = false;
 #ifdef HAVE_CROSSFADE
@@ -546,9 +566,6 @@
 
         /* Cancel any pending automatic gapless transition */
         pcmbuf_monitor_track_change(false);
-        
-        /* Notify the wps that the track change starts now */
-        audio_post_track_change(false);
 
         /* Can't do two crossfades at once and, no fade if pcm is off now */
         if (
@@ -559,7 +576,8 @@
         {
             pcmbuf_play_stop();
             pcm_play_unlock();
-            return;
+            /* Notify playback that the track change starts now */
+            return true;
         }
 
         /* Not enough data, or not crossfading, flush the old data instead */
@@ -584,6 +602,9 @@
         /* Keep trigger outside the play lock or HW FIFO underruns can happen
            since frequency scaling is *not* always fast */
         trigger_cpu_boost();
+
+        /* Notify playback that the track change starts now */
+        return true;
     }
     else    /* automatic and not crossfading, so do gapless track change */
     {
@@ -593,6 +614,7 @@
          * as the last one in the track. */
         logf("  gapless track change");
         pcmbuf_monitor_track_change(true);
+        return false;
     }
 }
 
@@ -623,7 +645,7 @@
         if (pcmbuf_current->end_of_track)
         {
             track_transition = false;
-            audio_post_track_change(true);
+            audio_pcmbuf_track_change(true);
         }
 
         /* Put the finished chunk back into circulation */
@@ -955,9 +977,6 @@
                 return;
         }
 
-        /* Commit samples to the buffer */
-        while (!prepare_insert(length))
-            sleep(1);
         while (length > 0)
         {
             COMMIT_IF_NEEDED;
Index: apps/pcmbuf.h
===================================================================
--- apps/pcmbuf.h	(revision 29752)
+++ apps/pcmbuf.h	(working copy)
@@ -33,13 +33,21 @@
 void pcmbuf_play_stop(void);
 void pcmbuf_pause(bool pause);
 void pcmbuf_monitor_track_change(bool monitor);
-void pcmbuf_start_track_change(bool manual_skip);
+bool pcmbuf_start_track_change(bool manual_skip);
 
 /* Crossfade */
 #ifdef HAVE_CROSSFADE
 bool pcmbuf_is_crossfade_active(void);
 void pcmbuf_request_crossfade_enable(bool on_off);
 bool pcmbuf_is_same_size(void);
+#else
+/* Dummy functions with sensible returns */
+static inline bool pcmbuf_is_crossfade_active(void)
+    { return false; }
+static inline void pcmbuf_request_crossfade_enable(bool on_off)
+    { return; (void)on_off; }
+static inline bool pcmbuf_is_same_size(void)
+    { return true; }
 #endif
 
 /* Voice */
Index: apps/plugins/SOURCES
===================================================================
--- apps/plugins/SOURCES	(revision 29752)
+++ apps/plugins/SOURCES	(working copy)
@@ -30,7 +30,9 @@
 shopper.c
 resistor.c
 
+test_codec.c
 
+
 #ifdef USB_ENABLE_HID
 remote_control.c
 #endif
Index: apps/plugins/test_codec.c
===================================================================
--- apps/plugins/test_codec.c	(revision 29752)
+++ apps/plugins/test_codec.c	(working copy)
@@ -127,7 +127,6 @@
 };
 
 static struct test_track_info track;
-static bool taginfo_ready = true;
 
 static bool use_dsp;
 
@@ -433,6 +432,7 @@
 static void set_elapsed(unsigned long value)
 {
     elapsed = value;
+    ci.id3->elapsed = value;
 }
 
 
@@ -482,6 +482,7 @@
 static void advance_buffer(size_t amount)
 {
     ci.curpos += amount;
+    ci.id3->offset = ci.curpos;
 }
 
 
@@ -499,20 +500,17 @@
     /* Do nothing */
 }
 
-/* Request file change from file buffer. Returns true is next
-   track is available and changed. If return value is false,
-   codec should exit immediately with PLUGIN_OK status. */
-static bool request_next_track(void)
+/* Codec calls this to know what it should do next. */
+static enum codec_command_action get_command(intptr_t *param)
 {
-    /* We are only decoding a single track */
-    return false;
+    rb->yield();
+    return CODEC_ACTION_NULL; /* just continue processing */
+    (void)param;
 }
 
-
 static void set_offset(size_t value)
 {
-    /* ??? */
-    (void)value;
+    ci.id3->offset = value;
 }
 
 
@@ -546,6 +544,9 @@
 {
     /* --- Our "fake" implementations of the codec API functions. --- */
 
+    ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
+                                                    CODEC_IDX_AUDIO);
+
     ci.codec_get_buffer = codec_get_buffer;
 
     if (wavinfo.fd >= 0 || checksum) {
@@ -560,11 +561,9 @@
     ci.advance_buffer = advance_buffer;
     ci.seek_buffer = seek_buffer;
     ci.seek_complete = seek_complete;
-    ci.request_next_track = request_next_track;
     ci.set_offset = set_offset;
     ci.configure = configure;
-    ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
-                                                    CODEC_IDX_AUDIO);
+    ci.get_command = get_command;
 
     /* --- "Core" functions --- */
 
@@ -620,20 +619,22 @@
 static void codec_thread(void)
 {
     const char* codecname;
-    void *handle;
-    int res = CODEC_ERROR;
+    int res;
 
     codecname = rb->get_codec_filename(track.id3.codectype);
 
-    /* Load the codec and start decoding. */
-    handle = rb->codec_load_file(codecname,&ci);
+    /* Load the codec */
+    res = rb->codec_load_file(codecname, &ci);
 
-    if (handle != NULL)
+    if (res >= 0)
     {
-        res = rb->codec_begin(handle);
-        rb->codec_close(handle);
+        /* Decode the file */
+        res = rb->codec_run_proc();
     }
 
+    /* Clean up */
+    rb->codec_close();
+
     /* Signal to the main thread that we are done */
     endtick = *rb->current_tick - rebuffertick;
     codec_playing = false;
@@ -705,11 +706,7 @@
     /* Prepare the codec struct for playing the whole file */
     ci.filesize = track.filesize;
     ci.id3 = &track.id3;
-    ci.taginfo_ready = &taginfo_ready;
     ci.curpos = 0;
-    ci.stop_codec = false;
-    ci.new_track = 0;
-    ci.seek_time = 0;
 
     if (use_dsp)
         rb->dsp_configure(ci.dsp, DSP_RESET, 0);
Index: apps/gui/wps.c
===================================================================
--- apps/gui/wps.c	(revision 29752)
+++ apps/gui/wps.c	(working copy)
@@ -219,10 +219,10 @@
 #endif
         case ACTION_TOUCH_SCROLLBAR:
             skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->length*offset/100;
-            if (!skin_get_global_state()->paused)
 #if (CONFIG_CODEC == SWCODEC)
-                audio_pre_ff_rewind();
+            audio_pre_ff_rewind();
 #else
+            if (!skin_get_global_state()->paused)
                 audio_pause();
 #endif
             audio_ff_rewind(skin_get_global_state()->id3->elapsed);
@@ -300,10 +300,10 @@
                     if ( (audio_status() & AUDIO_STATUS_PLAY) &&
                           skin_get_global_state()->id3 && skin_get_global_state()->id3->length )
                     {
-                        if (!skin_get_global_state()->paused)
 #if (CONFIG_CODEC == SWCODEC)
-                            audio_pre_ff_rewind();
+                        audio_pre_ff_rewind();
 #else
+                        if (!skin_get_global_state()->paused)
                             audio_pause();
 #endif
 #if CONFIG_KEYPAD == PLAYER_PAD
@@ -472,10 +472,10 @@
             return;
         }
 
-        if (!state->paused)
 #if (CONFIG_CODEC == SWCODEC)
-            audio_pre_ff_rewind();
+        audio_pre_ff_rewind();
 #else
+        if (!state->paused)
             audio_pause();
 #endif
 
@@ -554,16 +554,20 @@
     {
         elapsed += step * direction;
     }
-    if((audio_status() & AUDIO_STATUS_PLAY) && !state->paused)
+    if(audio_status() & AUDIO_STATUS_PLAY)
     {
 #if (CONFIG_CODEC == SWCODEC)
         audio_pre_ff_rewind();
 #else
-        audio_pause();
+        if (!state->paused)
+            audio_pause();
 #endif
     }
+
+#if (CONFIG_CODEC == SWCODEC)
+    audio_ff_rewind(elapsed);
+#else
     audio_ff_rewind(state->id3->elapsed = elapsed);
-#if (CONFIG_CODEC != SWCODEC)
     if (!state->paused)
         audio_resume();
 #endif
@@ -849,10 +853,10 @@
                 {
                     if (state->id3->cuesheet)
                     {
-                        if (!state->paused)
 #if (CONFIG_CODEC == SWCODEC)
-                            audio_pre_ff_rewind();
+                        audio_pre_ff_rewind();
 #else
+                        if (!state->paused)
                             audio_pause();
 #endif
                         audio_ff_rewind(0);
@@ -1146,6 +1150,17 @@
     skin_request_full_update(WPS);
 }
 
+#ifdef AUDIO_FAST_SKIP_PREVIEW
+/* this is called on the audio_skip caller thread */
+static void track_skip_callback(void *param)
+{
+    struct wps_state *state = skin_get_global_state();
+    state->id3 = audio_current_track();
+    state->nid3 = audio_next_track();
+    skin_request_full_update(WPS);
+    (void)param;
+}
+#endif /* AUDIO_FAST_SKIP_PREVIEW */
 
 static void wps_state_init(void)
 {
@@ -1167,6 +1182,9 @@
     /* add the WPS track event callbacks */
     add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback);
     add_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, false, nextid3available_callback);
+#ifdef AUDIO_FAST_SKIP_PREVIEW
+    add_event(PLAYBACK_EVENT_TRACK_SKIP, false, track_skip_callback);
+#endif
 }
 
 
Index: apps/menus/playback_menu.c
===================================================================
--- apps/menus/playback_menu.c	(revision 29752)
+++ apps/menus/playback_menu.c	(working copy)
@@ -159,9 +159,13 @@
     switch (action)
     {
         case ACTION_EXIT_MENUITEM: /* on exit */
+#if CONFIG_CODEC == SWCODEC
+            audio_set_cuesheet(global_settings.cuesheet);
+#else
             if (global_settings.cuesheet)
                 splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
             break;
+#endif
     }
     return action;
 }
Index: apps/appevents.h
===================================================================
--- apps/appevents.h	(revision 29752)
+++ apps/appevents.h	(working copy)
@@ -35,6 +35,7 @@
     PLAYBACK_EVENT_TRACK_BUFFER,
     PLAYBACK_EVENT_TRACK_FINISH,
     PLAYBACK_EVENT_TRACK_CHANGE,
+    PLAYBACK_EVENT_TRACK_SKIP,
     PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE,
 };
 
Index: apps/voice_thread.h
===================================================================
--- apps/voice_thread.h	(revision 29752)
+++ apps/voice_thread.h	(working copy)
@@ -31,5 +31,6 @@
 void voice_thread_init(void);
 void voice_thread_resume(void);
 void voice_thread_set_priority(int priority);
+void voice_wait(void);
 
 #endif /* VOICE_THREAD_H */
Index: apps/misc.c
===================================================================
--- apps/misc.c	(revision 29752)
+++ apps/misc.c	(working copy)
@@ -83,6 +83,9 @@
 #include "bookmark.h"
 #include "wps.h"
 #include "playback.h"
+#if CONFIG_CODEC == SWCODEC
+#include "voice_thread.h"
+#endif
 
 #ifdef BOOTFILE
 #if !defined(USB_NONE) && !defined(USB_HANDLED_BY_OF) \
