Index: trunk/apps/talk.c
===================================================================
--- trunk.orig/apps/talk.c
+++ trunk/apps/talk.c
@@ -41,6 +41,7 @@
#include "playback.h"
#endif
#include "debug.h"
+#include "kernel.h"
/* Memory layout varies between targets because the
@@ -108,6 +109,7 @@ struct queue_entry /* one entry of the i
/***************** Globals *****************/
static unsigned char* p_thumbnail = NULL; /* buffer for thumbnail */
+static volatile int thumbnail_buf_used;
static long size_for_thumbnail; /* leftover buffer size for it */
static struct voicefile* p_voicefile; /* loaded voicefile */
static bool has_voicefile; /* a voicefile file is present */
@@ -115,6 +117,15 @@ static struct queue_entry queue[QUEUE_SI
static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */
static int queue_write; /* write index of queue, by application */
static int queue_read; /* read index of queue, by ISR context */
+#if CONFIG_CODEC == SWCODEC
+struct mutex queue_mutex; /* protects queue_read, queue_write
+ and thumbnail_buf_used */
+#define queue_lock() do { mutex_lock(&queue_mutex); } while(0)
+#define queue_unlock() do { mutex_unlock(&queue_mutex); } while(0)
+#else
+#define queue_lock() do { } while(0)
+#define queue_unlock() do { } while(0)
+#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 */
@@ -296,7 +307,11 @@ static void mp3_callback(unsigned char**
*size = sent;
return;
}
- else if (sent > 0) /* go to next entry */
+ queue_lock();
+ if(p_thumbnail
+ && queue[queue_read].buf == p_thumbnail +thumbnail_buf_used)
+ thumbnail_buf_used = 0;
+ if (sent > 0) /* go to next entry */
{
queue_read = (queue_read + 1) & QUEUE_MASK;
}
@@ -318,7 +333,8 @@ re_check:
}
else if (p_silence != NULL /* silence clip available */
&& p_lastclip != p_silence /* previous clip wasn't silence */
- && p_lastclip != p_thumbnail) /* ..or thumbnail */
+ && !(p_lastclip >= p_thumbnail /* ..or thumbnail */
+ && p_lastclip < p_thumbnail +size_for_thumbnail))
{ /* add silence clip when queue runs empty playing a voice clip */
queue[queue_write].buf = p_silence;
queue[queue_write].len = silence_len;
@@ -330,6 +346,7 @@ re_check:
{
*size = 0; /* end of data */
}
+ queue_unlock();
}
/***************** Public routines *****************/
@@ -385,6 +402,7 @@ void talk_force_shutup(void)
DTCR3 = sent; /* let the DMA finish this frame */
CHCR3 |= 0x0001; /* re-enable DMA */
#endif /* CONFIG_CPU == SH7034 */
+ thumbnail_buf_used = 0;
return;
}
}
@@ -392,8 +410,10 @@ void talk_force_shutup(void)
/* Either SWCODEC, or MAS had nothing to do (was frame boundary or not our clip) */
mp3_play_stop();
+ queue_lock();
queue_write = queue_read = 0; /* reset the queue */
- return;
+ thumbnail_buf_used = 0;
+ queue_unlock();
}
/* Shutup the voice, except if force_enqueue_next is set. */
@@ -420,6 +440,7 @@ static void queue_clip(unsigned char* bu
/* disable the DMA temporarily, to be safe of race condition */
CHCR3 &= ~0x0001;
#endif
+ queue_lock();
queue_level = QUEUE_LEVEL; /* check old level */
if (queue_level < QUEUE_SIZE - 1) /* space left? */
@@ -428,7 +449,8 @@ static void queue_clip(unsigned char* bu
queue[queue_write].len = size;
queue_write = (queue_write + 1) & QUEUE_MASK;
}
-
+ queue_unlock();
+
if (queue_level == 0)
{ /* queue was empty, we have to do the initial start */
p_lastclip = buf;
@@ -458,6 +480,9 @@ static void queue_clip(unsigned char* bu
static void reset_state(void)
{
queue_write = queue_read = 0; /* reset the queue */
+#if CONFIG_CODEC == SWCODEC
+ mutex_init(&queue_mutex);
+#endif /* CONFIG_CODEC == SWCODEC */
p_voicefile = NULL; /* indicate no voicefile (trashed) */
#if CONFIG_CODEC == SWCODEC
/* Allocate a dedicated thumbnail buffer - once */
@@ -473,6 +498,7 @@ static void reset_state(void)
p_thumbnail = audiobuf;
size_for_thumbnail = audiobufend - audiobuf;
#endif
+ thumbnail_buf_used = 0;
p_silence = NULL; /* pause clip not accessible */
}
@@ -615,10 +641,13 @@ void talk_force_enqueue_next(void)
}
/* play a thumbnail from file */
-int talk_file(const char* filename, bool enqueue)
+/* Returns size of spoken thumbnail, so >0 means something is spoken,
+ <=0 means something went wrong. */
+int talk_file(const char* filename, long *prefix_ids, bool enqueue)
{
int fd;
int size;
+ int thumb_used;
#if CONFIG_CODEC != SWCODEC
struct mp3entry info;
#endif
@@ -640,32 +669,157 @@ int talk_file(const char* filename, bool
}
#endif
+ if (!enqueue)
+ /* We shutup() instead of relying on queue_clip() in case the
+ thumbnail buffer is currently in use: shutup now so it's
+ freed and we don't have to wait for it too long. */
+ talk_shutup();
+
fd = open(filename, O_RDONLY);
if (fd < 0) /* failed to open */
{
return 0;
}
+ if(filesize(fd) > size_for_thumbnail -thumbnail_buf_used)
+ { /* Don't play truncated clips */
+ close(fd);
+ return 0;
+ }
+
#if CONFIG_CODEC != SWCODEC
lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */
#endif
- size = read(fd, p_thumbnail, size_for_thumbnail);
+ thumb_used = thumbnail_buf_used;
+ size = read(fd, p_thumbnail +thumb_used,
+ size_for_thumbnail -thumb_used);
close(fd);
/* ToDo: find audio, skip ID headers and trailers */
- if (size > 0 && size != size_for_thumbnail) /* Don't play missing or truncated clips */
+ if (size > 0) /* Don't play missing clips */
{
#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
bitswap(p_thumbnail, size);
#endif
- queue_clip(p_thumbnail, size, enqueue);
+ if(prefix_ids)
+ /* prefix thumbnail by speaking these ids, but only now
+ that we know there's actually a thumbnail to be
+ spoken. */
+ talk_idarray(prefix_ids, true);
+ queue_lock();
+ thumbnail_buf_used = thumb_used +size;
+ queue_unlock();
+ queue_clip(p_thumbnail +thumb_used, size, true);
}
return size;
}
+/* Play a file's .talk thumbnail, fallback to spelling the filename, or
+ go straight to spelling depending on settings.
+ If dirname is non-NULL, it is prepended to the filename, otherwise
+ the filename should be a full path name. */
+int talk_file_or_spell(const char *dirname, const char* filename,
+ long *prefix_ids, bool enqueue)
+{
+ if (global_settings.talk_file_clip)
+ { /* .talk clips enabled */
+ char buf[MAX_PATH];
+ /* Does dirname end with a slash */
+ char *slash = (dirname && strlen(dirname) >1
+ && dirname[strlen(dirname)-1] != '/') ? "/" : "";
+ snprintf(buf, MAX_PATH, "%s%s%s%s",
+ dirname ? dirname : "",
+ slash,
+ filename,
+ file_thumbnail_ext);
+ if(talk_file(buf, prefix_ids, enqueue) > 0)
+ return 0;
+ }
+ if (global_settings.talk_file == 2)
+ { /* Either .talk clips are disabled, or as a fallback */
+ if(prefix_ids)
+ {
+ talk_idarray(prefix_ids, enqueue);
+ enqueue = true;
+ }
+ /* Spelling is slow, so speak only the basename (after the
+ last slash ) */
+ const char *ptr = strrchr(filename, '/');
+ if(ptr)
+ ++ptr;
+ else ptr = filename;
+ return talk_spell(ptr, enqueue);
+ }
+ return 0;
+}
+
+/* Play a directory's .talk thumbnail, fallback to spelling the filename, or
+ go straight to spelling depending on settings. */
+int talk_dir_or_spell(const char* dirname,
+ long *prefix_ids, bool enqueue)
+{
+ if (global_settings.talk_dir_clip)
+ { /* .talk clips enabled */
+ char buf[MAX_PATH];
+ /* Does directory name end in slash? */
+ char *slash = (strlen(dirname) >1
+ && dirname[strlen(dirname)-1] != '/') ? "/" : "";
+ snprintf(buf, MAX_PATH, "%s%s%s",
+ dirname, slash, dir_thumbnail_name);
+ if(talk_file(buf, prefix_ids, enqueue) > 0)
+ return 0;
+ }
+ if (global_settings.talk_file == 2)
+ { /* Either .talk clips disabled or as a fallback */
+ if(prefix_ids)
+ {
+ talk_idarray(prefix_ids, enqueue);
+ enqueue = true;
+ }
+ char buf[MAX_PATH];
+ /* Spell only the path component after the last slash */
+ strncpy(buf, dirname, MAX_PATH);
+ if(strlen(buf) >1 && buf[strlen(buf)-1] == '/')
+ /* strip trailing slash */
+ buf[strlen(buf)-1] = '\0';
+ char *ptr = strrchr(buf, '/');
+ if(ptr && strlen(buf) >1)
+ ++ptr;
+ else ptr = buf;
+ return talk_spell(ptr, enqueue);
+ }
+ return 0;
+}
+
+/* Speak thumbnail for each component of a full path, again falling
+ back or going straight to spelling depending on settings. */
+int talk_fullpath(const char* path, bool enqueue)
+{
+ if (!enqueue)
+ talk_shutup();
+ if(path[0] != '/')
+ /* path ought to start with /... */
+ return talk_spell(path, true);
+ talk_id(VOICE_CHAR_SLASH, true);
+ char buf[MAX_PATH];
+ strncpy(buf, path, MAX_PATH);
+ char *start = buf+1; /* start of current component */
+ char *ptr = strchr(start, '/'); /* end of current component */
+ while(ptr) { /* There are more slashes ahead */
+ /* temporarily poke a NULL at end of component to truncate string */
+ *ptr = '\0';
+ talk_dir_or_spell(buf, NULL, true);
+ *ptr = '/'; /* restore string */
+ start = ptr+1; /* setup for next component */
+ ptr = strchr(start, '/');
+ talk_id(VOICE_CHAR_SLASH, true);
+ }
+ /* no more slashes, final component is a filename */
+ return talk_file_or_spell(NULL, buf, NULL, true);
+}
/* say a numeric value, this word ordering works for english,
but not necessarily for other languages (e.g. german) */
@@ -863,6 +1017,8 @@ int talk_spell(const char* spell, bool e
talk_id(VOICE_DOT, true);
else if (c == ' ')
talk_id(VOICE_PAUSE, true);
+ else if (c == '/')
+ talk_id(VOICE_CHAR_SLASH, true);
}
return 0;
Index: trunk/apps/tree.c
===================================================================
--- trunk.orig/apps/tree.c
+++ trunk/apps/tree.c
@@ -1097,6 +1097,9 @@ void bookmark_play(char *resume_file, in
int i;
char* suffix = strrchr(resume_file, '.');
+ /* Nicely shuts up the voice while acknowledging the key press */
+ gui_syncsplash(0, ID2P(LANG_WAIT));
+
if (suffix != NULL &&
(!strcasecmp(suffix, ".m3u") || !strcasecmp(suffix, ".m3u8")))
{
@@ -1209,7 +1212,7 @@ static int ft_play_dirname(char* name)
DEBUGF("Found: %s\n", dirname_mp3_filename);
- talk_file(dirname_mp3_filename, false);
+ talk_file(dirname_mp3_filename, NULL, false);
if(global_settings.talk_filetype)
talk_id(VOICE_DIR, true);
return 1;
@@ -1230,14 +1233,15 @@ static void ft_play_filename(char *dir,
snprintf(name_mp3_filename, sizeof(name_mp3_filename),
"%s/%s%s", dir, file, file_thumbnail_ext);
- talk_file(name_mp3_filename, false);
+ talk_file(name_mp3_filename, NULL, false);
}
else
{ /* it already is a .talk file, play this directly */
snprintf(name_mp3_filename, sizeof(name_mp3_filename),
"%s/%s", dir, file);
talk_id(LANG_VOICE_DIR_HOVER, false); /* prefix it */
- talk_file(name_mp3_filename, true);
+ talk_file(name_mp3_filename, TALK_IDARRAY(LANG_VOICE_DIR_HOVER),
+ false);
}
}
Index: trunk/apps/talk.h
===================================================================
--- trunk.orig/apps/talk.h
+++ trunk/apps/talk.h
@@ -74,7 +74,16 @@ int talk_get_bufsize(void); /* get the l
void talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */
bool is_voice_queued(void); /* Are there more voice clips to be spoken? */
int talk_id(int32_t id, bool enqueue); /* play a voice ID from voicefont */
-int talk_file(const char* filename, bool enqueue); /* play a thumbnail from file */
+/* play a thumbnail from file */
+int talk_file(const char* filename, long *prefix_ids, bool enqueue);
+/* play file's thumbnail or spell name */
+int talk_file_or_spell(const char *dirname, const char* filename,
+ long *prefix_ids, bool enqueue);
+/* play dir's thumbnail or spell name */
+int talk_dir_or_spell(const char* filename,
+ long *prefix_ids, bool enqueue);
+/* play thumbnails for each components of full path, or spell */
+int talk_fullpath(const char* path, bool enqueue);
int talk_number(long n, bool enqueue); /* say a number */
int talk_value(long n, int unit, bool enqueue); /* say a numeric value */
int talk_spell(const char* spell, bool enqueue); /* spell a string */
Index: trunk/apps/lang/english.lang
===================================================================
--- trunk.orig/apps/lang/english.lang
+++ trunk/apps/lang/english.lang
@@ -7329,7 +7329,7 @@
*: "%s doesn't exist"
- *: ""
+ *: "Playlist directory doesn't exist"
@@ -7343,7 +7343,7 @@
*: "No Playlists"
- *: ""
+ *: "No Playlists"
@@ -9856,6 +9856,20 @@
+ id: VOICE_CHAR_SLASH
+ desc: spoken only, for spelling
+ user:
+
+ *: ""
+
+
+ *: ""
+
+
+ *: "slash"
+
+
+
id: VOICE_PAUSE
desc: spoken only, for spelling, a split second of silence (difficult to author)
user:
Index: trunk/apps/playlist_catalog.c
===================================================================
--- trunk.orig/apps/playlist_catalog.c
+++ trunk/apps/playlist_catalog.c
@@ -39,6 +39,7 @@
#include "yesno.h"
#include "filetypes.h"
#include "debug.h"
+#include "talk.h"
#define PLAYLIST_CATALOG_CFG ROCKBOX_DIR "/playlist_catalog.config"
#define PLAYLIST_CATALOG_DEFAULT_DIR "/Playlists"
@@ -114,7 +115,12 @@ static int initialize_catalog(void)
if (!playlist_dir_exists)
{
if (mkdir(playlist_dir) < 0) {
- gui_syncsplash(HZ*2, str(LANG_CATALOG_NO_DIRECTORY),
+ if (global_settings.talk_menu) {
+ talk_id(LANG_CATALOG_NO_DIRECTORY, false);
+ talk_spell(playlist_dir, true);
+ talk_force_enqueue_next();
+ }
+ gui_syncsplash(HZ*2, ID2P(LANG_CATALOG_NO_DIRECTORY),
playlist_dir);
return -1;
}
@@ -148,7 +154,7 @@ static int create_playlist_list(char** p
if (ft_load(tc, playlist_dir) < 0)
{
- gui_syncsplash(HZ*2, str(LANG_CATALOG_NO_DIRECTORY),
+ gui_syncsplash(HZ*2, ID2P(LANG_CATALOG_NO_DIRECTORY),
playlist_dir);
goto exit;
}
@@ -228,6 +234,13 @@ static char* playlist_callback_name(int
return buffer;
}
+static int playlist_callback_voice(int selected_item, void* data)
+{
+ char** playlists = (char**) data;
+ talk_file_or_spell(playlist_dir, playlists[selected_item], NULL, false);
+ return 0;
+}
+
/* Display all playlists in catalog. Selected "playlist" is returned.
If "view" mode is set then we're not adding anything into playlist. */
static int display_playlists(char* playlist, bool view)
@@ -245,7 +258,7 @@ static int display_playlists(char* playl
if (num_playlists <= 0)
{
- gui_syncsplash(HZ*2, str(LANG_CATALOG_NO_PLAYLISTS));
+ gui_syncsplash(HZ*2, ID2P(LANG_CATALOG_NO_PLAYLISTS));
return -1;
}
@@ -254,16 +267,19 @@ static int display_playlists(char* playl
gui_synclist_init(&playlist_lists, playlist_callback_name, playlists,
false, 1);
+ if(global_settings.talk_menu)
+ gui_synclist_set_voice_callback(&playlist_lists,
+ playlist_callback_voice);
gui_synclist_set_nb_items(&playlist_lists, num_playlists);
gui_synclist_draw(&playlist_lists);
+ gui_synclist_speak_item(&playlist_lists);
while (!exit)
{
- int button = get_action(CONTEXT_LIST,HZ/2);
+ int button;
char* sel_file;
-
- gui_synclist_do_button(&playlist_lists, &button,LIST_WRAP_UNLESS_HELD);
-
+ list_do_action(CONTEXT_LIST,HZ/2,
+ &playlist_lists, &button,LIST_WRAP_UNLESS_HELD);
sel_file = playlists[gui_synclist_get_sel_pos(&playlist_lists)];
switch (button)
@@ -299,7 +315,10 @@ static int display_playlists(char* playl
exit = true;
}
else
+ {
gui_synclist_draw(&playlist_lists);
+ gui_synclist_speak_item(&playlist_lists);
+ }
}
break;
@@ -323,6 +342,15 @@ static int display_playlists(char* playl
insert */
static void display_insert_count(int count)
{
+ static long talked_tick = 0;
+ if(count && (talked_tick == 0
+ || TIME_AFTER(current_tick, talked_tick+5*HZ)))
+ {
+ talked_tick = current_tick;
+ talk_number(count, false);
+ talk_id(LANG_PLAYLIST_INSERT_COUNT, true);
+ }
+
gui_syncsplash(0, str(LANG_PLAYLIST_INSERT_COUNT), count,
str(LANG_OFF_ABORT));
}
@@ -402,7 +430,7 @@ static int add_to_playlist(const char* p
/* search directory for tracks and append to playlist */
bool recurse = false;
char *lines[] = {
- (char *)str(LANG_RECURSE_DIRECTORY_QUESTION),
+ ID2P(LANG_RECURSE_DIRECTORY_QUESTION),
sel
};
struct text_message message={lines, 2};
Index: trunk/apps/bookmark.c
===================================================================
--- trunk.orig/apps/bookmark.c
+++ trunk/apps/bookmark.c
@@ -80,7 +80,7 @@ static bool check_bookmark(const char*
static char* create_bookmark(void);
static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id);
static void say_bookmark(const char* bookmark,
- int bookmark_id);
+ int bookmark_id, bool show_playlist_name);
static bool play_bookmark(const char* bookmark);
static bool generate_bookmark_file_name(const char *in);
static const char* skip_token(const char* s);
@@ -624,6 +624,22 @@ static char* get_bookmark_info(int list_
}
}
+static int bookmark_list_voice_cb(int list_index, void* data)
+{
+ struct bookmark_list* bookmarks = (struct bookmark_list*) data;
+ int index = list_index / 2;
+
+ if (bookmarks->show_dont_resume)
+ {
+ if (index == 0)
+ return talk_id(LANG_BOOKMARK_DONT_RESUME, false);
+ index--;
+ }
+ say_bookmark(bookmarks->items[index - bookmarks->start], index,
+ bookmarks->show_playlist_name);
+ return 0;
+}
+
/* ----------------------------------------------------------------------- */
/* This displays a the bookmarks in a file and allows the user to */
/* select one to play. */
@@ -632,7 +648,6 @@ static char* select_bookmark(const char*
{
struct bookmark_list* bookmarks;
struct gui_synclist list;
- int last_item = -2;
int item = 0;
int action;
size_t size;
@@ -647,6 +662,8 @@ static char* select_bookmark(const char*
bookmarks->show_playlist_name
= strcmp(bookmark_file_name, RECENT_BOOKMARK_FILE) == 0;
gui_synclist_init(&list, &get_bookmark_info, (void*) bookmarks, false, 2);
+ if(global_settings.talk_menu)
+ gui_synclist_set_voice_callback(&list, bookmark_list_voice_cb);
gui_synclist_set_title(&list, str(LANG_BOOKMARK_SELECT_BOOKMARK),
Icon_Bookmark);
gui_syncstatusbar_draw(&statusbars, true);
@@ -685,32 +702,20 @@ static char* select_bookmark(const char*
buffer_bookmarks(bookmarks, bookmarks->start);
gui_synclist_draw(&list);
+ cond_talk_ids_fq(VOICE_EXT_BMARK);
+ gui_synclist_speak_item(&list);
refresh = false;
}
- action = get_action(CONTEXT_BOOKMARKSCREEN, HZ / 2);
- gui_synclist_do_button(&list, &action, LIST_WRAP_UNLESS_HELD);
+ list_do_action(CONTEXT_BOOKMARKSCREEN, HZ / 2,
+ &list, &action, LIST_WRAP_UNLESS_HELD);
item = gui_synclist_get_sel_pos(&list) / 2;
if (bookmarks->show_dont_resume)
{
item--;
}
-
- if (item != last_item && global_settings.talk_menu)
- {
- last_item = item;
-
- if (item == -1)
- {
- talk_id(LANG_BOOKMARK_DONT_RESUME, true);
- }
- else
- {
- say_bookmark(bookmarks->items[item - bookmarks->start], item);
- }
- }
-
+
if (action == ACTION_STD_CONTEXT)
{
MENUITEM_STRINGLIST(menu_items, ID2P(LANG_BOOKMARK_CONTEXT_MENU),
@@ -752,7 +757,6 @@ static char* select_bookmark(const char*
delete_bookmark(bookmark_file_name, item);
bookmarks->reload = true;
refresh = true;
- last_item = -2;
}
break;
@@ -817,41 +821,53 @@ static bool delete_bookmark(const char*
/* This function parses a bookmark, says the voice UI part of it. */
/* ------------------------------------------------------------------------*/
static void say_bookmark(const char* bookmark,
- int bookmark_id)
+ int bookmark_id, bool show_playlist_name)
{
int resume_index;
long ms;
- bool enqueue = false; /* only the first voice is not queued */
+ bool playlist_shuffle = false;
+ bool is_dir;
- if (!parse_bookmark(bookmark, &resume_index, NULL, NULL, NULL,
- global_temp_buffer, sizeof(global_temp_buffer), &ms, NULL, NULL, NULL))
+ if (!parse_bookmark(bookmark, &resume_index, NULL, NULL, NULL,
+ global_temp_buffer,sizeof(global_temp_buffer),
+ &ms, NULL, &playlist_shuffle,
+ global_filename))
{
- talk_id(LANG_BOOKMARK_INVALID, true);
+ talk_id(LANG_BOOKMARK_INVALID, false);
return;
}
-/* disabled, because transition between talkbox and voice UI clip is not nice */
-#if 0
- if (global_settings.talk_dir >= 3)
- { /* "talkbox" enabled */
- char* last = strrchr(global_temp_buffer, '/');
- if (last)
- { /* compose filename for talkbox */
- strncpy(last + 1, dir_thumbnail_name,
- sizeof(global_temp_buffer) - (last - global_temp_buffer) - 1);
- talk_file(global_temp_buffer, enqueue);
- enqueue = true;
- }
+ talk_number(bookmark_id + 1, false);
+
+ is_dir = (global_temp_buffer[strlen(global_temp_buffer)-1] == '/');
+ if(show_playlist_name)
+ { /* It's useful to know which playlist this is */
+ if(is_dir)
+ talk_dir_or_spell(global_temp_buffer,
+ TALK_IDARRAY(VOICE_DIR), true);
+ else talk_file_or_spell(NULL, global_temp_buffer,
+ TALK_IDARRAY(LANG_PLAYLIST), true);
}
-#endif
- talk_id(VOICE_EXT_BMARK, enqueue);
- talk_number(bookmark_id + 1, true);
+
+ if(playlist_shuffle)
+ talk_id(LANG_SHUFFLE, true);
+
talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true);
talk_number(resume_index + 1, true);
talk_id(LANG_TIME, true);
- if (ms / 60000)
- talk_value(ms / 60000, UNIT_MIN, true);
- talk_value((ms % 60000) / 1000, UNIT_SEC, true);
+ talk_value(ms / 1000, UNIT_TIME, true);
+
+ /* Track filename */
+ if(is_dir)
+ talk_file_or_spell(global_temp_buffer, global_filename,
+ TALK_IDARRAY(VOICE_FILE), true);
+ else
+ { /* Unfortunately if this is a playlist, we do not know in which
+ directory the file is and therefore cannot find the track's
+ .talk file. */
+ talk_id(VOICE_FILE, true);
+ talk_spell(global_filename, true);
+ }
}
/* ----------------------------------------------------------------------- */