pcmbuf: clarify and simplify crossfade code, etc.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23538 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Jeffrey Goode 2009-11-05 21:59:36 +00:00
parent 013fe35992
commit 04b01e1831
5 changed files with 180 additions and 181 deletions

View file

@ -416,47 +416,7 @@ void codec_init_codec_api(void)
}
/** track change functions */
static inline void codec_crossfade_track_change(void)
{
/* Initiate automatic crossfade mode */
pcmbuf_crossfade_init(false);
/* Notify the wps that the track change starts now */
LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
}
static void codec_track_skip_done(bool was_manual)
{
/* Manual track change (always crossfade or flush audio). */
if (was_manual)
{
pcmbuf_crossfade_init(true);
LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
}
/* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
&& global_settings.crossfade != CROSSFADE_ENABLE_TRACKSKIP)
{
if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP)
{
if (global_settings.playlist_shuffle)
/* shuffle mode is on, so crossfade: */
codec_crossfade_track_change();
else
/* shuffle mode is off, so normal gapless playback */
pcmbuf_start_track_change();
}
else
/* normal crossfade: */
codec_crossfade_track_change();
}
else
/* normal gapless playback. */
pcmbuf_start_track_change();
}
/* track change */
static bool codec_load_next_track(void)
{
@ -487,7 +447,7 @@ static bool codec_load_next_track(void)
{
case Q_CODEC_REQUEST_COMPLETE:
LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
codec_track_skip_done(!automatic_skip);
pcmbuf_start_track_change(!automatic_skip);
return true;
case Q_CODEC_REQUEST_FAILED:

View file

@ -49,6 +49,17 @@ static inline int32_t clip_sample_16(int32_t sample)
return sample;
}
#define PCMBUF_TARGET_CHUNK 32768 /* This is the target fill size of chunks
on the pcm buffer */
#define PCMBUF_MINAVG_CHUNK 24576 /* This is the minimum average size of
chunks on the pcm buffer (or we run out
of buffer descriptors, which is
non-fatal) */
#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than
this to the DMA */
#define PCMBUF_MIX_CHUNK 8192 /* This is the maximum size of one packet
for mixing (crossfade or voice) */
#if MEMORYSIZE > 2
/* Keep watermark high for iPods at least (2s) */
#define PCMBUF_WATERMARK (NATIVE_FREQUENCY * 4 * 2)
@ -135,6 +146,10 @@ static bool prepare_insert(size_t length);
static void pcmbuf_under_watermark(bool under);
static bool pcmbuf_flush_fillpos(void);
static bool pcmbuf_crossfade_init(bool manual_skip);
static bool pcmbuf_is_crossfade_enabled(void);
static void pcmbuf_finish_crossfade_enable(void);
/**************************************/
@ -181,7 +196,7 @@ static bool show_desc(char *caller)
* still playing. Set flags to make sure the elapsed time of the current
* track is updated properly, and mark the currently written chunk as the
* last one in the track. */
void pcmbuf_start_track_change(void)
static void pcmbuf_gapless_track_change(void)
{
/* we're starting a track transition */
track_transition = true;
@ -190,6 +205,44 @@ void pcmbuf_start_track_change(void)
end_of_track = true;
}
static void pcmbuf_crossfade_track_change(void)
{
/* Initiate automatic crossfade mode */
pcmbuf_crossfade_init(false);
/* Notify the wps that the track change starts now */
audio_post_track_change(false);
}
void pcmbuf_start_track_change(bool manual_skip)
{
/* Manual track change (always crossfade or flush audio). */
if (manual_skip)
{
pcmbuf_crossfade_init(true);
audio_post_track_change(false);
}
/* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
&& global_settings.crossfade != CROSSFADE_ENABLE_TRACKSKIP)
{
if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP)
{
if (global_settings.playlist_shuffle)
/* shuffle mode is on, so crossfade: */
pcmbuf_crossfade_track_change();
else
/* shuffle mode is off, so normal gapless playback */
pcmbuf_gapless_track_change();
}
else
/* normal crossfade: */
pcmbuf_crossfade_track_change();
}
else
/* normal gapless playback. */
pcmbuf_gapless_track_change();
}
/* Called when the last chunk in the track has been played */
static void pcmbuf_finish_track_change(void)
{
@ -198,7 +251,7 @@ static void pcmbuf_finish_track_change(void)
track_transition = false;
/* notify playback that the track has just finished */
audio_post_track_change();
audio_post_track_change(true);
}
@ -274,15 +327,6 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
DISPLAY_DESC("callback");
}
static void pcmbuf_set_watermark_bytes(void)
{
pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ?
/* If crossfading, try to keep the buffer full other than 1 second */
(pcmbuf_size - (NATIVE_FREQUENCY * 4 * 1)) :
/* Otherwise, just use the default */
PCMBUF_WATERMARK;
}
/* This is really just part of pcmbuf_flush_fillpos, but is easier to keep
* in a separate function for the moment */
static inline void pcmbuf_add_chunk(void)
@ -330,6 +374,28 @@ static inline void pcmbuf_add_chunk(void)
DISPLAY_DESC("add_chunk");
}
/**
* Commit samples waiting to the pcm buffer.
*/
static bool pcmbuf_flush_fillpos(void)
{
if (audiobuffer_fillpos) {
/* Never use the last buffer descriptor */
while (pcmbuf_write == pcmbuf_write_end) {
/* If this happens, something is being stupid */
if (!pcm_is_playing()) {
logf("pcmbuf_flush_fillpos error");
pcmbuf_play_start();
}
/* Let approximately one chunk of data playback */
sleep(HZ*PCMBUF_TARGET_CHUNK/(NATIVE_FREQUENCY*4));
}
pcmbuf_add_chunk();
return true;
}
return false;
}
#ifdef HAVE_PRIORITY_SCHEDULING
static void boost_codec_thread(bool boost)
{
@ -431,7 +497,7 @@ inline size_t pcmbuf_free(void)
return pcmbuf_size;
}
bool pcmbuf_crossfade_init(bool manual_skip)
static bool pcmbuf_crossfade_init(bool manual_skip)
{
/* Can't do two crossfades at once and, no fade if pcm is off now */
if (crossfade_init || crossfade_active || !pcm_is_playing())
@ -542,11 +608,20 @@ static char *pcmbuf_calc_audiobuffer_ptr(size_t bufsize)
bool pcmbuf_is_same_size(void)
{
bool same_size;
if (audiobuffer == NULL)
return true; /* Not set up yet even once so always */
size_t bufsize = pcmbuf_get_next_required_pcmbuf_size();
return pcmbuf_calc_audiobuffer_ptr(bufsize) == audiobuffer;
same_size = true; /* Not set up yet even once so always */
else
{
size_t bufsize = pcmbuf_get_next_required_pcmbuf_size();
same_size = pcmbuf_calc_audiobuffer_ptr(bufsize) == audiobuffer;
}
if (same_size)
pcmbuf_finish_crossfade_enable();
return same_size;
}
/* Initialize the pcmbuffer the structure looks like this:
@ -566,7 +641,7 @@ size_t pcmbuf_init(unsigned char *bufend)
end_of_track = false;
track_transition = false;
pcmbuf_crossfade_enable_finished();
pcmbuf_finish_crossfade_enable();
pcmbuf_play_stop();
@ -606,28 +681,6 @@ void pcmbuf_play_start(void)
}
}
/**
* Commit samples waiting to the pcm buffer.
*/
static bool pcmbuf_flush_fillpos(void)
{
if (audiobuffer_fillpos) {
/* Never use the last buffer descriptor */
while (pcmbuf_write == pcmbuf_write_end) {
/* If this happens, something is being stupid */
if (!pcm_is_playing()) {
logf("pcmbuf_flush_fillpos error");
pcmbuf_play_start();
}
/* Let approximately one chunk of data playback */
sleep(HZ*PCMBUF_TARGET_CHUNK/(NATIVE_FREQUENCY*4));
}
pcmbuf_add_chunk();
return true;
}
return false;
}
/**
* Low memory targets don't have crossfade, so don't compile crossfade
* specific code in order to save some memory. */
@ -907,7 +960,7 @@ static bool prepare_insert(size_t length)
return true;
}
void* pcmbuf_request_buffer(int *count)
void *pcmbuf_request_buffer(int *count)
{
#ifdef HAVE_CROSSFADE
if (crossfade_init)
@ -943,38 +996,6 @@ void* pcmbuf_request_buffer(int *count)
}
}
void * pcmbuf_request_voice_buffer(int *count)
{
/* A get-it-to-work-for-now hack (audio status could change by
completion) */
if (audio_status() & AUDIO_STATUS_PLAY)
{
if (pcmbuf_read == NULL)
{
return NULL;
}
else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 &&
(pcmbuf_mix_chunk || pcmbuf_read->link))
{
*count = MIN(*count, PCMBUF_MIX_CHUNK/4);
return voicebuf;
}
else
{
return NULL;
}
}
else
{
return pcmbuf_request_buffer(count);
}
}
bool pcmbuf_is_crossfade_active(void)
{
return crossfade_active || crossfade_init;
}
void pcmbuf_write_complete(int count)
{
size_t length = (size_t)(unsigned int)count << 2;
@ -1098,12 +1119,12 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
#endif /* HAVE_HARDWARE_BEEP */
/* Returns pcm buffer usage in percents (0 to 100). */
int pcmbuf_usage(void)
static int pcmbuf_usage(void)
{
return pcmbuf_unplayed_bytes * 100 / pcmbuf_size;
}
int pcmbuf_mix_free(void)
static int pcmbuf_mix_free(void)
{
if (pcmbuf_mix_chunk)
{
@ -1117,6 +1138,33 @@ int pcmbuf_mix_free(void)
return 100;
}
void *pcmbuf_request_voice_buffer(int *count)
{
/* A get-it-to-work-for-now hack (audio status could change by
completion) */
if (audio_status() & AUDIO_STATUS_PLAY)
{
if (pcmbuf_read == NULL)
{
return NULL;
}
else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 &&
(pcmbuf_mix_chunk || pcmbuf_read->link))
{
*count = MIN(*count, PCMBUF_MIX_CHUNK/4);
return voicebuf;
}
else
{
return NULL;
}
}
else
{
return pcmbuf_request_buffer(count);
}
}
void pcmbuf_write_voice_complete(int count)
{
/* A get-it-to-work-for-now hack (audio status could have changed) */
@ -1163,23 +1211,33 @@ void pcmbuf_write_voice_complete(int count)
}
}
void pcmbuf_crossfade_enable(bool on_off)
void pcmbuf_request_crossfade_enable(bool on_off)
{
/* Next setting to be used, not applied now */
crossfade_enabled_pending = on_off;
}
void pcmbuf_crossfade_enable_finished(void)
static void pcmbuf_finish_crossfade_enable(void)
{
/* Copy the pending setting over now */
crossfade_enabled = crossfade_enabled_pending;
pcmbuf_set_watermark_bytes();
pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ?
/* If crossfading, try to keep the buffer full other than 1 second */
(pcmbuf_size - (NATIVE_FREQUENCY * 4 * 1)) :
/* Otherwise, just use the default */
PCMBUF_WATERMARK;
}
bool pcmbuf_is_crossfade_enabled(void)
static bool pcmbuf_is_crossfade_enabled(void)
{
if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE)
return global_settings.playlist_shuffle;
return crossfade_enabled;
}
bool pcmbuf_is_crossfade_active(void)
{
return crossfade_active || crossfade_init;
}

View file

@ -21,58 +21,37 @@
#ifndef PCMBUF_H
#define PCMBUF_H
#define PCMBUF_TARGET_CHUNK 32768 /* This is the target fill size of chunks
on the pcm buffer */
#define PCMBUF_MINAVG_CHUNK 24576 /* This is the minimum average size of
chunks on the pcm buffer (or we run out
of buffer descriptors, which is
non-fatal) */
#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than
this to the DMA */
#define PCMBUF_MIX_CHUNK 8192 /* This is the maximum size of one packet
for mixing (crossfade or voice) */
/* Returns true if the buffer needs to change size */
bool pcmbuf_is_same_size(void);
/* playback */
size_t pcmbuf_init(unsigned char *bufend);
/* Size in bytes used by the pcmbuffer */
size_t pcmbuf_get_bufsize(void);
#ifdef ROCKBOX_HAS_LOGF
/* just used for logging for now */
unsigned char * pcmbuf_get_meminfo(size_t *length);
#endif
void pcmbuf_pause(bool pause);
void pcmbuf_play_stop(void);
bool pcmbuf_is_crossfade_active(void);
/* These functions are for playing chained buffers of PCM data */
#if defined(HAVE_ADJUSTABLE_CPU_FREQ)
void pcmbuf_boost(bool state);
void pcmbuf_set_boost_mode(bool state);
#else
#define pcmbuf_boost(state) do { } while(0)
#define pcmbuf_set_boost_mode(state) do { } while(0)
#endif
bool pcmbuf_is_lowdata(void);
void pcmbuf_play_start(void);
bool pcmbuf_crossfade_init(bool manual_skip);
void pcmbuf_start_track_change(void);
size_t pcmbuf_free(void);
unsigned long pcmbuf_get_latency(void);
void pcmbuf_set_low_latency(bool state);
void * pcmbuf_request_buffer(int *count);
void pcmbuf_play_stop(void);
void pcmbuf_pause(bool pause);
void pcmbuf_start_track_change(bool manual_skip);
void *pcmbuf_request_buffer(int *count);
void pcmbuf_write_complete(int count);
void * pcmbuf_request_voice_buffer(int *count);
void pcmbuf_write_voice_complete(int count);
bool pcmbuf_is_crossfade_enabled(void);
void pcmbuf_crossfade_enable(bool on_off);
void pcmbuf_crossfade_enable_finished(void);
int pcmbuf_usage(void);
int pcmbuf_mix_free(void);
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
int pcmbuf_used_descs(void);
/* crossfade */
bool pcmbuf_is_crossfade_active(void);
void pcmbuf_request_crossfade_enable(bool on_off);
bool pcmbuf_is_same_size(void);
/* voice */
void *pcmbuf_request_voice_buffer(int *count);
void pcmbuf_write_voice_complete(int count);
/* debug menu, other metrics */
size_t pcmbuf_free(void);
size_t pcmbuf_get_bufsize(void);
int pcmbuf_descs(void);
int pcmbuf_used_descs(void);
#ifdef ROCKBOX_HAS_LOGF
unsigned char *pcmbuf_get_meminfo(size_t *length);
#endif
/* misc */
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
bool pcmbuf_is_lowdata(void);
void pcmbuf_set_low_latency(bool state);
unsigned long pcmbuf_get_latency(void);
#endif

View file

@ -246,10 +246,18 @@ void audio_pcmbuf_position_callback(size_t size)
/* Post message from pcmbuf that the end of the previous track
* has just been played. */
void audio_post_track_change(void)
void audio_post_track_change(bool pcmbuf)
{
LOGFQUEUE("pcmbuf > pcmbuf Q_AUDIO_TRACK_CHANGED");
queue_post(&pcmbuf_queue, Q_AUDIO_TRACK_CHANGED, 0);
if (pcmbuf)
{
LOGFQUEUE("pcmbuf > pcmbuf Q_AUDIO_TRACK_CHANGED");
queue_post(&pcmbuf_queue, Q_AUDIO_TRACK_CHANGED, 0);
}
else
{
LOGFQUEUE("pcmbuf > audio Q_AUDIO_TRACK_CHANGED");
queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
}
}
/* Scan the pcmbuf queue and return true if a message pulled.
@ -814,18 +822,12 @@ void audio_set_crossfade(int enable)
size_t size;
/* Tell it the next setting to use */
pcmbuf_crossfade_enable(enable);
pcmbuf_request_crossfade_enable(enable);
/* Return if size hasn't changed or this is too early to determine
which in the second case there's no way we could be playing
anything at all */
if (pcmbuf_is_same_size())
{
/* This function is a copout and just syncs some variables -
to be removed at a later date */
pcmbuf_crossfade_enable_finished();
return;
}
if (pcmbuf_is_same_size()) return;
offset = 0;
was_playing = playing;
@ -2058,7 +2060,7 @@ void audio_init(void)
#endif
/* Set crossfade setting for next buffer init which should be about... */
pcmbuf_crossfade_enable(global_settings.crossfade);
pcmbuf_request_crossfade_enable(global_settings.crossfade);
/* initialize the buffering system */

View file

@ -70,7 +70,7 @@ enum
bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
size_t audio_get_filebuflen(void);
void audio_pcmbuf_position_callback(size_t size) ICODE_ATTR;
void audio_post_track_change(void);
void audio_post_track_change(bool pcmbuf);
int get_audio_hid(void);
int *get_codec_hid(void);
void audio_set_prev_elapsed(unsigned long setting);