blob: 773e97cce0a3a43793bff2d83bcc42842190fc37 [file] [log] [blame]
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000011 * Copyright (C) 2011 by Michael Sevakis
Miika Pekkarinen20b38972005-07-13 12:48:22 +000012 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000013 * 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.
Miika Pekkarinen20b38972005-07-13 12:48:22 +000017 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
Miika Pekkarinen20b38972005-07-13 12:48:22 +000022#include <stdio.h>
23#include "config.h"
Michael Sevakis6c399b82009-02-19 20:40:03 +000024#include "system.h"
Miika Pekkarinen20b38972005-07-13 12:48:22 +000025#include "debug.h"
Miika Pekkarinen20b38972005-07-13 12:48:22 +000026#include <kernel.h>
Michael Sevakis6077e5b2007-10-06 22:27:27 +000027#include "pcm.h"
Michael Sevakisa2b67032011-06-29 06:37:04 +000028#include "pcm_mixer.h"
29#include "pcmbuf.h"
Michael Sevakisa43df152011-12-04 18:19:39 +000030#include "dsp-util.h"
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +000031#include "playback.h"
Michael Sevakis65109732011-02-23 14:31:13 +000032#include "codec_thread.h"
Jeffrey Goodefaa47bf2009-10-22 00:59:42 +000033
34/* Define LOGF_ENABLE to enable logf output in this file */
35/*#define LOGF_ENABLE*/
Miika Pekkarinen20b38972005-07-13 12:48:22 +000036#include "logf.h"
Thomas Martitz35e8b142010-06-21 16:53:00 +000037#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
Miika Pekkarinen20b38972005-07-13 12:48:22 +000038#include "cpu.h"
39#endif
Miika Pekkarinenf090dc32005-07-21 11:44:00 +000040#include "settings.h"
41#include "audio.h"
Jeffrey Gooded0ac0452009-11-09 05:58:02 +000042#include "voice_thread.h"
Miika Pekkarinen20b38972005-07-13 12:48:22 +000043
Michael Sevakis6c837392016-12-28 00:06:39 -050044/* 2 channels * 2 bytes/sample, interleaved */
45#define PCMBUF_SAMPLE_SIZE (2 * 2)
46
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000047/* 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
Michael Sevakis11cbffa2011-09-30 06:46:19 +000051
Michael Sevakisc9bcbe22012-03-27 19:52:15 -040052/* Small guard buf to give decent space near end */
53#define PCMBUF_GUARD_SIZE (PCMBUF_CHUNK_SIZE / 8)
Jeffrey Goode04b01e12009-11-05 21:59:36 +000054
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000055/* Mnemonics for common data commit thresholds */
56#define COMMIT_CHUNKS PCMBUF_CHUNK_SIZE
57#define COMMIT_ALL_DATA 1u
58
Michael Sevakisc9bcbe22012-03-27 19:52:15 -040059/* 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 */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000069
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -050070/* Return data level in 1/4-second increments */
Michael Sevakisd37bf242013-05-23 13:58:51 -040071#define DATA_LEVEL(quarter_secs) (pcmbuf_sampr * (quarter_secs))
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -050072
Michael Sevakis6c837392016-12-28 00:06:39 -050073/* Number of bytes played per second */
74#define BYTERATE (pcmbuf_sampr * PCMBUF_SAMPLE_SIZE)
Jeffrey Goodeba9280d2009-11-16 04:42:34 +000075
Björn Stenberg6427d122009-01-10 21:10:56 +000076#if MEMORYSIZE > 2
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000077/* Keep watermark high for large memory target - at least (2s) */
Jeffrey Goodeba9280d2009-11-16 04:42:34 +000078#define PCMBUF_WATERMARK (BYTERATE * 2)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000079#define MIN_BUFFER_SIZE (BYTERATE * 3)
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -050080/* 1 seconds of buffer is low data */
81#define LOW_DATA DATA_LEVEL(4)
Björn Stenberg6427d122009-01-10 21:10:56 +000082#else
Jeffrey Goodeba9280d2009-11-16 04:42:34 +000083#define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000084#define MIN_BUFFER_SIZE (BYTERATE * 1)
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -050085/* under watermark is low data */
86#define LOW_DATA pcmbuf_watermark
Björn Stenberg6427d122009-01-10 21:10:56 +000087#endif
Miika Pekkarinen20b38972005-07-13 12:48:22 +000088
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000089/* Describes each audio packet - keep it small since there are many of them */
Jeffrey Goode8edac6e2009-11-09 05:45:05 +000090struct chunkdesc
Miika Pekkarinen20b38972005-07-13 12:48:22 +000091{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000092 uint16_t size; /* Actual size (0 < size <= PCMBUF_CHUNK_SIZE) */
Michael Sevakis6c837392016-12-28 00:06:39 -050093 uint16_t pos_key; /* Who put the position info in
94 (undefined: 0, valid: 1..POSITION_KEY_MAX) */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000095 unsigned long elapsed; /* Elapsed time to use */
96 off_t offset; /* Offset to use */
Brandon Low413da2a2006-02-07 20:38:55 +000097};
Miika Pekkarinen20b38972005-07-13 12:48:22 +000098
Michael Sevakis6c837392016-12-28 00:06:39 -050099#define POSITION_KEY_MAX UINT16_MAX
100
101
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000102/* General PCM buffer data */
103#define INVALID_BUF_INDEX ((size_t)0 - (size_t)1)
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000104
Michael Sevakisfdd36342012-04-27 16:51:54 -0400105static void *pcmbuf_buffer;
106static void *pcmbuf_guardbuf;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000107static size_t pcmbuf_size;
108static struct chunkdesc *pcmbuf_descriptors;
109static unsigned int pcmbuf_desc_count;
110static unsigned int position_key = 1;
Michael Sevakisd37bf242013-05-23 13:58:51 -0400111static unsigned int pcmbuf_sampr = 0;
Brandon Low6c0908b2006-04-23 22:54:34 +0000112
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000113static size_t chunk_ridx;
114static size_t chunk_widx;
Michael Sevakisc537d592011-04-27 03:08:23 +0000115
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000116static size_t pcmbuf_bytes_waiting;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000117static struct chunkdesc *current_desc;
Michael Sevakis6c837392016-12-28 00:06:39 -0500118static size_t chunk_transidx;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000119
Michael Sevakisd37bf242013-05-23 13:58:51 -0400120static size_t pcmbuf_watermark = 0;
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -0500121
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000122static bool low_latency_mode = false;
123
124static bool pcmbuf_sync_position = false;
Brandon Low6c0908b2006-04-23 22:54:34 +0000125
Michael Sevakisa2b67032011-06-29 06:37:04 +0000126/* Fade effect */
127static unsigned int fade_vol = MIX_AMP_UNITY;
Michael Sevakis5078d462011-08-23 01:37:59 +0000128static 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;
Michael Sevakisa2b67032011-06-29 06:37:04 +0000135
136/* Voice */
137static bool soft_mode = false;
138
Jeffrey Goode9e095342009-11-10 03:46:08 +0000139#ifdef HAVE_CROSSFADE
Brandon Low6c0908b2006-04-23 22:54:34 +0000140/* Crossfade related state */
Michael Sevakis6c837392016-12-28 00:06:39 -0500141
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000142static int crossfade_setting;
143static int crossfade_enable_request;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000144
145static enum
146{
Michael Sevakis6c837392016-12-28 00:06:39 -0500147 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 */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000151} crossfade_status = CROSSFADE_INACTIVE;
Brandon Low6c0908b2006-04-23 22:54:34 +0000152
Michael Sevakis6c837392016-12-28 00:06:39 -0500153static bool crossfade_mixmode;
154static bool crossfade_auto_skip;
155static size_t crossfade_widx;
156static size_t crossfade_bufidx;
Brandon Low6c0908b2006-04-23 22:54:34 +0000157
Michael Sevakis6c837392016-12-28 00:06:39 -0500158struct 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;
Brandon Low6c0908b2006-04-23 22:54:34 +0000170
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000171/* 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};
Brandon Low2da61ff2006-04-24 01:32:28 +0000179
Michael Sevakis6c837392016-12-28 00:06:39 -0500180#define MIXFADE_UNITY_BITS 16
181#define MIXFADE_UNITY (1 << MIXFADE_UNITY_BITS)
182
183static void crossfade_cancel(void);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000184static 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);
Michael Sevakis6c837392016-12-28 00:06:39 -0500188#else
189#define crossfade_cancel() do {} while(0)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000190#endif /* HAVE_CROSSFADE */
Brandon Low2da61ff2006-04-24 01:32:28 +0000191
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000192/* Thread */
Michael Sevakis938593b2007-03-06 21:00:45 +0000193#ifdef HAVE_PRIORITY_SCHEDULING
Michael Sevakis27cf6772008-03-25 02:34:12 +0000194static int codec_thread_priority = PRIORITY_PLAYBACK;
Michael Sevakis938593b2007-03-06 21:00:45 +0000195#endif
Michael Sevakisb425de72007-03-06 20:32:13 +0000196
Michael Sevakisc537d592011-04-27 03:08:23 +0000197/* Callbacks into playback.c */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000198extern void audio_pcmbuf_position_callback(unsigned long elapsed,
199 off_t offset, unsigned int key);
Michael Sevakisc537d592011-04-27 03:08:23 +0000200extern void audio_pcmbuf_track_change(bool pcmbuf);
201extern bool audio_pcmbuf_may_play(void);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000202extern void audio_pcmbuf_sync_position(void);
Michael Sevakisc537d592011-04-27 03:08:23 +0000203
Jeffrey Goodeb6f15f22009-11-08 04:27:27 +0000204
Jeffrey Goode013fe352009-11-05 17:32:32 +0000205/**************************************/
206
Michael Sevakis5e4532c2017-04-06 19:32:35 -0400207/* 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
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000214/* 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)
Jeffrey Goode013fe352009-11-05 17:32:32 +0000217{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000218 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;
Jeffrey Goode013fe352009-11-05 17:32:32 +0000225}
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000226
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -0500227/* 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
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000233/* 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
Michael Sevakis6c837392016-12-28 00:06:39 -0500262/* Return the first byte of a chunk for a byte index in the buffer, offset by 'offset'
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000263 chunks */
Michael Sevakis6c837392016-12-28 00:06:39 -0500264static size_t index_chunk_offs(size_t index, int offset)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000265{
266 int i = index / PCMBUF_CHUNK_SIZE;
267
268 if (offset != 0)
269 {
Michael Sevakis662f7572013-06-28 02:17:58 -0400270 i = (i + offset) % (int)pcmbuf_desc_count;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000271
Michael Sevakis363f96b2013-06-28 02:17:17 -0400272 /* remainder => modulus */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000273 if (i < 0)
274 i += pcmbuf_desc_count;
275 }
276
Michael Sevakis6c837392016-12-28 00:06:39 -0500277 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 */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000324}
Jeffrey Goode013fe352009-11-05 17:32:32 +0000325
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +0000326
Jeffrey Goode0db33082009-11-11 07:02:18 +0000327/** Accept new PCM data */
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +0000328
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000329/* Split the uncommitted data as needed into chunks, stopping when uncommitted
330 data is below the threshold */
331static void commit_chunks(size_t threshold)
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000332{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000333 size_t index = chunk_widx;
334 size_t end_index = index + pcmbuf_bytes_waiting;
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000335
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000336 /* 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);
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000339
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000340 struct chunkdesc *desc = index_chunkdesc(index);
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000341
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000342 do
Jeffrey Goode73c47912009-11-09 06:53:22 +0000343 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000344 size_t size = MIN(pcmbuf_bytes_waiting, PCMBUF_CHUNK_SIZE);
345 pcmbuf_bytes_waiting -= size;
Michael Sevakisc537d592011-04-27 03:08:23 +0000346
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000347 /* 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 */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000356 desc->pos_key = 0;
Jeffrey Goode73c47912009-11-09 06:53:22 +0000357 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000358 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)
Jeffrey Goode73c47912009-11-09 06:53:22 +0000377 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000378 desc->pos_key = key;
379 desc->elapsed = elapsed;
380 desc->offset = offset;
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000381 }
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000382}
383
Jeffrey Goode0db33082009-11-11 07:02:18 +0000384/* Set priority of the codec thread */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000385#ifdef HAVE_PRIORITY_SCHEDULING
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000386/*
387 * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */
388static void boost_codec_thread(int pcm_fill_state)
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000389{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000390 static const int8_t prios[11] =
391 {
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000392 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% */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000399 /* raising priority above 70% shouldn't be needed */
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000400 PRIORITY_PLAYBACK, /* 70 - 80% */
401 PRIORITY_PLAYBACK, /* 80 - 90% */
402 PRIORITY_PLAYBACK, /* 90 -100% */
403 PRIORITY_PLAYBACK, /* 100% */
404 };
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000405
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000406 int new_prio = prios[pcm_fill_state];
407
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000408 /* Keep voice and codec threads at the same priority or else voice
409 * will starve if the codec thread's priority is boosted. */
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000410 if (new_prio != codec_thread_priority)
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000411 {
Michael Sevakis65109732011-02-23 14:31:13 +0000412 codec_thread_set_priority(new_prio);
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000413 voice_thread_set_priority(new_prio);
414 codec_thread_priority = new_prio;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000415 }
416}
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000417#else
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000418#define boost_codec_thread(pcm_fill_state) do{}while(0)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000419#endif /* HAVE_PRIORITY_SCHEDULING */
420
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000421/* Get the next available buffer and size - assumes adequate space exists */
422static void * get_write_buffer(size_t *size)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000423{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000424 /* Obtain current chunk fill address */
425 size_t index = chunk_widx + pcmbuf_bytes_waiting;
426 size_t index_end = pcmbuf_size + PCMBUF_GUARD_SIZE;
Michael Sevakisa2b67032011-06-29 06:37:04 +0000427
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000428 /* Get count to the end of the buffer where a wrap will happen +
429 the guard */
430 size_t endsize = index_end - index;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000431
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000432 /* Return available unwrapped space */
433 *size = MIN(*size, endsize);
434
435 return index_buffer(index);
436}
437
Michael Sevakis6c837392016-12-28 00:06:39 -0500438/* Commit outstanding data leaving less than a chunk size remaining */
439static void commit_write_buffer(size_t size)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000440{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000441 /* Add this data and commit if one or more chunks are ready */
442 pcmbuf_bytes_waiting += size;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000443 commit_if_needed(COMMIT_CHUNKS);
444}
445
446/* Request space in the buffer for writing output samples */
447void * pcmbuf_request_buffer(int *count)
448{
Michael Sevakis6c837392016-12-28 00:06:39 -0500449 size_t size = *count * PCMBUF_SAMPLE_SIZE;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000450
451#ifdef HAVE_CROSSFADE
452 /* We're going to crossfade to a new track, which is now on its way */
Michael Sevakis6c837392016-12-28 00:06:39 -0500453 if (crossfade_status > CROSSFADE_ACTIVE)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000454 crossfade_start();
455
Michael Sevakis6c837392016-12-28 00:06:39 -0500456 /* If crossfade has begun, put the new track samples in the crossfade
457 buffer area */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000458 if (crossfade_status != CROSSFADE_INACTIVE && size > CROSSFADE_BUFSIZE)
459 size = CROSSFADE_BUFSIZE;
Michael Sevakisc9bcbe22012-03-27 19:52:15 -0400460 else
461#endif /* HAVE_CROSSFADE */
Michael Sevakisc9bcbe22012-03-27 19:52:15 -0400462 if (size > PCMBUF_MAX_BUFFER)
Michael Sevakis6c837392016-12-28 00:06:39 -0500463 size = PCMBUF_MAX_BUFFER; /* constrain request */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000464
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;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000477
Jeffrey Goode0db33082009-11-11 07:02:18 +0000478 /* Maintain the buffer level above the watermark */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000479 if (status != CHANNEL_STOPPED)
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000480 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000481 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)
Michael Sevakis96f02a92011-07-21 22:25:09 +0000492 trigger_cpu_boost();
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000493
494 boost_codec_thread(realrem*10 / pcmbuf_size);
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000495 }
Michael Sevakisa2b67032011-06-29 06:37:04 +0000496 else /* !playing */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000497 {
Jeffrey Goode0db33082009-11-11 07:02:18 +0000498 /* Boost CPU for pre-buffer */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000499 trigger_cpu_boost();
500
Jeffrey Goode0db33082009-11-11 07:02:18 +0000501 /* If pre-buffered to the watermark, start playback */
Michael Sevakis5e4532c2017-04-06 19:32:35 -0400502 if (!pcmbuf_data_critical())
503 start_audio_playback();
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000504 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000505
Michael Sevakisc9bcbe22012-03-27 19:52:15 -0400506 void *buf;
507
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000508#ifdef HAVE_CROSSFADE
Michael Sevakisc9bcbe22012-03-27 19:52:15 -0400509 if (crossfade_status != CROSSFADE_INACTIVE)
510 {
Michael Sevakis6c837392016-12-28 00:06:39 -0500511 crossfade_bufidx = index_chunk_offs(chunk_ridx, -1);
512 buf = index_buffer(crossfade_bufidx); /* always CROSSFADE_BUFSIZE */
Michael Sevakisc9bcbe22012-03-27 19:52:15 -0400513 }
514 else
Jeffrey Goode9e095342009-11-10 03:46:08 +0000515#endif
Michael Sevakisc9bcbe22012-03-27 19:52:15 -0400516 {
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 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000523
Michael Sevakis6c837392016-12-28 00:06:39 -0500524 *count = size / PCMBUF_SAMPLE_SIZE;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000525 return buf;
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +0000526}
527
Jeffrey Goode0db33082009-11-11 07:02:18 +0000528/* Handle new samples to the buffer */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000529void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000530{
Michael Sevakis6c837392016-12-28 00:06:39 -0500531 size_t size = count * PCMBUF_SAMPLE_SIZE;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000532
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000533#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000534 if (crossfade_status != CROSSFADE_INACTIVE)
535 {
536 write_to_crossfade(size, elapsed, offset);
537 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000538 else
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000539#endif
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000540 {
Michael Sevakis6c837392016-12-28 00:06:39 -0500541 stamp_chunk(index_chunkdesc(chunk_widx), elapsed, offset);
542 commit_write_buffer(size);
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000543 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000544
545 /* Revert to position updates by PCM */
546 pcmbuf_sync_position = false;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000547}
548
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000549
Jeffrey Goode0db33082009-11-11 07:02:18 +0000550/** Init */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000551static unsigned int get_next_required_pcmbuf_chunks(void)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000552{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000553 size_t size = MIN_BUFFER_SIZE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000554
Jeffrey Goode9e095342009-11-10 03:46:08 +0000555#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000556 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 }
Jeffrey Goode9e095342009-11-10 03:46:08 +0000562#endif
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000563
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000564 logf("pcmbuf len: %lu", (unsigned long)(size / BYTERATE));
565 return size / PCMBUF_CHUNK_SIZE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000566}
567
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000568/* 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 */
Michael Sevakis6c837392016-12-28 00:06:39 -0500576 if (pcmbuf_descriptors)
577 pcmbuf_descriptors->pos_key = 0;
578
579 /* Clear change notification */
580 chunk_transidx = INVALID_BUF_INDEX;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000581}
582
583/* Initialize the PCM buffer. The structure looks like this:
Michael Sevakis6c837392016-12-28 00:06:39 -0500584 * ...|---------PCMBUF---------|GUARDBUF|DESCS| */
Michael Sevakisfdd36342012-04-27 16:51:54 -0400585size_t pcmbuf_init(void *bufend)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000586{
Michael Sevakisfdd36342012-04-27 16:51:54 -0400587 void *bufstart;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000588
589 /* Set up the buffers */
590 pcmbuf_desc_count = get_next_required_pcmbuf_chunks();
591 pcmbuf_size = pcmbuf_desc_count * PCMBUF_CHUNK_SIZE;
592 pcmbuf_descriptors = (struct chunkdesc *)bufend - pcmbuf_desc_count;
593
594 pcmbuf_buffer = (void *)pcmbuf_descriptors -
595 pcmbuf_size - PCMBUF_GUARD_SIZE;
596
597 /* Mem-align buffer chunks for more efficient handling in lower layers */
598 pcmbuf_buffer = ALIGN_DOWN(pcmbuf_buffer, (uintptr_t)MEM_ALIGN_SIZE);
599
600 pcmbuf_guardbuf = pcmbuf_buffer + pcmbuf_size;
601 bufstart = pcmbuf_buffer;
Michael Sevakisa802eba2011-07-09 01:49:00 +0000602
Jeffrey Goode9e095342009-11-10 03:46:08 +0000603#ifdef HAVE_CROSSFADE
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000604 pcmbuf_finish_crossfade_enable();
Michael Sevakisd37bf242013-05-23 13:58:51 -0400605#else
606 pcmbuf_watermark = PCMBUF_WATERMARK;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000607#endif /* HAVE_CROSSFADE */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000608
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000609 init_buffer_state();
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000610
Michael Sevakisa2b67032011-06-29 06:37:04 +0000611 pcmbuf_soft_mode(false);
612
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000613 return bufend - bufstart;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000614}
615
616
Jeffrey Goode0db33082009-11-11 07:02:18 +0000617/** Track change */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000618
619/* Place a track change notification in a specific descriptor or post it
620 immediately if the buffer is empty or the index is invalid */
Michael Sevakis6c837392016-12-28 00:06:39 -0500621static void pcmbuf_monitor_track_change_ex(size_t index)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000622{
Michael Sevakis6c837392016-12-28 00:06:39 -0500623 /* Call with PCM lockout */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000624 if (chunk_ridx != chunk_widx && index != INVALID_BUF_INDEX)
625 {
Michael Sevakis6c837392016-12-28 00:06:39 -0500626 /* If monitoring, set flag for one previous to specified chunk */
627 index = index_chunk_offs(index, -1);
628
629 /* Ensure PCM playback hasn't already played this out */
630 if (index_committed(index))
631 {
632 chunk_transidx = index;
633 return;
634 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000635 }
Michael Sevakis6c837392016-12-28 00:06:39 -0500636
637 /* Post now if buffer is no longer coming up */
638 chunk_transidx = INVALID_BUF_INDEX;
639 audio_pcmbuf_track_change(false);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000640}
641
642/* Clear end of track and optionally the positioning info for all data */
643static void pcmbuf_cancel_track_change(bool position)
644{
Michael Sevakis6c837392016-12-28 00:06:39 -0500645 /* Call with PCM lockout */
646 snip_buffer_tail(chunk_transidx, 1);
647
648 chunk_transidx = INVALID_BUF_INDEX;
649
650 if (!position)
651 return;
652
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000653 size_t index = chunk_ridx;
654
655 while (1)
656 {
Michael Sevakis6c837392016-12-28 00:06:39 -0500657 index_chunkdesc(index)->pos_key = 0;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000658
659 if (index == chunk_widx)
660 break;
661
662 index = index_next(index);
663 }
664}
665
666/* Place a track change notification at the end of the buffer or post it
667 immediately if the buffer is empty */
Michael Sevakis65109732011-02-23 14:31:13 +0000668void pcmbuf_monitor_track_change(bool monitor)
669{
670 pcm_play_lock();
671
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000672 if (monitor)
Michael Sevakis6c837392016-12-28 00:06:39 -0500673 pcmbuf_monitor_track_change_ex(chunk_widx);
Michael Sevakis65109732011-02-23 14:31:13 +0000674 else
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000675 pcmbuf_cancel_track_change(false);
Michael Sevakis65109732011-02-23 14:31:13 +0000676
677 pcm_play_unlock();
678}
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000679
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000680void pcmbuf_start_track_change(enum pcm_track_change_type type)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000681{
Michael Sevakis5e4532c2017-04-06 19:32:35 -0400682 /* Commit all outstanding data before starting next track - tracks don't
683 comingle inside a single buffer chunk */
684 commit_if_needed(COMMIT_ALL_DATA);
685
686 if (type == TRACK_CHANGE_AUTO_PILEUP)
687 {
688 /* Fill might not have been above watermark */
689 start_audio_playback();
690 return;
691 }
692
Jeffrey Goode9e095342009-11-10 03:46:08 +0000693#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000694 bool crossfade = false;
695#endif
696 bool auto_skip = type != TRACK_CHANGE_MANUAL;
697
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000698 /* Update position key so that:
699 1) Positions are keyed to the track to which they belong for sync
700 purposes
701
702 2) Buffers stamped with the outgoing track's positions are restamped
703 to the incoming track's positions when crossfading
704 */
Michael Sevakis6c837392016-12-28 00:06:39 -0500705 if (++position_key > POSITION_KEY_MAX)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000706 position_key = 1;
707
708 if (type == TRACK_CHANGE_END_OF_DATA)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000709 {
Michael Sevakis6c837392016-12-28 00:06:39 -0500710 crossfade_cancel();
711
Michael Sevakis5e4532c2017-04-06 19:32:35 -0400712 /* Fill might not have been above watermark */
713 start_audio_playback();
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000714 }
715#ifdef HAVE_CROSSFADE
716 /* Determine whether this track change needs to crossfaded and how */
Michael Sevakis6c837392016-12-28 00:06:39 -0500717 else if (crossfade_setting != CROSSFADE_ENABLE_OFF)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000718 {
Michael Sevakis6c837392016-12-28 00:06:39 -0500719 if (crossfade_status == CROSSFADE_INACTIVE &&
720 pcmbuf_unplayed_bytes() >= DATA_LEVEL(2) &&
721 !low_latency_mode)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000722 {
Michael Sevakis6c837392016-12-28 00:06:39 -0500723 switch (crossfade_setting)
724 {
725 case CROSSFADE_ENABLE_AUTOSKIP:
726 crossfade = auto_skip;
727 break;
728 case CROSSFADE_ENABLE_MANSKIP:
729 crossfade = !auto_skip;
730 break;
731 case CROSSFADE_ENABLE_SHUFFLE:
732 crossfade = global_settings.playlist_shuffle;
733 break;
734 case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP:
735 crossfade = global_settings.playlist_shuffle || !auto_skip;
736 break;
737 case CROSSFADE_ENABLE_ALWAYS:
738 crossfade = true;
739 break;
740 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000741 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000742 }
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000743
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000744 if (crossfade)
Jeffrey Goode664dc902009-11-11 00:48:17 +0000745 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000746 logf("crossfade track change");
Michael Sevakis65109732011-02-23 14:31:13 +0000747
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000748 /* Don't enable mix mode when skipping tracks manually */
749 crossfade_mixmode = auto_skip &&
750 global_settings.crossfade_fade_out_mixmode;
Michael Sevakis65109732011-02-23 14:31:13 +0000751
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000752 crossfade_auto_skip = auto_skip;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000753
Michael Sevakis6c837392016-12-28 00:06:39 -0500754 crossfade_status = CROSSFADE_START;
755
756 pcmbuf_monitor_track_change(auto_skip);
Jeffrey Goode664dc902009-11-11 00:48:17 +0000757
Michael Sevakis65109732011-02-23 14:31:13 +0000758 trigger_cpu_boost();
Jeffrey Goode664dc902009-11-11 00:48:17 +0000759 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000760 else
761#endif /* HAVE_CROSSFADE */
762 if (auto_skip)
Michael Sevakis6c837392016-12-28 00:06:39 -0500763 {
Jeffrey Goode664dc902009-11-11 00:48:17 +0000764 /* The codec is moving on to the next track, but the current track will
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000765 * continue to play, so mark the last write chunk as the last one in
766 * the track */
767 logf("gapless track change");
Michael Sevakis6c837392016-12-28 00:06:39 -0500768
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000769#ifdef HAVE_CROSSFADE
Michael Sevakis6c837392016-12-28 00:06:39 -0500770 if (crossfade_status == CROSSFADE_ACTIVE)
771 crossfade_status = CROSSFADE_CONTINUE;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000772#endif
Michael Sevakis6c837392016-12-28 00:06:39 -0500773
Michael Sevakis65109732011-02-23 14:31:13 +0000774 pcmbuf_monitor_track_change(true);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000775 }
776 else
777 {
778 /* Discard old data; caller needs no transition notification */
779 logf("manual track change");
780 pcmbuf_play_stop();
Jeffrey Goode664dc902009-11-11 00:48:17 +0000781 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000782}
783
784
Jeffrey Goode0db33082009-11-11 07:02:18 +0000785/** Playback */
Jeffrey Goode874c9112009-11-09 18:12:20 +0000786
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000787/* PCM driver callback */
Michael Sevakis286a4c52012-02-23 08:14:46 -0500788static void pcmbuf_pcm_callback(const void **start, size_t *size)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000789{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000790 /*- Process the chunk that just finished -*/
791 size_t index = chunk_ridx;
792 struct chunkdesc *desc = current_desc;
Michael Sevakis5078d462011-08-23 01:37:59 +0000793
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000794 if (desc)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000795 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000796 /* If last chunk in the track, notify of track change */
Michael Sevakis6c837392016-12-28 00:06:39 -0500797 if (index == chunk_transidx)
798 {
799 chunk_transidx = INVALID_BUF_INDEX;
Michael Sevakisc537d592011-04-27 03:08:23 +0000800 audio_pcmbuf_track_change(true);
Michael Sevakis6c837392016-12-28 00:06:39 -0500801 }
Jeffrey Goode874c9112009-11-09 18:12:20 +0000802
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000803 /* Free it for reuse */
804 chunk_ridx = index = index_next(index);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000805 }
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000806
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000807 /*- Process the new one -*/
808 if (index != chunk_widx && !fade_out_complete)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000809 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000810 current_desc = desc = index_chunkdesc(index);
811
812 *start = index_buffer(index);
813 *size = desc->size;
814
815 if (desc->pos_key != 0)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000816 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000817 /* Positioning chunk - notify playback */
818 audio_pcmbuf_position_callback(desc->elapsed, desc->offset,
819 desc->pos_key);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000820 }
821 }
Jeffrey Goode874c9112009-11-09 18:12:20 +0000822}
823
Jeffrey Goode0db33082009-11-11 07:02:18 +0000824/* Force playback */
Jeffrey Goode874c9112009-11-09 18:12:20 +0000825void pcmbuf_play_start(void)
826{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000827 logf("pcmbuf_play_start");
828
Michael Sevakisa2b67032011-06-29 06:37:04 +0000829 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED &&
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000830 chunk_widx != chunk_ridx)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000831 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000832 current_desc = NULL;
Michael Sevakis5078d462011-08-23 01:37:59 +0000833 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback,
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000834 NULL, 0);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000835 }
836}
837
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000838/* Stop channel, empty and reset buffer */
Jeffrey Goode874c9112009-11-09 18:12:20 +0000839void pcmbuf_play_stop(void)
840{
Jeffrey Goode664dc902009-11-11 00:48:17 +0000841 logf("pcmbuf_play_stop");
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000842
843 /* Reset channel */
Michael Sevakisa2b67032011-06-29 06:37:04 +0000844 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000845
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000846 /* Reset buffer */
847 init_buffer_state();
848
849 /* Revert to position updates by PCM */
850 pcmbuf_sync_position = false;
851
Michael Sevakis6c837392016-12-28 00:06:39 -0500852 /* Fader OFF */
853 crossfade_cancel();
Jeffrey Goode874c9112009-11-09 18:12:20 +0000854
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000855 /* Can unboost the codec thread here no matter who's calling,
856 * pretend full pcm buffer to unboost */
857 boost_codec_thread(10);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000858}
859
860void pcmbuf_pause(bool pause)
861{
Jeffrey Goode664dc902009-11-11 00:48:17 +0000862 logf("pcmbuf_pause: %s", pause?"pause":"play");
Michael Sevakisa2b67032011-06-29 06:37:04 +0000863
864 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED)
865 mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000866 else if (!pause)
867 pcmbuf_play_start();
868}
869
870
Jeffrey Goode0db33082009-11-11 07:02:18 +0000871/** Crossfade */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000872
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000873#ifdef HAVE_CROSSFADE
Michael Sevakis6c837392016-12-28 00:06:39 -0500874
875/* Initialize a fader */
876static void mixfader_init(struct mixfader *faderp, int32_t start_factor,
877 int32_t end_factor, size_t size, bool alloc)
878{
879 /* Linear fade */
880 faderp->endfac = end_factor;
881 faderp->nsamp2 = size / PCMBUF_SAMPLE_SIZE * 2;
882 faderp->alloc = alloc;
883
884 if (faderp->nsamp2 == 0)
885 {
886 /* No data; set up as if fader finished the fade */
887 faderp->factor = end_factor;
888 return;
889 }
890
891 int32_t dfact2 = 2*abs(end_factor - start_factor);
892 faderp->factor = start_factor;
893 faderp->ferr = dfact2 / 2;
894 faderp->dfquo = dfact2 / faderp->nsamp2;
895 faderp->dfrem = dfact2 - faderp->dfquo*faderp->nsamp2;
896 faderp->dfinc = end_factor < start_factor ? -1 : +1;
897 faderp->dfquo *= faderp->dfinc;
898}
899
900/* Query if the fader has finished its envelope */
901static inline bool mixfader_finished(const struct mixfader *faderp)
902{
903 return faderp->factor == faderp->endfac;
904}
905
906/* Step fader by one sample */
907static inline void mixfader_step(struct mixfader *faderp)
908{
909 if (mixfader_finished(faderp))
910 return;
911
912 faderp->factor += faderp->dfquo;
913 faderp->ferr += faderp->dfrem;
914
915 if (faderp->ferr >= faderp->nsamp2)
916 {
917 faderp->factor += faderp->dfinc;
918 faderp->ferr -= faderp->nsamp2;
919 }
920}
921
922static FORCE_INLINE int32_t mixfade_sample(const struct mixfader *faderp, int32_t s)
923{
924 return (faderp->factor * s + MIXFADE_UNITY/2) >> MIXFADE_UNITY_BITS;
925}
926
927/* Cancel crossfade operation */
928static void crossfade_cancel(void)
929{
930 crossfade_status = CROSSFADE_INACTIVE;
931 crossfade_widx = INVALID_BUF_INDEX;
932}
933
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000934/* Find the buffer index that's 'size' bytes away from 'index' */
935static size_t crossfade_find_index(size_t index, size_t size)
936{
937 if (index != INVALID_BUF_INDEX)
938 {
Michael Sevakis6c837392016-12-28 00:06:39 -0500939 size_t i = index_chunk_offs(index, 0);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000940 size += index - i;
941
942 while (i != chunk_widx)
943 {
944 size_t desc_size = index_chunkdesc(i)->size;
Michael Sevakis363f96b2013-06-28 02:17:17 -0400945
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000946 if (size < desc_size)
Michael Sevakis6c837392016-12-28 00:06:39 -0500947 {
948 index = i + size;
949 break;
950 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000951
952 size -= desc_size;
953 i = index_next(i);
954 }
955 }
956
Michael Sevakis6c837392016-12-28 00:06:39 -0500957 return index;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000958}
959
960/* Align the needed buffer area up to the end of existing data */
Michael Sevakis6c837392016-12-28 00:06:39 -0500961static size_t crossfade_find_buftail(bool auto_skip, size_t buffer_rem,
962 size_t buffer_need, size_t *buffer_rem_outp)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000963{
Michael Sevakis6c837392016-12-28 00:06:39 -0500964 size_t index = chunk_ridx;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000965
966 if (buffer_rem > buffer_need)
967 {
968 size_t distance;
969
Michael Sevakis6c837392016-12-28 00:06:39 -0500970 if (auto_skip)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000971 {
972 /* Automatic track changes only modify the last part of the buffer,
973 * so find the right chunk and sample to start the crossfade */
974 distance = buffer_rem - buffer_need;
975 buffer_rem = buffer_need;
976 }
977 else
978 {
979 /* Manual skips occur immediately, but give 1/5s to process */
Michael Sevakis6c837392016-12-28 00:06:39 -0500980 distance = MIN(BYTERATE / 5, buffer_rem);
981 buffer_rem -= distance;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000982 }
983
Michael Sevakis6c837392016-12-28 00:06:39 -0500984 index = crossfade_find_index(index, distance);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000985 }
986
Michael Sevakis6c837392016-12-28 00:06:39 -0500987 if (buffer_rem_outp)
988 *buffer_rem_outp = buffer_rem;
989
990 return index;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000991}
992
Michael Sevakis6c837392016-12-28 00:06:39 -0500993/* Run a fader on some buffers */
994static void crossfade_mix_fade(struct mixfader *faderp, size_t size,
995 void *input_buf, size_t *out_index,
996 unsigned long elapsed, off_t offset)
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000997{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000998 if (size == 0)
Michael Sevakis6c837392016-12-28 00:06:39 -0500999 return;
Steve Bavinb2dc7f02009-11-26 14:41:31 +00001000
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001001 size_t index = *out_index;
Jeffrey Gooded1963e12009-11-13 21:58:41 +00001002
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001003 if (index == INVALID_BUF_INDEX)
Michael Sevakis6c837392016-12-28 00:06:39 -05001004 return;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001005
Michael Sevakis6c837392016-12-28 00:06:39 -05001006 int16_t *inbuf = input_buf;
1007
1008 bool alloced = inbuf && faderp->alloc &&
1009 index_chunk_offs(index, 0) == chunk_widx;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001010
1011 while (size)
Jeffrey Goode873c5b62009-11-15 04:57:40 +00001012 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001013 struct chunkdesc *desc = index_chunkdesc(index);
Michael Sevakis6c837392016-12-28 00:06:39 -05001014 int16_t *outbuf = index_buffer(index);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001015
1016 switch (offset)
Jeffrey Gooded1963e12009-11-13 21:58:41 +00001017 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001018 case MIXFADE_NULLIFY_POS:
1019 /* Stop position updates for the chunk */
1020 desc->pos_key = 0;
1021 break;
1022 case MIXFADE_KEEP_POS:
1023 /* Keep position info as it is */
1024 break;
1025 default:
1026 /* Replace position info */
1027 stamp_chunk(desc, elapsed, offset);
Jeffrey Goode873c5b62009-11-15 04:57:40 +00001028 }
1029
Michael Sevakis6c837392016-12-28 00:06:39 -05001030 size_t amount = (alloced ? PCMBUF_CHUNK_SIZE : desc->size)
1031 - (index % PCMBUF_CHUNK_SIZE);
1032 int16_t *chunkend = SKIPBYTES(outbuf, amount);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001033
Michael Sevakis6c837392016-12-28 00:06:39 -05001034 if (size < amount)
1035 amount = size;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001036
Michael Sevakis6c837392016-12-28 00:06:39 -05001037 size -= amount;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001038
Michael Sevakis6c837392016-12-28 00:06:39 -05001039 if (alloced)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001040 {
Michael Sevakis6c837392016-12-28 00:06:39 -05001041 /* Fade the input buffer into the new destination chunk */
1042 for (size_t s = amount; s != 0; s -= PCMBUF_SAMPLE_SIZE)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001043 {
Michael Sevakis6c837392016-12-28 00:06:39 -05001044 *outbuf++ = mixfade_sample(faderp, *inbuf++);
1045 *outbuf++ = mixfade_sample(faderp, *inbuf++);
1046 mixfader_step(faderp);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001047 }
1048
Michael Sevakis6c837392016-12-28 00:06:39 -05001049 commit_write_buffer(amount);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001050 }
Michael Sevakis6c837392016-12-28 00:06:39 -05001051 else if (inbuf)
Jeffrey Goode873c5b62009-11-15 04:57:40 +00001052 {
Michael Sevakis6c837392016-12-28 00:06:39 -05001053 /* Fade the input buffer and mix into the destination chunk */
1054 for (size_t s = amount; s != 0; s -= PCMBUF_SAMPLE_SIZE)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001055 {
Michael Sevakis6c837392016-12-28 00:06:39 -05001056 int32_t left = outbuf[0];
1057 int32_t right = outbuf[1];
1058 left += mixfade_sample(faderp, *inbuf++);
1059 right += mixfade_sample(faderp, *inbuf++);
1060 *outbuf++ = clip_sample_16(left);
1061 *outbuf++ = clip_sample_16(right);
1062 mixfader_step(faderp);
1063 }
1064 }
1065 else
1066 {
1067 /* Fade the chunk in place */
1068 for (size_t s = amount; s != 0; s -= PCMBUF_SAMPLE_SIZE)
1069 {
1070 int32_t left = outbuf[0];
1071 int32_t right = outbuf[1];
1072 *outbuf++ = mixfade_sample(faderp, left);
1073 *outbuf++ = mixfade_sample(faderp, right);
1074 mixfader_step(faderp);
1075 }
1076 }
1077
1078 if (outbuf < chunkend)
1079 {
1080 index += amount;
1081 continue;
1082 }
1083
1084 /* Move destination to next chunk as needed */
1085 index = index_next(index);
1086
1087 if (index == chunk_widx)
1088 {
1089 /* End of existing data */
1090 if (!inbuf || !faderp->alloc)
1091 {
1092 index = INVALID_BUF_INDEX;
1093 break;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001094 }
1095
Michael Sevakis6c837392016-12-28 00:06:39 -05001096 alloced = true;
Jeffrey Gooded1963e12009-11-13 21:58:41 +00001097 }
1098 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001099
Michael Sevakis6c837392016-12-28 00:06:39 -05001100 *out_index = index;
Jeffrey Gooded1963e12009-11-13 21:58:41 +00001101}
1102
Jeffrey Goode0db33082009-11-11 07:02:18 +00001103/* Initializes crossfader, calculates all necessary parameters and performs
1104 * fade-out with the PCM buffer. */
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001105static void crossfade_start(void)
1106{
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001107 logf("crossfade_start");
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001108
1109 pcm_play_lock();
Brandon Low6c0908b2006-04-23 22:54:34 +00001110
Michael Sevakis6c837392016-12-28 00:06:39 -05001111 if (crossfade_status == CROSSFADE_CONTINUE)
1112 {
1113 logf("fade-in continuing");
1114
1115 crossfade_status = CROSSFADE_ACTIVE;
1116
1117 if (crossfade_auto_skip)
1118 pcmbuf_monitor_track_change_ex(crossfade_widx);
1119
1120 pcm_play_unlock();
1121 return;
1122 }
1123
Brandon Low413da2a2006-02-07 20:38:55 +00001124 /* Initialize the crossfade buffer size to all of the buffered data that
1125 * has not yet been sent to the DMA */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001126 size_t unplayed = pcmbuf_unplayed_bytes();
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001127
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001128 /* Reject crossfade if less than .5s of data */
1129 if (unplayed < DATA_LEVEL(2))
Brandon Low6c0908b2006-04-23 22:54:34 +00001130 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001131 logf("crossfade rejected");
Michael Sevakis6c837392016-12-28 00:06:39 -05001132 crossfade_cancel();
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001133 pcm_play_unlock();
1134 return;
1135 }
1136
1137 /* Fading will happen */
1138 crossfade_status = CROSSFADE_ACTIVE;
1139
1140 /* Get fade info from settings. */
1141 size_t fade_out_delay = global_settings.crossfade_fade_out_delay * BYTERATE;
1142 size_t fade_out_rem = global_settings.crossfade_fade_out_duration * BYTERATE;
1143 size_t fade_in_delay = global_settings.crossfade_fade_in_delay * BYTERATE;
1144 size_t fade_in_duration = global_settings.crossfade_fade_in_duration * BYTERATE;
1145
1146 if (!crossfade_auto_skip)
1147 {
1148 /* Forego fade-in delay on manual skip - do the best to preserve auto skip
1149 relationship */
Michael Sevakis6c837392016-12-28 00:06:39 -05001150 fade_out_delay -= MIN(fade_out_delay, fade_in_delay);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001151 fade_in_delay = 0;
1152 }
1153
1154 size_t fade_out_need = fade_out_delay + fade_out_rem;
1155
Jeffrey Goode15351e82009-11-12 04:24:41 +00001156 if (!crossfade_mixmode)
1157 {
Michael Sevakis6c837392016-12-28 00:06:39 -05001158 /* Completely process the crossfade fade-out effect with current PCM buffer */
1159 size_t buffer_rem;
1160 size_t index = crossfade_find_buftail(crossfade_auto_skip, unplayed,
1161 fade_out_need, &buffer_rem);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001162
1163 pcm_play_unlock();
1164
1165 if (buffer_rem < fade_out_need)
1166 {
1167 /* Existing buffers are short */
1168 size_t fade_out_short = fade_out_need - buffer_rem;
1169
Michael Sevakis6c837392016-12-28 00:06:39 -05001170 if (fade_out_delay >= fade_out_short)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001171 {
Michael Sevakis6c837392016-12-28 00:06:39 -05001172 /* Truncate fade-out delay */
1173 fade_out_delay -= fade_out_short;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001174 }
1175 else
1176 {
Michael Sevakis6c837392016-12-28 00:06:39 -05001177 /* Truncate fade-out and eliminate fade-out delay */
1178 fade_out_rem = buffer_rem;
1179 fade_out_delay = 0;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001180 }
Michael Sevakis6c837392016-12-28 00:06:39 -05001181
1182 fade_out_need = fade_out_delay + fade_out_rem;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001183 }
1184
Jeffrey Goode15351e82009-11-12 04:24:41 +00001185 /* Find the right chunk and sample to start fading out */
Michael Sevakis6c837392016-12-28 00:06:39 -05001186 index = crossfade_find_index(index, fade_out_delay);
Jeffrey Goode15351e82009-11-12 04:24:41 +00001187
Michael Sevakis6c837392016-12-28 00:06:39 -05001188 /* Fade out the specified amount of the already processed audio */
1189 struct mixfader outfader;
Jeffrey Goode15351e82009-11-12 04:24:41 +00001190
Michael Sevakis6c837392016-12-28 00:06:39 -05001191 mixfader_init(&outfader, MIXFADE_UNITY, 0, fade_out_rem, false);
1192 crossfade_mix_fade(&outfader, fade_out_rem, NULL, &index, 0,
1193 MIXFADE_KEEP_POS);
Jeffrey Goode15351e82009-11-12 04:24:41 +00001194
Michael Sevakis6c837392016-12-28 00:06:39 -05001195 /* Zero-out the rest of the buffer */
1196 crossfade_mix_fade(&outfader, pcmbuf_size, NULL, &index, 0,
1197 MIXFADE_NULLIFY_POS);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001198
1199 pcm_play_lock();
Jeffrey Goode15351e82009-11-12 04:24:41 +00001200 }
1201
1202 /* Initialize fade-in counters */
Michael Sevakis6c837392016-12-28 00:06:39 -05001203 mixfader_init(&crossfade_infader, 0, MIXFADE_UNITY, fade_in_duration, true);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001204
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001205 /* Find the right chunk and sample to start fading in - redo from read
1206 chunk in case original position were/was overrun in callback - the
1207 track change event _must not_ ever fail to happen */
1208 unplayed = pcmbuf_unplayed_bytes() + fade_in_delay;
Brandon Low6c0908b2006-04-23 22:54:34 +00001209
Michael Sevakis6c837392016-12-28 00:06:39 -05001210 crossfade_widx = crossfade_find_buftail(crossfade_auto_skip, unplayed,
1211 fade_out_need, NULL);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001212
Michael Sevakis6c837392016-12-28 00:06:39 -05001213 /* Move track transistion to chunk before the first one of incoming track */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001214 if (crossfade_auto_skip)
Michael Sevakis6c837392016-12-28 00:06:39 -05001215 pcmbuf_monitor_track_change_ex(crossfade_widx);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001216
1217 pcm_play_unlock();
1218
Jeffrey Goode15351e82009-11-12 04:24:41 +00001219 logf("crossfade_start done!");
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001220}
1221
Jeffrey Goode0db33082009-11-11 07:02:18 +00001222/* Perform fade-in of new track */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001223static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset)
Brandon Low6c0908b2006-04-23 22:54:34 +00001224{
Michael Sevakis6c837392016-12-28 00:06:39 -05001225 /* Mix the data */
1226 crossfade_mix_fade(&crossfade_infader, size, index_buffer(crossfade_bufidx),
1227 &crossfade_widx, elapsed, offset);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001228
Michael Sevakis6c837392016-12-28 00:06:39 -05001229 /* If no more fading-in to do, stop the crossfade */
1230 if (mixfader_finished(&crossfade_infader) &&
1231 index_chunk_offs(crossfade_widx, 0) == chunk_widx)
Brandon Low6c0908b2006-04-23 22:54:34 +00001232 {
Michael Sevakis6c837392016-12-28 00:06:39 -05001233 crossfade_cancel();
Brandon Low9aa49a42006-04-25 02:28:21 +00001234 }
Brandon Low413da2a2006-02-07 20:38:55 +00001235}
1236
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001237static void pcmbuf_finish_crossfade_enable(void)
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001238{
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001239 /* Copy the pending setting over now */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001240 crossfade_setting = crossfade_enable_request;
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001241
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001242 pcmbuf_watermark = (crossfade_setting != CROSSFADE_ENABLE_OFF && pcmbuf_size) ?
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001243 /* If crossfading, try to keep the buffer full other than 1 second */
Jeffrey Goodeba9280d2009-11-16 04:42:34 +00001244 (pcmbuf_size - BYTERATE) :
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001245 /* Otherwise, just use the default */
1246 PCMBUF_WATERMARK;
1247}
Brandon Low37faaab2006-04-20 02:30:59 +00001248
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001249void pcmbuf_request_crossfade_enable(int setting)
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001250{
1251 /* Next setting to be used, not applied now */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001252 crossfade_enable_request = setting;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001253}
1254
1255bool pcmbuf_is_same_size(void)
1256{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001257 /* if pcmbuf_buffer is NULL, then not set up yet even once so always */
1258 bool same_size = pcmbuf_buffer ?
1259 (get_next_required_pcmbuf_chunks() == pcmbuf_desc_count) : true;
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001260
Jeffrey Goode5c69a422009-11-09 17:11:53 +00001261 /* no buffer change needed, so finish crossfade setup now */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001262 if (same_size)
1263 pcmbuf_finish_crossfade_enable();
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001264
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001265 return same_size;
1266}
Jeffrey Goode9e095342009-11-10 03:46:08 +00001267#endif /* HAVE_CROSSFADE */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001268
1269
Jeffrey Goode0db33082009-11-11 07:02:18 +00001270/** Debug menu, other metrics */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001271
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001272/* Amount of bytes left in the buffer, accounting for uncommitted bytes */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001273size_t pcmbuf_free(void)
1274{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001275 return pcmbuf_size - pcmbuf_unplayed_bytes() - pcmbuf_bytes_waiting;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001276}
1277
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001278/* Data bytes allocated for buffer */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001279size_t pcmbuf_get_bufsize(void)
1280{
1281 return pcmbuf_size;
1282}
1283
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001284/* Number of committed descriptors */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001285int pcmbuf_used_descs(void)
1286{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001287 return pcmbuf_unplayed_bytes() / PCMBUF_CHUNK_SIZE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001288}
1289
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001290/* Total number of descriptors allocated */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001291int pcmbuf_descs(void)
1292{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001293 return pcmbuf_desc_count;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001294}
1295
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001296
Michael Sevakisa2b67032011-06-29 06:37:04 +00001297/** Fading and channel volume control */
1298
1299/* Sync the channel amplitude to all states */
1300static void pcmbuf_update_volume(void)
1301{
1302 unsigned int vol = fade_vol;
1303
1304 if (soft_mode)
1305 vol >>= 2;
1306
1307 mixer_channel_set_amplitude(PCM_MIXER_CHAN_PLAYBACK, vol);
1308}
1309
Michael Sevakis5078d462011-08-23 01:37:59 +00001310/* Tick that does the fade for the playback channel */
1311static void pcmbuf_fade_tick(void)
1312{
1313 /* ~1/3 second for full range fade */
1314 const unsigned int fade_step = MIX_AMP_UNITY / (HZ / 3);
1315
1316 if (fade_state == PCM_FADING_IN)
1317 fade_vol += MIN(fade_step, MIX_AMP_UNITY - fade_vol);
1318 else if (fade_state == PCM_FADING_OUT)
1319 fade_vol -= MIN(fade_step, fade_vol - MIX_AMP_MUTE);
1320
1321 pcmbuf_update_volume();
1322
1323 if (fade_vol == MIX_AMP_MUTE || fade_vol == MIX_AMP_UNITY)
1324 {
1325 /* Fade is complete */
1326 tick_remove_task(pcmbuf_fade_tick);
Michael Sevakis5078d462011-08-23 01:37:59 +00001327 if (fade_state == PCM_FADING_OUT)
1328 {
1329 /* Tell PCM to stop at its earliest convenience */
1330 fade_out_complete = true;
1331 }
1332
1333 fade_state = PCM_NOT_FADING;
1334 }
1335}
1336
Michael Sevakis4d903f22011-08-23 05:58:28 +00001337/* Fade channel in or out in the background */
Michael Sevakisa2b67032011-06-29 06:37:04 +00001338void pcmbuf_fade(bool fade, bool in)
1339{
Michael Sevakis4d903f22011-08-23 05:58:28 +00001340 /* Must pause any active fade */
Michael Sevakis5078d462011-08-23 01:37:59 +00001341 pcm_play_lock();
1342
1343 if (fade_state != PCM_NOT_FADING)
1344 tick_remove_task(pcmbuf_fade_tick);
1345
1346 fade_out_complete = false;
1347
1348 pcm_play_unlock();
1349
Michael Sevakisa2b67032011-06-29 06:37:04 +00001350 if (!fade)
1351 {
1352 /* Simply set the level */
Michael Sevakis5078d462011-08-23 01:37:59 +00001353 fade_state = PCM_NOT_FADING;
Michael Sevakisa2b67032011-06-29 06:37:04 +00001354 fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE;
Michael Sevakis5078d462011-08-23 01:37:59 +00001355 pcmbuf_update_volume();
Michael Sevakisa2b67032011-06-29 06:37:04 +00001356 }
1357 else
1358 {
Michael Sevakis5078d462011-08-23 01:37:59 +00001359 /* Set direction and resume fade from current point */
1360 fade_state = in ? PCM_FADING_IN : PCM_FADING_OUT;
1361 tick_add_task(pcmbuf_fade_tick);
Michael Sevakisa2b67032011-06-29 06:37:04 +00001362 }
1363}
1364
Michael Sevakis5078d462011-08-23 01:37:59 +00001365/* Return 'true' if fade is in progress */
1366bool pcmbuf_fading(void)
1367{
1368 return fade_state != PCM_NOT_FADING;
1369}
Michael Sevakisa2b67032011-06-29 06:37:04 +00001370
Michael Sevakis697aa7f2011-08-25 00:12:19 +00001371/* Quiet-down the channel if 'shhh' is true or else play at normal level */
1372void pcmbuf_soft_mode(bool shhh)
1373{
1374 /* Have to block the tick or improper order could leave volume in soft
1375 mode if fading reads the old value first but updates after us. */
1376 int res = fade_state != PCM_NOT_FADING ?
1377 tick_remove_task(pcmbuf_fade_tick) : -1;
1378
1379 soft_mode = shhh;
1380 pcmbuf_update_volume();
1381
1382 if (res == 0)
1383 tick_add_task(pcmbuf_fade_tick);
1384}
1385
1386
Michael Sevakis5833cc12012-02-20 00:47:21 -05001387/** Time and position */
1388
1389/* Return the current position key value */
1390unsigned int pcmbuf_get_position_key(void)
1391{
1392 return position_key;
1393}
1394
1395/* Set position updates to be synchronous and immediate in addition to during
1396 PCM frames - cancelled upon first codec insert or upon stopping */
1397void pcmbuf_sync_position_update(void)
1398{
1399 pcmbuf_sync_position = true;
1400}
1401
1402
1403
Jeffrey Goode0db33082009-11-11 07:02:18 +00001404/** Misc */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001405
1406bool pcmbuf_is_lowdata(void)
1407{
Michael Sevakis31567302011-08-28 14:26:59 +00001408 enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK);
1409
Michael Sevakisbc4c13e2017-01-05 03:22:47 -05001410 if (status != CHANNEL_PLAYING)
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001411 return false;
1412
Michael Sevakisbc4c13e2017-01-05 03:22:47 -05001413#ifdef HAVE_CROSSFADE
1414 if (crossfade_status != CROSSFADE_INACTIVE)
1415 return false;
1416#endif
1417
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -05001418 return pcmbuf_data_critical();
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001419}
1420
1421void pcmbuf_set_low_latency(bool state)
1422{
1423 low_latency_mode = state;
1424}
Michael Sevakisd37bf242013-05-23 13:58:51 -04001425
1426void pcmbuf_update_frequency(void)
1427{
1428 pcmbuf_sampr = mixer_get_frequency();
1429}
1430
1431unsigned int pcmbuf_get_frequency(void)
1432{
1433 return pcmbuf_sampr;
1434}