summaryrefslogtreecommitdiff
path: root/apps/pcmbuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r--apps/pcmbuf.c1415
1 files changed, 762 insertions, 653 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 8736fe2ae2..f57021d237 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2005 by Miika Pekkarinen 10 * Copyright (C) 2005 by Miika Pekkarinen
11 * Copyright (C) 2011 by Michael Sevakis
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -35,59 +36,69 @@
35#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 36#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
36#include "cpu.h" 37#include "cpu.h"
37#endif 38#endif
38#include <string.h>
39#include "settings.h" 39#include "settings.h"
40#include "audio.h" 40#include "audio.h"
41#include "voice_thread.h" 41#include "voice_thread.h"
42#include "dsp.h" 42#include "dsp.h"
43 43
44#define PCMBUF_TARGET_CHUNK 32768 /* This is the target fill size of chunks 44/* This is the target fill size of chunks on the pcm buffer
45 on the pcm buffer */ 45 Can be any number of samples but power of two sizes make for faster and
46#define PCMBUF_MINAVG_CHUNK 24576 /* This is the minimum average size of 46 smaller math - must be < 65536 bytes */
47 chunks on the pcm buffer (or we run out 47#define PCMBUF_CHUNK_SIZE 8192u
48 of buffer descriptors, which is 48#define PCMBUF_GUARD_SIZE 1024u
49 non-fatal) */ 49
50#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than 50/* Mnemonics for common data commit thresholds */
51 this to the DMA */ 51#define COMMIT_CHUNKS PCMBUF_CHUNK_SIZE
52#define CROSSFADE_BUFSIZE 8192 /* Size of the crossfade buffer */ 52#define COMMIT_ALL_DATA 1u
53 53
54/* number of bytes played per second (sample rate * 2 channels * 2 bytes/sample) */ 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) */
55#define BYTERATE (NATIVE_FREQUENCY * 4) 60#define BYTERATE (NATIVE_FREQUENCY * 4)
56 61
57#if MEMORYSIZE > 2 62#if MEMORYSIZE > 2
58/* Keep watermark high for iPods at least (2s) */ 63/* Keep watermark high for large memory target - at least (2s) */
59#define PCMBUF_WATERMARK (BYTERATE * 2) 64#define PCMBUF_WATERMARK (BYTERATE * 2)
65#define MIN_BUFFER_SIZE (BYTERATE * 3)
60#else 66#else
61#define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */ 67#define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */
68#define MIN_BUFFER_SIZE (BYTERATE * 1)
62#endif 69#endif
63 70
64/* Structure we can use to queue pcm chunks in memory to be played 71/* Describes each audio packet - keep it small since there are many of them */
65 * by the driver code. */
66struct chunkdesc 72struct chunkdesc
67{ 73{
68 unsigned char *addr; 74 uint16_t size; /* Actual size (0 < size <= PCMBUF_CHUNK_SIZE) */
69 size_t size; 75 uint8_t is_end; /* Flag indicating end of track */
70 struct chunkdesc* link; 76 uint8_t pos_key; /* Who put the position info in (0 = undefined) */
71 /* true if last chunk in the track */ 77 unsigned long elapsed; /* Elapsed time to use */
72 bool end_of_track; 78 off_t offset; /* Offset to use */
73}; 79};
74 80
75#define NUM_CHUNK_DESCS(bufsize) \ 81/* General PCM buffer data */
76 ((bufsize) / PCMBUF_MINAVG_CHUNK) 82#define INVALID_BUF_INDEX ((size_t)0 - (size_t)1)
83
84static 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;
90
91static size_t chunk_ridx;
92static size_t chunk_widx;
93
94static size_t pcmbuf_bytes_waiting;
77 95
78/* Size of the PCM buffer. */ 96static size_t pcmbuf_watermark;
79static size_t pcmbuf_size = 0; 97static struct chunkdesc *current_desc;
80static char *pcmbuf_bufend;
81static char *pcmbuffer;
82/* Current PCM buffer write index. */
83static size_t pcmbuffer_pos;
84/* Amount pcmbuffer_pos will be increased.*/
85static size_t pcmbuffer_fillpos;
86 98
87static struct chunkdesc *first_desc; 99static bool low_latency_mode = false;
88 100
89/* Gapless playback */ 101static bool pcmbuf_sync_position = false;
90static bool track_transition;
91 102
92/* Fade effect */ 103/* Fade effect */
93static unsigned int fade_vol = MIX_AMP_UNITY; 104static unsigned int fade_vol = MIX_AMP_UNITY;
@@ -104,184 +115,179 @@ static bool soft_mode = false;
104 115
105#ifdef HAVE_CROSSFADE 116#ifdef HAVE_CROSSFADE
106/* Crossfade buffer */ 117/* Crossfade buffer */
107static char *fadebuf; 118static unsigned char *crossfade_buffer;
108 119
109/* Crossfade related state */ 120/* Crossfade related state */
110static bool crossfade_enabled; 121static int crossfade_setting;
111static bool crossfade_enable_request; 122static int crossfade_enable_request;
112static bool crossfade_mixmode; 123static bool crossfade_mixmode;
113static bool crossfade_auto_skip; 124static bool crossfade_auto_skip;
114static bool crossfade_active; 125
115static bool crossfade_track_change_started; 126static enum
127{
128 CROSSFADE_INACTIVE = 0,
129 CROSSFADE_TRACK_CHANGE_STARTED,
130 CROSSFADE_ACTIVE,
131} crossfade_status = CROSSFADE_INACTIVE;
116 132
117/* Track the current location for processing crossfade */ 133/* Track the current location for processing crossfade */
118static struct chunkdesc *crossfade_chunk; 134static size_t crossfade_index;
119static size_t crossfade_sample;
120 135
121/* Counters for fading in new data */ 136/* Counters for fading in new data */
122static size_t crossfade_fade_in_total; 137static size_t crossfade_fade_in_total;
123static size_t crossfade_fade_in_rem; 138static size_t crossfade_fade_in_rem;
124#endif
125 139
126static struct chunkdesc *read_chunk; 140/* Defines for operations on position info when mixing/fading -
127static struct chunkdesc *read_end_chunk; 141 passed in offset parameter */
128static struct chunkdesc *write_chunk; 142enum
129static struct chunkdesc *write_end_chunk; 143{
130static size_t last_chunksize; 144 MIXFADE_KEEP_POS = -1, /* Keep position info in chunk */
131 145 MIXFADE_NULLIFY_POS = -2, /* Ignore position info in chunk */
132static size_t pcmbuf_unplayed_bytes; 146 /* Positive values cause stamping/restamping */
133static size_t pcmbuf_watermark; 147};
134 148
135static bool low_latency_mode = false; 149static void crossfade_start(void);
136static bool flush_pcmbuf = false; 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 */
137 154
155/* Thread */
138#ifdef HAVE_PRIORITY_SCHEDULING 156#ifdef HAVE_PRIORITY_SCHEDULING
139static int codec_thread_priority = PRIORITY_PLAYBACK; 157static int codec_thread_priority = PRIORITY_PLAYBACK;
140#endif 158#endif
141 159
142/* Helpful macros for use in conditionals this assumes some of the above 160/* Helpful macros for use in conditionals this assumes some of the above
143 * static variable names */ 161 * static variable names */
144#define COMMIT_IF_NEEDED if(pcmbuffer_fillpos > PCMBUF_TARGET_CHUNK || \ 162#define DATA_LEVEL(quarter_secs) (NATIVE_FREQUENCY * (quarter_secs))
145 (pcmbuffer_pos + pcmbuffer_fillpos) >= pcmbuf_size) commit_chunk(false)
146#define LOW_DATA(quarter_secs) \
147 (pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs)
148
149#ifdef HAVE_CROSSFADE
150static void crossfade_start(void);
151static void write_to_crossfade(size_t length);
152static void pcmbuf_finish_crossfade_enable(void);
153#endif
154 163
155/* Callbacks into playback.c */ 164/* Callbacks into playback.c */
156extern void audio_pcmbuf_position_callback(unsigned int time); 165extern void audio_pcmbuf_position_callback(unsigned long elapsed,
166 off_t offset, unsigned int key);
157extern void audio_pcmbuf_track_change(bool pcmbuf); 167extern void audio_pcmbuf_track_change(bool pcmbuf);
158extern bool audio_pcmbuf_may_play(void); 168extern bool audio_pcmbuf_may_play(void);
169extern void audio_pcmbuf_sync_position(void);
159 170
160 171
161/**************************************/ 172/**************************************/
162 173
163/* define this to show detailed chunkdesc usage information on the sim console */ 174/* Return number of commited bytes in buffer (committed chunks count as
164/*#define DESC_DEBUG*/ 175 a full chunk even if only partially filled) */
176static size_t pcmbuf_unplayed_bytes(void)
177{
178 size_t ridx = chunk_ridx;
179 size_t widx = chunk_widx;
180
181 if (ridx > widx)
182 widx += pcmbuf_size;
165 183
166#ifndef SIMULATOR 184 return widx - ridx;
167#undef DESC_DEBUG 185}
168#endif 186
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}
169 197
170#ifdef DESC_DEBUG 198/* Convert a byte offset in the PCM buffer into a pointer in the buffer */
171#define DISPLAY_DESC(caller) while(!show_desc(caller)) 199static FORCE_INLINE void * index_buffer(size_t index)
172#define DESC_IDX(desc) (desc ? desc - first_desc : -1)
173#define SHOW_DESC(desc) if(DESC_IDX(desc)==-1) DEBUGF("--"); \
174 else DEBUGF("%02d", DESC_IDX(desc))
175#define SHOW_DESC_LINK(desc) if(desc){SHOW_DESC(desc->link);DEBUGF(" ");} \
176 else DEBUGF("-- ")
177#define SHOW_DETAIL(desc) DEBUGF(":");SHOW_DESC(desc); DEBUGF(">"); \
178 SHOW_DESC_LINK(desc)
179#define SHOW_POINT(tag,desc) DEBUGF("%s",tag);SHOW_DETAIL(desc)
180#define SHOW_NUM(num,desc) DEBUGF("%02d>",num);SHOW_DESC_LINK(desc)
181
182static bool show_desc(char *caller)
183{ 200{
184 if (show_desc_in_use) return false; 201 return pcmbuf_buffer + index;
185 show_desc_in_use = true; 202}
186 DEBUGF("%-14s\t", caller); 203
187 SHOW_POINT("r", read_chunk); 204/* Convert a pointer in the buffer into an index offset */
188 SHOW_POINT("re", read_end_chunk); 205static FORCE_INLINE size_t buffer_index(void *p)
189 DEBUGF(" "); 206{
190 SHOW_POINT("w", write_chunk); 207 return (uintptr_t)p - (uintptr_t)pcmbuf_buffer;
191 SHOW_POINT("we", write_end_chunk); 208}
192 DEBUGF("\n"); 209
193 int i; 210/* Return a chunk descriptor for a byte index in the buffer */
194 for (i = 0; i < pcmbuf_descs(); i++) 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)
195 { 223 {
196 SHOW_NUM(i, (first_desc + i)); 224 i = (i + offset) % pcmbuf_desc_count;
197 if (i%10 == 9) DEBUGF("\n"); 225
226 /* remainder => modulus */
227 if (i < 0)
228 i += pcmbuf_desc_count;
198 } 229 }
199 DEBUGF("\n\n"); 230
200 show_desc_in_use = false; 231 return &pcmbuf_descriptors[i];
201 return true;
202} 232}
203#else
204#define DISPLAY_DESC(caller) do{}while(0)
205#endif
206 233
207 234
208/** Accept new PCM data */ 235/** Accept new PCM data */
209 236
210/* Commit PCM buffer samples as a new chunk for playback */ 237/* Split the uncommitted data as needed into chunks, stopping when uncommitted
211static void commit_chunk(bool flush_next_time) 238 data is below the threshold */
239static void commit_chunks(size_t threshold)
212{ 240{
213 if (!pcmbuffer_fillpos) 241 size_t index = chunk_widx;
214 return; 242 size_t end_index = index + pcmbuf_bytes_waiting;
215
216 /* Never use the last buffer descriptor */
217 while (write_chunk == write_end_chunk) {
218 /* If this happens, something is being stupid */
219 if (!pcm_is_playing()) {
220 logf("commit_chunk error");
221 pcmbuf_play_start();
222 }
223 /* Let approximately one chunk of data playback */
224 sleep(HZ * PCMBUF_TARGET_CHUNK / BYTERATE);
225 }
226 243
227 /* commit the chunk */ 244 /* 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);
228 247
229 register size_t size = pcmbuffer_fillpos; 248 struct chunkdesc *desc = index_chunkdesc(index);
230 /* Grab the next description to write, and change the write pointer */
231 register struct chunkdesc *pcmbuf_current = write_chunk;
232 write_chunk = pcmbuf_current->link;
233 /* Fill in the values in the new buffer chunk */
234 pcmbuf_current->addr = &pcmbuffer[pcmbuffer_pos];
235 pcmbuf_current->size = size;
236 pcmbuf_current->end_of_track = false;
237 pcmbuf_current->link = NULL;
238 249
239 if (read_chunk != NULL) 250 do
240 { 251 {
241 if (flush_pcmbuf) 252 size_t size = MIN(pcmbuf_bytes_waiting, PCMBUF_CHUNK_SIZE);
242 { 253 pcmbuf_bytes_waiting -= size;
243 /* Flush! Discard all data after the currently playing chunk,
244 and make the current chunk play next */
245 logf("commit_chunk: flush");
246 pcm_play_lock();
247 write_end_chunk->link = read_chunk->link;
248 read_chunk->link = pcmbuf_current;
249 while (write_end_chunk->link)
250 {
251 write_end_chunk = write_end_chunk->link;
252 pcmbuf_unplayed_bytes -= write_end_chunk->size;
253 }
254 254
255 read_chunk->end_of_track = track_transition; 255 /* Fill in the values in the new buffer chunk */
256 pcm_play_unlock(); 256 desc->size = (uint16_t)size;
257 }
258 /* If there is already a read buffer setup, add to it */
259 else
260 read_end_chunk->link = pcmbuf_current;
261 }
262 else
263 {
264 /* Otherwise create the buffer */
265 read_chunk = pcmbuf_current;
266 }
267 257
268 /* If flush_next_time is true, then the current chunk will be thrown out 258 /* Advance the current write chunk and make it available to the
269 * and the next chunk to be committed will be the next to be played. 259 PCM callback */
270 * This is used to empty the PCM buffer for a track change. */ 260 chunk_widx = index = index_next(index);
271 flush_pcmbuf = flush_next_time; 261 desc = index_chunkdesc(index);
272 262
273 /* This is now the last buffer to read */ 263 /* Reset it before using it */
274 read_end_chunk = pcmbuf_current; 264 desc->is_end = 0;
265 desc->pos_key = 0;
266 }
267 while (pcmbuf_bytes_waiting >= threshold);
268}
275 269
276 /* Update bytes counters */ 270/* If uncommitted data count is above or equal to the threshold, commit it */
277 pcmbuf_unplayed_bytes += size; 271static FORCE_INLINE void commit_if_needed(size_t threshold)
272{
273 if (pcmbuf_bytes_waiting >= threshold)
274 commit_chunks(threshold);
275}
278 276
279 pcmbuffer_pos += size; 277/* Place positioning information in the chunk */
280 if (pcmbuffer_pos >= pcmbuf_size) 278static void stamp_chunk(struct chunkdesc *desc, unsigned long elapsed,
281 pcmbuffer_pos -= pcmbuf_size; 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;
282 284
283 pcmbuffer_fillpos = 0; 285 if (desc->pos_key != key)
284 DISPLAY_DESC("commit_chunk"); 286 {
287 desc->pos_key = key;
288 desc->elapsed = elapsed;
289 desc->offset = offset;
290 }
285} 291}
286 292
287/* Set priority of the codec thread */ 293/* Set priority of the codec thread */
@@ -290,7 +296,8 @@ static void commit_chunk(bool flush_next_time)
290 * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */ 296 * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */
291static void boost_codec_thread(int pcm_fill_state) 297static void boost_codec_thread(int pcm_fill_state)
292{ 298{
293 static const int prios[11] = { 299 static const int8_t prios[11] =
300 {
294 PRIORITY_PLAYBACK_MAX, /* 0 - 10% */ 301 PRIORITY_PLAYBACK_MAX, /* 0 - 10% */
295 PRIORITY_PLAYBACK_MAX+1, /* 10 - 20% */ 302 PRIORITY_PLAYBACK_MAX+1, /* 10 - 20% */
296 PRIORITY_PLAYBACK_MAX+3, /* 20 - 30% */ 303 PRIORITY_PLAYBACK_MAX+3, /* 20 - 30% */
@@ -298,12 +305,13 @@ static void boost_codec_thread(int pcm_fill_state)
298 PRIORITY_PLAYBACK_MAX+7, /* 40 - 50% */ 305 PRIORITY_PLAYBACK_MAX+7, /* 40 - 50% */
299 PRIORITY_PLAYBACK_MAX+8, /* 50 - 60% */ 306 PRIORITY_PLAYBACK_MAX+8, /* 50 - 60% */
300 PRIORITY_PLAYBACK_MAX+9, /* 60 - 70% */ 307 PRIORITY_PLAYBACK_MAX+9, /* 60 - 70% */
301 /* raiseing priority above 70% shouldn't be needed */ 308 /* raising priority above 70% shouldn't be needed */
302 PRIORITY_PLAYBACK, /* 70 - 80% */ 309 PRIORITY_PLAYBACK, /* 70 - 80% */
303 PRIORITY_PLAYBACK, /* 80 - 90% */ 310 PRIORITY_PLAYBACK, /* 80 - 90% */
304 PRIORITY_PLAYBACK, /* 90 -100% */ 311 PRIORITY_PLAYBACK, /* 90 -100% */
305 PRIORITY_PLAYBACK, /* 100% */ 312 PRIORITY_PLAYBACK, /* 100% */
306 }; 313 };
314
307 int new_prio = prios[pcm_fill_state]; 315 int new_prio = prios[pcm_fill_state];
308 316
309 /* Keep voice and codec threads at the same priority or else voice 317 /* Keep voice and codec threads at the same priority or else voice
@@ -319,37 +327,86 @@ static void boost_codec_thread(int pcm_fill_state)
319#define boost_codec_thread(pcm_fill_state) do{}while(0) 327#define boost_codec_thread(pcm_fill_state) do{}while(0)
320#endif /* HAVE_PRIORITY_SCHEDULING */ 328#endif /* HAVE_PRIORITY_SCHEDULING */
321 329
322/* Return true if the PCM buffer is able to receive new data. 330/* Get the next available buffer and size - assumes adequate space exists */
323 * Also maintain buffer level above the watermark. */ 331static void * get_write_buffer(size_t *size)
324static bool prepare_insert(size_t length)
325{ 332{
326 bool playing = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED; 333 /* Obtain current chunk fill address */
334 size_t index = chunk_widx + pcmbuf_bytes_waiting;
335 size_t index_end = pcmbuf_size + PCMBUF_GUARD_SIZE;
327 336
328 if (low_latency_mode) 337 /* Get count to the end of the buffer where a wrap will happen +
329 { 338 the guard */
330 /* 1/4s latency. */ 339 size_t endsize = index_end - index;
331 if (!LOW_DATA(1) && playing)
332 return false;
333 }
334 340
335 /* Need to save PCMBUF_MIN_CHUNK to prevent wrapping overwriting */ 341 /* Return available unwrapped space */
336 if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK) 342 *size = MIN(*size, endsize);
337 return false; 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;
338 387
339 /* Maintain the buffer level above the watermark */ 388 /* Maintain the buffer level above the watermark */
340 if (playing) 389 if (status != CHANNEL_STOPPED)
341 { 390 {
342 /* boost cpu if necessary */ 391 if (low_latency_mode)
343 if (pcmbuf_unplayed_bytes < pcmbuf_watermark) 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)
344 trigger_cpu_boost(); 402 trigger_cpu_boost();
345 boost_codec_thread(pcmbuf_unplayed_bytes*10/pcmbuf_size); 403
404 boost_codec_thread(realrem*10 / pcmbuf_size);
346 405
347#ifdef HAVE_CROSSFADE 406#ifdef HAVE_CROSSFADE
348 /* Disable crossfade if < .5s of audio */ 407 /* Disable crossfade if < .5s of audio */
349 if (LOW_DATA(2)) 408 if (remaining < DATA_LEVEL(2))
350 { 409 crossfade_status = CROSSFADE_INACTIVE;
351 crossfade_active = false;
352 }
353#endif 410#endif
354 } 411 }
355 else /* !playing */ 412 else /* !playing */
@@ -359,378 +416,344 @@ static bool prepare_insert(size_t length)
359 416
360 /* If pre-buffered to the watermark, start playback */ 417 /* If pre-buffered to the watermark, start playback */
361#if MEMORYSIZE > 2 418#if MEMORYSIZE > 2
362 if (!LOW_DATA(4)) 419 if (remaining > DATA_LEVEL(4))
363#else 420#else
364 if (pcmbuf_unplayed_bytes > pcmbuf_watermark) 421 if (remaining > pcmbuf_watermark)
365#endif 422#endif
366 { 423 {
367 logf("pcm starting");
368 if (audio_pcmbuf_may_play()) 424 if (audio_pcmbuf_may_play())
369 pcmbuf_play_start(); 425 pcmbuf_play_start();
370 } 426 }
371 } 427 }
372 428
373 return true; 429 void *buf =
374}
375
376/* Request space in the buffer for writing output samples */
377void *pcmbuf_request_buffer(int *count)
378{
379#ifdef HAVE_CROSSFADE 430#ifdef HAVE_CROSSFADE
380 /* we're going to crossfade to a new track, which is now on its way */ 431 crossfade_status != CROSSFADE_INACTIVE ? crossfade_buffer :
381 if (crossfade_track_change_started)
382 crossfade_start();
383
384 /* crossfade has begun, put the new track samples in fadebuf */
385 if (crossfade_active)
386 {
387 int cnt = MIN(*count, CROSSFADE_BUFSIZE/4);
388 if (prepare_insert(cnt << 2))
389 {
390 *count = cnt;
391 return fadebuf;
392 }
393 }
394 else
395#endif 432#endif
396 /* if possible, reserve room in the PCM buffer for new samples */ 433 get_write_buffer(&size);
397 { 434
398 if(prepare_insert(*count << 2)) 435 *count = size / 4;
399 { 436 return buf;
400 size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos;
401 if (pcmbuf_size - pcmbuffer_index >= PCMBUF_MIN_CHUNK)
402 {
403 /* Usual case, there's space here */
404 return &pcmbuffer[pcmbuffer_index];
405 }
406 else
407 {
408 /* Wrap the buffer, the new samples go at the beginning */
409 commit_chunk(false);
410 pcmbuffer_pos = 0;
411 return &pcmbuffer[0];
412 }
413 }
414 }
415 /* PCM buffer not ready to receive new data yet */
416 return NULL;
417} 437}
418 438
419/* Handle new samples to the buffer */ 439/* Handle new samples to the buffer */
420void pcmbuf_write_complete(int count) 440void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset)
421{ 441{
422 size_t length = (size_t)(unsigned int)count << 2; 442 size_t size = count * 4;
443
423#ifdef HAVE_CROSSFADE 444#ifdef HAVE_CROSSFADE
424 if (crossfade_active) 445 if (crossfade_status != CROSSFADE_INACTIVE)
425 write_to_crossfade(length); 446 {
447 write_to_crossfade(size, elapsed, offset);
448 }
426 else 449 else
427#endif 450#endif
428 { 451 {
429 pcmbuffer_fillpos += length; 452 commit_write_buffer(size, elapsed, offset);
430 COMMIT_IF_NEEDED;
431 } 453 }
454
455 /* Revert to position updates by PCM */
456 pcmbuf_sync_position = false;
432} 457}
433 458
434 459
435/** Init */ 460/** Init */
436 461static unsigned int get_next_required_pcmbuf_chunks(void)
437static inline void init_pcmbuffers(void)
438{
439 first_desc = write_chunk;
440 struct chunkdesc *next = write_chunk;
441 next++;
442 write_end_chunk = write_chunk;
443 while ((void *)next < (void *)pcmbuf_bufend) {
444 write_end_chunk->link=next;
445 write_end_chunk=next;
446 next++;
447 }
448 DISPLAY_DESC("init");
449}
450
451static size_t get_next_required_pcmbuf_size(void)
452{ 462{
453 size_t seconds = 1; 463 size_t size = MIN_BUFFER_SIZE;
454 464
455#ifdef HAVE_CROSSFADE 465#ifdef HAVE_CROSSFADE
456 if (crossfade_enable_request) 466 if (crossfade_enable_request != CROSSFADE_ENABLE_OFF)
457 seconds += global_settings.crossfade_fade_out_delay + 467 {
458 global_settings.crossfade_fade_out_duration; 468 size_t seconds = global_settings.crossfade_fade_out_delay +
469 global_settings.crossfade_fade_out_duration;
470 size += seconds * BYTERATE;
471 }
459#endif 472#endif
460 473
461#if MEMORYSIZE > 2 474 logf("pcmbuf len: %lu", (unsigned long)(size / BYTERATE));
462 /* Buffer has to be at least 2s long. */ 475 return size / PCMBUF_CHUNK_SIZE;
463 seconds += 2; 476}
464#endif 477
465 logf("pcmbuf len: %ld", (long)seconds); 478/* Initialize the ringbuffer state */
466 return seconds * BYTERATE; 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;
467} 489}
468 490
469/* Initialize the pcmbuffer the structure looks like this: 491/* Initialize the PCM buffer. The structure looks like this:
470 * ...|---------PCMBUF---------[|FADEBUF]|DESCS|... */ 492 * ...[|FADEBUF]|---------PCMBUF---------|GUARDBUF|DESCS| */
471size_t pcmbuf_init(unsigned char *bufend) 493size_t pcmbuf_init(unsigned char *bufend)
472{ 494{
473 pcmbuf_bufend = bufend; 495 unsigned char *bufstart;
474 pcmbuf_size = get_next_required_pcmbuf_size();
475 write_chunk = (struct chunkdesc *)pcmbuf_bufend -
476 NUM_CHUNK_DESCS(pcmbuf_size);
477 496
478#ifdef HAVE_CROSSFADE 497 /* Set up the buffers */
479 fadebuf = (unsigned char *)write_chunk - 498 pcmbuf_desc_count = get_next_required_pcmbuf_chunks();
480 (crossfade_enable_request ? CROSSFADE_BUFSIZE : 0); 499 pcmbuf_size = pcmbuf_desc_count * PCMBUF_CHUNK_SIZE;
481 pcmbuffer = fadebuf - pcmbuf_size; 500 pcmbuf_descriptors = (struct chunkdesc *)bufend - pcmbuf_desc_count;
482#else 501
483 pcmbuffer = (unsigned char *)write_chunk - pcmbuf_size; 502 pcmbuf_buffer = (void *)pcmbuf_descriptors -
484#endif 503 pcmbuf_size - PCMBUF_GUARD_SIZE;
485 504
486 init_pcmbuffers(); 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;
487 510
488#ifdef HAVE_CROSSFADE 511#ifdef HAVE_CROSSFADE
512 /* 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 }
518
489 pcmbuf_finish_crossfade_enable(); 519 pcmbuf_finish_crossfade_enable();
490#else 520#else /* !HAVE_CROSSFADE */
491 pcmbuf_watermark = PCMBUF_WATERMARK; 521 pcmbuf_watermark = PCMBUF_WATERMARK;
492#endif 522#endif /* HAVE_CROSSFADE */
493 523
494 pcmbuf_play_stop(); 524 init_buffer_state();
495 525
496 pcmbuf_soft_mode(false); 526 pcmbuf_soft_mode(false);
497 527
498 return pcmbuf_bufend - pcmbuffer; 528 return bufend - bufstart;
499} 529}
500 530
501 531
502/** Track change */ 532/** Track change */
503void pcmbuf_monitor_track_change(bool monitor)
504{
505 pcm_play_lock();
506 533
507 if (last_chunksize != 0) 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)
508 { 539 {
509 /* If monitoring, wait until this track runs out. Place in 540 /* If monitoring, set flag in specified chunk */
510 currently playing chunk. If not, cancel notification. */ 541 index_chunkdesc_offs(index, offset)->is_end = 1;
511 track_transition = monitor;
512 read_end_chunk->end_of_track = monitor;
513 if (!monitor)
514 {
515 /* Clear all notifications */
516 struct chunkdesc *desc = first_desc;
517 struct chunkdesc *end = desc + pcmbuf_descs();
518 while (desc < end)
519 desc++->end_of_track = false;
520 }
521 } 542 }
522 else 543 else
523 { 544 {
524 /* Post now if PCM stopped and last buffer was sent. */ 545 /* Post now if no outstanding buffers exist */
525 track_transition = false; 546 audio_pcmbuf_track_change(false);
526 if (monitor)
527 audio_pcmbuf_track_change(false);
528 } 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 */
573void pcmbuf_monitor_track_change(bool monitor)
574{
575 pcm_play_lock();
576
577 if (monitor)
578 pcmbuf_monitor_track_change_ex(chunk_widx, -1);
579 else
580 pcmbuf_cancel_track_change(false);
529 581
530 pcm_play_unlock(); 582 pcm_play_unlock();
531} 583}
532 584
533bool pcmbuf_start_track_change(bool auto_skip) 585void pcmbuf_start_track_change(enum pcm_track_change_type type)
534{ 586{
535 bool crossfade = false;
536#ifdef HAVE_CROSSFADE 587#ifdef HAVE_CROSSFADE
537 /* Determine whether this track change needs to crossfade */ 588 bool crossfade = false;
538 if(crossfade_enabled && !pcmbuf_is_crossfade_active())
539 {
540 switch(global_settings.crossfade)
541 {
542 case CROSSFADE_ENABLE_AUTOSKIP:
543 crossfade = auto_skip;
544 break;
545 case CROSSFADE_ENABLE_MANSKIP:
546 crossfade = !auto_skip;
547 break;
548 case CROSSFADE_ENABLE_SHUFFLE:
549 crossfade = global_settings.playlist_shuffle;
550 break;
551 case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP:
552 crossfade = global_settings.playlist_shuffle || !auto_skip;
553 break;
554 case CROSSFADE_ENABLE_ALWAYS:
555 crossfade = true;
556 break;
557 }
558 }
559#endif 589#endif
590 bool auto_skip = type != TRACK_CHANGE_MANUAL;
560 591
561 if (!auto_skip || crossfade) 592 /* Commit all outstanding data before starting next track - tracks don't
562 /* manual skip or crossfade */ 593 comingle inside a single buffer chunk */
563 { 594 commit_if_needed(COMMIT_ALL_DATA);
564 if (crossfade)
565 { logf(" crossfade track change"); }
566 else
567 { logf(" manual track change"); }
568 595
569 pcm_play_lock(); 596 /* Update position key so that:
597 1) Positions are keyed to the track to which they belong for sync
598 purposes
570 599
571 /* Cancel any pending automatic gapless transition */ 600 2) Buffers stamped with the outgoing track's positions are restamped
572 pcmbuf_monitor_track_change(false); 601 to the incoming track's positions when crossfading
602 */
603 if (++position_key > UINT8_MAX)
604 position_key = 1;
573 605
574 /* Can't do two crossfades at once and, no fade if pcm is off now */ 606 if (type == TRACK_CHANGE_END_OF_DATA)
575 if ( 607 {
608 /* If end of all data, force playback */
609 if (audio_pcmbuf_may_play())
610 pcmbuf_play_start();
611 }
576#ifdef HAVE_CROSSFADE 612#ifdef HAVE_CROSSFADE
577 pcmbuf_is_crossfade_active() || 613 /* Determine whether this track change needs to crossfaded and how */
578#endif 614 else if (crossfade_setting != CROSSFADE_ENABLE_OFF &&
579 mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED) 615 !pcmbuf_is_crossfade_active() &&
616 pcmbuf_unplayed_bytes() >= DATA_LEVEL(2) &&
617 !low_latency_mode)
618 {
619 switch (crossfade_setting)
580 { 620 {
581 pcmbuf_play_stop(); 621 case CROSSFADE_ENABLE_AUTOSKIP:
582 pcm_play_unlock(); 622 crossfade = auto_skip;
583 /* Notify playback that the track change starts now */ 623 break;
584 return true; 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;
585 } 636 }
637 }
638 /* 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 */
586 641
587 /* Not enough data, or not crossfading, flush the old data instead */ 642 if (crossfade)
588 if (LOW_DATA(2) || !crossfade || low_latency_mode) 643 {
589 { 644 logf("crossfade track change");
590 commit_chunk(true);
591 }
592#ifdef HAVE_CROSSFADE
593 else
594 {
595 /* Don't enable mix mode when skipping tracks manually. */
596 crossfade_mixmode = auto_skip &&
597 global_settings.crossfade_fade_out_mixmode;
598 645
599 crossfade_auto_skip = auto_skip; 646 /* Don't enable mix mode when skipping tracks manually */
647 crossfade_mixmode = auto_skip &&
648 global_settings.crossfade_fade_out_mixmode;
600 649
601 crossfade_track_change_started = crossfade; 650 crossfade_auto_skip = auto_skip;
602 } 651
603#endif 652 crossfade_status = CROSSFADE_TRACK_CHANGE_STARTED;
604 pcm_play_unlock();
605 653
606 /* Keep trigger outside the play lock or HW FIFO underruns can happen
607 since frequency scaling is *not* always fast */
608 trigger_cpu_boost(); 654 trigger_cpu_boost();
609 655
610 /* Notify playback that the track change starts now */ 656 /* Cancel any pending automatic gapless transition and if a manual
611 return true; 657 skip, stop position updates */
658 pcm_play_lock();
659 pcmbuf_cancel_track_change(!auto_skip);
660 pcm_play_unlock();
612 } 661 }
613 else /* automatic and not crossfading, so do gapless track change */ 662 else
663#endif /* HAVE_CROSSFADE */
664 if (auto_skip)
614 { 665 {
615 /* The codec is moving on to the next track, but the current track will 666 /* The codec is moving on to the next track, but the current track will
616 * continue to play. Set a flag to make sure the elapsed time of the 667 * continue to play, so mark the last write chunk as the last one in
617 * current track will be updated properly, and mark the current chunk 668 * the track */
618 * as the last one in the track. */ 669 logf("gapless track change");
619 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
620 pcmbuf_monitor_track_change(true); 679 pcmbuf_monitor_track_change(true);
621 return false; 680 }
681 else
682 {
683 /* Discard old data; caller needs no transition notification */
684 logf("manual track change");
685 pcmbuf_play_stop();
622 } 686 }
623} 687}
624 688
625 689
626/** Playback */ 690/** Playback */
627 691
628/* PCM driver callback 692/* PCM driver callback */
629 * This function has 3 major logical parts (separated by brackets both for 693static void pcmbuf_pcm_callback(unsigned char **start, size_t *size)
630 * readability and variable scoping). The first part performs the
631 * operations related to finishing off the last chunk we fed to the DMA.
632 * The second part detects the end of playlist condition when the PCM
633 * buffer is empty except for uncommitted samples. Then they are committed
634 * and sent to the PCM driver for playback. The third part performs the
635 * operations involved in sending a new chunk to the DMA. */
636static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
637{ 694{
638 struct chunkdesc *pcmbuf_current = read_chunk; 695 /*- Process the chunk that just finished -*/
696 size_t index = chunk_ridx;
697 struct chunkdesc *desc = current_desc;
639 698
699 if (desc)
640 { 700 {
641 /* Take the finished chunk out of circulation */ 701 /* If last chunk in the track, notify of track change */
642 read_chunk = pcmbuf_current->link; 702 if (desc->is_end != 0)
643
644 /* if during a track transition, update the elapsed time in ms */
645 if (track_transition)
646 audio_pcmbuf_position_callback(last_chunksize * 1000 / BYTERATE);
647
648 /* if last chunk in the track, stop updates and notify audio thread */
649 if (pcmbuf_current->end_of_track)
650 {
651 track_transition = false;
652 audio_pcmbuf_track_change(true); 703 audio_pcmbuf_track_change(true);
653 }
654
655 /* Put the finished chunk back into circulation */
656 write_end_chunk->link = pcmbuf_current;
657 write_end_chunk = pcmbuf_current;
658 704
659#ifdef HAVE_CROSSFADE 705 /* Free it for reuse */
660 /* If we've read over the crossfade chunk while it's still fading */ 706 chunk_ridx = index = index_next(index);
661 if (pcmbuf_current == crossfade_chunk)
662 crossfade_chunk = read_chunk;
663#endif
664 } 707 }
665 708
709 /*- Process the new one -*/
710 if (index != chunk_widx && !fade_out_complete)
666 { 711 {
667 /* Commit last samples at end of playlist */ 712 current_desc = desc = index_chunkdesc(index);
668 if (pcmbuffer_fillpos && !pcmbuf_current)
669 {
670 logf("pcmbuf_pcm_callback: commit last samples");
671 commit_chunk(false);
672 }
673 }
674 713
675 /* Stop at this frame */ 714 *start = index_buffer(index);
676 pcmbuf_current = fade_out_complete ? NULL : read_chunk; 715 *size = desc->size;
677 716
678 { 717 if (desc->pos_key != 0)
679 /* Send the new chunk to the DMA */
680 if(pcmbuf_current)
681 {
682 last_chunksize = pcmbuf_current->size;
683 pcmbuf_unplayed_bytes -= last_chunksize;
684 *size = last_chunksize;
685 *start = pcmbuf_current->addr;
686 }
687 else
688 { 718 {
689 /* No more chunks or pause indicated */ 719 /* Positioning chunk - notify playback */
690 logf("pcmbuf_pcm_callback: no more chunks"); 720 audio_pcmbuf_position_callback(desc->elapsed, desc->offset,
691 last_chunksize = 0; 721 desc->pos_key);
692 *size = 0;
693 *start = NULL;
694 } 722 }
695 } 723 }
696 DISPLAY_DESC("callback");
697} 724}
698 725
699/* Force playback */ 726/* Force playback */
700void pcmbuf_play_start(void) 727void pcmbuf_play_start(void)
701{ 728{
729 logf("pcmbuf_play_start");
730
702 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED && 731 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED &&
703 pcmbuf_unplayed_bytes && read_chunk != NULL) 732 chunk_widx != chunk_ridx)
704 { 733 {
705 logf("pcmbuf_play_start"); 734 current_desc = NULL;
706 last_chunksize = read_chunk->size;
707 pcmbuf_unplayed_bytes -= last_chunksize;
708 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback, 735 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback,
709 read_chunk->addr, last_chunksize); 736 NULL, 0);
710 } 737 }
711} 738}
712 739
740/* Stop channel, empty and reset buffer */
713void pcmbuf_play_stop(void) 741void pcmbuf_play_stop(void)
714{ 742{
715 logf("pcmbuf_play_stop"); 743 logf("pcmbuf_play_stop");
744
745 /* Reset channel */
716 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); 746 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
717 747
718 pcmbuf_unplayed_bytes = 0; 748 /* Reset buffer */
719 if (read_chunk) { 749 init_buffer_state();
720 write_end_chunk->link = read_chunk; 750
721 write_end_chunk = read_end_chunk; 751 /* Revert to position updates by PCM */
722 read_chunk = read_end_chunk = NULL; 752 pcmbuf_sync_position = false;
723 } 753
724 last_chunksize = 0;
725 pcmbuffer_pos = 0;
726 pcmbuffer_fillpos = 0;
727#ifdef HAVE_CROSSFADE 754#ifdef HAVE_CROSSFADE
728 crossfade_track_change_started = false; 755 crossfade_status = CROSSFADE_INACTIVE;
729 crossfade_active = false;
730#endif 756#endif
731 track_transition = false;
732 flush_pcmbuf = false;
733 DISPLAY_DESC("play_stop");
734 757
735 /* Can unboost the codec thread here no matter who's calling, 758 /* Can unboost the codec thread here no matter who's calling,
736 * pretend full pcm buffer to unboost */ 759 * pretend full pcm buffer to unboost */
@@ -750,74 +773,153 @@ void pcmbuf_pause(bool pause)
750 773
751/** Crossfade */ 774/** Crossfade */
752 775
753/* Clip sample to signed 16 bit range */ 776#ifdef HAVE_CROSSFADE
754static inline int32_t clip_sample_16(int32_t sample) 777/* Find the buffer index that's 'size' bytes away from 'index' */
778static size_t crossfade_find_index(size_t index, size_t size)
755{ 779{
756 if ((int16_t)sample != sample) 780 if (index != INVALID_BUF_INDEX)
757 sample = 0x7fff ^ (sample >> 31); 781 {
758 return sample; 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;
759} 798}
760 799
761#ifdef HAVE_CROSSFADE 800/* Align the needed buffer area up to the end of existing data */
762/* Find the chunk that's (length) deep in the list. Return the position within 801static size_t crossfade_find_buftail(size_t buffer_rem, size_t buffer_need)
763 * the chunk, and leave the chunkdesc pointer pointing to the chunk. */
764static size_t find_chunk(size_t length, struct chunkdesc **chunk)
765{ 802{
766 while (*chunk && length >= (*chunk)->size) 803 crossfade_index = chunk_ridx;
804
805 if (buffer_rem > buffer_need)
767 { 806 {
768 length -= (*chunk)->size; 807 size_t distance;
769 *chunk = (*chunk)->link; 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);
770 } 824 }
771 return length; 825
826 return buffer_rem;
827}
828
829/* Clip sample to signed 16 bit range */
830static FORCE_INLINE int32_t clip_sample_16(int32_t sample)
831{
832 if ((int16_t)sample != sample)
833 sample = 0x7fff ^ (sample >> 31);
834 return sample;
772} 835}
773 836
774/* Returns the number of bytes _NOT_ mixed/faded */ 837/* Returns the number of bytes _NOT_ mixed/faded */
775static size_t crossfade_mix_fade(int factor, size_t length, const char *buf, 838static int crossfade_mix_fade(int factor, size_t size, void *buf, size_t *out_index,
776 size_t *out_sample, struct chunkdesc **out_chunk) 839 unsigned long elapsed, off_t offset)
777{ 840{
778 if (length == 0) 841 if (size == 0)
779 return 0; 842 return 0;
780 843
781 const int16_t *input_buf = (const int16_t *)buf; 844 size_t index = *out_index;
782 int16_t *output_buf = (int16_t *)((*out_chunk)->addr); 845
783 int16_t *chunk_end = SKIPBYTES(output_buf, (*out_chunk)->size); 846 if (index == INVALID_BUF_INDEX)
784 output_buf = &output_buf[*out_sample]; 847 return size;
785 int32_t sample; 848
849 const int16_t *input_buf = buf;
850 int16_t *output_buf = (int16_t *)index_buffer(index);
786 851
787 while (length) 852 while (size)
788 { 853 {
789 /* fade left and right channel at once to keep buffer alignment */ 854 struct chunkdesc *desc = index_chunkdesc(index);
790 int i; 855
791 for (i = 0; i < 2; i++) 856 switch (offset)
792 { 857 {
858 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);
868 }
869
870 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
793 if (input_buf) 884 if (input_buf)
794 /* fade the input buffer and mix into the chunk */
795 { 885 {
796 sample = *input_buf++; 886 /* fade the input buffer and mix into the chunk */
797 sample = ((sample * factor) >> 8) + *output_buf; 887 left += *input_buf++ * factor >> 8;
798 *output_buf++ = clip_sample_16(sample); 888 right += *input_buf++ * factor >> 8;
889 left = clip_sample_16(left);
890 right = clip_sample_16(right);
799 } 891 }
800 else 892 else
801 /* fade the chunk only */
802 { 893 {
803 sample = *output_buf; 894 /* fade the chunk only */
804 *output_buf++ = (sample * factor) >> 8; 895 left = left * factor >> 8;
896 right = right * factor >> 8;
805 } 897 }
806 }
807 898
808 length -= 4; /* 2 samples, each 16 bit -> 4 bytes */ 899 *output_buf++ = left;
900 *output_buf++ = right;
901
902 rem -= 4;
903 }
904 while (rem);
809 905
810 /* move to next chunk as needed */ 906 /* move to next chunk as needed */
811 if (output_buf >= chunk_end) 907 if (output_buf >= chunk_end)
812 { 908 {
813 *out_chunk = (*out_chunk)->link; 909 index = index_next(index);
814 if (!(*out_chunk)) 910
815 return length; 911 if (index == chunk_widx)
816 output_buf = (int16_t *)((*out_chunk)->addr); 912 {
817 chunk_end = SKIPBYTES(output_buf, (*out_chunk)->size); 913 /* End of existing data */
914 *out_index = INVALID_BUF_INDEX;
915 return size;
916 }
917
918 output_buf = (int16_t *)index_buffer(index);
818 } 919 }
819 } 920 }
820 *out_sample = output_buf - (int16_t *)((*out_chunk)->addr); 921
922 *out_index = buffer_index(output_buf);
821 return 0; 923 return 0;
822} 924}
823 925
@@ -825,184 +927,209 @@ static size_t crossfade_mix_fade(int factor, size_t length, const char *buf,
825 * fade-out with the PCM buffer. */ 927 * fade-out with the PCM buffer. */
826static void crossfade_start(void) 928static void crossfade_start(void)
827{ 929{
828 size_t crossfade_rem;
829 size_t crossfade_need;
830 size_t fade_out_rem;
831 size_t fade_out_delay;
832 size_t fade_in_delay;
833
834 crossfade_track_change_started = false;
835 /* Reject crossfade if less than .5s of data */
836 if (LOW_DATA(2)) {
837 logf("crossfade rejected");
838 pcmbuf_play_stop();
839 return ;
840 }
841
842 logf("crossfade_start"); 930 logf("crossfade_start");
843 commit_chunk(false); 931
844 crossfade_active = true; 932 pcm_play_lock();
845 933
846 /* Initialize the crossfade buffer size to all of the buffered data that 934 /* Initialize the crossfade buffer size to all of the buffered data that
847 * has not yet been sent to the DMA */ 935 * has not yet been sent to the DMA */
848 crossfade_rem = pcmbuf_unplayed_bytes; 936 size_t unplayed = pcmbuf_unplayed_bytes();
849 crossfade_chunk = read_chunk->link;
850 crossfade_sample = 0;
851
852 /* Get fade out info from settings. */
853 fade_out_delay = global_settings.crossfade_fade_out_delay * BYTERATE;
854 fade_out_rem = global_settings.crossfade_fade_out_duration * BYTERATE;
855 937
856 crossfade_need = fade_out_delay + fade_out_rem; 938 /* Reject crossfade if less than .5s of data */
857 if (crossfade_rem > crossfade_need) 939 if (unplayed < DATA_LEVEL(2))
858 { 940 {
941 logf("crossfade rejected");
942
943 crossfade_status = CROSSFADE_INACTIVE;
944
859 if (crossfade_auto_skip) 945 if (crossfade_auto_skip)
860 /* Automatic track changes only modify the last part of the buffer, 946 pcmbuf_monitor_track_change(true);
861 * so find the right chunk and sample to start the crossfade */ 947
862 { 948 pcm_play_unlock();
863 crossfade_sample = find_chunk(crossfade_rem - crossfade_need, 949 return;
864 &crossfade_chunk) / 2;
865 crossfade_rem = crossfade_need;
866 }
867 else
868 /* Manual skips occur immediately, but give time to process */
869 {
870 crossfade_rem -= crossfade_chunk->size;
871 crossfade_chunk = crossfade_chunk->link;
872 }
873 } 950 }
874 /* Truncate fade out duration if necessary. */ 951
875 if (crossfade_rem < crossfade_need) 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)
876 { 962 {
877 size_t crossfade_short = crossfade_need - crossfade_rem; 963 /* Forego fade-in delay on manual skip - do the best to preserve auto skip
878 if (fade_out_rem >= crossfade_short) 964 relationship */
879 fade_out_rem -= crossfade_short; 965 if (fade_out_delay > fade_in_delay)
966 fade_out_delay -= fade_in_delay;
880 else 967 else
881 { 968 fade_out_delay = 0;
882 fade_out_delay -= crossfade_short - fade_out_rem; 969
883 fade_out_rem = 0; 970 fade_in_delay = 0;
884 }
885 } 971 }
886 crossfade_rem -= fade_out_delay + fade_out_rem;
887 972
888 /* Completely process the crossfade fade-out effect with current PCM buffer */ 973 size_t fade_out_need = fade_out_delay + fade_out_rem;
974
889 if (!crossfade_mixmode) 975 if (!crossfade_mixmode)
890 { 976 {
977 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
891 /* Fade out the specified amount of the already processed audio */ 1001 /* Fade out the specified amount of the already processed audio */
892 size_t total_fade_out = fade_out_rem; 1002 size_t fade_out_total = fade_out_rem;
893 size_t fade_out_sample;
894 struct chunkdesc *fade_out_chunk = crossfade_chunk;
895 1003
896 /* Find the right chunk and sample to start fading out */ 1004 /* Find the right chunk and sample to start fading out */
897 fade_out_delay += crossfade_sample * 2; 1005 size_t fade_out_index = crossfade_find_index(crossfade_index, fade_out_delay);
898 fade_out_sample = find_chunk(fade_out_delay, &fade_out_chunk) / 2;
899 1006
900 while (fade_out_rem > 0) 1007 while (fade_out_rem > 0)
901 { 1008 {
902 /* Each 1/10 second of audio will have the same fade applied */ 1009 /* Each 1/20 second of audio will have the same fade applied */
903 size_t block_rem = MIN(BYTERATE / 10, fade_out_rem); 1010 size_t block_rem = MIN(BYTERATE / 20, fade_out_rem);
904 int factor = (fade_out_rem << 8) / total_fade_out; 1011 int factor = (fade_out_rem << 8) / fade_out_total;
905 1012
906 fade_out_rem -= block_rem; 1013 fade_out_rem -= block_rem;
907 1014
908 crossfade_mix_fade(factor, block_rem, NULL, 1015 crossfade_mix_fade(factor, block_rem, NULL, &fade_out_index,
909 &fade_out_sample, &fade_out_chunk); 1016 0, MIXFADE_KEEP_POS);
910 } 1017 }
911 1018
912 /* zero out the rest of the buffer */ 1019 /* zero out the rest of the buffer */
913 crossfade_mix_fade(0, crossfade_rem, NULL, 1020 crossfade_mix_fade(0, INT_MAX, NULL, &fade_out_index,
914 &fade_out_sample, &fade_out_chunk); 1021 0, MIXFADE_NULLIFY_POS);
1022
1023 pcm_play_lock();
915 } 1024 }
916 1025
917 /* Initialize fade-in counters */ 1026 /* Initialize fade-in counters */
918 crossfade_fade_in_total = global_settings.crossfade_fade_in_duration * BYTERATE; 1027 crossfade_fade_in_total = fade_in_duration;
919 crossfade_fade_in_rem = crossfade_fade_in_total; 1028 crossfade_fade_in_rem = fade_in_duration;
920 1029
921 fade_in_delay = global_settings.crossfade_fade_in_delay * BYTERATE; 1030 /* 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;
1034
1035 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();
922 1041
923 /* Find the right chunk and sample to start fading in */
924 fade_in_delay += crossfade_sample * 2;
925 crossfade_sample = find_chunk(fade_in_delay, &crossfade_chunk) / 2;
926 logf("crossfade_start done!"); 1042 logf("crossfade_start done!");
927} 1043}
928 1044
929/* Perform fade-in of new track */ 1045/* Perform fade-in of new track */
930static void write_to_crossfade(size_t length) 1046static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset)
931{ 1047{
932 if (length) 1048 unsigned char *buf = crossfade_buffer;
933 {
934 char *buf = fadebuf;
935 if (crossfade_fade_in_rem)
936 {
937 size_t samples;
938 int16_t *input_buf;
939 1049
940 /* Fade factor for this packet */ 1050 if (crossfade_fade_in_rem)
941 int factor = 1051 {
942 ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) / 1052 /* Fade factor for this packet */
943 crossfade_fade_in_total; 1053 int factor =
944 /* Bytes to fade */ 1054 ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) /
945 size_t fade_rem = MIN(length, crossfade_fade_in_rem); 1055 crossfade_fade_in_total;
946 1056 /* Bytes to fade */
947 /* We _will_ fade this many bytes */ 1057 size_t fade_rem = MIN(size, crossfade_fade_in_rem);
948 crossfade_fade_in_rem -= fade_rem;
949 1058
950 if (crossfade_chunk) 1059 /* We _will_ fade this many bytes */
951 { 1060 crossfade_fade_in_rem -= fade_rem;
952 /* Mix the data */
953 size_t fade_total = fade_rem;
954 fade_rem = crossfade_mix_fade(factor, fade_rem, buf,
955 &crossfade_sample, &crossfade_chunk);
956 length -= fade_total - fade_rem;
957 buf += fade_total - fade_rem;
958 if (!length)
959 return;
960 }
961 1061
962 samples = fade_rem / 2; 1062 if (crossfade_index != INVALID_BUF_INDEX)
963 input_buf = (int16_t *)buf;
964 /* Fade remaining samples in place */
965 while (samples--)
966 {
967 int32_t sample = *input_buf;
968 *input_buf++ = (sample * factor) >> 8;
969 }
970 }
971
972 if (crossfade_chunk)
973 { 1063 {
974 /* Mix the data */ 1064 /* Mix the data */
975 size_t mix_total = length; 1065 size_t fade_total = fade_rem;
976 /* A factor of 256 means mix only, no fading */ 1066 fade_rem = crossfade_mix_fade(factor, fade_rem, buf, &crossfade_index,
977 length = crossfade_mix_fade(256, length, buf, 1067 elapsed, offset);
978 &crossfade_sample, &crossfade_chunk); 1068 fade_total -= fade_rem;
979 buf += mix_total - length; 1069 size -= fade_total;
980 if (!length) 1070 buf += fade_total;
1071
1072 if (!size)
981 return; 1073 return;
982 } 1074 }
983 1075
984 while (length > 0) 1076 /* Fade remaining samples in place */
1077 int samples = fade_rem / 4;
1078 int16_t *input_buf = (int16_t *)buf;
1079
1080 while (samples--)
985 { 1081 {
986 COMMIT_IF_NEEDED; 1082 int32_t left = input_buf[0];
987 size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos; 1083 int32_t right = input_buf[1];
988 size_t copy_n = MIN(length, pcmbuf_size - pcmbuffer_index); 1084 *input_buf++ = left * factor >> 8;
989 memcpy(&pcmbuffer[pcmbuffer_index], buf, copy_n); 1085 *input_buf++ = right * factor >> 8;
990 buf += copy_n;
991 pcmbuffer_fillpos += copy_n;
992 length -= copy_n;
993 } 1086 }
994 } 1087 }
1088
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
995 /* if no more fading-in to do, stop the crossfade */ 1115 /* if no more fading-in to do, stop the crossfade */
996 if (!(crossfade_fade_in_rem || crossfade_chunk)) 1116#if 0
997 crossfade_active = false; 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;
998} 1125}
999 1126
1000static void pcmbuf_finish_crossfade_enable(void) 1127static void pcmbuf_finish_crossfade_enable(void)
1001{ 1128{
1002 /* Copy the pending setting over now */ 1129 /* Copy the pending setting over now */
1003 crossfade_enabled = crossfade_enable_request; 1130 crossfade_setting = crossfade_enable_request;
1004 1131
1005 pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ? 1132 pcmbuf_watermark = (crossfade_setting != CROSSFADE_ENABLE_OFF && pcmbuf_size) ?
1006 /* If crossfading, try to keep the buffer full other than 1 second */ 1133 /* If crossfading, try to keep the buffer full other than 1 second */
1007 (pcmbuf_size - BYTERATE) : 1134 (pcmbuf_size - BYTERATE) :
1008 /* Otherwise, just use the default */ 1135 /* Otherwise, just use the default */
@@ -1011,20 +1138,20 @@ static void pcmbuf_finish_crossfade_enable(void)
1011 1138
1012bool pcmbuf_is_crossfade_active(void) 1139bool pcmbuf_is_crossfade_active(void)
1013{ 1140{
1014 return crossfade_active || crossfade_track_change_started; 1141 return crossfade_status != CROSSFADE_INACTIVE;
1015} 1142}
1016 1143
1017void pcmbuf_request_crossfade_enable(bool on_off) 1144void pcmbuf_request_crossfade_enable(int setting)
1018{ 1145{
1019 /* Next setting to be used, not applied now */ 1146 /* Next setting to be used, not applied now */
1020 crossfade_enable_request = on_off; 1147 crossfade_enable_request = setting;
1021} 1148}
1022 1149
1023bool pcmbuf_is_same_size(void) 1150bool pcmbuf_is_same_size(void)
1024{ 1151{
1025 /* if pcmbuffer is NULL, then not set up yet even once so always */ 1152 /* if pcmbuf_buffer is NULL, then not set up yet even once so always */
1026 bool same_size = pcmbuffer ? 1153 bool same_size = pcmbuf_buffer ?
1027 (get_next_required_pcmbuf_size() == pcmbuf_size) : true; 1154 (get_next_required_pcmbuf_chunks() == pcmbuf_desc_count) : true;
1028 1155
1029 /* no buffer change needed, so finish crossfade setup now */ 1156 /* no buffer change needed, so finish crossfade setup now */
1030 if (same_size) 1157 if (same_size)
@@ -1037,49 +1164,29 @@ bool pcmbuf_is_same_size(void)
1037 1164
1038/** Debug menu, other metrics */ 1165/** Debug menu, other metrics */
1039 1166
1040/* Amount of bytes left in the buffer. */ 1167/* Amount of bytes left in the buffer, accounting for uncommitted bytes */
1041size_t pcmbuf_free(void) 1168size_t pcmbuf_free(void)
1042{ 1169{
1043 if (read_chunk != NULL) 1170 return pcmbuf_size - pcmbuf_unplayed_bytes() - pcmbuf_bytes_waiting;
1044 {
1045 void *read = (void *)read_chunk->addr;
1046 void *write = &pcmbuffer[pcmbuffer_pos + pcmbuffer_fillpos];
1047 if (read < write)
1048 return (size_t)(read - write) + pcmbuf_size;
1049 else
1050 return (size_t) (read - write);
1051 }
1052 return pcmbuf_size - pcmbuffer_fillpos;
1053} 1171}
1054 1172
1173/* Data bytes allocated for buffer */
1055size_t pcmbuf_get_bufsize(void) 1174size_t pcmbuf_get_bufsize(void)
1056{ 1175{
1057 return pcmbuf_size; 1176 return pcmbuf_size;
1058} 1177}
1059 1178
1179/* Number of committed descriptors */
1060int pcmbuf_used_descs(void) 1180int pcmbuf_used_descs(void)
1061{ 1181{
1062 struct chunkdesc *temp = read_chunk; 1182 return pcmbuf_unplayed_bytes() / PCMBUF_CHUNK_SIZE;
1063 unsigned int i = 0;
1064 while (temp) {
1065 temp = temp->link;
1066 i++;
1067 }
1068 return i;
1069} 1183}
1070 1184
1185/* Total number of descriptors allocated */
1071int pcmbuf_descs(void) 1186int pcmbuf_descs(void)
1072{ 1187{
1073 return NUM_CHUNK_DESCS(pcmbuf_size); 1188 return pcmbuf_desc_count;
1074}
1075
1076#ifdef ROCKBOX_HAS_LOGF
1077unsigned char *pcmbuf_get_meminfo(size_t *length)
1078{
1079 *length = pcmbuf_bufend - pcmbuffer;
1080 return pcmbuffer;
1081} 1189}
1082#endif
1083 1190
1084 1191
1085/** Fading and channel volume control */ 1192/** Fading and channel volume control */
@@ -1112,7 +1219,6 @@ static void pcmbuf_fade_tick(void)
1112 { 1219 {
1113 /* Fade is complete */ 1220 /* Fade is complete */
1114 tick_remove_task(pcmbuf_fade_tick); 1221 tick_remove_task(pcmbuf_fade_tick);
1115
1116 if (fade_state == PCM_FADING_OUT) 1222 if (fade_state == PCM_FADING_OUT)
1117 { 1223 {
1118 /* Tell PCM to stop at its earliest convenience */ 1224 /* Tell PCM to stop at its earliest convenience */
@@ -1177,19 +1283,15 @@ void pcmbuf_soft_mode(bool shhh)
1177 1283
1178bool pcmbuf_is_lowdata(void) 1284bool pcmbuf_is_lowdata(void)
1179{ 1285{
1180 if (!pcm_is_playing() || pcm_is_paused() 1286 if (!audio_pcmbuf_may_play() || pcmbuf_is_crossfade_active())
1181#ifdef HAVE_CROSSFADE
1182 || pcmbuf_is_crossfade_active()
1183#endif
1184 )
1185 return false; 1287 return false;
1186 1288
1187#if MEMORYSIZE > 2 1289#if MEMORYSIZE > 2
1188 /* 1 seconds of buffer is low data */ 1290 /* 1 seconds of buffer is low data */
1189 return LOW_DATA(4); 1291 return pcmbuf_unplayed_bytes() < DATA_LEVEL(4);
1190#else 1292#else
1191 /* under watermark is low data */ 1293 /* under watermark is low data */
1192 return (pcmbuf_unplayed_bytes < pcmbuf_watermark); 1294 return pcmbuf_unplayed_bytes() < pcmbuf_watermark;
1193#endif 1295#endif
1194} 1296}
1195 1297
@@ -1198,8 +1300,15 @@ void pcmbuf_set_low_latency(bool state)
1198 low_latency_mode = state; 1300 low_latency_mode = state;
1199} 1301}
1200 1302
1201unsigned long pcmbuf_get_latency(void) 1303/* Return the current position key value */
1304unsigned int pcmbuf_get_position_key(void)
1305{
1306 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)
1202{ 1312{
1203 return (pcmbuf_unplayed_bytes + 1313 pcmbuf_sync_position = true;
1204 mixer_channel_get_bytes_waiting(PCM_MIXER_CHAN_PLAYBACK)) * 1000 / BYTERATE;
1205} 1314}