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 @@
#include "debug.h"
#include "kernel.h"
#include "sprintf.h"
+#include "talk.h"
#define MAX_BOOKMARKS 10
#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 @@
#include "buffer.h"
#include "screens.h"
#include "playlist_menu.h"
+#include "talk.h"
#ifdef HAVE_FMRADIO
#include "radio.h"
#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)
struct playlist_info* playlist = &current_playlist;
playlist->index = start_index;
+ talk_buffer_steal(); /* will use the mp3 buffer */
mpeg_play(offset);
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 @@
#include "tree.h"
#include "settings.h"
#include "playlist_viewer.h"
-
+#include "talk.h"
#include "lang.h"
#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 @@
#include "keyboard.h"
#include "tree.h"
#include "onplay.h"
+#include "talk.h"
#ifdef HAVE_LCD_BITMAP
#include "widgets.h"
@@ -721,6 +722,7 @@ static int onplay_menu(int index)
if (tracks[index].display_index != viewer.num_tracks ||
global_settings.repeat_mode == REPEAT_ALL)
{
+ talk_buffer_steal(); /* will use the mp3 buffer */
mpeg_play(0);
viewer.current_playing_track = -1;
}
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 @@
#include "font.h"
#include "sound_menu.h"
#include "recording.h"
+#include "talk.h"
#ifdef HAVE_FMRADIO
@@ -174,6 +175,9 @@ bool radio_screen(void)
peak_meter_enabled = true;
+ if (global_settings.rec_prerecord_time)
+ talk_buffer_steal(); /* will use the mp3 buffer */
+
mpeg_set_recording_options(global_settings.rec_frequency,
global_settings.rec_quality,
1, /* Line In */
@@ -257,6 +261,7 @@ bool radio_screen(void)
else
{
have_recorded = true;
+ talk_buffer_steal(); /* we use the mp3 buffer */
mpeg_record(rec_create_filename(buf));
status_set_playmode(STATUS_RECORD);
update_screen = true;
@@ -704,6 +709,9 @@ static bool fm_recording_settings(void)
ret = recording_menu(true);
if(!ret)
{
+ if (global_settings.rec_prerecord_time)
+ talk_buffer_steal(); /* will use the mp3 buffer */
+
mpeg_set_recording_options(global_settings.rec_frequency,
global_settings.rec_quality,
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 @@
#include "string.h"
#include "dir.h"
#include "errno.h"
+#include "talk.h"
bool f2_rec_screen(void);
bool f3_rec_screen(void);
@@ -166,6 +167,9 @@ bool recording_screen(void)
peak_meter_enabled = true;
+ if (global_settings.rec_prerecord_time)
+ talk_buffer_steal(); /* will use the mp3 buffer */
+
mpeg_set_recording_options(global_settings.rec_frequency,
global_settings.rec_quality,
global_settings.rec_source,
@@ -223,6 +227,7 @@ bool recording_screen(void)
if(!(mpeg_status() & MPEG_STATUS_RECORD))
{
have_recorded = true;
+ talk_buffer_steal(); /* we use the mp3 buffer */
mpeg_record(rec_create_filename(path_buffer));
status_set_playmode(STATUS_RECORD);
update_countdown = 1; /* Update immediately */
@@ -336,6 +341,9 @@ bool recording_screen(void)
return SYS_USB_CONNECTED;
settings_save();
+ if (global_settings.rec_prerecord_time)
+ talk_buffer_steal(); /* will use the mp3 buffer */
+
mpeg_set_recording_options(global_settings.rec_frequency,
global_settings.rec_quality,
global_settings.rec_source,
@@ -660,6 +668,9 @@ bool f2_rec_screen(void)
}
}
+ if (global_settings.rec_prerecord_time)
+ talk_buffer_steal(); /* will use the mp3 buffer */
+
mpeg_set_recording_options(global_settings.rec_frequency,
global_settings.rec_quality,
global_settings.rec_source,
@@ -730,6 +741,9 @@ bool f3_rec_screen(void)
}
}
+ if (global_settings.rec_prerecord_time)
+ talk_buffer_steal(); /* will use the mp3 buffer */
+
mpeg_set_recording_options(global_settings.rec_frequency,
global_settings.rec_quality,
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 @@
#define FF_REWIND_45000 12
#define FF_REWIND_60000 13
-/* convenience macro to have both string and ID as arguments */
-#define STR(id) str(id), id
-
struct user_settings
{
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 @@
#include "ata.h"
#include "tree.h"
#include "screens.h"
+#include "talk.h"
#ifdef HAVE_LCD_BITMAP
#include "peakmeter.h"
#endif
@@ -167,23 +168,23 @@ static bool peak_meter_hold(void) {
bool retval = false;
struct opt_items names[] = {
{ STR(LANG_OFF) },
- { "200 ms " , -1 },
- { "300 ms " , -1 },
- { "500 ms " , -1 },
- { "1 s " , -1 },
- { "2 s " , -1 },
- { "3 s " , -1 },
- { "4 s " , -1 },
- { "5 s " , -1 },
- { "6 s " , -1 },
- { "7 s" , -1 },
- { "8 s" , -1 },
- { "9 s" , -1 },
- { "10 s" , -1 },
- { "15 s" , -1 },
- { "20 s" , -1 },
- { "30 s" , -1 },
- { "1 min" , -1 }
+ { "200 ms " , TALK_ID(200, UNIT_MS) },
+ { "300 ms " , TALK_ID(300, UNIT_MS) },
+ { "500 ms " , TALK_ID(500, UNIT_MS) },
+ { "1 s" , TALK_ID(1, UNIT_SEC) },
+ { "2 s" , TALK_ID(2, UNIT_SEC) },
+ { "3 s" , TALK_ID(3, UNIT_SEC) },
+ { "4 s" , TALK_ID(4, UNIT_SEC) },
+ { "5 s" , TALK_ID(5, UNIT_SEC) },
+ { "6 s" , TALK_ID(6, UNIT_SEC) },
+ { "7 s" , TALK_ID(7, UNIT_SEC) },
+ { "8 s" , TALK_ID(8, UNIT_SEC) },
+ { "9 s" , TALK_ID(9, UNIT_SEC) },
+ { "10 s" , TALK_ID(10, UNIT_SEC) },
+ { "15 s" , TALK_ID(15, UNIT_SEC) },
+ { "20 s" , TALK_ID(20, UNIT_SEC) },
+ { "30 s" , TALK_ID(30, UNIT_SEC) },
+ { "1 min" , TALK_ID(1, UNIT_MIN) }
};
retval = set_option( str(LANG_PM_PEAK_HOLD),
&global_settings.peak_meter_hold, INT, names,
@@ -204,30 +205,30 @@ static bool peak_meter_clip_hold(void) {
struct opt_items names[] = {
{ STR(LANG_PM_ETERNAL) },
- { "1s " , -1 },
- { "2s " , -1 },
- { "3s " , -1 },
- { "4s " , -1 },
- { "5s " , -1 },
- { "6s " , -1 },
- { "7s " , -1 },
- { "8s " , -1 },
- { "9s " , -1 },
- { "10s" , -1 },
- { "15s" , -1 },
- { "20s" , -1 },
- { "25s" , -1 },
- { "30s" , -1 },
- { "45s" , -1 },
- { "60s" , -1 },
- { "90s" , -1 },
- { "2min" , -1 },
- { "3min" , -1 },
- { "5min" , -1 },
- { "10min" , -1 },
- { "20min" , -1 },
- { "45min" , -1 },
- { "90min" , -1 }
+ { "1s " , TALK_ID(1, UNIT_SEC) },
+ { "2s " , TALK_ID(2, UNIT_SEC) },
+ { "3s " , TALK_ID(3, UNIT_SEC) },
+ { "4s " , TALK_ID(4, UNIT_SEC) },
+ { "5s " , TALK_ID(5, UNIT_SEC) },
+ { "6s " , TALK_ID(6, UNIT_SEC) },
+ { "7s " , TALK_ID(7, UNIT_SEC) },
+ { "8s " , TALK_ID(8, UNIT_SEC) },
+ { "9s " , TALK_ID(9, UNIT_SEC) },
+ { "10s" , TALK_ID(10, UNIT_SEC) },
+ { "15s" , TALK_ID(15, UNIT_SEC) },
+ { "20s" , TALK_ID(20, UNIT_SEC) },
+ { "25s" , TALK_ID(25, UNIT_SEC) },
+ { "30s" , TALK_ID(30, UNIT_SEC) },
+ { "45s" , TALK_ID(45, UNIT_SEC) },
+ { "60s" , TALK_ID(60, UNIT_SEC) },
+ { "90s" , TALK_ID(90, UNIT_SEC) },
+ { "2min" , TALK_ID(2, UNIT_MIN) },
+ { "3min" , TALK_ID(3, UNIT_MIN) },
+ { "5min" , TALK_ID(5, UNIT_MIN) },
+ { "10min" , TALK_ID(10, UNIT_MIN) },
+ { "20min" , TALK_ID(20, UNIT_MIN) },
+ { "45min" , TALK_ID(45, UNIT_MIN) },
+ { "90min" , TALK_ID(90, UNIT_MIN) }
};
retval = set_option( str(LANG_PM_CLIP_HOLD),
&global_settings.peak_meter_clip_hold, INT, names,
@@ -531,23 +532,23 @@ static bool backlight_timer(void)
struct opt_items names[] = {
{ STR(LANG_OFF) },
{ STR(LANG_ON) },
- { "1s ", -1 },
- { "2s ", -1 },
- { "3s ", -1 },
- { "4s ", -1 },
- { "5s ", -1 },
- { "6s ", -1 },
- { "7s ", -1 },
- { "8s ", -1 },
- { "9s ", -1 },
- { "10s", -1 },
- { "15s", -1 },
- { "20s", -1 },
- { "25s", -1 },
- { "30s", -1 },
- { "45s", -1 },
- { "60s", -1 },
- { "90s", -1 }
+ { "1s ", TALK_ID(1, UNIT_SEC) },
+ { "2s ", TALK_ID(2, UNIT_SEC) },
+ { "3s ", TALK_ID(3, UNIT_SEC) },
+ { "4s ", TALK_ID(4, UNIT_SEC) },
+ { "5s ", TALK_ID(5, UNIT_SEC) },
+ { "6s ", TALK_ID(6, UNIT_SEC) },
+ { "7s ", TALK_ID(7, UNIT_SEC) },
+ { "8s ", TALK_ID(8, UNIT_SEC) },
+ { "9s ", TALK_ID(9, UNIT_SEC) },
+ { "10s", TALK_ID(10, UNIT_SEC) },
+ { "15s", TALK_ID(15, UNIT_SEC) },
+ { "20s", TALK_ID(20, UNIT_SEC) },
+ { "25s", TALK_ID(25, UNIT_SEC) },
+ { "30s", TALK_ID(30, UNIT_SEC) },
+ { "45s", TALK_ID(45, UNIT_SEC) },
+ { "60s", TALK_ID(60, UNIT_SEC) },
+ { "90s", TALK_ID(90, UNIT_SEC) }
};
return set_option(str(LANG_BACKLIGHT), &global_settings.backlight_timeout,
INT, names, 19, backlight_set_timeout );
@@ -557,20 +558,20 @@ static bool poweroff_idle_timer(void)
{
struct opt_items names[] = {
{ STR(LANG_OFF) },
- { "1m ", -1 },
- { "2m ", -1 },
- { "3m ", -1 },
- { "4m ", -1 },
- { "5m ", -1 },
- { "6m ", -1 },
- { "7m ", -1 },
- { "8m ", -1 },
- { "9m ", -1 },
- { "10m", -1 },
- { "15m", -1 },
- { "30m", -1 },
- { "45m", -1 },
- { "60m", -1 }
+ { "1m ", TALK_ID(1, UNIT_MIN) },
+ { "2m ", TALK_ID(2, UNIT_MIN) },
+ { "3m ", TALK_ID(3, UNIT_MIN) },
+ { "4m ", TALK_ID(4, UNIT_MIN) },
+ { "5m ", TALK_ID(5, UNIT_MIN) },
+ { "6m ", TALK_ID(6, UNIT_MIN) },
+ { "7m ", TALK_ID(7, UNIT_MIN) },
+ { "8m ", TALK_ID(8, UNIT_MIN) },
+ { "9m ", TALK_ID(9, UNIT_MIN) },
+ { "10m", TALK_ID(10, UNIT_MIN) },
+ { "15m", TALK_ID(15, UNIT_MIN) },
+ { "30m", TALK_ID(30, UNIT_MIN) },
+ { "45m", TALK_ID(45, UNIT_MIN) },
+ { "60m", TALK_ID(60, UNIT_MIN) }
};
return set_option(str(LANG_POWEROFF_IDLE), &global_settings.poweroff,
INT, names, 15, set_poweroff_timeout);
@@ -613,9 +614,9 @@ static bool jump_scroll(void)
struct opt_items names[] = {
{ STR(LANG_OFF) },
{ STR(LANG_ONE_TIME) },
- { "2", -1 },
- { "3", -1 },
- { "4", -1 },
+ { "2", TALK_ID(2, UNIT_INT) },
+ { "3", TALK_ID(3, UNIT_INT) },
+ { "4", TALK_ID(4, UNIT_INT) },
{ STR(LANG_ALWAYS) }
};
bool ret;
@@ -799,20 +800,20 @@ static bool buffer_margin(void)
static bool ff_rewind_min_step(void)
{
struct opt_items names[] = {
- { "1s", -1 },
- { "2s", -1 },
- { "3s", -1 },
- { "4s", -1 },
- { "5s", -1 },
- { "6s", -1 },
- { "8s", -1 },
- { "10s", -1 },
- { "15s", -1 },
- { "20s", -1 },
- { "25s", -1 },
- { "30s", -1 },
- { "45s", -1 },
- { "60s", -1 }
+ { "1s", TALK_ID(1, UNIT_SEC) },
+ { "2s", TALK_ID(2, UNIT_SEC) },
+ { "3s", TALK_ID(3, UNIT_SEC) },
+ { "4s", TALK_ID(4, UNIT_SEC) },
+ { "5s", TALK_ID(5, UNIT_SEC) },
+ { "6s", TALK_ID(6, UNIT_SEC) },
+ { "8s", TALK_ID(8, UNIT_SEC) },
+ { "10s", TALK_ID(10, UNIT_SEC) },
+ { "15s", TALK_ID(15, UNIT_SEC) },
+ { "20s", TALK_ID(20, UNIT_SEC) },
+ { "25s", TALK_ID(25, UNIT_SEC) },
+ { "30s", TALK_ID(30, UNIT_SEC) },
+ { "45s", TALK_ID(45, UNIT_SEC) },
+ { "60s", TALK_ID(60, UNIT_SEC) }
};
return set_option(str(LANG_FFRW_STEP), &global_settings.ff_rewind_min_step,
INT, names, 14, NULL );
@@ -828,21 +829,21 @@ static bool ff_rewind_accel(void)
{
struct opt_items names[] = {
{ STR(LANG_OFF) },
- { "2x/1s", -1 },
- { "2x/2s", -1 },
- { "2x/3s", -1 },
- { "2x/4s", -1 },
- { "2x/5s", -1 },
- { "2x/6s", -1 },
- { "2x/7s", -1 },
- { "2x/8s", -1 },
- { "2x/9s", -1 },
- { "2x/10s", -1 },
- { "2x/11s", -1 },
- { "2x/12s", -1 },
- { "2x/13s", -1 },
- { "2x/14s", -1 },
- { "2x/15s", -1 }
+ { "2x/1s", TALK_ID(1, UNIT_SEC) },
+ { "2x/2s", TALK_ID(2, UNIT_SEC) },
+ { "2x/3s", TALK_ID(3, UNIT_SEC) },
+ { "2x/4s", TALK_ID(4, UNIT_SEC) },
+ { "2x/5s", TALK_ID(5, UNIT_SEC) },
+ { "2x/6s", TALK_ID(6, UNIT_SEC) },
+ { "2x/7s", TALK_ID(7, UNIT_SEC) },
+ { "2x/8s", TALK_ID(8, UNIT_SEC) },
+ { "2x/9s", TALK_ID(9, UNIT_SEC) },
+ { "2x/10s", TALK_ID(10, UNIT_SEC) },
+ { "2x/11s", TALK_ID(11, UNIT_SEC) },
+ { "2x/12s", TALK_ID(12, UNIT_SEC) },
+ { "2x/13s", TALK_ID(13, UNIT_SEC) },
+ { "2x/14s", TALK_ID(14, UNIT_SEC) },
+ { "2x/15s", TALK_ID(15, UNIT_SEC) }
};
return set_option(str(LANG_FFRW_ACCEL), &global_settings.ff_rewind_accel,
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 @@
#endif
#include "lang.h"
#include "sprintf.h"
+#include "talk.h"
static char *fmt[] =
{
@@ -53,11 +54,16 @@ bool set_sound(char* string,
int dec;
char* unit;
char str[32];
+ int talkunit = UNIT_INT;
unit = mpeg_sound_unit(setting);
numdec = mpeg_sound_numdecimals(setting);
min = mpeg_sound_min(setting);
max = mpeg_sound_max(setting);
+ if (*unit == 'd') /* crude reconstruction */
+ talkunit = UNIT_DB;
+ else if (*unit == '%')
+ talkunit = UNIT_PERCENT;
#ifdef HAVE_LCD_BITMAP
if(global_settings.statusbar)
@@ -81,6 +87,7 @@ bool set_sound(char* string,
{
snprintf(str,sizeof str,"%d %s ", val, unit);
}
+ talk_value(val, talkunit, false); /* speak it */
}
lcd_puts(0,1,str);
status_draw(true);
@@ -183,9 +190,9 @@ static bool avc(void)
{
struct opt_items names[] = {
{ STR(LANG_OFF) },
- { "2s", -1 },
- { "4s", -1 },
- { "8s", -1 }
+ { "2s", TALK_ID(2, UNIT_SEC) },
+ { "4s", TALK_ID(4, UNIT_SEC) },
+ { "8s", TALK_ID(8, UNIT_SEC) }
};
return set_option(str(LANG_DECAY), &global_settings.avc, INT,
names, 4, set_avc);
@@ -206,12 +213,12 @@ static bool recsource(void)
static bool recfrequency(void)
{
struct opt_items names[] = {
- { "44.1kHz", -1 },
- { "48kHz", -1 },
- { "32kHz", -1 },
- { "22.05kHz", -1 },
- { "24kHz", -1 },
- { "16kHz", -1 }
+ { "44.1kHz", TALK_ID(44, UNIT_KHZ) },
+ { "48kHz", TALK_ID(48, UNIT_KHZ) },
+ { "32kHz", TALK_ID(32, UNIT_KHZ) },
+ { "22.05kHz", TALK_ID(22, UNIT_KHZ) },
+ { "24kHz", TALK_ID(24, UNIT_KHZ) },
+ { "16kHz", TALK_ID(16, UNIT_KHZ) }
};
return set_option(str(LANG_RECORDING_FREQUENCY),
&global_settings.rec_frequency, INT,
@@ -246,19 +253,19 @@ static bool rectimesplit(void)
{
struct opt_items names[] = {
{ STR(LANG_OFF) },
- { "00:05" , -1 },
- { "00:10" , -1 },
- { "00:15" , -1 },
- { "00:30" , -1 },
- { "01:00" , -1 },
- { "02:00" , -1 },
- { "04:00" , -1 },
- { "06:00" , -1 },
- { "08:00" , -1 },
- { "10:00" , -1 },
- { "12:00" , -1 },
- { "18:00" , -1 },
- { "24:00" , -1 }
+ { "00:05" , TALK_ID(5, UNIT_MIN) },
+ { "00:10" , TALK_ID(10, UNIT_MIN) },
+ { "00:15" , TALK_ID(15, UNIT_MIN) },
+ { "00:30" , TALK_ID(30, UNIT_MIN) },
+ { "01:00" , TALK_ID(1, UNIT_HOUR) },
+ { "02:00" , TALK_ID(2, UNIT_HOUR) },
+ { "04:00" , TALK_ID(4, UNIT_HOUR) },
+ { "06:00" , TALK_ID(6, UNIT_HOUR) },
+ { "08:00" , TALK_ID(8, UNIT_HOUR) },
+ { "10:00" , TALK_ID(10, UNIT_HOUR) },
+ { "12:00" , TALK_ID(12, UNIT_HOUR) },
+ { "18:00" , TALK_ID(18, UNIT_HOUR) },
+ { "24:00" , TALK_ID(24, UNIT_HOUR) }
};
return set_option(str(LANG_RECORD_TIMESPLIT),
&global_settings.rec_timesplit, INT,
@@ -269,36 +276,36 @@ static bool recprerecord(void)
{
struct opt_items names[] = {
{ STR(LANG_OFF) },
- { "1s", -1 },
- { "2s", -1 },
- { "3s", -1 },
- { "4s", -1 },
- { "5s", -1 },
- { "6s", -1 },
- { "7s", -1 },
- { "8s", -1 },
- { "9s", -1 },
- { "10s", -1 },
- { "11s", -1 },
- { "12s", -1 },
- { "13s", -1 },
- { "14s", -1 },
- { "15s", -1 },
- { "16s", -1 },
- { "17s", -1 },
- { "18s", -1 },
- { "19s", -1 },
- { "10s", -1 },
- { "21s", -1 },
- { "22s", -1 },
- { "23s", -1 },
- { "24s", -1 },
- { "25s", -1 },
- { "26s", -1 },
- { "27s", -1 },
- { "28s", -1 },
- { "29s", -1 },
- { "30s", -1 }
+ { "1s", TALK_ID(1, UNIT_SEC) },
+ { "2s", TALK_ID(2, UNIT_SEC) },
+ { "3s", TALK_ID(3, UNIT_SEC) },
+ { "4s", TALK_ID(4, UNIT_SEC) },
+ { "5s", TALK_ID(5, UNIT_SEC) },
+ { "6s", TALK_ID(6, UNIT_SEC) },
+ { "7s", TALK_ID(7, UNIT_SEC) },
+ { "8s", TALK_ID(8, UNIT_SEC) },
+ { "9s", TALK_ID(9, UNIT_SEC) },
+ { "10s", TALK_ID(10, UNIT_SEC) },
+ { "11s", TALK_ID(11, UNIT_SEC) },
+ { "12s", TALK_ID(12, UNIT_SEC) },
+ { "13s", TALK_ID(13, UNIT_SEC) },
+ { "14s", TALK_ID(14, UNIT_SEC) },
+ { "15s", TALK_ID(15, UNIT_SEC) },
+ { "16s", TALK_ID(16, UNIT_SEC) },
+ { "17s", TALK_ID(17, UNIT_SEC) },
+ { "18s", TALK_ID(18, UNIT_SEC) },
+ { "19s", TALK_ID(19, UNIT_SEC) },
+ { "20s", TALK_ID(20, UNIT_SEC) },
+ { "21s", TALK_ID(21, UNIT_SEC) },
+ { "22s", TALK_ID(22, UNIT_SEC) },
+ { "23s", TALK_ID(23, UNIT_SEC) },
+ { "24s", TALK_ID(24, UNIT_SEC) },
+ { "25s", TALK_ID(25, UNIT_SEC) },
+ { "26s", TALK_ID(26, UNIT_SEC) },
+ { "27s", TALK_ID(27, UNIT_SEC) },
+ { "28s", TALK_ID(28, UNIT_SEC) },
+ { "29s", TALK_ID(29, UNIT_SEC) },
+ { "30s", TALK_ID(30, UNIT_SEC) }
};
return set_option(str(LANG_RECORD_PRERECORD_TIME),
&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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2004 Jörg Hohensohn
+ *
+ * This module collects the Talkbox and voice UI functions.
+ * (Talkbox reads directory names from mp3 clips called thumbnails,
+ * the voice UI lets menus and screens "talk" from a voicefont in memory.
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stddef.h>
+#include "file.h"
+#include "buffer.h"
+#include "system.h"
+#include "mp3_playback.h"
+#include "mpeg.h"
+#include "lang.h"
+#include "talk.h"
+#include "screens.h" /* test hack */
+extern void bitswap(unsigned char *data, int length); /* no header for this */
+
+/***************** Constants *****************/
+
+#define VOICEFONT_FILENAME "/.rockbox/langs/english.voice"
+#define QUEUE_SIZE 32
+
+
+/***************** Data types *****************/
+
+struct clip_entry /* one entry of the index table */
+{
+ int offset; /* offset from start of voicefont file */
+ int size; /* size of the clip */
+};
+
+struct voicefont /* file format of our "voicefont" */
+{
+ int version; /* version of the voicefont */
+ int headersize; /* size of the header, =offset to index */
+ int id_max; /* number of clips contained */
+ struct clip_entry index[]; /* followed by the index table */
+ /* and finally the bitswapped mp3 clips, not visible here */
+};
+
+
+struct queue_entry /* one entry of the internal queue */
+{
+ unsigned char* buf;
+ int len;
+};
+
+
+
+/***************** Globals *****************/
+
+static unsigned char* p_thumbnail; /* buffer for thumbnail */
+static long size_for_thumbnail; /* leftover buffer size for it */
+static struct voicefont* p_voicefont; /* loaded voicefont */
+static bool has_voicefont; /* a voicefont file is present */
+static bool is_playing; /* we're currently playing */
+static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */
+static int queue_write; /* write index of queue, by application */
+static int queue_read; /* read index of queue, by ISR context */
+
+
+
+/***************** Private implementation *****************/
+
+static int load_voicefont(void)
+{
+ int fd;
+ int size;
+
+ p_voicefont = NULL; /* indicate no voicefont if we fail below */
+
+ fd = open(VOICEFONT_FILENAME, O_RDONLY);
+ if (fd < 0) /* failed to open */
+ {
+ p_voicefont = NULL; /* indicate no voicefont */
+ has_voicefont = false; /* don't try again */
+ return 0;
+ }
+
+ size = read(fd, mp3buf, mp3end - mp3buf);
+ if (size > 1000
+ && ((struct voicefont*)mp3buf)->headersize
+ == offsetof(struct voicefont, index))
+ {
+ p_voicefont = (struct voicefont*)mp3buf;
+
+ /* thumbnail buffer is the remaining space behind */
+ p_thumbnail = mp3buf + size;
+ p_thumbnail += (int)p_thumbnail % 2; /* 16-bit align */
+ size_for_thumbnail = mp3end - p_thumbnail;
+ }
+ else
+ {
+ has_voicefont = false; /* don't try again */
+ }
+ close(fd);
+
+ return size;
+}
+
+
+/* called in ISR context if mp3 data got consumed */
+static void mp3_callback(unsigned char** start, int* size)
+{
+ int play_now;
+
+ if (queue[queue_read].len > 0) /* current clip not finished? */
+ { /* feed the next 64K-1 chunk */
+ play_now = MIN(queue[queue_read].len, 0xFFFF);
+ *start = queue[queue_read].buf;
+ *size = play_now;
+ queue[queue_read].buf += play_now;
+ queue[queue_read].len -= play_now;
+ return;
+ }
+ else /* go to next entry */
+ {
+ queue_read++;
+ if (queue_read >= QUEUE_SIZE)
+ queue_read = 0;
+ }
+
+ if (queue_read != queue_write) /* queue is not empty? */
+ { /* start next clip */
+ play_now = MIN(queue[queue_read].len, 0xFFFF);
+ *start = queue[queue_read].buf;
+ *size = play_now;
+ queue[queue_read].buf += play_now;
+ queue[queue_read].len -= play_now;
+ }
+ else
+ {
+ *size = 0; /* end of data */
+ is_playing = false;
+ mp3_play_stop(); /* fixme: should be done by caller */
+ }
+}
+
+
+/* stop the playback and the pending clips, but at frame boundary */
+static int shutup(void)
+{
+ mp3_play_pause(false); /* pause */
+
+ /* ToDo: search next frame boundary and continue up to there */
+
+ queue_write = queue_read;
+ is_playing = false;
+ mp3_play_stop();
+
+ return 0;
+}
+
+
+/* schedule a clip, at the end or discard the existing queue */
+static int queue_clip(unsigned char* buf, int size, bool enqueue)
+{
+ if (!enqueue)
+ shutup(); /* cut off all the pending stuff */
+
+ queue[queue_write].buf = buf;
+ queue[queue_write].len = size;
+
+ /* FixMe: make this IRQ-safe */
+
+ if (!is_playing)
+ { /* queue empty, we have to do the initial start */
+ int size_now = MIN(size, 0xFFFF); /* DMA can do no more */
+ is_playing = true;
+ mp3_play_data(buf, size_now, mp3_callback);
+ mp3_play_pause(true); /* kickoff audio */
+ queue[queue_write].buf += size_now;
+ queue[queue_write].len -= size_now;
+ }
+
+ queue_write++;
+ if (queue_write >= QUEUE_SIZE)
+ queue_write = 0;
+
+ return 0;
+}
+
+
+/***************** Public implementation *****************/
+
+void talk_init(void)
+{
+ has_voicefont = true; /* unless we fail later, assume we have one */
+ talk_buffer_steal();
+ queue_write = queue_read = 0;
+}
+
+
+/* somebody else claims the mp3 buffer, e.g. for regular play/record */
+int talk_buffer_steal(void)
+{
+ p_voicefont = NULL; /* indicate no voicefont (trashed) */
+ p_thumbnail = mp3buf; /* whole space for thumbnail */
+ size_for_thumbnail = mp3end - mp3buf;
+ return 0;
+}
+
+
+/* play a voice ID from voicefont */
+int talk_id(int id, bool enqueue)
+{
+ int clipsize;
+ unsigned char* clipbuf;
+ int unit;
+
+ if (mpeg_status()) /* busy, buffer in use */
+ return -1;
+
+ if (p_voicefont == NULL && has_voicefont)
+ load_voicefont(); /* reload needed */
+
+ if (p_voicefont == NULL) /* still no voices? */
+ return -1;
+
+ /* check if this is a special ID, with a value */
+ unit = ((unsigned)id) >> UNIT_SHIFT;
+ if (id != -1 && unit)
+ { /* sign-extend the value */
+ //splash(200, true,"unit=%d", unit);
+ id = (unsigned)id << (32-UNIT_SHIFT);
+ id >>= (32-UNIT_SHIFT);
+ talk_value(id, unit, enqueue); /* speak it */
+ return 0; /* and stop, end of special case */
+ }
+
+ if (id < 0 || id >= p_voicefont->id_max)
+ return -1;
+
+ clipsize = p_voicefont->index[id].size;
+ if (clipsize == 0) /* clip not included in voicefont */
+ return -1;
+
+ clipbuf = mp3buf + p_voicefont->index[id].offset;
+
+ queue_clip(clipbuf, clipsize, enqueue);
+
+ return 0;
+}
+
+
+/* play a thumbnail from file */
+int talk_file(char* filename, bool enqueue)
+{
+ int fd;
+ int size;
+
+ if (mpeg_status()) /* busy, buffer in use */
+ return -1;
+
+ if (p_thumbnail == NULL || size_for_thumbnail <= 0)
+ return -1;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) /* failed to open */
+ {
+ return 0;
+ }
+
+ size = read(fd, p_thumbnail, size_for_thumbnail);
+ close(fd);
+
+ /* ToDo: find audio, skip ID headers and trailers */
+
+ if (size)
+ {
+ bitswap(p_thumbnail, size);
+ queue_clip(p_thumbnail, size, enqueue);
+ }
+
+ return size;
+}
+
+
+/* say a numeric value, this works for english,
+ but not necessarily for other languages */
+int talk_number(int n, bool enqueue)
+{
+ int level = 0; // mille count
+ int mil = 1000000000; // highest possible "-illion"
+
+ if (!enqueue)
+ shutup(); /* cut off all the pending stuff */
+
+ if (n==0)
+ { // special case
+ talk_id(VOICE_ZERO, true);
+ return 0;
+ }
+
+ if (n<0)
+ {
+ talk_id(VOICE_MINUS, true);
+ n = -n;
+ }
+
+ while (n)
+ {
+ int segment = n / mil; // extract in groups of 3 digits
+ n -= segment * mil; // remove the used digits from number
+ mil /= 1000; // digit place for next round
+
+ if (segment)
+ {
+ int hundreds = segment / 100;
+ int ones = segment % 100;
+
+ if (hundreds)
+ {
+ talk_id(VOICE_ZERO + hundreds, true);
+ talk_id(VOICE_HUNDRED, true);
+ }
+
+ // combination indexing
+ if (ones > 20)
+ {
+ int tens = ones/10 + 18;
+ talk_id(VOICE_ZERO + tens, true);
+ ones %= 10;
+ }
+
+ // direct indexing
+ if (ones)
+ talk_id(VOICE_ZERO + ones, true);
+
+ // add billion, million, thousand
+ if (mil)
+ talk_id(VOICE_BILLION + level, true);
+ }
+ level++;
+ }
+
+ return 0;
+}
+
+int talk_value(int n, int unit, bool enqueue)
+{
+ int unit_id;
+ const int unit_voiced[] =
+ { /* lookup table for the voice ID of the units */
+ -1, -1, -1, /* regular ID, int, signed */
+ VOICE_MILLISECONDS, /* here come the "real" units */
+ VOICE_SECONDS,
+ VOICE_MINUTES,
+ VOICE_HOURS,
+ VOICE_KHZ,
+ VOICE_DB,
+ VOICE_PERCENT,
+ VOICE_MEGABYTE,
+ VOICE_GIGABYTE
+ };
+
+ if (unit < 0 || unit >= UNIT_LAST)
+ unit_id = -1;
+ else
+ unit_id = unit_voiced[unit];
+
+ if ((n==1 || n==-1) // singular?
+ && unit_id >= VOICE_SECONDS && unit_id <= VOICE_HOURS)
+ {
+ unit_id--; /* use the singular for those units which have */
+ }
+
+ /* special case with a "plus" before */
+ if (n > 0 && (unit == UNIT_SIGNED || unit == UNIT_DB))
+ {
+ talk_id(VOICE_PLUS, enqueue);
+ enqueue = true;
+ }
+
+ talk_number(n, enqueue); /* say the number */
+ talk_id(unit_id, true); /* say the unit, if any */
+
+ return 0;
+}
+
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 @@
#include <stdbool.h>
+enum {
+ UNIT_INT = 1, /* plain number */
+ UNIT_SIGNED, /* number with mandatory sign (even if positive) */
+ UNIT_MS, /* milliseconds */
+ UNIT_SEC, /* seconds */
+ UNIT_MIN, /* minutes */
+ UNIT_HOUR, /* hours */
+ UNIT_KHZ, /* kHz */
+ UNIT_DB, /* dB, mandatory sign */
+ UNIT_PERCENT, /* % */
+ UNIT_MB, /* megabyte */
+ UNIT_GB, /* gigabyte */
+ UNIT_LAST /* END MARKER */
+};
+
+#define UNIT_SHIFT (32-4) /* this many bits left from UNIT_xx enum */
+
+/* make a "talkable" ID from number + unit
+ unit is upper 4 bits, number the remaining (in regular 2's complement) */
+#define TALK_ID(n,u) ((u)<<UNIT_SHIFT | ((n) & ~(-1<<UNIT_SHIFT)))
+
+/* convenience macro to have both string and ID as arguments */
+#define STR(id) str(id), id
+
+
void talk_init(void);
int talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */
-int talk_id(int id, bool block); /* play a voice ID from voicefont */
-int talk_file(char* filename, bool block); /* play a thumbnail from file */
+int talk_id(int id, bool enqueue); /* play a voice ID from voicefont */
+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 */
#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 @@
#include "mp3data.h"
#include "buffer.h"
#include "mp3_playback.h"
-#include "talk.h"
#ifndef SIMULATOR
#include "i2c.h"
#include "mas.h"
@@ -2135,7 +2134,6 @@ void mpeg_record(char *filename)
recording_filename[MAX_PATH - 1] = 0;
disable_xing_header = false;
- talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
queue_post(&mpeg_queue, MPEG_RECORD, NULL);
}
@@ -2150,7 +2148,6 @@ static void start_prerecording(void)
prerecord_timeout = current_tick + HZ;
memset(prerecord_buffer, 0, sizeof(prerecord_buffer));
reset_mp3_buffer();
- talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
is_prerecording = true;
@@ -2407,7 +2404,6 @@ void mpeg_play(int offset)
#else
is_playing = true;
- talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
queue_post(&mpeg_queue, MPEG_PLAY, (void*)offset);
#endif /* #ifdef SIMULATOR */
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 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2004 Jörg Hohensohn
- *
- * This module collects the Talkbox and voice UI functions.
- * (Talkbox reads directory names from mp3 clips called thumbnails,
- * the voice UI lets menus and screens "talk" from a voicefont in memory.
- *
- * All files in this archive are subject to the GNU General Public License.
- * See the file COPYING in the source tree root for full license agreement.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#include <stdio.h>
-#include <stddef.h>
-#include "file.h"
-#include "buffer.h"
-#include "kernel.h"
-#include "mp3_playback.h"
-#include "mpeg.h"
-#include "talk.h"
-extern void bitswap(unsigned char *data, int length); /* no header for this */
-
-/***************** Constants *****************/
-
-#define VOICEFONT_FILENAME "/.rockbox/voicefont"
-
-
-/***************** Data types *****************/
-
-struct clip_entry /* one entry of the index table */
-{
- int offset; /* offset from start of voicefont file */
- int size; /* size of the clip */
-};
-
-struct voicefont /* file format of our "voicefont" */
-{
- int version; /* version of the voicefont */
- int headersize; /* size of the header, =offset to index */
- int id_max; /* number of clips contained */
- struct clip_entry index[]; /* followed by the index table */
- /* and finally the bitswapped mp3 clips, not visible here */
-};
-
-
-/***************** Globals *****************/
-
-static unsigned char* p_thumbnail; /* buffer for thumbnail */
-static long size_for_thumbnail; /* leftover buffer size for it */
-static struct voicefont* p_voicefont; /* loaded voicefont */
-static bool has_voicefont; /* a voicefont file is present */
-static bool is_playing; /* we're currently playing */
-
-
-
-/***************** Private implementation *****************/
-
-static int load_voicefont(void)
-{
- int fd;
- int size;
-
- p_voicefont = NULL; /* indicate no voicefont if we fail below */
-
- fd = open(VOICEFONT_FILENAME, O_RDONLY);
- if (fd < 0) /* failed to open */
- {
- p_voicefont = NULL; /* indicate no voicefont */
- has_voicefont = false; /* don't try again */
- return 0;
- }
-
- size = read(fd, mp3buf, mp3end - mp3buf);
- if (size > 1000
- && ((struct voicefont*)mp3buf)->headersize
- == offsetof(struct voicefont, index))
- {
- p_voicefont = (struct voicefont*)mp3buf;
-
- /* thumbnail buffer is the remaining space behind */
- p_thumbnail = mp3buf + size;
- p_thumbnail += (int)p_thumbnail % 2; /* 16-bit align */
- size_for_thumbnail = mp3end - p_thumbnail;
-
- /* max. DMA size, fixme */
- if (size_for_thumbnail > 0xFFFF)
- size_for_thumbnail = 0xFFFF;
- }
- else
- {
- has_voicefont = false; /* don't try again */
- }
- close(fd);
-
- return size;
-}
-
-
-/* called in ISR context if mp3 data got consumed */
-void mp3_callback(unsigned char** start, int* size)
-{
- (void)start; /* unused parameter, avoid warning */
- *size = 0; /* end of data */
- is_playing = false;
- mp3_play_stop(); /* fixme: should be done by caller */
-}
-
-/***************** Public implementation *****************/
-
-void talk_init(void)
-{
- has_voicefont = true; /* unless we fail later, assume we have one */
- talk_buffer_steal();
-}
-
-
-/* somebody else claims the mp3 buffer, e.g. for regular play/record */
-int talk_buffer_steal(void)
-{
- p_voicefont = NULL; /* indicate no voicefont (trashed) */
- p_thumbnail = mp3buf; /* whole space for thumbnail */
- size_for_thumbnail = mp3end - mp3buf;
- /* max. DMA size, fixme */
- if (size_for_thumbnail > 0xFFFF)
- size_for_thumbnail = 0xFFFF;
- return 0;
-}
-
-
-/* play a voice ID from voicefont */
-int talk_id(int id, bool block)
-{
- int clipsize;
-
- if (mpeg_status()) /* busy, buffer in use */
- return -1;
-
- if (p_voicefont == NULL && has_voicefont)
- load_voicefont(); /* reload needed */
-
- if (p_voicefont == NULL) /* still no voices? */
- return -1;
-
- if (id >= p_voicefont->id_max)
- return -1;
-
- clipsize = p_voicefont->index[id].size;
- if (clipsize == 0) /* clip not included in voicefont */
- return -1;
-
- is_playing = true;
- mp3_play_data(mp3buf + p_voicefont->index[id].offset,
- clipsize, mp3_callback);
- mp3_play_pause(true); /* kickoff audio */
-
- while(block && is_playing)
- sleep(1);
-
- return 0;
-}
-
-
-/* play a thumbnail from file */
-int talk_file(char* filename, bool block)
-{
- int fd;
- int size;
-
- if (mpeg_status()) /* busy, buffer in use */
- return -1;
-
- if (p_thumbnail == NULL || size_for_thumbnail <= 0)
- return -1;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0) /* failed to open */
- {
- return 0;
- }
-
- size = read(fd, p_thumbnail, size_for_thumbnail);
- close(fd);
-
- /* ToDo: find audio, skip ID headers and trailers */
-
- if (size)
- {
- bitswap(p_thumbnail, size);
- is_playing = true;
- mp3_play_data(p_thumbnail, size, mp3_callback);
- mp3_play_pause(true); /* kickoff audio */
-
- while(block && is_playing)
- sleep(1);
- }
-
- return size;
-}