diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2016-12-28 00:06:39 -0500 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2017-01-05 02:37:14 -0500 |
commit | 6c837394ca6d7a988bfbdc9e97630e69f0bc2025 (patch) | |
tree | b079e9ed146f86c164b9fffe89ee8d24e6d4fd2e /apps/pcmbuf.c | |
parent | dbee727664a69b82ce582e252501ec47e5533596 (diff) | |
download | rockbox-6c837394ca6d7a988bfbdc9e97630e69f0bc2025.tar.gz rockbox-6c837394ca6d7a988bfbdc9e97630e69f0bc2025.zip |
Playback: Fix problems with crossfade on short tracks.
Addresses issues brought up in this thread:
http://forums.rockbox.org/index.php/topic,51605.0.html
While we're at it, improve the quality with a sample-level fader.
Change-Id: I73dde60d6858a1c9042812e26d490739e3906a1e
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r-- | apps/pcmbuf.c | 646 |
1 files changed, 359 insertions, 287 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index ff9b3e16a2..8c1ca06c2b 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c | |||
@@ -41,6 +41,9 @@ | |||
41 | #include "audio.h" | 41 | #include "audio.h" |
42 | #include "voice_thread.h" | 42 | #include "voice_thread.h" |
43 | 43 | ||
44 | /* 2 channels * 2 bytes/sample, interleaved */ | ||
45 | #define PCMBUF_SAMPLE_SIZE (2 * 2) | ||
46 | |||
44 | /* This is the target fill size of chunks on the pcm buffer | 47 | /* This is the target fill size of chunks on the pcm buffer |
45 | Can be any number of samples but power of two sizes make for faster and | 48 | Can be any number of samples but power of two sizes make for faster and |
46 | smaller math - must be < 65536 bytes */ | 49 | smaller math - must be < 65536 bytes */ |
@@ -67,9 +70,8 @@ | |||
67 | /* Return data level in 1/4-second increments */ | 70 | /* Return data level in 1/4-second increments */ |
68 | #define DATA_LEVEL(quarter_secs) (pcmbuf_sampr * (quarter_secs)) | 71 | #define DATA_LEVEL(quarter_secs) (pcmbuf_sampr * (quarter_secs)) |
69 | 72 | ||
70 | /* Number of bytes played per second: | 73 | /* Number of bytes played per second */ |
71 | (sample rate * 2 channels * 2 bytes/sample) */ | 74 | #define BYTERATE (pcmbuf_sampr * PCMBUF_SAMPLE_SIZE) |
72 | #define BYTERATE (pcmbuf_sampr * 2 * 2) | ||
73 | 75 | ||
74 | #if MEMORYSIZE > 2 | 76 | #if MEMORYSIZE > 2 |
75 | /* Keep watermark high for large memory target - at least (2s) */ | 77 | /* Keep watermark high for large memory target - at least (2s) */ |
@@ -88,12 +90,15 @@ | |||
88 | struct chunkdesc | 90 | struct chunkdesc |
89 | { | 91 | { |
90 | uint16_t size; /* Actual size (0 < size <= PCMBUF_CHUNK_SIZE) */ | 92 | uint16_t size; /* Actual size (0 < size <= PCMBUF_CHUNK_SIZE) */ |
91 | uint8_t is_end; /* Flag indicating end of track */ | 93 | uint16_t pos_key; /* Who put the position info in |
92 | uint8_t pos_key; /* Who put the position info in (0 = undefined) */ | 94 | (undefined: 0, valid: 1..POSITION_KEY_MAX) */ |
93 | unsigned long elapsed; /* Elapsed time to use */ | 95 | unsigned long elapsed; /* Elapsed time to use */ |
94 | off_t offset; /* Offset to use */ | 96 | off_t offset; /* Offset to use */ |
95 | }; | 97 | }; |
96 | 98 | ||
99 | #define POSITION_KEY_MAX UINT16_MAX | ||
100 | |||
101 | |||
97 | /* General PCM buffer data */ | 102 | /* General PCM buffer data */ |
98 | #define INVALID_BUF_INDEX ((size_t)0 - (size_t)1) | 103 | #define INVALID_BUF_INDEX ((size_t)0 - (size_t)1) |
99 | 104 | ||
@@ -110,6 +115,7 @@ static size_t chunk_widx; | |||
110 | 115 | ||
111 | static size_t pcmbuf_bytes_waiting; | 116 | static size_t pcmbuf_bytes_waiting; |
112 | static struct chunkdesc *current_desc; | 117 | static struct chunkdesc *current_desc; |
118 | static size_t chunk_transidx; | ||
113 | 119 | ||
114 | static size_t pcmbuf_watermark = 0; | 120 | static size_t pcmbuf_watermark = 0; |
115 | 121 | ||
@@ -131,28 +137,36 @@ static bool fade_out_complete = false; | |||
131 | static bool soft_mode = false; | 137 | static bool soft_mode = false; |
132 | 138 | ||
133 | #ifdef HAVE_CROSSFADE | 139 | #ifdef HAVE_CROSSFADE |
134 | /* Crossfade buffer */ | ||
135 | static void *crossfade_buffer; | ||
136 | |||
137 | /* Crossfade related state */ | 140 | /* Crossfade related state */ |
141 | |||
138 | static int crossfade_setting; | 142 | static int crossfade_setting; |
139 | static int crossfade_enable_request; | 143 | static int crossfade_enable_request; |
140 | static bool crossfade_mixmode; | ||
141 | static bool crossfade_auto_skip; | ||
142 | 144 | ||
143 | static enum | 145 | static enum |
144 | { | 146 | { |
145 | CROSSFADE_INACTIVE = 0, | 147 | CROSSFADE_INACTIVE = 0, /* Crossfade is OFF */ |
146 | CROSSFADE_TRACK_CHANGE_STARTED, | 148 | CROSSFADE_ACTIVE, /* Crossfade is fading in */ |
147 | CROSSFADE_ACTIVE, | 149 | CROSSFADE_START, /* New crossfade is starting */ |
150 | CROSSFADE_CONTINUE, /* Next track continues fade */ | ||
148 | } crossfade_status = CROSSFADE_INACTIVE; | 151 | } crossfade_status = CROSSFADE_INACTIVE; |
149 | 152 | ||
150 | /* Track the current location for processing crossfade */ | 153 | static bool crossfade_mixmode; |
151 | static size_t crossfade_index; | 154 | static bool crossfade_auto_skip; |
155 | static size_t crossfade_widx; | ||
156 | static size_t crossfade_bufidx; | ||
152 | 157 | ||
153 | /* Counters for fading in new data */ | 158 | struct mixfader |
154 | static size_t crossfade_fade_in_total; | 159 | { |
155 | static size_t crossfade_fade_in_rem; | 160 | int32_t factor; /* Current volume factor to use */ |
161 | int32_t endfac; /* Saturating end factor */ | ||
162 | int32_t nsamp2; /* Twice the number of samples */ | ||
163 | int32_t dfact2; /* Twice the range of factors */ | ||
164 | int32_t ferr; /* Current error accumulator */ | ||
165 | int32_t dfquo; /* Quotient of fade range / sample range */ | ||
166 | int32_t dfrem; /* Remainder of fade range / sample range */ | ||
167 | int32_t dfinc; /* Base increment (-1 or +1) */ | ||
168 | bool alloc; /* Allocate blocks if needed else abort at EOB */ | ||
169 | } crossfade_infader; | ||
156 | 170 | ||
157 | /* Defines for operations on position info when mixing/fading - | 171 | /* Defines for operations on position info when mixing/fading - |
158 | passed in offset parameter */ | 172 | passed in offset parameter */ |
@@ -163,10 +177,16 @@ enum | |||
163 | /* Positive values cause stamping/restamping */ | 177 | /* Positive values cause stamping/restamping */ |
164 | }; | 178 | }; |
165 | 179 | ||
180 | #define MIXFADE_UNITY_BITS 16 | ||
181 | #define MIXFADE_UNITY (1 << MIXFADE_UNITY_BITS) | ||
182 | |||
183 | static void crossfade_cancel(void); | ||
166 | static void crossfade_start(void); | 184 | static void crossfade_start(void); |
167 | static void write_to_crossfade(size_t size, unsigned long elapsed, | 185 | static void write_to_crossfade(size_t size, unsigned long elapsed, |
168 | off_t offset); | 186 | off_t offset); |
169 | static void pcmbuf_finish_crossfade_enable(void); | 187 | static void pcmbuf_finish_crossfade_enable(void); |
188 | #else | ||
189 | #define crossfade_cancel() do {} while(0) | ||
170 | #endif /* HAVE_CROSSFADE */ | 190 | #endif /* HAVE_CROSSFADE */ |
171 | 191 | ||
172 | /* Thread */ | 192 | /* Thread */ |
@@ -232,9 +252,9 @@ static struct chunkdesc * index_chunkdesc(size_t index) | |||
232 | return &pcmbuf_descriptors[index / PCMBUF_CHUNK_SIZE]; | 252 | return &pcmbuf_descriptors[index / PCMBUF_CHUNK_SIZE]; |
233 | } | 253 | } |
234 | 254 | ||
235 | /* Return a chunk descriptor for a byte index in the buffer, offset by 'offset' | 255 | /* Return the first byte of a chunk for a byte index in the buffer, offset by 'offset' |
236 | chunks */ | 256 | chunks */ |
237 | static struct chunkdesc * index_chunkdesc_offs(size_t index, int offset) | 257 | static size_t index_chunk_offs(size_t index, int offset) |
238 | { | 258 | { |
239 | int i = index / PCMBUF_CHUNK_SIZE; | 259 | int i = index / PCMBUF_CHUNK_SIZE; |
240 | 260 | ||
@@ -247,7 +267,53 @@ static struct chunkdesc * index_chunkdesc_offs(size_t index, int offset) | |||
247 | i += pcmbuf_desc_count; | 267 | i += pcmbuf_desc_count; |
248 | } | 268 | } |
249 | 269 | ||
250 | return &pcmbuf_descriptors[i]; | 270 | return i * PCMBUF_CHUNK_SIZE; |
271 | } | ||
272 | |||
273 | /* Test if a buffer index lies within the committed data region */ | ||
274 | static bool index_committed(size_t index) | ||
275 | { | ||
276 | if (index == INVALID_BUF_INDEX) | ||
277 | return false; | ||
278 | |||
279 | size_t ridx = chunk_ridx; | ||
280 | size_t widx = chunk_widx; | ||
281 | |||
282 | if (widx < ridx) | ||
283 | { | ||
284 | widx += pcmbuf_size; | ||
285 | |||
286 | if (index < ridx) | ||
287 | index += pcmbuf_size; | ||
288 | } | ||
289 | |||
290 | return index >= ridx && index < widx; | ||
291 | } | ||
292 | |||
293 | /* Snip the tail of buffer at chunk of specified index plus chunk offset */ | ||
294 | void snip_buffer_tail(size_t index, int offset) | ||
295 | { | ||
296 | /* Call with PCM lockout */ | ||
297 | if (index == INVALID_BUF_INDEX) | ||
298 | return; | ||
299 | |||
300 | index = index_chunk_offs(index, offset); | ||
301 | |||
302 | if (!index_committed(index) && index != chunk_widx) | ||
303 | return; | ||
304 | |||
305 | chunk_widx = index; | ||
306 | pcmbuf_bytes_waiting = 0; | ||
307 | index_chunkdesc(index)->pos_key = 0; | ||
308 | |||
309 | #ifdef HAVE_CROSSFADE | ||
310 | /* Kill crossfade if it would now be operating in the void */ | ||
311 | if (crossfade_status != CROSSFADE_INACTIVE && | ||
312 | !index_committed(crossfade_widx) && crossfade_widx != chunk_widx) | ||
313 | { | ||
314 | crossfade_cancel(); | ||
315 | } | ||
316 | #endif /* HAVE_CROSSFADE */ | ||
251 | } | 317 | } |
252 | 318 | ||
253 | 319 | ||
@@ -280,7 +346,6 @@ static void commit_chunks(size_t threshold) | |||
280 | desc = index_chunkdesc(index); | 346 | desc = index_chunkdesc(index); |
281 | 347 | ||
282 | /* Reset it before using it */ | 348 | /* Reset it before using it */ |
283 | desc->is_end = 0; | ||
284 | desc->pos_key = 0; | 349 | desc->pos_key = 0; |
285 | } | 350 | } |
286 | while (pcmbuf_bytes_waiting >= threshold); | 351 | while (pcmbuf_bytes_waiting >= threshold); |
@@ -363,37 +428,32 @@ static void * get_write_buffer(size_t *size) | |||
363 | return index_buffer(index); | 428 | return index_buffer(index); |
364 | } | 429 | } |
365 | 430 | ||
366 | /* Commit outstanding data leaving less than a chunk size remaining and | 431 | /* Commit outstanding data leaving less than a chunk size remaining */ |
367 | write position info to the first chunk */ | 432 | static void commit_write_buffer(size_t size) |
368 | static void commit_write_buffer(size_t size, unsigned long elapsed, off_t offset) | ||
369 | { | 433 | { |
370 | struct chunkdesc *desc = index_chunkdesc(chunk_widx); | ||
371 | stamp_chunk(desc, elapsed, offset); | ||
372 | |||
373 | /* Add this data and commit if one or more chunks are ready */ | 434 | /* Add this data and commit if one or more chunks are ready */ |
374 | pcmbuf_bytes_waiting += size; | 435 | pcmbuf_bytes_waiting += size; |
375 | |||
376 | commit_if_needed(COMMIT_CHUNKS); | 436 | commit_if_needed(COMMIT_CHUNKS); |
377 | } | 437 | } |
378 | 438 | ||
379 | /* Request space in the buffer for writing output samples */ | 439 | /* Request space in the buffer for writing output samples */ |
380 | void * pcmbuf_request_buffer(int *count) | 440 | void * pcmbuf_request_buffer(int *count) |
381 | { | 441 | { |
382 | size_t size = *count * 4; | 442 | size_t size = *count * PCMBUF_SAMPLE_SIZE; |
383 | 443 | ||
384 | #ifdef HAVE_CROSSFADE | 444 | #ifdef HAVE_CROSSFADE |
385 | /* We're going to crossfade to a new track, which is now on its way */ | 445 | /* We're going to crossfade to a new track, which is now on its way */ |
386 | if (crossfade_status == CROSSFADE_TRACK_CHANGE_STARTED) | 446 | if (crossfade_status > CROSSFADE_ACTIVE) |
387 | crossfade_start(); | 447 | crossfade_start(); |
388 | 448 | ||
389 | /* If crossfade has begun, put the new track samples in crossfade_buffer */ | 449 | /* If crossfade has begun, put the new track samples in the crossfade |
450 | buffer area */ | ||
390 | if (crossfade_status != CROSSFADE_INACTIVE && size > CROSSFADE_BUFSIZE) | 451 | if (crossfade_status != CROSSFADE_INACTIVE && size > CROSSFADE_BUFSIZE) |
391 | size = CROSSFADE_BUFSIZE; | 452 | size = CROSSFADE_BUFSIZE; |
392 | else | 453 | else |
393 | #endif /* HAVE_CROSSFADE */ | 454 | #endif /* HAVE_CROSSFADE */ |
394 | |||
395 | if (size > PCMBUF_MAX_BUFFER) | 455 | if (size > PCMBUF_MAX_BUFFER) |
396 | size = PCMBUF_MAX_BUFFER; /* constrain */ | 456 | size = PCMBUF_MAX_BUFFER; /* constrain request */ |
397 | 457 | ||
398 | enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK); | 458 | enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK); |
399 | size_t remaining = pcmbuf_unplayed_bytes(); | 459 | size_t remaining = pcmbuf_unplayed_bytes(); |
@@ -425,12 +485,6 @@ void * pcmbuf_request_buffer(int *count) | |||
425 | trigger_cpu_boost(); | 485 | trigger_cpu_boost(); |
426 | 486 | ||
427 | boost_codec_thread(realrem*10 / pcmbuf_size); | 487 | boost_codec_thread(realrem*10 / pcmbuf_size); |
428 | |||
429 | #ifdef HAVE_CROSSFADE | ||
430 | /* Disable crossfade if < .5s of audio */ | ||
431 | if (remaining < DATA_LEVEL(2)) | ||
432 | crossfade_status = CROSSFADE_INACTIVE; | ||
433 | #endif | ||
434 | } | 488 | } |
435 | else /* !playing */ | 489 | else /* !playing */ |
436 | { | 490 | { |
@@ -447,7 +501,8 @@ void * pcmbuf_request_buffer(int *count) | |||
447 | #ifdef HAVE_CROSSFADE | 501 | #ifdef HAVE_CROSSFADE |
448 | if (crossfade_status != CROSSFADE_INACTIVE) | 502 | if (crossfade_status != CROSSFADE_INACTIVE) |
449 | { | 503 | { |
450 | buf = crossfade_buffer; /* always CROSSFADE_BUFSIZE */ | 504 | crossfade_bufidx = index_chunk_offs(chunk_ridx, -1); |
505 | buf = index_buffer(crossfade_bufidx); /* always CROSSFADE_BUFSIZE */ | ||
451 | } | 506 | } |
452 | else | 507 | else |
453 | #endif | 508 | #endif |
@@ -459,14 +514,14 @@ void * pcmbuf_request_buffer(int *count) | |||
459 | buf = get_write_buffer(&size); | 514 | buf = get_write_buffer(&size); |
460 | } | 515 | } |
461 | 516 | ||
462 | *count = size / 4; | 517 | *count = size / PCMBUF_SAMPLE_SIZE; |
463 | return buf; | 518 | return buf; |
464 | } | 519 | } |
465 | 520 | ||
466 | /* Handle new samples to the buffer */ | 521 | /* Handle new samples to the buffer */ |
467 | void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset) | 522 | void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset) |
468 | { | 523 | { |
469 | size_t size = count * 4; | 524 | size_t size = count * PCMBUF_SAMPLE_SIZE; |
470 | 525 | ||
471 | #ifdef HAVE_CROSSFADE | 526 | #ifdef HAVE_CROSSFADE |
472 | if (crossfade_status != CROSSFADE_INACTIVE) | 527 | if (crossfade_status != CROSSFADE_INACTIVE) |
@@ -476,7 +531,8 @@ void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset) | |||
476 | else | 531 | else |
477 | #endif | 532 | #endif |
478 | { | 533 | { |
479 | commit_write_buffer(size, elapsed, offset); | 534 | stamp_chunk(index_chunkdesc(chunk_widx), elapsed, offset); |
535 | commit_write_buffer(size); | ||
480 | } | 536 | } |
481 | 537 | ||
482 | /* Revert to position updates by PCM */ | 538 | /* Revert to position updates by PCM */ |
@@ -510,13 +566,15 @@ static void init_buffer_state(void) | |||
510 | pcmbuf_bytes_waiting = 0; | 566 | pcmbuf_bytes_waiting = 0; |
511 | 567 | ||
512 | /* Reset first descriptor */ | 568 | /* Reset first descriptor */ |
513 | struct chunkdesc *desc = pcmbuf_descriptors; | 569 | if (pcmbuf_descriptors) |
514 | desc->is_end = 0; | 570 | pcmbuf_descriptors->pos_key = 0; |
515 | desc->pos_key = 0; | 571 | |
572 | /* Clear change notification */ | ||
573 | chunk_transidx = INVALID_BUF_INDEX; | ||
516 | } | 574 | } |
517 | 575 | ||
518 | /* Initialize the PCM buffer. The structure looks like this: | 576 | /* Initialize the PCM buffer. The structure looks like this: |
519 | * ...[|FADEBUF]|---------PCMBUF---------|GUARDBUF|DESCS| */ | 577 | * ...|---------PCMBUF---------|GUARDBUF|DESCS| */ |
520 | size_t pcmbuf_init(void *bufend) | 578 | size_t pcmbuf_init(void *bufend) |
521 | { | 579 | { |
522 | void *bufstart; | 580 | void *bufstart; |
@@ -536,13 +594,6 @@ size_t pcmbuf_init(void *bufend) | |||
536 | bufstart = pcmbuf_buffer; | 594 | bufstart = pcmbuf_buffer; |
537 | 595 | ||
538 | #ifdef HAVE_CROSSFADE | 596 | #ifdef HAVE_CROSSFADE |
539 | /* Allocate FADEBUF if it will be needed */ | ||
540 | if (crossfade_enable_request != CROSSFADE_ENABLE_OFF) | ||
541 | { | ||
542 | bufstart -= CROSSFADE_BUFSIZE; | ||
543 | crossfade_buffer = bufstart; | ||
544 | } | ||
545 | |||
546 | pcmbuf_finish_crossfade_enable(); | 597 | pcmbuf_finish_crossfade_enable(); |
547 | #else | 598 | #else |
548 | pcmbuf_watermark = PCMBUF_WATERMARK; | 599 | pcmbuf_watermark = PCMBUF_WATERMARK; |
@@ -560,33 +611,43 @@ size_t pcmbuf_init(void *bufend) | |||
560 | 611 | ||
561 | /* Place a track change notification in a specific descriptor or post it | 612 | /* Place a track change notification in a specific descriptor or post it |
562 | immediately if the buffer is empty or the index is invalid */ | 613 | immediately if the buffer is empty or the index is invalid */ |
563 | static void pcmbuf_monitor_track_change_ex(size_t index, int offset) | 614 | static void pcmbuf_monitor_track_change_ex(size_t index) |
564 | { | 615 | { |
616 | /* Call with PCM lockout */ | ||
565 | if (chunk_ridx != chunk_widx && index != INVALID_BUF_INDEX) | 617 | if (chunk_ridx != chunk_widx && index != INVALID_BUF_INDEX) |
566 | { | 618 | { |
567 | /* If monitoring, set flag in specified chunk */ | 619 | /* If monitoring, set flag for one previous to specified chunk */ |
568 | index_chunkdesc_offs(index, offset)->is_end = 1; | 620 | index = index_chunk_offs(index, -1); |
569 | } | 621 | |
570 | else | 622 | /* Ensure PCM playback hasn't already played this out */ |
571 | { | 623 | if (index_committed(index)) |
572 | /* Post now if no outstanding buffers exist */ | 624 | { |
573 | audio_pcmbuf_track_change(false); | 625 | chunk_transidx = index; |
626 | return; | ||
627 | } | ||
574 | } | 628 | } |
629 | |||
630 | /* Post now if buffer is no longer coming up */ | ||
631 | chunk_transidx = INVALID_BUF_INDEX; | ||
632 | audio_pcmbuf_track_change(false); | ||
575 | } | 633 | } |
576 | 634 | ||
577 | /* Clear end of track and optionally the positioning info for all data */ | 635 | /* Clear end of track and optionally the positioning info for all data */ |
578 | static void pcmbuf_cancel_track_change(bool position) | 636 | static void pcmbuf_cancel_track_change(bool position) |
579 | { | 637 | { |
638 | /* Call with PCM lockout */ | ||
639 | snip_buffer_tail(chunk_transidx, 1); | ||
640 | |||
641 | chunk_transidx = INVALID_BUF_INDEX; | ||
642 | |||
643 | if (!position) | ||
644 | return; | ||
645 | |||
580 | size_t index = chunk_ridx; | 646 | size_t index = chunk_ridx; |
581 | 647 | ||
582 | while (1) | 648 | while (1) |
583 | { | 649 | { |
584 | struct chunkdesc *desc = index_chunkdesc(index); | 650 | index_chunkdesc(index)->pos_key = 0; |
585 | |||
586 | desc->is_end = 0; | ||
587 | |||
588 | if (position) | ||
589 | desc->pos_key = 0; | ||
590 | 651 | ||
591 | if (index == chunk_widx) | 652 | if (index == chunk_widx) |
592 | break; | 653 | break; |
@@ -602,7 +663,7 @@ void pcmbuf_monitor_track_change(bool monitor) | |||
602 | pcm_play_lock(); | 663 | pcm_play_lock(); |
603 | 664 | ||
604 | if (monitor) | 665 | if (monitor) |
605 | pcmbuf_monitor_track_change_ex(chunk_widx, -1); | 666 | pcmbuf_monitor_track_change_ex(chunk_widx); |
606 | else | 667 | else |
607 | pcmbuf_cancel_track_change(false); | 668 | pcmbuf_cancel_track_change(false); |
608 | 669 | ||
@@ -627,44 +688,45 @@ void pcmbuf_start_track_change(enum pcm_track_change_type type) | |||
627 | 2) Buffers stamped with the outgoing track's positions are restamped | 688 | 2) Buffers stamped with the outgoing track's positions are restamped |
628 | to the incoming track's positions when crossfading | 689 | to the incoming track's positions when crossfading |
629 | */ | 690 | */ |
630 | if (++position_key > UINT8_MAX) | 691 | if (++position_key > POSITION_KEY_MAX) |
631 | position_key = 1; | 692 | position_key = 1; |
632 | 693 | ||
633 | if (type == TRACK_CHANGE_END_OF_DATA) | 694 | if (type == TRACK_CHANGE_END_OF_DATA) |
634 | { | 695 | { |
696 | crossfade_cancel(); | ||
697 | |||
635 | /* If end of all data, force playback */ | 698 | /* If end of all data, force playback */ |
636 | if (audio_pcmbuf_may_play()) | 699 | if (audio_pcmbuf_may_play()) |
637 | pcmbuf_play_start(); | 700 | pcmbuf_play_start(); |
638 | } | 701 | } |
639 | #ifdef HAVE_CROSSFADE | 702 | #ifdef HAVE_CROSSFADE |
640 | /* Determine whether this track change needs to crossfaded and how */ | 703 | /* Determine whether this track change needs to crossfaded and how */ |
641 | else if (crossfade_setting != CROSSFADE_ENABLE_OFF && | 704 | else if (crossfade_setting != CROSSFADE_ENABLE_OFF) |
642 | !pcmbuf_is_crossfade_active() && | ||
643 | pcmbuf_unplayed_bytes() >= DATA_LEVEL(2) && | ||
644 | !low_latency_mode) | ||
645 | { | 705 | { |
646 | switch (crossfade_setting) | 706 | if (crossfade_status == CROSSFADE_INACTIVE && |
707 | pcmbuf_unplayed_bytes() >= DATA_LEVEL(2) && | ||
708 | !low_latency_mode) | ||
647 | { | 709 | { |
648 | case CROSSFADE_ENABLE_AUTOSKIP: | 710 | switch (crossfade_setting) |
649 | crossfade = auto_skip; | 711 | { |
650 | break; | 712 | case CROSSFADE_ENABLE_AUTOSKIP: |
651 | case CROSSFADE_ENABLE_MANSKIP: | 713 | crossfade = auto_skip; |
652 | crossfade = !auto_skip; | 714 | break; |
653 | break; | 715 | case CROSSFADE_ENABLE_MANSKIP: |
654 | case CROSSFADE_ENABLE_SHUFFLE: | 716 | crossfade = !auto_skip; |
655 | crossfade = global_settings.playlist_shuffle; | 717 | break; |
656 | break; | 718 | case CROSSFADE_ENABLE_SHUFFLE: |
657 | case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP: | 719 | crossfade = global_settings.playlist_shuffle; |
658 | crossfade = global_settings.playlist_shuffle || !auto_skip; | 720 | break; |
659 | break; | 721 | case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP: |
660 | case CROSSFADE_ENABLE_ALWAYS: | 722 | crossfade = global_settings.playlist_shuffle || !auto_skip; |
661 | crossfade = true; | 723 | break; |
662 | break; | 724 | case CROSSFADE_ENABLE_ALWAYS: |
725 | crossfade = true; | ||
726 | break; | ||
727 | } | ||
663 | } | 728 | } |
664 | } | 729 | } |
665 | /* else crossfade is off, crossfade is already active, not enough data, | ||
666 | * pcm is off now (implying low data), not crossfading or low latency mode | ||
667 | */ | ||
668 | 730 | ||
669 | if (crossfade) | 731 | if (crossfade) |
670 | { | 732 | { |
@@ -676,33 +738,26 @@ void pcmbuf_start_track_change(enum pcm_track_change_type type) | |||
676 | 738 | ||
677 | crossfade_auto_skip = auto_skip; | 739 | crossfade_auto_skip = auto_skip; |
678 | 740 | ||
679 | crossfade_status = CROSSFADE_TRACK_CHANGE_STARTED; | 741 | crossfade_status = CROSSFADE_START; |
680 | 742 | ||
681 | trigger_cpu_boost(); | 743 | pcmbuf_monitor_track_change(auto_skip); |
682 | 744 | ||
683 | /* Cancel any pending automatic gapless transition and if a manual | 745 | trigger_cpu_boost(); |
684 | skip, stop position updates */ | ||
685 | pcm_play_lock(); | ||
686 | pcmbuf_cancel_track_change(!auto_skip); | ||
687 | pcm_play_unlock(); | ||
688 | } | 746 | } |
689 | else | 747 | else |
690 | #endif /* HAVE_CROSSFADE */ | 748 | #endif /* HAVE_CROSSFADE */ |
691 | if (auto_skip) | 749 | if (auto_skip) |
692 | { | 750 | { |
693 | /* The codec is moving on to the next track, but the current track will | 751 | /* The codec is moving on to the next track, but the current track will |
694 | * continue to play, so mark the last write chunk as the last one in | 752 | * continue to play, so mark the last write chunk as the last one in |
695 | * the track */ | 753 | * the track */ |
696 | logf("gapless track change"); | 754 | logf("gapless track change"); |
755 | |||
697 | #ifdef HAVE_CROSSFADE | 756 | #ifdef HAVE_CROSSFADE |
698 | if (crossfade_status != CROSSFADE_INACTIVE) | 757 | if (crossfade_status == CROSSFADE_ACTIVE) |
699 | { | 758 | crossfade_status = CROSSFADE_CONTINUE; |
700 | /* Crossfade is still active but crossfade is not happening - for | ||
701 | * now, chicken-out and clear out the buffer (just like before) to | ||
702 | * avoid fade pile-up on short tracks fading-in over long ones */ | ||
703 | pcmbuf_play_stop(); | ||
704 | } | ||
705 | #endif | 759 | #endif |
760 | |||
706 | pcmbuf_monitor_track_change(true); | 761 | pcmbuf_monitor_track_change(true); |
707 | } | 762 | } |
708 | else | 763 | else |
@@ -726,8 +781,11 @@ static void pcmbuf_pcm_callback(const void **start, size_t *size) | |||
726 | if (desc) | 781 | if (desc) |
727 | { | 782 | { |
728 | /* If last chunk in the track, notify of track change */ | 783 | /* If last chunk in the track, notify of track change */ |
729 | if (desc->is_end != 0) | 784 | if (index == chunk_transidx) |
785 | { | ||
786 | chunk_transidx = INVALID_BUF_INDEX; | ||
730 | audio_pcmbuf_track_change(true); | 787 | audio_pcmbuf_track_change(true); |
788 | } | ||
731 | 789 | ||
732 | /* Free it for reuse */ | 790 | /* Free it for reuse */ |
733 | chunk_ridx = index = index_next(index); | 791 | chunk_ridx = index = index_next(index); |
@@ -778,9 +836,8 @@ void pcmbuf_play_stop(void) | |||
778 | /* Revert to position updates by PCM */ | 836 | /* Revert to position updates by PCM */ |
779 | pcmbuf_sync_position = false; | 837 | pcmbuf_sync_position = false; |
780 | 838 | ||
781 | #ifdef HAVE_CROSSFADE | 839 | /* Fader OFF */ |
782 | crossfade_status = CROSSFADE_INACTIVE; | 840 | crossfade_cancel(); |
783 | #endif | ||
784 | 841 | ||
785 | /* Can unboost the codec thread here no matter who's calling, | 842 | /* Can unboost the codec thread here no matter who's calling, |
786 | * pretend full pcm buffer to unboost */ | 843 | * pretend full pcm buffer to unboost */ |
@@ -801,12 +858,72 @@ void pcmbuf_pause(bool pause) | |||
801 | /** Crossfade */ | 858 | /** Crossfade */ |
802 | 859 | ||
803 | #ifdef HAVE_CROSSFADE | 860 | #ifdef HAVE_CROSSFADE |
861 | |||
862 | /* Initialize a fader */ | ||
863 | static void mixfader_init(struct mixfader *faderp, int32_t start_factor, | ||
864 | int32_t end_factor, size_t size, bool alloc) | ||
865 | { | ||
866 | /* Linear fade */ | ||
867 | faderp->endfac = end_factor; | ||
868 | faderp->nsamp2 = size / PCMBUF_SAMPLE_SIZE * 2; | ||
869 | faderp->alloc = alloc; | ||
870 | |||
871 | if (faderp->nsamp2 == 0) | ||
872 | { | ||
873 | /* No data; set up as if fader finished the fade */ | ||
874 | faderp->factor = end_factor; | ||
875 | return; | ||
876 | } | ||
877 | |||
878 | int32_t dfact2 = 2*abs(end_factor - start_factor); | ||
879 | faderp->factor = start_factor; | ||
880 | faderp->ferr = dfact2 / 2; | ||
881 | faderp->dfquo = dfact2 / faderp->nsamp2; | ||
882 | faderp->dfrem = dfact2 - faderp->dfquo*faderp->nsamp2; | ||
883 | faderp->dfinc = end_factor < start_factor ? -1 : +1; | ||
884 | faderp->dfquo *= faderp->dfinc; | ||
885 | } | ||
886 | |||
887 | /* Query if the fader has finished its envelope */ | ||
888 | static inline bool mixfader_finished(const struct mixfader *faderp) | ||
889 | { | ||
890 | return faderp->factor == faderp->endfac; | ||
891 | } | ||
892 | |||
893 | /* Step fader by one sample */ | ||
894 | static inline void mixfader_step(struct mixfader *faderp) | ||
895 | { | ||
896 | if (mixfader_finished(faderp)) | ||
897 | return; | ||
898 | |||
899 | faderp->factor += faderp->dfquo; | ||
900 | faderp->ferr += faderp->dfrem; | ||
901 | |||
902 | if (faderp->ferr >= faderp->nsamp2) | ||
903 | { | ||
904 | faderp->factor += faderp->dfinc; | ||
905 | faderp->ferr -= faderp->nsamp2; | ||
906 | } | ||
907 | } | ||
908 | |||
909 | static FORCE_INLINE int32_t mixfade_sample(const struct mixfader *faderp, int32_t s) | ||
910 | { | ||
911 | return (faderp->factor * s + MIXFADE_UNITY/2) >> MIXFADE_UNITY_BITS; | ||
912 | } | ||
913 | |||
914 | /* Cancel crossfade operation */ | ||
915 | static void crossfade_cancel(void) | ||
916 | { | ||
917 | crossfade_status = CROSSFADE_INACTIVE; | ||
918 | crossfade_widx = INVALID_BUF_INDEX; | ||
919 | } | ||
920 | |||
804 | /* Find the buffer index that's 'size' bytes away from 'index' */ | 921 | /* Find the buffer index that's 'size' bytes away from 'index' */ |
805 | static size_t crossfade_find_index(size_t index, size_t size) | 922 | static size_t crossfade_find_index(size_t index, size_t size) |
806 | { | 923 | { |
807 | if (index != INVALID_BUF_INDEX) | 924 | if (index != INVALID_BUF_INDEX) |
808 | { | 925 | { |
809 | size_t i = ALIGN_DOWN(index, PCMBUF_CHUNK_SIZE); | 926 | size_t i = index_chunk_offs(index, 0); |
810 | size += index - i; | 927 | size += index - i; |
811 | 928 | ||
812 | while (i != chunk_widx) | 929 | while (i != chunk_widx) |
@@ -814,26 +931,30 @@ static size_t crossfade_find_index(size_t index, size_t size) | |||
814 | size_t desc_size = index_chunkdesc(i)->size; | 931 | size_t desc_size = index_chunkdesc(i)->size; |
815 | 932 | ||
816 | if (size < desc_size) | 933 | if (size < desc_size) |
817 | return i + size; | 934 | { |
935 | index = i + size; | ||
936 | break; | ||
937 | } | ||
818 | 938 | ||
819 | size -= desc_size; | 939 | size -= desc_size; |
820 | i = index_next(i); | 940 | i = index_next(i); |
821 | } | 941 | } |
822 | } | 942 | } |
823 | 943 | ||
824 | return INVALID_BUF_INDEX; | 944 | return index; |
825 | } | 945 | } |
826 | 946 | ||
827 | /* Align the needed buffer area up to the end of existing data */ | 947 | /* Align the needed buffer area up to the end of existing data */ |
828 | static size_t crossfade_find_buftail(size_t buffer_rem, size_t buffer_need) | 948 | static size_t crossfade_find_buftail(bool auto_skip, size_t buffer_rem, |
949 | size_t buffer_need, size_t *buffer_rem_outp) | ||
829 | { | 950 | { |
830 | crossfade_index = chunk_ridx; | 951 | size_t index = chunk_ridx; |
831 | 952 | ||
832 | if (buffer_rem > buffer_need) | 953 | if (buffer_rem > buffer_need) |
833 | { | 954 | { |
834 | size_t distance; | 955 | size_t distance; |
835 | 956 | ||
836 | if (crossfade_auto_skip) | 957 | if (auto_skip) |
837 | { | 958 | { |
838 | /* Automatic track changes only modify the last part of the buffer, | 959 | /* Automatic track changes only modify the last part of the buffer, |
839 | * so find the right chunk and sample to start the crossfade */ | 960 | * so find the right chunk and sample to start the crossfade */ |
@@ -843,35 +964,41 @@ static size_t crossfade_find_buftail(size_t buffer_rem, size_t buffer_need) | |||
843 | else | 964 | else |
844 | { | 965 | { |
845 | /* Manual skips occur immediately, but give 1/5s to process */ | 966 | /* Manual skips occur immediately, but give 1/5s to process */ |
846 | distance = BYTERATE / 5; | 967 | distance = MIN(BYTERATE / 5, buffer_rem); |
847 | buffer_rem -= BYTERATE / 5; | 968 | buffer_rem -= distance; |
848 | } | 969 | } |
849 | 970 | ||
850 | crossfade_index = crossfade_find_index(crossfade_index, distance); | 971 | index = crossfade_find_index(index, distance); |
851 | } | 972 | } |
852 | 973 | ||
853 | return buffer_rem; | 974 | if (buffer_rem_outp) |
975 | *buffer_rem_outp = buffer_rem; | ||
976 | |||
977 | return index; | ||
854 | } | 978 | } |
855 | 979 | ||
856 | /* Returns the number of bytes _NOT_ mixed/faded */ | 980 | /* Run a fader on some buffers */ |
857 | static size_t crossfade_mix_fade(int factor, size_t size, void *buf, | 981 | static void crossfade_mix_fade(struct mixfader *faderp, size_t size, |
858 | size_t *out_index, unsigned long elapsed, | 982 | void *input_buf, size_t *out_index, |
859 | off_t offset) | 983 | unsigned long elapsed, off_t offset) |
860 | { | 984 | { |
861 | if (size == 0) | 985 | if (size == 0) |
862 | return 0; | 986 | return; |
863 | 987 | ||
864 | size_t index = *out_index; | 988 | size_t index = *out_index; |
865 | 989 | ||
866 | if (index == INVALID_BUF_INDEX) | 990 | if (index == INVALID_BUF_INDEX) |
867 | return size; | 991 | return; |
992 | |||
993 | int16_t *inbuf = input_buf; | ||
868 | 994 | ||
869 | const int16_t *input_buf = buf; | 995 | bool alloced = inbuf && faderp->alloc && |
870 | int16_t *output_buf = index_buffer(index); | 996 | index_chunk_offs(index, 0) == chunk_widx; |
871 | 997 | ||
872 | while (size) | 998 | while (size) |
873 | { | 999 | { |
874 | struct chunkdesc *desc = index_chunkdesc(index); | 1000 | struct chunkdesc *desc = index_chunkdesc(index); |
1001 | int16_t *outbuf = index_buffer(index); | ||
875 | 1002 | ||
876 | switch (offset) | 1003 | switch (offset) |
877 | { | 1004 | { |
@@ -887,60 +1014,77 @@ static size_t crossfade_mix_fade(int factor, size_t size, void *buf, | |||
887 | stamp_chunk(desc, elapsed, offset); | 1014 | stamp_chunk(desc, elapsed, offset); |
888 | } | 1015 | } |
889 | 1016 | ||
890 | size_t rem = desc->size - (index % PCMBUF_CHUNK_SIZE); | 1017 | size_t amount = (alloced ? PCMBUF_CHUNK_SIZE : desc->size) |
891 | int16_t *chunk_end = SKIPBYTES(output_buf, rem); | 1018 | - (index % PCMBUF_CHUNK_SIZE); |
1019 | int16_t *chunkend = SKIPBYTES(outbuf, amount); | ||
892 | 1020 | ||
893 | if (size < rem) | 1021 | if (size < amount) |
894 | rem = size; | 1022 | amount = size; |
895 | 1023 | ||
896 | size -= rem; | 1024 | size -= amount; |
897 | 1025 | ||
898 | do | 1026 | if (alloced) |
899 | { | 1027 | { |
900 | /* fade left and right channel at once to keep buffer alignment */ | 1028 | /* Fade the input buffer into the new destination chunk */ |
901 | int32_t left = output_buf[0]; | 1029 | for (size_t s = amount; s != 0; s -= PCMBUF_SAMPLE_SIZE) |
902 | int32_t right = output_buf[1]; | 1030 | { |
1031 | *outbuf++ = mixfade_sample(faderp, *inbuf++); | ||
1032 | *outbuf++ = mixfade_sample(faderp, *inbuf++); | ||
1033 | mixfader_step(faderp); | ||
1034 | } | ||
903 | 1035 | ||
904 | if (input_buf) | 1036 | commit_write_buffer(amount); |
1037 | } | ||
1038 | else if (inbuf) | ||
1039 | { | ||
1040 | /* Fade the input buffer and mix into the destination chunk */ | ||
1041 | for (size_t s = amount; s != 0; s -= PCMBUF_SAMPLE_SIZE) | ||
905 | { | 1042 | { |
906 | /* fade the input buffer and mix into the chunk */ | 1043 | int32_t left = outbuf[0]; |
907 | left += *input_buf++ * factor >> 8; | 1044 | int32_t right = outbuf[1]; |
908 | right += *input_buf++ * factor >> 8; | 1045 | left += mixfade_sample(faderp, *inbuf++); |
909 | left = clip_sample_16(left); | 1046 | right += mixfade_sample(faderp, *inbuf++); |
910 | right = clip_sample_16(right); | 1047 | *outbuf++ = clip_sample_16(left); |
1048 | *outbuf++ = clip_sample_16(right); | ||
1049 | mixfader_step(faderp); | ||
911 | } | 1050 | } |
912 | else | 1051 | } |
1052 | else | ||
1053 | { | ||
1054 | /* Fade the chunk in place */ | ||
1055 | for (size_t s = amount; s != 0; s -= PCMBUF_SAMPLE_SIZE) | ||
913 | { | 1056 | { |
914 | /* fade the chunk only */ | 1057 | int32_t left = outbuf[0]; |
915 | left = left * factor >> 8; | 1058 | int32_t right = outbuf[1]; |
916 | right = right * factor >> 8; | 1059 | *outbuf++ = mixfade_sample(faderp, left); |
1060 | *outbuf++ = mixfade_sample(faderp, right); | ||
1061 | mixfader_step(faderp); | ||
917 | } | 1062 | } |
918 | |||
919 | *output_buf++ = left; | ||
920 | *output_buf++ = right; | ||
921 | |||
922 | rem -= 4; | ||
923 | } | 1063 | } |
924 | while (rem); | ||
925 | 1064 | ||
926 | /* move to next chunk as needed */ | 1065 | if (outbuf < chunkend) |
927 | if (output_buf >= chunk_end) | ||
928 | { | 1066 | { |
929 | index = index_next(index); | 1067 | index += amount; |
1068 | continue; | ||
1069 | } | ||
1070 | |||
1071 | /* Move destination to next chunk as needed */ | ||
1072 | index = index_next(index); | ||
930 | 1073 | ||
931 | if (index == chunk_widx) | 1074 | if (index == chunk_widx) |
1075 | { | ||
1076 | /* End of existing data */ | ||
1077 | if (!inbuf || !faderp->alloc) | ||
932 | { | 1078 | { |
933 | /* End of existing data */ | 1079 | index = INVALID_BUF_INDEX; |
934 | *out_index = INVALID_BUF_INDEX; | 1080 | break; |
935 | return size; | ||
936 | } | 1081 | } |
937 | 1082 | ||
938 | output_buf = index_buffer(index); | 1083 | alloced = true; |
939 | } | 1084 | } |
940 | } | 1085 | } |
941 | 1086 | ||
942 | *out_index = buffer_index(output_buf); | 1087 | *out_index = index; |
943 | return 0; | ||
944 | } | 1088 | } |
945 | 1089 | ||
946 | /* Initializes crossfader, calculates all necessary parameters and performs | 1090 | /* Initializes crossfader, calculates all necessary parameters and performs |
@@ -951,6 +1095,19 @@ static void crossfade_start(void) | |||
951 | 1095 | ||
952 | pcm_play_lock(); | 1096 | pcm_play_lock(); |
953 | 1097 | ||
1098 | if (crossfade_status == CROSSFADE_CONTINUE) | ||
1099 | { | ||
1100 | logf("fade-in continuing"); | ||
1101 | |||
1102 | crossfade_status = CROSSFADE_ACTIVE; | ||
1103 | |||
1104 | if (crossfade_auto_skip) | ||
1105 | pcmbuf_monitor_track_change_ex(crossfade_widx); | ||
1106 | |||
1107 | pcm_play_unlock(); | ||
1108 | return; | ||
1109 | } | ||
1110 | |||
954 | /* Initialize the crossfade buffer size to all of the buffered data that | 1111 | /* Initialize the crossfade buffer size to all of the buffered data that |
955 | * has not yet been sent to the DMA */ | 1112 | * has not yet been sent to the DMA */ |
956 | size_t unplayed = pcmbuf_unplayed_bytes(); | 1113 | size_t unplayed = pcmbuf_unplayed_bytes(); |
@@ -959,12 +1116,7 @@ static void crossfade_start(void) | |||
959 | if (unplayed < DATA_LEVEL(2)) | 1116 | if (unplayed < DATA_LEVEL(2)) |
960 | { | 1117 | { |
961 | logf("crossfade rejected"); | 1118 | logf("crossfade rejected"); |
962 | 1119 | crossfade_cancel(); | |
963 | crossfade_status = CROSSFADE_INACTIVE; | ||
964 | |||
965 | if (crossfade_auto_skip) | ||
966 | pcmbuf_monitor_track_change(true); | ||
967 | |||
968 | pcm_play_unlock(); | 1120 | pcm_play_unlock(); |
969 | return; | 1121 | return; |
970 | } | 1122 | } |
@@ -982,11 +1134,7 @@ static void crossfade_start(void) | |||
982 | { | 1134 | { |
983 | /* Forego fade-in delay on manual skip - do the best to preserve auto skip | 1135 | /* Forego fade-in delay on manual skip - do the best to preserve auto skip |
984 | relationship */ | 1136 | relationship */ |
985 | if (fade_out_delay > fade_in_delay) | 1137 | fade_out_delay -= MIN(fade_out_delay, fade_in_delay); |
986 | fade_out_delay -= fade_in_delay; | ||
987 | else | ||
988 | fade_out_delay = 0; | ||
989 | |||
990 | fade_in_delay = 0; | 1138 | fade_in_delay = 0; |
991 | } | 1139 | } |
992 | 1140 | ||
@@ -994,7 +1142,10 @@ static void crossfade_start(void) | |||
994 | 1142 | ||
995 | if (!crossfade_mixmode) | 1143 | if (!crossfade_mixmode) |
996 | { | 1144 | { |
997 | size_t buffer_rem = crossfade_find_buftail(unplayed, fade_out_need); | 1145 | /* Completely process the crossfade fade-out effect with current PCM buffer */ |
1146 | size_t buffer_rem; | ||
1147 | size_t index = crossfade_find_buftail(crossfade_auto_skip, unplayed, | ||
1148 | fade_out_need, &buffer_rem); | ||
998 | 1149 | ||
999 | pcm_play_unlock(); | 1150 | pcm_play_unlock(); |
1000 | 1151 | ||
@@ -1003,59 +1154,52 @@ static void crossfade_start(void) | |||
1003 | /* Existing buffers are short */ | 1154 | /* Existing buffers are short */ |
1004 | size_t fade_out_short = fade_out_need - buffer_rem; | 1155 | size_t fade_out_short = fade_out_need - buffer_rem; |
1005 | 1156 | ||
1006 | if (fade_out_rem >= fade_out_short) | 1157 | if (fade_out_delay >= fade_out_short) |
1007 | { | 1158 | { |
1008 | /* Truncate fade-out duration */ | 1159 | /* Truncate fade-out delay */ |
1009 | fade_out_rem -= fade_out_short; | 1160 | fade_out_delay -= fade_out_short; |
1010 | } | 1161 | } |
1011 | else | 1162 | else |
1012 | { | 1163 | { |
1013 | /* Truncate fade-out and fade-out delay */ | 1164 | /* Truncate fade-out and eliminate fade-out delay */ |
1014 | fade_out_delay = fade_out_rem; | 1165 | fade_out_rem = buffer_rem; |
1015 | fade_out_rem = 0; | 1166 | fade_out_delay = 0; |
1016 | } | 1167 | } |
1017 | } | ||
1018 | |||
1019 | /* Completely process the crossfade fade-out effect with current PCM buffer */ | ||
1020 | 1168 | ||
1021 | /* Fade out the specified amount of the already processed audio */ | 1169 | fade_out_need = fade_out_delay + fade_out_rem; |
1022 | size_t fade_out_total = fade_out_rem; | 1170 | } |
1023 | 1171 | ||
1024 | /* Find the right chunk and sample to start fading out */ | 1172 | /* Find the right chunk and sample to start fading out */ |
1025 | size_t fade_out_index = crossfade_find_index(crossfade_index, fade_out_delay); | 1173 | index = crossfade_find_index(index, fade_out_delay); |
1026 | 1174 | ||
1027 | while (fade_out_rem > 0) | 1175 | /* Fade out the specified amount of the already processed audio */ |
1028 | { | 1176 | struct mixfader outfader; |
1029 | /* Each 1/20 second of audio will have the same fade applied */ | ||
1030 | size_t block_rem = MIN(BYTERATE / 20, fade_out_rem); | ||
1031 | int factor = (fade_out_rem << 8) / fade_out_total; | ||
1032 | 1177 | ||
1033 | fade_out_rem -= block_rem; | 1178 | mixfader_init(&outfader, MIXFADE_UNITY, 0, fade_out_rem, false); |
1179 | crossfade_mix_fade(&outfader, fade_out_rem, NULL, &index, 0, | ||
1180 | MIXFADE_KEEP_POS); | ||
1034 | 1181 | ||
1035 | crossfade_mix_fade(factor, block_rem, NULL, &fade_out_index, | 1182 | /* Zero-out the rest of the buffer */ |
1036 | 0, MIXFADE_KEEP_POS); | 1183 | crossfade_mix_fade(&outfader, pcmbuf_size, NULL, &index, 0, |
1037 | } | 1184 | MIXFADE_NULLIFY_POS); |
1038 | |||
1039 | /* zero out the rest of the buffer */ | ||
1040 | crossfade_mix_fade(0, pcmbuf_size, NULL, &fade_out_index, | ||
1041 | 0, MIXFADE_NULLIFY_POS); | ||
1042 | 1185 | ||
1043 | pcm_play_lock(); | 1186 | pcm_play_lock(); |
1044 | } | 1187 | } |
1045 | 1188 | ||
1046 | /* Initialize fade-in counters */ | 1189 | /* Initialize fade-in counters */ |
1047 | crossfade_fade_in_total = fade_in_duration; | 1190 | mixfader_init(&crossfade_infader, 0, MIXFADE_UNITY, fade_in_duration, true); |
1048 | crossfade_fade_in_rem = fade_in_duration; | ||
1049 | 1191 | ||
1050 | /* Find the right chunk and sample to start fading in - redo from read | 1192 | /* Find the right chunk and sample to start fading in - redo from read |
1051 | chunk in case original position were/was overrun in callback - the | 1193 | chunk in case original position were/was overrun in callback - the |
1052 | track change event _must not_ ever fail to happen */ | 1194 | track change event _must not_ ever fail to happen */ |
1053 | unplayed = pcmbuf_unplayed_bytes() + fade_in_delay; | 1195 | unplayed = pcmbuf_unplayed_bytes() + fade_in_delay; |
1054 | 1196 | ||
1055 | crossfade_find_buftail(unplayed, fade_out_need); | 1197 | crossfade_widx = crossfade_find_buftail(crossfade_auto_skip, unplayed, |
1198 | fade_out_need, NULL); | ||
1056 | 1199 | ||
1200 | /* Move track transistion to chunk before the first one of incoming track */ | ||
1057 | if (crossfade_auto_skip) | 1201 | if (crossfade_auto_skip) |
1058 | pcmbuf_monitor_track_change_ex(crossfade_index, 0); | 1202 | pcmbuf_monitor_track_change_ex(crossfade_widx); |
1059 | 1203 | ||
1060 | pcm_play_unlock(); | 1204 | pcm_play_unlock(); |
1061 | 1205 | ||
@@ -1065,83 +1209,16 @@ static void crossfade_start(void) | |||
1065 | /* Perform fade-in of new track */ | 1209 | /* Perform fade-in of new track */ |
1066 | static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset) | 1210 | static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset) |
1067 | { | 1211 | { |
1068 | void *buf = crossfade_buffer; | 1212 | /* Mix the data */ |
1213 | crossfade_mix_fade(&crossfade_infader, size, index_buffer(crossfade_bufidx), | ||
1214 | &crossfade_widx, elapsed, offset); | ||
1069 | 1215 | ||
1070 | if (crossfade_fade_in_rem) | 1216 | /* If no more fading-in to do, stop the crossfade */ |
1217 | if (mixfader_finished(&crossfade_infader) && | ||
1218 | index_chunk_offs(crossfade_widx, 0) == chunk_widx) | ||
1071 | { | 1219 | { |
1072 | /* Fade factor for this packet */ | 1220 | crossfade_cancel(); |
1073 | int factor = | ||
1074 | ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) / | ||
1075 | crossfade_fade_in_total; | ||
1076 | /* Bytes to fade */ | ||
1077 | size_t fade_rem = MIN(size, crossfade_fade_in_rem); | ||
1078 | |||
1079 | /* We _will_ fade this many bytes */ | ||
1080 | crossfade_fade_in_rem -= fade_rem; | ||
1081 | |||
1082 | if (crossfade_index != INVALID_BUF_INDEX) | ||
1083 | { | ||
1084 | /* Mix the data */ | ||
1085 | size_t fade_total = fade_rem; | ||
1086 | fade_rem = crossfade_mix_fade(factor, fade_rem, buf, &crossfade_index, | ||
1087 | elapsed, offset); | ||
1088 | fade_total -= fade_rem; | ||
1089 | size -= fade_total; | ||
1090 | buf += fade_total; | ||
1091 | |||
1092 | if (!size) | ||
1093 | return; | ||
1094 | } | ||
1095 | |||
1096 | /* Fade remaining samples in place */ | ||
1097 | int samples = fade_rem / 4; | ||
1098 | int16_t *input_buf = buf; | ||
1099 | |||
1100 | while (samples--) | ||
1101 | { | ||
1102 | int32_t left = input_buf[0]; | ||
1103 | int32_t right = input_buf[1]; | ||
1104 | *input_buf++ = left * factor >> 8; | ||
1105 | *input_buf++ = right * factor >> 8; | ||
1106 | } | ||
1107 | } | 1221 | } |
1108 | |||
1109 | if (crossfade_index != INVALID_BUF_INDEX) | ||
1110 | { | ||
1111 | /* Mix the data */ | ||
1112 | size_t mix_total = size; | ||
1113 | |||
1114 | /* A factor of 256 means mix only, no fading */ | ||
1115 | size = crossfade_mix_fade(256, size, buf, &crossfade_index, | ||
1116 | elapsed, offset); | ||
1117 | buf += mix_total - size; | ||
1118 | |||
1119 | if (!size) | ||
1120 | return; | ||
1121 | } | ||
1122 | |||
1123 | /* Data might remain in the fade buffer yet the fade-in has run its | ||
1124 | course - finish it off as normal chunks */ | ||
1125 | while (size > 0) | ||
1126 | { | ||
1127 | size_t copy_n = size; | ||
1128 | void *outbuf = get_write_buffer(©_n); | ||
1129 | memcpy(outbuf, buf, copy_n); | ||
1130 | commit_write_buffer(copy_n, elapsed, offset); | ||
1131 | buf += copy_n; | ||
1132 | size -= copy_n; | ||
1133 | } | ||
1134 | |||
1135 | /* if no more fading-in to do, stop the crossfade */ | ||
1136 | #if 0 | ||
1137 | /* This way (the previous way) can cause a sudden volume jump if mixable | ||
1138 | data is used up before the fade-in completes and that just sounds wrong | ||
1139 | -- jethead71 */ | ||
1140 | if (!crossfade_fade_in_rem || crossfade_index == INVALID_BUF_INDEX) | ||
1141 | #endif | ||
1142 | /* Let fade-in complete even if not fully overlapping the existing data */ | ||
1143 | if (!crossfade_fade_in_rem) | ||
1144 | crossfade_status = CROSSFADE_INACTIVE; | ||
1145 | } | 1222 | } |
1146 | 1223 | ||
1147 | static void pcmbuf_finish_crossfade_enable(void) | 1224 | static void pcmbuf_finish_crossfade_enable(void) |
@@ -1156,11 +1233,6 @@ static void pcmbuf_finish_crossfade_enable(void) | |||
1156 | PCMBUF_WATERMARK; | 1233 | PCMBUF_WATERMARK; |
1157 | } | 1234 | } |
1158 | 1235 | ||
1159 | bool pcmbuf_is_crossfade_active(void) | ||
1160 | { | ||
1161 | return crossfade_status != CROSSFADE_INACTIVE; | ||
1162 | } | ||
1163 | |||
1164 | void pcmbuf_request_crossfade_enable(int setting) | 1236 | void pcmbuf_request_crossfade_enable(int setting) |
1165 | { | 1237 | { |
1166 | /* Next setting to be used, not applied now */ | 1238 | /* Next setting to be used, not applied now */ |
@@ -1322,7 +1394,7 @@ bool pcmbuf_is_lowdata(void) | |||
1322 | { | 1394 | { |
1323 | enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK); | 1395 | enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK); |
1324 | 1396 | ||
1325 | if (status != CHANNEL_PLAYING || pcmbuf_is_crossfade_active()) | 1397 | if (status != CHANNEL_PLAYING || crossfade_status != CROSSFADE_INACTIVE) |
1326 | return false; | 1398 | return false; |
1327 | 1399 | ||
1328 | return pcmbuf_data_critical(); | 1400 | return pcmbuf_data_critical(); |