Commit work started in FS#12153 to put timing/position information in PCM

buffer chunks.

* Samples and position indication is closely associated with audio data
  instead of compensating by a latency constant. Alleviates problems with
  using the elapsed as a track indicator where it could be off by several
  steps.

* Timing is accurate throughout track even if resampling for pitch shift,
  whereas before it updated during transition latency at the normal 1:1 rate.

* Simpler PCM buffer with a constant chunk size, no linked lists.

In converting crossfade, a minor change was made to not change the WPS until
the fade-in of the incoming track, whereas before it would change upon the
start of the fade-out of the outgoing track possibly having the WPS change
with far too much lead time.

Codec changes are to set elapsed times *before* writing next PCM frame because
 time and position data last set are saved in the next committed PCM chunk. 


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30366 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2011-08-28 07:45:35 +00:00
parent 463b3ed8b2
commit 7ad2cad173
32 changed files with 969 additions and 830 deletions

View file

@ -77,9 +77,10 @@ struct codec_load_info
static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */ static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */
/* Private interfaces to main playback control */ /* Private interfaces to main playback control */
extern void audio_codec_update_elapsed(unsigned long value); extern void audio_codec_update_elapsed(unsigned long elapsed);
extern void audio_codec_update_offset(size_t value); extern void audio_codec_update_offset(size_t offset);
extern void audio_queue_post(long id, intptr_t data); extern void audio_codec_complete(int status);
extern void audio_codec_seek_complete(void);
extern struct codec_api ci; /* from codecs.c */ extern struct codec_api ci; /* from codecs.c */
/* Codec thread */ /* Codec thread */
@ -251,7 +252,7 @@ static void codec_pcmbuf_insert_callback(
if (out_count <= 0) if (out_count <= 0)
return; return;
pcmbuf_write_complete(out_count); pcmbuf_write_complete(out_count, ci.id3->elapsed, ci.id3->offset);
count -= inp_count; count -= inp_count;
} }
@ -334,9 +335,11 @@ static void codec_seek_complete_callback(void)
/* Clear DSP */ /* Clear DSP */
dsp_configure(ci.dsp, DSP_FLUSH, 0); dsp_configure(ci.dsp, DSP_FLUSH, 0);
/* Sync position */
audio_codec_update_offset(ci.curpos);
/* Post notification to audio thread */ /* Post notification to audio thread */
LOGFQUEUE("audio > Q_AUDIO_CODEC_SEEK_COMPLETE"); audio_codec_seek_complete();
audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
/* Wait for urgent or go message */ /* Wait for urgent or go message */
do do
@ -521,8 +524,7 @@ static void run_codec(void)
/* Notify audio that we're done for better or worse - advise of the /* Notify audio that we're done for better or worse - advise of the
status */ status */
LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status); audio_codec_complete(status);
audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
} }
} }

View file

@ -158,7 +158,7 @@ enum codec_status codec_run(void)
} }
else { else {
ci->seek_buffer(ci->id3->first_frame_offset); ci->seek_buffer(ci->id3->first_frame_offset);
samplesdone = 0; ci->set_elapsed(0);
} }
while (1) { while (1) {

View file

@ -178,6 +178,7 @@ enum codec_status codec_run(void)
} }
else { else {
/* Seek to the first packet */ /* Seek to the first packet */
ci->set_elapsed(0);
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE ); ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE );
} }

View file

@ -134,8 +134,6 @@ enum codec_status codec_run(void)
if (m4a_seek_raw(&demux_res, &input_stream, file_offset, if (m4a_seek_raw(&demux_res, &input_stream, file_offset,
&sound_samples_done, (int*) &i)) { &sound_samples_done, (int*) &i)) {
sound_samples_done *= sbr_fac; sound_samples_done *= sbr_fac;
elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
ci->set_elapsed(elapsed_time);
} else { } else {
sound_samples_done = 0; sound_samples_done = 0;
} }
@ -143,6 +141,9 @@ enum codec_status codec_run(void)
} else { } else {
sound_samples_done = 0; sound_samples_done = 0;
} }
elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
ci->set_elapsed(elapsed_time);
if (i == 0) if (i == 0)
{ {

View file

@ -209,7 +209,7 @@ enum codec_status codec_run(void)
/* get in position */ /* get in position */
ci->seek_buffer(bufoff); ci->seek_buffer(bufoff);
ci->set_elapsed(0);
/* setup pcm buffer format */ /* setup pcm buffer format */
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
@ -276,6 +276,11 @@ enum codec_status codec_run(void)
loop_count++; loop_count++;
} }
ci->seek_buffer(bufoff); ci->seek_buffer(bufoff);
ci->set_elapsed(
((end_adr-start_adr)*loop_count + bufoff-chanstart)*
1000LL/avgbytespersec);
ci->seek_complete(); ci->seek_complete();
} }

View file

@ -288,6 +288,8 @@ enum codec_status codec_run(void)
bytesdone = 0; bytesdone = 0;
} }
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
/* The main decoder loop */ /* The main decoder loop */
endofstream = 0; endofstream = 0;

View file

@ -97,6 +97,8 @@ enum codec_status codec_run(void)
} }
} }
ci->set_elapsed(elapsedtime);
/* The main decoding loop */ /* The main decoding loop */
while (i < demux_res.num_sample_byte_sizes) { while (i < demux_res.num_sample_byte_sizes) {
enum codec_command_action action = ci->get_command(&param); enum codec_command_action action = ci->get_command(&param);

View file

@ -220,6 +220,9 @@ enum codec_status codec_run(void)
firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */ firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */
} }
elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
ci->set_elapsed(elapsedtime);
/* Initialise the buffer */ /* Initialise the buffer */
inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);

View file

@ -253,6 +253,8 @@ enum codec_status codec_run(void)
bytesdone = 0; bytesdone = 0;
} }
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
/* The main decoder loop */ /* The main decoder loop */
endofstream = 0; endofstream = 0;

View file

@ -105,8 +105,10 @@ enum codec_status codec_run(void)
param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
action = CODEC_ACTION_SEEK_TIME; action = CODEC_ACTION_SEEK_TIME;
} }
else {
ci->set_elapsed(0);
}
ci->set_elapsed(0);
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
/* The main decoder loop */ /* The main decoder loop */

View file

@ -460,7 +460,9 @@ enum codec_status codec_run(void)
codec_set_replaygain(ci->id3); codec_set_replaygain(ci->id3);
flac_seek_offset(&fc, samplesdone); flac_seek_offset(&fc, samplesdone);
samplesdone=0; samplesdone=fc.samplenumber+fc.blocksize;
elapsedtime=(samplesdone*10)/(ci->id3->frequency/100);
ci->set_elapsed(elapsedtime);
/* The main decoding loop */ /* The main decoding loop */
frame=0; frame=0;

View file

@ -1333,12 +1333,11 @@ enum codec_status codec_run(void)
/* New time is ready in param */ /* New time is ready in param */
modplayer.patterntableposition = param/1000; modplayer.patterntableposition = param/1000;
modplayer.currentline = 0; modplayer.currentline = 0;
ci->set_elapsed(modplayer.patterntableposition*1000+500);
ci->seek_complete(); ci->seek_complete();
} }
if(old_patterntableposition != modplayer.patterntableposition) { if(old_patterntableposition != modplayer.patterntableposition) {
ci->set_elapsed(modplayer.patterntableposition*1000+500); ci->set_elapsed(modplayer.patterntableposition*1000);
old_patterntableposition=modplayer.patterntableposition; old_patterntableposition=modplayer.patterntableposition;
} }

View file

@ -144,6 +144,7 @@ static void set_elapsed(struct mp3entry* id3)
{ {
unsigned long offset = id3->offset > id3->first_frame_offset ? unsigned long offset = id3->offset > id3->first_frame_offset ?
id3->offset - id3->first_frame_offset : 0; id3->offset - id3->first_frame_offset : 0;
unsigned long elapsed = id3->elapsed;
if ( id3->vbr ) { if ( id3->vbr ) {
if ( id3->has_toc ) { if ( id3->has_toc ) {
@ -172,27 +173,28 @@ static void set_elapsed(struct mp3entry* id3)
/* set time for this percent (divide before multiply to prevent /* set time for this percent (divide before multiply to prevent
overflow on long files. loss of precision is negligible on overflow on long files. loss of precision is negligible on
short files) */ short files) */
id3->elapsed = i * (id3->length / 100); elapsed = i * (id3->length / 100);
/* calculate remainder time */ /* calculate remainder time */
plen = (nextpos - relpos) * (id3->filesize / 256); plen = (nextpos - relpos) * (id3->filesize / 256);
id3->elapsed += (((remainder * 100) / plen) * elapsed += (((remainder * 100) / plen) * (id3->length / 10000));
(id3->length / 10000));
} }
else { else {
/* no TOC exists. set a rough estimate using average bitrate */ /* no TOC exists. set a rough estimate using average bitrate */
int tpk = id3->length / int tpk = id3->length /
((id3->filesize - id3->first_frame_offset - id3->id3v1len) / ((id3->filesize - id3->first_frame_offset - id3->id3v1len) /
1024); 1024);
id3->elapsed = offset / 1024 * tpk; elapsed = offset / 1024 * tpk;
} }
} }
else else
{ {
/* constant bitrate, use exact calculation */ /* constant bitrate, use exact calculation */
if (id3->bitrate != 0) if (id3->bitrate != 0)
id3->elapsed = offset / (id3->bitrate / 8); elapsed = offset / (id3->bitrate / 8);
} }
ci->set_elapsed(elapsed);
} }
#ifdef MPA_SYNTH_ON_COP #ifdef MPA_SYNTH_ON_COP

View file

@ -123,6 +123,8 @@ enum codec_status codec_run(void)
codec_set_replaygain(ci->id3); codec_set_replaygain(ci->id3);
/* Resume to saved sample offset. */ /* Resume to saved sample offset. */
elapsed_time = 0;
if (samplesdone > 0) if (samplesdone > 0)
{ {
if (mpc_demux_seek_sample(demux, samplesdone) == MPC_STATUS_OK) if (mpc_demux_seek_sample(demux, samplesdone) == MPC_STATUS_OK)
@ -136,6 +138,8 @@ enum codec_status codec_run(void)
} }
} }
ci->set_elapsed(elapsed_time);
/* This is the decoding loop. */ /* This is the decoding loop. */
do do
{ {

View file

@ -99,6 +99,8 @@ enum codec_status codec_run(void)
sc.bitindex = sc.gb.index - 8*consumed; sc.bitindex = sc.gb.index - 8*consumed;
seek_start: seek_start:
ci->set_elapsed(0);
/* The main decoding loop */ /* The main decoding loop */
ci->memset(&decoded0, 0, sizeof(int32_t)*MAX_DECODE_SIZE); ci->memset(&decoded0, 0, sizeof(int32_t)*MAX_DECODE_SIZE);
ci->memset(&decoded1, 0, sizeof(int32_t)*MAX_DECODE_SIZE); ci->memset(&decoded1, 0, sizeof(int32_t)*MAX_DECODE_SIZE);
@ -118,7 +120,6 @@ seek_start:
if (param == 0 && if (param == 0 &&
ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) { ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) {
sc.bitindex = sc.header_bits - 8*(sc.header_bits/8); sc.bitindex = sc.header_bits - 8*(sc.header_bits/8);
ci->set_elapsed(0);
ci->seek_complete(); ci->seek_complete();
goto seek_start; goto seek_start;
} }

View file

@ -1299,8 +1299,8 @@ enum codec_status codec_run(void)
nSamplesToRender = 0; /* Start the rendering from scratch */ nSamplesToRender = 0; /* Start the rendering from scratch */
/* Set the elapsed time to the current subsong (in seconds) */ /* Set the elapsed time to the current subsong (in seconds) */
ci->seek_complete();
ci->set_elapsed(subSong*1000); ci->set_elapsed(subSong*1000);
ci->seek_complete();
} }
nSamplesRendered = 0; nSamplesRendered = 0;

View file

@ -429,6 +429,8 @@ enum codec_status codec_run(void)
bytesdone = 0; bytesdone = 0;
} }
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
/* The main decoder loop */ /* The main decoder loop */
endofstream = 0; endofstream = 0;

View file

@ -560,6 +560,8 @@ enum codec_status codec_run(void)
return CODEC_ERROR; return CODEC_ERROR;
DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize); DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize);
ci->set_elapsed(0);
do do
{ {
if (load_spc_buffer(buffer, buffersize)) { if (load_spc_buffer(buffer, buffersize)) {

View file

@ -417,6 +417,7 @@ enum codec_status codec_run(void)
} }
ci->seek_buffer(0); ci->seek_buffer(0);
ci->set_elapsed(0);
stereo = speex_stereo_state_init(); stereo = speex_stereo_state_init();
spx_ogg_sync_init(&oy); spx_ogg_sync_init(&oy);

View file

@ -90,6 +90,8 @@ enum codec_status codec_run(void)
decodedsamples = new_pos; decodedsamples = new_pos;
} }
ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH);
while (!endofstream) while (!endofstream)
{ {
enum codec_command_action action = ci->get_command(&param); enum codec_command_action action = ci->get_command(&param);

View file

@ -196,6 +196,9 @@ enum codec_status codec_run(void)
ci->set_elapsed(ov_time_tell(&vf)); ci->set_elapsed(ov_time_tell(&vf));
ci->set_offset(ov_raw_tell(&vf)); ci->set_offset(ov_raw_tell(&vf));
} }
else {
ci->set_elapsed(0);
}
previous_section = -1; previous_section = -1;
eof = 0; eof = 0;

View file

@ -141,6 +141,8 @@ enum codec_status codec_run(void)
bytesdone = 0; bytesdone = 0;
} }
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
/* The main decoder loop */ /* The main decoder loop */
endofstream = 0; endofstream = 0;

View file

@ -378,6 +378,8 @@ enum codec_status codec_run(void)
bytesdone = 0; bytesdone = 0;
} }
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
/* The main decoder loop */ /* The main decoder loop */
endofstream = 0; endofstream = 0;

View file

@ -381,6 +381,8 @@ enum codec_status codec_run(void)
bytesdone = 0; bytesdone = 0;
} }
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
/* The main decoder loop */ /* The main decoder loop */
endofstream = 0; endofstream = 0;

View file

@ -75,7 +75,7 @@ enum codec_status codec_run(void)
ci->configure(DSP_SET_STEREO_MODE, nchans == 2 ? STEREO_INTERLEAVED : STEREO_MONO); ci->configure(DSP_SET_STEREO_MODE, nchans == 2 ? STEREO_INTERLEAVED : STEREO_MONO);
sr_100 = ci->id3->frequency / 100; sr_100 = ci->id3->frequency / 100;
ci->set_elapsed (0); ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
/* The main decoder loop */ /* The main decoder loop */

View file

@ -84,7 +84,6 @@ restart_track:
% wfx.packet_size; % wfx.packet_size;
ci->seek_buffer(resume_offset - packet_offset); ci->seek_buffer(resume_offset - packet_offset);
elapsedtime = asf_get_timestamp(&i); elapsedtime = asf_get_timestamp(&i);
ci->set_elapsed(elapsedtime);
} }
else else
{ {
@ -93,6 +92,8 @@ restart_track:
elapsedtime = 0; elapsedtime = 0;
} }
ci->set_elapsed(elapsedtime);
resume_offset = 0; resume_offset = 0;
ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate); ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate);
ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ? ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?

View file

@ -79,6 +79,7 @@ restart_track:
ci->seek_buffer(ci->id3->first_frame_offset); ci->seek_buffer(ci->id3->first_frame_offset);
elapsedtime = 0; elapsedtime = 0;
ci->set_elapsed(0);
/* The main decoding loop */ /* The main decoding loop */

View file

@ -109,6 +109,8 @@ restart_track:
ci->seek_buffer(ci->id3->first_frame_offset); ci->seek_buffer(ci->id3->first_frame_offset);
elapsedtime = 0; elapsedtime = 0;
ci->set_elapsed(0);
resume_offset = 0; resume_offset = 0;
/* The main decoding loop */ /* The main decoding loop */

File diff suppressed because it is too large Load diff

View file

@ -21,9 +21,11 @@
#ifndef PCMBUF_H #ifndef PCMBUF_H
#define PCMBUF_H #define PCMBUF_H
#include <sys/types.h>
/* Commit PCM data */ /* Commit PCM data */
void *pcmbuf_request_buffer(int *count); void *pcmbuf_request_buffer(int *count);
void pcmbuf_write_complete(int count); void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset);
/* Init */ /* Init */
size_t pcmbuf_init(unsigned char *bufend); size_t pcmbuf_init(unsigned char *bufend);
@ -33,20 +35,30 @@ void pcmbuf_play_start(void);
void pcmbuf_play_stop(void); void pcmbuf_play_stop(void);
void pcmbuf_pause(bool pause); void pcmbuf_pause(bool pause);
void pcmbuf_monitor_track_change(bool monitor); void pcmbuf_monitor_track_change(bool monitor);
bool pcmbuf_start_track_change(bool manual_skip); void pcmbuf_sync_position_update(void);
/* Track change origin type */
enum pcm_track_change_type
{
TRACK_CHANGE_NONE = 0, /* No track change pending */
TRACK_CHANGE_MANUAL, /* Manual change (from user) */
TRACK_CHANGE_AUTO, /* Automatic change (from codec) */
TRACK_CHANGE_END_OF_DATA, /* Expect no more data (from codec) */
};
void pcmbuf_start_track_change(enum pcm_track_change_type type);
/* Crossfade */ /* Crossfade */
#ifdef HAVE_CROSSFADE #ifdef HAVE_CROSSFADE
bool pcmbuf_is_crossfade_active(void); bool pcmbuf_is_crossfade_active(void);
void pcmbuf_request_crossfade_enable(bool on_off); void pcmbuf_request_crossfade_enable(int setting);
bool pcmbuf_is_same_size(void); bool pcmbuf_is_same_size(void);
#else #else
/* Dummy functions with sensible returns */ /* Dummy functions with sensible returns */
static inline bool pcmbuf_is_crossfade_active(void) static FORCE_INLINE bool pcmbuf_is_crossfade_active(void)
{ return false; } { return false; }
static inline void pcmbuf_request_crossfade_enable(bool on_off) static FORCE_INLINE void pcmbuf_request_crossfade_enable(bool on_off)
{ return; (void)on_off; } { return; (void)on_off; }
static inline bool pcmbuf_is_same_size(void) static FORCE_INLINE bool pcmbuf_is_same_size(void)
{ return true; } { return true; }
#endif #endif
@ -59,9 +71,7 @@ size_t pcmbuf_free(void);
size_t pcmbuf_get_bufsize(void); size_t pcmbuf_get_bufsize(void);
int pcmbuf_descs(void); int pcmbuf_descs(void);
int pcmbuf_used_descs(void); int pcmbuf_used_descs(void);
#ifdef ROCKBOX_HAS_LOGF unsigned int pcmbuf_get_position_key(void);
unsigned char *pcmbuf_get_meminfo(size_t *length);
#endif
/* Misc */ /* Misc */
void pcmbuf_fade(bool fade, bool in); void pcmbuf_fade(bool fade, bool in);
@ -69,6 +79,5 @@ bool pcmbuf_fading(void);
void pcmbuf_soft_mode(bool shhh); void pcmbuf_soft_mode(bool shhh);
bool pcmbuf_is_lowdata(void); bool pcmbuf_is_lowdata(void);
void pcmbuf_set_low_latency(bool state); void pcmbuf_set_low_latency(bool state);
unsigned long pcmbuf_get_latency(void);
#endif #endif

View file

@ -330,7 +330,7 @@ static struct
static bool codec_skip_pending = false; static bool codec_skip_pending = false;
static int codec_skip_status; static int codec_skip_status;
static bool codec_seeking = false; /* Codec seeking ack expected? */ static bool codec_seeking = false; /* Codec seeking ack expected? */
static unsigned int position_key = 0;
/* Event queues */ /* Event queues */
static struct event_queue audio_queue SHAREDBSS_ATTR; static struct event_queue audio_queue SHAREDBSS_ATTR;
@ -353,14 +353,13 @@ static void audio_stop_playback(void);
static void buffer_event_buffer_low_callback(void *data); static void buffer_event_buffer_low_callback(void *data);
static void buffer_event_rebuffer_callback(void *data); static void buffer_event_rebuffer_callback(void *data);
static void buffer_event_finished_callback(void *data); static void buffer_event_finished_callback(void *data);
void audio_pcmbuf_sync_position(void);
/**************************************/ /**************************************/
/** --- audio_queue helpers --- **/ /** --- audio_queue helpers --- **/
static void audio_queue_post(long id, intptr_t data)
/* codec thread needs access */
void audio_queue_post(long id, intptr_t data)
{ {
queue_post(&audio_queue, id, data); queue_post(&audio_queue, id, data);
} }
@ -805,14 +804,10 @@ static void audio_reset_buffer(void)
aids viewing and the summation of certain variables should add up to aids viewing and the summation of certain variables should add up to
the location of others. */ the location of others. */
{ {
size_t pcmbufsize;
const unsigned char *pcmbuf = pcmbuf_get_meminfo(&pcmbufsize);
logf("fbuf: %08X", (unsigned)filebuf); logf("fbuf: %08X", (unsigned)filebuf);
logf("fbufe: %08X", (unsigned)(filebuf + filebuflen)); logf("fbufe: %08X", (unsigned)(filebuf + filebuflen));
logf("sbuf: %08X", (unsigned)audio_scratch_memory); logf("sbuf: %08X", (unsigned)audio_scratch_memory);
logf("sbufe: %08X", (unsigned)(audio_scratch_memory + allocsize)); logf("sbufe: %08X", (unsigned)(audio_scratch_memory + allocsize));
logf("pcmb: %08X", (unsigned)pcmbuf);
logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize));
} }
#endif #endif
@ -978,7 +973,8 @@ static void audio_handle_track_load_status(int trackstat)
/* Announce the end of playing the current track */ /* Announce the end of playing the current track */
static void audio_playlist_track_finish(void) static void audio_playlist_track_finish(void)
{ {
struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3)); struct mp3entry *ply_id3 = id3_get(PLAYING_ID3);
struct mp3entry *id3 = valid_mp3entry(ply_id3);
playlist_update_resume_info(filling == STATE_ENDED ? NULL : id3); playlist_update_resume_info(filling == STATE_ENDED ? NULL : id3);
@ -1001,6 +997,8 @@ static void audio_playlist_track_change(void)
if (id3) if (id3)
send_event(PLAYBACK_EVENT_TRACK_CHANGE, id3); send_event(PLAYBACK_EVENT_TRACK_CHANGE, id3);
position_key = pcmbuf_get_position_key();
playlist_update_resume_info(id3); playlist_update_resume_info(id3);
} }
@ -1014,26 +1012,28 @@ static void audio_update_and_announce_next_track(const struct mp3entry *id3_next
/* Bring the user current mp3entry up to date and set a new offset for the /* Bring the user current mp3entry up to date and set a new offset for the
buffered metadata */ buffered metadata */
static void playing_id3_sync(struct track_info *user_info, size_t offset) static void playing_id3_sync(struct track_info *user_info, off_t offset)
{ {
id3_mutex_lock(); id3_mutex_lock();
struct mp3entry *id3 = bufgetid3(user_info->id3_hid); struct mp3entry *id3 = bufgetid3(user_info->id3_hid);
struct mp3entry *playing_id3 = id3_get(PLAYING_ID3);
if (offset == (size_t)-1) pcm_play_lock();
unsigned long e = playing_id3->elapsed;
unsigned long o = playing_id3->offset;
id3_write(PLAYING_ID3, id3);
if (offset < 0)
{ {
struct mp3entry *ply_id3 = id3_get(PLAYING_ID3); playing_id3->elapsed = e;
size_t play_offset = ply_id3->offset; playing_id3->offset = o;
long play_elapsed = ply_id3->elapsed;
id3_write(PLAYING_ID3, id3);
ply_id3->offset = play_offset;
ply_id3->elapsed = play_elapsed;
offset = 0; offset = 0;
} }
else
{ pcm_play_unlock();
id3_write(PLAYING_ID3, id3);
}
if (id3) if (id3)
id3->offset = offset; id3->offset = offset;
@ -1093,13 +1093,6 @@ static bool halt_decoding_track(bool stop)
return retval; return retval;
} }
/* Clear the PCM on a manual skip */
static void audio_clear_paused_pcm(void)
{
if (play_status == PLAY_PAUSED && !pcmbuf_is_crossfade_active())
pcmbuf_play_stop();
}
/* Wait for any in-progress fade to complete */ /* Wait for any in-progress fade to complete */
static void audio_wait_fade_complete(void) static void audio_wait_fade_complete(void)
{ {
@ -1121,6 +1114,7 @@ static void audio_ff_rewind_end(void)
{ {
/* Clear the buffer */ /* Clear the buffer */
pcmbuf_play_stop(); pcmbuf_play_stop();
audio_pcmbuf_sync_position();
} }
if (play_status != PLAY_PAUSED) if (play_status != PLAY_PAUSED)
@ -2063,7 +2057,7 @@ static void audio_on_handle_finished(int hid)
/* Called to make an outstanding track skip the current track and to send the /* Called to make an outstanding track skip the current track and to send the
transition events */ transition events */
static void audio_finalise_track_change(bool delayed) static void audio_finalise_track_change(void)
{ {
switch (skip_pending) switch (skip_pending)
{ {
@ -2117,15 +2111,6 @@ static void audio_finalise_track_change(bool delayed)
id3_write(PLAYING_ID3, track_id3); id3_write(PLAYING_ID3, track_id3);
if (delayed)
{
/* Delayed skip where codec is ahead of user's current track */
struct mp3entry *ci_id3 = id3_get(CODEC_ID3);
struct mp3entry *ply_id3 = id3_get(PLAYING_ID3);
ply_id3->elapsed = ci_id3->elapsed;
ply_id3->offset = ci_id3->offset;
}
/* The skip is technically over */ /* The skip is technically over */
skip_pending = TRACK_SKIP_NONE; skip_pending = TRACK_SKIP_NONE;
@ -2141,25 +2126,25 @@ static void audio_finalise_track_change(bool delayed)
} }
/* Actually begin a transition and take care of the codec change - may complete /* Actually begin a transition and take care of the codec change - may complete
it now or ask pcmbuf for notification depending on the type and what pcmbuf it now or ask pcmbuf for notification depending on the type */
has to say */ static void audio_begin_track_change(enum pcm_track_change_type type,
static void audio_begin_track_change(bool auto_skip, int trackstat) int trackstat)
{ {
/* Even if the new track is bad, the old track must be finished off */ /* Even if the new track is bad, the old track must be finished off */
bool finalised = pcmbuf_start_track_change(auto_skip); pcmbuf_start_track_change(type);
if (finalised) bool auto_skip = type != TRACK_CHANGE_MANUAL;
if (!auto_skip)
{ {
/* pcmbuf says that the transition happens now - complete it */ /* Manual track change happens now */
audio_finalise_track_change(false); audio_finalise_track_change();
pcmbuf_sync_position_update();
if (play_status == PLAY_STOPPED) if (play_status == PLAY_STOPPED)
return; /* Stopped us */ return; /* Stopped us */
} }
if (!auto_skip)
audio_clear_paused_pcm();
if (trackstat >= LOAD_TRACK_OK) if (trackstat >= LOAD_TRACK_OK)
{ {
struct track_info *info = track_list_current(0); struct track_info *info = track_list_current(0);
@ -2170,7 +2155,7 @@ static void audio_begin_track_change(bool auto_skip, int trackstat)
/* Everything needed for the codec is ready - start it */ /* Everything needed for the codec is ready - start it */
if (audio_start_codec(auto_skip)) if (audio_start_codec(auto_skip))
{ {
if (finalised) if (!auto_skip)
playing_id3_sync(info, -1); playing_id3_sync(info, -1);
return; return;
} }
@ -2186,7 +2171,7 @@ static void audio_monitor_end_of_playlist(void)
{ {
skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST; skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST;
filling = STATE_ENDING; filling = STATE_ENDING;
pcmbuf_monitor_track_change(true); pcmbuf_start_track_change(TRACK_CHANGE_END_OF_DATA);
} }
/* Codec has completed decoding the track /* Codec has completed decoding the track
@ -2221,14 +2206,6 @@ static void audio_on_codec_complete(int status)
codec_skip_pending = false; codec_skip_pending = false;
#ifdef AB_REPEAT_ENABLE
if (status >= 0)
{
/* Normal automatic skip */
ab_end_of_track_report();
}
#endif
int trackstat = LOAD_TRACK_OK; int trackstat = LOAD_TRACK_OK;
automatic_skip = true; automatic_skip = true;
@ -2263,7 +2240,7 @@ static void audio_on_codec_complete(int status)
{ {
/* Continue filling after this track */ /* Continue filling after this track */
audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1); audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1);
audio_begin_track_change(true, trackstat); audio_begin_track_change(TRACK_CHANGE_AUTO, trackstat);
return; return;
} }
/* else rebuffer at this track; status applies to the track we /* else rebuffer at this track; status applies to the track we
@ -2299,7 +2276,7 @@ static void audio_on_codec_complete(int status)
} }
} }
audio_begin_track_change(true, trackstat); audio_begin_track_change(TRACK_CHANGE_AUTO, trackstat);
} }
/* Called when codec completes seek operation /* Called when codec completes seek operation
@ -2316,7 +2293,7 @@ static void audio_on_codec_seek_complete(void)
static void audio_on_track_changed(void) static void audio_on_track_changed(void)
{ {
/* Finish whatever is pending so that the WPS is in sync */ /* Finish whatever is pending so that the WPS is in sync */
audio_finalise_track_change(true); audio_finalise_track_change();
if (codec_skip_pending) if (codec_skip_pending)
{ {
@ -2367,8 +2344,7 @@ static void audio_start_playback(size_t offset, unsigned int flags)
track_list_clear(TRACK_LIST_CLEAR_ALL); track_list_clear(TRACK_LIST_CLEAR_ALL);
/* Indicate manual track change */ /* Indicate manual track change */
pcmbuf_start_track_change(false); pcmbuf_start_track_change(TRACK_CHANGE_MANUAL);
audio_clear_paused_pcm();
wipe_track_metadata(true); wipe_track_metadata(true);
} }
@ -2398,6 +2374,10 @@ static void audio_start_playback(size_t offset, unsigned int flags)
play_status = PLAY_PLAYING; play_status = PLAY_PLAYING;
} }
/* Codec's position should be available as soon as it knows it */
position_key = pcmbuf_get_position_key();
pcmbuf_sync_position_update();
/* Start fill from beginning of playlist */ /* Start fill from beginning of playlist */
playlist_peek_offset = -1; playlist_peek_offset = -1;
buf_set_base_handle(-1); buf_set_base_handle(-1);
@ -2592,7 +2572,7 @@ static void audio_on_skip(void)
trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1); trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1);
} }
audio_begin_track_change(false, trackstat); audio_begin_track_change(TRACK_CHANGE_MANUAL, trackstat);
} }
/* Skip to the next/previous directory /* Skip to the next/previous directory
@ -2638,7 +2618,7 @@ static void audio_on_dir_skip(int direction)
return; return;
} }
audio_begin_track_change(false, trackstat); audio_begin_track_change(TRACK_CHANGE_MANUAL, trackstat);
} }
/* Enter seek mode in order to start a seek /* Enter seek mode in order to start a seek
@ -2689,11 +2669,6 @@ static void audio_on_ff_rewind(long time)
if (time == 0) if (time == 0)
send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); send_event(PLAYBACK_EVENT_TRACK_FINISH, id3);
/* Prevent user codec time update - coerce to something that is
innocuous concerning lookaheads */
if (pending == TRACK_SKIP_NONE)
skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST;
id3->elapsed = time; id3->elapsed = time;
queue_reply(&audio_queue, 1); queue_reply(&audio_queue, 1);
@ -2703,6 +2678,9 @@ static void audio_on_ff_rewind(long time)
halt that will reset it */ halt that will reset it */
codec_seeking = true; codec_seeking = true;
/* If in transition, key will have changed - sync to it */
position_key = pcmbuf_get_position_key();
if (pending == TRACK_SKIP_AUTO) if (pending == TRACK_SKIP_AUTO)
{ {
if (!track_list_advance_current(-1)) if (!track_list_advance_current(-1))
@ -3124,75 +3102,66 @@ static void buffer_event_finished_callback(void *data)
/** -- Codec callbacks -- **/ /** -- Codec callbacks -- **/
/* Update elapsed times with latency-adjusted values */ /* Update elapsed time for next PCM insert */
void audio_codec_update_elapsed(unsigned long value) void audio_codec_update_elapsed(unsigned long elapsed)
{ {
#ifdef AB_REPEAT_ENABLE #ifdef AB_REPEAT_ENABLE
ab_position_report(value); ab_position_report(elapsed);
#endif #endif
/* Save in codec's id3 where it is used at next pcm insert */
unsigned long latency = pcmbuf_get_latency(); id3_get(CODEC_ID3)->elapsed = elapsed;
if (LIKELY(value >= latency))
{
unsigned long elapsed = value - latency;
if (elapsed > value || elapsed < value - 2)
value = elapsed;
}
else
{
value = 0;
}
/* Track codec: used later when updating the playing at the user
transition */
id3_get(CODEC_ID3)->elapsed = value;
/* If a skip is pending, the PCM buffer is updating the time on the
previous song */
if (LIKELY(skip_pending == TRACK_SKIP_NONE))
id3_get(PLAYING_ID3)->elapsed = value;
} }
/* Update offsets with latency-adjusted values */ /* Update offset for next PCM insert */
void audio_codec_update_offset(size_t value) void audio_codec_update_offset(size_t offset)
{ {
struct mp3entry *ci_id3 = id3_get(CODEC_ID3); /* Save in codec's id3 where it is used at next pcm insert */
unsigned long latency = pcmbuf_get_latency() * ci_id3->bitrate / 8; id3_get(CODEC_ID3)->offset = offset;
}
if (LIKELY(value >= latency)) /* Codec has finished running */
void audio_codec_complete(int status)
{
#ifdef AB_REPEAT_ENABLE
if (status >= CODEC_OK)
{ {
value -= latency; /* Normal automatic skip */
} ab_end_of_track_report();
else
{
value = 0;
} }
#endif
/* Track codec: used later when updating the playing id3 at the user LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status);
transition */ audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
ci_id3->offset = value; }
/* If a skip is pending, the PCM buffer is updating the time on the /* Codec has finished seeking */
previous song */ void audio_codec_seek_complete(void)
if (LIKELY(skip_pending == TRACK_SKIP_NONE)) {
id3_get(PLAYING_ID3)->offset = value; LOGFQUEUE("codec > audio Q_AUDIO_CODEC_SEEK_COMPLETE");
audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
} }
/** --- Pcmbuf callbacks --- **/ /** --- Pcmbuf callbacks --- **/
/* Between the codec and PCM track change, we need to keep updating the /* Update the elapsed and offset from the information cached during the
* "elapsed" value of the previous (to the codec, but current to the PCM buffer insert */
* user/PCM/WPS) track, so that the progressbar reaches the end. */ void audio_pcmbuf_position_callback(unsigned long elapsed, off_t offset,
void audio_pcmbuf_position_callback(unsigned int time) unsigned int key)
{ {
struct mp3entry *id3 = id3_get(PLAYING_ID3); if (key == position_key)
{
struct mp3entry *id3 = id3_get(PLAYING_ID3);
id3->elapsed = elapsed;
id3->offset = offset;
}
}
time += id3->elapsed; /* Synchronize position info to the codec's */
void audio_pcmbuf_sync_position(void)
id3->elapsed = MIN(time, id3->length); {
audio_pcmbuf_position_callback(ci.id3->elapsed, ci.id3->offset,
pcmbuf_get_position_key());
} }
/* Post message from pcmbuf that the end of the previous track has just /* Post message from pcmbuf that the end of the previous track has just

View file

@ -378,12 +378,16 @@ static inline void cpucache_flush(void)
#if defined(CPU_ARM) #if defined(CPU_ARM)
/* Use ARMs cache alignment. */ /* Use ARMs cache alignment. */
#define MEM_ALIGN_ATTR CACHEALIGN_ATTR #define MEM_ALIGN_ATTR CACHEALIGN_ATTR
#define MEM_ALIGN_SIZE CACHEALIGN_SIZE
#elif defined(CPU_COLDFIRE) #elif defined(CPU_COLDFIRE)
/* Use fixed alignment of 16 bytes. Speed up only for 'movem' in DRAM. */ /* Use fixed alignment of 16 bytes. Speed up only for 'movem' in DRAM. */
#define MEM_ALIGN_ATTR __attribute__((aligned(16))) #define MEM_ALIGN_ATTR __attribute__((aligned(16)))
#define MEM_ALIGN_SIZE 16
#else #else
/* Do nothing. */ /* Do nothing. */
#define MEM_ALIGN_ATTR #define MEM_ALIGN_ATTR
/* Align pointer size */
#define MEM_ALIGN_SIZE sizeof(intptr_t)
#endif #endif
#ifdef STORAGE_WANTS_ALIGN #ifdef STORAGE_WANTS_ALIGN