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 ****************************************************************************/
25 #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 */
71 /* Type of codec loaded? (C/A) */
72 static int current_codectype SHAREDBSS_ATTR
= AFMT_UNKNOWN
;
74 extern struct mp3entry
*thistrack_id3
, /* the currently playing track */
75 *othertrack_id3
; /* prev track during track-change-transition, or end of playlist,
76 * next track otherwise */
78 /* Track change controls */
79 extern struct event_queue audio_queue SHAREDBSS_ATTR
;
82 extern struct codec_api ci
; /* from codecs.c */
85 static unsigned int codec_thread_id
; /* For modifying thread priority later */
86 static struct event_queue codec_queue SHAREDBSS_ATTR
;
87 static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR
;
88 static long codec_stack
[(DEFAULT_STACK_SIZE
+ 0x2000)/sizeof(long)]
90 static const char codec_thread_name
[] = "codec";
93 static void codec_queue_ack(intptr_t ackme
)
95 queue_reply(&codec_queue
, ackme
);
98 static intptr_t codec_queue_send(long id
, intptr_t data
)
100 return queue_send(&codec_queue
, id
, data
);
103 /**************************************/
105 /** misc external functions */
107 /* Used to check whether a new codec must be loaded. See array audio_formats[]
109 int get_codec_base_type(int type
)
111 int base_type
= type
;
116 base_type
= AFMT_MPA_L3
;
120 base_type
= AFMT_MPC_SV7
;
123 case AFMT_MP4_AAC_HE
:
124 base_type
= AFMT_MP4_AAC
;
139 base_type
= AFMT_SAP
;
148 const char *get_codec_filename(int cod_spec
)
152 #ifdef HAVE_RECORDING
153 /* Can choose decoder or encoder if one available */
154 int type
= cod_spec
& CODEC_TYPE_MASK
;
155 int afmt
= cod_spec
& CODEC_AFMT_MASK
;
157 if ((unsigned)afmt
>= AFMT_NUM_CODECS
)
158 type
= AFMT_UNKNOWN
| (type
& CODEC_TYPE_MASK
);
160 fname
= (type
== CODEC_TYPE_ENCODER
) ?
161 audio_formats
[afmt
].codec_enc_root_fn
:
162 audio_formats
[afmt
].codec_root_fn
;
165 (type
== CODEC_TYPE_ENCODER
) ? "Encoder" : "Decoder",
166 afmt
, fname
? fname
: "<unknown>");
167 #else /* !HAVE_RECORDING */
169 if ((unsigned)cod_spec
>= AFMT_NUM_CODECS
)
170 cod_spec
= AFMT_UNKNOWN
;
171 fname
= audio_formats
[cod_spec
].codec_root_fn
;
172 logf("Codec: %d - %s", cod_spec
, fname
? fname
: "<unknown>");
173 #endif /* HAVE_RECORDING */
176 } /* get_codec_filename */
178 /* Borrow the codec thread and return the ID */
179 void codec_thread_do_callback(void (*fn
)(void), unsigned int *id
)
181 /* Set id before telling thread to call something; it may be
182 * needed before this function returns. */
184 *id
= codec_thread_id
;
186 /* Codec thread will signal just before entering callback */
187 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
188 codec_queue_send(Q_CODEC_DO_CALLBACK
, (intptr_t)fn
);
192 /** codec API callbacks */
194 static void* codec_get_buffer(size_t *size
)
196 ssize_t s
= CODEC_SIZE
- codec_size
;
197 void *buf
= &codecbuf
[codec_size
];
198 ALIGN_BUFFER(buf
, s
, CACHEALIGN_SIZE
);
207 static void codec_pcmbuf_insert_callback(
208 const void *ch1
, const void *ch2
, int count
)
210 const char *src
[2] = { ch1
, ch2
};
214 int out_count
= dsp_output_count(ci
.dsp
, count
);
218 /* Prevent audio from a previous track from playing */
219 if (ci
.new_track
|| ci
.stop_codec
)
222 while ((dest
= pcmbuf_request_buffer(&out_count
)) == NULL
)
226 if (ci
.seek_time
|| ci
.new_track
|| ci
.stop_codec
)
230 /* Get the real input_size for output_size bytes, guarding
231 * against resampling buffer overflows. */
232 inp_count
= dsp_input_count(ci
.dsp
, out_count
);
237 /* Input size has grown, no error, just don't write more than length */
238 if (inp_count
> count
)
241 out_count
= dsp_process(ci
.dsp
, dest
, src
, inp_count
);
246 pcmbuf_write_complete(out_count
);
250 } /* codec_pcmbuf_insert_callback */
252 static void codec_set_elapsed_callback(unsigned long value
)
257 #ifdef AB_REPEAT_ENABLE
258 ab_position_report(value
);
261 unsigned long latency
= pcmbuf_get_latency();
263 thistrack_id3
->elapsed
= 0;
266 unsigned long elapsed
= value
- latency
;
267 if (elapsed
> thistrack_id3
->elapsed
||
268 elapsed
< thistrack_id3
->elapsed
- 2)
270 thistrack_id3
->elapsed
= elapsed
;
275 static void codec_set_offset_callback(size_t value
)
280 unsigned long latency
= pcmbuf_get_latency() * thistrack_id3
->bitrate
/ 8;
282 thistrack_id3
->offset
= 0;
284 thistrack_id3
->offset
= value
- latency
;
287 /* helper function, not a callback */
288 static void codec_advance_buffer_counters(size_t amount
)
290 bufadvance(get_audio_hid(), amount
);
294 /* copy up-to size bytes into ptr and return the actual size copied */
295 static size_t codec_filebuf_callback(void *ptr
, size_t size
)
302 copy_n
= bufread(get_audio_hid(), size
, ptr
);
304 /* Nothing requested OR nothing left */
308 /* Update read and other position pointers */
309 codec_advance_buffer_counters(copy_n
);
311 /* Return the actual amount of data copied to the buffer */
313 } /* codec_filebuf_callback */
315 static void* codec_request_buffer_callback(size_t *realsize
, size_t reqsize
)
317 size_t copy_n
= reqsize
;
321 ret
= bufgetdata(get_audio_hid(), reqsize
, &ptr
);
323 copy_n
= MIN((size_t)ret
, reqsize
);
332 } /* codec_request_buffer_callback */
334 static void codec_advance_buffer_callback(size_t amount
)
336 codec_advance_buffer_counters(amount
);
337 codec_set_offset_callback(ci
.curpos
);
340 static bool codec_seek_buffer_callback(size_t newpos
)
342 logf("codec_seek_buffer_callback");
344 int ret
= bufseek(get_audio_hid(), newpos
);
354 static void codec_seek_complete_callback(void)
356 struct queue_event ev
;
358 logf("seek_complete");
361 dsp_configure(ci
.dsp
, DSP_FLUSH
, 0);
363 /* Post notification to audio thread */
364 LOGFQUEUE("audio > Q_AUDIO_SEEK_COMPLETE");
365 queue_post(&audio_queue
, Q_AUDIO_SEEK_COMPLETE
, 0);
368 queue_wait(&codec_queue
, &ev
);
370 /* ACK back in context */
371 codec_queue_ack(Q_AUDIO_SEEK_COMPLETE
);
374 static bool codec_request_next_track_callback(void)
376 struct queue_event ev
;
378 logf("Request new track");
380 audio_set_prev_elapsed(thistrack_id3
->elapsed
);
382 #ifdef AB_REPEAT_ENABLE
383 ab_end_of_track_report();
388 /* Handle ACK in outer loop */
389 LOGFQUEUE("codec: already stopping");
395 /* Post request to audio thread */
396 LOGFQUEUE("codec > audio Q_AUDIO_CHECK_NEW_TRACK");
397 queue_post(&audio_queue
, Q_AUDIO_CHECK_NEW_TRACK
, 0);
400 queue_wait(&codec_queue
, &ev
);
402 if (ev
.data
== Q_CODEC_REQUEST_COMPLETE
)
404 /* Seek to the beginning of the new track because if the struct
405 mp3entry was buffered, "elapsed" might not be zero (if the track has
406 been played already but not unbuffered) */
407 codec_seek_buffer_callback(thistrack_id3
->first_frame_offset
);
410 /* ACK back in context */
411 codec_queue_ack(Q_AUDIO_CHECK_NEW_TRACK
);
413 if (ev
.data
!= Q_CODEC_REQUEST_COMPLETE
|| ci
.stop_codec
)
415 LOGFQUEUE("codec <= request failed (%d)", ev
.data
);
419 LOGFQUEUE("codec <= Q_CODEC_REQEST_COMPLETE");
423 static void codec_configure_callback(int setting
, intptr_t value
)
425 if (!dsp_configure(ci
.dsp
, setting
, value
))
426 { logf("Illegal key:%d", setting
); }
429 /* Initialize codec API */
430 void codec_init_codec_api(void)
432 ci
.dsp
= (struct dsp_config
*)dsp_configure(NULL
, DSP_MYDSP
,
434 ci
.codec_get_buffer
= codec_get_buffer
;
435 ci
.pcmbuf_insert
= codec_pcmbuf_insert_callback
;
436 ci
.set_elapsed
= codec_set_elapsed_callback
;
437 ci
.read_filebuf
= codec_filebuf_callback
;
438 ci
.request_buffer
= codec_request_buffer_callback
;
439 ci
.advance_buffer
= codec_advance_buffer_callback
;
440 ci
.seek_buffer
= codec_seek_buffer_callback
;
441 ci
.seek_complete
= codec_seek_complete_callback
;
442 ci
.request_next_track
= codec_request_next_track_callback
;
443 ci
.set_offset
= codec_set_offset_callback
;
444 ci
.configure
= codec_configure_callback
;
451 static void codec_thread(void)
453 struct queue_event ev
;
458 int status
= CODEC_OK
;
461 const char *codec_fn
;
463 #ifdef HAVE_CROSSFADE
464 if (!pcmbuf_is_crossfade_active())
470 queue_wait(&codec_queue
, &ev
);
474 case Q_CODEC_LOAD_DISK
:
475 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
476 codec_fn
= get_codec_filename(ev
.data
);
479 #ifdef AUDIO_HAVE_RECORDING
480 if (ev
.data
& CODEC_TYPE_ENCODER
)
482 ev
.id
= Q_ENCODER_LOAD_DISK
;
483 handle
= codec_load_file(codec_fn
, &ci
);
485 codec_queue_ack(Q_ENCODER_LOAD_DISK
);
490 codec_queue_ack(Q_CODEC_LOAD_DISK
);
491 handle
= codec_load_file(codec_fn
, &ci
);
496 LOGFQUEUE("codec < Q_CODEC_LOAD");
497 codec_queue_ack(Q_CODEC_LOAD
);
499 handle
= codec_load_buf(hid
, &ci
);
503 case Q_CODEC_DO_CALLBACK
:
504 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
505 codec_queue_ack(Q_CODEC_DO_CALLBACK
);
506 if ((void*)ev
.data
!= NULL
)
508 cpucache_commit_discard();
509 ((void (*)(void))ev
.data
)();
515 LOGFQUEUE("codec < default : %ld", ev
.id
);
520 /* Codec loaded - call the entrypoint */
522 logf("codec running");
523 status
= codec_begin(handle
);
524 logf("codec stopped");
526 current_codectype
= AFMT_UNKNOWN
;
534 #ifdef AUDIO_HAVE_RECORDING
535 case Q_ENCODER_LOAD_DISK
:
537 case Q_CODEC_LOAD_DISK
:
539 /* Notify about the status */
541 status
= CODEC_ERROR
;
542 LOGFQUEUE("codec > audio notify status: %d", status
);
543 queue_post(&audio_queue
, ev
.id
, status
);
549 void make_codec_thread(void)
551 queue_init(&codec_queue
, false);
552 codec_thread_id
= create_thread(
553 codec_thread
, codec_stack
, sizeof(codec_stack
),
554 CREATE_THREAD_FROZEN
,
555 codec_thread_name
IF_PRIO(, PRIORITY_PLAYBACK
)
557 queue_enable_queue_send(&codec_queue
, &codec_queue_sender_list
,
561 void codec_thread_resume(void)
563 thread_thaw(codec_thread_id
);
566 bool is_codec_thread(void)
568 return thread_self() == codec_thread_id
;
571 #ifdef HAVE_PRIORITY_SCHEDULING
572 int codec_thread_get_priority(void)
574 return thread_get_priority(codec_thread_id
);
577 int codec_thread_set_priority(int priority
)
579 return thread_set_priority(codec_thread_id
, priority
);
581 #endif /* HAVE_PRIORITY_SCHEDULING */
583 /* functions for audio thread use */
584 intptr_t codec_ack_msg(intptr_t data
, bool stop_codec
)
587 LOGFQUEUE("codec >| Q_CODEC_ACK: %d", data
);
589 ci
.stop_codec
= true;
590 resp
= codec_queue_send(Q_CODEC_ACK
, data
);
593 LOGFQUEUE(" ack: %ld", resp
);
597 bool codec_load(int hid
, int cod_spec
)
601 ci
.stop_codec
= false;
602 current_codectype
= cod_spec
;
606 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d", hid
);
607 retval
= codec_queue_send(Q_CODEC_LOAD
, hid
) != Q_NULL
;
611 LOGFQUEUE("audio >| codec Q_CODEC_LOAD_DISK: %d", cod_spec
);
612 retval
= codec_queue_send(Q_CODEC_LOAD_DISK
, cod_spec
) != Q_NULL
;
617 ci
.stop_codec
= true;
618 current_codectype
= AFMT_UNKNOWN
;
624 void codec_stop(void)
626 ci
.stop_codec
= true;
627 /* Wait until it's in the main loop */
628 while (codec_ack_msg(0, false) != Q_NULL
);
629 current_codectype
= AFMT_UNKNOWN
;
632 int codec_loaded(void)
634 return current_codectype
;