1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2005-2007 Miika Pekkarinen
11 * Copyright (C) 2007-2008 Nicolas Pennequin
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
24 #include "codec_thread.h"
28 #include "buffering.h"
35 /* Define LOGF_ENABLE to enable logf output in this file */
36 /*#define LOGF_ENABLE*/
39 /* macros to enable logf for queues
40 logging on SYS_TIMEOUT can be disabled */
42 /* Define this for logf output of all queuing except SYS_TIMEOUT */
43 #define PLAYBACK_LOGQUEUES
44 /* Define this to logf SYS_TIMEOUT messages */
45 /*#define PLAYBACK_LOGQUEUES_SYS_TIMEOUT*/
48 #ifdef PLAYBACK_LOGQUEUES
49 #define LOGFQUEUE logf
51 #define LOGFQUEUE(...)
54 #ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT
55 #define LOGFQUEUE_SYS_TIMEOUT logf
57 #define LOGFQUEUE_SYS_TIMEOUT(...)
61 /* Variables are commented with the threads that use them:
62 * A=audio, C=codec, V=voice. A suffix of - indicates that
63 * the variable is read but not updated on that thread.
65 * Unless otherwise noted, the extern variables are located
69 /* Main state control */
70 volatile bool audio_codec_loaded SHAREDBSS_ATTR
= false; /* Codec loaded? (C/A-) */
72 extern struct mp3entry
*thistrack_id3
, /* the currently playing track */
73 *othertrack_id3
; /* prev track during track-change-transition, or end of playlist,
74 * next track otherwise */
76 /* Track change controls */
77 extern bool automatic_skip
; /* Who initiated in-progress skip? (C/A-) */
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).
82 static bool codec_requested_stop
= false;
84 extern struct event_queue audio_queue
;
85 extern struct event_queue codec_queue
;
87 extern struct codec_api ci
; /* from codecs.c */
90 unsigned int codec_thread_id
; /* For modifying thread priority later.
91 Used by playback.c and pcmbuf.c */
92 static struct queue_sender_list codec_queue_sender_list
;
93 static long codec_stack
[(DEFAULT_STACK_SIZE
+ 0x2000)/sizeof(long)]
95 static const char codec_thread_name
[] = "codec";
97 /* function prototypes */
98 static bool codec_load_next_track(void);
101 /**************************************/
103 /** misc external functions */
105 int get_codec_base_type(int type
)
117 const char *get_codec_filename(int cod_spec
)
121 #ifdef HAVE_RECORDING
122 /* Can choose decoder or encoder if one available */
123 int type
= cod_spec
& CODEC_TYPE_MASK
;
124 int afmt
= cod_spec
& CODEC_AFMT_MASK
;
126 if ((unsigned)afmt
>= AFMT_NUM_CODECS
)
127 type
= AFMT_UNKNOWN
| (type
& CODEC_TYPE_MASK
);
129 fname
= (type
== CODEC_TYPE_ENCODER
) ?
130 audio_formats
[afmt
].codec_enc_root_fn
:
131 audio_formats
[afmt
].codec_root_fn
;
134 (type
== CODEC_TYPE_ENCODER
) ? "Encoder" : "Decoder",
135 afmt
, fname
? fname
: "<unknown>");
136 #else /* !HAVE_RECORDING */
138 if ((unsigned)cod_spec
>= AFMT_NUM_CODECS
)
139 cod_spec
= AFMT_UNKNOWN
;
140 fname
= audio_formats
[cod_spec
].codec_root_fn
;
141 logf("Codec: %d - %s", cod_spec
, fname
? fname
: "<unknown>");
142 #endif /* HAVE_RECORDING */
145 } /* get_codec_filename */
147 /* Borrow the codec thread and return the ID */
148 void codec_thread_do_callback(void (*fn
)(void), unsigned int *id
)
150 /* Set id before telling thread to call something; it may be
151 * needed before this function returns. */
153 *id
= codec_thread_id
;
155 /* Codec thread will signal just before entering callback */
156 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
157 queue_send(&codec_queue
, Q_CODEC_DO_CALLBACK
, (intptr_t)fn
);
161 /** codec API callbacks */
163 static void* codec_get_buffer(size_t *size
)
165 if (codec_size
>= CODEC_SIZE
)
167 *size
= CODEC_SIZE
- codec_size
;
168 return &codecbuf
[codec_size
];
171 static bool codec_pcmbuf_insert_callback(
172 const void *ch1
, const void *ch2
, int count
)
174 const char *src
[2] = { ch1
, ch2
};
178 int out_count
= dsp_output_count(ci
.dsp
, count
);
182 /* Prevent audio from a previous track from playing */
183 if (ci
.new_track
|| ci
.stop_codec
)
186 while ((dest
= pcmbuf_request_buffer(&out_count
)) == NULL
)
190 if (ci
.seek_time
|| ci
.new_track
|| ci
.stop_codec
)
194 /* Get the real input_size for output_size bytes, guarding
195 * against resampling buffer overflows. */
196 inp_count
= dsp_input_count(ci
.dsp
, out_count
);
201 /* Input size has grown, no error, just don't write more than length */
202 if (inp_count
> count
)
205 out_count
= dsp_process(ci
.dsp
, dest
, src
, inp_count
);
210 pcmbuf_write_complete(out_count
);
216 } /* codec_pcmbuf_insert_callback */
218 static void codec_set_elapsed_callback(unsigned int value
)
223 #ifdef AB_REPEAT_ENABLE
224 ab_position_report(value
);
227 unsigned int latency
= pcmbuf_get_latency();
229 thistrack_id3
->elapsed
= 0;
232 unsigned int elapsed
= value
- latency
;
233 if (elapsed
> thistrack_id3
->elapsed
||
234 elapsed
< thistrack_id3
->elapsed
- 2)
236 thistrack_id3
->elapsed
= elapsed
;
241 static void codec_set_offset_callback(size_t value
)
246 unsigned int latency
= pcmbuf_get_latency() * thistrack_id3
->bitrate
/ 8;
248 thistrack_id3
->offset
= 0;
250 thistrack_id3
->offset
= value
- latency
;
253 /* helper function, not a callback */
254 static void codec_advance_buffer_counters(size_t amount
)
256 bufadvance(get_audio_hid(), amount
);
260 /* copy up-to size bytes into ptr and return the actual size copied */
261 static size_t codec_filebuf_callback(void *ptr
, size_t size
)
265 if (ci
.stop_codec
|| !audio_is_playing())
268 copy_n
= bufread(get_audio_hid(), size
, ptr
);
270 /* Nothing requested OR nothing left */
274 /* Update read and other position pointers */
275 codec_advance_buffer_counters(copy_n
);
277 /* Return the actual amount of data copied to the buffer */
279 } /* codec_filebuf_callback */
281 static void* codec_request_buffer_callback(size_t *realsize
, size_t reqsize
)
283 size_t copy_n
= reqsize
;
287 if (!audio_is_playing())
293 ret
= bufgetdata(get_audio_hid(), reqsize
, &ptr
);
295 copy_n
= MIN((size_t)ret
, reqsize
);
306 } /* codec_request_buffer_callback */
308 static void codec_advance_buffer_callback(size_t amount
)
310 codec_advance_buffer_counters(amount
);
311 codec_set_offset_callback(ci
.curpos
);
314 static void codec_advance_buffer_loc_callback(void *ptr
)
316 size_t amount
= buf_get_offset(get_audio_hid(), ptr
);
317 codec_advance_buffer_callback(amount
);
320 static bool codec_seek_buffer_callback(size_t newpos
)
322 logf("codec_seek_buffer_callback");
324 int ret
= bufseek(get_audio_hid(), newpos
);
334 static void codec_seek_complete_callback(void)
336 logf("seek_complete");
337 /* If seeking-while-playing, pcm_is_paused() is true.
338 * If seeking-while-paused, audio_is_paused() is true.
339 * A seamless seek skips this section. */
340 if (pcm_is_paused() || audio_is_paused())
342 /* Clear the buffer */
344 dsp_configure(ci
.dsp
, DSP_FLUSH
, 0);
346 /* If seeking-while-playing, resume pcm playback */
347 if (!audio_is_paused())
353 static void codec_discard_codec_callback(void)
355 int *codec_hid
= get_codec_hid();
358 bufclose(*codec_hid
);
363 static bool codec_request_next_track_callback(void)
367 if (ci
.stop_codec
|| !audio_is_playing())
370 prev_codectype
= get_codec_base_type(thistrack_id3
->codectype
);
371 if (!codec_load_next_track())
374 /* Seek to the beginning of the new track because if the struct
375 mp3entry was buffered, "elapsed" might not be zero (if the track has
376 been played already but not unbuffered) */
377 codec_seek_buffer_callback(thistrack_id3
->first_frame_offset
);
378 /* Check if the next codec is the same file. */
379 if (prev_codectype
== get_codec_base_type(thistrack_id3
->codectype
))
381 logf("New track loaded");
382 codec_discard_codec_callback();
387 logf("New codec:%d/%d", thistrack_id3
->codectype
, prev_codectype
);
392 static void codec_configure_callback(int setting
, intptr_t value
)
394 if (!dsp_configure(ci
.dsp
, setting
, value
))
395 { logf("Illegal key:%d", setting
); }
398 /* Initialize codec API */
399 void codec_init_codec_api(void)
401 ci
.dsp
= (struct dsp_config
*)dsp_configure(NULL
, DSP_MYDSP
,
403 ci
.codec_get_buffer
= codec_get_buffer
;
404 ci
.pcmbuf_insert
= codec_pcmbuf_insert_callback
;
405 ci
.set_elapsed
= codec_set_elapsed_callback
;
406 ci
.read_filebuf
= codec_filebuf_callback
;
407 ci
.request_buffer
= codec_request_buffer_callback
;
408 ci
.advance_buffer
= codec_advance_buffer_callback
;
409 ci
.advance_buffer_loc
= codec_advance_buffer_loc_callback
;
410 ci
.seek_buffer
= codec_seek_buffer_callback
;
411 ci
.seek_complete
= codec_seek_complete_callback
;
412 ci
.request_next_track
= codec_request_next_track_callback
;
413 ci
.discard_codec
= codec_discard_codec_callback
;
414 ci
.set_offset
= codec_set_offset_callback
;
415 ci
.configure
= codec_configure_callback
;
419 /** pcmbuf track change callbacks */
421 /* Between the codec and PCM track change, we need to keep updating the
422 "elapsed" value of the previous (to the codec, but current to the
423 user/PCM/WPS) track, so that the progressbar reaches the end.
424 During that transition, the WPS will display prevtrack_id3. */
425 static void codec_pcmbuf_position_callback(size_t size
) ICODE_ATTR
;
426 static void codec_pcmbuf_position_callback(size_t size
)
428 /* This is called from an ISR, so be quick */
429 unsigned int time
= size
* 1000 / 4 / NATIVE_FREQUENCY
+
430 othertrack_id3
->elapsed
;
432 if (time
>= othertrack_id3
->length
)
434 pcmbuf_set_position_callback(NULL
);
435 othertrack_id3
->elapsed
= othertrack_id3
->length
;
438 othertrack_id3
->elapsed
= time
;
441 static void codec_pcmbuf_track_changed_callback(void)
443 LOGFQUEUE("codec > pcmbuf/audio Q_AUDIO_TRACK_CHANGED");
444 pcmbuf_set_position_callback(NULL
);
445 audio_post_track_change();
449 /** track change functions */
451 static inline void codec_gapless_track_change(void)
453 /* callback keeps the progress bar moving while the pcmbuf empties */
454 pcmbuf_set_position_callback(codec_pcmbuf_position_callback
);
455 /* set the pcmbuf callback for when the track really changes */
456 pcmbuf_set_event_handler(codec_pcmbuf_track_changed_callback
);
459 static inline void codec_crossfade_track_change(void)
461 /* Initiate automatic crossfade mode */
462 pcmbuf_crossfade_init(false);
463 /* Notify the wps that the track change starts now */
464 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
465 queue_post(&audio_queue
, Q_AUDIO_TRACK_CHANGED
, 0);
468 static void codec_track_skip_done(bool was_manual
)
470 /* Manual track change (always crossfade or flush audio). */
473 pcmbuf_crossfade_init(true);
474 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
475 queue_post(&audio_queue
, Q_AUDIO_TRACK_CHANGED
, 0);
477 /* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
478 else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
479 && global_settings
.crossfade
!= CROSSFADE_ENABLE_TRACKSKIP
)
481 if (global_settings
.crossfade
== CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP
)
483 if (global_settings
.playlist_shuffle
)
484 /* shuffle mode is on, so crossfade: */
485 codec_crossfade_track_change();
487 /* shuffle mode is off, so do a gapless track change */
488 codec_gapless_track_change();
491 /* normal crossfade: */
492 codec_crossfade_track_change();
495 /* normal gapless playback. */
496 codec_gapless_track_change();
499 static bool codec_load_next_track(void)
501 intptr_t result
= Q_CODEC_REQUEST_FAILED
;
503 audio_set_prev_elapsed(thistrack_id3
->elapsed
);
505 #ifdef AB_REPEAT_ENABLE
506 ab_end_of_track_report();
509 logf("Request new track");
511 if (ci
.new_track
== 0)
514 automatic_skip
= true;
520 LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK");
521 result
= queue_send(&audio_queue
, Q_AUDIO_CHECK_NEW_TRACK
, 0);
526 case Q_CODEC_REQUEST_COMPLETE
:
527 LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
528 codec_track_skip_done(!automatic_skip
);
531 case Q_CODEC_REQUEST_FAILED
:
532 LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED");
534 ci
.stop_codec
= true;
535 codec_requested_stop
= true;
539 LOGFQUEUE("codec |< default");
540 ci
.stop_codec
= true;
541 codec_requested_stop
= true;
547 static void codec_thread(void)
549 struct queue_event ev
;
555 if (!pcmbuf_is_crossfade_active()) {
559 queue_wait(&codec_queue
, &ev
);
560 codec_requested_stop
= false;
563 case Q_CODEC_LOAD_DISK
:
564 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
565 queue_reply(&codec_queue
, 1);
566 audio_codec_loaded
= true;
567 ci
.stop_codec
= false;
568 status
= codec_load_file((const char *)ev
.data
, &ci
);
569 LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev
.data
, status
);
573 LOGFQUEUE("codec < Q_CODEC_LOAD");
574 if (*get_codec_hid() < 0) {
575 logf("Codec slot is empty!");
576 /* Wait for the pcm buffer to go empty */
577 while (pcm_is_playing())
579 /* This must be set to prevent an infinite loop */
580 ci
.stop_codec
= true;
581 LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
582 queue_post(&codec_queue
, Q_AUDIO_PLAY
, 0);
586 audio_codec_loaded
= true;
587 ci
.stop_codec
= false;
588 status
= codec_load_buf(*get_codec_hid(), &ci
);
589 LOGFQUEUE("codec_load_buf %d\n", status
);
592 case Q_CODEC_DO_CALLBACK
:
593 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
594 queue_reply(&codec_queue
, 1);
595 if ((void*)ev
.data
!= NULL
)
597 cpucache_invalidate();
598 ((void (*)(void))ev
.data
)();
603 #ifdef AUDIO_HAVE_RECORDING
604 case Q_ENCODER_LOAD_DISK
:
605 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
606 audio_codec_loaded
= false; /* Not audio codec! */
607 logf("loading encoder");
608 ci
.stop_encoder
= false;
609 status
= codec_load_file((const char *)ev
.data
, &ci
);
610 logf("encoder stopped");
612 #endif /* AUDIO_HAVE_RECORDING */
615 LOGFQUEUE("codec < default");
618 if (audio_codec_loaded
)
623 if (!audio_is_playing())
627 audio_codec_loaded
= false;
631 case Q_CODEC_LOAD_DISK
:
633 LOGFQUEUE("codec < Q_CODEC_LOAD");
634 if (audio_is_playing())
636 if (ci
.new_track
|| status
!= CODEC_OK
)
640 logf("Codec failure, %d %d", ci
.new_track
, status
);
641 splash(HZ
*2, "Codec failure");
644 if (!codec_load_next_track())
646 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
647 /* End of playlist */
648 queue_post(&audio_queue
, Q_AUDIO_STOP
, 0);
654 logf("Codec finished");
657 /* Wait for the audio to stop playing before
658 * triggering the WPS exit */
659 while(pcm_is_playing())
661 /* There has been one too many struct pointer swaps by now
662 * so even though it says othertrack_id3, its the correct one! */
663 othertrack_id3
->elapsed
=
664 othertrack_id3
->length
- pcmbuf_get_latency();
668 if (codec_requested_stop
)
670 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
671 queue_post(&audio_queue
, Q_AUDIO_STOP
, 0);
677 if (*get_codec_hid() >= 0)
679 LOGFQUEUE("codec > codec Q_CODEC_LOAD");
680 queue_post(&codec_queue
, Q_CODEC_LOAD
, 0);
684 const char *codec_fn
=
685 get_codec_filename(thistrack_id3
->codectype
);
688 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
689 queue_post(&codec_queue
, Q_CODEC_LOAD_DISK
,
696 #ifdef AUDIO_HAVE_RECORDING
697 case Q_ENCODER_LOAD_DISK
:
698 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
700 if (status
== CODEC_OK
)
703 logf("Encoder failure");
704 splash(HZ
*2, "Encoder failure");
706 if (ci
.enc_codec_loaded
< 0)
709 logf("Encoder failed to load");
710 ci
.enc_codec_loaded
= -1;
712 #endif /* AUDIO_HAVE_RECORDING */
715 LOGFQUEUE("codec < default");
721 void make_codec_thread(void)
723 codec_thread_id
= create_thread(
724 codec_thread
, codec_stack
, sizeof(codec_stack
),
725 CREATE_THREAD_FROZEN
,
726 codec_thread_name
IF_PRIO(, PRIORITY_PLAYBACK
)
728 queue_enable_queue_send(&codec_queue
, &codec_queue_sender_list
,