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:
parent
463b3ed8b2
commit
7ad2cad173
32 changed files with 969 additions and 830 deletions
|
@ -77,9 +77,10 @@ struct codec_load_info
|
|||
static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */
|
||||
|
||||
/* Private interfaces to main playback control */
|
||||
extern void audio_codec_update_elapsed(unsigned long value);
|
||||
extern void audio_codec_update_offset(size_t value);
|
||||
extern void audio_queue_post(long id, intptr_t data);
|
||||
extern void audio_codec_update_elapsed(unsigned long elapsed);
|
||||
extern void audio_codec_update_offset(size_t offset);
|
||||
extern void audio_codec_complete(int status);
|
||||
extern void audio_codec_seek_complete(void);
|
||||
extern struct codec_api ci; /* from codecs.c */
|
||||
|
||||
/* Codec thread */
|
||||
|
@ -251,7 +252,7 @@ static void codec_pcmbuf_insert_callback(
|
|||
if (out_count <= 0)
|
||||
return;
|
||||
|
||||
pcmbuf_write_complete(out_count);
|
||||
pcmbuf_write_complete(out_count, ci.id3->elapsed, ci.id3->offset);
|
||||
|
||||
count -= inp_count;
|
||||
}
|
||||
|
@ -334,9 +335,11 @@ static void codec_seek_complete_callback(void)
|
|||
/* Clear DSP */
|
||||
dsp_configure(ci.dsp, DSP_FLUSH, 0);
|
||||
|
||||
/* Sync position */
|
||||
audio_codec_update_offset(ci.curpos);
|
||||
|
||||
/* Post notification to audio thread */
|
||||
LOGFQUEUE("audio > Q_AUDIO_CODEC_SEEK_COMPLETE");
|
||||
audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
|
||||
audio_codec_seek_complete();
|
||||
|
||||
/* Wait for urgent or go message */
|
||||
do
|
||||
|
@ -521,8 +524,7 @@ static void run_codec(void)
|
|||
|
||||
/* Notify audio that we're done for better or worse - advise of the
|
||||
status */
|
||||
LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status);
|
||||
audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
|
||||
audio_codec_complete(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ enum codec_status codec_run(void)
|
|||
}
|
||||
else {
|
||||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
samplesdone = 0;
|
||||
ci->set_elapsed(0);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
|
|
@ -178,6 +178,7 @@ enum codec_status codec_run(void)
|
|||
}
|
||||
else {
|
||||
/* Seek to the first packet */
|
||||
ci->set_elapsed(0);
|
||||
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE );
|
||||
}
|
||||
|
||||
|
|
|
@ -134,8 +134,6 @@ enum codec_status codec_run(void)
|
|||
if (m4a_seek_raw(&demux_res, &input_stream, file_offset,
|
||||
&sound_samples_done, (int*) &i)) {
|
||||
sound_samples_done *= sbr_fac;
|
||||
elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
|
||||
ci->set_elapsed(elapsed_time);
|
||||
} else {
|
||||
sound_samples_done = 0;
|
||||
}
|
||||
|
@ -143,6 +141,9 @@ enum codec_status codec_run(void)
|
|||
} else {
|
||||
sound_samples_done = 0;
|
||||
}
|
||||
|
||||
elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
|
||||
ci->set_elapsed(elapsed_time);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
|
|
|
@ -209,7 +209,7 @@ enum codec_status codec_run(void)
|
|||
|
||||
/* get in position */
|
||||
ci->seek_buffer(bufoff);
|
||||
|
||||
ci->set_elapsed(0);
|
||||
|
||||
/* setup pcm buffer format */
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -276,6 +276,11 @@ enum codec_status codec_run(void)
|
|||
loop_count++;
|
||||
}
|
||||
ci->seek_buffer(bufoff);
|
||||
|
||||
ci->set_elapsed(
|
||||
((end_adr-start_adr)*loop_count + bufoff-chanstart)*
|
||||
1000LL/avgbytespersec);
|
||||
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
|
|
|
@ -288,6 +288,8 @@ enum codec_status codec_run(void)
|
|||
bytesdone = 0;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
|
||||
/* The main decoder loop */
|
||||
endofstream = 0;
|
||||
|
||||
|
|
|
@ -97,6 +97,8 @@ enum codec_status codec_run(void)
|
|||
}
|
||||
}
|
||||
|
||||
ci->set_elapsed(elapsedtime);
|
||||
|
||||
/* The main decoding loop */
|
||||
while (i < demux_res.num_sample_byte_sizes) {
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
|
|
@ -220,6 +220,9 @@ enum codec_status codec_run(void)
|
|||
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 */
|
||||
inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
|
||||
|
||||
|
|
|
@ -253,6 +253,8 @@ enum codec_status codec_run(void)
|
|||
bytesdone = 0;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
|
||||
/* The main decoder loop */
|
||||
endofstream = 0;
|
||||
|
||||
|
|
|
@ -105,8 +105,10 @@ enum codec_status codec_run(void)
|
|||
param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
|
||||
action = CODEC_ACTION_SEEK_TIME;
|
||||
}
|
||||
else {
|
||||
ci->set_elapsed(0);
|
||||
}
|
||||
|
||||
ci->set_elapsed(0);
|
||||
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
|
||||
/* The main decoder loop */
|
||||
|
|
|
@ -460,7 +460,9 @@ enum codec_status codec_run(void)
|
|||
codec_set_replaygain(ci->id3);
|
||||
|
||||
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 */
|
||||
frame=0;
|
||||
|
|
|
@ -1333,12 +1333,11 @@ enum codec_status codec_run(void)
|
|||
/* New time is ready in param */
|
||||
modplayer.patterntableposition = param/1000;
|
||||
modplayer.currentline = 0;
|
||||
ci->set_elapsed(modplayer.patterntableposition*1000+500);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
if(old_patterntableposition != modplayer.patterntableposition) {
|
||||
ci->set_elapsed(modplayer.patterntableposition*1000+500);
|
||||
ci->set_elapsed(modplayer.patterntableposition*1000);
|
||||
old_patterntableposition=modplayer.patterntableposition;
|
||||
}
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ static void set_elapsed(struct mp3entry* id3)
|
|||
{
|
||||
unsigned long offset = id3->offset > id3->first_frame_offset ?
|
||||
id3->offset - id3->first_frame_offset : 0;
|
||||
unsigned long elapsed = id3->elapsed;
|
||||
|
||||
if ( id3->vbr ) {
|
||||
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
|
||||
overflow on long files. loss of precision is negligible on
|
||||
short files) */
|
||||
id3->elapsed = i * (id3->length / 100);
|
||||
elapsed = i * (id3->length / 100);
|
||||
|
||||
/* calculate remainder time */
|
||||
plen = (nextpos - relpos) * (id3->filesize / 256);
|
||||
id3->elapsed += (((remainder * 100) / plen) *
|
||||
(id3->length / 10000));
|
||||
elapsed += (((remainder * 100) / plen) * (id3->length / 10000));
|
||||
}
|
||||
else {
|
||||
/* no TOC exists. set a rough estimate using average bitrate */
|
||||
int tpk = id3->length /
|
||||
((id3->filesize - id3->first_frame_offset - id3->id3v1len) /
|
||||
1024);
|
||||
id3->elapsed = offset / 1024 * tpk;
|
||||
elapsed = offset / 1024 * tpk;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* constant bitrate, use exact calculation */
|
||||
if (id3->bitrate != 0)
|
||||
id3->elapsed = offset / (id3->bitrate / 8);
|
||||
elapsed = offset / (id3->bitrate / 8);
|
||||
}
|
||||
|
||||
ci->set_elapsed(elapsed);
|
||||
}
|
||||
|
||||
#ifdef MPA_SYNTH_ON_COP
|
||||
|
|
|
@ -123,6 +123,8 @@ enum codec_status codec_run(void)
|
|||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Resume to saved sample offset. */
|
||||
elapsed_time = 0;
|
||||
|
||||
if (samplesdone > 0)
|
||||
{
|
||||
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. */
|
||||
do
|
||||
{
|
||||
|
|
|
@ -99,6 +99,8 @@ enum codec_status codec_run(void)
|
|||
sc.bitindex = sc.gb.index - 8*consumed;
|
||||
|
||||
seek_start:
|
||||
ci->set_elapsed(0);
|
||||
|
||||
/* The main decoding loop */
|
||||
ci->memset(&decoded0, 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 &&
|
||||
ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) {
|
||||
sc.bitindex = sc.header_bits - 8*(sc.header_bits/8);
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
goto seek_start;
|
||||
}
|
||||
|
|
|
@ -1299,8 +1299,8 @@ enum codec_status codec_run(void)
|
|||
nSamplesToRender = 0; /* Start the rendering from scratch */
|
||||
|
||||
/* Set the elapsed time to the current subsong (in seconds) */
|
||||
ci->seek_complete();
|
||||
ci->set_elapsed(subSong*1000);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
nSamplesRendered = 0;
|
||||
|
|
|
@ -429,6 +429,8 @@ enum codec_status codec_run(void)
|
|||
bytesdone = 0;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
|
||||
/* The main decoder loop */
|
||||
endofstream = 0;
|
||||
|
||||
|
|
|
@ -560,6 +560,8 @@ enum codec_status codec_run(void)
|
|||
return CODEC_ERROR;
|
||||
|
||||
DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize);
|
||||
ci->set_elapsed(0);
|
||||
|
||||
do
|
||||
{
|
||||
if (load_spc_buffer(buffer, buffersize)) {
|
||||
|
|
|
@ -417,6 +417,7 @@ enum codec_status codec_run(void)
|
|||
}
|
||||
|
||||
ci->seek_buffer(0);
|
||||
ci->set_elapsed(0);
|
||||
|
||||
stereo = speex_stereo_state_init();
|
||||
spx_ogg_sync_init(&oy);
|
||||
|
|
|
@ -90,6 +90,8 @@ enum codec_status codec_run(void)
|
|||
decodedsamples = new_pos;
|
||||
}
|
||||
|
||||
ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH);
|
||||
|
||||
while (!endofstream)
|
||||
{
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
|
|
@ -196,6 +196,9 @@ enum codec_status codec_run(void)
|
|||
ci->set_elapsed(ov_time_tell(&vf));
|
||||
ci->set_offset(ov_raw_tell(&vf));
|
||||
}
|
||||
else {
|
||||
ci->set_elapsed(0);
|
||||
}
|
||||
|
||||
previous_section = -1;
|
||||
eof = 0;
|
||||
|
|
|
@ -141,6 +141,8 @@ enum codec_status codec_run(void)
|
|||
bytesdone = 0;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
|
||||
/* The main decoder loop */
|
||||
endofstream = 0;
|
||||
|
||||
|
|
|
@ -378,6 +378,8 @@ enum codec_status codec_run(void)
|
|||
bytesdone = 0;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
|
||||
/* The main decoder loop */
|
||||
endofstream = 0;
|
||||
|
||||
|
|
|
@ -381,6 +381,8 @@ enum codec_status codec_run(void)
|
|||
bytesdone = 0;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
|
||||
/* The main decoder loop */
|
||||
endofstream = 0;
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ enum codec_status codec_run(void)
|
|||
ci->configure(DSP_SET_STEREO_MODE, nchans == 2 ? STEREO_INTERLEAVED : STEREO_MONO);
|
||||
sr_100 = ci->id3->frequency / 100;
|
||||
|
||||
ci->set_elapsed (0);
|
||||
ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
|
||||
|
||||
/* The main decoder loop */
|
||||
|
||||
|
|
|
@ -84,7 +84,6 @@ restart_track:
|
|||
% wfx.packet_size;
|
||||
ci->seek_buffer(resume_offset - packet_offset);
|
||||
elapsedtime = asf_get_timestamp(&i);
|
||||
ci->set_elapsed(elapsedtime);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -93,6 +92,8 @@ restart_track:
|
|||
elapsedtime = 0;
|
||||
}
|
||||
|
||||
ci->set_elapsed(elapsedtime);
|
||||
|
||||
resume_offset = 0;
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate);
|
||||
ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
|
||||
|
|
|
@ -79,6 +79,7 @@ restart_track:
|
|||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
|
||||
elapsedtime = 0;
|
||||
ci->set_elapsed(0);
|
||||
|
||||
/* The main decoding loop */
|
||||
|
||||
|
|
|
@ -109,6 +109,8 @@ restart_track:
|
|||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
|
||||
elapsedtime = 0;
|
||||
ci->set_elapsed(0);
|
||||
|
||||
resume_offset = 0;
|
||||
|
||||
/* The main decoding loop */
|
||||
|
|
1457
apps/pcmbuf.c
1457
apps/pcmbuf.c
File diff suppressed because it is too large
Load diff
|
@ -21,9 +21,11 @@
|
|||
#ifndef PCMBUF_H
|
||||
#define PCMBUF_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/* Commit PCM data */
|
||||
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 */
|
||||
size_t pcmbuf_init(unsigned char *bufend);
|
||||
|
@ -33,20 +35,30 @@ void pcmbuf_play_start(void);
|
|||
void pcmbuf_play_stop(void);
|
||||
void pcmbuf_pause(bool pause);
|
||||
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 */
|
||||
#ifdef HAVE_CROSSFADE
|
||||
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);
|
||||
#else
|
||||
/* Dummy functions with sensible returns */
|
||||
static inline bool pcmbuf_is_crossfade_active(void)
|
||||
static FORCE_INLINE bool pcmbuf_is_crossfade_active(void)
|
||||
{ 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; }
|
||||
static inline bool pcmbuf_is_same_size(void)
|
||||
static FORCE_INLINE bool pcmbuf_is_same_size(void)
|
||||
{ return true; }
|
||||
#endif
|
||||
|
||||
|
@ -59,9 +71,7 @@ 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
|
||||
unsigned int pcmbuf_get_position_key(void);
|
||||
|
||||
/* Misc */
|
||||
void pcmbuf_fade(bool fade, bool in);
|
||||
|
@ -69,6 +79,5 @@ bool pcmbuf_fading(void);
|
|||
void pcmbuf_soft_mode(bool shhh);
|
||||
bool pcmbuf_is_lowdata(void);
|
||||
void pcmbuf_set_low_latency(bool state);
|
||||
unsigned long pcmbuf_get_latency(void);
|
||||
|
||||
#endif
|
||||
|
|
211
apps/playback.c
211
apps/playback.c
|
@ -330,7 +330,7 @@ static struct
|
|||
static bool codec_skip_pending = false;
|
||||
static int codec_skip_status;
|
||||
static bool codec_seeking = false; /* Codec seeking ack expected? */
|
||||
|
||||
static unsigned int position_key = 0;
|
||||
|
||||
/* Event queues */
|
||||
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_rebuffer_callback(void *data);
|
||||
static void buffer_event_finished_callback(void *data);
|
||||
void audio_pcmbuf_sync_position(void);
|
||||
|
||||
|
||||
/**************************************/
|
||||
|
||||
/** --- audio_queue helpers --- **/
|
||||
|
||||
/* codec thread needs access */
|
||||
void audio_queue_post(long id, intptr_t data)
|
||||
static void audio_queue_post(long id, intptr_t 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
|
||||
the location of others. */
|
||||
{
|
||||
size_t pcmbufsize;
|
||||
const unsigned char *pcmbuf = pcmbuf_get_meminfo(&pcmbufsize);
|
||||
logf("fbuf: %08X", (unsigned)filebuf);
|
||||
logf("fbufe: %08X", (unsigned)(filebuf + filebuflen));
|
||||
logf("sbuf: %08X", (unsigned)audio_scratch_memory);
|
||||
logf("sbufe: %08X", (unsigned)(audio_scratch_memory + allocsize));
|
||||
logf("pcmb: %08X", (unsigned)pcmbuf);
|
||||
logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -978,7 +973,8 @@ static void audio_handle_track_load_status(int trackstat)
|
|||
/* Announce the end of playing the current track */
|
||||
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);
|
||||
|
||||
|
@ -1001,6 +997,8 @@ static void audio_playlist_track_change(void)
|
|||
if (id3)
|
||||
send_event(PLAYBACK_EVENT_TRACK_CHANGE, id3);
|
||||
|
||||
position_key = pcmbuf_get_position_key();
|
||||
|
||||
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
|
||||
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();
|
||||
|
||||
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);
|
||||
size_t play_offset = ply_id3->offset;
|
||||
long play_elapsed = ply_id3->elapsed;
|
||||
id3_write(PLAYING_ID3, id3);
|
||||
ply_id3->offset = play_offset;
|
||||
ply_id3->elapsed = play_elapsed;
|
||||
playing_id3->elapsed = e;
|
||||
playing_id3->offset = o;
|
||||
offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
id3_write(PLAYING_ID3, id3);
|
||||
}
|
||||
|
||||
pcm_play_unlock();
|
||||
|
||||
if (id3)
|
||||
id3->offset = offset;
|
||||
|
@ -1093,13 +1093,6 @@ static bool halt_decoding_track(bool stop)
|
|||
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 */
|
||||
static void audio_wait_fade_complete(void)
|
||||
{
|
||||
|
@ -1121,6 +1114,7 @@ static void audio_ff_rewind_end(void)
|
|||
{
|
||||
/* Clear the buffer */
|
||||
pcmbuf_play_stop();
|
||||
audio_pcmbuf_sync_position();
|
||||
}
|
||||
|
||||
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
|
||||
transition events */
|
||||
static void audio_finalise_track_change(bool delayed)
|
||||
static void audio_finalise_track_change(void)
|
||||
{
|
||||
switch (skip_pending)
|
||||
{
|
||||
|
@ -2117,15 +2111,6 @@ static void audio_finalise_track_change(bool delayed)
|
|||
|
||||
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 */
|
||||
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
|
||||
it now or ask pcmbuf for notification depending on the type and what pcmbuf
|
||||
has to say */
|
||||
static void audio_begin_track_change(bool auto_skip, int trackstat)
|
||||
it now or ask pcmbuf for notification depending on the type */
|
||||
static void audio_begin_track_change(enum pcm_track_change_type type,
|
||||
int trackstat)
|
||||
{
|
||||
/* 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 */
|
||||
audio_finalise_track_change(false);
|
||||
/* Manual track change happens now */
|
||||
audio_finalise_track_change();
|
||||
pcmbuf_sync_position_update();
|
||||
|
||||
if (play_status == PLAY_STOPPED)
|
||||
return; /* Stopped us */
|
||||
}
|
||||
|
||||
if (!auto_skip)
|
||||
audio_clear_paused_pcm();
|
||||
|
||||
if (trackstat >= LOAD_TRACK_OK)
|
||||
{
|
||||
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 */
|
||||
if (audio_start_codec(auto_skip))
|
||||
{
|
||||
if (finalised)
|
||||
if (!auto_skip)
|
||||
playing_id3_sync(info, -1);
|
||||
return;
|
||||
}
|
||||
|
@ -2186,7 +2171,7 @@ static void audio_monitor_end_of_playlist(void)
|
|||
{
|
||||
skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST;
|
||||
filling = STATE_ENDING;
|
||||
pcmbuf_monitor_track_change(true);
|
||||
pcmbuf_start_track_change(TRACK_CHANGE_END_OF_DATA);
|
||||
}
|
||||
|
||||
/* Codec has completed decoding the track
|
||||
|
@ -2221,14 +2206,6 @@ static void audio_on_codec_complete(int status)
|
|||
|
||||
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;
|
||||
|
||||
automatic_skip = true;
|
||||
|
@ -2263,7 +2240,7 @@ static void audio_on_codec_complete(int status)
|
|||
{
|
||||
/* Continue filling after this track */
|
||||
audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1);
|
||||
audio_begin_track_change(true, trackstat);
|
||||
audio_begin_track_change(TRACK_CHANGE_AUTO, trackstat);
|
||||
return;
|
||||
}
|
||||
/* 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
|
||||
|
@ -2316,7 +2293,7 @@ static void audio_on_codec_seek_complete(void)
|
|||
static void audio_on_track_changed(void)
|
||||
{
|
||||
/* Finish whatever is pending so that the WPS is in sync */
|
||||
audio_finalise_track_change(true);
|
||||
audio_finalise_track_change();
|
||||
|
||||
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);
|
||||
|
||||
/* Indicate manual track change */
|
||||
pcmbuf_start_track_change(false);
|
||||
audio_clear_paused_pcm();
|
||||
pcmbuf_start_track_change(TRACK_CHANGE_MANUAL);
|
||||
wipe_track_metadata(true);
|
||||
}
|
||||
|
||||
|
@ -2398,6 +2374,10 @@ static void audio_start_playback(size_t offset, unsigned int flags)
|
|||
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 */
|
||||
playlist_peek_offset = -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);
|
||||
}
|
||||
|
||||
audio_begin_track_change(false, trackstat);
|
||||
audio_begin_track_change(TRACK_CHANGE_MANUAL, trackstat);
|
||||
}
|
||||
|
||||
/* Skip to the next/previous directory
|
||||
|
@ -2638,7 +2618,7 @@ static void audio_on_dir_skip(int direction)
|
|||
return;
|
||||
}
|
||||
|
||||
audio_begin_track_change(false, trackstat);
|
||||
audio_begin_track_change(TRACK_CHANGE_MANUAL, trackstat);
|
||||
}
|
||||
|
||||
/* Enter seek mode in order to start a seek
|
||||
|
@ -2689,11 +2669,6 @@ static void audio_on_ff_rewind(long time)
|
|||
if (time == 0)
|
||||
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;
|
||||
queue_reply(&audio_queue, 1);
|
||||
|
||||
|
@ -2703,6 +2678,9 @@ static void audio_on_ff_rewind(long time)
|
|||
halt that will reset it */
|
||||
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 (!track_list_advance_current(-1))
|
||||
|
@ -3124,75 +3102,66 @@ static void buffer_event_finished_callback(void *data)
|
|||
|
||||
/** -- Codec callbacks -- **/
|
||||
|
||||
/* Update elapsed times with latency-adjusted values */
|
||||
void audio_codec_update_elapsed(unsigned long value)
|
||||
/* Update elapsed time for next PCM insert */
|
||||
void audio_codec_update_elapsed(unsigned long elapsed)
|
||||
{
|
||||
#ifdef AB_REPEAT_ENABLE
|
||||
ab_position_report(value);
|
||||
ab_position_report(elapsed);
|
||||
#endif
|
||||
|
||||
unsigned long latency = pcmbuf_get_latency();
|
||||
|
||||
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;
|
||||
/* Save in codec's id3 where it is used at next pcm insert */
|
||||
id3_get(CODEC_ID3)->elapsed = elapsed;
|
||||
}
|
||||
|
||||
/* Update offsets with latency-adjusted values */
|
||||
void audio_codec_update_offset(size_t value)
|
||||
/* Update offset for next PCM insert */
|
||||
void audio_codec_update_offset(size_t offset)
|
||||
{
|
||||
struct mp3entry *ci_id3 = id3_get(CODEC_ID3);
|
||||
unsigned long latency = pcmbuf_get_latency() * ci_id3->bitrate / 8;
|
||||
/* Save in codec's id3 where it is used at next pcm insert */
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = 0;
|
||||
/* Normal automatic skip */
|
||||
ab_end_of_track_report();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Track codec: used later when updating the playing id3 at the user
|
||||
transition */
|
||||
ci_id3->offset = value;
|
||||
LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status);
|
||||
audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
|
||||
}
|
||||
|
||||
/* 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)->offset = value;
|
||||
/* Codec has finished seeking */
|
||||
void audio_codec_seek_complete(void)
|
||||
{
|
||||
LOGFQUEUE("codec > audio Q_AUDIO_CODEC_SEEK_COMPLETE");
|
||||
audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
|
||||
}
|
||||
|
||||
|
||||
/** --- Pcmbuf callbacks --- **/
|
||||
|
||||
/* Between the codec and PCM track change, we need to keep updating the
|
||||
* "elapsed" value of the previous (to the codec, but current to the
|
||||
* user/PCM/WPS) track, so that the progressbar reaches the end. */
|
||||
void audio_pcmbuf_position_callback(unsigned int time)
|
||||
/* Update the elapsed and offset from the information cached during the
|
||||
PCM buffer insert */
|
||||
void audio_pcmbuf_position_callback(unsigned long elapsed, off_t offset,
|
||||
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;
|
||||
|
||||
id3->elapsed = MIN(time, id3->length);
|
||||
/* Synchronize position info to the codec's */
|
||||
void audio_pcmbuf_sync_position(void)
|
||||
{
|
||||
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
|
||||
|
|
|
@ -378,12 +378,16 @@ static inline void cpucache_flush(void)
|
|||
#if defined(CPU_ARM)
|
||||
/* Use ARMs cache alignment. */
|
||||
#define MEM_ALIGN_ATTR CACHEALIGN_ATTR
|
||||
#define MEM_ALIGN_SIZE CACHEALIGN_SIZE
|
||||
#elif defined(CPU_COLDFIRE)
|
||||
/* Use fixed alignment of 16 bytes. Speed up only for 'movem' in DRAM. */
|
||||
#define MEM_ALIGN_ATTR __attribute__((aligned(16)))
|
||||
#define MEM_ALIGN_SIZE 16
|
||||
#else
|
||||
/* Do nothing. */
|
||||
#define MEM_ALIGN_ATTR
|
||||
/* Align pointer size */
|
||||
#define MEM_ALIGN_SIZE sizeof(intptr_t)
|
||||
#endif
|
||||
|
||||
#ifdef STORAGE_WANTS_ALIGN
|
||||
|
|
Loading…
Reference in a new issue