Index: apps/playlist.c =================================================================== --- apps/playlist.c.orig +++ apps/playlist.c @@ -181,7 +181,8 @@ static int get_previous_directory(char * static int check_subdir_for_music(char *dir, char *subdir); static int format_track_path(char *dest, char *src, int buf_length, int max, char *dir); -static void display_playlist_count(int count, const unsigned char *fmt); +static void display_playlist_count(int count, const unsigned char *fmt, + bool final); static void display_buffer_full(void); static int flush_cached_control(struct playlist_info* playlist); static int update_control(struct playlist_info* playlist, @@ -288,6 +289,7 @@ static void create_control(struct playli { if (check_rockboxdir()) { + cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR); gui_syncsplash(HZ*2, (unsigned char *)"%s (%d)", str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR), playlist->control_fd); @@ -483,8 +485,7 @@ static int add_indices_to_playlist(struc else lcd_setmargins(0, 0); #endif - - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); if (!buffer) { @@ -770,11 +771,11 @@ static int directory_search_callback(cha unsigned char* count_str; if (c->queue) - count_str = str(LANG_PLAYLIST_QUEUE_COUNT); + count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT); else - count_str = str(LANG_PLAYLIST_INSERT_COUNT); + count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT); - display_playlist_count(c->count, count_str); + display_playlist_count(c->count, count_str, false); if ((c->count) == PLAYLIST_DISPLAY_COUNT && (audio_status() & AUDIO_STATUS_PLAY) && @@ -1350,9 +1351,9 @@ static int get_filename(struct playlist_ if (max < 0) { if (control_file) - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); else - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); return max; } @@ -1445,7 +1446,7 @@ static int get_next_dir(char *dir, bool if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); exit = true; result = -1; break; @@ -1530,7 +1531,7 @@ static int check_subdir_for_music(char * if (ft_load(tc, dir) < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); return -2; } @@ -1584,7 +1585,7 @@ static int check_subdir_for_music(char * /* we now need to reload our current directory */ if(ft_load(tc, dir) < 0) gui_syncsplash(HZ*2, - str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); + ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); } return result; @@ -1657,8 +1658,23 @@ static int format_track_path(char *dest, * Display splash message showing progress of playlist/directory insertion or * save. */ -static void display_playlist_count(int count, const unsigned char *fmt) +static void display_playlist_count(int count, const unsigned char *fmt, + bool final) { + static long talked_tick = 0; + long id = P2ID(fmt); + if(talk_menus_enabled() && id>=0) + { + if(final || (count && (talked_tick == 0 + || TIME_AFTER(current_tick, talked_tick+5*HZ)))) + { + talked_tick = current_tick; + talk_number(count, false); + talk_id(id, true); + } + } + fmt = P2STR(fmt); + lcd_clear_display(); #ifdef HAVE_LCD_BITMAP @@ -1676,7 +1692,7 @@ static void display_playlist_count(int c */ static void display_buffer_full(void) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_BUFFER_FULL)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL)); } /* @@ -1755,7 +1771,7 @@ static int flush_cached_control(struct p else { result = -1; - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); } return result; @@ -1942,11 +1958,11 @@ int playlist_resume(void) empty_playlist(playlist, true); - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); playlist->control_fd = open(playlist->control_filename, O_RDWR); if (playlist->control_fd < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } playlist->control_created = true; @@ -1954,7 +1970,7 @@ int playlist_resume(void) control_file_size = filesize(playlist->control_fd); if (control_file_size <= 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } @@ -1963,7 +1979,7 @@ int playlist_resume(void) PLAYLIST_COMMAND_SIZE= control_file_size) { /* no newline at end of control file */ - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_INVALID)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID)); return -1; } @@ -2361,6 +2377,7 @@ int playlist_shuffle(int random_seed, in start_current = true; } + cond_talk_ids(LANG_WAIT); gui_syncsplash(0, str(LANG_PLAYLIST_SHUFFLE)); randomise_playlist(playlist, random_seed, start_current, true); @@ -2853,7 +2870,7 @@ int playlist_insert_track(struct playlis if (check_control(playlist) < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } @@ -2884,7 +2901,7 @@ int playlist_insert_directory(struct pla if (check_control(playlist) < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } @@ -2897,11 +2914,11 @@ int playlist_insert_directory(struct pla } if (queue) - count_str = str(LANG_PLAYLIST_QUEUE_COUNT); + count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT); else - count_str = str(LANG_PLAYLIST_INSERT_COUNT); + count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT); - display_playlist_count(0, count_str); + display_playlist_count(0, count_str, false); context.playlist = playlist; context.position = position; @@ -2917,7 +2934,7 @@ int playlist_insert_directory(struct pla cpu_boost(false); - display_playlist_count(context.count, count_str); + display_playlist_count(context.count, count_str, true); if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) audio_flush_and_reload_tracks(); @@ -2950,14 +2967,14 @@ int playlist_insert_playlist(struct play if (check_control(playlist) < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } fd = open(filename, O_RDONLY); if (fd < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); return -1; } @@ -2971,11 +2988,11 @@ int playlist_insert_playlist(struct play dir = "/"; if (queue) - count_str = str(LANG_PLAYLIST_QUEUE_COUNT); + count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT); else - count_str = str(LANG_PLAYLIST_INSERT_COUNT); + count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT); - display_playlist_count(count, count_str); + display_playlist_count(count, count_str, false); if (position == PLAYLIST_REPLACE) { @@ -3023,7 +3040,7 @@ int playlist_insert_playlist(struct play if ((count%PLAYLIST_DISPLAY_COUNT) == 0) { - display_playlist_count(count, count_str); + display_playlist_count(count, count_str, false); if (count == PLAYLIST_DISPLAY_COUNT && (audio_status() & AUDIO_STATUS_PLAY) && @@ -3045,7 +3062,7 @@ int playlist_insert_playlist(struct play cpu_boost(false); - display_playlist_count(count, count_str); + display_playlist_count(count, count_str, true); if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) audio_flush_and_reload_tracks(); @@ -3070,7 +3087,7 @@ int playlist_delete(struct playlist_info if (check_control(playlist) < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } @@ -3105,7 +3122,7 @@ int playlist_move(struct playlist_info* if (check_control(playlist) < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } @@ -3368,7 +3385,7 @@ int playlist_save(struct playlist_info* if (playlist->buffer_size < (int)(playlist->amount * sizeof(int))) { /* not enough buffer space to store updated indices */ - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); return -1; } @@ -3384,11 +3401,11 @@ int playlist_save(struct playlist_info* fd = open(path, O_CREAT|O_WRONLY|O_TRUNC); if (fd < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); return -1; } - display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); + display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false); cpu_boost(true); @@ -3425,7 +3442,7 @@ int playlist_save(struct playlist_info* if (fdprintf(fd, "%s\n", tmp_buf) < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); result = -1; break; } @@ -3433,7 +3450,7 @@ int playlist_save(struct playlist_info* count++; if ((count % PLAYLIST_DISPLAY_COUNT) == 0) - display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); + display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false); yield(); } @@ -3441,7 +3458,7 @@ int playlist_save(struct playlist_info* index = (index+1)%playlist->amount; } - display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); + display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true); close(fd); @@ -3512,7 +3529,7 @@ int playlist_directory_tracksearch(const if (ft_load(tc, dirname) < 0) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); *(tc->dirfilter) = old_dirfilter; return -1; } Index: apps/tree.c =================================================================== --- apps/tree.c.orig +++ apps/tree.c @@ -325,7 +325,7 @@ static int update_dir(void) (tc.dirfull || tc.filesindir == global_settings.max_files_in_dir) ) { - gui_syncsplash(HZ, str(LANG_SHOWDIR_BUFFER_FULL)); + gui_syncsplash(HZ, ID2P(LANG_SHOWDIR_BUFFER_FULL)); } } #ifdef HAVE_TAGCACHE @@ -556,7 +556,7 @@ static int dirbrowse() if (*tc.dirfilter > NUM_FILTER_MODES && numentries==0) { - gui_syncsplash(HZ*2, str(LANG_NO_FILES)); + gui_syncsplash(HZ*2, ID2P(LANG_NO_FILES)); return false; /* No files found for rockbox_browser() */ } @@ -567,7 +567,7 @@ static int dirbrowse() tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */ #ifdef BOOTFILE if (boot_changed) { - char *lines[]={str(LANG_BOOT_CHANGED), str(LANG_REBOOT_NOW)}; + char *lines[]={ID2P(LANG_BOOT_CHANGED), ID2P(LANG_REBOOT_NOW)}; struct text_message message={lines, 2}; if(gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES) rolo_load("/" BOOTFILE); @@ -1336,6 +1336,7 @@ void tree_restore(void) str(LANG_SCANNING_DISK)); gui_textarea_update(&screens[i]); } + cond_talk_ids_fq(LANG_SCANNING_DISK); dircache_build(global_status.dircache_size); Index: apps/settings.c =================================================================== --- apps/settings.c.orig +++ apps/settings.c @@ -544,6 +544,7 @@ int settings_save( void ) screens[i].update(); #endif } + cond_talk_ids_fq(LANG_SETTINGS_SAVE_FAILED); sleep(HZ*2); return -1; } @@ -575,15 +576,15 @@ bool settings_save_config(int options) break; } else { - gui_syncsplash(HZ, str(LANG_CANCEL)); + gui_syncsplash(HZ, ID2P(LANG_CANCEL)); return false; } } if (settings_write_config(filename, options)) - gui_syncsplash(HZ, str(LANG_SETTINGS_SAVED)); + gui_syncsplash(HZ, ID2P(LANG_SETTINGS_SAVED)); else - gui_syncsplash(HZ, str(LANG_FAILED)); + gui_syncsplash(HZ, ID2P(LANG_FAILED)); return true; } Index: apps/onplay.c =================================================================== --- apps/onplay.c.orig +++ apps/onplay.c @@ -160,12 +160,12 @@ static bool add_to_playlist(int position { bool new_playlist = !(audio_status() & AUDIO_STATUS_PLAY); char *lines[] = { - (char *)str(LANG_RECURSE_DIRECTORY_QUESTION), + ID2P(LANG_RECURSE_DIRECTORY_QUESTION), selected_file }; struct text_message message={lines, 2}; - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); if (new_playlist) playlist_create(NULL, NULL); @@ -502,7 +502,7 @@ static int remove_dir(char* dirname, int #endif if(ACTION_STD_CANCEL == get_action(CONTEXT_STD,TIMEOUT_NOBLOCK)) { - gui_syncsplash(HZ, str(LANG_CANCEL)); + gui_syncsplash(HZ, ID2P(LANG_CANCEL)); result = -1; break; } @@ -524,11 +524,11 @@ static int remove_dir(char* dirname, int static bool delete_handler(bool is_dir) { char *lines[]={ - (char *)str(LANG_REALLY_DELETE), + ID2P(LANG_REALLY_DELETE), selected_file }; char *yes_lines[]={ - (char *)str(LANG_DELETED), + ID2P(LANG_DELETED), selected_file }; @@ -599,6 +599,7 @@ static bool rename_file(void) lcd_puts(0,0,str(LANG_RENAME)); lcd_puts(0,1,str(LANG_FAILED)); lcd_update(); + cond_talk_ids_fq(LANG_RENAME, LANG_FAILED); sleep(HZ*2); } else @@ -628,6 +629,7 @@ static bool create_dir(void) rc = mkdir(dirname); if (rc < 0) { + cond_talk_ids_fq(LANG_CREATE_DIR, LANG_FAILED); gui_syncsplash(HZ, (unsigned char *)"%s %s", str(LANG_CREATE_DIR), str(LANG_FAILED)); } else { @@ -873,7 +875,7 @@ static bool clipboard_paste(void) bool success; int target_fd; - unsigned char *lines[]={str(LANG_REALLY_OVERWRITE)}; + unsigned char *lines[]={ID2P(LANG_REALLY_OVERWRITE)}; struct text_message message={(char **)lines, 1}; /* Get the name of the current directory */ @@ -896,11 +898,11 @@ static bool clipboard_paste(void) } if (clipboard_is_copy) { - gui_syncsplash(0, str(LANG_COPYING)); + gui_syncsplash(0, ID2P(LANG_COPYING)); } else { - gui_syncsplash(0, str(LANG_MOVING)); + gui_syncsplash(0, ID2P(LANG_MOVING)); } /* Now figure out what we're doing */ @@ -939,6 +941,7 @@ static bool clipboard_paste(void) /* Force reload of the current directory */ onplay_result = ONPLAY_RELOAD_DIR; } else { + cond_talk_ids_fq(LANG_PASTE, LANG_FAILED); gui_syncsplash(HZ, (unsigned char *)"%s %s", str(LANG_PASTE), str(LANG_FAILED)); } Index: apps/gui/splash.c =================================================================== --- apps/gui/splash.c.orig +++ apps/gui/splash.c @@ -22,6 +22,9 @@ #include "stdio.h" #include "kernel.h" #include "screen_access.h" +#include "lang.h" +#include "settings.h" +#include "talk.h" #ifndef MAX #define MAX(a, b) (((a)>(b))?(a):(b)) @@ -197,6 +200,15 @@ void gui_syncsplash(int ticks, const uns { va_list ap; int i; + long id; + /* fmt may be a so called virtual pointer. See settings.h. */ + if((id = P2ID(fmt)) >= 0) + /* If fmt specifies a voicefont ID, and voice menus are + enabled, then speak it. */ + cond_talk_ids_fq(id); + /* If fmt is a lang ID then get the corresponding string (which + still might contain % place holders). */ + fmt = P2STR(fmt); va_start( ap, fmt ); FOR_NB_SCREENS(i) splash(&(screens[i]), fmt, ap); Index: apps/gui/yesno.c =================================================================== --- apps/gui/yesno.c.orig +++ apps/gui/yesno.c @@ -23,6 +23,7 @@ #include "misc.h" #include "lang.h" #include "action.h" +#include "talk.h" /* * Initializes the yesno asker @@ -97,7 +98,28 @@ static bool gui_yesno_draw_result(struct gui_textarea_put_message(yn->display, message, 0); return(true); } + #include "debug.h" + +/* Processes a text_message whose lines may be virtual pointers + representing language / voicefont IDs (see settings.h). Copies out + the IDs to the ids array, which is of length maxlen, and replaces + the pointers in the text_message with the actual language strings. + The ids array is terminated with the TALK_FINAL_ID sentinel + element. */ +static void extract_talk_ids(struct text_message *m, long *ids, int maxlen) +{ + int line, i=0; + if(m) + for(line=0; linenb_lines; line++) { + long id = P2ID((unsigned char *)m->message_lines[line]); + if(id>=0 && imessage_lines[line] = (char *)P2STR((unsigned char *)m->message_lines[line]); + } + ids[i] = TALK_FINAL_ID; +} + enum yesno_res gui_syncyesno_run(struct text_message * main_message, struct text_message * yes_message, struct text_message * no_message) @@ -107,6 +129,13 @@ enum yesno_res gui_syncyesno_run(struct int result=-1; bool result_displayed; struct gui_yesno yn[NB_SCREENS]; + long voice_ids[5]; + long talked_tick = 0; + /* The text messages may contain virtual pointers to IDs (see + settings.h) instead of plain strings. Copy the IDs out so we + can speak them, and unwrap the actual language strings. */ + extract_talk_ids(main_message, voice_ids, + sizeof(voice_ids)/sizeof(voice_ids[0])); FOR_NB_SCREENS(i) { gui_yesno_init(&(yn[i]), main_message, yes_message, no_message); @@ -115,7 +144,14 @@ enum yesno_res gui_syncyesno_run(struct } while (result==-1) { - button = get_action(CONTEXT_YESNOSCREEN,TIMEOUT_BLOCK); + /* Repeat the question every 5secs (more or less) */ + if (talk_menus_enabled() + && (talked_tick==0 || TIME_AFTER(current_tick, talked_tick+HZ*5))) + { + talked_tick = current_tick; + talk_idarray(voice_ids, false); + } + button = get_action(CONTEXT_YESNOSCREEN, HZ*5); switch (button) { case ACTION_YESNO_ACCEPT: @@ -133,6 +169,13 @@ enum yesno_res gui_syncyesno_run(struct } FOR_NB_SCREENS(i) result_displayed=gui_yesno_draw_result(&(yn[i]), result); + extract_talk_ids((result == YESNO_YES) ? yes_message : no_message, + voice_ids, sizeof(voice_ids)/sizeof(voice_ids[0])); + if (talk_menus_enabled()) + { + talk_idarray(voice_ids, false); + talk_force_enqueue_next(); + } if(result_displayed) sleep(HZ); return(result); Index: apps/menus/settings_menu.c =================================================================== --- apps/menus/settings_menu.c.orig +++ apps/menus/settings_menu.c @@ -49,13 +49,13 @@ static void tagcache_rebuild_with_splash(void) { tagcache_rebuild(); - gui_syncsplash(HZ*2, str(LANG_TAGCACHE_FORCE_UPDATE_SPLASH)); + gui_syncsplash(HZ*2, ID2P(LANG_TAGCACHE_FORCE_UPDATE_SPLASH)); } static void tagcache_update_with_splash(void) { tagcache_update(); - gui_syncsplash(HZ*2, str(LANG_TAGCACHE_FORCE_UPDATE_SPLASH)); + gui_syncsplash(HZ*2, ID2P(LANG_TAGCACHE_FORCE_UPDATE_SPLASH)); } #ifdef HAVE_TC_RAMCACHE @@ -174,7 +174,7 @@ static int dircache_callback(int action, { case true: if (!dircache_is_enabled()) - gui_syncsplash(HZ*2, str(LANG_PLEASE_REBOOT)); + gui_syncsplash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); break; case false: if (dircache_is_enabled()) Index: apps/talk.c =================================================================== --- apps/talk.c.orig +++ apps/talk.c @@ -117,6 +117,8 @@ static long size_for_thumbnail; /* lefto static struct voicefile* p_voicefile; /* loaded voicefile */ static bool has_voicefile; /* a voicefile file is present */ static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ +/* enqueue next utterance even if enqueue is false. */ +static bool force_enqueue_next; static int queue_write; /* write index of queue, by application */ static int queue_read; /* read index of queue, by ISR context */ static int sent; /* how many bytes handed over to playback, owned by ISR */ @@ -134,7 +136,6 @@ static int talk_menu_disable; /* if non- static void load_voicefile(void); static void mp3_callback(unsigned char** start, size_t* size); -static int shutup(void); static int queue_clip(unsigned char* buf, long size, bool enqueue); static int open_voicefile(void); static unsigned char* get_clip(long id, long* p_size); @@ -267,6 +268,13 @@ load_err: } +/* Are more voice clips queued and waiting? */ +bool is_voice_queued() +{ + return !!QUEUE_LEVEL; +} + + /* called in ISR context if mp3 data got consumed */ static void mp3_callback(unsigned char** start, size_t* size) { @@ -321,7 +329,7 @@ re_check: } /* stop the playback and the pending clips */ -static int shutup(void) +int do_shutup(void) { #if CONFIG_CODEC != SWCODEC unsigned char* pos; @@ -387,6 +395,13 @@ static int shutup(void) return 0; } +/* Shutup the voice, except if force_enqueue_next is set. */ +int shutup(void) +{ + if (!force_enqueue_next) + return do_shutup(); + return 0; +} /* schedule a clip, at the end or discard the existing queue */ static int queue_clip(unsigned char* buf, long size, bool enqueue) @@ -395,6 +410,9 @@ static int queue_clip(unsigned char* buf if (!enqueue) shutup(); /* cut off all the pending stuff */ + /* Something is being enqueued, force_enqueue_next override is no + longer in effect. */ + force_enqueue_next = false; if (!size) return 0; /* safety check */ @@ -617,6 +635,26 @@ int talk_id(long id, bool enqueue) return 0; } +/* Speaks zero or more IDs (from an array). */ +int talk_idarray(long *ids, bool enqueue) +{ + int r; + if(!ids) + return 0; + while(*ids != TALK_FINAL_ID) + { + if((r = talk_id(*ids++, enqueue)) <0) + return r; + enqueue = true; + } + return 0; +} + +/* Make sure the current utterance is not interrupted by the next one. */ +void talk_force_enqueue_next(void) +{ + force_enqueue_next = true; +} /* play a thumbnail from file */ int talk_file(const char* filename, bool enqueue) Index: apps/filetree.c =================================================================== --- apps/filetree.c.orig +++ apps/filetree.c @@ -342,21 +342,21 @@ int ft_enter(struct tree_context* c) switch ( file->attr & FILE_ATTR_MASK ) { case FILE_ATTR_M3U: if (global_settings.party_mode) { - gui_syncsplash(HZ, str(LANG_PARTY_MODE)); + gui_syncsplash(HZ, ID2P(LANG_PARTY_MODE)); break; } if (bookmark_autoload(buf)) break; - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); /* about to create a new current playlist... allow user to cancel the operation */ if (global_settings.warnon_erase_dynplaylist && playlist_modified(NULL)) { - char *lines[]={str(LANG_WARN_ERASEDYNPLAYLIST_PROMPT)}; + char *lines[]={ID2P(LANG_WARN_ERASEDYNPLAYLIST_PROMPT)}; struct text_message message={lines, 1}; if(gui_syncyesno_run(&message, NULL, NULL) != YESNO_YES) @@ -377,7 +377,7 @@ int ft_enter(struct tree_context* c) if (bookmark_autoload(c->currdir)) break; - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); /* about to create a new current playlist... allow user to cancel the operation */ @@ -385,7 +385,7 @@ int ft_enter(struct tree_context* c) !global_settings.party_mode && playlist_modified(NULL)) { - char *lines[]={str(LANG_WARN_ERASEDYNPLAYLIST_PROMPT)}; + char *lines[]={ID2P(LANG_WARN_ERASEDYNPLAYLIST_PROMPT)}; struct text_message message={lines, 1}; if(gui_syncyesno_run(&message, NULL, NULL) != YESNO_YES) @@ -396,7 +396,7 @@ int ft_enter(struct tree_context* c) { playlist_insert_track(NULL, buf, PLAYLIST_INSERT_LAST, true, true); - gui_syncsplash(HZ, str(LANG_QUEUE_LAST)); + gui_syncsplash(HZ, ID2P(LANG_QUEUE_LAST)); } else if (playlist_create(c->currdir, NULL) != -1) { @@ -421,7 +421,7 @@ int ft_enter(struct tree_context* c) /* fmr preset file */ case FILE_ATTR_FMR: - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); /* Preset inside the default folder. */ if(!strncasecmp(FMPRESET_PATH, buf, strlen(FMPRESET_PATH))) @@ -448,7 +448,7 @@ int ft_enter(struct tree_context* c) /* wps config file */ case FILE_ATTR_WPS: - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); #if LCD_DEPTH > 1 unload_wps_backdrop(); #endif @@ -460,7 +460,7 @@ int ft_enter(struct tree_context* c) #if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1) /* remote-wps config file */ case FILE_ATTR_RWPS: - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 unload_remote_wps_backdrop(); #endif @@ -471,39 +471,39 @@ int ft_enter(struct tree_context* c) #endif case FILE_ATTR_CFG: - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); if (!settings_load_config(buf,true)) break; - gui_syncsplash(HZ, str(LANG_SETTINGS_LOADED)); + gui_syncsplash(HZ, ID2P(LANG_SETTINGS_LOADED)); break; case FILE_ATTR_BMARK: - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); bookmark_load(buf, false); reload_dir = true; break; case FILE_ATTR_LNG: - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); if(!lang_load(buf)) { set_file(buf, (char *)global_settings.lang_file, MAX_FILENAME); talk_init(); /* use voice of same language */ - gui_syncsplash(HZ, str(LANG_LANGUAGE_LOADED)); + gui_syncsplash(HZ, ID2P(LANG_LANGUAGE_LOADED)); } break; #ifdef HAVE_LCD_BITMAP case FILE_ATTR_FONT: - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); font_load(buf); set_file(buf, (char *)global_settings.font_file, MAX_FILENAME); break; case FILE_ATTR_KBD: - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); if (!load_kbd(buf)) - gui_syncsplash(HZ, str(LANG_KEYBOARD_LOADED)); + gui_syncsplash(HZ, ID2P(LANG_KEYBOARD_LOADED)); set_file(buf, (char *)global_settings.kbd_file, MAX_FILENAME); break; #endif @@ -511,7 +511,7 @@ int ft_enter(struct tree_context* c) #ifndef SIMULATOR /* firmware file */ case FILE_ATTR_MOD: - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); rolo_load(buf); break; #endif @@ -519,11 +519,11 @@ int ft_enter(struct tree_context* c) /* plugin file */ case FILE_ATTR_ROCK: if (global_settings.party_mode) { - gui_syncsplash(HZ, str(LANG_PARTY_MODE)); + gui_syncsplash(HZ, ID2P(LANG_PARTY_MODE)); break; } - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); if (plugin_load(buf,NULL) == PLUGIN_USB_CONNECTED) { @@ -545,7 +545,7 @@ int ft_enter(struct tree_context* c) char* plugin; if (global_settings.party_mode) { - gui_syncsplash(HZ, str(LANG_PARTY_MODE)); + gui_syncsplash(HZ, ID2P(LANG_PARTY_MODE)); break; } Index: apps/talk.h =================================================================== --- apps/talk.h.orig +++ apps/talk.h @@ -66,6 +66,9 @@ bool talk_voice_required(void); /* retur int talk_get_bufsize(void); /* get the loaded voice file size */ /* talk_buffer_steal - on SWCODEC, for use by buffer functions only */ int talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */ +int shutup(void); /* Interrupt voice, as when enqueue is false */ +int do_shutup(void); /* kill voice unconditionally */ +bool is_voice_queued(void); /* Are there more voice clips to be spoken? */ int talk_id(long id, bool enqueue); /* play a voice ID from voicefont */ int talk_file(const char* filename, bool enqueue); /* play a thumbnail from file */ int talk_number(long n, bool enqueue); /* say a number */ @@ -74,4 +77,37 @@ int talk_spell(const char* spell, bool e bool talk_menus_enabled(void); /* returns true if menus should be voiced */ void talk_disable_menus(void); /* disable voice menus (temporarily, not persisted) */ void talk_enable_menus(void); /* re-enable voice menus */ + +/* Enqueue next utterance even if enqueue parameter is false: don't + interrupt the current utterance. */ +void talk_force_enqueue_next(void); + +/* speaks one or more IDs (from an array)). */ +int talk_idarray(long *idarray, bool enqueue); +/* This (otherwise invalid) ID signals the end of the array. */ +#define TALK_FINAL_ID LANG_LAST_INDEX_IN_ARRAY +/* This makes an initializer for the array of IDs and takes care to + put the final sentinel element at the end. */ +#define TALK_IDARRAY(ids...) ((long[]){ids,TALK_FINAL_ID}) +/* And this handy macro makes it look like a variadic function. */ +#define talk_ids(enqueue, ids...) talk_idarray(TALK_IDARRAY(ids), enqueue) +/* This version talks only if talking menus are enabled, and does not + enqueue the initial id. */ +#define cond_talk_ids(ids...) do { \ + if (talk_menus_enabled()) \ + talk_ids(false, ids); \ + } while(0) +/* And a version that takes the array parameter... */ +#define cond_talk_idarray(idarray) do { \ + if (talk_menus_enabled() \ + talk_idarray(idarray, false); \ + } while(0) +/* Convenience macro to conditionally speak something and not have + it interrupted. */ +#define cond_talk_ids_fq(ids...) do { \ + if (talk_menus_enabled()) { \ + talk_ids(false, ids); \ + talk_force_enqueue_next(); \ + } \ + }while(0) #endif /* __TALK_H__ */ Index: apps/playback.c =================================================================== --- apps/playback.c.orig +++ apps/playback.c @@ -957,6 +957,24 @@ static void voice_stop(void) pcmbuf_play_stop(); #endif } /* voice_stop */ + +/* Is voice still speaking */ +/* Unfortunately only reliable when music is not also playing. */ +bool is_voice_speaking(void) +{ + return is_voice_queued() + || voice_is_playing + || (!playing && pcm_is_playing()); +} + +/* Wait for voice to finish speaking. */ +/* Also only reliable when music is not also playing. */ +void voice_wait(void) +{ + while (is_voice_speaking()) + sleep(HZ/10); +} + #endif /* PLAYBACK_VOICE */ static void set_filebuf_watermark(int seconds) Index: apps/playback.h =================================================================== --- apps/playback.h.orig +++ apps/playback.h @@ -63,6 +63,8 @@ void audio_set_track_buffer_event(void ( bool last_track)); void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3, bool last_track)); +bool is_voice_speaking(void); +void voice_wait(void); #if CONFIG_CODEC == SWCODEC /* This #ifdef is better here than gui/gwps.c */ extern void audio_next_dir(void); Index: apps/plugin.c =================================================================== --- apps/plugin.c.orig +++ apps/plugin.c @@ -537,7 +537,7 @@ int plugin_load(const char* plugin, void plugin_loaded = false; } - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); strcpy(current_plugin,p); #ifdef SIMULATOR Index: apps/filetypes.c =================================================================== --- apps/filetypes.c.orig +++ apps/filetypes.c @@ -300,7 +300,7 @@ static void read_config(char* config_fil { if (filetype_count >= MAX_FILETYPES) { - gui_syncsplash(HZ, str(LANG_FILETYPES_FULL)); + gui_syncsplash(HZ, ID2P(LANG_FILETYPES_FULL)); break; } rm_whitespaces(line); Index: apps/bookmark.c =================================================================== --- apps/bookmark.c.orig +++ apps/bookmark.c @@ -186,10 +186,10 @@ bool bookmark_autobookmark(void) return write_bookmark(false); } #ifdef HAVE_LCD_BITMAP - unsigned char *lines[]={str(LANG_AUTO_BOOKMARK_QUERY)}; + unsigned char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)}; struct text_message message={(char **)lines, 1}; #else - unsigned char *lines[]={str(LANG_AUTO_BOOKMARK_QUERY), + unsigned char *lines[]={D2P(LANG_AUTO_BOOKMARK_QUERY), str(LANG_CONFIRM_WITH_BUTTON)}; struct text_message message={(char **)lines, 2}; #endif @@ -244,8 +244,8 @@ static bool write_bookmark(bool create_b } } - gui_syncsplash(HZ, str(success ? LANG_BOOKMARK_CREATE_SUCCESS - : LANG_BOOKMARK_CREATE_FAILURE)); + gui_syncsplash(HZ, success ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS) + : ID2P(LANG_BOOKMARK_CREATE_FAILURE)); return true; } @@ -634,7 +634,7 @@ static char* select_bookmark(const char* if (bookmarks->total_count < 1) { /* No more bookmarks, delete file and exit */ - gui_syncsplash(HZ, str(LANG_BOOKMARK_LOAD_EMPTY)); + gui_syncsplash(HZ, ID2P(LANG_BOOKMARK_LOAD_EMPTY)); remove(bookmark_file_name); return NULL; } Index: apps/root_menu.c =================================================================== --- apps/root_menu.c.orig +++ apps/root_menu.c @@ -129,7 +129,7 @@ static int browser(void* param) /* Maybe just needs to reboot due to delayed commit */ if (stat->commit_delayed) { - gui_syncsplash(HZ*2, str(LANG_PLEASE_REBOOT)); + gui_syncsplash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); break; } @@ -146,7 +146,7 @@ static int browser(void* param) { /* Prompt the user */ reinit_attempted = true; - char *lines[]={str(LANG_TAGCACHE_BUSY), str(LANG_TAGCACHE_FORCE_UPDATE)}; + char *lines[]={ID2P(LANG_TAGCACHE_BUSY), ID2P(LANG_TAGCACHE_FORCE_UPDATE)}; struct text_message message={lines, 2}; if(gui_syncyesno_run(&message, NULL, NULL) == YESNO_NO) break; @@ -159,6 +159,24 @@ static int browser(void* param) } /* Display building progress */ + static long talked_tick = 0; + if(talk_menus_enabled() && + (talked_tick == 0 + || TIME_AFTER(current_tick, talked_tick+7*HZ))) + { + talked_tick = current_tick; + if (stat->commit_step > 0) + { + talk_id(LANG_TAGCACHE_INIT, false); + talk_number(stat->commit_step, true); + talk_id(VOICE_OF, true); + talk_number(tagcache_get_max_commit_step(), true); + } else if(stat->processed_entries) + { + talk_number(stat->processed_entries, false); + talk_id(LANG_BUILDING_DATABASE, true); + } + } if (stat->commit_step > 0) { gui_syncsplash(0, "%s [%d/%d]", @@ -236,7 +254,7 @@ static int wpsscrn(void* param) } else { - gui_syncsplash(HZ*2, str(LANG_NOTHING_TO_RESUME)); + gui_syncsplash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); } #if LCD_DEPTH > 1 show_main_backdrop(); Index: apps/misc.c =================================================================== --- apps/misc.c.orig +++ apps/misc.c @@ -62,6 +62,7 @@ #include "bookmark.h" #include "misc.h" +#include "playback.h" #ifdef BOOTFILE #if !defined(USB_NONE) && !defined(USB_IPODSTYLE) @@ -597,6 +598,7 @@ static void system_restore(void) static bool clean_shutdown(void (*callback)(void *), void *parameter) { + long msg_id = -1; #ifdef SIMULATOR (void)callback; (void)parameter; @@ -625,19 +627,23 @@ static bool clean_shutdown(void (*callba if (!tagcache_prepare_shutdown()) { cancel_shutdown(); - gui_syncsplash(HZ, str(LANG_TAGCACHE_BUSY)); + gui_syncsplash(HZ, ID2P(LANG_TAGCACHE_BUSY)); return false; } #endif if (battery_level() > 10) gui_syncsplash(0, str(LANG_SHUTTINGDOWN)); else + { + msg_id = LANG_WARNING_BATTERY_LOW; gui_syncsplash(0, "%s %s", str(LANG_WARNING_BATTERY_LOW), str(LANG_SHUTTINGDOWN)); + } } else { + msg_id = LANG_WARNING_BATTERY_EMPTY; gui_syncsplash(0, "%s %s", str(LANG_WARNING_BATTERY_EMPTY), str(LANG_SHUTTINGDOWN)); @@ -675,6 +681,19 @@ static bool clean_shutdown(void (*callba #if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC audio_close_recording(); #endif + + if(talk_menus_enabled()) + { + bool enqueue = false; + if(msg_id != -1) + { + talk_id(msg_id, enqueue); + enqueue = true; + } + talk_id(LANG_SHUTTINGDOWN, enqueue); + voice_wait(); + } + system_flush(); #ifdef HAVE_EEPROM_SETTINGS if (firmware_settings.initialized) @@ -974,8 +993,8 @@ void check_bootfile(bool do_rolo) if((entry->wrtdate != wrtdate) || (entry->wrttime != wrttime)) { - char *lines[] = { str(LANG_BOOT_CHANGED), - str(LANG_REBOOT_NOW) }; + char *lines[] = { ID2P(LANG_BOOT_CHANGED), + ID2P(LANG_REBOOT_NOW) }; struct text_message message={ lines, 2 }; button_clear_queue(); /* Empty the keyboard buffer */ if(gui_syncyesno_run(&message, NULL, NULL) == YESNO_YES) Index: apps/tagtree.c =================================================================== --- apps/tagtree.c.orig +++ apps/tagtree.c @@ -690,7 +690,7 @@ bool tagtree_export(void) gui_syncsplash(0, str(LANG_CREATING)); if (!tagcache_create_changelog(&tcs)) { - gui_syncsplash(HZ*2, str(LANG_FAILED)); + gui_syncsplash(HZ*2, ID2P(LANG_FAILED)); } return false; @@ -698,10 +698,10 @@ bool tagtree_export(void) bool tagtree_import(void) { - gui_syncsplash(0, str(LANG_WAIT)); + gui_syncsplash(0, ID2P(LANG_WAIT)); if (!tagcache_import_changelog()) { - gui_syncsplash(HZ*2, str(LANG_FAILED)); + gui_syncsplash(HZ*2, ID2P(LANG_FAILED)); } return false; @@ -1228,7 +1228,7 @@ static int retrieve_entries(struct tree_ if (!sort && (sort_inverse || sort_limit)) { - gui_syncsplash(HZ*4, str(LANG_SHOWDIR_BUFFER_FULL), total_count); + gui_syncsplash(HZ*4, ID2P(LANG_SHOWDIR_BUFFER_FULL), total_count); logf("Too small dir buffer"); return 0; } @@ -1419,7 +1419,7 @@ int tagtree_enter(struct tree_context* c !global_settings.party_mode && playlist_modified(NULL)) { - char *lines[]={str(LANG_WARN_ERASEDYNPLAYLIST_PROMPT)}; + char *lines[]={ID2P(LANG_WARN_ERASEDYNPLAYLIST_PROMPT)}; struct text_message message={lines, 1}; if (gui_syncyesno_run(&message, NULL, NULL) != YESNO_YES) @@ -1496,7 +1496,7 @@ static bool insert_all_playlist(struct t cpu_boost(true); if (!tagcache_search(&tcs, tag_filename)) { - gui_syncsplash(HZ, str(LANG_TAGCACHE_BUSY)); + gui_syncsplash(HZ, ID2P(LANG_TAGCACHE_BUSY)); cpu_boost(false); return false; } @@ -1599,12 +1599,12 @@ bool tagtree_insert_selection_playlist(i } if (tc->filesindir <= 0) - gui_syncsplash(HZ, str(LANG_END_PLAYLIST)); + gui_syncsplash(HZ, ID2P(LANG_END_PLAYLIST)); else { logf("insert_all_playlist"); if (!insert_all_playlist(tc, position, queue)) - gui_syncsplash(HZ*2, str(LANG_FAILED)); + gui_syncsplash(HZ*2, ID2P(LANG_FAILED)); } /* Finally return the dirlevel to its original value. */ Index: apps/gui/gwps-common.c =================================================================== --- apps/gui/gwps-common.c.orig +++ apps/gui/gwps-common.c @@ -294,7 +294,7 @@ bool gui_wps_display(void) #ifdef HAVE_LCD_BITMAP gui_syncstatusbar_draw(&statusbars, true); #endif - gui_syncsplash(HZ, str(LANG_END_PLAYLIST)); + gui_syncsplash(HZ, ID2P(LANG_END_PLAYLIST)); return true; } else Index: apps/main.c =================================================================== --- apps/main.c.orig +++ apps/main.c @@ -217,6 +217,17 @@ static void init_tagcache(void) if (ret > 0) { + static long talked_tick = 0; + if(talk_menus_enabled() + && (talked_tick == 0 + || TIME_AFTER(current_tick, talked_tick+7*HZ))) + { + talked_tick = current_tick; + talk_id(LANG_TAGCACHE_INIT, false); + talk_number(ret, true); + talk_id(VOICE_OF, true); + talk_number(tagcache_get_max_commit_step(), true); + } #ifdef HAVE_LCD_BITMAP gui_syncsplash(0, "%s [%d/%d]", str(LANG_TAGCACHE_INIT), ret, @@ -474,7 +485,7 @@ static void init(void) if (button_hold()) #endif { - gui_syncsplash(HZ*2, str(LANG_RESET_DONE_CLEAR)); + gui_syncsplash(HZ*2, ID2P(LANG_RESET_DONE_CLEAR)); settings_reset(); } else Index: apps/menus/main_menu.c =================================================================== --- apps/menus/main_menu.c.orig +++ apps/menus/main_menu.c @@ -67,12 +67,12 @@ int browse_folder(void *param) static int reset_settings(void) { - unsigned char *lines[]={str(LANG_RESET_ASK)}; + unsigned char *lines[]={ID2P(LANG_RESET_ASK)}; unsigned char *yes_lines[]={ str(LANG_SETTINGS), - str(LANG_RESET_DONE_CLEAR) + ID2P(LANG_RESET_DONE_CLEAR) }; - unsigned char *no_lines[]={yes_lines[0], str(LANG_CANCEL)}; + unsigned char *no_lines[]={yes_lines[0], ID2P(LANG_CANCEL)}; struct text_message message={(char **)lines, 1}; struct text_message yes_message={(char **)yes_lines, 2}; struct text_message no_message={(char **)no_lines, 2}; Index: apps/menus/playback_menu.c =================================================================== --- apps/menus/playback_menu.c.orig +++ apps/menus/playback_menu.c @@ -138,7 +138,7 @@ int audioscrobbler_callback(int action,c { case ACTION_EXIT_MENUITEM: /* on exit */ if (!scrobbler_is_enabled() && global_settings.audioscrobbler) - gui_syncsplash(HZ*2, str(LANG_PLEASE_REBOOT)); + gui_syncsplash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); if(scrobbler_is_enabled() && !global_settings.audioscrobbler) scrobbler_shutdown(); @@ -156,7 +156,7 @@ int cuesheet_callback(int action,const s { case ACTION_EXIT_MENUITEM: /* on exit */ if (!cuesheet_is_enabled() && global_settings.cuesheet) - gui_syncsplash(HZ*2, str(LANG_PLEASE_REBOOT)); + gui_syncsplash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); break; } return action; Index: apps/recorder/radio.c =================================================================== --- apps/recorder/radio.c.orig +++ apps/recorder/radio.c @@ -487,7 +487,7 @@ int radio_screen(void) radio_start(); #endif - if(num_presets < 1 && yesno_pop(str(LANG_FM_FIRST_AUTOSCAN))) + if(num_presets < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN))) scan_presets(); curr_preset = find_preset(curr_freq); @@ -551,7 +551,7 @@ int radio_screen(void) done = true; if(presets_changed) { - if(yesno_pop(str(LANG_FM_SAVE_CHANGES))) + if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES))) { if(filepreset[0] == '\0') save_preset_list(); @@ -606,7 +606,7 @@ int radio_screen(void) ret_val = GO_TO_ROOT; if(presets_changed) { - if(yesno_pop(str(LANG_FM_SAVE_CHANGES))) + if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES))) { if(filepreset[0] == '\0') save_preset_list(); @@ -690,7 +690,7 @@ int radio_screen(void) case ACTION_FM_PRESET: if(num_presets < 1) { - gui_syncsplash(HZ, str(LANG_FM_NO_PRESETS)); + gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS)); update_screen = true; FOR_NB_SCREENS(i) { @@ -996,7 +996,7 @@ static void radio_save_presets(void) } else { - gui_syncsplash(HZ, str(LANG_FM_PRESET_SAVE_FAILED)); + gui_syncsplash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED)); } } @@ -1082,7 +1082,7 @@ static int radio_add_preset(void) } else { - gui_syncsplash(HZ, str(LANG_FM_NO_FREE_PRESETS)); + gui_syncsplash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS)); } return true; } @@ -1170,7 +1170,7 @@ static int save_preset_list(void) if((!p1) || (len > MAX_FILENAME) || (len == 0)) { /* no slash, too long or too short */ - gui_syncsplash(HZ, str(LANG_INVALID_FILENAME)); + gui_syncsplash(HZ, ID2P(LANG_INVALID_FILENAME)); } else { @@ -1190,7 +1190,7 @@ static int save_preset_list(void) } } else - gui_syncsplash(HZ, str(LANG_FM_NO_PRESETS)); + gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS)); return true; } @@ -1339,7 +1339,7 @@ static int scan_presets(void) bool do_scan = true; if(num_presets > 0) /* Do that to avoid 2 questions. */ - do_scan = yesno_pop(str(LANG_FM_CLEAR_PRESETS)); + do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS)); if(do_scan) { Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang.orig +++ apps/lang/english.lang @@ -188,25 +188,25 @@ id: LANG_WAIT desc: general please wait splash user: *: "Loading..." *: "Loading..." - *: "" + *: "Loading" id: LANG_LOADING_PERCENT desc: splash number of percents loaded user: *: "Loading... %d%% done (%s)" *: "Loading... %d%% done (%s)" @@ -216,39 +216,39 @@ id: LANG_SCANNING_DISK desc: when booting up and rebuilding the cache and calculating free space user: *: "Scanning disk..." *: "Scanning disk..." - *: "" + *: "Scanning disk" id: LANG_SHUTTINGDOWN desc: in main menu user: *: "Shutting down..." *: "Shutting down..." - *: "" + *: "Shutting down" id: LANG_PLAYLIST_SHUFFLE desc: displayed on screen while shuffling a playlist user: *: "Shuffling..." *: "Shuffling..." @@ -258,39 +258,39 @@ id: LANG_CANCEL desc: Visual confirmation of canceling a changed setting user: *: "Cancelled" *: "Cancelled" - *: "" + *: "Cancelled" id: LANG_FAILED desc: Something failed. To be appended after actions user: *: "Failed" *: "Failed" - *: "" + *: "Failed" id: LANG_CHANNELS desc: in sound_settings user: *: "Channels" *: "Channels" @@ -300,25 +300,25 @@ id: LANG_RESET_ASK desc: confirm to reset settings user: *: "Are You Sure?" *: "Are You Sure?" - *: "" + *: "Are You Sure?" id: LANG_CONFIRM_WITH_BUTTON desc: Generic string to use to confirm user: *: "PLAY = Yes" h100,h120,h300: "NAVI = Yes" ipod*,x5,m5,gigabeatf,e200,h10,h10_5gb: "SELECT = Yes" player: "(PLAY/STOP)" @@ -623,67 +623,67 @@ id: LANG_AUTO_BOOKMARK_QUERY desc: prompt for user to decide to create an bookmark user: *: "Create a Bookmark?" *: "Create a Bookmark?" - *: "" + *: "Create a Bookmark?" id: LANG_BOOKMARK_CREATE_SUCCESS desc: Indicates bookmark was successfully created user: *: "Bookmark Created" *: "Bookmark Created" - *: "" + *: "Bookmark Created" id: LANG_BOOKMARK_CREATE_FAILURE desc: Indicates bookmark was not created user: *: "Bookmark Failed!" *: "Bookmark Failed!" - *: "" + *: "Bookmark Failed!" id: LANG_BOOKMARK_LOAD_EMPTY desc: Indicates bookmark was empty user: *: "Bookmark Empty" *: "Bookmark Empty" - *: "" + *: "Bookmark Empty" id: LANG_SOUND_SETTINGS desc: in the main menu user: *: "Sound Settings" *: "Sound Settings" @@ -2421,25 +2421,29 @@ ipod*: "Building database... %d found (PLAY/PAUSE to return)" x5,m5: "Building database... %d found (Long PLAY to return)" h10,h10_5gb,e200: "Building database... %d found (PREV to return)" *: "Building database... %d found (OFF to return)" h100,h120,h300: "Building database... %d found (STOP to return)" ipod*: "Building database... %d found (PLAY/PAUSE to return)" x5,m5: "Building database... %d found (Long PLAY to return)" h10,h10_5gb,e200: "Building database... %d found (PREV to return)" - *: "" + *: "entries found for database" + h100,h120,h300: "entries found for database" + ipod*: "entries found for database" + x5,m5: "entries found for database" + h10,h10_5gb,e200: "entries found for database" id: LANG_TAGCACHE_RAM desc: in tag cache settings user: *: none tc_ramcache: "Load to RAM" *: none @@ -2536,39 +2540,39 @@ id: LANG_TAGCACHE_FORCE_UPDATE_SPLASH desc: in tag cache settings user: *: "Updating in background" *: "Updating in background" - *: "" + *: "Updating in background" id: LANG_TAGCACHE_INIT desc: while initializing tagcache on boot user: *: "Committing database" *: "Committing database" - *: "" + *: "Committing database" id: LANG_TAGCACHE_BUSY desc: when trying to shutdown and tagcache is committing *: "Database is not ready" *: "Database is not ready" @@ -4884,25 +4888,25 @@ id: LANG_LANGUAGE_LOADED desc: shown when a language has been loaded from the dir browser user: *: "New Language" *: "New Language" - *: "" + *: "New Language" id: LANG_VOICE desc: root of voice menu user: *: "Voice" *: "Voice" @@ -5052,25 +5056,25 @@ id: LANG_SETTINGS_LOADED desc: Feedback shown when a .cfg file is loaded user: *: "Settings Loaded" *: "Settings Loaded" - *: "" + *: "Settings Loaded" id: LANG_RESET desc: in system_settings_menu() user: *: "Reset Settings" *: "Reset Settings" @@ -5080,25 +5084,25 @@ id: LANG_RESET_DONE_CLEAR desc: visual confirmation after settings reset user: *: "Cleared" *: "Cleared" - *: "" + *: "Settings Cleared" id: LANG_SAVE_SETTINGS desc: in system_settings_menu() user: *: "Write .cfg file" *: "Write .cfg file" @@ -5108,25 +5112,25 @@ id: LANG_SETTINGS_SAVED desc: Feedback shown when a .cfg file is saved user: *: "Settings Saved" *: "Settings Saved" - *: "" + *: "Settings Saved" id: LANG_SAVE_THEME desc: save a theme file user: *: "Save Theme Settings" *: "Save Theme Settings" @@ -5204,25 +5208,25 @@ desc: error when preset list is empty user: *: none radio: "No presets" *: none radio: "No presets" *: none - radio: "" + radio: "No presets" id: LANG_FM_ADD_PRESET desc: in radio menu user: *: none radio: "Add Preset" *: none @@ -5272,42 +5276,42 @@ desc: in radio screen user: *: none radio: "Preset Save Failed" *: none radio: "Preset Save Failed" *: none - radio: "" + radio: "Preset Save Failed" id: LANG_FM_NO_FREE_PRESETS desc: in radio screen user: *: none radio: "The Preset List is Full" *: none radio: "The Preset List is Full" *: none - radio: "" + radio: "The Preset List is Full" id: LANG_BUTTONBAR_MENU desc: in button bar user: *: none radio_screen_button_bar: "Menu" *: none @@ -5459,25 +5463,25 @@ desc: confirmation if presets can be cleared user: *: none radio: "Clear Current Presets?" *: none radio: "Clear Current Presets?" *: none - radio: "" + radio: "Clear Current Presets?" id: LANG_FM_SCANNING desc: during auto scan user: *: none radio: "Scanning %d.%02d MHz" *: none @@ -5595,42 +5599,42 @@ desc: When you run the radio without an fmr file in settings user: *: none radio: "No settings found. Autoscan?" *: none radio: "No settings found. Autoscan?" *: none - radio: "" + radio: "No settings found. Autoscan?" id: LANG_FM_SAVE_CHANGES desc: When you try to exit radio to confirm save user: *: none radio: "Save Changes?" *: none radio: "Save Changes?" *: none - radio: "" + radio: "Save Changes?" id: LANG_FM_REGION desc: fm tuner region setting *: none radio: "Region" *: none radio: "Region" @@ -6757,25 +6761,25 @@ id: LANG_PLAYLIST_SAVE_COUNT desc: splash number of tracks saved user: *: "Saved %d tracks (%s)" *: "Saved %d tracks (%s)" - *: "" + *: "tracks saved" id: LANG_CATALOG desc: in onplay menu user: *: "Playlist Catalog" *: "Playlist Catalog" @@ -6799,25 +6803,25 @@ id: LANG_RECURSE_DIRECTORY_QUESTION desc: Asked from onplay screen user: *: "Recursively?" *: "Recursively?" - *: "" + *: "Recursively?" id: LANG_WARN_ERASEDYNPLAYLIST_MENU desc: in playlist options menu, option to warn when erasing dynamic playlist user: *: "Warn When Erasing Dynamic Playlist" *: "Warn When Erasing Dynamic Playlist" @@ -6827,25 +6831,25 @@ id: LANG_WARN_ERASEDYNPLAYLIST_PROMPT desc: prompt shown when about to erase a modified dynamic playlist user: *: "Erase dynamic playlist?" *: "Erase dynamic playlist?" - *: "" + *: "Erase dynamic playlist?" id: LANG_SHUTDOWN desc: in main menu user: *: none soft_shutdown: "Shut down" *: none @@ -7212,39 +7216,39 @@ id: LANG_PLAYLIST_INSERT_COUNT desc: splash number of tracks inserted user: *: "Inserted %d tracks (%s)" *: "Inserted %d tracks (%s)" - *: "" + *: "tracks inserted" id: LANG_PLAYLIST_QUEUE_COUNT desc: splash number of tracks queued user: *: "Queued %d tracks (%s)" *: "Queued %d tracks (%s)" - *: "" + *: "tracks queued" id: LANG_VIEW desc: in on+play menu user: *: "View" *: "View" @@ -7772,25 +7776,25 @@ id: LANG_REALLY_OVERWRITE desc: The verb/action Paste user: *: "File/directory exists. Overwrite?" *: "File/directory exists. Overwrite?" - *: "" + *: "File or directory exists. Overwrite?" id: LANG_DELETE desc: The verb/action Delete user: *: "Delete" *: "Delete" @@ -7814,25 +7818,25 @@ id: LANG_REALLY_DELETE desc: Really Delete? user: *: "Delete?" *: "Delete?" - *: "" + *: "Really delete?" id: LANG_COPYING desc: user: *: "Copying..." *: "Copying..." @@ -7870,25 +7874,25 @@ id: LANG_DELETED desc: A file has beed deleted user: *: "Deleted" *: "Deleted" - *: "" + *: "Deleted" id: LANG_SET_AS_BACKDROP desc: text for onplay menu entry user: *: none lcd_non-mono: "Set As Backdrop" *: none @@ -8076,41 +8080,42 @@ id: LANG_PLAYLIST_BUFFER_FULL desc: in playlist.indices() when playlist is full user: *: "Playlist Buffer Full" *: "Playlist Buffer Full" - *: "" + *: "Playlist Buffer Full" id: LANG_END_PLAYLIST desc: when playlist has finished user: *: "End of Song List" player: "End of List" *: "End of Song List" player: "End of List" - *: "" + *: "End of Song List" + player: "End of List" id: LANG_CREATING desc: Screen feedback during playlist creation user: *: "Creating" *: "Creating" @@ -8120,109 +8125,109 @@ id: LANG_NOTHING_TO_RESUME desc: Error message displayed when resume button pressed but no playlist user: *: "Nothing to resume" *: "Nothing to resume" - *: "" + *: "Nothing to resume" id: LANG_PLAYLIST_CONTROL_UPDATE_ERROR desc: Playlist error user: *: "Error updating playlist control file" *: "Error updating playlist control file" - *: "" + *: "Error updating playlist control file" id: LANG_PLAYLIST_ACCESS_ERROR desc: Playlist error user: *: "Error accessing playlist file" *: "Error accessing playlist file" - *: "" + *: "Error accessing playlist file" id: LANG_PLAYLIST_CONTROL_ACCESS_ERROR desc: Playlist error user: *: "Error accessing playlist control file" *: "Error accessing playlist control file" - *: "" + *: "Error accessing playlist control file" id: LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR desc: Playlist error user: *: "Error accessing directory" *: "Error accessing directory" - *: "" + *: "Error accessing directory" id: LANG_PLAYLIST_CONTROL_INVALID desc: Playlist resume error user: *: "Playlist control file is invalid" *: "Playlist control file is invalid" - *: "" + *: "Playlist control file is invalid" id: LANG_SETTINGS_SAVE_FAILED desc: displayed if save settings has failed user: *: "Save Failed" *: "Save Failed" - *: "" + *: "Save Failed" id: LANG_SETTINGS_PARTITION desc: if save settings has failed user: *: "No partition?" player: "Partition?" *: "No partition?" @@ -8410,39 +8415,39 @@ id: LANG_BOOT_CHANGED desc: File browser discovered the boot file was changed user: *: "Boot changed" *: "Boot changed" - *: "" + *: "Boot changed" id: LANG_REBOOT_NOW desc: Do you want to reboot? user: *: "Reboot now?" *: "Reboot now?" - *: "" + *: "Reboot now?" id: LANG_OFF_ABORT desc: Used on recorder models user: *: "OFF to abort" player,h100,h120,h300: "STOP to abort" ipod*: "PLAY/PAUSE to abort" x5,m5: "Long PLAY to abort" h10,h10_5gb,e200: "PREV to abort" @@ -8460,39 +8465,39 @@ id: LANG_NO_FILES desc: in settings_menu user: *: "No files" *: "No files" - *: "" + *: "No files" id: LANG_KEYBOARD_LOADED desc: shown when a keyboard has been loaded from the dir browser user: *: "New Keyboard" *: "New Keyboard" - *: "" + *: "New Keyboard" id: LANG_PLUGIN_CANT_OPEN desc: Plugin open error message user: *: "Can't open %s" *: "Can't open %s" @@ -8558,53 +8563,53 @@ id: LANG_FILETYPES_EXTENSION_FULL desc: Extension array full user: *: "Extension array full" *: "Extension array full" - *: "" + *: "Extension array full" id: LANG_FILETYPES_FULL desc: Filetype array full user: *: "Filetype array full" *: "Filetype array full" - *: "" + *: "Filetype array full" id: LANG_SHOWDIR_BUFFER_FULL desc: in showdir(), displayed on screen when you reach buffer limit user: *: "Dir Buffer is Full!" *: "Dir Buffer is Full!" - *: "" + *: "Directory Buffer is Full!" id: LANG_INVALID_FILENAME desc: "invalid filename entered" error message user: *: "Invalid Filename!" *: "Invalid Filename!" @@ -8614,25 +8619,25 @@ id: LANG_FILETYPES_PLUGIN_NAME_LONG desc: Viewer plugin name too long user: *: "Plugin name too long" *: "Plugin name too long" - *: "" + *: "Plugin name too long" id: LANG_FILETYPES_STRING_BUFFER_FULL desc: Filetype string buffer full user: *: "Filetype string buffer full" *: "Filetype string buffer full" @@ -8659,25 +8664,25 @@ id: LANG_PLEASE_REBOOT desc: when activating an option that requires a reboot user: *: "Please reboot to enable" *: "Please reboot to enable" - *: "" + *: "Please reboot to enable" id: LANG_BATTERY_CHARGE desc: tells that the battery is charging, instead of battery level user: *: none charging: "Battery: Charging" *: none @@ -8725,39 +8730,39 @@ id: LANG_WARNING_BATTERY_LOW desc: general warning user: *: "WARNING! Low Battery!" *: "WARNING! Low Battery!" - *: "" + *: "WARNING! Low Battery!" id: LANG_WARNING_BATTERY_EMPTY desc: general warning user: *: "Battery empty! RECHARGE!" *: "Battery empty! RECHARGE!" - *: "" + *: "Battery empty! RECHARGE!" id: LANG_BYTE desc: a unit postfix user: *: "B" *: "B" @@ -9863,36 +9868,36 @@ user: *: "" *: "" *: "Z" - id: VOICE_DOT - desc: spoken only, for spelling - user: - - *: "" - - - *: "" - - - *: "dot" - +id: VOICE_DOT +desc: spoken only, for spelling +user: + +*: "" + + +*: "" + + +*: "dot" + id: VOICE_PAUSE desc: spoken only, for spelling, a split second of silence (difficult to author) user: *: "" *: "" @@ -10948,13 +10953,27 @@ *: none agc: "AGC max. gain" *: none agc: "AGC max. gain" *: none agc: "AGC maximum gain" + + id: VOICE_OF + desc: spoken only, as in 3/8 => 3 of 8 + user: + + *: "" + + + *: "" + + + *: "of" + +