Index: apps/pcmbuf.c =================================================================== --- apps/pcmbuf.c (revision 29993) +++ apps/pcmbuf.c (working copy) @@ -40,14 +40,11 @@ #include "voice_thread.h" #include "dsp.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 */ +/* This is the target fill size of chunks on the pcm buffer */ +#define PCMBUF_CHUNK_SIZE 8192 + +/* Mix subchunk mode for in-callback DSP to reduce processing overhead */ +#define PCMBUF_SUBCHUNK_SIZE (MIX_FRAME_SAMPLES*4) #define CROSSFADE_BUFSIZE 8192 /* Size of the crossfade buffer */ #define AUX_BUFSIZE 512 /* Size of the aux buffer; can be 512 if no resampling or timestretching is allowed in @@ -65,89 +62,91 @@ /* Structure we can use to queue pcm chunks in memory to be played * by the driver code. */ +enum chunk_flags +{ + CHUNK_END_OF_TRACK = 0x1, + CHUNK_HAS_POSITION = 0x2, +}; + struct chunkdesc { - unsigned char *addr; - size_t size; - struct chunkdesc* link; - /* true if last chunk in the track */ - bool end_of_track; + unsigned char *addr; /* Corresponding address for audio data (fixed) */ + size_t size; /* Actual size of data (0 < size <= PCMBUF_CHUNK_SIZE) */ + unsigned short flags; /* Flags indicating additional properties */ + unsigned short version; /* Who put the timestamps in */ + unsigned long elapsed; /* Elapsed time to use (if CHUNK_HAS_POSITION) */ + unsigned long offset; /* Offset to use (if CHUNK_HAS_POSITION) */ }; -#define NUM_CHUNK_DESCS(bufsize) \ - ((bufsize) / PCMBUF_MINAVG_CHUNK) +static unsigned char *pcmbuffer; +static unsigned char *guardbuf; +static unsigned char *pcmbuf_memend; +static size_t pcmbuf_size; +static struct chunkdesc *pcmbuf_descriptors; +static unsigned int pcmbuf_desc_count; +static unsigned short position_version; -/* Size of the PCM buffer. */ -static size_t pcmbuf_size IDATA_ATTR = 0; -static char *pcmbuf_bufend IDATA_ATTR; -static char *pcmbuffer IDATA_ATTR; -/* Current PCM buffer write index. */ -static size_t pcmbuffer_pos IDATA_ATTR; -/* Amount pcmbuffer_pos will be increased.*/ -static size_t pcmbuffer_fillpos IDATA_ATTR; +#define FOR_EACH_DESC(i) for (i = 0; i < pcmbuf_desc_count; i++) -static struct chunkdesc *first_desc; +static size_t pcmbuf_bytes_waiting; -/* Gapless playback */ -static bool track_transition IDATA_ATTR; - #ifdef HAVE_CROSSFADE /* Crossfade buffer */ -static char *fadebuf IDATA_ATTR; +static char *fadebuf; /* Crossfade related state */ static bool crossfade_enabled; static bool crossfade_enable_request; static bool crossfade_mixmode; static bool crossfade_auto_skip; -static bool crossfade_active IDATA_ATTR; -static bool crossfade_track_change_started IDATA_ATTR; +static bool crossfade_active; +static bool crossfade_track_change_started; /* Track the current location for processing crossfade */ -static struct chunkdesc *crossfade_chunk IDATA_ATTR; -static size_t crossfade_sample IDATA_ATTR; +static struct chunkdesc *crossfade_chunk; +static unsigned int crossfade_chunk_idx; +static size_t crossfade_sample; /* Counters for fading in new data */ -static size_t crossfade_fade_in_total IDATA_ATTR; -static size_t crossfade_fade_in_rem IDATA_ATTR; +static size_t crossfade_fade_in_total; +static size_t crossfade_fade_in_rem; #endif -static struct chunkdesc *read_chunk IDATA_ATTR; -static struct chunkdesc *read_end_chunk IDATA_ATTR; -static struct chunkdesc *write_chunk IDATA_ATTR; -static struct chunkdesc *write_end_chunk IDATA_ATTR; -static size_t last_chunksize IDATA_ATTR; +static unsigned int chunk_ridx, chunk_widx; -static size_t pcmbuf_unplayed_bytes IDATA_ATTR; -static size_t pcmbuf_watermark IDATA_ATTR; +static volatile size_t pcmbuf_bytes_written; +static volatile size_t pcmbuf_bytes_read; +static size_t pcmbuf_watermark; +static size_t last_chunksize; +static bool low_latency_mode = false; + /* Voice */ -static char *voicebuf IDATA_ATTR; -static struct chunkdesc *mix_chunk IDATA_ATTR; -static size_t pcmbuf_mix_sample IDATA_ATTR; +static char *voicebuf; +static struct chunkdesc *mix_chunk; +static unsigned int mix_chunk_idx; +static size_t pcmbuf_mix_sample; -static bool low_latency_mode = false; -static bool flush_pcmbuf = false; - +/* Thread */ #ifdef HAVE_PRIORITY_SCHEDULING static int codec_thread_priority = PRIORITY_PLAYBACK; #endif /* Helpful macros for use in conditionals this assumes some of the above * static variable names */ -#define COMMIT_IF_NEEDED if(pcmbuffer_fillpos > PCMBUF_TARGET_CHUNK || \ - (pcmbuffer_pos + pcmbuffer_fillpos) >= pcmbuf_size) commit_chunk(false) #define LOW_DATA(quarter_secs) \ - (pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs) + (pcmbuf_unplayed_bytes() < NATIVE_FREQUENCY * quarter_secs) #ifdef HAVE_CROSSFADE static void crossfade_start(void); -static void write_to_crossfade(size_t length); +static void write_to_crossfade(size_t length, unsigned long elapsed, + unsigned long offset); static void pcmbuf_finish_crossfade_enable(void); #endif /* Callbacks into playback.c */ -extern void audio_pcmbuf_position_callback(unsigned int time); +extern void audio_pcmbuf_position_callback(unsigned long elapsed, + unsigned long offset); extern void audio_pcmbuf_track_change(bool pcmbuf); extern bool audio_pcmbuf_may_play(void); @@ -198,86 +197,112 @@ #define DISPLAY_DESC(caller) do{}while(0) #endif +static inline ssize_t pcmbuf_unplayed_bytes(void) +{ + return pcmbuf_bytes_written - pcmbuf_bytes_read; +} -/** Accept new PCM data */ - -/* Commit PCM buffer samples as a new chunk for playback */ -static void commit_chunk(bool flush_next_time) +/* Return the descriptor's pointer at the specified index and offset */ +static struct chunkdesc * get_chunk(unsigned int index, int offset) { - if (!pcmbuffer_fillpos) - return; + int idx = index + offset; - /* Never use the last buffer descriptor */ - while (write_chunk == write_end_chunk) { - /* If this happens, something is being stupid */ - if (!pcm_is_playing()) { - logf("commit_chunk error"); - pcmbuf_play_start(); - } - /* Let approximately one chunk of data playback */ - sleep(HZ * PCMBUF_TARGET_CHUNK / BYTERATE); + if (offset != 0) + { + idx = (offset + index) % pcmbuf_desc_count; + + /* Remainder -> modulus */ + if (idx < 0) + idx += pcmbuf_desc_count; } - /* commit the chunk */ + return &pcmbuf_descriptors[idx]; +} - register size_t size = pcmbuffer_fillpos; - /* Grab the next description to write, and change the write pointer */ - register struct chunkdesc *pcmbuf_current = write_chunk; - write_chunk = pcmbuf_current->link; - /* Fill in the values in the new buffer chunk */ - pcmbuf_current->addr = &pcmbuffer[pcmbuffer_pos]; - pcmbuf_current->size = size; - pcmbuf_current->end_of_track = false; - pcmbuf_current->link = NULL; +/* Convert chunkdesc pointer to an index into the array */ +static unsigned int get_chunk_index(struct chunkdesc *desc) +{ + return desc - pcmbuf_descriptors; +} - if (read_chunk != NULL) +/* Move the chunk index one chunk forward (wrapped) and return the + descriptor's pointer */ +static struct chunkdesc * increment_chunk(unsigned int *index) +{ + unsigned int idx = *index + 1; + + if (idx >= pcmbuf_desc_count) + idx -= pcmbuf_desc_count; + + *index = idx; + return &pcmbuf_descriptors[idx]; +} + + +/** Accept new PCM data */ + +/* Split the uncommitted data as needed into the chunk descriptors, stopping + when below the threshold */ +static void commit_chunks(size_t threshold) +{ + if (pcmbuf_bytes_waiting == 0) { - if (flush_pcmbuf) - { - /* Flush! Discard all data after the currently playing chunk, - and make the current chunk play next */ - logf("commit_chunk: flush"); - pcm_play_lock(); - write_end_chunk->link = read_chunk->link; - read_chunk->link = pcmbuf_current; - while (write_end_chunk->link) - { - write_end_chunk = write_end_chunk->link; - pcmbuf_unplayed_bytes -= write_end_chunk->size; - } + /* No outstanding data */ + return; + } - read_chunk->end_of_track = track_transition; - pcm_play_unlock(); - } - /* If there is already a read buffer setup, add to it */ - else - read_end_chunk->link = pcmbuf_current; - } - else + struct chunkdesc *chunk = get_chunk(chunk_widx, 0); + unsigned char *addr = chunk->addr + pcmbuf_bytes_waiting; + + if (addr > guardbuf) { - /* Otherwise create the buffer */ - read_chunk = pcmbuf_current; + memcpy(pcmbuffer, guardbuf, addr - guardbuf); } - /* If flush_next_time is true, then the current chunk will be thrown out - * and the next chunk to be committed will be the next to be played. - * This is used to empty the PCM buffer for a track change. */ - flush_pcmbuf = flush_next_time; + for (;;) + { + size_t size = MIN(pcmbuf_bytes_waiting, PCMBUF_CHUNK_SIZE); - /* This is now the last buffer to read */ - read_end_chunk = pcmbuf_current; + /* Fill in the values in the new buffer chunk */ + chunk->size = size; + pcmbuf_bytes_waiting -= size; - /* Update bytes counters */ - pcmbuf_unplayed_bytes += size; + /* Make the next chunk current */ + chunk = increment_chunk(&chunk_widx); - pcmbuffer_pos += size; - if (pcmbuffer_pos >= pcmbuf_size) - pcmbuffer_pos -= pcmbuf_size; + /* Update bytes counters and make it visible to the callback */ + pcmbuf_bytes_written += PCMBUF_CHUNK_SIZE; - pcmbuffer_fillpos = 0; - DISPLAY_DESC("commit_chunk"); + if (pcmbuf_bytes_waiting < threshold) + return; + } } +/* If uncommitted data is above or equal to the threshold, split into chunks + and commit it */ +static inline void commit_if_needed(size_t threshold) +{ + if (pcmbuf_bytes_waiting >= threshold) + commit_chunks(threshold); +} + +/* Place positioning information in the chunk */ +static void stamp_chunk(struct chunkdesc *desc, + unsigned long elapsed, + unsigned long offset) +{ + /* One-time stamping of a given chunk by the same track - new track may + overwrite */ + if (!(desc->flags & CHUNK_HAS_POSITION) || + desc->version != position_version) + { + desc->flags |= CHUNK_HAS_POSITION; + desc->elapsed = elapsed; + desc->offset = offset; + desc->version = position_version; + } +} + /* Set priority of the codec thread */ #ifdef HAVE_PRIORITY_SCHEDULING /* @@ -324,8 +349,8 @@ return false; } - /* Need to save PCMBUF_MIN_CHUNK to prevent wrapping overwriting */ - if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK) + /* Need to have length bytes to prevent wrapping overwriting */ + if (pcmbuf_free() < length) return false; /* Maintain the buffer level above the watermark */ @@ -338,9 +363,10 @@ #endif /* SIMULATOR */ { /* boost cpu if necessary */ - if (pcmbuf_unplayed_bytes < pcmbuf_watermark) + size_t remaining = pcmbuf_unplayed_bytes(); + if (remaining < pcmbuf_watermark) trigger_cpu_boost(); - boost_codec_thread(pcmbuf_unplayed_bytes*10/pcmbuf_size); + boost_codec_thread(remaining*10/pcmbuf_size); } #ifdef HAVE_CROSSFADE @@ -360,7 +386,7 @@ #if MEMORYSIZE > 2 if (!LOW_DATA(4)) #else - if (pcmbuf_unplayed_bytes > pcmbuf_watermark) + if (pcmbuf_unplayed_bytes() > pcmbuf_watermark) #endif { logf("pcm starting"); @@ -375,6 +401,8 @@ /* Request space in the buffer for writing output samples */ void *pcmbuf_request_buffer(int *count) { + commit_if_needed(PCMBUF_CHUNK_SIZE); + #ifdef HAVE_CROSSFADE /* we're going to crossfade to a new track, which is now on its way */ if (crossfade_track_change_started) @@ -394,39 +422,47 @@ #endif /* if possible, reserve room in the PCM buffer for new samples */ { - if(prepare_insert(*count << 2)) + if (prepare_insert(*count << 2)) { - size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos; - if (pcmbuf_size - pcmbuffer_index >= PCMBUF_MIN_CHUNK) - { - /* Usual case, there's space here */ - return &pcmbuffer[pcmbuffer_index]; - } - else - { - /* Wrap the buffer, the new samples go at the beginning */ - commit_chunk(false); - pcmbuffer_pos = 0; - return &pcmbuffer[0]; - } + /* Obtain current chunk fill address */ + unsigned char *addr = + get_chunk(chunk_widx, 0)->addr + pcmbuf_bytes_waiting; + unsigned char *end = guardbuf + PCMBUF_CHUNK_SIZE; + + /* Get count to the end of the buffer where a wrap will happen + + the guard */ + int endcount = (end - addr) >> 2; + + /* Return available unwrapped space */ + *count = MIN(*count, endcount); + + return addr; } } + /* PCM buffer not ready to receive new data yet */ return NULL; } /* Handle new samples to the buffer */ -void pcmbuf_write_complete(int count) +void pcmbuf_write_complete(int count, unsigned long elapsed, + unsigned long offset) { size_t length = (size_t)(unsigned int)count << 2; #ifdef HAVE_CROSSFADE if (crossfade_active) - write_to_crossfade(length); + write_to_crossfade(length, elapsed, offset); else #endif { - pcmbuffer_fillpos += length; - COMMIT_IF_NEEDED; + struct chunkdesc *chunk = get_chunk(chunk_widx, 0); + + stamp_chunk(chunk, elapsed, offset); + + /* Add this data and commit if one or more chunks are ready */ + pcmbuf_bytes_waiting += length; + + commit_if_needed(PCMBUF_CHUNK_SIZE); } } @@ -435,15 +471,20 @@ static inline void init_pcmbuffers(void) { - first_desc = write_chunk; - struct chunkdesc *next = write_chunk; - next++; - write_end_chunk = write_chunk; - while ((void *)next < (void *)pcmbuf_bufend) { - write_end_chunk->link=next; - write_end_chunk=next; - next++; + unsigned char *addr = pcmbuffer; + unsigned int i; + + FOR_EACH_DESC(i) + { + struct chunkdesc *desc = &pcmbuf_descriptors[i]; + desc->addr = addr; + desc->flags = 0; + desc->version = 0; + addr += PCMBUF_CHUNK_SIZE; } + + position_version = 1; + DISPLAY_DESC("init"); } @@ -466,21 +507,28 @@ } /* Initialize the pcmbuffer the structure looks like this: - * ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */ + * ...|---------PCMBUF---------|GUARDCHUNK|FADEBUF|VOICEBUF|DESCS|... */ size_t pcmbuf_init(unsigned char *bufend) { - pcmbuf_bufend = bufend; + pcmbuf_memend = bufend; pcmbuf_size = get_next_required_pcmbuf_size(); - write_chunk = (struct chunkdesc *)pcmbuf_bufend - - NUM_CHUNK_DESCS(pcmbuf_size); - voicebuf = (char *)write_chunk - AUX_BUFSIZE; + pcmbuf_desc_count = pcmbuf_size / PCMBUF_CHUNK_SIZE - 1; + pcmbuf_size = pcmbuf_desc_count * PCMBUF_CHUNK_SIZE; + pcmbuf_descriptors = (struct chunkdesc *)pcmbuf_memend - + pcmbuf_desc_count; + + voicebuf = (char *)pcmbuf_descriptors - AUX_BUFSIZE; #ifdef HAVE_CROSSFADE fadebuf = voicebuf - CROSSFADE_BUFSIZE; - pcmbuffer = fadebuf - pcmbuf_size; + pcmbuffer = fadebuf - pcmbuf_size - PCMBUF_CHUNK_SIZE; #else - pcmbuffer = voicebuf - pcmbuf_size; + pcmbuffer = voicebuf - pcmbuf_size - PCMBUF_CHUNK_SIZE; #endif + pcmbuffer = (typeof (pcmbuffer))ALIGN_DOWN((uintptr_t)pcmbuffer, 16); + + guardbuf = pcmbuffer + pcmbuf_size; + init_pcmbuffers(); #ifdef HAVE_CROSSFADE @@ -491,34 +539,33 @@ pcmbuf_play_stop(); - return pcmbuf_bufend - pcmbuffer; + return pcmbuf_memend - pcmbuffer; } - /** Track change */ void pcmbuf_monitor_track_change(bool monitor) { pcm_play_lock(); - if (last_chunksize != 0) + if (pcmbuf_unplayed_bytes() > 0) { /* If monitoring, wait until this track runs out. Place in currently playing chunk. If not, cancel notification. */ - track_transition = monitor; - read_end_chunk->end_of_track = monitor; if (!monitor) { /* Clear all notifications */ - struct chunkdesc *desc = first_desc; - struct chunkdesc *end = desc + pcmbuf_descs(); - while (desc < end) - desc++->end_of_track = false; + unsigned int i; + FOR_EACH_DESC(i) + pcmbuf_descriptors[i].flags &= ~CHUNK_END_OF_TRACK; } + else + { + get_chunk(chunk_widx, -1)->flags |= CHUNK_END_OF_TRACK; + } } else { /* Post now if PCM stopped and last buffer was sent. */ - track_transition = false; if (monitor) audio_pcmbuf_track_change(false); } @@ -526,6 +573,16 @@ pcm_play_unlock(); } +/* Commit any outstanding data */ +void pcmbuf_drain(void) +{ + commit_if_needed(1); + + /* Kick playback if things stopped or never got started */ + if (audio_pcmbuf_may_play()) + pcmbuf_play_start(); +} + bool pcmbuf_start_track_change(bool auto_skip) { bool crossfade = false; @@ -554,6 +611,11 @@ } #endif + /* Update position version so buffers stamped to the outgoing track's + positions are restamped to the incoming track's positions when + crossfading */ + position_version++; + if (!auto_skip || crossfade) /* manual skip or crossfade */ { @@ -583,7 +645,7 @@ /* Not enough data, or not crossfading, flush the old data instead */ if (LOW_DATA(2) || !crossfade || low_latency_mode) { - commit_chunk(true); + pcmbuf_play_stop(); } #ifdef HAVE_CROSSFADE else @@ -594,7 +656,7 @@ crossfade_auto_skip = auto_skip; - crossfade_track_change_started = crossfade; + crossfade_track_change_started = true; } #endif pcm_play_unlock(); @@ -622,87 +684,71 @@ /** Playback */ /* PCM driver callback - * This function has 3 major logical parts (separated by brackets both for + * This function has 2 major logical parts (separated by brackets both for * readability and variable scoping). The first part performs the * operations related to finishing off the last chunk we fed to the DMA. - * The second part detects the end of playlist condition when the PCM - * buffer is empty except for uncommitted samples. Then they are committed - * and sent to the PCM driver for playback. The third part performs the - * operations involved in sending a new chunk to the DMA. */ -static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) ICODE_ATTR; -static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) + * The third part performs the operations involved in sending a new chunk to the DMA. */ +static void ICODE_ATTR pcmbuf_pcm_callback(unsigned char** start, size_t* size) { + /* Get the chunk that just completed */ + struct chunkdesc *chunk = get_chunk(chunk_ridx, 0); + bool endoftrack = false; + + if (last_chunksize != 0) { - struct chunkdesc *pcmbuf_current = read_chunk; - /* Take the finished chunk out of circulation */ - read_chunk = pcmbuf_current->link; + /* If last chunk in the track, notify of track change */ + endoftrack = chunk->flags & CHUNK_END_OF_TRACK; - /* if during a track transition, update the elapsed time in ms */ - if (track_transition) - audio_pcmbuf_position_callback(last_chunksize * 1000 / BYTERATE); - - /* if last chunk in the track, stop updates and notify audio thread */ - if (pcmbuf_current->end_of_track) - { - track_transition = false; - audio_pcmbuf_track_change(true); - } - - /* Put the finished chunk back into circulation */ - write_end_chunk->link = pcmbuf_current; - write_end_chunk = pcmbuf_current; - /* If we've read over the mix chunk while it's still mixing there */ - if (pcmbuf_current == mix_chunk) + if (chunk == mix_chunk) mix_chunk = NULL; #ifdef HAVE_CROSSFADE /* If we've read over the crossfade chunk while it's still fading */ - if (pcmbuf_current == crossfade_chunk) - crossfade_chunk = read_chunk; + if (chunk == crossfade_chunk) + crossfade_chunk = chunk; #endif + chunk->flags = 0; /* Invalidate old chunk's fields */ + + /* Free previous chunk */ + chunk = increment_chunk(&chunk_ridx); + pcmbuf_bytes_read += PCMBUF_CHUNK_SIZE; } + if ((size_t)pcmbuf_unplayed_bytes() > 0) { - /* Commit last samples at end of playlist */ - if (pcmbuffer_fillpos && !read_chunk) + /* Send the new chunk to the mixer */ + last_chunksize = PCMBUF_CHUNK_SIZE; + *start = chunk->addr; + *size = chunk->size; + + if (chunk->flags & CHUNK_HAS_POSITION) { - logf("pcmbuf_pcm_callback: commit last samples"); - commit_chunk(false); + /* Timestamped - update the elapsed time and offset */ + audio_pcmbuf_position_callback(chunk->elapsed, chunk->offset); } } - + else { - /* Send the new chunk to the DMA */ - if(read_chunk) - { - last_chunksize = read_chunk->size; - pcmbuf_unplayed_bytes -= last_chunksize; - *size = last_chunksize; - *start = read_chunk->addr; - } - else - { - /* No more chunks */ - logf("pcmbuf_pcm_callback: no more chunks"); - last_chunksize = 0; - *size = 0; - *start = NULL; - } + /* No more chunks */ + logf("pcmbuf_pcm_callback: no more chunks"); + last_chunksize = 0; } + + if (endoftrack) + audio_pcmbuf_track_change(true); + DISPLAY_DESC("callback"); } /* Force playback */ void pcmbuf_play_start(void) { - if (!pcm_is_playing() && pcmbuf_unplayed_bytes && read_chunk != NULL) + logf("pcmbuf_play_start"); + + if (!pcm_is_playing() && pcmbuf_unplayed_bytes() > 0) { - logf("pcmbuf_play_start"); - last_chunksize = read_chunk->size; - pcmbuf_unplayed_bytes -= last_chunksize; - pcm_play_data(pcmbuf_pcm_callback, - read_chunk->addr, last_chunksize); + pcm_play_data(pcmbuf_pcm_callback, NULL, 0); } } @@ -711,22 +757,17 @@ logf("pcmbuf_play_stop"); pcm_play_stop(); - pcmbuf_unplayed_bytes = 0; - mix_chunk = NULL; - if (read_chunk) { - write_end_chunk->link = read_chunk; - write_end_chunk = read_end_chunk; - read_chunk = read_end_chunk = NULL; - } + init_pcmbuffers(); + + pcmbuf_bytes_written = pcmbuf_bytes_read = 0; + chunk_ridx = chunk_widx = 0; + pcmbuf_bytes_waiting = 0; last_chunksize = 0; - pcmbuffer_pos = 0; - pcmbuffer_fillpos = 0; #ifdef HAVE_CROSSFADE crossfade_track_change_started = false; crossfade_active = false; #endif - track_transition = false; - flush_pcmbuf = false; + DISPLAY_DESC("play_stop"); /* Can unboost the codec thread here no matter who's calling, @@ -757,31 +798,56 @@ #ifdef HAVE_CROSSFADE /* Find the chunk that's (length) deep in the list. Return the position within * the chunk, and leave the chunkdesc pointer pointing to the chunk. */ -static size_t find_chunk(size_t length, struct chunkdesc **chunk) +static size_t find_chunk(size_t length, struct chunkdesc **chunk, + unsigned int *index) { - while (*chunk && length >= (*chunk)->size) + unsigned int i = *index; + struct chunkdesc *desc; + + while (1) { - length -= (*chunk)->size; - *chunk = (*chunk)->link; + desc = increment_chunk(&i); + + if (i == chunk_widx) + { + /* No more data */ + desc = NULL; + break; + } + + if (length < (*chunk)->size) + desc = *chunk; + break; + + length -= desc->size; } + + *chunk = desc; + *index = i; return length; } /* Returns the number of bytes _NOT_ mixed/faded */ static size_t crossfade_mix_fade(int factor, size_t length, const char *buf, - size_t *out_sample, struct chunkdesc **out_chunk) + size_t *out_sample, struct chunkdesc **out_chunk, + unsigned int *index, + unsigned long elapsed, unsigned long offset, + bool stamp) { if (length == 0) return 0; const int16_t *input_buf = (const int16_t *)buf; - int16_t *output_buf = (int16_t *)((*out_chunk)->addr); + int16_t *output_buf = (int16_t *)(*out_chunk)->addr; int16_t *chunk_end = SKIPBYTES(output_buf, (*out_chunk)->size); output_buf = &output_buf[*out_sample]; int32_t sample; while (length) { + if (stamp) + stamp_chunk(*out_chunk, elapsed, offset); + /* fade left and right channel at once to keep buffer alignment */ int i; for (i = 0; i < 2; i++) @@ -806,14 +872,22 @@ /* move to next chunk as needed */ if (output_buf >= chunk_end) { - *out_chunk = (*out_chunk)->link; - if (!(*out_chunk)) + struct chunkdesc *desc = increment_chunk(index); + + if (*index == chunk_widx) + { + /* End of existing data */ + *out_chunk = NULL; return length; - output_buf = (int16_t *)((*out_chunk)->addr); + } + + *out_chunk = desc; + output_buf = (int16_t *)desc->addr; chunk_end = SKIPBYTES(output_buf, (*out_chunk)->size); } } - *out_sample = output_buf - (int16_t *)((*out_chunk)->addr); + + *out_sample = output_buf - (int16_t *)(*out_chunk)->addr; return 0; } @@ -831,18 +905,20 @@ /* Reject crossfade if less than .5s of data */ if (LOW_DATA(2)) { logf("crossfade rejected"); - pcmbuf_play_stop(); - return ; + crossfade_active = false; + return; } + commit_if_needed(1); + logf("crossfade_start"); - commit_chunk(false); crossfade_active = true; /* Initialize the crossfade buffer size to all of the buffered data that * has not yet been sent to the DMA */ - crossfade_rem = pcmbuf_unplayed_bytes; - crossfade_chunk = read_chunk->link; + crossfade_rem = pcmbuf_unplayed_bytes(); + crossfade_chunk_idx = chunk_ridx; + crossfade_chunk = get_chunk(crossfade_chunk_idx, 0); crossfade_sample = 0; /* Get fade out info from settings. */ @@ -856,15 +932,16 @@ /* Automatic track changes only modify the last part of the buffer, * so find the right chunk and sample to start the crossfade */ { - crossfade_sample = find_chunk(crossfade_rem - crossfade_need, - &crossfade_chunk) / 2; + find_chunk(crossfade_rem - crossfade_need, + &crossfade_chunk, &crossfade_chunk_idx); crossfade_rem = crossfade_need; } else /* Manual skips occur immediately, but give time to process */ { crossfade_rem -= crossfade_chunk->size; - crossfade_chunk = crossfade_chunk->link; + crossfade_chunk = get_chunk(crossfade_chunk_idx, 2); + crossfade_chunk_idx = get_chunk_index(crossfade_chunk); } } /* Truncate fade out duration if necessary. */ @@ -888,26 +965,30 @@ size_t total_fade_out = fade_out_rem; size_t fade_out_sample; struct chunkdesc *fade_out_chunk = crossfade_chunk; + unsigned int fade_out_index = crossfade_chunk_idx; /* Find the right chunk and sample to start fading out */ fade_out_delay += crossfade_sample * 2; - fade_out_sample = find_chunk(fade_out_delay, &fade_out_chunk) / 2; + fade_out_sample = find_chunk(fade_out_delay, &fade_out_chunk, + &fade_out_index) / 2; while (fade_out_rem > 0) { - /* Each 1/10 second of audio will have the same fade applied */ - size_t block_rem = MIN(BYTERATE / 10, fade_out_rem); + /* Each ~1/20 second of audio will have the same fade applied */ + size_t block_rem = MIN(CROSSFADE_BUFSIZE, fade_out_rem); int factor = (fade_out_rem << 8) / total_fade_out; fade_out_rem -= block_rem; crossfade_mix_fade(factor, block_rem, NULL, - &fade_out_sample, &fade_out_chunk); + &fade_out_sample, &fade_out_chunk, &fade_out_index, + 0, 0, false); } /* zero out the rest of the buffer */ crossfade_mix_fade(0, crossfade_rem, NULL, - &fade_out_sample, &fade_out_chunk); + &fade_out_sample, &fade_out_chunk, &fade_out_index, + 0, 0, false); } /* Initialize fade-in counters */ @@ -918,12 +999,14 @@ /* Find the right chunk and sample to start fading in */ fade_in_delay += crossfade_sample * 2; - crossfade_sample = find_chunk(fade_in_delay, &crossfade_chunk) / 2; + find_chunk(fade_in_delay, &crossfade_chunk, &crossfade_chunk_idx); logf("crossfade_start done!"); } /* Perform fade-in of new track */ -static void write_to_crossfade(size_t length) +static void write_to_crossfade(size_t length, + unsigned long elapsed, + unsigned long offset) { if (length) { @@ -948,7 +1031,8 @@ /* Mix the data */ size_t fade_total = fade_rem; fade_rem = crossfade_mix_fade(factor, fade_rem, buf, - &crossfade_sample, &crossfade_chunk); + &crossfade_sample, &crossfade_chunk, + &crossfade_chunk_idx, elapsed, offset, true); length -= fade_total - fade_rem; buf += fade_total - fade_rem; if (!length) @@ -971,7 +1055,8 @@ size_t mix_total = length; /* A factor of 256 means mix only, no fading */ length = crossfade_mix_fade(256, length, buf, - &crossfade_sample, &crossfade_chunk); + &crossfade_sample, &crossfade_chunk, + &crossfade_chunk_idx, elapsed, offset, true); buf += mix_total - length; if (!length) return; @@ -979,12 +1064,18 @@ while (length > 0) { - COMMIT_IF_NEEDED; - size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos; - size_t copy_n = MIN(length, pcmbuf_size - pcmbuffer_index); - memcpy(&pcmbuffer[pcmbuffer_index], buf, copy_n); + commit_if_needed(PCMBUF_CHUNK_SIZE); + + struct chunkdesc *desc = get_chunk(chunk_widx, 0); + unsigned char *outbuf = desc->addr + pcmbuf_bytes_waiting; + unsigned char *end = guardbuf + PCMBUF_CHUNK_SIZE; + size_t copy_n = MIN(length, (size_t)(end - outbuf)); + memcpy(outbuf, buf, copy_n); + + stamp_chunk(desc, elapsed, offset); + buf += copy_n; - pcmbuffer_fillpos += copy_n; + pcmbuf_bytes_waiting += copy_n; length -= copy_n; } } @@ -1036,19 +1127,19 @@ /* Returns pcm buffer usage in percents (0 to 100). */ static int pcmbuf_usage(void) { - return pcmbuf_unplayed_bytes * 100 / pcmbuf_size; + return pcmbuf_unplayed_bytes() * 100 / pcmbuf_size; } static int pcmbuf_mix_free(void) { if (mix_chunk) { - size_t my_mix_end = - (size_t)&((int16_t *)mix_chunk->addr)[pcmbuf_mix_sample]; - size_t my_write_pos = (size_t)&pcmbuffer[pcmbuffer_pos]; + unsigned char *my_mix_end = &mix_chunk->addr[pcmbuf_mix_sample]; + unsigned char *my_write_pos = + &get_chunk(chunk_widx, 0)->addr[pcmbuf_bytes_waiting]; if (my_write_pos < my_mix_end) my_write_pos += pcmbuf_size; - return (my_write_pos - my_mix_end) * 100 / pcmbuf_unplayed_bytes; + return (my_write_pos - my_mix_end) * 100 / pcmbuf_unplayed_bytes(); } return 100; } @@ -1059,25 +1150,18 @@ completion) */ if (audio_status() & AUDIO_STATUS_PLAY) { - if (read_chunk == NULL) + if (pcmbuf_usage() >= 8 && pcmbuf_mix_free() >= 30) { - return NULL; - } - else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 && - (mix_chunk || read_chunk->link)) - { *count = MIN(*count, AUX_BUFSIZE/4); return voicebuf; } - else - { - return NULL; - } } else { return pcmbuf_request_buffer(count); } + + return NULL; } void pcmbuf_write_voice_complete(int count) @@ -1085,7 +1169,7 @@ /* A get-it-to-work-for-now hack (audio status could have changed) */ if (!(audio_status() & AUDIO_STATUS_PLAY)) { - pcmbuf_write_complete(count); + pcmbuf_write_complete(count, 0, 0); return; } @@ -1093,11 +1177,16 @@ int16_t *obuf; size_t chunk_samples; - if (mix_chunk == NULL && read_chunk != NULL) + if (mix_chunk == NULL) { - mix_chunk = read_chunk->link; - /* Start 1/8s into the next chunk */ - pcmbuf_mix_sample = BYTERATE / 16; + /* Allow at least one interrupt so callback doesn't nullify the mix + chunk */ + if (pcmbuf_unplayed_bytes() < 4*PCMBUF_CHUNK_SIZE) + return; + + mix_chunk = get_chunk(chunk_ridx, 2); + mix_chunk_idx = get_chunk_index(mix_chunk); + pcmbuf_mix_sample = 0; } if (!mix_chunk) @@ -1114,13 +1203,17 @@ if (pcmbuf_mix_sample >= chunk_samples) { - mix_chunk = mix_chunk->link; - if (!mix_chunk) + pcmbuf_mix_sample = 0; + + mix_chunk = increment_chunk(&mix_chunk_idx); + + if (mix_chunk_idx == chunk_widx) return; - pcmbuf_mix_sample = 0; + obuf = (int16_t *)mix_chunk->addr; chunk_samples = mix_chunk->size / 2; } + sample += obuf[pcmbuf_mix_sample] >> 2; obuf[pcmbuf_mix_sample++] = clip_sample_16(sample); } @@ -1132,16 +1225,7 @@ /* Amount of bytes left in the buffer. */ size_t pcmbuf_free(void) { - if (read_chunk != NULL) - { - void *read = (void *)read_chunk->addr; - void *write = &pcmbuffer[pcmbuffer_pos + pcmbuffer_fillpos]; - if (read < write) - return (size_t)(read - write) + pcmbuf_size; - else - return (size_t) (read - write); - } - return pcmbuf_size - pcmbuffer_fillpos; + return pcmbuf_size - pcmbuf_unplayed_bytes() - pcmbuf_bytes_waiting; } size_t pcmbuf_get_bufsize(void) @@ -1151,24 +1235,23 @@ int pcmbuf_used_descs(void) { - struct chunkdesc *temp = read_chunk; - unsigned int i = 0; - while (temp) { - temp = temp->link; - i++; - } - return i; + int used = chunk_widx - chunk_ridx; + + if (used < 0 || (used == 0 && pcmbuf_unplayed_bytes())) + used += pcmbuf_desc_count; + + return used; } int pcmbuf_descs(void) { - return NUM_CHUNK_DESCS(pcmbuf_size); + return pcmbuf_desc_count; } #ifdef ROCKBOX_HAS_LOGF unsigned char *pcmbuf_get_meminfo(size_t *length) { - *length = pcmbuf_bufend - pcmbuffer; + *length = pcmbuf_memend - pcmbuffer; return pcmbuffer; } #endif @@ -1178,7 +1261,7 @@ bool pcmbuf_is_lowdata(void) { - if (!pcm_is_playing() || pcm_is_paused() + if (!audio_pcmbuf_may_play() #ifdef HAVE_CROSSFADE || pcmbuf_is_crossfade_active() #endif @@ -1190,7 +1273,7 @@ return LOW_DATA(4); #else /* under watermark is low data */ - return (pcmbuf_unplayed_bytes < pcmbuf_watermark); + return (pcmbuf_unplayed_bytes() < pcmbuf_watermark); #endif } @@ -1199,11 +1282,6 @@ low_latency_mode = state; } -unsigned long pcmbuf_get_latency(void) -{ - return (pcmbuf_unplayed_bytes + pcm_get_bytes_waiting()) * 1000 / BYTERATE; -} - #ifndef HAVE_HARDWARE_BEEP #define MINIBUF_SAMPLES (NATIVE_FREQUENCY / 1000 * KEYCLICK_DURATION) #define MINIBUF_SIZE (MINIBUF_SAMPLES*4) @@ -1217,10 +1295,10 @@ int16_t *bufptr, *bufstart, *bufend; int32_t sample; int nsamples = NATIVE_FREQUENCY / 1000 * duration; - bool mix = read_chunk != NULL && read_chunk->link != NULL; + bool mix = last_chunksize != 0; int i; - bufend = SKIPBYTES((int16_t *)pcmbuffer, pcmbuf_size); + bufend = (int16_t *)guardbuf; /* Find the insertion point and set bufstart to the start of it */ if (mix) Index: apps/pcmbuf.h =================================================================== --- apps/pcmbuf.h (revision 29993) +++ apps/pcmbuf.h (working copy) @@ -23,7 +23,8 @@ /* Commit PCM data */ void *pcmbuf_request_buffer(int *count); -void pcmbuf_write_complete(int count); +void pcmbuf_write_complete(int count, unsigned long elapsed, + unsigned long offset); /* Init */ size_t pcmbuf_init(unsigned char *bufend); @@ -34,6 +35,7 @@ void pcmbuf_pause(bool pause); void pcmbuf_monitor_track_change(bool monitor); bool pcmbuf_start_track_change(bool manual_skip); +void pcmbuf_drain(void); /* Crossfade */ #ifdef HAVE_CROSSFADE @@ -66,7 +68,6 @@ /* Misc */ bool pcmbuf_is_lowdata(void); void pcmbuf_set_low_latency(bool state); -unsigned long pcmbuf_get_latency(void); void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude); #endif Index: apps/codec_thread.c =================================================================== --- apps/codec_thread.c (revision 29993) +++ apps/codec_thread.c (working copy) @@ -79,7 +79,8 @@ /* 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_complete(int status); +extern void audio_codec_seek_complete(void); extern struct codec_api ci; /* from codecs.c */ /* Codec thread */ @@ -251,7 +252,7 @@ if (out_count <= 0) return; - pcmbuf_write_complete(out_count); + pcmbuf_write_complete(out_count, ci.id3->elapsed, ci.id3->offset); count -= inp_count; } @@ -335,8 +336,7 @@ dsp_configure(ci.dsp, DSP_FLUSH, 0); /* 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 +521,7 @@ /* 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); } } Index: apps/playback.c =================================================================== --- apps/playback.c (revision 29993) +++ apps/playback.c (working copy) @@ -39,6 +39,7 @@ #include "abrepeat.h" #include "pcmbuf.h" #include "playback.h" +#include "misc.h" #ifdef HAVE_TAGCACHE #include "tagcache.h" @@ -357,9 +358,7 @@ /**************************************/ /** --- 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); } @@ -2084,16 +2083,15 @@ track_id3 = bufgetid3(info->id3_hid); } - 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; + playing_id3_sync(info, -1); } + else + { + id3_write(PLAYING_ID3, track_id3); + } /* The skip is technically over */ skip_pending = TRACK_SKIP_NONE; @@ -2190,19 +2188,14 @@ 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; skip_pending = TRACK_SKIP_AUTO; + /* Let PCM play any remaining data */ + pcmbuf_drain(); + /* Does this track have an entry allocated? */ struct track_info *info = track_list_advance_current(1); @@ -3078,75 +3071,56 @@ /** -- 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; + /* Normal automatic skip */ + ab_end_of_track_report(); } - else - { - value = 0; - } +#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, + unsigned long offset) { struct mp3entry *id3 = id3_get(PLAYING_ID3); - - time += id3->elapsed; - - id3->elapsed = MIN(time, id3->length); + id3->elapsed = MIN(elapsed, id3->length); + id3->offset = offset; } /* Post message from pcmbuf that the end of the previous track has just Index: apps/voice_thread.c =================================================================== --- apps/voice_thread.c (revision 29993) +++ apps/voice_thread.c (working copy) @@ -361,8 +361,8 @@ } /* If all clips are done and not playing, force pcm playback. */ - if (!pcm_is_playing()) - pcmbuf_play_start(); + if (!playback_is_playing()) + pcmbuf_drain(); /* Synthesize a stop request */ /* NOTE: We have no way to know when the pcm data placed in the Index: apps/codecs/vorbis.c =================================================================== --- apps/codecs/vorbis.c (revision 29993) +++ apps/codecs/vorbis.c (working copy) @@ -196,6 +196,9 @@ ci->set_elapsed(ov_time_tell(&vf)); ci->set_offset(ov_raw_tell(&vf)); } + else { + ci->set_elapsed(0); + } previous_section = -1; eof = 0; Index: apps/codecs/flac.c =================================================================== --- apps/codecs/flac.c (revision 29993) +++ apps/codecs/flac.c (working copy) @@ -460,7 +460,9 @@ 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; Index: apps/codecs/aiff.c =================================================================== --- apps/codecs/aiff.c (revision 29993) +++ apps/codecs/aiff.c (working copy) @@ -288,6 +288,8 @@ bytesdone = 0; } + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + /* The main decoder loop */ endofstream = 0; Index: apps/codecs/ape.c =================================================================== --- apps/codecs/ape.c (revision 29993) +++ apps/codecs/ape.c (working copy) @@ -220,6 +220,9 @@ 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); Index: apps/codecs/au.c =================================================================== --- apps/codecs/au.c (revision 29993) +++ apps/codecs/au.c (working copy) @@ -253,6 +253,8 @@ bytesdone = 0; } + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + /* The main decoder loop */ endofstream = 0; Index: apps/codecs/adx.c =================================================================== --- apps/codecs/adx.c (revision 29993) +++ apps/codecs/adx.c (working copy) @@ -209,8 +209,8 @@ /* get in position */ ci->seek_buffer(bufoff); + ci->set_elapsed(0); - /* setup pcm buffer format */ ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); if (channels == 2) { @@ -276,6 +276,11 @@ loop_count++; } ci->seek_buffer(bufoff); + + ci->set_elapsed( + ((end_adr-start_adr)*loop_count + bufoff-chanstart)* + 1000LL/avgbytespersec); + ci->seek_complete(); } Index: apps/codecs/vox.c =================================================================== --- apps/codecs/vox.c (revision 29993) +++ apps/codecs/vox.c (working copy) @@ -141,6 +141,8 @@ bytesdone = 0; } + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + /* The main decoder loop */ endofstream = 0; Index: apps/codecs/mpa.c =================================================================== --- apps/codecs/mpa.c (revision 29993) +++ apps/codecs/mpa.c (working copy) @@ -144,6 +144,7 @@ { 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 @@ /* 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 Index: apps/codecs/mpc.c =================================================================== --- apps/codecs/mpc.c (revision 29993) +++ apps/codecs/mpc.c (working copy) @@ -123,6 +123,8 @@ 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 @@ } } + ci->set_elapsed(elapsed_time); + /* This is the decoding loop. */ do { Index: apps/codecs/aac.c =================================================================== --- apps/codecs/aac.c (revision 29993) +++ apps/codecs/aac.c (working copy) @@ -132,8 +132,6 @@ 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; } @@ -141,6 +139,9 @@ } else { sound_samples_done = 0; } + + elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); + ci->set_elapsed(elapsed_time); if (i == 0) { Index: apps/codecs/speex.c =================================================================== --- apps/codecs/speex.c (revision 29993) +++ apps/codecs/speex.c (working copy) @@ -417,6 +417,7 @@ } ci->seek_buffer(0); + ci->set_elapsed(0); stereo = speex_stereo_state_init(); spx_ogg_sync_init(&oy); Index: apps/codecs/spc.c =================================================================== --- apps/codecs/spc.c (revision 29993) +++ apps/codecs/spc.c (working copy) @@ -560,6 +560,8 @@ return CODEC_ERROR; DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize); + ci->set_elapsed(0); + do { if (load_spc_buffer(buffer, buffersize)) { Index: apps/codecs/tta.c =================================================================== --- apps/codecs/tta.c (revision 29993) +++ apps/codecs/tta.c (working copy) @@ -90,6 +90,8 @@ 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); Index: apps/codecs/alac.c =================================================================== --- apps/codecs/alac.c (revision 29993) +++ apps/codecs/alac.c (working copy) @@ -97,6 +97,8 @@ } } + 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); Index: apps/codecs/wmapro.c =================================================================== --- apps/codecs/wmapro.c (revision 29993) +++ apps/codecs/wmapro.c (working copy) @@ -79,6 +79,7 @@ ci->seek_buffer(ci->id3->first_frame_offset); elapsedtime = 0; + ci->set_elapsed(0); /* The main decoding loop */ Index: apps/codecs/wav64.c =================================================================== --- apps/codecs/wav64.c (revision 29993) +++ apps/codecs/wav64.c (working copy) @@ -381,6 +381,8 @@ bytesdone = 0; } + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + /* The main decoder loop */ endofstream = 0; Index: apps/codecs/wmavoice.c =================================================================== --- apps/codecs/wmavoice.c (revision 29993) +++ apps/codecs/wmavoice.c (working copy) @@ -109,6 +109,8 @@ ci->seek_buffer(ci->id3->first_frame_offset); elapsedtime = 0; + ci->set_elapsed(0); + resume_offset = 0; /* The main decoding loop */ Index: apps/codecs/mod.c =================================================================== --- apps/codecs/mod.c (revision 29993) +++ apps/codecs/mod.c (working copy) @@ -1273,12 +1273,11 @@ /* 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; } Index: apps/codecs/sid.c =================================================================== --- apps/codecs/sid.c (revision 29993) +++ apps/codecs/sid.c (working copy) @@ -1299,8 +1299,8 @@ nSamplesToRender = 0; /* Start the rendering from scratch */ /* Set the elapsed time to the current subsong (in seconds) */ + ci->set_elapsed(subSong*1000); ci->seek_complete(); - ci->set_elapsed(subSong*1000); } nSamplesRendered = 0; Index: apps/codecs/shorten.c =================================================================== --- apps/codecs/shorten.c (revision 29993) +++ apps/codecs/shorten.c (working copy) @@ -99,6 +99,8 @@ 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 @@ 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; } Index: apps/codecs/wma.c =================================================================== --- apps/codecs/wma.c (revision 29993) +++ apps/codecs/wma.c (working copy) @@ -84,7 +84,6 @@ % wfx.packet_size; ci->seek_buffer(resume_offset - packet_offset); elapsedtime = asf_get_timestamp(&i); - ci->set_elapsed(elapsedtime); } else { @@ -93,6 +92,8 @@ 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 ? Index: apps/codecs/a52_rm.c =================================================================== --- apps/codecs/a52_rm.c (revision 29993) +++ apps/codecs/a52_rm.c (working copy) @@ -178,6 +178,7 @@ } else { /* Seek to the first packet */ + ci->set_elapsed(0); ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE ); } Index: apps/codecs/smaf.c =================================================================== --- apps/codecs/smaf.c (revision 29993) +++ apps/codecs/smaf.c (working copy) @@ -429,6 +429,8 @@ bytesdone = 0; } + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + /* The main decoder loop */ endofstream = 0; Index: apps/codecs/a52.c =================================================================== --- apps/codecs/a52.c (revision 29993) +++ apps/codecs/a52.c (working copy) @@ -158,7 +158,7 @@ } else { ci->seek_buffer(ci->id3->first_frame_offset); - samplesdone = 0; + ci->set_elapsed(0); } while (1) { Index: apps/codecs/cook.c =================================================================== --- apps/codecs/cook.c (revision 29993) +++ apps/codecs/cook.c (working copy) @@ -105,8 +105,10 @@ 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 */ Index: apps/codecs/wavpack.c =================================================================== --- apps/codecs/wavpack.c (revision 29993) +++ apps/codecs/wavpack.c (working copy) @@ -75,7 +75,7 @@ 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 */ Index: apps/codecs/wav.c =================================================================== --- apps/codecs/wav.c (revision 29993) +++ apps/codecs/wav.c (working copy) @@ -378,6 +378,8 @@ bytesdone = 0; } + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + /* The main decoder loop */ endofstream = 0;