A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
11 * Copyright (C) 2011 by Michael Sevakis
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include <stdio.h>
23#include "config.h"
24#include "system.h"
25#include "debug.h"
26#include <kernel.h>
27#include "pcm.h"
28#include "pcm_mixer.h"
29#include "pcmbuf.h"
30#include "dsp-util.h"
31#include "playback.h"
32#include "codec_thread.h"
33
34/* Define LOGF_ENABLE to enable logf output in this file */
35/*#define LOGF_ENABLE*/
36#include "logf.h"
37#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
38#include "cpu.h"
39#endif
40#include "settings.h"
41#include "audio.h"
42#include "voice_thread.h"
43
44/* 2 channels * 2 bytes/sample, interleaved */
45#define PCMBUF_SAMPLE_SIZE (2 * 2)
46
47/* This is the target fill size of chunks on the pcm buffer
48 Can be any number of samples but power of two sizes make for faster and
49 smaller math - must be < 65536 bytes */
50#define PCMBUF_CHUNK_SIZE 8192u
51
52/* Small guard buf to give decent space near end */
53#define PCMBUF_GUARD_SIZE (PCMBUF_CHUNK_SIZE / 8)
54
55/* Mnemonics for common data commit thresholds */
56#define COMMIT_CHUNKS PCMBUF_CHUNK_SIZE
57#define COMMIT_ALL_DATA 1u
58
59/* Size of the crossfade buffer where codec data is written to be faded
60 on commit */
61#define CROSSFADE_BUFSIZE PCMBUF_CHUNK_SIZE
62
63/* Maximum contiguous space that PCM buffer will allow (to avoid excessive
64 draining between inserts and observe low-latency mode) */
65#define PCMBUF_MAX_BUFFER (PCMBUF_CHUNK_SIZE * 4)
66
67/* Forced buffer insert constraint can thus be from 1KB to 32KB using 8KB
68 chunks */
69
70/* Return data level in 1/4-second increments */
71#define DATA_LEVEL(quarter_secs) (pcmbuf_sampr * (quarter_secs))
72
73/* Number of bytes played per second */
74#define BYTERATE (pcmbuf_sampr * PCMBUF_SAMPLE_SIZE)
75
76#if MEMORYSIZE > 2
77/* Keep watermark high for large memory target - at least (2s) */
78#define PCMBUF_WATERMARK (BYTERATE * 2)
79#define MIN_BUFFER_SIZE (BYTERATE * 3)
80/* 1 seconds of buffer is low data */
81#define LOW_DATA DATA_LEVEL(4)
82#else
83#define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */
84#define MIN_BUFFER_SIZE (BYTERATE * 1)
85/* under watermark is low data */
86#define LOW_DATA pcmbuf_watermark
87#endif
88
89/* Describes each audio packet - keep it small since there are many of them */
90struct chunkdesc
91{
92 uint16_t size; /* Actual size (0 < size <= PCMBUF_CHUNK_SIZE) */
93 uint16_t pos_key; /* Who put the position info in
94 (undefined: 0, valid: 1..POSITION_KEY_MAX) */
95 unsigned long elapsed; /* Elapsed time to use */
96 off_t offset; /* Offset to use */
97};
98
99#define POSITION_KEY_MAX UINT16_MAX
100
101
102/* General PCM buffer data */
103#define INVALID_BUF_INDEX ((size_t)0 - (size_t)1)
104
105static void *pcmbuf_buffer;
106static void *pcmbuf_guardbuf;
107static size_t pcmbuf_size;
108static struct chunkdesc *pcmbuf_descriptors;
109static unsigned int pcmbuf_desc_count;
110static unsigned int position_key = 1;
111static unsigned int pcmbuf_sampr = 0;
112
113static size_t chunk_ridx;
114static size_t chunk_widx;
115
116static size_t pcmbuf_bytes_waiting;
117static struct chunkdesc *current_desc;
118static size_t chunk_transidx;
119
120static size_t pcmbuf_watermark = 0;
121
122static bool low_latency_mode = false;
123
124static bool pcmbuf_sync_position = false;
125
126/* Fade effect */
127static unsigned int fade_vol = MIX_AMP_UNITY;
128static enum
129{
130 PCM_NOT_FADING = 0,
131 PCM_FADING_IN,
132 PCM_FADING_OUT,
133} fade_state = PCM_NOT_FADING;
134static bool fade_out_complete = false;
135
136/* Voice */
137static bool soft_mode = false;
138
139#ifdef HAVE_CROSSFADE
140/* Crossfade related state */
141
142static int crossfade_setting;
143static int crossfade_enable_request;
144
145static enum
146{
147 CROSSFADE_INACTIVE = 0, /* Crossfade is OFF */
148 CROSSFADE_ACTIVE, /* Crossfade is fading in */
149 CROSSFADE_START, /* New crossfade is starting */
150 CROSSFADE_CONTINUE, /* Next track continues fade */
151} crossfade_status = CROSSFADE_INACTIVE;
152
153static bool crossfade_mixmode;
154static bool crossfade_auto_skip;
155static size_t crossfade_widx;
156static size_t crossfade_bufidx;
157
158struct mixfader
159{
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;
170
171/* Defines for operations on position info when mixing/fading -
172 passed in offset parameter */
173enum
174{
175 MIXFADE_KEEP_POS = -1, /* Keep position info in chunk */
176 MIXFADE_NULLIFY_POS = -2, /* Ignore position info in chunk */
177 /* Positive values cause stamping/restamping */
178};
179
180#define MIXFADE_UNITY_BITS 16
181#define MIXFADE_UNITY (1 << MIXFADE_UNITY_BITS)
182
183static void crossfade_cancel(void);
184static void crossfade_start(void);
185static void write_to_crossfade(size_t size, unsigned long elapsed,
186 off_t offset);
187static void pcmbuf_finish_crossfade_enable(void);
188#else
189#define crossfade_cancel() do {} while(0)
190#endif /* HAVE_CROSSFADE */
191
192/* Thread */
193#ifdef HAVE_PRIORITY_SCHEDULING
194static int codec_thread_priority = PRIORITY_PLAYBACK;
195#endif
196
197/* Callbacks into playback.c */
198extern void audio_pcmbuf_position_callback(unsigned long elapsed,
199 off_t offset, unsigned int key);
200extern void audio_pcmbuf_track_change(bool pcmbuf);
201extern bool audio_pcmbuf_may_play(void);
202extern void audio_pcmbuf_sync_position(void);
203
204
205/**************************************/
206
207/* start PCM if callback says it's alright */
208static void start_audio_playback(void)
209{
210 if (audio_pcmbuf_may_play())
211 pcmbuf_play_start();
212}
213
214/* Return number of commited bytes in buffer (committed chunks count as
215 a full chunk even if only partially filled) */
216static size_t pcmbuf_unplayed_bytes(void)
217{
218 size_t ridx = chunk_ridx;
219 size_t widx = chunk_widx;
220
221 if (ridx > widx)
222 widx += pcmbuf_size;
223
224 return widx - ridx;
225}
226
227/* Returns TRUE if amount of data is under the target fill size */
228static bool pcmbuf_data_critical(void)
229{
230 return pcmbuf_unplayed_bytes() < LOW_DATA;
231}
232
233/* Return the next PCM chunk in the PCM buffer given a byte index into it */
234static size_t index_next(size_t index)
235{
236 index = ALIGN_DOWN(index + PCMBUF_CHUNK_SIZE, PCMBUF_CHUNK_SIZE);
237
238 if (index >= pcmbuf_size)
239 index -= pcmbuf_size;
240
241 return index;
242}
243
244/* Convert a byte offset in the PCM buffer into a pointer in the buffer */
245static FORCE_INLINE void * index_buffer(size_t index)
246{
247 return pcmbuf_buffer + index;
248}
249
250/* Convert a pointer in the buffer into an index offset */
251static FORCE_INLINE size_t buffer_index(void *p)
252{
253 return (uintptr_t)p - (uintptr_t)pcmbuf_buffer;
254}
255
256/* Return a chunk descriptor for a byte index in the buffer */
257static struct chunkdesc * index_chunkdesc(size_t index)
258{
259 return &pcmbuf_descriptors[index / PCMBUF_CHUNK_SIZE];
260}
261
262/* Return the first byte of a chunk for a byte index in the buffer, offset by 'offset'
263 chunks */
264static size_t index_chunk_offs(size_t index, int offset)
265{
266 int i = index / PCMBUF_CHUNK_SIZE;
267
268 if (offset != 0)
269 {
270 i = (i + offset) % (int)pcmbuf_desc_count;
271
272 /* remainder => modulus */
273 if (i < 0)
274 i += pcmbuf_desc_count;
275 }
276
277 return i * PCMBUF_CHUNK_SIZE;
278}
279
280/* Test if a buffer index lies within the committed data region */
281static bool index_committed(size_t index)
282{
283 if (index == INVALID_BUF_INDEX)
284 return false;
285
286 size_t ridx = chunk_ridx;
287 size_t widx = chunk_widx;
288
289 if (widx < ridx)
290 {
291 widx += pcmbuf_size;
292
293 if (index < ridx)
294 index += pcmbuf_size;
295 }
296
297 return index >= ridx && index < widx;
298}
299
300/* Snip the tail of buffer at chunk of specified index plus chunk offset */
301void snip_buffer_tail(size_t index, int offset)
302{
303 /* Call with PCM lockout */
304 if (index == INVALID_BUF_INDEX)
305 return;
306
307 index = index_chunk_offs(index, offset);
308
309 if (!index_committed(index) && index != chunk_widx)
310 return;
311
312 chunk_widx = index;
313 pcmbuf_bytes_waiting = 0;
314 index_chunkdesc(index)->pos_key = 0;
315
316#ifdef HAVE_CROSSFADE
317 /* Kill crossfade if it would now be operating in the void */
318 if (crossfade_status != CROSSFADE_INACTIVE &&
319 !index_committed(crossfade_widx) && crossfade_widx != chunk_widx)
320 {
321 crossfade_cancel();
322 }
323#endif /* HAVE_CROSSFADE */
324}
325
326
327/** Accept new PCM data */
328
329/* Split the uncommitted data as needed into chunks, stopping when uncommitted
330 data is below the threshold */
331static void commit_chunks(size_t threshold)
332{
333 size_t index = chunk_widx;
334 size_t end_index = index + pcmbuf_bytes_waiting;
335
336 /* Copy to the beginning of the buffer all data that must wrap */
337 if (end_index > pcmbuf_size)
338 memcpy(pcmbuf_buffer, pcmbuf_guardbuf, end_index - pcmbuf_size);
339
340 struct chunkdesc *desc = index_chunkdesc(index);
341
342 do
343 {
344 size_t size = MIN(pcmbuf_bytes_waiting, PCMBUF_CHUNK_SIZE);
345 pcmbuf_bytes_waiting -= size;
346
347 /* Fill in the values in the new buffer chunk */
348 desc->size = (uint16_t)size;
349
350 /* Advance the current write chunk and make it available to the
351 PCM callback */
352 chunk_widx = index = index_next(index);
353 desc = index_chunkdesc(index);
354
355 /* Reset it before using it */
356 desc->pos_key = 0;
357 }
358 while (pcmbuf_bytes_waiting >= threshold);
359}
360
361/* If uncommitted data count is above or equal to the threshold, commit it */
362static FORCE_INLINE void commit_if_needed(size_t threshold)
363{
364 if (pcmbuf_bytes_waiting >= threshold)
365 commit_chunks(threshold);
366}
367
368/* Place positioning information in the chunk */
369static void stamp_chunk(struct chunkdesc *desc, unsigned long elapsed,
370 off_t offset)
371{
372 /* One-time stamping of a given chunk by the same track - new track may
373 overwrite */
374 unsigned int key = position_key;
375
376 if (desc->pos_key != key)
377 {
378 desc->pos_key = key;
379 desc->elapsed = elapsed;
380 desc->offset = offset;
381 }
382}
383
384/* Set priority of the codec thread */
385#ifdef HAVE_PRIORITY_SCHEDULING
386/*
387 * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */
388static void boost_codec_thread(int pcm_fill_state)
389{
390 static const int8_t prios[11] =
391 {
392 PRIORITY_PLAYBACK_MAX, /* 0 - 10% */
393 PRIORITY_PLAYBACK_MAX+1, /* 10 - 20% */
394 PRIORITY_PLAYBACK_MAX+3, /* 20 - 30% */
395 PRIORITY_PLAYBACK_MAX+5, /* 30 - 40% */
396 PRIORITY_PLAYBACK_MAX+7, /* 40 - 50% */
397 PRIORITY_PLAYBACK_MAX+8, /* 50 - 60% */
398 PRIORITY_PLAYBACK_MAX+9, /* 60 - 70% */
399 /* raising priority above 70% shouldn't be needed */
400 PRIORITY_PLAYBACK, /* 70 - 80% */
401 PRIORITY_PLAYBACK, /* 80 - 90% */
402 PRIORITY_PLAYBACK, /* 90 -100% */
403 PRIORITY_PLAYBACK, /* 100% */
404 };
405
406 int new_prio = prios[pcm_fill_state];
407
408 /* Keep voice and codec threads at the same priority or else voice
409 * will starve if the codec thread's priority is boosted. */
410 if (new_prio != codec_thread_priority)
411 {
412 codec_thread_set_priority(new_prio);
413 voice_thread_set_priority(new_prio);
414 codec_thread_priority = new_prio;
415 }
416}
417#else
418#define boost_codec_thread(pcm_fill_state) do{}while(0)
419#endif /* HAVE_PRIORITY_SCHEDULING */
420
421/* Get the next available buffer and size - assumes adequate space exists */
422static void * get_write_buffer(size_t *size)
423{
424 /* Obtain current chunk fill address */
425 size_t index = chunk_widx + pcmbuf_bytes_waiting;
426 size_t index_end = pcmbuf_size + PCMBUF_GUARD_SIZE;
427
428 /* Get count to the end of the buffer where a wrap will happen +
429 the guard */
430 size_t endsize = index_end - index;
431
432 /* Return available unwrapped space */
433 *size = MIN(*size, endsize);
434
435 return index_buffer(index);
436}
437
438/* Commit outstanding data leaving less than a chunk size remaining */
439static void commit_write_buffer(size_t size)
440{
441 /* Add this data and commit if one or more chunks are ready */
442 pcmbuf_bytes_waiting += size;
443 commit_if_needed(COMMIT_CHUNKS);
444}
445
446/* Request space in the buffer for writing output samples */
447void * pcmbuf_request_buffer(int *count)
448{
449 size_t size = *count * PCMBUF_SAMPLE_SIZE;
450
451#ifdef HAVE_CROSSFADE
452 /* We're going to crossfade to a new track, which is now on its way */
453 if (crossfade_status > CROSSFADE_ACTIVE)
454 crossfade_start();
455
456 /* If crossfade has begun, put the new track samples in the crossfade
457 buffer area */
458 if (crossfade_status != CROSSFADE_INACTIVE && size > CROSSFADE_BUFSIZE)
459 size = CROSSFADE_BUFSIZE;
460 else
461#endif /* HAVE_CROSSFADE */
462 if (size > PCMBUF_MAX_BUFFER)
463 size = PCMBUF_MAX_BUFFER; /* constrain request */
464
465 enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK);
466 size_t remaining = pcmbuf_unplayed_bytes();
467
468 /* Need to have length bytes to prevent wrapping overwriting - leave one
469 descriptor free to guard so that 0 != full in ring buffer */
470 size_t freespace = pcmbuf_free();
471
472 if (pcmbuf_sync_position)
473 audio_pcmbuf_sync_position();
474
475 if (freespace < size + PCMBUF_CHUNK_SIZE)
476 return NULL;
477
478 /* Maintain the buffer level above the watermark */
479 if (status != CHANNEL_STOPPED)
480 {
481 if (low_latency_mode)
482 {
483 /* 1/4s latency. */
484 if (remaining > DATA_LEVEL(1))
485 return NULL;
486 }
487
488 /* Boost CPU if necessary */
489 size_t realrem = pcmbuf_size - freespace;
490
491 if (realrem < pcmbuf_watermark)
492 trigger_cpu_boost();
493
494 boost_codec_thread(realrem*10 / pcmbuf_size);
495 }
496 else /* !playing */
497 {
498 /* Boost CPU for pre-buffer */
499 trigger_cpu_boost();
500
501 /* If pre-buffered to the watermark, start playback */
502 if (!pcmbuf_data_critical())
503 start_audio_playback();
504 }
505
506 void *buf;
507
508#ifdef HAVE_CROSSFADE
509 if (crossfade_status != CROSSFADE_INACTIVE)
510 {
511 crossfade_bufidx = index_chunk_offs(chunk_ridx, -1);
512 buf = index_buffer(crossfade_bufidx); /* always CROSSFADE_BUFSIZE */
513 }
514 else
515#endif
516 {
517 /* Give the maximum amount available if there's more */
518 if (size + PCMBUF_CHUNK_SIZE < freespace)
519 size = freespace - PCMBUF_CHUNK_SIZE;
520
521 buf = get_write_buffer(&size);
522 }
523
524 *count = size / PCMBUF_SAMPLE_SIZE;
525 return buf;
526}
527
528/* Handle new samples to the buffer */
529void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset)
530{
531 size_t size = count * PCMBUF_SAMPLE_SIZE;
532
533#ifdef HAVE_CROSSFADE
534 if (crossfade_status != CROSSFADE_INACTIVE)
535 {
536 write_to_crossfade(size, elapsed, offset);
537 }
538 else
539#endif
540 {
541 stamp_chunk(index_chunkdesc(chunk_widx), elapsed, offset);
542 commit_write_buffer(size);
543 }
544
545 /* Revert to position updates by PCM */
546 pcmbuf_sync_position = false;
547}
548
549
550/** Init */
551static unsigned int get_next_required_pcmbuf_chunks(void)
552{
553 size_t size = MIN_BUFFER_SIZE;
554
555#ifdef HAVE_CROSSFADE
556 if (crossfade_enable_request != CROSSFADE_ENABLE_OFF)
557 {
558 size_t seconds = global_settings.crossfade_fade_out_delay +
559 global_settings.crossfade_fade_out_duration;
560 size += seconds * BYTERATE;
561 }
562#endif
563
564 logf("pcmbuf len: %lu", (unsigned long)(size / BYTERATE));
565 return size / PCMBUF_CHUNK_SIZE;
566}
567
568/* Initialize the ringbuffer state */
569static void init_buffer_state(void)
570{
571 /* Reset counters */
572 chunk_ridx = chunk_widx = 0;
573 pcmbuf_bytes_waiting = 0;
574
575 /* Reset first descriptor */
576 if (pcmbuf_descriptors)
577 pcmbuf_descriptors->pos_key = 0;
578
579 /* Clear change notification */
580 chunk_transidx = INVALID_BUF_INDEX;
581}
582
583/* call prior to init to get bytes required */
584size_t pcmbuf_size_reqd(void)
585{
586 return get_next_required_pcmbuf_chunks() * PCMBUF_CHUNK_SIZE;
587}
588
589/* Initialize the PCM buffer. The structure looks like this:
590 * ...|---------PCMBUF---------|GUARDBUF|DESCS| */
591size_t pcmbuf_init(void *bufend)
592{
593 void *bufstart;
594
595 /* Set up the buffers */
596 pcmbuf_desc_count = get_next_required_pcmbuf_chunks();
597 pcmbuf_size = pcmbuf_desc_count * PCMBUF_CHUNK_SIZE;
598 pcmbuf_descriptors = (struct chunkdesc *)bufend - pcmbuf_desc_count;
599
600 pcmbuf_buffer = (void *)pcmbuf_descriptors -
601 pcmbuf_size - PCMBUF_GUARD_SIZE;
602
603 /* Mem-align buffer chunks for more efficient handling in lower layers */
604 pcmbuf_buffer = ALIGN_DOWN(pcmbuf_buffer, (uintptr_t)MEM_ALIGN_SIZE);
605
606 pcmbuf_guardbuf = pcmbuf_buffer + pcmbuf_size;
607 bufstart = pcmbuf_buffer;
608
609#ifdef HAVE_CROSSFADE
610 pcmbuf_finish_crossfade_enable();
611#else
612 pcmbuf_watermark = PCMBUF_WATERMARK;
613#endif /* HAVE_CROSSFADE */
614
615 init_buffer_state();
616
617 pcmbuf_soft_mode(false);
618
619 return bufend - bufstart;
620}
621
622
623/** Track change */
624
625/* Place a track change notification in a specific descriptor or post it
626 immediately if the buffer is empty or the index is invalid */
627static void pcmbuf_monitor_track_change_ex(size_t index)
628{
629 /* Call with PCM lockout */
630 if (chunk_ridx != chunk_widx && index != INVALID_BUF_INDEX)
631 {
632 /* If monitoring, set flag for one previous to specified chunk */
633 index = index_chunk_offs(index, -1);
634
635 /* Ensure PCM playback hasn't already played this out */
636 if (index_committed(index))
637 {
638 chunk_transidx = index;
639 return;
640 }
641 }
642
643 /* Post now if buffer is no longer coming up */
644 chunk_transidx = INVALID_BUF_INDEX;
645 audio_pcmbuf_track_change(false);
646}
647
648/* Clear end of track and optionally the positioning info for all data */
649static void pcmbuf_cancel_track_change(bool position)
650{
651 /* Call with PCM lockout */
652 snip_buffer_tail(chunk_transidx, 1);
653
654 chunk_transidx = INVALID_BUF_INDEX;
655
656 if (!position)
657 return;
658
659 size_t index = chunk_ridx;
660
661 while (1)
662 {
663 index_chunkdesc(index)->pos_key = 0;
664
665 if (index == chunk_widx)
666 break;
667
668 index = index_next(index);
669 }
670}
671
672/* Place a track change notification at the end of the buffer or post it
673 immediately if the buffer is empty */
674void pcmbuf_monitor_track_change(bool monitor)
675{
676 pcm_play_lock();
677
678 if (monitor)
679 pcmbuf_monitor_track_change_ex(chunk_widx);
680 else
681 pcmbuf_cancel_track_change(false);
682
683 pcm_play_unlock();
684}
685
686void pcmbuf_start_track_change(enum pcm_track_change_type type)
687{
688 /* Commit all outstanding data before starting next track - tracks don't
689 comingle inside a single buffer chunk */
690 commit_if_needed(COMMIT_ALL_DATA);
691
692 if (type == TRACK_CHANGE_AUTO_PILEUP)
693 {
694 /* Fill might not have been above watermark */
695 start_audio_playback();
696 return;
697 }
698
699#ifdef HAVE_CROSSFADE
700 bool crossfade = false;
701#endif
702 bool auto_skip = type != TRACK_CHANGE_MANUAL;
703
704 /* Update position key so that:
705 1) Positions are keyed to the track to which they belong for sync
706 purposes
707
708 2) Buffers stamped with the outgoing track's positions are restamped
709 to the incoming track's positions when crossfading
710 */
711 if (++position_key > POSITION_KEY_MAX)
712 position_key = 1;
713
714 if (type == TRACK_CHANGE_END_OF_DATA)
715 {
716 crossfade_cancel();
717
718 /* Fill might not have been above watermark */
719 start_audio_playback();
720 }
721#ifdef HAVE_CROSSFADE
722 /* Determine whether this track change needs to crossfaded and how */
723 else if (crossfade_setting != CROSSFADE_ENABLE_OFF)
724 {
725 if (crossfade_status == CROSSFADE_INACTIVE &&
726 pcmbuf_unplayed_bytes() >= DATA_LEVEL(2) &&
727 !low_latency_mode)
728 {
729 switch (crossfade_setting)
730 {
731 case CROSSFADE_ENABLE_AUTOSKIP:
732 crossfade = auto_skip;
733 break;
734 case CROSSFADE_ENABLE_MANSKIP:
735 crossfade = !auto_skip;
736 break;
737 case CROSSFADE_ENABLE_SHUFFLE:
738 crossfade = global_settings.playlist_shuffle;
739 break;
740 case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP:
741 crossfade = global_settings.playlist_shuffle || !auto_skip;
742 break;
743 case CROSSFADE_ENABLE_ALWAYS:
744 crossfade = true;
745 break;
746 }
747 }
748 }
749
750 if (crossfade)
751 {
752 logf("crossfade track change");
753
754 /* Don't enable mix mode when skipping tracks manually */
755 crossfade_mixmode = auto_skip &&
756 global_settings.crossfade_fade_out_mixmode;
757
758 crossfade_auto_skip = auto_skip;
759
760 crossfade_status = CROSSFADE_START;
761
762 pcmbuf_monitor_track_change(auto_skip);
763
764 trigger_cpu_boost();
765 }
766 else
767#endif /* HAVE_CROSSFADE */
768 if (auto_skip)
769 {
770 /* The codec is moving on to the next track, but the current track will
771 * continue to play, so mark the last write chunk as the last one in
772 * the track */
773 logf("gapless track change");
774
775#ifdef HAVE_CROSSFADE
776 if (crossfade_status == CROSSFADE_ACTIVE)
777 crossfade_status = CROSSFADE_CONTINUE;
778#endif
779
780 pcmbuf_monitor_track_change(true);
781 }
782 else
783 {
784 /* Discard old data; caller needs no transition notification */
785 logf("manual track change");
786 pcmbuf_play_stop();
787 }
788}
789
790
791/** Playback */
792
793/* PCM driver callback */
794static void pcmbuf_pcm_callback(const void **start, size_t *size)
795{
796 /*- Process the chunk that just finished -*/
797 size_t index = chunk_ridx;
798 struct chunkdesc *desc = current_desc;
799
800 if (desc)
801 {
802 /* If last chunk in the track, notify of track change */
803 if (index == chunk_transidx)
804 {
805 chunk_transidx = INVALID_BUF_INDEX;
806 audio_pcmbuf_track_change(true);
807 }
808
809 /* Free it for reuse */
810 chunk_ridx = index = index_next(index);
811 }
812
813 /*- Process the new one -*/
814 if (index != chunk_widx && !fade_out_complete)
815 {
816 current_desc = desc = index_chunkdesc(index);
817
818 *start = index_buffer(index);
819 *size = desc->size;
820
821 if (desc->pos_key != 0)
822 {
823 /* Positioning chunk - notify playback */
824 audio_pcmbuf_position_callback(desc->elapsed, desc->offset,
825 desc->pos_key);
826 }
827 }
828}
829
830/* Force playback */
831void pcmbuf_play_start(void)
832{
833 logf("pcmbuf_play_start");
834
835 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED &&
836 chunk_widx != chunk_ridx)
837 {
838 current_desc = NULL;
839 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback,
840 NULL, 0);
841 }
842}
843
844/* Stop channel, empty and reset buffer */
845void pcmbuf_play_stop(void)
846{
847 logf("pcmbuf_play_stop");
848
849 /* Reset channel */
850 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
851
852 /* Reset buffer */
853 init_buffer_state();
854
855 /* Revert to position updates by PCM */
856 pcmbuf_sync_position = false;
857
858 /* Fader OFF */
859 crossfade_cancel();
860
861 /* Can unboost the codec thread here no matter who's calling,
862 * pretend full pcm buffer to unboost */
863 boost_codec_thread(10);
864}
865
866void pcmbuf_pause(bool pause)
867{
868 logf("pcmbuf_pause: %s", pause?"pause":"play");
869
870 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED)
871 mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
872 else if (!pause)
873 pcmbuf_play_start();
874}
875
876
877/** Crossfade */
878
879#ifdef HAVE_CROSSFADE
880
881/* Initialize a fader */
882static void mixfader_init(struct mixfader *faderp, int32_t start_factor,
883 int32_t end_factor, size_t size, bool alloc)
884{
885 /* Linear fade */
886 faderp->endfac = end_factor;
887 faderp->nsamp2 = size / PCMBUF_SAMPLE_SIZE * 2;
888 faderp->alloc = alloc;
889
890 if (faderp->nsamp2 == 0)
891 {
892 /* No data; set up as if fader finished the fade */
893 faderp->factor = end_factor;
894 return;
895 }
896
897 int32_t dfact2 = 2*abs(end_factor - start_factor);
898 faderp->factor = start_factor;
899 faderp->ferr = dfact2 / 2;
900 faderp->dfquo = dfact2 / faderp->nsamp2;
901 faderp->dfrem = dfact2 - faderp->dfquo*faderp->nsamp2;
902 faderp->dfinc = end_factor < start_factor ? -1 : +1;
903 faderp->dfquo *= faderp->dfinc;
904}
905
906/* Query if the fader has finished its envelope */
907static inline bool mixfader_finished(const struct mixfader *faderp)
908{
909 return faderp->factor == faderp->endfac;
910}
911
912/* Step fader by one sample */
913static inline void mixfader_step(struct mixfader *faderp)
914{
915 if (mixfader_finished(faderp))
916 return;
917
918 faderp->factor += faderp->dfquo;
919 faderp->ferr += faderp->dfrem;
920
921 if (faderp->ferr >= faderp->nsamp2)
922 {
923 faderp->factor += faderp->dfinc;
924 faderp->ferr -= faderp->nsamp2;
925 }
926}
927
928static FORCE_INLINE int32_t mixfade_sample(const struct mixfader *faderp, int32_t s)
929{
930 return (faderp->factor * s + MIXFADE_UNITY/2) >> MIXFADE_UNITY_BITS;
931}
932
933/* Cancel crossfade operation */
934static void crossfade_cancel(void)
935{
936 crossfade_status = CROSSFADE_INACTIVE;
937 crossfade_widx = INVALID_BUF_INDEX;
938}
939
940/* Find the buffer index that's 'size' bytes away from 'index' */
941static size_t crossfade_find_index(size_t index, size_t size)
942{
943 if (index != INVALID_BUF_INDEX)
944 {
945 size_t i = index_chunk_offs(index, 0);
946 size += index - i;
947
948 while (i != chunk_widx)
949 {
950 size_t desc_size = index_chunkdesc(i)->size;
951
952 if (size < desc_size)
953 {
954 index = i + size;
955 break;
956 }
957
958 size -= desc_size;
959 i = index_next(i);
960 }
961 }
962
963 return index;
964}
965
966/* Align the needed buffer area up to the end of existing data */
967static size_t crossfade_find_buftail(bool auto_skip, size_t buffer_rem,
968 size_t buffer_need, size_t *buffer_rem_outp)
969{
970 size_t index = chunk_ridx;
971
972 if (buffer_rem > buffer_need)
973 {
974 size_t distance;
975
976 if (auto_skip)
977 {
978 /* Automatic track changes only modify the last part of the buffer,
979 * so find the right chunk and sample to start the crossfade */
980 distance = buffer_rem - buffer_need;
981 buffer_rem = buffer_need;
982 }
983 else
984 {
985 /* Manual skips occur immediately, but give 1/5s to process */
986 distance = MIN(BYTERATE / 5, buffer_rem);
987 buffer_rem -= distance;
988 }
989
990 index = crossfade_find_index(index, distance);
991 }
992
993 if (buffer_rem_outp)
994 *buffer_rem_outp = buffer_rem;
995
996 return index;
997}
998
999/* Run a fader on some buffers */
1000static void crossfade_mix_fade(struct mixfader *faderp, size_t size,
1001 void *input_buf, size_t *out_index,
1002 unsigned long elapsed, off_t offset)
1003{
1004 if (size == 0)
1005 return;
1006
1007 size_t index = *out_index;
1008
1009 if (index == INVALID_BUF_INDEX)
1010 return;
1011
1012 int16_t *inbuf = input_buf;
1013
1014 bool alloced = inbuf && faderp->alloc &&
1015 index_chunk_offs(index, 0) == chunk_widx;
1016
1017 while (size)
1018 {
1019 struct chunkdesc *desc = index_chunkdesc(index);
1020 int16_t *outbuf = index_buffer(index);
1021
1022 switch (offset)
1023 {
1024 case MIXFADE_NULLIFY_POS:
1025 /* Stop position updates for the chunk */
1026 desc->pos_key = 0;
1027 break;
1028 case MIXFADE_KEEP_POS:
1029 /* Keep position info as it is */
1030 break;
1031 default:
1032 /* Replace position info */
1033 stamp_chunk(desc, elapsed, offset);
1034 }
1035
1036 size_t amount = (alloced ? PCMBUF_CHUNK_SIZE : desc->size)
1037 - (index % PCMBUF_CHUNK_SIZE);
1038 int16_t *chunkend = SKIPBYTES(outbuf, amount);
1039
1040 if (size < amount)
1041 amount = size;
1042
1043 size -= amount;
1044
1045 if (alloced)
1046 {
1047 /* Fade the input buffer into the new destination chunk */
1048 for (size_t s = amount; s != 0; s -= PCMBUF_SAMPLE_SIZE)
1049 {
1050 *outbuf++ = mixfade_sample(faderp, *inbuf++);
1051 *outbuf++ = mixfade_sample(faderp, *inbuf++);
1052 mixfader_step(faderp);
1053 }
1054
1055 commit_write_buffer(amount);
1056 }
1057 else if (inbuf)
1058 {
1059 /* Fade the input buffer and mix into the destination chunk */
1060 for (size_t s = amount; s != 0; s -= PCMBUF_SAMPLE_SIZE)
1061 {
1062 int32_t left = outbuf[0];
1063 int32_t right = outbuf[1];
1064 left += mixfade_sample(faderp, *inbuf++);
1065 right += mixfade_sample(faderp, *inbuf++);
1066 *outbuf++ = clip_sample_16(left);
1067 *outbuf++ = clip_sample_16(right);
1068 mixfader_step(faderp);
1069 }
1070 }
1071 else
1072 {
1073 /* Fade the chunk in place */
1074 for (size_t s = amount; s != 0; s -= PCMBUF_SAMPLE_SIZE)
1075 {
1076 int32_t left = outbuf[0];
1077 int32_t right = outbuf[1];
1078 *outbuf++ = mixfade_sample(faderp, left);
1079 *outbuf++ = mixfade_sample(faderp, right);
1080 mixfader_step(faderp);
1081 }
1082 }
1083
1084 if (outbuf < chunkend)
1085 {
1086 index += amount;
1087 continue;
1088 }
1089
1090 /* Move destination to next chunk as needed */
1091 index = index_next(index);
1092
1093 if (index == chunk_widx)
1094 {
1095 /* End of existing data */
1096 if (!inbuf || !faderp->alloc)
1097 {
1098 index = INVALID_BUF_INDEX;
1099 break;
1100 }
1101
1102 alloced = true;
1103 }
1104 }
1105
1106 *out_index = index;
1107}
1108
1109/* Initializes crossfader, calculates all necessary parameters and performs
1110 * fade-out with the PCM buffer. */
1111static void crossfade_start(void)
1112{
1113 logf("crossfade_start");
1114
1115 pcm_play_lock();
1116
1117 if (crossfade_status == CROSSFADE_CONTINUE)
1118 {
1119 logf("fade-in continuing");
1120
1121 crossfade_status = CROSSFADE_ACTIVE;
1122
1123 if (crossfade_auto_skip)
1124 pcmbuf_monitor_track_change_ex(crossfade_widx);
1125
1126 pcm_play_unlock();
1127 return;
1128 }
1129
1130 /* Initialize the crossfade buffer size to all of the buffered data that
1131 * has not yet been sent to the DMA */
1132 size_t unplayed = pcmbuf_unplayed_bytes();
1133
1134 /* Reject crossfade if less than .5s of data */
1135 if (unplayed < DATA_LEVEL(2))
1136 {
1137 logf("crossfade rejected");
1138 crossfade_cancel();
1139 pcm_play_unlock();
1140 return;
1141 }
1142
1143 /* Fading will happen */
1144 crossfade_status = CROSSFADE_ACTIVE;
1145
1146 /* Get fade info from settings. */
1147 size_t fade_out_delay = global_settings.crossfade_fade_out_delay * BYTERATE;
1148 size_t fade_out_rem = global_settings.crossfade_fade_out_duration * BYTERATE;
1149 size_t fade_in_delay = global_settings.crossfade_fade_in_delay * BYTERATE;
1150 size_t fade_in_duration = global_settings.crossfade_fade_in_duration * BYTERATE;
1151
1152 if (!crossfade_auto_skip)
1153 {
1154 /* Forego fade-in delay on manual skip - do the best to preserve auto skip
1155 relationship */
1156 fade_out_delay -= MIN(fade_out_delay, fade_in_delay);
1157 fade_in_delay = 0;
1158 }
1159
1160 size_t fade_out_need = fade_out_delay + fade_out_rem;
1161
1162 if (!crossfade_mixmode)
1163 {
1164 /* Completely process the crossfade fade-out effect with current PCM buffer */
1165 size_t buffer_rem;
1166 size_t index = crossfade_find_buftail(crossfade_auto_skip, unplayed,
1167 fade_out_need, &buffer_rem);
1168
1169 pcm_play_unlock();
1170
1171 if (buffer_rem < fade_out_need)
1172 {
1173 /* Existing buffers are short */
1174 size_t fade_out_short = fade_out_need - buffer_rem;
1175
1176 if (fade_out_delay >= fade_out_short)
1177 {
1178 /* Truncate fade-out delay */
1179 fade_out_delay -= fade_out_short;
1180 }
1181 else
1182 {
1183 /* Truncate fade-out and eliminate fade-out delay */
1184 fade_out_rem = buffer_rem;
1185 fade_out_delay = 0;
1186 }
1187
1188 fade_out_need = fade_out_delay + fade_out_rem;
1189 }
1190
1191 /* Find the right chunk and sample to start fading out */
1192 index = crossfade_find_index(index, fade_out_delay);
1193
1194 /* Fade out the specified amount of the already processed audio */
1195 struct mixfader outfader;
1196
1197 mixfader_init(&outfader, MIXFADE_UNITY, 0, fade_out_rem, false);
1198 crossfade_mix_fade(&outfader, fade_out_rem, NULL, &index, 0,
1199 MIXFADE_KEEP_POS);
1200
1201 /* Zero-out the rest of the buffer */
1202 crossfade_mix_fade(&outfader, pcmbuf_size, NULL, &index, 0,
1203 MIXFADE_NULLIFY_POS);
1204
1205 pcm_play_lock();
1206 }
1207
1208 /* Initialize fade-in counters */
1209 mixfader_init(&crossfade_infader, 0, MIXFADE_UNITY, fade_in_duration, true);
1210
1211 /* Find the right chunk and sample to start fading in - redo from read
1212 chunk in case original position were/was overrun in callback - the
1213 track change event _must not_ ever fail to happen */
1214 unplayed = pcmbuf_unplayed_bytes() + fade_in_delay;
1215
1216 crossfade_widx = crossfade_find_buftail(crossfade_auto_skip, unplayed,
1217 fade_out_need, NULL);
1218
1219 /* Move track transistion to chunk before the first one of incoming track */
1220 if (crossfade_auto_skip)
1221 pcmbuf_monitor_track_change_ex(crossfade_widx);
1222
1223 pcm_play_unlock();
1224
1225 logf("crossfade_start done!");
1226}
1227
1228/* Perform fade-in of new track */
1229static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset)
1230{
1231 /* Mix the data */
1232 crossfade_mix_fade(&crossfade_infader, size, index_buffer(crossfade_bufidx),
1233 &crossfade_widx, elapsed, offset);
1234
1235 /* If no more fading-in to do, stop the crossfade */
1236 if (mixfader_finished(&crossfade_infader) &&
1237 index_chunk_offs(crossfade_widx, 0) == chunk_widx)
1238 {
1239 crossfade_cancel();
1240 }
1241}
1242
1243static void pcmbuf_finish_crossfade_enable(void)
1244{
1245 /* Copy the pending setting over now */
1246 crossfade_setting = crossfade_enable_request;
1247
1248 pcmbuf_watermark = (crossfade_setting != CROSSFADE_ENABLE_OFF && pcmbuf_size) ?
1249 /* If crossfading, try to keep the buffer full other than 1 second */
1250 (pcmbuf_size - BYTERATE) :
1251 /* Otherwise, just use the default */
1252 PCMBUF_WATERMARK;
1253}
1254
1255void pcmbuf_request_crossfade_enable(int setting)
1256{
1257 /* Next setting to be used, not applied now */
1258 crossfade_enable_request = setting;
1259}
1260
1261bool pcmbuf_is_same_size(void)
1262{
1263 /* if pcmbuf_buffer is NULL, then not set up yet even once so always */
1264 bool same_size = pcmbuf_buffer ?
1265 (get_next_required_pcmbuf_chunks() == pcmbuf_desc_count) : true;
1266
1267 /* no buffer change needed, so finish crossfade setup now */
1268 if (same_size)
1269 pcmbuf_finish_crossfade_enable();
1270
1271 return same_size;
1272}
1273#endif /* HAVE_CROSSFADE */
1274
1275
1276/** Debug menu, other metrics */
1277
1278/* Amount of bytes left in the buffer, accounting for uncommitted bytes */
1279size_t pcmbuf_free(void)
1280{
1281 return pcmbuf_size - pcmbuf_unplayed_bytes() - pcmbuf_bytes_waiting;
1282}
1283
1284/* Data bytes allocated for buffer */
1285size_t pcmbuf_get_bufsize(void)
1286{
1287 return pcmbuf_size;
1288}
1289
1290/* Number of committed descriptors */
1291int pcmbuf_used_descs(void)
1292{
1293 return pcmbuf_unplayed_bytes() / PCMBUF_CHUNK_SIZE;
1294}
1295
1296/* Total number of descriptors allocated */
1297int pcmbuf_descs(void)
1298{
1299 return pcmbuf_desc_count;
1300}
1301
1302
1303/** Fading and channel volume control */
1304
1305/* Sync the channel amplitude to all states */
1306static void pcmbuf_update_volume(void)
1307{
1308 unsigned int vol = fade_vol;
1309
1310 if (soft_mode)
1311 vol >>= 2;
1312
1313 mixer_channel_set_amplitude(PCM_MIXER_CHAN_PLAYBACK, vol);
1314}
1315
1316/* Tick that does the fade for the playback channel */
1317static void pcmbuf_fade_tick(void)
1318{
1319 /* ~1/3 second for full range fade */
1320 const unsigned int fade_step = MIX_AMP_UNITY / (HZ / 3);
1321
1322 if (fade_state == PCM_FADING_IN)
1323 fade_vol += MIN(fade_step, MIX_AMP_UNITY - fade_vol);
1324 else if (fade_state == PCM_FADING_OUT)
1325 fade_vol -= MIN(fade_step, fade_vol - MIX_AMP_MUTE);
1326
1327 pcmbuf_update_volume();
1328
1329 if (fade_vol == MIX_AMP_MUTE || fade_vol == MIX_AMP_UNITY)
1330 {
1331 /* Fade is complete */
1332 tick_remove_task(pcmbuf_fade_tick);
1333 if (fade_state == PCM_FADING_OUT)
1334 {
1335 /* Tell PCM to stop at its earliest convenience */
1336 fade_out_complete = true;
1337 }
1338
1339 fade_state = PCM_NOT_FADING;
1340 }
1341}
1342
1343/* Fade channel in or out in the background */
1344void pcmbuf_fade(bool fade, bool in)
1345{
1346 /* Must pause any active fade */
1347 pcm_play_lock();
1348
1349 if (fade_state != PCM_NOT_FADING)
1350 tick_remove_task(pcmbuf_fade_tick);
1351
1352 fade_out_complete = false;
1353
1354 pcm_play_unlock();
1355
1356 if (!fade)
1357 {
1358 /* Simply set the level */
1359 fade_state = PCM_NOT_FADING;
1360 fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE;
1361 pcmbuf_update_volume();
1362 }
1363 else
1364 {
1365 /* Set direction and resume fade from current point */
1366 fade_state = in ? PCM_FADING_IN : PCM_FADING_OUT;
1367 tick_add_task(pcmbuf_fade_tick);
1368 }
1369}
1370
1371/* Return 'true' if fade is in progress */
1372bool pcmbuf_fading(void)
1373{
1374 return fade_state != PCM_NOT_FADING;
1375}
1376
1377/* Quiet-down the channel if 'shhh' is true or else play at normal level */
1378void pcmbuf_soft_mode(bool shhh)
1379{
1380 /* Have to block the tick or improper order could leave volume in soft
1381 mode if fading reads the old value first but updates after us. */
1382 int res = fade_state != PCM_NOT_FADING ?
1383 tick_remove_task(pcmbuf_fade_tick) : -1;
1384
1385 soft_mode = shhh;
1386 pcmbuf_update_volume();
1387
1388 if (res == 0)
1389 tick_add_task(pcmbuf_fade_tick);
1390}
1391
1392
1393/** Time and position */
1394
1395/* Return the current position key value */
1396unsigned int pcmbuf_get_position_key(void)
1397{
1398 return position_key;
1399}
1400
1401/* Set position updates to be synchronous and immediate in addition to during
1402 PCM frames - cancelled upon first codec insert or upon stopping */
1403void pcmbuf_sync_position_update(void)
1404{
1405 pcmbuf_sync_position = true;
1406}
1407
1408
1409
1410/** Misc */
1411
1412bool pcmbuf_is_lowdata(void)
1413{
1414 enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK);
1415
1416 if (status != CHANNEL_PLAYING)
1417 return false;
1418
1419#ifdef HAVE_CROSSFADE
1420 if (crossfade_status != CROSSFADE_INACTIVE)
1421 return false;
1422#endif
1423
1424 return pcmbuf_data_critical();
1425}
1426
1427void pcmbuf_set_low_latency(bool state)
1428{
1429 low_latency_mode = state;
1430}
1431
1432void pcmbuf_update_frequency(void)
1433{
1434 pcmbuf_sampr = mixer_get_frequency();
1435}
1436
1437unsigned int pcmbuf_get_frequency(void)
1438{
1439 return pcmbuf_sampr;
1440}