blob: aa30c64912378f695d8f6ffae12b332ecb3163db [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 Pekkarinen159c52d2005-08-20 11:13:19 +000043#include "dsp.h"
Miika Pekkarinen20b38972005-07-13 12:48:22 +000044
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000045/* This is the target fill size of chunks on the pcm buffer
46 Can be any number of samples but power of two sizes make for faster and
47 smaller math - must be < 65536 bytes */
48#define PCMBUF_CHUNK_SIZE 8192u
Michael Sevakis11cbffa2011-09-30 06:46:19 +000049
50/* Massive size is a nasty temp fix */
51#define PCMBUF_GUARD_SIZE (1024u*12*((NATIVE_FREQUENCY+7999)/8000))
Jeffrey Goode04b01e12009-11-05 21:59:36 +000052
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000053/* Mnemonics for common data commit thresholds */
54#define COMMIT_CHUNKS PCMBUF_CHUNK_SIZE
55#define COMMIT_ALL_DATA 1u
56
57 /* Size of the crossfade buffer where codec data is written to be faded
58 on commit */
59#define CROSSFADE_BUFSIZE 8192u
60
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -050061/* Return data level in 1/4-second increments */
62#define DATA_LEVEL(quarter_secs) (NATIVE_FREQUENCY * (quarter_secs))
63
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000064/* Number of bytes played per second:
65 (sample rate * 2 channels * 2 bytes/sample) */
Jeffrey Goodeba9280d2009-11-16 04:42:34 +000066#define BYTERATE (NATIVE_FREQUENCY * 4)
67
Björn Stenberg6427d122009-01-10 21:10:56 +000068#if MEMORYSIZE > 2
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000069/* Keep watermark high for large memory target - at least (2s) */
Jeffrey Goodeba9280d2009-11-16 04:42:34 +000070#define PCMBUF_WATERMARK (BYTERATE * 2)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000071#define MIN_BUFFER_SIZE (BYTERATE * 3)
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -050072/* 1 seconds of buffer is low data */
73#define LOW_DATA DATA_LEVEL(4)
Björn Stenberg6427d122009-01-10 21:10:56 +000074#else
Jeffrey Goodeba9280d2009-11-16 04:42:34 +000075#define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000076#define MIN_BUFFER_SIZE (BYTERATE * 1)
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -050077/* under watermark is low data */
78#define LOW_DATA pcmbuf_watermark
Björn Stenberg6427d122009-01-10 21:10:56 +000079#endif
Miika Pekkarinen20b38972005-07-13 12:48:22 +000080
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000081/* Describes each audio packet - keep it small since there are many of them */
Jeffrey Goode8edac6e2009-11-09 05:45:05 +000082struct chunkdesc
Miika Pekkarinen20b38972005-07-13 12:48:22 +000083{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000084 uint16_t size; /* Actual size (0 < size <= PCMBUF_CHUNK_SIZE) */
85 uint8_t is_end; /* Flag indicating end of track */
86 uint8_t pos_key; /* Who put the position info in (0 = undefined) */
87 unsigned long elapsed; /* Elapsed time to use */
88 off_t offset; /* Offset to use */
Brandon Low413da2a2006-02-07 20:38:55 +000089};
Miika Pekkarinen20b38972005-07-13 12:48:22 +000090
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000091/* General PCM buffer data */
92#define INVALID_BUF_INDEX ((size_t)0 - (size_t)1)
Michael Sevakis0f5cb942006-11-06 18:07:30 +000093
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000094static unsigned char *pcmbuf_buffer;
95static unsigned char *pcmbuf_guardbuf;
96static size_t pcmbuf_size;
97static struct chunkdesc *pcmbuf_descriptors;
98static unsigned int pcmbuf_desc_count;
99static unsigned int position_key = 1;
Brandon Low6c0908b2006-04-23 22:54:34 +0000100
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000101static size_t chunk_ridx;
102static size_t chunk_widx;
Michael Sevakisc537d592011-04-27 03:08:23 +0000103
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000104static size_t pcmbuf_bytes_waiting;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000105static struct chunkdesc *current_desc;
106
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -0500107/* Only written if HAVE_CROSSFADE */
108static size_t pcmbuf_watermark = PCMBUF_WATERMARK;
109
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000110static bool low_latency_mode = false;
111
112static bool pcmbuf_sync_position = false;
Brandon Low6c0908b2006-04-23 22:54:34 +0000113
Michael Sevakisa2b67032011-06-29 06:37:04 +0000114/* Fade effect */
115static unsigned int fade_vol = MIX_AMP_UNITY;
Michael Sevakis5078d462011-08-23 01:37:59 +0000116static enum
117{
118 PCM_NOT_FADING = 0,
119 PCM_FADING_IN,
120 PCM_FADING_OUT,
121} fade_state = PCM_NOT_FADING;
122static bool fade_out_complete = false;
Michael Sevakisa2b67032011-06-29 06:37:04 +0000123
124/* Voice */
125static bool soft_mode = false;
126
Jeffrey Goode9e095342009-11-10 03:46:08 +0000127#ifdef HAVE_CROSSFADE
128/* Crossfade buffer */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000129static unsigned char *crossfade_buffer;
Jeffrey Goode9e095342009-11-10 03:46:08 +0000130
Brandon Low6c0908b2006-04-23 22:54:34 +0000131/* Crossfade related state */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000132static int crossfade_setting;
133static int crossfade_enable_request;
Miika Pekkarinenc9a1b4e2006-05-14 14:08:26 +0000134static bool crossfade_mixmode;
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000135static bool crossfade_auto_skip;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000136
137static enum
138{
139 CROSSFADE_INACTIVE = 0,
140 CROSSFADE_TRACK_CHANGE_STARTED,
141 CROSSFADE_ACTIVE,
142} crossfade_status = CROSSFADE_INACTIVE;
Brandon Low6c0908b2006-04-23 22:54:34 +0000143
144/* Track the current location for processing crossfade */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000145static size_t crossfade_index;
Brandon Low6c0908b2006-04-23 22:54:34 +0000146
147/* Counters for fading in new data */
Michael Sevakis5ff641f2011-06-29 09:39:13 +0000148static size_t crossfade_fade_in_total;
149static size_t crossfade_fade_in_rem;
Brandon Low6c0908b2006-04-23 22:54:34 +0000150
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000151/* Defines for operations on position info when mixing/fading -
152 passed in offset parameter */
153enum
154{
155 MIXFADE_KEEP_POS = -1, /* Keep position info in chunk */
156 MIXFADE_NULLIFY_POS = -2, /* Ignore position info in chunk */
157 /* Positive values cause stamping/restamping */
158};
Brandon Low2da61ff2006-04-24 01:32:28 +0000159
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000160static void crossfade_start(void);
161static void write_to_crossfade(size_t size, unsigned long elapsed,
162 off_t offset);
163static void pcmbuf_finish_crossfade_enable(void);
164#endif /* HAVE_CROSSFADE */
Brandon Low2da61ff2006-04-24 01:32:28 +0000165
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000166/* Thread */
Michael Sevakis938593b2007-03-06 21:00:45 +0000167#ifdef HAVE_PRIORITY_SCHEDULING
Michael Sevakis27cf6772008-03-25 02:34:12 +0000168static int codec_thread_priority = PRIORITY_PLAYBACK;
Michael Sevakis938593b2007-03-06 21:00:45 +0000169#endif
Michael Sevakisb425de72007-03-06 20:32:13 +0000170
Michael Sevakisc537d592011-04-27 03:08:23 +0000171/* Callbacks into playback.c */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000172extern void audio_pcmbuf_position_callback(unsigned long elapsed,
173 off_t offset, unsigned int key);
Michael Sevakisc537d592011-04-27 03:08:23 +0000174extern void audio_pcmbuf_track_change(bool pcmbuf);
175extern bool audio_pcmbuf_may_play(void);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000176extern void audio_pcmbuf_sync_position(void);
Michael Sevakisc537d592011-04-27 03:08:23 +0000177
Jeffrey Goodeb6f15f22009-11-08 04:27:27 +0000178
Jeffrey Goode013fe352009-11-05 17:32:32 +0000179/**************************************/
180
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000181/* Return number of commited bytes in buffer (committed chunks count as
182 a full chunk even if only partially filled) */
183static size_t pcmbuf_unplayed_bytes(void)
Jeffrey Goode013fe352009-11-05 17:32:32 +0000184{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000185 size_t ridx = chunk_ridx;
186 size_t widx = chunk_widx;
187
188 if (ridx > widx)
189 widx += pcmbuf_size;
190
191 return widx - ridx;
Jeffrey Goode013fe352009-11-05 17:32:32 +0000192}
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000193
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -0500194/* Returns TRUE if amount of data is under the target fill size */
195static bool pcmbuf_data_critical(void)
196{
197 return pcmbuf_unplayed_bytes() < LOW_DATA;
198}
199
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000200/* Return the next PCM chunk in the PCM buffer given a byte index into it */
201static size_t index_next(size_t index)
202{
203 index = ALIGN_DOWN(index + PCMBUF_CHUNK_SIZE, PCMBUF_CHUNK_SIZE);
204
205 if (index >= pcmbuf_size)
206 index -= pcmbuf_size;
207
208 return index;
209}
210
211/* Convert a byte offset in the PCM buffer into a pointer in the buffer */
212static FORCE_INLINE void * index_buffer(size_t index)
213{
214 return pcmbuf_buffer + index;
215}
216
217/* Convert a pointer in the buffer into an index offset */
218static FORCE_INLINE size_t buffer_index(void *p)
219{
220 return (uintptr_t)p - (uintptr_t)pcmbuf_buffer;
221}
222
223/* Return a chunk descriptor for a byte index in the buffer */
224static struct chunkdesc * index_chunkdesc(size_t index)
225{
226 return &pcmbuf_descriptors[index / PCMBUF_CHUNK_SIZE];
227}
228
229/* Return a chunk descriptor for a byte index in the buffer, offset by 'offset'
230 chunks */
231static struct chunkdesc * index_chunkdesc_offs(size_t index, int offset)
232{
233 int i = index / PCMBUF_CHUNK_SIZE;
234
235 if (offset != 0)
236 {
237 i = (i + offset) % pcmbuf_desc_count;
238
239 /* remainder => modulus */
240 if (i < 0)
241 i += pcmbuf_desc_count;
242 }
243
244 return &pcmbuf_descriptors[i];
245}
Jeffrey Goode013fe352009-11-05 17:32:32 +0000246
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +0000247
Jeffrey Goode0db33082009-11-11 07:02:18 +0000248/** Accept new PCM data */
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +0000249
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000250/* Split the uncommitted data as needed into chunks, stopping when uncommitted
251 data is below the threshold */
252static void commit_chunks(size_t threshold)
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000253{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000254 size_t index = chunk_widx;
255 size_t end_index = index + pcmbuf_bytes_waiting;
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000256
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000257 /* Copy to the beginning of the buffer all data that must wrap */
258 if (end_index > pcmbuf_size)
259 memcpy(pcmbuf_buffer, pcmbuf_guardbuf, end_index - pcmbuf_size);
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000260
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000261 struct chunkdesc *desc = index_chunkdesc(index);
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000262
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000263 do
Jeffrey Goode73c47912009-11-09 06:53:22 +0000264 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000265 size_t size = MIN(pcmbuf_bytes_waiting, PCMBUF_CHUNK_SIZE);
266 pcmbuf_bytes_waiting -= size;
Michael Sevakisc537d592011-04-27 03:08:23 +0000267
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000268 /* Fill in the values in the new buffer chunk */
269 desc->size = (uint16_t)size;
270
271 /* Advance the current write chunk and make it available to the
272 PCM callback */
273 chunk_widx = index = index_next(index);
274 desc = index_chunkdesc(index);
275
276 /* Reset it before using it */
277 desc->is_end = 0;
278 desc->pos_key = 0;
Jeffrey Goode73c47912009-11-09 06:53:22 +0000279 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000280 while (pcmbuf_bytes_waiting >= threshold);
281}
282
283/* If uncommitted data count is above or equal to the threshold, commit it */
284static FORCE_INLINE void commit_if_needed(size_t threshold)
285{
286 if (pcmbuf_bytes_waiting >= threshold)
287 commit_chunks(threshold);
288}
289
290/* Place positioning information in the chunk */
291static void stamp_chunk(struct chunkdesc *desc, unsigned long elapsed,
292 off_t offset)
293{
294 /* One-time stamping of a given chunk by the same track - new track may
295 overwrite */
296 unsigned int key = position_key;
297
298 if (desc->pos_key != key)
Jeffrey Goode73c47912009-11-09 06:53:22 +0000299 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000300 desc->pos_key = key;
301 desc->elapsed = elapsed;
302 desc->offset = offset;
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000303 }
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000304}
305
Jeffrey Goode0db33082009-11-11 07:02:18 +0000306/* Set priority of the codec thread */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000307#ifdef HAVE_PRIORITY_SCHEDULING
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000308/*
309 * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */
310static void boost_codec_thread(int pcm_fill_state)
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000311{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000312 static const int8_t prios[11] =
313 {
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000314 PRIORITY_PLAYBACK_MAX, /* 0 - 10% */
315 PRIORITY_PLAYBACK_MAX+1, /* 10 - 20% */
316 PRIORITY_PLAYBACK_MAX+3, /* 20 - 30% */
317 PRIORITY_PLAYBACK_MAX+5, /* 30 - 40% */
318 PRIORITY_PLAYBACK_MAX+7, /* 40 - 50% */
319 PRIORITY_PLAYBACK_MAX+8, /* 50 - 60% */
320 PRIORITY_PLAYBACK_MAX+9, /* 60 - 70% */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000321 /* raising priority above 70% shouldn't be needed */
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000322 PRIORITY_PLAYBACK, /* 70 - 80% */
323 PRIORITY_PLAYBACK, /* 80 - 90% */
324 PRIORITY_PLAYBACK, /* 90 -100% */
325 PRIORITY_PLAYBACK, /* 100% */
326 };
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000327
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000328 int new_prio = prios[pcm_fill_state];
329
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000330 /* Keep voice and codec threads at the same priority or else voice
331 * will starve if the codec thread's priority is boosted. */
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000332 if (new_prio != codec_thread_priority)
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000333 {
Michael Sevakis65109732011-02-23 14:31:13 +0000334 codec_thread_set_priority(new_prio);
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000335 voice_thread_set_priority(new_prio);
336 codec_thread_priority = new_prio;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000337 }
338}
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000339#else
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000340#define boost_codec_thread(pcm_fill_state) do{}while(0)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000341#endif /* HAVE_PRIORITY_SCHEDULING */
342
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000343/* Get the next available buffer and size - assumes adequate space exists */
344static void * get_write_buffer(size_t *size)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000345{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000346 /* Obtain current chunk fill address */
347 size_t index = chunk_widx + pcmbuf_bytes_waiting;
348 size_t index_end = pcmbuf_size + PCMBUF_GUARD_SIZE;
Michael Sevakisa2b67032011-06-29 06:37:04 +0000349
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000350 /* Get count to the end of the buffer where a wrap will happen +
351 the guard */
352 size_t endsize = index_end - index;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000353
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000354 /* Return available unwrapped space */
355 *size = MIN(*size, endsize);
356
357 return index_buffer(index);
358}
359
360/* Commit outstanding data leaving less than a chunk size remaining and
361 write position info to the first chunk */
362static void commit_write_buffer(size_t size, unsigned long elapsed, off_t offset)
363{
364 struct chunkdesc *desc = index_chunkdesc(chunk_widx);
365 stamp_chunk(desc, elapsed, offset);
366
367 /* Add this data and commit if one or more chunks are ready */
368 pcmbuf_bytes_waiting += size;
369
370 commit_if_needed(COMMIT_CHUNKS);
371}
372
373/* Request space in the buffer for writing output samples */
374void * pcmbuf_request_buffer(int *count)
375{
376 size_t size = *count * 4;
377
378#ifdef HAVE_CROSSFADE
379 /* We're going to crossfade to a new track, which is now on its way */
380 if (crossfade_status == CROSSFADE_TRACK_CHANGE_STARTED)
381 crossfade_start();
382
383 /* If crossfade has begun, put the new track samples in crossfade_buffer */
384 if (crossfade_status != CROSSFADE_INACTIVE && size > CROSSFADE_BUFSIZE)
385 size = CROSSFADE_BUFSIZE;
386#endif
387
388 enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK);
389 size_t remaining = pcmbuf_unplayed_bytes();
390
391 /* Need to have length bytes to prevent wrapping overwriting - leave one
392 descriptor free to guard so that 0 != full in ring buffer */
393 size_t freespace = pcmbuf_free();
394
395 if (pcmbuf_sync_position)
396 audio_pcmbuf_sync_position();
397
398 if (freespace < size + PCMBUF_CHUNK_SIZE)
399 return NULL;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000400
Jeffrey Goode0db33082009-11-11 07:02:18 +0000401 /* Maintain the buffer level above the watermark */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000402 if (status != CHANNEL_STOPPED)
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000403 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000404 if (low_latency_mode)
405 {
406 /* 1/4s latency. */
407 if (remaining > DATA_LEVEL(1))
408 return NULL;
409 }
410
411 /* Boost CPU if necessary */
412 size_t realrem = pcmbuf_size - freespace;
413
414 if (realrem < pcmbuf_watermark)
Michael Sevakis96f02a92011-07-21 22:25:09 +0000415 trigger_cpu_boost();
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000416
417 boost_codec_thread(realrem*10 / pcmbuf_size);
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000418
Jeffrey Goode9e095342009-11-10 03:46:08 +0000419#ifdef HAVE_CROSSFADE
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000420 /* Disable crossfade if < .5s of audio */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000421 if (remaining < DATA_LEVEL(2))
422 crossfade_status = CROSSFADE_INACTIVE;
Jeffrey Goode9e095342009-11-10 03:46:08 +0000423#endif
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000424 }
Michael Sevakisa2b67032011-06-29 06:37:04 +0000425 else /* !playing */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000426 {
Jeffrey Goode0db33082009-11-11 07:02:18 +0000427 /* Boost CPU for pre-buffer */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000428 trigger_cpu_boost();
429
Jeffrey Goode0db33082009-11-11 07:02:18 +0000430 /* If pre-buffered to the watermark, start playback */
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -0500431 if (!pcmbuf_data_critical() && audio_pcmbuf_may_play())
432 pcmbuf_play_start();
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000433 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000434
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000435 void *buf =
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000436#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000437 crossfade_status != CROSSFADE_INACTIVE ? crossfade_buffer :
Jeffrey Goode9e095342009-11-10 03:46:08 +0000438#endif
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000439 get_write_buffer(&size);
440
441 *count = size / 4;
442 return buf;
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +0000443}
444
Jeffrey Goode0db33082009-11-11 07:02:18 +0000445/* Handle new samples to the buffer */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000446void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000447{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000448 size_t size = count * 4;
449
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000450#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000451 if (crossfade_status != CROSSFADE_INACTIVE)
452 {
453 write_to_crossfade(size, elapsed, offset);
454 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000455 else
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000456#endif
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000457 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000458 commit_write_buffer(size, elapsed, offset);
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000459 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000460
461 /* Revert to position updates by PCM */
462 pcmbuf_sync_position = false;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000463}
464
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000465
Jeffrey Goode0db33082009-11-11 07:02:18 +0000466/** Init */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000467static unsigned int get_next_required_pcmbuf_chunks(void)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000468{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000469 size_t size = MIN_BUFFER_SIZE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000470
Jeffrey Goode9e095342009-11-10 03:46:08 +0000471#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000472 if (crossfade_enable_request != CROSSFADE_ENABLE_OFF)
473 {
474 size_t seconds = global_settings.crossfade_fade_out_delay +
475 global_settings.crossfade_fade_out_duration;
476 size += seconds * BYTERATE;
477 }
Jeffrey Goode9e095342009-11-10 03:46:08 +0000478#endif
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000479
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000480 logf("pcmbuf len: %lu", (unsigned long)(size / BYTERATE));
481 return size / PCMBUF_CHUNK_SIZE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000482}
483
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000484/* Initialize the ringbuffer state */
485static void init_buffer_state(void)
486{
487 /* Reset counters */
488 chunk_ridx = chunk_widx = 0;
489 pcmbuf_bytes_waiting = 0;
490
491 /* Reset first descriptor */
492 struct chunkdesc *desc = pcmbuf_descriptors;
493 desc->is_end = 0;
494 desc->pos_key = 0;
495}
496
497/* Initialize the PCM buffer. The structure looks like this:
498 * ...[|FADEBUF]|---------PCMBUF---------|GUARDBUF|DESCS| */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000499size_t pcmbuf_init(unsigned char *bufend)
500{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000501 unsigned char *bufstart;
502
503 /* Set up the buffers */
504 pcmbuf_desc_count = get_next_required_pcmbuf_chunks();
505 pcmbuf_size = pcmbuf_desc_count * PCMBUF_CHUNK_SIZE;
506 pcmbuf_descriptors = (struct chunkdesc *)bufend - pcmbuf_desc_count;
507
508 pcmbuf_buffer = (void *)pcmbuf_descriptors -
509 pcmbuf_size - PCMBUF_GUARD_SIZE;
510
511 /* Mem-align buffer chunks for more efficient handling in lower layers */
512 pcmbuf_buffer = ALIGN_DOWN(pcmbuf_buffer, (uintptr_t)MEM_ALIGN_SIZE);
513
514 pcmbuf_guardbuf = pcmbuf_buffer + pcmbuf_size;
515 bufstart = pcmbuf_buffer;
Michael Sevakisa802eba2011-07-09 01:49:00 +0000516
Jeffrey Goode9e095342009-11-10 03:46:08 +0000517#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000518 /* Allocate FADEBUF if it will be needed */
519 if (crossfade_enable_request != CROSSFADE_ENABLE_OFF)
520 {
521 bufstart -= CROSSFADE_BUFSIZE;
522 crossfade_buffer = bufstart;
523 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000524
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000525 pcmbuf_finish_crossfade_enable();
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000526#endif /* HAVE_CROSSFADE */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000527
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000528 init_buffer_state();
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000529
Michael Sevakisa2b67032011-06-29 06:37:04 +0000530 pcmbuf_soft_mode(false);
531
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000532 return bufend - bufstart;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000533}
534
535
Jeffrey Goode0db33082009-11-11 07:02:18 +0000536/** Track change */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000537
538/* Place a track change notification in a specific descriptor or post it
539 immediately if the buffer is empty or the index is invalid */
540static void pcmbuf_monitor_track_change_ex(size_t index, int offset)
541{
542 if (chunk_ridx != chunk_widx && index != INVALID_BUF_INDEX)
543 {
544 /* If monitoring, set flag in specified chunk */
545 index_chunkdesc_offs(index, offset)->is_end = 1;
546 }
547 else
548 {
549 /* Post now if no outstanding buffers exist */
550 audio_pcmbuf_track_change(false);
551 }
552}
553
554/* Clear end of track and optionally the positioning info for all data */
555static void pcmbuf_cancel_track_change(bool position)
556{
557 size_t index = chunk_ridx;
558
559 while (1)
560 {
561 struct chunkdesc *desc = index_chunkdesc(index);
562
563 desc->is_end = 0;
564
565 if (position)
566 desc->pos_key = 0;
567
568 if (index == chunk_widx)
569 break;
570
571 index = index_next(index);
572 }
573}
574
575/* Place a track change notification at the end of the buffer or post it
576 immediately if the buffer is empty */
Michael Sevakis65109732011-02-23 14:31:13 +0000577void pcmbuf_monitor_track_change(bool monitor)
578{
579 pcm_play_lock();
580
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000581 if (monitor)
582 pcmbuf_monitor_track_change_ex(chunk_widx, -1);
Michael Sevakis65109732011-02-23 14:31:13 +0000583 else
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000584 pcmbuf_cancel_track_change(false);
Michael Sevakis65109732011-02-23 14:31:13 +0000585
586 pcm_play_unlock();
587}
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000588
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000589void pcmbuf_start_track_change(enum pcm_track_change_type type)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000590{
Jeffrey Goode9e095342009-11-10 03:46:08 +0000591#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000592 bool crossfade = false;
593#endif
594 bool auto_skip = type != TRACK_CHANGE_MANUAL;
595
596 /* Commit all outstanding data before starting next track - tracks don't
597 comingle inside a single buffer chunk */
598 commit_if_needed(COMMIT_ALL_DATA);
599
600 /* Update position key so that:
601 1) Positions are keyed to the track to which they belong for sync
602 purposes
603
604 2) Buffers stamped with the outgoing track's positions are restamped
605 to the incoming track's positions when crossfading
606 */
607 if (++position_key > UINT8_MAX)
608 position_key = 1;
609
610 if (type == TRACK_CHANGE_END_OF_DATA)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000611 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000612 /* If end of all data, force playback */
613 if (audio_pcmbuf_may_play())
614 pcmbuf_play_start();
615 }
616#ifdef HAVE_CROSSFADE
617 /* Determine whether this track change needs to crossfaded and how */
618 else if (crossfade_setting != CROSSFADE_ENABLE_OFF &&
619 !pcmbuf_is_crossfade_active() &&
620 pcmbuf_unplayed_bytes() >= DATA_LEVEL(2) &&
621 !low_latency_mode)
622 {
623 switch (crossfade_setting)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000624 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000625 case CROSSFADE_ENABLE_AUTOSKIP:
626 crossfade = auto_skip;
627 break;
628 case CROSSFADE_ENABLE_MANSKIP:
629 crossfade = !auto_skip;
630 break;
631 case CROSSFADE_ENABLE_SHUFFLE:
632 crossfade = global_settings.playlist_shuffle;
633 break;
634 case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP:
635 crossfade = global_settings.playlist_shuffle || !auto_skip;
636 break;
637 case CROSSFADE_ENABLE_ALWAYS:
638 crossfade = true;
639 break;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000640 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000641 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000642 /* else crossfade is off, crossfade is already active, not enough data,
643 * pcm is off now (implying low data), not crossfading or low latency mode
644 */
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000645
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000646 if (crossfade)
Jeffrey Goode664dc902009-11-11 00:48:17 +0000647 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000648 logf("crossfade track change");
Michael Sevakis65109732011-02-23 14:31:13 +0000649
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000650 /* Don't enable mix mode when skipping tracks manually */
651 crossfade_mixmode = auto_skip &&
652 global_settings.crossfade_fade_out_mixmode;
Michael Sevakis65109732011-02-23 14:31:13 +0000653
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000654 crossfade_auto_skip = auto_skip;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000655
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000656 crossfade_status = CROSSFADE_TRACK_CHANGE_STARTED;
Jeffrey Goode664dc902009-11-11 00:48:17 +0000657
Michael Sevakis65109732011-02-23 14:31:13 +0000658 trigger_cpu_boost();
Michael Sevakisc537d592011-04-27 03:08:23 +0000659
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000660 /* Cancel any pending automatic gapless transition and if a manual
661 skip, stop position updates */
662 pcm_play_lock();
663 pcmbuf_cancel_track_change(!auto_skip);
664 pcm_play_unlock();
Jeffrey Goode664dc902009-11-11 00:48:17 +0000665 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000666 else
667#endif /* HAVE_CROSSFADE */
668 if (auto_skip)
Jeffrey Goode664dc902009-11-11 00:48:17 +0000669 {
670 /* The codec is moving on to the next track, but the current track will
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000671 * continue to play, so mark the last write chunk as the last one in
672 * the track */
673 logf("gapless track change");
674#ifdef HAVE_CROSSFADE
675 if (crossfade_status != CROSSFADE_INACTIVE)
676 {
677 /* Crossfade is still active but crossfade is not happening - for
678 * now, chicken-out and clear out the buffer (just like before) to
679 * avoid fade pile-up on short tracks fading-in over long ones */
680 pcmbuf_play_stop();
681 }
682#endif
Michael Sevakis65109732011-02-23 14:31:13 +0000683 pcmbuf_monitor_track_change(true);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000684 }
685 else
686 {
687 /* Discard old data; caller needs no transition notification */
688 logf("manual track change");
689 pcmbuf_play_stop();
Jeffrey Goode664dc902009-11-11 00:48:17 +0000690 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000691}
692
693
Jeffrey Goode0db33082009-11-11 07:02:18 +0000694/** Playback */
Jeffrey Goode874c9112009-11-09 18:12:20 +0000695
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000696/* PCM driver callback */
697static void pcmbuf_pcm_callback(unsigned char **start, size_t *size)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000698{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000699 /*- Process the chunk that just finished -*/
700 size_t index = chunk_ridx;
701 struct chunkdesc *desc = current_desc;
Michael Sevakis5078d462011-08-23 01:37:59 +0000702
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000703 if (desc)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000704 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000705 /* If last chunk in the track, notify of track change */
706 if (desc->is_end != 0)
Michael Sevakisc537d592011-04-27 03:08:23 +0000707 audio_pcmbuf_track_change(true);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000708
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000709 /* Free it for reuse */
710 chunk_ridx = index = index_next(index);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000711 }
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000712
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000713 /*- Process the new one -*/
714 if (index != chunk_widx && !fade_out_complete)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000715 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000716 current_desc = desc = index_chunkdesc(index);
717
718 *start = index_buffer(index);
719 *size = desc->size;
720
721 if (desc->pos_key != 0)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000722 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000723 /* Positioning chunk - notify playback */
724 audio_pcmbuf_position_callback(desc->elapsed, desc->offset,
725 desc->pos_key);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000726 }
727 }
Jeffrey Goode874c9112009-11-09 18:12:20 +0000728}
729
Jeffrey Goode0db33082009-11-11 07:02:18 +0000730/* Force playback */
Jeffrey Goode874c9112009-11-09 18:12:20 +0000731void pcmbuf_play_start(void)
732{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000733 logf("pcmbuf_play_start");
734
Michael Sevakisa2b67032011-06-29 06:37:04 +0000735 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED &&
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000736 chunk_widx != chunk_ridx)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000737 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000738 current_desc = NULL;
Michael Sevakis5078d462011-08-23 01:37:59 +0000739 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback,
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000740 NULL, 0);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000741 }
742}
743
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000744/* Stop channel, empty and reset buffer */
Jeffrey Goode874c9112009-11-09 18:12:20 +0000745void pcmbuf_play_stop(void)
746{
Jeffrey Goode664dc902009-11-11 00:48:17 +0000747 logf("pcmbuf_play_stop");
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000748
749 /* Reset channel */
Michael Sevakisa2b67032011-06-29 06:37:04 +0000750 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000751
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000752 /* Reset buffer */
753 init_buffer_state();
754
755 /* Revert to position updates by PCM */
756 pcmbuf_sync_position = false;
757
Jeffrey Goode9e095342009-11-10 03:46:08 +0000758#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000759 crossfade_status = CROSSFADE_INACTIVE;
Jeffrey Goode9e095342009-11-10 03:46:08 +0000760#endif
Jeffrey Goode874c9112009-11-09 18:12:20 +0000761
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000762 /* Can unboost the codec thread here no matter who's calling,
763 * pretend full pcm buffer to unboost */
764 boost_codec_thread(10);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000765}
766
767void pcmbuf_pause(bool pause)
768{
Jeffrey Goode664dc902009-11-11 00:48:17 +0000769 logf("pcmbuf_pause: %s", pause?"pause":"play");
Michael Sevakisa2b67032011-06-29 06:37:04 +0000770
771 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED)
772 mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000773 else if (!pause)
774 pcmbuf_play_start();
775}
776
777
Jeffrey Goode0db33082009-11-11 07:02:18 +0000778/** Crossfade */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000779
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000780#ifdef HAVE_CROSSFADE
781/* Find the buffer index that's 'size' bytes away from 'index' */
782static size_t crossfade_find_index(size_t index, size_t size)
783{
784 if (index != INVALID_BUF_INDEX)
785 {
786 size_t i = ALIGN_DOWN(index, PCMBUF_CHUNK_SIZE);
787 size += index - i;
788
789 while (i != chunk_widx)
790 {
791 size_t desc_size = index_chunkdesc(i)->size;
792
793 if (size < desc_size)
794 return i + size;
795
796 size -= desc_size;
797 i = index_next(i);
798 }
799 }
800
801 return INVALID_BUF_INDEX;
802}
803
804/* Align the needed buffer area up to the end of existing data */
805static size_t crossfade_find_buftail(size_t buffer_rem, size_t buffer_need)
806{
807 crossfade_index = chunk_ridx;
808
809 if (buffer_rem > buffer_need)
810 {
811 size_t distance;
812
813 if (crossfade_auto_skip)
814 {
815 /* Automatic track changes only modify the last part of the buffer,
816 * so find the right chunk and sample to start the crossfade */
817 distance = buffer_rem - buffer_need;
818 buffer_rem = buffer_need;
819 }
820 else
821 {
822 /* Manual skips occur immediately, but give 1/5s to process */
823 distance = BYTERATE / 5;
824 buffer_rem -= BYTERATE / 5;
825 }
826
827 crossfade_index = crossfade_find_index(crossfade_index, distance);
828 }
829
830 return buffer_rem;
831}
832
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000833/* Returns the number of bytes _NOT_ mixed/faded */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000834static int crossfade_mix_fade(int factor, size_t size, void *buf, size_t *out_index,
835 unsigned long elapsed, off_t offset)
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000836{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000837 if (size == 0)
Steve Bavinb2dc7f02009-11-26 14:41:31 +0000838 return 0;
839
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000840 size_t index = *out_index;
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000841
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000842 if (index == INVALID_BUF_INDEX)
843 return size;
844
845 const int16_t *input_buf = buf;
846 int16_t *output_buf = (int16_t *)index_buffer(index);
847
848 while (size)
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000849 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000850 struct chunkdesc *desc = index_chunkdesc(index);
851
852 switch (offset)
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000853 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000854 case MIXFADE_NULLIFY_POS:
855 /* Stop position updates for the chunk */
856 desc->pos_key = 0;
857 break;
858 case MIXFADE_KEEP_POS:
859 /* Keep position info as it is */
860 break;
861 default:
862 /* Replace position info */
863 stamp_chunk(desc, elapsed, offset);
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000864 }
865
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000866 size_t rem = desc->size - (index % PCMBUF_CHUNK_SIZE);
867 int16_t *chunk_end = SKIPBYTES(output_buf, rem);
868
869 if (size < rem)
870 rem = size;
871
872 size -= rem;
873
874 do
875 {
876 /* fade left and right channel at once to keep buffer alignment */
877 int32_t left = output_buf[0];
878 int32_t right = output_buf[1];
879
880 if (input_buf)
881 {
882 /* fade the input buffer and mix into the chunk */
883 left += *input_buf++ * factor >> 8;
884 right += *input_buf++ * factor >> 8;
885 left = clip_sample_16(left);
886 right = clip_sample_16(right);
887 }
888 else
889 {
890 /* fade the chunk only */
891 left = left * factor >> 8;
892 right = right * factor >> 8;
893 }
894
895 *output_buf++ = left;
896 *output_buf++ = right;
897
898 rem -= 4;
899 }
900 while (rem);
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000901
902 /* move to next chunk as needed */
903 if (output_buf >= chunk_end)
904 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000905 index = index_next(index);
906
907 if (index == chunk_widx)
908 {
909 /* End of existing data */
910 *out_index = INVALID_BUF_INDEX;
911 return size;
912 }
913
914 output_buf = (int16_t *)index_buffer(index);
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000915 }
916 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000917
918 *out_index = buffer_index(output_buf);
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000919 return 0;
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000920}
921
Jeffrey Goode0db33082009-11-11 07:02:18 +0000922/* Initializes crossfader, calculates all necessary parameters and performs
923 * fade-out with the PCM buffer. */
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000924static void crossfade_start(void)
925{
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000926 logf("crossfade_start");
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000927
928 pcm_play_lock();
Brandon Low6c0908b2006-04-23 22:54:34 +0000929
Brandon Low413da2a2006-02-07 20:38:55 +0000930 /* Initialize the crossfade buffer size to all of the buffered data that
931 * has not yet been sent to the DMA */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000932 size_t unplayed = pcmbuf_unplayed_bytes();
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000933
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000934 /* Reject crossfade if less than .5s of data */
935 if (unplayed < DATA_LEVEL(2))
Brandon Low6c0908b2006-04-23 22:54:34 +0000936 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000937 logf("crossfade rejected");
938
939 crossfade_status = CROSSFADE_INACTIVE;
940
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000941 if (crossfade_auto_skip)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000942 pcmbuf_monitor_track_change(true);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000943
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000944 pcm_play_unlock();
945 return;
946 }
947
948 /* Fading will happen */
949 crossfade_status = CROSSFADE_ACTIVE;
950
951 /* Get fade info from settings. */
952 size_t fade_out_delay = global_settings.crossfade_fade_out_delay * BYTERATE;
953 size_t fade_out_rem = global_settings.crossfade_fade_out_duration * BYTERATE;
954 size_t fade_in_delay = global_settings.crossfade_fade_in_delay * BYTERATE;
955 size_t fade_in_duration = global_settings.crossfade_fade_in_duration * BYTERATE;
956
957 if (!crossfade_auto_skip)
958 {
959 /* Forego fade-in delay on manual skip - do the best to preserve auto skip
960 relationship */
961 if (fade_out_delay > fade_in_delay)
962 fade_out_delay -= fade_in_delay;
963 else
964 fade_out_delay = 0;
965
966 fade_in_delay = 0;
967 }
968
969 size_t fade_out_need = fade_out_delay + fade_out_rem;
970
Jeffrey Goode15351e82009-11-12 04:24:41 +0000971 if (!crossfade_mixmode)
972 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000973 size_t buffer_rem = crossfade_find_buftail(unplayed, fade_out_need);
974
975 pcm_play_unlock();
976
977 if (buffer_rem < fade_out_need)
978 {
979 /* Existing buffers are short */
980 size_t fade_out_short = fade_out_need - buffer_rem;
981
982 if (fade_out_rem >= fade_out_short)
983 {
984 /* Truncate fade-out duration */
985 fade_out_rem -= fade_out_short;
986 }
987 else
988 {
989 /* Truncate fade-out and fade-out delay */
990 fade_out_delay = fade_out_rem;
991 fade_out_rem = 0;
992 }
993 }
994
995 /* Completely process the crossfade fade-out effect with current PCM buffer */
996
Jeffrey Goode15351e82009-11-12 04:24:41 +0000997 /* Fade out the specified amount of the already processed audio */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000998 size_t fade_out_total = fade_out_rem;
Jeffrey Goode15351e82009-11-12 04:24:41 +0000999
1000 /* Find the right chunk and sample to start fading out */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001001 size_t fade_out_index = crossfade_find_index(crossfade_index, fade_out_delay);
Jeffrey Goode15351e82009-11-12 04:24:41 +00001002
1003 while (fade_out_rem > 0)
1004 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001005 /* Each 1/20 second of audio will have the same fade applied */
1006 size_t block_rem = MIN(BYTERATE / 20, fade_out_rem);
1007 int factor = (fade_out_rem << 8) / fade_out_total;
Jeffrey Goode15351e82009-11-12 04:24:41 +00001008
1009 fade_out_rem -= block_rem;
1010
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001011 crossfade_mix_fade(factor, block_rem, NULL, &fade_out_index,
1012 0, MIXFADE_KEEP_POS);
Jeffrey Goode15351e82009-11-12 04:24:41 +00001013 }
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001014
Jeffrey Gooded1963e12009-11-13 21:58:41 +00001015 /* zero out the rest of the buffer */
Michael Sevakis9384cda2011-08-28 08:59:14 +00001016 crossfade_mix_fade(0, pcmbuf_size, NULL, &fade_out_index,
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001017 0, MIXFADE_NULLIFY_POS);
1018
1019 pcm_play_lock();
Jeffrey Goode15351e82009-11-12 04:24:41 +00001020 }
1021
1022 /* Initialize fade-in counters */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001023 crossfade_fade_in_total = fade_in_duration;
1024 crossfade_fade_in_rem = fade_in_duration;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001025
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001026 /* Find the right chunk and sample to start fading in - redo from read
1027 chunk in case original position were/was overrun in callback - the
1028 track change event _must not_ ever fail to happen */
1029 unplayed = pcmbuf_unplayed_bytes() + fade_in_delay;
Brandon Low6c0908b2006-04-23 22:54:34 +00001030
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001031 crossfade_find_buftail(unplayed, fade_out_need);
1032
1033 if (crossfade_auto_skip)
1034 pcmbuf_monitor_track_change_ex(crossfade_index, 0);
1035
1036 pcm_play_unlock();
1037
Jeffrey Goode15351e82009-11-12 04:24:41 +00001038 logf("crossfade_start done!");
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001039}
1040
Jeffrey Goode0db33082009-11-11 07:02:18 +00001041/* Perform fade-in of new track */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001042static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset)
Brandon Low6c0908b2006-04-23 22:54:34 +00001043{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001044 unsigned char *buf = crossfade_buffer;
1045
1046 if (crossfade_fade_in_rem)
Brandon Low6c0908b2006-04-23 22:54:34 +00001047 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001048 /* Fade factor for this packet */
1049 int factor =
1050 ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) /
1051 crossfade_fade_in_total;
1052 /* Bytes to fade */
1053 size_t fade_rem = MIN(size, crossfade_fade_in_rem);
Brandon Low9602dd72006-04-25 12:34:28 +00001054
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001055 /* We _will_ fade this many bytes */
1056 crossfade_fade_in_rem -= fade_rem;
Brandon Low9602dd72006-04-25 12:34:28 +00001057
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001058 if (crossfade_index != INVALID_BUF_INDEX)
Brandon Low6c0908b2006-04-23 22:54:34 +00001059 {
Brandon Low9602dd72006-04-25 12:34:28 +00001060 /* Mix the data */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001061 size_t fade_total = fade_rem;
1062 fade_rem = crossfade_mix_fade(factor, fade_rem, buf, &crossfade_index,
1063 elapsed, offset);
1064 fade_total -= fade_rem;
1065 size -= fade_total;
1066 buf += fade_total;
1067
1068 if (!size)
Brandon Low08cdc432006-04-25 16:12:43 +00001069 return;
Brandon Low6c0908b2006-04-23 22:54:34 +00001070 }
Brandon Low9602dd72006-04-25 12:34:28 +00001071
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001072 /* Fade remaining samples in place */
1073 int samples = fade_rem / 4;
1074 int16_t *input_buf = (int16_t *)buf;
1075
1076 while (samples--)
Jeffrey Goode013fe352009-11-05 17:32:32 +00001077 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001078 int32_t left = input_buf[0];
1079 int32_t right = input_buf[1];
1080 *input_buf++ = left * factor >> 8;
1081 *input_buf++ = right * factor >> 8;
Jeffrey Goode013fe352009-11-05 17:32:32 +00001082 }
Brandon Low9aa49a42006-04-25 02:28:21 +00001083 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001084
1085 if (crossfade_index != INVALID_BUF_INDEX)
1086 {
1087 /* Mix the data */
1088 size_t mix_total = size;
1089
1090 /* A factor of 256 means mix only, no fading */
1091 size = crossfade_mix_fade(256, size, buf, &crossfade_index,
1092 elapsed, offset);
1093 buf += mix_total - size;
1094
1095 if (!size)
1096 return;
1097 }
1098
1099 /* Data might remain in the fade buffer yet the fade-in has run its
1100 course - finish it off as normal chunks */
1101 while (size > 0)
1102 {
1103 size_t copy_n = size;
1104 unsigned char *outbuf = get_write_buffer(&copy_n);
1105 memcpy(outbuf, buf, copy_n);
1106 commit_write_buffer(copy_n, elapsed, offset);
1107 buf += copy_n;
1108 size -= copy_n;
1109 }
1110
Jeffrey Goode0db33082009-11-11 07:02:18 +00001111 /* if no more fading-in to do, stop the crossfade */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001112#if 0
1113 /* This way (the previous way) can cause a sudden volume jump if mixable
1114 data is used up before the fade-in completes and that just sounds wrong
1115 -- jethead71 */
1116 if (!crossfade_fade_in_rem || crossfade_index == INVALID_BUF_INDEX)
1117#endif
1118 /* Let fade-in complete even if not fully overlapping the existing data */
1119 if (!crossfade_fade_in_rem)
1120 crossfade_status = CROSSFADE_INACTIVE;
Brandon Low413da2a2006-02-07 20:38:55 +00001121}
1122
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001123static void pcmbuf_finish_crossfade_enable(void)
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001124{
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001125 /* Copy the pending setting over now */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001126 crossfade_setting = crossfade_enable_request;
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001127
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001128 pcmbuf_watermark = (crossfade_setting != CROSSFADE_ENABLE_OFF && pcmbuf_size) ?
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001129 /* If crossfading, try to keep the buffer full other than 1 second */
Jeffrey Goodeba9280d2009-11-16 04:42:34 +00001130 (pcmbuf_size - BYTERATE) :
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001131 /* Otherwise, just use the default */
1132 PCMBUF_WATERMARK;
1133}
Brandon Low37faaab2006-04-20 02:30:59 +00001134
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001135bool pcmbuf_is_crossfade_active(void)
1136{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001137 return crossfade_status != CROSSFADE_INACTIVE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001138}
1139
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001140void pcmbuf_request_crossfade_enable(int setting)
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001141{
1142 /* Next setting to be used, not applied now */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001143 crossfade_enable_request = setting;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001144}
1145
1146bool pcmbuf_is_same_size(void)
1147{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001148 /* if pcmbuf_buffer is NULL, then not set up yet even once so always */
1149 bool same_size = pcmbuf_buffer ?
1150 (get_next_required_pcmbuf_chunks() == pcmbuf_desc_count) : true;
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001151
Jeffrey Goode5c69a422009-11-09 17:11:53 +00001152 /* no buffer change needed, so finish crossfade setup now */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001153 if (same_size)
1154 pcmbuf_finish_crossfade_enable();
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001155
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001156 return same_size;
1157}
Jeffrey Goode9e095342009-11-10 03:46:08 +00001158#endif /* HAVE_CROSSFADE */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001159
1160
Jeffrey Goode0db33082009-11-11 07:02:18 +00001161/** Debug menu, other metrics */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001162
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001163/* Amount of bytes left in the buffer, accounting for uncommitted bytes */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001164size_t pcmbuf_free(void)
1165{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001166 return pcmbuf_size - pcmbuf_unplayed_bytes() - pcmbuf_bytes_waiting;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001167}
1168
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001169/* Data bytes allocated for buffer */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001170size_t pcmbuf_get_bufsize(void)
1171{
1172 return pcmbuf_size;
1173}
1174
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001175/* Number of committed descriptors */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001176int pcmbuf_used_descs(void)
1177{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001178 return pcmbuf_unplayed_bytes() / PCMBUF_CHUNK_SIZE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001179}
1180
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001181/* Total number of descriptors allocated */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001182int pcmbuf_descs(void)
1183{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001184 return pcmbuf_desc_count;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001185}
1186
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001187
Michael Sevakisa2b67032011-06-29 06:37:04 +00001188/** Fading and channel volume control */
1189
1190/* Sync the channel amplitude to all states */
1191static void pcmbuf_update_volume(void)
1192{
1193 unsigned int vol = fade_vol;
1194
1195 if (soft_mode)
1196 vol >>= 2;
1197
1198 mixer_channel_set_amplitude(PCM_MIXER_CHAN_PLAYBACK, vol);
1199}
1200
Michael Sevakis5078d462011-08-23 01:37:59 +00001201/* Tick that does the fade for the playback channel */
1202static void pcmbuf_fade_tick(void)
1203{
1204 /* ~1/3 second for full range fade */
1205 const unsigned int fade_step = MIX_AMP_UNITY / (HZ / 3);
1206
1207 if (fade_state == PCM_FADING_IN)
1208 fade_vol += MIN(fade_step, MIX_AMP_UNITY - fade_vol);
1209 else if (fade_state == PCM_FADING_OUT)
1210 fade_vol -= MIN(fade_step, fade_vol - MIX_AMP_MUTE);
1211
1212 pcmbuf_update_volume();
1213
1214 if (fade_vol == MIX_AMP_MUTE || fade_vol == MIX_AMP_UNITY)
1215 {
1216 /* Fade is complete */
1217 tick_remove_task(pcmbuf_fade_tick);
Michael Sevakis5078d462011-08-23 01:37:59 +00001218 if (fade_state == PCM_FADING_OUT)
1219 {
1220 /* Tell PCM to stop at its earliest convenience */
1221 fade_out_complete = true;
1222 }
1223
1224 fade_state = PCM_NOT_FADING;
1225 }
1226}
1227
Michael Sevakis4d903f22011-08-23 05:58:28 +00001228/* Fade channel in or out in the background */
Michael Sevakisa2b67032011-06-29 06:37:04 +00001229void pcmbuf_fade(bool fade, bool in)
1230{
Michael Sevakis4d903f22011-08-23 05:58:28 +00001231 /* Must pause any active fade */
Michael Sevakis5078d462011-08-23 01:37:59 +00001232 pcm_play_lock();
1233
1234 if (fade_state != PCM_NOT_FADING)
1235 tick_remove_task(pcmbuf_fade_tick);
1236
1237 fade_out_complete = false;
1238
1239 pcm_play_unlock();
1240
Michael Sevakisa2b67032011-06-29 06:37:04 +00001241 if (!fade)
1242 {
1243 /* Simply set the level */
Michael Sevakis5078d462011-08-23 01:37:59 +00001244 fade_state = PCM_NOT_FADING;
Michael Sevakisa2b67032011-06-29 06:37:04 +00001245 fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE;
Michael Sevakis5078d462011-08-23 01:37:59 +00001246 pcmbuf_update_volume();
Michael Sevakisa2b67032011-06-29 06:37:04 +00001247 }
1248 else
1249 {
Michael Sevakis5078d462011-08-23 01:37:59 +00001250 /* Set direction and resume fade from current point */
1251 fade_state = in ? PCM_FADING_IN : PCM_FADING_OUT;
1252 tick_add_task(pcmbuf_fade_tick);
Michael Sevakisa2b67032011-06-29 06:37:04 +00001253 }
1254}
1255
Michael Sevakis5078d462011-08-23 01:37:59 +00001256/* Return 'true' if fade is in progress */
1257bool pcmbuf_fading(void)
1258{
1259 return fade_state != PCM_NOT_FADING;
1260}
Michael Sevakisa2b67032011-06-29 06:37:04 +00001261
Michael Sevakis697aa7f2011-08-25 00:12:19 +00001262/* Quiet-down the channel if 'shhh' is true or else play at normal level */
1263void pcmbuf_soft_mode(bool shhh)
1264{
1265 /* Have to block the tick or improper order could leave volume in soft
1266 mode if fading reads the old value first but updates after us. */
1267 int res = fade_state != PCM_NOT_FADING ?
1268 tick_remove_task(pcmbuf_fade_tick) : -1;
1269
1270 soft_mode = shhh;
1271 pcmbuf_update_volume();
1272
1273 if (res == 0)
1274 tick_add_task(pcmbuf_fade_tick);
1275}
1276
1277
Jeffrey Goode0db33082009-11-11 07:02:18 +00001278/** Misc */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001279
1280bool pcmbuf_is_lowdata(void)
1281{
Michael Sevakis31567302011-08-28 14:26:59 +00001282 enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK);
1283
1284 if (status != CHANNEL_PLAYING || pcmbuf_is_crossfade_active())
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001285 return false;
1286
Michael Sevakisc7f3a0b2012-01-20 18:10:12 -05001287 return pcmbuf_data_critical();
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001288}
1289
1290void pcmbuf_set_low_latency(bool state)
1291{
1292 low_latency_mode = state;
1293}
1294
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001295/* Return the current position key value */
1296unsigned int pcmbuf_get_position_key(void)
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001297{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001298 return position_key;
1299}
1300
1301/* Set position updates to be synchronous and immediate in addition to during
1302 PCM frames - cancelled upon first codec insert or upon stopping */
1303void pcmbuf_sync_position_update(void)
1304{
1305 pcmbuf_sync_position = true;
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001306}