Index: rockbox-devel/apps/lang/english.lang
===================================================================
--- rockbox-devel.orig/apps/lang/english.lang
+++ rockbox-devel/apps/lang/english.lang
@@ -5388,7 +5388,7 @@
*: "[Title]"
- *: ""
+ *: "Title"
@@ -5402,7 +5402,7 @@
*: "[Artist]"
- *: ""
+ *: "Artist"
@@ -5416,7 +5416,7 @@
*: "[Album]"
- *: ""
+ *: "Album"
@@ -5430,7 +5430,7 @@
*: "[Tracknum]"
- *: ""
+ *: "Track number"
@@ -5444,7 +5444,7 @@
*: "[Genre]"
- *: ""
+ *: "Genre"
@@ -5458,7 +5458,7 @@
*: "[Year]"
- *: ""
+ *: "Year"
@@ -5472,7 +5472,7 @@
*: "[Length]"
- *: ""
+ *: "Length"
@@ -5486,7 +5486,7 @@
*: "[Playlist]"
- *: ""
+ *: "Playlist"
@@ -5500,7 +5500,7 @@
*: "[Bitrate]"
- *: ""
+ *: "Bit rate"
@@ -5528,7 +5528,7 @@
*: " (VBR)"
- *: ""
+ *: "V B R"
@@ -5542,7 +5542,7 @@
*: "[Frequency]"
- *: ""
+ *: "Frequency"
@@ -5556,7 +5556,7 @@
*: "[Track Gain]"
- *: ""
+ *: "Track gain"
@@ -5570,7 +5570,7 @@
*: "[Album Gain]"
- *: ""
+ *: "Album gain"
@@ -5584,7 +5584,7 @@
*: "[Path]"
- *: ""
+ *: "Path"
@@ -5598,7 +5598,7 @@
*: ""
- *: ""
+ *: "No info"
@@ -10434,3 +10434,17 @@
*: "If bookmark file already exists"
+
+ id: VOICE_OF
+ desc: in id3 info, as in "two of ten" "2/10"
+ user:
+
+ *: ""
+
+
+ *: ""
+
+
+ *: "of"
+
+
Index: rockbox-devel/apps/screens.c
===================================================================
--- rockbox-devel.orig/apps/screens.c
+++ rockbox-devel/apps/screens.c
@@ -78,6 +78,8 @@
#ifdef HAVE_LCD_COLOR
#include "backdrop.h"
#endif
+#include "ctype.h"
+#include "atoi.h"
#ifdef HAVE_LCD_BITMAP
#define SCROLLBAR_WIDTH 6
@@ -1126,7 +1128,88 @@ bool shutdown_screen(void)
#define ID3_ITEMS 11
#endif
-char * id3_get_info(int selected_item, void* data, char *buffer)
+/* Spell out a buffer, but when successive digits are encountered, say
+ the whole number. Useful for some ID3 tags that usually contain a
+ number but are in fact free-form. */
+void say_number_and_spell(char *buf, bool year_style)
+{
+ char *ptr = buf;
+ while(*ptr) {
+ if(isdigit(*ptr)) {
+ /* parse the number */
+ int n = atoi(ptr);
+ /* skip over digits to rest of string */
+ while(isdigit(*++ptr));
+ /* say the number */
+ if(year_style)
+ talk_id(TALK_ID(n, UNIT_DATEYEAR), true);
+ else talk_number(n, true);
+ }else{
+ /* Spell a sequence of non-digits */
+ char tmp, *start = ptr;
+ while(*++ptr && !isdigit(*ptr));
+ /* temporarily truncate the string here */
+ tmp = *ptr;
+ *ptr = '\0';
+ talk_spell(start, true);
+ *ptr = tmp; /* restore string */
+ }
+ }
+}
+
+/* Say a replaygain ID3 value from its text form */
+void say_gain(char *buf)
+{
+ /* Expected form is "-5.74 dB". We'll try to parse out the number
+ until the dot, say it (forcing the + sign), then say dot and
+ spell the following numbers, and then say the decibel unit. */
+ char *ptr = buf;
+ if(*ptr == '-' || *ptr == '+')
+ /* skip sign */
+ ++ptr;
+ /* See if we can parse out a number. */
+ if(isdigit(*ptr)) {
+ int n;
+ char tmp;
+ /* skip successive digits */
+ while(isdigit(*++ptr));
+ /* temporarily truncate the string here */
+ tmp = *ptr;
+ *ptr = '\0';
+ /* parse out the number we just skipped */
+ n = atoi(buf);
+ *ptr = tmp; /* restore the string */
+ talk_id(TALK_ID(n, UNIT_SIGNED), true); /* say the number with sign */
+ if(*ptr == '.') {
+ /* found the dot, say it */
+ ++ptr;
+ talk_id(LANG_POINT, true);
+ }
+ buf = ptr;
+ if(strlen(buf) >2 && !strcmp(buf+strlen(buf)-2, "dB")) {
+ /* String does end with "dB" */
+ char tmp;
+ /* point to that "dB" */
+ ptr = buf+strlen(buf)-2;
+ /* backup any spaces */
+ while(ptr >buf && ptr[-1] == ' ')
+ --ptr;
+ /* temporarily truncate string here */
+ tmp = *ptr;
+ *ptr = '\0';
+ /* Spell everything between the number or dot until the
+ "dB". Normaly that's the numbers of the fraction part. */
+ talk_spell(buf, true);
+ *ptr = tmp; /* restore string */
+ talk_id(VOICE_DB, true); /* say the dB unit */
+ }else /* doesn't end with dB, just spell everything after the
+ number of dot. */
+ talk_spell(buf, true);
+ }else /* we didn't find a number, just spell everything */
+ talk_spell(buf, true);
+}
+
+char * id3_get_or_speak_info(int selected_item, void* data, char *buffer, bool say_it)
{
struct mp3entry* id3 =(struct mp3entry*)data;
int info_no=selected_item/2;
@@ -1150,6 +1233,8 @@ char * id3_get_info(int selected_item, v
#endif
LANG_ID3_PATH,
};
+ if(say_it)
+ talk_id(headers[info_no], false);
return( str(headers[info_no]));
}
else
@@ -1160,77 +1245,131 @@ char * id3_get_info(int selected_item, v
{
case 0:/*LANG_ID3_TITLE*/
info=id3->title;
+ if(say_it && info)
+ talk_spell(info, true);
break;
case 1:/*LANG_ID3_ARTIST*/
info=id3->artist;
+ if(say_it && info)
+ talk_spell(info, true);
break;
case 2:/*LANG_ID3_ALBUM*/
info=id3->album;
+ if(say_it && info)
+ talk_spell(info, true);
break;
case 3:/*LANG_ID3_TRACKNUM*/
if (id3->track_string)
+ {
info = id3->track_string;
+ if(say_it)
+ say_number_and_spell(info, false);
+ }
else if (id3->tracknum)
{
snprintf(buffer, MAX_PATH, "%d", id3->tracknum);
info = buffer;
+ if(say_it)
+ talk_number(id3->tracknum, true);
}
break;
case 4:/*LANG_ID3_GENRE*/
info = id3_get_genre(id3);
+ if(say_it && info)
+ talk_spell(info, true);
break;
case 5:/*LANG_ID3_YEAR*/
if (id3->year_string)
+ {
info = id3->year_string;
+ if(say_it)
+ say_number_and_spell(info, true);
+ }
else if (id3->year)
{
snprintf(buffer, MAX_PATH, "%d", id3->year);
info = buffer;
+ if(say_it)
+ talk_id(TALK_ID(id3->year, UNIT_DATEYEAR), false);
}
break;
case 6:/*LANG_ID3_LENGTH*/
gui_wps_format_time(buffer, MAX_PATH, id3->length);
info=buffer;
+ if(say_it)
+ talk_id(TALK_ID(id3->length /1000, UNIT_TIME), true);
break;
case 7:/*LANG_ID3_PLAYLIST*/
snprintf(buffer, MAX_PATH, "%d/%d", playlist_get_display_index(),
playlist_amount());
info=buffer;
+ if(say_it)
+ {
+ talk_number(playlist_get_display_index(), true);
+ talk_id(VOICE_OF, true);
+ talk_number(playlist_amount(), true);
+ }
break;
case 8:/*LANG_ID3_BITRATE*/
snprintf(buffer, MAX_PATH, "%d kbps%s", id3->bitrate,
id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) "");
info=buffer;
+ if(say_it)
+ {
+ talk_value(id3->bitrate, UNIT_KBIT, true);
+ if(id3->vbr)
+ talk_id(LANG_ID3_VBR, true);
+ }
break;
case 9:/*LANG_ID3_FRECUENCY*/
snprintf(buffer, MAX_PATH, "%ld Hz", id3->frequency);
info=buffer;
+ if(say_it)
+ talk_value(id3->frequency, UNIT_HERTZ, true);
break;
#if CONFIG_CODEC == SWCODEC
case 10:/*LANG_ID3_TRACK_GAIN*/
info=id3->track_gain_string;
+ if(say_it && info)
+ say_gain(info);
break;
case 11:/*LANG_ID3_ALBUM_GAIN*/
info=id3->album_gain_string;
+ if(say_it && info)
+ say_gain(info);
break;
case 12:/*LANG_ID3_PATH*/
#else
case 10:/*LANG_ID3_PATH*/
#endif
info=id3->path;
+ if(say_it && info)
+ talk_fullpath(info, true);
break;
}
if(info==NULL)
+ {
+ if(say_it)
+ talk_id(LANG_ID3_NO_INFO, true);
return(str(LANG_ID3_NO_INFO));
+ }
return(info);
}
}
+/* gui_synclist callback */
+char * id3_get_info(int selected_item, void* data, char *buffer)
+{
+ return id3_get_or_speak_info(selected_item, data, buffer, false);
+}
+
bool browse_id3(void)
{
struct gui_synclist id3_lists;
struct mp3entry* id3 = audio_current_track();
int key;
+ bool say_fresh_line = true;
+ unsigned returned_button;
gui_synclist_init(&id3_lists, &id3_get_info, id3, true, 2);
gui_synclist_set_nb_items(&id3_lists, ID3_ITEMS*2);
@@ -1238,14 +1377,29 @@ bool browse_id3(void)
gui_syncstatusbar_draw(&statusbars, true);
action_signalscreenchange();
while (true) {
+ if (global_settings.talk_menu && say_fresh_line)
+ {
+ say_fresh_line = false;
+ char buffer[MAX_PATH];
+ int item = gui_synclist_get_sel_pos(&id3_lists);
+ /* say field name */
+ id3_get_or_speak_info(item, id3, buffer, true);
+ /* and field value */
+ id3_get_or_speak_info(item+1, id3, buffer, true);
+ }
gui_syncstatusbar_draw(&statusbars, false);
key = get_action(CONTEXT_LIST,HZ/2);
- if(key!=ACTION_NONE && key!=ACTION_UNKNOWN
- && !gui_synclist_do_button(&id3_lists, key,LIST_WRAP_UNLESS_HELD))
+ if(key==ACTION_NONE || key==ACTION_UNKNOWN)
+ continue;
+ if(!(returned_button = gui_synclist_do_button(&id3_lists, key,
+ LIST_WRAP_UNLESS_HELD)))
{
action_signalscreenchange();
return(default_event_handler(key) == SYS_USB_CONNECTED);
}
+ if(returned_button == ACTION_STD_NEXT
+ || returned_button == ACTION_STD_PREV)
+ say_fresh_line = true;
}
}
Index: rockbox-devel/apps/talk.c
===================================================================
--- rockbox-devel.orig/apps/talk.c
+++ rockbox-devel/apps/talk.c
@@ -912,6 +912,45 @@ int talk_number(long n, bool enqueue)
return 0;
}
+/* Say year like "nineteen ninety nine" instead of "one thousand 9
+ hundred ninety nine". */
+static int talk_year(long year, bool enqueue)
+{
+ int rem;
+ if(year < 1100 || year >=2000)
+ /* just say it as a regular number */
+ return talk_number(year, enqueue);
+ /* Say century */
+ talk_number(year/100, enqueue);
+ rem = year%100;
+ if(rem == 0)
+ /* as in 1900 */
+ return talk_id(VOICE_HUNDRED, true);
+ if(rem <10)
+ /* as in 1905 */
+ talk_id(VOICE_ZERO, true);
+ /* sub-century year */
+ return talk_number(rem, true);
+}
+
+/* Say time duration/interval. Input is time in seconds,
+ say hours,minutes,seconds. */
+int talk_time(long secs, bool enqueue)
+{
+ int hours, mins;
+ if (!enqueue)
+ shutup();
+ if((hours = secs/3600)) {
+ secs %= 3600;
+ talk_value(hours, UNIT_HOUR, true);
+ }
+ if((mins = secs/60)) {
+ secs %= 60;
+ talk_value(mins, UNIT_MIN, true);
+ }
+ return talk_value(secs, UNIT_SEC, true);
+}
+
/* singular/plural aware saying of a value */
int talk_value(long n, int unit, bool enqueue)
{
@@ -952,6 +991,13 @@ int talk_value(long n, int unit, bool en
return -1;
#endif
+ /* special pronounciation for year number */
+ if (unit == UNIT_DATEYEAR)
+ return talk_year(n, enqueue);
+ /* special case for time duration */
+ if (unit == UNIT_TIME)
+ return talk_time(n, enqueue);
+
if (unit < 0 || unit >= UNIT_LAST)
unit_id = -1;
else
Index: rockbox-devel/apps/talk.h
===================================================================
--- rockbox-devel.orig/apps/talk.h
+++ rockbox-devel/apps/talk.h
@@ -30,6 +30,7 @@ enum {
/* See array "unit_voiced" in talk.c function "talk_value" */
UNIT_INT = 1, /* plain number */
UNIT_SIGNED, /* number with mandatory sign (even if positive) */
+ UNIT_DATEYEAR,/* for 1999 say nineteen ninety nine */
UNIT_MS, /* milliseconds */
UNIT_SEC, /* seconds */
UNIT_MIN, /* minutes */
@@ -43,6 +44,7 @@ enum {
UNIT_HERTZ, /* hertz */
UNIT_MB, /* Megabytes */
UNIT_KBIT, /* kilobits per sec */
+ UNIT_TIME, /* time duration/interval in seconds, says hours,mins,secs */
UNIT_LAST /* END MARKER */
};