summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/bookmark.c1
-rw-r--r--apps/main_menu.c1
-rw-r--r--apps/playlist.c1
-rw-r--r--apps/playlist_menu.c2
-rw-r--r--apps/playlist_viewer.c2
-rw-r--r--apps/recorder/radio.c8
-rw-r--r--apps/recorder/recording.c14
-rw-r--r--apps/settings.h3
-rw-r--r--apps/settings_menu.c209
-rw-r--r--apps/sound_menu.c111
-rw-r--r--apps/talk.c398
-rw-r--r--apps/talk.h (renamed from firmware/export/talk.h)31
-rw-r--r--firmware/mpeg.c4
-rw-r--r--firmware/talk.c209
14 files changed, 619 insertions, 375 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 = &current_playlist; 1596 struct playlist_info* playlist = &current_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
50bool f2_rec_screen(void); 51bool f2_rec_screen(void);
51bool f3_rec_screen(void); 52bool 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
67struct user_settings 64struct 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)
799static bool ff_rewind_min_step(void) 800static 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
36static char *fmt[] = 37static 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)
206static bool recfrequency(void) 213static 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 */
34extern 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
44struct 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
50struct 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
60struct queue_entry /* one entry of the internal queue */
61{
62 unsigned char* buf;
63 int len;
64};
65
66
67
68/***************** Globals *****************/
69
70static unsigned char* p_thumbnail; /* buffer for thumbnail */
71static long size_for_thumbnail; /* leftover buffer size for it */
72static struct voicefont* p_voicefont; /* loaded voicefont */
73static bool has_voicefont; /* a voicefont file is present */
74static bool is_playing; /* we're currently playing */
75static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */
76static int queue_write; /* write index of queue, by application */
77static int queue_read; /* read index of queue, by ISR context */
78
79
80
81/***************** Private implementation *****************/
82
83static 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 */
121static 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 */
159static 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 */
174static 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
204void 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 */
213int 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 */
223int 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 */
265int 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 */
299int 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
358int 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/firmware/export/talk.h b/apps/talk.h
index b2af46f15e..4851e7b6fb 100644
--- a/firmware/export/talk.h
+++ b/apps/talk.h
@@ -26,9 +26,36 @@
26 26
27#include <stdbool.h> 27#include <stdbool.h>
28 28
29enum {
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
29void talk_init(void); 54void talk_init(void);
30int talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */ 55int talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */
31int talk_id(int id, bool block); /* play a voice ID from voicefont */ 56int talk_id(int id, bool enqueue); /* play a voice ID from voicefont */
32int talk_file(char* filename, bool block); /* play a thumbnail from file */ 57int talk_file(char* filename, bool enqueue); /* play a thumbnail from file */
58int talk_number(int n, bool enqueue); /* say a number */
59int talk_value(int n, int unit, bool enqueue); /* say a numeric value */
33 60
34#endif /* __TALK_H__ */ 61#endif /* __TALK_H__ */
diff --git a/firmware/mpeg.c b/firmware/mpeg.c
index fcb0d54bb7..ff545f03d0 100644
--- a/firmware/mpeg.c
+++ b/firmware/mpeg.c
@@ -30,7 +30,6 @@
30#include "mp3data.h" 30#include "mp3data.h"
31#include "buffer.h" 31#include "buffer.h"
32#include "mp3_playback.h" 32#include "mp3_playback.h"
33#include "talk.h"
34#ifndef SIMULATOR 33#ifndef SIMULATOR
35#include "i2c.h" 34#include "i2c.h"
36#include "mas.h" 35#include "mas.h"
@@ -2135,7 +2134,6 @@ void mpeg_record(char *filename)
2135 recording_filename[MAX_PATH - 1] = 0; 2134 recording_filename[MAX_PATH - 1] = 0;
2136 2135
2137 disable_xing_header = false; 2136 disable_xing_header = false;
2138 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2139 queue_post(&mpeg_queue, MPEG_RECORD, NULL); 2137 queue_post(&mpeg_queue, MPEG_RECORD, NULL);
2140} 2138}
2141 2139
@@ -2150,7 +2148,6 @@ static void start_prerecording(void)
2150 prerecord_timeout = current_tick + HZ; 2148 prerecord_timeout = current_tick + HZ;
2151 memset(prerecord_buffer, 0, sizeof(prerecord_buffer)); 2149 memset(prerecord_buffer, 0, sizeof(prerecord_buffer));
2152 reset_mp3_buffer(); 2150 reset_mp3_buffer();
2153 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2154 2151
2155 is_prerecording = true; 2152 is_prerecording = true;
2156 2153
@@ -2407,7 +2404,6 @@ void mpeg_play(int offset)
2407#else 2404#else
2408 is_playing = true; 2405 is_playing = true;
2409 2406
2410 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2411 queue_post(&mpeg_queue, MPEG_PLAY, (void*)offset); 2407 queue_post(&mpeg_queue, MPEG_PLAY, (void*)offset);
2412#endif /* #ifdef SIMULATOR */ 2408#endif /* #ifdef SIMULATOR */
2413 2409
diff --git a/firmware/talk.c b/firmware/talk.c
deleted file mode 100644
index d02a4efd7f..0000000000
--- a/firmware/talk.c
+++ /dev/null
@@ -1,209 +0,0 @@
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 "kernel.h"
29#include "mp3_playback.h"
30#include "mpeg.h"
31#include "talk.h"
32extern void bitswap(unsigned char *data, int length); /* no header for this */
33
34/***************** Constants *****************/
35
36#define VOICEFONT_FILENAME "/.rockbox/voicefont"
37
38
39/***************** Data types *****************/
40
41struct clip_entry /* one entry of the index table */
42{
43 int offset; /* offset from start of voicefont file */
44 int size; /* size of the clip */
45};
46
47struct voicefont /* file format of our "voicefont" */
48{
49 int version; /* version of the voicefont */
50 int headersize; /* size of the header, =offset to index */
51 int id_max; /* number of clips contained */
52 struct clip_entry index[]; /* followed by the index table */
53 /* and finally the bitswapped mp3 clips, not visible here */
54};
55
56
57/***************** Globals *****************/
58
59static unsigned char* p_thumbnail; /* buffer for thumbnail */
60static long size_for_thumbnail; /* leftover buffer size for it */
61static struct voicefont* p_voicefont; /* loaded voicefont */
62static bool has_voicefont; /* a voicefont file is present */
63static bool is_playing; /* we're currently playing */
64
65
66
67/***************** Private implementation *****************/
68
69static int load_voicefont(void)
70{
71 int fd;
72 int size;
73
74 p_voicefont = NULL; /* indicate no voicefont if we fail below */
75
76 fd = open(VOICEFONT_FILENAME, O_RDONLY);
77 if (fd < 0) /* failed to open */
78 {
79 p_voicefont = NULL; /* indicate no voicefont */
80 has_voicefont = false; /* don't try again */
81 return 0;
82 }
83
84 size = read(fd, mp3buf, mp3end - mp3buf);
85 if (size > 1000
86 && ((struct voicefont*)mp3buf)->headersize
87 == offsetof(struct voicefont, index))
88 {
89 p_voicefont = (struct voicefont*)mp3buf;
90
91 /* thumbnail buffer is the remaining space behind */
92 p_thumbnail = mp3buf + size;
93 p_thumbnail += (int)p_thumbnail % 2; /* 16-bit align */
94 size_for_thumbnail = mp3end - p_thumbnail;
95
96 /* max. DMA size, fixme */
97 if (size_for_thumbnail > 0xFFFF)
98 size_for_thumbnail = 0xFFFF;
99 }
100 else
101 {
102 has_voicefont = false; /* don't try again */
103 }
104 close(fd);
105
106 return size;
107}
108
109
110/* called in ISR context if mp3 data got consumed */
111void mp3_callback(unsigned char** start, int* size)
112{
113 (void)start; /* unused parameter, avoid warning */
114 *size = 0; /* end of data */
115 is_playing = false;
116 mp3_play_stop(); /* fixme: should be done by caller */
117}
118
119/***************** Public implementation *****************/
120
121void talk_init(void)
122{
123 has_voicefont = true; /* unless we fail later, assume we have one */
124 talk_buffer_steal();
125}
126
127
128/* somebody else claims the mp3 buffer, e.g. for regular play/record */
129int talk_buffer_steal(void)
130{
131 p_voicefont = NULL; /* indicate no voicefont (trashed) */
132 p_thumbnail = mp3buf; /* whole space for thumbnail */
133 size_for_thumbnail = mp3end - mp3buf;
134 /* max. DMA size, fixme */
135 if (size_for_thumbnail > 0xFFFF)
136 size_for_thumbnail = 0xFFFF;
137 return 0;
138}
139
140
141/* play a voice ID from voicefont */
142int talk_id(int id, bool block)
143{
144 int clipsize;
145
146 if (mpeg_status()) /* busy, buffer in use */
147 return -1;
148
149 if (p_voicefont == NULL && has_voicefont)
150 load_voicefont(); /* reload needed */
151
152 if (p_voicefont == NULL) /* still no voices? */
153 return -1;
154
155 if (id >= p_voicefont->id_max)
156 return -1;
157
158 clipsize = p_voicefont->index[id].size;
159 if (clipsize == 0) /* clip not included in voicefont */
160 return -1;
161
162 is_playing = true;
163 mp3_play_data(mp3buf + p_voicefont->index[id].offset,
164 clipsize, mp3_callback);
165 mp3_play_pause(true); /* kickoff audio */
166
167 while(block && is_playing)
168 sleep(1);
169
170 return 0;
171}
172
173
174/* play a thumbnail from file */
175int talk_file(char* filename, bool block)
176{
177 int fd;
178 int size;
179
180 if (mpeg_status()) /* busy, buffer in use */
181 return -1;
182
183 if (p_thumbnail == NULL || size_for_thumbnail <= 0)
184 return -1;
185
186 fd = open(filename, O_RDONLY);
187 if (fd < 0) /* failed to open */
188 {
189 return 0;
190 }
191
192 size = read(fd, p_thumbnail, size_for_thumbnail);
193 close(fd);
194
195 /* ToDo: find audio, skip ID headers and trailers */
196
197 if (size)
198 {
199 bitswap(p_thumbnail, size);
200 is_playing = true;
201 mp3_play_data(p_thumbnail, size, mp3_callback);
202 mp3_play_pause(true); /* kickoff audio */
203
204 while(block && is_playing)
205 sleep(1);
206 }
207
208 return size;
209}