Index: trunk/apps/talk.c =================================================================== --- trunk.orig/apps/talk.c +++ trunk/apps/talk.c @@ -43,6 +43,7 @@ #include "debug.h" #include "kernel.h" #include "pcmbuf.h" +#include "rbunicode.h" /* Memory layout varies between targets because the @@ -1017,10 +1018,116 @@ int talk_value(long n, int unit, bool en return 0; } +/* Spelling table, indexed by char number, gives id to speak. Hack: + optional second ID for each entry placed in higher bits, mostly used + for accents. Also a one bit flag indicates capital letters. */ +static unsigned long spell_table[256] = { +#define DOUBLE_SHIFT 16 +#define DOUBLE_MASK 0x9FFF +#define VALID1_FLAG 0x4000 /* lower bits ID is valid */ +#define VALID2_FLAG 0x40000000 /* upper bits ID is valid */ +#define CAP_FLAG 0x20000000 /* capital letter */ +#define S(x) ((x) | VALID1_FLAG) +#define D(x,y) ((x) | ((y)< */, + [63] = S(VOICE_CHAR_QUESTION_MARK) /* ? */, + [64] = S(VOICE_CHAR_AT_SIGN) /* @ */, + [91] = D(VOICE_CHAR_LEFT, VOICE_CHAR_BRACKET) /* [ */, + [92] = S(VOICE_CHAR_BACKSLASH) /* \ */, + [93] = D(VOICE_CHAR_RIGHT, VOICE_CHAR_BRACKET) /* ] */, + [94] = S(VOICE_CHAR_CARET) /* ^ */, + [95] = S(VOICE_CHAR_UNDERLINE) /* _ */, + [96] = S(VOICE_CHAR_BACK_QUOTE) /* ` */, + [123] = D(VOICE_CHAR_LEFT, VOICE_CHAR_BRACE) /* { */, + [124] = S(VOICE_CHAR_BAR) /* | */, + [125] = D(VOICE_CHAR_RIGHT, VOICE_CHAR_BRACE) /* } */, + [126] = S(VOICE_CHAR_TILDA) /* ~ */, + [162] = S(VOICE_CHAR_CENTS) /* ¢ */, + [163] = S(VOICE_CHAR_POUNDS) /* £ */, + [169] = S(VOICE_CHAR_COPYRIGHT) /* © */, + [171] = D(VOICE_CHAR_LEFT, VOICE_CHAR_DOUBLE_ANGLE_BRACKET) /* « */, + [174] = S(VOICE_CHAR_REGISTERED) /* ® */, + [176] = S(VOICE_CHAR_DEGREES) /* ° */, + [177] = S(VOICE_CHAR_PLUS_OR_MINUS) /* ± */, + [178] = S(VOICE_CHAR_SUPERSCRIPT_2) /* ² */, + [179] = S(VOICE_CHAR_SUPERSCRIPT_3) /* ³ */, + [180] = S(VOICE_CHAR_ACUTE) /* ´ */, + [184] = S(VOICE_CHAR_CEDILLA) /* ¸ */, + [186] = S(VOICE_CHAR_DEGREES) /* º */, + [187] = D(VOICE_CHAR_RIGHT, VOICE_CHAR_DOUBLE_ANGLE_BRACKET) /* » */, + [188] = S(VOICE_CHAR_ONE_FOURTH) /* ¼ */, + [189] = S(VOICE_CHAR_ONE_HALF) /* ½ */, + [190] = S(VOICE_CHAR_THREE_FOURTHS) /* ¾ */, + [191] = D(VOICE_CHAR_INVERTED, VOICE_CHAR_QUESTION_MARK) /* ¿ */, + [192] = CAP(D(VOICE_CHAR_A, VOICE_CHAR_GRAVE)) /* À */, + [194] = CAP(D(VOICE_CHAR_A, VOICE_CHAR_CIRCUMFLEX)) /* Â */, + [196] = CAP(D(VOICE_CHAR_A, VOICE_CHAR_UMLAUT)) /* Ä */, + [199] = CAP(D(VOICE_CHAR_C, VOICE_CHAR_CEDILLA)) /* Ç */, + [200] = CAP(D(VOICE_CHAR_E, VOICE_CHAR_GRAVE)) /* È */, + [201] = CAP(D(VOICE_CHAR_E, VOICE_CHAR_ACUTE)) /* É */, + [202] = CAP(D(VOICE_CHAR_E, VOICE_CHAR_CIRCUMFLEX)) /* Ê */, + [203] = CAP(D(VOICE_CHAR_E, VOICE_CHAR_UMLAUT)) /* Ë */, + [204] = CAP(D(VOICE_CHAR_I, VOICE_CHAR_GRAVE)) /* Ì */, + [205] = CAP(D(VOICE_CHAR_I, VOICE_CHAR_ACUTE)) /* Í */, + [206] = CAP(D(VOICE_CHAR_I, VOICE_CHAR_CIRCUMFLEX)) /* Î */, + [207] = CAP(D(VOICE_CHAR_I, VOICE_CHAR_UMLAUT)) /* Ï */, + [210] = CAP(D(VOICE_CHAR_O, VOICE_CHAR_GRAVE)) /* Ò */, + [211] = CAP(D(VOICE_CHAR_O, VOICE_CHAR_ACUTE)) /* Ó */, + [212] = CAP(D(VOICE_CHAR_O, VOICE_CHAR_CIRCUMFLEX)) /* Ô */, + [214] = CAP(D(VOICE_CHAR_O, VOICE_CHAR_UMLAUT)) /* Ö */, + [215] = S(VOICE_CHAR_TIMES) /* × */, + [217] = CAP(D(VOICE_CHAR_U, VOICE_CHAR_GRAVE)) /* Ù */, + [218] = CAP(D(VOICE_CHAR_U, VOICE_CHAR_ACUTE)) /* Ú */, + [219] = CAP(D(VOICE_CHAR_U, VOICE_CHAR_CIRCUMFLEX)) /* Û */, + [220] = CAP(D(VOICE_CHAR_U, VOICE_CHAR_UMLAUT)) /* Ü */, + [224] = D(VOICE_CHAR_A, VOICE_CHAR_GRAVE) /* à */, + [225] = D(VOICE_CHAR_A, VOICE_CHAR_ACUTE) /* á */, + [226] = D(VOICE_CHAR_A, VOICE_CHAR_CIRCUMFLEX) /* â */, + [228] = D(VOICE_CHAR_A, VOICE_CHAR_UMLAUT) /* ä */, + [231] = D(VOICE_CHAR_C, VOICE_CHAR_CEDILLA) /* ç */, + [232] = D(VOICE_CHAR_E, VOICE_CHAR_GRAVE) /* è */, + [233] = D(VOICE_CHAR_E, VOICE_CHAR_ACUTE) /* é */, + [234] = D(VOICE_CHAR_E, VOICE_CHAR_CIRCUMFLEX) /* ê */, + [235] = D(VOICE_CHAR_E, VOICE_CHAR_UMLAUT) /* ë */, + [236] = D(VOICE_CHAR_I, VOICE_CHAR_GRAVE) /* ì */, + [237] = D(VOICE_CHAR_I, VOICE_CHAR_ACUTE) /* í */, + [238] = D(VOICE_CHAR_I, VOICE_CHAR_CIRCUMFLEX) /* î */, + [239] = D(VOICE_CHAR_I, VOICE_CHAR_UMLAUT) /* ï */, + [242] = D(VOICE_CHAR_O, VOICE_CHAR_GRAVE) /* ò */, + [243] = D(VOICE_CHAR_O, VOICE_CHAR_ACUTE) /* ó */, + [244] = D(VOICE_CHAR_O, VOICE_CHAR_CIRCUMFLEX) /* ô */, + [246] = D(VOICE_CHAR_O, VOICE_CHAR_UMLAUT) /* ö */, + [247] = S(VOICE_CHAR_DIVIDED_BY) /* ÷ */, + [249] = D(VOICE_CHAR_U, VOICE_CHAR_GRAVE) /* ù */, + [250] = D(VOICE_CHAR_U, VOICE_CHAR_ACUTE) /* ú */, + [251] = D(VOICE_CHAR_U, VOICE_CHAR_CIRCUMFLEX) /* û */, + [252] = D(VOICE_CHAR_U, VOICE_CHAR_UMLAUT) /* ü */, +}; + /* spell a string */ int talk_spell(const char* spell, bool enqueue) { - char c; /* currently processed char */ + unsigned short c; /* currently processed char */ #if CONFIG_CODEC != SWCODEC if (audio_status()) /* busy, buffer in use */ @@ -1030,25 +1137,58 @@ int talk_spell(const char* spell, bool e if (!enqueue) shutup(); /* cut off all the pending stuff */ - while ((c = *spell++) != '\0') + while (*spell) { - /* if this grows into too many cases, I should use a table */ - if (c >= 'A' && c <= 'Z') - talk_id(VOICE_CHAR_A + c - 'A', true); - else if (c >= 'a' && c <= 'z') - talk_id(VOICE_CHAR_A + c - 'a', true); - else if (c >= '0' && c <= '9') - talk_id(VOICE_ZERO + c - '0', true); - else if (c == '-') - talk_id(VOICE_MINUS, true); - else if (c == '+') - talk_id(VOICE_PLUS, true); - else if (c == '.') - talk_id(VOICE_DOT, true); - else if (c == ' ') - talk_id(VOICE_PAUSE, true); - else if (c == '/') - talk_id(VOICE_CHAR_SLASH, true); + bool cap = false; + long id = -1, id2 = -1; + spell = utf8decode(spell, &c); + switch(c) { + case 'A' ... 'Z': + cap = true; + id = VOICE_CHAR_A + c - 'A'; + break; + case 'a' ... 'z': + id = VOICE_CHAR_A + c - 'a'; + break; + case '0' ... '9': + id = VOICE_ZERO + c - '0'; + break; + case '-': + id = VOICE_MINUS; + break; + case '+': + id = VOICE_PLUS; + break; + case '.': + id = VOICE_DOT; + break; + case ' ': + id = VOICE_PAUSE; + break; + case '/': + id = VOICE_CHAR_SLASH; + break; + default: + if(c>DOUBLE_SHIFT) &DOUBLE_MASK; + cap = (x & CAP_FLAG); + } + }; + if(id == -1 && id2 == -1) { + /* unknown char */ + talk_id(VOICE_CHAR_UNKNOWN, true); + talk_number(c, true); + } + if(cap) + talk_id(VOICE_CHAR_CAP, true); + if(id != -1) + talk_id(id, true); + if(id2 != -1) + talk_id(id2, true); } return 0; Index: rockbox-devel/apps/lang/english.lang =================================================================== --- rockbox-devel.orig/apps/lang/english.lang +++ rockbox-devel/apps/lang/english.lang @@ -10694,3 +10694,689 @@ *: "Blank" + + id: VOICE_CHAR_APOSTROPHE + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "apostrophe" + + + + id: VOICE_CHAR_COMMA + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "comma" + + + + id: VOICE_CHAR_COLON + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "colon" + + + + id: VOICE_CHAR_SEMICOLON + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "semicolon" + + + + id: VOICE_CHAR_LESS_THAN + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "less than" + + + + id: VOICE_CHAR_GREATER_THAN + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "greater than" + + + + id: VOICE_CHAR_BACK_QUOTE + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "back quote" + + + + id: VOICE_CHAR_TILDA + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "tilda" + + + + id: VOICE_CHAR_LEFT + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "left" + + + + id: VOICE_CHAR_RIGHT + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "right" + + + + id: VOICE_CHAR_BRACKET + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "bracket" + + + + id: VOICE_CHAR_BRACE + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "brace" + + + + id: VOICE_CHAR_EQUAL + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "equal" + + + + id: VOICE_CHAR_UNDERLINE + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "underline" + + + + id: VOICE_CHAR_PARENTHESIS + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "parenthesis" + + + + id: VOICE_CHAR_ASTERISK + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "asterisk" + + + + id: VOICE_CHAR_AND + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "and" + + + + id: VOICE_CHAR_QUESTION_MARK + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "question mark" + + + + id: VOICE_CHAR_PERCENT + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "percent" + + + + id: VOICE_CHAR_DOLLAR + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "dollar" + + + + id: VOICE_CHAR_QUOTE + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "quote" + + + + id: VOICE_CHAR_EXCLAMATION_POINT + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "exclamation point" + + + + id: VOICE_CHAR_BAR + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "bar" + + + + id: VOICE_CHAR_NUMBER + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "number" + + + + id: VOICE_CHAR_BACKSLASH + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "backslash" + + + + id: VOICE_CHAR_PLUS_OR_MINUS + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "plus or minus" + + + + id: VOICE_CHAR_AT_SIGN + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "att sign" + + + + id: VOICE_CHAR_CARET + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "caret" + + + + id: VOICE_CHAR_POUNDS + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "pounds" + + + + id: VOICE_CHAR_CENTS + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "cents" + + + + id: VOICE_CHAR_SUPERSCRIPT_2 + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "superscript 2" + + + + id: VOICE_CHAR_SUPERSCRIPT_3 + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "superscript 3" + + + + id: VOICE_CHAR_ONE_FOURTH + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "one fourth" + + + + id: VOICE_CHAR_ONE_HALF + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "one half" + + + + id: VOICE_CHAR_THREE_FOURTHS + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "three fourths" + + + + id: VOICE_CHAR_COPYRIGHT + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "copyright" + + + + id: VOICE_CHAR_REGISTERED + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "registered" + + + + id: VOICE_CHAR_DEGREES + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "degrees" + + + + id: VOICE_CHAR_DOUBLE_ANGLE_BRACKET + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "double angle bracket" + + + + id: VOICE_CHAR_INVERTED + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "inverted" + + + + id: VOICE_CHAR_TIMES + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "times" + + + + id: VOICE_CHAR_DIVIDED_BY + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "divided by" + + + + id: VOICE_CHAR_ACUTE + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "acute" + + + + id: VOICE_CHAR_GRAVE + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "grave" + + + + id: VOICE_CHAR_CIRCUMFLEX + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "circumflex" + + + + id: VOICE_CHAR_UMLAUT + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "umlaut" + + + + id: VOICE_CHAR_CEDILLA + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "cedilla" + + + + id: VOICE_CHAR_CAP + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "cap" + + + + id: VOICE_CHAR_UNKNOWN + desc: for spelling + user: + + *: "" + + + *: "" + + + *: "unknown char" + +