blob: 1f08274fc45e137a1ef3f0953ec09c333c428be2 [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
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
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
36#define CHUNK_SIZE 32768
37/* Must be a power of 2 */
38#define NUM_PCM_BUFFERS (PCMBUF_SIZE / CHUNK_SIZE)
39#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
40#define PCMBUF_WATERMARK (CHUNK_SIZE * 10)
41#define PCMBUF_CF_WATERMARK (PCMBUF_SIZE - CHUNK_SIZE*8)
42
43/* Audio buffer related settings. */
44static char *audiobuffer;
45static long audiobuffer_pos; /* Current audio buffer write index. */
46long audiobuffer_free; /* Amount of bytes left in the buffer. */
47static long audiobuffer_fillpos; /* Amount audiobuffer_pos will be increased. */
48static char *guardbuf;
49
50static void (*pcmbuf_event_handler)(void);
51
52/* Crossfade related. */
53static int crossfade_mode;
54static bool crossfade_enabled;
55static bool crossfade_active;
56static bool crossfade_init;
57static int crossfade_pos;
58static int crossfade_amount;
59static int crossfade_rem;
60
61
62static bool boost_mode;
63
64/* Crossfade modes. If CFM_CROSSFADE is selected, normal
65 * crossfader will activate. Selecting CFM_FLUSH is a special
66 * operation that only overwrites the pcm buffer without crossfading.
67 */
68enum {
69 CFM_CROSSFADE,
70 CFM_FLUSH
71};
72
73/* Structure we can use to queue pcm chunks in memory to be played
74 * by the driver code. */
75struct pcmbufdesc
76{
77 void *addr;
78 int size;
79 /* Call this when the buffer has been played */
80 void (*callback)(void);
81} pcmbuffers[NUM_PCM_BUFFERS];
82
83volatile int pcmbuf_read_index;
84volatile int pcmbuf_write_index;
85int pcmbuf_unplayed_bytes;
86int pcmbuf_watermark;
87void (*pcmbuf_watermark_event)(int bytes_left);
88static int last_chunksize;
89
90static void pcmbuf_boost(bool state)
91{
92 static bool boost_state = false;
93
94 if (crossfade_init || crossfade_active || boost_mode)
95 return ;
96
97 if (state != boost_state) {
98#ifdef HAVE_ADJUSTABLE_CPU_FREQ
99 cpu_boost(state);
100#endif
101 boost_state = state;
102 }
103}
104
105int pcmbuf_num_used_buffers(void)
106{
107 return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
108}
109
110static void pcmbuf_callback(unsigned char** start, long* size)
111{
112 struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
113 int sz;
114
115 pcmbuf_unplayed_bytes -= last_chunksize;
116 audiobuffer_free += last_chunksize;
117
118
119 if(desc->size == 0)
120 {
121 /* The buffer is finished, call the callback function */
122 if(desc->callback)
123 desc->callback();
124
125 /* Advance to the next buffer */
126 pcmbuf_read_index = (pcmbuf_read_index + 1) & NUM_PCM_BUFFERS_MASK;
127 desc = &pcmbuffers[pcmbuf_read_index];
128 }
129
130 if(pcmbuf_num_used_buffers())
131 {
132 /* Play max 64K at a time */
133 //sz = MIN(desc->size, 32768);
134 sz = desc->size;
135 *start = desc->addr;
136 *size = sz;
137
138 /* Update the buffer descriptor */
139 desc->size -= sz;
140 desc->addr += sz;
141
142 last_chunksize = sz;
143 }
144 else
145 {
146 /* No more buffers */
147 *size = 0;
148 if (pcmbuf_event_handler)
149 pcmbuf_event_handler();
150 }
151
152 logf("cbfm:%d", *size);
153 if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
154 {
155 if(pcmbuf_watermark_event)
156 {
157 pcmbuf_watermark_event(pcmbuf_unplayed_bytes);
158 }
159 }
160}
161
162void pcmbuf_set_watermark(int numbytes, void (*callback)(int bytes_left))
163{
164 pcmbuf_watermark = numbytes;
165 pcmbuf_watermark_event = callback;
166}
167
168bool pcmbuf_add_chunk(void *addr, int size, void (*callback)(void))
169{
170 /* We don't use the last buffer, since we can't see the difference
171 between the full and empty condition */
172 if(pcmbuf_num_used_buffers() < (NUM_PCM_BUFFERS - 2))
173 {
174 pcmbuffers[pcmbuf_write_index].addr = addr;
175 pcmbuffers[pcmbuf_write_index].size = size;
176 pcmbuffers[pcmbuf_write_index].callback = callback;
177 pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK;
178 pcmbuf_unplayed_bytes += size;
179 return true;
180 }
181 else
182 return false;
183}
184
185void pcmbuf_watermark_callback(int bytes_left)
186{
187 /* Fill audio buffer by boosting cpu */
188 pcmbuf_boost(true);
189 if (bytes_left <= CHUNK_SIZE * 2)
190 crossfade_active = false;
191}
192
193void pcmbuf_set_boost_mode(bool state)
194{
195 if (state)
196 pcmbuf_boost(true);
197 boost_mode = state;
198}
199
200void pcmbuf_add_event(void (*event_handler)(void))
201{
202 pcmbuf_event_handler = event_handler;
203}
204
205unsigned int pcmbuf_get_latency(void)
206{
207 int latency;
208
209 /* This has to be done better. */
210 latency = (PCMBUF_SIZE - audiobuffer_free - CHUNK_SIZE)/4 / (44100/1000);
211 if (latency < 0)
212 latency = 0;
213
214 return latency;
215}
216
217bool pcmbuf_is_lowdata(void)
218{
219 if (!pcm_is_playing() || pcm_is_paused() || crossfade_init || crossfade_active)
220 return false;
221
222 if (pcmbuf_unplayed_bytes < PCMBUF_WATERMARK)
223 return true;
224
225 return false;
226}
227
228bool pcmbuf_crossfade_init(void)
229{
230 if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled
231 || crossfade_active || crossfade_init) {
232 return false;
233 }
234 logf("pcmbuf_crossfade_init");
235 pcmbuf_boost(true);
236 crossfade_mode = CFM_CROSSFADE;
237 crossfade_init = true;
238
239 return true;
240
241}
242
243void pcmbuf_play_stop(void)
244{
245 pcm_play_stop();
246 last_chunksize = 0;
247 pcmbuf_unplayed_bytes = 0;
248 pcmbuf_read_index = 0;
249 pcmbuf_write_index = 0;
250 audiobuffer_pos = 0;
251 audiobuffer_fillpos = 0;
252 audiobuffer_free = PCMBUF_SIZE;
253 crossfade_init = false;
254 crossfade_active = false;
255
256 pcmbuf_set_boost_mode(false);
257 pcmbuf_boost(false);
258
259}
260
261void pcmbuf_init(void)
262{
263 audiobuffer = &audiobuf[(audiobufend - audiobuf) -
264 PCMBUF_SIZE - PCMBUF_GUARD];
265 guardbuf = &audiobuffer[PCMBUF_SIZE];
266 pcmbuf_event_handler = NULL;
267 pcm_init();
268 pcmbuf_play_stop();
269}
270
271/** Initialize a track switch so that audio playback will not stop but
272 * the switch to next track would happen as soon as possible.
273 */
274void pcmbuf_flush_audio(void)
275{
276 if (crossfade_init || crossfade_active || !pcm_is_playing()) {
277 pcmbuf_play_stop();
278 return ;
279 }
280
281 crossfade_mode = CFM_FLUSH;
282 crossfade_init = true;
283}
284
285void pcmbuf_flush_fillpos(void)
286{
287 int copy_n;
288
289 copy_n = MIN(audiobuffer_fillpos, CHUNK_SIZE);
290
291 if (copy_n) {
292 while (!pcmbuf_add_chunk(&audiobuffer[audiobuffer_pos],
293 copy_n, pcmbuf_event_handler)) {
294 pcmbuf_boost(false);
295 sleep(1);
296 /* This is a fatal error situation that should never happen. */
297 if (!pcm_is_playing()) {
298 logf("pcm_flush_fillpos error");
299 break ;
300 }
301 }
302 pcmbuf_event_handler = NULL;
303 audiobuffer_pos += copy_n;
304 if (audiobuffer_pos >= PCMBUF_SIZE)
305 audiobuffer_pos -= PCMBUF_SIZE;
306 audiobuffer_free -= copy_n;
307 audiobuffer_fillpos -= copy_n;
308 }
309}
310
311static void crossfade_start(void)
312{
313 int bytesleft = pcmbuf_unplayed_bytes;
314
315 crossfade_init = 0;
316 if (bytesleft < CHUNK_SIZE * 3) {
317 logf("crossfade rejected");
318 pcmbuf_play_stop();
319 return ;
320 }
321
322 logf("crossfade_start");
323 pcmbuf_flush_fillpos();
324 pcmbuf_boost(true);
325 crossfade_active = true;
326 crossfade_pos = audiobuffer_pos;
327
328 switch (crossfade_mode) {
329 case CFM_CROSSFADE:
330 crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2;
331 crossfade_rem = crossfade_amount;
332 break ;
333
334 case CFM_FLUSH:
335 crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2;
336 crossfade_rem = crossfade_amount;
337 break ;
338 }
339
340 crossfade_pos -= crossfade_amount*2;
341 if (crossfade_pos < 0)
342 crossfade_pos += PCMBUF_SIZE;
343}
344
345static __inline
346int crossfade(short *buf, const short *buf2, int length)
347{
348 int size, i;
349 int val1, val2;
350
351 size = MIN(length, crossfade_rem);
352 switch (crossfade_mode) {
353 case CFM_CROSSFADE:
354 val1 = (crossfade_rem<<10)/crossfade_amount;
355 val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount;
356
357 for (i = 0; i < size; i++) {
358 buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10;
359 }
360 break ;
361
362 case CFM_FLUSH:
363 for (i = 0; i < size; i++) {
364 buf[i] = buf2[i];
365 }
366 //memcpy((char *)buf, (char *)buf2, size*2);
367 break ;
368 }
369
370 crossfade_rem -= size;
371 if (crossfade_rem <= 0)
372 crossfade_active = false;
373
374 return size;
375}
376
377static bool prepare_insert(long length)
378{
379 if (crossfade_init)
380 crossfade_start();
381
382 if (audiobuffer_free < length + audiobuffer_fillpos
383 + CHUNK_SIZE && !crossfade_active) {
384 pcmbuf_boost(false);
385 return false;
386 }
387
388 if (!pcm_is_playing()) {
389 pcmbuf_boost(true);
390 crossfade_active = false;
391 if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*4) {
392 logf("pcm starting");
393 pcm_play_data(pcmbuf_callback);
394 }
395 }
396
397 return true;
398}
399
400void* pcmbuf_request_buffer(long length, long *realsize)
401{
402 void *ptr = NULL;
403
404 while (audiobuffer_free < length + audiobuffer_fillpos
405 + CHUNK_SIZE && !crossfade_active) {
406 pcmbuf_boost(false);
407 sleep(1);
408 }
409
410 if (crossfade_active) {
411 *realsize = MIN(length, PCMBUF_GUARD);
412 ptr = &guardbuf[0];
413 } else {
414 *realsize = MIN(length, PCMBUF_SIZE - audiobuffer_pos
415 - audiobuffer_fillpos);
416 if (*realsize < length) {
417 *realsize += MIN((long)(length - *realsize), PCMBUF_GUARD);
418 }
419 ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos];
420 }
421
422 return ptr;
423}
424
425bool pcmbuf_is_crossfade_active(void)
426{
427 return crossfade_active || crossfade_init;
428}
429
430void pcmbuf_flush_buffer(long length)
431{
432 int copy_n;
433 char *buf;
434
435 prepare_insert(length);
436
437 if (crossfade_active) {
438 buf = &guardbuf[0];
439 length = MIN(length, PCMBUF_GUARD);
440 while (length > 0 && crossfade_active) {
441 copy_n = MIN(length, PCMBUF_SIZE - crossfade_pos);
442 copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos],
443 (const short *)buf, copy_n/2);
444 buf += copy_n;
445 length -= copy_n;
446 crossfade_pos += copy_n;
447 if (crossfade_pos >= PCMBUF_SIZE)
448 crossfade_pos -= PCMBUF_SIZE;
449 }
450
451 while (length > 0) {
452 copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos);
453 memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
454 audiobuffer_fillpos = copy_n;
455 buf += copy_n;
456 length -= copy_n;
457 if (length > 0)
458 pcmbuf_flush_fillpos();
459 }
460 }
461
462 audiobuffer_fillpos += length;
463
464 try_flush:
465 if (audiobuffer_fillpos < CHUNK_SIZE && PCMBUF_SIZE
466 - audiobuffer_pos - audiobuffer_fillpos > 0)
467 return ;
468
469 copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos);
470 if (copy_n > 0) {
471 audiobuffer_fillpos -= copy_n;
472 pcmbuf_flush_fillpos();
473 copy_n = MIN(copy_n, PCMBUF_GUARD);
474 memcpy(&audiobuffer[0], &guardbuf[0], copy_n);
475 audiobuffer_fillpos = copy_n;
476 goto try_flush;
477 }
478 pcmbuf_flush_fillpos();
479}
480
481bool pcmbuf_insert_buffer(char *buf, long length)
482{
483 long copy_n = 0;
484
485 if (!prepare_insert(length))
486 return false;
487
488
489 if (crossfade_active) {
490 while (length > 0 && crossfade_active) {
491 copy_n = MIN(length, PCMBUF_SIZE - crossfade_pos);
492
493 copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos],
494 (const short *)buf, copy_n/2);
495 buf += copy_n;
496 length -= copy_n;
497 crossfade_pos += copy_n;
498 if (crossfade_pos >= PCMBUF_SIZE)
499 crossfade_pos -= PCMBUF_SIZE;
500 }
501
502 while (length > 0) {
503 copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos);
504 memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
505 audiobuffer_fillpos = copy_n;
506 buf += copy_n;
507 length -= copy_n;
508 if (length > 0)
509 pcmbuf_flush_fillpos();
510 }
511 }
512
513 while (length > 0) {
514 copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos -
515 audiobuffer_fillpos);
516 copy_n = MIN(CHUNK_SIZE - audiobuffer_fillpos, copy_n);
517
518 memcpy(&audiobuffer[audiobuffer_pos+audiobuffer_fillpos],
519 buf, copy_n);
520 buf += copy_n;
521 audiobuffer_fillpos += copy_n;
522 length -= copy_n;
523
524 /* Pre-buffer to meet CHUNK_SIZE requirement */
525 if (audiobuffer_fillpos < CHUNK_SIZE && length == 0) {
526 return true;
527 }
528
529 pcmbuf_flush_fillpos();
530 }
531
532 return true;
533}
534
535void pcmbuf_crossfade_enable(bool on_off)
536{
537 crossfade_enabled = on_off;
538
539 if (crossfade_enabled) {
540 pcmbuf_set_watermark(PCMBUF_CF_WATERMARK, pcmbuf_watermark_callback);
541 } else {
542 pcmbuf_set_watermark(PCMBUF_WATERMARK, pcmbuf_watermark_callback);
543 }
544}
545
546bool pcmbuf_is_crossfade_enabled(void)
547{
548 return crossfade_enabled;
549}
550