Fix for ff/rw in long MP3 files.
[kugel-rb.git] / apps / pcmbuf.c
blob691f8d5a1959a9ca9fc460cba7ccd97537e09f38
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Miika Pekkarinen
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include "config.h"
23 #include "debug.h"
24 #include "panic.h"
25 #include <kernel.h>
26 #include "pcmbuf.h"
27 #include "pcm_playback.h"
28 #include "logf.h"
29 #ifndef SIMULATOR
30 #include "cpu.h"
31 #endif
32 #include "system.h"
33 #include <string.h>
34 #include "buffer.h"
35 #include "settings.h"
36 #include "audio.h"
38 #define CHUNK_SIZE PCMBUF_GUARD
39 /* Must be a power of 2 */
40 #define NUM_PCM_BUFFERS 64
41 #define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
42 #define PCMBUF_WATERMARK (CHUNK_SIZE * 6)
44 /* Audio buffer related settings. */
45 static long pcmbuf_size = 0; /* Size of the PCM buffer. */
46 static char *audiobuffer;
47 static long audiobuffer_pos; /* Current audio buffer write index. */
48 long audiobuffer_free; /* Amount of bytes left in the buffer. */
49 static long audiobuffer_fillpos; /* Amount audiobuffer_pos will be increased. */
50 static char *guardbuf;
52 static void (*pcmbuf_event_handler)(void);
54 /* Crossfade related. */
55 static int crossfade_mode;
56 static bool crossfade_enabled;
57 static bool crossfade_active;
58 static bool crossfade_init;
59 static int crossfade_pos;
60 static int crossfade_amount;
61 static int crossfade_rem;
64 static bool boost_mode;
66 /* Crossfade modes. If CFM_CROSSFADE is selected, normal
67 * crossfader will activate. Selecting CFM_FLUSH is a special
68 * operation that only overwrites the pcm buffer without crossfading.
70 enum {
71 CFM_CROSSFADE,
72 CFM_MIX,
73 CFM_FLUSH
76 /* Structure we can use to queue pcm chunks in memory to be played
77 * by the driver code. */
78 struct pcmbufdesc
80 void *addr;
81 int size;
82 /* Call this when the buffer has been played */
83 void (*callback)(void);
84 } pcmbuffers[NUM_PCM_BUFFERS];
86 volatile int pcmbuf_read_index;
87 volatile int pcmbuf_write_index;
88 int pcmbuf_unplayed_bytes;
89 int pcmbuf_watermark;
90 void (*pcmbuf_watermark_event)(int bytes_left);
91 static int last_chunksize;
93 static void pcmbuf_boost(bool state)
95 static bool boost_state = false;
97 if (crossfade_init || crossfade_active || boost_mode)
98 return ;
100 if (state != boost_state) {
101 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
102 cpu_boost(state);
103 #endif
104 boost_state = state;
108 int pcmbuf_num_used_buffers(void)
110 return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
113 static void pcmbuf_callback(unsigned char** start, long* size)
115 struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
117 pcmbuf_unplayed_bytes -= last_chunksize;
118 audiobuffer_free += last_chunksize;
120 if(desc->size == 0)
122 /* The buffer is finished, call the callback function */
123 if(desc->callback)
124 desc->callback();
126 /* Advance to the next buffer */
127 pcmbuf_read_index = (pcmbuf_read_index + 1) & NUM_PCM_BUFFERS_MASK;
128 desc = &pcmbuffers[pcmbuf_read_index];
131 if(pcmbuf_num_used_buffers())
134 *start = desc->addr;
135 *size = desc->size;
137 /* Update the buffer descriptor */
138 desc->addr += desc->size;
139 desc->size = 0;
141 else
143 /* No more buffers */
144 *size = 0;
145 if (pcmbuf_event_handler)
146 pcmbuf_event_handler();
149 last_chunksize = *size;
150 if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
152 if(pcmbuf_watermark_event)
154 pcmbuf_watermark_event(pcmbuf_unplayed_bytes);
159 void pcmbuf_set_watermark(int numbytes, void (*callback)(int bytes_left))
161 pcmbuf_watermark = numbytes;
162 pcmbuf_watermark_event = callback;
165 bool pcmbuf_add_chunk(void *addr, int size, void (*callback)(void))
167 /* We don't use the last buffer, since we can't see the difference
168 between the full and empty condition */
169 if(pcmbuf_num_used_buffers() < (NUM_PCM_BUFFERS - 2))
171 pcmbuffers[pcmbuf_write_index].addr = addr;
172 pcmbuffers[pcmbuf_write_index].size = size;
173 pcmbuffers[pcmbuf_write_index].callback = callback;
174 pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK;
175 pcmbuf_unplayed_bytes += size;
176 return true;
178 else
179 return false;
182 void pcmbuf_watermark_callback(int bytes_left)
184 /* Fill audio buffer by boosting cpu */
185 pcmbuf_boost(true);
186 if (bytes_left <= CHUNK_SIZE * 2)
187 crossfade_active = false;
190 void pcmbuf_set_boost_mode(bool state)
192 if (state)
193 pcmbuf_boost(true);
194 boost_mode = state;
197 void pcmbuf_add_event(void (*event_handler)(void))
199 pcmbuf_event_handler = event_handler;
202 unsigned int pcmbuf_get_latency(void)
204 int latency;
206 latency = (pcmbuf_unplayed_bytes + pcm_get_bytes_waiting())
207 / 4 / (44100/1000);
208 if (latency < 0)
209 latency = 0;
211 return latency;
214 bool pcmbuf_is_lowdata(void)
216 if (!pcm_is_playing() || pcm_is_paused() || crossfade_init || crossfade_active)
217 return false;
219 if (pcmbuf_unplayed_bytes < CHUNK_SIZE * 4)
220 return true;
222 return false;
225 bool pcmbuf_crossfade_init(int type)
227 if (pcmbuf_size - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled
228 || crossfade_active || crossfade_init) {
229 pcmbuf_flush_audio();
230 return false;
232 logf("pcmbuf_crossfade_init");
233 pcmbuf_boost(true);
235 switch (type) {
236 case CROSSFADE_MODE_CROSSFADE:
237 crossfade_mode = CFM_CROSSFADE;
238 break;
239 case CROSSFADE_MODE_MIX:
240 crossfade_mode = CFM_MIX;
241 break;
242 default:
243 return false;
246 crossfade_init = true;
248 return true;
252 void pcmbuf_play_stop(void)
254 pcm_play_stop();
255 last_chunksize = 0;
256 pcmbuf_unplayed_bytes = 0;
257 pcmbuf_read_index = 0;
258 pcmbuf_write_index = 0;
259 audiobuffer_pos = 0;
260 audiobuffer_fillpos = 0;
261 audiobuffer_free = pcmbuf_size;
262 crossfade_init = false;
263 crossfade_active = false;
265 pcmbuf_set_boost_mode(false);
266 pcmbuf_boost(false);
270 void pcmbuf_init(long bufsize)
272 pcmbuf_size = bufsize;
273 audiobuffer = &audiobuf[(audiobufend - audiobuf) -
274 pcmbuf_size - PCMBUF_GUARD];
275 guardbuf = &audiobuffer[pcmbuf_size];
276 pcmbuf_event_handler = NULL;
277 pcmbuf_play_stop();
280 long pcmbuf_get_bufsize(void)
282 return pcmbuf_size;
285 /** Initialize a track switch so that audio playback will not stop but
286 * the switch to next track would happen as soon as possible.
288 void pcmbuf_flush_audio(void)
290 if (crossfade_init || crossfade_active || !pcm_is_playing()) {
291 pcmbuf_play_stop();
292 return ;
295 pcmbuf_boost(true);
296 crossfade_mode = CFM_FLUSH;
297 crossfade_init = true;
300 void pcmbuf_flush_fillpos(void)
302 int copy_n;
304 copy_n = MIN(audiobuffer_fillpos, CHUNK_SIZE);
306 if (copy_n) {
307 while (!pcmbuf_add_chunk(&audiobuffer[audiobuffer_pos],
308 copy_n, pcmbuf_event_handler)) {
309 pcmbuf_boost(false);
310 sleep(1);
311 /* This is a fatal error situation that should never happen. */
312 if (!pcm_is_playing()) {
313 logf("pcm_flush_fillpos error");
314 pcm_play_data(pcmbuf_callback);
315 return ;
318 pcmbuf_event_handler = NULL;
319 audiobuffer_pos += copy_n;
320 if (audiobuffer_pos >= pcmbuf_size)
321 audiobuffer_pos -= pcmbuf_size;
322 audiobuffer_free -= copy_n;
323 audiobuffer_fillpos -= copy_n;
327 static void crossfade_start(void)
329 int bytesleft = pcmbuf_unplayed_bytes;
331 crossfade_init = 0;
332 if (bytesleft < CHUNK_SIZE * 4) {
333 logf("crossfade rejected");
334 pcmbuf_play_stop();
335 return ;
338 logf("crossfade_start");
339 audiobuffer_fillpos = 0;
340 pcmbuf_boost(true);
341 crossfade_active = true;
342 crossfade_pos = audiobuffer_pos;
344 switch (crossfade_mode) {
345 case CFM_MIX:
346 case CFM_CROSSFADE:
347 crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2;
348 crossfade_rem = crossfade_amount;
349 break ;
351 case CFM_FLUSH:
352 crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2;
353 crossfade_rem = crossfade_amount;
354 break ;
357 crossfade_pos -= crossfade_amount*2;
358 if (crossfade_pos < 0)
359 crossfade_pos += pcmbuf_size;
362 static __inline
363 int crossfade(short *buf, const short *buf2, int length)
365 int size, i;
366 int val1, val2;
368 size = MIN(length, crossfade_rem);
369 switch (crossfade_mode) {
370 /* Mix two streams. */
371 case CFM_MIX:
372 /* Bias & add & clip. */
373 for (i = 0; i < size; i++) {
374 buf[i] = MIN(MAX(buf[i] + buf2[i], -32768), 32767);
376 break ;
378 /* Fade two streams. */
379 case CFM_CROSSFADE:
380 val1 = (crossfade_rem<<10)/crossfade_amount;
381 val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount;
383 for (i = 0; i < size; i++) {
384 buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10;
386 break ;
388 /* Join two streams. */
389 case CFM_FLUSH:
390 for (i = 0; i < size; i++) {
391 buf[i] = buf2[i];
393 //memcpy((char *)buf, (char *)buf2, size*2);
394 break ;
397 crossfade_rem -= size;
398 if (crossfade_rem <= 0)
399 crossfade_active = false;
401 return size;
404 static bool prepare_insert(long length)
406 if (crossfade_init)
407 crossfade_start();
409 if (audiobuffer_free < length + audiobuffer_fillpos
410 + CHUNK_SIZE && !crossfade_active) {
411 pcmbuf_boost(false);
412 return false;
415 if (!pcm_is_playing()) {
416 pcmbuf_boost(true);
417 crossfade_active = false;
418 if (audiobuffer_free < pcmbuf_size - CHUNK_SIZE*4) {
419 logf("pcm starting");
420 pcm_play_data(pcmbuf_callback);
424 return true;
427 void* pcmbuf_request_buffer(long length, long *realsize)
429 void *ptr = NULL;
431 if (crossfade_init)
432 crossfade_start();
434 while (audiobuffer_free < length + audiobuffer_fillpos
435 + CHUNK_SIZE && !crossfade_active) {
436 pcmbuf_boost(false);
437 sleep(1);
440 if (crossfade_active) {
441 *realsize = MIN(length, PCMBUF_GUARD);
442 ptr = &guardbuf[0];
443 } else {
444 *realsize = MIN(length, pcmbuf_size - audiobuffer_pos
445 - audiobuffer_fillpos);
446 if (*realsize < length) {
447 *realsize += MIN((long)(length - *realsize), PCMBUF_GUARD);
449 ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos];
452 return ptr;
455 bool pcmbuf_is_crossfade_active(void)
457 return crossfade_active || crossfade_init;
460 void pcmbuf_flush_buffer(long length)
462 int copy_n;
463 char *buf;
465 prepare_insert(length);
467 if (crossfade_active) {
468 buf = &guardbuf[0];
469 length = MIN(length, PCMBUF_GUARD);
470 while (length > 0 && crossfade_active) {
471 copy_n = MIN(length, pcmbuf_size - crossfade_pos);
472 copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos],
473 (const short *)buf, copy_n/2);
474 buf += copy_n;
475 length -= copy_n;
476 crossfade_pos += copy_n;
477 if (crossfade_pos >= pcmbuf_size)
478 crossfade_pos -= pcmbuf_size;
481 while (length > 0) {
482 copy_n = MIN(length, pcmbuf_size - audiobuffer_pos);
483 memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
484 audiobuffer_fillpos = copy_n;
485 buf += copy_n;
486 length -= copy_n;
487 if (length > 0)
488 pcmbuf_flush_fillpos();
492 audiobuffer_fillpos += length;
494 try_flush:
495 if (audiobuffer_fillpos < CHUNK_SIZE && pcmbuf_size
496 - audiobuffer_pos - audiobuffer_fillpos > 0)
497 return ;
499 copy_n = audiobuffer_fillpos - (pcmbuf_size - audiobuffer_pos);
500 if (copy_n > 0) {
501 audiobuffer_fillpos -= copy_n;
502 pcmbuf_flush_fillpos();
503 copy_n = MIN(copy_n, PCMBUF_GUARD);
504 memcpy(&audiobuffer[0], &guardbuf[0], copy_n);
505 audiobuffer_fillpos = copy_n;
506 goto try_flush;
508 pcmbuf_flush_fillpos();
511 bool pcmbuf_insert_buffer(char *buf, long length)
513 long copy_n = 0;
515 if (!prepare_insert(length))
516 return false;
519 if (crossfade_active) {
520 while (length > 0 && crossfade_active) {
521 copy_n = MIN(length, pcmbuf_size - crossfade_pos);
523 copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos],
524 (const short *)buf, copy_n/2);
525 buf += copy_n;
526 length -= copy_n;
527 crossfade_pos += copy_n;
528 if (crossfade_pos >= pcmbuf_size)
529 crossfade_pos -= pcmbuf_size;
532 while (length > 0) {
533 copy_n = MIN(length, pcmbuf_size - audiobuffer_pos);
534 memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
535 audiobuffer_fillpos = copy_n;
536 buf += copy_n;
537 length -= copy_n;
538 if (length > 0)
539 pcmbuf_flush_fillpos();
543 while (length > 0) {
544 copy_n = MIN(length, pcmbuf_size - audiobuffer_pos -
545 audiobuffer_fillpos);
546 copy_n = MIN(CHUNK_SIZE - audiobuffer_fillpos, copy_n);
548 memcpy(&audiobuffer[audiobuffer_pos+audiobuffer_fillpos],
549 buf, copy_n);
550 buf += copy_n;
551 audiobuffer_fillpos += copy_n;
552 length -= copy_n;
554 /* Pre-buffer to meet CHUNK_SIZE requirement */
555 if (audiobuffer_fillpos < CHUNK_SIZE && length == 0) {
556 return true;
559 pcmbuf_flush_fillpos();
562 return true;
565 void pcmbuf_crossfade_enable(bool on_off)
567 crossfade_enabled = on_off;
569 if (crossfade_enabled) {
570 pcmbuf_set_watermark(pcmbuf_size - (CHUNK_SIZE*6), pcmbuf_watermark_callback);
571 } else {
572 pcmbuf_set_watermark(PCMBUF_WATERMARK, pcmbuf_watermark_callback);
576 bool pcmbuf_is_crossfade_enabled(void)
578 return crossfade_enabled;