Index: apps/tree.c
===================================================================
--- apps/tree.c	(revision 29688)
+++ apps/tree.c	(arbetskopia)
@@ -104,7 +104,6 @@
 
 static int dirbrowse(void);
 static int ft_play_dirname(char* name);
-static void ft_play_filename(char *dir, char *file);
 static void say_filetype(int attr);
 
 static const char* tree_get_filename(int selected_item, void *data,
@@ -279,6 +278,7 @@
 /* do this really late in the init sequence */
 void tree_gui_init(void)
 {
+printf("tree_gui_init\n");
     check_rockboxdir();
 
     strcpy(tc.currdir, "/");
@@ -1144,8 +1144,9 @@
         return -1;
 }
 
-static void ft_play_filename(char *dir, char *file)
+void ft_play_filename(char *dir, char *file)
 {
+printf("dir: %s, file: %s\n", dir, file);
 #if CONFIG_CODEC != SWCODEC
     if (audio_status() & AUDIO_STATUS_PLAY)
         return;
Index: apps/tree.h
===================================================================
--- apps/tree.h	(revision 29688)
+++ apps/tree.h	(arbetskopia)
@@ -122,5 +122,7 @@
 bool bookmark_play(char* resume_file, int index, int offset, int seed,
                    char *filename);
 
+void ft_play_filename(char *dir, char *file);
+
 extern struct gui_synclist tree_lists;
 #endif
Index: apps/playlist_viewer.c
===================================================================
--- apps/playlist_viewer.c	(revision 29688)
+++ apps/playlist_viewer.c	(arbetskopia)
@@ -48,6 +48,7 @@
 #include "list.h"
 #include "splash.h"
 #include "playlist_menu.h"
+#include "tree.h"
 
 /* Maximum number of tracks we can have loaded at one time */
 #define MAX_PLAYLIST_ENTRIES 200
@@ -608,6 +609,101 @@
         return Icon_NOICON;
 }
 
+static int playlist_callback_voice(int selected_item, void * data)
+{
+    struct playlist_viewer * local_viewer = (struct playlist_viewer *)data;
+    
+    int track_num = get_track_num(local_viewer, selected_item);
+    struct playlist_entry *track =
+       playlist_buffer_get_track(&(local_viewer->buffer), track_num);
+    printf("%s\n", track->name);
+    
+    char tmp[MAX_PATH];
+    strlcpy(tmp, track->name, MAX_PATH);
+    char *c = strrchr(tmp, '/');
+    
+    /* TODO: fall back to spelling if there's no clip */
+    if (global_settings.talk_file_clip)
+    {
+        if (c && c != tmp)
+        {
+            *c = '\0';
+            ft_play_filename(tmp, c+1);
+        }
+        else
+        {
+            ft_play_filename("/", tmp);
+        }
+    }
+#if 0
+    struct tree_context * local_tc=(struct tree_context *)data;
+    char *name;
+    int attr=0;
+#ifdef HAVE_TAGCACHE
+    bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
+
+    if (id3db)
+    {
+        attr = tagtree_get_attr(local_tc);
+        name = tagtree_get_entry(local_tc, selected_item)->name;
+    }
+    else
+#endif
+    {
+        struct entry* dc = local_tc->dircache;
+        struct entry* e = &dc[selected_item];
+        name = e->name;
+        attr = e->attr;
+    }
+    bool is_dir = (attr & ATTR_DIRECTORY);
+    bool did_clip = false;
+    /* First the .talk clip case */
+    if(is_dir)
+    {
+        if(global_settings.talk_dir_clip)
+        {
+            did_clip = true;
+            if(ft_play_dirname(name) <0)
+                /* failed, not existing */
+                did_clip = false;
+        }
+    } else { /* it's a file */
+        if (global_settings.talk_file_clip && (attr & FILE_ATTR_THUMBNAIL))
+        {
+            did_clip = true;
+            ft_play_filename(local_tc->currdir, name);
+        }
+    }
+    if(!did_clip)
+    {
+        /* say the number or spell if required or as a fallback */
+        switch (is_dir ? global_settings.talk_dir : global_settings.talk_file)
+        {
+        case 1: /* as numbers */
+            talk_id(is_dir ? VOICE_DIR : VOICE_FILE, false);
+            talk_number(selected_item+1        - (is_dir ? 0 : local_tc->dirsindir),
+                        true);
+            if(global_settings.talk_filetype
+               && !is_dir && *local_tc->dirfilter < NUM_FILTER_MODES)
+                say_filetype(attr);
+            break;
+        case 2: /* spelled */
+            talk_shutup();
+            if(global_settings.talk_filetype)
+            {
+                if(is_dir)
+                    talk_id(VOICE_DIR, true);
+                else if(*local_tc->dirfilter < NUM_FILTER_MODES)
+                    say_filetype(attr);
+            }
+            talk_spell(name, true);
+            break;
+        }
+    }
+#endif
+    return 0;
+}
+
 /* Main viewer function.  Filename identifies playlist to be viewed.  If NULL,
    view current playlist. */
 enum playlist_viewer_result playlist_viewer_ex(const char* filename)
@@ -621,6 +717,7 @@
 
     gui_synclist_init(&playlist_lists, playlist_callback_name,
                       &viewer, false, 1, NULL);
+    gui_synclist_set_voice_callback(&playlist_lists, playlist_callback_voice);
     gui_synclist_set_icon_callback(&playlist_lists,
                   global_settings.playlist_viewer_icons?
                   &playlist_callback_icons:NULL);
@@ -628,6 +725,7 @@
     gui_synclist_set_title(&playlist_lists, str(LANG_PLAYLIST), Icon_Playlist);
     gui_synclist_select_item(&playlist_lists, viewer.selected_track);
     gui_synclist_draw(&playlist_lists);
+    gui_synclist_speak_item(&playlist_lists);
     while (!exit)
     {
         int track;
