diff --git a/apps/settings_menu.c b/apps/settings_menu.c index 9ddf4c499b..ab4f242816 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -924,8 +924,16 @@ static bool voice_dirs(void) static bool voice_files(void) { - return set_option( str(LANG_VOICE_FILE), + int oldval = global_settings.talk_file; + bool ret; + ret = set_option( str(LANG_VOICE_FILE), &global_settings.talk_file, INT, voice_names, 4, NULL); + if (oldval != 3 && global_settings.talk_file == 3) + { /* force reload if newly talking thumbnails, + because the clip presence is cached only if enabled */ + reload_directory(); + } + return ret; } static bool voice_menu(void) diff --git a/apps/talk.c b/apps/talk.c index 7cfcb46254..db52afc834 100644 --- a/apps/talk.c +++ b/apps/talk.c @@ -39,6 +39,7 @@ #define QUEUE_SIZE 64 /* must be a power of two */ #define QUEUE_MASK (QUEUE_SIZE-1) const char* const dir_thumbnail_name = "_dirname.talk"; +const char* const file_thumbnail_ext = ".talk"; /***************** Functional Macros *****************/ diff --git a/apps/talk.h b/apps/talk.h index 09d71866b0..d07f955e67 100644 --- a/apps/talk.h +++ b/apps/talk.h @@ -54,9 +54,9 @@ enum { /* convenience macro to have both virtual pointer and ID as arguments */ #define STR(id) ID2P(id), id -/* publish this string, so it's stored only once (better than #define) */ +/* publish these strings, so they're stored only once (better than #define) */ extern const char* const dir_thumbnail_name; /* "_dirname.talk" */ -#define TALK_EXT ".talk" /* extra extension for file voicing */ +extern const char* const file_thumbnail_ext; /* ".talk" for file voicing */ void talk_init(void); int talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */ diff --git a/apps/tree.c b/apps/tree.c index 1025306848..89864f5be5 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -269,29 +269,19 @@ static int play_dirname(int start_index) return 1; } -static int play_filename(char *dir, char *file) +static void play_filename(char *dir, char *file) { - int fd; char name_mp3_filename[MAX_PATH+1]; if (mpeg_status() & MPEG_STATUS_PLAY) - return 0; + return; - if (strcasecmp(&file[strlen(file) - strlen(TALK_EXT)], TALK_EXT)) + if (strcasecmp(&file[strlen(file) - strlen(file_thumbnail_ext)], + file_thumbnail_ext)) { /* file has no .talk extension */ snprintf(name_mp3_filename, sizeof(name_mp3_filename), - "%s/%s" TALK_EXT, dir, file); + "%s/%s%s", dir, file, file_thumbnail_ext); - /* check if a corresponding .talk file exists */ - DEBUGF("Checking for Filename Thumb %s\n", name_mp3_filename); - fd = open(name_mp3_filename, O_RDONLY); - if (fd < 0) - { - DEBUGF("Failed to find: %s\n", name_mp3_filename); - return -1; - } - DEBUGF("Found: %s\n", name_mp3_filename); - close(fd); talk_file(name_mp3_filename, false); } else @@ -301,8 +291,6 @@ static int play_filename(char *dir, char *file) talk_id(LANG_VOICE_DIR_HOVER, false); /* prefix it */ talk_file(name_mp3_filename, true); } - - return 1; } static int compare(const void* p1, const void* p2) @@ -388,6 +376,94 @@ static void showfileline(int line, int direntry, bool scroll, const int *dirfilt *dotpos = '.'; } +/* walk a directory and check all dircache entries if a .talk file exists */ +void check_file_thumbnails(const char *dirname, int num_files) +{ + int i; + struct dirent *entry; + DIR *dir; + + dir = opendir(dirname); + if(!dir) + return; + + for (i=0; id_name) - strlen(file_thumbnail_ext); + if (ext_pos <= 0 /* too short to carry ".talk" */ + || (entry->attribute & ATTR_DIRECTORY) /* no file */ + || strcasecmp(&entry->d_name[ext_pos], file_thumbnail_ext)) + { /* or doesn't end with ".talk", no candidate */ + continue; + } + + /* terminate the (disposable) name in dir buffer, + this truncates off the ".talk" without needing an extra buffer */ + entry->d_name[ext_pos] = '\0'; + + /* search corresponding file in dir cache */ + for (i=0; id_name)) + { /* match */ + dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set the flag */ + break; /* exit search loop, because we found it */ + } + } + } + closedir(dir); +} + +/* check all dircache directories if they contain a "_dirname.talk" file */ +#if 0 /* not practical, this is too slow */ +void check_dir_thumbnails(const char *dirname, int num_files) +{ + int i; + int fd; + char clipfile[MAX_PATH]; + + for (i=0; i= 0) + { /* there is one */ + dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set the flag */ + close(fd); + } + else + { /* none found, clear the flag */ + dircache[i].attr &= ~TREE_ATTR_THUMBNAIL; + } + } +} +#endif /* #if 0 */ + /* load sorted directory into dircache. returns NULL on failure. */ struct entry* load_and_sort_directory(const char *dirname, const int *dirfilter, int *num_files, bool *buffer_full) @@ -490,6 +566,14 @@ struct entry* load_and_sort_directory(const char *dirname, const int *dirfilter, lastdir[sizeof(lastdir)-1] = 0; qsort(dircache,i,sizeof(struct entry),compare); + /* If thumbnail talking is enabled, make an extra run to mark files with + associated thumbnails, so we don't do unsuccessful spinups later. */ + if (global_settings.talk_file == 3) + check_file_thumbnails(dirname, i); /* map .talk to ours */ +#if 0 /* not practical, this is too slow */ + if (global_settings.talk_dir == 3) + check_dir_thumbnails(dirname, i); /* try in the directories */ +#endif /* #if 0 */ return dircache; } @@ -1373,15 +1457,12 @@ static bool dirbrowse(const char *root, const int *dirfilter) } } else - { + { DEBUGF("Playing file thumbnail: %s/%s%s\n", - currdir, dircache[lasti].name, TALK_EXT); - res = play_filename(currdir, dircache[lasti].name); - if (res < 0) /* failed, not existing */ - { /* say the number instead, as a fallback */ - play_filenumber(lasti-dirsindir+1, - dircache[lasti].attr); - } + currdir, dircache[lasti].name, file_thumbnail_ext); + /* no fallback necessary, we knew in advance + that the file exists */ + play_filename(currdir, dircache[lasti].name); } thumbnail_time = -1; /* job done */ } @@ -1501,20 +1582,30 @@ static bool dirbrowse(const char *root, const int *dirfilter) talk_spell(dircache[i].name, false); } } - else if (global_settings.talk_file == 1) /* files as numbers */ + else /* file */ { - play_filenumber(i-dirsindir+1, - dircache[i].attr & TREE_ATTR_MASK); + int voicemethod = global_settings.talk_file; + if (voicemethod == 3) /* thumbnail clip */ + { /* "schedule" a thumbnail, to have a little delay */ + if (dircache[i].attr & TREE_ATTR_THUMBNAIL) + { + thumbnail_time = current_tick + HOVER_DELAY; + } + else + { /* say the number as fallback */ + voicemethod = 1; + } + } + if (voicemethod == 1) /* files as numbers */ + { + play_filenumber(i-dirsindir+1, + dircache[i].attr & TREE_ATTR_MASK); + } + else if (voicemethod == 2) /* files spelled */ + { + talk_spell(dircache[i].name, false); + } } - else if (global_settings.talk_file == 2) /* files spelled */ - { - talk_spell(dircache[i].name, false); - } - else if (global_settings.talk_file == 3) /* hover */ - { /* "schedule" a thumbnail, to have a little dalay */ - thumbnail_time = current_tick + HOVER_DELAY; - } - } } diff --git a/apps/tree.h b/apps/tree.h index 297d611c70..de5f2d0e01 100644 --- a/apps/tree.h +++ b/apps/tree.h @@ -88,7 +88,10 @@ struct filetype { }; -/* using attribute not used by FAT */ +/* using attribute bits not used by FAT (FAT uses lower 6) */ + +#define TREE_ATTR_THUMBNAIL 0x0080 /* corresponding .talk file exists */ + /* (this also reflects the sort order if by type) */ #define TREE_ATTR_BMARK 0x0100 /* book mark file */ #define TREE_ATTR_M3U 0x0200 /* playlist */ @@ -99,7 +102,7 @@ struct filetype { #define TREE_ATTR_LNG 0x0700 /* binary lang file */ #define TREE_ATTR_ROCK 0x0800 /* binary rockbox plugin */ #define TREE_ATTR_MOD 0x0900 /* firmware file */ -#define TREE_ATTR_MASK 0xFFC0 /* which bits tree.c uses (above) */ +#define TREE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */ void tree_get_filetypes(const struct filetype**, int*); void tree_init(void);