summaryrefslogtreecommitdiff
path: root/apps/talk.c
diff options
context:
space:
mode:
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;