summaryrefslogtreecommitdiff
path: root/apps/talk.c
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2013-11-21 11:44:04 +0100
committerThomas Martitz <kugel@rockbox.org>2014-02-02 19:40:39 +0100
commitc46f9be10a9b3f34f804583d8cdc980ab62c58bd (patch)
tree73f425a50ece157d888e288881d45fe402f1faca /apps/talk.c
parentdac40fdd60872da55b60ae9912b31e6549f1cf4a (diff)
downloadrockbox-c46f9be10a9b3f34f804583d8cdc980ab62c58bd.tar.gz
rockbox-c46f9be10a9b3f34f804583d8cdc980ab62c58bd.zip
talk: Smarter cache management for TALK_PARTIAL_LOAD.
Previously the clip cache of TALK_PARTIAL_LOAD reserved space N clips, each slot was as big as the maximum sized clip which was necessary to replace clips in-memory in MRU-style. The cache management now uses buflib to allocate and free each clip, using the clip's real size. This allows the clip cache to be much more compact, because no space is wasted for the max. sized clip. This makes use of buflib's ability to easily manage differently-sized memory chunks by moving them to make free space. As an example: for english.voice TALK_PARTIAL_LOAD allocated 288k in advance. for just 64 clips. With this patch ~70 clips can be stored in a 100k buffer. This, the memory usage is cut by 2/3 and almost optimal (there's still the buflib per-alloc cookie overhead). As a result the TALK_PARTIAL_LOAD buffer is restricted to 100k which still allows for more clips than previously, on average. Change-Id: I257654071e9a95770cd6db2c2765f020befce412
Diffstat (limited to 'apps/talk.c')
-rw-r--r--apps/talk.c655
1 files changed, 373 insertions, 282 deletions
diff --git a/apps/talk.c b/apps/talk.c
index 0904e41c3b..4c4e6184b8 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -87,6 +87,8 @@ const char* const file_thumbnail_ext = ".talk";
87#define MAX_THUMBNAIL_BUFSIZE 0x10000 87#define MAX_THUMBNAIL_BUFSIZE 0x10000
88#endif 88#endif
89 89
90#define DEFAULT_VOICE_LANG "english"
91
90/***************** Data types *****************/ 92/***************** Data types *****************/
91 93
92struct clip_entry /* one entry of the index table */ 94struct clip_entry /* one entry of the index table */
@@ -114,16 +116,7 @@ struct voicefile_header /* file format of our voice file */
114 * The Ondios have slow storage access and loading the entire voice file would 116 * The Ondios have slow storage access and loading the entire voice file would
115 * take several seconds, so we use the same mechanism. */ 117 * take several seconds, so we use the same mechanism. */
116#define TALK_PARTIAL_LOAD 118#define TALK_PARTIAL_LOAD
117#endif 119#define MAX_CLIP_BUFFER_SIZE 100000 /* 70+ clips should fit into 100k */
118
119#ifdef TALK_PARTIAL_LOAD
120static long max_clipsize; /* size of the biggest clip */
121static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */
122static uint8_t clip_age[QUEUE_SIZE];
123#if QUEUE_SIZE > 255
124# error clip_age[] type too small
125#endif
126static int cache_hits, cache_misses;
127#endif 120#endif
128 121
129/* Multiple thumbnails can be loaded back-to-back in this buffer. */ 122/* Multiple thumbnails can be loaded back-to-back in this buffer. */
@@ -147,9 +140,6 @@ static struct mutex queue_mutex SHAREDBSS_ATTR;
147#endif /* CONFIG_CODEC */ 140#endif /* CONFIG_CODEC */
148static int sent; /* how many bytes handed over to playback, owned by ISR */ 141static int sent; /* how many bytes handed over to playback, owned by ISR */
149static unsigned char curr_hd[3]; /* current frame header, for re-sync */ 142static unsigned char curr_hd[3]; /* current frame header, for re-sync */
150static int silence_offset; /* VOICE_PAUSE clip, used for termination */
151static long silence_length; /* length of the VOICE_PAUSE clip */
152static unsigned long lastclip_offset; /* address of latest clip, for silence add */
153static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ 143static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */
154static bool talk_initialized; /* true if talk_init has been called */ 144static bool talk_initialized; /* true if talk_init has been called */
155static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */ 145static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */
@@ -161,27 +151,65 @@ static unsigned long voicefile_size;
161 151
162struct queue_entry /* one entry of the internal queue */ 152struct queue_entry /* one entry of the internal queue */
163{ 153{
164 int offset, length; 154 int offset; /* actually a buflib handle if type == HANDLE */
155 int length; /* total length of the clip */
156 int remaining; /* bytes that still need to be deoded */
157 /* for large QUEUE_SIZE values it might be worthwhile to merge the type
158 * into the bits of the above members (as a space saver). For small values
159 * the required extra code outweights this so it's not done here */
160 enum offset_type {
161 TALK_OFFSET,
162 THUMB_OFFSET,
163#ifdef TALK_PARTIAL_LOAD
164 TALK_HANDLE,
165#endif
166 } type;
165}; 167};
166 168
167static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ 169#ifdef TALK_PARTIAL_LOAD
170static struct buflib_context clip_ctx;
168 171
169#define DEFAULT_VOICE_LANG "english" 172struct clip_cache_metadata {
173 long tick;
174 int handle, voice_id;
175};
176
177static int metadata_table_handle;
178static unsigned max_clips;
179static int cache_hits, cache_misses;
180#endif
181
182static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */
183static struct queue_entry silence, *last_clip;
170 184
171/***************** Private implementation *****************/ 185/***************** Private implementation *****************/
172 186
173static int thumb_handle; 187static int index_handle, talk_handle, thumb_handle;
174static int talk_handle, talk_handle_locked; 188
189static int move_callback(int handle, void *current, void *new)
190{
191 (void)handle; (void)current; (void)new;
192#ifdef TALK_PARTIAL_LOAD
193 if (handle == talk_handle)
194 if (!buflib_context_relocate(&clip_ctx, new))
195 return BUFLIB_CB_CANNOT_MOVE;
196#endif
197 return BUFLIB_CB_OK;
198}
199
200
201static struct mutex read_buffer_mutex;
175 202
176#if CONFIG_CODEC != SWCODEC
177 203
178/* on HWCODEC only voice xor audio can be active at a time */ 204/* on HWCODEC only voice xor audio can be active at a time */
179static bool check_audio_status(void) 205static bool check_audio_status(void)
180{ 206{
207#if CONFIG_CODEC != SWCODEC
181 if (audio_status()) /* busy, buffer in use */ 208 if (audio_status()) /* busy, buffer in use */
182 return false; 209 return false;
183 /* ensure playback is given up on the buffer */ 210 /* ensure playback is given up on the buffer */
184 audio_hard_stop(); 211 audio_hard_stop();
212#endif
185 return true; 213 return true;
186} 214}
187 215
@@ -190,31 +218,43 @@ static bool check_audio_status(void)
190static void sync_callback(int handle, bool sync_on) 218static void sync_callback(int handle, bool sync_on)
191{ 219{
192 (void) handle; 220 (void) handle;
193 (void) sync_on;
194#if CONFIG_CPU == SH7034
195 if (sync_on) 221 if (sync_on)
196 CHCR3 &= ~0x0001; /* disable the DMA (and therefore the interrupt also) */ 222 mutex_lock(&read_buffer_mutex);
197 else 223 else
198 CHCR3 |= 0x0001; /* re-enable the DMA */ 224 mutex_unlock(&read_buffer_mutex);
199#endif
200} 225}
201#else
202#define check_audio_status() (true)
203#endif
204 226
205static int move_callback(int handle, void *current, void *new) 227static ssize_t read_to_handle_ex(int fd, struct buflib_context *ctx, int handle,
228 int handle_offset, size_t count)
206{ 229{
207 (void)handle;(void)current;(void)new; 230 unsigned char *buf;
208 if (UNLIKELY(talk_handle_locked)) 231 ssize_t ret;
209 return BUFLIB_CB_CANNOT_MOVE; 232 mutex_lock(&read_buffer_mutex);
210 return BUFLIB_CB_OK; 233
234 if (!ctx)
235 buf = core_get_data(handle);
236 else
237 buf = buflib_get_data(ctx, handle);
238
239 buf += handle_offset;
240 ret = read(fd, buf, count);
241
242 mutex_unlock(&read_buffer_mutex);
243
244 return ret;
211} 245}
212 246
213static int clip_shrink_callback(int handle, unsigned hints, void *start, size_t old_size) 247static ssize_t read_to_handle(int fd, int handle, int handle_offset, size_t count)
214{ 248{
215 (void)start;(void)old_size;(void)hints; 249 return read_to_handle_ex(fd, NULL, handle, handle_offset, count);
250}
251
216 252
217#if (!defined(TALK_PARTIAL_LOAD) || (MEMORYSIZE > 2)) 253static int shrink_callback(int handle, unsigned hints, void *start, size_t old_size)
254{
255 (void)start;(void)old_size;(void)hints;
256 int *h;
257#if (MAX_CLIP_BUFFER_SIZE < (MEMORYSIZE<<20) || (MEMORYSIZE > 2))
218 /* on low-mem and when the voice buffer size is not limited (i.e. 258 /* on low-mem and when the voice buffer size is not limited (i.e.
219 * on 2MB HWCODEC) we effectively own the entire buffer because 259 * on 2MB HWCODEC) we effectively own the entire buffer because
220 * the voicefile takes up all RAM. This blocks other Rockbox parts 260 * the voicefile takes up all RAM. This blocks other Rockbox parts
@@ -222,12 +262,22 @@ static int clip_shrink_callback(int handle, unsigned hints, void *start, size_t
222 * up the buffer and reload when clips are played back. On high-mem 262 * up the buffer and reload when clips are played back. On high-mem
223 * or when the clip buffer is limited to a few 100K this provision is 263 * or when the clip buffer is limited to a few 100K this provision is
224 * not necessary. */ 264 * not necessary. */
225 if (LIKELY(!talk_handle_locked) 265 if (give_buffer_away
226 && give_buffer_away
227 && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK) 266 && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK)
228#endif 267#endif
229 { 268 {
230 talk_handle = core_free(handle); 269 if (handle == talk_handle)
270 h = &talk_handle;
271 else //if (handle == index_handle)
272 h = &index_handle;
273
274 mutex_lock(&read_buffer_mutex);
275 /* the clip buffer isn't usable without index table */
276 if (handle == index_handle && talk_handle > 0)
277 talk_handle = core_free(talk_handle);
278 *h = core_free(handle);
279 mutex_unlock(&read_buffer_mutex);
280
231 return BUFLIB_CB_OK; 281 return BUFLIB_CB_OK;
232 } 282 }
233 return BUFLIB_CB_CANNOT_SHRINK; 283 return BUFLIB_CB_CANNOT_SHRINK;
@@ -237,62 +287,31 @@ static int thumb_shrink_callback(int handle, unsigned hints, void *start, size_t
237{ 287{
238 (void)start;(void)old_size;(void)hints; 288 (void)start;(void)old_size;(void)hints;
239 289
240 /* be generous about the thumbnail buffer unless currently used */ 290 if (handle == thumb_handle && thumbnail_buf_used == 0)
241 if (LIKELY(!talk_handle_locked) && thumbnail_buf_used == 0)
242 { 291 {
292 mutex_lock(&read_buffer_mutex);
243 thumb_handle = core_free(handle); 293 thumb_handle = core_free(handle);
294 mutex_unlock(&read_buffer_mutex);
244 return BUFLIB_CB_OK; 295 return BUFLIB_CB_OK;
245 } 296 }
297
246 return BUFLIB_CB_CANNOT_SHRINK; 298 return BUFLIB_CB_CANNOT_SHRINK;
247} 299}
248 300
249static struct buflib_callbacks clip_ops = { 301
302
303static struct buflib_callbacks talk_ops = {
250 .move_callback = move_callback, 304 .move_callback = move_callback,
251#if CONFIG_CODEC != SWCODEC
252 .sync_callback = sync_callback, 305 .sync_callback = sync_callback,
253#endif 306 .shrink_callback = shrink_callback,
254 .shrink_callback = clip_shrink_callback,
255}; 307};
256 308
257static struct buflib_callbacks thumb_ops = { 309static struct buflib_callbacks thumb_ops = {
258 .move_callback = move_callback, 310 .move_callback = move_callback,
259#if CONFIG_CODEC != SWCODEC
260 .sync_callback = sync_callback, 311 .sync_callback = sync_callback,
261#endif
262 .shrink_callback = thumb_shrink_callback, 312 .shrink_callback = thumb_shrink_callback,
263}; 313};
264 314
265
266static int index_handle, index_handle_locked;
267static int index_move_callback(int handle, void *current, void *new)
268{
269 (void)handle;(void)current;(void)new;
270 if (UNLIKELY(index_handle_locked))
271 return BUFLIB_CB_CANNOT_MOVE;
272 return BUFLIB_CB_OK;
273}
274
275static int index_shrink_callback(int handle, unsigned hints, void *start, size_t old_size)
276{
277 (void)start;(void)old_size;
278 if (LIKELY(!index_handle_locked)
279 && give_buffer_away
280 && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK)
281 {
282 index_handle = core_free(handle);
283 /* the clip buffer isn't usable without index table */
284 if (LIKELY(!talk_handle_locked))
285 talk_handle = core_free(talk_handle);
286 return BUFLIB_CB_OK;
287 }
288 return BUFLIB_CB_CANNOT_SHRINK;
289}
290
291static struct buflib_callbacks index_ops = {
292 .move_callback = index_move_callback,
293 .shrink_callback = index_shrink_callback,
294};
295
296static int open_voicefile(void) 315static int open_voicefile(void)
297{ 316{
298 char buf[64]; 317 char buf[64];
@@ -310,114 +329,143 @@ static int open_voicefile(void)
310} 329}
311 330
312 331
313/* fetch a clip from the voice file */ 332static int id2index(int id)
314static int get_clip(long id, long* p_size)
315{ 333{
316 int retval = -1; 334 int index = id;
317 struct clip_entry* clipbuf;
318 size_t clipsize;
319
320 if (id > VOICEONLY_DELIMITER) 335 if (id > VOICEONLY_DELIMITER)
321 { /* voice-only entries use the second part of the table. 336 { /* voice-only entries use the second part of the table.
322 The first string comes after VOICEONLY_DELIMITER so we need to 337 The first string comes after VOICEONLY_DELIMITER so we need to
323 substract VOICEONLY_DELIMITER + 1 */ 338 substract VOICEONLY_DELIMITER + 1 */
324 id -= VOICEONLY_DELIMITER + 1; 339 index -= VOICEONLY_DELIMITER + 1;
325 if (id >= voicefile.id2_max) 340 if (index >= voicefile.id2_max)
326 return -1; /* must be newer than we have */ 341 return -1; /* must be newer than we have */
327 id += voicefile.id1_max; /* table 2 is behind table 1 */ 342 index += voicefile.id1_max; /* table 2 is behind table 1 */
328 } 343 }
329 else 344 else
330 { /* normal use of the first table */ 345 { /* normal use of the first table */
331 if (id >= voicefile.id1_max) 346 if (id >= voicefile.id1_max)
332 return -1; /* must be newer than we have */ 347 return -1; /* must be newer than we have */
333 } 348 }
349 return index;
350}
351
352#ifdef TALK_PARTIAL_LOAD
353static int free_oldest_clip(void)
354{
355 unsigned i;
356 int oldest = 0;
357 long age, now;
358 struct clip_entry* clipbuf;
359 struct clip_cache_metadata *cc = buflib_get_data(&clip_ctx, metadata_table_handle);
360 for(age = i = 0, now = current_tick; i < max_clips; i++)
361 {
362 if (cc[i].handle && (now - cc[i].tick) > age
363 && cc[i].voice_id != VOICE_PAUSE) /* never consider silence */
364 {
365 age = now - cc[i].tick;
366 oldest = i;
367 }
368 }
369 cc = &cc[oldest];
370 cc->handle = buflib_free(&clip_ctx, cc->handle);
371 /* need to clear the LOADED bit too */
372 clipbuf = core_get_data(index_handle);
373 clipbuf[id2index(cc->voice_id)].size &= ~LOADED_MASK;
374
375 return oldest;
376}
377#endif
378/* fetch a clip from the voice file */
379static int get_clip(long id, struct queue_entry *q)
380{
381 int index;
382 int retval = -1;
383 struct clip_entry* clipbuf;
384 size_t clipsize;
385 enum offset_type type;
386
387 index = id2index(id);
388 if (index == -1)
389 return -1;
334 390
335 clipbuf = core_get_data(index_handle); 391 clipbuf = core_get_data(index_handle);
336 clipsize = clipbuf[id].size; 392 clipsize = clipbuf[index].size;
337 if (clipsize == 0) /* clip not included in voicefile */ 393 if (clipsize == 0) /* clip not included in voicefile */
338 return -1; 394 return -1;
339 395
340#ifndef TALK_PARTIAL_LOAD 396#ifndef TALK_PARTIAL_LOAD
341 retval = clipbuf[id].offset; 397 retval = clipbuf[index].offset;
398 type = TALK_OFFSET;
342 399
343#else 400#else
344 if (!(clipsize & LOADED_MASK)) 401 if (!(clipsize & LOADED_MASK))
345 { /* clip needs loading */ 402 { /* clip needs loading */
403 struct clip_cache_metadata *cc;
404 int fd, handle, oldest = -1;
405 unsigned i;
346 ssize_t ret; 406 ssize_t ret;
347 int fd, idx = 0;
348 unsigned char *voicebuf;
349 cache_misses++; 407 cache_misses++;
350 if (id == VOICE_PAUSE) { 408 /* free clips from cache until this one succeeds to allocate */
351 idx = QUEUE_SIZE; /* we keep VOICE_PAUSE loaded */ 409 while ((handle = buflib_alloc(&clip_ctx, clipsize)) < 0)
352 } else { 410 oldest = free_oldest_clip();
353 int oldest = 0, i; 411 /* handle should now hold a valid alloc. Load from disk
354 for(i=0; i<QUEUE_SIZE; i++) { 412 * and insert into cache */
355 if (buffered_id[i] < 0) {
356 /* found a free entry, that means the buffer isn't
357 * full yet. */
358 idx = i;
359 break;
360 }
361
362 /* find the oldest clip */
363 if(clip_age[i] > oldest) {
364 idx = i;
365 oldest = clip_age[i];
366 }
367
368 /* increment age of each loaded clip */
369 clip_age[i]++;
370 }
371 clip_age[idx] = 0; /* reset clip's age */
372 }
373 retval = idx * max_clipsize;
374 fd = open_voicefile(); 413 fd = open_voicefile();
375 if (fd < 0) 414 if (fd < 0)
415 {
416 buflib_free(&clip_ctx, handle);
376 return -1; /* open error */ 417 return -1; /* open error */
377 418 }
378 talk_handle_locked++;
379 voicebuf = core_get_data(talk_handle);
380 clipbuf = core_get_data(index_handle); 419 clipbuf = core_get_data(index_handle);
381 lseek(fd, clipbuf[id].offset, SEEK_SET); 420 lseek(fd, clipbuf[index].offset, SEEK_SET);
382 ret = read(fd, &voicebuf[retval], clipsize); 421 ret = read_to_handle_ex(fd, &clip_ctx, handle, 0, clipsize);
383 close(fd); 422 close(fd);
384 talk_handle_locked--;
385 423
386 if (ret < 0 || clipsize != (size_t)ret) 424 if (ret < 0 || clipsize != (size_t)ret)
425 {
426 buflib_free(&clip_ctx, handle);
387 return -1; /* read error */ 427 return -1; /* read error */
428 }
388 429
389 clipbuf = core_get_data(index_handle); 430 clipbuf = core_get_data(index_handle);
390 clipbuf[id].size |= LOADED_MASK; /* mark as loaded */ 431 clipbuf[index].size |= LOADED_MASK; /* mark as loaded */
391 432
392 if (id != VOICE_PAUSE) { 433 /* finally insert into metadata table */
393 if (buffered_id[idx] >= 0) { 434 cc = buflib_get_data(&clip_ctx, metadata_table_handle);
394 /* mark previously loaded clip as unloaded */ 435 if (oldest != -1)
395 clipbuf[buffered_id[idx]].size &= ~LOADED_MASK; 436 /* went through the cache in the above loop already, re-use the slot */
396 } 437 cc = &cc[oldest];
397 buffered_id[idx] = id; 438 else
439 { /* find an empty slot */
440 for(i = 0; cc[i].handle && i < max_clips; i++) ;
441 if (i == max_clips) /* no free slot in the cache table? */
442 i = free_oldest_clip();
443 cc = &cc[i];
398 } 444 }
445 cc->handle = handle;
446 cc->tick = current_tick;
447 cc->voice_id = id;
448 retval = handle;
399 } 449 }
400 else 450 else
401 { /* clip is in memory already */ 451 { /* clip is in memory already; find where it was loaded */
402 /* Find where it was loaded */
403 cache_hits++; 452 cache_hits++;
404 if (id == VOICE_PAUSE) { 453 struct clip_cache_metadata *cc;
405 retval = QUEUE_SIZE * max_clipsize; 454 static int i;
406 } else { 455 cc = buflib_get_data(&clip_ctx, metadata_table_handle);
407 int idx; 456 for (i = 0; cc[i].voice_id != id || !cc[i].handle; i++) ;
408 for (idx=0; idx<QUEUE_SIZE; idx++) 457 cc[i].tick = current_tick; /* reset age */
409 if (buffered_id[idx] == id) {
410 retval = idx * max_clipsize;
411 clip_age[idx] = 0; /* reset clip's age */
412 break;
413 }
414 }
415 clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */ 458 clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */
459 retval = cc[i].handle;
416 } 460 }
461 type = TALK_HANDLE;
417#endif /* TALK_PARTIAL_LOAD */ 462#endif /* TALK_PARTIAL_LOAD */
418 463
419 *p_size = clipsize; 464 q->offset = retval;
420 return retval; 465 q->length = clipsize;
466 q->remaining = clipsize;
467 q->type = type;
468 return 0;
421} 469}
422 470
423static bool load_index_table(int fd, const struct voicefile_header *hdr) 471static bool load_index_table(int fd, const struct voicefile_header *hdr)
@@ -429,13 +477,11 @@ static bool load_index_table(int fd, const struct voicefile_header *hdr)
429 return true; 477 return true;
430 478
431 ssize_t alloc_size = (hdr->id1_max + hdr->id2_max) * sizeof(struct clip_entry); 479 ssize_t alloc_size = (hdr->id1_max + hdr->id2_max) * sizeof(struct clip_entry);
432 index_handle = core_alloc_ex("voice index", alloc_size, &index_ops); 480 index_handle = core_alloc_ex("voice index", alloc_size, &talk_ops);
433 if (index_handle < 0) 481 if (index_handle < 0)
434 return false; 482 return false;
435 483
436 index_handle_locked++; 484 ret = read_to_handle(fd, index_handle, 0, alloc_size);
437 buf = core_get_data(index_handle);
438 ret = read(fd, buf, alloc_size);
439 485
440#ifndef TALK_PARTIAL_LOAD 486#ifndef TALK_PARTIAL_LOAD
441 int clips_offset, num_clips; 487 int clips_offset, num_clips;
@@ -447,19 +493,20 @@ static bool load_index_table(int fd, const struct voicefile_header *hdr)
447 clips_offset += num_clips * sizeof(struct clip_entry); /* skip index */ 493 clips_offset += num_clips * sizeof(struct clip_entry); /* skip index */
448#endif 494#endif
449 if (ret == alloc_size) 495 if (ret == alloc_size)
496 {
497 buf = core_get_data(index_handle);
450 for (int i = 0; i < hdr->id1_max + hdr->id2_max; i++) 498 for (int i = 0; i < hdr->id1_max + hdr->id2_max; i++)
451 { 499 {
452#ifdef ROCKBOX_LITTLE_ENDIAN 500#ifdef ROCKBOX_LITTLE_ENDIAN
501 /* doesn't yield() */
453 structec_convert(&buf[i], "ll", 1, true); 502 structec_convert(&buf[i], "ll", 1, true);
454#endif 503#endif
455#ifndef TALK_PARTIAL_LOAD 504#ifndef TALK_PARTIAL_LOAD
456 buf[i].offset -= clips_offset; 505 buf[i].offset -= clips_offset;
457#endif 506#endif
458 } 507 }
459 508 }
460 index_handle_locked--; 509 else
461
462 if (ret != alloc_size)
463 index_handle = core_free(index_handle); 510 index_handle = core_free(index_handle);
464 511
465 return ret == alloc_size; 512 return ret == alloc_size;
@@ -481,20 +528,16 @@ static bool load_header(int fd, struct voicefile_header *hdr)
481#ifndef TALK_PARTIAL_LOAD 528#ifndef TALK_PARTIAL_LOAD
482static bool load_data(int fd, ssize_t size_to_read) 529static bool load_data(int fd, ssize_t size_to_read)
483{ 530{
484 unsigned char *buf;
485 ssize_t ret; 531 ssize_t ret;
486 532
487 if (size_to_read < 0) 533 if (size_to_read < 0)
488 return false; 534 return false;
489 535
490 talk_handle = core_alloc_ex("voice data", size_to_read, &clip_ops); 536 talk_handle = core_alloc_ex("voice data", size_to_read, &talk_ops);
491 if (talk_handle < 0) 537 if (talk_handle < 0)
492 return false; 538 return false;
493 539
494 talk_handle_locked++; 540 ret = read_to_handle(fd, talk_handle, 0, size_to_read);
495 buf = core_get_data(talk_handle);
496 ret = read(fd, buf, size_to_read);
497 talk_handle_locked--;
498 541
499 if (ret != size_to_read) 542 if (ret != size_to_read)
500 talk_handle = core_free(talk_handle); 543 talk_handle = core_free(talk_handle);
@@ -558,23 +601,33 @@ static bool load_voicefile_data(int fd, size_t max_size)
558{ 601{
559#ifdef TALK_PARTIAL_LOAD 602#ifdef TALK_PARTIAL_LOAD
560 (void)fd; 603 (void)fd;
561 /* just allocate, populate on an as-needed basis later */ 604 size_t alloc_size;
562 talk_handle = core_alloc_ex("voice data", max_size, &clip_ops); 605 /* just allocate, populate on an as-needed basis later
606 * re-create the clip buffer to ensure clip_ctx is up-to-date */
607 if (talk_handle > 0)
608 talk_handle = core_free(talk_handle);
609 talk_handle = core_alloc_ex("voice data", max_size, &talk_ops);
563 if (talk_handle < 0) 610 if (talk_handle < 0)
564 goto load_err_free; 611 goto load_err_free;
612
613 buflib_init(&clip_ctx, core_get_data(talk_handle), max_size);
614
615 alloc_size = max_clips * sizeof(struct clip_cache_metadata);
616 /* the first alloc is the clip metadata table */
617 metadata_table_handle = buflib_alloc(&clip_ctx, alloc_size);
618 memset(buflib_get_data(&clip_ctx, metadata_table_handle), 0, alloc_size);
619
565#else 620#else
566 size_t load_size, clips_size;
567 /* load the entire file into memory */ 621 /* load the entire file into memory */
568 clips_size = (voicefile.id1_max+voicefile.id2_max) * sizeof(struct clip_entry); 622 if (!load_data(fd, max_size))
569 load_size = max_size - voicefile.table - clips_size;
570 if (!load_data(fd, load_size))
571 goto load_err_free; 623 goto load_err_free;
572#endif 624#endif
573 625
574 /* make sure to have the silence clip, if available 626 /* make sure to have the silence clip, if available
575 * return value can be cached globally even for TALK_PARTIAL_LOAD because 627 * return value can be cached globally even for TALK_PARTIAL_LOAD because
576 * the VOICE_PAUSE clip is specially handled */ 628 * the VOICE_PAUSE clip is specially handled */
577 silence_offset = get_clip(VOICE_PAUSE, &silence_length); 629 if (get_clip(VOICE_PAUSE, &silence))
630 goto load_err_free;
578 631
579 /* not an error if this fails here, might try again when the 632 /* not an error if this fails here, might try again when the
580 * actual thumbnails are attempted to be played back */ 633 * actual thumbnails are attempted to be played back */
@@ -587,74 +640,94 @@ load_err_free:
587 return false; 640 return false;
588} 641}
589 642
643/* most, if not all, clips should be well below 32k (largest in english.lang is
644 * 4.5K). Currently there is a problem with voice decoding such that clips
645 * cannot be decoded in chunks. Once that is resolved this buffer could be
646 * smaller and clips be decoded in multiple chunks */
647static unsigned char commit_buffer[32<<10];
648
649static void* commit_transfer(struct queue_entry *qe, size_t *size)
650{
651 void *buf = NULL; /* shut up gcc */
652 static unsigned char *bufpos = commit_buffer;
653 int offset = qe->offset;
654#if CONFIG_CODEC != SWCODEC
655 sent = MIN(qe->remaining, 0xFFFF);
656#else
657 sent = qe->remaining;
658#endif
659 sent = MIN((size_t)sent, sizeof(commit_buffer));
660 switch (qe->type)
661 {
662 case TALK_OFFSET: buf = core_get_data(talk_handle) + offset; break;
663 case THUMB_OFFSET: buf = core_get_data(thumb_handle) + offset; break;
664#ifdef TALK_PARTIAL_LOAD
665 case TALK_HANDLE: buf = buflib_get_data(&clip_ctx, offset); break;
666#endif
667 }
668 /* adjust buffer position to what has been played already */
669 buf += (qe->length - qe->remaining);
670 memcpy(bufpos, buf, sent);
671 *size = sent;
672
673
674 return commit_buffer;
675}
676
677static inline bool is_silence(struct queue_entry *qe)
678{
679 if (silence.length > 0) /* silence clip available? */
680 return (qe->offset == silence.offset && qe->type == silence.type);
681 else
682 return false;
683}
684
590/* called in ISR context (on HWCODEC) if mp3 data got consumed */ 685/* called in ISR context (on HWCODEC) if mp3 data got consumed */
591static void mp3_callback(const void** start, size_t* size) 686static void mp3_callback(const void** start, size_t* size)
592{ 687{
593 queue[queue_read].length -= sent; /* we completed this */ 688 struct queue_entry *qe = &queue[queue_read];
594 queue[queue_read].offset += sent; 689 qe->remaining -= sent; /* we completed this */
595 690
596 if (queue[queue_read].length > 0) /* current clip not finished? */ 691 if (qe->remaining > 0) /* current clip not finished? */
597 { /* feed the next 64K-1 chunk */ 692 { /* feed the next 64K-1 chunk */
598 int offset; 693 *start = commit_transfer(qe, size);
599#if CONFIG_CODEC != SWCODEC
600 sent = MIN(queue[queue_read].length, 0xFFFF);
601#else
602 sent = queue[queue_read].length;
603#endif
604 offset = queue[queue_read].offset;
605 if ((unsigned long)offset >= voicefile_size)
606 *start = core_get_data(thumb_handle) + offset - voicefile_size;
607 else
608 *start = core_get_data(talk_handle) + offset;
609 *size = sent;
610 return; 694 return;
611 } 695 }
696
612 talk_queue_lock(); 697 talk_queue_lock();
613 if(thumb_handle && (unsigned long)queue[queue_read].offset == voicefile_size+thumbnail_buf_used) 698 /* check if thumbnails have been played */
614 thumbnail_buf_used = 0; 699 if (qe->type == THUMB_OFFSET)
615 if (sent > 0) /* go to next entry */
616 { 700 {
617 queue_read = (queue_read + 1) & QUEUE_MASK; 701 if (qe->remaining == 0 && (qe->length + qe->offset) == thumbnail_buf_used)
702 thumbnail_buf_used = 0;
618 } 703 }
619 704
620re_check: 705 /* increment read position for the just played clip */
706 queue_read = (queue_read + 1) & QUEUE_MASK;
621 707
622 if (QUEUE_LEVEL != 0) /* queue is not empty? */ 708 if (QUEUE_LEVEL == 0)
623 { /* start next clip */ 709 {
624 unsigned char *buf; 710 if (!is_silence(last_clip) && last_clip->type != THUMB_OFFSET)
625#if CONFIG_CODEC != SWCODEC 711 { /* add silence clip when queue runs empty playing a voice clip,
626 sent = MIN(queue[queue_read].length, 0xFFFF); 712 * only if the previous clip wasn't silence or thumbnail */
627#else 713 queue[queue_write] = silence;
628 sent = queue[queue_read].length; 714 queue_write = (queue_write + 1) & QUEUE_MASK;
629#endif 715 }
630 lastclip_offset = queue[queue_read].offset;
631 /* offsets larger than voicefile_size denote thumbnail clips */
632 if (lastclip_offset >= voicefile_size)
633 buf = core_get_data(thumb_handle) + lastclip_offset - voicefile_size;
634 else 716 else
635 buf = core_get_data(talk_handle) + lastclip_offset; 717 {
636 *start = buf; 718 *size = 0; /* end of data */
637 *size = sent; 719 }
638 curr_hd[0] = buf[1];
639 curr_hd[1] = buf[2];
640 curr_hd[2] = buf[3];
641 } 720 }
642 else if (silence_offset > 0 /* silence clip available */
643 && lastclip_offset != (unsigned long)silence_offset /* previous clip wasn't silence */
644 && !(lastclip_offset >= voicefile_size /* ..or thumbnail */
645 && lastclip_offset < voicefile_size +size_for_thumbnail))
646 { /* add silence clip when queue runs empty playing a voice clip */
647 queue[queue_write].offset = silence_offset;
648 queue[queue_write].length = silence_length;
649 queue_write = (queue_write + 1) & QUEUE_MASK;
650 721
651 goto re_check; 722 if (QUEUE_LEVEL != 0) /* queue is not empty? */
652 } 723 { /* start next clip */
653 else 724 last_clip = &queue[queue_read];
654 { 725 *start = commit_transfer(last_clip, size);
655 *size = 0; /* end of data */ 726 curr_hd[0] = commit_buffer[1];
656 talk_handle_locked--; 727 curr_hd[1] = commit_buffer[2];
728 curr_hd[2] = commit_buffer[3];
657 } 729 }
730
658 talk_queue_unlock(); 731 talk_queue_unlock();
659} 732}
660 733
@@ -672,7 +745,7 @@ void talk_force_shutup(void)
672 unsigned char* search; 745 unsigned char* search;
673 unsigned char* end; 746 unsigned char* end;
674 int len; 747 int len;
675 unsigned clip_offset; 748 unsigned offset;
676 if (QUEUE_LEVEL == 0) /* has ended anyway */ 749 if (QUEUE_LEVEL == 0) /* has ended anyway */
677 return; 750 return;
678 751
@@ -681,11 +754,16 @@ void talk_force_shutup(void)
681#endif /* CONFIG_CPU == SH7034 */ 754#endif /* CONFIG_CPU == SH7034 */
682 /* search next frame boundary and continue up to there */ 755 /* search next frame boundary and continue up to there */
683 pos = search = mp3_get_pos(); 756 pos = search = mp3_get_pos();
684 clip_offset = queue[queue_read].offset; 757 offset = queue[queue_read].offset;
685 if (clip_offset >= voicefile_size) 758 switch (queue[queue_read].type)
686 end = core_get_data(thumb_handle) + clip_offset - voicefile_size; 759 {
687 else 760 case TALK_OFFSET: end = core_get_data(talk_handle) + offset; break;
688 end = core_get_data(talk_handle) + clip_offset; 761 case THUMB_OFFSET: end = core_get_data(thumb_handle) + offset; break;
762#ifdef TALK_PARTIAL_LOAD
763 case TALK_HANDLE: end = buflib_get_data(&clip_ctx, offset); break;
764#endif
765 default: end = NULL; /* shut up gcc */
766 }
689 len = queue[queue_read].length; 767 len = queue[queue_read].length;
690 768
691 if (pos >= end && pos <= (end+len)) /* really our clip? */ 769 if (pos >= end && pos <= (end+len)) /* really our clip? */
@@ -727,7 +805,6 @@ void talk_force_shutup(void)
727 mp3_play_stop(); 805 mp3_play_stop();
728 talk_queue_lock(); 806 talk_queue_lock();
729 queue_write = queue_read = 0; /* reset the queue */ 807 queue_write = queue_read = 0; /* reset the queue */
730 talk_handle_locked = MAX(talk_handle_locked-1, 0);
731 thumbnail_buf_used = 0; 808 thumbnail_buf_used = 0;
732 talk_queue_unlock(); 809 talk_queue_unlock();
733 need_shutup = false; 810 need_shutup = false;
@@ -741,9 +818,9 @@ void talk_shutup(void)
741} 818}
742 819
743/* schedule a clip, at the end or discard the existing queue */ 820/* schedule a clip, at the end or discard the existing queue */
744static void queue_clip(unsigned long clip_offset, long size, bool enqueue) 821static void queue_clip(struct queue_entry *clip, bool enqueue)
745{ 822{
746 unsigned char *buf; 823 struct queue_entry *qe;
747 int queue_level; 824 int queue_level;
748 825
749 if (!enqueue) 826 if (!enqueue)
@@ -752,7 +829,7 @@ static void queue_clip(unsigned long clip_offset, long size, bool enqueue)
752 longer in effect. */ 829 longer in effect. */
753 force_enqueue_next = false; 830 force_enqueue_next = false;
754 831
755 if (!size) 832 if (!clip->length)
756 return; /* safety check */ 833 return; /* safety check */
757#if CONFIG_CPU == SH7034 834#if CONFIG_CPU == SH7034
758 /* disable the DMA temporarily, to be safe of race condition */ 835 /* disable the DMA temporarily, to be safe of race condition */
@@ -760,32 +837,24 @@ static void queue_clip(unsigned long clip_offset, long size, bool enqueue)
760#endif 837#endif
761 talk_queue_lock(); 838 talk_queue_lock();
762 queue_level = QUEUE_LEVEL; /* check old level */ 839 queue_level = QUEUE_LEVEL; /* check old level */
840 qe = &queue[queue_write];
763 841
764 if (queue_level < QUEUE_SIZE - 1) /* space left? */ 842 if (queue_level < QUEUE_SIZE - 1) /* space left? */
765 { 843 {
766 queue[queue_write].offset = clip_offset; /* populate an entry */ 844 queue[queue_write] = *clip;
767 queue[queue_write].length = size;
768 queue_write = (queue_write + 1) & QUEUE_MASK; 845 queue_write = (queue_write + 1) & QUEUE_MASK;
769 } 846 }
770 talk_queue_unlock(); 847 talk_queue_unlock();
771 848
772 if (queue_level == 0) 849 if (queue_level == 0)
773 { /* queue was empty, we have to do the initial start */ 850 { /* queue was empty, we have to do the initial start */
774 lastclip_offset = clip_offset; 851 size_t size;
775#if CONFIG_CODEC != SWCODEC 852 void *buf = commit_transfer(qe, &size);
776 sent = MIN(size, 0xFFFF); /* DMA can do no more */ 853 last_clip = qe;
777#else 854 mp3_play_data(buf, size, mp3_callback);
778 sent = size; 855 curr_hd[0] = commit_buffer[1];
779#endif 856 curr_hd[1] = commit_buffer[2];
780 talk_handle_locked++; 857 curr_hd[2] = commit_buffer[3];
781 if (clip_offset >= voicefile_size)
782 buf = core_get_data(thumb_handle) + clip_offset - voicefile_size;
783 else
784 buf = core_get_data(talk_handle) + clip_offset;
785 mp3_play_data(buf, sent, mp3_callback);
786 curr_hd[0] = buf[1];
787 curr_hd[1] = buf[2];
788 curr_hd[2] = buf[3];
789 mp3_play_pause(true); /* kickoff audio */ 858 mp3_play_pause(true); /* kickoff audio */
790 } 859 }
791 else 860 else
@@ -822,11 +891,13 @@ void talk_init(void)
822 return; 891 return;
823 } 892 }
824 893
825#if CONFIG_CODEC == SWCODEC
826 if(!talk_initialized) 894 if(!talk_initialized)
895 {
896#if CONFIG_CODEC == SWCODEC
827 mutex_init(&queue_mutex); 897 mutex_init(&queue_mutex);
828#endif /* CONFIG_CODEC == SWCODEC */ 898#endif /* CONFIG_CODEC == SWCODEC */
829 899 mutex_init(&read_buffer_mutex);
900 }
830 talk_initialized = true; 901 talk_initialized = true;
831 strlcpy((char *)last_lang, (char *)global_settings.lang_file, 902 strlcpy((char *)last_lang, (char *)global_settings.lang_file,
832 MAX_FILENAME); 903 MAX_FILENAME);
@@ -835,12 +906,7 @@ void talk_init(void)
835 queue_write = queue_read = 0; /* reset the queue */ 906 queue_write = queue_read = 0; /* reset the queue */
836 memset(&voicefile, 0, sizeof(voicefile)); 907 memset(&voicefile, 0, sizeof(voicefile));
837 908
838#ifdef TALK_PARTIAL_LOAD 909 silence.offset = -1; /* pause clip not accessible */
839 for(int i=0; i<QUEUE_SIZE; i++)
840 buffered_id[i] = -1;
841#endif
842
843 silence_offset = -1; /* pause clip not accessible */
844 voicefile_size = has_voicefile = 0; 910 voicefile_size = has_voicefile = 0;
845 /* need to free these as their size depends on the voice file, and 911 /* need to free these as their size depends on the voice file, and
846 * this function is called when the talk voice file changes */ 912 * this function is called when the talk voice file changes */
@@ -861,34 +927,59 @@ void talk_init(void)
861 * at once */ 927 * at once */
862 unsigned num_clips = voicefile.id1_max + voicefile.id2_max; 928 unsigned num_clips = voicefile.id1_max + voicefile.id2_max;
863 struct clip_entry *clips = core_get_data(index_handle); 929 struct clip_entry *clips = core_get_data(index_handle);
864 int silence_size = 0; 930 int avg_size = clips[0].size;
865 931 int real_clips = 1; /* shut up gcc */
866 for(unsigned i=0; i<num_clips; i++) { 932 /* check for the smallest clip size to estimate the max. number of clips
867 int size = clips[i].size; 933 * the buffer has to hold */
868 if (size > max_clipsize) 934 for(unsigned i=1; i<num_clips; i++)
869 max_clipsize = size; 935 {
870 if (i == VOICE_PAUSE) 936 if (clips[i].size)
871 silence_size = size; 937 { /* don't consider empty clips, they are not stored anyway */
938 real_clips += 1;
939 avg_size += clips[i].size;
940 }
872 } 941 }
873 942 avg_size /= real_clips;
874 voicefile_size = voicefile.table + num_clips * sizeof(struct clip_entry); 943
875 voicefile_size += max_clipsize * QUEUE_SIZE + silence_size; 944 max_clips = MIN((int)(MAX_CLIP_BUFFER_SIZE/avg_size) + 1, real_clips);
876 945 voicefile_size = MAX_CLIP_BUFFER_SIZE;
877 /* test if we can open and if it fits in the audiobuffer */ 946 /* additionally to the clip we need a table to record the age of the clips
878 size_t audiobufsz = audio_buffer_available(); 947 * so that, when memory is tight, only the most recently used ones are kept */
879 has_voicefile = audiobufsz >= voicefile_size; 948 voicefile_size += sizeof(struct clip_cache_metadata) * max_clips;
949 /* compensate a bit for buflib per-alloc overhead */
950 voicefile_size += BUFLIB_ALLOC_OVERHEAD * max_clips;
951 /* cap to the max. number of clips or the size of the available audio
952 * buffer which we grab. We leave some to the rest of the system.
953 * While that reduces our buffer size it improves the chance that
954 * other allocs succeed without disabling voice which would require
955 * reloading the voice from disk (as we do not shrink our buffer when
956 * other code attempts new allocs these would fail) */
957 ssize_t cap = MIN(MAX_CLIP_BUFFER_SIZE, audio_buffer_available() - (64<<10));
958 if (UNLIKELY(cap < 0))
959 {
960 logf("Not enough memory for voice. Disabling...\n");
961 if (index_handle > 0)
962 index_handle = core_free(index_handle);
963 voicefile_size = 0;
964 goto out;
965 }
966 else if (voicefile_size > (size_t)cap)
967 voicefile_size = cap;
880 968
881#else 969#else
882 /* load the compressed clip data into memory, in its entirety */ 970 size_t clips_size;
883 voicefile_size = filesize(filehandle); 971 clips_size = (voicefile.id1_max+voicefile.id2_max) * sizeof(struct clip_entry);
972 voicefile_size = filesize(filehandle) - voicefile.table - clips_size;
973 /* load the compressed clip data into memory */
884 if (!load_voicefile_data(filehandle, voicefile_size)) 974 if (!load_voicefile_data(filehandle, voicefile_size))
885 { 975 {
886 voicefile_size = 0; 976 voicefile_size = 0;
887 goto out; 977 goto out;
888 } 978 }
889 has_voicefile = true;
890#endif 979#endif
891 980
981 has_voicefile = true;
982
892#if CONFIG_CODEC == SWCODEC 983#if CONFIG_CODEC == SWCODEC
893 /* Initialize the actual voice clip playback engine as well */ 984 /* Initialize the actual voice clip playback engine as well */
894 if (talk_voice_required()) 985 if (talk_voice_required())
@@ -915,10 +1006,9 @@ void talk_buffer_set_policy(int policy)
915/* play a voice ID from voicefile */ 1006/* play a voice ID from voicefile */
916int talk_id(int32_t id, bool enqueue) 1007int talk_id(int32_t id, bool enqueue)
917{ 1008{
918 int clip;
919 long clipsize;
920 int32_t unit; 1009 int32_t unit;
921 int decimals; 1010 int decimals;
1011 struct queue_entry clip;
922 1012
923 if (!has_voicefile) 1013 if (!has_voicefile)
924 return 0; /* no voicefile loaded, not an error -> pretent success */ 1014 return 0; /* no voicefile loaded, not an error -> pretent success */
@@ -952,8 +1042,7 @@ int talk_id(int32_t id, bool enqueue)
952 return 0; /* and stop, end of special case */ 1042 return 0; /* and stop, end of special case */
953 } 1043 }
954 1044
955 clip = get_clip(id, &clipsize); 1045 if (get_clip(id, &clip) < 0)
956 if (clip < 0)
957 return -1; /* not present */ 1046 return -1; /* not present */
958 1047
959#ifdef LOGF_ENABLE 1048#ifdef LOGF_ENABLE
@@ -963,7 +1052,7 @@ int talk_id(int32_t id, bool enqueue)
963 logf("\ntalk_id: Say '%s'\n", str(id)); 1052 logf("\ntalk_id: Say '%s'\n", str(id));
964#endif 1053#endif
965 1054
966 queue_clip(clip, clipsize, enqueue); 1055 queue_clip(&clip, enqueue);
967 1056
968 return 0; 1057 return 0;
969} 1058}
@@ -997,11 +1086,11 @@ static int _talk_file(const char* filename,
997 int fd; 1086 int fd;
998 int size; 1087 int size;
999 int thumb_used; 1088 int thumb_used;
1000 char *buf;
1001#if CONFIG_CODEC != SWCODEC 1089#if CONFIG_CODEC != SWCODEC
1002 struct mp3entry info; 1090 struct mp3entry info;
1003#endif 1091#endif
1004 1092
1093 /* reload needed? */
1005 if (talk_temp_disable_count > 0) 1094 if (talk_temp_disable_count > 0)
1006 return -1; /* talking has been disabled */ 1095 return -1; /* talking has been disabled */
1007 if (!check_audio_status()) 1096 if (!check_audio_status())
@@ -1038,16 +1127,14 @@ static int _talk_file(const char* filename,
1038 lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ 1127 lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */
1039#endif 1128#endif
1040 1129
1041 talk_handle_locked++; 1130 size = read_to_handle(fd, thumb_handle, thumb_used, size_for_thumbnail - thumb_used);
1042 buf = core_get_data(thumb_handle);
1043 size = read(fd, buf+thumb_used, size_for_thumbnail - thumb_used);
1044 talk_handle_locked--;
1045 close(fd); 1131 close(fd);
1046 1132
1047 /* ToDo: find audio, skip ID headers and trailers */ 1133 /* ToDo: find audio, skip ID headers and trailers */
1048 1134
1049 if (size > 0) /* Don't play missing clips */ 1135 if (size > 0) /* Don't play missing clips */
1050 { 1136 {
1137 struct queue_entry clip;
1051#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) 1138#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
1052 /* bitswap doesnt yield() */ 1139 /* bitswap doesnt yield() */
1053 bitswap(core_get_data(thumb_handle), size); 1140 bitswap(core_get_data(thumb_handle), size);
@@ -1060,7 +1147,8 @@ static int _talk_file(const char* filename,
1060 talk_queue_lock(); 1147 talk_queue_lock();
1061 thumbnail_buf_used = thumb_used + size; 1148 thumbnail_buf_used = thumb_used + size;
1062 talk_queue_unlock(); 1149 talk_queue_unlock();
1063 queue_clip(voicefile_size + thumb_used, size, true); 1150 clip = (struct queue_entry){ .offset = thumb_used, .length = size, .remaining = size, .type = THUMB_OFFSET };
1151 queue_clip(&clip, true);
1064 } 1152 }
1065 1153
1066 return size; 1154 return size;
@@ -1460,7 +1548,6 @@ void talk_time(const struct tm *tm, bool enqueue)
1460 1548
1461#endif /* CONFIG_RTC */ 1549#endif /* CONFIG_RTC */
1462 1550
1463
1464bool talk_get_debug_data(struct talk_debug_data *data) 1551bool talk_get_debug_data(struct talk_debug_data *data)
1465{ 1552{
1466 char* p_lang = DEFAULT_VOICE_LANG; /* default */ 1553 char* p_lang = DEFAULT_VOICE_LANG; /* default */
@@ -1500,9 +1587,13 @@ bool talk_get_debug_data(struct talk_debug_data *data)
1500 } 1587 }
1501 data->avg_clipsize /= real_clips; 1588 data->avg_clipsize /= real_clips;
1502 data->num_empty_clips = data->num_clips - real_clips; 1589 data->num_empty_clips = data->num_clips - real_clips;
1503 data->memory_allocated = voicefile_size + size_for_thumbnail; 1590 data->memory_allocated = sizeof(commit_buffer) + sizeof(voicefile)
1504 data->memory_used = voicefile_size + thumbnail_buf_used; 1591 + data->num_clips * sizeof(struct clip_entry)
1592 + voicefile_size + size_for_thumbnail;
1593 data->memory_used = data->memory_allocated - size_for_thumbnail + thumbnail_buf_used;
1505#ifdef TALK_PARTIAL_LOAD 1594#ifdef TALK_PARTIAL_LOAD
1595 if (talk_handle > 0)
1596 data->memory_used -= buflib_available(&clip_ctx);
1506 data->cached_clips = cached; 1597 data->cached_clips = cached;
1507 data->cache_hits = cache_hits; 1598 data->cache_hits = cache_hits;
1508 data->cache_misses = cache_misses; 1599 data->cache_misses = cache_misses;