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
This commit is contained in:
Jörg Hohensohn 2004-11-17 22:30:38 +00:00
parent 605cf4c779
commit 24e6dffa50

View file

@ -85,6 +85,10 @@ static int queue_read; /* read index of queue, by ISR context */
static int sent; /* how many bytes handed over to playback, owned by ISR */ static int sent; /* how many bytes handed over to playback, owned by ISR */
static unsigned char curr_hd[3]; /* current frame header, for re-sync */ static unsigned char curr_hd[3]; /* current frame header, for re-sync */
static int filehandle; /* global, so the MMC variant can keep the file open */ static int filehandle; /* global, so the MMC variant can keep the file open */
static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */
static int silence_len; /* length of the VOICE_PAUSE clip */
static bool silence_add; /* flag if trailing silence shall be added */
static unsigned char* p_lastclip; /* address of latest clip, for silence add */
/***************** Private prototypes *****************/ /***************** Private prototypes *****************/
@ -94,6 +98,7 @@ static void mp3_callback(unsigned char** start, int* size);
static int shutup(void); static int shutup(void);
static int queue_clip(unsigned char* buf, int size, bool enqueue); static int queue_clip(unsigned char* buf, int size, bool enqueue);
static int open_voicefile(void); static int open_voicefile(void);
static unsigned char* get_clip(int id, int* p_size);
/***************** Private implementation *****************/ /***************** Private implementation *****************/
@ -164,6 +169,9 @@ static void load_voicefile(void)
filehandle = -1; filehandle = -1;
#endif #endif
/* make sure to have the silence clip, if available */
p_silence = get_clip(VOICE_PAUSE, &silence_len);
return; return;
load_err: load_err:
@ -191,7 +199,7 @@ static void mp3_callback(unsigned char** start, int* size)
*size = sent; *size = sent;
return; return;
} }
else /* go to next entry */ else if (sent > 0) /* go to next entry */
{ {
queue_read++; queue_read++;
if (queue_read >= QUEUE_SIZE) if (queue_read >= QUEUE_SIZE)
@ -201,12 +209,20 @@ static void mp3_callback(unsigned char** start, int* size)
if (QUEUE_LEVEL) /* queue is not empty? */ if (QUEUE_LEVEL) /* queue is not empty? */
{ /* start next clip */ { /* start next clip */
sent = MIN(queue[queue_read].len, 0xFFFF); sent = MIN(queue[queue_read].len, 0xFFFF);
*start = queue[queue_read].buf; *start = p_lastclip = queue[queue_read].buf;
*size = sent; *size = sent;
curr_hd[0] = *start[1]; curr_hd[0] = *start[1];
curr_hd[1] = *start[2]; curr_hd[1] = *start[2];
curr_hd[2] = *start[3]; curr_hd[2] = *start[3];
} }
else if (silence_add && p_silence != NULL /* want and can add silence */
&& p_lastclip < p_thumbnail) /* and wasn't playing thumbnail file */
{ /* add silence clip when queue runs empty playing a voice clip */
silence_add = false; /* do this only once */
sent = 0; /* not part of "official" data from queue */
*start = p_silence;
*size = silence_len;
}
else else
{ {
*size = 0; /* end of data */ *size = 0; /* end of data */
@ -302,6 +318,8 @@ static int queue_clip(unsigned char* buf, int size, bool enqueue)
if (queue_level == 0) if (queue_level == 0)
{ /* queue was empty, we have to do the initial start */ { /* queue was empty, we have to do the initial start */
silence_add = true;
p_lastclip = buf;
sent = MIN(size, 0xFFFF); /* DMA can do no more */ sent = MIN(size, 0xFFFF); /* DMA can do no more */
mp3_play_data(buf, sent, mp3_callback); mp3_play_data(buf, sent, mp3_callback);
curr_hd[0] = buf[1]; curr_hd[0] = buf[1];
@ -317,6 +335,50 @@ static int queue_clip(unsigned char* buf, int size, bool enqueue)
return 0; return 0;
} }
/* fetch a clip from the voice file */
static unsigned char* get_clip(int id, int* p_size)
{
int clipsize;
unsigned char* clipbuf;
if (id > VOICEONLY_DELIMITER)
{ /* voice-only entries use the second part of the table */
id -= VOICEONLY_DELIMITER + 1;
if (id >= p_voicefile->id2_max)
return NULL; /* must be newer than we have */
id += p_voicefile->id1_max; /* table 2 is behind table 1 */
}
else
{ /* normal use of the first table */
if (id >= p_voicefile->id1_max)
return NULL; /* must be newer than we have */
}
clipsize = p_voicefile->index[id].size;
if (clipsize == 0) /* clip not included in voicefile */
return NULL;
clipbuf = mp3buf + p_voicefile->index[id].offset;
#ifdef HAVE_MMC /* dynamic loading, on demand */
if (!(clipsize & LOADED_MASK))
{ /* clip used for the first time, needs loading */
lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET);
if (read(filehandle, clipbuf, clipsize) != clipsize)
return NULL; /* read error */
p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */
}
else
{ /* clip is in memory already */
clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */
}
#endif
*p_size = clipsize;
return clipbuf;
}
/* common code for talk_init() and talk_buffer_steal() */ /* common code for talk_init() and talk_buffer_steal() */
static void reset_state(void) static void reset_state(void)
{ {
@ -324,6 +386,7 @@ static void reset_state(void)
p_voicefile = NULL; /* indicate no voicefile (trashed) */ p_voicefile = NULL; /* indicate no voicefile (trashed) */
p_thumbnail = mp3buf; /* whole space for thumbnail */ p_thumbnail = mp3buf; /* whole space for thumbnail */
size_for_thumbnail = mp3end - mp3buf; size_for_thumbnail = mp3end - mp3buf;
p_silence = NULL; /* pause clip not accessible */
} }
/***************** Public implementation *****************/ /***************** Public implementation *****************/
@ -392,38 +455,9 @@ int talk_id(int id, bool enqueue)
return 0; /* and stop, end of special case */ return 0; /* and stop, end of special case */
} }
if (id > VOICEONLY_DELIMITER) clipbuf = get_clip(id, &clipsize);
{ /* voice-only entries use the second part of the table */ if (clipbuf == NULL)
id -= VOICEONLY_DELIMITER + 1; return -1; /* not present */
if (id >= p_voicefile->id2_max)
return -1; /* must be newer than we have */
id += p_voicefile->id1_max; /* table 2 is behind table 1 */
}
else
{ /* normal use of the first table */
if (id >= p_voicefile->id1_max)
return -1; /* must be newer than we have */
}
clipsize = p_voicefile->index[id].size;
if (clipsize == 0) /* clip not included in voicefile */
return -1;
clipbuf = mp3buf + p_voicefile->index[id].offset;
#ifdef HAVE_MMC /* dynamic loading, on demand */
if (!(clipsize & LOADED_MASK))
{ /* clip used for the first time, needs loading */
lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET);
if (read(filehandle, clipbuf, clipsize) != clipsize)
return -1; /* read error */
p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */
}
else
{ /* clip is in memory already */
clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */
}
#endif
queue_clip(clipbuf, clipsize, enqueue); queue_clip(clipbuf, clipsize, enqueue);