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
This commit is contained in:
Jörg Hohensohn 2004-03-19 22:15:53 +00:00
parent 239b70fad3
commit fa97f161ab
14 changed files with 619 additions and 375 deletions

View file

@ -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

View file

@ -44,6 +44,7 @@
#include "buffer.h"
#include "screens.h"
#include "playlist_menu.h"
#include "talk.h"
#ifdef HAVE_FMRADIO
#include "radio.h"
#endif

View file

@ -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;

View file

@ -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"

View file

@ -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;
}

View file

@ -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 */

View file

@ -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,

View file

@ -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
{

View file

@ -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 );

View file

@ -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,

398
apps/talk.c Normal file
View file

@ -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;
}

View file

@ -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__ */

View file

@ -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 */

View file

@ -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;
}