1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
27 #include "pcm_playback.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.
76 /* Structure we can use to queue pcm chunks in memory to be played
77 * by the driver code. */
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
;
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
)
100 if (state
!= boost_state
) {
101 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
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
;
122 /* The buffer is finished, call the callback function */
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())
137 /* Update the buffer descriptor */
138 desc
->addr
+= desc
->size
;
143 /* No more buffers */
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
;
182 void pcmbuf_watermark_callback(int bytes_left
)
184 /* Fill audio buffer by boosting cpu */
186 if (bytes_left
<= CHUNK_SIZE
* 2)
187 crossfade_active
= false;
190 void pcmbuf_set_boost_mode(bool state
)
197 void pcmbuf_add_event(void (*event_handler
)(void))
199 pcmbuf_event_handler
= event_handler
;
202 unsigned int pcmbuf_get_latency(void)
206 latency
= (pcmbuf_unplayed_bytes
+ pcm_get_bytes_waiting())
214 bool pcmbuf_is_lowdata(void)
216 if (!pcm_is_playing() || pcm_is_paused() || crossfade_init
|| crossfade_active
)
219 if (pcmbuf_unplayed_bytes
< CHUNK_SIZE
* 4)
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();
232 logf("pcmbuf_crossfade_init");
236 case CROSSFADE_MODE_CROSSFADE
:
237 crossfade_mode
= CFM_CROSSFADE
;
239 case CROSSFADE_MODE_MIX
:
240 crossfade_mode
= CFM_MIX
;
246 crossfade_init
= true;
252 void pcmbuf_play_stop(void)
256 pcmbuf_unplayed_bytes
= 0;
257 pcmbuf_read_index
= 0;
258 pcmbuf_write_index
= 0;
260 audiobuffer_fillpos
= 0;
261 audiobuffer_free
= pcmbuf_size
;
262 crossfade_init
= false;
263 crossfade_active
= false;
265 pcmbuf_set_boost_mode(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
;
280 long pcmbuf_get_bufsize(void)
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()) {
296 crossfade_mode
= CFM_FLUSH
;
297 crossfade_init
= true;
300 void pcmbuf_flush_fillpos(void)
304 copy_n
= MIN(audiobuffer_fillpos
, CHUNK_SIZE
);
307 while (!pcmbuf_add_chunk(&audiobuffer
[audiobuffer_pos
],
308 copy_n
, pcmbuf_event_handler
)) {
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
);
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
;
332 if (bytesleft
< CHUNK_SIZE
* 4) {
333 logf("crossfade rejected");
338 logf("crossfade_start");
339 audiobuffer_fillpos
= 0;
341 crossfade_active
= true;
342 crossfade_pos
= audiobuffer_pos
;
344 switch (crossfade_mode
) {
347 crossfade_amount
= (bytesleft
- (CHUNK_SIZE
* 2))/2;
348 crossfade_rem
= crossfade_amount
;
352 crossfade_amount
= (bytesleft
- (CHUNK_SIZE
* 2))/2;
353 crossfade_rem
= crossfade_amount
;
357 crossfade_pos
-= crossfade_amount
*2;
358 if (crossfade_pos
< 0)
359 crossfade_pos
+= pcmbuf_size
;
363 int crossfade(short *buf
, const short *buf2
, int length
)
368 size
= MIN(length
, crossfade_rem
);
369 switch (crossfade_mode
) {
370 /* Mix two streams. */
372 /* Bias & add & clip. */
373 for (i
= 0; i
< size
; i
++) {
374 buf
[i
] = MIN(MAX(buf
[i
] + buf2
[i
], -32768), 32767);
378 /* Fade two streams. */
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;
388 /* Join two streams. */
390 for (i
= 0; i
< size
; i
++) {
393 //memcpy((char *)buf, (char *)buf2, size*2);
397 crossfade_rem
-= size
;
398 if (crossfade_rem
<= 0)
399 crossfade_active
= false;
404 static bool prepare_insert(long length
)
409 if (audiobuffer_free
< length
+ audiobuffer_fillpos
410 + CHUNK_SIZE
&& !crossfade_active
) {
415 if (!pcm_is_playing()) {
417 crossfade_active
= false;
418 if (audiobuffer_free
< pcmbuf_size
- CHUNK_SIZE
*4) {
419 logf("pcm starting");
420 pcm_play_data(pcmbuf_callback
);
427 void* pcmbuf_request_buffer(long length
, long *realsize
)
434 while (audiobuffer_free
< length
+ audiobuffer_fillpos
435 + CHUNK_SIZE
&& !crossfade_active
) {
440 if (crossfade_active
) {
441 *realsize
= MIN(length
, PCMBUF_GUARD
);
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
];
455 bool pcmbuf_is_crossfade_active(void)
457 return crossfade_active
|| crossfade_init
;
460 void pcmbuf_flush_buffer(long length
)
465 prepare_insert(length
);
467 if (crossfade_active
) {
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);
476 crossfade_pos
+= copy_n
;
477 if (crossfade_pos
>= pcmbuf_size
)
478 crossfade_pos
-= pcmbuf_size
;
482 copy_n
= MIN(length
, pcmbuf_size
- audiobuffer_pos
);
483 memcpy(&audiobuffer
[audiobuffer_pos
], buf
, copy_n
);
484 audiobuffer_fillpos
= copy_n
;
488 pcmbuf_flush_fillpos();
492 audiobuffer_fillpos
+= length
;
495 if (audiobuffer_fillpos
< CHUNK_SIZE
&& pcmbuf_size
496 - audiobuffer_pos
- audiobuffer_fillpos
> 0)
499 copy_n
= audiobuffer_fillpos
- (pcmbuf_size
- audiobuffer_pos
);
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
;
508 pcmbuf_flush_fillpos();
511 bool pcmbuf_insert_buffer(char *buf
, long length
)
515 if (!prepare_insert(length
))
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);
527 crossfade_pos
+= copy_n
;
528 if (crossfade_pos
>= pcmbuf_size
)
529 crossfade_pos
-= pcmbuf_size
;
533 copy_n
= MIN(length
, pcmbuf_size
- audiobuffer_pos
);
534 memcpy(&audiobuffer
[audiobuffer_pos
], buf
, copy_n
);
535 audiobuffer_fillpos
= copy_n
;
539 pcmbuf_flush_fillpos();
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
],
551 audiobuffer_fillpos
+= copy_n
;
554 /* Pre-buffer to meet CHUNK_SIZE requirement */
555 if (audiobuffer_fillpos
< CHUNK_SIZE
&& length
== 0) {
559 pcmbuf_flush_fillpos();
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
);
572 pcmbuf_set_watermark(PCMBUF_WATERMARK
, pcmbuf_watermark_callback
);
576 bool pcmbuf_is_crossfade_enabled(void)
578 return crossfade_enabled
;