summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2011-08-28 07:45:35 +0000
committerMichael Sevakis <jethead71@rockbox.org>2011-08-28 07:45:35 +0000
commit7ad2cad173ffa094bb285112582afee1c9aea4e5 (patch)
treece23e816cfdffb1767ebe44f4f960c304d8a5fb9
parent463b3ed8b2630d1b9d656dd2a52bbcbd429b4c08 (diff)
downloadrockbox-7ad2cad173ffa094bb285112582afee1c9aea4e5.tar.gz
rockbox-7ad2cad173ffa094bb285112582afee1c9aea4e5.zip
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
-rw-r--r--apps/codec_thread.c18
-rw-r--r--apps/codecs/a52.c2
-rw-r--r--apps/codecs/a52_rm.c1
-rw-r--r--apps/codecs/aac.c5
-rw-r--r--apps/codecs/adx.c7
-rw-r--r--apps/codecs/aiff.c2
-rw-r--r--apps/codecs/alac.c2
-rw-r--r--apps/codecs/ape.c3
-rw-r--r--apps/codecs/au.c2
-rw-r--r--apps/codecs/cook.c4
-rw-r--r--apps/codecs/flac.c4
-rw-r--r--apps/codecs/mod.c3
-rw-r--r--apps/codecs/mpa.c12
-rw-r--r--apps/codecs/mpc.c4
-rw-r--r--apps/codecs/shorten.c3
-rw-r--r--apps/codecs/sid.c2
-rw-r--r--apps/codecs/smaf.c2
-rw-r--r--apps/codecs/spc.c2
-rw-r--r--apps/codecs/speex.c1
-rw-r--r--apps/codecs/tta.c2
-rw-r--r--apps/codecs/vorbis.c3
-rw-r--r--apps/codecs/vox.c2
-rw-r--r--apps/codecs/wav.c2
-rw-r--r--apps/codecs/wav64.c2
-rw-r--r--apps/codecs/wavpack.c2
-rw-r--r--apps/codecs/wma.c3
-rw-r--r--apps/codecs/wmapro.c1
-rw-r--r--apps/codecs/wmavoice.c2
-rw-r--r--apps/pcmbuf.c1415
-rw-r--r--apps/pcmbuf.h29
-rw-r--r--apps/playback.c211
-rw-r--r--firmware/export/system.h4
32 files changed, 948 insertions, 809 deletions
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index 945f0b0605..199bb0e742 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -77,9 +77,10 @@ struct codec_load_info
77static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */ 77static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */
78 78
79/* Private interfaces to main playback control */ 79/* Private interfaces to main playback control */
80extern void audio_codec_update_elapsed(unsigned long value); 80extern void audio_codec_update_elapsed(unsigned long elapsed);
81extern void audio_codec_update_offset(size_t value); 81extern void audio_codec_update_offset(size_t offset);
82extern void audio_queue_post(long id, intptr_t data); 82extern void audio_codec_complete(int status);
83extern void audio_codec_seek_complete(void);
83extern struct codec_api ci; /* from codecs.c */ 84extern struct codec_api ci; /* from codecs.c */
84 85
85/* Codec thread */ 86/* Codec thread */
@@ -251,7 +252,7 @@ static void codec_pcmbuf_insert_callback(
251 if (out_count <= 0) 252 if (out_count <= 0)
252 return; 253 return;
253 254
254 pcmbuf_write_complete(out_count); 255 pcmbuf_write_complete(out_count, ci.id3->elapsed, ci.id3->offset);
255 256
256 count -= inp_count; 257 count -= inp_count;
257 } 258 }
@@ -334,9 +335,11 @@ static void codec_seek_complete_callback(void)
334 /* Clear DSP */ 335 /* Clear DSP */
335 dsp_configure(ci.dsp, DSP_FLUSH, 0); 336 dsp_configure(ci.dsp, DSP_FLUSH, 0);
336 337
338 /* Sync position */
339 audio_codec_update_offset(ci.curpos);
340
337 /* Post notification to audio thread */ 341 /* Post notification to audio thread */
338 LOGFQUEUE("audio > Q_AUDIO_CODEC_SEEK_COMPLETE"); 342 audio_codec_seek_complete();
339 audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
340 343
341 /* Wait for urgent or go message */ 344 /* Wait for urgent or go message */
342 do 345 do
@@ -521,8 +524,7 @@ static void run_codec(void)
521 524
522 /* Notify audio that we're done for better or worse - advise of the 525 /* Notify audio that we're done for better or worse - advise of the
523 status */ 526 status */
524 LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status); 527 audio_codec_complete(status);
525 audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
526 } 528 }
527} 529}
528 530
diff --git a/apps/codecs/a52.c b/apps/codecs/a52.c
index 4cd293e37f..641e2d05da 100644
--- a/apps/codecs/a52.c
+++ b/apps/codecs/a52.c
@@ -158,7 +158,7 @@ enum codec_status codec_run(void)
158 } 158 }
159 else { 159 else {
160 ci->seek_buffer(ci->id3->first_frame_offset); 160 ci->seek_buffer(ci->id3->first_frame_offset);
161 samplesdone = 0; 161 ci->set_elapsed(0);
162 } 162 }
163 163
164 while (1) { 164 while (1) {
diff --git a/apps/codecs/a52_rm.c b/apps/codecs/a52_rm.c
index c1930aa7b4..0aa3edca3a 100644
--- a/apps/codecs/a52_rm.c
+++ b/apps/codecs/a52_rm.c
@@ -178,6 +178,7 @@ enum codec_status codec_run(void)
178 } 178 }
179 else { 179 else {
180 /* Seek to the first packet */ 180 /* Seek to the first packet */
181 ci->set_elapsed(0);
181 ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE ); 182 ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE );
182 } 183 }
183 184
diff --git a/apps/codecs/aac.c b/apps/codecs/aac.c
index 52e08c7b56..f1e81bad81 100644
--- a/apps/codecs/aac.c
+++ b/apps/codecs/aac.c
@@ -134,8 +134,6 @@ enum codec_status codec_run(void)
134 if (m4a_seek_raw(&demux_res, &input_stream, file_offset, 134 if (m4a_seek_raw(&demux_res, &input_stream, file_offset,
135 &sound_samples_done, (int*) &i)) { 135 &sound_samples_done, (int*) &i)) {
136 sound_samples_done *= sbr_fac; 136 sound_samples_done *= sbr_fac;
137 elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
138 ci->set_elapsed(elapsed_time);
139 } else { 137 } else {
140 sound_samples_done = 0; 138 sound_samples_done = 0;
141 } 139 }
@@ -143,6 +141,9 @@ enum codec_status codec_run(void)
143 } else { 141 } else {
144 sound_samples_done = 0; 142 sound_samples_done = 0;
145 } 143 }
144
145 elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
146 ci->set_elapsed(elapsed_time);
146 147
147 if (i == 0) 148 if (i == 0)
148 { 149 {
diff --git a/apps/codecs/adx.c b/apps/codecs/adx.c
index e75e7dca1e..8144d5f468 100644
--- a/apps/codecs/adx.c
+++ b/apps/codecs/adx.c
@@ -209,7 +209,7 @@ enum codec_status codec_run(void)
209 209
210 /* get in position */ 210 /* get in position */
211 ci->seek_buffer(bufoff); 211 ci->seek_buffer(bufoff);
212 212 ci->set_elapsed(0);
213 213
214 /* setup pcm buffer format */ 214 /* setup pcm buffer format */
215 ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); 215 ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
@@ -276,6 +276,11 @@ enum codec_status codec_run(void)
276 loop_count++; 276 loop_count++;
277 } 277 }
278 ci->seek_buffer(bufoff); 278 ci->seek_buffer(bufoff);
279
280 ci->set_elapsed(
281 ((end_adr-start_adr)*loop_count + bufoff-chanstart)*
282 1000LL/avgbytespersec);
283
279 ci->seek_complete(); 284 ci->seek_complete();
280 } 285 }
281 286
diff --git a/apps/codecs/aiff.c b/apps/codecs/aiff.c
index a8185b4910..333bcd0455 100644
--- a/apps/codecs/aiff.c
+++ b/apps/codecs/aiff.c
@@ -288,6 +288,8 @@ enum codec_status codec_run(void)
288 bytesdone = 0; 288 bytesdone = 0;
289 } 289 }
290 290
291 ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
292
291 /* The main decoder loop */ 293 /* The main decoder loop */
292 endofstream = 0; 294 endofstream = 0;
293 295
diff --git a/apps/codecs/alac.c b/apps/codecs/alac.c
index b6852f5b3f..144d796e5f 100644
--- a/apps/codecs/alac.c
+++ b/apps/codecs/alac.c
@@ -97,6 +97,8 @@ enum codec_status codec_run(void)
97 } 97 }
98 } 98 }
99 99
100 ci->set_elapsed(elapsedtime);
101
100 /* The main decoding loop */ 102 /* The main decoding loop */
101 while (i < demux_res.num_sample_byte_sizes) { 103 while (i < demux_res.num_sample_byte_sizes) {
102 enum codec_command_action action = ci->get_command(&param); 104 enum codec_command_action action = ci->get_command(&param);
diff --git a/apps/codecs/ape.c b/apps/codecs/ape.c
index 8f95a01ec7..ed6ea21685 100644
--- a/apps/codecs/ape.c
+++ b/apps/codecs/ape.c
@@ -220,6 +220,9 @@ enum codec_status codec_run(void)
220 firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */ 220 firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */
221 } 221 }
222 222
223 elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
224 ci->set_elapsed(elapsedtime);
225
223 /* Initialise the buffer */ 226 /* Initialise the buffer */
224 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); 227 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
225 228
diff --git a/apps/codecs/au.c b/apps/codecs/au.c
index ef308358f4..cb75c7423d 100644
--- a/apps/codecs/au.c
+++ b/apps/codecs/au.c
@@ -253,6 +253,8 @@ enum codec_status codec_run(void)
253 bytesdone = 0; 253 bytesdone = 0;
254 } 254 }
255 255
256 ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
257
256 /* The main decoder loop */ 258 /* The main decoder loop */
257 endofstream = 0; 259 endofstream = 0;
258 260
diff --git a/apps/codecs/cook.c b/apps/codecs/cook.c
index 4a47e74f60..55188aad36 100644
--- a/apps/codecs/cook.c
+++ b/apps/codecs/cook.c
@@ -105,8 +105,10 @@ enum codec_status codec_run(void)
105 param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); 105 param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
106 action = CODEC_ACTION_SEEK_TIME; 106 action = CODEC_ACTION_SEEK_TIME;
107 } 107 }
108 else {
109 ci->set_elapsed(0);
110 }
108 111
109 ci->set_elapsed(0);
110 ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); 112 ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
111 113
112 /* The main decoder loop */ 114 /* The main decoder loop */
diff --git a/apps/codecs/flac.c b/apps/codecs/flac.c
index a5521b584f..c91a173f4a 100644
--- a/apps/codecs/flac.c
+++ b/apps/codecs/flac.c
@@ -460,7 +460,9 @@ enum codec_status codec_run(void)
460 codec_set_replaygain(ci->id3); 460 codec_set_replaygain(ci->id3);
461 461
462 flac_seek_offset(&fc, samplesdone); 462 flac_seek_offset(&fc, samplesdone);
463 samplesdone=0; 463 samplesdone=fc.samplenumber+fc.blocksize;
464 elapsedtime=(samplesdone*10)/(ci->id3->frequency/100);
465 ci->set_elapsed(elapsedtime);
464 466
465 /* The main decoding loop */ 467 /* The main decoding loop */
466 frame=0; 468 frame=0;
diff --git a/apps/codecs/mod.c b/apps/codecs/mod.c
index 3703ecd304..5bd64994f1 100644
--- a/apps/codecs/mod.c
+++ b/apps/codecs/mod.c
@@ -1333,12 +1333,11 @@ enum codec_status codec_run(void)
1333 /* New time is ready in param */ 1333 /* New time is ready in param */
1334 modplayer.patterntableposition = param/1000; 1334 modplayer.patterntableposition = param/1000;
1335 modplayer.currentline = 0; 1335 modplayer.currentline = 0;
1336 ci->set_elapsed(modplayer.patterntableposition*1000+500);
1337 ci->seek_complete(); 1336 ci->seek_complete();
1338 } 1337 }
1339 1338
1340 if(old_patterntableposition != modplayer.patterntableposition) { 1339 if(old_patterntableposition != modplayer.patterntableposition) {
1341 ci->set_elapsed(modplayer.patterntableposition*1000+500); 1340 ci->set_elapsed(modplayer.patterntableposition*1000);
1342 old_patterntableposition=modplayer.patterntableposition; 1341 old_patterntableposition=modplayer.patterntableposition;
1343 } 1342 }
1344 1343
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c
index c9e2131450..ac81f06cab 100644
--- a/apps/codecs/mpa.c
+++ b/apps/codecs/mpa.c
@@ -144,6 +144,7 @@ static void set_elapsed(struct mp3entry* id3)
144{ 144{
145 unsigned long offset = id3->offset > id3->first_frame_offset ? 145 unsigned long offset = id3->offset > id3->first_frame_offset ?
146 id3->offset - id3->first_frame_offset : 0; 146 id3->offset - id3->first_frame_offset : 0;
147 unsigned long elapsed = id3->elapsed;
147 148
148 if ( id3->vbr ) { 149 if ( id3->vbr ) {
149 if ( id3->has_toc ) { 150 if ( id3->has_toc ) {
@@ -172,27 +173,28 @@ static void set_elapsed(struct mp3entry* id3)
172 /* set time for this percent (divide before multiply to prevent 173 /* set time for this percent (divide before multiply to prevent
173 overflow on long files. loss of precision is negligible on 174 overflow on long files. loss of precision is negligible on
174 short files) */ 175 short files) */
175 id3->elapsed = i * (id3->length / 100); 176 elapsed = i * (id3->length / 100);
176 177
177 /* calculate remainder time */ 178 /* calculate remainder time */
178 plen = (nextpos - relpos) * (id3->filesize / 256); 179 plen = (nextpos - relpos) * (id3->filesize / 256);
179 id3->elapsed += (((remainder * 100) / plen) * 180 elapsed += (((remainder * 100) / plen) * (id3->length / 10000));
180 (id3->length / 10000));
181 } 181 }
182 else { 182 else {
183 /* no TOC exists. set a rough estimate using average bitrate */ 183 /* no TOC exists. set a rough estimate using average bitrate */
184 int tpk = id3->length / 184 int tpk = id3->length /
185 ((id3->filesize - id3->first_frame_offset - id3->id3v1len) / 185 ((id3->filesize - id3->first_frame_offset - id3->id3v1len) /
186 1024); 186 1024);
187 id3->elapsed = offset / 1024 * tpk; 187 elapsed = offset / 1024 * tpk;
188 } 188 }
189 } 189 }
190 else 190 else
191 { 191 {
192 /* constant bitrate, use exact calculation */ 192 /* constant bitrate, use exact calculation */
193 if (id3->bitrate != 0) 193 if (id3->bitrate != 0)
194 id3->elapsed = offset / (id3->bitrate / 8); 194 elapsed = offset / (id3->bitrate / 8);
195 } 195 }
196
197 ci->set_elapsed(elapsed);
196} 198}
197 199
198#ifdef MPA_SYNTH_ON_COP 200#ifdef MPA_SYNTH_ON_COP
diff --git a/apps/codecs/mpc.c b/apps/codecs/mpc.c
index 7388799ee8..a405c72f79 100644
--- a/apps/codecs/mpc.c
+++ b/apps/codecs/mpc.c
@@ -123,6 +123,8 @@ enum codec_status codec_run(void)
123 codec_set_replaygain(ci->id3); 123 codec_set_replaygain(ci->id3);
124 124
125 /* Resume to saved sample offset. */ 125 /* Resume to saved sample offset. */
126 elapsed_time = 0;
127
126 if (samplesdone > 0) 128 if (samplesdone > 0)
127 { 129 {
128 if (mpc_demux_seek_sample(demux, samplesdone) == MPC_STATUS_OK) 130 if (mpc_demux_seek_sample(demux, samplesdone) == MPC_STATUS_OK)
@@ -136,6 +138,8 @@ enum codec_status codec_run(void)
136 } 138 }
137 } 139 }
138 140
141 ci->set_elapsed(elapsed_time);
142
139 /* This is the decoding loop. */ 143 /* This is the decoding loop. */
140 do 144 do
141 { 145 {
diff --git a/apps/codecs/shorten.c b/apps/codecs/shorten.c
index db66991679..a8ab3f30a0 100644
--- a/apps/codecs/shorten.c
+++ b/apps/codecs/shorten.c
@@ -99,6 +99,8 @@ enum codec_status codec_run(void)
99 sc.bitindex = sc.gb.index - 8*consumed; 99 sc.bitindex = sc.gb.index - 8*consumed;
100 100
101seek_start: 101seek_start:
102 ci->set_elapsed(0);
103
102 /* The main decoding loop */ 104 /* The main decoding loop */
103 ci->memset(&decoded0, 0, sizeof(int32_t)*MAX_DECODE_SIZE); 105 ci->memset(&decoded0, 0, sizeof(int32_t)*MAX_DECODE_SIZE);
104 ci->memset(&decoded1, 0, sizeof(int32_t)*MAX_DECODE_SIZE); 106 ci->memset(&decoded1, 0, sizeof(int32_t)*MAX_DECODE_SIZE);
@@ -118,7 +120,6 @@ seek_start:
118 if (param == 0 && 120 if (param == 0 &&
119 ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) { 121 ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) {
120 sc.bitindex = sc.header_bits - 8*(sc.header_bits/8); 122 sc.bitindex = sc.header_bits - 8*(sc.header_bits/8);
121 ci->set_elapsed(0);
122 ci->seek_complete(); 123 ci->seek_complete();
123 goto seek_start; 124 goto seek_start;
124 } 125 }
diff --git a/apps/codecs/sid.c b/apps/codecs/sid.c
index fed1e8acaa..9b19a20ba7 100644
--- a/apps/codecs/sid.c
+++ b/apps/codecs/sid.c
@@ -1299,8 +1299,8 @@ enum codec_status codec_run(void)
1299 nSamplesToRender = 0; /* Start the rendering from scratch */ 1299 nSamplesToRender = 0; /* Start the rendering from scratch */
1300 1300
1301 /* Set the elapsed time to the current subsong (in seconds) */ 1301 /* Set the elapsed time to the current subsong (in seconds) */
1302 ci->seek_complete();
1303 ci->set_elapsed(subSong*1000); 1302 ci->set_elapsed(subSong*1000);
1303 ci->seek_complete();
1304 } 1304 }
1305 1305
1306 nSamplesRendered = 0; 1306 nSamplesRendered = 0;
diff --git a/apps/codecs/smaf.c b/apps/codecs/smaf.c
index 9211daa9aa..0203d1b4e3 100644
--- a/apps/codecs/smaf.c
+++ b/apps/codecs/smaf.c
@@ -429,6 +429,8 @@ enum codec_status codec_run(void)
429 bytesdone = 0; 429 bytesdone = 0;
430 } 430 }
431 431
432 ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
433
432 /* The main decoder loop */ 434 /* The main decoder loop */
433 endofstream = 0; 435 endofstream = 0;
434 436
diff --git a/apps/codecs/spc.c b/apps/codecs/spc.c
index 6b21f9ad19..3b007471a9 100644
--- a/apps/codecs/spc.c
+++ b/apps/codecs/spc.c
@@ -560,6 +560,8 @@ enum codec_status codec_run(void)
560 return CODEC_ERROR; 560 return CODEC_ERROR;
561 561
562 DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize); 562 DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize);
563 ci->set_elapsed(0);
564
563 do 565 do
564 { 566 {
565 if (load_spc_buffer(buffer, buffersize)) { 567 if (load_spc_buffer(buffer, buffersize)) {
diff --git a/apps/codecs/speex.c b/apps/codecs/speex.c
index c01bcfdf9c..3d82ce7ad0 100644
--- a/apps/codecs/speex.c
+++ b/apps/codecs/speex.c
@@ -417,6 +417,7 @@ enum codec_status codec_run(void)
417 } 417 }
418 418
419 ci->seek_buffer(0); 419 ci->seek_buffer(0);
420 ci->set_elapsed(0);
420 421
421 stereo = speex_stereo_state_init(); 422 stereo = speex_stereo_state_init();
422 spx_ogg_sync_init(&oy); 423 spx_ogg_sync_init(&oy);
diff --git a/apps/codecs/tta.c b/apps/codecs/tta.c
index c75f2b0a57..dda33b3f5e 100644
--- a/apps/codecs/tta.c
+++ b/apps/codecs/tta.c
@@ -90,6 +90,8 @@ enum codec_status codec_run(void)
90 decodedsamples = new_pos; 90 decodedsamples = new_pos;
91 } 91 }
92 92
93 ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH);
94
93 while (!endofstream) 95 while (!endofstream)
94 { 96 {
95 enum codec_command_action action = ci->get_command(&param); 97 enum codec_command_action action = ci->get_command(&param);
diff --git a/apps/codecs/vorbis.c b/apps/codecs/vorbis.c
index e02d459262..fb1c9d7f7c 100644
--- a/apps/codecs/vorbis.c
+++ b/apps/codecs/vorbis.c
@@ -196,6 +196,9 @@ enum codec_status codec_run(void)
196 ci->set_elapsed(ov_time_tell(&vf)); 196 ci->set_elapsed(ov_time_tell(&vf));
197 ci->set_offset(ov_raw_tell(&vf)); 197 ci->set_offset(ov_raw_tell(&vf));
198 } 198 }
199 else {
200 ci->set_elapsed(0);
201 }
199 202
200 previous_section = -1; 203 previous_section = -1;
201 eof = 0; 204 eof = 0;
diff --git a/apps/codecs/vox.c b/apps/codecs/vox.c
index bf274c6917..279d003162 100644
--- a/apps/codecs/vox.c
+++ b/apps/codecs/vox.c
@@ -141,6 +141,8 @@ enum codec_status codec_run(void)
141 bytesdone = 0; 141 bytesdone = 0;
142 } 142 }
143 143
144 ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
145
144 /* The main decoder loop */ 146 /* The main decoder loop */
145 endofstream = 0; 147 endofstream = 0;
146 148
diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c
index f6f83b174d..d20331bc6c 100644
--- a/apps/codecs/wav.c
+++ b/apps/codecs/wav.c
@@ -378,6 +378,8 @@ enum codec_status codec_run(void)
378 bytesdone = 0; 378 bytesdone = 0;
379 } 379 }
380 380
381 ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
382
381 /* The main decoder loop */ 383 /* The main decoder loop */
382 endofstream = 0; 384 endofstream = 0;
383 385
diff --git a/apps/codecs/wav64.c b/apps/codecs/wav64.c
index c763e6f7f0..184f39bf18 100644
--- a/apps/codecs/wav64.c
+++ b/apps/codecs/wav64.c
@@ -381,6 +381,8 @@ enum codec_status codec_run(void)
381 bytesdone = 0; 381 bytesdone = 0;
382 } 382 }
383 383
384 ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
385
384 /* The main decoder loop */ 386 /* The main decoder loop */
385 endofstream = 0; 387 endofstream = 0;
386 388
diff --git a/apps/codecs/wavpack.c b/apps/codecs/wavpack.c
index 32f09d53e4..4d42391fc1 100644
--- a/apps/codecs/wavpack.c
+++ b/apps/codecs/wavpack.c
@@ -75,7 +75,7 @@ enum codec_status codec_run(void)
75 ci->configure(DSP_SET_STEREO_MODE, nchans == 2 ? STEREO_INTERLEAVED : STEREO_MONO); 75 ci->configure(DSP_SET_STEREO_MODE, nchans == 2 ? STEREO_INTERLEAVED : STEREO_MONO);
76 sr_100 = ci->id3->frequency / 100; 76 sr_100 = ci->id3->frequency / 100;
77 77
78 ci->set_elapsed (0); 78 ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
79 79
80 /* The main decoder loop */ 80 /* The main decoder loop */
81 81
diff --git a/apps/codecs/wma.c b/apps/codecs/wma.c
index c327fafb5a..8986531744 100644
--- a/apps/codecs/wma.c
+++ b/apps/codecs/wma.c
@@ -84,7 +84,6 @@ restart_track:
84 % wfx.packet_size; 84 % wfx.packet_size;
85 ci->seek_buffer(resume_offset - packet_offset); 85 ci->seek_buffer(resume_offset - packet_offset);
86 elapsedtime = asf_get_timestamp(&i); 86 elapsedtime = asf_get_timestamp(&i);
87 ci->set_elapsed(elapsedtime);
88 } 87 }
89 else 88 else
90 { 89 {
@@ -93,6 +92,8 @@ restart_track:
93 elapsedtime = 0; 92 elapsedtime = 0;
94 } 93 }
95 94
95 ci->set_elapsed(elapsedtime);
96
96 resume_offset = 0; 97 resume_offset = 0;
97 ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate); 98 ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate);
98 ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ? 99 ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
diff --git a/apps/codecs/wmapro.c b/apps/codecs/wmapro.c
index 17e311c5c0..bab3b5a027 100644
--- a/apps/codecs/wmapro.c
+++ b/apps/codecs/wmapro.c
@@ -79,6 +79,7 @@ restart_track:
79 ci->seek_buffer(ci->id3->first_frame_offset); 79 ci->seek_buffer(ci->id3->first_frame_offset);
80 80
81 elapsedtime = 0; 81 elapsedtime = 0;
82 ci->set_elapsed(0);
82 83
83 /* The main decoding loop */ 84 /* The main decoding loop */
84 85
diff --git a/apps/codecs/wmavoice.c b/apps/codecs/wmavoice.c
index 64c8cd1692..9cf5a49f1a 100644
--- a/apps/codecs/wmavoice.c
+++ b/apps/codecs/wmavoice.c
@@ -109,6 +109,8 @@ restart_track:
109 ci->seek_buffer(ci->id3->first_frame_offset); 109 ci->seek_buffer(ci->id3->first_frame_offset);
110 110
111 elapsedtime = 0; 111 elapsedtime = 0;
112 ci->set_elapsed(0);
113
112 resume_offset = 0; 114 resume_offset = 0;
113 115
114 /* The main decoding loop */ 116 /* The main decoding loop */
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 8736fe2ae2..f57021d237 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2005 by Miika Pekkarinen 10 * Copyright (C) 2005 by Miika Pekkarinen
11 * Copyright (C) 2011 by Michael Sevakis
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -35,59 +36,69 @@
35#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 36#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
36#include "cpu.h" 37#include "cpu.h"
37#endif 38#endif
38#include <string.h>
39#include "settings.h" 39#include "settings.h"
40#include "audio.h" 40#include "audio.h"
41#include "voice_thread.h" 41#include "voice_thread.h"
42#include "dsp.h" 42#include "dsp.h"
43 43
44#define PCMBUF_TARGET_CHUNK 32768 /* This is the target fill size of chunks 44/* This is the target fill size of chunks on the pcm buffer
45 on the pcm buffer */ 45 Can be any number of samples but power of two sizes make for faster and
46#define PCMBUF_MINAVG_CHUNK 24576 /* This is the minimum average size of 46 smaller math - must be < 65536 bytes */
47 chunks on the pcm buffer (or we run out 47#define PCMBUF_CHUNK_SIZE 8192u
48 of buffer descriptors, which is 48#define PCMBUF_GUARD_SIZE 1024u
49 non-fatal) */ 49
50#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than 50/* Mnemonics for common data commit thresholds */
51 this to the DMA */ 51#define COMMIT_CHUNKS PCMBUF_CHUNK_SIZE
52#define CROSSFADE_BUFSIZE 8192 /* Size of the crossfade buffer */ 52#define COMMIT_ALL_DATA 1u
53 53
54/* number of bytes played per second (sample rate * 2 channels * 2 bytes/sample) */ 54 /* Size of the crossfade buffer where codec data is written to be faded
55 on commit */
56#define CROSSFADE_BUFSIZE 8192u
57
58/* Number of bytes played per second:
59 (sample rate * 2 channels * 2 bytes/sample) */
55#define BYTERATE (NATIVE_FREQUENCY * 4) 60#define BYTERATE (NATIVE_FREQUENCY * 4)
56 61
57#if MEMORYSIZE > 2 62#if MEMORYSIZE > 2
58/* Keep watermark high for iPods at least (2s) */ 63/* Keep watermark high for large memory target - at least (2s) */
59#define PCMBUF_WATERMARK (BYTERATE * 2) 64#define PCMBUF_WATERMARK (BYTERATE * 2)
65#define MIN_BUFFER_SIZE (BYTERATE * 3)
60#else 66#else
61#define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */ 67#define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */
68#define MIN_BUFFER_SIZE (BYTERATE * 1)
62#endif 69#endif
63 70
64/* Structure we can use to queue pcm chunks in memory to be played 71/* Describes each audio packet - keep it small since there are many of them */
65 * by the driver code. */
66struct chunkdesc 72struct chunkdesc
67{ 73{
68 unsigned char *addr; 74 uint16_t size; /* Actual size (0 < size <= PCMBUF_CHUNK_SIZE) */
69 size_t size; 75 uint8_t is_end; /* Flag indicating end of track */
70 struct chunkdesc* link; 76 uint8_t pos_key; /* Who put the position info in (0 = undefined) */
71 /* true if last chunk in the track */ 77 unsigned long elapsed; /* Elapsed time to use */
72 bool end_of_track; 78 off_t offset; /* Offset to use */
73}; 79};
74 80
75#define NUM_CHUNK_DESCS(bufsize) \ 81/* General PCM buffer data */
76 ((bufsize) / PCMBUF_MINAVG_CHUNK) 82#define INVALID_BUF_INDEX ((size_t)0 - (size_t)1)
83
84static unsigned char *pcmbuf_buffer;
85static unsigned char *pcmbuf_guardbuf;
86static size_t pcmbuf_size;
87static struct chunkdesc *pcmbuf_descriptors;
88static unsigned int pcmbuf_desc_count;
89static unsigned int position_key = 1;
90
91static size_t chunk_ridx;
92static size_t chunk_widx;
93
94static size_t pcmbuf_bytes_waiting;
77 95
78/* Size of the PCM buffer. */ 96static size_t pcmbuf_watermark;
79static size_t pcmbuf_size = 0; 97static struct chunkdesc *current_desc;
80static char *pcmbuf_bufend;
81static char *pcmbuffer;
82/* Current PCM buffer write index. */
83static size_t pcmbuffer_pos;
84/* Amount pcmbuffer_pos will be increased.*/
85static size_t pcmbuffer_fillpos;
86 98
87static struct chunkdesc *first_desc; 99static bool low_latency_mode = false;
88 100
89/* Gapless playback */ 101static bool pcmbuf_sync_position = false;
90static bool track_transition;
91 102
92/* Fade effect */ 103/* Fade effect */
93static unsigned int fade_vol = MIX_AMP_UNITY; 104static unsigned int fade_vol = MIX_AMP_UNITY;
@@ -104,184 +115,179 @@ static bool soft_mode = false;
104 115
105#ifdef HAVE_CROSSFADE 116#ifdef HAVE_CROSSFADE
106/* Crossfade buffer */ 117/* Crossfade buffer */
107static char *fadebuf; 118static unsigned char *crossfade_buffer;
108 119
109/* Crossfade related state */ 120/* Crossfade related state */
110static bool crossfade_enabled; 121static int crossfade_setting;
111static bool crossfade_enable_request; 122static int crossfade_enable_request;
112static bool crossfade_mixmode; 123static bool crossfade_mixmode;
113static bool crossfade_auto_skip; 124static bool crossfade_auto_skip;
114static bool crossfade_active; 125
115static bool crossfade_track_change_started; 126static enum
127{
128 CROSSFADE_INACTIVE = 0,
129 CROSSFADE_TRACK_CHANGE_STARTED,
130 CROSSFADE_ACTIVE,
131} crossfade_status = CROSSFADE_INACTIVE;
116 132
117/* Track the current location for processing crossfade */ 133/* Track the current location for processing crossfade */
118static struct chunkdesc *crossfade_chunk; 134static size_t crossfade_index;
119static size_t crossfade_sample;
120 135
121/* Counters for fading in new data */ 136/* Counters for fading in new data */
122static size_t crossfade_fade_in_total; 137static size_t crossfade_fade_in_total;
123static size_t crossfade_fade_in_rem; 138static size_t crossfade_fade_in_rem;
124#endif
125 139
126static struct chunkdesc *read_chunk; 140/* Defines for operations on position info when mixing/fading -
127static struct chunkdesc *read_end_chunk; 141 passed in offset parameter */
128static struct chunkdesc *write_chunk; 142enum
129static struct chunkdesc *write_end_chunk; 143{
130static size_t last_chunksize; 144 MIXFADE_KEEP_POS = -1, /* Keep position info in chunk */
131 145 MIXFADE_NULLIFY_POS = -2, /* Ignore position info in chunk */
132static size_t pcmbuf_unplayed_bytes; 146 /* Positive values cause stamping/restamping */
133static size_t pcmbuf_watermark; 147};
134 148
135static bool low_latency_mode = false; 149static void crossfade_start(void);
136static bool flush_pcmbuf = false; 150static void write_to_crossfade(size_t size, unsigned long elapsed,
151 off_t offset);
152static void pcmbuf_finish_crossfade_enable(void);
153#endif /* HAVE_CROSSFADE */
137 154
155/* Thread */
138#ifdef HAVE_PRIORITY_SCHEDULING 156#ifdef HAVE_PRIORITY_SCHEDULING
139static int codec_thread_priority = PRIORITY_PLAYBACK; 157static int codec_thread_priority = PRIORITY_PLAYBACK;
140#endif 158#endif
141 159
142/* Helpful macros for use in conditionals this assumes some of the above 160/* Helpful macros for use in conditionals this assumes some of the above
143 * static variable names */ 161 * static variable names */
144#define COMMIT_IF_NEEDED if(pcmbuffer_fillpos > PCMBUF_TARGET_CHUNK || \ 162#define DATA_LEVEL(quarter_secs) (NATIVE_FREQUENCY * (quarter_secs))
145 (pcmbuffer_pos + pcmbuffer_fillpos) >= pcmbuf_size) commit_chunk(false)
146#define LOW_DATA(quarter_secs) \
147 (pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs)
148
149#ifdef HAVE_CROSSFADE
150static void crossfade_start(void);
151static void write_to_crossfade(size_t length);
152static void pcmbuf_finish_crossfade_enable(void);
153#endif
154 163
155/* Callbacks into playback.c */ 164/* Callbacks into playback.c */
156extern void audio_pcmbuf_position_callback(unsigned int time); 165extern void audio_pcmbuf_position_callback(unsigned long elapsed,
166 off_t offset, unsigned int key);
157extern void audio_pcmbuf_track_change(bool pcmbuf); 167extern void audio_pcmbuf_track_change(bool pcmbuf);
158extern bool audio_pcmbuf_may_play(void); 168extern bool audio_pcmbuf_may_play(void);
169extern void audio_pcmbuf_sync_position(void);
159 170
160 171
161/**************************************/ 172/**************************************/
162 173
163/* define this to show detailed chunkdesc usage information on the sim console */ 174/* Return number of commited bytes in buffer (committed chunks count as
164/*#define DESC_DEBUG*/ 175 a full chunk even if only partially filled) */
176static size_t pcmbuf_unplayed_bytes(void)
177{
178 size_t ridx = chunk_ridx;
179 size_t widx = chunk_widx;
180
181 if (ridx > widx)
182 widx += pcmbuf_size;
165 183
166#ifndef SIMULATOR 184 return widx - ridx;
167#undef DESC_DEBUG 185}
168#endif 186
187/* Return the next PCM chunk in the PCM buffer given a byte index into it */
188static size_t index_next(size_t index)
189{
190 index = ALIGN_DOWN(index + PCMBUF_CHUNK_SIZE, PCMBUF_CHUNK_SIZE);
191
192 if (index >= pcmbuf_size)
193 index -= pcmbuf_size;
194
195 return index;
196}
169 197
170#ifdef DESC_DEBUG 198/* Convert a byte offset in the PCM buffer into a pointer in the buffer */
171#define DISPLAY_DESC(caller) while(!show_desc(caller)) 199static FORCE_INLINE void * index_buffer(size_t index)
172#define DESC_IDX(desc) (desc ? desc - first_desc : -1)
173#define SHOW_DESC(desc) if(DESC_IDX(desc)==-1) DEBUGF("--"); \
174 else DEBUGF("%02d", DESC_IDX(desc))
175#define SHOW_DESC_LINK(desc) if(desc){SHOW_DESC(desc->link);DEBUGF(" ");} \
176 else DEBUGF("-- ")
177#define SHOW_DETAIL(desc) DEBUGF(":");SHOW_DESC(desc); DEBUGF(">"); \
178 SHOW_DESC_LINK(desc)
179#define SHOW_POINT(tag,desc) DEBUGF("%s",tag);SHOW_DETAIL(desc)
180#define SHOW_NUM(num,desc) DEBUGF("%02d>",num);SHOW_DESC_LINK(desc)
181
182static bool show_desc(char *caller)
183{ 200{
184 if (show_desc_in_use) return false; 201 return pcmbuf_buffer + index;
185 show_desc_in_use = true; 202}
186 DEBUGF("%-14s\t", caller); 203
187 SHOW_POINT("r", read_chunk); 204/* Convert a pointer in the buffer into an index offset */
188 SHOW_POINT("re", read_end_chunk); 205static FORCE_INLINE size_t buffer_index(void *p)
189 DEBUGF(" "); 206{
190 SHOW_POINT("w", write_chunk); 207 return (uintptr_t)p - (uintptr_t)pcmbuf_buffer;
191 SHOW_POINT("we", write_end_chunk); 208}
192 DEBUGF("\n"); 209
193 int i; 210/* Return a chunk descriptor for a byte index in the buffer */
194 for (i = 0; i < pcmbuf_descs(); i++) 211static struct chunkdesc * index_chunkdesc(size_t index)
212{
213 return &pcmbuf_descriptors[index / PCMBUF_CHUNK_SIZE];
214}
215
216/* Return a chunk descriptor for a byte index in the buffer, offset by 'offset'
217 chunks */
218static struct chunkdesc * index_chunkdesc_offs(size_t index, int offset)
219{
220 int i = index / PCMBUF_CHUNK_SIZE;
221
222 if (offset != 0)
195 { 223 {
196 SHOW_NUM(i, (first_desc + i)); 224 i = (i + offset) % pcmbuf_desc_count;
197 if (i%10 == 9) DEBUGF("\n"); 225
226 /* remainder => modulus */
227 if (i < 0)
228 i += pcmbuf_desc_count;
198 } 229 }
199 DEBUGF("\n\n"); 230
200 show_desc_in_use = false; 231 return &pcmbuf_descriptors[i];
201 return true;
202} 232}
203#else
204#define DISPLAY_DESC(caller) do{}while(0)
205#endif
206 233
207 234
208/** Accept new PCM data */ 235/** Accept new PCM data */
209 236
210/* Commit PCM buffer samples as a new chunk for playback */ 237/* Split the uncommitted data as needed into chunks, stopping when uncommitted
211static void commit_chunk(bool flush_next_time) 238 data is below the threshold */
239static void commit_chunks(size_t threshold)
212{ 240{
213 if (!pcmbuffer_fillpos) 241 size_t index = chunk_widx;
214 return; 242 size_t end_index = index + pcmbuf_bytes_waiting;
215
216 /* Never use the last buffer descriptor */
217 while (write_chunk == write_end_chunk) {
218 /* If this happens, something is being stupid */
219 if (!pcm_is_playing()) {
220 logf("commit_chunk error");
221 pcmbuf_play_start();
222 }
223 /* Let approximately one chunk of data playback */
224 sleep(HZ * PCMBUF_TARGET_CHUNK / BYTERATE);
225 }
226 243
227 /* commit the chunk */ 244 /* Copy to the beginning of the buffer all data that must wrap */
245 if (end_index > pcmbuf_size)
246 memcpy(pcmbuf_buffer, pcmbuf_guardbuf, end_index - pcmbuf_size);
228 247
229 register size_t size = pcmbuffer_fillpos; 248 struct chunkdesc *desc = index_chunkdesc(index);
230 /* Grab the next description to write, and change the write pointer */
231 register struct chunkdesc *pcmbuf_current = write_chunk;
232 write_chunk = pcmbuf_current->link;
233 /* Fill in the values in the new buffer chunk */
234 pcmbuf_current->addr = &pcmbuffer[pcmbuffer_pos];
235 pcmbuf_current->size = size;
236 pcmbuf_current->end_of_track = false;
237 pcmbuf_current->link = NULL;
238 249
239 if (read_chunk != NULL) 250 do
240 { 251 {
241 if (flush_pcmbuf) 252 size_t size = MIN(pcmbuf_bytes_waiting, PCMBUF_CHUNK_SIZE);
242 { 253 pcmbuf_bytes_waiting -= size;
243 /* Flush! Discard all data after the currently playing chunk,
244 and make the current chunk play next */
245 logf("commit_chunk: flush");
246 pcm_play_lock();
247 write_end_chunk->link = read_chunk->link;
248 read_chunk->link = pcmbuf_current;
249 while (write_end_chunk->link)
250 {
251 write_end_chunk = write_end_chunk->link;
252 pcmbuf_unplayed_bytes -= write_end_chunk->size;
253 }
254 254
255 read_chunk->end_of_track = track_transition; 255 /* Fill in the values in the new buffer chunk */
256 pcm_play_unlock(); 256 desc->size = (uint16_t)size;
257 }
258 /* If there is already a read buffer setup, add to it */
259 else
260 read_end_chunk->link = pcmbuf_current;
261 }
262 else
263 {
264 /* Otherwise create the buffer */
265 read_chunk = pcmbuf_current;
266 }
267 257
268 /* If flush_next_time is true, then the current chunk will be thrown out 258 /* Advance the current write chunk and make it available to the
269 * and the next chunk to be committed will be the next to be played. 259 PCM callback */
270 * This is used to empty the PCM buffer for a track change. */ 260 chunk_widx = index = index_next(index);
271 flush_pcmbuf = flush_next_time; 261 desc = index_chunkdesc(index);
272 262
273 /* This is now the last buffer to read */ 263 /* Reset it before using it */
274 read_end_chunk = pcmbuf_current; 264 desc->is_end = 0;
265 desc->pos_key = 0;
266 }
267 while (pcmbuf_bytes_waiting >= threshold);
268}
275 269
276 /* Update bytes counters */ 270/* If uncommitted data count is above or equal to the threshold, commit it */
277 pcmbuf_unplayed_bytes += size; 271static FORCE_INLINE void commit_if_needed(size_t threshold)
272{
273 if (pcmbuf_bytes_waiting >= threshold)
274 commit_chunks(threshold);
275}
278 276
279 pcmbuffer_pos += size; 277/* Place positioning information in the chunk */
280 if (pcmbuffer_pos >= pcmbuf_size) 278static void stamp_chunk(struct chunkdesc *desc, unsigned long elapsed,
281 pcmbuffer_pos -= pcmbuf_size; 279 off_t offset)
280{
281 /* One-time stamping of a given chunk by the same track - new track may
282 overwrite */
283 unsigned int key = position_key;
282 284
283 pcmbuffer_fillpos = 0; 285 if (desc->pos_key != key)
284 DISPLAY_DESC("commit_chunk"); 286 {
287 desc->pos_key = key;
288 desc->elapsed = elapsed;
289 desc->offset = offset;
290 }
285} 291}
286 292
287/* Set priority of the codec thread */ 293/* Set priority of the codec thread */
@@ -290,7 +296,8 @@ static void commit_chunk(bool flush_next_time)
290 * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */ 296 * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */
291static void boost_codec_thread(int pcm_fill_state) 297static void boost_codec_thread(int pcm_fill_state)
292{ 298{
293 static const int prios[11] = { 299 static const int8_t prios[11] =
300 {
294 PRIORITY_PLAYBACK_MAX, /* 0 - 10% */ 301 PRIORITY_PLAYBACK_MAX, /* 0 - 10% */
295 PRIORITY_PLAYBACK_MAX+1, /* 10 - 20% */ 302 PRIORITY_PLAYBACK_MAX+1, /* 10 - 20% */
296 PRIORITY_PLAYBACK_MAX+3, /* 20 - 30% */ 303 PRIORITY_PLAYBACK_MAX+3, /* 20 - 30% */
@@ -298,12 +305,13 @@ static void boost_codec_thread(int pcm_fill_state)
298 PRIORITY_PLAYBACK_MAX+7, /* 40 - 50% */ 305 PRIORITY_PLAYBACK_MAX+7, /* 40 - 50% */
299 PRIORITY_PLAYBACK_MAX+8, /* 50 - 60% */ 306 PRIORITY_PLAYBACK_MAX+8, /* 50 - 60% */
300 PRIORITY_PLAYBACK_MAX+9, /* 60 - 70% */ 307 PRIORITY_PLAYBACK_MAX+9, /* 60 - 70% */
301 /* raiseing priority above 70% shouldn't be needed */ 308 /* raising priority above 70% shouldn't be needed */
302 PRIORITY_PLAYBACK, /* 70 - 80% */ 309 PRIORITY_PLAYBACK, /* 70 - 80% */
303 PRIORITY_PLAYBACK, /* 80 - 90% */ 310 PRIORITY_PLAYBACK, /* 80 - 90% */
304 PRIORITY_PLAYBACK, /* 90 -100% */ 311 PRIORITY_PLAYBACK, /* 90 -100% */
305 PRIORITY_PLAYBACK, /* 100% */ 312 PRIORITY_PLAYBACK, /* 100% */
306 }; 313 };
314
307 int new_prio = prios[pcm_fill_state]; 315 int new_prio = prios[pcm_fill_state];
308 316
309 /* Keep voice and codec threads at the same priority or else voice 317 /* Keep voice and codec threads at the same priority or else voice
@@ -319,37 +327,86 @@ static void boost_codec_thread(int pcm_fill_state)
319#define boost_codec_thread(pcm_fill_state) do{}while(0) 327#define boost_codec_thread(pcm_fill_state) do{}while(0)
320#endif /* HAVE_PRIORITY_SCHEDULING */ 328#endif /* HAVE_PRIORITY_SCHEDULING */
321 329
322/* Return true if the PCM buffer is able to receive new data. 330/* Get the next available buffer and size - assumes adequate space exists */
323 * Also maintain buffer level above the watermark. */ 331static void * get_write_buffer(size_t *size)
324static bool prepare_insert(size_t length)
325{ 332{
326 bool playing = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED; 333 /* Obtain current chunk fill address */
334 size_t index = chunk_widx + pcmbuf_bytes_waiting;
335 size_t index_end = pcmbuf_size + PCMBUF_GUARD_SIZE;
327 336
328 if (low_latency_mode) 337 /* Get count to the end of the buffer where a wrap will happen +
329 { 338 the guard */
330 /* 1/4s latency. */ 339 size_t endsize = index_end - index;
331 if (!LOW_DATA(1) && playing)
332 return false;
333 }
334 340
335 /* Need to save PCMBUF_MIN_CHUNK to prevent wrapping overwriting */ 341 /* Return available unwrapped space */
336 if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK) 342 *size = MIN(*size, endsize);
337 return false; 343
344 return index_buffer(index);
345}
346
347/* Commit outstanding data leaving less than a chunk size remaining and
348 write position info to the first chunk */
349static void commit_write_buffer(size_t size, unsigned long elapsed, off_t offset)
350{
351 struct chunkdesc *desc = index_chunkdesc(chunk_widx);
352 stamp_chunk(desc, elapsed, offset);
353
354 /* Add this data and commit if one or more chunks are ready */
355 pcmbuf_bytes_waiting += size;
356
357 commit_if_needed(COMMIT_CHUNKS);
358}
359
360/* Request space in the buffer for writing output samples */
361void * pcmbuf_request_buffer(int *count)
362{
363 size_t size = *count * 4;
364
365#ifdef HAVE_CROSSFADE
366 /* We're going to crossfade to a new track, which is now on its way */
367 if (crossfade_status == CROSSFADE_TRACK_CHANGE_STARTED)
368 crossfade_start();
369
370 /* If crossfade has begun, put the new track samples in crossfade_buffer */
371 if (crossfade_status != CROSSFADE_INACTIVE && size > CROSSFADE_BUFSIZE)
372 size = CROSSFADE_BUFSIZE;
373#endif
374
375 enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK);
376 size_t remaining = pcmbuf_unplayed_bytes();
377
378 /* Need to have length bytes to prevent wrapping overwriting - leave one
379 descriptor free to guard so that 0 != full in ring buffer */
380 size_t freespace = pcmbuf_free();
381
382 if (pcmbuf_sync_position)
383 audio_pcmbuf_sync_position();
384
385 if (freespace < size + PCMBUF_CHUNK_SIZE)
386 return NULL;
338 387
339 /* Maintain the buffer level above the watermark */ 388 /* Maintain the buffer level above the watermark */
340 if (playing) 389 if (status != CHANNEL_STOPPED)
341 { 390 {
342 /* boost cpu if necessary */ 391 if (low_latency_mode)
343 if (pcmbuf_unplayed_bytes < pcmbuf_watermark) 392 {
393 /* 1/4s latency. */
394 if (remaining > DATA_LEVEL(1))
395 return NULL;
396 }
397
398 /* Boost CPU if necessary */
399 size_t realrem = pcmbuf_size - freespace;
400
401 if (realrem < pcmbuf_watermark)
344 trigger_cpu_boost(); 402 trigger_cpu_boost();
345 boost_codec_thread(pcmbuf_unplayed_bytes*10/pcmbuf_size); 403
404 boost_codec_thread(realrem*10 / pcmbuf_size);
346 405
347#ifdef HAVE_CROSSFADE 406#ifdef HAVE_CROSSFADE
348 /* Disable crossfade if < .5s of audio */ 407 /* Disable crossfade if < .5s of audio */
349 if (LOW_DATA(2)) 408 if (remaining < DATA_LEVEL(2))
350 { 409 crossfade_status = CROSSFADE_INACTIVE;
351 crossfade_active = false;
352 }
353#endif 410#endif
354 } 411 }
355 else /* !playing */ 412 else /* !playing */
@@ -359,378 +416,344 @@ static bool prepare_insert(size_t length)
359 416
360 /* If pre-buffered to the watermark, start playback */ 417 /* If pre-buffered to the watermark, start playback */
361#if MEMORYSIZE > 2 418#if MEMORYSIZE > 2
362 if (!LOW_DATA(4)) 419 if (remaining > DATA_LEVEL(4))
363#else 420#else
364 if (pcmbuf_unplayed_bytes > pcmbuf_watermark) 421 if (remaining > pcmbuf_watermark)
365#endif 422#endif
366 { 423 {
367 logf("pcm starting");
368 if (audio_pcmbuf_may_play()) 424 if (audio_pcmbuf_may_play())
369 pcmbuf_play_start(); 425 pcmbuf_play_start();
370 } 426 }
371 } 427 }
372 428
373 return true; 429 void *buf =
374}
375
376/* Request space in the buffer for writing output samples */
377void *pcmbuf_request_buffer(int *count)
378{
379#ifdef HAVE_CROSSFADE 430#ifdef HAVE_CROSSFADE
380 /* we're going to crossfade to a new track, which is now on its way */ 431 crossfade_status != CROSSFADE_INACTIVE ? crossfade_buffer :
381 if (crossfade_track_change_started)
382 crossfade_start();
383
384 /* crossfade has begun, put the new track samples in fadebuf */
385 if (crossfade_active)
386 {
387 int cnt = MIN(*count, CROSSFADE_BUFSIZE/4);
388 if (prepare_insert(cnt << 2))
389 {
390 *count = cnt;
391 return fadebuf;
392 }
393 }
394 else
395#endif 432#endif
396 /* if possible, reserve room in the PCM buffer for new samples */ 433 get_write_buffer(&size);
397 { 434
398 if(prepare_insert(*count << 2)) 435 *count = size / 4;
399 { 436 return buf;
400 size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos;
401 if (pcmbuf_size - pcmbuffer_index >= PCMBUF_MIN_CHUNK)
402 {
403 /* Usual case, there's space here */
404 return &pcmbuffer[pcmbuffer_index];
405 }
406 else
407 {
408 /* Wrap the buffer, the new samples go at the beginning */
409 commit_chunk(false);
410 pcmbuffer_pos = 0;
411 return &pcmbuffer[0];
412 }
413 }
414 }
415 /* PCM buffer not ready to receive new data yet */
416 return NULL;
417} 437}
418 438
419/* Handle new samples to the buffer */ 439/* Handle new samples to the buffer */
420void pcmbuf_write_complete(int count) 440void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset)
421{ 441{
422 size_t length = (size_t)(unsigned int)count << 2; 442 size_t size = count * 4;
443
423#ifdef HAVE_CROSSFADE 444#ifdef HAVE_CROSSFADE
424 if (crossfade_active) 445 if (crossfade_status != CROSSFADE_INACTIVE)
425 write_to_crossfade(length); 446 {
447 write_to_crossfade(size, elapsed, offset);
448 }
426 else 449 else
427#endif 450#endif
428 { 451 {
429 pcmbuffer_fillpos += length; 452 commit_write_buffer(size, elapsed, offset);
430 COMMIT_IF_NEEDED;
431 } 453 }
454
455 /* Revert to position updates by PCM */
456 pcmbuf_sync_position = false;
432} 457}
433 458
434 459
435/** Init */ 460/** Init */
436 461static unsigned int get_next_required_pcmbuf_chunks(void)
437static inline void init_pcmbuffers(void)
438{
439 first_desc = write_chunk;
440 struct chunkdesc *next = write_chunk;
441 next++;
442 write_end_chunk = write_chunk;
443 while ((void *)next < (void *)pcmbuf_bufend) {
444 write_end_chunk->link=next;
445 write_end_chunk=next;
446 next++;
447 }
448 DISPLAY_DESC("init");
449}
450
451static size_t get_next_required_pcmbuf_size(void)
452{ 462{
453 size_t seconds = 1; 463 size_t size = MIN_BUFFER_SIZE;
454 464
455#ifdef HAVE_CROSSFADE 465#ifdef HAVE_CROSSFADE
456 if (crossfade_enable_request) 466 if (crossfade_enable_request != CROSSFADE_ENABLE_OFF)
457 seconds += global_settings.crossfade_fade_out_delay + 467 {
458 global_settings.crossfade_fade_out_duration; 468 size_t seconds = global_settings.crossfade_fade_out_delay +
469 global_settings.crossfade_fade_out_duration;
470 size += seconds * BYTERATE;
471 }
459#endif 472#endif
460 473
461#if MEMORYSIZE > 2 474 logf("pcmbuf len: %lu", (unsigned long)(size / BYTERATE));
462 /* Buffer has to be at least 2s long. */ 475 return size / PCMBUF_CHUNK_SIZE;
463 seconds += 2; 476}
464#endif 477
465 logf("pcmbuf len: %ld", (long)seconds); 478/* Initialize the ringbuffer state */
466 return seconds * BYTERATE; 479static void init_buffer_state(void)
480{
481 /* Reset counters */
482 chunk_ridx = chunk_widx = 0;
483 pcmbuf_bytes_waiting = 0;
484
485 /* Reset first descriptor */
486 struct chunkdesc *desc = pcmbuf_descriptors;
487 desc->is_end = 0;
488 desc->pos_key = 0;
467} 489}
468 490
469/* Initialize the pcmbuffer the structure looks like this: 491/* Initialize the PCM buffer. The structure looks like this:
470 * ...|---------PCMBUF---------[|FADEBUF]|DESCS|... */ 492 * ...[|FADEBUF]|---------PCMBUF---------|GUARDBUF|DESCS| */
471size_t pcmbuf_init(unsigned char *bufend) 493size_t pcmbuf_init(unsigned char *bufend)
472{ 494{
473 pcmbuf_bufend = bufend; 495 unsigned char *bufstart;
474 pcmbuf_size = get_next_required_pcmbuf_size();
475 write_chunk = (struct chunkdesc *)pcmbuf_bufend -
476 NUM_CHUNK_DESCS(pcmbuf_size);
477 496
478#ifdef HAVE_CROSSFADE 497 /* Set up the buffers */
479 fadebuf = (unsigned char *)write_chunk - 498 pcmbuf_desc_count = get_next_required_pcmbuf_chunks();
480 (crossfade_enable_request ? CROSSFADE_BUFSIZE : 0); 499 pcmbuf_size = pcmbuf_desc_count * PCMBUF_CHUNK_SIZE;
481 pcmbuffer = fadebuf - pcmbuf_size; 500 pcmbuf_descriptors = (struct chunkdesc *)bufend - pcmbuf_desc_count;
482#else 501
483 pcmbuffer = (unsigned char *)write_chunk - pcmbuf_size; 502 pcmbuf_buffer = (void *)pcmbuf_descriptors -
484#endif 503 pcmbuf_size - PCMBUF_GUARD_SIZE;
485 504
486 init_pcmbuffers(); 505 /* Mem-align buffer chunks for more efficient handling in lower layers */
506 pcmbuf_buffer = ALIGN_DOWN(pcmbuf_buffer, (uintptr_t)MEM_ALIGN_SIZE);
507
508 pcmbuf_guardbuf = pcmbuf_buffer + pcmbuf_size;
509 bufstart = pcmbuf_buffer;
487 510
488#ifdef HAVE_CROSSFADE 511#ifdef HAVE_CROSSFADE
512 /* Allocate FADEBUF if it will be needed */
513 if (crossfade_enable_request != CROSSFADE_ENABLE_OFF)
514 {
515 bufstart -= CROSSFADE_BUFSIZE;
516 crossfade_buffer = bufstart;
517 }
518
489 pcmbuf_finish_crossfade_enable(); 519 pcmbuf_finish_crossfade_enable();
490#else 520#else /* !HAVE_CROSSFADE */
491 pcmbuf_watermark = PCMBUF_WATERMARK; 521 pcmbuf_watermark = PCMBUF_WATERMARK;
492#endif 522#endif /* HAVE_CROSSFADE */
493 523
494 pcmbuf_play_stop(); 524 init_buffer_state();
495 525
496 pcmbuf_soft_mode(false); 526 pcmbuf_soft_mode(false);
497 527
498 return pcmbuf_bufend - pcmbuffer; 528 return bufend - bufstart;
499} 529}
500 530
501 531
502/** Track change */ 532/** Track change */
503void pcmbuf_monitor_track_change(bool monitor)
504{
505 pcm_play_lock();
506 533
507 if (last_chunksize != 0) 534/* Place a track change notification in a specific descriptor or post it
535 immediately if the buffer is empty or the index is invalid */
536static void pcmbuf_monitor_track_change_ex(size_t index, int offset)
537{
538 if (chunk_ridx != chunk_widx && index != INVALID_BUF_INDEX)
508 { 539 {
509 /* If monitoring, wait until this track runs out. Place in 540 /* If monitoring, set flag in specified chunk */
510 currently playing chunk. If not, cancel notification. */ 541 index_chunkdesc_offs(index, offset)->is_end = 1;
511 track_transition = monitor;
512 read_end_chunk->end_of_track = monitor;
513 if (!monitor)
514 {
515 /* Clear all notifications */
516 struct chunkdesc *desc = first_desc;
517 struct chunkdesc *end = desc + pcmbuf_descs();
518 while (desc < end)
519 desc++->end_of_track = false;
520 }
521 } 542 }
522 else 543 else
523 { 544 {
524 /* Post now if PCM stopped and last buffer was sent. */ 545 /* Post now if no outstanding buffers exist */
525 track_transition = false; 546 audio_pcmbuf_track_change(false);
526 if (monitor)
527 audio_pcmbuf_track_change(false);
528 } 547 }
548}
549
550/* Clear end of track and optionally the positioning info for all data */
551static void pcmbuf_cancel_track_change(bool position)
552{
553 size_t index = chunk_ridx;
554
555 while (1)
556 {
557 struct chunkdesc *desc = index_chunkdesc(index);
558
559 desc->is_end = 0;
560
561 if (position)
562 desc->pos_key = 0;
563
564 if (index == chunk_widx)
565 break;
566
567 index = index_next(index);
568 }
569}
570
571/* Place a track change notification at the end of the buffer or post it
572 immediately if the buffer is empty */
573void pcmbuf_monitor_track_change(bool monitor)
574{
575 pcm_play_lock();
576
577 if (monitor)
578 pcmbuf_monitor_track_change_ex(chunk_widx, -1);
579 else
580 pcmbuf_cancel_track_change(false);
529 581
530 pcm_play_unlock(); 582 pcm_play_unlock();
531} 583}
532 584
533bool pcmbuf_start_track_change(bool auto_skip) 585void pcmbuf_start_track_change(enum pcm_track_change_type type)
534{ 586{
535 bool crossfade = false;
536#ifdef HAVE_CROSSFADE 587#ifdef HAVE_CROSSFADE
537 /* Determine whether this track change needs to crossfade */ 588 bool crossfade = false;
538 if(crossfade_enabled && !pcmbuf_is_crossfade_active())
539 {
540 switch(global_settings.crossfade)
541 {
542 case CROSSFADE_ENABLE_AUTOSKIP:
543 crossfade = auto_skip;
544 break;
545 case CROSSFADE_ENABLE_MANSKIP:
546 crossfade = !auto_skip;
547 break;
548 case CROSSFADE_ENABLE_SHUFFLE:
549 crossfade = global_settings.playlist_shuffle;
550 break;
551 case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP:
552 crossfade = global_settings.playlist_shuffle || !auto_skip;
553 break;
554 case CROSSFADE_ENABLE_ALWAYS:
555 crossfade = true;
556 break;
557 }
558 }
559#endif 589#endif
590 bool auto_skip = type != TRACK_CHANGE_MANUAL;
560 591
561 if (!auto_skip || crossfade) 592 /* Commit all outstanding data before starting next track - tracks don't
562 /* manual skip or crossfade */ 593 comingle inside a single buffer chunk */
563 { 594 commit_if_needed(COMMIT_ALL_DATA);
564 if (crossfade)
565 { logf(" crossfade track change"); }
566 else
567 { logf(" manual track change"); }
568 595
569 pcm_play_lock(); 596 /* Update position key so that:
597 1) Positions are keyed to the track to which they belong for sync
598 purposes
570 599
571 /* Cancel any pending automatic gapless transition */ 600 2) Buffers stamped with the outgoing track's positions are restamped
572 pcmbuf_monitor_track_change(false); 601 to the incoming track's positions when crossfading
602 */
603 if (++position_key > UINT8_MAX)
604 position_key = 1;
573 605
574 /* Can't do two crossfades at once and, no fade if pcm is off now */ 606 if (type == TRACK_CHANGE_END_OF_DATA)
575 if ( 607 {
608 /* If end of all data, force playback */
609 if (audio_pcmbuf_may_play())
610 pcmbuf_play_start();
611 }
576#ifdef HAVE_CROSSFADE 612#ifdef HAVE_CROSSFADE
577 pcmbuf_is_crossfade_active() || 613 /* Determine whether this track change needs to crossfaded and how */
578#endif 614 else if (crossfade_setting != CROSSFADE_ENABLE_OFF &&
579 mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED) 615 !pcmbuf_is_crossfade_active() &&
616 pcmbuf_unplayed_bytes() >= DATA_LEVEL(2) &&
617 !low_latency_mode)
618 {
619 switch (crossfade_setting)
580 { 620 {
581 pcmbuf_play_stop(); 621 case CROSSFADE_ENABLE_AUTOSKIP:
582 pcm_play_unlock(); 622 crossfade = auto_skip;
583 /* Notify playback that the track change starts now */ 623 break;
584 return true; 624 case CROSSFADE_ENABLE_MANSKIP:
625 crossfade = !auto_skip;
626 break;
627 case CROSSFADE_ENABLE_SHUFFLE:
628 crossfade = global_settings.playlist_shuffle;
629 break;
630 case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP:
631 crossfade = global_settings.playlist_shuffle || !auto_skip;
632 break;
633 case CROSSFADE_ENABLE_ALWAYS:
634 crossfade = true;
635 break;
585 } 636 }
637 }
638 /* else crossfade is off, crossfade is already active, not enough data,
639 * pcm is off now (implying low data), not crossfading or low latency mode
640 */
586 641
587 /* Not enough data, or not crossfading, flush the old data instead */ 642 if (crossfade)
588 if (LOW_DATA(2) || !crossfade || low_latency_mode) 643 {
589 { 644 logf("crossfade track change");
590 commit_chunk(true);
591 }
592#ifdef HAVE_CROSSFADE
593 else
594 {
595 /* Don't enable mix mode when skipping tracks manually. */
596 crossfade_mixmode = auto_skip &&
597 global_settings.crossfade_fade_out_mixmode;
598 645
599 crossfade_auto_skip = auto_skip; 646 /* Don't enable mix mode when skipping tracks manually */
647 crossfade_mixmode = auto_skip &&
648 global_settings.crossfade_fade_out_mixmode;
600 649
601 crossfade_track_change_started = crossfade; 650 crossfade_auto_skip = auto_skip;
602 } 651
603#endif 652 crossfade_status = CROSSFADE_TRACK_CHANGE_STARTED;
604 pcm_play_unlock();
605 653
606 /* Keep trigger outside the play lock or HW FIFO underruns can happen
607 since frequency scaling is *not* always fast */
608 trigger_cpu_boost(); 654 trigger_cpu_boost();
609 655
610 /* Notify playback that the track change starts now */ 656 /* Cancel any pending automatic gapless transition and if a manual
611 return true; 657 skip, stop position updates */
658 pcm_play_lock();
659 pcmbuf_cancel_track_change(!auto_skip);
660 pcm_play_unlock();
612 } 661 }
613 else /* automatic and not crossfading, so do gapless track change */ 662 else
663#endif /* HAVE_CROSSFADE */
664 if (auto_skip)
614 { 665 {
615 /* The codec is moving on to the next track, but the current track will 666 /* The codec is moving on to the next track, but the current track will
616 * continue to play. Set a flag to make sure the elapsed time of the 667 * continue to play, so mark the last write chunk as the last one in
617 * current track will be updated properly, and mark the current chunk 668 * the track */
618 * as the last one in the track. */ 669 logf("gapless track change");
619 logf(" gapless track change"); 670#ifdef HAVE_CROSSFADE
671 if (crossfade_status != CROSSFADE_INACTIVE)
672 {
673 /* Crossfade is still active but crossfade is not happening - for
674 * now, chicken-out and clear out the buffer (just like before) to
675 * avoid fade pile-up on short tracks fading-in over long ones */
676 pcmbuf_play_stop();
677 }
678#endif
620 pcmbuf_monitor_track_change(true); 679 pcmbuf_monitor_track_change(true);
621 return false; 680 }
681 else
682 {
683 /* Discard old data; caller needs no transition notification */
684 logf("manual track change");
685 pcmbuf_play_stop();
622 } 686 }
623} 687}
624 688
625 689
626/** Playback */ 690/** Playback */
627 691
628/* PCM driver callback 692/* PCM driver callback */
629 * This function has 3 major logical parts (separated by brackets both for 693static void pcmbuf_pcm_callback(unsigned char **start, size_t *size)
630 * readability and variable scoping). The first part performs the
631 * operations related to finishing off the last chunk we fed to the DMA.
632 * The second part detects the end of playlist condition when the PCM
633 * buffer is empty except for uncommitted samples. Then they are committed
634 * and sent to the PCM driver for playback. The third part performs the
635 * operations involved in sending a new chunk to the DMA. */
636static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
637{ 694{
638 struct chunkdesc *pcmbuf_current = read_chunk; 695 /*- Process the chunk that just finished -*/
696 size_t index = chunk_ridx;
697 struct chunkdesc *desc = current_desc;
639 698
699 if (desc)
640 { 700 {
641 /* Take the finished chunk out of circulation */ 701 /* If last chunk in the track, notify of track change */
642 read_chunk = pcmbuf_current->link; 702 if (desc->is_end != 0)
643
644 /* if during a track transition, update the elapsed time in ms */
645 if (track_transition)
646 audio_pcmbuf_position_callback(last_chunksize * 1000 / BYTERATE);
647
648 /* if last chunk in the track, stop updates and notify audio thread */
649 if (pcmbuf_current->end_of_track)
650 {
651 track_transition = false;
652 audio_pcmbuf_track_change(true); 703 audio_pcmbuf_track_change(true);
653 }
654
655 /* Put the finished chunk back into circulation */
656 write_end_chunk->link = pcmbuf_current;
657 write_end_chunk = pcmbuf_current;
658 704
659#ifdef HAVE_CROSSFADE 705 /* Free it for reuse */
660 /* If we've read over the crossfade chunk while it's still fading */ 706 chunk_ridx = index = index_next(index);
661 if (pcmbuf_current == crossfade_chunk)
662 crossfade_chunk = read_chunk;
663#endif
664 } 707 }
665 708
709 /*- Process the new one -*/
710 if (index != chunk_widx && !fade_out_complete)
666 { 711 {
667 /* Commit last samples at end of playlist */ 712 current_desc = desc = index_chunkdesc(index);
668 if (pcmbuffer_fillpos && !pcmbuf_current)
669 {
670 logf("pcmbuf_pcm_callback: commit last samples");
671 commit_chunk(false);
672 }
673 }
674 713
675 /* Stop at this frame */ 714 *start = index_buffer(index);
676 pcmbuf_current = fade_out_complete ? NULL : read_chunk; 715 *size = desc->size;
677 716
678 { 717 if (desc->pos_key != 0)
679 /* Send the new chunk to the DMA */
680 if(pcmbuf_current)
681 {
682 last_chunksize = pcmbuf_current->size;
683 pcmbuf_unplayed_bytes -= last_chunksize;
684 *size = last_chunksize;
685 *start = pcmbuf_current->addr;
686 }
687 else
688 { 718 {
689 /* No more chunks or pause indicated */ 719 /* Positioning chunk - notify playback */
690 logf("pcmbuf_pcm_callback: no more chunks"); 720 audio_pcmbuf_position_callback(desc->elapsed, desc->offset,
691 last_chunksize = 0; 721 desc->pos_key);
692 *size = 0;
693 *start = NULL;
694 } 722 }
695 } 723 }
696 DISPLAY_DESC("callback");
697} 724}
698 725
699/* Force playback */ 726/* Force playback */
700void pcmbuf_play_start(void) 727void pcmbuf_play_start(void)
701{ 728{
729 logf("pcmbuf_play_start");
730
702 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED && 731 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED &&
703 pcmbuf_unplayed_bytes && read_chunk != NULL) 732 chunk_widx != chunk_ridx)
704 { 733 {
705 logf("pcmbuf_play_start"); 734 current_desc = NULL;
706 last_chunksize = read_chunk->size;
707 pcmbuf_unplayed_bytes -= last_chunksize;
708 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback, 735 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback,
709 read_chunk->addr, last_chunksize); 736 NULL, 0);
710 } 737 }
711} 738}
712 739
740/* Stop channel, empty and reset buffer */
713void pcmbuf_play_stop(void) 741void pcmbuf_play_stop(void)
714{ 742{
715 logf("pcmbuf_play_stop"); 743 logf("pcmbuf_play_stop");
744
745 /* Reset channel */
716 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); 746 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
717 747
718 pcmbuf_unplayed_bytes = 0; 748 /* Reset buffer */
719 if (read_chunk) { 749 init_buffer_state();
720 write_end_chunk->link = read_chunk; 750
721 write_end_chunk = read_end_chunk; 751 /* Revert to position updates by PCM */
722 read_chunk = read_end_chunk = NULL; 752 pcmbuf_sync_position = false;
723 } 753
724 last_chunksize = 0;
725 pcmbuffer_pos = 0;
726 pcmbuffer_fillpos = 0;
727#ifdef HAVE_CROSSFADE 754#ifdef HAVE_CROSSFADE
728 crossfade_track_change_started = false; 755 crossfade_status = CROSSFADE_INACTIVE;
729 crossfade_active = false;
730#endif 756#endif
731 track_transition = false;
732 flush_pcmbuf = false;
733 DISPLAY_DESC("play_stop");
734 757
735 /* Can unboost the codec thread here no matter who's calling, 758 /* Can unboost the codec thread here no matter who's calling,
736 * pretend full pcm buffer to unboost */ 759 * pretend full pcm buffer to unboost */
@@ -750,74 +773,153 @@ void pcmbuf_pause(bool pause)
750 773
751/** Crossfade */ 774/** Crossfade */
752 775
753/* Clip sample to signed 16 bit range */ 776#ifdef HAVE_CROSSFADE
754static inline int32_t clip_sample_16(int32_t sample) 777/* Find the buffer index that's 'size' bytes away from 'index' */
778static size_t crossfade_find_index(size_t index, size_t size)
755{ 779{
756 if ((int16_t)sample != sample) 780 if (index != INVALID_BUF_INDEX)
757 sample = 0x7fff ^ (sample >> 31); 781 {
758 return sample; 782 size_t i = ALIGN_DOWN(index, PCMBUF_CHUNK_SIZE);
783 size += index - i;
784
785 while (i != chunk_widx)
786 {
787 size_t desc_size = index_chunkdesc(i)->size;
788
789 if (size < desc_size)
790 return i + size;
791
792 size -= desc_size;
793 i = index_next(i);
794 }
795 }
796
797 return INVALID_BUF_INDEX;
759} 798}
760 799
761#ifdef HAVE_CROSSFADE 800/* Align the needed buffer area up to the end of existing data */
762/* Find the chunk that's (length) deep in the list. Return the position within 801static size_t crossfade_find_buftail(size_t buffer_rem, size_t buffer_need)
763 * the chunk, and leave the chunkdesc pointer pointing to the chunk. */
764static size_t find_chunk(size_t length, struct chunkdesc **chunk)
765{ 802{
766 while (*chunk && length >= (*chunk)->size) 803 crossfade_index = chunk_ridx;
804
805 if (buffer_rem > buffer_need)
767 { 806 {
768 length -= (*chunk)->size; 807 size_t distance;
769 *chunk = (*chunk)->link; 808
809 if (crossfade_auto_skip)
810 {
811 /* Automatic track changes only modify the last part of the buffer,
812 * so find the right chunk and sample to start the crossfade */
813 distance = buffer_rem - buffer_need;
814 buffer_rem = buffer_need;
815 }
816 else
817 {
818 /* Manual skips occur immediately, but give 1/5s to process */
819 distance = BYTERATE / 5;
820 buffer_rem -= BYTERATE / 5;
821 }
822
823 crossfade_index = crossfade_find_index(crossfade_index, distance);
770 } 824 }
771 return length; 825
826 return buffer_rem;
827}
828
829/* Clip sample to signed 16 bit range */
830static FORCE_INLINE int32_t clip_sample_16(int32_t sample)
831{
832 if ((int16_t)sample != sample)
833 sample = 0x7fff ^ (sample >> 31);
834 return sample;
772} 835}
773 836
774/* Returns the number of bytes _NOT_ mixed/faded */ 837/* Returns the number of bytes _NOT_ mixed/faded */
775static size_t crossfade_mix_fade(int factor, size_t length, const char *buf, 838static int crossfade_mix_fade(int factor, size_t size, void *buf, size_t *out_index,
776 size_t *out_sample, struct chunkdesc **out_chunk) 839 unsigned long elapsed, off_t offset)
777{ 840{
778 if (length == 0) 841 if (size == 0)
779 return 0; 842 return 0;
780 843
781 const int16_t *input_buf = (const int16_t *)buf; 844 size_t index = *out_index;
782 int16_t *output_buf = (int16_t *)((*out_chunk)->addr); 845
783 int16_t *chunk_end = SKIPBYTES(output_buf, (*out_chunk)->size); 846 if (index == INVALID_BUF_INDEX)
784 output_buf = &output_buf[*out_sample]; 847 return size;
785 int32_t sample; 848
849 const int16_t *input_buf = buf;
850 int16_t *output_buf = (int16_t *)index_buffer(index);
786 851
787 while (length) 852 while (size)
788 { 853 {
789 /* fade left and right channel at once to keep buffer alignment */ 854 struct chunkdesc *desc = index_chunkdesc(index);
790 int i; 855
791 for (i = 0; i < 2; i++) 856 switch (offset)
792 { 857 {
858 case MIXFADE_NULLIFY_POS:
859 /* Stop position updates for the chunk */
860 desc->pos_key = 0;
861 break;
862 case MIXFADE_KEEP_POS:
863 /* Keep position info as it is */
864 break;
865 default:
866 /* Replace position info */
867 stamp_chunk(desc, elapsed, offset);
868 }
869
870 size_t rem = desc->size - (index % PCMBUF_CHUNK_SIZE);
871 int16_t *chunk_end = SKIPBYTES(output_buf, rem);
872
873 if (size < rem)
874 rem = size;
875
876 size -= rem;
877
878 do
879 {
880 /* fade left and right channel at once to keep buffer alignment */
881 int32_t left = output_buf[0];
882 int32_t right = output_buf[1];
883
793 if (input_buf) 884 if (input_buf)
794 /* fade the input buffer and mix into the chunk */
795 { 885 {
796 sample = *input_buf++; 886 /* fade the input buffer and mix into the chunk */
797 sample = ((sample * factor) >> 8) + *output_buf; 887 left += *input_buf++ * factor >> 8;
798 *output_buf++ = clip_sample_16(sample); 888 right += *input_buf++ * factor >> 8;
889 left = clip_sample_16(left);
890 right = clip_sample_16(right);
799 } 891 }
800 else 892 else
801 /* fade the chunk only */
802 { 893 {
803 sample = *output_buf; 894 /* fade the chunk only */
804 *output_buf++ = (sample * factor) >> 8; 895 left = left * factor >> 8;
896 right = right * factor >> 8;
805 } 897 }
806 }
807 898
808 length -= 4; /* 2 samples, each 16 bit -> 4 bytes */ 899 *output_buf++ = left;
900 *output_buf++ = right;
901
902 rem -= 4;
903 }
904 while (rem);
809 905
810 /* move to next chunk as needed */ 906 /* move to next chunk as needed */
811 if (output_buf >= chunk_end) 907 if (output_buf >= chunk_end)
812 { 908 {
813 *out_chunk = (*out_chunk)->link; 909 index = index_next(index);
814 if (!(*out_chunk)) 910
815 return length; 911 if (index == chunk_widx)
816 output_buf = (int16_t *)((*out_chunk)->addr); 912 {
817 chunk_end = SKIPBYTES(output_buf, (*out_chunk)->size); 913 /* End of existing data */
914 *out_index = INVALID_BUF_INDEX;
915 return size;
916 }
917
918 output_buf = (int16_t *)index_buffer(index);
818 } 919 }
819 } 920 }
820 *out_sample = output_buf - (int16_t *)((*out_chunk)->addr); 921
922 *out_index = buffer_index(output_buf);
821 return 0; 923 return 0;
822} 924}
823 925
@@ -825,184 +927,209 @@ static size_t crossfade_mix_fade(int factor, size_t length, const char *buf,
825 * fade-out with the PCM buffer. */ 927 * fade-out with the PCM buffer. */
826static void crossfade_start(void) 928static void crossfade_start(void)
827{ 929{
828 size_t crossfade_rem;
829 size_t crossfade_need;
830 size_t fade_out_rem;
831 size_t fade_out_delay;
832 size_t fade_in_delay;
833
834 crossfade_track_change_started = false;
835 /* Reject crossfade if less than .5s of data */
836 if (LOW_DATA(2)) {
837 logf("crossfade rejected");
838 pcmbuf_play_stop();
839 return ;
840 }
841
842 logf("crossfade_start"); 930 logf("crossfade_start");
843 commit_chunk(false); 931
844 crossfade_active = true; 932 pcm_play_lock();
845 933
846 /* Initialize the crossfade buffer size to all of the buffered data that 934 /* Initialize the crossfade buffer size to all of the buffered data that
847 * has not yet been sent to the DMA */ 935 * has not yet been sent to the DMA */
848 crossfade_rem = pcmbuf_unplayed_bytes; 936 size_t unplayed = pcmbuf_unplayed_bytes();
849 crossfade_chunk = read_chunk->link;
850 crossfade_sample = 0;
851
852 /* Get fade out info from settings. */
853 fade_out_delay = global_settings.crossfade_fade_out_delay * BYTERATE;
854 fade_out_rem = global_settings.crossfade_fade_out_duration * BYTERATE;
855 937
856 crossfade_need = fade_out_delay + fade_out_rem; 938 /* Reject crossfade if less than .5s of data */
857 if (crossfade_rem > crossfade_need) 939 if (unplayed < DATA_LEVEL(2))
858 { 940 {
941 logf("crossfade rejected");
942
943 crossfade_status = CROSSFADE_INACTIVE;
944
859 if (crossfade_auto_skip) 945 if (crossfade_auto_skip)
860 /* Automatic track changes only modify the last part of the buffer, 946 pcmbuf_monitor_track_change(true);
861 * so find the right chunk and sample to start the crossfade */ 947
862 { 948 pcm_play_unlock();
863 crossfade_sample = find_chunk(crossfade_rem - crossfade_need, 949 return;
864 &crossfade_chunk) / 2;
865 crossfade_rem = crossfade_need;
866 }
867 else
868 /* Manual skips occur immediately, but give time to process */
869 {
870 crossfade_rem -= crossfade_chunk->size;
871 crossfade_chunk = crossfade_chunk->link;
872 }
873 } 950 }
874 /* Truncate fade out duration if necessary. */ 951
875 if (crossfade_rem < crossfade_need) 952 /* Fading will happen */
953 crossfade_status = CROSSFADE_ACTIVE;
954
955 /* Get fade info from settings. */
956 size_t fade_out_delay = global_settings.crossfade_fade_out_delay * BYTERATE;
957 size_t fade_out_rem = global_settings.crossfade_fade_out_duration * BYTERATE;
958 size_t fade_in_delay = global_settings.crossfade_fade_in_delay * BYTERATE;
959 size_t fade_in_duration = global_settings.crossfade_fade_in_duration * BYTERATE;
960
961 if (!crossfade_auto_skip)
876 { 962 {
877 size_t crossfade_short = crossfade_need - crossfade_rem; 963 /* Forego fade-in delay on manual skip - do the best to preserve auto skip
878 if (fade_out_rem >= crossfade_short) 964 relationship */
879 fade_out_rem -= crossfade_short; 965 if (fade_out_delay > fade_in_delay)
966 fade_out_delay -= fade_in_delay;
880 else 967 else
881 { 968 fade_out_delay = 0;
882 fade_out_delay -= crossfade_short - fade_out_rem; 969
883 fade_out_rem = 0; 970 fade_in_delay = 0;
884 }
885 } 971 }
886 crossfade_rem -= fade_out_delay + fade_out_rem;
887 972
888 /* Completely process the crossfade fade-out effect with current PCM buffer */ 973 size_t fade_out_need = fade_out_delay + fade_out_rem;
974
889 if (!crossfade_mixmode) 975 if (!crossfade_mixmode)
890 { 976 {
977 size_t buffer_rem = crossfade_find_buftail(unplayed, fade_out_need);
978
979 pcm_play_unlock();
980
981 if (buffer_rem < fade_out_need)
982 {
983 /* Existing buffers are short */
984 size_t fade_out_short = fade_out_need - buffer_rem;
985
986 if (fade_out_rem >= fade_out_short)
987 {
988 /* Truncate fade-out duration */
989 fade_out_rem -= fade_out_short;
990 }
991 else
992 {
993 /* Truncate fade-out and fade-out delay */
994 fade_out_delay = fade_out_rem;
995 fade_out_rem = 0;
996 }
997 }
998
999 /* Completely process the crossfade fade-out effect with current PCM buffer */
1000
891 /* Fade out the specified amount of the already processed audio */ 1001 /* Fade out the specified amount of the already processed audio */
892 size_t total_fade_out = fade_out_rem; 1002 size_t fade_out_total = fade_out_rem;
893 size_t fade_out_sample;
894 struct chunkdesc *fade_out_chunk = crossfade_chunk;
895 1003
896 /* Find the right chunk and sample to start fading out */ 1004 /* Find the right chunk and sample to start fading out */
897 fade_out_delay += crossfade_sample * 2; 1005 size_t fade_out_index = crossfade_find_index(crossfade_index, fade_out_delay);
898 fade_out_sample = find_chunk(fade_out_delay, &fade_out_chunk) / 2;
899 1006
900 while (fade_out_rem > 0) 1007 while (fade_out_rem > 0)
901 { 1008 {
902 /* Each 1/10 second of audio will have the same fade applied */ 1009 /* Each 1/20 second of audio will have the same fade applied */
903 size_t block_rem = MIN(BYTERATE / 10, fade_out_rem); 1010 size_t block_rem = MIN(BYTERATE / 20, fade_out_rem);
904 int factor = (fade_out_rem << 8) / total_fade_out; 1011 int factor = (fade_out_rem << 8) / fade_out_total;
905 1012
906 fade_out_rem -= block_rem; 1013 fade_out_rem -= block_rem;
907 1014
908 crossfade_mix_fade(factor, block_rem, NULL, 1015 crossfade_mix_fade(factor, block_rem, NULL, &fade_out_index,
909 &fade_out_sample, &fade_out_chunk); 1016 0, MIXFADE_KEEP_POS);
910 } 1017 }
911 1018
912 /* zero out the rest of the buffer */ 1019 /* zero out the rest of the buffer */
913 crossfade_mix_fade(0, crossfade_rem, NULL, 1020 crossfade_mix_fade(0, INT_MAX, NULL, &fade_out_index,
914 &fade_out_sample, &fade_out_chunk); 1021 0, MIXFADE_NULLIFY_POS);
1022
1023 pcm_play_lock();
915 } 1024 }
916 1025
917 /* Initialize fade-in counters */ 1026 /* Initialize fade-in counters */
918 crossfade_fade_in_total = global_settings.crossfade_fade_in_duration * BYTERATE; 1027 crossfade_fade_in_total = fade_in_duration;
919 crossfade_fade_in_rem = crossfade_fade_in_total; 1028 crossfade_fade_in_rem = fade_in_duration;
920 1029
921 fade_in_delay = global_settings.crossfade_fade_in_delay * BYTERATE; 1030 /* Find the right chunk and sample to start fading in - redo from read
1031 chunk in case original position were/was overrun in callback - the
1032 track change event _must not_ ever fail to happen */
1033 unplayed = pcmbuf_unplayed_bytes() + fade_in_delay;
1034
1035 crossfade_find_buftail(unplayed, fade_out_need);
1036
1037 if (crossfade_auto_skip)
1038 pcmbuf_monitor_track_change_ex(crossfade_index, 0);
1039
1040 pcm_play_unlock();
922 1041
923 /* Find the right chunk and sample to start fading in */
924 fade_in_delay += crossfade_sample * 2;
925 crossfade_sample = find_chunk(fade_in_delay, &crossfade_chunk) / 2;
926 logf("crossfade_start done!"); 1042 logf("crossfade_start done!");
927} 1043}
928 1044
929/* Perform fade-in of new track */ 1045/* Perform fade-in of new track */
930static void write_to_crossfade(size_t length) 1046static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset)
931{ 1047{
932 if (length) 1048 unsigned char *buf = crossfade_buffer;
933 {
934 char *buf = fadebuf;
935 if (crossfade_fade_in_rem)
936 {
937 size_t samples;
938 int16_t *input_buf;
939 1049
940 /* Fade factor for this packet */ 1050 if (crossfade_fade_in_rem)
941 int factor = 1051 {
942 ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) / 1052 /* Fade factor for this packet */
943 crossfade_fade_in_total; 1053 int factor =
944 /* Bytes to fade */ 1054 ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) /
945 size_t fade_rem = MIN(length, crossfade_fade_in_rem); 1055 crossfade_fade_in_total;
946 1056 /* Bytes to fade */
947 /* We _will_ fade this many bytes */ 1057 size_t fade_rem = MIN(size, crossfade_fade_in_rem);
948 crossfade_fade_in_rem -= fade_rem;
949 1058
950 if (crossfade_chunk) 1059 /* We _will_ fade this many bytes */
951 { 1060 crossfade_fade_in_rem -= fade_rem;
952 /* Mix the data */
953 size_t fade_total = fade_rem;
954 fade_rem = crossfade_mix_fade(factor, fade_rem, buf,
955 &crossfade_sample, &crossfade_chunk);
956 length -= fade_total - fade_rem;
957 buf += fade_total - fade_rem;
958 if (!length)
959 return;
960 }
961 1061
962 samples = fade_rem / 2; 1062 if (crossfade_index != INVALID_BUF_INDEX)
963 input_buf = (int16_t *)buf;
964 /* Fade remaining samples in place */
965 while (samples--)
966 {
967 int32_t sample = *input_buf;
968 *input_buf++ = (sample * factor) >> 8;
969 }
970 }
971
972 if (crossfade_chunk)
973 { 1063 {
974 /* Mix the data */ 1064 /* Mix the data */
975 size_t mix_total = length; 1065 size_t fade_total = fade_rem;
976 /* A factor of 256 means mix only, no fading */ 1066 fade_rem = crossfade_mix_fade(factor, fade_rem, buf, &crossfade_index,
977 length = crossfade_mix_fade(256, length, buf, 1067 elapsed, offset);
978 &crossfade_sample, &crossfade_chunk); 1068 fade_total -= fade_rem;
979 buf += mix_total - length; 1069 size -= fade_total;
980 if (!length) 1070 buf += fade_total;
1071
1072 if (!size)
981 return; 1073 return;
982 } 1074 }
983 1075
984 while (length > 0) 1076 /* Fade remaining samples in place */
1077 int samples = fade_rem / 4;
1078 int16_t *input_buf = (int16_t *)buf;
1079
1080 while (samples--)
985 { 1081 {
986 COMMIT_IF_NEEDED; 1082 int32_t left = input_buf[0];
987 size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos; 1083 int32_t right = input_buf[1];
988 size_t copy_n = MIN(length, pcmbuf_size - pcmbuffer_index); 1084 *input_buf++ = left * factor >> 8;
989 memcpy(&pcmbuffer[pcmbuffer_index], buf, copy_n); 1085 *input_buf++ = right * factor >> 8;
990 buf += copy_n;
991 pcmbuffer_fillpos += copy_n;
992 length -= copy_n;
993 } 1086 }
994 } 1087 }
1088
1089 if (crossfade_index != INVALID_BUF_INDEX)
1090 {
1091 /* Mix the data */
1092 size_t mix_total = size;
1093
1094 /* A factor of 256 means mix only, no fading */
1095 size = crossfade_mix_fade(256, size, buf, &crossfade_index,
1096 elapsed, offset);
1097 buf += mix_total - size;
1098
1099 if (!size)
1100 return;
1101 }
1102
1103 /* Data might remain in the fade buffer yet the fade-in has run its
1104 course - finish it off as normal chunks */
1105 while (size > 0)
1106 {
1107 size_t copy_n = size;
1108 unsigned char *outbuf = get_write_buffer(&copy_n);
1109 memcpy(outbuf, buf, copy_n);
1110 commit_write_buffer(copy_n, elapsed, offset);
1111 buf += copy_n;
1112 size -= copy_n;
1113 }
1114
995 /* if no more fading-in to do, stop the crossfade */ 1115 /* if no more fading-in to do, stop the crossfade */
996 if (!(crossfade_fade_in_rem || crossfade_chunk)) 1116#if 0
997 crossfade_active = false; 1117 /* This way (the previous way) can cause a sudden volume jump if mixable
1118 data is used up before the fade-in completes and that just sounds wrong
1119 -- jethead71 */
1120 if (!crossfade_fade_in_rem || crossfade_index == INVALID_BUF_INDEX)
1121#endif
1122 /* Let fade-in complete even if not fully overlapping the existing data */
1123 if (!crossfade_fade_in_rem)
1124 crossfade_status = CROSSFADE_INACTIVE;
998} 1125}
999 1126
1000static void pcmbuf_finish_crossfade_enable(void) 1127static void pcmbuf_finish_crossfade_enable(void)
1001{ 1128{
1002 /* Copy the pending setting over now */ 1129 /* Copy the pending setting over now */
1003 crossfade_enabled = crossfade_enable_request; 1130 crossfade_setting = crossfade_enable_request;
1004 1131
1005 pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ? 1132 pcmbuf_watermark = (crossfade_setting != CROSSFADE_ENABLE_OFF && pcmbuf_size) ?
1006 /* If crossfading, try to keep the buffer full other than 1 second */ 1133 /* If crossfading, try to keep the buffer full other than 1 second */
1007 (pcmbuf_size - BYTERATE) : 1134 (pcmbuf_size - BYTERATE) :
1008 /* Otherwise, just use the default */ 1135 /* Otherwise, just use the default */
@@ -1011,20 +1138,20 @@ static void pcmbuf_finish_crossfade_enable(void)
1011 1138
1012bool pcmbuf_is_crossfade_active(void) 1139bool pcmbuf_is_crossfade_active(void)
1013{ 1140{
1014 return crossfade_active || crossfade_track_change_started; 1141 return crossfade_status != CROSSFADE_INACTIVE;
1015} 1142}
1016 1143
1017void pcmbuf_request_crossfade_enable(bool on_off) 1144void pcmbuf_request_crossfade_enable(int setting)
1018{ 1145{
1019 /* Next setting to be used, not applied now */ 1146 /* Next setting to be used, not applied now */
1020 crossfade_enable_request = on_off; 1147 crossfade_enable_request = setting;
1021} 1148}
1022 1149
1023bool pcmbuf_is_same_size(void) 1150bool pcmbuf_is_same_size(void)
1024{ 1151{
1025 /* if pcmbuffer is NULL, then not set up yet even once so always */ 1152 /* if pcmbuf_buffer is NULL, then not set up yet even once so always */
1026 bool same_size = pcmbuffer ? 1153 bool same_size = pcmbuf_buffer ?
1027 (get_next_required_pcmbuf_size() == pcmbuf_size) : true; 1154 (get_next_required_pcmbuf_chunks() == pcmbuf_desc_count) : true;
1028 1155
1029 /* no buffer change needed, so finish crossfade setup now */ 1156 /* no buffer change needed, so finish crossfade setup now */
1030 if (same_size) 1157 if (same_size)
@@ -1037,49 +1164,29 @@ bool pcmbuf_is_same_size(void)
1037 1164
1038/** Debug menu, other metrics */ 1165/** Debug menu, other metrics */
1039 1166
1040/* Amount of bytes left in the buffer. */ 1167/* Amount of bytes left in the buffer, accounting for uncommitted bytes */
1041size_t pcmbuf_free(void) 1168size_t pcmbuf_free(void)
1042{ 1169{
1043 if (read_chunk != NULL) 1170 return pcmbuf_size - pcmbuf_unplayed_bytes() - pcmbuf_bytes_waiting;
1044 {
1045 void *read = (void *)read_chunk->addr;
1046 void *write = &pcmbuffer[pcmbuffer_pos + pcmbuffer_fillpos];
1047 if (read < write)
1048 return (size_t)(read - write) + pcmbuf_size;
1049 else
1050 return (size_t) (read - write);
1051 }
1052 return pcmbuf_size - pcmbuffer_fillpos;
1053} 1171}
1054 1172
1173/* Data bytes allocated for buffer */
1055size_t pcmbuf_get_bufsize(void) 1174size_t pcmbuf_get_bufsize(void)
1056{ 1175{
1057 return pcmbuf_size; 1176 return pcmbuf_size;
1058} 1177}
1059 1178
1179/* Number of committed descriptors */
1060int pcmbuf_used_descs(void) 1180int pcmbuf_used_descs(void)
1061{ 1181{
1062 struct chunkdesc *temp = read_chunk; 1182 return pcmbuf_unplayed_bytes() / PCMBUF_CHUNK_SIZE;
1063 unsigned int i = 0;
1064 while (temp) {
1065 temp = temp->link;
1066 i++;
1067 }
1068 return i;
1069} 1183}
1070 1184
1185/* Total number of descriptors allocated */
1071int pcmbuf_descs(void) 1186int pcmbuf_descs(void)
1072{ 1187{
1073 return NUM_CHUNK_DESCS(pcmbuf_size); 1188 return pcmbuf_desc_count;
1074}
1075
1076#ifdef ROCKBOX_HAS_LOGF
1077unsigned char *pcmbuf_get_meminfo(size_t *length)
1078{
1079 *length = pcmbuf_bufend - pcmbuffer;
1080 return pcmbuffer;
1081} 1189}
1082#endif
1083 1190
1084 1191
1085/** Fading and channel volume control */ 1192/** Fading and channel volume control */
@@ -1112,7 +1219,6 @@ static void pcmbuf_fade_tick(void)
1112 { 1219 {
1113 /* Fade is complete */ 1220 /* Fade is complete */
1114 tick_remove_task(pcmbuf_fade_tick); 1221 tick_remove_task(pcmbuf_fade_tick);
1115
1116 if (fade_state == PCM_FADING_OUT) 1222 if (fade_state == PCM_FADING_OUT)
1117 { 1223 {
1118 /* Tell PCM to stop at its earliest convenience */ 1224 /* Tell PCM to stop at its earliest convenience */
@@ -1177,19 +1283,15 @@ void pcmbuf_soft_mode(bool shhh)
1177 1283
1178bool pcmbuf_is_lowdata(void) 1284bool pcmbuf_is_lowdata(void)
1179{ 1285{
1180 if (!pcm_is_playing() || pcm_is_paused() 1286 if (!audio_pcmbuf_may_play() || pcmbuf_is_crossfade_active())
1181#ifdef HAVE_CROSSFADE
1182 || pcmbuf_is_crossfade_active()
1183#endif
1184 )
1185 return false; 1287 return false;
1186 1288
1187#if MEMORYSIZE > 2 1289#if MEMORYSIZE > 2
1188 /* 1 seconds of buffer is low data */ 1290 /* 1 seconds of buffer is low data */
1189 return LOW_DATA(4); 1291 return pcmbuf_unplayed_bytes() < DATA_LEVEL(4);
1190#else 1292#else
1191 /* under watermark is low data */ 1293 /* under watermark is low data */
1192 return (pcmbuf_unplayed_bytes < pcmbuf_watermark); 1294 return pcmbuf_unplayed_bytes() < pcmbuf_watermark;
1193#endif 1295#endif
1194} 1296}
1195 1297
@@ -1198,8 +1300,15 @@ void pcmbuf_set_low_latency(bool state)
1198 low_latency_mode = state; 1300 low_latency_mode = state;
1199} 1301}
1200 1302
1201unsigned long pcmbuf_get_latency(void) 1303/* Return the current position key value */
1304unsigned int pcmbuf_get_position_key(void)
1305{
1306 return position_key;
1307}
1308
1309/* Set position updates to be synchronous and immediate in addition to during
1310 PCM frames - cancelled upon first codec insert or upon stopping */
1311void pcmbuf_sync_position_update(void)
1202{ 1312{
1203 return (pcmbuf_unplayed_bytes + 1313 pcmbuf_sync_position = true;
1204 mixer_channel_get_bytes_waiting(PCM_MIXER_CHAN_PLAYBACK)) * 1000 / BYTERATE;
1205} 1314}
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index a5cd3163e1..3261b20b35 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -21,9 +21,11 @@
21#ifndef PCMBUF_H 21#ifndef PCMBUF_H
22#define PCMBUF_H 22#define PCMBUF_H
23 23
24#include <sys/types.h>
25
24/* Commit PCM data */ 26/* Commit PCM data */
25void *pcmbuf_request_buffer(int *count); 27void *pcmbuf_request_buffer(int *count);
26void pcmbuf_write_complete(int count); 28void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset);
27 29
28/* Init */ 30/* Init */
29size_t pcmbuf_init(unsigned char *bufend); 31size_t pcmbuf_init(unsigned char *bufend);
@@ -33,20 +35,30 @@ void pcmbuf_play_start(void);
33void pcmbuf_play_stop(void); 35void pcmbuf_play_stop(void);
34void pcmbuf_pause(bool pause); 36void pcmbuf_pause(bool pause);
35void pcmbuf_monitor_track_change(bool monitor); 37void pcmbuf_monitor_track_change(bool monitor);
36bool pcmbuf_start_track_change(bool manual_skip); 38void pcmbuf_sync_position_update(void);
39
40/* Track change origin type */
41enum pcm_track_change_type
42{
43 TRACK_CHANGE_NONE = 0, /* No track change pending */
44 TRACK_CHANGE_MANUAL, /* Manual change (from user) */
45 TRACK_CHANGE_AUTO, /* Automatic change (from codec) */
46 TRACK_CHANGE_END_OF_DATA, /* Expect no more data (from codec) */
47};
48void pcmbuf_start_track_change(enum pcm_track_change_type type);
37 49
38/* Crossfade */ 50/* Crossfade */
39#ifdef HAVE_CROSSFADE 51#ifdef HAVE_CROSSFADE
40bool pcmbuf_is_crossfade_active(void); 52bool pcmbuf_is_crossfade_active(void);
41void pcmbuf_request_crossfade_enable(bool on_off); 53void pcmbuf_request_crossfade_enable(int setting);
42bool pcmbuf_is_same_size(void); 54bool pcmbuf_is_same_size(void);
43#else 55#else
44/* Dummy functions with sensible returns */ 56/* Dummy functions with sensible returns */
45static inline bool pcmbuf_is_crossfade_active(void) 57static FORCE_INLINE bool pcmbuf_is_crossfade_active(void)
46 { return false; } 58 { return false; }
47static inline void pcmbuf_request_crossfade_enable(bool on_off) 59static FORCE_INLINE void pcmbuf_request_crossfade_enable(bool on_off)
48 { return; (void)on_off; } 60 { return; (void)on_off; }
49static inline bool pcmbuf_is_same_size(void) 61static FORCE_INLINE bool pcmbuf_is_same_size(void)
50 { return true; } 62 { return true; }
51#endif 63#endif
52 64
@@ -59,9 +71,7 @@ size_t pcmbuf_free(void);
59size_t pcmbuf_get_bufsize(void); 71size_t pcmbuf_get_bufsize(void);
60int pcmbuf_descs(void); 72int pcmbuf_descs(void);
61int pcmbuf_used_descs(void); 73int pcmbuf_used_descs(void);
62#ifdef ROCKBOX_HAS_LOGF 74unsigned int pcmbuf_get_position_key(void);
63unsigned char *pcmbuf_get_meminfo(size_t *length);
64#endif
65 75
66/* Misc */ 76/* Misc */
67void pcmbuf_fade(bool fade, bool in); 77void pcmbuf_fade(bool fade, bool in);
@@ -69,6 +79,5 @@ bool pcmbuf_fading(void);
69void pcmbuf_soft_mode(bool shhh); 79void pcmbuf_soft_mode(bool shhh);
70bool pcmbuf_is_lowdata(void); 80bool pcmbuf_is_lowdata(void);
71void pcmbuf_set_low_latency(bool state); 81void pcmbuf_set_low_latency(bool state);
72unsigned long pcmbuf_get_latency(void);
73 82
74#endif 83#endif
diff --git a/apps/playback.c b/apps/playback.c
index 65783414fe..7dad08644a 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -330,7 +330,7 @@ static struct
330static bool codec_skip_pending = false; 330static bool codec_skip_pending = false;
331static int codec_skip_status; 331static int codec_skip_status;
332static bool codec_seeking = false; /* Codec seeking ack expected? */ 332static bool codec_seeking = false; /* Codec seeking ack expected? */
333 333static unsigned int position_key = 0;
334 334
335/* Event queues */ 335/* Event queues */
336static struct event_queue audio_queue SHAREDBSS_ATTR; 336static struct event_queue audio_queue SHAREDBSS_ATTR;
@@ -353,14 +353,13 @@ static void audio_stop_playback(void);
353static void buffer_event_buffer_low_callback(void *data); 353static void buffer_event_buffer_low_callback(void *data);
354static void buffer_event_rebuffer_callback(void *data); 354static void buffer_event_rebuffer_callback(void *data);
355static void buffer_event_finished_callback(void *data); 355static void buffer_event_finished_callback(void *data);
356void audio_pcmbuf_sync_position(void);
356 357
357 358
358/**************************************/ 359/**************************************/
359 360
360/** --- audio_queue helpers --- **/ 361/** --- audio_queue helpers --- **/
361 362static void audio_queue_post(long id, intptr_t data)
362/* codec thread needs access */
363void audio_queue_post(long id, intptr_t data)
364{ 363{
365 queue_post(&audio_queue, id, data); 364 queue_post(&audio_queue, id, data);
366} 365}
@@ -805,14 +804,10 @@ static void audio_reset_buffer(void)
805 aids viewing and the summation of certain variables should add up to 804 aids viewing and the summation of certain variables should add up to
806 the location of others. */ 805 the location of others. */
807 { 806 {
808 size_t pcmbufsize;
809 const unsigned char *pcmbuf = pcmbuf_get_meminfo(&pcmbufsize);
810 logf("fbuf: %08X", (unsigned)filebuf); 807 logf("fbuf: %08X", (unsigned)filebuf);
811 logf("fbufe: %08X", (unsigned)(filebuf + filebuflen)); 808 logf("fbufe: %08X", (unsigned)(filebuf + filebuflen));
812 logf("sbuf: %08X", (unsigned)audio_scratch_memory); 809 logf("sbuf: %08X", (unsigned)audio_scratch_memory);
813 logf("sbufe: %08X", (unsigned)(audio_scratch_memory + allocsize)); 810 logf("sbufe: %08X", (unsigned)(audio_scratch_memory + allocsize));
814 logf("pcmb: %08X", (unsigned)pcmbuf);
815 logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize));
816 } 811 }
817#endif 812#endif
818 813
@@ -978,7 +973,8 @@ static void audio_handle_track_load_status(int trackstat)
978/* Announce the end of playing the current track */ 973/* Announce the end of playing the current track */
979static void audio_playlist_track_finish(void) 974static void audio_playlist_track_finish(void)
980{ 975{
981 struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3)); 976 struct mp3entry *ply_id3 = id3_get(PLAYING_ID3);
977 struct mp3entry *id3 = valid_mp3entry(ply_id3);
982 978
983 playlist_update_resume_info(filling == STATE_ENDED ? NULL : id3); 979 playlist_update_resume_info(filling == STATE_ENDED ? NULL : id3);
984 980
@@ -1001,6 +997,8 @@ static void audio_playlist_track_change(void)
1001 if (id3) 997 if (id3)
1002 send_event(PLAYBACK_EVENT_TRACK_CHANGE, id3); 998 send_event(PLAYBACK_EVENT_TRACK_CHANGE, id3);
1003 999
1000 position_key = pcmbuf_get_position_key();
1001
1004 playlist_update_resume_info(id3); 1002 playlist_update_resume_info(id3);
1005} 1003}
1006 1004
@@ -1014,26 +1012,28 @@ static void audio_update_and_announce_next_track(const struct mp3entry *id3_next
1014 1012
1015/* Bring the user current mp3entry up to date and set a new offset for the 1013/* Bring the user current mp3entry up to date and set a new offset for the
1016 buffered metadata */ 1014 buffered metadata */
1017static void playing_id3_sync(struct track_info *user_info, size_t offset) 1015static void playing_id3_sync(struct track_info *user_info, off_t offset)
1018{ 1016{
1019 id3_mutex_lock(); 1017 id3_mutex_lock();
1020 1018
1021 struct mp3entry *id3 = bufgetid3(user_info->id3_hid); 1019 struct mp3entry *id3 = bufgetid3(user_info->id3_hid);
1020 struct mp3entry *playing_id3 = id3_get(PLAYING_ID3);
1022 1021
1023 if (offset == (size_t)-1) 1022 pcm_play_lock();
1023
1024 unsigned long e = playing_id3->elapsed;
1025 unsigned long o = playing_id3->offset;
1026
1027 id3_write(PLAYING_ID3, id3);
1028
1029 if (offset < 0)
1024 { 1030 {
1025 struct mp3entry *ply_id3 = id3_get(PLAYING_ID3); 1031 playing_id3->elapsed = e;
1026 size_t play_offset = ply_id3->offset; 1032 playing_id3->offset = o;
1027 long play_elapsed = ply_id3->elapsed;
1028 id3_write(PLAYING_ID3, id3);
1029 ply_id3->offset = play_offset;
1030 ply_id3->elapsed = play_elapsed;
1031 offset = 0; 1033 offset = 0;
1032 } 1034 }
1033 else 1035
1034 { 1036 pcm_play_unlock();
1035 id3_write(PLAYING_ID3, id3);
1036 }
1037 1037
1038 if (id3) 1038 if (id3)
1039 id3->offset = offset; 1039 id3->offset = offset;
@@ -1093,13 +1093,6 @@ static bool halt_decoding_track(bool stop)
1093 return retval; 1093 return retval;
1094} 1094}
1095 1095
1096/* Clear the PCM on a manual skip */
1097static void audio_clear_paused_pcm(void)
1098{
1099 if (play_status == PLAY_PAUSED && !pcmbuf_is_crossfade_active())
1100 pcmbuf_play_stop();
1101}
1102
1103/* Wait for any in-progress fade to complete */ 1096/* Wait for any in-progress fade to complete */
1104static void audio_wait_fade_complete(void) 1097static void audio_wait_fade_complete(void)
1105{ 1098{
@@ -1121,6 +1114,7 @@ static void audio_ff_rewind_end(void)
1121 { 1114 {
1122 /* Clear the buffer */ 1115 /* Clear the buffer */
1123 pcmbuf_play_stop(); 1116 pcmbuf_play_stop();
1117 audio_pcmbuf_sync_position();
1124 } 1118 }
1125 1119
1126 if (play_status != PLAY_PAUSED) 1120 if (play_status != PLAY_PAUSED)
@@ -2063,7 +2057,7 @@ static void audio_on_handle_finished(int hid)
2063 2057
2064/* Called to make an outstanding track skip the current track and to send the 2058/* Called to make an outstanding track skip the current track and to send the
2065 transition events */ 2059 transition events */
2066static void audio_finalise_track_change(bool delayed) 2060static void audio_finalise_track_change(void)
2067{ 2061{
2068 switch (skip_pending) 2062 switch (skip_pending)
2069 { 2063 {
@@ -2117,15 +2111,6 @@ static void audio_finalise_track_change(bool delayed)
2117 2111
2118 id3_write(PLAYING_ID3, track_id3); 2112 id3_write(PLAYING_ID3, track_id3);
2119 2113
2120 if (delayed)
2121 {
2122 /* Delayed skip where codec is ahead of user's current track */
2123 struct mp3entry *ci_id3 = id3_get(CODEC_ID3);
2124 struct mp3entry *ply_id3 = id3_get(PLAYING_ID3);
2125 ply_id3->elapsed = ci_id3->elapsed;
2126 ply_id3->offset = ci_id3->offset;
2127 }
2128
2129 /* The skip is technically over */ 2114 /* The skip is technically over */
2130 skip_pending = TRACK_SKIP_NONE; 2115 skip_pending = TRACK_SKIP_NONE;
2131 2116
@@ -2141,25 +2126,25 @@ static void audio_finalise_track_change(bool delayed)
2141} 2126}
2142 2127
2143/* Actually begin a transition and take care of the codec change - may complete 2128/* Actually begin a transition and take care of the codec change - may complete
2144 it now or ask pcmbuf for notification depending on the type and what pcmbuf 2129 it now or ask pcmbuf for notification depending on the type */
2145 has to say */ 2130static void audio_begin_track_change(enum pcm_track_change_type type,
2146static void audio_begin_track_change(bool auto_skip, int trackstat) 2131 int trackstat)
2147{ 2132{
2148 /* Even if the new track is bad, the old track must be finished off */ 2133 /* Even if the new track is bad, the old track must be finished off */
2149 bool finalised = pcmbuf_start_track_change(auto_skip); 2134 pcmbuf_start_track_change(type);
2135
2136 bool auto_skip = type != TRACK_CHANGE_MANUAL;
2150 2137
2151 if (finalised) 2138 if (!auto_skip)
2152 { 2139 {
2153 /* pcmbuf says that the transition happens now - complete it */ 2140 /* Manual track change happens now */
2154 audio_finalise_track_change(false); 2141 audio_finalise_track_change();
2142 pcmbuf_sync_position_update();
2155 2143
2156 if (play_status == PLAY_STOPPED) 2144 if (play_status == PLAY_STOPPED)
2157 return; /* Stopped us */ 2145 return; /* Stopped us */
2158 } 2146 }
2159 2147
2160 if (!auto_skip)
2161 audio_clear_paused_pcm();
2162
2163 if (trackstat >= LOAD_TRACK_OK) 2148 if (trackstat >= LOAD_TRACK_OK)
2164 { 2149 {
2165 struct track_info *info = track_list_current(0); 2150 struct track_info *info = track_list_current(0);
@@ -2170,7 +2155,7 @@ static void audio_begin_track_change(bool auto_skip, int trackstat)
2170 /* Everything needed for the codec is ready - start it */ 2155 /* Everything needed for the codec is ready - start it */
2171 if (audio_start_codec(auto_skip)) 2156 if (audio_start_codec(auto_skip))
2172 { 2157 {
2173 if (finalised) 2158 if (!auto_skip)
2174 playing_id3_sync(info, -1); 2159 playing_id3_sync(info, -1);
2175 return; 2160 return;
2176 } 2161 }
@@ -2186,7 +2171,7 @@ static void audio_monitor_end_of_playlist(void)
2186{ 2171{
2187 skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST; 2172 skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST;
2188 filling = STATE_ENDING; 2173 filling = STATE_ENDING;
2189 pcmbuf_monitor_track_change(true); 2174 pcmbuf_start_track_change(TRACK_CHANGE_END_OF_DATA);
2190} 2175}
2191 2176
2192/* Codec has completed decoding the track 2177/* Codec has completed decoding the track
@@ -2221,14 +2206,6 @@ static void audio_on_codec_complete(int status)
2221 2206
2222 codec_skip_pending = false; 2207 codec_skip_pending = false;
2223 2208
2224#ifdef AB_REPEAT_ENABLE
2225 if (status >= 0)
2226 {
2227 /* Normal automatic skip */
2228 ab_end_of_track_report();
2229 }
2230#endif
2231
2232 int trackstat = LOAD_TRACK_OK; 2209 int trackstat = LOAD_TRACK_OK;
2233 2210
2234 automatic_skip = true; 2211 automatic_skip = true;
@@ -2263,7 +2240,7 @@ static void audio_on_codec_complete(int status)
2263 { 2240 {
2264 /* Continue filling after this track */ 2241 /* Continue filling after this track */
2265 audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1); 2242 audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1);
2266 audio_begin_track_change(true, trackstat); 2243 audio_begin_track_change(TRACK_CHANGE_AUTO, trackstat);
2267 return; 2244 return;
2268 } 2245 }
2269 /* else rebuffer at this track; status applies to the track we 2246 /* else rebuffer at this track; status applies to the track we
@@ -2299,7 +2276,7 @@ static void audio_on_codec_complete(int status)
2299 } 2276 }
2300 } 2277 }
2301 2278
2302 audio_begin_track_change(true, trackstat); 2279 audio_begin_track_change(TRACK_CHANGE_AUTO, trackstat);
2303} 2280}
2304 2281
2305/* Called when codec completes seek operation 2282/* Called when codec completes seek operation
@@ -2316,7 +2293,7 @@ static void audio_on_codec_seek_complete(void)
2316static void audio_on_track_changed(void) 2293static void audio_on_track_changed(void)
2317{ 2294{
2318 /* Finish whatever is pending so that the WPS is in sync */ 2295 /* Finish whatever is pending so that the WPS is in sync */
2319 audio_finalise_track_change(true); 2296 audio_finalise_track_change();
2320 2297
2321 if (codec_skip_pending) 2298 if (codec_skip_pending)
2322 { 2299 {
@@ -2367,8 +2344,7 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2367 track_list_clear(TRACK_LIST_CLEAR_ALL); 2344 track_list_clear(TRACK_LIST_CLEAR_ALL);
2368 2345
2369 /* Indicate manual track change */ 2346 /* Indicate manual track change */
2370 pcmbuf_start_track_change(false); 2347 pcmbuf_start_track_change(TRACK_CHANGE_MANUAL);
2371 audio_clear_paused_pcm();
2372 wipe_track_metadata(true); 2348 wipe_track_metadata(true);
2373 } 2349 }
2374 2350
@@ -2398,6 +2374,10 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2398 play_status = PLAY_PLAYING; 2374 play_status = PLAY_PLAYING;
2399 } 2375 }
2400 2376
2377 /* Codec's position should be available as soon as it knows it */
2378 position_key = pcmbuf_get_position_key();
2379 pcmbuf_sync_position_update();
2380
2401 /* Start fill from beginning of playlist */ 2381 /* Start fill from beginning of playlist */
2402 playlist_peek_offset = -1; 2382 playlist_peek_offset = -1;
2403 buf_set_base_handle(-1); 2383 buf_set_base_handle(-1);
@@ -2592,7 +2572,7 @@ static void audio_on_skip(void)
2592 trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1); 2572 trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1);
2593 } 2573 }
2594 2574
2595 audio_begin_track_change(false, trackstat); 2575 audio_begin_track_change(TRACK_CHANGE_MANUAL, trackstat);
2596} 2576}
2597 2577
2598/* Skip to the next/previous directory 2578/* Skip to the next/previous directory
@@ -2638,7 +2618,7 @@ static void audio_on_dir_skip(int direction)
2638 return; 2618 return;
2639 } 2619 }
2640 2620
2641 audio_begin_track_change(false, trackstat); 2621 audio_begin_track_change(TRACK_CHANGE_MANUAL, trackstat);
2642} 2622}
2643 2623
2644/* Enter seek mode in order to start a seek 2624/* Enter seek mode in order to start a seek
@@ -2689,11 +2669,6 @@ static void audio_on_ff_rewind(long time)
2689 if (time == 0) 2669 if (time == 0)
2690 send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); 2670 send_event(PLAYBACK_EVENT_TRACK_FINISH, id3);
2691 2671
2692 /* Prevent user codec time update - coerce to something that is
2693 innocuous concerning lookaheads */
2694 if (pending == TRACK_SKIP_NONE)
2695 skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST;
2696
2697 id3->elapsed = time; 2672 id3->elapsed = time;
2698 queue_reply(&audio_queue, 1); 2673 queue_reply(&audio_queue, 1);
2699 2674
@@ -2703,6 +2678,9 @@ static void audio_on_ff_rewind(long time)
2703 halt that will reset it */ 2678 halt that will reset it */
2704 codec_seeking = true; 2679 codec_seeking = true;
2705 2680
2681 /* If in transition, key will have changed - sync to it */
2682 position_key = pcmbuf_get_position_key();
2683
2706 if (pending == TRACK_SKIP_AUTO) 2684 if (pending == TRACK_SKIP_AUTO)
2707 { 2685 {
2708 if (!track_list_advance_current(-1)) 2686 if (!track_list_advance_current(-1))
@@ -3124,75 +3102,66 @@ static void buffer_event_finished_callback(void *data)
3124 3102
3125/** -- Codec callbacks -- **/ 3103/** -- Codec callbacks -- **/
3126 3104
3127/* Update elapsed times with latency-adjusted values */ 3105/* Update elapsed time for next PCM insert */
3128void audio_codec_update_elapsed(unsigned long value) 3106void audio_codec_update_elapsed(unsigned long elapsed)
3129{ 3107{
3130#ifdef AB_REPEAT_ENABLE 3108#ifdef AB_REPEAT_ENABLE
3131 ab_position_report(value); 3109 ab_position_report(elapsed);
3132#endif 3110#endif
3133 3111 /* Save in codec's id3 where it is used at next pcm insert */
3134 unsigned long latency = pcmbuf_get_latency(); 3112 id3_get(CODEC_ID3)->elapsed = elapsed;
3135
3136 if (LIKELY(value >= latency))
3137 {
3138 unsigned long elapsed = value - latency;
3139
3140 if (elapsed > value || elapsed < value - 2)
3141 value = elapsed;
3142 }
3143 else
3144 {
3145 value = 0;
3146 }
3147
3148 /* Track codec: used later when updating the playing at the user
3149 transition */
3150 id3_get(CODEC_ID3)->elapsed = value;
3151
3152 /* If a skip is pending, the PCM buffer is updating the time on the
3153 previous song */
3154 if (LIKELY(skip_pending == TRACK_SKIP_NONE))
3155 id3_get(PLAYING_ID3)->elapsed = value;
3156} 3113}
3157 3114
3158/* Update offsets with latency-adjusted values */ 3115/* Update offset for next PCM insert */
3159void audio_codec_update_offset(size_t value) 3116void audio_codec_update_offset(size_t offset)
3160{ 3117{
3161 struct mp3entry *ci_id3 = id3_get(CODEC_ID3); 3118 /* Save in codec's id3 where it is used at next pcm insert */
3162 unsigned long latency = pcmbuf_get_latency() * ci_id3->bitrate / 8; 3119 id3_get(CODEC_ID3)->offset = offset;
3120}
3163 3121
3164 if (LIKELY(value >= latency)) 3122/* Codec has finished running */
3165 { 3123void audio_codec_complete(int status)
3166 value -= latency; 3124{
3167 } 3125#ifdef AB_REPEAT_ENABLE
3168 else 3126 if (status >= CODEC_OK)
3169 { 3127 {
3170 value = 0; 3128 /* Normal automatic skip */
3129 ab_end_of_track_report();
3171 } 3130 }
3131#endif
3172 3132
3173 /* Track codec: used later when updating the playing id3 at the user 3133 LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status);
3174 transition */ 3134 audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
3175 ci_id3->offset = value; 3135}
3176 3136
3177 /* If a skip is pending, the PCM buffer is updating the time on the 3137/* Codec has finished seeking */
3178 previous song */ 3138void audio_codec_seek_complete(void)
3179 if (LIKELY(skip_pending == TRACK_SKIP_NONE)) 3139{
3180 id3_get(PLAYING_ID3)->offset = value; 3140 LOGFQUEUE("codec > audio Q_AUDIO_CODEC_SEEK_COMPLETE");
3141 audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
3181} 3142}
3182 3143
3183 3144
3184/** --- Pcmbuf callbacks --- **/ 3145/** --- Pcmbuf callbacks --- **/
3185 3146
3186/* Between the codec and PCM track change, we need to keep updating the 3147/* Update the elapsed and offset from the information cached during the
3187 * "elapsed" value of the previous (to the codec, but current to the 3148 PCM buffer insert */
3188 * user/PCM/WPS) track, so that the progressbar reaches the end. */ 3149void audio_pcmbuf_position_callback(unsigned long elapsed, off_t offset,
3189void audio_pcmbuf_position_callback(unsigned int time) 3150 unsigned int key)
3190{ 3151{
3191 struct mp3entry *id3 = id3_get(PLAYING_ID3); 3152 if (key == position_key)
3192 3153 {
3193 time += id3->elapsed; 3154 struct mp3entry *id3 = id3_get(PLAYING_ID3);
3155 id3->elapsed = elapsed;
3156 id3->offset = offset;
3157 }
3158}
3194 3159
3195 id3->elapsed = MIN(time, id3->length); 3160/* Synchronize position info to the codec's */
3161void audio_pcmbuf_sync_position(void)
3162{
3163 audio_pcmbuf_position_callback(ci.id3->elapsed, ci.id3->offset,
3164 pcmbuf_get_position_key());
3196} 3165}
3197 3166
3198/* Post message from pcmbuf that the end of the previous track has just 3167/* Post message from pcmbuf that the end of the previous track has just
diff --git a/firmware/export/system.h b/firmware/export/system.h
index 5128801725..1f8ed033fd 100644
--- a/firmware/export/system.h
+++ b/firmware/export/system.h
@@ -378,12 +378,16 @@ static inline void cpucache_flush(void)
378#if defined(CPU_ARM) 378#if defined(CPU_ARM)
379 /* Use ARMs cache alignment. */ 379 /* Use ARMs cache alignment. */
380 #define MEM_ALIGN_ATTR CACHEALIGN_ATTR 380 #define MEM_ALIGN_ATTR CACHEALIGN_ATTR
381 #define MEM_ALIGN_SIZE CACHEALIGN_SIZE
381#elif defined(CPU_COLDFIRE) 382#elif defined(CPU_COLDFIRE)
382 /* Use fixed alignment of 16 bytes. Speed up only for 'movem' in DRAM. */ 383 /* Use fixed alignment of 16 bytes. Speed up only for 'movem' in DRAM. */
383 #define MEM_ALIGN_ATTR __attribute__((aligned(16))) 384 #define MEM_ALIGN_ATTR __attribute__((aligned(16)))
385 #define MEM_ALIGN_SIZE 16
384#else 386#else
385 /* Do nothing. */ 387 /* Do nothing. */
386 #define MEM_ALIGN_ATTR 388 #define MEM_ALIGN_ATTR
389 /* Align pointer size */
390 #define MEM_ALIGN_SIZE sizeof(intptr_t)
387#endif 391#endif
388 392
389#ifdef STORAGE_WANTS_ALIGN 393#ifdef STORAGE_WANTS_ALIGN