diff options
-rw-r--r-- | apps/talk.c | 655 |
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 | ||
92 | struct clip_entry /* one entry of the index table */ | 94 | struct 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 | ||
120 | static long max_clipsize; /* size of the biggest clip */ | ||
121 | static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */ | ||
122 | static uint8_t clip_age[QUEUE_SIZE]; | ||
123 | #if QUEUE_SIZE > 255 | ||
124 | # error clip_age[] type too small | ||
125 | #endif | ||
126 | static 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 */ |
148 | static int sent; /* how many bytes handed over to playback, owned by ISR */ | 141 | static int sent; /* how many bytes handed over to playback, owned by ISR */ |
149 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ | 142 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ |
150 | static int silence_offset; /* VOICE_PAUSE clip, used for termination */ | ||
151 | static long silence_length; /* length of the VOICE_PAUSE clip */ | ||
152 | static unsigned long lastclip_offset; /* address of latest clip, for silence add */ | ||
153 | static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ | 143 | static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ |
154 | static bool talk_initialized; /* true if talk_init has been called */ | 144 | static bool talk_initialized; /* true if talk_init has been called */ |
155 | static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */ | 145 | static 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 | ||
162 | struct queue_entry /* one entry of the internal queue */ | 152 | struct 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 | ||
167 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | 169 | #ifdef TALK_PARTIAL_LOAD |
170 | static struct buflib_context clip_ctx; | ||
168 | 171 | ||
169 | #define DEFAULT_VOICE_LANG "english" | 172 | struct clip_cache_metadata { |
173 | long tick; | ||
174 | int handle, voice_id; | ||
175 | }; | ||
176 | |||
177 | static int metadata_table_handle; | ||
178 | static unsigned max_clips; | ||
179 | static int cache_hits, cache_misses; | ||
180 | #endif | ||
181 | |||
182 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | ||
183 | static struct queue_entry silence, *last_clip; | ||
170 | 184 | ||
171 | /***************** Private implementation *****************/ | 185 | /***************** Private implementation *****************/ |
172 | 186 | ||
173 | static int thumb_handle; | 187 | static int index_handle, talk_handle, thumb_handle; |
174 | static int talk_handle, talk_handle_locked; | 188 | |
189 | static 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 | |||
201 | static 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 */ |
179 | static bool check_audio_status(void) | 205 | static 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) | |||
190 | static void sync_callback(int handle, bool sync_on) | 218 | static 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 | ||
205 | static int move_callback(int handle, void *current, void *new) | 227 | static 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 | ||
213 | static int clip_shrink_callback(int handle, unsigned hints, void *start, size_t old_size) | 247 | static 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)) | 253 | static 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 | ||
249 | static struct buflib_callbacks clip_ops = { | 301 | |
302 | |||
303 | static 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 | ||
257 | static struct buflib_callbacks thumb_ops = { | 309 | static 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 | |||
266 | static int index_handle, index_handle_locked; | ||
267 | static 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 | |||
275 | static 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 | |||
291 | static struct buflib_callbacks index_ops = { | ||
292 | .move_callback = index_move_callback, | ||
293 | .shrink_callback = index_shrink_callback, | ||
294 | }; | ||
295 | |||
296 | static int open_voicefile(void) | 315 | static 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 */ | 332 | static int id2index(int id) |
314 | static 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 | ||
353 | static 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 */ | ||
379 | static 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 | ||
423 | static bool load_index_table(int fd, const struct voicefile_header *hdr) | 471 | static 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 |
482 | static bool load_data(int fd, ssize_t size_to_read) | 529 | static 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 */ | ||
647 | static unsigned char commit_buffer[32<<10]; | ||
648 | |||
649 | static 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 | |||
677 | static 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 */ |
591 | static void mp3_callback(const void** start, size_t* size) | 686 | static 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 | ||
620 | re_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 */ |
744 | static void queue_clip(unsigned long clip_offset, long size, bool enqueue) | 821 | static 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 */ |
916 | int talk_id(int32_t id, bool enqueue) | 1007 | int 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 | |||
1464 | bool talk_get_debug_data(struct talk_debug_data *data) | 1551 | bool 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; |