diff --git a/apps/codec_thread.c b/apps/codec_thread.c index fbcb23179d..e3f190e69c 100644 --- a/apps/codec_thread.c +++ b/apps/codec_thread.c @@ -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: diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index c8f89d9af9..1e286fafc6 100644 --- a/apps/pcmbuf.c +++ b/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) { + 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; +} diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h index 1d893f9c62..f8e69055be 100644 --- a/apps/pcmbuf.h +++ b/apps/pcmbuf.h @@ -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 diff --git a/apps/playback.c b/apps/playback.c index d3c4b46a99..fbaaa5c974 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -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 */ diff --git a/apps/playback.h b/apps/playback.h index b168770361..c0fd15e590 100644 --- a/apps/playback.h +++ b/apps/playback.h @@ -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);