summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/talk.c269
1 files changed, 104 insertions, 165 deletions
diff --git a/apps/talk.c b/apps/talk.c
index 820cb75..a49de09 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -80,14 +80,6 @@ const char* const file_thumbnail_ext = ".talk";
#define LOADED_MASK 0x80000000 /* MSB */
-/* swcodec: cap thumbnail buffer to MAX_THUMNAIL_BUFSIZE since audio keeps
- * playing while voice
- * hwcodec: just use whatever is left in the audiobuffer, music
- * playback is impossible => no cap */
-#if CONFIG_CODEC == SWCODEC
-#define MAX_THUMBNAIL_BUFSIZE 0x10000
-#endif
-
#define DEFAULT_VOICE_LANG "english"
/***************** Data types *****************/
@@ -124,13 +116,14 @@ struct voicefile_header /* file format of our voice file */
#endif
#ifndef MAX_CLIP_BUFFER_SIZE
-#define MAX_CLIP_BUFFER_SIZE (1<<30) /* 1GB should be enough for everybody */
+/* 1GB should be enough for everybody; will be capped to voicefile size */
+#define MAX_CLIP_BUFFER_SIZE (1<<30)
#endif
+#define THUMBNAIL_RESERVE (50000)
/* 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 */
-static long size_for_thumbnail; /* total thumbnail buffer size */
static struct voicefile_header voicefile; /* loaded voicefile */
static bool has_voicefile; /* a voicefile file is present */
static bool need_shutup; /* is there possibly any voice playing to be shutup */
@@ -158,16 +151,9 @@ static unsigned long voicefile_size, voicebuf_size;
struct queue_entry /* one entry of the internal queue */
{
- int offset; /* actually a buflib handle if type == HANDLE */
- int length; /* total length of the clip */
+ int handle; /* buflib handle to the clip data */
+ int length; /* total length of the clip */
int remaining; /* bytes that still need to be deoded */
- /* for large QUEUE_SIZE values it might be worthwhile to merge the type
- * into the bits of the above members (as a space saver). For small values
- * the required extra code outweights this so it's not done here */
- enum offset_type {
- TALK_HANDLE,
- THUMB_OFFSET,
- } type;
};
static struct buflib_context clip_ctx;
@@ -186,7 +172,7 @@ static struct queue_entry silence, *last_clip;
/***************** Private implementation *****************/
-static int index_handle, talk_handle, thumb_handle;
+static int index_handle, talk_handle;
static int move_callback(int handle, void *current, void *new)
{
@@ -282,34 +268,12 @@ static int shrink_callback(int handle, unsigned hints, void *start, size_t old_s
return BUFLIB_CB_CANNOT_SHRINK;
}
-static int thumb_shrink_callback(int handle, unsigned hints, void *start, size_t old_size)
-{
- (void)start;(void)old_size;(void)hints;
-
- if (handle == thumb_handle && thumbnail_buf_used == 0)
- {
- mutex_lock(&read_buffer_mutex);
- thumb_handle = core_free(handle);
- mutex_unlock(&read_buffer_mutex);
- return BUFLIB_CB_OK;
- }
-
- return BUFLIB_CB_CANNOT_SHRINK;
-}
-
-
-
static struct buflib_callbacks talk_ops = {
.move_callback = move_callback,
.sync_callback = sync_callback,
.shrink_callback = shrink_callback,
};
-static struct buflib_callbacks thumb_ops = {
- .move_callback = move_callback,
- .sync_callback = sync_callback,
- .shrink_callback = thumb_shrink_callback,
-};
static int open_voicefile(void)
{
@@ -375,19 +339,31 @@ static int free_oldest_clip(void)
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 && (now - cc[i].tick) > age
- && cc[i].voice_id != VOICE_PAUSE) /* never consider silence */
+ if (cc[i].handle)
{
- age = now - cc[i].tick;
- oldest = i;
+ if ((now - cc[i].tick) > age && cc[i].voice_id != VOICE_PAUSE)
+ {
+ /* find the last-used clip but never consider silence */
+ age = now - cc[i].tick;
+ oldest = i;
+ }
+ else if (cc[i].voice_id == VOICEONLY_DELIMITER)
+ {
+ /* thumb clips are freed immediately */
+ oldest = i;
+ break;
+ }
}
}
+ /* free the last one if no oldest one could be determined */
cc = &cc[oldest];
cc->handle = buflib_free(&clip_ctx, cc->handle);
- /* need to clear the LOADED bit too */
- clipbuf = core_get_data(index_handle);
- clipbuf[id2index(cc->voice_id)].size &= ~LOADED_MASK;
-
+ /* need to clear the LOADED bit too (not for thumb clips) */
+ if (cc->voice_id != VOICEONLY_DELIMITER)
+ {
+ clipbuf = core_get_data(index_handle);
+ clipbuf[id2index(cc->voice_id)].size &= ~LOADED_MASK;
+ }
return oldest;
}
@@ -458,6 +434,8 @@ static void load_initial_clips(int fd)
int handle;
struct clip_entry* clipbuf = core_get_data(index_handle);
size_t clipsize = clipbuf[index].size;
+ ssize_t ret;
+
if (clipsize == 0) /* clip not included in voicefile */
continue;
@@ -465,7 +443,8 @@ static void load_initial_clips(int fd)
if (handle < 0)
break;
- if (read_clip_data(fd, index, handle) < 0)
+ ret = read_clip_data(fd, index, handle);
+ if (ret < 0)
break;
add_cache_entry(handle, i++, index2id(index));
@@ -480,7 +459,6 @@ static int get_clip(long id, struct queue_entry *q)
int retval = -1;
struct clip_entry* clipbuf;
size_t clipsize;
- enum offset_type type;
index = id2index(id);
if (index == -1)
@@ -521,12 +499,10 @@ static int get_clip(long id, struct queue_entry *q)
clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */
retval = cc[i].handle;
}
- type = TALK_HANDLE;
- q->offset = retval;
+ q->handle = retval;
q->length = clipsize;
q->remaining = clipsize;
- q->type = type;
return 0;
}
@@ -596,33 +572,6 @@ alloc_err:
index_handle = core_free(index_handle);
return false;
}
-
-static bool alloc_thumbnail_buf(void)
-{
- int handle;
- size_t size;
- if (thumb_handle > 0)
- return true; /* nothing to do? */
-#if CONFIG_CODEC == SWCODEC
- /* try to allocate the max. first, and take whatever we can get if that
- * fails */
- size = MAX_THUMBNAIL_BUFSIZE;
- handle = core_alloc_ex("voice thumb", MAX_THUMBNAIL_BUFSIZE, &thumb_ops);
- if (handle < 0)
- {
- size = core_allocatable();
- handle = core_alloc_ex("voice thumb", size, &thumb_ops);
- }
-#else
- /* on HWCODEC, just use the rest of the remaining buffer,
- * normal playback cannot happen anyway */
- size = audio_buffer_available();
- handle = core_alloc_ex("voice thumb", size, &thumb_ops);
-#endif
- thumb_handle = handle;
- size_for_thumbnail = (handle > 0) ? size : 0;
- return handle > 0;
-}
/* load the voice file into the mp3 buffer */
static bool load_voicefile_index(int fd)
@@ -684,10 +633,6 @@ static bool load_voicefile_data(int fd)
* VOICE_PAUSE clip is specially handled */
get_clip(VOICE_PAUSE, &silence);
- /* not an error if this fails here, might try again when the
- * actual thumbnails are attempted to be played back */
- alloc_thumbnail_buf();
-
return true;
}
@@ -701,18 +646,13 @@ static void* commit_transfer(struct queue_entry *qe, size_t *size)
{
void *buf = NULL; /* shut up gcc */
static unsigned char *bufpos = commit_buffer;
- int offset = qe->offset;
#if CONFIG_CODEC != SWCODEC
sent = MIN(qe->remaining, 0xFFFF);
#else
sent = qe->remaining;
#endif
sent = MIN((size_t)sent, sizeof(commit_buffer));
- switch (qe->type)
- {
- case TALK_HANDLE: buf = buflib_get_data(&clip_ctx, offset); break;
- case THUMB_OFFSET: buf = core_get_data(thumb_handle) + offset; break;
- }
+ buf = buflib_get_data(&clip_ctx, qe->handle);
/* adjust buffer position to what has been played already */
buf += (qe->length - qe->remaining);
memcpy(bufpos, buf, sent);
@@ -724,8 +664,8 @@ static void* commit_transfer(struct queue_entry *qe, size_t *size)
static inline bool is_silence(struct queue_entry *qe)
{
- if (silence.length > 0) /* silence clip available? */
- return (qe->offset == silence.offset && qe->type == silence.type);
+ if (silence.handle > 0) /* silence clip available? */
+ return (qe->handle == silence.handle);
else
return false;
}
@@ -743,21 +683,15 @@ static void mp3_callback(const void** start, size_t* size)
}
talk_queue_lock();
- /* check if thumbnails have been played */
- if (qe->type == THUMB_OFFSET)
- {
- if (qe->remaining == 0 && (qe->length + qe->offset) == thumbnail_buf_used)
- thumbnail_buf_used = 0;
- }
/* increment read position for the just played clip */
queue_read = (queue_read + 1) & QUEUE_MASK;
if (QUEUE_LEVEL == 0)
{
- if (!is_silence(last_clip) && last_clip->type != THUMB_OFFSET)
+ if (!is_silence(last_clip))
{ /* add silence clip when queue runs empty playing a voice clip,
- * only if the previous clip wasn't silence or thumbnail */
+ * only if the previous clip wasn't already silence */
queue[queue_write] = silence;
queue_write = (queue_write + 1) & QUEUE_MASK;
}
@@ -793,7 +727,6 @@ void talk_force_shutup(void)
unsigned char* search;
unsigned char* end;
int len;
- unsigned offset;
if (QUEUE_LEVEL == 0) /* has ended anyway */
return;
@@ -802,13 +735,7 @@ void talk_force_shutup(void)
#endif /* CONFIG_CPU == SH7034 */
/* search next frame boundary and continue up to there */
pos = search = mp3_get_pos();
- offset = queue[queue_read].offset;
- switch (queue[queue_read].type)
- {
- case THUMB_OFFSET: end = core_get_data(thumb_handle) + offset; break;
- case TALK_HANDLE: end = buflib_get_data(&clip_ctx, offset); break;
- default: end = NULL; /* shut up gcc */
- }
+ end = buflib_get_data(&clip_ctx, queue[queue_read].handle);
len = queue[queue_read].length;
if (pos >= end && pos <= (end+len)) /* really our clip? */
@@ -914,7 +841,6 @@ static void queue_clip(struct queue_entry *clip, bool enqueue)
return;
}
-#if CONFIG_CODEC == SWCODEC
/* return if a voice codec is required or not */
static bool talk_voice_required(void)
{
@@ -922,7 +848,6 @@ static bool talk_voice_required(void)
|| (global_settings.talk_dir_clip) /* Thumbnail clips are required */
|| (global_settings.talk_file_clip);
}
-#endif
/***************** Public implementation *****************/
@@ -951,7 +876,7 @@ void talk_init(void)
queue_write = queue_read = 0; /* reset the queue */
memset(&voicefile, 0, sizeof(voicefile));
- silence.offset = -1; /* pause clip not accessible */
+ silence.handle = -1; /* pause clip not accessible */
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 */
@@ -961,37 +886,46 @@ void talk_init(void)
* and so we can re-use it if it's already allocated in any event */
filehandle = open_voicefile();
- if (filehandle < 0)
- return;
-
- if (!load_voicefile_index(filehandle))
- goto out;
-
- /* Now determine the maximum buffer size needed for the voicefile.
- * The below pretends the entire voicefile would be loaded. The buffer
- * size is eventually capped later on in load_voicefile_data() */
- int num_clips = voicefile.id1_max + voicefile.id2_max;
- int non_empty = num_clips;
- int total_size = 0, avg_size;
- struct clip_entry *clips = core_get_data(index_handle);
- /* check for the average clip size to estimate the maximum number of
- * clips the buffer can hold */
- for (int i = 0; i<num_clips; i++)
+ if (filehandle > 0)
{
- if (clips[i].size)
- total_size += ALIGN_UP(clips[i].size, sizeof(void *));
- else
- non_empty -= 1;
+ if (!load_voicefile_index(filehandle))
+ goto out;
+
+ /* Now determine the maximum buffer size needed for the voicefile.
+ * The below pretends the entire voicefile would be loaded. The buffer
+ * size is eventually capped later on in load_voicefile_data() */
+ int num_clips = voicefile.id1_max + voicefile.id2_max;
+ int non_empty = num_clips;
+ int total_size = 0, avg_size;
+ struct clip_entry *clips = core_get_data(index_handle);
+ /* check for the average clip size to estimate the maximum number of
+ * clips the buffer can hold */
+ for (int i = 0; i<num_clips; i++)
+ {
+ if (clips[i].size)
+ total_size += ALIGN_UP(clips[i].size, sizeof(void *));
+ else
+ non_empty -= 1;
+ }
+ avg_size = total_size / non_empty;
+ max_clips = MIN((int)(MAX_CLIP_BUFFER_SIZE/avg_size) + 1, non_empty);
+ /* account for possible thumb clips */
+ total_size += THUMBNAIL_RESERVE;
+ max_clips += 16;
+ voicefile_size = total_size;
+ has_voicefile = true;
}
- avg_size = total_size / non_empty;
- max_clips = MIN((int)(MAX_CLIP_BUFFER_SIZE/avg_size) + 1, non_empty);
- voicefile_size = total_size;
- /* additionally to the clips we need a table to record the age of the clips
+ else if (talk_voice_required())
+ {
+ /* create buffer just for thumb clips */
+ max_clips = 16;
+ voicefile_size = THUMBNAIL_RESERVE;
+ }
+ /* additionally to the clip we need a table to record the age of the clips
* so that, when memory is tight, only the most recently used ones are kept */
voicefile_size += sizeof(struct clip_cache_metadata) * max_clips;
/* compensate a bit for buflib alloc overhead. */
voicefile_size += BUFLIB_ALLOC_OVERHEAD * max_clips + 64;
- has_voicefile = true;
load_voicefile_data(filehandle);
@@ -1003,7 +937,6 @@ void talk_init(void)
out:
close(filehandle); /* close again, this was just to detect presence */
- filehandle = -1;
}
/* somebody else claims the mp3 buffer, e.g. for regular play/record */
@@ -1035,10 +968,10 @@ int talk_id(int32_t id, bool enqueue)
if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */
{
int fd = open_voicefile();
- if (fd < 0
- || !load_voicefile_index(fd)
- || !load_voicefile_data(fd))
+ if (fd < 0 || !load_voicefile_index(fd))
return -1;
+ load_voicefile_data(fd);
+ close(fd);
}
if (id == -1) /* -1 is an indication for silence */
@@ -1100,7 +1033,7 @@ static int _talk_file(const char* filename,
{
int fd;
int size;
- int thumb_used;
+ int handle, oldest = -1;
#if CONFIG_CODEC != SWCODEC
struct mp3entry info;
#endif
@@ -1110,9 +1043,8 @@ static int _talk_file(const char* filename,
return -1; /* talking has been disabled */
if (!check_audio_status())
return -1;
-
- if (!alloc_thumbnail_buf())
- return -1;
+ if (talk_handle <= 0)
+ load_voicefile_data(-1);
#if CONFIG_CODEC != SWCODEC
if(mp3info(&info, filename)) /* use this to find real start */
@@ -1130,19 +1062,17 @@ static int _talk_file(const char* filename,
{
return 0;
}
-
- thumb_used = thumbnail_buf_used;
- if(filesize(fd) > size_for_thumbnail -thumb_used)
- { /* Don't play truncated clips */
- close(fd);
- return 0;
- }
+ size = filesize(fd);
#if CONFIG_CODEC != SWCODEC
- lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */
+ size -= lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */
#endif
- size = read_to_handle(fd, thumb_handle, thumb_used, size_for_thumbnail - thumb_used);
+ /* free clips from cache until this one succeeds to allocate */
+ while ((handle = buflib_alloc(&clip_ctx, size)) < 0)
+ oldest = free_oldest_clip();
+
+ size = read_to_handle_ex(fd, &clip_ctx, handle, 0, size);
close(fd);
/* ToDo: find audio, skip ID headers and trailers */
@@ -1150,21 +1080,25 @@ static int _talk_file(const char* filename,
if (size > 0) /* Don't play missing clips */
{
struct queue_entry clip;
+ clip.handle = handle;
+ clip.length = clip.remaining = size;
#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
/* bitswap doesnt yield() */
- bitswap(core_get_data(thumb_handle), size);
+ bitswap(buflib_get_data(&clip_ctx, handle), size);
#endif
if(prefix_ids)
/* prefix thumbnail by speaking these ids, but only now
that we know there's actually a thumbnail to be
spoken. */
talk_idarray(prefix_ids, true);
- talk_queue_lock();
- thumbnail_buf_used = thumb_used + size;
- talk_queue_unlock();
- clip = (struct queue_entry){ .offset = thumb_used, .length = size, .remaining = size, .type = THUMB_OFFSET };
+ /* finally insert into metadata table. thumb clips go under the
+ * VOICEONLY_DELIMITER id so the cache can distinguish them from
+ * normal clips */
+ add_cache_entry(handle, oldest, VOICEONLY_DELIMITER);
queue_clip(&clip, true);
}
+ else
+ buflib_free(&clip_ctx, handle);
return size;
}
@@ -1566,6 +1500,7 @@ void talk_time(const struct tm *tm, bool enqueue)
bool talk_get_debug_data(struct talk_debug_data *data)
{
char* p_lang = DEFAULT_VOICE_LANG; /* default */
+ struct clip_cache_metadata *cc;
memset(data, 0, sizeof(*data));
@@ -1593,17 +1528,21 @@ bool talk_get_debug_data(struct talk_debug_data *data)
if (size > data->max_clipsize)
data->max_clipsize = size;
data->avg_clipsize += size;
- if (clips[i].size & LOADED_MASK)
- cached++;
+ }
+ cc = buflib_get_data(&clip_ctx, metadata_table_handle);
+ for (int i = 0; i < (int) max_clips; i++)
+ {
+ if (cc[i].handle > 0)
+ cached += 1;
}
data->avg_clipsize /= real_clips;
data->num_empty_clips = data->num_clips - real_clips;
data->memory_allocated = sizeof(commit_buffer) + sizeof(voicefile)
+ data->num_clips * sizeof(struct clip_entry)
- + voicebuf_size + size_for_thumbnail;
- data->memory_used = data->memory_allocated - size_for_thumbnail + thumbnail_buf_used;
+ + voicebuf_size;
+ data->memory_used = 0;
if (talk_handle > 0)
- data->memory_used -= buflib_available(&clip_ctx);
+ data->memory_used = data->memory_allocated - buflib_available(&clip_ctx);
data->cached_clips = cached;
data->cache_hits = cache_hits;
data->cache_misses = cache_misses;