blob: f57021d2374c1aa84d68d7ac0bf35310c3d16c23 [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"
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +000030#include "playback.h"
Michael Sevakis65109732011-02-23 14:31:13 +000031#include "codec_thread.h"
Jeffrey Goodefaa47bf2009-10-22 00:59:42 +000032
33/* Define LOGF_ENABLE to enable logf output in this file */
34/*#define LOGF_ENABLE*/
Miika Pekkarinen20b38972005-07-13 12:48:22 +000035#include "logf.h"
Thomas Martitz35e8b142010-06-21 16:53:00 +000036#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
Miika Pekkarinen20b38972005-07-13 12:48:22 +000037#include "cpu.h"
38#endif
Miika Pekkarinenf090dc32005-07-21 11:44:00 +000039#include "settings.h"
40#include "audio.h"
Jeffrey Gooded0ac0452009-11-09 05:58:02 +000041#include "voice_thread.h"
Miika Pekkarinen159c52d2005-08-20 11:13:19 +000042#include "dsp.h"
Miika Pekkarinen20b38972005-07-13 12:48:22 +000043
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000044/* This is the target fill size of chunks on the pcm buffer
45 Can be any number of samples but power of two sizes make for faster and
46 smaller math - must be < 65536 bytes */
47#define PCMBUF_CHUNK_SIZE 8192u
48#define PCMBUF_GUARD_SIZE 1024u
Jeffrey Goode04b01e12009-11-05 21:59:36 +000049
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000050/* Mnemonics for common data commit thresholds */
51#define COMMIT_CHUNKS PCMBUF_CHUNK_SIZE
52#define COMMIT_ALL_DATA 1u
53
54 /* Size of the crossfade buffer where codec data is written to be faded
55 on commit */
56#define CROSSFADE_BUFSIZE 8192u
57
58/* Number of bytes played per second:
59 (sample rate * 2 channels * 2 bytes/sample) */
Jeffrey Goodeba9280d2009-11-16 04:42:34 +000060#define BYTERATE (NATIVE_FREQUENCY * 4)
61
Björn Stenberg6427d122009-01-10 21:10:56 +000062#if MEMORYSIZE > 2
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000063/* Keep watermark high for large memory target - at least (2s) */
Jeffrey Goodeba9280d2009-11-16 04:42:34 +000064#define PCMBUF_WATERMARK (BYTERATE * 2)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000065#define MIN_BUFFER_SIZE (BYTERATE * 3)
Björn Stenberg6427d122009-01-10 21:10:56 +000066#else
Jeffrey Goodeba9280d2009-11-16 04:42:34 +000067#define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000068#define MIN_BUFFER_SIZE (BYTERATE * 1)
Björn Stenberg6427d122009-01-10 21:10:56 +000069#endif
Miika Pekkarinen20b38972005-07-13 12:48:22 +000070
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000071/* Describes each audio packet - keep it small since there are many of them */
Jeffrey Goode8edac6e2009-11-09 05:45:05 +000072struct chunkdesc
Miika Pekkarinen20b38972005-07-13 12:48:22 +000073{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000074 uint16_t size; /* Actual size (0 < size <= PCMBUF_CHUNK_SIZE) */
75 uint8_t is_end; /* Flag indicating end of track */
76 uint8_t pos_key; /* Who put the position info in (0 = undefined) */
77 unsigned long elapsed; /* Elapsed time to use */
78 off_t offset; /* Offset to use */
Brandon Low413da2a2006-02-07 20:38:55 +000079};
Miika Pekkarinen20b38972005-07-13 12:48:22 +000080
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000081/* General PCM buffer data */
82#define INVALID_BUF_INDEX ((size_t)0 - (size_t)1)
Michael Sevakis0f5cb942006-11-06 18:07:30 +000083
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000084static unsigned char *pcmbuf_buffer;
85static unsigned char *pcmbuf_guardbuf;
86static size_t pcmbuf_size;
87static struct chunkdesc *pcmbuf_descriptors;
88static unsigned int pcmbuf_desc_count;
89static unsigned int position_key = 1;
Brandon Low6c0908b2006-04-23 22:54:34 +000090
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000091static size_t chunk_ridx;
92static size_t chunk_widx;
Michael Sevakisc537d592011-04-27 03:08:23 +000093
Michael Sevakis7ad2cad2011-08-28 07:45:35 +000094static size_t pcmbuf_bytes_waiting;
95
96static size_t pcmbuf_watermark;
97static struct chunkdesc *current_desc;
98
99static bool low_latency_mode = false;
100
101static bool pcmbuf_sync_position = false;
Brandon Low6c0908b2006-04-23 22:54:34 +0000102
Michael Sevakisa2b67032011-06-29 06:37:04 +0000103/* Fade effect */
104static unsigned int fade_vol = MIX_AMP_UNITY;
Michael Sevakis5078d462011-08-23 01:37:59 +0000105static enum
106{
107 PCM_NOT_FADING = 0,
108 PCM_FADING_IN,
109 PCM_FADING_OUT,
110} fade_state = PCM_NOT_FADING;
111static bool fade_out_complete = false;
Michael Sevakisa2b67032011-06-29 06:37:04 +0000112
113/* Voice */
114static bool soft_mode = false;
115
Jeffrey Goode9e095342009-11-10 03:46:08 +0000116#ifdef HAVE_CROSSFADE
117/* Crossfade buffer */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000118static unsigned char *crossfade_buffer;
Jeffrey Goode9e095342009-11-10 03:46:08 +0000119
Brandon Low6c0908b2006-04-23 22:54:34 +0000120/* Crossfade related state */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000121static int crossfade_setting;
122static int crossfade_enable_request;
Miika Pekkarinenc9a1b4e2006-05-14 14:08:26 +0000123static bool crossfade_mixmode;
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000124static bool crossfade_auto_skip;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000125
126static enum
127{
128 CROSSFADE_INACTIVE = 0,
129 CROSSFADE_TRACK_CHANGE_STARTED,
130 CROSSFADE_ACTIVE,
131} crossfade_status = CROSSFADE_INACTIVE;
Brandon Low6c0908b2006-04-23 22:54:34 +0000132
133/* Track the current location for processing crossfade */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000134static size_t crossfade_index;
Brandon Low6c0908b2006-04-23 22:54:34 +0000135
136/* Counters for fading in new data */
Michael Sevakis5ff641f2011-06-29 09:39:13 +0000137static size_t crossfade_fade_in_total;
138static size_t crossfade_fade_in_rem;
Brandon Low6c0908b2006-04-23 22:54:34 +0000139
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000140/* Defines for operations on position info when mixing/fading -
141 passed in offset parameter */
142enum
143{
144 MIXFADE_KEEP_POS = -1, /* Keep position info in chunk */
145 MIXFADE_NULLIFY_POS = -2, /* Ignore position info in chunk */
146 /* Positive values cause stamping/restamping */
147};
Brandon Low2da61ff2006-04-24 01:32:28 +0000148
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000149static void crossfade_start(void);
150static void write_to_crossfade(size_t size, unsigned long elapsed,
151 off_t offset);
152static void pcmbuf_finish_crossfade_enable(void);
153#endif /* HAVE_CROSSFADE */
Brandon Low2da61ff2006-04-24 01:32:28 +0000154
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000155/* Thread */
Michael Sevakis938593b2007-03-06 21:00:45 +0000156#ifdef HAVE_PRIORITY_SCHEDULING
Michael Sevakis27cf6772008-03-25 02:34:12 +0000157static int codec_thread_priority = PRIORITY_PLAYBACK;
Michael Sevakis938593b2007-03-06 21:00:45 +0000158#endif
Michael Sevakisb425de72007-03-06 20:32:13 +0000159
Brandon Low413da2a2006-02-07 20:38:55 +0000160/* Helpful macros for use in conditionals this assumes some of the above
161 * static variable names */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000162#define DATA_LEVEL(quarter_secs) (NATIVE_FREQUENCY * (quarter_secs))
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000163
Michael Sevakisc537d592011-04-27 03:08:23 +0000164/* Callbacks into playback.c */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000165extern void audio_pcmbuf_position_callback(unsigned long elapsed,
166 off_t offset, unsigned int key);
Michael Sevakisc537d592011-04-27 03:08:23 +0000167extern void audio_pcmbuf_track_change(bool pcmbuf);
168extern bool audio_pcmbuf_may_play(void);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000169extern void audio_pcmbuf_sync_position(void);
Michael Sevakisc537d592011-04-27 03:08:23 +0000170
Jeffrey Goodeb6f15f22009-11-08 04:27:27 +0000171
Jeffrey Goode013fe352009-11-05 17:32:32 +0000172/**************************************/
173
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000174/* Return number of commited bytes in buffer (committed chunks count as
175 a full chunk even if only partially filled) */
176static size_t pcmbuf_unplayed_bytes(void)
Jeffrey Goode013fe352009-11-05 17:32:32 +0000177{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000178 size_t ridx = chunk_ridx;
179 size_t widx = chunk_widx;
180
181 if (ridx > widx)
182 widx += pcmbuf_size;
183
184 return widx - ridx;
Jeffrey Goode013fe352009-11-05 17:32:32 +0000185}
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000186
187/* Return the next PCM chunk in the PCM buffer given a byte index into it */
188static size_t index_next(size_t index)
189{
190 index = ALIGN_DOWN(index + PCMBUF_CHUNK_SIZE, PCMBUF_CHUNK_SIZE);
191
192 if (index >= pcmbuf_size)
193 index -= pcmbuf_size;
194
195 return index;
196}
197
198/* Convert a byte offset in the PCM buffer into a pointer in the buffer */
199static FORCE_INLINE void * index_buffer(size_t index)
200{
201 return pcmbuf_buffer + index;
202}
203
204/* Convert a pointer in the buffer into an index offset */
205static FORCE_INLINE size_t buffer_index(void *p)
206{
207 return (uintptr_t)p - (uintptr_t)pcmbuf_buffer;
208}
209
210/* Return a chunk descriptor for a byte index in the buffer */
211static struct chunkdesc * index_chunkdesc(size_t index)
212{
213 return &pcmbuf_descriptors[index / PCMBUF_CHUNK_SIZE];
214}
215
216/* Return a chunk descriptor for a byte index in the buffer, offset by 'offset'
217 chunks */
218static struct chunkdesc * index_chunkdesc_offs(size_t index, int offset)
219{
220 int i = index / PCMBUF_CHUNK_SIZE;
221
222 if (offset != 0)
223 {
224 i = (i + offset) % pcmbuf_desc_count;
225
226 /* remainder => modulus */
227 if (i < 0)
228 i += pcmbuf_desc_count;
229 }
230
231 return &pcmbuf_descriptors[i];
232}
Jeffrey Goode013fe352009-11-05 17:32:32 +0000233
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +0000234
Jeffrey Goode0db33082009-11-11 07:02:18 +0000235/** Accept new PCM data */
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +0000236
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000237/* Split the uncommitted data as needed into chunks, stopping when uncommitted
238 data is below the threshold */
239static void commit_chunks(size_t threshold)
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000240{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000241 size_t index = chunk_widx;
242 size_t end_index = index + pcmbuf_bytes_waiting;
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000243
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000244 /* Copy to the beginning of the buffer all data that must wrap */
245 if (end_index > pcmbuf_size)
246 memcpy(pcmbuf_buffer, pcmbuf_guardbuf, end_index - pcmbuf_size);
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000247
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000248 struct chunkdesc *desc = index_chunkdesc(index);
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000249
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000250 do
Jeffrey Goode73c47912009-11-09 06:53:22 +0000251 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000252 size_t size = MIN(pcmbuf_bytes_waiting, PCMBUF_CHUNK_SIZE);
253 pcmbuf_bytes_waiting -= size;
Michael Sevakisc537d592011-04-27 03:08:23 +0000254
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000255 /* Fill in the values in the new buffer chunk */
256 desc->size = (uint16_t)size;
257
258 /* Advance the current write chunk and make it available to the
259 PCM callback */
260 chunk_widx = index = index_next(index);
261 desc = index_chunkdesc(index);
262
263 /* Reset it before using it */
264 desc->is_end = 0;
265 desc->pos_key = 0;
Jeffrey Goode73c47912009-11-09 06:53:22 +0000266 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000267 while (pcmbuf_bytes_waiting >= threshold);
268}
269
270/* If uncommitted data count is above or equal to the threshold, commit it */
271static FORCE_INLINE void commit_if_needed(size_t threshold)
272{
273 if (pcmbuf_bytes_waiting >= threshold)
274 commit_chunks(threshold);
275}
276
277/* Place positioning information in the chunk */
278static void stamp_chunk(struct chunkdesc *desc, unsigned long elapsed,
279 off_t offset)
280{
281 /* One-time stamping of a given chunk by the same track - new track may
282 overwrite */
283 unsigned int key = position_key;
284
285 if (desc->pos_key != key)
Jeffrey Goode73c47912009-11-09 06:53:22 +0000286 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000287 desc->pos_key = key;
288 desc->elapsed = elapsed;
289 desc->offset = offset;
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000290 }
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000291}
292
Jeffrey Goode0db33082009-11-11 07:02:18 +0000293/* Set priority of the codec thread */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000294#ifdef HAVE_PRIORITY_SCHEDULING
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000295/*
296 * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */
297static void boost_codec_thread(int pcm_fill_state)
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000298{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000299 static const int8_t prios[11] =
300 {
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000301 PRIORITY_PLAYBACK_MAX, /* 0 - 10% */
302 PRIORITY_PLAYBACK_MAX+1, /* 10 - 20% */
303 PRIORITY_PLAYBACK_MAX+3, /* 20 - 30% */
304 PRIORITY_PLAYBACK_MAX+5, /* 30 - 40% */
305 PRIORITY_PLAYBACK_MAX+7, /* 40 - 50% */
306 PRIORITY_PLAYBACK_MAX+8, /* 50 - 60% */
307 PRIORITY_PLAYBACK_MAX+9, /* 60 - 70% */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000308 /* raising priority above 70% shouldn't be needed */
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000309 PRIORITY_PLAYBACK, /* 70 - 80% */
310 PRIORITY_PLAYBACK, /* 80 - 90% */
311 PRIORITY_PLAYBACK, /* 90 -100% */
312 PRIORITY_PLAYBACK, /* 100% */
313 };
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000314
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000315 int new_prio = prios[pcm_fill_state];
316
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000317 /* Keep voice and codec threads at the same priority or else voice
318 * will starve if the codec thread's priority is boosted. */
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000319 if (new_prio != codec_thread_priority)
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000320 {
Michael Sevakis65109732011-02-23 14:31:13 +0000321 codec_thread_set_priority(new_prio);
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000322 voice_thread_set_priority(new_prio);
323 codec_thread_priority = new_prio;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000324 }
325}
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000326#else
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000327#define boost_codec_thread(pcm_fill_state) do{}while(0)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000328#endif /* HAVE_PRIORITY_SCHEDULING */
329
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000330/* Get the next available buffer and size - assumes adequate space exists */
331static void * get_write_buffer(size_t *size)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000332{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000333 /* Obtain current chunk fill address */
334 size_t index = chunk_widx + pcmbuf_bytes_waiting;
335 size_t index_end = pcmbuf_size + PCMBUF_GUARD_SIZE;
Michael Sevakisa2b67032011-06-29 06:37:04 +0000336
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000337 /* Get count to the end of the buffer where a wrap will happen +
338 the guard */
339 size_t endsize = index_end - index;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000340
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000341 /* Return available unwrapped space */
342 *size = MIN(*size, endsize);
343
344 return index_buffer(index);
345}
346
347/* Commit outstanding data leaving less than a chunk size remaining and
348 write position info to the first chunk */
349static void commit_write_buffer(size_t size, unsigned long elapsed, off_t offset)
350{
351 struct chunkdesc *desc = index_chunkdesc(chunk_widx);
352 stamp_chunk(desc, elapsed, offset);
353
354 /* Add this data and commit if one or more chunks are ready */
355 pcmbuf_bytes_waiting += size;
356
357 commit_if_needed(COMMIT_CHUNKS);
358}
359
360/* Request space in the buffer for writing output samples */
361void * pcmbuf_request_buffer(int *count)
362{
363 size_t size = *count * 4;
364
365#ifdef HAVE_CROSSFADE
366 /* We're going to crossfade to a new track, which is now on its way */
367 if (crossfade_status == CROSSFADE_TRACK_CHANGE_STARTED)
368 crossfade_start();
369
370 /* If crossfade has begun, put the new track samples in crossfade_buffer */
371 if (crossfade_status != CROSSFADE_INACTIVE && size > CROSSFADE_BUFSIZE)
372 size = CROSSFADE_BUFSIZE;
373#endif
374
375 enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK);
376 size_t remaining = pcmbuf_unplayed_bytes();
377
378 /* Need to have length bytes to prevent wrapping overwriting - leave one
379 descriptor free to guard so that 0 != full in ring buffer */
380 size_t freespace = pcmbuf_free();
381
382 if (pcmbuf_sync_position)
383 audio_pcmbuf_sync_position();
384
385 if (freespace < size + PCMBUF_CHUNK_SIZE)
386 return NULL;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000387
Jeffrey Goode0db33082009-11-11 07:02:18 +0000388 /* Maintain the buffer level above the watermark */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000389 if (status != CHANNEL_STOPPED)
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000390 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000391 if (low_latency_mode)
392 {
393 /* 1/4s latency. */
394 if (remaining > DATA_LEVEL(1))
395 return NULL;
396 }
397
398 /* Boost CPU if necessary */
399 size_t realrem = pcmbuf_size - freespace;
400
401 if (realrem < pcmbuf_watermark)
Michael Sevakis96f02a92011-07-21 22:25:09 +0000402 trigger_cpu_boost();
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000403
404 boost_codec_thread(realrem*10 / pcmbuf_size);
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000405
Jeffrey Goode9e095342009-11-10 03:46:08 +0000406#ifdef HAVE_CROSSFADE
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000407 /* Disable crossfade if < .5s of audio */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000408 if (remaining < DATA_LEVEL(2))
409 crossfade_status = CROSSFADE_INACTIVE;
Jeffrey Goode9e095342009-11-10 03:46:08 +0000410#endif
Jeffrey Goode8edac6e2009-11-09 05:45:05 +0000411 }
Michael Sevakisa2b67032011-06-29 06:37:04 +0000412 else /* !playing */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000413 {
Jeffrey Goode0db33082009-11-11 07:02:18 +0000414 /* Boost CPU for pre-buffer */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000415 trigger_cpu_boost();
416
Jeffrey Goode0db33082009-11-11 07:02:18 +0000417 /* If pre-buffered to the watermark, start playback */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000418#if MEMORYSIZE > 2
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000419 if (remaining > DATA_LEVEL(4))
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000420#else
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000421 if (remaining > pcmbuf_watermark)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000422#endif
423 {
Michael Sevakisc537d592011-04-27 03:08:23 +0000424 if (audio_pcmbuf_may_play())
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000425 pcmbuf_play_start();
426 }
Jeffrey Goode04b01e12009-11-05 21:59:36 +0000427 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000428
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000429 void *buf =
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000430#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000431 crossfade_status != CROSSFADE_INACTIVE ? crossfade_buffer :
Jeffrey Goode9e095342009-11-10 03:46:08 +0000432#endif
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000433 get_write_buffer(&size);
434
435 *count = size / 4;
436 return buf;
Jeffrey Goode5ce8e2c2009-11-04 03:58:33 +0000437}
438
Jeffrey Goode0db33082009-11-11 07:02:18 +0000439/* Handle new samples to the buffer */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000440void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000441{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000442 size_t size = count * 4;
443
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000444#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000445 if (crossfade_status != CROSSFADE_INACTIVE)
446 {
447 write_to_crossfade(size, elapsed, offset);
448 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000449 else
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000450#endif
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000451 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000452 commit_write_buffer(size, elapsed, offset);
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000453 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000454
455 /* Revert to position updates by PCM */
456 pcmbuf_sync_position = false;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000457}
458
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000459
Jeffrey Goode0db33082009-11-11 07:02:18 +0000460/** Init */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000461static unsigned int get_next_required_pcmbuf_chunks(void)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000462{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000463 size_t size = MIN_BUFFER_SIZE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000464
Jeffrey Goode9e095342009-11-10 03:46:08 +0000465#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000466 if (crossfade_enable_request != CROSSFADE_ENABLE_OFF)
467 {
468 size_t seconds = global_settings.crossfade_fade_out_delay +
469 global_settings.crossfade_fade_out_duration;
470 size += seconds * BYTERATE;
471 }
Jeffrey Goode9e095342009-11-10 03:46:08 +0000472#endif
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000473
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000474 logf("pcmbuf len: %lu", (unsigned long)(size / BYTERATE));
475 return size / PCMBUF_CHUNK_SIZE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000476}
477
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000478/* Initialize the ringbuffer state */
479static void init_buffer_state(void)
480{
481 /* Reset counters */
482 chunk_ridx = chunk_widx = 0;
483 pcmbuf_bytes_waiting = 0;
484
485 /* Reset first descriptor */
486 struct chunkdesc *desc = pcmbuf_descriptors;
487 desc->is_end = 0;
488 desc->pos_key = 0;
489}
490
491/* Initialize the PCM buffer. The structure looks like this:
492 * ...[|FADEBUF]|---------PCMBUF---------|GUARDBUF|DESCS| */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000493size_t pcmbuf_init(unsigned char *bufend)
494{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000495 unsigned char *bufstart;
496
497 /* Set up the buffers */
498 pcmbuf_desc_count = get_next_required_pcmbuf_chunks();
499 pcmbuf_size = pcmbuf_desc_count * PCMBUF_CHUNK_SIZE;
500 pcmbuf_descriptors = (struct chunkdesc *)bufend - pcmbuf_desc_count;
501
502 pcmbuf_buffer = (void *)pcmbuf_descriptors -
503 pcmbuf_size - PCMBUF_GUARD_SIZE;
504
505 /* Mem-align buffer chunks for more efficient handling in lower layers */
506 pcmbuf_buffer = ALIGN_DOWN(pcmbuf_buffer, (uintptr_t)MEM_ALIGN_SIZE);
507
508 pcmbuf_guardbuf = pcmbuf_buffer + pcmbuf_size;
509 bufstart = pcmbuf_buffer;
Michael Sevakisa802eba2011-07-09 01:49:00 +0000510
Jeffrey Goode9e095342009-11-10 03:46:08 +0000511#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000512 /* Allocate FADEBUF if it will be needed */
513 if (crossfade_enable_request != CROSSFADE_ENABLE_OFF)
514 {
515 bufstart -= CROSSFADE_BUFSIZE;
516 crossfade_buffer = bufstart;
517 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000518
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000519 pcmbuf_finish_crossfade_enable();
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000520#else /* !HAVE_CROSSFADE */
Jeffrey Goode9e095342009-11-10 03:46:08 +0000521 pcmbuf_watermark = PCMBUF_WATERMARK;
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000522#endif /* HAVE_CROSSFADE */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000523
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000524 init_buffer_state();
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000525
Michael Sevakisa2b67032011-06-29 06:37:04 +0000526 pcmbuf_soft_mode(false);
527
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000528 return bufend - bufstart;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000529}
530
531
Jeffrey Goode0db33082009-11-11 07:02:18 +0000532/** Track change */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000533
534/* Place a track change notification in a specific descriptor or post it
535 immediately if the buffer is empty or the index is invalid */
536static void pcmbuf_monitor_track_change_ex(size_t index, int offset)
537{
538 if (chunk_ridx != chunk_widx && index != INVALID_BUF_INDEX)
539 {
540 /* If monitoring, set flag in specified chunk */
541 index_chunkdesc_offs(index, offset)->is_end = 1;
542 }
543 else
544 {
545 /* Post now if no outstanding buffers exist */
546 audio_pcmbuf_track_change(false);
547 }
548}
549
550/* Clear end of track and optionally the positioning info for all data */
551static void pcmbuf_cancel_track_change(bool position)
552{
553 size_t index = chunk_ridx;
554
555 while (1)
556 {
557 struct chunkdesc *desc = index_chunkdesc(index);
558
559 desc->is_end = 0;
560
561 if (position)
562 desc->pos_key = 0;
563
564 if (index == chunk_widx)
565 break;
566
567 index = index_next(index);
568 }
569}
570
571/* Place a track change notification at the end of the buffer or post it
572 immediately if the buffer is empty */
Michael Sevakis65109732011-02-23 14:31:13 +0000573void pcmbuf_monitor_track_change(bool monitor)
574{
575 pcm_play_lock();
576
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000577 if (monitor)
578 pcmbuf_monitor_track_change_ex(chunk_widx, -1);
Michael Sevakis65109732011-02-23 14:31:13 +0000579 else
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000580 pcmbuf_cancel_track_change(false);
Michael Sevakis65109732011-02-23 14:31:13 +0000581
582 pcm_play_unlock();
583}
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000584
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000585void pcmbuf_start_track_change(enum pcm_track_change_type type)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000586{
Jeffrey Goode9e095342009-11-10 03:46:08 +0000587#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000588 bool crossfade = false;
589#endif
590 bool auto_skip = type != TRACK_CHANGE_MANUAL;
591
592 /* Commit all outstanding data before starting next track - tracks don't
593 comingle inside a single buffer chunk */
594 commit_if_needed(COMMIT_ALL_DATA);
595
596 /* Update position key so that:
597 1) Positions are keyed to the track to which they belong for sync
598 purposes
599
600 2) Buffers stamped with the outgoing track's positions are restamped
601 to the incoming track's positions when crossfading
602 */
603 if (++position_key > UINT8_MAX)
604 position_key = 1;
605
606 if (type == TRACK_CHANGE_END_OF_DATA)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000607 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000608 /* If end of all data, force playback */
609 if (audio_pcmbuf_may_play())
610 pcmbuf_play_start();
611 }
612#ifdef HAVE_CROSSFADE
613 /* Determine whether this track change needs to crossfaded and how */
614 else if (crossfade_setting != CROSSFADE_ENABLE_OFF &&
615 !pcmbuf_is_crossfade_active() &&
616 pcmbuf_unplayed_bytes() >= DATA_LEVEL(2) &&
617 !low_latency_mode)
618 {
619 switch (crossfade_setting)
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000620 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000621 case CROSSFADE_ENABLE_AUTOSKIP:
622 crossfade = auto_skip;
623 break;
624 case CROSSFADE_ENABLE_MANSKIP:
625 crossfade = !auto_skip;
626 break;
627 case CROSSFADE_ENABLE_SHUFFLE:
628 crossfade = global_settings.playlist_shuffle;
629 break;
630 case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP:
631 crossfade = global_settings.playlist_shuffle || !auto_skip;
632 break;
633 case CROSSFADE_ENABLE_ALWAYS:
634 crossfade = true;
635 break;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000636 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000637 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000638 /* else crossfade is off, crossfade is already active, not enough data,
639 * pcm is off now (implying low data), not crossfading or low latency mode
640 */
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000641
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000642 if (crossfade)
Jeffrey Goode664dc902009-11-11 00:48:17 +0000643 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000644 logf("crossfade track change");
Michael Sevakis65109732011-02-23 14:31:13 +0000645
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000646 /* Don't enable mix mode when skipping tracks manually */
647 crossfade_mixmode = auto_skip &&
648 global_settings.crossfade_fade_out_mixmode;
Michael Sevakis65109732011-02-23 14:31:13 +0000649
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000650 crossfade_auto_skip = auto_skip;
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000651
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000652 crossfade_status = CROSSFADE_TRACK_CHANGE_STARTED;
Jeffrey Goode664dc902009-11-11 00:48:17 +0000653
Michael Sevakis65109732011-02-23 14:31:13 +0000654 trigger_cpu_boost();
Michael Sevakisc537d592011-04-27 03:08:23 +0000655
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000656 /* Cancel any pending automatic gapless transition and if a manual
657 skip, stop position updates */
658 pcm_play_lock();
659 pcmbuf_cancel_track_change(!auto_skip);
660 pcm_play_unlock();
Jeffrey Goode664dc902009-11-11 00:48:17 +0000661 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000662 else
663#endif /* HAVE_CROSSFADE */
664 if (auto_skip)
Jeffrey Goode664dc902009-11-11 00:48:17 +0000665 {
666 /* The codec is moving on to the next track, but the current track will
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000667 * continue to play, so mark the last write chunk as the last one in
668 * the track */
669 logf("gapless track change");
670#ifdef HAVE_CROSSFADE
671 if (crossfade_status != CROSSFADE_INACTIVE)
672 {
673 /* Crossfade is still active but crossfade is not happening - for
674 * now, chicken-out and clear out the buffer (just like before) to
675 * avoid fade pile-up on short tracks fading-in over long ones */
676 pcmbuf_play_stop();
677 }
678#endif
Michael Sevakis65109732011-02-23 14:31:13 +0000679 pcmbuf_monitor_track_change(true);
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000680 }
681 else
682 {
683 /* Discard old data; caller needs no transition notification */
684 logf("manual track change");
685 pcmbuf_play_stop();
Jeffrey Goode664dc902009-11-11 00:48:17 +0000686 }
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000687}
688
689
Jeffrey Goode0db33082009-11-11 07:02:18 +0000690/** Playback */
Jeffrey Goode874c9112009-11-09 18:12:20 +0000691
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000692/* PCM driver callback */
693static void pcmbuf_pcm_callback(unsigned char **start, size_t *size)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000694{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000695 /*- Process the chunk that just finished -*/
696 size_t index = chunk_ridx;
697 struct chunkdesc *desc = current_desc;
Michael Sevakis5078d462011-08-23 01:37:59 +0000698
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000699 if (desc)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000700 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000701 /* If last chunk in the track, notify of track change */
702 if (desc->is_end != 0)
Michael Sevakisc537d592011-04-27 03:08:23 +0000703 audio_pcmbuf_track_change(true);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000704
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000705 /* Free it for reuse */
706 chunk_ridx = index = index_next(index);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000707 }
Jeffrey Goode8cb4b362011-05-09 21:52:06 +0000708
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000709 /*- Process the new one -*/
710 if (index != chunk_widx && !fade_out_complete)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000711 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000712 current_desc = desc = index_chunkdesc(index);
713
714 *start = index_buffer(index);
715 *size = desc->size;
716
717 if (desc->pos_key != 0)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000718 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000719 /* Positioning chunk - notify playback */
720 audio_pcmbuf_position_callback(desc->elapsed, desc->offset,
721 desc->pos_key);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000722 }
723 }
Jeffrey Goode874c9112009-11-09 18:12:20 +0000724}
725
Jeffrey Goode0db33082009-11-11 07:02:18 +0000726/* Force playback */
Jeffrey Goode874c9112009-11-09 18:12:20 +0000727void pcmbuf_play_start(void)
728{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000729 logf("pcmbuf_play_start");
730
Michael Sevakisa2b67032011-06-29 06:37:04 +0000731 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED &&
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000732 chunk_widx != chunk_ridx)
Jeffrey Goode874c9112009-11-09 18:12:20 +0000733 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000734 current_desc = NULL;
Michael Sevakis5078d462011-08-23 01:37:59 +0000735 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback,
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000736 NULL, 0);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000737 }
738}
739
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000740/* Stop channel, empty and reset buffer */
Jeffrey Goode874c9112009-11-09 18:12:20 +0000741void pcmbuf_play_stop(void)
742{
Jeffrey Goode664dc902009-11-11 00:48:17 +0000743 logf("pcmbuf_play_stop");
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000744
745 /* Reset channel */
Michael Sevakisa2b67032011-06-29 06:37:04 +0000746 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000747
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000748 /* Reset buffer */
749 init_buffer_state();
750
751 /* Revert to position updates by PCM */
752 pcmbuf_sync_position = false;
753
Jeffrey Goode9e095342009-11-10 03:46:08 +0000754#ifdef HAVE_CROSSFADE
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000755 crossfade_status = CROSSFADE_INACTIVE;
Jeffrey Goode9e095342009-11-10 03:46:08 +0000756#endif
Jeffrey Goode874c9112009-11-09 18:12:20 +0000757
Thomas Martitz9afb55a2010-12-22 16:03:15 +0000758 /* Can unboost the codec thread here no matter who's calling,
759 * pretend full pcm buffer to unboost */
760 boost_codec_thread(10);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000761}
762
763void pcmbuf_pause(bool pause)
764{
Jeffrey Goode664dc902009-11-11 00:48:17 +0000765 logf("pcmbuf_pause: %s", pause?"pause":"play");
Michael Sevakisa2b67032011-06-29 06:37:04 +0000766
767 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED)
768 mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
Jeffrey Goode874c9112009-11-09 18:12:20 +0000769 else if (!pause)
770 pcmbuf_play_start();
771}
772
773
Jeffrey Goode0db33082009-11-11 07:02:18 +0000774/** Crossfade */
Jeffrey Goode37adbee2009-11-06 04:13:36 +0000775
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000776#ifdef HAVE_CROSSFADE
777/* Find the buffer index that's 'size' bytes away from 'index' */
778static size_t crossfade_find_index(size_t index, size_t size)
779{
780 if (index != INVALID_BUF_INDEX)
781 {
782 size_t i = ALIGN_DOWN(index, PCMBUF_CHUNK_SIZE);
783 size += index - i;
784
785 while (i != chunk_widx)
786 {
787 size_t desc_size = index_chunkdesc(i)->size;
788
789 if (size < desc_size)
790 return i + size;
791
792 size -= desc_size;
793 i = index_next(i);
794 }
795 }
796
797 return INVALID_BUF_INDEX;
798}
799
800/* Align the needed buffer area up to the end of existing data */
801static size_t crossfade_find_buftail(size_t buffer_rem, size_t buffer_need)
802{
803 crossfade_index = chunk_ridx;
804
805 if (buffer_rem > buffer_need)
806 {
807 size_t distance;
808
809 if (crossfade_auto_skip)
810 {
811 /* Automatic track changes only modify the last part of the buffer,
812 * so find the right chunk and sample to start the crossfade */
813 distance = buffer_rem - buffer_need;
814 buffer_rem = buffer_need;
815 }
816 else
817 {
818 /* Manual skips occur immediately, but give 1/5s to process */
819 distance = BYTERATE / 5;
820 buffer_rem -= BYTERATE / 5;
821 }
822
823 crossfade_index = crossfade_find_index(crossfade_index, distance);
824 }
825
826 return buffer_rem;
827}
828
Jeffrey Goodeb6f15f22009-11-08 04:27:27 +0000829/* Clip sample to signed 16 bit range */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000830static FORCE_INLINE int32_t clip_sample_16(int32_t sample)
Jeffrey Goodeb6f15f22009-11-08 04:27:27 +0000831{
832 if ((int16_t)sample != sample)
833 sample = 0x7fff ^ (sample >> 31);
834 return sample;
835}
836
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000837/* Returns the number of bytes _NOT_ mixed/faded */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000838static int crossfade_mix_fade(int factor, size_t size, void *buf, size_t *out_index,
839 unsigned long elapsed, off_t offset)
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000840{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000841 if (size == 0)
Steve Bavinb2dc7f02009-11-26 14:41:31 +0000842 return 0;
843
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000844 size_t index = *out_index;
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000845
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000846 if (index == INVALID_BUF_INDEX)
847 return size;
848
849 const int16_t *input_buf = buf;
850 int16_t *output_buf = (int16_t *)index_buffer(index);
851
852 while (size)
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000853 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000854 struct chunkdesc *desc = index_chunkdesc(index);
855
856 switch (offset)
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000857 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000858 case MIXFADE_NULLIFY_POS:
859 /* Stop position updates for the chunk */
860 desc->pos_key = 0;
861 break;
862 case MIXFADE_KEEP_POS:
863 /* Keep position info as it is */
864 break;
865 default:
866 /* Replace position info */
867 stamp_chunk(desc, elapsed, offset);
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000868 }
869
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000870 size_t rem = desc->size - (index % PCMBUF_CHUNK_SIZE);
871 int16_t *chunk_end = SKIPBYTES(output_buf, rem);
872
873 if (size < rem)
874 rem = size;
875
876 size -= rem;
877
878 do
879 {
880 /* fade left and right channel at once to keep buffer alignment */
881 int32_t left = output_buf[0];
882 int32_t right = output_buf[1];
883
884 if (input_buf)
885 {
886 /* fade the input buffer and mix into the chunk */
887 left += *input_buf++ * factor >> 8;
888 right += *input_buf++ * factor >> 8;
889 left = clip_sample_16(left);
890 right = clip_sample_16(right);
891 }
892 else
893 {
894 /* fade the chunk only */
895 left = left * factor >> 8;
896 right = right * factor >> 8;
897 }
898
899 *output_buf++ = left;
900 *output_buf++ = right;
901
902 rem -= 4;
903 }
904 while (rem);
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000905
906 /* move to next chunk as needed */
907 if (output_buf >= chunk_end)
908 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000909 index = index_next(index);
910
911 if (index == chunk_widx)
912 {
913 /* End of existing data */
914 *out_index = INVALID_BUF_INDEX;
915 return size;
916 }
917
918 output_buf = (int16_t *)index_buffer(index);
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000919 }
920 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000921
922 *out_index = buffer_index(output_buf);
Jeffrey Goode873c5b62009-11-15 04:57:40 +0000923 return 0;
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000924}
925
Jeffrey Goode0db33082009-11-11 07:02:18 +0000926/* Initializes crossfader, calculates all necessary parameters and performs
927 * fade-out with the PCM buffer. */
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000928static void crossfade_start(void)
929{
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000930 logf("crossfade_start");
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000931
932 pcm_play_lock();
Brandon Low6c0908b2006-04-23 22:54:34 +0000933
Brandon Low413da2a2006-02-07 20:38:55 +0000934 /* Initialize the crossfade buffer size to all of the buffered data that
935 * has not yet been sent to the DMA */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000936 size_t unplayed = pcmbuf_unplayed_bytes();
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000937
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000938 /* Reject crossfade if less than .5s of data */
939 if (unplayed < DATA_LEVEL(2))
Brandon Low6c0908b2006-04-23 22:54:34 +0000940 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000941 logf("crossfade rejected");
942
943 crossfade_status = CROSSFADE_INACTIVE;
944
Jeffrey Gooded1963e12009-11-13 21:58:41 +0000945 if (crossfade_auto_skip)
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000946 pcmbuf_monitor_track_change(true);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000947
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000948 pcm_play_unlock();
949 return;
950 }
951
952 /* Fading will happen */
953 crossfade_status = CROSSFADE_ACTIVE;
954
955 /* Get fade info from settings. */
956 size_t fade_out_delay = global_settings.crossfade_fade_out_delay * BYTERATE;
957 size_t fade_out_rem = global_settings.crossfade_fade_out_duration * BYTERATE;
958 size_t fade_in_delay = global_settings.crossfade_fade_in_delay * BYTERATE;
959 size_t fade_in_duration = global_settings.crossfade_fade_in_duration * BYTERATE;
960
961 if (!crossfade_auto_skip)
962 {
963 /* Forego fade-in delay on manual skip - do the best to preserve auto skip
964 relationship */
965 if (fade_out_delay > fade_in_delay)
966 fade_out_delay -= fade_in_delay;
967 else
968 fade_out_delay = 0;
969
970 fade_in_delay = 0;
971 }
972
973 size_t fade_out_need = fade_out_delay + fade_out_rem;
974
Jeffrey Goode15351e82009-11-12 04:24:41 +0000975 if (!crossfade_mixmode)
976 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +0000977 size_t buffer_rem = crossfade_find_buftail(unplayed, fade_out_need);
978
979 pcm_play_unlock();
980
981 if (buffer_rem < fade_out_need)
982 {
983 /* Existing buffers are short */
984 size_t fade_out_short = fade_out_need - buffer_rem;
985
986 if (fade_out_rem >= fade_out_short)
987 {
988 /* Truncate fade-out duration */
989 fade_out_rem -= fade_out_short;
990 }
991 else
992 {
993 /* Truncate fade-out and fade-out delay */
994 fade_out_delay = fade_out_rem;
995 fade_out_rem = 0;
996 }
997 }
998
999 /* Completely process the crossfade fade-out effect with current PCM buffer */
1000
Jeffrey Goode15351e82009-11-12 04:24:41 +00001001 /* Fade out the specified amount of the already processed audio */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001002 size_t fade_out_total = fade_out_rem;
Jeffrey Goode15351e82009-11-12 04:24:41 +00001003
1004 /* Find the right chunk and sample to start fading out */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001005 size_t fade_out_index = crossfade_find_index(crossfade_index, fade_out_delay);
Jeffrey Goode15351e82009-11-12 04:24:41 +00001006
1007 while (fade_out_rem > 0)
1008 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001009 /* Each 1/20 second of audio will have the same fade applied */
1010 size_t block_rem = MIN(BYTERATE / 20, fade_out_rem);
1011 int factor = (fade_out_rem << 8) / fade_out_total;
Jeffrey Goode15351e82009-11-12 04:24:41 +00001012
1013 fade_out_rem -= block_rem;
1014
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001015 crossfade_mix_fade(factor, block_rem, NULL, &fade_out_index,
1016 0, MIXFADE_KEEP_POS);
Jeffrey Goode15351e82009-11-12 04:24:41 +00001017 }
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001018
Jeffrey Gooded1963e12009-11-13 21:58:41 +00001019 /* zero out the rest of the buffer */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001020 crossfade_mix_fade(0, INT_MAX, NULL, &fade_out_index,
1021 0, MIXFADE_NULLIFY_POS);
1022
1023 pcm_play_lock();
Jeffrey Goode15351e82009-11-12 04:24:41 +00001024 }
1025
1026 /* Initialize fade-in counters */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001027 crossfade_fade_in_total = fade_in_duration;
1028 crossfade_fade_in_rem = fade_in_duration;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001029
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001030 /* Find the right chunk and sample to start fading in - redo from read
1031 chunk in case original position were/was overrun in callback - the
1032 track change event _must not_ ever fail to happen */
1033 unplayed = pcmbuf_unplayed_bytes() + fade_in_delay;
Brandon Low6c0908b2006-04-23 22:54:34 +00001034
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001035 crossfade_find_buftail(unplayed, fade_out_need);
1036
1037 if (crossfade_auto_skip)
1038 pcmbuf_monitor_track_change_ex(crossfade_index, 0);
1039
1040 pcm_play_unlock();
1041
Jeffrey Goode15351e82009-11-12 04:24:41 +00001042 logf("crossfade_start done!");
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001043}
1044
Jeffrey Goode0db33082009-11-11 07:02:18 +00001045/* Perform fade-in of new track */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001046static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset)
Brandon Low6c0908b2006-04-23 22:54:34 +00001047{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001048 unsigned char *buf = crossfade_buffer;
1049
1050 if (crossfade_fade_in_rem)
Brandon Low6c0908b2006-04-23 22:54:34 +00001051 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001052 /* Fade factor for this packet */
1053 int factor =
1054 ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) /
1055 crossfade_fade_in_total;
1056 /* Bytes to fade */
1057 size_t fade_rem = MIN(size, crossfade_fade_in_rem);
Brandon Low9602dd72006-04-25 12:34:28 +00001058
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001059 /* We _will_ fade this many bytes */
1060 crossfade_fade_in_rem -= fade_rem;
Brandon Low9602dd72006-04-25 12:34:28 +00001061
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001062 if (crossfade_index != INVALID_BUF_INDEX)
Brandon Low6c0908b2006-04-23 22:54:34 +00001063 {
Brandon Low9602dd72006-04-25 12:34:28 +00001064 /* Mix the data */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001065 size_t fade_total = fade_rem;
1066 fade_rem = crossfade_mix_fade(factor, fade_rem, buf, &crossfade_index,
1067 elapsed, offset);
1068 fade_total -= fade_rem;
1069 size -= fade_total;
1070 buf += fade_total;
1071
1072 if (!size)
Brandon Low08cdc432006-04-25 16:12:43 +00001073 return;
Brandon Low6c0908b2006-04-23 22:54:34 +00001074 }
Brandon Low9602dd72006-04-25 12:34:28 +00001075
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001076 /* Fade remaining samples in place */
1077 int samples = fade_rem / 4;
1078 int16_t *input_buf = (int16_t *)buf;
1079
1080 while (samples--)
Jeffrey Goode013fe352009-11-05 17:32:32 +00001081 {
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001082 int32_t left = input_buf[0];
1083 int32_t right = input_buf[1];
1084 *input_buf++ = left * factor >> 8;
1085 *input_buf++ = right * factor >> 8;
Jeffrey Goode013fe352009-11-05 17:32:32 +00001086 }
Brandon Low9aa49a42006-04-25 02:28:21 +00001087 }
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001088
1089 if (crossfade_index != INVALID_BUF_INDEX)
1090 {
1091 /* Mix the data */
1092 size_t mix_total = size;
1093
1094 /* A factor of 256 means mix only, no fading */
1095 size = crossfade_mix_fade(256, size, buf, &crossfade_index,
1096 elapsed, offset);
1097 buf += mix_total - size;
1098
1099 if (!size)
1100 return;
1101 }
1102
1103 /* Data might remain in the fade buffer yet the fade-in has run its
1104 course - finish it off as normal chunks */
1105 while (size > 0)
1106 {
1107 size_t copy_n = size;
1108 unsigned char *outbuf = get_write_buffer(&copy_n);
1109 memcpy(outbuf, buf, copy_n);
1110 commit_write_buffer(copy_n, elapsed, offset);
1111 buf += copy_n;
1112 size -= copy_n;
1113 }
1114
Jeffrey Goode0db33082009-11-11 07:02:18 +00001115 /* if no more fading-in to do, stop the crossfade */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001116#if 0
1117 /* This way (the previous way) can cause a sudden volume jump if mixable
1118 data is used up before the fade-in completes and that just sounds wrong
1119 -- jethead71 */
1120 if (!crossfade_fade_in_rem || crossfade_index == INVALID_BUF_INDEX)
1121#endif
1122 /* Let fade-in complete even if not fully overlapping the existing data */
1123 if (!crossfade_fade_in_rem)
1124 crossfade_status = CROSSFADE_INACTIVE;
Brandon Low413da2a2006-02-07 20:38:55 +00001125}
1126
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001127static void pcmbuf_finish_crossfade_enable(void)
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001128{
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001129 /* Copy the pending setting over now */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001130 crossfade_setting = crossfade_enable_request;
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001131
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001132 pcmbuf_watermark = (crossfade_setting != CROSSFADE_ENABLE_OFF && pcmbuf_size) ?
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001133 /* If crossfading, try to keep the buffer full other than 1 second */
Jeffrey Goodeba9280d2009-11-16 04:42:34 +00001134 (pcmbuf_size - BYTERATE) :
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001135 /* Otherwise, just use the default */
1136 PCMBUF_WATERMARK;
1137}
Brandon Low37faaab2006-04-20 02:30:59 +00001138
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001139bool pcmbuf_is_crossfade_active(void)
1140{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001141 return crossfade_status != CROSSFADE_INACTIVE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001142}
1143
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001144void pcmbuf_request_crossfade_enable(int setting)
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001145{
1146 /* Next setting to be used, not applied now */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001147 crossfade_enable_request = setting;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001148}
1149
1150bool pcmbuf_is_same_size(void)
1151{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001152 /* if pcmbuf_buffer is NULL, then not set up yet even once so always */
1153 bool same_size = pcmbuf_buffer ?
1154 (get_next_required_pcmbuf_chunks() == pcmbuf_desc_count) : true;
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001155
Jeffrey Goode5c69a422009-11-09 17:11:53 +00001156 /* no buffer change needed, so finish crossfade setup now */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001157 if (same_size)
1158 pcmbuf_finish_crossfade_enable();
Jeffrey Goode8cb4b362011-05-09 21:52:06 +00001159
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001160 return same_size;
1161}
Jeffrey Goode9e095342009-11-10 03:46:08 +00001162#endif /* HAVE_CROSSFADE */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001163
1164
Jeffrey Goode0db33082009-11-11 07:02:18 +00001165/** Debug menu, other metrics */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001166
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001167/* Amount of bytes left in the buffer, accounting for uncommitted bytes */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001168size_t pcmbuf_free(void)
1169{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001170 return pcmbuf_size - pcmbuf_unplayed_bytes() - pcmbuf_bytes_waiting;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001171}
1172
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001173/* Data bytes allocated for buffer */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001174size_t pcmbuf_get_bufsize(void)
1175{
1176 return pcmbuf_size;
1177}
1178
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001179/* Number of committed descriptors */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001180int pcmbuf_used_descs(void)
1181{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001182 return pcmbuf_unplayed_bytes() / PCMBUF_CHUNK_SIZE;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001183}
1184
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001185/* Total number of descriptors allocated */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001186int pcmbuf_descs(void)
1187{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001188 return pcmbuf_desc_count;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001189}
1190
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001191
Michael Sevakisa2b67032011-06-29 06:37:04 +00001192/** Fading and channel volume control */
1193
1194/* Sync the channel amplitude to all states */
1195static void pcmbuf_update_volume(void)
1196{
1197 unsigned int vol = fade_vol;
1198
1199 if (soft_mode)
1200 vol >>= 2;
1201
1202 mixer_channel_set_amplitude(PCM_MIXER_CHAN_PLAYBACK, vol);
1203}
1204
Michael Sevakis5078d462011-08-23 01:37:59 +00001205/* Tick that does the fade for the playback channel */
1206static void pcmbuf_fade_tick(void)
1207{
1208 /* ~1/3 second for full range fade */
1209 const unsigned int fade_step = MIX_AMP_UNITY / (HZ / 3);
1210
1211 if (fade_state == PCM_FADING_IN)
1212 fade_vol += MIN(fade_step, MIX_AMP_UNITY - fade_vol);
1213 else if (fade_state == PCM_FADING_OUT)
1214 fade_vol -= MIN(fade_step, fade_vol - MIX_AMP_MUTE);
1215
1216 pcmbuf_update_volume();
1217
1218 if (fade_vol == MIX_AMP_MUTE || fade_vol == MIX_AMP_UNITY)
1219 {
1220 /* Fade is complete */
1221 tick_remove_task(pcmbuf_fade_tick);
Michael Sevakis5078d462011-08-23 01:37:59 +00001222 if (fade_state == PCM_FADING_OUT)
1223 {
1224 /* Tell PCM to stop at its earliest convenience */
1225 fade_out_complete = true;
1226 }
1227
1228 fade_state = PCM_NOT_FADING;
1229 }
1230}
1231
Michael Sevakis4d903f22011-08-23 05:58:28 +00001232/* Fade channel in or out in the background */
Michael Sevakisa2b67032011-06-29 06:37:04 +00001233void pcmbuf_fade(bool fade, bool in)
1234{
Michael Sevakis4d903f22011-08-23 05:58:28 +00001235 /* Must pause any active fade */
Michael Sevakis5078d462011-08-23 01:37:59 +00001236 pcm_play_lock();
1237
1238 if (fade_state != PCM_NOT_FADING)
1239 tick_remove_task(pcmbuf_fade_tick);
1240
1241 fade_out_complete = false;
1242
1243 pcm_play_unlock();
1244
Michael Sevakisa2b67032011-06-29 06:37:04 +00001245 if (!fade)
1246 {
1247 /* Simply set the level */
Michael Sevakis5078d462011-08-23 01:37:59 +00001248 fade_state = PCM_NOT_FADING;
Michael Sevakisa2b67032011-06-29 06:37:04 +00001249 fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE;
Michael Sevakis5078d462011-08-23 01:37:59 +00001250 pcmbuf_update_volume();
Michael Sevakisa2b67032011-06-29 06:37:04 +00001251 }
1252 else
1253 {
Michael Sevakis5078d462011-08-23 01:37:59 +00001254 /* Set direction and resume fade from current point */
1255 fade_state = in ? PCM_FADING_IN : PCM_FADING_OUT;
1256 tick_add_task(pcmbuf_fade_tick);
Michael Sevakisa2b67032011-06-29 06:37:04 +00001257 }
1258}
1259
Michael Sevakis5078d462011-08-23 01:37:59 +00001260/* Return 'true' if fade is in progress */
1261bool pcmbuf_fading(void)
1262{
1263 return fade_state != PCM_NOT_FADING;
1264}
Michael Sevakisa2b67032011-06-29 06:37:04 +00001265
Michael Sevakis697aa7f2011-08-25 00:12:19 +00001266/* Quiet-down the channel if 'shhh' is true or else play at normal level */
1267void pcmbuf_soft_mode(bool shhh)
1268{
1269 /* Have to block the tick or improper order could leave volume in soft
1270 mode if fading reads the old value first but updates after us. */
1271 int res = fade_state != PCM_NOT_FADING ?
1272 tick_remove_task(pcmbuf_fade_tick) : -1;
1273
1274 soft_mode = shhh;
1275 pcmbuf_update_volume();
1276
1277 if (res == 0)
1278 tick_add_task(pcmbuf_fade_tick);
1279}
1280
1281
Jeffrey Goode0db33082009-11-11 07:02:18 +00001282/** Misc */
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001283
1284bool pcmbuf_is_lowdata(void)
1285{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001286 if (!audio_pcmbuf_may_play() || pcmbuf_is_crossfade_active())
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001287 return false;
1288
1289#if MEMORYSIZE > 2
1290 /* 1 seconds of buffer is low data */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001291 return pcmbuf_unplayed_bytes() < DATA_LEVEL(4);
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001292#else
1293 /* under watermark is low data */
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001294 return pcmbuf_unplayed_bytes() < pcmbuf_watermark;
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001295#endif
1296}
1297
1298void pcmbuf_set_low_latency(bool state)
1299{
1300 low_latency_mode = state;
1301}
1302
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001303/* Return the current position key value */
1304unsigned int pcmbuf_get_position_key(void)
Jeffrey Goode37adbee2009-11-06 04:13:36 +00001305{
Michael Sevakis7ad2cad2011-08-28 07:45:35 +00001306 return position_key;
1307}
1308
1309/* Set position updates to be synchronous and immediate in addition to during
1310 PCM frames - cancelled upon first codec insert or upon stopping */
1311void pcmbuf_sync_position_update(void)
1312{
1313 pcmbuf_sync_position = true;
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001314}