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:
parent
013fe35992
commit
04b01e1831
5 changed files with 180 additions and 181 deletions
|
@ -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:
|
||||
|
|
210
apps/pcmbuf.c
210
apps/pcmbuf.c
|
@ -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)
|
||||
{
|
||||
if (audiobuffer == NULL)
|
||||
return true; /* Not set up yet even once so always */
|
||||
bool same_size;
|
||||
|
||||
if (audiobuffer == NULL)
|
||||
same_size = true; /* Not set up yet even once so always */
|
||||
else
|
||||
{
|
||||
size_t bufsize = pcmbuf_get_next_required_pcmbuf_size();
|
||||
return pcmbuf_calc_audiobuffer_ptr(bufsize) == audiobuffer;
|
||||
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. */
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
void pcmbuf_play_start(void);
|
||||
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);
|
||||
|
||||
/* 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
|
||||
/* 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_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);
|
||||
/* misc */
|
||||
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
|
||||
|
||||
int pcmbuf_used_descs(void);
|
||||
int pcmbuf_descs(void);
|
||||
bool pcmbuf_is_lowdata(void);
|
||||
void pcmbuf_set_low_latency(bool state);
|
||||
unsigned long pcmbuf_get_latency(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -246,11 +246,19 @@ 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)
|
||||
{
|
||||
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.
|
||||
* Permissible Context(s): Thread
|
||||
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue