Index: rockbox-devel/apps/talk.h =================================================================== --- rockbox-devel.orig/apps/talk.h +++ rockbox-devel/apps/talk.h @@ -69,4 +69,26 @@ int talk_number(long n, bool enqueue); / int talk_value(long n, int unit, bool enqueue); /* say a numeric value */ int talk_spell(const char* spell, bool enqueue); /* spell a string */ +/* Enqueue next utterance even if not asked to. In other words, don't + interrupt the current utterance. */ +void talk_force_enqueue_next(void); + +/* If and only if talking menus are enabled, speaks one or more IDs + (from an array), first is not queued and subsequent are. */ +int cond_talk_idarray(long *idarray); +/* 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 cond_talk_ids(ids...) cond_talk_idarray(TALK_IDARRAY(ids)) + +/* Convenience macros to speak something and not have it interrupted. */ +#define cond_talk_idarray_fq(idarray) do { \ + cond_talk_idarray(idarray); \ + talk_force_enqueue_next(); \ + }while(0) +#define cond_talk_ids_fq(ids...) cond_talk_idarray_fq(TALK_IDARRAY(ids)) + #endif /* __TALK_H__ */ Index: rockbox-devel/apps/talk.c =================================================================== --- rockbox-devel.orig/apps/talk.c +++ rockbox-devel/apps/talk.c @@ -107,6 +107,7 @@ 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 */ +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 */ @@ -380,8 +381,9 @@ static int queue_clip(unsigned char* buf { int queue_level; - if (!enqueue) + if (!force_enqueue_next && !enqueue) shutup(); /* cut off all the pending stuff */ + force_enqueue_next = false; if (!size) return 0; /* safety check */ @@ -599,6 +601,29 @@ int talk_id(long id, bool enqueue) return 0; } +/* If and only if talking menus are enabled, speaks one or more IDs + (from an array), first is not queued and subsequent are. */ +int cond_talk_idarray(long *ids) +{ + long *id = ids; + int r, queue = false; + if (!global_settings.talk_menu) + return 0; + if(!id) + return 0; + while(*id != TALK_FINAL_ID) { + if((r = talk_id(*(id++), queue)) <0) + return r; + queue = 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: rockbox-devel/apps/gui/splash.c =================================================================== --- rockbox-devel.orig/apps/gui/splash.c +++ rockbox-devel/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)) @@ -201,6 +204,15 @@ void gui_syncsplash(int ticks, bool cent { 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]), center, fmt, ap); Index: rockbox-devel/apps/gui/yesno.c =================================================================== --- rockbox-devel.orig/apps/gui/yesno.c +++ rockbox-devel/apps/gui/yesno.c @@ -4,6 +4,7 @@ #include "misc.h" #include "lang.h" #include "action.h" +#include "talk.h" void gui_yesno_init(struct gui_yesno * yn, struct text_message * main_message, @@ -55,7 +56,28 @@ bool gui_yesno_draw_result(struct gui_ye 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) @@ -65,6 +87,14 @@ enum yesno_res gui_syncyesno_run(struct int result=-1; bool result_displayed; struct gui_yesno yn[NB_SCREENS]; + long main_ids[5], yes_ids[5], no_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, main_ids, sizeof(main_ids)/sizeof(main_ids[0])); + extract_talk_ids(yes_message, yes_ids, sizeof(yes_ids)/sizeof(yes_ids[0])); + extract_talk_ids(no_message, no_ids, sizeof(no_ids)/sizeof(no_ids[0])); FOR_NB_SCREENS(i) { gui_yesno_init(&(yn[i]), main_message, yes_message, no_message); @@ -74,7 +104,12 @@ enum yesno_res gui_syncyesno_run(struct action_signalscreenchange(); while (result==-1) { - button = get_action(CONTEXT_YESNOSCREEN,TIMEOUT_BLOCK); + /* Repeat the question every 5secs (more or less) */ + if(talked_tick==0 || TIME_AFTER(current_tick, talked_tick+HZ*5)) { + talked_tick = current_tick; + cond_talk_idarray(main_ids); + } + button = get_action(CONTEXT_YESNOSCREEN, HZ*5); switch (button) { case ACTION_YESNO_ACCEPT: @@ -91,6 +126,7 @@ enum yesno_res gui_syncyesno_run(struct action_signalscreenchange(); FOR_NB_SCREENS(i) result_displayed=gui_yesno_draw_result(&(yn[i]), result); + cond_talk_idarray_fq((result == YESNO_YES) ? yes_ids : no_ids); if(result_displayed) sleep(HZ); return(result);