First commit of reworking voice to be mroe stable on swcodec


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9758 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/codecs/a52.c b/apps/codecs/a52.c
index 8ad9d37..71e0fda 100644
--- a/apps/codecs/a52.c
+++ b/apps/codecs/a52.c
@@ -141,7 +141,6 @@
     ci->memset(iedata, 0, iend - iedata);
     #endif
 
-    ci->configure(CODEC_DSP_ENABLE, (bool *)true);
     ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_STEREO_MODE, (long *)STEREO_NONINTERLEAVED);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)28);
@@ -185,9 +184,11 @@
         a52_decode_data(filebuf, filebuf + n);
         ci->advance_buffer(n);
     }
+    retval = CODEC_OK;
+
     if (ci->request_next_track())
         goto next_track;
-    retval = CODEC_OK;
+
 exit:
     a52_free(state);
     return retval;
diff --git a/apps/codecs/aac.c b/apps/codecs/aac.c
index 02d4606..532082f 100644
--- a/apps/codecs/aac.c
+++ b/apps/codecs/aac.c
@@ -63,7 +63,6 @@
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16));
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
 
-    ci->configure(CODEC_DSP_ENABLE, (bool *)true);
     ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(29));
@@ -88,7 +87,7 @@
     if (!qtmovie_read(&input_stream, &demux_res)) {
         LOGF("FAAD: Error initialising file\n");
         err = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     /* initialise the sound converter */
@@ -98,7 +97,7 @@
     if (!hDecoder) {
         LOGF("FAAD: Error opening decoder\n");
         err = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(hDecoder);
@@ -112,7 +111,7 @@
     if (err) {
         LOGF("FAAD: Error initialising decoder: %d, type=%d\n", err,hDecoder->object_type);
         err = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     ci->id3->frequency=s;
@@ -142,7 +141,7 @@
                              &sample_byte_size)) {
             LOGF("AAC: Error in get_sample_info\n");
             err = CODEC_ERROR;
-            goto exit;
+            goto done;
         }
 
         /* Request the required number of bytes from the input buffer */
@@ -156,7 +155,7 @@
         if (frameInfo.error > 0) {
              LOGF("FAAD: decoding error \"%s\"\n", NeAACDecGetErrorMessage(frameInfo.error));
              err = CODEC_ERROR;
-             goto exit;
+             goto done;
         }
 
         /* Get the number of decoded samples */
@@ -182,13 +181,14 @@
 
         i++;
     }
+    err = CODEC_OK;
 
+done:
     LOGF("AAC: Decoded %d samples\n",samplesdone);
 
     if (ci->request_next_track())
         goto next_track;
 
-    err = CODEC_OK;
 exit:
     return err;
 }
diff --git a/apps/codecs/aiff.c b/apps/codecs/aiff.c
index 20d2dd3..1e7adca 100644
--- a/apps/codecs/aiff.c
+++ b/apps/codecs/aiff.c
@@ -98,11 +98,11 @@
     buf = ci->request_buffer(&n, 1024);
     if (n < 44) {
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
     if ((memcmp(buf, "FORM", 4) != 0) || (memcmp(&buf[8], "AIFF", 4) != 0)) {
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     buf += 12;
@@ -117,7 +117,7 @@
             if (i != 18) {
                 DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu != 18\n", i);
                 i = CODEC_ERROR;
-                goto exit;
+                goto done;
             }
             /* num_channels */
             num_channels = ((buf[8]<<8)|buf[9]);
@@ -130,7 +130,7 @@
             if (buf[16] != 0x40) {
                 DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n", i);
                 i = CODEC_ERROR;
-                goto exit;
+                goto done;
             }
             sample_rate = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1;
             sample_rate = sample_rate >> (16 + 14 - buf[17]);
@@ -140,7 +140,7 @@
             if (sample_size == 0) {
                 DEBUGF("CODEC_ERROR: unsupported chunk order\n");
                 i = CODEC_ERROR;
-                goto exit;
+                goto done;
             }
             /* offset2snd */
             offset2snd = ((buf[8]<<8)|buf[9]);
@@ -161,7 +161,7 @@
         if (n < (i + 8)) {
             DEBUGF("CODEC_ERROR: AIFF header size > 1024\n");
             i = CODEC_ERROR;
-            goto exit;
+            goto done;
         }
         n -= i + 8;
     } /* while 'SSND' */
@@ -169,21 +169,20 @@
     if (num_channels == 0) {
         DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n");
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
     if (numbytes == 0) {
         DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n");
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
     if (sample_size > 24) {
         DEBUGF("CODEC_ERROR: PCM with more than 24 bits per sample "
                "is unsupported\n");
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
-    ci->configure(CODEC_DSP_ENABLE, (bool *)true);
     ci->configure(DSP_SET_FREQUENCY, (long *)(ci->id3->frequency));
     ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)28);
 
@@ -194,7 +193,7 @@
     } else {
         DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     firstblockposn = 1024 - n;
@@ -277,11 +276,12 @@
 
         ci->set_elapsed(bytesdone*1000LL/avgbytespersec);
     }
+    i = CODEC_OK;
 
+done:
     if (ci->request_next_track())
         goto next_track;
 
-    i = CODEC_OK;
 exit:
     return i;
 }
diff --git a/apps/codecs/alac.c b/apps/codecs/alac.c
index 73f45fc..01172ee 100644
--- a/apps/codecs/alac.c
+++ b/apps/codecs/alac.c
@@ -64,7 +64,6 @@
   ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
   ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128));
 
-  ci->configure(CODEC_DSP_ENABLE, (bool *)true);
   ci->configure(DSP_DITHER, (bool *)false);
   ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED);
   ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(ALAC_OUTPUT_DEPTH-1));
@@ -89,7 +88,7 @@
   if (!qtmovie_read(&input_stream, &demux_res)) {
     LOGF("ALAC: Error initialising file\n");
     retval = CODEC_ERROR;
-    goto exit;
+    goto done;
   }
 
   /* initialise the sound converter */
@@ -121,7 +120,7 @@
                          &sample_byte_size)) {
       LOGF("ALAC: Error in get_sample_info\n");
       retval = CODEC_ERROR;
-      goto exit;
+      goto done;
     }
 
     /* Request the required number of bytes from the input buffer */
@@ -129,7 +128,7 @@
     buffer=ci->request_buffer(&n,sample_byte_size);
     if (n!=sample_byte_size) {
         retval = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     /* Decode one block - returned samples will be host-endian */
@@ -156,13 +155,14 @@
 
     i++;
   }
+  retval = CODEC_OK;
 
+done:
   LOGF("ALAC: Decoded %d samples\n",samplesdone);
 
   if (ci->request_next_track())
    goto next_track;
 
-  retval = CODEC_OK;
 exit:
   return retval;
 }
diff --git a/apps/codecs/flac.c b/apps/codecs/flac.c
index cc2ce63..5e392da 100644
--- a/apps/codecs/flac.c
+++ b/apps/codecs/flac.c
@@ -224,7 +224,7 @@
 {
     int8_t *buf;
     FLACContext fc;
-    uint32_t samplesdone;
+    uint32_t samplesdone = 0;
     uint32_t elapsedtime;
     size_t bytesleft;
     int consumed;
@@ -244,7 +244,6 @@
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128));
 
-    ci->configure(CODEC_DSP_ENABLE, (bool *)true);
     ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_STEREO_MODE, (long *)STEREO_NONINTERLEAVED);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(FLAC_OUTPUT_DEPTH-1));
@@ -260,7 +259,7 @@
     if (!flac_init(&fc,ci->id3->first_frame_offset)) {
         LOGF("FLAC: Error initialising codec\n");
         retval = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     while (!*ci->taginfo_ready)
@@ -292,7 +291,7 @@
                              bytesleft,ci->yield)) < 0) {
              LOGF("FLAC: Frame %d, error %d\n",frame,res);
              retval = CODEC_ERROR;
-             goto exit;
+             goto done;
         }
         consumed=fc.gb.index/8;
         frame++;
@@ -312,12 +311,14 @@
 
         buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
     }
+    retval = CODEC_OK;
+
+done:
     LOGF("FLAC: Decoded %d samples\n",samplesdone);
 
     if (ci->request_next_track())
         goto next_track;
 
-    retval = CODEC_OK;
 exit:
     return retval;
 }
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c
index 7219095..51038c5 100644
--- a/apps/codecs/mpa.c
+++ b/apps/codecs/mpa.c
@@ -91,7 +91,7 @@
 /* this is the codec entry point */
 enum codec_status codec_start(struct codec_api *api)
 {
-    int status = CODEC_OK;
+    int status;
     size_t size;
     int file_end;
     int frame_skip;      /* samples to skip current frame */
@@ -110,7 +110,6 @@
 
     /* Create a decoder instance */
 
-    ci->configure(CODEC_DSP_ENABLE, (bool *)true);
     ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(MAD_F_FRACBITS));
     ci->configure(DSP_SET_CLIP_MIN, (int *)-MAD_F_ONE);
@@ -122,6 +121,7 @@
      * for gapless playback.
      * Reinitializing seems to be necessary to avoid playback quircks when seeking. */
     next_track:
+    status = CODEC_OK;
         
     init_mad();
 
@@ -171,7 +171,7 @@
                     ci->id3->first_frame_offset;
 
             if (!ci->seek_buffer(newpos))
-                goto next_track;
+                break;
             ci->seek_complete();
             init_mad();
         }
@@ -192,7 +192,7 @@
                     break;
         
                 /* Fill the buffer */
-                if (stream.next_frame)
+                if (stream.next_frame && stream.next_frame != stream.this_frame)
                     ci->advance_buffer_loc((void *)stream.next_frame);
                 else
                     ci->advance_buffer(size);
diff --git a/apps/codecs/mpc.c b/apps/codecs/mpc.c
index 4201b25..ee63901 100644
--- a/apps/codecs/mpc.c
+++ b/apps/codecs/mpc.c
@@ -90,7 +90,6 @@
     ci->memset(iedata, 0, iend - iedata);
     #endif
     
-    ci->configure(CODEC_DSP_ENABLE, (bool *)true);
     ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)(28));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (long *)(1024*16));
@@ -113,7 +112,7 @@
     mpc_streaminfo_init(&info);
     if (mpc_streaminfo_read(&info, &reader) != ERROR_CODE_OK) {
         retval = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
     frequency = info.sample_freq;
     ci->configure(DSP_SET_FREQUENCY, (long *)(long)info.sample_freq);
@@ -127,7 +126,7 @@
         ci->configure(DSP_SET_STEREO_MODE, (long *)STEREO_MONO);
     else {
        retval = CODEC_ERROR;
-       goto exit;
+       goto done;
     }
     
     codec_set_replaygain(ci->id3);
@@ -135,7 +134,7 @@
     mpc_decoder_setup(&decoder, &reader);
     if (!mpc_decoder_initialize(&decoder, &info)) {
         retval = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     /* This is the decoding loop. */
@@ -171,7 +170,7 @@
         ci->yield();
         if (status == (unsigned)(-1)) { /* decode error */
             retval = CODEC_ERROR;
-            goto exit;
+            goto done;
         } else {
             while (!ci->pcmbuf_insert_split(sample_buffer,
                                             sample_buffer + MPC_FRAME_LENGTH,
@@ -181,11 +180,12 @@
             ci->set_elapsed(samplesdone/(frequency/1000));
         }
     } while (status != 0);
-    
+    retval = CODEC_OK;
+
+done:
     if (ci->request_next_track())
         goto next_track;
 
-    retval = CODEC_OK;
 exit:
     return retval;
 }
diff --git a/apps/codecs/shorten.c b/apps/codecs/shorten.c
index 03a0802..8d62a12 100644
--- a/apps/codecs/shorten.c
+++ b/apps/codecs/shorten.c
@@ -63,7 +63,6 @@
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128));
 
-    ci->configure(CODEC_DSP_ENABLE, (bool *)true);
     ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_STEREO_MODE, (long *)STEREO_NONINTERLEAVED);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(SHN_OUTPUT_DEPTH-1));
@@ -146,7 +145,7 @@
  
         if (res == FN_ERROR) {
             LOGF("Shorten: shorten_decode_frames error (%d)\n", samplesdone);
-            return CODEC_ERROR;
+            break;
         } else {
             /* Insert decoded samples in pcmbuf */
             if (nsamples) {
diff --git a/apps/codecs/vorbis.c b/apps/codecs/vorbis.c
index a9a2745..1900364 100644
--- a/apps/codecs/vorbis.c
+++ b/apps/codecs/vorbis.c
@@ -129,7 +129,6 @@
     rb->memset(iedata, 0, iend - iedata);
     #endif
 
-    rb->configure(CODEC_DSP_ENABLE, (bool *)true);
     rb->configure(DSP_DITHER, (bool *)false);
     rb->configure(DSP_SET_SAMPLE_DEPTH, (long *)24);
     rb->configure(DSP_SET_CLIP_MAX, (long *)((1 << 24) - 1));
@@ -194,7 +193,7 @@
     } else {
          //rb->logf("ov_open: %d", error);
          error = CODEC_ERROR;
-         goto exit;
+         goto done;
     }
 
     if (rb->id3->offset) {
@@ -224,7 +223,7 @@
         if (current_section != previous_section) {
             if (!vorbis_set_codec_parameters(&vf)) {
                 error = CODEC_ERROR;
-                goto exit;
+                goto done;
             } else {
                 previous_section = current_section;
             }
@@ -243,7 +242,9 @@
             rb->set_elapsed(ov_time_tell(&vf));
         }
     }
+    error = CODEC_OK;
     
+done:
     if (rb->request_next_track()) {
         /* Clean things up for the next track */
         vf.dataoffsets = NULL;
@@ -255,7 +256,6 @@
         goto next_track;
     }
         
-    error = CODEC_OK;
 exit:
     return error;
 }
diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c
index c89d121..d9be0a3 100644
--- a/apps/codecs/wav.c
+++ b/apps/codecs/wav.c
@@ -242,7 +242,6 @@
     ci->memset(iedata, 0, iend - iedata);
 #endif
 
-    ci->configure(CODEC_DSP_ENABLE, (bool *)true);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)28);
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256));
@@ -261,11 +260,11 @@
     buf = ci->request_buffer(&n, 1024);
     if (n < 44) {
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
     if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) {
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     buf += 12;
@@ -281,7 +280,7 @@
             if (i < 16) {
                 DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%lu < 16\n", i);
                 i = CODEC_ERROR;
-                goto exit;
+                goto done;
             }
             /* wFormatTag */
             formattag=buf[8]|(buf[9]<<8);
@@ -309,7 +308,7 @@
                         DEBUGF("CODEC_ERROR: dvi_adpcm is missing "
                                "SamplesPerBlock value\n");
                         i = CODEC_ERROR;
-                        goto exit;
+                        goto done;
                     }
                     samplesperblock = buf[26]|(buf[27]<<8);
                 } else if (formattag == WAVE_FORMAT_EXTENSIBLE) {
@@ -317,7 +316,7 @@
                         DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is "
                                "missing extension\n");
                         i = CODEC_ERROR;
-                        goto exit;
+                        goto done;
                     }
                     /* wValidBitsPerSample */
                     bitspersample = buf[26]|(buf[27]<<8);
@@ -344,7 +343,7 @@
         if (n < (i + 8)) {
             DEBUGF("CODEC_ERROR: WAVE header size > 1024\n");
             i = CODEC_ERROR;
-            goto exit;
+            goto done;
         }
         n -= i + 8;
     }
@@ -352,12 +351,12 @@
     if (channels == 0) {
         DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
     if (numbytes == 0) {
         DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
     if (formattag != WAVE_FORMAT_PCM && totalsamples == 0) {
         /* This is non-fatal for some formats */
@@ -368,7 +367,7 @@
         if (bitspersample != 8) {
             DEBUGF("CODEC_ERROR: alaw and mulaw must have 8 bitspersample\n");
             i = CODEC_ERROR;
-            goto exit;
+            goto done;
         }
         bytespersample = channels;
     }
@@ -376,13 +375,13 @@
         && bitspersample != 4 && bitspersample != 3) {
         DEBUGF("CODEC_ERROR: dvi_adpcm must have 3 or 4 bitspersample\n");
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
     if (formattag == WAVE_FORMAT_PCM && bitspersample > 32) {
         DEBUGF("CODEC_ERROR: pcm with more than 32 bitspersample "
                "is unsupported\n");
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     ci->configure(DSP_SET_FREQUENCY, (long *)(ci->id3->frequency));
@@ -393,7 +392,7 @@
     } else {
         DEBUGF("CODEC_ERROR: more than 2 channels\n");
         i = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     if (totalsamples == 0) {
@@ -406,7 +405,7 @@
         } else {
             DEBUGF("CODEC_ERROR: cannot compute totalsamples\n");
             i = CODEC_ERROR;
-            goto exit;
+            goto done;
         }
     }
 
@@ -505,14 +504,14 @@
                                      samples + i*samplesperblock*channels,
                                      &decodedsize) != CODEC_OK) {
                     i = CODEC_ERROR;
-                    goto exit;
+                    goto done;
                 }
             }
             bufsize = nblocks*samplesperblock*channels*4;
         } else {
             DEBUGF("CODEC_ERROR: unsupported format %x\n", formattag);
             i = CODEC_ERROR;
-            goto exit;
+            goto done;
         }
 
         while (!ci->pcmbuf_insert((char *)samples, bufsize))
@@ -524,11 +523,12 @@
             endofstream = 1;
         ci->set_elapsed(bytesdone*1000LL/avgbytespersec);
     }
+    i = CODEC_OK;
 
+done:
     if (ci->request_next_track())
         goto next_track;
 
-    i = CODEC_OK;
 exit:
     return i;
 }
diff --git a/apps/codecs/wavpack.c b/apps/codecs/wavpack.c
index f864aa3..1871b46 100644
--- a/apps/codecs/wavpack.c
+++ b/apps/codecs/wavpack.c
@@ -75,7 +75,6 @@
     while (!*ci->taginfo_ready && !ci->stop_codec)
         ci->sleep(1);
         
-    ci->configure(CODEC_DSP_ENABLE, (bool *)true);
     ci->configure(DSP_SET_FREQUENCY, (long *)(ci->id3->frequency));
     codec_set_replaygain(ci->id3);
    
@@ -84,7 +83,7 @@
 
     if (!wpc) {
         retval = CODEC_ERROR;
-        goto exit;
+        goto done;
     }
 
     bps = WavpackGetBytesPerSample (wpc);
@@ -143,11 +142,12 @@
         ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
         ci->yield ();
     }
+    retval = CODEC_OK;
 
+done:
     if (ci->request_next_track())
         goto next_track;
 
-    retval = CODEC_OK;
 exit:
     return retval;
 }
diff --git a/apps/dsp.h b/apps/dsp.h
index 501e238..9d0f7de 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -31,7 +31,6 @@
 enum {
     CODEC_SET_FILEBUF_WATERMARK = 1,
     CODEC_SET_FILEBUF_CHUNKSIZE,
-    CODEC_DSP_ENABLE,
     DSP_SET_FREQUENCY,
     DSP_SWITCH_FREQUENCY,
     DSP_SET_CLIP_MIN,
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 071e5d4..85a23e7 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -48,7 +48,8 @@
 size_t audiobuffer_free IDATA_ATTR;
 /* Amount audiobuffer_pos will be increased.*/
 static size_t audiobuffer_fillpos IDATA_ATTR;
-static char *guardbuf IDATA_ATTR;
+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;
@@ -93,9 +94,9 @@
 static struct pcmbufdesc *pcmbuf_write_end IDATA_ATTR;
 static size_t last_chunksize IDATA_ATTR;
 static size_t pcmbuf_unplayed_bytes IDATA_ATTR;
-static size_t pcmbuf_mix_used_bytes IDATA_ATTR;
 static size_t pcmbuf_watermark IDATA_ATTR;
-static short *mixpos IDATA_ATTR;
+static struct pcmbufdesc *pcmbuf_mix_chunk IDATA_ATTR;
+static size_t pcmbuf_mix_sample IDATA_ATTR;
 static bool low_latency_mode = false;
 
 /* Helpful macros for use in conditionals this assumes some of the above
@@ -151,6 +152,10 @@
         /* Put the finished buffer back into circulation */
         pcmbuf_write_end->link = pcmbuf_current;
         pcmbuf_write_end = pcmbuf_current;
+
+        /* If we've read through the mix chunk while it's still mixing there */
+        if (pcmbuf_current == pcmbuf_mix_chunk)
+            pcmbuf_mix_chunk = NULL;
     }
 
 process_new_buffer:
@@ -162,9 +167,10 @@
         if(pcmbuf_new)
         {
             size_t current_size = pcmbuf_new->size;
+
             pcmbuf_unplayed_bytes -= current_size;
-            *realsize = current_size;
             last_chunksize = current_size;
+            *realsize = current_size;
             *realstart = pcmbuf_new->addr;
         }
         else
@@ -219,14 +225,10 @@
 
     /* Update bytes counters */
     pcmbuf_unplayed_bytes += size;
-    if (pcmbuf_mix_used_bytes > size)
-        pcmbuf_mix_used_bytes -= size;
-    else
-        pcmbuf_mix_used_bytes = 0;
 
     audiobuffer_pos += size;
     if (audiobuffer_pos >= pcmbuf_size)
-        audiobuffer_pos = 0;
+        audiobuffer_pos -= pcmbuf_size;
 
     audiobuffer_fillpos = 0;
 }
@@ -299,7 +301,7 @@
     pcm_mute(false);
 
     pcmbuf_unplayed_bytes = 0;
-    pcmbuf_mix_used_bytes = 0;
+    pcmbuf_mix_chunk = NULL;
     if (pcmbuf_read) {
         pcmbuf_write_end->link = pcmbuf_read;
         pcmbuf_write_end = pcmbuf_read_end;
@@ -351,9 +353,10 @@
     pcmbuf_size = bufsize;
     pcmbuf_descsize = pcmbuf_descs()*sizeof(struct pcmbufdesc);
     audiobuffer = (char *)&audiobuf[(audiobufend - audiobuf) -
-        (pcmbuf_size + PCMBUF_FADE_CHUNK + pcmbuf_descsize)];
-    guardbuf = &audiobuffer[pcmbuf_size];
-    pcmbuf_write = (struct pcmbufdesc *)(&guardbuf[PCMBUF_FADE_CHUNK]);
+        (pcmbuf_size + PCMBUF_MIX_CHUNK * 2 + pcmbuf_descsize)];
+    fadebuf = &audiobuffer[pcmbuf_size];
+    voicebuf = &fadebuf[PCMBUF_MIX_CHUNK];
+    pcmbuf_write = (struct pcmbufdesc *)(&voicebuf[PCMBUF_MIX_CHUNK]);
     pcmbuf_init_pcmbuffers();
     position_callback = NULL;
     pcmbuf_event_handler = NULL;
@@ -444,7 +447,7 @@
         /* Fade out the specified amount of the already processed audio */
         size_t total_fade_out = fade_out_rem;
         short *buf = (short *)&audiobuffer[crossfade_pos + fade_out_delay * 2];
-        short *buf_end = (short *)guardbuf;
+        short *buf_end = (short *)fadebuf;
 
         /* Wrap the starting position if needed */
         if (buf >= buf_end) buf -= pcmbuf_size / 2;
@@ -738,8 +741,8 @@
         crossfade_start();
 
     if (crossfade_active) {
-        *realsize = MIN(length, PCMBUF_FADE_CHUNK);
-        return &guardbuf[0];
+        *realsize = MIN(length, PCMBUF_MIX_CHUNK);
+        return fadebuf;
     }
     else
     {
@@ -772,8 +775,16 @@
 {
     if (mix)
     {
-        *realsize = MIN(length, PCMBUF_FADE_CHUNK);
-        return &guardbuf[0];
+        if (pcmbuf_mix_chunk || pcmbuf_read->link)
+        {
+            *realsize = MIN(length, PCMBUF_MIX_CHUNK);
+            return voicebuf;
+        }
+        else
+        {
+            *realsize = 0;
+            return NULL;
+        }
     }
     else
         return pcmbuf_request_buffer(length, realsize);
@@ -787,7 +798,7 @@
 void pcmbuf_write_complete(size_t length)
 {
     if (crossfade_active)
-        flush_crossfade(guardbuf, length);
+        flush_crossfade(fadebuf, length);
     else
     {
         audiobuffer_free -= length;
@@ -814,16 +825,16 @@
 }
 
 /* Get a pointer to where to mix immediate audio */
-static inline short* get_mix_insert_pos(void) {
-    /* Give at least 1/8s clearance here */
-    size_t pcmbuf_mix_back_pos =
-        pcmbuf_unplayed_bytes - NATIVE_FREQUENCY * 4 / 8;
+static inline short* get_mix_insert_buf(void) {
+    if (pcmbuf_read->link)
+    {
+        /* Get the next chunk */
+        char *pcmbuf_mix_buf = pcmbuf_read->link->addr;
 
-    if (audiobuffer_pos < pcmbuf_mix_back_pos)
-        return (short *)&audiobuffer[pcmbuf_size +
-            audiobuffer_pos - pcmbuf_mix_back_pos];
-    else
-        return (short *)&audiobuffer[audiobuffer_pos - pcmbuf_mix_back_pos];
+        /* Give at least 1/8s clearance. TODO: Check size here? */
+        return (short *)&pcmbuf_mix_buf[NATIVE_FREQUENCY * 4 / 8];
+    }
+    return NULL;
 }
 
 /* Generates a constant square wave sound with a given frequency
@@ -834,12 +845,12 @@
     unsigned int interval = NATIVE_FREQUENCY / frequency;
     long sample;
     short *buf;
-    short *pcmbuf_end = (short *)guardbuf;
+    short *pcmbuf_end = (short *)fadebuf;
     size_t samples = NATIVE_FREQUENCY / 1000 * duration;
 
     if (pcm_is_playing())
     {
-        buf = get_mix_insert_pos();
+        buf = get_mix_insert_buf();
         while (i++ < samples)
         {
             sample = *buf;
@@ -888,35 +899,56 @@
     return pcmbuf_unplayed_bytes * 100 / pcmbuf_size;
 }
 
-int pcmbuf_mix_usage(void)
+int pcmbuf_mix_free(void)
 {
-    return pcmbuf_mix_used_bytes * 100 / pcmbuf_unplayed_bytes;
+    if (pcmbuf_mix_chunk)
+    {
+        size_t my_mix_end =
+            (size_t)&((short *)pcmbuf_mix_chunk->addr)[pcmbuf_mix_sample];
+        size_t my_write_pos = (size_t)&audiobuffer[audiobuffer_pos];
+        if (my_write_pos < my_mix_end)
+            my_write_pos += pcmbuf_size;
+        return (my_write_pos - my_mix_end) * 100 / pcmbuf_unplayed_bytes;
+    }
+    return 100;
 }
 
-void pcmbuf_reset_mixpos(void)
+/* This function does  not check for writing over the current main insertion
+ * point of the pcm buffer (audiobuffer_fillpos) so that must be checked by
+ * the caller */
+void pcmbuf_mix_voice(size_t length)
 {
-    mixpos = get_mix_insert_pos();
-    pcmbuf_mix_used_bytes = 0;
-}
+    short *ibuf = (short *)voicebuf;
+    short *obuf;
+    size_t chunk_samples;
 
-void pcmbuf_mix(char *buf, size_t length)
-{
-    short *ibuf = (short *)buf;
-    short *pcmbuf_end = (short *)guardbuf;
+    if (!pcmbuf_mix_chunk && pcmbuf_read)
+    {
+        pcmbuf_mix_chunk = pcmbuf_read->link;
+        /* Start 1/8s into the next chunk */
+        pcmbuf_mix_sample = NATIVE_FREQUENCY * 4 / 16;
+    }
+    if (!pcmbuf_mix_chunk)
+        return;
 
-    if (pcmbuf_mix_used_bytes == 0)
-        pcmbuf_reset_mixpos();
+    obuf = (short *)pcmbuf_mix_chunk->addr;
+    chunk_samples = pcmbuf_mix_chunk->size / 2;
 
-    pcmbuf_mix_used_bytes += length;
     length /= 2;
 
     while (length-- > 0) {
         long sample = *ibuf++;
-        sample += *mixpos >> 2;
-        *mixpos++ = MIN(MAX(sample, -32768), 32767);
-
-        if (mixpos >= pcmbuf_end)
-            mixpos = (short *)audiobuffer;
+        if (pcmbuf_mix_sample >= chunk_samples)
+        {
+            pcmbuf_mix_chunk = pcmbuf_mix_chunk->link;
+            if (!pcmbuf_mix_chunk)
+                return;
+            pcmbuf_mix_sample = 0;
+            obuf = pcmbuf_mix_chunk->addr;
+            chunk_samples = pcmbuf_mix_chunk->size / 2;
+        }
+        sample += obuf[pcmbuf_mix_sample];
+        obuf[pcmbuf_mix_sample++] = MIN(MAX(sample, -32768), 32767);
     }
 }
 
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index b659e8f..819d501 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -27,7 +27,7 @@
                                      non-fatal) */
 #define PCMBUF_MIN_CHUNK     4096 /* We try to never feed a chunk smaller than
                                      this to the DMA */
-#define PCMBUF_FADE_CHUNK    8192 /* This is the maximum size of one packet
+#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 */
@@ -64,10 +64,9 @@
 void pcmbuf_crossfade_enable(bool on_off);
 
 int pcmbuf_usage(void);
-int pcmbuf_mix_usage(void);
+int pcmbuf_mix_free(void);
 void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
-void pcmbuf_reset_mixpos(void);
-void pcmbuf_mix(char *buf, size_t length);
+void pcmbuf_mix_voice(size_t length);
 
 int pcmbuf_used_descs(void);
 int pcmbuf_descs(void);
diff --git a/apps/playback.c b/apps/playback.c
index c34f83a..35f5d7f 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -126,6 +126,9 @@
     Q_CODEC_REQUEST_COMPLETE,
     Q_CODEC_REQUEST_FAILED,
 
+    Q_VOICE_PLAY,
+    Q_VOICE_STOP,
+
     Q_CODEC_LOAD,
     Q_CODEC_LOAD_DISK,
 };
@@ -160,6 +163,12 @@
 static long voice_codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
 IBSS_ATTR;
 static const char voice_codec_thread_name[] = "voice codec";
+struct voice_info {
+    void (*callback)(unsigned char **start, int *size);
+    int size;
+    char *buf;
+};
+
 
 static struct mutex mutex_codecthread;
 static struct event_queue codec_callback_queue;
@@ -170,12 +179,12 @@
 static size_t voice_remaining;
 static bool voice_is_playing;
 static void (*voice_getmore)(unsigned char** start, int* size);
+static int voice_thread_num = -1;
 
 /* Is file buffer currently being refilled? */
-static volatile bool filling;
-static volatile bool filling_short;
+static volatile bool filling IDATA_ATTR;
 
-volatile int current_codec;
+volatile int current_codec IDATA_ATTR;
 extern unsigned char codecbuf[];
 
 /* Ring buffer where tracks and codecs are loaded. */
@@ -188,8 +197,8 @@
 size_t filebufused;
 
 /* Ring buffer read and write indexes. */
-static volatile size_t buf_ridx;
-static volatile size_t buf_widx;
+static volatile size_t buf_ridx IDATA_ATTR;
+static volatile size_t buf_widx IDATA_ATTR;
 
 #ifndef SIMULATOR
 static unsigned char *iram_buf[2];
@@ -244,18 +253,18 @@
 static bool v1first = false;
 
 static void mp3_set_elapsed(struct mp3entry* id3);
-int mp3_get_file_pos(void);
+static int mp3_get_file_pos(void);
 
-static void audio_clear_track_entries(bool clear_unbuffered);
-static void initialize_buffer_fill(bool clear_tracks, bool short_fill);
-static void audio_fill_file_buffer(
-        bool start_play, bool short_fill, size_t offset);
+static void audio_clear_track_entries(
+        bool clear_buffered, bool clear_unbuffered);
+static void initialize_buffer_fill(bool clear_tracks);
+static void audio_fill_file_buffer(bool start_play, size_t offset);
 
 static void swap_codec(void)
 {
     int my_codec = current_codec;
 
-    logf("swapping out codec:%d", current_codec);
+    logf("swapping out codec:%d", my_codec);
 
     /* Save our current IRAM and DRAM */
 #ifndef SIMULATOR
@@ -281,7 +290,7 @@
     invalidate_icache();
     memcpy(codecbuf, dram_buf[my_codec], CODEC_SIZE);
 
-    logf("codec resuming:%d", current_codec);
+    logf("resuming codec:%d", my_codec);
 }
 
 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
@@ -289,9 +298,6 @@
 {
     static bool voice_cpu_boosted = false;
 
-    if (!voice_codec_loaded)
-        state = false;
-
     if (state != voice_cpu_boosted)
     {
         cpu_boost(state);
@@ -302,8 +308,8 @@
 #define voice_boost_cpu(state)   do { } while(0)
 #endif
 
-bool codec_pcmbuf_insert_split_callback(const void *ch1, const void *ch2,
-                                        size_t length)
+static bool voice_pcmbuf_insert_split_callback(
+        const void *ch1, const void *ch2, size_t length)
 {
     const char* src[2];
     char *dest;
@@ -316,25 +322,75 @@
     if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
         length *= 2;    /* Length is per channel */
 
-    while (length > 0) {
+    do {
         long est_output_size = dsp_output_size(length);
-        if (current_codec == CODEC_IDX_VOICE) {
-            while ((dest = pcmbuf_request_voice_buffer(est_output_size,
-                            &output_size, audio_codec_loaded)) == NULL)
-                sleep(1);
+        while ((dest = pcmbuf_request_voice_buffer(est_output_size,
+                        &output_size, playing)) == NULL)
+            if (playing)
+                swap_codec();
+            else
+                yield();
+
+        /* Get the real input_size for output_size bytes, guarding
+         * against resampling buffer overflows. */
+        input_size = dsp_input_size(output_size);
+
+        if (input_size <= 0) {
+            DEBUGF("Error: dsp_input_size(%ld=dsp_output_size(%ld))=%ld<=0\n",
+                    output_size, length, input_size);
+            /* If this happens, there are samples of codec data that don't
+             * become a number of pcm samples, and something is broken */
+            return false;
+        }
+
+        /* Input size has grown, no error, just don't write more than length */
+        if ((size_t)input_size > length)
+            input_size = length;
+
+        output_size = dsp_process(dest, src, input_size);
+
+        if (playing)
+        {
+            pcmbuf_mix_voice(output_size);
+            if (pcmbuf_usage() < 10 || pcmbuf_mix_free() < 30)
+                swap_codec();
         }
         else
-        {
-            /* Prevent audio from a previous position from hitting the buffer */
-            if (ci.new_track || ci.stop_codec)
-                return true;
+            pcmbuf_write_complete(output_size);
 
-            while ((dest = pcmbuf_request_buffer(est_output_size,
-                            &output_size)) == NULL) {
-                sleep(1);
-                if (ci.seek_time || ci.new_track || ci.stop_codec)
-                    return true;
-            }
+        length -= input_size;
+
+    } while (length > 0);
+
+
+    return true;
+}
+
+static bool codec_pcmbuf_insert_split_callback(
+        const void *ch1, const void *ch2, size_t length)
+{
+    const char* src[2];
+    char *dest;
+    long input_size;
+    size_t output_size;
+
+    src[0] = ch1;
+    src[1] = ch2;
+
+    if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
+        length *= 2;    /* Length is per channel */
+
+    do {
+        long est_output_size = dsp_output_size(length);
+        /* Prevent audio from a previous track from playing */
+        if (ci.new_track || ci.stop_codec)
+            return true;
+
+        while ((dest = pcmbuf_request_buffer(est_output_size,
+                        &output_size)) == NULL) {
+            sleep(1);
+            if (ci.seek_time || ci.new_track || ci.stop_codec)
+                return true;
         }
 
         /* Get the real input_size for output_size bytes, guarding
@@ -344,73 +400,66 @@
         if (input_size <= 0) {
             DEBUGF("Error: dsp_input_size(%ld=dsp_output_size(%ld))=%ld<=0\n",
                     output_size, length, input_size);
-            /* If this happens, then some samples have been lost */
-            break;
+            /* If this happens, there are samples of codec data that don't
+             * become a number of pcm samples, and something is broken */
+            return false;
         }
 
-        if ((size_t)input_size > length) {
-            DEBUGF("Error: dsp_input_size(%ld=dsp_output_size(%ld))=%ld>%ld\n",
-                   output_size, length, input_size, length);
+        /* Input size has grown, no error, just don't write more than length */
+        if ((size_t)input_size > length)
             input_size = length;
-        }
 
         output_size = dsp_process(dest, src, input_size);
 
-        /* Hotswap between audio and voice codecs as necessary. */
-        switch (current_codec)
-        {
-            case CODEC_IDX_AUDIO:
-                pcmbuf_write_complete(output_size);
-                if (voice_is_playing && pcmbuf_usage() > 30
-                    && pcmbuf_mix_usage() < 20)
-                {
-                    voice_boost_cpu(true);
-                    swap_codec();
-                    voice_boost_cpu(false);
-                }
-                break ;
+        pcmbuf_write_complete(output_size);
 
-            case CODEC_IDX_VOICE:
-                if (audio_codec_loaded) {
-                    pcmbuf_mix(dest, output_size);
-                    if ((pcmbuf_usage() < 10)
-                        || pcmbuf_mix_usage() > 70)
-                        swap_codec();
-                } else
-                    pcmbuf_write_complete(output_size);
-                break ;
-        }
+        if (voice_is_playing && pcm_is_playing() &&
+                pcmbuf_usage() > 30 && pcmbuf_mix_free() > 80)
+            swap_codec();
 
         length -= input_size;
-    }
+
+    } while (length > 0);
 
     return true;
 }
 
-bool codec_pcmbuf_insert_callback(const char *buf, size_t length)
+static bool voice_pcmbuf_insert_callback(const char *buf, size_t length)
 {
     /* TODO: The audiobuffer API should probably be updated, and be based on
      *       pcmbuf_insert_split().  */
     long real_length = length;
 
     if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
-    {
         length /= 2;    /* Length is per channel */
-    }
+
+    /* Second channel is only used for non-interleaved stereo. */
+    return voice_pcmbuf_insert_split_callback(buf, buf + (real_length / 2),
+        length);
+}
+
+static bool codec_pcmbuf_insert_callback(const char *buf, size_t length)
+{
+    /* TODO: The audiobuffer API should probably be updated, and be based on
+     *       pcmbuf_insert_split().  */
+    long real_length = length;
+
+    if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
+        length /= 2;    /* Length is per channel */
 
     /* Second channel is only used for non-interleaved stereo. */
     return codec_pcmbuf_insert_split_callback(buf, buf + (real_length / 2),
         length);
 }
 
-void* get_codec_memory_callback(size_t *size)
+static void* get_voice_memory_callback(size_t *size)
 {
-    if (current_codec == CODEC_IDX_VOICE)
-    {
-        *size = 0;
-        return NULL;
-    }
+    *size = 0;
+    return NULL;
+}
 
+static void* get_codec_memory_callback(size_t *size)
+{
     *size = MALLOC_BUFSIZE;
     if (voice_codec_loaded)
         return &audiobuf[talk_get_bufsize()];
@@ -430,14 +479,15 @@
     }
 }
 
-void codec_set_elapsed_callback(unsigned int value)
+static void voice_set_elapsed_callback(unsigned int value)
+{
+    (void)value;
+}
+
+static void codec_set_elapsed_callback(unsigned int value)
 {
     unsigned int latency;
 
-    /* We don't save or display offsets for voice */
-    if (current_codec == CODEC_IDX_VOICE)
-        return ;
-
 #ifdef AB_REPEAT_ENABLE
     ab_position_report(value);
 #endif
@@ -450,16 +500,14 @@
         cur_ti->id3.elapsed = value - latency;
 }
 
-void codec_set_offset_callback(size_t value)
+static void voice_set_offset_callback(size_t value)
 {
-    unsigned int latency;
+    (void)value;
+}
 
-    /* We don't save or display offsets for voice */
-    if (current_codec == CODEC_IDX_VOICE)
-        return ;
-
-    latency = pcmbuf_get_latency() * cur_ti->id3.bitrate / 8;
-
+static void codec_set_offset_callback(size_t value)
+{
+    unsigned int latency = pcmbuf_get_latency() * cur_ti->id3.bitrate / 8;
     if (value < latency)
         cur_ti->id3.offset = 0;
     else
@@ -512,14 +560,22 @@
             queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0);
 }
 
+static size_t voice_filebuf_callback(void *ptr, size_t size)
+{
+    (void)ptr;
+    (void)size;
+
+    return 0;
+}
+
 /* copy up-to size bytes into ptr and return the actual size copied */
-size_t codec_filebuf_callback(void *ptr, size_t size)
+static size_t codec_filebuf_callback(void *ptr, size_t size)
 {
     char *buf = (char *)ptr;
     size_t copy_n;
     size_t part_n;
 
-    if (ci.stop_codec || !playing || current_codec == CODEC_IDX_VOICE)
+    if (ci.stop_codec || !playing)
         return 0;
 
     /* The ammount to copy is the lesser of the requested amount and the
@@ -554,39 +610,91 @@
     return copy_n;
 }
 
-void* voice_request_data(size_t *realsize, size_t reqsize)
+static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
 {
-    while (queue_empty(&voice_codec_queue) && (voice_remaining == 0
-            || voicebuf == NULL) && !ci_voice.stop_codec)
+    struct event ev;
+
+    if (ci_voice.new_track)
     {
-        yield();
-        if (audio_codec_loaded && (pcmbuf_usage() < 30
-            || !voice_is_playing || voicebuf == NULL))
-            swap_codec();
-        else if (!voice_is_playing)
+        *realsize = 0;
+        return NULL;
+    }
+
+    while (1)
+    {
+        if (voice_is_playing)
+            queue_wait_w_tmo(&voice_codec_queue, &ev, 0);
+        else if (playing)
         {
-            voice_boost_cpu(false);
-            if (!pcm_is_playing())
-                pcmbuf_boost(false);
-            sleep(HZ/16);
+            queue_wait_w_tmo(&voice_codec_queue, &ev, 0);
+            if (ev.id == SYS_TIMEOUT)
+                ev.id = Q_AUDIO_PLAY;
         }
+        else
+            queue_wait(&voice_codec_queue, &ev);
 
-        if (voice_remaining)
-            voice_is_playing = true;
-        else if (voice_getmore != NULL)
-        {
-            voice_getmore((unsigned char **)&voicebuf, (int *)&voice_remaining);
+        switch (ev.id) {
+            case Q_AUDIO_PLAY:
+                swap_codec();
+                break;
 
-            if (!voice_remaining)
-            {
-                voice_is_playing = false;
-                /* Force pcm playback. */
-                pcmbuf_play_start();
-            }
+            case Q_VOICE_STOP:
+                if (voice_is_playing)
+                {
+                    /* Clear the current buffer */
+                    voice_is_playing = false;
+                    voice_getmore = NULL;
+                    voice_remaining = 0;
+                    voicebuf = NULL;
+                    voice_boost_cpu(false);
+                    ci_voice.new_track = 1;
+                    /* Force the codec to think it's changing tracks */
+                    *realsize = 0;
+                    return NULL;
+                }
+                else
+                    break;
+
+            case SYS_USB_CONNECTED:
+                logf("USB: Audio core");
+                usb_acknowledge(SYS_USB_CONNECTED_ACK);
+                if (audio_codec_loaded)
+                    swap_codec();
+                usb_wait_for_disconnect(&voice_codec_queue);
+                break;
+
+            case Q_VOICE_PLAY:
+                {
+                    struct voice_info *voice_data;
+                    voice_is_playing = true;
+                    voice_boost_cpu(true);
+                    voice_data = ev.data;
+                    voice_remaining = voice_data->size;
+                    voicebuf = voice_data->buf;
+                    voice_getmore = voice_data->callback;
+                }
+            case SYS_TIMEOUT:
+                goto voice_play_clip;
         }
     }
 
-    voice_is_playing = true;
+voice_play_clip:
+
+    if (voice_remaining == 0 || voicebuf == NULL)
+    {
+        if (voice_getmore)
+            voice_getmore((unsigned char **)&voicebuf, (int *)&voice_remaining);
+
+        /* If this clip is done */
+        if (!voice_remaining)
+        {
+            queue_post(&voice_codec_queue, Q_VOICE_STOP, 0);
+            /* Force pcm playback. */
+            if (!pcm_is_playing())
+                pcmbuf_play_start();
+        }
+    }
+    
     *realsize = MIN(voice_remaining, reqsize);
 
     if (*realsize == 0)
@@ -595,14 +703,10 @@
     return voicebuf;
 }
 
-void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
+static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
 {
     size_t short_n, copy_n, buf_rem;
 
-    /* Voice codec. */
-    if (current_codec == CODEC_IDX_VOICE)
-        return voice_request_data(realsize, reqsize);
-
     if (!playing) {
         *realsize = 0;
         return NULL;
@@ -654,7 +758,7 @@
 }
 
 /* Count the data BETWEEN the selected tracks */
-size_t buffer_count_tracks(int from_track, int to_track) {
+static size_t buffer_count_tracks(int from_track, int to_track) {
     size_t amount = 0;
     bool need_wrap = to_track < from_track;
 
@@ -772,6 +876,7 @@
     /* Reset buffer and track pointers */
     buf_ridx = buf_widx = 0;
     track_widx = track_ridx;
+    audio_clear_track_entries(false, true);
     filebufused = 0;
 
     /* Cause the buffer fill to return as soon as the codec is loaded */
@@ -780,7 +885,7 @@
     last_peek_offset = -1;
     cur_ti->filesize = 0;
     cur_ti->start_pos = 0;
-    audio_fill_file_buffer(false, true, 0);
+    audio_fill_file_buffer(false, 0);
 }
 
 static void audio_check_new_track(void)
@@ -935,7 +1040,7 @@
     track_widx = track_ridx;
 
     last_peek_offset = 0;
-    initialize_buffer_fill(true, true);
+    initialize_buffer_fill(true);
 
     if (newpos > AUDIO_REBUFFER_GUESS_SIZE)
         cur_ti->start_pos = newpos - AUDIO_REBUFFER_GUESS_SIZE;
@@ -950,16 +1055,15 @@
     queue_post(&codec_callback_queue, Q_CODEC_REQUEST_COMPLETE, 0);
 }
 
-void codec_advance_buffer_callback(size_t amount)
+static void voice_advance_buffer_callback(size_t amount)
 {
-    if (current_codec == CODEC_IDX_VOICE) {
-        amount = MIN(amount, voice_remaining);
-        voicebuf += amount;
-        voice_remaining -= amount;
+    amount = MIN(amount, voice_remaining);
+    voicebuf += amount;
+    voice_remaining -= amount;
+}
 
-        return ;
-    }
-
+static void codec_advance_buffer_callback(size_t amount)
+{
     if (amount > cur_ti->available + cur_ti->filerem)
         amount = cur_ti->available + cur_ti->filerem;
 
@@ -989,19 +1093,25 @@
     codec_set_offset_callback(ci.curpos);
 }
 
-void codec_advance_buffer_loc_callback(void *ptr)
+static void voice_advance_buffer_loc_callback(void *ptr)
 {
-    size_t amount;
+    size_t amount = (size_t)ptr - (size_t)voicebuf;
+    voice_advance_buffer_callback(amount);
+}
 
-    if (current_codec == CODEC_IDX_VOICE)
-        amount = (size_t)ptr - (size_t)voicebuf;
-    else
-        amount = (size_t)ptr - (size_t)&filebuf[buf_ridx];
-
+static void codec_advance_buffer_loc_callback(void *ptr)
+{
+    size_t amount = (size_t)ptr - (size_t)&filebuf[buf_ridx];
     codec_advance_buffer_callback(amount);
 }
 
-off_t codec_mp3_get_filepos_callback(int newtime)
+static off_t voice_mp3_get_filepos_callback(int newtime)
+{
+    (void)newtime;
+    return 0;
+}
+
+static off_t codec_mp3_get_filepos_callback(int newtime)
 {
     off_t newpos;
 
@@ -1011,7 +1121,12 @@
     return newpos;
 }
 
-void codec_seek_complete_callback(void)
+static void voice_do_nothing(void)
+{
+    return;
+}
+
+static void codec_seek_complete_callback(void)
 {
     logf("seek_complete");
     if (pcm_is_paused()) {
@@ -1024,13 +1139,16 @@
     ci.seek_time = 0;
 }
 
-bool codec_seek_buffer_callback(size_t newpos)
+static bool voice_seek_buffer_callback(size_t newpos)
+{
+    (void)newpos;
+    return false;
+}
+
+static bool codec_seek_buffer_callback(size_t newpos)
 {
     int difference;
 
-    if (current_codec == CODEC_IDX_VOICE)
-        return false;
-
     if (newpos >= cur_ti->filesize)
         newpos = cur_ti->filesize - 1;
 
@@ -1083,7 +1201,7 @@
     size_t bytes;
 
     if (current_codec == CODEC_IDX_VOICE)
-        return ;
+        return;
 
     if (!filebuf)
         return;     /* Audio buffers not yet set up */
@@ -1105,13 +1223,6 @@
         conf_filechunk = (unsigned long)value;
         break;
 
-    case CODEC_DSP_ENABLE:
-        if ((bool)value)
-            ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
-        else
-            ci.pcmbuf_insert = pcmbuf_insert_buffer;
-        break ;
-
     default:
         if (!dsp_configure(setting, value)) { logf("Illegal key:%d", setting); }
     }
@@ -1165,7 +1276,7 @@
 }
 
 /* FIXME: This code should be made more generic and move to metadata.c */
-void strip_id3v1_tag(void)
+static void strip_id3v1_tag(void)
 {
     int i;
     static const unsigned char tag[] = "TAG";
@@ -1259,10 +1370,6 @@
             track_widx = 0;
 
         tracks[track_widx].filesize = 0;
-        /* If we're short filling, and have at least twice the watermark
-         * of data, stop filling after this track */
-        if (filling_short && filebufused > conf_watermark * 2)
-            fill_bytesleft = 0;
     } else {
         logf("Partially buf:%dB",
                 tracks[track_widx].filesize - tracks[track_widx].filerem);
@@ -1526,7 +1633,6 @@
         conf_watermark = AUDIO_DEFAULT_WATERMARK;
         conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
         dsp_configure(DSP_RESET, 0);
-        ci.configure(CODEC_DSP_ENABLE, false);
         current_codec = last_codec;
     }
 
@@ -1628,7 +1734,8 @@
     return true;
 }
 
-static void audio_clear_track_entries(bool clear_unbuffered)
+static void audio_clear_track_entries(
+        bool clear_buffered, bool clear_unbuffered)
 {
     int cur_idx = track_widx;
     int last_idx = -1;
@@ -1644,16 +1751,18 @@
         /* If the track is buffered, conditionally clear/notify,
          * otherwise clear the track if that option is selected */
         if (tracks[cur_idx].event_sent) {
-            if (last_idx >= 0)
-            {
-                /* If there is an unbuffer callback, call it, otherwise, just
-                 * clear the track */
-                if (track_unbuffer_callback)
-                    track_unbuffer_callback(&tracks[last_idx].id3, false);
+            if (clear_buffered) {
+                if (last_idx >= 0)
+                {
+                    /* If there is an unbuffer callback, call it, otherwise,
+                     * just clear the track */
+                    if (track_unbuffer_callback)
+                        track_unbuffer_callback(&tracks[last_idx].id3, false);
 
-                memset(&tracks[last_idx], 0, sizeof(struct track_info));
+                    memset(&tracks[last_idx], 0, sizeof(struct track_info));
+                }
+                last_idx = cur_idx;
             }
-            last_idx = cur_idx;
         } else if (clear_unbuffered)
             memset(&tracks[cur_idx], 0, sizeof(struct track_info));
     }
@@ -1686,6 +1795,7 @@
         /* Save the current playing spot, or NULL if the playlist has ended */
         playlist_update_resume_info(playlist_end?NULL:audio_current_track());
     }
+    filebufused = 0;
     playing = false;
     filling = false;
     paused = false;
@@ -1733,7 +1843,7 @@
     
     last_peek_offset = -1;
 
-    audio_fill_file_buffer(true, true, offset);
+    audio_fill_file_buffer(true, offset);
 }
 
 /* Send callback events to notify about new tracks. */
@@ -1772,20 +1882,11 @@
     }
 }
 
-static void initialize_buffer_fill(bool clear_tracks, bool short_fill)
+static void initialize_buffer_fill(bool clear_tracks)
 {
-    if (short_fill) {
-        filling_short = true;
-        fill_bytesleft = filebuflen >> 2;
-        cur_ti->start_pos = ci.curpos;
-    }
-    /* Recalculate remaining bytes to buffer */
-    else if (!filling_short)
-    {
-        fill_bytesleft = filebuflen - filebufused;
-        if (buf_ridx > cur_ti->buf_idx)
-            cur_ti->start_pos = buf_ridx - cur_ti->buf_idx;
-    }
+    fill_bytesleft = filebuflen - filebufused;
+    if (buf_ridx > cur_ti->buf_idx)
+        cur_ti->start_pos = buf_ridx - cur_ti->buf_idx;
 
     /* Don't initialize if we're already initialized */
     if (filling)
@@ -1794,7 +1895,7 @@
     logf("Starting buffer fill");
 
     if (clear_tracks)
-        audio_clear_track_entries(short_fill);
+        audio_clear_track_entries(true, false);
 
     /* Save the current resume position once. */
     playlist_update_resume_info(audio_current_track());
@@ -1802,10 +1903,9 @@
     filling = true;
 }
 
-static void audio_fill_file_buffer(
-        bool start_play, bool short_fill, size_t offset)
+static void audio_fill_file_buffer(bool start_play, size_t offset)
 {
-    initialize_buffer_fill(!start_play, short_fill);
+    initialize_buffer_fill(!start_play);
 
     /* If we have a partially buffered track, continue loading,
      * otherwise load a new track */
@@ -1821,7 +1921,6 @@
 
         generate_postbuffer_events();
         filling = false;
-        filling_short = false;
 
 #ifndef SIMULATOR
         if (playing)
@@ -1899,17 +1998,16 @@
     }
 }
 
+static bool voice_request_next_track_callback(void)
+{
+    ci_voice.new_track = 0;
+    return true;
+}
+
 static bool codec_request_next_track_callback(void)
 {
     int prev_codectype;
 
-    if (current_codec == CODEC_IDX_VOICE) {
-        voice_remaining = 0;
-        /* Terminate the codec if there are messages waiting on the queue or
-           the core has been requested the codec to be terminated. */
-        return !ci_voice.stop_codec && queue_empty(&voice_codec_queue);
-    }
-
     if (ci.stop_codec || !playing)
         return false;
 
@@ -1941,7 +2039,7 @@
 
         track_widx = track_ridx;
 
-        audio_clear_track_entries(true);
+        audio_clear_track_entries(true, true);
 
         /* If the current track is fully buffered, advance the write pointer */
         if (tracks[track_widx].filerem == 0)
@@ -1993,7 +2091,7 @@
                 if (!filling)
                     if (!playing || playlist_end || ci.stop_codec)
                         break;
-                audio_fill_file_buffer(false, false, 0);
+                audio_fill_file_buffer(false, 0);
                 break;
 
             case Q_AUDIO_PLAY:
@@ -2075,7 +2173,7 @@
     }
 }
 
-void codec_thread(void)
+static void codec_thread(void)
 {
     struct event ev;
     int status;
@@ -2088,16 +2186,18 @@
         switch (ev.id) {
             case Q_CODEC_LOAD_DISK:
                 logf("Codec load disk");
-                ci.stop_codec = false;
                 audio_codec_loaded = true;
+                if (voice_codec_loaded)
+                    queue_post(&voice_codec_queue, Q_AUDIO_PLAY, 0);
                 mutex_lock(&mutex_codecthread);
                 current_codec = CODEC_IDX_AUDIO;
+                ci.stop_codec = false;
                 status = codec_load_file((const char *)ev.data, &ci);
                 mutex_unlock(&mutex_codecthread);
                 break ;
 
             case Q_CODEC_LOAD:
-                logf("Codec start");
+                logf("Codec load ram");
                 if (!cur_ti->has_codec) {
                     logf("Codec slot is empty!");
                     /* Wait for the pcm buffer to go empty */
@@ -2109,11 +2209,13 @@
                     break ;
                 }
 
-                ci.stop_codec = false;
-                wrap = (size_t)&filebuf[filebuflen] - (size_t)cur_ti->codecbuf;
                 audio_codec_loaded = true;
+                if (voice_codec_loaded)
+                    queue_post(&voice_codec_queue, Q_AUDIO_PLAY, 0);
                 mutex_lock(&mutex_codecthread);
                 current_codec = CODEC_IDX_AUDIO;
+                ci.stop_codec = false;
+                wrap = (size_t)&filebuf[filebuflen] - (size_t)cur_ti->codecbuf;
                 status = codec_load_ram(cur_ti->codecbuf, cur_ti->codecsize,
                         &filebuf[0], wrap, &ci);
                 mutex_unlock(&mutex_codecthread);
@@ -2121,28 +2223,26 @@
 
 #ifndef SIMULATOR
             case SYS_USB_CONNECTED:
-                while (voice_codec_loaded) {
-                    if (current_codec != CODEC_IDX_VOICE)
-                        swap_codec();
-                    sleep(1);
-                }
                 queue_clear(&codec_queue);
                 logf("USB: Audio codec");
                 usb_acknowledge(SYS_USB_CONNECTED_ACK);
+                if (voice_codec_loaded)
+                    swap_codec();
                 usb_wait_for_disconnect(&codec_queue);
                 break ;
 #endif
         }
 
         if (audio_codec_loaded)
+        {
             if (ci.stop_codec)
             {
                 status = CODEC_OK;
                 if (!playing)
                     pcmbuf_play_stop();
             }
-
-        audio_codec_loaded = false;
+            audio_codec_loaded = false;
+        }
 
         switch (ev.id) {
             case Q_CODEC_LOAD_DISK:
@@ -2163,6 +2263,10 @@
                         logf("Codec finished");
                         if (ci.stop_codec)
                         {
+                            /* Wait for the audio to stop playing before
+                             * triggering the WPS exit */
+                            while(pcm_is_playing())
+                                sleep(1);
                             queue_post(&audio_queue, Q_AUDIO_STOP, 0);
                             break;
                         }
@@ -2186,12 +2290,20 @@
 
     filebuf = (char *)&audiobuf[MALLOC_BUFSIZE];
     filebuflen = audiobufend - audiobuf - MALLOC_BUFSIZE - GUARD_BUFSIZE -
-        (pcmbuf_get_bufsize() + get_pcmbuf_descsize() + PCMBUF_FADE_CHUNK);
+        (pcmbuf_get_bufsize() + get_pcmbuf_descsize() + PCMBUF_MIX_CHUNK * 2);
 
-    if (talk_get_bufsize() && voice_codec_loaded)
+    if (talk_get_bufsize())
     {
         filebuf = &filebuf[talk_get_bufsize()];
         filebuflen -= 2*CODEC_IRAM_SIZE + 2*CODEC_SIZE + talk_get_bufsize();
+
+#ifndef SIMULATOR
+        iram_buf[0] = &filebuf[filebuflen];
+        iram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE];
+#endif
+        dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2];
+        dram_buf[1] =
+            (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2+CODEC_SIZE];
     }
 
     /* Ensure that everything is aligned */
@@ -2199,60 +2311,25 @@
     filebuf += offset;
     filebuflen -= offset;
     filebuflen &= ~3;
-
-#ifndef SIMULATOR
-    iram_buf[0] = &filebuf[filebuflen];
-    iram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE];
-#endif
-    dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2];
-    dram_buf[1] =
-        (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2+CODEC_SIZE];
-
 }
 
-void voice_codec_thread(void)
+static void voice_codec_thread(void)
 {
-    struct event ev;
-    int status;
+    while (1)
+    {
+        logf("Loading voice codec");
+        voice_codec_loaded = true;
+        mutex_lock(&mutex_codecthread);
+        current_codec = CODEC_IDX_VOICE;
+        dsp_configure(DSP_RESET, 0);
+        voice_remaining = 0;
+        voice_getmore = NULL;
 
-    current_codec = CODEC_IDX_AUDIO;
-    voice_codec_loaded = false;
-    while (1) {
-        status = 0;
-        voice_is_playing = false;
-        queue_wait(&voice_codec_queue, &ev);
-        switch (ev.id) {
-            case Q_CODEC_LOAD_DISK:
-                logf("Loading voice codec");
-                audio_stop_playback();
-                mutex_lock(&mutex_codecthread);
-                current_codec = CODEC_IDX_VOICE;
-                dsp_configure(DSP_RESET, 0);
-                ci.configure(CODEC_DSP_ENABLE, (bool *)true);
-                voice_remaining = 0;
-                voice_getmore = NULL;
-                voice_codec_loaded = true;
-                reset_buffer();
-                ci_voice.stop_codec = false;
+        codec_load_file(CODEC_MPA_L3, &ci_voice);
 
-                status = codec_load_file((char *)ev.data, &ci_voice);
-
-                logf("Voice codec finished");
-                audio_stop_playback();
-                mutex_unlock(&mutex_codecthread);
-                current_codec = CODEC_IDX_AUDIO;
-                voice_codec_loaded = false;
-                reset_buffer();
-                break ;
-
-#ifndef SIMULATOR
-            case SYS_USB_CONNECTED:
-                logf("USB: Voice codec");
-                usb_acknowledge(SYS_USB_CONNECTED_ACK);
-                usb_wait_for_disconnect(&voice_codec_queue);
-                break ;
-#endif
-        }
+        logf("Voice codec finished");
+        mutex_unlock(&mutex_codecthread);
+        voice_codec_loaded = false;
     }
 }
 
@@ -2261,20 +2338,24 @@
     if (!filebuf)
         return;     /* Audio buffers not yet set up */
 
-    while (voice_codec_loaded)
+    if (voice_thread_num >= 0)
     {
         logf("Terminating voice codec");
-        ci_voice.stop_codec = true;
-        sleep(1);
+        remove_thread(voice_thread_num);
+        queue_delete(&voice_codec_queue);
+        voice_thread_num = -1;
+        voice_codec_loaded = false;
     }
 
     if (!talk_get_bufsize())
         return ;
 
     logf("Starting voice codec");
-    queue_post(&voice_codec_queue, Q_CODEC_LOAD_DISK, (void *)CODEC_MPA_L3);
+    queue_init(&voice_codec_queue);
+    voice_thread_num = create_thread(voice_codec_thread, voice_codec_stack,
+            sizeof(voice_codec_stack), voice_codec_thread_name);
     while (!voice_codec_loaded)
-        sleep(1);
+        yield();
 }
 
 struct mp3entry* audio_current_track(void)
@@ -2495,7 +2576,7 @@
 }
 
 /* Copied from mpeg.c. Should be moved somewhere else. */
-int mp3_get_file_pos(void)
+static int mp3_get_file_pos(void)
 {
     int pos = -1;
     struct mp3entry *id3 = audio_current_track();
@@ -2553,11 +2634,19 @@
 void mp3_play_data(const unsigned char* start, int size,
                    void (*get_more)(unsigned char** start, int* size))
 {
-    voice_getmore = get_more;
-    voicebuf = (char *)start;
-    voice_remaining = size;
+    static struct voice_info voice_clip;
+    voice_clip.callback = get_more;
+    voice_clip.buf = (char *)start;
+    voice_clip.size = size;
+    queue_post(&voice_codec_queue, Q_VOICE_STOP, 0);
+    queue_post(&voice_codec_queue, Q_VOICE_PLAY, &voice_clip);
     voice_is_playing = true;
-    pcmbuf_reset_mixpos();
+    voice_boost_cpu(true);
+}
+
+void mp3_play_stop(void)
+{
+    queue_post(&voice_codec_queue, Q_VOICE_STOP, 0);
 }
 
 void audio_set_buffer_margin(int setting)
@@ -2579,10 +2668,6 @@
     if (!filebuf)
         return;     /* Audio buffers not yet set up */
 
-    /* Store the track resume position */
-    if (was_playing)
-        offset = cur_ti->id3.offset;
-
     if (enable)
         seconds = global_settings.crossfade_fade_out_delay
                 + global_settings.crossfade_fade_out_duration;
@@ -2596,8 +2681,12 @@
 
     if (was_playing)
     {
+        /* Store the track resume position */
+        offset = cur_ti->id3.offset;
         /* Playback has to be stopped before changing the buffer size. */
-        audio_stop_playback();
+        queue_post(&audio_queue, Q_AUDIO_STOP, 0);
+        while (audio_codec_loaded)
+            yield();
         gui_syncsplash(0, true, (char *)str(LANG_RESTARTING_PLAYBACK));
     }
 
@@ -2612,11 +2701,11 @@
 
     /* Restart playback. */
     if (was_playing) {
-        audio_play(offset);
+        playing = true;
+        queue_post(&audio_queue, Q_AUDIO_PLAY, (void *)offset);
 
         /* Wait for the playback to start again (and display the splash
            screen during that period. */
-        playing = true;
         while (playing && !audio_codec_loaded)
             yield();
     }
@@ -2648,8 +2737,7 @@
 {
     (void)id3;
 
-    logf("tce:%s", id3->artist);
-    logf("tce:%s", id3->album);
+    logf("tce:%s", id3->path);
 }
 #endif
 
@@ -2674,7 +2762,7 @@
 
     /* Initialize codec api. */
     ci.read_filebuf = codec_filebuf_callback;
-    ci.pcmbuf_insert = pcmbuf_insert_buffer;
+    ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
     ci.pcmbuf_insert_split = codec_pcmbuf_insert_split_callback;
     ci.get_codec_memory = get_codec_memory_callback;
     ci.request_buffer = codec_request_buffer_callback;
@@ -2689,18 +2777,30 @@
     ci.configure = codec_configure_callback;
     ci.discard_codec = codec_discard_codec_callback;
 
+    /* Initialize voice codec api. */
     memcpy(&ci_voice, &ci, sizeof(struct codec_api));
     memset(&id3_voice, 0, sizeof(struct mp3entry));
+    ci_voice.read_filebuf = voice_filebuf_callback;
+    ci_voice.pcmbuf_insert = voice_pcmbuf_insert_callback;
+    ci_voice.pcmbuf_insert_split = voice_pcmbuf_insert_split_callback;
+    ci_voice.get_codec_memory = get_voice_memory_callback;
+    ci_voice.request_buffer = voice_request_buffer_callback;
+    ci_voice.advance_buffer = voice_advance_buffer_callback;
+    ci_voice.advance_buffer_loc = voice_advance_buffer_loc_callback;
+    ci_voice.request_next_track = voice_request_next_track_callback;
+    ci_voice.mp3_get_filepos = voice_mp3_get_filepos_callback;
+    ci_voice.seek_buffer = voice_seek_buffer_callback;
+    ci_voice.seek_complete = voice_do_nothing;
+    ci_voice.set_elapsed = voice_set_elapsed_callback;
+    ci_voice.set_offset = voice_set_offset_callback;
+    ci_voice.discard_codec = voice_do_nothing;
     ci_voice.taginfo_ready = &voicetagtrue;
     ci_voice.id3 = &id3_voice;
-    ci_voice.pcmbuf_insert = codec_pcmbuf_insert_callback;
     id3_voice.frequency = 11200;
     id3_voice.length = 1000000L;
 
     create_thread(codec_thread, codec_stack, sizeof(codec_stack),
             codec_thread_name);
-    create_thread(voice_codec_thread, voice_codec_stack,
-            sizeof(voice_codec_stack), voice_codec_thread_name);
 
     while (1)
     {
@@ -2720,8 +2820,9 @@
 
     filebuf = (char *)&audiobuf[MALLOC_BUFSIZE];
 
-    /* Apply relevant settings */
-    audio_set_buffer_margin(global_settings.buffer_margin);
+    /* FIXME: This call will infinite loop if called on the audio thread
+     * while playing, fortunately this is an init call so that should be
+     * impossible. */
     audio_set_crossfade(global_settings.crossfade);
 
     sound_settings_apply();
@@ -2750,7 +2851,6 @@
 
     queue_init(&audio_queue);
     queue_init(&codec_queue);
-    queue_init(&voice_codec_queue);
     /* clear, not init to create a private queue */
     queue_clear(&codec_callback_queue);
 
diff --git a/firmware/mp3_playback.c b/firmware/mp3_playback.c
index 2739253..01b538a 100644
--- a/firmware/mp3_playback.c
+++ b/firmware/mp3_playback.c
@@ -629,11 +629,6 @@
     /* a dummy */
 }
 
-void mp3_play_stop(void)
-{
-    /* a dummy */
-}
-
 void mp3_play_pause(bool play)
 {
     /* a dummy */