summaryrefslogtreecommitdiffstats
path: root/apps/talk.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/talk.c')
-rw-r--r--apps/talk.c438
1 files changed, 246 insertions, 192 deletions
diff --git a/apps/talk.c b/apps/talk.c
index 73191c22c3..fb2f9cf3df 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -35,12 +35,12 @@
#include "voice_thread.h"
#include "audio.h"
#include "lang.h"
+#include "language.h"
#include "talk.h"
#include "metadata.h"
/*#define LOGF_ENABLE*/
#include "logf.h"
#include "bitswap.h"
-#include "structec.h"
#include "plugin.h" /* plugin_get_buffer() */
#include "debug.h"
#include "panic.h"
@@ -97,7 +97,7 @@ struct voicefile_header /* file format of our voice file */
#define MAX_CLIP_BUFFER_SIZE (1<<30)
#endif
#define THUMBNAIL_RESERVE (50000)
-
+#define NULL_TERMINATED ((size_t)-1)
/* Multiple thumbnails can be loaded back-to-back in this buffer. */
static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in
thumbnail buffer */
@@ -118,6 +118,7 @@ static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (
static bool talk_initialized; /* true if talk_init has been called */
static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */
static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */
+
/* size of the voice data in the voice file and the actually allocated buffer
* for it. voicebuf_size is always smaller or equal to voicefile_size */
static unsigned long voicefile_size, voicebuf_size;
@@ -226,7 +227,7 @@ static int shrink_callback(int handle, unsigned hints, void *start, size_t old_s
mutex_lock(&read_buffer_mutex);
/* the clip buffer isn't usable without index table */
- if (handle == index_handle && talk_handle > 0)
+ if (handle == index_handle)
talk_handle = core_free(talk_handle);
if (h)
*h = core_free(handle);
@@ -246,7 +247,7 @@ static struct buflib_callbacks talk_ops = {
static int open_voicefile(void)
{
- char buf[64];
+ char fname[MAX_PATH];
char* p_lang = DEFAULT_VOICE_LANG; /* default */
if ( global_settings.lang_file[0] &&
@@ -255,9 +256,8 @@ static int open_voicefile(void)
p_lang = (char *)global_settings.lang_file;
}
- snprintf(buf, sizeof(buf), LANG_DIR "/%s.voice", p_lang);
-
- return open(buf, O_RDONLY);
+ return open_pathfmt(fname, sizeof(fname),
+ O_RDONLY, LANG_DIR "/%s.voice", p_lang);
}
@@ -306,14 +306,15 @@ static int free_oldest_clip(void)
unsigned i;
int oldest = 0;
bool thumb = false;
- long age, now;
+ long age, now, next_age;
struct clip_entry* clipbuf;
struct clip_cache_metadata *cc = buflib_get_data(&clip_ctx, metadata_table_handle);
for(age = i = 0, now = current_tick; i < max_clips; i++)
{
if (cc[i].handle)
{
- if (thumb && cc[i].voice_id == VOICEONLY_DELIMITER && (now - cc[i].tick) > age)
+ next_age = (now - cc[i].tick);
+ if (thumb && cc[i].voice_id == VOICEONLY_DELIMITER && next_age > age)
{
/* thumb clips are freed first */
age = now - cc[i].tick;
@@ -323,14 +324,14 @@ static int free_oldest_clip(void)
{
if (cc[i].voice_id == VOICEONLY_DELIMITER)
{
- age = now - cc[i].tick;
+ age = next_age;
oldest = i;
thumb = true;
}
- else if ((now - cc[i].tick) > age && cc[i].voice_id != VOICE_PAUSE)
+ else if (next_age > age && cc[i].voice_id != VOICE_PAUSE)
{
/* find the last-used clip but never consider silence */
- age = now - cc[i].tick;
+ age = next_age;
oldest = i;
}
}
@@ -348,7 +349,6 @@ static int free_oldest_clip(void)
return oldest;
}
-
/* common code for load_initial_clips() and get_clip() */
static void add_cache_entry(int clip_handle, int table_index, int id)
{
@@ -496,28 +496,29 @@ static bool load_index_table(int fd, const struct voicefile_header *hdr)
return true;
ssize_t alloc_size = (hdr->id1_max + hdr->id2_max) * sizeof(struct clip_entry);
- index_handle = core_alloc_ex("voice index", alloc_size, &talk_ops);
+ index_handle = core_alloc_ex(alloc_size, &talk_ops);
if (index_handle < 0)
return false;
ret = read_to_handle(fd, index_handle, 0, alloc_size);
-
- if (ret == alloc_size)
+ if (ret != alloc_size)
{
+ index_handle = core_free(index_handle);
+ return false;
+ }
+
#ifdef ROCKBOX_LITTLE_ENDIAN
- struct clip_entry *buf;
- buf = core_get_data(index_handle);
- for (int i = 0; i < hdr->id1_max + hdr->id2_max; i++)
- {
- /* doesn't yield() */
- structec_convert(&buf[i], "ll", 1, true);
- }
-#endif
+ struct clip_entry *buf, *end;
+ buf = core_get_data(index_handle);
+ end = buf + hdr->id1_max + hdr->id2_max;
+ for (; buf != end; buf++)
+ {
+ buf->offset = swap32(buf->offset);
+ buf->size = swap32(buf->size);
}
- else
- index_handle = core_free(index_handle);
+#endif
- return ret == alloc_size;
+ return true;
}
static bool load_header(int fd, struct voicefile_header *hdr)
@@ -527,8 +528,11 @@ static bool load_header(int fd, struct voicefile_header *hdr)
return false;
#ifdef ROCKBOX_LITTLE_ENDIAN
- logf("Byte swapping voice file");
- structec_convert(&voicefile, "lllll", 1, true);
+ hdr->version = swap32(hdr->version);
+ hdr->target_id = swap32(hdr->target_id);
+ hdr->table = swap32(hdr->table);
+ hdr->id1_max = swap32(hdr->id1_max);
+ hdr->id2_max = swap32(hdr->id2_max);
#endif
return true;
}
@@ -536,7 +540,7 @@ static bool load_header(int fd, struct voicefile_header *hdr)
static bool create_clip_buffer(size_t max_size)
{
/* just allocate, populate on an as-needed basis later */
- talk_handle = core_alloc_ex("voice data", max_size, &talk_ops);
+ talk_handle = core_alloc_ex(max_size, &talk_ops);
if (talk_handle < 0)
goto alloc_err;
@@ -546,16 +550,17 @@ static bool create_clip_buffer(size_t max_size)
alloc_err:
talk_status = TALK_STATUS_ERR_ALLOC;
- if (index_handle > 0)
- index_handle = core_free(index_handle);
+ index_handle = core_free(index_handle);
return false;
}
+
static inline int load_voicefile_failure(int fd)
{
- /*if (fd >= 0) probably redundant */
- close(fd);
+ if (fd >= 0)
+ close(fd);
return -1;
}
+
/* load the voice file into the mp3 buffer */
static bool load_voicefile_index(int fd)
{
@@ -621,8 +626,7 @@ static bool load_voicefile_data(int fd)
/* just allocate, populate on an as-needed basis later
* re-create the clip buffer to ensure clip_ctx is up-to-date */
- if (talk_handle > 0)
- talk_handle = core_free(talk_handle);
+ talk_handle = core_free(talk_handle);
if (!create_clip_buffer(voicebuf_size))
return false;
@@ -721,6 +725,102 @@ static void mp3_callback(const void** start, size_t* size)
talk_queue_unlock();
}
+/***************** Private routines *****************/
+
+/* return if a voice codec is required or not */
+static bool talk_voice_required(void)
+{
+ return (has_voicefile) /* Voice file is available */
+ || (global_settings.talk_dir_clip) /* Thumbnail clips are required */
+ || (global_settings.talk_file_clip);
+}
+
+static bool talk_is_disabled(void)
+{
+ if (talk_temp_disable_count > 0 || (!check_audio_status()))
+ return true;
+ return false;
+}
+
+static void do_enqueue(bool enqueue)
+{
+ if (!enqueue)
+ talk_shutup(); /* cut off all the pending stuff */
+}
+
+/* spell a string */
+static int _talk_spell(const char* spell, size_t len, bool enqueue)
+{
+ char c; /* currently processed char */
+
+ if (talk_is_disabled())
+ return -1;
+
+ do_enqueue(enqueue); /* cut off all the pending stuff */
+
+ size_t len0 = len - 1;
+
+ while ((c = *spell++) != '\0' && len0-- < len)
+ {
+ /* if this grows into too many cases, I should use a table */
+ if (c >= 'A' && c <= 'Z')
+ talk_id(VOICE_CHAR_A + c - 'A', true);
+ else if (c >= 'a' && c <= 'z')
+ talk_id(VOICE_CHAR_A + c - 'a', true);
+ else if (c >= '0' && c <= '9')
+ talk_id(VOICE_ZERO + c - '0', true);
+ else if (c == '-')
+ talk_id(VOICE_MINUS, true);
+ else if (c == '+')
+ talk_id(VOICE_PLUS, true);
+ else if (c == '.')
+ talk_id(VOICE_DOT, true);
+ else if (c == ' ')
+ talk_id(VOICE_PAUSE, true);
+ else if (c == '/')
+ talk_id(VOICE_CHAR_SLASH, true);
+
+ while (QUEUE_LEVEL == QUEUE_SIZE - 1) /* queue full - busy loop */
+ yield();
+ }
+ return 0;
+}
+
+static int talk_spell_basename(const char *path,
+ const long *prefix_ids, bool enqueue)
+{
+ if(prefix_ids)
+ {
+ talk_idarray(prefix_ids, enqueue);
+ enqueue = true;
+ }
+ const char *basename;
+ size_t len = path_basename(path, &basename);
+
+ return _talk_spell(basename, len, enqueue);
+}
+
+/* Say year like "nineteen ninety nine" instead of "one thousand 9
+ hundred ninety nine". */
+static int talk_year(long year, bool enqueue)
+{
+ int rem;
+ if(year < 1100 || (year >=2000 && year < 2100))
+ /* just say it as a regular number */
+ return talk_number(year, enqueue);
+ /* Say century */
+ talk_number(year/100, enqueue);
+ rem = year%100;
+ if(rem == 0)
+ /* as in 1900 */
+ return talk_id(VOICE_HUNDRED, true);
+ if(rem <10)
+ /* as in 1905 */
+ talk_id(VOICE_ZERO, true);
+ /* sub-century year */
+ return talk_number(rem, true);
+}
+
/***************** Public routines *****************/
/* stop the playback and the pending clips */
@@ -748,8 +848,8 @@ static void queue_clip(struct queue_entry *clip, bool enqueue)
struct queue_entry *qe;
int queue_level;
- if (!enqueue)
- talk_shutup(); /* cut off all the pending stuff */
+ do_enqueue(enqueue); /* cut off all the pending stuff */
+
/* Something is being enqueued, force_enqueue_next override is no
longer in effect. */
force_enqueue_next = false;
@@ -783,14 +883,6 @@ static void queue_clip(struct queue_entry *clip, bool enqueue)
return;
}
-/* return if a voice codec is required or not */
-static bool talk_voice_required(void)
-{
- return (has_voicefile) /* Voice file is available */
- || (global_settings.talk_dir_clip) /* Thumbnail clips are required */
- || (global_settings.talk_file_clip);
-}
-
/***************** Public implementation *****************/
void talk_init(void)
@@ -812,8 +904,8 @@ void talk_init(void)
talk_force_shutup(); /* In case we have something speaking! */
talk_initialized = true;
- strlcpy((char *)last_lang, (char *)global_settings.lang_file,
- MAX_FILENAME);
+ strmemccpy((char *)last_lang, (char *)global_settings.lang_file,
+ MAX_FILENAME);
/* reset some states */
queue_write = queue_read = 0; /* reset the queue */
@@ -823,13 +915,13 @@ void talk_init(void)
voicefile_size = has_voicefile = 0;
/* need to free these as their size depends on the voice file, and
* this function is called when the talk voice file changes */
- if (index_handle > 0) index_handle = core_free(index_handle);
- if (talk_handle > 0) talk_handle = core_free(talk_handle);
+ index_handle = core_free(index_handle);
+ talk_handle = core_free(talk_handle);
/* don't free thumb handle, it doesn't depend on the actual voice file
* and so we can re-use it if it's already allocated in any event */
filehandle = open_voicefile();
- if (filehandle > -1)
+ if (filehandle >= 0)
{
if (!load_voicefile_index(filehandle))
{
@@ -880,12 +972,14 @@ void talk_init(void)
voice_thread_init();
out:
- close(filehandle); /* close again, this was just to detect presence */
+ if (filehandle >= 0)
+ close(filehandle); /* close again, this was just to detect presence */
}
/* somebody else claims the mp3 buffer, e.g. for regular play/record */
void talk_buffer_set_policy(int policy)
{
+#ifdef DEBUG
switch(policy)
{
case TALK_BUFFER_DEFAULT:
@@ -893,6 +987,9 @@ void talk_buffer_set_policy(int policy)
case TALK_BUFFER_LOOSE: give_buffer_away = true; break;
default: DEBUGF("Ignoring unknown policy\n"); break;
}
+#else
+ give_buffer_away = (policy == TALK_BUFFER_LOOSE);
+#endif
}
/* play a voice ID from voicefile */
@@ -904,10 +1001,8 @@ int talk_id(int32_t id, bool enqueue)
bool isloaded = true;
if (!has_voicefile)
- return 0; /* no voicefile loaded, not an error -> pretent success */
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ return 0; /* no voicefile loaded, not an error -> pretend success */
+ if (talk_is_disabled())
return -1;
if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */
@@ -949,6 +1044,7 @@ int talk_id(int32_t id, bool enqueue)
return 0;
}
+
/* Speaks zero or more IDs (from an array). */
int talk_idarray(const long *ids, bool enqueue)
{
@@ -982,22 +1078,18 @@ static int _talk_file(const char* filename,
int oldest = -1;
/* reload needed? */
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ if (talk_is_disabled())
return -1;
if (talk_handle <= 0 || index_handle <= 0)
{
- int fd = open_voicefile();
+ fd = open_voicefile();
if (fd < 0 || !load_voicefile_index(fd))
return load_voicefile_failure(fd);
load_voicefile_data(fd);
close(fd);
}
- if (!enqueue)
- /* shutup now to free the thumbnail buffer */
- talk_shutup();
+ do_enqueue(enqueue); /* shutup now to free the thumbnail buffer */
fd = open(filename, O_RDONLY);
if (fd < 0) /* failed to open */
@@ -1046,38 +1138,18 @@ int talk_file(const char *root, const char *dir, const char *file,
/* Play a thumbnail file */
{
char buf[MAX_PATH];
+ const char *fmt = "%s%s%s%s%s";
/* Does root end with a slash */
- char *slash = (root && root[0]
- && root[strlen(root)-1] != '/') ? "/" : "";
- snprintf(buf, MAX_PATH, "%s%s%s%s%s%s",
- root ? root : "", slash,
+ if(root && root[0] && root[strlen(root)-1] != '/')
+ fmt = "%s/%s%s%s%s";
+ snprintf(buf, MAX_PATH, fmt,
+ root ? root : "",
dir ? dir : "", dir ? "/" : "",
file ? file : "",
ext ? ext : "");
return _talk_file(buf, prefix_ids, enqueue);
}
-static int talk_spell_basename(const char *path,
- const long *prefix_ids, bool enqueue)
-{
- if(prefix_ids)
- {
- talk_idarray(prefix_ids, enqueue);
- enqueue = true;
- }
- char buf[MAX_PATH];
- /* Spell only the path component after the last slash */
- strlcpy(buf, path, sizeof(buf));
- if(strlen(buf) >1 && buf[strlen(buf)-1] == '/')
- /* strip trailing slash */
- buf[strlen(buf)-1] = '\0';
- char *ptr = strrchr(buf, '/');
- if(ptr && strlen(buf) >1)
- ++ptr;
- else ptr = buf;
- return talk_spell(ptr, enqueue);
-}
-
/* Play a file's .talk thumbnail, fallback to spelling the filename, or
go straight to spelling depending on settings. */
int talk_file_or_spell(const char *dirname, const char *filename,
@@ -1089,7 +1161,7 @@ int talk_file_or_spell(const char *dirname, const char *filename,
prefix_ids, enqueue) >0)
return 0;
}
- if (global_settings.talk_file == 2)
+ if (global_settings.talk_file == TALK_SPEAK_SPELL)
/* Either .talk clips are disabled, or as a fallback */
return talk_spell_basename(filename, prefix_ids, enqueue);
return 0;
@@ -1106,7 +1178,7 @@ int talk_dir_or_spell(const char* dirname,
prefix_ids, enqueue) >0)
return 0;
}
- if (global_settings.talk_dir == 2)
+ if (global_settings.talk_dir == TALK_SPEAK_SPELL)
/* Either .talk clips disabled or as a fallback */
return talk_spell_basename(dirname, prefix_ids, enqueue);
return 0;
@@ -1116,14 +1188,14 @@ int talk_dir_or_spell(const char* dirname,
back or going straight to spelling depending on settings. */
int talk_fullpath(const char* path, bool enqueue)
{
- if (!enqueue)
- talk_shutup();
+ do_enqueue(enqueue); /* cut off all the pending stuff */
+
if(path[0] != '/')
/* path ought to start with /... */
return talk_spell(path, true);
talk_id(VOICE_CHAR_SLASH, true);
char buf[MAX_PATH];
- strlcpy(buf, path, MAX_PATH);
+ strmemccpy(buf, path, MAX_PATH);
char *start = buf+1; /* start of current component */
char *ptr = strchr(start, '/'); /* end of current component */
while(ptr) { /* There are more slashes ahead */
@@ -1146,13 +1218,10 @@ int talk_number(long n, bool enqueue)
int level = 2; /* mille count */
long mil = 1000000000; /* highest possible "-illion" */
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ if (talk_is_disabled())
return -1;
- if (!enqueue)
- talk_shutup(); /* cut off all the pending stuff */
+ do_enqueue(enqueue); /* cut off all the pending stuff */
if (n==0)
{ /* special case */
@@ -1188,17 +1257,44 @@ int talk_number(long n, bool enqueue)
talk_id(VOICE_HUNDRED, true);
}
- /* combination indexing */
- if (ones > 20)
+ struct queue_entry tens_swap;
+ if (get_clip(VOICE_NUMERIC_TENS_SWAP_SEPARATOR, &tens_swap) >= 0)
{
- int tens = ones/10 + 18;
- talk_id(VOICE_ZERO + tens, true);
- ones %= 10;
+ /* direct indexing */
+ if (ones <= 20)
+ {
+ talk_id(VOICE_ZERO + ones, true);
+ }
+ else if (ones)
+ {
+ int tmp = ones % 10;
+ if (tmp)
+ {
+ talk_id(VOICE_ZERO + tmp, true);
+ talk_id(VOICE_NUMERIC_TENS_SWAP_SEPARATOR, true);
+ }
+ }
+ /* combination indexing */
+ if (ones > 20)
+ {
+ int tens = ones/10 + 18;
+ talk_id(VOICE_ZERO + tens, true);
+ }
}
+ else
+ {
+ /* 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);
+ /* direct indexing */
+ if (ones)
+ talk_id(VOICE_ZERO + ones, true);
+ }
/* add billion, million, thousand */
if (mil)
@@ -1210,27 +1306,6 @@ int talk_number(long n, bool enqueue)
return 0;
}
-/* Say year like "nineteen ninety nine" instead of "one thousand 9
- hundred ninety nine". */
-static int talk_year(long year, bool enqueue)
-{
- int rem;
- if(year < 1100 || year >=2000)
- /* just say it as a regular number */
- return talk_number(year, enqueue);
- /* Say century */
- talk_number(year/100, enqueue);
- rem = year%100;
- if(rem == 0)
- /* as in 1900 */
- return talk_id(VOICE_HUNDRED, true);
- if(rem <10)
- /* as in 1905 */
- talk_id(VOICE_ZERO, true);
- /* sub-century year */
- return talk_number(rem, true);
-}
-
/* Say time duration/interval. Input is time in seconds,
say hours,minutes,seconds. */
static int talk_time_unit(long secs, bool enqueue)
@@ -1303,9 +1378,7 @@ int talk_value_decimal(long n, int unit, int decimals, bool enqueue)
char tbuf[8];
char fmt[] = "%0nd";
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ if (talk_is_disabled())
return -1;
/* special pronounciation for year number */
@@ -1350,21 +1423,28 @@ int talk_value_decimal(long n, int unit, int decimals, bool enqueue)
return 0;
}
+ if (lang_units_first())
+ talk_id(unit_id, true); /* say the unit, if any */
talk_number(n, enqueue); /* say the number */
- talk_id(unit_id, true); /* say the unit, if any */
+ if (!lang_units_first())
+ talk_id(unit_id, true); /* say the unit, if any */
return 0;
}
+static inline void talk_time_value(long n, int unit, bool enqueue)
+{
+ if (n != 0)
+ talk_value_decimal(n, unit, 0, enqueue);
+}
+
/* Say time duration/interval. Input is time unit specifies base unit,
say hours,minutes,seconds, milliseconds. or any combination thereof */
int talk_time_intervals(long time, int unit_idx, bool enqueue)
{
unsigned long units_in[UNIT_IDX_TIME_COUNT];
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ if (talk_is_disabled())
return -1;
if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */
@@ -1376,8 +1456,7 @@ int talk_time_intervals(long time, int unit_idx, bool enqueue)
close(fd);
}
- if (!enqueue)
- talk_shutup(); /* cut off all the pending stuff */
+ do_enqueue(enqueue); /* cut off all the pending stuff */
time_split_units(unit_idx, labs(time), &units_in);
@@ -1388,22 +1467,10 @@ int talk_time_intervals(long time, int unit_idx, bool enqueue)
talk_value(0, unit_idx, true);
else
{
- if (units_in[UNIT_IDX_HR] != 0)
- {
- talk_value(units_in[UNIT_IDX_HR], UNIT_HOUR, true);
- }
- if (units_in[UNIT_IDX_MIN] != 0)
- {
- talk_value(units_in[UNIT_IDX_MIN], UNIT_MIN, true);
- }
- if (units_in[UNIT_IDX_SEC] != 0)
- {
- talk_value(units_in[UNIT_IDX_SEC], UNIT_SEC, true);
- }
- if (units_in[UNIT_IDX_MS] != 0)
- {
- talk_value(units_in[UNIT_IDX_MS], UNIT_MS, true);
- }
+ talk_time_value(units_in[UNIT_IDX_HR], UNIT_HOUR, true);
+ talk_time_value(units_in[UNIT_IDX_MIN], UNIT_MIN, true);
+ talk_time_value(units_in[UNIT_IDX_SEC], UNIT_SEC, true);
+ talk_time_value(units_in[UNIT_IDX_MS], UNIT_MS, true);
}
return -1;
@@ -1412,38 +1479,7 @@ int talk_time_intervals(long time, int unit_idx, bool enqueue)
/* spell a string */
int talk_spell(const char* spell, bool enqueue)
{
- char c; /* currently processed char */
-
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
- return -1;
-
- if (!enqueue)
- talk_shutup(); /* cut off all the pending stuff */
-
- while ((c = *spell++) != '\0')
- {
- /* if this grows into too many cases, I should use a table */
- if (c >= 'A' && c <= 'Z')
- talk_id(VOICE_CHAR_A + c - 'A', true);
- else if (c >= 'a' && c <= 'z')
- talk_id(VOICE_CHAR_A + c - 'a', true);
- else if (c >= '0' && c <= '9')
- talk_id(VOICE_ZERO + c - '0', true);
- else if (c == '-')
- talk_id(VOICE_MINUS, true);
- else if (c == '+')
- talk_id(VOICE_PLUS, true);
- else if (c == '.')
- talk_id(VOICE_DOT, true);
- else if (c == ' ')
- talk_id(VOICE_PAUSE, true);
- else if (c == '/')
- talk_id(VOICE_CHAR_SLASH, true);
- }
-
- return 0;
+ return _talk_spell(spell, NULL_TERMINATED, enqueue);
}
void talk_disable(bool disable)
@@ -1459,19 +1495,38 @@ void talk_setting(const void *global_settings_variable)
const struct settings_list *setting;
if (!global_settings.talk_menu)
return;
- setting = find_setting(global_settings_variable, NULL);
+ setting = find_setting(global_settings_variable);
if (setting == NULL)
return;
if (setting->lang_id)
talk_id(setting->lang_id,false);
}
-
void talk_date(const struct tm *tm, bool enqueue)
{
- talk_id(LANG_MONTH_JANUARY + tm->tm_mon, enqueue);
- talk_number(tm->tm_mday, true);
- talk_number(1900 + tm->tm_year, true);
+ const char *format = str(LANG_VOICED_DATE_FORMAT);
+ const char *ptr;
+
+ do_enqueue(enqueue); /* cut off all the pending stuff */
+
+ for (ptr = format ; *ptr ; ptr++) {
+ switch(*ptr) {
+ case 'Y':
+ talk_number(1900 + tm->tm_year, true);
+ break;
+ case 'A':
+ talk_id(LANG_MONTH_JANUARY + tm->tm_mon, true);
+ break;
+ case 'm':
+ talk_number(tm->tm_mon + 1, true);
+ break;
+ case 'd':
+ talk_number(tm->tm_mday, true);
+ break;
+ default:
+ break;
+ }
+ }
}
void talk_time(const struct tm *tm, bool enqueue)
@@ -1532,8 +1587,7 @@ void talk_announce_voice_invalid(void)
int buf_handle;
struct queue_entry qe;
- const char talkfile[] =
- LANG_DIR "/InvalidVoice_" DEFAULT_VOICE_LANG ".talk";
+ const char talkfile[] = LANG_DIR "/InvalidVoice_" DEFAULT_VOICE_LANG ".talk";
if (global_settings.talk_menu && talk_status != TALK_STATUS_OK)
{
@@ -1589,7 +1643,7 @@ bool talk_get_debug_data(struct talk_debug_data *data)
if (global_settings.lang_file[0] && global_settings.lang_file[0] != 0xff)
p_lang = (char *)global_settings.lang_file;
- strlcpy(data->voicefile, p_lang, sizeof(data->voicefile));
+ strmemccpy(data->voicefile, p_lang, sizeof(data->voicefile));
if (!has_voicefile || index_handle <= 0)
{