summaryrefslogtreecommitdiff
path: root/apps/pcmbuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r--apps/pcmbuf.c216
1 files changed, 176 insertions, 40 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 8ab4ffaae1..6024756c74 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -38,7 +38,7 @@
38 38
39#define CHUNK_SIZE PCMBUF_GUARD 39#define CHUNK_SIZE PCMBUF_GUARD
40/* Must be a power of 2 */ 40/* Must be a power of 2 */
41#define NUM_PCM_BUFFERS 64 41#define NUM_PCM_BUFFERS 128
42#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1) 42#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
43#define PCMBUF_WATERMARK (CHUNK_SIZE * 6) 43#define PCMBUF_WATERMARK (CHUNK_SIZE * 6)
44 44
@@ -58,7 +58,6 @@ static bool crossfade_enabled;
58static bool crossfade_active; 58static bool crossfade_active;
59static bool crossfade_init; 59static bool crossfade_init;
60static int crossfade_pos; 60static int crossfade_pos;
61static int crossfade_amount;
62static int crossfade_rem; 61static int crossfade_rem;
63 62
64/* Crossfade modes. If CFM_CROSSFADE is selected, normal 63/* Crossfade modes. If CFM_CROSSFADE is selected, normal
@@ -71,6 +70,9 @@ enum {
71 CFM_FLUSH 70 CFM_FLUSH
72}; 71};
73 72
73static int crossfade_fade_in_amount;
74static int crossfade_fade_in_rem;
75
74/* Structure we can use to queue pcm chunks in memory to be played 76/* Structure we can use to queue pcm chunks in memory to be played
75 * by the driver code. */ 77 * by the driver code. */
76struct pcmbufdesc 78struct pcmbufdesc
@@ -225,7 +227,7 @@ bool pcmbuf_is_lowdata(void)
225 return false; 227 return false;
226} 228}
227 229
228bool pcmbuf_crossfade_init(int type) 230bool pcmbuf_crossfade_init(void)
229{ 231{
230 if (pcmbuf_size - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled 232 if (pcmbuf_size - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled
231 || crossfade_active || crossfade_init) { 233 || crossfade_active || crossfade_init) {
@@ -235,17 +237,8 @@ bool pcmbuf_crossfade_init(int type)
235 logf("pcmbuf_crossfade_init"); 237 logf("pcmbuf_crossfade_init");
236 pcmbuf_boost(true); 238 pcmbuf_boost(true);
237 239
238 switch (type) { 240 crossfade_mode = global_settings.crossfade_fade_out_mixmode
239 case CROSSFADE_MODE_CROSSFADE: 241 ? CFM_MIX : CFM_CROSSFADE;
240 crossfade_mode = CFM_CROSSFADE;
241 break;
242 case CROSSFADE_MODE_MIX:
243 crossfade_mode = CFM_MIX;
244 break;
245 default:
246 return false;
247 }
248
249 crossfade_init = true; 242 crossfade_init = true;
250 243
251 return true; 244 return true;
@@ -308,6 +301,9 @@ void pcmbuf_play_start(void)
308 pcm_play_data(pcmbuf_callback); 301 pcm_play_data(pcmbuf_callback);
309} 302}
310 303
304/**
305 * Commit samples waiting to the pcm buffer.
306 */
311void pcmbuf_flush_fillpos(void) 307void pcmbuf_flush_fillpos(void)
312{ 308{
313 int copy_n; 309 int copy_n;
@@ -335,9 +331,59 @@ void pcmbuf_flush_fillpos(void)
335 } 331 }
336} 332}
337 333
334/**
335 * Completely process the crossfade fade out effect with current pcm buffer.
336 */
337static void crossfade_process_buffer(
338 int fade_in_delay, int fade_out_delay, int fade_out_rem)
339{
340 int amount;
341 int pos;
342 short *buf;
343
344 /* Fade out the entire current buffer according to settings. */
345 amount = fade_out_rem;
346 pos = crossfade_pos + fade_out_delay*2;
347
348 while (fade_out_rem > 0 && crossfade_mode == CFM_CROSSFADE)
349 {
350 int blocksize = MIN(8192, fade_out_rem);
351 int factor = (fade_out_rem<<8)/amount;
352
353 /* Prevent pcmbuffer from wrapping. */
354 if (pos >= pcmbuf_size)
355 pos -= pcmbuf_size;
356 blocksize = MIN(pcmbuf_size - pos, blocksize);
357 buf = (short *)&audiobuffer[pos];
358
359 fade_out_rem -= blocksize;
360 pos += blocksize * 2;
361 while (blocksize > 0)
362 {
363 *buf = (*buf * factor) >> 8;
364 *buf++;
365 blocksize--;
366 }
367 //yield();
368 }
369
370 /* And finally set the mixing position where we should start fading in. */
371 crossfade_rem -= fade_in_delay;
372 crossfade_pos += fade_in_delay*2;
373 if (crossfade_pos >= pcmbuf_size)
374 crossfade_pos -= pcmbuf_size;
375 logf("process done!");
376}
377
378/**
379 * Initializes crossfader, calculates all necessary parameters and
380 * performs fade-out with the pcm buffer.
381 */
338static void crossfade_start(void) 382static void crossfade_start(void)
339{ 383{
340 int bytesleft = pcmbuf_unplayed_bytes; 384 int bytesleft = pcmbuf_unplayed_bytes;
385 int fade_out_rem = 0, fade_out_delay = 0;
386 int fade_in_delay = 0;
341 387
342 crossfade_init = 0; 388 crossfade_init = 0;
343 if (bytesleft < CHUNK_SIZE * 4) { 389 if (bytesleft < CHUNK_SIZE * 4) {
@@ -356,44 +402,125 @@ static void crossfade_start(void)
356 switch (crossfade_mode) { 402 switch (crossfade_mode) {
357 case CFM_MIX: 403 case CFM_MIX:
358 case CFM_CROSSFADE: 404 case CFM_CROSSFADE:
359 crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2; 405 /* Initialize the crossfade buffer size. */
360 crossfade_rem = crossfade_amount; 406 crossfade_rem = (bytesleft - (CHUNK_SIZE * 2))/2;
407
408 /* Get fade out delay from settings. */
409 fade_out_delay = NATIVE_FREQUENCY
410 * global_settings.crossfade_fade_out_delay * 2;
411
412 /* Get fade out duration from settings. */
413 fade_out_rem = NATIVE_FREQUENCY
414 * global_settings.crossfade_fade_out_duration * 2;
415
416 /* Truncate fade out duration if necessary. */
417 if (fade_out_rem > crossfade_rem)
418 fade_out_rem = crossfade_rem;
419
420 /* We want only to modify the last part of the buffer. */
421 if (crossfade_rem > fade_out_rem + fade_out_delay)
422 crossfade_rem = fade_out_rem + fade_out_delay;
423
424 /* Get also fade in duration and delays from settings. */
425 crossfade_fade_in_rem = NATIVE_FREQUENCY
426 * global_settings.crossfade_fade_in_duration * 2;
427 crossfade_fade_in_amount = crossfade_fade_in_rem;
428
429 /* We should avoid to divide by zero. */
430 if (crossfade_fade_in_amount == 0)
431 crossfade_fade_in_amount = 1;
432
433 fade_in_delay = NATIVE_FREQUENCY
434 * global_settings.crossfade_fade_in_delay * 2;
435
436 /* Decrease the fade out delay if necessary. */
437 fade_out_delay += MIN(crossfade_rem -
438 fade_out_rem -
439 fade_out_delay, 0);
440 if (fade_out_delay < 0)
441 fade_out_delay = 0;
361 break ; 442 break ;
362 443
363 case CFM_FLUSH: 444 case CFM_FLUSH:
364 crossfade_amount = bytesleft /2; 445 crossfade_rem = bytesleft /2;
365 crossfade_rem = crossfade_amount;
366 break ; 446 break ;
367 } 447 }
368 448
369 crossfade_pos -= crossfade_amount*2; 449 crossfade_pos -= crossfade_rem*2;
370 if (crossfade_pos < 0) 450 if (crossfade_pos < 0)
371 crossfade_pos += pcmbuf_size; 451 crossfade_pos += pcmbuf_size;
452
453 if (crossfade_mode != CFM_FLUSH) {
454 /* Process the fade out part of the crossfade. */
455 crossfade_process_buffer(fade_in_delay, fade_out_delay, fade_out_rem);
456 }
457
458}
459
460/**
461 * Fades in samples passed to the function and inserts them
462 * to the pcm buffer.
463 */
464static void fade_insert(const short *inbuf, int length)
465{
466 int copy_n;
467 int factor;
468 int i, samples;
469 short *buf;
470
471 factor = ((crossfade_fade_in_amount-crossfade_fade_in_rem)
472 <<8)/crossfade_fade_in_amount;
473
474 while (audiobuffer_free < length + audiobuffer_fillpos
475 + CHUNK_SIZE)
476 {
477 pcmbuf_boost(false);
478 sleep(1);
479 }
480
481 while (length > 0) {
482 copy_n = MIN(length, pcmbuf_size - audiobuffer_pos -
483 audiobuffer_fillpos);
484 copy_n = MIN(CHUNK_SIZE - audiobuffer_fillpos, copy_n);
485
486 buf = (short *)&audiobuffer[audiobuffer_pos+audiobuffer_fillpos];
487 samples = copy_n / 2;
488 for (i = 0; i < samples; i++)
489 buf[i] = (inbuf[i] * factor) >> 8;
490
491 inbuf += samples;
492 audiobuffer_fillpos += copy_n;
493 length -= copy_n;
494
495 /* Pre-buffer to meet CHUNK_SIZE requirement */
496 if (audiobuffer_fillpos < CHUNK_SIZE && length == 0) {
497 break ;
498 }
499
500 pcmbuf_flush_fillpos();
501 }
372} 502}
373 503
504/**
505 * Fades in buf2 and mixes it with buf.
506 */
374static __inline 507static __inline
375int crossfade(short *buf, const short *buf2, int length) 508int crossfade(short *buf, const short *buf2, int length)
376{ 509{
377 int size, i; 510 int size, i;
378 int val1, val2; 511 int size_insert = 0;
512 int factor;
379 513
380 size = MIN(length, crossfade_rem); 514 size = MAX(0, MIN(length, crossfade_rem));
381 switch (crossfade_mode) { 515 switch (crossfade_mode) {
382 /* Mix two streams. */ 516 /* Fade in the current stream and mix it. */
383 case CFM_MIX: 517 case CFM_MIX:
384 /* Bias & add & clip. */
385 for (i = 0; i < size; i++) {
386 buf[i] = MIN(MAX(buf[i] + buf2[i], -32768), 32767);
387 }
388 break ;
389
390 /* Fade two streams. */
391 case CFM_CROSSFADE: 518 case CFM_CROSSFADE:
392 val1 = (crossfade_rem<<10)/crossfade_amount; 519 factor = ((crossfade_fade_in_amount-crossfade_fade_in_rem)
393 val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount; 520 <<8)/crossfade_fade_in_amount;
394 521
395 for (i = 0; i < size; i++) { 522 for (i = 0; i < size; i++) {
396 buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10; 523 buf[i] = MIN(MAX(buf[i] + ((buf2[i] * factor) >> 8), -32768), 32767);
397 } 524 }
398 break ; 525 break ;
399 526
@@ -405,12 +532,20 @@ int crossfade(short *buf, const short *buf2, int length)
405 //memcpy((char *)buf, (char *)buf2, size*2); 532 //memcpy((char *)buf, (char *)buf2, size*2);
406 break ; 533 break ;
407 } 534 }
408 535
536 crossfade_fade_in_rem = MAX(0, crossfade_fade_in_rem - size);
409 crossfade_rem -= size; 537 crossfade_rem -= size;
410 if (crossfade_rem <= 0) 538 if (crossfade_rem <= 0)
411 crossfade_active = false; 539 {
412 540 size_insert = MAX(0, MIN(crossfade_fade_in_rem, length - size));
413 return size; 541 fade_insert(&buf2[size], size_insert*2);
542 crossfade_fade_in_rem -= size_insert;
543
544 if (crossfade_fade_in_rem <= 0)
545 crossfade_active = false;
546 }
547
548 return size + size_insert;
414} 549}
415 550
416static bool prepare_insert(long length) 551static bool prepare_insert(long length)
@@ -486,13 +621,12 @@ void pcmbuf_flush_buffer(long length)
486 } 621 }
487 622
488 while (length > 0) { 623 while (length > 0) {
624 pcmbuf_flush_fillpos();
489 copy_n = MIN(length, pcmbuf_size - audiobuffer_pos); 625 copy_n = MIN(length, pcmbuf_size - audiobuffer_pos);
490 memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n); 626 memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
491 audiobuffer_fillpos = copy_n; 627 audiobuffer_fillpos = copy_n;
492 buf += copy_n; 628 buf += copy_n;
493 length -= copy_n; 629 length -= copy_n;
494 if (length > 0)
495 pcmbuf_flush_fillpos();
496 } 630 }
497 } 631 }
498 632
@@ -537,13 +671,12 @@ bool pcmbuf_insert_buffer(char *buf, long length)
537 } 671 }
538 672
539 while (length > 0) { 673 while (length > 0) {
674 pcmbuf_flush_fillpos();
540 copy_n = MIN(length, pcmbuf_size - audiobuffer_pos); 675 copy_n = MIN(length, pcmbuf_size - audiobuffer_pos);
541 memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n); 676 memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
542 audiobuffer_fillpos = copy_n; 677 audiobuffer_fillpos = copy_n;
543 buf += copy_n; 678 buf += copy_n;
544 length -= copy_n; 679 length -= copy_n;
545 if (length > 0)
546 pcmbuf_flush_fillpos();
547 } 680 }
548 } 681 }
549 682
@@ -674,6 +807,9 @@ void pcmbuf_crossfade_enable(bool on_off)
674 807
675bool pcmbuf_is_crossfade_enabled(void) 808bool pcmbuf_is_crossfade_enabled(void)
676{ 809{
810 if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE)
811 return global_settings.playlist_shuffle;
812
677 return crossfade_enabled; 813 return crossfade_enabled;
678} 814}
679 815