diff options
Diffstat (limited to 'apps/talk.c')
-rw-r--r-- | apps/talk.c | 761 |
1 files changed, 430 insertions, 331 deletions
diff --git a/apps/talk.c b/apps/talk.c index b94dcf18ee..baf854fce3 100644 --- a/apps/talk.c +++ b/apps/talk.c | |||
@@ -79,8 +79,8 @@ const char* const file_thumbnail_ext = ".talk"; | |||
79 | 79 | ||
80 | #define LOADED_MASK 0x80000000 /* MSB */ | 80 | #define LOADED_MASK 0x80000000 /* MSB */ |
81 | 81 | ||
82 | /* swcodec: cap p_thumnail to MAX_THUMNAIL_BUFSIZE since audio keeps playing | 82 | /* swcodec: cap thumbnail buffer to MAX_THUMNAIL_BUFSIZE since audio keeps |
83 | * while voice | 83 | * playing while voice |
84 | * hwcodec: just use whatever is left in the audiobuffer, music | 84 | * hwcodec: just use whatever is left in the audiobuffer, music |
85 | * playback is impossible => no cap */ | 85 | * playback is impossible => no cap */ |
86 | #if CONFIG_CODEC == SWCODEC | 86 | #if CONFIG_CODEC == SWCODEC |
@@ -95,25 +95,17 @@ struct clip_entry /* one entry of the index table */ | |||
95 | int size; /* size of the clip */ | 95 | int size; /* size of the clip */ |
96 | }; | 96 | }; |
97 | 97 | ||
98 | struct voicefile /* file format of our voice file */ | 98 | struct voicefile_header /* file format of our voice file */ |
99 | { | 99 | { |
100 | int version; /* version of the voicefile */ | 100 | int version; /* version of the voicefile */ |
101 | int target_id; /* the rockbox target the file was made for */ | 101 | int target_id; /* the rockbox target the file was made for */ |
102 | int table; /* offset to index table, (=header size) */ | 102 | int table; /* offset to index table, (=header size) */ |
103 | int id1_max; /* number of "normal" clips contained in above index */ | 103 | int id1_max; /* number of "normal" clips contained in above index */ |
104 | int id2_max; /* number of "voice only" clips contained in above index */ | 104 | int id2_max; /* number of "voice only" clips contained in above index */ |
105 | struct clip_entry index[]; /* followed by the index tables */ | 105 | /* The header is folled by the index tables (n*struct clip_entry), |
106 | /* and finally the mp3 clips, not visible here, bitswapped | 106 | * which is followed by the mp3/speex encoded clip data */ |
107 | for SH based players */ | ||
108 | }; | 107 | }; |
109 | 108 | ||
110 | struct queue_entry /* one entry of the internal queue */ | ||
111 | { | ||
112 | unsigned char* buf; | ||
113 | long len; | ||
114 | }; | ||
115 | |||
116 | |||
117 | /***************** Globals *****************/ | 109 | /***************** Globals *****************/ |
118 | 110 | ||
119 | #if (CONFIG_CODEC == SWCODEC && MEMORYSIZE <= 2) || defined(ONDIO_SERIES) | 111 | #if (CONFIG_CODEC == SWCODEC && MEMORYSIZE <= 2) || defined(ONDIO_SERIES) |
@@ -125,7 +117,6 @@ struct queue_entry /* one entry of the internal queue */ | |||
125 | #endif | 117 | #endif |
126 | 118 | ||
127 | #ifdef TALK_PARTIAL_LOAD | 119 | #ifdef TALK_PARTIAL_LOAD |
128 | static unsigned char *clip_buffer; | ||
129 | static long max_clipsize; /* size of the biggest clip */ | 120 | static long max_clipsize; /* size of the biggest clip */ |
130 | static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */ | 121 | static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */ |
131 | static uint8_t clip_age[QUEUE_SIZE]; | 122 | static uint8_t clip_age[QUEUE_SIZE]; |
@@ -134,16 +125,13 @@ static uint8_t clip_age[QUEUE_SIZE]; | |||
134 | #endif | 125 | #endif |
135 | #endif | 126 | #endif |
136 | 127 | ||
137 | static char* voicebuf; /* root pointer to our buffer */ | ||
138 | static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */ | ||
139 | /* Multiple thumbnails can be loaded back-to-back in this buffer. */ | 128 | /* Multiple thumbnails can be loaded back-to-back in this buffer. */ |
140 | static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in | 129 | static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in |
141 | thumbnail buffer */ | 130 | thumbnail buffer */ |
142 | static long size_for_thumbnail; /* total thumbnail buffer size */ | 131 | static long size_for_thumbnail; /* total thumbnail buffer size */ |
143 | static struct voicefile* p_voicefile; /* loaded voicefile */ | 132 | static struct voicefile_header voicefile; /* loaded voicefile */ |
144 | static bool has_voicefile; /* a voicefile file is present */ | 133 | static bool has_voicefile; /* a voicefile file is present */ |
145 | static bool need_shutup; /* is there possibly any voice playing to be shutup */ | 134 | static bool need_shutup; /* is there possibly any voice playing to be shutup */ |
146 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | ||
147 | static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */ | 135 | static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */ |
148 | static int queue_write; /* write index of queue, by application */ | 136 | static int queue_write; /* write index of queue, by application */ |
149 | static int queue_read; /* read index of queue, by ISR context */ | 137 | static int queue_read; /* read index of queue, by ISR context */ |
@@ -158,18 +146,126 @@ static struct mutex queue_mutex SHAREDBSS_ATTR; | |||
158 | #endif /* CONFIG_CODEC */ | 146 | #endif /* CONFIG_CODEC */ |
159 | static int sent; /* how many bytes handed over to playback, owned by ISR */ | 147 | static int sent; /* how many bytes handed over to playback, owned by ISR */ |
160 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ | 148 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ |
161 | static int filehandle = -1; /* global, so we can keep the file open if needed */ | 149 | static int silence_offset; /* VOICE_PAUSE clip, used for termination */ |
162 | static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */ | 150 | static long silence_length; /* length of the VOICE_PAUSE clip */ |
163 | static long silence_len; /* length of the VOICE_PAUSE clip */ | 151 | static unsigned long lastclip_offset; /* address of latest clip, for silence add */ |
164 | static unsigned char* p_lastclip; /* address of latest clip, for silence add */ | ||
165 | static unsigned long voicefile_size = 0; /* size of the loaded voice file */ | ||
166 | static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ | 152 | static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ |
167 | static bool talk_initialized; /* true if talk_init has been called */ | 153 | static bool talk_initialized; /* true if talk_init has been called */ |
154 | static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */ | ||
168 | static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */ | 155 | static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */ |
156 | /* size of the loaded voice file | ||
157 | * offsets smaller than this denote a clip from teh voice file, | ||
158 | * offsets larger than this denote a thumbnail clip */ | ||
159 | static unsigned long voicefile_size; | ||
160 | |||
161 | struct queue_entry /* one entry of the internal queue */ | ||
162 | { | ||
163 | int offset, length; | ||
164 | }; | ||
165 | |||
166 | static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ | ||
169 | 167 | ||
170 | 168 | ||
171 | /***************** Private implementation *****************/ | 169 | /***************** Private implementation *****************/ |
172 | 170 | ||
171 | static int thumb_handle; | ||
172 | static int talk_handle, talk_handle_locked; | ||
173 | |||
174 | #if CONFIG_CODEC != SWCODEC | ||
175 | |||
176 | /* on HWCODEC only voice xor audio can be active at a time */ | ||
177 | static bool check_audio_status(void) | ||
178 | { | ||
179 | if (audio_status()) /* busy, buffer in use */ | ||
180 | return false; | ||
181 | /* ensure playback is given up on the buffer */ | ||
182 | audio_hard_stop(); | ||
183 | return true; | ||
184 | } | ||
185 | |||
186 | /* ISR (mp3_callback()) must not run during moving of the clip buffer, | ||
187 | * because the MAS may get out-of-sync */ | ||
188 | static void sync_callback(int handle, bool sync_on) | ||
189 | { | ||
190 | (void) handle; | ||
191 | (void) sync_on; | ||
192 | #if CONFIG_CPU == SH7034 | ||
193 | if (sync_on) | ||
194 | CHCR3 &= ~0x0001; /* disable the DMA (and therefore the interrupt also) */ | ||
195 | else | ||
196 | CHCR3 |= 0x0001; /* re-enable the DMA */ | ||
197 | #endif | ||
198 | } | ||
199 | #else | ||
200 | #define check_audio_status() (true) | ||
201 | #endif | ||
202 | |||
203 | static int move_callback(int handle, void *current, void *new) | ||
204 | { | ||
205 | (void)handle;(void)current;(void)new; | ||
206 | if (UNLIKELY(talk_handle_locked)) | ||
207 | return BUFLIB_CB_CANNOT_MOVE; | ||
208 | return BUFLIB_CB_OK; | ||
209 | } | ||
210 | |||
211 | static int shrink_callback(int handle, unsigned hints, void *start, size_t old_size) | ||
212 | { | ||
213 | (void)start;(void)old_size; | ||
214 | int *h; | ||
215 | if (handle == talk_handle) | ||
216 | h = &talk_handle; | ||
217 | else // if (handle == thumb_handle) | ||
218 | h = &thumb_handle; | ||
219 | |||
220 | if (LIKELY(!talk_handle_locked) | ||
221 | && give_buffer_away | ||
222 | && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK) | ||
223 | { | ||
224 | *h = core_free(handle); | ||
225 | return BUFLIB_CB_OK; | ||
226 | } | ||
227 | return BUFLIB_CB_CANNOT_SHRINK; | ||
228 | } | ||
229 | |||
230 | static struct buflib_callbacks talk_ops = { | ||
231 | .move_callback = move_callback, | ||
232 | #if CONFIG_CODEC != SWCODEC | ||
233 | .sync_callback = sync_callback, | ||
234 | #endif | ||
235 | .shrink_callback = shrink_callback, | ||
236 | }; | ||
237 | |||
238 | |||
239 | static int index_handle, index_handle_locked; | ||
240 | static int index_move_callback(int handle, void *current, void *new) | ||
241 | { | ||
242 | (void)handle;(void)current;(void)new; | ||
243 | if (UNLIKELY(index_handle_locked)) | ||
244 | return BUFLIB_CB_CANNOT_MOVE; | ||
245 | return BUFLIB_CB_OK; | ||
246 | } | ||
247 | |||
248 | static int index_shrink_callback(int handle, unsigned hints, void *start, size_t old_size) | ||
249 | { | ||
250 | (void)start;(void)old_size; | ||
251 | if (LIKELY(!index_handle_locked) | ||
252 | && give_buffer_away | ||
253 | && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK) | ||
254 | { | ||
255 | index_handle = core_free(handle); | ||
256 | /* the clip buffer isn't usable without index table */ | ||
257 | if (LIKELY(!talk_handle_locked)) | ||
258 | talk_handle = core_free(talk_handle); | ||
259 | return BUFLIB_CB_OK; | ||
260 | } | ||
261 | return BUFLIB_CB_CANNOT_SHRINK; | ||
262 | } | ||
263 | |||
264 | static struct buflib_callbacks index_ops = { | ||
265 | .move_callback = index_move_callback, | ||
266 | .shrink_callback = index_shrink_callback, | ||
267 | }; | ||
268 | |||
173 | static int open_voicefile(void) | 269 | static int open_voicefile(void) |
174 | { | 270 | { |
175 | char buf[64]; | 271 | char buf[64]; |
@@ -188,38 +284,41 @@ static int open_voicefile(void) | |||
188 | 284 | ||
189 | 285 | ||
190 | /* fetch a clip from the voice file */ | 286 | /* fetch a clip from the voice file */ |
191 | static unsigned char* get_clip(long id, long* p_size) | 287 | static int get_clip(long id, long* p_size) |
192 | { | 288 | { |
193 | long clipsize; | 289 | int retval = -1; |
194 | unsigned char* clipbuf; | 290 | struct clip_entry* clipbuf; |
195 | 291 | size_t clipsize; | |
292 | |||
196 | if (id > VOICEONLY_DELIMITER) | 293 | if (id > VOICEONLY_DELIMITER) |
197 | { /* voice-only entries use the second part of the table. | 294 | { /* voice-only entries use the second part of the table. |
198 | The first string comes after VOICEONLY_DELIMITER so we need to | 295 | The first string comes after VOICEONLY_DELIMITER so we need to |
199 | substract VOICEONLY_DELIMITER + 1 */ | 296 | substract VOICEONLY_DELIMITER + 1 */ |
200 | id -= VOICEONLY_DELIMITER + 1; | 297 | id -= VOICEONLY_DELIMITER + 1; |
201 | if (id >= p_voicefile->id2_max) | 298 | if (id >= voicefile.id2_max) |
202 | return NULL; /* must be newer than we have */ | 299 | return -1; /* must be newer than we have */ |
203 | id += p_voicefile->id1_max; /* table 2 is behind table 1 */ | 300 | id += voicefile.id1_max; /* table 2 is behind table 1 */ |
204 | } | 301 | } |
205 | else | 302 | else |
206 | { /* normal use of the first table */ | 303 | { /* normal use of the first table */ |
207 | if (id >= p_voicefile->id1_max) | 304 | if (id >= voicefile.id1_max) |
208 | return NULL; /* must be newer than we have */ | 305 | return -1; /* must be newer than we have */ |
209 | } | 306 | } |
210 | 307 | ||
211 | clipsize = p_voicefile->index[id].size; | 308 | clipbuf = core_get_data(index_handle); |
309 | clipsize = clipbuf[id].size; | ||
212 | if (clipsize == 0) /* clip not included in voicefile */ | 310 | if (clipsize == 0) /* clip not included in voicefile */ |
213 | return NULL; | 311 | return -1; |
214 | 312 | ||
215 | #ifndef TALK_PARTIAL_LOAD | 313 | #ifndef TALK_PARTIAL_LOAD |
216 | clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset; | 314 | retval = clipbuf[id].offset; |
217 | #endif | ||
218 | 315 | ||
219 | #ifdef TALK_PARTIAL_LOAD | 316 | #else |
220 | if (!(clipsize & LOADED_MASK)) | 317 | if (!(clipsize & LOADED_MASK)) |
221 | { /* clip needs loading */ | 318 | { /* clip needs loading */ |
222 | int idx = 0; | 319 | ssize_t ret; |
320 | int fd, idx = 0; | ||
321 | unsigned char *voicebuf; | ||
223 | if (id == VOICE_PAUSE) { | 322 | if (id == VOICE_PAUSE) { |
224 | idx = QUEUE_SIZE; /* we keep VOICE_PAUSE loaded */ | 323 | idx = QUEUE_SIZE; /* we keep VOICE_PAUSE loaded */ |
225 | } else { | 324 | } else { |
@@ -243,18 +342,29 @@ static unsigned char* get_clip(long id, long* p_size) | |||
243 | } | 342 | } |
244 | clip_age[idx] = 0; /* reset clip's age */ | 343 | clip_age[idx] = 0; /* reset clip's age */ |
245 | } | 344 | } |
246 | clipbuf = clip_buffer + idx * max_clipsize; | 345 | retval = idx * max_clipsize; |
346 | fd = open_voicefile(); | ||
347 | if (fd < 0) | ||
348 | return -1; /* open error */ | ||
349 | |||
350 | talk_handle_locked++; | ||
351 | voicebuf = core_get_data(talk_handle); | ||
352 | clipbuf = core_get_data(index_handle); | ||
353 | lseek(fd, clipbuf[id].offset, SEEK_SET); | ||
354 | ret = read(fd, &voicebuf[retval], clipsize); | ||
355 | close(fd); | ||
356 | talk_handle_locked--; | ||
247 | 357 | ||
248 | lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET); | 358 | if (ret < 0 || clipsize != (size_t)ret) |
249 | if (read(filehandle, clipbuf, clipsize) != clipsize) | 359 | return -1; /* read error */ |
250 | return NULL; /* read error */ | ||
251 | 360 | ||
252 | p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */ | 361 | clipbuf = core_get_data(index_handle); |
362 | clipbuf[id].size |= LOADED_MASK; /* mark as loaded */ | ||
253 | 363 | ||
254 | if (id != VOICE_PAUSE) { | 364 | if (id != VOICE_PAUSE) { |
255 | if (buffered_id[idx] >= 0) { | 365 | if (buffered_id[idx] >= 0) { |
256 | /* mark previously loaded clip as unloaded */ | 366 | /* mark previously loaded clip as unloaded */ |
257 | p_voicefile->index[buffered_id[idx]].size &= ~LOADED_MASK; | 367 | clipbuf[buffered_id[idx]].size &= ~LOADED_MASK; |
258 | } | 368 | } |
259 | buffered_id[idx] = id; | 369 | buffered_id[idx] = id; |
260 | } | 370 | } |
@@ -262,14 +372,13 @@ static unsigned char* get_clip(long id, long* p_size) | |||
262 | else | 372 | else |
263 | { /* clip is in memory already */ | 373 | { /* clip is in memory already */ |
264 | /* Find where it was loaded */ | 374 | /* Find where it was loaded */ |
265 | clipbuf = clip_buffer; | ||
266 | if (id == VOICE_PAUSE) { | 375 | if (id == VOICE_PAUSE) { |
267 | clipbuf += QUEUE_SIZE * max_clipsize; | 376 | retval = QUEUE_SIZE * max_clipsize; |
268 | } else { | 377 | } else { |
269 | int idx; | 378 | int idx; |
270 | for (idx=0; idx<QUEUE_SIZE; idx++) | 379 | for (idx=0; idx<QUEUE_SIZE; idx++) |
271 | if (buffered_id[idx] == id) { | 380 | if (buffered_id[idx] == id) { |
272 | clipbuf += idx * max_clipsize; | 381 | retval = idx * max_clipsize; |
273 | clip_age[idx] = 0; /* reset clip's age */ | 382 | clip_age[idx] = 0; /* reset clip's age */ |
274 | break; | 383 | break; |
275 | } | 384 | } |
@@ -279,153 +388,200 @@ static unsigned char* get_clip(long id, long* p_size) | |||
279 | #endif /* TALK_PARTIAL_LOAD */ | 388 | #endif /* TALK_PARTIAL_LOAD */ |
280 | 389 | ||
281 | *p_size = clipsize; | 390 | *p_size = clipsize; |
282 | return clipbuf; | 391 | return retval; |
283 | } | 392 | } |
284 | 393 | ||
285 | 394 | static bool load_index_table(int fd, const struct voicefile_header *hdr) | |
286 | /* load the voice file into the mp3 buffer */ | ||
287 | static void load_voicefile(bool probe, char* buf, size_t bufsize) | ||
288 | { | 395 | { |
289 | union voicebuf { | 396 | ssize_t ret; |
290 | unsigned char* buf; | 397 | struct clip_entry *buf; |
291 | struct voicefile* file; | ||
292 | }; | ||
293 | union voicebuf voicebuf; | ||
294 | 398 | ||
295 | size_t load_size, alloc_size; | 399 | if (index_handle > 0) /* nothing to do? */ |
296 | ssize_t got_size; | 400 | return true; |
297 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
298 | int i; | ||
299 | #endif | ||
300 | 401 | ||
301 | if (!probe) | 402 | ssize_t alloc_size = (hdr->id1_max + hdr->id2_max) * sizeof(struct clip_entry); |
302 | filehandle = open_voicefile(); | 403 | index_handle = core_alloc_ex("voice index", alloc_size, &index_ops); |
303 | if (filehandle < 0) /* failed to open */ | 404 | if (index_handle < 0) |
304 | goto load_err; | 405 | return false; |
305 | 406 | ||
306 | voicebuf.buf = buf; | 407 | index_handle_locked++; |
307 | if (!voicebuf.buf) | 408 | buf = core_get_data(index_handle); |
308 | goto load_err; | 409 | ret = read(fd, buf, alloc_size); |
309 | 410 | ||
310 | #ifdef TALK_PARTIAL_LOAD | 411 | #ifndef TALK_PARTIAL_LOAD |
311 | /* load only the header for now */ | 412 | int clips_offset, num_clips; |
312 | load_size = sizeof(struct voicefile); | 413 | /* adjust the offsets of the clips, they are relative to the file |
313 | #else | 414 | * TALK_PARTUAL_LOAD needs the file offset instead as it loads |
314 | /* load the entire file */ | 415 | * the clips later */ |
315 | load_size = filesize(filehandle); | 416 | clips_offset = hdr->table; |
417 | num_clips = hdr->id1_max + hdr->id2_max; | ||
418 | clips_offset += num_clips * sizeof(struct clip_entry); /* skip index */ | ||
419 | #endif | ||
420 | if (ret == alloc_size) | ||
421 | for (int i = 0; i < hdr->id1_max + hdr->id2_max; i++) | ||
422 | { | ||
423 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
424 | structec_convert(&buf[i], "ll", 1, true); | ||
316 | #endif | 425 | #endif |
317 | if (load_size > bufsize) /* won't fit? */ | 426 | #ifndef TALK_PARTIAL_LOAD |
318 | goto load_err; | 427 | buf[i].offset -= clips_offset; |
428 | #endif | ||
429 | } | ||
430 | |||
431 | index_handle_locked--; | ||
319 | 432 | ||
320 | got_size = read(filehandle, voicebuf.buf, load_size); | 433 | if (ret != alloc_size) |
321 | if (got_size != (ssize_t)load_size /* failure */) | 434 | index_handle = core_free(index_handle); |
322 | goto load_err; | ||
323 | 435 | ||
324 | alloc_size = load_size; | 436 | return ret == alloc_size; |
437 | } | ||
438 | |||
439 | static bool load_header(int fd, struct voicefile_header *hdr) | ||
440 | { | ||
441 | ssize_t got_size = read(fd, hdr, sizeof(*hdr)); | ||
442 | if (got_size != sizeof(*hdr)) | ||
443 | return false; | ||
325 | 444 | ||
326 | #ifdef ROCKBOX_LITTLE_ENDIAN | 445 | #ifdef ROCKBOX_LITTLE_ENDIAN |
327 | logf("Byte swapping voice file"); | 446 | logf("Byte swapping voice file"); |
328 | structec_convert(voicebuf.buf, "lllll", 1, true); | 447 | structec_convert(&voicefile, "lllll", 1, true); |
329 | #endif | 448 | #endif |
449 | return true; | ||
450 | } | ||
330 | 451 | ||
331 | /* format check */ | 452 | #ifndef TALK_PARTIAL_LOAD |
332 | if (voicebuf.file->table == sizeof(struct voicefile)) | 453 | static bool load_data(int fd, ssize_t size_to_read) |
333 | { | 454 | { |
334 | p_voicefile = voicebuf.file; | 455 | unsigned char *buf; |
335 | 456 | ssize_t ret; | |
336 | if (p_voicefile->version != VOICE_VERSION || | ||
337 | p_voicefile->target_id != TARGET_ID) | ||
338 | { | ||
339 | logf("Incompatible voice file"); | ||
340 | goto load_err; | ||
341 | } | ||
342 | } | ||
343 | else | ||
344 | goto load_err; | ||
345 | 457 | ||
346 | #ifdef TALK_PARTIAL_LOAD | 458 | if (size_to_read < 0) |
347 | /* load the index table, now that we know its size from the header */ | 459 | return false; |
348 | load_size = (p_voicefile->id1_max + p_voicefile->id2_max) | ||
349 | * sizeof(struct clip_entry); | ||
350 | 460 | ||
351 | if (load_size > bufsize) /* won't fit? */ | 461 | talk_handle = core_alloc_ex("voice data", size_to_read, &talk_ops); |
352 | goto load_err; | 462 | if (talk_handle < 0) |
463 | return false; | ||
353 | 464 | ||
354 | got_size = read(filehandle, &p_voicefile->index[0], load_size); | 465 | talk_handle_locked++; |
355 | if (got_size != (ssize_t)load_size) /* read error */ | 466 | buf = core_get_data(talk_handle); |
356 | goto load_err; | 467 | ret = read(fd, buf, size_to_read); |
468 | talk_handle_locked--; | ||
357 | 469 | ||
358 | alloc_size += load_size; | 470 | if (ret != size_to_read) |
359 | #else | 471 | talk_handle = core_free(talk_handle); |
360 | close(filehandle); | ||
361 | filehandle = -1; | ||
362 | #endif /* TALK_PARTIAL_LOAD */ | ||
363 | 472 | ||
364 | #ifdef ROCKBOX_LITTLE_ENDIAN | 473 | return ret == size_to_read; |
365 | for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++) | 474 | } |
366 | structec_convert(&p_voicefile->index[i], "ll", 1, true); | ||
367 | #endif | 475 | #endif |
368 | 476 | ||
369 | #ifdef TALK_PARTIAL_LOAD | 477 | static bool alloc_thumbnail_buf(void) |
370 | clip_buffer = (unsigned char *) p_voicefile + p_voicefile->table; | 478 | { |
371 | unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max; | 479 | int handle; |
372 | clip_buffer += clips * sizeof(struct clip_entry); /* skip index */ | 480 | size_t size; |
481 | if (thumb_handle > 0) | ||
482 | return true; /* nothing to do? */ | ||
483 | #if CONFIG_CODEC == SWCODEC | ||
484 | /* try to allocate the max. first, and take whatever we can get if that | ||
485 | * fails */ | ||
486 | size = MAX_THUMBNAIL_BUFSIZE; | ||
487 | handle = core_alloc_ex("voice thumb", MAX_THUMBNAIL_BUFSIZE, &talk_ops); | ||
488 | if (handle < 0) | ||
489 | { | ||
490 | size = core_allocatable(); | ||
491 | handle = core_alloc_ex("voice thumb", size, &talk_ops); | ||
492 | } | ||
493 | #else | ||
494 | /* on HWCODEC, just use the rest of the remaining buffer, | ||
495 | * normal playback cannot happen anyway */ | ||
496 | handle = core_alloc_maximum("voice thumb", &size, &talk_ops); | ||
373 | #endif | 497 | #endif |
374 | if (!probe) { | 498 | thumb_handle = handle; |
375 | /* make sure to have the silence clip, if available */ | 499 | size_for_thumbnail = (handle > 0) ? size : 0; |
376 | p_silence = get_clip(VOICE_PAUSE, &silence_len); | 500 | return handle > 0; |
501 | } | ||
502 | |||
503 | /* load the voice file into the mp3 buffer */ | ||
504 | static bool load_voicefile_index(int fd) | ||
505 | { | ||
506 | if (fd < 0) /* failed to open */ | ||
507 | return false; | ||
508 | |||
509 | /* load the header first */ | ||
510 | if (!load_header(fd, &voicefile)) | ||
511 | return false; | ||
512 | |||
513 | /* format check */ | ||
514 | if (voicefile.table == sizeof(struct voicefile_header)) | ||
515 | { | ||
516 | if (voicefile.version == VOICE_VERSION && | ||
517 | voicefile.target_id == TARGET_ID) | ||
518 | { | ||
519 | if (load_index_table(fd, &voicefile)) | ||
520 | return true; | ||
521 | } | ||
377 | } | 522 | } |
378 | 523 | ||
524 | logf("Incompatible voice file"); | ||
525 | return false; | ||
526 | } | ||
527 | |||
528 | static bool load_voicefile_data(int fd, size_t max_size) | ||
529 | { | ||
379 | #ifdef TALK_PARTIAL_LOAD | 530 | #ifdef TALK_PARTIAL_LOAD |
380 | alloc_size += silence_len + QUEUE_SIZE; | 531 | /* just allocate, populate on an as-needed basis later */ |
532 | talk_handle = core_alloc_ex("voice data", max_size, &talk_ops); | ||
533 | if (talk_handle < 0) | ||
534 | goto load_err_free; | ||
535 | #else | ||
536 | size_t load_size, clips_size; | ||
537 | /* load the entire file into memory */ | ||
538 | clips_size = (voicefile.id1_max+voicefile.id2_max) * sizeof(struct clip_entry); | ||
539 | load_size = max_size - voicefile.table - clips_size; | ||
540 | if (!load_data(fd, load_size)) | ||
541 | goto load_err_free; | ||
381 | #endif | 542 | #endif |
382 | 543 | ||
383 | if (alloc_size > bufsize) | 544 | /* make sure to have the silence clip, if available |
384 | goto load_err; | 545 | * return value can be cached globally even for TALK_PARTIAL_LOAD because |
546 | * the VOICE_PAUSE clip is specially handled */ | ||
547 | silence_offset = get_clip(VOICE_PAUSE, &silence_length); | ||
385 | 548 | ||
386 | /* now move p_thumbnail behind the voice clip buffer */ | 549 | /* not an error if this fails here, might try again when the |
387 | p_thumbnail = voicebuf.buf + alloc_size; | 550 | * actual thumbnails are attempted to be played back */ |
388 | p_thumbnail += (long)p_thumbnail % 2; /* 16-bit align */ | 551 | alloc_thumbnail_buf(); |
389 | size_for_thumbnail = voicebuf.buf + bufsize - p_thumbnail; | ||
390 | #if CONFIG_CODEC == SWCODEC | ||
391 | size_for_thumbnail = MIN(size_for_thumbnail, MAX_THUMBNAIL_BUFSIZE); | ||
392 | #endif | ||
393 | if (size_for_thumbnail <= 0) | ||
394 | p_thumbnail = NULL; | ||
395 | 552 | ||
396 | return; | 553 | return true; |
397 | load_err: | 554 | |
398 | p_voicefile = NULL; | 555 | load_err_free: |
399 | has_voicefile = false; /* don't try again */ | 556 | index_handle = core_free(index_handle); |
400 | if (filehandle >= 0) | 557 | return false; |
401 | { | ||
402 | close(filehandle); | ||
403 | filehandle = -1; | ||
404 | } | ||
405 | return; | ||
406 | } | 558 | } |
407 | 559 | ||
408 | 560 | ||
409 | /* called in ISR context (on HWCODEC) if mp3 data got consumed */ | 561 | /* called in ISR context (on HWCODEC) if mp3 data got consumed */ |
410 | static void mp3_callback(const void** start, size_t* size) | 562 | static void mp3_callback(const void** start, size_t* size) |
411 | { | 563 | { |
412 | queue[queue_read].len -= sent; /* we completed this */ | 564 | queue[queue_read].length -= sent; /* we completed this */ |
413 | queue[queue_read].buf += sent; | 565 | queue[queue_read].offset += sent; |
414 | 566 | ||
415 | if (queue[queue_read].len > 0) /* current clip not finished? */ | 567 | if (queue[queue_read].length > 0) /* current clip not finished? */ |
416 | { /* feed the next 64K-1 chunk */ | 568 | { /* feed the next 64K-1 chunk */ |
569 | int offset; | ||
417 | #if CONFIG_CODEC != SWCODEC | 570 | #if CONFIG_CODEC != SWCODEC |
418 | sent = MIN(queue[queue_read].len, 0xFFFF); | 571 | sent = MIN(queue[queue_read].length, 0xFFFF); |
419 | #else | 572 | #else |
420 | sent = queue[queue_read].len; | 573 | sent = queue[queue_read].length; |
421 | #endif | 574 | #endif |
422 | *start = queue[queue_read].buf; | 575 | offset = queue[queue_read].offset; |
576 | if ((unsigned long)offset >= voicefile_size) | ||
577 | *start = core_get_data(thumb_handle) + offset - voicefile_size; | ||
578 | else | ||
579 | *start = core_get_data(talk_handle) + offset; | ||
423 | *size = sent; | 580 | *size = sent; |
424 | return; | 581 | return; |
425 | } | 582 | } |
426 | talk_queue_lock(); | 583 | talk_queue_lock(); |
427 | if(p_thumbnail | 584 | if(thumb_handle && (unsigned long)queue[queue_read].offset == voicefile_size+thumbnail_buf_used) |
428 | && queue[queue_read].buf == p_thumbnail +thumbnail_buf_used) | ||
429 | thumbnail_buf_used = 0; | 585 | thumbnail_buf_used = 0; |
430 | if (sent > 0) /* go to next entry */ | 586 | if (sent > 0) /* go to next entry */ |
431 | { | 587 | { |
@@ -436,24 +592,31 @@ re_check: | |||
436 | 592 | ||
437 | if (QUEUE_LEVEL != 0) /* queue is not empty? */ | 593 | if (QUEUE_LEVEL != 0) /* queue is not empty? */ |
438 | { /* start next clip */ | 594 | { /* start next clip */ |
595 | unsigned char *buf; | ||
439 | #if CONFIG_CODEC != SWCODEC | 596 | #if CONFIG_CODEC != SWCODEC |
440 | sent = MIN(queue[queue_read].len, 0xFFFF); | 597 | sent = MIN(queue[queue_read].length, 0xFFFF); |
441 | #else | 598 | #else |
442 | sent = queue[queue_read].len; | 599 | sent = queue[queue_read].length; |
443 | #endif | 600 | #endif |
444 | *start = p_lastclip = queue[queue_read].buf; | 601 | lastclip_offset = queue[queue_read].offset; |
602 | /* offsets larger than voicefile_size denote thumbnail clips */ | ||
603 | if (lastclip_offset >= voicefile_size) | ||
604 | buf = core_get_data(thumb_handle) + lastclip_offset - voicefile_size; | ||
605 | else | ||
606 | buf = core_get_data(talk_handle) + lastclip_offset; | ||
607 | *start = buf; | ||
445 | *size = sent; | 608 | *size = sent; |
446 | curr_hd[0] = p_lastclip[1]; | 609 | curr_hd[0] = buf[1]; |
447 | curr_hd[1] = p_lastclip[2]; | 610 | curr_hd[1] = buf[2]; |
448 | curr_hd[2] = p_lastclip[3]; | 611 | curr_hd[2] = buf[3]; |
449 | } | 612 | } |
450 | else if (p_silence != NULL /* silence clip available */ | 613 | else if (silence_offset > 0 /* silence clip available */ |
451 | && p_lastclip != p_silence /* previous clip wasn't silence */ | 614 | && lastclip_offset != (unsigned long)silence_offset /* previous clip wasn't silence */ |
452 | && !(p_lastclip >= p_thumbnail /* ..or thumbnail */ | 615 | && !(lastclip_offset >= voicefile_size /* ..or thumbnail */ |
453 | && p_lastclip < p_thumbnail +size_for_thumbnail)) | 616 | && lastclip_offset < voicefile_size +size_for_thumbnail)) |
454 | { /* add silence clip when queue runs empty playing a voice clip */ | 617 | { /* add silence clip when queue runs empty playing a voice clip */ |
455 | queue[queue_write].buf = p_silence; | 618 | queue[queue_write].offset = silence_offset; |
456 | queue[queue_write].len = silence_len; | 619 | queue[queue_write].length = silence_length; |
457 | queue_write = (queue_write + 1) & QUEUE_MASK; | 620 | queue_write = (queue_write + 1) & QUEUE_MASK; |
458 | 621 | ||
459 | goto re_check; | 622 | goto re_check; |
@@ -461,6 +624,7 @@ re_check: | |||
461 | else | 624 | else |
462 | { | 625 | { |
463 | *size = 0; /* end of data */ | 626 | *size = 0; /* end of data */ |
627 | talk_handle_locked--; | ||
464 | } | 628 | } |
465 | talk_queue_unlock(); | 629 | talk_queue_unlock(); |
466 | } | 630 | } |
@@ -478,6 +642,8 @@ void talk_force_shutup(void) | |||
478 | unsigned char* pos; | 642 | unsigned char* pos; |
479 | unsigned char* search; | 643 | unsigned char* search; |
480 | unsigned char* end; | 644 | unsigned char* end; |
645 | int len; | ||
646 | unsigned clip_offset; | ||
481 | if (QUEUE_LEVEL == 0) /* has ended anyway */ | 647 | if (QUEUE_LEVEL == 0) /* has ended anyway */ |
482 | return; | 648 | return; |
483 | 649 | ||
@@ -486,13 +652,17 @@ void talk_force_shutup(void) | |||
486 | #endif /* CONFIG_CPU == SH7034 */ | 652 | #endif /* CONFIG_CPU == SH7034 */ |
487 | /* search next frame boundary and continue up to there */ | 653 | /* search next frame boundary and continue up to there */ |
488 | pos = search = mp3_get_pos(); | 654 | pos = search = mp3_get_pos(); |
489 | end = queue[queue_read].buf + queue[queue_read].len; | 655 | clip_offset = queue[queue_read].offset; |
656 | if (clip_offset >= voicefile_size) | ||
657 | end = core_get_data(thumb_handle) + clip_offset - voicefile_size; | ||
658 | else | ||
659 | end = core_get_data(talk_handle) + clip_offset; | ||
660 | len = queue[queue_read].length; | ||
490 | 661 | ||
491 | if (pos >= queue[queue_read].buf | 662 | if (pos >= end && pos <= (end+len)) /* really our clip? */ |
492 | && pos <= end) /* really our clip? */ | ||
493 | { /* (for strange reasons this isn't nesessarily the case) */ | 663 | { /* (for strange reasons this isn't nesessarily the case) */ |
494 | /* find the next frame boundary */ | 664 | /* find the next frame boundary */ |
495 | while (search < end) /* search the remaining data */ | 665 | while (search < (end+len)) /* search the remaining data */ |
496 | { | 666 | { |
497 | if (*search++ != 0xFF) /* quick search for frame sync byte */ | 667 | if (*search++ != 0xFF) /* quick search for frame sync byte */ |
498 | continue; /* (this does the majority of the job) */ | 668 | continue; /* (this does the majority of the job) */ |
@@ -512,7 +682,7 @@ void talk_force_shutup(void) | |||
512 | sent = search-pos; | 682 | sent = search-pos; |
513 | 683 | ||
514 | queue_write = (queue_read + 1) & QUEUE_MASK; /* will be empty after next callback */ | 684 | queue_write = (queue_read + 1) & QUEUE_MASK; /* will be empty after next callback */ |
515 | queue[queue_read].len = sent; /* current one ends after this */ | 685 | queue[queue_read].length = sent; /* current one ends after this */ |
516 | 686 | ||
517 | #if CONFIG_CPU == SH7034 | 687 | #if CONFIG_CPU == SH7034 |
518 | DTCR3 = sent; /* let the DMA finish this frame */ | 688 | DTCR3 = sent; /* let the DMA finish this frame */ |
@@ -528,6 +698,7 @@ void talk_force_shutup(void) | |||
528 | mp3_play_stop(); | 698 | mp3_play_stop(); |
529 | talk_queue_lock(); | 699 | talk_queue_lock(); |
530 | queue_write = queue_read = 0; /* reset the queue */ | 700 | queue_write = queue_read = 0; /* reset the queue */ |
701 | talk_handle_locked = MAX(talk_handle_locked-1, 0); | ||
531 | thumbnail_buf_used = 0; | 702 | thumbnail_buf_used = 0; |
532 | talk_queue_unlock(); | 703 | talk_queue_unlock(); |
533 | need_shutup = false; | 704 | need_shutup = false; |
@@ -541,8 +712,9 @@ void talk_shutup(void) | |||
541 | } | 712 | } |
542 | 713 | ||
543 | /* schedule a clip, at the end or discard the existing queue */ | 714 | /* schedule a clip, at the end or discard the existing queue */ |
544 | static void queue_clip(unsigned char* buf, long size, bool enqueue) | 715 | static void queue_clip(unsigned long clip_offset, long size, bool enqueue) |
545 | { | 716 | { |
717 | unsigned char *buf; | ||
546 | int queue_level; | 718 | int queue_level; |
547 | 719 | ||
548 | if (!enqueue) | 720 | if (!enqueue) |
@@ -562,20 +734,25 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue) | |||
562 | 734 | ||
563 | if (queue_level < QUEUE_SIZE - 1) /* space left? */ | 735 | if (queue_level < QUEUE_SIZE - 1) /* space left? */ |
564 | { | 736 | { |
565 | queue[queue_write].buf = buf; /* populate an entry */ | 737 | queue[queue_write].offset = clip_offset; /* populate an entry */ |
566 | queue[queue_write].len = size; | 738 | queue[queue_write].length = size; |
567 | queue_write = (queue_write + 1) & QUEUE_MASK; | 739 | queue_write = (queue_write + 1) & QUEUE_MASK; |
568 | } | 740 | } |
569 | talk_queue_unlock(); | 741 | talk_queue_unlock(); |
570 | 742 | ||
571 | if (queue_level == 0) | 743 | if (queue_level == 0) |
572 | { /* queue was empty, we have to do the initial start */ | 744 | { /* queue was empty, we have to do the initial start */ |
573 | p_lastclip = buf; | 745 | lastclip_offset = clip_offset; |
574 | #if CONFIG_CODEC != SWCODEC | 746 | #if CONFIG_CODEC != SWCODEC |
575 | sent = MIN(size, 0xFFFF); /* DMA can do no more */ | 747 | sent = MIN(size, 0xFFFF); /* DMA can do no more */ |
576 | #else | 748 | #else |
577 | sent = size; | 749 | sent = size; |
578 | #endif | 750 | #endif |
751 | talk_handle_locked++; | ||
752 | if (clip_offset >= voicefile_size) | ||
753 | buf = core_get_data(thumb_handle) + clip_offset - voicefile_size; | ||
754 | else | ||
755 | buf = core_get_data(talk_handle) + clip_offset; | ||
579 | mp3_play_data(buf, sent, mp3_callback); | 756 | mp3_play_data(buf, sent, mp3_callback); |
580 | curr_hd[0] = buf[1]; | 757 | curr_hd[0] = buf[1]; |
581 | curr_hd[1] = buf[2]; | 758 | curr_hd[1] = buf[2]; |
@@ -594,53 +771,11 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue) | |||
594 | return; | 771 | return; |
595 | } | 772 | } |
596 | 773 | ||
597 | static void alloc_thumbnail_buf(void) | ||
598 | { | ||
599 | /* use the audio buffer now, need to release before loading a voice */ | ||
600 | p_thumbnail = voicebuf; | ||
601 | #if CONFIG_CODEC == SWCODEC | ||
602 | size_for_thumbnail = MAX_THUMBNAIL_BUFSIZE; | ||
603 | #endif | ||
604 | thumbnail_buf_used = 0; | ||
605 | } | ||
606 | |||
607 | /* common code for talk_init() and talk_buffer_steal() */ | ||
608 | static void reset_state(void) | ||
609 | { | ||
610 | queue_write = queue_read = 0; /* reset the queue */ | ||
611 | p_voicefile = NULL; /* indicate no voicefile (trashed) */ | ||
612 | p_thumbnail = NULL; /* no thumbnails either */ | ||
613 | |||
614 | #ifdef TALK_PARTIAL_LOAD | ||
615 | int i; | ||
616 | for(i=0; i<QUEUE_SIZE; i++) | ||
617 | buffered_id[i] = -1; | ||
618 | #endif | ||
619 | |||
620 | p_silence = NULL; /* pause clip not accessible */ | ||
621 | voicebuf = NULL; /* voice buffer is gone */ | ||
622 | } | ||
623 | |||
624 | #if CONFIG_CODEC == SWCODEC | ||
625 | static bool restore_state(void) | ||
626 | { | ||
627 | if (!voicebuf) | ||
628 | { | ||
629 | size_t size; | ||
630 | audio_restore_playback(AUDIO_WANT_VOICE); | ||
631 | voicebuf = audio_get_buffer(true, &size); | ||
632 | audio_get_buffer(false, &size); | ||
633 | } | ||
634 | |||
635 | return !!voicebuf; | ||
636 | } | ||
637 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
638 | |||
639 | |||
640 | /***************** Public implementation *****************/ | 774 | /***************** Public implementation *****************/ |
641 | 775 | ||
642 | void talk_init(void) | 776 | void talk_init(void) |
643 | { | 777 | { |
778 | int filehandle; | ||
644 | talk_temp_disable_count = 0; | 779 | talk_temp_disable_count = 0; |
645 | if (talk_initialized && !strcasecmp(last_lang, global_settings.lang_file)) | 780 | if (talk_initialized && !strcasecmp(last_lang, global_settings.lang_file)) |
646 | { | 781 | { |
@@ -648,14 +783,6 @@ void talk_init(void) | |||
648 | return; | 783 | return; |
649 | } | 784 | } |
650 | 785 | ||
651 | #if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD) | ||
652 | if (filehandle >= 0) | ||
653 | { | ||
654 | close(filehandle); | ||
655 | filehandle = -1; | ||
656 | } | ||
657 | #endif | ||
658 | |||
659 | #if CONFIG_CODEC == SWCODEC | 786 | #if CONFIG_CODEC == SWCODEC |
660 | if(!talk_initialized) | 787 | if(!talk_initialized) |
661 | mutex_init(&queue_mutex); | 788 | mutex_init(&queue_mutex); |
@@ -665,138 +792,118 @@ void talk_init(void) | |||
665 | strlcpy((char *)last_lang, (char *)global_settings.lang_file, | 792 | strlcpy((char *)last_lang, (char *)global_settings.lang_file, |
666 | MAX_FILENAME); | 793 | MAX_FILENAME); |
667 | 794 | ||
795 | /* reset some states */ | ||
796 | queue_write = queue_read = 0; /* reset the queue */ | ||
797 | memset(&voicefile, 0, sizeof(voicefile)); | ||
798 | |||
799 | #ifdef TALK_PARTIAL_LOAD | ||
800 | for(int i=0; i<QUEUE_SIZE; i++) | ||
801 | buffered_id[i] = -1; | ||
802 | #endif | ||
803 | |||
804 | silence_offset = -1; /* pause clip not accessible */ | ||
805 | voicefile_size = has_voicefile = 0; | ||
806 | /* need to free these as their size depends on the voice file, and | ||
807 | * this function is called when the talk voice file changes */ | ||
808 | if (index_handle > 0) index_handle = core_free(index_handle); | ||
809 | if (talk_handle > 0) talk_handle = core_free(talk_handle); | ||
810 | /* don't free thumb handle, it doesn't depend on the actual voice file | ||
811 | * and so we can re-use it if it's already allocated in any event */ | ||
812 | |||
668 | filehandle = open_voicefile(); | 813 | filehandle = open_voicefile(); |
669 | if (filehandle < 0) { | 814 | if (filehandle < 0) |
670 | has_voicefile = false; | ||
671 | voicefile_size = 0; | ||
672 | return; | 815 | return; |
673 | } | ||
674 | 816 | ||
675 | voicefile_size = filesize(filehandle); | 817 | if (!load_voicefile_index(filehandle)) |
676 | 818 | goto out; | |
677 | audio_get_buffer(false, NULL); /* Must tell audio to reinitialize */ | ||
678 | reset_state(); /* use this for most of our inits */ | ||
679 | 819 | ||
680 | #ifdef TALK_PARTIAL_LOAD | 820 | #ifdef TALK_PARTIAL_LOAD |
681 | size_t bufsize; | 821 | /* TALK_PARTIAL_LOAD loads the actual clip data later, and not all |
682 | char* buf = plugin_get_buffer(&bufsize); | 822 | * at once */ |
683 | /* we won't load the full file, we only need the index */ | 823 | unsigned num_clips = voicefile.id1_max + voicefile.id2_max; |
684 | load_voicefile(true, buf, bufsize); | 824 | struct clip_entry *clips = core_get_data(index_handle); |
685 | if (!p_voicefile) | ||
686 | return; | ||
687 | |||
688 | unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max; | ||
689 | unsigned i; | ||
690 | int silence_size = 0; | 825 | int silence_size = 0; |
691 | 826 | ||
692 | for(i=0; i<clips; i++) { | 827 | for(unsigned i=0; i<num_clips; i++) { |
693 | int size = p_voicefile->index[i].size; | 828 | int size = clips[i].size; |
694 | if (size > max_clipsize) | 829 | if (size > max_clipsize) |
695 | max_clipsize = size; | 830 | max_clipsize = size; |
696 | if (i == VOICE_PAUSE) | 831 | if (i == VOICE_PAUSE) |
697 | silence_size = size; | 832 | silence_size = size; |
698 | } | 833 | } |
699 | 834 | ||
700 | voicefile_size = p_voicefile->table + clips * sizeof(struct clip_entry); | 835 | voicefile_size = voicefile.table + num_clips * sizeof(struct clip_entry); |
701 | voicefile_size += max_clipsize * QUEUE_SIZE + silence_size; | 836 | voicefile_size += max_clipsize * QUEUE_SIZE + silence_size; |
702 | p_voicefile = NULL; /* Don't pretend we can load talk clips just yet */ | ||
703 | #endif | ||
704 | |||
705 | 837 | ||
706 | /* test if we can open and if it fits in the audiobuffer */ | 838 | /* test if we can open and if it fits in the audiobuffer */ |
707 | size_t audiobufsz = audio_buffer_available(); | 839 | size_t audiobufsz = audio_buffer_available(); |
708 | if (voicefile_size <= audiobufsz) { | 840 | has_voicefile = audiobufsz >= voicefile_size; |
709 | has_voicefile = true; | 841 | |
710 | } else { | 842 | #else |
711 | has_voicefile = false; | 843 | /* load the compressed clip data into memory, in its entirety */ |
844 | voicefile_size = filesize(filehandle); | ||
845 | if (!load_voicefile_data(filehandle, voicefile_size)) | ||
846 | { | ||
712 | voicefile_size = 0; | 847 | voicefile_size = 0; |
848 | goto out; | ||
713 | } | 849 | } |
714 | 850 | has_voicefile = true; | |
715 | close(filehandle); /* close again, this was just to detect presence */ | 851 | #endif |
716 | filehandle = -1; | ||
717 | 852 | ||
718 | #if CONFIG_CODEC == SWCODEC | 853 | #if CONFIG_CODEC == SWCODEC |
719 | /* Safe to init voice playback engine now since we now know if talk is | 854 | /* Safe to init voice playback engine now since we now know if talk is |
720 | required or not */ | 855 | required or not */ |
721 | voice_thread_init(); | 856 | voice_thread_init(); |
722 | #endif | 857 | #endif |
858 | |||
859 | out: | ||
860 | close(filehandle); /* close again, this was just to detect presence */ | ||
861 | filehandle = -1; | ||
723 | } | 862 | } |
724 | 863 | ||
725 | #if CONFIG_CODEC == SWCODEC | 864 | #if CONFIG_CODEC == SWCODEC |
726 | /* return if a voice codec is required or not */ | 865 | /* return if a voice codec is required or not */ |
727 | bool talk_voice_required(void) | 866 | bool talk_voice_required(void) |
728 | { | 867 | { |
729 | return (voicefile_size != 0) /* Voice file is available */ | 868 | return (has_voicefile) /* Voice file is available */ |
730 | || (global_settings.talk_dir_clip) /* Thumbnail clips are required */ | 869 | || (global_settings.talk_dir_clip) /* Thumbnail clips are required */ |
731 | || (global_settings.talk_file_clip); | 870 | || (global_settings.talk_file_clip); |
732 | } | 871 | } |
733 | #endif | 872 | #endif |
734 | 873 | ||
735 | /* return size of voice file */ | ||
736 | static size_t talk_get_buffer_size(void) | ||
737 | { | ||
738 | #if CONFIG_CODEC == SWCODEC | ||
739 | return voicefile_size + MAX_THUMBNAIL_BUFSIZE; | ||
740 | #else | ||
741 | return audio_buffer_available(); | ||
742 | #endif | ||
743 | } | ||
744 | |||
745 | /* Sets the buffer for the voicefile and returns how many bytes of this | ||
746 | * buffer we will use for the voicefile */ | ||
747 | size_t talkbuf_init(char *bufstart) | ||
748 | { | ||
749 | bool changed = voicebuf != bufstart; | ||
750 | |||
751 | if (changed) /* must reload voice file */ | ||
752 | reset_state(); | ||
753 | |||
754 | if (bufstart) | ||
755 | voicebuf = bufstart; | ||
756 | |||
757 | return talk_get_buffer_size(); | ||
758 | } | ||
759 | |||
760 | /* somebody else claims the mp3 buffer, e.g. for regular play/record */ | 874 | /* somebody else claims the mp3 buffer, e.g. for regular play/record */ |
761 | void talk_buffer_steal(void) | 875 | void talk_buffer_set_policy(int policy) |
762 | { | 876 | { |
763 | #if CONFIG_CODEC != SWCODEC | 877 | switch(policy) |
764 | mp3_play_stop(); | ||
765 | #endif | ||
766 | #if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD) | ||
767 | if (filehandle >= 0) | ||
768 | { | 878 | { |
769 | close(filehandle); | 879 | case TALK_BUFFER_DEFAULT: |
770 | filehandle = -1; | 880 | case TALK_BUFFER_HOLD: give_buffer_away = false; break; |
881 | case TALK_BUFFER_LOOSE: give_buffer_away = true; break; | ||
882 | default: DEBUGF("Ignoring unknown policy\n"); break; | ||
771 | } | 883 | } |
772 | #endif | ||
773 | reset_state(); | ||
774 | } | 884 | } |
775 | 885 | ||
776 | /* play a voice ID from voicefile */ | 886 | /* play a voice ID from voicefile */ |
777 | int talk_id(int32_t id, bool enqueue) | 887 | int talk_id(int32_t id, bool enqueue) |
778 | { | 888 | { |
889 | int clip; | ||
779 | long clipsize; | 890 | long clipsize; |
780 | unsigned char* clipbuf; | ||
781 | int32_t unit; | 891 | int32_t unit; |
782 | int decimals; | 892 | int decimals; |
783 | 893 | ||
784 | if (talk_temp_disable_count > 0) | 894 | if (talk_temp_disable_count > 0) |
785 | return -1; /* talking has been disabled */ | 895 | return -1; /* talking has been disabled */ |
786 | #if CONFIG_CODEC == SWCODEC | 896 | if (!check_audio_status()) |
787 | /* If talk buffer was stolen, it must be restored for voicefile's sake */ | ||
788 | if (!restore_state()) | ||
789 | return -1; /* cannot get any space */ | ||
790 | #else | ||
791 | if (audio_status()) /* busy, buffer in use */ | ||
792 | return -1; | 897 | return -1; |
793 | #endif | ||
794 | |||
795 | if (p_voicefile == NULL && has_voicefile) /* reload needed? */ | ||
796 | load_voicefile(false, voicebuf, talk_get_buffer_size()); | ||
797 | 898 | ||
798 | if (p_voicefile == NULL) /* still no voices? */ | 899 | if (has_voicefile && (talk_handle <= 0 || index_handle <= 0)) /* reload needed? */ |
799 | return -1; | 900 | { |
901 | int fd = open_voicefile(); | ||
902 | if (fd < 0 | ||
903 | || !load_voicefile_index(fd) | ||
904 | || !load_voicefile_data(fd, voicefile_size)) | ||
905 | return -1; | ||
906 | } | ||
800 | 907 | ||
801 | if (id == -1) /* -1 is an indication for silence */ | 908 | if (id == -1) /* -1 is an indication for silence */ |
802 | return -1; | 909 | return -1; |
@@ -814,8 +921,8 @@ int talk_id(int32_t id, bool enqueue) | |||
814 | return 0; /* and stop, end of special case */ | 921 | return 0; /* and stop, end of special case */ |
815 | } | 922 | } |
816 | 923 | ||
817 | clipbuf = get_clip(id, &clipsize); | 924 | clip = get_clip(id, &clipsize); |
818 | if (clipbuf == NULL) | 925 | if (clip < 0) |
819 | return -1; /* not present */ | 926 | return -1; /* not present */ |
820 | 927 | ||
821 | #ifdef LOGF_ENABLE | 928 | #ifdef LOGF_ENABLE |
@@ -825,7 +932,7 @@ int talk_id(int32_t id, bool enqueue) | |||
825 | logf("\ntalk_id: Say '%s'\n", str(id)); | 932 | logf("\ntalk_id: Say '%s'\n", str(id)); |
826 | #endif | 933 | #endif |
827 | 934 | ||
828 | queue_clip(clipbuf, clipsize, enqueue); | 935 | queue_clip(clip, clipsize, enqueue); |
829 | 936 | ||
830 | return 0; | 937 | return 0; |
831 | } | 938 | } |
@@ -859,23 +966,18 @@ static int _talk_file(const char* filename, | |||
859 | int fd; | 966 | int fd; |
860 | int size; | 967 | int size; |
861 | int thumb_used; | 968 | int thumb_used; |
969 | char *buf; | ||
862 | #if CONFIG_CODEC != SWCODEC | 970 | #if CONFIG_CODEC != SWCODEC |
863 | struct mp3entry info; | 971 | struct mp3entry info; |
864 | #endif | 972 | #endif |
865 | 973 | ||
866 | if (talk_temp_disable_count > 0) | 974 | if (talk_temp_disable_count > 0) |
867 | return -1; /* talking has been disabled */ | 975 | return -1; /* talking has been disabled */ |
868 | #if CONFIG_CODEC == SWCODEC | 976 | if (!check_audio_status()) |
869 | /* If talk buffer was stolen, it must be restored for thumbnail's sake */ | 977 | return -1; |
870 | if (!restore_state()) | ||
871 | return -1; /* cannot get any space */ | ||
872 | #else | ||
873 | if (audio_status()) /* busy, buffer in use */ | ||
874 | return -1; | ||
875 | #endif | ||
876 | 978 | ||
877 | if (p_thumbnail == NULL || size_for_thumbnail <= 0) | 979 | if (!alloc_thumbnail_buf()) |
878 | alloc_thumbnail_buf(); | 980 | return -1; |
879 | 981 | ||
880 | #if CONFIG_CODEC != SWCODEC | 982 | #if CONFIG_CODEC != SWCODEC |
881 | if(mp3info(&info, filename)) /* use this to find real start */ | 983 | if(mp3info(&info, filename)) /* use this to find real start */ |
@@ -905,8 +1007,10 @@ static int _talk_file(const char* filename, | |||
905 | lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ | 1007 | lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ |
906 | #endif | 1008 | #endif |
907 | 1009 | ||
908 | size = read(fd, p_thumbnail +thumb_used, | 1010 | talk_handle_locked++; |
909 | size_for_thumbnail -thumb_used); | 1011 | buf = core_get_data(thumb_handle); |
1012 | size = read(fd, buf+thumb_used, size_for_thumbnail - thumb_used); | ||
1013 | talk_handle_locked--; | ||
910 | close(fd); | 1014 | close(fd); |
911 | 1015 | ||
912 | /* ToDo: find audio, skip ID headers and trailers */ | 1016 | /* ToDo: find audio, skip ID headers and trailers */ |
@@ -914,7 +1018,8 @@ static int _talk_file(const char* filename, | |||
914 | if (size > 0) /* Don't play missing clips */ | 1018 | if (size > 0) /* Don't play missing clips */ |
915 | { | 1019 | { |
916 | #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) | 1020 | #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) |
917 | bitswap(p_thumbnail, size); | 1021 | /* bitswap doesnt yield() */ |
1022 | bitswap(core_get_data(thumb_handle), size); | ||
918 | #endif | 1023 | #endif |
919 | if(prefix_ids) | 1024 | if(prefix_ids) |
920 | /* prefix thumbnail by speaking these ids, but only now | 1025 | /* prefix thumbnail by speaking these ids, but only now |
@@ -922,9 +1027,9 @@ static int _talk_file(const char* filename, | |||
922 | spoken. */ | 1027 | spoken. */ |
923 | talk_idarray(prefix_ids, true); | 1028 | talk_idarray(prefix_ids, true); |
924 | talk_queue_lock(); | 1029 | talk_queue_lock(); |
925 | thumbnail_buf_used = thumb_used +size; | 1030 | thumbnail_buf_used = thumb_used + size; |
926 | talk_queue_unlock(); | 1031 | talk_queue_unlock(); |
927 | queue_clip(p_thumbnail +thumb_used, size, true); | 1032 | queue_clip(voicefile_size + thumb_used, size, true); |
928 | } | 1033 | } |
929 | 1034 | ||
930 | return size; | 1035 | return size; |
@@ -1012,10 +1117,8 @@ int talk_number(long n, bool enqueue) | |||
1012 | 1117 | ||
1013 | if (talk_temp_disable_count > 0) | 1118 | if (talk_temp_disable_count > 0) |
1014 | return -1; /* talking has been disabled */ | 1119 | return -1; /* talking has been disabled */ |
1015 | #if CONFIG_CODEC != SWCODEC | 1120 | if (!check_audio_status()) |
1016 | if (audio_status()) /* busy, buffer in use */ | 1121 | return -1; |
1017 | return -1; | ||
1018 | #endif | ||
1019 | 1122 | ||
1020 | if (!enqueue) | 1123 | if (!enqueue) |
1021 | talk_shutup(); /* cut off all the pending stuff */ | 1124 | talk_shutup(); /* cut off all the pending stuff */ |
@@ -1160,10 +1263,8 @@ int talk_value_decimal(long n, int unit, int decimals, bool enqueue) | |||
1160 | 1263 | ||
1161 | if (talk_temp_disable_count > 0) | 1264 | if (talk_temp_disable_count > 0) |
1162 | return -1; /* talking has been disabled */ | 1265 | return -1; /* talking has been disabled */ |
1163 | #if CONFIG_CODEC != SWCODEC | 1266 | if (!check_audio_status()) |
1164 | if (audio_status()) /* busy, buffer in use */ | 1267 | return -1; |
1165 | return -1; | ||
1166 | #endif | ||
1167 | 1268 | ||
1168 | /* special case for time duration */ | 1269 | /* special case for time duration */ |
1169 | if (unit == UNIT_TIME) | 1270 | if (unit == UNIT_TIME) |
@@ -1217,10 +1318,8 @@ int talk_spell(const char* spell, bool enqueue) | |||
1217 | 1318 | ||
1218 | if (talk_temp_disable_count > 0) | 1319 | if (talk_temp_disable_count > 0) |
1219 | return -1; /* talking has been disabled */ | 1320 | return -1; /* talking has been disabled */ |
1220 | #if CONFIG_CODEC != SWCODEC | 1321 | if (!check_audio_status()) |
1221 | if (audio_status()) /* busy, buffer in use */ | 1322 | return -1; |
1222 | return -1; | ||
1223 | #endif | ||
1224 | 1323 | ||
1225 | if (!enqueue) | 1324 | if (!enqueue) |
1226 | talk_shutup(); /* cut off all the pending stuff */ | 1325 | talk_shutup(); /* cut off all the pending stuff */ |