Index: apps/playlist.c =================================================================== --- apps/playlist.c (revision 13678) +++ apps/playlist.c (working copy) @@ -289,6 +289,7 @@ { 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); @@ -485,6 +486,7 @@ lcd_setmargins(0, 0); #endif + cond_talk_ids(LANG_WAIT); gui_syncsplash(0, str(LANG_PLAYLIST_LOAD)); if (!buffer) @@ -771,9 +773,9 @@ 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); @@ -1349,9 +1351,9 @@ 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; } @@ -1444,7 +1446,7 @@ 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; @@ -1529,7 +1531,7 @@ 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; } @@ -1583,7 +1585,7 @@ /* 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; @@ -1658,6 +1660,20 @@ */ static void display_playlist_count(int count, const unsigned char *fmt) { + static long talked_tick = 0; + if(count && (talked_tick == 0 + || TIME_AFTER(current_tick, talked_tick+5*HZ))) + { + talked_tick = current_tick; + long id = P2ID(fmt); + if(id>=0) + { + talk_number(count, false); + talk_id(id, true); + } + } + fmt = P2STR(fmt); + lcd_clear_display(); #ifdef HAVE_LCD_BITMAP @@ -1681,7 +1697,7 @@ */ static void display_buffer_full(void) { - gui_syncsplash(HZ*2, str(LANG_PLAYLIST_BUFFER_FULL)); + gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL)); } /* @@ -1760,7 +1776,7 @@ 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; @@ -1947,11 +1963,11 @@ 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; @@ -1959,7 +1975,7 @@ 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; } @@ -1968,7 +1984,7 @@ 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; } @@ -2371,6 +2387,7 @@ start_current = true; } + cond_talk_ids(LANG_WAIT); gui_syncsplash(0, str(LANG_PLAYLIST_SHUFFLE)); randomise_playlist(playlist, random_seed, start_current, true); @@ -2863,7 +2880,7 @@ 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; } @@ -2894,7 +2911,7 @@ 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; } @@ -2907,9 +2924,9 @@ } 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); @@ -2960,14 +2977,14 @@ 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; } @@ -2981,9 +2998,9 @@ 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); @@ -3080,7 +3097,7 @@ 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; } @@ -3115,7 +3132,7 @@ 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; } @@ -3378,7 +3395,7 @@ 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; } @@ -3394,11 +3411,11 @@ 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)); cpu_boost(true); @@ -3435,7 +3452,7 @@ 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; } @@ -3522,7 +3539,7 @@ 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 (revision 13678) +++ apps/tree.c (working copy) @@ -325,7 +325,7 @@ (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 Index: apps/settings.c =================================================================== --- apps/settings.c (revision 13678) +++ apps/settings.c (working copy) @@ -547,6 +547,7 @@ screens[i].update(); #endif } + cond_talk_ids_fq(LANG_SETTINGS_SAVE_RECORDER); sleep(HZ*2); return -1; } @@ -578,15 +579,15 @@ break; } else { - gui_syncsplash(HZ, str(LANG_MENU_SETTING_CANCEL)); + gui_syncsplash(HZ, ID2P(LANG_MENU_SETTING_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; } @@ -1142,7 +1143,7 @@ { if (*(int*)variable != oldvalue) { - gui_syncsplash(HZ/2, str(LANG_MENU_SETTING_CANCEL)); + gui_syncsplash(HZ/2, ID2P(LANG_MENU_SETTING_CANCEL)); *(int*)variable = oldvalue; } } @@ -1150,7 +1151,7 @@ { if (*(bool*)variable != (bool)oldvalue) { - gui_syncsplash(HZ/2, str(LANG_MENU_SETTING_CANCEL)); + gui_syncsplash(HZ/2, ID2P(LANG_MENU_SETTING_CANCEL)); *(bool*)variable = (bool)oldvalue; } } Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 13678) +++ apps/lang/english.lang (working copy) @@ -152,7 +152,7 @@ *: "Shutting down..." - *: "" + *: "Shutting down" @@ -194,7 +194,7 @@ *: "Cancelled" - *: "" + *: "Cancelled" @@ -208,7 +208,7 @@ *: "Failed" - *: "" + *: "Failed" @@ -852,7 +852,7 @@ *: "Are You Sure?" - *: "" + *: "Are You Sure?" @@ -920,7 +920,7 @@ *: "Cleared" - *: "" + *: "Settings Cleared" @@ -934,7 +934,7 @@ *: "Cancelled" - *: "" + *: "Canceled" @@ -962,7 +962,7 @@ *: "Save Failed" - *: "" + *: "Save Failed" @@ -1176,17 +1176,17 @@ - id: LANG_EQUALIZER_BAND_PEAK + id: LANG_EQUALIZER_BAND_PEAK1 desc: in the equalizer settings menu user: - *: "Peak Filter %d" + *: "Peak Filter 1" - *: "Peak Filter %d" + *: "Peak Filter 1" - *: "Peak filter" + *: "Peak Filter 1" @@ -1760,7 +1760,7 @@ *: "Updating in background" - *: "" + *: "Updating in background" @@ -4129,7 +4129,7 @@ *: "File/directory exists. Overwrite?" - *: "" + *: "File or directory exists. Overwrite?" @@ -4185,7 +4185,7 @@ *: "Delete?" - *: "" + *: "Really delete?" @@ -4199,7 +4199,7 @@ *: "Deleted" - *: "" + *: "Deleted" @@ -4833,7 +4833,7 @@ *: "Create a Bookmark?" - *: "" + *: "Create a Bookmark?" @@ -4847,7 +4847,7 @@ *: "Bookmark Created" - *: "" + *: "Bookmark Created" @@ -4861,7 +4861,7 @@ *: "Bookmark Failed!" - *: "" + *: "Bookmark Failed!" @@ -4875,7 +4875,7 @@ *: "Bookmark Empty" - *: "" + *: "Bookmark Empty" @@ -7253,7 +7253,7 @@ *: "Playlist Buffer Full" - *: "" + *: "Playlist Buffer Full" @@ -7283,7 +7283,7 @@ player: "End of List" - *: "" + *: "End of Song List" @@ -7311,7 +7311,7 @@ *: "Inserted %d tracks (%s)" - *: "" + *: "Inserted tracks so far" @@ -7325,7 +7325,7 @@ *: "Queued %d tracks (%s)" - *: "" + *: "Queued tracks so far" @@ -7339,7 +7339,7 @@ *: "Saved %d tracks (%s)" - *: "" + *: "Tracks saved" @@ -7353,7 +7353,7 @@ *: "Recursively?" - *: "" + *: "Recursively?" @@ -7367,7 +7367,7 @@ *: "Erase dynamic playlist?" - *: "" + *: "Erase dynamic playlist?" @@ -7381,7 +7381,7 @@ *: "Nothing to resume" - *: "" + *: "Nothing to resume" @@ -7395,7 +7395,7 @@ *: "Error updating playlist control file" - *: "" + *: "Error updating playlist control file" @@ -7409,7 +7409,7 @@ *: "Error accessing playlist file" - *: "" + *: "Error accessing playlist file" @@ -7423,7 +7423,7 @@ *: "Error accessing playlist control file" - *: "" + *: "Error accessing playlist control file" @@ -7437,7 +7437,7 @@ *: "Error accessing directory" - *: "" + *: "Error accessing directory" @@ -7451,7 +7451,7 @@ *: "Playlist control file is invalid" - *: "" + *: "Playlist control file is invalid" @@ -7815,7 +7815,7 @@ *: "Dir Buffer is Full!" - *: "" + *: "Directory Buffer is Full!" @@ -7829,7 +7829,7 @@ *: "New Language" - *: "" + *: "New Language" @@ -7843,7 +7843,7 @@ *: "Settings Loaded" - *: "" + *: "Settings Loaded" @@ -7857,7 +7857,7 @@ *: "Settings Saved" - *: "" + *: "Settings Saved" @@ -7937,7 +7937,7 @@ *: "No files" - *: "" + *: "No files" @@ -7979,7 +7979,7 @@ *: "New Keyboard" - *: "" + *: "New Keyboard" @@ -8147,7 +8147,7 @@ *: "Move Failed" - *: "" + *: "Move Failed" @@ -8315,7 +8315,7 @@ *: "Extension array full" - *: "" + *: "Extension array full" @@ -8329,7 +8329,7 @@ *: "Filetype array full" - *: "" + *: "Filetype array full" @@ -8343,7 +8343,7 @@ *: "Plugin name too long" - *: "" + *: "Plugin name too long" @@ -8357,7 +8357,7 @@ *: "Filetype string buffer empty" - *: "" + *: "Filetype string buffer empty" @@ -8702,7 +8702,7 @@ *: "%s doesn't exist" - *: "" + *: "Playlist directory doesn't exist" @@ -8716,7 +8716,7 @@ *: "No Playlists" - *: "" + *: "No Playlists" @@ -10028,7 +10028,7 @@ *: "Please reboot to enable" - *: "" + *: "Please Reboot to enable the cache" @@ -10248,7 +10248,7 @@ *: "WARNING! Low Battery!" - *: "" + *: "WARNING! Low Battery!" @@ -10262,7 +10262,7 @@ *: "Battery empty! RECHARGE!" - *: "" + *: "Battery empty! RECHARGE!" @@ -11006,3 +11006,31 @@ *: "Use File .talk Clips" + + id: LANG_EQUALIZER_BAND_PEAK2 + desc: in the equalizer settings menu + user: + + *: "Peak Filter 2" + + + *: "Peak Filter 2" + + + *: "Peak Filter 2" + + + + id: LANG_EQUALIZER_BAND_PEAK3 + desc: in the equalizer settings menu + user: + + *: "Peak Filter 3" + + + *: "Peak Filter 3" + + + *: "Peak Filter 3" + + Index: apps/onplay.c =================================================================== --- apps/onplay.c (revision 13678) +++ apps/onplay.c (working copy) @@ -160,12 +160,12 @@ { 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); @@ -519,11 +519,11 @@ 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 }; @@ -594,6 +594,7 @@ 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 @@ -623,6 +624,7 @@ 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 { @@ -852,7 +854,7 @@ 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 */ @@ -918,6 +920,7 @@ /* 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 (revision 13678) +++ apps/gui/splash.c (working copy) @@ -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 @@ { 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 (revision 13678) +++ apps/gui/yesno.c (working copy) @@ -23,6 +23,7 @@ #include "misc.h" #include "lang.h" #include "action.h" +#include "talk.h" /* * Initializes the yesno asker @@ -95,7 +96,28 @@ 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) @@ -105,6 +127,13 @@ 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); @@ -114,7 +143,14 @@ action_signalscreenchange(); while (result==-1) { - button = get_action(CONTEXT_YESNOSCREEN,TIMEOUT_BLOCK); + /* Repeat the question every 5secs (more or less) */ + if (global_settings.talk_menu + && (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 @@ action_signalscreenchange(); 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 (global_settings.talk_menu) + { + talk_idarray(voice_ids, false); + talk_force_enqueue_next(); + } if(result_displayed) sleep(HZ); return(result); Index: apps/menus/recording_menu.c =================================================================== --- apps/menus/recording_menu.c (revision 13678) +++ apps/menus/recording_menu.c (working copy) @@ -638,7 +638,7 @@ switch (button) { case ACTION_STD_CANCEL: - gui_syncsplash(50, str(LANG_MENU_SETTING_CANCEL)); + gui_syncsplash(50, ID2P(LANG_MENU_SETTING_CANCEL)); global_settings.rec_start_thres = old_start_thres; global_settings.rec_start_duration = old_start_duration; global_settings.rec_prerecord_time = old_prerecord_time; Index: apps/menus/settings_menu.c =================================================================== --- apps/menus/settings_menu.c (revision 13678) +++ apps/menus/settings_menu.c (working copy) @@ -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 @@ -167,7 +167,7 @@ { 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/menus/eq_menu.c =================================================================== --- apps/menus/eq_menu.c (revision 13678) +++ apps/menus/eq_menu.c (working copy) @@ -161,46 +161,14 @@ MAKE_MENU(gain_menu, ID2P(LANG_EQUALIZER_GAIN), NULL, Icon_NOICON, &gain_item_0, &gain_item_1, &gain_item_2, &gain_item_3, &gain_item_4); -static const struct menu_item_ex *band_items[3][3] = { - { &cutoff_1, &q_1, &gain_1 }, - { &cutoff_2, &q_2, &gain_2 }, - { &cutoff_3, &q_3, &gain_3 } -}; -char* centerband_get_name(int selected_item, void * data, char *buffer) -{ - (void)selected_item; - int band = (intptr_t)data; - snprintf(buffer, MAX_PATH, str(LANG_EQUALIZER_BAND_PEAK), band); - return buffer; -} -int do_center_band_menu(void* param) -{ - int band = (intptr_t)param; - struct menu_item_ex menu; - struct menu_callback_with_desc cb_and_desc; - char desc[MAX_PATH]; - - cb_and_desc.menu_callback = NULL; - snprintf(desc, MAX_PATH, str(LANG_EQUALIZER_BAND_PEAK), band); - cb_and_desc.desc = desc; - cb_and_desc.icon_id = Icon_EQ; - menu.flags = MT_MENU|(3<index); if (ret_val < 0) - gui_syncsplash(HZ, str(LANG_MOVE_FAILED)); + gui_syncsplash(HZ, ID2P(LANG_MOVE_FAILED)); update_playlist(true); viewer.move_track = -1; Index: apps/talk.c =================================================================== --- apps/talk.c (revision 13678) +++ apps/talk.c (working copy) @@ -116,6 +116,8 @@ 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 */ @@ -133,7 +135,6 @@ 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); @@ -261,6 +262,13 @@ } +/* 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) { @@ -315,7 +323,7 @@ } /* stop the playback and the pending clips */ -static int shutup(void) +int do_shutup(void) { #if CONFIG_CODEC != SWCODEC unsigned char* pos; @@ -381,6 +389,13 @@ 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) @@ -389,6 +404,9 @@ 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 */ @@ -611,7 +629,27 @@ 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 (revision 13678) +++ apps/filetree.c (working copy) @@ -343,21 +343,21 @@ 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) @@ -378,7 +378,7 @@ 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 */ @@ -386,7 +386,7 @@ !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) @@ -397,7 +397,7 @@ { 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) { @@ -422,7 +422,7 @@ /* 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))) @@ -449,7 +449,7 @@ /* 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 @@ -461,7 +461,7 @@ #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 @@ -472,39 +472,39 @@ #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 @@ -512,7 +512,7 @@ #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 @@ -520,11 +520,11 @@ /* 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) { @@ -546,7 +546,7 @@ 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 (revision 13678) +++ apps/talk.h (working copy) @@ -66,6 +66,9 @@ 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 @@ 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 (global_settings.talk_menu) \ + talk_ids(false, ids); \ + } while(0) +/* And a version that takes the array parameter... */ +#define cond_talk_idarray(idarray) do { \ + if (global_settings.talk_menu) \ + 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 (global_settings.talk_menu) { \ + talk_ids(false, ids); \ + talk_force_enqueue_next(); \ + } \ + }while(0) #endif /* __TALK_H__ */ Index: apps/playback.c =================================================================== --- apps/playback.c (revision 13678) +++ apps/playback.c (working copy) @@ -959,6 +959,24 @@ 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(1); +} + #endif /* PLAYBACK_VOICE */ static void set_filebuf_watermark(int seconds) Index: apps/playback.h =================================================================== --- apps/playback.h (revision 13678) +++ apps/playback.h (working copy) @@ -63,6 +63,8 @@ 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 (revision 13678) +++ apps/plugin.c (working copy) @@ -538,7 +538,7 @@ 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 (revision 13678) +++ apps/filetypes.c (working copy) @@ -298,7 +298,7 @@ { 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 (revision 13678) +++ apps/bookmark.c (working copy) @@ -186,10 +186,10 @@ 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[]={ID2P(LANG_AUTO_BOOKMARK_QUERY), str(LANG_RESUME_CONFIRM_PLAYER)}; struct text_message message={(char **)lines, 2}; #endif @@ -244,8 +244,8 @@ } } - 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; } @@ -405,6 +405,7 @@ { return bookmark_load(global_bookmark_file_name, true); } + cond_talk_ids(LANG_BOOKMARK_AUTOLOAD_QUERY); action_signalscreenchange(); return false; @@ -636,7 +637,7 @@ 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); action_signalscreenchange(); return NULL; Index: apps/root_menu.c =================================================================== --- apps/root_menu.c (revision 13678) +++ apps/root_menu.c (working copy) @@ -229,7 +229,7 @@ } 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 (revision 13678) +++ apps/misc.c (working copy) @@ -62,6 +62,7 @@ #include "bookmark.h" #include "misc.h" +#include "playback.h" #ifdef BOOTFILE #ifndef USB_IPODSTYLE @@ -618,23 +619,27 @@ x5_backlight_shutdown(); #endif if (!battery_level_safe()) + { + cond_talk_ids(LANG_WARNING_BATTERY_EMPTY, LANG_SHUTTINGDOWN); gui_syncsplash(3*HZ, "%s %s", str(LANG_WARNING_BATTERY_EMPTY), str(LANG_SHUTTINGDOWN)); - else if (battery_level_critical()) + } else if (battery_level_critical()) + { + cond_talk_ids(LANG_WARNING_BATTERY_LOW, LANG_SHUTTINGDOWN); gui_syncsplash(3*HZ, "%s %s", str(LANG_WARNING_BATTERY_LOW), str(LANG_SHUTTINGDOWN)); - else { + } else { #ifdef HAVE_TAGCACHE if (!tagcache_prepare_shutdown()) { cancel_shutdown(); - gui_syncsplash(HZ, str(LANG_TAGCACHE_BUSY)); + gui_syncsplash(HZ, ID2P(LANG_TAGCACHE_BUSY)); return false; } #endif - gui_syncsplash(0, str(LANG_SHUTTINGDOWN)); + gui_syncsplash(0, ID2P(LANG_SHUTTINGDOWN)); } if (global_settings.fade_on_stop @@ -658,7 +663,11 @@ audio_stop(); while (audio_status()) sleep(1); - + +#if CONFIG_CODEC == SWCODEC + voice_wait(); +#endif + if (callback != NULL) callback(parameter); Index: apps/playlist_catalog.c =================================================================== --- apps/playlist_catalog.c (revision 13678) +++ apps/playlist_catalog.c (working copy) @@ -38,6 +38,7 @@ #include "tree.h" #include "yesno.h" #include "filetypes.h" +#include "talk.h" #define PLAYLIST_CATALOG_CFG ROCKBOX_DIR "/playlist_catalog.config" #define PLAYLIST_CATALOG_DEFAULT_DIR "/Playlists" @@ -113,7 +114,7 @@ if (!playlist_dir_exists) { if (mkdir(playlist_dir) < 0) { - gui_syncsplash(HZ*2, str(LANG_CATALOG_NO_DIRECTORY), + gui_syncsplash(HZ*2, ID2P(LANG_CATALOG_NO_DIRECTORY), playlist_dir); return -1; } @@ -147,7 +148,7 @@ 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; } @@ -232,7 +233,7 @@ if (num_playlists <= 0) { - gui_syncsplash(HZ*2, str(LANG_CATALOG_NO_PLAYLISTS)); + gui_syncsplash(HZ*2, ID2P(LANG_CATALOG_NO_PLAYLISTS)); return -1; } @@ -320,6 +321,15 @@ 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, #if CONFIG_KEYPAD == PLAYER_PAD str(LANG_STOP_ABORT) @@ -404,7 +414,7 @@ /* 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: apps/tagtree.c =================================================================== --- apps/tagtree.c (revision 13678) +++ apps/tagtree.c (working copy) @@ -688,7 +688,7 @@ 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; @@ -696,10 +696,10 @@ 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; @@ -1217,7 +1217,7 @@ 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; } @@ -1408,7 +1408,7 @@ !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) @@ -1485,7 +1485,7 @@ 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; } @@ -1588,12 +1588,12 @@ } if (tc->filesindir <= 0) - gui_syncsplash(HZ, str(LANG_END_PLAYLIST_PLAYER)); + gui_syncsplash(HZ, ID2P(LANG_END_PLAYLIST_PLAYER)); 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. */