summaryrefslogtreecommitdiff
path: root/apps/talk.c
diff options
context:
space:
mode:
authorRafaël Carré <rafael.carre@gmail.com>2010-09-01 00:08:50 +0000
committerRafaël Carré <rafael.carre@gmail.com>2010-09-01 00:08:50 +0000
commitca91d0fd7add9b4745b81b3579635f471314a4ac (patch)
tree88a74b65a9a71fdaea52d66187f496883068c548 /apps/talk.c
parenteb39236ff7ff37c436d94ddeadb58fcc1c2c92ca (diff)
downloadrockbox-ca91d0fd7add9b4745b81b3579635f471314a4ac.tar.gz
rockbox-ca91d0fd7add9b4745b81b3579635f471314a4ac.zip
FS#11587 : voice for SWCODEC and low memory
On these targets the full voice file can't be loaded so only load 64 clips at a time (the size of the queue) Voice now works on clipv1 git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27962 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/talk.c')
-rw-r--r--apps/talk.c198
1 files changed, 168 insertions, 30 deletions
diff --git a/apps/talk.c b/apps/talk.c
index e07dcb8262..7dbfb2ef77 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -108,6 +108,26 @@ struct queue_entry /* one entry of the internal queue */
108 108
109/***************** Globals *****************/ 109/***************** Globals *****************/
110 110
111#if CONFIG_STORAGE & STORAGE_MMC
112/* The MMC storage on the Ondios is slow enough that we want to buffer the
113 * talk clips only when they are needed */
114# define TALK_PROGRESSIVE_LOAD
115#elif CONFIG_CODEC == SWCODEC && MEM <= 2
116/* The entire voice file wouldn't fit in memory together with codecs, so we
117 * load clips each time they are accessed */
118# define TALK_PARTIAL_LOAD
119#endif
120
121#ifdef TALK_PARTIAL_LOAD
122static unsigned char *clip_buffer;
123static long max_clipsize; /* size of the biggest clip */
124static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */
125static uint8_t clip_age[QUEUE_SIZE];
126#if QUEUE_SIZE > 255
127# error clip_age[] type too small
128#endif
129#endif
130
111static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */ 131static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */
112/* Multiple thumbnails can be loaded back-to-back in this buffer. */ 132/* Multiple thumbnails can be loaded back-to-back in this buffer. */
113static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in 133static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in
@@ -131,7 +151,7 @@ static struct mutex queue_mutex SHAREDBSS_ATTR;
131#endif /* CONFIG_CODEC */ 151#endif /* CONFIG_CODEC */
132static int sent; /* how many bytes handed over to playback, owned by ISR */ 152static int sent; /* how many bytes handed over to playback, owned by ISR */
133static unsigned char curr_hd[3]; /* current frame header, for re-sync */ 153static unsigned char curr_hd[3]; /* current frame header, for re-sync */
134static int filehandle = -1; /* global, so the MMC variant can keep the file open */ 154static int filehandle = -1; /* global, so we can keep the file open if needed */
135static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */ 155static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */
136static long silence_len; /* length of the VOICE_PAUSE clip */ 156static long silence_len; /* length of the VOICE_PAUSE clip */
137static unsigned char* p_lastclip; /* address of latest clip, for silence add */ 157static unsigned char* p_lastclip; /* address of latest clip, for silence add */
@@ -182,22 +202,78 @@ static unsigned char* get_clip(long id, long* p_size)
182 clipsize = p_voicefile->index[id].size; 202 clipsize = p_voicefile->index[id].size;
183 if (clipsize == 0) /* clip not included in voicefile */ 203 if (clipsize == 0) /* clip not included in voicefile */
184 return NULL; 204 return NULL;
205
206#ifndef TALK_PARTIAL_LOAD
185 clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset; 207 clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset;
208#endif
186 209
187#if (CONFIG_STORAGE & STORAGE_MMC) /* dynamic loading, on demand */ 210#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
188 if (!(clipsize & LOADED_MASK)) 211 if (!(clipsize & LOADED_MASK))
189 { /* clip used for the first time, needs loading */ 212 { /* clip needs loading */
213#ifdef TALK_PARTIAL_LOAD
214 int idx = 0;
215 if (id == VOICE_PAUSE) {
216 idx = QUEUE_SIZE; /* we keep VOICE_PAUSE loaded */
217 } else {
218 int oldest = 0, i;
219 for(i=0; i<QUEUE_SIZE; i++) {
220 if (buffered_id[i] < 0) {
221 /* found a free entry, that means the buffer isn't
222 * full yet. */
223 idx = i;
224 break;
225 }
226
227 /* find the oldest clip */
228 if(clip_age[i] > oldest) {
229 idx = i;
230 oldest = clip_age[i];
231 }
232
233 /* increment age of each loaded clip */
234 clip_age[i]++;
235 }
236 clip_age[idx] = 0; /* reset clip's age */
237 }
238 clipbuf = clip_buffer + idx * max_clipsize;
239#endif
240
190 lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET); 241 lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET);
191 if (read(filehandle, clipbuf, clipsize) != clipsize) 242 if (read(filehandle, clipbuf, clipsize) != clipsize)
192 return NULL; /* read error */ 243 return NULL; /* read error */
193 244
194 p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */ 245 p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */
246
247#ifdef TALK_PARTIAL_LOAD
248 if (id != VOICE_PAUSE) {
249 if (buffered_id[idx] >= 0) {
250 /* mark previously loaded clip as unloaded */
251 p_voicefile->index[buffered_id[idx]].size &= ~LOADED_MASK;
252 }
253 buffered_id[idx] = id;
254 }
255#endif
195 } 256 }
196 else 257 else
197 { /* clip is in memory already */ 258 { /* clip is in memory already */
259#ifdef TALK_PARTIAL_LOAD
260 /* Find where it was loaded */
261 clipbuf = clip_buffer;
262 if (id == VOICE_PAUSE) {
263 clipbuf += QUEUE_SIZE * max_clipsize;
264 } else {
265 int idx;
266 for (idx=0; idx<QUEUE_SIZE; idx++)
267 if (buffered_id[idx] == id) {
268 clipbuf += idx * max_clipsize;
269 clip_age[idx] = 0; /* reset clip's age */
270 break;
271 }
272 }
273#endif
198 clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */ 274 clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */
199 } 275 }
200#endif 276#endif /* defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD) */
201 277
202 *p_size = clipsize; 278 *p_size = clipsize;
203 return clipbuf; 279 return clipbuf;
@@ -205,29 +281,40 @@ static unsigned char* get_clip(long id, long* p_size)
205 281
206 282
207/* load the voice file into the mp3 buffer */ 283/* load the voice file into the mp3 buffer */
208static void load_voicefile(void) 284static void load_voicefile(bool probe)
209{ 285{
210 int load_size; 286 int load_size;
211 int got_size; 287 int got_size;
288#ifndef TALK_PARTIAL_LOAD
212 int file_size; 289 int file_size;
290#endif
213#ifdef ROCKBOX_LITTLE_ENDIAN 291#ifdef ROCKBOX_LITTLE_ENDIAN
214 int i; 292 int i;
215#endif 293#endif
216 294
217 filehandle = open_voicefile(); 295 if (!probe)
296 filehandle = open_voicefile();
218 if (filehandle < 0) /* failed to open */ 297 if (filehandle < 0) /* failed to open */
219 goto load_err; 298 goto load_err;
220 299
300#ifndef TALK_PARTIAL_LOAD
221 file_size = filesize(filehandle); 301 file_size = filesize(filehandle);
222 if (file_size > audiobufend - audiobuf) /* won't fit? */ 302 if (file_size > audiobufend - audiobuf) /* won't fit? */
223 goto load_err; 303 goto load_err;
304#endif
224 305
225#if (CONFIG_STORAGE & STORAGE_MMC) /* load only the header for now */ 306#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
307 /* load only the header for now */
226 load_size = offsetof(struct voicefile, index); 308 load_size = offsetof(struct voicefile, index);
227#else /* load the full file */ 309#else /* load the full file */
228 load_size = file_size; 310 load_size = file_size;
229#endif 311#endif
230 312
313#ifdef TALK_PARTIAL_LOAD
314 if (load_size > audiobufend - audiobuf) /* won't fit? */
315 goto load_err;
316#endif
317
231 got_size = read(filehandle, audiobuf, load_size); 318 got_size = read(filehandle, audiobuf, load_size);
232 if (got_size != load_size /* failure */) 319 if (got_size != load_size /* failure */)
233 goto load_err; 320 goto load_err;
@@ -258,26 +345,39 @@ static void load_voicefile(void)
258 else 345 else
259 goto load_err; 346 goto load_err;
260 347
261#ifdef ROCKBOX_LITTLE_ENDIAN 348#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
262 for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++)
263 structec_convert(&p_voicefile->index[i], "ll", 1, true);
264#endif
265
266#if (CONFIG_STORAGE & STORAGE_MMC)
267 /* load the index table, now that we know its size from the header */ 349 /* load the index table, now that we know its size from the header */
268 load_size = (p_voicefile->id1_max + p_voicefile->id2_max) 350 load_size = (p_voicefile->id1_max + p_voicefile->id2_max)
269 * sizeof(struct clip_entry); 351 * sizeof(struct clip_entry);
352
353#ifdef TALK_PARTIAL_LOAD
354 if (load_size > audiobufend - audiobuf) /* won't fit? */
355 goto load_err;
356#endif
357
270 got_size = read(filehandle, 358 got_size = read(filehandle,
271 (unsigned char *) p_voicefile + offsetof(struct voicefile, index), load_size); 359 (unsigned char *) p_voicefile + offsetof(struct voicefile, index), load_size);
272 if (got_size != load_size) /* read error */ 360 if (got_size != load_size) /* read error */
273 goto load_err; 361 goto load_err;
274#else 362#else
275 close(filehandle); /* only the MMC variant leaves it open */ 363 close(filehandle);
276 filehandle = -1; 364 filehandle = -1;
365#endif /* defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD) */
366
367#ifdef ROCKBOX_LITTLE_ENDIAN
368 for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++)
369 structec_convert(&p_voicefile->index[i], "ll", 1, true);
277#endif 370#endif
278 371
279 /* make sure to have the silence clip, if available */ 372#ifdef TALK_PARTIAL_LOAD
280 p_silence = get_clip(VOICE_PAUSE, &silence_len); 373 clip_buffer = (unsigned char *) p_voicefile + p_voicefile->table;
374 unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
375 clip_buffer += clips * sizeof(struct clip_entry); /* skip index */
376#endif
377 if (!probe) {
378 /* make sure to have the silence clip, if available */
379 p_silence = get_clip(VOICE_PAUSE, &silence_len);
380 }
281 381
282 return; 382 return;
283 383
@@ -501,6 +601,13 @@ static void reset_state(void)
501 p_thumbnail = audiobuf; 601 p_thumbnail = audiobuf;
502 size_for_thumbnail = audiobufend - audiobuf; 602 size_for_thumbnail = audiobufend - audiobuf;
503#endif 603#endif
604
605#ifdef TALK_PARTIAL_LOAD
606 int i;
607 for(i=0; i<QUEUE_SIZE; i++)
608 buffered_id[i] = -1;
609#endif
610
504 thumbnail_buf_used = 0; 611 thumbnail_buf_used = 0;
505 p_silence = NULL; /* pause clip not accessible */ 612 p_silence = NULL; /* pause clip not accessible */
506} 613}
@@ -517,8 +624,8 @@ void talk_init(void)
517 return; 624 return;
518 } 625 }
519 626
520#if (CONFIG_STORAGE & STORAGE_MMC) 627#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
521 if (filehandle >= 0) /* MMC: An old voice file might still be open */ 628 if (filehandle >= 0)
522 { 629 {
523 close(filehandle); 630 close(filehandle);
524 filehandle = -1; 631 filehandle = -1;
@@ -540,18 +647,49 @@ void talk_init(void)
540 reset_state(); /* use this for most of our inits */ 647 reset_state(); /* use this for most of our inits */
541 648
542 filehandle = open_voicefile(); 649 filehandle = open_voicefile();
543 size_t audiobufsz = audiobufend - audiobuf; 650 if (filehandle < 0) {
651 has_voicefile = false;
652 voicefile_size = 0;
653 return;
654 }
655
656 voicefile_size = filesize(filehandle);
657
544 /* test if we can open and if it fits in the audiobuffer */ 658 /* test if we can open and if it fits in the audiobuffer */
545 has_voicefile = filehandle >= 0 && filesize(filehandle) <= (off_t)audiobufsz; 659 size_t audiobufsz = audiobufend - audiobuf;
546 voicefile_size = 0;
547 660
548 if (has_voicefile) 661#ifdef TALK_PARTIAL_LOAD
549 { 662 /* we won't load the full file, we only need the index */
550 voicefile_size = filesize(filehandle); 663 load_voicefile(true);
551 close(filehandle); /* close again, this was just to detect presence */ 664 if (!p_voicefile)
552 filehandle = -1; 665 return;
666
667 unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
668 unsigned i;
669 int silence_size = 0;
670
671 for(i=0; i<clips; i++) {
672 int size = p_voicefile->index[i].size;
673 if (size > max_clipsize)
674 max_clipsize = size;
675 if (i == VOICE_PAUSE)
676 silence_size = size;
553 } 677 }
554 678
679 voicefile_size = p_voicefile->table + clips * sizeof(struct clip_entry);
680 voicefile_size += max_clipsize * QUEUE_SIZE + silence_size;
681 p_voicefile = NULL; /* Don't pretend we can load talk clips just yet */
682#endif
683
684 if (voicefile_size <= audiobufsz) {
685 has_voicefile = true;
686 } else {
687 has_voicefile = false;
688 voicefile_size = 0;
689 }
690
691 close(filehandle); /* close again, this was just to detect presence */
692 filehandle = -1;
555} 693}
556 694
557#if CONFIG_CODEC == SWCODEC 695#if CONFIG_CODEC == SWCODEC
@@ -576,8 +714,8 @@ void talk_buffer_steal(void)
576#if CONFIG_CODEC != SWCODEC 714#if CONFIG_CODEC != SWCODEC
577 mp3_play_stop(); 715 mp3_play_stop();
578#endif 716#endif
579#if (CONFIG_STORAGE & STORAGE_MMC) 717#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
580 if (filehandle >= 0) /* only relevant for MMC */ 718 if (filehandle >= 0)
581 { 719 {
582 close(filehandle); 720 close(filehandle);
583 filehandle = -1; 721 filehandle = -1;
@@ -603,7 +741,7 @@ int talk_id(int32_t id, bool enqueue)
603#endif 741#endif
604 742
605 if (p_voicefile == NULL && has_voicefile) 743 if (p_voicefile == NULL && has_voicefile)
606 load_voicefile(); /* reload needed */ 744 load_voicefile(false); /* reload needed */
607 745
608 if (p_voicefile == NULL) /* still no voices? */ 746 if (p_voicefile == NULL) /* still no voices? */
609 return -1; 747 return -1;
@@ -1134,4 +1272,4 @@ void talk_time(const struct tm *tm, bool enqueue)
1134 } 1272 }
1135} 1273}
1136 1274
1137#endif 1275#endif /* CONFIG_RTC */