summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/pcmbuf.c646
-rw-r--r--apps/pcmbuf.h3
2 files changed, 359 insertions, 290 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 @@
88struct chunkdesc 90struct 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
111static size_t pcmbuf_bytes_waiting; 116static size_t pcmbuf_bytes_waiting;
112static struct chunkdesc *current_desc; 117static struct chunkdesc *current_desc;
118static size_t chunk_transidx;
113 119
114static size_t pcmbuf_watermark = 0; 120static size_t pcmbuf_watermark = 0;
115 121
@@ -131,28 +137,36 @@ static bool fade_out_complete = false;
131static bool soft_mode = false; 137static bool soft_mode = false;
132 138
133#ifdef HAVE_CROSSFADE 139#ifdef HAVE_CROSSFADE
134/* Crossfade buffer */
135static void *crossfade_buffer;
136
137/* Crossfade related state */ 140/* Crossfade related state */
141
138static int crossfade_setting; 142static int crossfade_setting;
139static int crossfade_enable_request; 143static int crossfade_enable_request;
140static bool crossfade_mixmode;
141static bool crossfade_auto_skip;
142 144
143static enum 145static 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 */ 153static bool crossfade_mixmode;
151static size_t crossfade_index; 154static bool crossfade_auto_skip;
155static size_t crossfade_widx;
156static size_t crossfade_bufidx;
152 157
153/* Counters for fading in new data */ 158struct mixfader
154static size_t crossfade_fade_in_total; 159{
155static 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
183static void crossfade_cancel(void);
166static void crossfade_start(void); 184static void crossfade_start(void);
167static void write_to_crossfade(size_t size, unsigned long elapsed, 185static void write_to_crossfade(size_t size, unsigned long elapsed,
168 off_t offset); 186 off_t offset);
169static void pcmbuf_finish_crossfade_enable(void); 187static 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 */
237static struct chunkdesc * index_chunkdesc_offs(size_t index, int offset) 257static 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 */
274static 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 */
294void 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 */ 432static void commit_write_buffer(size_t size)
368static 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 */
380void * pcmbuf_request_buffer(int *count) 440void * 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 */
467void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset) 522void 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| */
520size_t pcmbuf_init(void *bufend) 578size_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 */
563static void pcmbuf_monitor_track_change_ex(size_t index, int offset) 614static 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 */
578static void pcmbuf_cancel_track_change(bool position) 636static 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 */
863static 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 */
888static inline bool mixfader_finished(const struct mixfader *faderp)
889{
890 return faderp->factor == faderp->endfac;
891}
892
893/* Step fader by one sample */
894static 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
909static 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 */
915static 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' */
805static size_t crossfade_find_index(size_t index, size_t size) 922static 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 */
828static size_t crossfade_find_buftail(size_t buffer_rem, size_t buffer_need) 948static 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 */
857static size_t crossfade_mix_fade(int factor, size_t size, void *buf, 981static 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 */
1066static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset) 1210static 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(&copy_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
1147static void pcmbuf_finish_crossfade_enable(void) 1224static 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
1159bool pcmbuf_is_crossfade_active(void)
1160{
1161 return crossfade_status != CROSSFADE_INACTIVE;
1162}
1163
1164void pcmbuf_request_crossfade_enable(int setting) 1236void 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();
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index 008872be59..e16f86174c 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -50,13 +50,10 @@ void pcmbuf_start_track_change(enum pcm_track_change_type type);
50 50
51/* Crossfade */ 51/* Crossfade */
52#ifdef HAVE_CROSSFADE 52#ifdef HAVE_CROSSFADE
53bool pcmbuf_is_crossfade_active(void);
54void pcmbuf_request_crossfade_enable(int setting); 53void pcmbuf_request_crossfade_enable(int setting);
55bool pcmbuf_is_same_size(void); 54bool pcmbuf_is_same_size(void);
56#else 55#else
57/* Dummy functions with sensible returns */ 56/* Dummy functions with sensible returns */
58static FORCE_INLINE bool pcmbuf_is_crossfade_active(void)
59 { return false; }
60static FORCE_INLINE void pcmbuf_request_crossfade_enable(bool on_off) 57static FORCE_INLINE void pcmbuf_request_crossfade_enable(bool on_off)
61 { return; (void)on_off; } 58 { return; (void)on_off; }
62static FORCE_INLINE bool pcmbuf_is_same_size(void) 59static FORCE_INLINE bool pcmbuf_is_same_size(void)