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 void 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
);
214 } /* codec_pcmbuf_insert_callback */
216 static void codec_set_elapsed_callback(unsigned long value
)
221 #ifdef AB_REPEAT_ENABLE
222 ab_position_report(value
);
225 unsigned long latency
= pcmbuf_get_latency();
227 thistrack_id3
->elapsed
= 0;
230 unsigned long elapsed
= value
- latency
;
231 if (elapsed
> thistrack_id3
->elapsed
||
232 elapsed
< thistrack_id3
->elapsed
- 2)
234 thistrack_id3
->elapsed
= elapsed
;
239 static void codec_set_offset_callback(size_t value
)
244 unsigned long latency
= pcmbuf_get_latency() * thistrack_id3
->bitrate
/ 8;
246 thistrack_id3
->offset
= 0;
248 thistrack_id3
->offset
= value
- latency
;
251 /* helper function, not a callback */
252 static void codec_advance_buffer_counters(size_t amount
)
254 bufadvance(get_audio_hid(), amount
);
258 /* copy up-to size bytes into ptr and return the actual size copied */
259 static size_t codec_filebuf_callback(void *ptr
, size_t size
)
263 if (ci
.stop_codec
|| !(audio_status() & AUDIO_STATUS_PLAY
))
266 copy_n
= bufread(get_audio_hid(), size
, ptr
);
268 /* Nothing requested OR nothing left */
272 /* Update read and other position pointers */
273 codec_advance_buffer_counters(copy_n
);
275 /* Return the actual amount of data copied to the buffer */
277 } /* codec_filebuf_callback */
279 static void* codec_request_buffer_callback(size_t *realsize
, size_t reqsize
)
281 size_t copy_n
= reqsize
;
285 if (!(audio_status() & AUDIO_STATUS_PLAY
))
291 ret
= bufgetdata(get_audio_hid(), reqsize
, &ptr
);
293 copy_n
= MIN((size_t)ret
, reqsize
);
304 } /* codec_request_buffer_callback */
306 static void codec_advance_buffer_callback(size_t amount
)
308 codec_advance_buffer_counters(amount
);
309 codec_set_offset_callback(ci
.curpos
);
312 static void codec_advance_buffer_loc_callback(void *ptr
)
314 size_t amount
= buf_get_offset(get_audio_hid(), ptr
);
315 codec_advance_buffer_callback(amount
);
318 static bool codec_seek_buffer_callback(size_t newpos
)
320 logf("codec_seek_buffer_callback");
322 int ret
= bufseek(get_audio_hid(), newpos
);
332 static void codec_seek_complete_callback(void)
334 logf("seek_complete");
335 /* If seeking-while-playing, pcm_is_paused() is true.
336 * If seeking-while-paused, audio_status PAUSE is true.
337 * A seamless seek skips this section. */
338 bool audio_paused
= audio_status() & AUDIO_STATUS_PAUSE
;
339 if (pcm_is_paused() || audio_paused
)
341 /* Clear the buffer */
343 dsp_configure(ci
.dsp
, DSP_FLUSH
, 0);
345 /* If seeking-while-playing, resume pcm playback */
352 static void codec_discard_codec_callback(void)
354 int *codec_hid
= get_codec_hid();
357 bufclose(*codec_hid
);
362 static bool codec_request_next_track_callback(void)
366 if (ci
.stop_codec
|| !(audio_status() & AUDIO_STATUS_PLAY
))
369 prev_codectype
= get_codec_base_type(thistrack_id3
->codectype
);
370 if (!codec_load_next_track())
373 /* Seek to the beginning of the new track because if the struct
374 mp3entry was buffered, "elapsed" might not be zero (if the track has
375 been played already but not unbuffered) */
376 codec_seek_buffer_callback(thistrack_id3
->first_frame_offset
);
377 /* Check if the next codec is the same file. */
378 if (prev_codectype
== get_codec_base_type(thistrack_id3
->codectype
))
380 logf("New track loaded");
381 codec_discard_codec_callback();
386 logf("New codec:%d/%d", thistrack_id3
->codectype
, prev_codectype
);
391 static void codec_configure_callback(int setting
, intptr_t value
)
393 if (!dsp_configure(ci
.dsp
, setting
, value
))
394 { logf("Illegal key:%d", setting
); }
397 /* Initialize codec API */
398 void codec_init_codec_api(void)
400 ci
.dsp
= (struct dsp_config
*)dsp_configure(NULL
, DSP_MYDSP
,
402 ci
.codec_get_buffer
= codec_get_buffer
;
403 ci
.pcmbuf_insert
= codec_pcmbuf_insert_callback
;
404 ci
.set_elapsed
= codec_set_elapsed_callback
;
405 ci
.read_filebuf
= codec_filebuf_callback
;
406 ci
.request_buffer
= codec_request_buffer_callback
;
407 ci
.advance_buffer
= codec_advance_buffer_callback
;
408 ci
.advance_buffer_loc
= codec_advance_buffer_loc_callback
;
409 ci
.seek_buffer
= codec_seek_buffer_callback
;
410 ci
.seek_complete
= codec_seek_complete_callback
;
411 ci
.request_next_track
= codec_request_next_track_callback
;
412 ci
.discard_codec
= codec_discard_codec_callback
;
413 ci
.set_offset
= codec_set_offset_callback
;
414 ci
.configure
= codec_configure_callback
;
420 static bool codec_load_next_track(void)
422 intptr_t result
= Q_CODEC_REQUEST_FAILED
;
424 audio_set_prev_elapsed(thistrack_id3
->elapsed
);
426 #ifdef AB_REPEAT_ENABLE
427 ab_end_of_track_report();
430 logf("Request new track");
432 if (ci
.new_track
== 0)
435 automatic_skip
= true;
441 LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK");
442 result
= queue_send(&audio_queue
, Q_AUDIO_CHECK_NEW_TRACK
, 0);
447 case Q_CODEC_REQUEST_COMPLETE
:
448 LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
449 pcmbuf_start_track_change(automatic_skip
);
452 case Q_CODEC_REQUEST_FAILED
:
453 LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED");
455 ci
.stop_codec
= true;
456 codec_requested_stop
= true;
460 LOGFQUEUE("codec |< default");
461 ci
.stop_codec
= true;
462 codec_requested_stop
= true;
468 static void codec_thread(void)
470 struct queue_event ev
;
476 #ifdef HAVE_CROSSFADE
477 if (!pcmbuf_is_crossfade_active())
483 queue_wait(&codec_queue
, &ev
);
484 codec_requested_stop
= false;
487 case Q_CODEC_LOAD_DISK
:
488 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
489 queue_reply(&codec_queue
, 1);
490 audio_codec_loaded
= true;
491 ci
.stop_codec
= false;
492 status
= codec_load_file((const char *)ev
.data
, &ci
);
493 LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev
.data
, status
);
497 LOGFQUEUE("codec < Q_CODEC_LOAD");
498 if (*get_codec_hid() < 0) {
499 logf("Codec slot is empty!");
500 /* Wait for the pcm buffer to go empty */
501 while (pcm_is_playing())
503 /* This must be set to prevent an infinite loop */
504 ci
.stop_codec
= true;
505 LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
506 queue_post(&codec_queue
, Q_AUDIO_PLAY
, 0);
510 audio_codec_loaded
= true;
511 ci
.stop_codec
= false;
512 status
= codec_load_buf(*get_codec_hid(), &ci
);
513 LOGFQUEUE("codec_load_buf %d\n", status
);
516 case Q_CODEC_DO_CALLBACK
:
517 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
518 queue_reply(&codec_queue
, 1);
519 if ((void*)ev
.data
!= NULL
)
521 cpucache_invalidate();
522 ((void (*)(void))ev
.data
)();
527 #ifdef AUDIO_HAVE_RECORDING
528 case Q_ENCODER_LOAD_DISK
:
529 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
530 audio_codec_loaded
= false; /* Not audio codec! */
531 logf("loading encoder");
532 ci
.stop_encoder
= false;
533 status
= codec_load_file((const char *)ev
.data
, &ci
);
534 logf("encoder stopped");
536 #endif /* AUDIO_HAVE_RECORDING */
539 LOGFQUEUE("codec < default");
542 if (audio_codec_loaded
)
547 if (!(audio_status() & AUDIO_STATUS_PLAY
))
551 audio_codec_loaded
= false;
555 case Q_CODEC_LOAD_DISK
:
557 LOGFQUEUE("codec < Q_CODEC_LOAD");
558 if (audio_status() & AUDIO_STATUS_PLAY
)
560 if (ci
.new_track
|| status
!= CODEC_OK
)
564 logf("Codec failure, %d %d", ci
.new_track
, status
);
565 splash(HZ
*2, "Codec failure");
568 if (!codec_load_next_track())
570 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
571 /* End of playlist */
572 queue_post(&audio_queue
, Q_AUDIO_STOP
, 0);
578 logf("Codec finished");
581 /* Wait for the audio to stop playing before
582 * triggering the WPS exit */
583 while(pcm_is_playing())
585 /* There has been one too many struct pointer swaps by now
586 * so even though it says othertrack_id3, its the correct one! */
587 othertrack_id3
->elapsed
=
588 othertrack_id3
->length
- pcmbuf_get_latency();
592 if (codec_requested_stop
)
594 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
595 queue_post(&audio_queue
, Q_AUDIO_STOP
, 0);
601 if (*get_codec_hid() >= 0)
603 LOGFQUEUE("codec > codec Q_CODEC_LOAD");
604 queue_post(&codec_queue
, Q_CODEC_LOAD
, 0);
608 const char *codec_fn
=
609 get_codec_filename(thistrack_id3
->codectype
);
612 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
613 queue_post(&codec_queue
, Q_CODEC_LOAD_DISK
,
620 #ifdef AUDIO_HAVE_RECORDING
621 case Q_ENCODER_LOAD_DISK
:
622 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
624 if (status
== CODEC_OK
)
627 logf("Encoder failure");
628 splash(HZ
*2, "Encoder failure");
630 if (ci
.enc_codec_loaded
< 0)
633 logf("Encoder failed to load");
634 ci
.enc_codec_loaded
= -1;
636 #endif /* AUDIO_HAVE_RECORDING */
639 LOGFQUEUE("codec < default");
645 void make_codec_thread(void)
647 codec_thread_id
= create_thread(
648 codec_thread
, codec_stack
, sizeof(codec_stack
),
649 CREATE_THREAD_FROZEN
,
650 codec_thread_name
IF_PRIO(, PRIORITY_PLAYBACK
)
652 queue_enable_queue_send(&codec_queue
, &codec_queue_sender_list
,