summaryrefslogtreecommitdiff
path: root/apps/codec_thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codec_thread.c')
-rw-r--r--apps/codec_thread.c454
1 files changed, 204 insertions, 250 deletions
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index 03ab5622e2..f166f2ba18 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -19,10 +19,10 @@
19 * KIND, either express or implied. 19 * KIND, either express or implied.
20 * 20 *
21 ****************************************************************************/ 21 ****************************************************************************/
22 22#include "config.h"
23#include "system.h"
23#include "playback.h" 24#include "playback.h"
24#include "codec_thread.h" 25#include "codec_thread.h"
25#include "system.h"
26#include "kernel.h" 26#include "kernel.h"
27#include "codecs.h" 27#include "codecs.h"
28#include "buffering.h" 28#include "buffering.h"
@@ -67,36 +67,38 @@
67 */ 67 */
68 68
69/* Main state control */ 69/* Main state control */
70volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */ 70
71/* Type of codec loaded? (C/A) */
72static int current_codectype SHAREDBSS_ATTR = AFMT_UNKNOWN;
71 73
72extern struct mp3entry *thistrack_id3, /* the currently playing track */ 74extern struct mp3entry *thistrack_id3, /* the currently playing track */
73 *othertrack_id3; /* prev track during track-change-transition, or end of playlist, 75 *othertrack_id3; /* prev track during track-change-transition, or end of playlist,
74 * next track otherwise */ 76 * next track otherwise */
75 77
76/* Track change controls */ 78/* Track change controls */
77extern bool automatic_skip; /* Who initiated in-progress skip? (C/A-) */
78
79/* Set to true if the codec thread should send an audio stop request
80 * (typically because the end of the playlist has been reached).
81 */
82static bool codec_requested_stop = false;
83
84extern struct event_queue audio_queue SHAREDBSS_ATTR; 79extern struct event_queue audio_queue SHAREDBSS_ATTR;
85extern struct event_queue codec_queue SHAREDBSS_ATTR; 80
86 81
87extern struct codec_api ci; /* from codecs.c */ 82extern struct codec_api ci; /* from codecs.c */
88 83
89/* Codec thread */ 84/* Codec thread */
90unsigned int codec_thread_id; /* For modifying thread priority later. 85static unsigned int codec_thread_id; /* For modifying thread priority later */
91 Used by playback.c and pcmbuf.c */ 86static struct event_queue codec_queue SHAREDBSS_ATTR;
92static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR; 87static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR;
93static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] 88static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
94IBSS_ATTR; 89 IBSS_ATTR;
95static const char codec_thread_name[] = "codec"; 90static const char codec_thread_name[] = "codec";
96 91
97/* function prototypes */ 92/* static routines */
98static bool codec_load_next_track(void); 93static void codec_queue_ack(intptr_t ackme)
94{
95 queue_reply(&codec_queue, ackme);
96}
99 97
98static intptr_t codec_queue_send(long id, intptr_t data)
99{
100 return queue_send(&codec_queue, id, data);
101}
100 102
101/**************************************/ 103/**************************************/
102 104
@@ -183,7 +185,7 @@ void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
183 185
184 /* Codec thread will signal just before entering callback */ 186 /* Codec thread will signal just before entering callback */
185 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK"); 187 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
186 queue_send(&codec_queue, Q_CODEC_DO_CALLBACK, (intptr_t)fn); 188 codec_queue_send(Q_CODEC_DO_CALLBACK, (intptr_t)fn);
187} 189}
188 190
189 191
@@ -289,7 +291,7 @@ static size_t codec_filebuf_callback(void *ptr, size_t size)
289{ 291{
290 ssize_t copy_n; 292 ssize_t copy_n;
291 293
292 if (ci.stop_codec || !(audio_status() & AUDIO_STATUS_PLAY)) 294 if (ci.stop_codec)
293 return 0; 295 return 0;
294 296
295 copy_n = bufread(get_audio_hid(), size, ptr); 297 copy_n = bufread(get_audio_hid(), size, ptr);
@@ -311,24 +313,16 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
311 ssize_t ret; 313 ssize_t ret;
312 void *ptr; 314 void *ptr;
313 315
314 if (!(audio_status() & AUDIO_STATUS_PLAY))
315 {
316 *realsize = 0;
317 return NULL;
318 }
319
320 ret = bufgetdata(get_audio_hid(), reqsize, &ptr); 316 ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
321 if (ret >= 0) 317 if (ret >= 0)
322 copy_n = MIN((size_t)ret, reqsize); 318 copy_n = MIN((size_t)ret, reqsize);
319 else
320 copy_n = 0;
323 321
324 if (copy_n == 0) 322 if (copy_n == 0)
325 { 323 ptr = NULL;
326 *realsize = 0;
327 return NULL;
328 }
329 324
330 *realsize = copy_n; 325 *realsize = copy_n;
331
332 return ptr; 326 return ptr;
333} /* codec_request_buffer_callback */ 327} /* codec_request_buffer_callback */
334 328
@@ -360,61 +354,71 @@ static bool codec_seek_buffer_callback(size_t newpos)
360 354
361static void codec_seek_complete_callback(void) 355static void codec_seek_complete_callback(void)
362{ 356{
357 struct queue_event ev;
358
363 logf("seek_complete"); 359 logf("seek_complete");
364 /* If seeking-while-playing, pcm_is_paused() is true.
365 * If seeking-while-paused, audio_status PAUSE is true.
366 * A seamless seek skips this section. */
367 bool audio_paused = audio_status() & AUDIO_STATUS_PAUSE;
368 if (pcm_is_paused() || audio_paused)
369 {
370 /* Clear the buffer */
371 pcmbuf_play_stop();
372 dsp_configure(ci.dsp, DSP_FLUSH, 0);
373 360
374 /* If seeking-while-playing, resume pcm playback */ 361 /* Clear DSP */
375 if (!audio_paused) 362 dsp_configure(ci.dsp, DSP_FLUSH, 0);
376 pcmbuf_pause(false);
377 }
378 ci.seek_time = 0;
379}
380 363
381static void codec_discard_codec_callback(void) 364 /* Post notification to audio thread */
382{ 365 LOGFQUEUE("audio > Q_AUDIO_SEEK_COMPLETE");
383 int *codec_hid = get_codec_hid(); 366 queue_post(&audio_queue, Q_AUDIO_SEEK_COMPLETE, 0);
384 if (*codec_hid >= 0) 367
385 { 368 /* Wait for ACK */
386 bufclose(*codec_hid); 369 queue_wait(&codec_queue, &ev);
387 *codec_hid = -1; 370
388 } 371 /* ACK back in context */
372 codec_queue_ack(Q_AUDIO_SEEK_COMPLETE);
389} 373}
390 374
391static bool codec_request_next_track_callback(void) 375static bool codec_request_next_track_callback(void)
392{ 376{
393 int prev_codectype; 377 struct queue_event ev;
394 378
395 if (ci.stop_codec || !(audio_status() & AUDIO_STATUS_PLAY)) 379 logf("Request new track");
396 return false;
397 380
398 prev_codectype = get_codec_base_type(thistrack_id3->codectype); 381 audio_set_prev_elapsed(thistrack_id3->elapsed);
399 if (!codec_load_next_track()) 382
383#ifdef AB_REPEAT_ENABLE
384 ab_end_of_track_report();
385#endif
386
387 if (ci.stop_codec)
388 {
389 /* Handle ACK in outer loop */
390 LOGFQUEUE("codec: already stopping");
400 return false; 391 return false;
392 }
393
394 trigger_cpu_boost();
401 395
402 /* Seek to the beginning of the new track because if the struct 396 /* Post request to audio thread */
403 mp3entry was buffered, "elapsed" might not be zero (if the track has 397 LOGFQUEUE("codec > audio Q_AUDIO_CHECK_NEW_TRACK");
404 been played already but not unbuffered) */ 398 queue_post(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
405 codec_seek_buffer_callback(thistrack_id3->first_frame_offset); 399
406 /* Check if the next codec is the same file. */ 400 /* Wait for ACK */
407 if (prev_codectype == get_codec_base_type(thistrack_id3->codectype)) 401 queue_wait(&codec_queue, &ev);
402
403 if (ev.data == Q_CODEC_REQUEST_COMPLETE)
408 { 404 {
409 logf("New track loaded"); 405 /* Seek to the beginning of the new track because if the struct
410 codec_discard_codec_callback(); 406 mp3entry was buffered, "elapsed" might not be zero (if the track has
411 return true; 407 been played already but not unbuffered) */
408 codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
412 } 409 }
413 else 410
411 /* ACK back in context */
412 codec_queue_ack(Q_AUDIO_CHECK_NEW_TRACK);
413
414 if (ev.data != Q_CODEC_REQUEST_COMPLETE || ci.stop_codec)
414 { 415 {
415 logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype); 416 LOGFQUEUE("codec <= request failed (%d)", ev.data);
416 return false; 417 return false;
417 } 418 }
419
420 LOGFQUEUE("codec <= Q_CODEC_REQEST_COMPLETE");
421 return true;
418} 422}
419 423
420static void codec_configure_callback(int setting, intptr_t value) 424static void codec_configure_callback(int setting, intptr_t value)
@@ -438,7 +442,6 @@ void codec_init_codec_api(void)
438 ci.seek_buffer = codec_seek_buffer_callback; 442 ci.seek_buffer = codec_seek_buffer_callback;
439 ci.seek_complete = codec_seek_complete_callback; 443 ci.seek_complete = codec_seek_complete_callback;
440 ci.request_next_track = codec_request_next_track_callback; 444 ci.request_next_track = codec_request_next_track_callback;
441 ci.discard_codec = codec_discard_codec_callback;
442 ci.set_offset = codec_set_offset_callback; 445 ci.set_offset = codec_set_offset_callback;
443 ci.configure = codec_configure_callback; 446 ci.configure = codec_configure_callback;
444} 447}
@@ -446,61 +449,18 @@ void codec_init_codec_api(void)
446 449
447/* track change */ 450/* track change */
448 451
449static bool codec_load_next_track(void)
450{
451 intptr_t result = Q_CODEC_REQUEST_FAILED;
452
453 audio_set_prev_elapsed(thistrack_id3->elapsed);
454
455#ifdef AB_REPEAT_ENABLE
456 ab_end_of_track_report();
457#endif
458
459 logf("Request new track");
460
461 if (ci.new_track == 0)
462 {
463 ci.new_track++;
464 automatic_skip = true;
465 }
466
467 if (!ci.stop_codec)
468 {
469 trigger_cpu_boost();
470 LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK");
471 result = queue_send(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
472 }
473
474 switch (result)
475 {
476 case Q_CODEC_REQUEST_COMPLETE:
477 LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
478 pcmbuf_start_track_change(automatic_skip);
479 return true;
480
481 case Q_CODEC_REQUEST_FAILED:
482 LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED");
483 ci.new_track = 0;
484 ci.stop_codec = true;
485 codec_requested_stop = true;
486 return false;
487
488 default:
489 LOGFQUEUE("codec |< default");
490 ci.stop_codec = true;
491 codec_requested_stop = true;
492 return false;
493 }
494}
495
496/** CODEC THREAD */ 452/** CODEC THREAD */
497static void codec_thread(void) 453static void codec_thread(void)
498{ 454{
499 struct queue_event ev; 455 struct queue_event ev;
500 int status;
501 456
502 while (1) { 457
503 status = 0; 458 while (1)
459 {
460 int status = CODEC_OK;
461 void *handle = NULL;
462 int hid;
463 const char *codec_fn;
504 464
505#ifdef HAVE_CROSSFADE 465#ifdef HAVE_CROSSFADE
506 if (!pcmbuf_is_crossfade_active()) 466 if (!pcmbuf_is_crossfade_active())
@@ -508,171 +468,89 @@ static void codec_thread(void)
508 { 468 {
509 cancel_cpu_boost(); 469 cancel_cpu_boost();
510 } 470 }
511 471
512 queue_wait(&codec_queue, &ev); 472 queue_wait(&codec_queue, &ev);
513 codec_requested_stop = false;
514 473
515 switch (ev.id) { 474 switch (ev.id)
475 {
516 case Q_CODEC_LOAD_DISK: 476 case Q_CODEC_LOAD_DISK:
517 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); 477 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
518 queue_reply(&codec_queue, 1); 478 codec_fn = get_codec_filename(ev.data);
519 audio_codec_loaded = true; 479 if (!codec_fn)
520 ci.stop_codec = false; 480 break;
521 status = codec_load_file((const char *)ev.data, &ci); 481#ifdef AUDIO_HAVE_RECORDING
522 LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev.data, status); 482 if (ev.data & CODEC_TYPE_ENCODER)
483 {
484 ev.id = Q_ENCODER_LOAD_DISK;
485 handle = codec_load_file(codec_fn, &ci);
486 if (handle)
487 codec_queue_ack(Q_ENCODER_LOAD_DISK);
488 }
489 else
490#endif
491 {
492 codec_queue_ack(Q_CODEC_LOAD_DISK);
493 handle = codec_load_file(codec_fn, &ci);
494 }
523 break; 495 break;
524 496
525 case Q_CODEC_LOAD: 497 case Q_CODEC_LOAD:
526 LOGFQUEUE("codec < Q_CODEC_LOAD"); 498 LOGFQUEUE("codec < Q_CODEC_LOAD");
527 if (*get_codec_hid() < 0) { 499 codec_queue_ack(Q_CODEC_LOAD);
528 logf("Codec slot is empty!"); 500 hid = (int)ev.data;
529 /* Wait for the pcm buffer to go empty */ 501 handle = codec_load_buf(hid, &ci);
530 while (pcm_is_playing()) 502 bufclose(hid);
531 yield();
532 /* This must be set to prevent an infinite loop */
533 ci.stop_codec = true;
534 LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
535 queue_post(&codec_queue, Q_AUDIO_PLAY, 0);
536 break;
537 }
538
539 audio_codec_loaded = true;
540 ci.stop_codec = false;
541 status = codec_load_buf(*get_codec_hid(), &ci);
542 LOGFQUEUE("codec_load_buf %d\n", status);
543 break; 503 break;
544 504
545 case Q_CODEC_DO_CALLBACK: 505 case Q_CODEC_DO_CALLBACK:
546 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK"); 506 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
547 queue_reply(&codec_queue, 1); 507 codec_queue_ack(Q_CODEC_DO_CALLBACK);
548 if ((void*)ev.data != NULL) 508 if ((void*)ev.data != NULL)
549 { 509 {
550 cpucache_invalidate(); 510 cpucache_commit_discard();
551 ((void (*)(void))ev.data)(); 511 ((void (*)(void))ev.data)();
552 cpucache_flush(); 512 cpucache_commit();
553 } 513 }
554 break; 514 break;
555 515
556#ifdef AUDIO_HAVE_RECORDING
557 case Q_ENCODER_LOAD_DISK:
558 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
559 audio_codec_loaded = false; /* Not audio codec! */
560 logf("loading encoder");
561 ci.stop_encoder = false;
562 status = codec_load_file((const char *)ev.data, &ci);
563 logf("encoder stopped");
564 break;
565#endif /* AUDIO_HAVE_RECORDING */
566
567 default: 516 default:
568 LOGFQUEUE("codec < default"); 517 LOGFQUEUE("codec < default : %ld", ev.id);
569 } 518 }
570 519
571 if (audio_codec_loaded) 520 if (handle)
572 { 521 {
522 /* Codec loaded - call the entrypoint */
523 yield();
524 logf("codec running");
525 status = codec_begin(handle);
526 logf("codec stopped");
527 codec_close(handle);
528 current_codectype = AFMT_UNKNOWN;
529
573 if (ci.stop_codec) 530 if (ci.stop_codec)
574 {
575 status = CODEC_OK; 531 status = CODEC_OK;
576 if (!(audio_status() & AUDIO_STATUS_PLAY))
577 pcmbuf_play_stop();
578
579 }
580 audio_codec_loaded = false;
581 } 532 }
582 533
583 switch (ev.id) { 534 switch (ev.id)
584 case Q_CODEC_LOAD_DISK: 535 {
585 case Q_CODEC_LOAD:
586 LOGFQUEUE("codec < Q_CODEC_LOAD");
587 if (audio_status() & AUDIO_STATUS_PLAY)
588 {
589 if (ci.new_track || status != CODEC_OK)
590 {
591 if (!ci.new_track)
592 {
593 logf("Codec failure, %d %d", ci.new_track, status);
594 splash(HZ*2, "Codec failure");
595 }
596
597 if (!codec_load_next_track())
598 {
599 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
600 /* End of playlist */
601 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
602 break;
603 }
604 }
605 else
606 {
607 logf("Codec finished");
608 if (ci.stop_codec)
609 {
610 /* Wait for the audio to stop playing before
611 * triggering the WPS exit */
612 while(pcm_is_playing())
613 {
614 /* There has been one too many struct pointer swaps by now
615 * so even though it says othertrack_id3, its the correct one! */
616 othertrack_id3->elapsed =
617 othertrack_id3->length - pcmbuf_get_latency();
618 sleep(1);
619 }
620
621 if (codec_requested_stop)
622 {
623 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
624 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
625 }
626 break;
627 }
628 }
629
630 if (*get_codec_hid() >= 0)
631 {
632 LOGFQUEUE("codec > codec Q_CODEC_LOAD");
633 queue_post(&codec_queue, Q_CODEC_LOAD, 0);
634 }
635 else
636 {
637 const char *codec_fn =
638 get_codec_filename(thistrack_id3->codectype);
639 if (codec_fn)
640 {
641 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
642 queue_post(&codec_queue, Q_CODEC_LOAD_DISK,
643 (intptr_t)codec_fn);
644 }
645 }
646 }
647 break;
648
649#ifdef AUDIO_HAVE_RECORDING 536#ifdef AUDIO_HAVE_RECORDING
650 case Q_ENCODER_LOAD_DISK: 537 case Q_ENCODER_LOAD_DISK:
651 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK"); 538#endif
652 539 case Q_CODEC_LOAD_DISK:
653 if (status == CODEC_OK) 540 case Q_CODEC_LOAD:
654 break; 541 /* Notify about the status */
655 542 if (!handle)
656 logf("Encoder failure"); 543 status = CODEC_ERROR;
657 splash(HZ*2, "Encoder failure"); 544 LOGFQUEUE("codec > audio notify status: %d", status);
658 545 queue_post(&audio_queue, ev.id, status);
659 if (ci.enc_codec_loaded < 0)
660 break;
661
662 logf("Encoder failed to load");
663 ci.enc_codec_loaded = -1;
664 break; 546 break;
665#endif /* AUDIO_HAVE_RECORDING */ 547 }
666
667 default:
668 LOGFQUEUE("codec < default");
669
670 } /* end switch */
671 } 548 }
672} 549}
673 550
674void make_codec_thread(void) 551void make_codec_thread(void)
675{ 552{
553 queue_init(&codec_queue, false);
676 codec_thread_id = create_thread( 554 codec_thread_id = create_thread(
677 codec_thread, codec_stack, sizeof(codec_stack), 555 codec_thread, codec_stack, sizeof(codec_stack),
678 CREATE_THREAD_FROZEN, 556 CREATE_THREAD_FROZEN,
@@ -681,3 +559,79 @@ void make_codec_thread(void)
681 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list, 559 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
682 codec_thread_id); 560 codec_thread_id);
683} 561}
562
563void codec_thread_resume(void)
564{
565 thread_thaw(codec_thread_id);
566}
567
568bool is_codec_thread(void)
569{
570 return thread_get_current() == codec_thread_id;
571}
572
573#ifdef HAVE_PRIORITY_SCHEDULING
574int codec_thread_get_priority(void)
575{
576 return thread_get_priority(codec_thread_id);
577}
578
579int codec_thread_set_priority(int priority)
580{
581 return thread_set_priority(codec_thread_id, priority);
582}
583#endif /* HAVE_PRIORITY_SCHEDULING */
584
585/* functions for audio thread use */
586intptr_t codec_ack_msg(intptr_t data, bool stop_codec)
587{
588 intptr_t resp;
589 LOGFQUEUE("codec >| Q_CODEC_ACK: %d", data);
590 if (stop_codec)
591 ci.stop_codec = true;
592 resp = codec_queue_send(Q_CODEC_ACK, data);
593 if (stop_codec)
594 codec_stop();
595 LOGFQUEUE(" ack: %ld", resp);
596 return resp;
597}
598
599bool codec_load(int hid, int cod_spec)
600{
601 bool retval = false;
602
603 ci.stop_codec = false;
604 current_codectype = cod_spec;
605
606 if (hid >= 0)
607 {
608 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d", hid);
609 retval = codec_queue_send(Q_CODEC_LOAD, hid) != Q_NULL;
610 }
611 else
612 {
613 LOGFQUEUE("audio >| codec Q_CODEC_LOAD_DISK: %d", cod_spec);
614 retval = codec_queue_send(Q_CODEC_LOAD_DISK, cod_spec) != Q_NULL;
615 }
616
617 if (!retval)
618 {
619 ci.stop_codec = true;
620 current_codectype = AFMT_UNKNOWN;
621 }
622
623 return retval;
624}
625
626void codec_stop(void)
627{
628 ci.stop_codec = true;
629 /* Wait until it's in the main loop */
630 while (codec_ack_msg(0, false) != Q_NULL);
631 current_codectype = AFMT_UNKNOWN;
632}
633
634int codec_loaded(void)
635{
636 return current_codectype;
637}