diff options
author | Jörg Hohensohn <hohensoh@rockbox.org> | 2004-03-19 22:15:53 +0000 |
---|---|---|
committer | Jörg Hohensohn <hohensoh@rockbox.org> | 2004-03-19 22:15:53 +0000 |
commit | fa97f161abc45bfd5db86bceb8803d2661e65447 (patch) | |
tree | 1e06ac8c47bd9165e46e85235f059372ddad2b90 /apps | |
parent | 239b70fad38be55075dd14ea8e3c0f6f54a1f35b (diff) | |
download | rockbox-fa97f161abc45bfd5db86bceb8803d2661e65447.tar.gz rockbox-fa97f161abc45bfd5db86bceb8803d2661e65447.zip |
Third step of the voice-UI: numerical settings are spoken (composed at runtime), needs new voicefont at the new location ".rockbox/langs/english.voice"
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4414 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r-- | apps/bookmark.c | 1 | ||||
-rw-r--r-- | apps/main_menu.c | 1 | ||||
-rw-r--r-- | apps/playlist.c | 1 | ||||
-rw-r--r-- | apps/playlist_menu.c | 2 | ||||
-rw-r--r-- | apps/playlist_viewer.c | 2 | ||||
-rw-r--r-- | apps/recorder/radio.c | 8 | ||||
-rw-r--r-- | apps/recorder/recording.c | 14 | ||||
-rw-r--r-- | apps/settings.h | 3 | ||||
-rw-r--r-- | apps/settings_menu.c | 209 | ||||
-rw-r--r-- | apps/sound_menu.c | 111 | ||||
-rw-r--r-- | apps/talk.c | 398 | ||||
-rw-r--r-- | apps/talk.h | 61 |
12 files changed, 651 insertions, 160 deletions
diff --git a/apps/bookmark.c b/apps/bookmark.c index 32521a3868..b376cdfa20 100644 --- a/apps/bookmark.c +++ b/apps/bookmark.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include "debug.h" | 44 | #include "debug.h" |
45 | #include "kernel.h" | 45 | #include "kernel.h" |
46 | #include "sprintf.h" | 46 | #include "sprintf.h" |
47 | #include "talk.h" | ||
47 | 48 | ||
48 | #define MAX_BOOKMARKS 10 | 49 | #define MAX_BOOKMARKS 10 |
49 | #define MAX_BOOKMARK_SIZE 350 | 50 | #define MAX_BOOKMARK_SIZE 350 |
diff --git a/apps/main_menu.c b/apps/main_menu.c index 6fec696240..dc6e84d626 100644 --- a/apps/main_menu.c +++ b/apps/main_menu.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include "buffer.h" | 44 | #include "buffer.h" |
45 | #include "screens.h" | 45 | #include "screens.h" |
46 | #include "playlist_menu.h" | 46 | #include "playlist_menu.h" |
47 | #include "talk.h" | ||
47 | #ifdef HAVE_FMRADIO | 48 | #ifdef HAVE_FMRADIO |
48 | #include "radio.h" | 49 | #include "radio.h" |
49 | #endif | 50 | #endif |
diff --git a/apps/playlist.c b/apps/playlist.c index 53a18c606e..f38f215361 100644 --- a/apps/playlist.c +++ b/apps/playlist.c | |||
@@ -1596,6 +1596,7 @@ int playlist_start(int start_index, int offset) | |||
1596 | struct playlist_info* playlist = ¤t_playlist; | 1596 | struct playlist_info* playlist = ¤t_playlist; |
1597 | 1597 | ||
1598 | playlist->index = start_index; | 1598 | playlist->index = start_index; |
1599 | talk_buffer_steal(); /* will use the mp3 buffer */ | ||
1599 | mpeg_play(offset); | 1600 | mpeg_play(offset); |
1600 | 1601 | ||
1601 | return 0; | 1602 | return 0; |
diff --git a/apps/playlist_menu.c b/apps/playlist_menu.c index da1f9f3734..d1c69bfdae 100644 --- a/apps/playlist_menu.c +++ b/apps/playlist_menu.c | |||
@@ -26,7 +26,7 @@ | |||
26 | #include "tree.h" | 26 | #include "tree.h" |
27 | #include "settings.h" | 27 | #include "settings.h" |
28 | #include "playlist_viewer.h" | 28 | #include "playlist_viewer.h" |
29 | 29 | #include "talk.h" | |
30 | #include "lang.h" | 30 | #include "lang.h" |
31 | 31 | ||
32 | #define DEFAULT_PLAYLIST_NAME "/dynamic.m3u" | 32 | #define DEFAULT_PLAYLIST_NAME "/dynamic.m3u" |
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c index e41b942538..d7c34e4300 100644 --- a/apps/playlist_viewer.c +++ b/apps/playlist_viewer.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include "keyboard.h" | 31 | #include "keyboard.h" |
32 | #include "tree.h" | 32 | #include "tree.h" |
33 | #include "onplay.h" | 33 | #include "onplay.h" |
34 | #include "talk.h" | ||
34 | 35 | ||
35 | #ifdef HAVE_LCD_BITMAP | 36 | #ifdef HAVE_LCD_BITMAP |
36 | #include "widgets.h" | 37 | #include "widgets.h" |
@@ -721,6 +722,7 @@ static int onplay_menu(int index) | |||
721 | if (tracks[index].display_index != viewer.num_tracks || | 722 | if (tracks[index].display_index != viewer.num_tracks || |
722 | global_settings.repeat_mode == REPEAT_ALL) | 723 | global_settings.repeat_mode == REPEAT_ALL) |
723 | { | 724 | { |
725 | talk_buffer_steal(); /* will use the mp3 buffer */ | ||
724 | mpeg_play(0); | 726 | mpeg_play(0); |
725 | viewer.current_playing_track = -1; | 727 | viewer.current_playing_track = -1; |
726 | } | 728 | } |
diff --git a/apps/recorder/radio.c b/apps/recorder/radio.c index 5430279f85..f8da238c6f 100644 --- a/apps/recorder/radio.c +++ b/apps/recorder/radio.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include "font.h" | 46 | #include "font.h" |
47 | #include "sound_menu.h" | 47 | #include "sound_menu.h" |
48 | #include "recording.h" | 48 | #include "recording.h" |
49 | #include "talk.h" | ||
49 | 50 | ||
50 | #ifdef HAVE_FMRADIO | 51 | #ifdef HAVE_FMRADIO |
51 | 52 | ||
@@ -174,6 +175,9 @@ bool radio_screen(void) | |||
174 | 175 | ||
175 | peak_meter_enabled = true; | 176 | peak_meter_enabled = true; |
176 | 177 | ||
178 | if (global_settings.rec_prerecord_time) | ||
179 | talk_buffer_steal(); /* will use the mp3 buffer */ | ||
180 | |||
177 | mpeg_set_recording_options(global_settings.rec_frequency, | 181 | mpeg_set_recording_options(global_settings.rec_frequency, |
178 | global_settings.rec_quality, | 182 | global_settings.rec_quality, |
179 | 1, /* Line In */ | 183 | 1, /* Line In */ |
@@ -257,6 +261,7 @@ bool radio_screen(void) | |||
257 | else | 261 | else |
258 | { | 262 | { |
259 | have_recorded = true; | 263 | have_recorded = true; |
264 | talk_buffer_steal(); /* we use the mp3 buffer */ | ||
260 | mpeg_record(rec_create_filename(buf)); | 265 | mpeg_record(rec_create_filename(buf)); |
261 | status_set_playmode(STATUS_RECORD); | 266 | status_set_playmode(STATUS_RECORD); |
262 | update_screen = true; | 267 | update_screen = true; |
@@ -704,6 +709,9 @@ static bool fm_recording_settings(void) | |||
704 | ret = recording_menu(true); | 709 | ret = recording_menu(true); |
705 | if(!ret) | 710 | if(!ret) |
706 | { | 711 | { |
712 | if (global_settings.rec_prerecord_time) | ||
713 | talk_buffer_steal(); /* will use the mp3 buffer */ | ||
714 | |||
707 | mpeg_set_recording_options(global_settings.rec_frequency, | 715 | mpeg_set_recording_options(global_settings.rec_frequency, |
708 | global_settings.rec_quality, | 716 | global_settings.rec_quality, |
709 | 1, /* Line In */ | 717 | 1, /* Line In */ |
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c index c7d4b803d2..c2f2462a5c 100644 --- a/apps/recorder/recording.c +++ b/apps/recorder/recording.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include "string.h" | 46 | #include "string.h" |
47 | #include "dir.h" | 47 | #include "dir.h" |
48 | #include "errno.h" | 48 | #include "errno.h" |
49 | #include "talk.h" | ||
49 | 50 | ||
50 | bool f2_rec_screen(void); | 51 | bool f2_rec_screen(void); |
51 | bool f3_rec_screen(void); | 52 | bool f3_rec_screen(void); |
@@ -166,6 +167,9 @@ bool recording_screen(void) | |||
166 | 167 | ||
167 | peak_meter_enabled = true; | 168 | peak_meter_enabled = true; |
168 | 169 | ||
170 | if (global_settings.rec_prerecord_time) | ||
171 | talk_buffer_steal(); /* will use the mp3 buffer */ | ||
172 | |||
169 | mpeg_set_recording_options(global_settings.rec_frequency, | 173 | mpeg_set_recording_options(global_settings.rec_frequency, |
170 | global_settings.rec_quality, | 174 | global_settings.rec_quality, |
171 | global_settings.rec_source, | 175 | global_settings.rec_source, |
@@ -223,6 +227,7 @@ bool recording_screen(void) | |||
223 | if(!(mpeg_status() & MPEG_STATUS_RECORD)) | 227 | if(!(mpeg_status() & MPEG_STATUS_RECORD)) |
224 | { | 228 | { |
225 | have_recorded = true; | 229 | have_recorded = true; |
230 | talk_buffer_steal(); /* we use the mp3 buffer */ | ||
226 | mpeg_record(rec_create_filename(path_buffer)); | 231 | mpeg_record(rec_create_filename(path_buffer)); |
227 | status_set_playmode(STATUS_RECORD); | 232 | status_set_playmode(STATUS_RECORD); |
228 | update_countdown = 1; /* Update immediately */ | 233 | update_countdown = 1; /* Update immediately */ |
@@ -336,6 +341,9 @@ bool recording_screen(void) | |||
336 | return SYS_USB_CONNECTED; | 341 | return SYS_USB_CONNECTED; |
337 | settings_save(); | 342 | settings_save(); |
338 | 343 | ||
344 | if (global_settings.rec_prerecord_time) | ||
345 | talk_buffer_steal(); /* will use the mp3 buffer */ | ||
346 | |||
339 | mpeg_set_recording_options(global_settings.rec_frequency, | 347 | mpeg_set_recording_options(global_settings.rec_frequency, |
340 | global_settings.rec_quality, | 348 | global_settings.rec_quality, |
341 | global_settings.rec_source, | 349 | global_settings.rec_source, |
@@ -660,6 +668,9 @@ bool f2_rec_screen(void) | |||
660 | } | 668 | } |
661 | } | 669 | } |
662 | 670 | ||
671 | if (global_settings.rec_prerecord_time) | ||
672 | talk_buffer_steal(); /* will use the mp3 buffer */ | ||
673 | |||
663 | mpeg_set_recording_options(global_settings.rec_frequency, | 674 | mpeg_set_recording_options(global_settings.rec_frequency, |
664 | global_settings.rec_quality, | 675 | global_settings.rec_quality, |
665 | global_settings.rec_source, | 676 | global_settings.rec_source, |
@@ -730,6 +741,9 @@ bool f3_rec_screen(void) | |||
730 | } | 741 | } |
731 | } | 742 | } |
732 | 743 | ||
744 | if (global_settings.rec_prerecord_time) | ||
745 | talk_buffer_steal(); /* will use the mp3 buffer */ | ||
746 | |||
733 | mpeg_set_recording_options(global_settings.rec_frequency, | 747 | mpeg_set_recording_options(global_settings.rec_frequency, |
734 | global_settings.rec_quality, | 748 | global_settings.rec_quality, |
735 | global_settings.rec_source, | 749 | global_settings.rec_source, |
diff --git a/apps/settings.h b/apps/settings.h index d5992ccd79..b51c4d8e47 100644 --- a/apps/settings.h +++ b/apps/settings.h | |||
@@ -60,9 +60,6 @@ | |||
60 | #define FF_REWIND_45000 12 | 60 | #define FF_REWIND_45000 12 |
61 | #define FF_REWIND_60000 13 | 61 | #define FF_REWIND_60000 13 |
62 | 62 | ||
63 | /* convenience macro to have both string and ID as arguments */ | ||
64 | #define STR(id) str(id), id | ||
65 | |||
66 | 63 | ||
67 | struct user_settings | 64 | struct user_settings |
68 | { | 65 | { |
diff --git a/apps/settings_menu.c b/apps/settings_menu.c index 64df007afe..e1dcf6582c 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include "ata.h" | 40 | #include "ata.h" |
41 | #include "tree.h" | 41 | #include "tree.h" |
42 | #include "screens.h" | 42 | #include "screens.h" |
43 | #include "talk.h" | ||
43 | #ifdef HAVE_LCD_BITMAP | 44 | #ifdef HAVE_LCD_BITMAP |
44 | #include "peakmeter.h" | 45 | #include "peakmeter.h" |
45 | #endif | 46 | #endif |
@@ -167,23 +168,23 @@ static bool peak_meter_hold(void) { | |||
167 | bool retval = false; | 168 | bool retval = false; |
168 | struct opt_items names[] = { | 169 | struct opt_items names[] = { |
169 | { STR(LANG_OFF) }, | 170 | { STR(LANG_OFF) }, |
170 | { "200 ms " , -1 }, | 171 | { "200 ms " , TALK_ID(200, UNIT_MS) }, |
171 | { "300 ms " , -1 }, | 172 | { "300 ms " , TALK_ID(300, UNIT_MS) }, |
172 | { "500 ms " , -1 }, | 173 | { "500 ms " , TALK_ID(500, UNIT_MS) }, |
173 | { "1 s " , -1 }, | 174 | { "1 s" , TALK_ID(1, UNIT_SEC) }, |
174 | { "2 s " , -1 }, | 175 | { "2 s" , TALK_ID(2, UNIT_SEC) }, |
175 | { "3 s " , -1 }, | 176 | { "3 s" , TALK_ID(3, UNIT_SEC) }, |
176 | { "4 s " , -1 }, | 177 | { "4 s" , TALK_ID(4, UNIT_SEC) }, |
177 | { "5 s " , -1 }, | 178 | { "5 s" , TALK_ID(5, UNIT_SEC) }, |
178 | { "6 s " , -1 }, | 179 | { "6 s" , TALK_ID(6, UNIT_SEC) }, |
179 | { "7 s" , -1 }, | 180 | { "7 s" , TALK_ID(7, UNIT_SEC) }, |
180 | { "8 s" , -1 }, | 181 | { "8 s" , TALK_ID(8, UNIT_SEC) }, |
181 | { "9 s" , -1 }, | 182 | { "9 s" , TALK_ID(9, UNIT_SEC) }, |
182 | { "10 s" , -1 }, | 183 | { "10 s" , TALK_ID(10, UNIT_SEC) }, |
183 | { "15 s" , -1 }, | 184 | { "15 s" , TALK_ID(15, UNIT_SEC) }, |
184 | { "20 s" , -1 }, | 185 | { "20 s" , TALK_ID(20, UNIT_SEC) }, |
185 | { "30 s" , -1 }, | 186 | { "30 s" , TALK_ID(30, UNIT_SEC) }, |
186 | { "1 min" , -1 } | 187 | { "1 min" , TALK_ID(1, UNIT_MIN) } |
187 | }; | 188 | }; |
188 | retval = set_option( str(LANG_PM_PEAK_HOLD), | 189 | retval = set_option( str(LANG_PM_PEAK_HOLD), |
189 | &global_settings.peak_meter_hold, INT, names, | 190 | &global_settings.peak_meter_hold, INT, names, |
@@ -204,30 +205,30 @@ static bool peak_meter_clip_hold(void) { | |||
204 | 205 | ||
205 | struct opt_items names[] = { | 206 | struct opt_items names[] = { |
206 | { STR(LANG_PM_ETERNAL) }, | 207 | { STR(LANG_PM_ETERNAL) }, |
207 | { "1s " , -1 }, | 208 | { "1s " , TALK_ID(1, UNIT_SEC) }, |
208 | { "2s " , -1 }, | 209 | { "2s " , TALK_ID(2, UNIT_SEC) }, |
209 | { "3s " , -1 }, | 210 | { "3s " , TALK_ID(3, UNIT_SEC) }, |
210 | { "4s " , -1 }, | 211 | { "4s " , TALK_ID(4, UNIT_SEC) }, |
211 | { "5s " , -1 }, | 212 | { "5s " , TALK_ID(5, UNIT_SEC) }, |
212 | { "6s " , -1 }, | 213 | { "6s " , TALK_ID(6, UNIT_SEC) }, |
213 | { "7s " , -1 }, | 214 | { "7s " , TALK_ID(7, UNIT_SEC) }, |
214 | { "8s " , -1 }, | 215 | { "8s " , TALK_ID(8, UNIT_SEC) }, |
215 | { "9s " , -1 }, | 216 | { "9s " , TALK_ID(9, UNIT_SEC) }, |
216 | { "10s" , -1 }, | 217 | { "10s" , TALK_ID(10, UNIT_SEC) }, |
217 | { "15s" , -1 }, | 218 | { "15s" , TALK_ID(15, UNIT_SEC) }, |
218 | { "20s" , -1 }, | 219 | { "20s" , TALK_ID(20, UNIT_SEC) }, |
219 | { "25s" , -1 }, | 220 | { "25s" , TALK_ID(25, UNIT_SEC) }, |
220 | { "30s" , -1 }, | 221 | { "30s" , TALK_ID(30, UNIT_SEC) }, |
221 | { "45s" , -1 }, | 222 | { "45s" , TALK_ID(45, UNIT_SEC) }, |
222 | { "60s" , -1 }, | 223 | { "60s" , TALK_ID(60, UNIT_SEC) }, |
223 | { "90s" , -1 }, | 224 | { "90s" , TALK_ID(90, UNIT_SEC) }, |
224 | { "2min" , -1 }, | 225 | { "2min" , TALK_ID(2, UNIT_MIN) }, |
225 | { "3min" , -1 }, | 226 | { "3min" , TALK_ID(3, UNIT_MIN) }, |
226 | { "5min" , -1 }, | 227 | { "5min" , TALK_ID(5, UNIT_MIN) }, |
227 | { "10min" , -1 }, | 228 | { "10min" , TALK_ID(10, UNIT_MIN) }, |
228 | { "20min" , -1 }, | 229 | { "20min" , TALK_ID(20, UNIT_MIN) }, |
229 | { "45min" , -1 }, | 230 | { "45min" , TALK_ID(45, UNIT_MIN) }, |
230 | { "90min" , -1 } | 231 | { "90min" , TALK_ID(90, UNIT_MIN) } |
231 | }; | 232 | }; |
232 | retval = set_option( str(LANG_PM_CLIP_HOLD), | 233 | retval = set_option( str(LANG_PM_CLIP_HOLD), |
233 | &global_settings.peak_meter_clip_hold, INT, names, | 234 | &global_settings.peak_meter_clip_hold, INT, names, |
@@ -531,23 +532,23 @@ static bool backlight_timer(void) | |||
531 | struct opt_items names[] = { | 532 | struct opt_items names[] = { |
532 | { STR(LANG_OFF) }, | 533 | { STR(LANG_OFF) }, |
533 | { STR(LANG_ON) }, | 534 | { STR(LANG_ON) }, |
534 | { "1s ", -1 }, | 535 | { "1s ", TALK_ID(1, UNIT_SEC) }, |
535 | { "2s ", -1 }, | 536 | { "2s ", TALK_ID(2, UNIT_SEC) }, |
536 | { "3s ", -1 }, | 537 | { "3s ", TALK_ID(3, UNIT_SEC) }, |
537 | { "4s ", -1 }, | 538 | { "4s ", TALK_ID(4, UNIT_SEC) }, |
538 | { "5s ", -1 }, | 539 | { "5s ", TALK_ID(5, UNIT_SEC) }, |
539 | { "6s ", -1 }, | 540 | { "6s ", TALK_ID(6, UNIT_SEC) }, |
540 | { "7s ", -1 }, | 541 | { "7s ", TALK_ID(7, UNIT_SEC) }, |
541 | { "8s ", -1 }, | 542 | { "8s ", TALK_ID(8, UNIT_SEC) }, |
542 | { "9s ", -1 }, | 543 | { "9s ", TALK_ID(9, UNIT_SEC) }, |
543 | { "10s", -1 }, | 544 | { "10s", TALK_ID(10, UNIT_SEC) }, |
544 | { "15s", -1 }, | 545 | { "15s", TALK_ID(15, UNIT_SEC) }, |
545 | { "20s", -1 }, | 546 | { "20s", TALK_ID(20, UNIT_SEC) }, |
546 | { "25s", -1 }, | 547 | { "25s", TALK_ID(25, UNIT_SEC) }, |
547 | { "30s", -1 }, | 548 | { "30s", TALK_ID(30, UNIT_SEC) }, |
548 | { "45s", -1 }, | 549 | { "45s", TALK_ID(45, UNIT_SEC) }, |
549 | { "60s", -1 }, | 550 | { "60s", TALK_ID(60, UNIT_SEC) }, |
550 | { "90s", -1 } | 551 | { "90s", TALK_ID(90, UNIT_SEC) } |
551 | }; | 552 | }; |
552 | return set_option(str(LANG_BACKLIGHT), &global_settings.backlight_timeout, | 553 | return set_option(str(LANG_BACKLIGHT), &global_settings.backlight_timeout, |
553 | INT, names, 19, backlight_set_timeout ); | 554 | INT, names, 19, backlight_set_timeout ); |
@@ -557,20 +558,20 @@ static bool poweroff_idle_timer(void) | |||
557 | { | 558 | { |
558 | struct opt_items names[] = { | 559 | struct opt_items names[] = { |
559 | { STR(LANG_OFF) }, | 560 | { STR(LANG_OFF) }, |
560 | { "1m ", -1 }, | 561 | { "1m ", TALK_ID(1, UNIT_MIN) }, |
561 | { "2m ", -1 }, | 562 | { "2m ", TALK_ID(2, UNIT_MIN) }, |
562 | { "3m ", -1 }, | 563 | { "3m ", TALK_ID(3, UNIT_MIN) }, |
563 | { "4m ", -1 }, | 564 | { "4m ", TALK_ID(4, UNIT_MIN) }, |
564 | { "5m ", -1 }, | 565 | { "5m ", TALK_ID(5, UNIT_MIN) }, |
565 | { "6m ", -1 }, | 566 | { "6m ", TALK_ID(6, UNIT_MIN) }, |
566 | { "7m ", -1 }, | 567 | { "7m ", TALK_ID(7, UNIT_MIN) }, |
567 | { "8m ", -1 }, | 568 | { "8m ", TALK_ID(8, UNIT_MIN) }, |
568 | { "9m ", -1 }, | 569 | { "9m ", TALK_ID(9, UNIT_MIN) }, |
569 | { "10m", -1 }, | 570 | { "10m", TALK_ID(10, UNIT_MIN) }, |
570 | { "15m", -1 }, | 571 | { "15m", TALK_ID(15, UNIT_MIN) }, |
571 | { "30m", -1 }, | 572 | { "30m", TALK_ID(30, UNIT_MIN) }, |
572 | { "45m", -1 }, | 573 | { "45m", TALK_ID(45, UNIT_MIN) }, |
573 | { "60m", -1 } | 574 | { "60m", TALK_ID(60, UNIT_MIN) } |
574 | }; | 575 | }; |
575 | return set_option(str(LANG_POWEROFF_IDLE), &global_settings.poweroff, | 576 | return set_option(str(LANG_POWEROFF_IDLE), &global_settings.poweroff, |
576 | INT, names, 15, set_poweroff_timeout); | 577 | INT, names, 15, set_poweroff_timeout); |
@@ -613,9 +614,9 @@ static bool jump_scroll(void) | |||
613 | struct opt_items names[] = { | 614 | struct opt_items names[] = { |
614 | { STR(LANG_OFF) }, | 615 | { STR(LANG_OFF) }, |
615 | { STR(LANG_ONE_TIME) }, | 616 | { STR(LANG_ONE_TIME) }, |
616 | { "2", -1 }, | 617 | { "2", TALK_ID(2, UNIT_INT) }, |
617 | { "3", -1 }, | 618 | { "3", TALK_ID(3, UNIT_INT) }, |
618 | { "4", -1 }, | 619 | { "4", TALK_ID(4, UNIT_INT) }, |
619 | { STR(LANG_ALWAYS) } | 620 | { STR(LANG_ALWAYS) } |
620 | }; | 621 | }; |
621 | bool ret; | 622 | bool ret; |
@@ -799,20 +800,20 @@ static bool buffer_margin(void) | |||
799 | static bool ff_rewind_min_step(void) | 800 | static bool ff_rewind_min_step(void) |
800 | { | 801 | { |
801 | struct opt_items names[] = { | 802 | struct opt_items names[] = { |
802 | { "1s", -1 }, | 803 | { "1s", TALK_ID(1, UNIT_SEC) }, |
803 | { "2s", -1 }, | 804 | { "2s", TALK_ID(2, UNIT_SEC) }, |
804 | { "3s", -1 }, | 805 | { "3s", TALK_ID(3, UNIT_SEC) }, |
805 | { "4s", -1 }, | 806 | { "4s", TALK_ID(4, UNIT_SEC) }, |
806 | { "5s", -1 }, | 807 | { "5s", TALK_ID(5, UNIT_SEC) }, |
807 | { "6s", -1 }, | 808 | { "6s", TALK_ID(6, UNIT_SEC) }, |
808 | { "8s", -1 }, | 809 | { "8s", TALK_ID(8, UNIT_SEC) }, |
809 | { "10s", -1 }, | 810 | { "10s", TALK_ID(10, UNIT_SEC) }, |
810 | { "15s", -1 }, | 811 | { "15s", TALK_ID(15, UNIT_SEC) }, |
811 | { "20s", -1 }, | 812 | { "20s", TALK_ID(20, UNIT_SEC) }, |
812 | { "25s", -1 }, | 813 | { "25s", TALK_ID(25, UNIT_SEC) }, |
813 | { "30s", -1 }, | 814 | { "30s", TALK_ID(30, UNIT_SEC) }, |
814 | { "45s", -1 }, | 815 | { "45s", TALK_ID(45, UNIT_SEC) }, |
815 | { "60s", -1 } | 816 | { "60s", TALK_ID(60, UNIT_SEC) } |
816 | }; | 817 | }; |
817 | return set_option(str(LANG_FFRW_STEP), &global_settings.ff_rewind_min_step, | 818 | return set_option(str(LANG_FFRW_STEP), &global_settings.ff_rewind_min_step, |
818 | INT, names, 14, NULL ); | 819 | INT, names, 14, NULL ); |
@@ -828,21 +829,21 @@ static bool ff_rewind_accel(void) | |||
828 | { | 829 | { |
829 | struct opt_items names[] = { | 830 | struct opt_items names[] = { |
830 | { STR(LANG_OFF) }, | 831 | { STR(LANG_OFF) }, |
831 | { "2x/1s", -1 }, | 832 | { "2x/1s", TALK_ID(1, UNIT_SEC) }, |
832 | { "2x/2s", -1 }, | 833 | { "2x/2s", TALK_ID(2, UNIT_SEC) }, |
833 | { "2x/3s", -1 }, | 834 | { "2x/3s", TALK_ID(3, UNIT_SEC) }, |
834 | { "2x/4s", -1 }, | 835 | { "2x/4s", TALK_ID(4, UNIT_SEC) }, |
835 | { "2x/5s", -1 }, | 836 | { "2x/5s", TALK_ID(5, UNIT_SEC) }, |
836 | { "2x/6s", -1 }, | 837 | { "2x/6s", TALK_ID(6, UNIT_SEC) }, |
837 | { "2x/7s", -1 }, | 838 | { "2x/7s", TALK_ID(7, UNIT_SEC) }, |
838 | { "2x/8s", -1 }, | 839 | { "2x/8s", TALK_ID(8, UNIT_SEC) }, |
839 | { "2x/9s", -1 }, | 840 | { "2x/9s", TALK_ID(9, UNIT_SEC) }, |
840 | { "2x/10s", -1 }, | 841 | { "2x/10s", TALK_ID(10, UNIT_SEC) }, |
841 | { "2x/11s", -1 }, | 842 | { "2x/11s", TALK_ID(11, UNIT_SEC) }, |
842 | { "2x/12s", -1 }, | 843 | { "2x/12s", TALK_ID(12, UNIT_SEC) }, |
843 | { "2x/13s", -1 }, | 844 | { "2x/13s", TALK_ID(13, UNIT_SEC) }, |
844 | { "2x/14s", -1 }, | 845 | { "2x/14s", TALK_ID(14, UNIT_SEC) }, |
845 | { "2x/15s", -1 } | 846 | { "2x/15s", TALK_ID(15, UNIT_SEC) } |
846 | }; | 847 | }; |
847 | return set_option(str(LANG_FFRW_ACCEL), &global_settings.ff_rewind_accel, | 848 | return set_option(str(LANG_FFRW_ACCEL), &global_settings.ff_rewind_accel, |
848 | INT, names, 16, NULL ); | 849 | INT, names, 16, NULL ); |
diff --git a/apps/sound_menu.c b/apps/sound_menu.c index 17f6eb5072..0001d8b2fe 100644 --- a/apps/sound_menu.c +++ b/apps/sound_menu.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #endif | 32 | #endif |
33 | #include "lang.h" | 33 | #include "lang.h" |
34 | #include "sprintf.h" | 34 | #include "sprintf.h" |
35 | #include "talk.h" | ||
35 | 36 | ||
36 | static char *fmt[] = | 37 | static char *fmt[] = |
37 | { | 38 | { |
@@ -53,11 +54,16 @@ bool set_sound(char* string, | |||
53 | int dec; | 54 | int dec; |
54 | char* unit; | 55 | char* unit; |
55 | char str[32]; | 56 | char str[32]; |
57 | int talkunit = UNIT_INT; | ||
56 | 58 | ||
57 | unit = mpeg_sound_unit(setting); | 59 | unit = mpeg_sound_unit(setting); |
58 | numdec = mpeg_sound_numdecimals(setting); | 60 | numdec = mpeg_sound_numdecimals(setting); |
59 | min = mpeg_sound_min(setting); | 61 | min = mpeg_sound_min(setting); |
60 | max = mpeg_sound_max(setting); | 62 | max = mpeg_sound_max(setting); |
63 | if (*unit == 'd') /* crude reconstruction */ | ||
64 | talkunit = UNIT_DB; | ||
65 | else if (*unit == '%') | ||
66 | talkunit = UNIT_PERCENT; | ||
61 | 67 | ||
62 | #ifdef HAVE_LCD_BITMAP | 68 | #ifdef HAVE_LCD_BITMAP |
63 | if(global_settings.statusbar) | 69 | if(global_settings.statusbar) |
@@ -81,6 +87,7 @@ bool set_sound(char* string, | |||
81 | { | 87 | { |
82 | snprintf(str,sizeof str,"%d %s ", val, unit); | 88 | snprintf(str,sizeof str,"%d %s ", val, unit); |
83 | } | 89 | } |
90 | talk_value(val, talkunit, false); /* speak it */ | ||
84 | } | 91 | } |
85 | lcd_puts(0,1,str); | 92 | lcd_puts(0,1,str); |
86 | status_draw(true); | 93 | status_draw(true); |
@@ -183,9 +190,9 @@ static bool avc(void) | |||
183 | { | 190 | { |
184 | struct opt_items names[] = { | 191 | struct opt_items names[] = { |
185 | { STR(LANG_OFF) }, | 192 | { STR(LANG_OFF) }, |
186 | { "2s", -1 }, | 193 | { "2s", TALK_ID(2, UNIT_SEC) }, |
187 | { "4s", -1 }, | 194 | { "4s", TALK_ID(4, UNIT_SEC) }, |
188 | { "8s", -1 } | 195 | { "8s", TALK_ID(8, UNIT_SEC) } |
189 | }; | 196 | }; |
190 | return set_option(str(LANG_DECAY), &global_settings.avc, INT, | 197 | return set_option(str(LANG_DECAY), &global_settings.avc, INT, |
191 | names, 4, set_avc); | 198 | names, 4, set_avc); |
@@ -206,12 +213,12 @@ static bool recsource(void) | |||
206 | static bool recfrequency(void) | 213 | static bool recfrequency(void) |
207 | { | 214 | { |
208 | struct opt_items names[] = { | 215 | struct opt_items names[] = { |
209 | { "44.1kHz", -1 }, | 216 | { "44.1kHz", TALK_ID(44, UNIT_KHZ) }, |
210 | { "48kHz", -1 }, | 217 | { "48kHz", TALK_ID(48, UNIT_KHZ) }, |
211 | { "32kHz", -1 }, | 218 | { "32kHz", TALK_ID(32, UNIT_KHZ) }, |
212 | { "22.05kHz", -1 }, | 219 | { "22.05kHz", TALK_ID(22, UNIT_KHZ) }, |
213 | { "24kHz", -1 }, | 220 | { "24kHz", TALK_ID(24, UNIT_KHZ) }, |
214 | { "16kHz", -1 } | 221 | { "16kHz", TALK_ID(16, UNIT_KHZ) } |
215 | }; | 222 | }; |
216 | return set_option(str(LANG_RECORDING_FREQUENCY), | 223 | return set_option(str(LANG_RECORDING_FREQUENCY), |
217 | &global_settings.rec_frequency, INT, | 224 | &global_settings.rec_frequency, INT, |
@@ -246,19 +253,19 @@ static bool rectimesplit(void) | |||
246 | { | 253 | { |
247 | struct opt_items names[] = { | 254 | struct opt_items names[] = { |
248 | { STR(LANG_OFF) }, | 255 | { STR(LANG_OFF) }, |
249 | { "00:05" , -1 }, | 256 | { "00:05" , TALK_ID(5, UNIT_MIN) }, |
250 | { "00:10" , -1 }, | 257 | { "00:10" , TALK_ID(10, UNIT_MIN) }, |
251 | { "00:15" , -1 }, | 258 | { "00:15" , TALK_ID(15, UNIT_MIN) }, |
252 | { "00:30" , -1 }, | 259 | { "00:30" , TALK_ID(30, UNIT_MIN) }, |
253 | { "01:00" , -1 }, | 260 | { "01:00" , TALK_ID(1, UNIT_HOUR) }, |
254 | { "02:00" , -1 }, | 261 | { "02:00" , TALK_ID(2, UNIT_HOUR) }, |
255 | { "04:00" , -1 }, | 262 | { "04:00" , TALK_ID(4, UNIT_HOUR) }, |
256 | { "06:00" , -1 }, | 263 | { "06:00" , TALK_ID(6, UNIT_HOUR) }, |
257 | { "08:00" , -1 }, | 264 | { "08:00" , TALK_ID(8, UNIT_HOUR) }, |
258 | { "10:00" , -1 }, | 265 | { "10:00" , TALK_ID(10, UNIT_HOUR) }, |
259 | { "12:00" , -1 }, | 266 | { "12:00" , TALK_ID(12, UNIT_HOUR) }, |
260 | { "18:00" , -1 }, | 267 | { "18:00" , TALK_ID(18, UNIT_HOUR) }, |
261 | { "24:00" , -1 } | 268 | { "24:00" , TALK_ID(24, UNIT_HOUR) } |
262 | }; | 269 | }; |
263 | return set_option(str(LANG_RECORD_TIMESPLIT), | 270 | return set_option(str(LANG_RECORD_TIMESPLIT), |
264 | &global_settings.rec_timesplit, INT, | 271 | &global_settings.rec_timesplit, INT, |
@@ -269,36 +276,36 @@ static bool recprerecord(void) | |||
269 | { | 276 | { |
270 | struct opt_items names[] = { | 277 | struct opt_items names[] = { |
271 | { STR(LANG_OFF) }, | 278 | { STR(LANG_OFF) }, |
272 | { "1s", -1 }, | 279 | { "1s", TALK_ID(1, UNIT_SEC) }, |
273 | { "2s", -1 }, | 280 | { "2s", TALK_ID(2, UNIT_SEC) }, |
274 | { "3s", -1 }, | 281 | { "3s", TALK_ID(3, UNIT_SEC) }, |
275 | { "4s", -1 }, | 282 | { "4s", TALK_ID(4, UNIT_SEC) }, |
276 | { "5s", -1 }, | 283 | { "5s", TALK_ID(5, UNIT_SEC) }, |
277 | { "6s", -1 }, | 284 | { "6s", TALK_ID(6, UNIT_SEC) }, |
278 | { "7s", -1 }, | 285 | { "7s", TALK_ID(7, UNIT_SEC) }, |
279 | { "8s", -1 }, | 286 | { "8s", TALK_ID(8, UNIT_SEC) }, |
280 | { "9s", -1 }, | 287 | { "9s", TALK_ID(9, UNIT_SEC) }, |
281 | { "10s", -1 }, | 288 | { "10s", TALK_ID(10, UNIT_SEC) }, |
282 | { "11s", -1 }, | 289 | { "11s", TALK_ID(11, UNIT_SEC) }, |
283 | { "12s", -1 }, | 290 | { "12s", TALK_ID(12, UNIT_SEC) }, |
284 | { "13s", -1 }, | 291 | { "13s", TALK_ID(13, UNIT_SEC) }, |
285 | { "14s", -1 }, | 292 | { "14s", TALK_ID(14, UNIT_SEC) }, |
286 | { "15s", -1 }, | 293 | { "15s", TALK_ID(15, UNIT_SEC) }, |
287 | { "16s", -1 }, | 294 | { "16s", TALK_ID(16, UNIT_SEC) }, |
288 | { "17s", -1 }, | 295 | { "17s", TALK_ID(17, UNIT_SEC) }, |
289 | { "18s", -1 }, | 296 | { "18s", TALK_ID(18, UNIT_SEC) }, |
290 | { "19s", -1 }, | 297 | { "19s", TALK_ID(19, UNIT_SEC) }, |
291 | { "10s", -1 }, | 298 | { "20s", TALK_ID(20, UNIT_SEC) }, |
292 | { "21s", -1 }, | 299 | { "21s", TALK_ID(21, UNIT_SEC) }, |
293 | { "22s", -1 }, | 300 | { "22s", TALK_ID(22, UNIT_SEC) }, |
294 | { "23s", -1 }, | 301 | { "23s", TALK_ID(23, UNIT_SEC) }, |
295 | { "24s", -1 }, | 302 | { "24s", TALK_ID(24, UNIT_SEC) }, |
296 | { "25s", -1 }, | 303 | { "25s", TALK_ID(25, UNIT_SEC) }, |
297 | { "26s", -1 }, | 304 | { "26s", TALK_ID(26, UNIT_SEC) }, |
298 | { "27s", -1 }, | 305 | { "27s", TALK_ID(27, UNIT_SEC) }, |
299 | { "28s", -1 }, | 306 | { "28s", TALK_ID(28, UNIT_SEC) }, |
300 | { "29s", -1 }, | 307 | { "29s", TALK_ID(29, UNIT_SEC) }, |
301 | { "30s", -1 } | 308 | { "30s", TALK_ID(30, UNIT_SEC) } |
302 | }; | 309 | }; |
303 | return set_option(str(LANG_RECORD_PRERECORD_TIME), | 310 | return set_option(str(LANG_RECORD_PRERECORD_TIME), |
304 | &global_settings.rec_prerecord_time, INT, | 311 | &global_settings.rec_prerecord_time, INT, |
diff --git a/apps/talk.c b/apps/talk.c new file mode 100644 index 0000000000..d208c7296c --- /dev/null +++ b/apps/talk.c | |||
@@ -0,0 +1,398 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2004 Jörg Hohensohn | ||
11 | * | ||
12 | * This module collects the Talkbox and voice UI functions. | ||
13 | * (Talkbox reads directory names from mp3 clips called thumbnails, | ||
14 | * the voice UI lets menus and screens "talk" from a voicefont in memory. | ||
15 | * | ||
16 | * All files in this archive are subject to the GNU General Public License. | ||
17 | * See the file COPYING in the source tree root for full license agreement. | ||
18 | * | ||
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
20 | * KIND, either express or implied. | ||
21 | * | ||
22 | ****************************************************************************/ | ||
23 | |||
24 | #include <stdio.h> | ||
25 | #include <stddef.h> | ||
26 | #include "file.h" | ||
27 | #include "buffer.h" | ||
28 | #include "system.h" | ||
29 | #include "mp3_playback.h" | ||
30 | #include "mpeg.h" | ||
31 | #include "lang.h" | ||
32 | #include "talk.h" | ||
33 | #include "screens.h" /* test hack */ | ||
34 | extern void bitswap(unsigned char *data, int length); /* no header for this */ | ||
35 | |||
36 | /***************** Constants *****************/ | ||
37 | |||
38 | #define VOICEFONT_FILENAME "/.rockbox/langs/english.voice" | ||
39 | #define QUEUE_SIZE 32 | ||
40 | |||
41 | |||
42 | /***************** Data types *****************/ | ||
43 | |||
44 | struct clip_entry /* one entry of the index table */ | ||
45 | { | ||
46 | int offset; /* offset from start of voicefont file */ | ||
47 | int size; /* size of the clip */ | ||
48 | }; | ||
49 | |||
50 | struct voicefont /* file format of our "voicefont" */ | ||
51 | { | ||
52 | int version; /* version of the voicefont */ | ||
53 | int headersize; /* size of the header, =offset to index */ | ||
54 | int id_max; /* number of clips contained */ | ||
55 | struct clip_entry index[]; /* followed by the index table */ | ||
56 | /* and finally the bitswapped mp3 clips, not visible here */ | ||
57 | }; | ||
58 | |||
59 | |||
60 | struct queue_entry /* one entry of the internal queue */ | ||
61 | { | ||
62 | unsigned char* buf; | ||
63 | int len; | ||
64 | }; | ||
65 | |||
66 | |||
67 | |||
68 | /***************** Globals *****************/ | ||
69 | |||
70 | static unsigned char* p_thumbnail; /* buffer for thumbnail */ | ||
71 | static long size_for_thumbnail; /* leftover buffer size for it */ | ||
72 | static struct voicefont* p_voicefont; /* loaded voicefont */ | ||
73 | static bool has_voicefont; /* a voicefont file is present */ | ||
74 | static bool is_playing; /* we're currently playing */ | ||
75 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | ||
76 | static int queue_write; /* write index of queue, by application */ | ||
77 | static int queue_read; /* read index of queue, by ISR context */ | ||
78 | |||
79 | |||
80 | |||
81 | /***************** Private implementation *****************/ | ||
82 | |||
83 | static int load_voicefont(void) | ||
84 | { | ||
85 | int fd; | ||
86 | int size; | ||
87 | |||
88 | p_voicefont = NULL; /* indicate no voicefont if we fail below */ | ||
89 | |||
90 | fd = open(VOICEFONT_FILENAME, O_RDONLY); | ||
91 | if (fd < 0) /* failed to open */ | ||
92 | { | ||
93 | p_voicefont = NULL; /* indicate no voicefont */ | ||
94 | has_voicefont = false; /* don't try again */ | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | size = read(fd, mp3buf, mp3end - mp3buf); | ||
99 | if (size > 1000 | ||
100 | && ((struct voicefont*)mp3buf)->headersize | ||
101 | == offsetof(struct voicefont, index)) | ||
102 | { | ||
103 | p_voicefont = (struct voicefont*)mp3buf; | ||
104 | |||
105 | /* thumbnail buffer is the remaining space behind */ | ||
106 | p_thumbnail = mp3buf + size; | ||
107 | p_thumbnail += (int)p_thumbnail % 2; /* 16-bit align */ | ||
108 | size_for_thumbnail = mp3end - p_thumbnail; | ||
109 | } | ||
110 | else | ||
111 | { | ||
112 | has_voicefont = false; /* don't try again */ | ||
113 | } | ||
114 | close(fd); | ||
115 | |||
116 | return size; | ||
117 | } | ||
118 | |||
119 | |||
120 | /* called in ISR context if mp3 data got consumed */ | ||
121 | static void mp3_callback(unsigned char** start, int* size) | ||
122 | { | ||
123 | int play_now; | ||
124 | |||
125 | if (queue[queue_read].len > 0) /* current clip not finished? */ | ||
126 | { /* feed the next 64K-1 chunk */ | ||
127 | play_now = MIN(queue[queue_read].len, 0xFFFF); | ||
128 | *start = queue[queue_read].buf; | ||
129 | *size = play_now; | ||
130 | queue[queue_read].buf += play_now; | ||
131 | queue[queue_read].len -= play_now; | ||
132 | return; | ||
133 | } | ||
134 | else /* go to next entry */ | ||
135 | { | ||
136 | queue_read++; | ||
137 | if (queue_read >= QUEUE_SIZE) | ||
138 | queue_read = 0; | ||
139 | } | ||
140 | |||
141 | if (queue_read != queue_write) /* queue is not empty? */ | ||
142 | { /* start next clip */ | ||
143 | play_now = MIN(queue[queue_read].len, 0xFFFF); | ||
144 | *start = queue[queue_read].buf; | ||
145 | *size = play_now; | ||
146 | queue[queue_read].buf += play_now; | ||
147 | queue[queue_read].len -= play_now; | ||
148 | } | ||
149 | else | ||
150 | { | ||
151 | *size = 0; /* end of data */ | ||
152 | is_playing = false; | ||
153 | mp3_play_stop(); /* fixme: should be done by caller */ | ||
154 | } | ||
155 | } | ||
156 | |||
157 | |||
158 | /* stop the playback and the pending clips, but at frame boundary */ | ||
159 | static int shutup(void) | ||
160 | { | ||
161 | mp3_play_pause(false); /* pause */ | ||
162 | |||
163 | /* ToDo: search next frame boundary and continue up to there */ | ||
164 | |||
165 | queue_write = queue_read; | ||
166 | is_playing = false; | ||
167 | mp3_play_stop(); | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | |||
173 | /* schedule a clip, at the end or discard the existing queue */ | ||
174 | static int queue_clip(unsigned char* buf, int size, bool enqueue) | ||
175 | { | ||
176 | if (!enqueue) | ||
177 | shutup(); /* cut off all the pending stuff */ | ||
178 | |||
179 | queue[queue_write].buf = buf; | ||
180 | queue[queue_write].len = size; | ||
181 | |||
182 | /* FixMe: make this IRQ-safe */ | ||
183 | |||
184 | if (!is_playing) | ||
185 | { /* queue empty, we have to do the initial start */ | ||
186 | int size_now = MIN(size, 0xFFFF); /* DMA can do no more */ | ||
187 | is_playing = true; | ||
188 | mp3_play_data(buf, size_now, mp3_callback); | ||
189 | mp3_play_pause(true); /* kickoff audio */ | ||
190 | queue[queue_write].buf += size_now; | ||
191 | queue[queue_write].len -= size_now; | ||
192 | } | ||
193 | |||
194 | queue_write++; | ||
195 | if (queue_write >= QUEUE_SIZE) | ||
196 | queue_write = 0; | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | |||
202 | /***************** Public implementation *****************/ | ||
203 | |||
204 | void talk_init(void) | ||
205 | { | ||
206 | has_voicefont = true; /* unless we fail later, assume we have one */ | ||
207 | talk_buffer_steal(); | ||
208 | queue_write = queue_read = 0; | ||
209 | } | ||
210 | |||
211 | |||
212 | /* somebody else claims the mp3 buffer, e.g. for regular play/record */ | ||
213 | int talk_buffer_steal(void) | ||
214 | { | ||
215 | p_voicefont = NULL; /* indicate no voicefont (trashed) */ | ||
216 | p_thumbnail = mp3buf; /* whole space for thumbnail */ | ||
217 | size_for_thumbnail = mp3end - mp3buf; | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | |||
222 | /* play a voice ID from voicefont */ | ||
223 | int talk_id(int id, bool enqueue) | ||
224 | { | ||
225 | int clipsize; | ||
226 | unsigned char* clipbuf; | ||
227 | int unit; | ||
228 | |||
229 | if (mpeg_status()) /* busy, buffer in use */ | ||
230 | return -1; | ||
231 | |||
232 | if (p_voicefont == NULL && has_voicefont) | ||
233 | load_voicefont(); /* reload needed */ | ||
234 | |||
235 | if (p_voicefont == NULL) /* still no voices? */ | ||
236 | return -1; | ||
237 | |||
238 | /* check if this is a special ID, with a value */ | ||
239 | unit = ((unsigned)id) >> UNIT_SHIFT; | ||
240 | if (id != -1 && unit) | ||
241 | { /* sign-extend the value */ | ||
242 | //splash(200, true,"unit=%d", unit); | ||
243 | id = (unsigned)id << (32-UNIT_SHIFT); | ||
244 | id >>= (32-UNIT_SHIFT); | ||
245 | talk_value(id, unit, enqueue); /* speak it */ | ||
246 | return 0; /* and stop, end of special case */ | ||
247 | } | ||
248 | |||
249 | if (id < 0 || id >= p_voicefont->id_max) | ||
250 | return -1; | ||
251 | |||
252 | clipsize = p_voicefont->index[id].size; | ||
253 | if (clipsize == 0) /* clip not included in voicefont */ | ||
254 | return -1; | ||
255 | |||
256 | clipbuf = mp3buf + p_voicefont->index[id].offset; | ||
257 | |||
258 | queue_clip(clipbuf, clipsize, enqueue); | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | |||
264 | /* play a thumbnail from file */ | ||
265 | int talk_file(char* filename, bool enqueue) | ||
266 | { | ||
267 | int fd; | ||
268 | int size; | ||
269 | |||
270 | if (mpeg_status()) /* busy, buffer in use */ | ||
271 | return -1; | ||
272 | |||
273 | if (p_thumbnail == NULL || size_for_thumbnail <= 0) | ||
274 | return -1; | ||
275 | |||
276 | fd = open(filename, O_RDONLY); | ||
277 | if (fd < 0) /* failed to open */ | ||
278 | { | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | size = read(fd, p_thumbnail, size_for_thumbnail); | ||
283 | close(fd); | ||
284 | |||
285 | /* ToDo: find audio, skip ID headers and trailers */ | ||
286 | |||
287 | if (size) | ||
288 | { | ||
289 | bitswap(p_thumbnail, size); | ||
290 | queue_clip(p_thumbnail, size, enqueue); | ||
291 | } | ||
292 | |||
293 | return size; | ||
294 | } | ||
295 | |||
296 | |||
297 | /* say a numeric value, this works for english, | ||
298 | but not necessarily for other languages */ | ||
299 | int talk_number(int n, bool enqueue) | ||
300 | { | ||
301 | int level = 0; // mille count | ||
302 | int mil = 1000000000; // highest possible "-illion" | ||
303 | |||
304 | if (!enqueue) | ||
305 | shutup(); /* cut off all the pending stuff */ | ||
306 | |||
307 | if (n==0) | ||
308 | { // special case | ||
309 | talk_id(VOICE_ZERO, true); | ||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | if (n<0) | ||
314 | { | ||
315 | talk_id(VOICE_MINUS, true); | ||
316 | n = -n; | ||
317 | } | ||
318 | |||
319 | while (n) | ||
320 | { | ||
321 | int segment = n / mil; // extract in groups of 3 digits | ||
322 | n -= segment * mil; // remove the used digits from number | ||
323 | mil /= 1000; // digit place for next round | ||
324 | |||
325 | if (segment) | ||
326 | { | ||
327 | int hundreds = segment / 100; | ||
328 | int ones = segment % 100; | ||
329 | |||
330 | if (hundreds) | ||
331 | { | ||
332 | talk_id(VOICE_ZERO + hundreds, true); | ||
333 | talk_id(VOICE_HUNDRED, true); | ||
334 | } | ||
335 | |||
336 | // combination indexing | ||
337 | if (ones > 20) | ||
338 | { | ||
339 | int tens = ones/10 + 18; | ||
340 | talk_id(VOICE_ZERO + tens, true); | ||
341 | ones %= 10; | ||
342 | } | ||
343 | |||
344 | // direct indexing | ||
345 | if (ones) | ||
346 | talk_id(VOICE_ZERO + ones, true); | ||
347 | |||
348 | // add billion, million, thousand | ||
349 | if (mil) | ||
350 | talk_id(VOICE_BILLION + level, true); | ||
351 | } | ||
352 | level++; | ||
353 | } | ||
354 | |||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | int talk_value(int n, int unit, bool enqueue) | ||
359 | { | ||
360 | int unit_id; | ||
361 | const int unit_voiced[] = | ||
362 | { /* lookup table for the voice ID of the units */ | ||
363 | -1, -1, -1, /* regular ID, int, signed */ | ||
364 | VOICE_MILLISECONDS, /* here come the "real" units */ | ||
365 | VOICE_SECONDS, | ||
366 | VOICE_MINUTES, | ||
367 | VOICE_HOURS, | ||
368 | VOICE_KHZ, | ||
369 | VOICE_DB, | ||
370 | VOICE_PERCENT, | ||
371 | VOICE_MEGABYTE, | ||
372 | VOICE_GIGABYTE | ||
373 | }; | ||
374 | |||
375 | if (unit < 0 || unit >= UNIT_LAST) | ||
376 | unit_id = -1; | ||
377 | else | ||
378 | unit_id = unit_voiced[unit]; | ||
379 | |||
380 | if ((n==1 || n==-1) // singular? | ||
381 | && unit_id >= VOICE_SECONDS && unit_id <= VOICE_HOURS) | ||
382 | { | ||
383 | unit_id--; /* use the singular for those units which have */ | ||
384 | } | ||
385 | |||
386 | /* special case with a "plus" before */ | ||
387 | if (n > 0 && (unit == UNIT_SIGNED || unit == UNIT_DB)) | ||
388 | { | ||
389 | talk_id(VOICE_PLUS, enqueue); | ||
390 | enqueue = true; | ||
391 | } | ||
392 | |||
393 | talk_number(n, enqueue); /* say the number */ | ||
394 | talk_id(unit_id, true); /* say the unit, if any */ | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||
diff --git a/apps/talk.h b/apps/talk.h new file mode 100644 index 0000000000..4851e7b6fb --- /dev/null +++ b/apps/talk.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2004 Jörg Hohensohn | ||
11 | * | ||
12 | * This module collects the Talkbox and voice UI functions. | ||
13 | * (Talkbox reads directory names from mp3 clips called thumbnails, | ||
14 | * the voice UI lets menus and screens "talk" from a voicefont in memory. | ||
15 | * | ||
16 | * All files in this archive are subject to the GNU General Public License. | ||
17 | * See the file COPYING in the source tree root for full license agreement. | ||
18 | * | ||
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
20 | * KIND, either express or implied. | ||
21 | * | ||
22 | ****************************************************************************/ | ||
23 | |||
24 | #ifndef __TALK_H__ | ||
25 | #define __TALK_H__ | ||
26 | |||
27 | #include <stdbool.h> | ||
28 | |||
29 | enum { | ||
30 | UNIT_INT = 1, /* plain number */ | ||
31 | UNIT_SIGNED, /* number with mandatory sign (even if positive) */ | ||
32 | UNIT_MS, /* milliseconds */ | ||
33 | UNIT_SEC, /* seconds */ | ||
34 | UNIT_MIN, /* minutes */ | ||
35 | UNIT_HOUR, /* hours */ | ||
36 | UNIT_KHZ, /* kHz */ | ||
37 | UNIT_DB, /* dB, mandatory sign */ | ||
38 | UNIT_PERCENT, /* % */ | ||
39 | UNIT_MB, /* megabyte */ | ||
40 | UNIT_GB, /* gigabyte */ | ||
41 | UNIT_LAST /* END MARKER */ | ||
42 | }; | ||
43 | |||
44 | #define UNIT_SHIFT (32-4) /* this many bits left from UNIT_xx enum */ | ||
45 | |||
46 | /* make a "talkable" ID from number + unit | ||
47 | unit is upper 4 bits, number the remaining (in regular 2's complement) */ | ||
48 | #define TALK_ID(n,u) ((u)<<UNIT_SHIFT | ((n) & ~(-1<<UNIT_SHIFT))) | ||
49 | |||
50 | /* convenience macro to have both string and ID as arguments */ | ||
51 | #define STR(id) str(id), id | ||
52 | |||
53 | |||
54 | void talk_init(void); | ||
55 | int talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */ | ||
56 | int talk_id(int id, bool enqueue); /* play a voice ID from voicefont */ | ||
57 | int talk_file(char* filename, bool enqueue); /* play a thumbnail from file */ | ||
58 | int talk_number(int n, bool enqueue); /* say a number */ | ||
59 | int talk_value(int n, int unit, bool enqueue); /* say a numeric value */ | ||
60 | |||
61 | #endif /* __TALK_H__ */ | ||