summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafaël Carré <rafael.carre@gmail.com>2010-09-01 00:08:50 +0000
committerRafaël Carré <rafael.carre@gmail.com>2010-09-01 00:08:50 +0000
commitca91d0fd7add9b4745b81b3579635f471314a4ac (patch)
tree88a74b65a9a71fdaea52d66187f496883068c548
parenteb39236ff7ff37c436d94ddeadb58fcc1c2c92ca (diff)
downloadrockbox-ca91d0fd7add9b4745b81b3579635f471314a4ac.tar.gz
rockbox-ca91d0fd7add9b4745b81b3579635f471314a4ac.zip
FS#11587 : voice for SWCODEC and low memory
On these targets the full voice file can't be loaded so only load 64 clips at a time (the size of the queue) Voice now works on clipv1 git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27962 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/talk.c198
1 files changed, 168 insertions, 30 deletions
diff --git a/apps/talk.c b/apps/talk.c
index e07dcb8262..7dbfb2ef77 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -108,6 +108,26 @@ struct queue_entry /* one entry of the internal queue */
/***************** Globals *****************/
+#if CONFIG_STORAGE & STORAGE_MMC
+/* The MMC storage on the Ondios is slow enough that we want to buffer the
+ * talk clips only when they are needed */
+# define TALK_PROGRESSIVE_LOAD
+#elif CONFIG_CODEC == SWCODEC && MEM <= 2
+/* The entire voice file wouldn't fit in memory together with codecs, so we
+ * load clips each time they are accessed */
+# define TALK_PARTIAL_LOAD
+#endif
+
+#ifdef TALK_PARTIAL_LOAD
+static unsigned char *clip_buffer;
+static long max_clipsize; /* size of the biggest clip */
+static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */
+static uint8_t clip_age[QUEUE_SIZE];
+#if QUEUE_SIZE > 255
+# error clip_age[] type too small
+#endif
+#endif
+
static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */
/* Multiple thumbnails can be loaded back-to-back in this buffer. */
static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in
@@ -131,7 +151,7 @@ static struct mutex queue_mutex SHAREDBSS_ATTR;
#endif /* CONFIG_CODEC */
static int sent; /* how many bytes handed over to playback, owned by ISR */
static unsigned char curr_hd[3]; /* current frame header, for re-sync */
-static int filehandle = -1; /* global, so the MMC variant can keep the file open */
+static int filehandle = -1; /* global, so we can keep the file open if needed */
static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */
static long silence_len; /* length of the VOICE_PAUSE clip */
static unsigned char* p_lastclip; /* address of latest clip, for silence add */
@@ -182,22 +202,78 @@ static unsigned char* get_clip(long id, long* p_size)
clipsize = p_voicefile->index[id].size;
if (clipsize == 0) /* clip not included in voicefile */
return NULL;
+
+#ifndef TALK_PARTIAL_LOAD
clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset;
+#endif
-#if (CONFIG_STORAGE & STORAGE_MMC) /* dynamic loading, on demand */
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
if (!(clipsize & LOADED_MASK))
- { /* clip used for the first time, needs loading */
+ { /* clip needs loading */
+#ifdef TALK_PARTIAL_LOAD
+ int idx = 0;
+ if (id == VOICE_PAUSE) {
+ idx = QUEUE_SIZE; /* we keep VOICE_PAUSE loaded */
+ } else {
+ int oldest = 0, i;
+ for(i=0; i<QUEUE_SIZE; i++) {
+ if (buffered_id[i] < 0) {
+ /* found a free entry, that means the buffer isn't
+ * full yet. */
+ idx = i;
+ break;
+ }
+
+ /* find the oldest clip */
+ if(clip_age[i] > oldest) {
+ idx = i;
+ oldest = clip_age[i];
+ }
+
+ /* increment age of each loaded clip */
+ clip_age[i]++;
+ }
+ clip_age[idx] = 0; /* reset clip's age */
+ }
+ clipbuf = clip_buffer + idx * max_clipsize;
+#endif
+
lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET);
if (read(filehandle, clipbuf, clipsize) != clipsize)
return NULL; /* read error */
p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */
+
+#ifdef TALK_PARTIAL_LOAD
+ if (id != VOICE_PAUSE) {
+ if (buffered_id[idx] >= 0) {
+ /* mark previously loaded clip as unloaded */
+ p_voicefile->index[buffered_id[idx]].size &= ~LOADED_MASK;
+ }
+ buffered_id[idx] = id;
+ }
+#endif
}
else
{ /* clip is in memory already */
+#ifdef TALK_PARTIAL_LOAD
+ /* Find where it was loaded */
+ clipbuf = clip_buffer;
+ if (id == VOICE_PAUSE) {
+ clipbuf += QUEUE_SIZE * max_clipsize;
+ } else {
+ int idx;
+ for (idx=0; idx<QUEUE_SIZE; idx++)
+ if (buffered_id[idx] == id) {
+ clipbuf += idx * max_clipsize;
+ clip_age[idx] = 0; /* reset clip's age */
+ break;
+ }
+ }
+#endif
clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */
}
-#endif
+#endif /* defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD) */
*p_size = clipsize;
return clipbuf;
@@ -205,29 +281,40 @@ static unsigned char* get_clip(long id, long* p_size)
/* load the voice file into the mp3 buffer */
-static void load_voicefile(void)
+static void load_voicefile(bool probe)
{
int load_size;
int got_size;
+#ifndef TALK_PARTIAL_LOAD
int file_size;
+#endif
#ifdef ROCKBOX_LITTLE_ENDIAN
int i;
#endif
- filehandle = open_voicefile();
+ if (!probe)
+ filehandle = open_voicefile();
if (filehandle < 0) /* failed to open */
goto load_err;
+#ifndef TALK_PARTIAL_LOAD
file_size = filesize(filehandle);
if (file_size > audiobufend - audiobuf) /* won't fit? */
goto load_err;
+#endif
-#if (CONFIG_STORAGE & STORAGE_MMC) /* load only the header for now */
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
+ /* load only the header for now */
load_size = offsetof(struct voicefile, index);
#else /* load the full file */
load_size = file_size;
#endif
+#ifdef TALK_PARTIAL_LOAD
+ if (load_size > audiobufend - audiobuf) /* won't fit? */
+ goto load_err;
+#endif
+
got_size = read(filehandle, audiobuf, load_size);
if (got_size != load_size /* failure */)
goto load_err;
@@ -258,26 +345,39 @@ static void load_voicefile(void)
else
goto load_err;
-#ifdef ROCKBOX_LITTLE_ENDIAN
- for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++)
- structec_convert(&p_voicefile->index[i], "ll", 1, true);
-#endif
-
-#if (CONFIG_STORAGE & STORAGE_MMC)
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
/* load the index table, now that we know its size from the header */
load_size = (p_voicefile->id1_max + p_voicefile->id2_max)
* sizeof(struct clip_entry);
+
+#ifdef TALK_PARTIAL_LOAD
+ if (load_size > audiobufend - audiobuf) /* won't fit? */
+ goto load_err;
+#endif
+
got_size = read(filehandle,
(unsigned char *) p_voicefile + offsetof(struct voicefile, index), load_size);
if (got_size != load_size) /* read error */
goto load_err;
#else
- close(filehandle); /* only the MMC variant leaves it open */
+ close(filehandle);
filehandle = -1;
+#endif /* defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD) */
+
+#ifdef ROCKBOX_LITTLE_ENDIAN
+ for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++)
+ structec_convert(&p_voicefile->index[i], "ll", 1, true);
#endif
- /* make sure to have the silence clip, if available */
- p_silence = get_clip(VOICE_PAUSE, &silence_len);
+#ifdef TALK_PARTIAL_LOAD
+ clip_buffer = (unsigned char *) p_voicefile + p_voicefile->table;
+ unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
+ clip_buffer += clips * sizeof(struct clip_entry); /* skip index */
+#endif
+ if (!probe) {
+ /* make sure to have the silence clip, if available */
+ p_silence = get_clip(VOICE_PAUSE, &silence_len);
+ }
return;
@@ -501,6 +601,13 @@ static void reset_state(void)
p_thumbnail = audiobuf;
size_for_thumbnail = audiobufend - audiobuf;
#endif
+
+#ifdef TALK_PARTIAL_LOAD
+ int i;
+ for(i=0; i<QUEUE_SIZE; i++)
+ buffered_id[i] = -1;
+#endif
+
thumbnail_buf_used = 0;
p_silence = NULL; /* pause clip not accessible */
}
@@ -517,8 +624,8 @@ void talk_init(void)
return;
}
-#if (CONFIG_STORAGE & STORAGE_MMC)
- if (filehandle >= 0) /* MMC: An old voice file might still be open */
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
+ if (filehandle >= 0)
{
close(filehandle);
filehandle = -1;
@@ -540,18 +647,49 @@ void talk_init(void)
reset_state(); /* use this for most of our inits */
filehandle = open_voicefile();
- size_t audiobufsz = audiobufend - audiobuf;
+ if (filehandle < 0) {
+ has_voicefile = false;
+ voicefile_size = 0;
+ return;
+ }
+
+ voicefile_size = filesize(filehandle);
+
/* test if we can open and if it fits in the audiobuffer */
- has_voicefile = filehandle >= 0 && filesize(filehandle) <= (off_t)audiobufsz;
- voicefile_size = 0;
+ size_t audiobufsz = audiobufend - audiobuf;
- if (has_voicefile)
- {
- voicefile_size = filesize(filehandle);
- close(filehandle); /* close again, this was just to detect presence */
- filehandle = -1;
+#ifdef TALK_PARTIAL_LOAD
+ /* we won't load the full file, we only need the index */
+ load_voicefile(true);
+ if (!p_voicefile)
+ return;
+
+ unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
+ unsigned i;
+ int silence_size = 0;
+
+ for(i=0; i<clips; i++) {
+ int size = p_voicefile->index[i].size;
+ if (size > max_clipsize)
+ max_clipsize = size;
+ if (i == VOICE_PAUSE)
+ silence_size = size;
}
+ voicefile_size = p_voicefile->table + clips * sizeof(struct clip_entry);
+ voicefile_size += max_clipsize * QUEUE_SIZE + silence_size;
+ p_voicefile = NULL; /* Don't pretend we can load talk clips just yet */
+#endif
+
+ if (voicefile_size <= audiobufsz) {
+ has_voicefile = true;
+ } else {
+ has_voicefile = false;
+ voicefile_size = 0;
+ }
+
+ close(filehandle); /* close again, this was just to detect presence */
+ filehandle = -1;
}
#if CONFIG_CODEC == SWCODEC
@@ -576,8 +714,8 @@ void talk_buffer_steal(void)
#if CONFIG_CODEC != SWCODEC
mp3_play_stop();
#endif
-#if (CONFIG_STORAGE & STORAGE_MMC)
- if (filehandle >= 0) /* only relevant for MMC */
+#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
+ if (filehandle >= 0)
{
close(filehandle);
filehandle = -1;
@@ -603,7 +741,7 @@ int talk_id(int32_t id, bool enqueue)
#endif
if (p_voicefile == NULL && has_voicefile)
- load_voicefile(); /* reload needed */
+ load_voicefile(false); /* reload needed */
if (p_voicefile == NULL) /* still no voices? */
return -1;
@@ -1134,4 +1272,4 @@ void talk_time(const struct tm *tm, bool enqueue)
}
}
-#endif
+#endif /* CONFIG_RTC */