diff --git a/apps/talk.c b/apps/talk.c
index e07dcb8..cdf8e91 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -108,6 +108,19 @@ struct queue_entry /* one entry of the internal queue */
 
 /***************** Globals *****************/
 
+#if CONFIG_STORAGE & STORAGE_MMC
+#define TALK_PROGRESSIVE_LOAD
+#elif CONFIG_CODEC == SWCODEC && MEM <= 2
+#define TALK_PARTIAL_LOAD
+#endif
+
+#ifdef TALK_PARTIAL_LOAD
+static unsigned char *clip_buffer;
+static long           max_clipsize;
+static long           buffered_id[QUEUE_SIZE];
+static int            buffered_clips;
+#endif
+
 static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */
 /* Multiple thumbnails can be loaded back-to-back in this buffer. */
 static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in
@@ -131,7 +144,7 @@ static struct mutex queue_mutex SHAREDBSS_ATTR;
 #endif /* CONFIG_CODEC */
 static int sent; /* how many bytes handed over to playback, owned by ISR */
 static unsigned char curr_hd[3]; /* current frame header, for re-sync */
-static int filehandle = -1; /* global, so the MMC variant can keep the file open */
+static int filehandle = -1; /* global, so we can keep the file open if needed */
 static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */
 static long silence_len; /* length of the VOICE_PAUSE clip */
 static unsigned char* p_lastclip; /* address of latest clip, for silence add */
@@ -182,9 +195,21 @@ static unsigned char* get_clip(long id, long* p_size)
     clipsize = p_voicefile->index[id].size;
     if (clipsize == 0) /* clip not included in voicefile */
         return NULL;
+
+#ifndef TALK_PARTIAL_LOAD
     clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset;
+#else
+    int idx;
+    if (id == VOICE_PAUSE) {
+        idx = QUEUE_SIZE;
+    } else {
+        idx = buffered_clips;
+        buffered_clips = (buffered_clips + 1) & QUEUE_MASK;
+    }
+    clipbuf = clip_buffer + idx * max_clipsize;
+#endif
 
-#if (CONFIG_STORAGE & STORAGE_MMC) /* dynamic loading, on demand */
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
     if (!(clipsize & LOADED_MASK))
     {   /* clip used for the first time, needs loading */
         lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET);
@@ -192,6 +217,13 @@ static unsigned char* get_clip(long id, long* p_size)
             return NULL; /* read error */
 
         p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */
+#ifdef TALK_PARTIAL_LOAD
+        /* mark previously loaded clip as unloaded */
+        if (id != VOICE_PAUSE && buffered_id[idx] >= 0) {
+            p_voicefile->index[buffered_id[idx]].size &= ~LOADED_MASK;
+            buffered_id[idx] = id;
+        }
+#endif
     }
     else
     {   /* clip is in memory already */
@@ -205,29 +237,40 @@ static unsigned char* get_clip(long id, long* p_size)
 
 
 /* load the voice file into the mp3 buffer */
-static void load_voicefile(void)
+static void load_voicefile(bool probe)
 {
     int load_size;
     int got_size;
+#ifndef TALK_PARTIAL_LOAD
     int file_size;
+#endif
 #ifdef ROCKBOX_LITTLE_ENDIAN
     int i;
 #endif
 
-    filehandle = open_voicefile();
+    if (!probe)
+        filehandle = open_voicefile();
     if (filehandle < 0) /* failed to open */
         goto load_err;
 
+#ifndef TALK_PARTIAL_LOAD
     file_size = filesize(filehandle);
     if (file_size > audiobufend - audiobuf) /* won't fit? */
         goto load_err;
+#endif
 
-#if (CONFIG_STORAGE & STORAGE_MMC) /* load only the header for now */
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
+    /* load only the header for now */
     load_size = offsetof(struct voicefile, index);
 #else /* load the full file */
     load_size = file_size; 
 #endif
 
+#ifdef TALK_PARTIAL_LOAD
+    if (load_size > audiobufend - audiobuf) /* won't fit? */
+        goto load_err;
+#endif
+
     got_size = read(filehandle, audiobuf, load_size);
     if (got_size != load_size /* failure */)
         goto load_err;
@@ -258,26 +301,39 @@ static void load_voicefile(void)
     else
         goto load_err;
 
-#ifdef ROCKBOX_LITTLE_ENDIAN
-    for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++)
-        structec_convert(&p_voicefile->index[i], "ll", 1, true);
-#endif
-
-#if (CONFIG_STORAGE & STORAGE_MMC)
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
     /* load the index table, now that we know its size from the header */
     load_size = (p_voicefile->id1_max + p_voicefile->id2_max)
                 * sizeof(struct clip_entry);
+
+#ifdef TALK_PARTIAL_LOAD
+    if (load_size > audiobufend - audiobuf) /* won't fit? */
+        goto load_err;
+#endif
+
     got_size = read(filehandle, 
                     (unsigned char *) p_voicefile + offsetof(struct voicefile, index), load_size);
     if (got_size != load_size) /* read error */
         goto load_err;
 #else
-    close(filehandle); /* only the MMC variant leaves it open */
+    close(filehandle);
     filehandle = -1;
 #endif
 
-    /* make sure to have the silence clip, if available */
-    p_silence = get_clip(VOICE_PAUSE, &silence_len);
+#ifdef ROCKBOX_LITTLE_ENDIAN
+    for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++)
+        structec_convert(&p_voicefile->index[i], "ll", 1, true);
+#endif
+
+#ifdef TALK_PARTIAL_LOAD
+    clip_buffer = (unsigned char *) p_voicefile + p_voicefile->table;
+    unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
+    clip_buffer += clips * sizeof(struct clip_entry);
+#endif
+    if (!probe) {
+        /* make sure to have the silence clip, if available */
+        p_silence = get_clip(VOICE_PAUSE, &silence_len);
+    }
 
     return;
 
@@ -501,6 +557,14 @@ static void reset_state(void)
     p_thumbnail = audiobuf;
     size_for_thumbnail = audiobufend - audiobuf;
 #endif
+
+#ifdef TALK_PARTIAL_LOAD
+    int i;
+    for(i=0; i<QUEUE_SIZE; i++)
+        buffered_id[i] = -1;
+    buffered_clips = 0;
+#endif
+
     thumbnail_buf_used = 0;
     p_silence = NULL; /* pause clip not accessible */
 }
@@ -517,8 +581,8 @@ void talk_init(void)
         return;
     }
 
-#if (CONFIG_STORAGE & STORAGE_MMC)
-    if (filehandle >= 0) /* MMC: An old voice file might still be open */
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
+    if (filehandle >= 0)
     {
         close(filehandle);
         filehandle = -1;
@@ -540,18 +604,47 @@ void talk_init(void)
     reset_state(); /* use this for most of our inits */
 
     filehandle = open_voicefile();
-    size_t audiobufsz = audiobufend - audiobuf;
+    if (filehandle < 0) {
+        has_voicefile = false;
+        voicefile_size = 0;
+        return;
+    }
+
+    voicefile_size = filesize(filehandle);
+
     /* test if we can open and if it fits in the audiobuffer */
-    has_voicefile = filehandle >= 0 && filesize(filehandle) <= (off_t)audiobufsz;
-    voicefile_size = 0;
+    size_t audiobufsz = audiobufend - audiobuf;
 
-    if (has_voicefile)
-    {
-        voicefile_size = filesize(filehandle);
-        close(filehandle); /* close again, this was just to detect presence */
-        filehandle = -1;
+#ifdef TALK_PARTIAL_LOAD
+    /* we won't load the full file */
+    load_voicefile(true);
+    if (!p_voicefile)
+        return;
+    unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
+    unsigned i;
+    int silence_size = 0;
+    for(i=0; i<clips; i++) {
+        int size = p_voicefile->index[i].size;
+        if (size > max_clipsize)
+            max_clipsize = size;
+        if (i == VOICE_PAUSE)
+            silence_size = size;
     }
 
+    voicefile_size = p_voicefile->table + clips * sizeof(struct clip_entry);
+    voicefile_size += max_clipsize * QUEUE_SIZE + silence_size;
+    p_voicefile = NULL;
+#endif
+
+    if (voicefile_size <= audiobufsz) {
+        has_voicefile = true;
+    } else {
+        has_voicefile = false;
+        voicefile_size = 0;
+    }
+
+    close(filehandle); /* close again, this was just to detect presence */
+    filehandle = -1;
 }
 
 #if CONFIG_CODEC == SWCODEC
@@ -576,8 +669,8 @@ void talk_buffer_steal(void)
 #if CONFIG_CODEC != SWCODEC
     mp3_play_stop();
 #endif
-#if (CONFIG_STORAGE & STORAGE_MMC)
-    if (filehandle >= 0) /* only relevant for MMC */
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
+    if (filehandle >= 0)
     {
         close(filehandle);
         filehandle = -1;
@@ -603,7 +696,7 @@ int talk_id(int32_t id, bool enqueue)
 #endif
 
     if (p_voicefile == NULL && has_voicefile)
-        load_voicefile(); /* reload needed */
+        load_voicefile(false); /* reload needed */
 
     if (p_voicefile == NULL) /* still no voices? */
         return -1;
