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:
parent
db4cab0e66
commit
0db3308cb5
4 changed files with 64 additions and 68 deletions
|
@ -168,7 +168,7 @@ static void* codec_get_buffer(size_t *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 char *src[2] = { ch1, ch2 };
|
||||
|
@ -181,14 +181,14 @@ static bool codec_pcmbuf_insert_callback(
|
|||
|
||||
/* Prevent audio from a previous track from playing */
|
||||
if (ci.new_track || ci.stop_codec)
|
||||
return true;
|
||||
return;
|
||||
|
||||
while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
|
||||
{
|
||||
cancel_cpu_boost();
|
||||
sleep(1);
|
||||
if (ci.seek_time || ci.new_track || ci.stop_codec)
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
if (inp_count <= 0)
|
||||
return true;
|
||||
return;
|
||||
|
||||
/* Input size has grown, no error, just don't write more than length */
|
||||
if (inp_count > count)
|
||||
|
@ -205,14 +205,12 @@ static bool codec_pcmbuf_insert_callback(
|
|||
out_count = dsp_process(ci.dsp, dest, src, inp_count);
|
||||
|
||||
if (out_count <= 0)
|
||||
return true;
|
||||
return;
|
||||
|
||||
pcmbuf_write_complete(out_count);
|
||||
|
||||
count -= inp_count;
|
||||
}
|
||||
|
||||
return true;
|
||||
} /* codec_pcmbuf_insert_callback */
|
||||
|
||||
static void codec_set_elapsed_callback(unsigned long value)
|
||||
|
|
|
@ -121,7 +121,7 @@ struct codec_api {
|
|||
void* (*codec_get_buffer)(size_t *size);
|
||||
/* Insert PCM data into audio buffer for playback. Playback will start
|
||||
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). */
|
||||
void (*set_elapsed)(unsigned long value);
|
||||
|
||||
|
|
108
apps/pcmbuf.c
108
apps/pcmbuf.c
|
@ -119,7 +119,7 @@ static struct chunkdesc *mix_chunk IDATA_ATTR;
|
|||
static size_t pcmbuf_mix_sample IDATA_ATTR;
|
||||
|
||||
static bool low_latency_mode = false;
|
||||
static bool flush_pcmbuf;
|
||||
static bool flush_pcmbuf = false;
|
||||
|
||||
#ifdef HAVE_PRIORITY_SCHEDULING
|
||||
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
|
||||
* static variable names */
|
||||
#define NEED_FLUSH(position) \
|
||||
(pcmbuffer_fillpos > PCMBUF_TARGET_CHUNK || position >= pcmbuf_size)
|
||||
#define COMMIT_IF_NEEDED if(pcmbuffer_fillpos > PCMBUF_TARGET_CHUNK || \
|
||||
(pcmbuffer_pos + pcmbuffer_fillpos) >= pcmbuf_size) commit_chunk(false)
|
||||
#define LOW_DATA(quarter_secs) \
|
||||
(pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs)
|
||||
|
||||
#ifdef HAVE_CROSSFADE
|
||||
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);
|
||||
#endif
|
||||
|
||||
|
@ -181,12 +181,10 @@ static bool show_desc(char *caller)
|
|||
#endif
|
||||
|
||||
|
||||
/* Commit PCM data */
|
||||
/** Accept new PCM data */
|
||||
|
||||
/**
|
||||
* Commit samples waiting to the pcm buffer.
|
||||
*/
|
||||
static void commit_chunk(void)
|
||||
/* Commit PCM buffer samples as a new chunk for playback */
|
||||
static void commit_chunk(bool flush_next_time)
|
||||
{
|
||||
if (!pcmbuffer_fillpos)
|
||||
return;
|
||||
|
@ -219,7 +217,7 @@ static void commit_chunk(void)
|
|||
{
|
||||
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 */
|
||||
write_end_chunk->link = read_chunk->link;
|
||||
read_chunk->link = pcmbuf_current;
|
||||
|
@ -228,7 +226,6 @@ static void commit_chunk(void)
|
|||
write_end_chunk = write_end_chunk->link;
|
||||
pcmbuf_unplayed_bytes -= write_end_chunk->size;
|
||||
}
|
||||
flush_pcmbuf = false;
|
||||
}
|
||||
/* If there is already a read buffer setup, add to it */
|
||||
else
|
||||
|
@ -240,6 +237,11 @@ static void commit_chunk(void)
|
|||
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 */
|
||||
read_end_chunk = pcmbuf_current;
|
||||
|
||||
|
@ -254,6 +256,7 @@ static void commit_chunk(void)
|
|||
DISPLAY_DESC("commit_chunk");
|
||||
}
|
||||
|
||||
/* Set priority of the codec thread */
|
||||
#ifdef HAVE_PRIORITY_SCHEDULING
|
||||
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)
|
||||
#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)
|
||||
{
|
||||
if (low_latency_mode)
|
||||
|
@ -295,7 +300,7 @@ static bool prepare_insert(size_t length)
|
|||
if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK)
|
||||
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())
|
||||
{
|
||||
/* 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 */
|
||||
{
|
||||
/* Boost CPU for pre-buffer */
|
||||
trigger_cpu_boost();
|
||||
|
||||
/* Pre-buffer up to watermark */
|
||||
/* If pre-buffered to the watermark, start playback */
|
||||
#if MEMORYSIZE > 2
|
||||
if (!LOW_DATA(4))
|
||||
#else
|
||||
|
@ -346,18 +352,23 @@ static bool prepare_insert(size_t length)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Request space in the buffer for writing output samples */
|
||||
void *pcmbuf_request_buffer(int *count)
|
||||
{
|
||||
#ifdef HAVE_CROSSFADE
|
||||
/* we're going to crossfade to a new track, which is now on its way */
|
||||
if (crossfade_track_change_started)
|
||||
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);
|
||||
return fadebuf;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
/* if possible, reserve room in the PCM buffer for new samples */
|
||||
{
|
||||
if(prepare_insert(*count << 2))
|
||||
{
|
||||
|
@ -369,38 +380,34 @@ void *pcmbuf_request_buffer(int *count)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* Flush and wrap the buffer */
|
||||
commit_chunk();
|
||||
/* Wrap the buffer, the new samples go at the beginning */
|
||||
commit_chunk(false);
|
||||
pcmbuffer_pos = 0;
|
||||
return &pcmbuffer[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* PCM buffer not ready to receive new data yet */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Handle new samples to the buffer */
|
||||
void pcmbuf_write_complete(int count)
|
||||
{
|
||||
size_t length = (size_t)(unsigned int)count << 2;
|
||||
#ifdef HAVE_CROSSFADE
|
||||
if (crossfade_active)
|
||||
{
|
||||
flush_crossfade(fadebuf, length);
|
||||
if (!(crossfade_fade_in_rem || crossfade_chunk))
|
||||
crossfade_active = false;
|
||||
}
|
||||
write_to_crossfade(fadebuf, length);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
pcmbuffer_fillpos += length;
|
||||
|
||||
if (NEED_FLUSH(pcmbuffer_pos + pcmbuffer_fillpos))
|
||||
commit_chunk();
|
||||
COMMIT_IF_NEEDED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Init */
|
||||
/** Init */
|
||||
|
||||
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)
|
||||
{
|
||||
bool crossfade = false;
|
||||
#ifdef HAVE_CROSSFADE
|
||||
/* Determine whether this track change needs to crossfade */
|
||||
|
||||
if(crossfade_enabled && !pcmbuf_is_crossfade_active())
|
||||
{
|
||||
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 */
|
||||
if (LOW_DATA(2) || !crossfade || low_latency_mode)
|
||||
{
|
||||
flush_pcmbuf = true;
|
||||
commit_chunk();
|
||||
commit_chunk(true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -536,7 +541,7 @@ void pcmbuf_start_track_change(bool auto_skip)
|
|||
crossfade_track_change_started = crossfade;
|
||||
#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
|
||||
* 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
|
||||
* readability and variable scoping). The first part performs the
|
||||
* 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)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
/* Force playback. */
|
||||
/* Force playback */
|
||||
void pcmbuf_play_start(void)
|
||||
{
|
||||
if (!pcm_is_playing() && pcmbuf_unplayed_bytes && read_chunk != NULL)
|
||||
|
@ -658,7 +663,6 @@ void pcmbuf_play_stop(void)
|
|||
#endif
|
||||
end_of_track = false;
|
||||
track_transition = false;
|
||||
flush_pcmbuf = false;
|
||||
DISPLAY_DESC("play_stop");
|
||||
|
||||
/* 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 */
|
||||
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
|
||||
/**
|
||||
* 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,
|
||||
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!");
|
||||
}
|
||||
|
||||
/* Initializes crossfader, calculates all necessary parameters and
|
||||
* performs fade-out with the pcm buffer. */
|
||||
/* Initializes crossfader, calculates all necessary parameters and performs
|
||||
* fade-out with the PCM buffer. */
|
||||
static void crossfade_start(void)
|
||||
{
|
||||
size_t crossfade_rem;
|
||||
|
@ -766,7 +768,7 @@ static void crossfade_start(void)
|
|||
}
|
||||
|
||||
logf("crossfade_start");
|
||||
commit_chunk();
|
||||
commit_chunk(false);
|
||||
crossfade_active = true;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -904,17 +907,13 @@ static void flush_crossfade(char *buf, size_t length)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Flush samples to the buffer */
|
||||
/* Commit samples to the buffer */
|
||||
while (!prepare_insert(length))
|
||||
sleep(1);
|
||||
while (length > 0)
|
||||
{
|
||||
COMMIT_IF_NEEDED;
|
||||
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);
|
||||
memcpy(&pcmbuffer[pcmbuffer_index], buf, copy_n);
|
||||
buf += copy_n;
|
||||
|
@ -922,6 +921,9 @@ static void flush_crossfade(char *buf, size_t length)
|
|||
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)
|
||||
|
@ -962,7 +964,7 @@ bool pcmbuf_is_same_size(void)
|
|||
#endif /* HAVE_CROSSFADE */
|
||||
|
||||
|
||||
/* Voice */
|
||||
/** Voice */
|
||||
|
||||
/* Returns pcm buffer usage in percents (0 to 100). */
|
||||
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. */
|
||||
size_t pcmbuf_free(void)
|
||||
|
@ -1072,7 +1074,7 @@ size_t pcmbuf_free(void)
|
|||
else
|
||||
return (size_t) (read - write);
|
||||
}
|
||||
return pcmbuf_size;
|
||||
return pcmbuf_size - pcmbuffer_fillpos;
|
||||
}
|
||||
|
||||
size_t pcmbuf_get_bufsize(void)
|
||||
|
@ -1105,7 +1107,7 @@ unsigned char * pcmbuf_get_meminfo(size_t *length)
|
|||
#endif
|
||||
|
||||
|
||||
/* Misc */
|
||||
/** Misc */
|
||||
|
||||
bool pcmbuf_is_lowdata(void)
|
||||
{
|
||||
|
|
|
@ -239,14 +239,12 @@ static int process_dsp(const void *ch1, const void *ch2, int count)
|
|||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Prevent idle poweroff */
|
||||
rb->reset_poweroff_timer();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int32_t clip_sample(int32_t sample)
|
||||
|
@ -259,7 +257,7 @@ static inline int32_t clip_sample(int32_t sample)
|
|||
|
||||
|
||||
/* 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* data2_16;
|
||||
|
@ -360,8 +358,6 @@ static bool pcmbuf_insert_wav(const void *ch1, const void *ch2, int count)
|
|||
wavinfo.totalsamples += count;
|
||||
rb->write(wavinfo.fd, wavbuffer, p - wavbuffer);
|
||||
} /* else */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Set song position in WPS (value in ms). */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue