diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/codec_thread.c | 18 | ||||
-rw-r--r-- | apps/codecs/a52.c | 2 | ||||
-rw-r--r-- | apps/codecs/a52_rm.c | 1 | ||||
-rw-r--r-- | apps/codecs/aac.c | 5 | ||||
-rw-r--r-- | apps/codecs/adx.c | 7 | ||||
-rw-r--r-- | apps/codecs/aiff.c | 2 | ||||
-rw-r--r-- | apps/codecs/alac.c | 2 | ||||
-rw-r--r-- | apps/codecs/ape.c | 3 | ||||
-rw-r--r-- | apps/codecs/au.c | 2 | ||||
-rw-r--r-- | apps/codecs/cook.c | 4 | ||||
-rw-r--r-- | apps/codecs/flac.c | 4 | ||||
-rw-r--r-- | apps/codecs/mod.c | 3 | ||||
-rw-r--r-- | apps/codecs/mpa.c | 12 | ||||
-rw-r--r-- | apps/codecs/mpc.c | 4 | ||||
-rw-r--r-- | apps/codecs/shorten.c | 3 | ||||
-rw-r--r-- | apps/codecs/sid.c | 2 | ||||
-rw-r--r-- | apps/codecs/smaf.c | 2 | ||||
-rw-r--r-- | apps/codecs/spc.c | 2 | ||||
-rw-r--r-- | apps/codecs/speex.c | 1 | ||||
-rw-r--r-- | apps/codecs/tta.c | 2 | ||||
-rw-r--r-- | apps/codecs/vorbis.c | 3 | ||||
-rw-r--r-- | apps/codecs/vox.c | 2 | ||||
-rw-r--r-- | apps/codecs/wav.c | 2 | ||||
-rw-r--r-- | apps/codecs/wav64.c | 2 | ||||
-rw-r--r-- | apps/codecs/wavpack.c | 2 | ||||
-rw-r--r-- | apps/codecs/wma.c | 3 | ||||
-rw-r--r-- | apps/codecs/wmapro.c | 1 | ||||
-rw-r--r-- | apps/codecs/wmavoice.c | 2 | ||||
-rw-r--r-- | apps/pcmbuf.c | 1415 | ||||
-rw-r--r-- | apps/pcmbuf.h | 29 | ||||
-rw-r--r-- | apps/playback.c | 211 |
31 files changed, 944 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 | |||
77 | static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */ | 77 | static 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 */ |
80 | extern void audio_codec_update_elapsed(unsigned long value); | 80 | extern void audio_codec_update_elapsed(unsigned long elapsed); |
81 | extern void audio_codec_update_offset(size_t value); | 81 | extern void audio_codec_update_offset(size_t offset); |
82 | extern void audio_queue_post(long id, intptr_t data); | 82 | extern void audio_codec_complete(int status); |
83 | extern void audio_codec_seek_complete(void); | ||
83 | extern struct codec_api ci; /* from codecs.c */ | 84 | extern 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(¶m); | 104 | enum codec_command_action action = ci->get_command(¶m); |
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 | ||
101 | seek_start: | 101 | seek_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(¶m); | 97 | enum codec_command_action action = ci->get_command(¶m); |
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. */ | ||
66 | struct chunkdesc | 72 | struct 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 | |||
84 | static unsigned char *pcmbuf_buffer; | ||
85 | static unsigned char *pcmbuf_guardbuf; | ||
86 | static size_t pcmbuf_size; | ||
87 | static struct chunkdesc *pcmbuf_descriptors; | ||
88 | static unsigned int pcmbuf_desc_count; | ||
89 | static unsigned int position_key = 1; | ||
90 | |||
91 | static size_t chunk_ridx; | ||
92 | static size_t chunk_widx; | ||
93 | |||
94 | static size_t pcmbuf_bytes_waiting; | ||
77 | 95 | ||
78 | /* Size of the PCM buffer. */ | 96 | static size_t pcmbuf_watermark; |
79 | static size_t pcmbuf_size = 0; | 97 | static struct chunkdesc *current_desc; |
80 | static char *pcmbuf_bufend; | ||
81 | static char *pcmbuffer; | ||
82 | /* Current PCM buffer write index. */ | ||
83 | static size_t pcmbuffer_pos; | ||
84 | /* Amount pcmbuffer_pos will be increased.*/ | ||
85 | static size_t pcmbuffer_fillpos; | ||
86 | 98 | ||
87 | static struct chunkdesc *first_desc; | 99 | static bool low_latency_mode = false; |
88 | 100 | ||
89 | /* Gapless playback */ | 101 | static bool pcmbuf_sync_position = false; |
90 | static bool track_transition; | ||
91 | 102 | ||
92 | /* Fade effect */ | 103 | /* Fade effect */ |
93 | static unsigned int fade_vol = MIX_AMP_UNITY; | 104 | static 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 */ |
107 | static char *fadebuf; | 118 | static unsigned char *crossfade_buffer; |
108 | 119 | ||
109 | /* Crossfade related state */ | 120 | /* Crossfade related state */ |
110 | static bool crossfade_enabled; | 121 | static int crossfade_setting; |
111 | static bool crossfade_enable_request; | 122 | static int crossfade_enable_request; |
112 | static bool crossfade_mixmode; | 123 | static bool crossfade_mixmode; |
113 | static bool crossfade_auto_skip; | 124 | static bool crossfade_auto_skip; |
114 | static bool crossfade_active; | 125 | |
115 | static bool crossfade_track_change_started; | 126 | static 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 */ |
118 | static struct chunkdesc *crossfade_chunk; | 134 | static size_t crossfade_index; |
119 | static size_t crossfade_sample; | ||
120 | 135 | ||
121 | /* Counters for fading in new data */ | 136 | /* Counters for fading in new data */ |
122 | static size_t crossfade_fade_in_total; | 137 | static size_t crossfade_fade_in_total; |
123 | static size_t crossfade_fade_in_rem; | 138 | static size_t crossfade_fade_in_rem; |
124 | #endif | ||
125 | 139 | ||
126 | static struct chunkdesc *read_chunk; | 140 | /* Defines for operations on position info when mixing/fading - |
127 | static struct chunkdesc *read_end_chunk; | 141 | passed in offset parameter */ |
128 | static struct chunkdesc *write_chunk; | 142 | enum |
129 | static struct chunkdesc *write_end_chunk; | 143 | { |
130 | static 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 */ | |
132 | static size_t pcmbuf_unplayed_bytes; | 146 | /* Positive values cause stamping/restamping */ |
133 | static size_t pcmbuf_watermark; | 147 | }; |
134 | 148 | ||
135 | static bool low_latency_mode = false; | 149 | static void crossfade_start(void); |
136 | static bool flush_pcmbuf = false; | 150 | static void write_to_crossfade(size_t size, unsigned long elapsed, |
151 | off_t offset); | ||
152 | static void pcmbuf_finish_crossfade_enable(void); | ||
153 | #endif /* HAVE_CROSSFADE */ | ||
137 | 154 | ||
155 | /* Thread */ | ||
138 | #ifdef HAVE_PRIORITY_SCHEDULING | 156 | #ifdef HAVE_PRIORITY_SCHEDULING |
139 | static int codec_thread_priority = PRIORITY_PLAYBACK; | 157 | static 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 | ||
150 | static void crossfade_start(void); | ||
151 | static void write_to_crossfade(size_t length); | ||
152 | static void pcmbuf_finish_crossfade_enable(void); | ||
153 | #endif | ||
154 | 163 | ||
155 | /* Callbacks into playback.c */ | 164 | /* Callbacks into playback.c */ |
156 | extern void audio_pcmbuf_position_callback(unsigned int time); | 165 | extern void audio_pcmbuf_position_callback(unsigned long elapsed, |
166 | off_t offset, unsigned int key); | ||
157 | extern void audio_pcmbuf_track_change(bool pcmbuf); | 167 | extern void audio_pcmbuf_track_change(bool pcmbuf); |
158 | extern bool audio_pcmbuf_may_play(void); | 168 | extern bool audio_pcmbuf_may_play(void); |
169 | extern 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) */ |
176 | static 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 */ | ||
188 | static 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)) | 199 | static 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 | |||
182 | static 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); | 205 | static 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++) | 211 | static 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 */ | ||
218 | static 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 |
211 | static void commit_chunk(bool flush_next_time) | 238 | data is below the threshold */ |
239 | static 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; | 271 | static 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) | 278 | static 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) */ |
291 | static void boost_codec_thread(int pcm_fill_state) | 297 | static 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. */ | 331 | static void * get_write_buffer(size_t *size) |
324 | static 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 */ | ||
349 | static 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 */ | ||
361 | void * 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 */ | ||
377 | void *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 */ |
420 | void pcmbuf_write_complete(int count) | 440 | void 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 | 461 | static unsigned int get_next_required_pcmbuf_chunks(void) | |
437 | static 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 | |||
451 | static 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; | 479 | static 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| */ |
471 | size_t pcmbuf_init(unsigned char *bufend) | 493 | size_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 */ |
503 | void 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 */ | ||
536 | static 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 */ | ||
551 | static 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 */ | ||
573 | void 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 | ||
533 | bool pcmbuf_start_track_change(bool auto_skip) | 585 | void 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 | 693 | static 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. */ | ||
636 | static 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 */ |
700 | void pcmbuf_play_start(void) | 727 | void 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 */ | ||
713 | void pcmbuf_play_stop(void) | 741 | void 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 |
754 | static inline int32_t clip_sample_16(int32_t sample) | 777 | /* Find the buffer index that's 'size' bytes away from 'index' */ |
778 | static 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 | 801 | static 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. */ | ||
764 | static 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 */ | ||
830 | static 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 */ |
775 | static size_t crossfade_mix_fade(int factor, size_t length, const char *buf, | 838 | static 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. */ |
826 | static void crossfade_start(void) | 928 | static 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 */ |
930 | static void write_to_crossfade(size_t length) | 1046 | static 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(©_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 | ||
1000 | static void pcmbuf_finish_crossfade_enable(void) | 1127 | static 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 | ||
1012 | bool pcmbuf_is_crossfade_active(void) | 1139 | bool 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 | ||
1017 | void pcmbuf_request_crossfade_enable(bool on_off) | 1144 | void 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 | ||
1023 | bool pcmbuf_is_same_size(void) | 1150 | bool 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 */ |
1041 | size_t pcmbuf_free(void) | 1168 | size_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 */ | ||
1055 | size_t pcmbuf_get_bufsize(void) | 1174 | size_t pcmbuf_get_bufsize(void) |
1056 | { | 1175 | { |
1057 | return pcmbuf_size; | 1176 | return pcmbuf_size; |
1058 | } | 1177 | } |
1059 | 1178 | ||
1179 | /* Number of committed descriptors */ | ||
1060 | int pcmbuf_used_descs(void) | 1180 | int 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 */ | ||
1071 | int pcmbuf_descs(void) | 1186 | int pcmbuf_descs(void) |
1072 | { | 1187 | { |
1073 | return NUM_CHUNK_DESCS(pcmbuf_size); | 1188 | return pcmbuf_desc_count; |
1074 | } | ||
1075 | |||
1076 | #ifdef ROCKBOX_HAS_LOGF | ||
1077 | unsigned 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 | ||
1178 | bool pcmbuf_is_lowdata(void) | 1284 | bool 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 | ||
1201 | unsigned long pcmbuf_get_latency(void) | 1303 | /* Return the current position key value */ |
1304 | unsigned 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 */ | ||
1311 | void 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 */ |
25 | void *pcmbuf_request_buffer(int *count); | 27 | void *pcmbuf_request_buffer(int *count); |
26 | void pcmbuf_write_complete(int count); | 28 | void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset); |
27 | 29 | ||
28 | /* Init */ | 30 | /* Init */ |
29 | size_t pcmbuf_init(unsigned char *bufend); | 31 | size_t pcmbuf_init(unsigned char *bufend); |
@@ -33,20 +35,30 @@ void pcmbuf_play_start(void); | |||
33 | void pcmbuf_play_stop(void); | 35 | void pcmbuf_play_stop(void); |
34 | void pcmbuf_pause(bool pause); | 36 | void pcmbuf_pause(bool pause); |
35 | void pcmbuf_monitor_track_change(bool monitor); | 37 | void pcmbuf_monitor_track_change(bool monitor); |
36 | bool pcmbuf_start_track_change(bool manual_skip); | 38 | void pcmbuf_sync_position_update(void); |
39 | |||
40 | /* Track change origin type */ | ||
41 | enum 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 | }; | ||
48 | void pcmbuf_start_track_change(enum pcm_track_change_type type); | ||
37 | 49 | ||
38 | /* Crossfade */ | 50 | /* Crossfade */ |
39 | #ifdef HAVE_CROSSFADE | 51 | #ifdef HAVE_CROSSFADE |
40 | bool pcmbuf_is_crossfade_active(void); | 52 | bool pcmbuf_is_crossfade_active(void); |
41 | void pcmbuf_request_crossfade_enable(bool on_off); | 53 | void pcmbuf_request_crossfade_enable(int setting); |
42 | bool pcmbuf_is_same_size(void); | 54 | bool pcmbuf_is_same_size(void); |
43 | #else | 55 | #else |
44 | /* Dummy functions with sensible returns */ | 56 | /* Dummy functions with sensible returns */ |
45 | static inline bool pcmbuf_is_crossfade_active(void) | 57 | static FORCE_INLINE bool pcmbuf_is_crossfade_active(void) |
46 | { return false; } | 58 | { return false; } |
47 | static inline void pcmbuf_request_crossfade_enable(bool on_off) | 59 | static FORCE_INLINE void pcmbuf_request_crossfade_enable(bool on_off) |
48 | { return; (void)on_off; } | 60 | { return; (void)on_off; } |
49 | static inline bool pcmbuf_is_same_size(void) | 61 | static 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); | |||
59 | size_t pcmbuf_get_bufsize(void); | 71 | size_t pcmbuf_get_bufsize(void); |
60 | int pcmbuf_descs(void); | 72 | int pcmbuf_descs(void); |
61 | int pcmbuf_used_descs(void); | 73 | int pcmbuf_used_descs(void); |
62 | #ifdef ROCKBOX_HAS_LOGF | 74 | unsigned int pcmbuf_get_position_key(void); |
63 | unsigned char *pcmbuf_get_meminfo(size_t *length); | ||
64 | #endif | ||
65 | 75 | ||
66 | /* Misc */ | 76 | /* Misc */ |
67 | void pcmbuf_fade(bool fade, bool in); | 77 | void pcmbuf_fade(bool fade, bool in); |
@@ -69,6 +79,5 @@ bool pcmbuf_fading(void); | |||
69 | void pcmbuf_soft_mode(bool shhh); | 79 | void pcmbuf_soft_mode(bool shhh); |
70 | bool pcmbuf_is_lowdata(void); | 80 | bool pcmbuf_is_lowdata(void); |
71 | void pcmbuf_set_low_latency(bool state); | 81 | void pcmbuf_set_low_latency(bool state); |
72 | unsigned 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 | |||
330 | static bool codec_skip_pending = false; | 330 | static bool codec_skip_pending = false; |
331 | static int codec_skip_status; | 331 | static int codec_skip_status; |
332 | static bool codec_seeking = false; /* Codec seeking ack expected? */ | 332 | static bool codec_seeking = false; /* Codec seeking ack expected? */ |
333 | 333 | static unsigned int position_key = 0; | |
334 | 334 | ||
335 | /* Event queues */ | 335 | /* Event queues */ |
336 | static struct event_queue audio_queue SHAREDBSS_ATTR; | 336 | static struct event_queue audio_queue SHAREDBSS_ATTR; |
@@ -353,14 +353,13 @@ static void audio_stop_playback(void); | |||
353 | static void buffer_event_buffer_low_callback(void *data); | 353 | static void buffer_event_buffer_low_callback(void *data); |
354 | static void buffer_event_rebuffer_callback(void *data); | 354 | static void buffer_event_rebuffer_callback(void *data); |
355 | static void buffer_event_finished_callback(void *data); | 355 | static void buffer_event_finished_callback(void *data); |
356 | void audio_pcmbuf_sync_position(void); | ||
356 | 357 | ||
357 | 358 | ||
358 | /**************************************/ | 359 | /**************************************/ |
359 | 360 | ||
360 | /** --- audio_queue helpers --- **/ | 361 | /** --- audio_queue helpers --- **/ |
361 | 362 | static void audio_queue_post(long id, intptr_t data) | |
362 | /* codec thread needs access */ | ||
363 | void 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 */ |
979 | static void audio_playlist_track_finish(void) | 974 | static 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 */ |
1017 | static void playing_id3_sync(struct track_info *user_info, size_t offset) | 1015 | static 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 */ | ||
1097 | static 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 */ |
1104 | static void audio_wait_fade_complete(void) | 1097 | static 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 */ |
2066 | static void audio_finalise_track_change(bool delayed) | 2060 | static 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 */ | 2130 | static void audio_begin_track_change(enum pcm_track_change_type type, |
2146 | static 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) | |||
2316 | static void audio_on_track_changed(void) | 2293 | static 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 */ |
3128 | void audio_codec_update_elapsed(unsigned long value) | 3106 | void 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 */ |
3159 | void audio_codec_update_offset(size_t value) | 3116 | void 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 | { | 3123 | void 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 */ | 3138 | void 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. */ | 3149 | void audio_pcmbuf_position_callback(unsigned long elapsed, off_t offset, |
3189 | void 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 */ |
3161 | void 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 |