diff options
author | Jörg Hohensohn <hohensoh@rockbox.org> | 2004-11-17 22:30:38 +0000 |
---|---|---|
committer | Jörg Hohensohn <hohensoh@rockbox.org> | 2004-11-17 22:30:38 +0000 |
commit | 24e6dffa50ce702034185e1df6bdd238b3cf21ce (patch) | |
tree | f7f8ebc016a5cabe44643926684506c3d86c5574 /apps | |
parent | 605cf4c779f214c8a6f413d401e14a192352e62d (diff) | |
download | rockbox-24e6dffa50ce702034185e1df6bdd238b3cf21ce.tar.gz rockbox-24e6dffa50ce702034185e1df6bdd238b3cf21ce.zip |
append the "silence" clip at the end of voice output, this avoids clicks and missing ends especially with low bitrates
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5423 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r-- | apps/talk.c | 102 |
1 files changed, 68 insertions, 34 deletions
diff --git a/apps/talk.c b/apps/talk.c index 826b02d6e5..09d1727571 100644 --- a/apps/talk.c +++ b/apps/talk.c | |||
@@ -85,6 +85,10 @@ static int queue_read; /* read index of queue, by ISR context */ | |||
85 | static int sent; /* how many bytes handed over to playback, owned by ISR */ | 85 | static int sent; /* how many bytes handed over to playback, owned by ISR */ |
86 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ | 86 | static unsigned char curr_hd[3]; /* current frame header, for re-sync */ |
87 | static int filehandle; /* global, so the MMC variant can keep the file open */ | 87 | static int filehandle; /* global, so the MMC variant can keep the file open */ |
88 | static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */ | ||
89 | static int silence_len; /* length of the VOICE_PAUSE clip */ | ||
90 | static bool silence_add; /* flag if trailing silence shall be added */ | ||
91 | static unsigned char* p_lastclip; /* address of latest clip, for silence add */ | ||
88 | 92 | ||
89 | 93 | ||
90 | /***************** Private prototypes *****************/ | 94 | /***************** Private prototypes *****************/ |
@@ -94,6 +98,7 @@ static void mp3_callback(unsigned char** start, int* size); | |||
94 | static int shutup(void); | 98 | static int shutup(void); |
95 | static int queue_clip(unsigned char* buf, int size, bool enqueue); | 99 | static int queue_clip(unsigned char* buf, int size, bool enqueue); |
96 | static int open_voicefile(void); | 100 | static int open_voicefile(void); |
101 | static unsigned char* get_clip(int id, int* p_size); | ||
97 | 102 | ||
98 | 103 | ||
99 | /***************** Private implementation *****************/ | 104 | /***************** Private implementation *****************/ |
@@ -164,6 +169,9 @@ static void load_voicefile(void) | |||
164 | filehandle = -1; | 169 | filehandle = -1; |
165 | #endif | 170 | #endif |
166 | 171 | ||
172 | /* make sure to have the silence clip, if available */ | ||
173 | p_silence = get_clip(VOICE_PAUSE, &silence_len); | ||
174 | |||
167 | return; | 175 | return; |
168 | 176 | ||
169 | load_err: | 177 | load_err: |
@@ -191,7 +199,7 @@ static void mp3_callback(unsigned char** start, int* size) | |||
191 | *size = sent; | 199 | *size = sent; |
192 | return; | 200 | return; |
193 | } | 201 | } |
194 | else /* go to next entry */ | 202 | else if (sent > 0) /* go to next entry */ |
195 | { | 203 | { |
196 | queue_read++; | 204 | queue_read++; |
197 | if (queue_read >= QUEUE_SIZE) | 205 | if (queue_read >= QUEUE_SIZE) |
@@ -201,12 +209,20 @@ static void mp3_callback(unsigned char** start, int* size) | |||
201 | if (QUEUE_LEVEL) /* queue is not empty? */ | 209 | if (QUEUE_LEVEL) /* queue is not empty? */ |
202 | { /* start next clip */ | 210 | { /* start next clip */ |
203 | sent = MIN(queue[queue_read].len, 0xFFFF); | 211 | sent = MIN(queue[queue_read].len, 0xFFFF); |
204 | *start = queue[queue_read].buf; | 212 | *start = p_lastclip = queue[queue_read].buf; |
205 | *size = sent; | 213 | *size = sent; |
206 | curr_hd[0] = *start[1]; | 214 | curr_hd[0] = *start[1]; |
207 | curr_hd[1] = *start[2]; | 215 | curr_hd[1] = *start[2]; |
208 | curr_hd[2] = *start[3]; | 216 | curr_hd[2] = *start[3]; |
209 | } | 217 | } |
218 | else if (silence_add && p_silence != NULL /* want and can add silence */ | ||
219 | && p_lastclip < p_thumbnail) /* and wasn't playing thumbnail file */ | ||
220 | { /* add silence clip when queue runs empty playing a voice clip */ | ||
221 | silence_add = false; /* do this only once */ | ||
222 | sent = 0; /* not part of "official" data from queue */ | ||
223 | *start = p_silence; | ||
224 | *size = silence_len; | ||
225 | } | ||
210 | else | 226 | else |
211 | { | 227 | { |
212 | *size = 0; /* end of data */ | 228 | *size = 0; /* end of data */ |
@@ -302,6 +318,8 @@ static int queue_clip(unsigned char* buf, int size, bool enqueue) | |||
302 | 318 | ||
303 | if (queue_level == 0) | 319 | if (queue_level == 0) |
304 | { /* queue was empty, we have to do the initial start */ | 320 | { /* queue was empty, we have to do the initial start */ |
321 | silence_add = true; | ||
322 | p_lastclip = buf; | ||
305 | sent = MIN(size, 0xFFFF); /* DMA can do no more */ | 323 | sent = MIN(size, 0xFFFF); /* DMA can do no more */ |
306 | mp3_play_data(buf, sent, mp3_callback); | 324 | mp3_play_data(buf, sent, mp3_callback); |
307 | curr_hd[0] = buf[1]; | 325 | curr_hd[0] = buf[1]; |
@@ -317,6 +335,50 @@ static int queue_clip(unsigned char* buf, int size, bool enqueue) | |||
317 | return 0; | 335 | return 0; |
318 | } | 336 | } |
319 | 337 | ||
338 | /* fetch a clip from the voice file */ | ||
339 | static unsigned char* get_clip(int id, int* p_size) | ||
340 | { | ||
341 | int clipsize; | ||
342 | unsigned char* clipbuf; | ||
343 | |||
344 | if (id > VOICEONLY_DELIMITER) | ||
345 | { /* voice-only entries use the second part of the table */ | ||
346 | id -= VOICEONLY_DELIMITER + 1; | ||
347 | if (id >= p_voicefile->id2_max) | ||
348 | return NULL; /* must be newer than we have */ | ||
349 | id += p_voicefile->id1_max; /* table 2 is behind table 1 */ | ||
350 | } | ||
351 | else | ||
352 | { /* normal use of the first table */ | ||
353 | if (id >= p_voicefile->id1_max) | ||
354 | return NULL; /* must be newer than we have */ | ||
355 | } | ||
356 | |||
357 | clipsize = p_voicefile->index[id].size; | ||
358 | if (clipsize == 0) /* clip not included in voicefile */ | ||
359 | return NULL; | ||
360 | clipbuf = mp3buf + p_voicefile->index[id].offset; | ||
361 | |||
362 | #ifdef HAVE_MMC /* dynamic loading, on demand */ | ||
363 | if (!(clipsize & LOADED_MASK)) | ||
364 | { /* clip used for the first time, needs loading */ | ||
365 | lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET); | ||
366 | if (read(filehandle, clipbuf, clipsize) != clipsize) | ||
367 | return NULL; /* read error */ | ||
368 | |||
369 | p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */ | ||
370 | } | ||
371 | else | ||
372 | { /* clip is in memory already */ | ||
373 | clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */ | ||
374 | } | ||
375 | #endif | ||
376 | |||
377 | *p_size = clipsize; | ||
378 | return clipbuf; | ||
379 | } | ||
380 | |||
381 | |||
320 | /* common code for talk_init() and talk_buffer_steal() */ | 382 | /* common code for talk_init() and talk_buffer_steal() */ |
321 | static void reset_state(void) | 383 | static void reset_state(void) |
322 | { | 384 | { |
@@ -324,6 +386,7 @@ static void reset_state(void) | |||
324 | p_voicefile = NULL; /* indicate no voicefile (trashed) */ | 386 | p_voicefile = NULL; /* indicate no voicefile (trashed) */ |
325 | p_thumbnail = mp3buf; /* whole space for thumbnail */ | 387 | p_thumbnail = mp3buf; /* whole space for thumbnail */ |
326 | size_for_thumbnail = mp3end - mp3buf; | 388 | size_for_thumbnail = mp3end - mp3buf; |
389 | p_silence = NULL; /* pause clip not accessible */ | ||
327 | } | 390 | } |
328 | 391 | ||
329 | /***************** Public implementation *****************/ | 392 | /***************** Public implementation *****************/ |
@@ -392,38 +455,9 @@ int talk_id(int id, bool enqueue) | |||
392 | return 0; /* and stop, end of special case */ | 455 | return 0; /* and stop, end of special case */ |
393 | } | 456 | } |
394 | 457 | ||
395 | if (id > VOICEONLY_DELIMITER) | 458 | clipbuf = get_clip(id, &clipsize); |
396 | { /* voice-only entries use the second part of the table */ | 459 | if (clipbuf == NULL) |
397 | id -= VOICEONLY_DELIMITER + 1; | 460 | return -1; /* not present */ |
398 | if (id >= p_voicefile->id2_max) | ||
399 | return -1; /* must be newer than we have */ | ||
400 | id += p_voicefile->id1_max; /* table 2 is behind table 1 */ | ||
401 | } | ||
402 | else | ||
403 | { /* normal use of the first table */ | ||
404 | if (id >= p_voicefile->id1_max) | ||
405 | return -1; /* must be newer than we have */ | ||
406 | } | ||
407 | |||
408 | clipsize = p_voicefile->index[id].size; | ||
409 | if (clipsize == 0) /* clip not included in voicefile */ | ||
410 | return -1; | ||
411 | clipbuf = mp3buf + p_voicefile->index[id].offset; | ||
412 | |||
413 | #ifdef HAVE_MMC /* dynamic loading, on demand */ | ||
414 | if (!(clipsize & LOADED_MASK)) | ||
415 | { /* clip used for the first time, needs loading */ | ||
416 | lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET); | ||
417 | if (read(filehandle, clipbuf, clipsize) != clipsize) | ||
418 | return -1; /* read error */ | ||
419 | |||
420 | p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */ | ||
421 | } | ||
422 | else | ||
423 | { /* clip is in memory already */ | ||
424 | clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */ | ||
425 | } | ||
426 | #endif | ||
427 | 461 | ||
428 | queue_clip(clipbuf, clipsize, enqueue); | 462 | queue_clip(clipbuf, clipsize, enqueue); |
429 | 463 | ||