Don't try to resolve an empty path.
[maemo-rb.git] / apps / talk.c
blobf3ca967d3a7b697e29f343a1ba25a7552ee6b27f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 Jörg Hohensohn
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 voicefile in memory.
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
22 * KIND, either express or implied.
24 ****************************************************************************/
26 #include <stdio.h>
27 #include <stddef.h>
28 #include "string-extra.h"
29 #include "file.h"
30 #include "system.h"
31 #include "kernel.h"
32 #include "settings.h"
33 #include "settings_list.h"
34 #if CONFIG_CODEC == SWCODEC
35 #include "voice_thread.h"
36 #else
37 #include "mp3_playback.h"
38 #endif
39 #include "audio.h"
40 #include "lang.h"
41 #include "talk.h"
42 #include "metadata.h"
43 /*#define LOGF_ENABLE*/
44 #include "logf.h"
45 #include "bitswap.h"
46 #include "structec.h"
47 #include "plugin.h" /* plugin_get_buffer() */
48 #include "debug.h"
50 /* Memory layout varies between targets because the
51 Archos (MASCODEC) devices cannot mix voice and audio playback
53 MASCODEC | MASCODEC | SWCODEC
54 (playing) | (stopped) |
55 voicebuf-----------+-----------+------------
56 audio | voice | voice
57 |-----------|------------
58 | thumbnail | thumbnail
59 | |------------
60 | | filebuf
61 | |------------
62 | | audio
63 voicebufend----------+-----------+------------
65 SWCODEC allocates dedicated buffers (except voice and thumbnail are together
66 in the talkbuf), MASCODEC reuses audiobuf. */
69 /***************** Constants *****************/
71 #define QUEUE_SIZE 64 /* must be a power of two */
72 #define QUEUE_MASK (QUEUE_SIZE-1)
73 const char* const dir_thumbnail_name = "_dirname.talk";
74 const char* const file_thumbnail_ext = ".talk";
76 /***************** Functional Macros *****************/
78 #define QUEUE_LEVEL ((queue_write - queue_read) & QUEUE_MASK)
80 #define LOADED_MASK 0x80000000 /* MSB */
82 /* swcodec: cap p_thumnail to MAX_THUMNAIL_BUFSIZE since audio keeps playing
83 * while voice
84 * hwcodec: just use whatever is left in the audiobuffer, music
85 * playback is impossible => no cap */
86 #if CONFIG_CODEC == SWCODEC
87 #define MAX_THUMBNAIL_BUFSIZE 0x10000
88 #endif
90 /***************** Data types *****************/
92 struct clip_entry /* one entry of the index table */
94 int offset; /* offset from start of voicefile file */
95 int size; /* size of the clip */
98 struct voicefile /* file format of our voice file */
100 int version; /* version of the voicefile */
101 int target_id; /* the rockbox target the file was made for */
102 int table; /* offset to index table, (=header size) */
103 int id1_max; /* number of "normal" clips contained in above index */
104 int id2_max; /* number of "voice only" clips contained in above index */
105 struct clip_entry index[]; /* followed by the index tables */
106 /* and finally the mp3 clips, not visible here, bitswapped
107 for SH based players */
110 struct queue_entry /* one entry of the internal queue */
112 unsigned char* buf;
113 long len;
117 /***************** Globals *****************/
119 #if (CONFIG_CODEC == SWCODEC && MEMORYSIZE <= 2) || defined(ONDIO_SERIES)
120 /* On low memory swcodec targets the entire voice file wouldn't fit in memory
121 * together with codecs, so we load clips each time they are accessed.
122 * The Ondios have slow storage access and loading the entire voice file would
123 * take several seconds, so we use the same mechanism. */
124 #define TALK_PARTIAL_LOAD
125 #endif
127 #ifdef TALK_PARTIAL_LOAD
128 static unsigned char *clip_buffer;
129 static long max_clipsize; /* size of the biggest clip */
130 static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */
131 static uint8_t clip_age[QUEUE_SIZE];
132 #if QUEUE_SIZE > 255
133 # error clip_age[] type too small
134 #endif
135 #endif
137 static char* voicebuf; /* root pointer to our buffer */
138 static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */
139 /* Multiple thumbnails can be loaded back-to-back in this buffer. */
140 static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in
141 thumbnail buffer */
142 static long size_for_thumbnail; /* total thumbnail buffer size */
143 static struct voicefile* p_voicefile; /* loaded voicefile */
144 static bool has_voicefile; /* a voicefile file is present */
145 static bool need_shutup; /* is there possibly any voice playing to be shutup */
146 static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */
147 static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */
148 static int queue_write; /* write index of queue, by application */
149 static int queue_read; /* read index of queue, by ISR context */
150 #if CONFIG_CODEC == SWCODEC
151 /* protects queue_read, queue_write and thumbnail_buf_used */
152 static struct mutex queue_mutex SHAREDBSS_ATTR;
153 #define talk_queue_lock() ({ mutex_lock(&queue_mutex); })
154 #define talk_queue_unlock() ({ mutex_unlock(&queue_mutex); })
155 #else
156 #define talk_queue_lock() ({ })
157 #define talk_queue_unlock() ({ })
158 #endif /* CONFIG_CODEC */
159 static int sent; /* how many bytes handed over to playback, owned by ISR */
160 static unsigned char curr_hd[3]; /* current frame header, for re-sync */
161 static int filehandle = -1; /* global, so we can keep the file open if needed */
162 static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */
163 static long silence_len; /* length of the VOICE_PAUSE clip */
164 static unsigned char* p_lastclip; /* address of latest clip, for silence add */
165 static unsigned long voicefile_size = 0; /* size of the loaded voice file */
166 static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */
167 static bool talk_initialized; /* true if talk_init has been called */
168 static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */
171 /***************** Private implementation *****************/
173 static int open_voicefile(void)
175 char buf[64];
176 char* p_lang = "english"; /* default */
178 if ( global_settings.lang_file[0] &&
179 global_settings.lang_file[0] != 0xff )
180 { /* try to open the voice file of the selected language */
181 p_lang = (char *)global_settings.lang_file;
184 snprintf(buf, sizeof(buf), LANG_DIR "/%s.voice", p_lang);
186 return open(buf, O_RDONLY);
190 /* fetch a clip from the voice file */
191 static unsigned char* get_clip(long id, long* p_size)
193 long clipsize;
194 unsigned char* clipbuf;
196 if (id > VOICEONLY_DELIMITER)
197 { /* voice-only entries use the second part of the table */
198 id -= VOICEONLY_DELIMITER + 1;
199 if (id >= p_voicefile->id2_max)
200 return NULL; /* must be newer than we have */
201 id += p_voicefile->id1_max; /* table 2 is behind table 1 */
203 else
204 { /* normal use of the first table */
205 if (id >= p_voicefile->id1_max)
206 return NULL; /* must be newer than we have */
209 clipsize = p_voicefile->index[id].size;
210 if (clipsize == 0) /* clip not included in voicefile */
211 return NULL;
213 #ifndef TALK_PARTIAL_LOAD
214 clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset;
215 #endif
217 #ifdef TALK_PARTIAL_LOAD
218 if (!(clipsize & LOADED_MASK))
219 { /* clip needs loading */
220 int idx = 0;
221 if (id == VOICE_PAUSE) {
222 idx = QUEUE_SIZE; /* we keep VOICE_PAUSE loaded */
223 } else {
224 int oldest = 0, i;
225 for(i=0; i<QUEUE_SIZE; i++) {
226 if (buffered_id[i] < 0) {
227 /* found a free entry, that means the buffer isn't
228 * full yet. */
229 idx = i;
230 break;
233 /* find the oldest clip */
234 if(clip_age[i] > oldest) {
235 idx = i;
236 oldest = clip_age[i];
239 /* increment age of each loaded clip */
240 clip_age[i]++;
242 clip_age[idx] = 0; /* reset clip's age */
244 clipbuf = clip_buffer + idx * max_clipsize;
246 lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET);
247 if (read(filehandle, clipbuf, clipsize) != clipsize)
248 return NULL; /* read error */
250 p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */
252 if (id != VOICE_PAUSE) {
253 if (buffered_id[idx] >= 0) {
254 /* mark previously loaded clip as unloaded */
255 p_voicefile->index[buffered_id[idx]].size &= ~LOADED_MASK;
257 buffered_id[idx] = id;
260 else
261 { /* clip is in memory already */
262 /* Find where it was loaded */
263 clipbuf = clip_buffer;
264 if (id == VOICE_PAUSE) {
265 clipbuf += QUEUE_SIZE * max_clipsize;
266 } else {
267 int idx;
268 for (idx=0; idx<QUEUE_SIZE; idx++)
269 if (buffered_id[idx] == id) {
270 clipbuf += idx * max_clipsize;
271 clip_age[idx] = 0; /* reset clip's age */
272 break;
275 clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */
277 #endif /* TALK_PARTIAL_LOAD */
279 *p_size = clipsize;
280 return clipbuf;
284 /* load the voice file into the mp3 buffer */
285 static void load_voicefile(bool probe, char* buf, size_t bufsize)
287 union voicebuf {
288 unsigned char* buf;
289 struct voicefile* file;
291 union voicebuf voicebuf;
293 size_t load_size, alloc_size;
294 ssize_t got_size;
295 #ifdef ROCKBOX_LITTLE_ENDIAN
296 int i;
297 #endif
299 if (!probe)
300 filehandle = open_voicefile();
301 if (filehandle < 0) /* failed to open */
302 goto load_err;
304 voicebuf.buf = buf;
305 if (!voicebuf.buf)
306 goto load_err;
308 #ifdef TALK_PARTIAL_LOAD
309 /* load only the header for now */
310 load_size = sizeof(struct voicefile);
311 #else
312 /* load the entire file */
313 load_size = filesize(filehandle);
314 #endif
315 if (load_size > bufsize) /* won't fit? */
316 goto load_err;
318 got_size = read(filehandle, voicebuf.buf, load_size);
319 if (got_size != (ssize_t)load_size /* failure */)
320 goto load_err;
322 alloc_size = load_size;
324 #ifdef ROCKBOX_LITTLE_ENDIAN
325 logf("Byte swapping voice file");
326 structec_convert(voicebuf.buf, "lllll", 1, true);
327 #endif
329 /* format check */
330 if (voicebuf.file->table == sizeof(struct voicefile))
332 p_voicefile = voicebuf.file;
334 if (p_voicefile->version != VOICE_VERSION ||
335 p_voicefile->target_id != TARGET_ID)
337 logf("Incompatible voice file");
338 goto load_err;
341 else
342 goto load_err;
344 #ifdef TALK_PARTIAL_LOAD
345 /* load the index table, now that we know its size from the header */
346 load_size = (p_voicefile->id1_max + p_voicefile->id2_max)
347 * sizeof(struct clip_entry);
349 if (load_size > bufsize) /* won't fit? */
350 goto load_err;
352 got_size = read(filehandle, &p_voicefile->index[0], load_size);
353 if (got_size != (ssize_t)load_size) /* read error */
354 goto load_err;
356 alloc_size += load_size;
357 #else
358 close(filehandle);
359 filehandle = -1;
360 #endif /* TALK_PARTIAL_LOAD */
362 #ifdef ROCKBOX_LITTLE_ENDIAN
363 for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++)
364 structec_convert(&p_voicefile->index[i], "ll", 1, true);
365 #endif
367 #ifdef TALK_PARTIAL_LOAD
368 clip_buffer = (unsigned char *) p_voicefile + p_voicefile->table;
369 unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
370 clip_buffer += clips * sizeof(struct clip_entry); /* skip index */
371 #endif
372 if (!probe) {
373 /* make sure to have the silence clip, if available */
374 p_silence = get_clip(VOICE_PAUSE, &silence_len);
377 #ifdef TALK_PARTIAL_LOAD
378 alloc_size += silence_len + QUEUE_SIZE;
379 #endif
381 if (alloc_size > bufsize)
382 goto load_err;
384 /* now move p_thumbnail behind the voice clip buffer */
385 p_thumbnail = voicebuf.buf + alloc_size;
386 p_thumbnail += (long)p_thumbnail % 2; /* 16-bit align */
387 size_for_thumbnail = voicebuf.buf + bufsize - p_thumbnail;
388 #if CONFIG_CODEC == SWCODEC
389 size_for_thumbnail = MIN(size_for_thumbnail, MAX_THUMBNAIL_BUFSIZE);
390 #endif
391 if (size_for_thumbnail <= 0)
392 p_thumbnail = NULL;
394 return;
395 load_err:
396 p_voicefile = NULL;
397 has_voicefile = false; /* don't try again */
398 if (filehandle >= 0)
400 close(filehandle);
401 filehandle = -1;
403 return;
407 /* called in ISR context (on HWCODEC) if mp3 data got consumed */
408 static void mp3_callback(const void** start, size_t* size)
410 queue[queue_read].len -= sent; /* we completed this */
411 queue[queue_read].buf += sent;
413 if (queue[queue_read].len > 0) /* current clip not finished? */
414 { /* feed the next 64K-1 chunk */
415 #if CONFIG_CODEC != SWCODEC
416 sent = MIN(queue[queue_read].len, 0xFFFF);
417 #else
418 sent = queue[queue_read].len;
419 #endif
420 *start = queue[queue_read].buf;
421 *size = sent;
422 return;
424 talk_queue_lock();
425 if(p_thumbnail
426 && queue[queue_read].buf == p_thumbnail +thumbnail_buf_used)
427 thumbnail_buf_used = 0;
428 if (sent > 0) /* go to next entry */
430 queue_read = (queue_read + 1) & QUEUE_MASK;
433 re_check:
435 if (QUEUE_LEVEL != 0) /* queue is not empty? */
436 { /* start next clip */
437 #if CONFIG_CODEC != SWCODEC
438 sent = MIN(queue[queue_read].len, 0xFFFF);
439 #else
440 sent = queue[queue_read].len;
441 #endif
442 *start = p_lastclip = queue[queue_read].buf;
443 *size = sent;
444 curr_hd[0] = p_lastclip[1];
445 curr_hd[1] = p_lastclip[2];
446 curr_hd[2] = p_lastclip[3];
448 else if (p_silence != NULL /* silence clip available */
449 && p_lastclip != p_silence /* previous clip wasn't silence */
450 && !(p_lastclip >= p_thumbnail /* ..or thumbnail */
451 && p_lastclip < p_thumbnail +size_for_thumbnail))
452 { /* add silence clip when queue runs empty playing a voice clip */
453 queue[queue_write].buf = p_silence;
454 queue[queue_write].len = silence_len;
455 queue_write = (queue_write + 1) & QUEUE_MASK;
457 goto re_check;
459 else
461 *size = 0; /* end of data */
463 talk_queue_unlock();
466 /***************** Public routines *****************/
468 /* stop the playback and the pending clips */
469 void talk_force_shutup(void)
471 /* Most of this is MAS only */
472 #if CONFIG_CODEC != SWCODEC
473 #ifdef SIMULATOR
474 return;
475 #endif
476 unsigned char* pos;
477 unsigned char* search;
478 unsigned char* end;
479 if (QUEUE_LEVEL == 0) /* has ended anyway */
480 return;
482 #if CONFIG_CPU == SH7034
483 CHCR3 &= ~0x0001; /* disable the DMA (and therefore the interrupt also) */
484 #endif /* CONFIG_CPU == SH7034 */
485 /* search next frame boundary and continue up to there */
486 pos = search = mp3_get_pos();
487 end = queue[queue_read].buf + queue[queue_read].len;
489 if (pos >= queue[queue_read].buf
490 && pos <= end) /* really our clip? */
491 { /* (for strange reasons this isn't nesessarily the case) */
492 /* find the next frame boundary */
493 while (search < end) /* search the remaining data */
495 if (*search++ != 0xFF) /* quick search for frame sync byte */
496 continue; /* (this does the majority of the job) */
498 /* look at the (bitswapped) rest of header candidate */
499 if (search[0] == curr_hd[0] /* do the quicker checks first */
500 && search[2] == curr_hd[2]
501 && (search[1] & 0x30) == (curr_hd[1] & 0x30)) /* sample rate */
503 search--; /* back to the sync byte */
504 break; /* From looking at it, this is our header. */
508 if (search-pos)
509 { /* play old data until the frame end, to keep the MAS in sync */
510 sent = search-pos;
512 queue_write = (queue_read + 1) & QUEUE_MASK; /* will be empty after next callback */
513 queue[queue_read].len = sent; /* current one ends after this */
515 #if CONFIG_CPU == SH7034
516 DTCR3 = sent; /* let the DMA finish this frame */
517 CHCR3 |= 0x0001; /* re-enable DMA */
518 #endif /* CONFIG_CPU == SH7034 */
519 thumbnail_buf_used = 0;
520 return;
523 #endif /* CONFIG_CODEC != SWCODEC */
525 /* Either SWCODEC, or MAS had nothing to do (was frame boundary or not our clip) */
526 mp3_play_stop();
527 talk_queue_lock();
528 queue_write = queue_read = 0; /* reset the queue */
529 thumbnail_buf_used = 0;
530 talk_queue_unlock();
531 need_shutup = false;
534 /* Shutup the voice, except if force_enqueue_next is set. */
535 void talk_shutup(void)
537 if (need_shutup && !force_enqueue_next)
538 talk_force_shutup();
541 /* schedule a clip, at the end or discard the existing queue */
542 static void queue_clip(unsigned char* buf, long size, bool enqueue)
544 int queue_level;
546 if (!enqueue)
547 talk_shutup(); /* cut off all the pending stuff */
548 /* Something is being enqueued, force_enqueue_next override is no
549 longer in effect. */
550 force_enqueue_next = false;
552 if (!size)
553 return; /* safety check */
554 #if CONFIG_CPU == SH7034
555 /* disable the DMA temporarily, to be safe of race condition */
556 CHCR3 &= ~0x0001;
557 #endif
558 talk_queue_lock();
559 queue_level = QUEUE_LEVEL; /* check old level */
561 if (queue_level < QUEUE_SIZE - 1) /* space left? */
563 queue[queue_write].buf = buf; /* populate an entry */
564 queue[queue_write].len = size;
565 queue_write = (queue_write + 1) & QUEUE_MASK;
567 talk_queue_unlock();
569 if (queue_level == 0)
570 { /* queue was empty, we have to do the initial start */
571 p_lastclip = buf;
572 #if CONFIG_CODEC != SWCODEC
573 sent = MIN(size, 0xFFFF); /* DMA can do no more */
574 #else
575 sent = size;
576 #endif
577 mp3_play_data(buf, sent, mp3_callback);
578 curr_hd[0] = buf[1];
579 curr_hd[1] = buf[2];
580 curr_hd[2] = buf[3];
581 mp3_play_pause(true); /* kickoff audio */
583 else
585 #if CONFIG_CPU == SH7034
586 CHCR3 |= 0x0001; /* re-enable DMA */
587 #endif
590 need_shutup = true;
592 return;
595 static void alloc_thumbnail_buf(void)
597 /* use the audio buffer now, need to release before loading a voice */
598 p_thumbnail = voicebuf;
599 #if CONFIG_CODEC == SWCODEC
600 size_for_thumbnail = MAX_THUMBNAIL_BUFSIZE;
601 #endif
602 thumbnail_buf_used = 0;
605 /* common code for talk_init() and talk_buffer_steal() */
606 static void reset_state(void)
608 queue_write = queue_read = 0; /* reset the queue */
609 p_voicefile = NULL; /* indicate no voicefile (trashed) */
610 p_thumbnail = NULL; /* no thumbnails either */
612 #ifdef TALK_PARTIAL_LOAD
613 int i;
614 for(i=0; i<QUEUE_SIZE; i++)
615 buffered_id[i] = -1;
616 #endif
618 p_silence = NULL; /* pause clip not accessible */
619 voicebuf = NULL; /* voice buffer is gone */
622 #if CONFIG_CODEC == SWCODEC
623 static bool restore_state(void)
625 if (!voicebuf)
627 size_t size;
628 audio_restore_playback(AUDIO_WANT_VOICE);
629 voicebuf = audio_get_buffer(false, &size);
632 return !!voicebuf;
634 #endif /* CONFIG_CODEC == SWCODEC */
637 /***************** Public implementation *****************/
639 void talk_init(void)
641 talk_temp_disable_count = 0;
642 if (talk_initialized && !strcasecmp(last_lang, global_settings.lang_file))
644 /* not a new file, nothing to do */
645 return;
648 #if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
649 if (filehandle >= 0)
651 close(filehandle);
652 filehandle = -1;
654 #endif
656 #if CONFIG_CODEC == SWCODEC
657 if(!talk_initialized)
658 mutex_init(&queue_mutex);
659 #endif /* CONFIG_CODEC == SWCODEC */
661 talk_initialized = true;
662 strlcpy((char *)last_lang, (char *)global_settings.lang_file,
663 MAX_FILENAME);
665 filehandle = open_voicefile();
666 if (filehandle < 0) {
667 has_voicefile = false;
668 voicefile_size = 0;
669 return;
672 voicefile_size = filesize(filehandle);
674 audio_get_buffer(false, NULL); /* Must tell audio to reinitialize */
675 reset_state(); /* use this for most of our inits */
677 #ifdef TALK_PARTIAL_LOAD
678 size_t bufsize;
679 char* buf = plugin_get_buffer(&bufsize);
680 /* we won't load the full file, we only need the index */
681 load_voicefile(true, buf, bufsize);
682 if (!p_voicefile)
683 return;
685 unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
686 unsigned i;
687 int silence_size = 0;
689 for(i=0; i<clips; i++) {
690 int size = p_voicefile->index[i].size;
691 if (size > max_clipsize)
692 max_clipsize = size;
693 if (i == VOICE_PAUSE)
694 silence_size = size;
697 voicefile_size = p_voicefile->table + clips * sizeof(struct clip_entry);
698 voicefile_size += max_clipsize * QUEUE_SIZE + silence_size;
699 p_voicefile = NULL; /* Don't pretend we can load talk clips just yet */
700 #endif
703 /* test if we can open and if it fits in the audiobuffer */
704 size_t audiobufsz = audio_buffer_available();
705 if (voicefile_size <= audiobufsz) {
706 has_voicefile = true;
707 } else {
708 has_voicefile = false;
709 voicefile_size = 0;
712 close(filehandle); /* close again, this was just to detect presence */
713 filehandle = -1;
715 #if CONFIG_CODEC == SWCODEC
716 /* Safe to init voice playback engine now since we now know if talk is
717 required or not */
718 voice_thread_init();
719 #endif
722 #if CONFIG_CODEC == SWCODEC
723 /* return if a voice codec is required or not */
724 bool talk_voice_required(void)
726 return (voicefile_size != 0) /* Voice file is available */
727 || (global_settings.talk_dir_clip) /* Thumbnail clips are required */
728 || (global_settings.talk_file_clip);
730 #endif
732 /* return size of voice file */
733 static size_t talk_get_buffer_size(void)
735 #if CONFIG_CODEC == SWCODEC
736 return voicefile_size + MAX_THUMBNAIL_BUFSIZE;
737 #else
738 return audio_buffer_available();
739 #endif
742 /* Sets the buffer for the voicefile and returns how many bytes of this
743 * buffer we will use for the voicefile */
744 size_t talkbuf_init(char *bufstart)
746 bool changed = voicebuf != bufstart;
748 if (changed) /* must reload voice file */
749 reset_state();
751 if (bufstart)
752 voicebuf = bufstart;
754 return talk_get_buffer_size();
757 /* somebody else claims the mp3 buffer, e.g. for regular play/record */
758 void talk_buffer_steal(void)
760 #if CONFIG_CODEC != SWCODEC
761 mp3_play_stop();
762 #endif
763 #if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
764 if (filehandle >= 0)
766 close(filehandle);
767 filehandle = -1;
769 #endif
770 reset_state();
773 /* play a voice ID from voicefile */
774 int talk_id(int32_t id, bool enqueue)
776 long clipsize;
777 unsigned char* clipbuf;
778 int32_t unit;
779 int decimals;
781 if (talk_temp_disable_count > 0)
782 return -1; /* talking has been disabled */
783 #if CONFIG_CODEC == SWCODEC
784 /* If talk buffer was stolen, it must be restored for voicefile's sake */
785 if (!restore_state())
786 return -1; /* cannot get any space */
787 #else
788 if (audio_status()) /* busy, buffer in use */
789 return -1;
790 #endif
792 if (p_voicefile == NULL && has_voicefile) /* reload needed? */
793 load_voicefile(false, voicebuf, talk_get_buffer_size());
795 if (p_voicefile == NULL) /* still no voices? */
796 return -1;
798 if (id == -1) /* -1 is an indication for silence */
799 return -1;
801 decimals = (((uint32_t)id) >> DECIMAL_SHIFT) & 0x7;
803 /* check if this is a special ID, with a value */
804 unit = ((uint32_t)id) >> UNIT_SHIFT;
805 if (unit || decimals)
806 { /* sign-extend the value */
807 id = (uint32_t)id << (32-DECIMAL_SHIFT);
808 id >>= (32-DECIMAL_SHIFT);
810 talk_value_decimal(id, unit, decimals, enqueue); /* speak it */
811 return 0; /* and stop, end of special case */
814 clipbuf = get_clip(id, &clipsize);
815 if (clipbuf == NULL)
816 return -1; /* not present */
818 #ifdef LOGF_ENABLE
819 if (id > VOICEONLY_DELIMITER)
820 logf("\ntalk_id: Say voice clip 0x%x\n", id);
821 else
822 logf("\ntalk_id: Say '%s'\n", str(id));
823 #endif
825 queue_clip(clipbuf, clipsize, enqueue);
827 return 0;
829 /* Speaks zero or more IDs (from an array). */
830 int talk_idarray(const long *ids, bool enqueue)
832 int r;
833 if(!ids)
834 return 0;
835 while(*ids != TALK_FINAL_ID)
837 if((r = talk_id(*ids++, enqueue)) <0)
838 return r;
839 enqueue = true;
841 return 0;
844 /* Make sure the current utterance is not interrupted by the next one. */
845 void talk_force_enqueue_next(void)
847 force_enqueue_next = true;
850 /* play a thumbnail from file */
851 /* Returns size of spoken thumbnail, so >0 means something is spoken,
852 <=0 means something went wrong. */
853 static int _talk_file(const char* filename,
854 const long *prefix_ids, bool enqueue)
856 int fd;
857 int size;
858 int thumb_used;
859 #if CONFIG_CODEC != SWCODEC
860 struct mp3entry info;
861 #endif
863 if (talk_temp_disable_count > 0)
864 return -1; /* talking has been disabled */
865 #if CONFIG_CODEC == SWCODEC
866 /* If talk buffer was stolen, it must be restored for thumbnail's sake */
867 if (!restore_state())
868 return -1; /* cannot get any space */
869 #else
870 if (audio_status()) /* busy, buffer in use */
871 return -1;
872 #endif
874 if (p_thumbnail == NULL || size_for_thumbnail <= 0)
875 alloc_thumbnail_buf();
877 #if CONFIG_CODEC != SWCODEC
878 if(mp3info(&info, filename)) /* use this to find real start */
880 return 0; /* failed to open, or invalid */
882 #endif
884 if (!enqueue)
885 /* shutup now to free the thumbnail buffer */
886 talk_shutup();
888 fd = open(filename, O_RDONLY);
889 if (fd < 0) /* failed to open */
891 return 0;
894 thumb_used = thumbnail_buf_used;
895 if(filesize(fd) > size_for_thumbnail -thumb_used)
896 { /* Don't play truncated clips */
897 close(fd);
898 return 0;
901 #if CONFIG_CODEC != SWCODEC
902 lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */
903 #endif
905 size = read(fd, p_thumbnail +thumb_used,
906 size_for_thumbnail -thumb_used);
907 close(fd);
909 /* ToDo: find audio, skip ID headers and trailers */
911 if (size > 0) /* Don't play missing clips */
913 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
914 bitswap(p_thumbnail, size);
915 #endif
916 if(prefix_ids)
917 /* prefix thumbnail by speaking these ids, but only now
918 that we know there's actually a thumbnail to be
919 spoken. */
920 talk_idarray(prefix_ids, true);
921 talk_queue_lock();
922 thumbnail_buf_used = thumb_used +size;
923 talk_queue_unlock();
924 queue_clip(p_thumbnail +thumb_used, size, true);
927 return size;
930 int talk_file(const char *root, const char *dir, const char *file,
931 const char *ext, const long *prefix_ids, bool enqueue)
932 /* Play a thumbnail file */
934 char buf[MAX_PATH];
935 /* Does root end with a slash */
936 char *slash = (root && root[0]
937 && root[strlen(root)-1] != '/') ? "/" : "";
938 snprintf(buf, MAX_PATH, "%s%s%s%s%s%s",
939 root ? root : "", slash,
940 dir ? dir : "", dir ? "/" : "",
941 file ? file : "",
942 ext ? ext : "");
943 return _talk_file(buf, prefix_ids, enqueue);
946 static int talk_spell_basename(const char *path,
947 const long *prefix_ids, bool enqueue)
949 if(prefix_ids)
951 talk_idarray(prefix_ids, enqueue);
952 enqueue = true;
954 char buf[MAX_PATH];
955 /* Spell only the path component after the last slash */
956 strlcpy(buf, path, sizeof(buf));
957 if(strlen(buf) >1 && buf[strlen(buf)-1] == '/')
958 /* strip trailing slash */
959 buf[strlen(buf)-1] = '\0';
960 char *ptr = strrchr(buf, '/');
961 if(ptr && strlen(buf) >1)
962 ++ptr;
963 else ptr = buf;
964 return talk_spell(ptr, enqueue);
967 /* Play a file's .talk thumbnail, fallback to spelling the filename, or
968 go straight to spelling depending on settings. */
969 int talk_file_or_spell(const char *dirname, const char *filename,
970 const long *prefix_ids, bool enqueue)
972 if (global_settings.talk_file_clip)
973 { /* .talk clips enabled */
974 if(talk_file(dirname, NULL, filename, file_thumbnail_ext,
975 prefix_ids, enqueue) >0)
976 return 0;
978 if (global_settings.talk_file == 2)
979 /* Either .talk clips are disabled, or as a fallback */
980 return talk_spell_basename(filename, prefix_ids, enqueue);
981 return 0;
984 #if CONFIG_CODEC == SWCODEC
985 /* Play a directory's .talk thumbnail, fallback to spelling the filename, or
986 go straight to spelling depending on settings. */
987 int talk_dir_or_spell(const char* dirname,
988 const long *prefix_ids, bool enqueue)
990 if (global_settings.talk_dir_clip)
991 { /* .talk clips enabled */
992 if(talk_file(dirname, NULL, dir_thumbnail_name, NULL,
993 prefix_ids, enqueue) >0)
994 return 0;
996 if (global_settings.talk_dir == 2)
997 /* Either .talk clips disabled or as a fallback */
998 return talk_spell_basename(dirname, prefix_ids, enqueue);
999 return 0;
1001 #endif
1003 /* say a numeric value, this word ordering works for english,
1004 but not necessarily for other languages (e.g. german) */
1005 int talk_number(long n, bool enqueue)
1007 int level = 2; /* mille count */
1008 long mil = 1000000000; /* highest possible "-illion" */
1010 if (talk_temp_disable_count > 0)
1011 return -1; /* talking has been disabled */
1012 #if CONFIG_CODEC != SWCODEC
1013 if (audio_status()) /* busy, buffer in use */
1014 return -1;
1015 #endif
1017 if (!enqueue)
1018 talk_shutup(); /* cut off all the pending stuff */
1020 if (n==0)
1021 { /* special case */
1022 talk_id(VOICE_ZERO, true);
1023 return 0;
1026 if (n<0)
1028 talk_id(VOICE_MINUS, true);
1029 n = -n;
1032 while (n)
1034 int segment = n / mil; /* extract in groups of 3 digits */
1035 n -= segment * mil; /* remove the used digits from number */
1036 mil /= 1000; /* digit place for next round */
1038 if (segment)
1040 int hundreds = segment / 100;
1041 int ones = segment % 100;
1043 if (hundreds)
1045 talk_id(VOICE_ZERO + hundreds, true);
1046 talk_id(VOICE_HUNDRED, true);
1049 /* combination indexing */
1050 if (ones > 20)
1052 int tens = ones/10 + 18;
1053 talk_id(VOICE_ZERO + tens, true);
1054 ones %= 10;
1057 /* direct indexing */
1058 if (ones)
1059 talk_id(VOICE_ZERO + ones, true);
1061 /* add billion, million, thousand */
1062 if (mil)
1063 talk_id(VOICE_THOUSAND + level, true);
1065 level--;
1068 return 0;
1071 /* Say time duration/interval. Input is time in seconds,
1072 say hours,minutes,seconds. */
1073 static int talk_time_unit(long secs, bool enqueue)
1075 int hours, mins;
1076 if (!enqueue)
1077 talk_shutup();
1078 if((hours = secs/3600)) {
1079 secs %= 3600;
1080 talk_value(hours, UNIT_HOUR, true);
1082 if((mins = secs/60)) {
1083 secs %= 60;
1084 talk_value(mins, UNIT_MIN, true);
1086 if((secs) || (!hours && !mins))
1087 talk_value(secs, UNIT_SEC, true);
1088 else if(!hours && secs)
1089 talk_number(secs, true);
1090 return 0;
1093 void talk_fractional(char *tbuf, int value, int unit)
1095 int i;
1096 /* strip trailing zeros from the fraction */
1097 for (i = strlen(tbuf) - 1; (i >= 0) && (tbuf[i] == '0'); i--)
1098 tbuf[i] = '\0';
1100 talk_number(value, true);
1101 if (tbuf[0] != 0)
1103 talk_id(LANG_POINT, true);
1104 talk_spell(tbuf, true);
1106 talk_id(unit, true);
1109 int talk_value(long n, int unit, bool enqueue)
1111 return talk_value_decimal(n, unit, 0, enqueue);
1114 /* singular/plural aware saying of a value */
1115 int talk_value_decimal(long n, int unit, int decimals, bool enqueue)
1117 int unit_id;
1118 static const int unit_voiced[] =
1119 { /* lookup table for the voice ID of the units */
1120 [0 ... UNIT_LAST-1] = -1, /* regular ID, int, signed */
1121 [UNIT_MS]
1122 = VOICE_MILLISECONDS, /* here come the "real" units */
1123 [UNIT_SEC]
1124 = VOICE_SECONDS,
1125 [UNIT_MIN]
1126 = VOICE_MINUTES,
1127 [UNIT_HOUR]
1128 = VOICE_HOURS,
1129 [UNIT_KHZ]
1130 = VOICE_KHZ,
1131 [UNIT_DB]
1132 = VOICE_DB,
1133 [UNIT_PERCENT]
1134 = VOICE_PERCENT,
1135 [UNIT_MAH]
1136 = VOICE_MILLIAMPHOURS,
1137 [UNIT_PIXEL]
1138 = VOICE_PIXEL,
1139 [UNIT_PER_SEC]
1140 = VOICE_PER_SEC,
1141 [UNIT_HERTZ]
1142 = VOICE_HERTZ,
1143 [UNIT_MB]
1144 = LANG_MEGABYTE,
1145 [UNIT_KBIT]
1146 = VOICE_KBIT_PER_SEC,
1147 [UNIT_PM_TICK]
1148 = VOICE_PM_UNITS_PER_TICK,
1151 static const int pow10[] = { /* 10^0 - 10^7 */
1152 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
1155 char tbuf[8];
1156 char fmt[] = "%0nd";
1158 if (talk_temp_disable_count > 0)
1159 return -1; /* talking has been disabled */
1160 #if CONFIG_CODEC != SWCODEC
1161 if (audio_status()) /* busy, buffer in use */
1162 return -1;
1163 #endif
1165 /* special case for time duration */
1166 if (unit == UNIT_TIME)
1167 return talk_time_unit(n, enqueue);
1169 if (unit < 0 || unit >= UNIT_LAST)
1170 unit_id = -1;
1171 else
1172 unit_id = unit_voiced[unit];
1174 if ((n==1 || n==-1) /* singular? */
1175 && unit_id >= VOICE_SECONDS && unit_id <= VOICE_HOURS)
1177 unit_id--; /* use the singular for those units which have */
1180 /* special case with a "plus" before */
1181 if (n > 0 && (unit == UNIT_SIGNED || unit == UNIT_DB))
1183 talk_id(VOICE_PLUS, enqueue);
1184 enqueue = true;
1187 if (decimals)
1189 /* needed for the "-0.5" corner case */
1190 if (n < 0)
1192 talk_id(VOICE_MINUS, enqueue);
1193 n = -n;
1196 fmt[2] = '0' + decimals;
1198 snprintf(tbuf, sizeof(tbuf), fmt, n % pow10[decimals]);
1199 talk_fractional(tbuf, n / pow10[decimals], unit_id);
1201 return 0;
1204 talk_number(n, enqueue); /* say the number */
1205 talk_id(unit_id, true); /* say the unit, if any */
1207 return 0;
1210 /* spell a string */
1211 int talk_spell(const char* spell, bool enqueue)
1213 char c; /* currently processed char */
1215 if (talk_temp_disable_count > 0)
1216 return -1; /* talking has been disabled */
1217 #if CONFIG_CODEC != SWCODEC
1218 if (audio_status()) /* busy, buffer in use */
1219 return -1;
1220 #endif
1222 if (!enqueue)
1223 talk_shutup(); /* cut off all the pending stuff */
1225 while ((c = *spell++) != '\0')
1227 /* if this grows into too many cases, I should use a table */
1228 if (c >= 'A' && c <= 'Z')
1229 talk_id(VOICE_CHAR_A + c - 'A', true);
1230 else if (c >= 'a' && c <= 'z')
1231 talk_id(VOICE_CHAR_A + c - 'a', true);
1232 else if (c >= '0' && c <= '9')
1233 talk_id(VOICE_ZERO + c - '0', true);
1234 else if (c == '-')
1235 talk_id(VOICE_MINUS, true);
1236 else if (c == '+')
1237 talk_id(VOICE_PLUS, true);
1238 else if (c == '.')
1239 talk_id(VOICE_DOT, true);
1240 else if (c == ' ')
1241 talk_id(VOICE_PAUSE, true);
1242 else if (c == '/')
1243 talk_id(VOICE_CHAR_SLASH, true);
1246 return 0;
1249 void talk_disable(bool disable)
1251 if (disable)
1252 talk_temp_disable_count++;
1253 else
1254 talk_temp_disable_count--;
1257 void talk_setting(const void *global_settings_variable)
1259 const struct settings_list *setting;
1260 if (!global_settings.talk_menu)
1261 return;
1262 setting = find_setting(global_settings_variable, NULL);
1263 if (setting == NULL)
1264 return;
1265 if (setting->lang_id)
1266 talk_id(setting->lang_id,false);
1270 #if CONFIG_RTC
1271 void talk_date(const struct tm *tm, bool enqueue)
1273 talk_id(LANG_MONTH_JANUARY + tm->tm_mon, enqueue);
1274 talk_number(tm->tm_mday, true);
1275 talk_number(1900 + tm->tm_year, true);
1278 void talk_time(const struct tm *tm, bool enqueue)
1280 if (global_settings.timeformat == 1)
1282 /* Voice the hour */
1283 long am_pm_id = VOICE_AM;
1284 int hour = tm->tm_hour;
1285 if (hour >= 12)
1287 am_pm_id = VOICE_PM;
1288 hour -= 12;
1290 if (hour == 0)
1291 hour = 12;
1292 talk_number(hour, enqueue);
1294 /* Voice the minutes */
1295 if (tm->tm_min == 0)
1297 /* Say o'clock if the minute is 0. */
1298 talk_id(VOICE_OCLOCK, true);
1300 else
1302 /* Pronounce the leading 0 */
1303 if(tm->tm_min < 10)
1304 talk_id(VOICE_OH, true);
1305 talk_number(tm->tm_min, true);
1307 talk_id(am_pm_id, true);
1309 else
1311 /* Voice the time in 24 hour format */
1312 talk_number(tm->tm_hour, enqueue);
1313 if (tm->tm_min == 0)
1315 talk_id(VOICE_HUNDRED, true);
1316 talk_id(VOICE_HOUR, true);
1318 else
1320 /* Pronounce the leading 0 */
1321 if(tm->tm_min < 10)
1322 talk_id(VOICE_OH, true);
1323 talk_number(tm->tm_min, true);
1328 #endif /* CONFIG_RTC */