All kernel objects in code shared amongs targets (core, plugins, codecs) should be...
[kugel-rb.git] / apps / codec_thread.c
blob03ab5622e2f0f566d3c76656e32d1fb6f53b2db9
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
23 #include "playback.h"
24 #include "codec_thread.h"
25 #include "system.h"
26 #include "kernel.h"
27 #include "codecs.h"
28 #include "buffering.h"
29 #include "pcmbuf.h"
30 #include "dsp.h"
31 #include "abrepeat.h"
32 #include "metadata.h"
33 #include "splash.h"
35 /* Define LOGF_ENABLE to enable logf output in this file */
36 /*#define LOGF_ENABLE*/
37 #include "logf.h"
39 /* macros to enable logf for queues
40 logging on SYS_TIMEOUT can be disabled */
41 #ifdef SIMULATOR
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*/
46 #endif
48 #ifdef PLAYBACK_LOGQUEUES
49 #define LOGFQUEUE logf
50 #else
51 #define LOGFQUEUE(...)
52 #endif
54 #ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT
55 #define LOGFQUEUE_SYS_TIMEOUT logf
56 #else
57 #define LOGFQUEUE_SYS_TIMEOUT(...)
58 #endif
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
66 * in playback.c.
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 SHAREDBSS_ATTR;
85 extern struct event_queue codec_queue SHAREDBSS_ATTR;
87 extern struct codec_api ci; /* from codecs.c */
89 /* Codec thread */
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 SHAREDBSS_ATTR;
93 static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
94 IBSS_ATTR;
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 /* Used to check whether a new codec must be loaded. See array audio_formats[]
106 * in metadata.c */
107 int get_codec_base_type(int type)
109 int base_type = type;
110 switch (type) {
111 case AFMT_MPA_L1:
112 case AFMT_MPA_L2:
113 case AFMT_MPA_L3:
114 base_type = AFMT_MPA_L3;
115 break;
116 case AFMT_MPC_SV7:
117 case AFMT_MPC_SV8:
118 base_type = AFMT_MPC_SV7;
119 break;
120 case AFMT_MP4_AAC:
121 case AFMT_MP4_AAC_HE:
122 base_type = AFMT_MP4_AAC;
123 break;
124 case AFMT_SAP:
125 case AFMT_CMC:
126 case AFMT_CM3:
127 case AFMT_CMR:
128 case AFMT_CMS:
129 case AFMT_DMC:
130 case AFMT_DLT:
131 case AFMT_MPT:
132 case AFMT_MPD:
133 case AFMT_RMT:
134 case AFMT_TMC:
135 case AFMT_TM8:
136 case AFMT_TM2:
137 base_type = AFMT_SAP;
138 break;
139 default:
140 break;
143 return base_type;
146 const char *get_codec_filename(int cod_spec)
148 const char *fname;
150 #ifdef HAVE_RECORDING
151 /* Can choose decoder or encoder if one available */
152 int type = cod_spec & CODEC_TYPE_MASK;
153 int afmt = cod_spec & CODEC_AFMT_MASK;
155 if ((unsigned)afmt >= AFMT_NUM_CODECS)
156 type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
158 fname = (type == CODEC_TYPE_ENCODER) ?
159 audio_formats[afmt].codec_enc_root_fn :
160 audio_formats[afmt].codec_root_fn;
162 logf("%s: %d - %s",
163 (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
164 afmt, fname ? fname : "<unknown>");
165 #else /* !HAVE_RECORDING */
166 /* Always decoder */
167 if ((unsigned)cod_spec >= AFMT_NUM_CODECS)
168 cod_spec = AFMT_UNKNOWN;
169 fname = audio_formats[cod_spec].codec_root_fn;
170 logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>");
171 #endif /* HAVE_RECORDING */
173 return fname;
174 } /* get_codec_filename */
176 /* Borrow the codec thread and return the ID */
177 void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
179 /* Set id before telling thread to call something; it may be
180 * needed before this function returns. */
181 if (id != NULL)
182 *id = codec_thread_id;
184 /* Codec thread will signal just before entering callback */
185 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
186 queue_send(&codec_queue, Q_CODEC_DO_CALLBACK, (intptr_t)fn);
190 /** codec API callbacks */
192 static void* codec_get_buffer(size_t *size)
194 if (codec_size >= CODEC_SIZE)
195 return NULL;
196 *size = CODEC_SIZE - codec_size;
197 return &codecbuf[codec_size];
200 static void codec_pcmbuf_insert_callback(
201 const void *ch1, const void *ch2, int count)
203 const char *src[2] = { ch1, ch2 };
205 while (count > 0)
207 int out_count = dsp_output_count(ci.dsp, count);
208 int inp_count;
209 char *dest;
211 /* Prevent audio from a previous track from playing */
212 if (ci.new_track || ci.stop_codec)
213 return;
215 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
217 cancel_cpu_boost();
218 sleep(1);
219 if (ci.seek_time || ci.new_track || ci.stop_codec)
220 return;
223 /* Get the real input_size for output_size bytes, guarding
224 * against resampling buffer overflows. */
225 inp_count = dsp_input_count(ci.dsp, out_count);
227 if (inp_count <= 0)
228 return;
230 /* Input size has grown, no error, just don't write more than length */
231 if (inp_count > count)
232 inp_count = count;
234 out_count = dsp_process(ci.dsp, dest, src, inp_count);
236 if (out_count <= 0)
237 return;
239 pcmbuf_write_complete(out_count);
241 count -= inp_count;
243 } /* codec_pcmbuf_insert_callback */
245 static void codec_set_elapsed_callback(unsigned long value)
247 if (ci.seek_time)
248 return;
250 #ifdef AB_REPEAT_ENABLE
251 ab_position_report(value);
252 #endif
254 unsigned long latency = pcmbuf_get_latency();
255 if (value < latency)
256 thistrack_id3->elapsed = 0;
257 else
259 unsigned long elapsed = value - latency;
260 if (elapsed > thistrack_id3->elapsed ||
261 elapsed < thistrack_id3->elapsed - 2)
263 thistrack_id3->elapsed = elapsed;
268 static void codec_set_offset_callback(size_t value)
270 if (ci.seek_time)
271 return;
273 unsigned long latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
274 if (value < latency)
275 thistrack_id3->offset = 0;
276 else
277 thistrack_id3->offset = value - latency;
280 /* helper function, not a callback */
281 static void codec_advance_buffer_counters(size_t amount)
283 bufadvance(get_audio_hid(), amount);
284 ci.curpos += amount;
287 /* copy up-to size bytes into ptr and return the actual size copied */
288 static size_t codec_filebuf_callback(void *ptr, size_t size)
290 ssize_t copy_n;
292 if (ci.stop_codec || !(audio_status() & AUDIO_STATUS_PLAY))
293 return 0;
295 copy_n = bufread(get_audio_hid(), size, ptr);
297 /* Nothing requested OR nothing left */
298 if (copy_n == 0)
299 return 0;
301 /* Update read and other position pointers */
302 codec_advance_buffer_counters(copy_n);
304 /* Return the actual amount of data copied to the buffer */
305 return copy_n;
306 } /* codec_filebuf_callback */
308 static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
310 size_t copy_n = reqsize;
311 ssize_t ret;
312 void *ptr;
314 if (!(audio_status() & AUDIO_STATUS_PLAY))
316 *realsize = 0;
317 return NULL;
320 ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
321 if (ret >= 0)
322 copy_n = MIN((size_t)ret, reqsize);
324 if (copy_n == 0)
326 *realsize = 0;
327 return NULL;
330 *realsize = copy_n;
332 return ptr;
333 } /* codec_request_buffer_callback */
335 static void codec_advance_buffer_callback(size_t amount)
337 codec_advance_buffer_counters(amount);
338 codec_set_offset_callback(ci.curpos);
341 static void codec_advance_buffer_loc_callback(void *ptr)
343 size_t amount = buf_get_offset(get_audio_hid(), ptr);
344 codec_advance_buffer_callback(amount);
347 static bool codec_seek_buffer_callback(size_t newpos)
349 logf("codec_seek_buffer_callback");
351 int ret = bufseek(get_audio_hid(), newpos);
352 if (ret == 0) {
353 ci.curpos = newpos;
354 return true;
356 else {
357 return false;
361 static void codec_seek_complete_callback(void)
363 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)
370 /* Clear the buffer */
371 pcmbuf_play_stop();
372 dsp_configure(ci.dsp, DSP_FLUSH, 0);
374 /* If seeking-while-playing, resume pcm playback */
375 if (!audio_paused)
376 pcmbuf_pause(false);
378 ci.seek_time = 0;
381 static void codec_discard_codec_callback(void)
383 int *codec_hid = get_codec_hid();
384 if (*codec_hid >= 0)
386 bufclose(*codec_hid);
387 *codec_hid = -1;
391 static bool codec_request_next_track_callback(void)
393 int prev_codectype;
395 if (ci.stop_codec || !(audio_status() & AUDIO_STATUS_PLAY))
396 return false;
398 prev_codectype = get_codec_base_type(thistrack_id3->codectype);
399 if (!codec_load_next_track())
400 return false;
402 /* Seek to the beginning of the new track because if the struct
403 mp3entry was buffered, "elapsed" might not be zero (if the track has
404 been played already but not unbuffered) */
405 codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
406 /* Check if the next codec is the same file. */
407 if (prev_codectype == get_codec_base_type(thistrack_id3->codectype))
409 logf("New track loaded");
410 codec_discard_codec_callback();
411 return true;
413 else
415 logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype);
416 return false;
420 static void codec_configure_callback(int setting, intptr_t value)
422 if (!dsp_configure(ci.dsp, setting, value))
423 { logf("Illegal key:%d", setting); }
426 /* Initialize codec API */
427 void codec_init_codec_api(void)
429 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
430 CODEC_IDX_AUDIO);
431 ci.codec_get_buffer = codec_get_buffer;
432 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
433 ci.set_elapsed = codec_set_elapsed_callback;
434 ci.read_filebuf = codec_filebuf_callback;
435 ci.request_buffer = codec_request_buffer_callback;
436 ci.advance_buffer = codec_advance_buffer_callback;
437 ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
438 ci.seek_buffer = codec_seek_buffer_callback;
439 ci.seek_complete = codec_seek_complete_callback;
440 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;
443 ci.configure = codec_configure_callback;
447 /* track change */
449 static bool codec_load_next_track(void)
451 intptr_t result = Q_CODEC_REQUEST_FAILED;
453 audio_set_prev_elapsed(thistrack_id3->elapsed);
455 #ifdef AB_REPEAT_ENABLE
456 ab_end_of_track_report();
457 #endif
459 logf("Request new track");
461 if (ci.new_track == 0)
463 ci.new_track++;
464 automatic_skip = true;
467 if (!ci.stop_codec)
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);
474 switch (result)
476 case Q_CODEC_REQUEST_COMPLETE:
477 LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
478 pcmbuf_start_track_change(automatic_skip);
479 return true;
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;
488 default:
489 LOGFQUEUE("codec |< default");
490 ci.stop_codec = true;
491 codec_requested_stop = true;
492 return false;
496 /** CODEC THREAD */
497 static void codec_thread(void)
499 struct queue_event ev;
500 int status;
502 while (1) {
503 status = 0;
505 #ifdef HAVE_CROSSFADE
506 if (!pcmbuf_is_crossfade_active())
507 #endif
509 cancel_cpu_boost();
512 queue_wait(&codec_queue, &ev);
513 codec_requested_stop = false;
515 switch (ev.id) {
516 case Q_CODEC_LOAD_DISK:
517 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
518 queue_reply(&codec_queue, 1);
519 audio_codec_loaded = true;
520 ci.stop_codec = false;
521 status = codec_load_file((const char *)ev.data, &ci);
522 LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev.data, status);
523 break;
525 case Q_CODEC_LOAD:
526 LOGFQUEUE("codec < Q_CODEC_LOAD");
527 if (*get_codec_hid() < 0) {
528 logf("Codec slot is empty!");
529 /* Wait for the pcm buffer to go empty */
530 while (pcm_is_playing())
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;
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;
545 case Q_CODEC_DO_CALLBACK:
546 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
547 queue_reply(&codec_queue, 1);
548 if ((void*)ev.data != NULL)
550 cpucache_invalidate();
551 ((void (*)(void))ev.data)();
552 cpucache_flush();
554 break;
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 */
567 default:
568 LOGFQUEUE("codec < default");
571 if (audio_codec_loaded)
573 if (ci.stop_codec)
575 status = CODEC_OK;
576 if (!(audio_status() & AUDIO_STATUS_PLAY))
577 pcmbuf_play_stop();
580 audio_codec_loaded = false;
583 switch (ev.id) {
584 case Q_CODEC_LOAD_DISK:
585 case Q_CODEC_LOAD:
586 LOGFQUEUE("codec < Q_CODEC_LOAD");
587 if (audio_status() & AUDIO_STATUS_PLAY)
589 if (ci.new_track || status != CODEC_OK)
591 if (!ci.new_track)
593 logf("Codec failure, %d %d", ci.new_track, status);
594 splash(HZ*2, "Codec failure");
597 if (!codec_load_next_track())
599 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
600 /* End of playlist */
601 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
602 break;
605 else
607 logf("Codec finished");
608 if (ci.stop_codec)
610 /* Wait for the audio to stop playing before
611 * triggering the WPS exit */
612 while(pcm_is_playing())
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);
621 if (codec_requested_stop)
623 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
624 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
626 break;
630 if (*get_codec_hid() >= 0)
632 LOGFQUEUE("codec > codec Q_CODEC_LOAD");
633 queue_post(&codec_queue, Q_CODEC_LOAD, 0);
635 else
637 const char *codec_fn =
638 get_codec_filename(thistrack_id3->codectype);
639 if (codec_fn)
641 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
642 queue_post(&codec_queue, Q_CODEC_LOAD_DISK,
643 (intptr_t)codec_fn);
647 break;
649 #ifdef AUDIO_HAVE_RECORDING
650 case Q_ENCODER_LOAD_DISK:
651 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
653 if (status == CODEC_OK)
654 break;
656 logf("Encoder failure");
657 splash(HZ*2, "Encoder failure");
659 if (ci.enc_codec_loaded < 0)
660 break;
662 logf("Encoder failed to load");
663 ci.enc_codec_loaded = -1;
664 break;
665 #endif /* AUDIO_HAVE_RECORDING */
667 default:
668 LOGFQUEUE("codec < default");
670 } /* end switch */
674 void make_codec_thread(void)
676 codec_thread_id = create_thread(
677 codec_thread, codec_stack, sizeof(codec_stack),
678 CREATE_THREAD_FROZEN,
679 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
680 IF_COP(, CPU));
681 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
682 codec_thread_id);