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:
parent
eb39236ff7
commit
ca91d0fd7a
1 changed files with 171 additions and 33 deletions
204
apps/talk.c
204
apps/talk.c
|
|
@ -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 */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue