1
0
Fork 0
forked from len0rd/rockbox

pcmbuf: bug fix with pcmbuf flush, code cleanup, added comments

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23608 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Jeffrey Goode 2009-11-11 07:02:18 +00:00
parent db4cab0e66
commit 0db3308cb5
4 changed files with 64 additions and 68 deletions

View file

@ -168,7 +168,7 @@ static void* codec_get_buffer(size_t *size)
return &codecbuf[codec_size]; return &codecbuf[codec_size];
} }
static bool codec_pcmbuf_insert_callback( static void codec_pcmbuf_insert_callback(
const void *ch1, const void *ch2, int count) const void *ch1, const void *ch2, int count)
{ {
const char *src[2] = { ch1, ch2 }; const char *src[2] = { ch1, ch2 };
@ -181,14 +181,14 @@ static bool codec_pcmbuf_insert_callback(
/* Prevent audio from a previous track from playing */ /* Prevent audio from a previous track from playing */
if (ci.new_track || ci.stop_codec) if (ci.new_track || ci.stop_codec)
return true; return;
while ((dest = pcmbuf_request_buffer(&out_count)) == NULL) while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
{ {
cancel_cpu_boost(); cancel_cpu_boost();
sleep(1); sleep(1);
if (ci.seek_time || ci.new_track || ci.stop_codec) if (ci.seek_time || ci.new_track || ci.stop_codec)
return true; return;
} }
/* Get the real input_size for output_size bytes, guarding /* Get the real input_size for output_size bytes, guarding
@ -196,7 +196,7 @@ static bool codec_pcmbuf_insert_callback(
inp_count = dsp_input_count(ci.dsp, out_count); inp_count = dsp_input_count(ci.dsp, out_count);
if (inp_count <= 0) if (inp_count <= 0)
return true; return;
/* Input size has grown, no error, just don't write more than length */ /* Input size has grown, no error, just don't write more than length */
if (inp_count > count) if (inp_count > count)
@ -205,14 +205,12 @@ static bool codec_pcmbuf_insert_callback(
out_count = dsp_process(ci.dsp, dest, src, inp_count); out_count = dsp_process(ci.dsp, dest, src, inp_count);
if (out_count <= 0) if (out_count <= 0)
return true; return;
pcmbuf_write_complete(out_count); pcmbuf_write_complete(out_count);
count -= inp_count; count -= inp_count;
} }
return true;
} /* codec_pcmbuf_insert_callback */ } /* codec_pcmbuf_insert_callback */
static void codec_set_elapsed_callback(unsigned long value) static void codec_set_elapsed_callback(unsigned long value)

View file

@ -121,7 +121,7 @@ struct codec_api {
void* (*codec_get_buffer)(size_t *size); void* (*codec_get_buffer)(size_t *size);
/* Insert PCM data into audio buffer for playback. Playback will start /* Insert PCM data into audio buffer for playback. Playback will start
automatically. */ automatically. */
bool (*pcmbuf_insert)(const void *ch1, const void *ch2, int count); void (*pcmbuf_insert)(const void *ch1, const void *ch2, int count);
/* Set song position in WPS (value in ms). */ /* Set song position in WPS (value in ms). */
void (*set_elapsed)(unsigned long value); void (*set_elapsed)(unsigned long value);

View file

@ -119,7 +119,7 @@ static struct chunkdesc *mix_chunk IDATA_ATTR;
static size_t pcmbuf_mix_sample IDATA_ATTR; static size_t pcmbuf_mix_sample IDATA_ATTR;
static bool low_latency_mode = false; static bool low_latency_mode = false;
static bool flush_pcmbuf; static bool flush_pcmbuf = false;
#ifdef HAVE_PRIORITY_SCHEDULING #ifdef HAVE_PRIORITY_SCHEDULING
static int codec_thread_priority = PRIORITY_PLAYBACK; static int codec_thread_priority = PRIORITY_PLAYBACK;
@ -129,14 +129,14 @@ extern unsigned int codec_thread_id;
/* Helpful macros for use in conditionals this assumes some of the above /* Helpful macros for use in conditionals this assumes some of the above
* static variable names */ * static variable names */
#define NEED_FLUSH(position) \ #define COMMIT_IF_NEEDED if(pcmbuffer_fillpos > PCMBUF_TARGET_CHUNK || \
(pcmbuffer_fillpos > PCMBUF_TARGET_CHUNK || position >= pcmbuf_size) (pcmbuffer_pos + pcmbuffer_fillpos) >= pcmbuf_size) commit_chunk(false)
#define LOW_DATA(quarter_secs) \ #define LOW_DATA(quarter_secs) \
(pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs) (pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs)
#ifdef HAVE_CROSSFADE #ifdef HAVE_CROSSFADE
static void crossfade_start(void); static void crossfade_start(void);
static void flush_crossfade(char *buf, size_t length); static void write_to_crossfade(char *buf, size_t length);
static void pcmbuf_finish_crossfade_enable(void); static void pcmbuf_finish_crossfade_enable(void);
#endif #endif
@ -181,12 +181,10 @@ static bool show_desc(char *caller)
#endif #endif
/* Commit PCM data */ /** Accept new PCM data */
/** /* Commit PCM buffer samples as a new chunk for playback */
* Commit samples waiting to the pcm buffer. static void commit_chunk(bool flush_next_time)
*/
static void commit_chunk(void)
{ {
if (!pcmbuffer_fillpos) if (!pcmbuffer_fillpos)
return; return;
@ -219,7 +217,7 @@ static void commit_chunk(void)
{ {
if (flush_pcmbuf) if (flush_pcmbuf)
{ {
/* flush! discard all data after the currently playing chunk, /* Flush! Discard all data after the currently playing chunk,
and make the current chunk play next */ and make the current chunk play next */
write_end_chunk->link = read_chunk->link; write_end_chunk->link = read_chunk->link;
read_chunk->link = pcmbuf_current; read_chunk->link = pcmbuf_current;
@ -228,7 +226,6 @@ static void commit_chunk(void)
write_end_chunk = write_end_chunk->link; write_end_chunk = write_end_chunk->link;
pcmbuf_unplayed_bytes -= write_end_chunk->size; pcmbuf_unplayed_bytes -= write_end_chunk->size;
} }
flush_pcmbuf = false;
} }
/* If there is already a read buffer setup, add to it */ /* If there is already a read buffer setup, add to it */
else else
@ -240,6 +237,11 @@ static void commit_chunk(void)
read_chunk = pcmbuf_current; read_chunk = pcmbuf_current;
} }
/* If flush_next_time is true, then the current chunk will be thrown out
* and the next chunk to be committed will be the next to be played.
* This is used to empty the PCM buffer for a track change. */
flush_pcmbuf = flush_next_time;
/* This is now the last buffer to read */ /* This is now the last buffer to read */
read_end_chunk = pcmbuf_current; read_end_chunk = pcmbuf_current;
@ -254,6 +256,7 @@ static void commit_chunk(void)
DISPLAY_DESC("commit_chunk"); DISPLAY_DESC("commit_chunk");
} }
/* Set priority of the codec thread */
#ifdef HAVE_PRIORITY_SCHEDULING #ifdef HAVE_PRIORITY_SCHEDULING
static void boost_codec_thread(bool boost) static void boost_codec_thread(bool boost)
{ {
@ -282,6 +285,8 @@ static void boost_codec_thread(bool boost)
#define boost_codec_thread(boost) do{}while(0) #define boost_codec_thread(boost) do{}while(0)
#endif /* HAVE_PRIORITY_SCHEDULING */ #endif /* HAVE_PRIORITY_SCHEDULING */
/* Return true if the PCM buffer is able to receive new data.
* Also maintain buffer level above the watermark. */
static bool prepare_insert(size_t length) static bool prepare_insert(size_t length)
{ {
if (low_latency_mode) if (low_latency_mode)
@ -295,7 +300,7 @@ static bool prepare_insert(size_t length)
if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK) if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK)
return false; return false;
/* boost CPU if needed to either fill to watermark or for pre-buffer */ /* Maintain the buffer level above the watermark */
if (pcm_is_playing()) if (pcm_is_playing())
{ {
/* Only codec thread initiates boost - voice boosts the cpu when playing /* Only codec thread initiates boost - voice boosts the cpu when playing
@ -328,9 +333,10 @@ static bool prepare_insert(size_t length)
} }
else /* pcm_is_playing */ else /* pcm_is_playing */
{ {
/* Boost CPU for pre-buffer */
trigger_cpu_boost(); trigger_cpu_boost();
/* Pre-buffer up to watermark */ /* If pre-buffered to the watermark, start playback */
#if MEMORYSIZE > 2 #if MEMORYSIZE > 2
if (!LOW_DATA(4)) if (!LOW_DATA(4))
#else #else
@ -346,18 +352,23 @@ static bool prepare_insert(size_t length)
return true; return true;
} }
/* Request space in the buffer for writing output samples */
void *pcmbuf_request_buffer(int *count) void *pcmbuf_request_buffer(int *count)
{ {
#ifdef HAVE_CROSSFADE #ifdef HAVE_CROSSFADE
/* we're going to crossfade to a new track, which is now on its way */
if (crossfade_track_change_started) if (crossfade_track_change_started)
crossfade_start(); crossfade_start();
if (crossfade_active) { /* crossfade has begun, put the new track samples in fadebuf */
if (crossfade_active)
{
*count = MIN(*count, PCMBUF_MIX_CHUNK/4); *count = MIN(*count, PCMBUF_MIX_CHUNK/4);
return fadebuf; return fadebuf;
} }
else else
#endif #endif
/* if possible, reserve room in the PCM buffer for new samples */
{ {
if(prepare_insert(*count << 2)) if(prepare_insert(*count << 2))
{ {
@ -369,38 +380,34 @@ void *pcmbuf_request_buffer(int *count)
} }
else else
{ {
/* Flush and wrap the buffer */ /* Wrap the buffer, the new samples go at the beginning */
commit_chunk(); commit_chunk(false);
pcmbuffer_pos = 0; pcmbuffer_pos = 0;
return &pcmbuffer[0]; return &pcmbuffer[0];
} }
} }
} }
/* PCM buffer not ready to receive new data yet */
return NULL; return NULL;
} }
/* Handle new samples to the buffer */
void pcmbuf_write_complete(int count) void pcmbuf_write_complete(int count)
{ {
size_t length = (size_t)(unsigned int)count << 2; size_t length = (size_t)(unsigned int)count << 2;
#ifdef HAVE_CROSSFADE #ifdef HAVE_CROSSFADE
if (crossfade_active) if (crossfade_active)
{ write_to_crossfade(fadebuf, length);
flush_crossfade(fadebuf, length);
if (!(crossfade_fade_in_rem || crossfade_chunk))
crossfade_active = false;
}
else else
#endif #endif
{ {
pcmbuffer_fillpos += length; pcmbuffer_fillpos += length;
COMMIT_IF_NEEDED;
if (NEED_FLUSH(pcmbuffer_pos + pcmbuffer_fillpos))
commit_chunk();
} }
} }
/* Init */ /** Init */
static inline void init_pcmbuffers(void) static inline void init_pcmbuffers(void)
{ {
@ -466,14 +473,13 @@ size_t pcmbuf_init(unsigned char *bufend)
} }
/* Track change */ /** Track change */
void pcmbuf_start_track_change(bool auto_skip) void pcmbuf_start_track_change(bool auto_skip)
{ {
bool crossfade = false; bool crossfade = false;
#ifdef HAVE_CROSSFADE #ifdef HAVE_CROSSFADE
/* Determine whether this track change needs to crossfade */ /* Determine whether this track change needs to crossfade */
if(crossfade_enabled && !pcmbuf_is_crossfade_active()) if(crossfade_enabled && !pcmbuf_is_crossfade_active())
{ {
switch(global_settings.crossfade) switch(global_settings.crossfade)
@ -524,8 +530,7 @@ void pcmbuf_start_track_change(bool auto_skip)
/* Not enough data, or not crossfading, flush the old data instead */ /* Not enough data, or not crossfading, flush the old data instead */
if (LOW_DATA(2) || !crossfade || low_latency_mode) if (LOW_DATA(2) || !crossfade || low_latency_mode)
{ {
flush_pcmbuf = true; commit_chunk(true);
commit_chunk();
return; return;
} }
@ -536,7 +541,7 @@ void pcmbuf_start_track_change(bool auto_skip)
crossfade_track_change_started = crossfade; crossfade_track_change_started = crossfade;
#endif #endif
} }
else /* automatic and not crossfading, so gapless track change */ else /* automatic and not crossfading, so do gapless track change */
{ {
/* The codec is moving on to the next track, but the current track will /* The codec is moving on to the next track, but the current track will
* continue to play. Set a flag to make sure the elapsed time of the * continue to play. Set a flag to make sure the elapsed time of the
@ -549,9 +554,9 @@ void pcmbuf_start_track_change(bool auto_skip)
} }
/* Playback */ /** Playback */
/** PCM driver callback /* PCM driver callback
* This function has 3 major logical parts (separated by brackets both for * This function has 3 major logical parts (separated by brackets both for
* readability and variable scoping). The first part performs the * readability and variable scoping). The first part performs the
* operations related to finishing off the last chunk we fed to the DMA. * operations related to finishing off the last chunk we fed to the DMA.
@ -598,7 +603,7 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
if (pcmbuffer_fillpos && !read_chunk) if (pcmbuffer_fillpos && !read_chunk)
{ {
logf("pcmbuf_pcm_callback: commit last samples"); logf("pcmbuf_pcm_callback: commit last samples");
commit_chunk(); commit_chunk(false);
} }
} }
@ -625,7 +630,7 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
DISPLAY_DESC("callback"); DISPLAY_DESC("callback");
} }
/* Force playback. */ /* Force playback */
void pcmbuf_play_start(void) void pcmbuf_play_start(void)
{ {
if (!pcm_is_playing() && pcmbuf_unplayed_bytes && read_chunk != NULL) if (!pcm_is_playing() && pcmbuf_unplayed_bytes && read_chunk != NULL)
@ -658,7 +663,6 @@ void pcmbuf_play_stop(void)
#endif #endif
end_of_track = false; end_of_track = false;
track_transition = false; track_transition = false;
flush_pcmbuf = false;
DISPLAY_DESC("play_stop"); DISPLAY_DESC("play_stop");
/* Can unboost the codec thread here no matter who's calling */ /* Can unboost the codec thread here no matter who's calling */
@ -675,7 +679,7 @@ void pcmbuf_pause(bool pause)
} }
/* Crossfade */ /** Crossfade */
/* Clip sample to signed 16 bit range */ /* Clip sample to signed 16 bit range */
static inline int32_t clip_sample_16(int32_t sample) static inline int32_t clip_sample_16(int32_t sample)
@ -686,9 +690,7 @@ static inline int32_t clip_sample_16(int32_t sample)
} }
#ifdef HAVE_CROSSFADE #ifdef HAVE_CROSSFADE
/** /* Completely process the crossfade fade-out effect with current PCM buffer */
* Completely process the crossfade fade out effect with current pcm buffer.
*/
static void crossfade_process_buffer(size_t fade_in_delay, static void crossfade_process_buffer(size_t fade_in_delay,
size_t fade_out_delay, size_t fade_out_rem) size_t fade_out_delay, size_t fade_out_rem)
{ {
@ -747,8 +749,8 @@ static void crossfade_process_buffer(size_t fade_in_delay,
logf("process done!"); logf("process done!");
} }
/* Initializes crossfader, calculates all necessary parameters and /* Initializes crossfader, calculates all necessary parameters and performs
* performs fade-out with the pcm buffer. */ * fade-out with the PCM buffer. */
static void crossfade_start(void) static void crossfade_start(void)
{ {
size_t crossfade_rem; size_t crossfade_rem;
@ -766,7 +768,7 @@ static void crossfade_start(void)
} }
logf("crossfade_start"); logf("crossfade_start");
commit_chunk(); commit_chunk(false);
crossfade_active = true; crossfade_active = true;
/* Initialize the crossfade buffer size to all of the buffered data that /* Initialize the crossfade buffer size to all of the buffered data that
@ -854,7 +856,8 @@ static size_t crossfade_mix(int factor, const char *buf, size_t length)
return 0; return 0;
} }
static void flush_crossfade(char *buf, size_t length) /* Perform fade-in of new track */
static void write_to_crossfade(char *buf, size_t length)
{ {
if (length) if (length)
{ {
@ -904,17 +907,13 @@ static void flush_crossfade(char *buf, size_t length)
return; return;
} }
/* Flush samples to the buffer */ /* Commit samples to the buffer */
while (!prepare_insert(length)) while (!prepare_insert(length))
sleep(1); sleep(1);
while (length > 0) while (length > 0)
{ {
COMMIT_IF_NEEDED;
size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos; size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos;
if (NEED_FLUSH(pcmbuffer_index))
{
commit_chunk();
pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos;
}
size_t copy_n = MIN(length, pcmbuf_size - pcmbuffer_index); size_t copy_n = MIN(length, pcmbuf_size - pcmbuffer_index);
memcpy(&pcmbuffer[pcmbuffer_index], buf, copy_n); memcpy(&pcmbuffer[pcmbuffer_index], buf, copy_n);
buf += copy_n; buf += copy_n;
@ -922,6 +921,9 @@ static void flush_crossfade(char *buf, size_t length)
length -= copy_n; length -= copy_n;
} }
} }
/* if no more fading-in to do, stop the crossfade */
if (!(crossfade_fade_in_rem || crossfade_chunk))
crossfade_active = false;
} }
static void pcmbuf_finish_crossfade_enable(void) static void pcmbuf_finish_crossfade_enable(void)
@ -962,7 +964,7 @@ bool pcmbuf_is_same_size(void)
#endif /* HAVE_CROSSFADE */ #endif /* HAVE_CROSSFADE */
/* Voice */ /** Voice */
/* Returns pcm buffer usage in percents (0 to 100). */ /* Returns pcm buffer usage in percents (0 to 100). */
static int pcmbuf_usage(void) static int pcmbuf_usage(void)
@ -1058,7 +1060,7 @@ void pcmbuf_write_voice_complete(int count)
} }
/* Debug menu, other metrics */ /** Debug menu, other metrics */
/* Amount of bytes left in the buffer. */ /* Amount of bytes left in the buffer. */
size_t pcmbuf_free(void) size_t pcmbuf_free(void)
@ -1072,7 +1074,7 @@ size_t pcmbuf_free(void)
else else
return (size_t) (read - write); return (size_t) (read - write);
} }
return pcmbuf_size; return pcmbuf_size - pcmbuffer_fillpos;
} }
size_t pcmbuf_get_bufsize(void) size_t pcmbuf_get_bufsize(void)
@ -1105,7 +1107,7 @@ unsigned char * pcmbuf_get_meminfo(size_t *length)
#endif #endif
/* Misc */ /** Misc */
bool pcmbuf_is_lowdata(void) bool pcmbuf_is_lowdata(void)
{ {

View file

@ -239,14 +239,12 @@ static int process_dsp(const void *ch1, const void *ch2, int count)
} }
/* Null output */ /* Null output */
static bool pcmbuf_insert_null(const void *ch1, const void *ch2, int count) static void pcmbuf_insert_null(const void *ch1, const void *ch2, int count)
{ {
if (use_dsp) process_dsp(ch1, ch2, count); if (use_dsp) process_dsp(ch1, ch2, count);
/* Prevent idle poweroff */ /* Prevent idle poweroff */
rb->reset_poweroff_timer(); rb->reset_poweroff_timer();
return true;
} }
static inline int32_t clip_sample(int32_t sample) static inline int32_t clip_sample(int32_t sample)
@ -259,7 +257,7 @@ static inline int32_t clip_sample(int32_t sample)
/* WAV output */ /* WAV output */
static bool pcmbuf_insert_wav(const void *ch1, const void *ch2, int count) static void pcmbuf_insert_wav(const void *ch1, const void *ch2, int count)
{ {
const int16_t* data1_16; const int16_t* data1_16;
const int16_t* data2_16; const int16_t* data2_16;
@ -360,8 +358,6 @@ static bool pcmbuf_insert_wav(const void *ch1, const void *ch2, int count)
wavinfo.totalsamples += count; wavinfo.totalsamples += count;
rb->write(wavinfo.fd, wavbuffer, p - wavbuffer); rb->write(wavinfo.fd, wavbuffer, p - wavbuffer);
} /* else */ } /* else */
return true;
} }
/* Set song position in WPS (value in ms). */ /* Set song position in WPS (value in ms). */