diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/pcm_playback.h | 2 | ||||
-rw-r--r-- | firmware/pcm_playback.c | 97 |
2 files changed, 73 insertions, 26 deletions
diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h index 07e33e96bf..554e975354 100644 --- a/firmware/export/pcm_playback.h +++ b/firmware/export/pcm_playback.h | |||
@@ -35,6 +35,7 @@ void pcm_play_data(const unsigned char* start, int size, | |||
35 | void pcm_play_stop(void); | 35 | void pcm_play_stop(void); |
36 | void pcm_play_pause(bool play); | 36 | void pcm_play_pause(bool play); |
37 | bool pcm_is_playing(void); | 37 | bool pcm_is_playing(void); |
38 | bool pcm_is_crossfade_active(void); | ||
38 | 39 | ||
39 | /* These functions are for playing chained buffers of PCM data */ | 40 | /* These functions are for playing chained buffers of PCM data */ |
40 | void pcm_play_init(void); | 41 | void pcm_play_init(void); |
@@ -45,6 +46,7 @@ void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left)); | |||
45 | 46 | ||
46 | void pcm_set_boost_mode(bool state); | 47 | void pcm_set_boost_mode(bool state); |
47 | bool pcm_is_lowdata(void); | 48 | bool pcm_is_lowdata(void); |
49 | void pcm_flush_buffer(long length); | ||
48 | bool pcm_crossfade_init(void); | 50 | bool pcm_crossfade_init(void); |
49 | void audiobuffer_add_event(void (*event_handler)(void)); | 51 | void audiobuffer_add_event(void (*event_handler)(void)); |
50 | unsigned int audiobuffer_get_latency(void); | 52 | unsigned int audiobuffer_get_latency(void); |
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index b0bdfbbb32..03cc106016 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c | |||
@@ -47,7 +47,7 @@ | |||
47 | /* Must be a power of 2 */ | 47 | /* Must be a power of 2 */ |
48 | #define NUM_PCM_BUFFERS (PCMBUF_SIZE / CHUNK_SIZE) | 48 | #define NUM_PCM_BUFFERS (PCMBUF_SIZE / CHUNK_SIZE) |
49 | #define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1) | 49 | #define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1) |
50 | #define PCM_WATERMARK (CHUNK_SIZE * 4) | 50 | #define PCM_WATERMARK (CHUNK_SIZE * 6) |
51 | #define PCM_CF_WATERMARK (PCMBUF_SIZE - CHUNK_SIZE*8) | 51 | #define PCM_CF_WATERMARK (PCMBUF_SIZE - CHUNK_SIZE*8) |
52 | 52 | ||
53 | static bool pcm_playing; | 53 | static bool pcm_playing; |
@@ -60,6 +60,16 @@ long audiobuffer_free; | |||
60 | static long audiobuffer_fillpos; | 60 | static long audiobuffer_fillpos; |
61 | static bool boost_mode; | 61 | static bool boost_mode; |
62 | 62 | ||
63 | /* Crossfade modes. If CFM_CROSSFADE is selected, normal | ||
64 | * crossfader will activate. Selecting CFM_FLUSH is a special | ||
65 | * operation that only overwrites the pcm buffer without crossfading. | ||
66 | */ | ||
67 | enum { | ||
68 | CFM_CROSSFADE, | ||
69 | CFM_FLUSH | ||
70 | }; | ||
71 | |||
72 | static int crossfade_mode; | ||
63 | static bool crossfade_enabled; | 73 | static bool crossfade_enabled; |
64 | static bool crossfade_active; | 74 | static bool crossfade_active; |
65 | static bool crossfade_init; | 75 | static bool crossfade_init; |
@@ -346,8 +356,6 @@ bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void)) | |||
346 | 356 | ||
347 | void pcm_watermark_callback(int bytes_left) | 357 | void pcm_watermark_callback(int bytes_left) |
348 | { | 358 | { |
349 | (void)bytes_left; | ||
350 | |||
351 | /* Fill audio buffer by boosting cpu */ | 359 | /* Fill audio buffer by boosting cpu */ |
352 | pcm_boost(true); | 360 | pcm_boost(true); |
353 | if (bytes_left <= CHUNK_SIZE * 2) | 361 | if (bytes_left <= CHUNK_SIZE * 2) |
@@ -395,12 +403,25 @@ bool pcm_crossfade_init(void) | |||
395 | return false; | 403 | return false; |
396 | } | 404 | } |
397 | logf("crossfading!"); | 405 | logf("crossfading!"); |
406 | crossfade_mode = CFM_CROSSFADE; | ||
398 | crossfade_init = true; | 407 | crossfade_init = true; |
399 | 408 | ||
400 | return true; | 409 | return true; |
401 | 410 | ||
402 | } | 411 | } |
403 | 412 | ||
413 | /** Initialize a track switch so that audio playback will not stop but | ||
414 | * the switch to next track would happen as soon as possible. | ||
415 | */ | ||
416 | void pcm_flush_audio(void) | ||
417 | { | ||
418 | if (crossfade_init || crossfade_active) | ||
419 | return ; | ||
420 | |||
421 | crossfade_mode = CFM_FLUSH; | ||
422 | crossfade_init = true; | ||
423 | } | ||
424 | |||
404 | void pcm_flush_fillpos(void) | 425 | void pcm_flush_fillpos(void) |
405 | { | 426 | { |
406 | if (audiobuffer_fillpos) { | 427 | if (audiobuffer_fillpos) { |
@@ -419,19 +440,29 @@ void pcm_flush_fillpos(void) | |||
419 | 440 | ||
420 | static void crossfade_start(void) | 441 | static void crossfade_start(void) |
421 | { | 442 | { |
422 | if (!crossfade_init) | ||
423 | return ; | ||
424 | |||
425 | crossfade_init = 0; | 443 | crossfade_init = 0; |
426 | if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 6) | 444 | if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 4) { |
445 | if (crossfade_mode == CFM_FLUSH) | ||
446 | pcm_play_stop(); | ||
427 | return ; | 447 | return ; |
428 | 448 | } | |
449 | |||
429 | pcm_flush_fillpos(); | 450 | pcm_flush_fillpos(); |
430 | pcm_boost(true); | 451 | pcm_boost(true); |
431 | crossfade_active = true; | 452 | crossfade_active = true; |
432 | crossfade_pos = audiobuffer_pos; | 453 | crossfade_pos = audiobuffer_pos; |
433 | crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - (CHUNK_SIZE * 2))/2; | 454 | |
434 | crossfade_rem = crossfade_amount; | 455 | switch (crossfade_mode) { |
456 | case CFM_CROSSFADE: | ||
457 | crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - (CHUNK_SIZE * 2))/2; | ||
458 | crossfade_rem = crossfade_amount; | ||
459 | break ; | ||
460 | |||
461 | case CFM_FLUSH: | ||
462 | crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - (CHUNK_SIZE * 2))/2; | ||
463 | crossfade_rem = crossfade_amount; | ||
464 | break ; | ||
465 | } | ||
435 | 466 | ||
436 | crossfade_pos -= crossfade_amount*2; | 467 | crossfade_pos -= crossfade_amount*2; |
437 | if (crossfade_pos < 0) | 468 | if (crossfade_pos < 0) |
@@ -441,25 +472,40 @@ static void crossfade_start(void) | |||
441 | static __inline | 472 | static __inline |
442 | int crossfade(short *buf, const short *buf2, int length) | 473 | int crossfade(short *buf, const short *buf2, int length) |
443 | { | 474 | { |
444 | int i, size; | 475 | int size, i; |
445 | int val1 = (crossfade_rem<<10)/crossfade_amount; | 476 | int val1, val2; |
446 | int val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount; | ||
447 | 477 | ||
448 | // logf("cfi: %d/%d", length, crossfade_rem); | ||
449 | size = MIN(length, crossfade_rem); | 478 | size = MIN(length, crossfade_rem); |
450 | for (i = 0; i < size; i++) { | 479 | switch (crossfade_mode) { |
451 | buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10; | 480 | case CFM_CROSSFADE: |
481 | val1 = (crossfade_rem<<10)/crossfade_amount; | ||
482 | val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount; | ||
483 | |||
484 | for (i = 0; i < size; i++) { | ||
485 | buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10; | ||
486 | } | ||
487 | break ; | ||
488 | |||
489 | case CFM_FLUSH: | ||
490 | for (i = 0; i < size; i++) { | ||
491 | buf[i] = buf2[i]; | ||
492 | } | ||
493 | //memcpy((char *)buf, (char *)buf2, size*2); | ||
494 | break ; | ||
452 | } | 495 | } |
453 | crossfade_rem -= i; | 496 | |
497 | crossfade_rem -= size; | ||
454 | if (crossfade_rem <= 0) | 498 | if (crossfade_rem <= 0) |
455 | crossfade_active = false; | 499 | crossfade_active = false; |
456 | 500 | ||
457 | return size; | 501 | return size; |
458 | } | 502 | } |
459 | 503 | ||
460 | inline static bool prepare_insert(long length) | 504 | inline static bool prepare_insert(long length) |
461 | { | 505 | { |
462 | crossfade_start(); | 506 | if (crossfade_init) |
507 | crossfade_start(); | ||
508 | |||
463 | if (audiobuffer_free < length + audiobuffer_fillpos | 509 | if (audiobuffer_free < length + audiobuffer_fillpos |
464 | + CHUNK_SIZE && !crossfade_active) { | 510 | + CHUNK_SIZE && !crossfade_active) { |
465 | pcm_boost(false); | 511 | pcm_boost(false); |
@@ -487,15 +533,12 @@ void* pcm_request_buffer(long length, long *realsize) | |||
487 | 533 | ||
488 | if (crossfade_active) { | 534 | if (crossfade_active) { |
489 | *realsize = MIN(length, PCMBUF_GUARD); | 535 | *realsize = MIN(length, PCMBUF_GUARD); |
490 | //logf("cfb:%d/%d", *realsize, length); | ||
491 | ptr = &guardbuf[0]; | 536 | ptr = &guardbuf[0]; |
492 | } else { | 537 | } else { |
493 | *realsize = MIN(length, PCMBUF_SIZE - audiobuffer_pos | 538 | *realsize = MIN(length, PCMBUF_SIZE - audiobuffer_pos |
494 | - audiobuffer_fillpos); | 539 | - audiobuffer_fillpos); |
495 | if (*realsize < length) { | 540 | if (*realsize < length) { |
496 | //logf("gbr1:%d/%d", *realsize, length); | ||
497 | *realsize += MIN((long)(length - *realsize), PCMBUF_GUARD); | 541 | *realsize += MIN((long)(length - *realsize), PCMBUF_GUARD); |
498 | //logf("gbr2:%d/%d", *realsize, length); | ||
499 | } | 542 | } |
500 | ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos]; | 543 | ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos]; |
501 | } | 544 | } |
@@ -503,17 +546,20 @@ void* pcm_request_buffer(long length, long *realsize) | |||
503 | return ptr; | 546 | return ptr; |
504 | } | 547 | } |
505 | 548 | ||
549 | bool pcm_is_crossfade_active(void) | ||
550 | { | ||
551 | return crossfade_active; | ||
552 | } | ||
553 | |||
506 | void pcm_flush_buffer(long length) | 554 | void pcm_flush_buffer(long length) |
507 | { | 555 | { |
508 | int copy_n; | 556 | int copy_n; |
509 | char *buf; | 557 | char *buf; |
510 | 558 | ||
511 | if (crossfade_active) { | 559 | if (crossfade_active) { |
512 | //logf("cfbf"); | ||
513 | buf = &guardbuf[0]; | 560 | buf = &guardbuf[0]; |
514 | length = MIN(length, PCMBUF_GUARD); | 561 | length = MIN(length, PCMBUF_GUARD); |
515 | while (length > 0 && crossfade_active) { | 562 | while (length > 0 && crossfade_active) { |
516 | //logf("cfl:%d", length); | ||
517 | copy_n = MIN(length, PCMBUF_SIZE - crossfade_pos); | 563 | copy_n = MIN(length, PCMBUF_SIZE - crossfade_pos); |
518 | copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos], | 564 | copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos], |
519 | (const short *)buf, copy_n/2); | 565 | (const short *)buf, copy_n/2); |
@@ -525,7 +571,6 @@ void pcm_flush_buffer(long length) | |||
525 | } | 571 | } |
526 | 572 | ||
527 | while (length > 0) { | 573 | while (length > 0) { |
528 | //logf("cfl2:%d", length); | ||
529 | copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos); | 574 | copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos); |
530 | memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n); | 575 | memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n); |
531 | audiobuffer_fillpos = copy_n; | 576 | audiobuffer_fillpos = copy_n; |
@@ -545,7 +590,6 @@ void pcm_flush_buffer(long length) | |||
545 | 590 | ||
546 | copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos); | 591 | copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos); |
547 | if (copy_n > 0) { | 592 | if (copy_n > 0) { |
548 | //logf("gbu:%d/%d/%d", copy_n, audiobuffer_fillpos, audiobuffer_pos); | ||
549 | audiobuffer_fillpos -= copy_n; | 593 | audiobuffer_fillpos -= copy_n; |
550 | pcm_flush_fillpos(); | 594 | pcm_flush_fillpos(); |
551 | copy_n = MIN(copy_n, PCMBUF_GUARD); | 595 | copy_n = MIN(copy_n, PCMBUF_GUARD); |
@@ -652,6 +696,7 @@ void pcm_play_start(void) | |||
652 | pcm_play_set_watermark(PCM_WATERMARK, pcm_watermark_callback); | 696 | pcm_play_set_watermark(PCM_WATERMARK, pcm_watermark_callback); |
653 | } | 697 | } |
654 | crossfade_active = false; | 698 | crossfade_active = false; |
699 | |||
655 | if(!pcm_is_playing()) | 700 | if(!pcm_is_playing()) |
656 | { | 701 | { |
657 | size = MIN(desc->size, 32768); | 702 | size = MIN(desc->size, 32768); |