1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Rafaël Carré 2010-09-01 00:08:50 +00:00
parent eb39236ff7
commit ca91d0fd7a

View file

@ -108,6 +108,26 @@ struct queue_entry /* one entry of the internal queue */
/***************** Globals *****************/ /***************** 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 */ static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */
/* Multiple thumbnails can be loaded back-to-back in this buffer. */ /* Multiple thumbnails can be loaded back-to-back in this buffer. */
static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in 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 */ #endif /* CONFIG_CODEC */
static int sent; /* how many bytes handed over to playback, owned by ISR */ 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 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 unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */
static long silence_len; /* length of the VOICE_PAUSE clip */ static long silence_len; /* length of the VOICE_PAUSE clip */
static unsigned char* p_lastclip; /* address of latest clip, for silence add */ 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; clipsize = p_voicefile->index[id].size;
if (clipsize == 0) /* clip not included in voicefile */ if (clipsize == 0) /* clip not included in voicefile */
return NULL; return NULL;
clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset;
#if (CONFIG_STORAGE & STORAGE_MMC) /* dynamic loading, on demand */ #ifndef TALK_PARTIAL_LOAD
clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset;
#endif
#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
if (!(clipsize & LOADED_MASK)) 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); lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET);
if (read(filehandle, clipbuf, clipsize) != clipsize) if (read(filehandle, clipbuf, clipsize) != clipsize)
return NULL; /* read error */ return NULL; /* read error */
p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */ 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 else
{ /* clip is in memory already */ { /* 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 */ clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */
} }
#endif #endif /* defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD) */
*p_size = clipsize; *p_size = clipsize;
return clipbuf; return clipbuf;
@ -205,29 +281,40 @@ static unsigned char* get_clip(long id, long* p_size)
/* load the voice file into the mp3 buffer */ /* load the voice file into the mp3 buffer */
static void load_voicefile(void) static void load_voicefile(bool probe)
{ {
int load_size; int load_size;
int got_size; int got_size;
#ifndef TALK_PARTIAL_LOAD
int file_size; int file_size;
#endif
#ifdef ROCKBOX_LITTLE_ENDIAN #ifdef ROCKBOX_LITTLE_ENDIAN
int i; int i;
#endif #endif
filehandle = open_voicefile(); if (!probe)
filehandle = open_voicefile();
if (filehandle < 0) /* failed to open */ if (filehandle < 0) /* failed to open */
goto load_err; goto load_err;
#ifndef TALK_PARTIAL_LOAD
file_size = filesize(filehandle); file_size = filesize(filehandle);
if (file_size > audiobufend - audiobuf) /* won't fit? */ if (file_size > audiobufend - audiobuf) /* won't fit? */
goto load_err; 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); load_size = offsetof(struct voicefile, index);
#else /* load the full file */ #else /* load the full file */
load_size = file_size; load_size = file_size;
#endif #endif
#ifdef TALK_PARTIAL_LOAD
if (load_size > audiobufend - audiobuf) /* won't fit? */
goto load_err;
#endif
got_size = read(filehandle, audiobuf, load_size); got_size = read(filehandle, audiobuf, load_size);
if (got_size != load_size /* failure */) if (got_size != load_size /* failure */)
goto load_err; goto load_err;
@ -258,26 +345,39 @@ static void load_voicefile(void)
else else
goto load_err; goto load_err;
#ifdef ROCKBOX_LITTLE_ENDIAN #if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
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)
/* load the index table, now that we know its size from the header */ /* load the index table, now that we know its size from the header */
load_size = (p_voicefile->id1_max + p_voicefile->id2_max) load_size = (p_voicefile->id1_max + p_voicefile->id2_max)
* sizeof(struct clip_entry); * sizeof(struct clip_entry);
#ifdef TALK_PARTIAL_LOAD
if (load_size > audiobufend - audiobuf) /* won't fit? */
goto load_err;
#endif
got_size = read(filehandle, got_size = read(filehandle,
(unsigned char *) p_voicefile + offsetof(struct voicefile, index), load_size); (unsigned char *) p_voicefile + offsetof(struct voicefile, index), load_size);
if (got_size != load_size) /* read error */ if (got_size != load_size) /* read error */
goto load_err; goto load_err;
#else #else
close(filehandle); /* only the MMC variant leaves it open */ close(filehandle);
filehandle = -1; 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 #endif
/* make sure to have the silence clip, if available */ #ifdef TALK_PARTIAL_LOAD
p_silence = get_clip(VOICE_PAUSE, &silence_len); 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; return;
@ -501,6 +601,13 @@ static void reset_state(void)
p_thumbnail = audiobuf; p_thumbnail = audiobuf;
size_for_thumbnail = audiobufend - audiobuf; size_for_thumbnail = audiobufend - audiobuf;
#endif #endif
#ifdef TALK_PARTIAL_LOAD
int i;
for(i=0; i<QUEUE_SIZE; i++)
buffered_id[i] = -1;
#endif
thumbnail_buf_used = 0; thumbnail_buf_used = 0;
p_silence = NULL; /* pause clip not accessible */ p_silence = NULL; /* pause clip not accessible */
} }
@ -517,8 +624,8 @@ void talk_init(void)
return; return;
} }
#if (CONFIG_STORAGE & STORAGE_MMC) #if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
if (filehandle >= 0) /* MMC: An old voice file might still be open */ if (filehandle >= 0)
{ {
close(filehandle); close(filehandle);
filehandle = -1; filehandle = -1;
@ -540,18 +647,49 @@ void talk_init(void)
reset_state(); /* use this for most of our inits */ reset_state(); /* use this for most of our inits */
filehandle = open_voicefile(); filehandle = open_voicefile();
size_t audiobufsz = audiobufend - audiobuf; if (filehandle < 0) {
/* test if we can open and if it fits in the audiobuffer */ has_voicefile = false;
has_voicefile = filehandle >= 0 && filesize(filehandle) <= (off_t)audiobufsz; voicefile_size = 0;
voicefile_size = 0; return;
if (has_voicefile)
{
voicefile_size = filesize(filehandle);
close(filehandle); /* close again, this was just to detect presence */
filehandle = -1;
} }
voicefile_size = filesize(filehandle);
/* test if we can open and if it fits in the audiobuffer */
size_t audiobufsz = audiobufend - audiobuf;
#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 #if CONFIG_CODEC == SWCODEC
@ -576,8 +714,8 @@ void talk_buffer_steal(void)
#if CONFIG_CODEC != SWCODEC #if CONFIG_CODEC != SWCODEC
mp3_play_stop(); mp3_play_stop();
#endif #endif
#if (CONFIG_STORAGE & STORAGE_MMC) #if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
if (filehandle >= 0) /* only relevant for MMC */ if (filehandle >= 0)
{ {
close(filehandle); close(filehandle);
filehandle = -1; filehandle = -1;
@ -603,7 +741,7 @@ int talk_id(int32_t id, bool enqueue)
#endif #endif
if (p_voicefile == NULL && has_voicefile) if (p_voicefile == NULL && has_voicefile)
load_voicefile(); /* reload needed */ load_voicefile(false); /* reload needed */
if (p_voicefile == NULL) /* still no voices? */ if (p_voicefile == NULL) /* still no voices? */
return -1; return -1;
@ -1134,4 +1272,4 @@ void talk_time(const struct tm *tm, bool enqueue)
} }
} }
#endif #endif /* CONFIG_RTC */