Voice UI: option to spell dirs/files, say (known) extension with file number, minor fixes


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4465 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index c44d522..bdd26ea 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -2427,3 +2427,219 @@
 voice: "folder"
 new:
 
+id: LANG_VOICE_SPELL
+desc: "talkbox" mode for files+directories
+eng: "Spell"
+voice: "Spell"
+new:
+
+id: VOICE_CHAR_A
+desc: spoken only, for spelling
+eng: ""
+voice: "A"
+new:
+
+id: VOICE_CHAR_B
+desc: spoken only, for spelling
+eng: ""
+voice: "B"
+new:
+
+id: VOICE_CHAR_C
+desc: spoken only, for spelling
+eng: ""
+voice: "C"
+new:
+
+id: VOICE_CHAR_D
+desc: spoken only, for spelling
+eng: ""
+voice: "D"
+new:
+
+id: VOICE_CHAR_E
+desc: spoken only, for spelling
+eng: ""
+voice: "E"
+new:
+
+id: VOICE_CHAR_F
+desc: spoken only, for spelling
+eng: ""
+voice: "F"
+new:
+
+id: VOICE_CHAR_G
+desc: spoken only, for spelling
+eng: ""
+voice: "G"
+new:
+
+id: VOICE_CHAR_H
+desc: spoken only, for spelling
+eng: ""
+voice: "H"
+new:
+
+id: VOICE_CHAR_I
+desc: spoken only, for spelling
+eng: ""
+voice: "I"
+new:
+
+id: VOICE_CHAR_J
+desc: spoken only, for spelling
+eng: ""
+voice: "J"
+new:
+
+id: VOICE_CHAR_K
+desc: spoken only, for spelling
+eng: ""
+voice: "K"
+new:
+
+id: VOICE_CHAR_L
+desc: spoken only, for spelling
+eng: ""
+voice: "L"
+new:
+
+id: VOICE_CHAR_M
+desc: spoken only, for spelling
+eng: ""
+voice: "M"
+new:
+
+id: VOICE_CHAR_N
+desc: spoken only, for spelling
+eng: ""
+voice: "N"
+new:
+
+id: VOICE_CHAR_O
+desc: spoken only, for spelling
+eng: ""
+voice: "O"
+new:
+
+id: VOICE_CHAR_P
+desc: spoken only, for spelling
+eng: ""
+voice: "P"
+new:
+
+id: VOICE_CHAR_Q
+desc: spoken only, for spelling
+eng: ""
+voice: "Q"
+new:
+
+id: VOICE_CHAR_R
+desc: spoken only, for spelling
+eng: ""
+voice: "R"
+new:
+
+id: VOICE_CHAR_S
+desc: spoken only, for spelling
+eng: ""
+voice: "S"
+new:
+
+id: VOICE_CHAR_T
+desc: spoken only, for spelling
+eng: ""
+voice: "T"
+new:
+
+id: VOICE_CHAR_U
+desc: spoken only, for spelling
+eng: ""
+voice: "U"
+new:
+
+id: VOICE_CHAR_V
+desc: spoken only, for spelling
+eng: ""
+voice: "V"
+new:
+
+id: VOICE_CHAR_W
+desc: spoken only, for spelling
+eng: ""
+voice: "W"
+new:
+
+id: VOICE_CHAR_X
+desc: spoken only, for spelling
+eng: ""
+voice: "X"
+new:
+
+id: VOICE_CHAR_Y
+desc: spoken only, for spelling
+eng: ""
+voice: "Y"
+new:
+
+id: VOICE_CHAR_Z
+desc: spoken only, for spelling
+eng: ""
+voice: "Z"
+new:
+
+id: VOICE_EXT_MPA
+desc: spoken only, for file extension
+eng: ""
+voice: "audio"
+new:
+
+id: VOICE_EXT_CFG
+desc: spoken only, for file extension
+eng: ""
+voice: "configuration"
+new:
+
+id: VOICE_EXT_WPS
+desc: spoken only, for file extension
+eng: ""
+voice: "while-playing-screen"
+new:
+
+id: VOICE_EXT_TXT
+desc: spoken only, for file extension
+eng: ""
+voice: "text"
+new:
+
+id: VOICE_EXT_ROCK
+desc: spoken only, for file extension
+eng: ""
+voice: "plug-in"
+new:
+
+id: VOICE_EXT_FONT
+desc: spoken only, for file extension
+eng: ""
+voice: "font"
+new:
+
+id: VOICE_EXT_BMARK
+desc: spoken only, for file extension
+eng: ""
+voice: "bookmark"
+new:
+
+id: VOICE_EXT_UCL
+desc: spoken only, for file extension
+eng: ""
+voice: "flash"
+new:
+
+id: VOICE_EXT_AJZ
+desc: spoken only, for file extension
+eng: ""
+voice: "firmware"
+new:
+
diff --git a/apps/settings.c b/apps/settings.c
index a9691f3..2e1a664 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -1203,13 +1203,13 @@
         }
         else if (!strcasecmp(name, "talk dir"))
         {
-            static char* options[] = {"off", "number", "enter", "hover"};
-            set_cfg_option(&global_settings.talk_dir, value, options, 4);
+            static char* options[] = {"off", "number", "spell", "enter", "hover"};
+            set_cfg_option(&global_settings.talk_dir, value, options, 5);
         }
         else if (!strcasecmp(name, "talk file"))
         {
-            static char* options[] = {"off", "number"};
-            set_cfg_option(&global_settings.talk_dir, value, options, 2);
+            static char* options[] = {"off", "number", "spell"};
+            set_cfg_option(&global_settings.talk_file, value, options, 3);
         }
         else if (!strcasecmp(name, "talk menu"))
         {
@@ -1563,11 +1563,11 @@
     }
     fprintf(fd, "#\r\n# Voice\r\n#\r\n");
     {
-        static char* options[] = {"off", "number", "enter", "hover"};
+        static char* options[] = {"off", "number", "spell", "enter", "hover"};
         fprintf(fd, "talk dir: %s\r\n",
             options[global_settings.talk_dir]);
         fprintf(fd, "talk file: %s\r\n", /* recycle the options, */
-            options[global_settings.talk_file]); /* first 2 are alike */
+            options[global_settings.talk_file]); /* first 3 are alike */
         fprintf(fd, "talk menu: %s\r\n",
             boolopt[global_settings.talk_menu]);
     }
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index a0f039b..6cdf262 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -891,21 +891,23 @@
     struct opt_items names[] = {
         { STR(LANG_OFF) }, 
         { STR(LANG_VOICE_NUMBER) },
+        { STR(LANG_VOICE_SPELL) },
         { STR(LANG_VOICE_DIR_ENTER) },
         { STR(LANG_VOICE_DIR_HOVER) }
     };
     return set_option( str(LANG_VOICE_DIR), 
-                       &global_settings.talk_dir, INT, names, 4, NULL);
+                       &global_settings.talk_dir, INT, names, 5, NULL);
 }
 
 static bool voice_files(void)
 {
     struct opt_items names[] = {
         { STR(LANG_OFF) }, 
-        { STR(LANG_VOICE_NUMBER) }
+        { STR(LANG_VOICE_NUMBER) },
+        { STR(LANG_VOICE_SPELL) }
     };
-    return set_option( str(LANG_VOICE_DIR), 
-                       &global_settings.talk_file, INT, names, 2, NULL);
+    return set_option( str(LANG_VOICE_FILE), 
+                       &global_settings.talk_file, INT, names, 3, NULL);
 }
 
 static bool voice_menu(void)
diff --git a/apps/talk.c b/apps/talk.c
index 7ba858b..a0c730c 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -35,7 +35,7 @@
 
 /***************** Constants *****************/
 
-#define QUEUE_SIZE 20
+#define QUEUE_SIZE 50
 const char* voicefont_file = "/.rockbox/langs/english.voice";
 const char* dir_thumbnail_name = ".dirname.mp3";
 
@@ -487,3 +487,33 @@
     return 0;
 }
 
+/* spell a string */
+int talk_spell(char* spell, bool enqueue) 
+{
+    char c; /* currently processed char */
+    
+    if (mpeg_status()) /* busy, buffer in use */
+        return -1; 
+
+    if (!enqueue)
+        shutup(); /* cut off all the pending stuff */
+    
+    while ((c = *spell++) != '\0')
+    {
+        /* 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_POINT, true);
+    }
+
+    return 0;
+}
diff --git a/apps/talk.h b/apps/talk.h
index f3c1366..51504c1 100644
--- a/apps/talk.h
+++ b/apps/talk.h
@@ -64,5 +64,6 @@
 int talk_file(char* filename, bool enqueue); /* play a thumbnail from file */
 int talk_number(int n, bool enqueue); /* say a number */
 int talk_value(int n, int unit, bool enqueue); /* say a numeric value */
+int talk_spell(char* spell, bool enqueue); /* spell a string */
 
 #endif /* __TALK_H__ */
diff --git a/apps/tree.c b/apps/tree.c
index 8bd21bb..01aabf7 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -70,32 +70,33 @@
     char* extension; /* extension for which the file type is recognized */
     int tree_attr; /* which identifier */
     int icon; /* the icon which shall be used for it, -1 if unknown */
+    int voiceclip; /* spoken extension */
     /* To have it extendable, there could be more useful stuff in here,
        like handler functions, plugin name, etc. */
 } filetypes[] = {
-    { ".mp3", TREE_ATTR_MPA, File     },
-    { ".mp2", TREE_ATTR_MPA, File     },
-    { ".mpa", TREE_ATTR_MPA, File     },
-    { ".m3u", TREE_ATTR_M3U, Playlist },
-    { ".cfg", TREE_ATTR_CFG, Config   },
-    { ".wps", TREE_ATTR_WPS, Wps,     },
-    { ".txt", TREE_ATTR_TXT, Text     },
-    { ".lng", TREE_ATTR_LNG, Language },
-    { ".rock",TREE_ATTR_ROCK,Plugin   },
+    { ".mp3", TREE_ATTR_MPA, File, VOICE_EXT_MPA },
+    { ".mp2", TREE_ATTR_MPA, File, VOICE_EXT_MPA },
+    { ".mpa", TREE_ATTR_MPA, File, VOICE_EXT_MPA },
+    { ".m3u", TREE_ATTR_M3U, Playlist, LANG_PLAYINDICES_PLAYLIST },
+    { ".cfg", TREE_ATTR_CFG, Config, VOICE_EXT_CFG },
+    { ".wps", TREE_ATTR_WPS, Wps, VOICE_EXT_WPS },
+    { ".txt", TREE_ATTR_TXT, Text, VOICE_EXT_TXT },
+    { ".lng", TREE_ATTR_LNG, Language, LANG_LANGUAGE },
+    { ".rock",TREE_ATTR_ROCK,Plugin, VOICE_EXT_ROCK },
 #ifdef HAVE_LCD_BITMAP
-    { ".fnt", TREE_ATTR_FONT,Font     },
-    { ".ch8", TREE_ATTR_CH8, Chip8    },
-    { ".rvf", TREE_ATTR_RVF, Video    },
-    { ".bmark",TREE_ATTR_BMARK,Bookmark   },
+    { ".fnt", TREE_ATTR_FONT,Font, VOICE_EXT_FONT },
+    { ".ch8", TREE_ATTR_CH8, Chip8, -1 },
+    { ".rvf", TREE_ATTR_RVF, Video, -1 },
+    { ".bmark",TREE_ATTR_BMARK, Bookmark, VOICE_EXT_BMARK },
 #else
-   {  ".bmark", TREE_ATTR_BMARK, -1       },
+    {  ".bmark", TREE_ATTR_BMARK, -1, VOICE_EXT_BMARK },
 #endif
 #ifndef SIMULATOR
 #ifdef HAVE_LCD_BITMAP
-    { ".ucl", TREE_ATTR_UCL, Flashfile},
-    { ".ajz", TREE_ATTR_MOD, Mod_Ajz  },
+    { ".ucl", TREE_ATTR_UCL, Flashfile, VOICE_EXT_UCL },
+    { ".ajz", TREE_ATTR_MOD, Mod_Ajz, VOICE_EXT_AJZ },
 #else
-    { ".mod", TREE_ATTR_MOD, Mod_Ajz  },
+    { ".mod", TREE_ATTR_MOD, Mod_Ajz, VOICE_EXT_AJZ },
 #endif
 #endif /* #ifndef SIMULATOR */
 };
@@ -195,7 +196,7 @@
 #endif /* HAVE_RECORDER_KEYPAD */
 
 /* talkbox hovering delay, to avoid immediate disk activity */
-#define HOVER_DELAY (HZ)
+#define HOVER_DELAY (HZ/2)
 
 static int build_playlist(int start_index)
 {
@@ -1061,7 +1062,7 @@
                     snprintf(buf,sizeof(buf),"/%s",file->name);
 
                 if (file->attr & ATTR_DIRECTORY) {
-                    if (global_settings.talk_dir == 2) /* enter */
+                    if (global_settings.talk_dir == 3) /* enter */
                     {
                         /* play_dirname */
                         DEBUGF("Playing directory thumbnail: %s", currdir);
@@ -1493,7 +1494,7 @@
                 if (dircache[i].attr & ATTR_DIRECTORY) /* directory? */
                 {
                     /* play directory thumbnail */
-                    if (global_settings.talk_dir == 3) /* hover */
+                    if (global_settings.talk_dir == 4) /* hover */
                     {   /* "schedule" a thumbnail, to have a little dalay */
                         thumbnail_time = current_tick + HOVER_DELAY;
                     }
@@ -1502,12 +1503,35 @@
                         talk_id(VOICE_DIR, false);
                         talk_number(i+1, true);
                     }
+                    else if (global_settings.talk_dir == 2) /* dirs spelled */
+                    {
+                        talk_spell(dircache[i].name, false);
+                    }
                 }
                 else if (global_settings.talk_file == 1) /* files as numbers */
                 {
+                    /* try to find a voice ID for the extension, if known */
+                    int j;
+                    int ext_id = -1; /* default to none */
+                    for (j=0; j<sizeof(filetypes)/sizeof(*filetypes); j++)
+                    {
+                        if ((dircache[i].attr & TREE_ATTR_MASK) == filetypes[j].tree_attr)
+                        {
+                            ext_id = filetypes[j].voiceclip;
+                            break;
+                        }
+                    }
+
                     /* enqueue_next is true if still talking the dir name */
                     talk_id(VOICE_FILE, enqueue_next);
                     talk_number(i-dirsindir+1, true);
+                    talk_id(ext_id, true);
+                    enqueue_next = false;
+                }
+                else if (global_settings.talk_file == 2) /* files spelled */
+                {
+                    /* enqueue_next is true if still talking the dir name */
+                    talk_spell(dircache[i].name, enqueue_next);
                     enqueue_next = false;
                 }