really fix early usb this time
[kugel-rb.git] / apps / codec_thread.c
blobdc6533dfc97891099de20296c8489446ca2bf6ba
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;
85 extern struct event_queue codec_queue;
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;
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 int get_codec_base_type(int type)
107 switch (type) {
108 case AFMT_MPA_L1:
109 case AFMT_MPA_L2:
110 case AFMT_MPA_L3:
111 return AFMT_MPA_L3;
114 return type;
117 const char *get_codec_filename(int cod_spec)
119 const char *fname;
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;
133 logf("%s: %d - %s",
134 (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
135 afmt, fname ? fname : "<unknown>");
136 #else /* !HAVE_RECORDING */
137 /* Always decoder */
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 */
144 return fname;
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. */
152 if (id != NULL)
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)
166 return NULL;
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 };
176 while (count > 0)
178 int out_count = dsp_output_count(ci.dsp, count);
179 int inp_count;
180 char *dest;
182 /* Prevent audio from a previous track from playing */
183 if (ci.new_track || ci.stop_codec)
184 return true;
186 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
188 cancel_cpu_boost();
189 sleep(1);
190 if (ci.seek_time || ci.new_track || ci.stop_codec)
191 return true;
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);
198 if (inp_count <= 0)
199 return true;
201 /* Input size has grown, no error, just don't write more than length */
202 if (inp_count > count)
203 inp_count = count;
205 out_count = dsp_process(ci.dsp, dest, src, inp_count);
207 if (out_count <= 0)
208 return true;
210 pcmbuf_write_complete(out_count);
212 count -= inp_count;
215 return true;
216 } /* codec_pcmbuf_insert_callback */
218 static void codec_set_elapsed_callback(unsigned long value)
220 if (ci.seek_time)
221 return;
223 #ifdef AB_REPEAT_ENABLE
224 ab_position_report(value);
225 #endif
227 unsigned long latency = pcmbuf_get_latency();
228 if (value < latency)
229 thistrack_id3->elapsed = 0;
230 else
232 unsigned long 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)
243 if (ci.seek_time)
244 return;
246 unsigned long latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
247 if (value < latency)
248 thistrack_id3->offset = 0;
249 else
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);
257 ci.curpos += 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)
263 ssize_t copy_n;
265 if (ci.stop_codec || !(audio_status() & AUDIO_STATUS_PLAY))
266 return 0;
268 copy_n = bufread(get_audio_hid(), size, ptr);
270 /* Nothing requested OR nothing left */
271 if (copy_n == 0)
272 return 0;
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 */
278 return copy_n;
279 } /* codec_filebuf_callback */
281 static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
283 size_t copy_n = reqsize;
284 ssize_t ret;
285 void *ptr;
287 if (!(audio_status() & AUDIO_STATUS_PLAY))
289 *realsize = 0;
290 return NULL;
293 ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
294 if (ret >= 0)
295 copy_n = MIN((size_t)ret, reqsize);
297 if (copy_n == 0)
299 *realsize = 0;
300 return NULL;
303 *realsize = copy_n;
305 return ptr;
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);
325 if (ret == 0) {
326 ci.curpos = newpos;
327 return true;
329 else {
330 return false;
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_status PAUSE is true.
339 * A seamless seek skips this section. */
340 bool audio_paused = audio_status() & AUDIO_STATUS_PAUSE;
341 if (pcm_is_paused() || audio_paused)
343 /* Clear the buffer */
344 pcmbuf_play_stop();
345 dsp_configure(ci.dsp, DSP_FLUSH, 0);
347 /* If seeking-while-playing, resume pcm playback */
348 if (!audio_paused)
349 pcmbuf_pause(false);
351 ci.seek_time = 0;
354 static void codec_discard_codec_callback(void)
356 int *codec_hid = get_codec_hid();
357 if (*codec_hid >= 0)
359 bufclose(*codec_hid);
360 *codec_hid = -1;
364 static bool codec_request_next_track_callback(void)
366 int prev_codectype;
368 if (ci.stop_codec || !(audio_status() & AUDIO_STATUS_PLAY))
369 return false;
371 prev_codectype = get_codec_base_type(thistrack_id3->codectype);
372 if (!codec_load_next_track())
373 return false;
375 /* Seek to the beginning of the new track because if the struct
376 mp3entry was buffered, "elapsed" might not be zero (if the track has
377 been played already but not unbuffered) */
378 codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
379 /* Check if the next codec is the same file. */
380 if (prev_codectype == get_codec_base_type(thistrack_id3->codectype))
382 logf("New track loaded");
383 codec_discard_codec_callback();
384 return true;
386 else
388 logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype);
389 return false;
393 static void codec_configure_callback(int setting, intptr_t value)
395 if (!dsp_configure(ci.dsp, setting, value))
396 { logf("Illegal key:%d", setting); }
399 /* Initialize codec API */
400 void codec_init_codec_api(void)
402 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
403 CODEC_IDX_AUDIO);
404 ci.codec_get_buffer = codec_get_buffer;
405 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
406 ci.set_elapsed = codec_set_elapsed_callback;
407 ci.read_filebuf = codec_filebuf_callback;
408 ci.request_buffer = codec_request_buffer_callback;
409 ci.advance_buffer = codec_advance_buffer_callback;
410 ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
411 ci.seek_buffer = codec_seek_buffer_callback;
412 ci.seek_complete = codec_seek_complete_callback;
413 ci.request_next_track = codec_request_next_track_callback;
414 ci.discard_codec = codec_discard_codec_callback;
415 ci.set_offset = codec_set_offset_callback;
416 ci.configure = codec_configure_callback;
420 /* track change */
422 static bool codec_load_next_track(void)
424 intptr_t result = Q_CODEC_REQUEST_FAILED;
426 audio_set_prev_elapsed(thistrack_id3->elapsed);
428 #ifdef AB_REPEAT_ENABLE
429 ab_end_of_track_report();
430 #endif
432 logf("Request new track");
434 if (ci.new_track == 0)
436 ci.new_track++;
437 automatic_skip = true;
440 if (!ci.stop_codec)
442 trigger_cpu_boost();
443 LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK");
444 result = queue_send(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
447 switch (result)
449 case Q_CODEC_REQUEST_COMPLETE:
450 LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
451 pcmbuf_start_track_change(automatic_skip);
452 return true;
454 case Q_CODEC_REQUEST_FAILED:
455 LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED");
456 ci.new_track = 0;
457 ci.stop_codec = true;
458 codec_requested_stop = true;
459 return false;
461 default:
462 LOGFQUEUE("codec |< default");
463 ci.stop_codec = true;
464 codec_requested_stop = true;
465 return false;
469 /** CODEC THREAD */
470 static void codec_thread(void)
472 struct queue_event ev;
473 int status;
475 while (1) {
476 status = 0;
478 #ifdef HAVE_CROSSFADE
479 if (!pcmbuf_is_crossfade_active())
480 #endif
482 cancel_cpu_boost();
485 queue_wait(&codec_queue, &ev);
486 codec_requested_stop = false;
488 switch (ev.id) {
489 case Q_CODEC_LOAD_DISK:
490 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
491 queue_reply(&codec_queue, 1);
492 audio_codec_loaded = true;
493 ci.stop_codec = false;
494 status = codec_load_file((const char *)ev.data, &ci);
495 LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev.data, status);
496 break;
498 case Q_CODEC_LOAD:
499 LOGFQUEUE("codec < Q_CODEC_LOAD");
500 if (*get_codec_hid() < 0) {
501 logf("Codec slot is empty!");
502 /* Wait for the pcm buffer to go empty */
503 while (pcm_is_playing())
504 yield();
505 /* This must be set to prevent an infinite loop */
506 ci.stop_codec = true;
507 LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
508 queue_post(&codec_queue, Q_AUDIO_PLAY, 0);
509 break;
512 audio_codec_loaded = true;
513 ci.stop_codec = false;
514 status = codec_load_buf(*get_codec_hid(), &ci);
515 LOGFQUEUE("codec_load_buf %d\n", status);
516 break;
518 case Q_CODEC_DO_CALLBACK:
519 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
520 queue_reply(&codec_queue, 1);
521 if ((void*)ev.data != NULL)
523 cpucache_invalidate();
524 ((void (*)(void))ev.data)();
525 cpucache_flush();
527 break;
529 #ifdef AUDIO_HAVE_RECORDING
530 case Q_ENCODER_LOAD_DISK:
531 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
532 audio_codec_loaded = false; /* Not audio codec! */
533 logf("loading encoder");
534 ci.stop_encoder = false;
535 status = codec_load_file((const char *)ev.data, &ci);
536 logf("encoder stopped");
537 break;
538 #endif /* AUDIO_HAVE_RECORDING */
540 default:
541 LOGFQUEUE("codec < default");
544 if (audio_codec_loaded)
546 if (ci.stop_codec)
548 status = CODEC_OK;
549 if (!(audio_status() & AUDIO_STATUS_PLAY))
550 pcmbuf_play_stop();
553 audio_codec_loaded = false;
556 switch (ev.id) {
557 case Q_CODEC_LOAD_DISK:
558 case Q_CODEC_LOAD:
559 LOGFQUEUE("codec < Q_CODEC_LOAD");
560 if (audio_status() & AUDIO_STATUS_PLAY)
562 if (ci.new_track || status != CODEC_OK)
564 if (!ci.new_track)
566 logf("Codec failure, %d %d", ci.new_track, status);
567 splash(HZ*2, "Codec failure");
570 if (!codec_load_next_track())
572 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
573 /* End of playlist */
574 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
575 break;
578 else
580 logf("Codec finished");
581 if (ci.stop_codec)
583 /* Wait for the audio to stop playing before
584 * triggering the WPS exit */
585 while(pcm_is_playing())
587 /* There has been one too many struct pointer swaps by now
588 * so even though it says othertrack_id3, its the correct one! */
589 othertrack_id3->elapsed =
590 othertrack_id3->length - pcmbuf_get_latency();
591 sleep(1);
594 if (codec_requested_stop)
596 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
597 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
599 break;
603 if (*get_codec_hid() >= 0)
605 LOGFQUEUE("codec > codec Q_CODEC_LOAD");
606 queue_post(&codec_queue, Q_CODEC_LOAD, 0);
608 else
610 const char *codec_fn =
611 get_codec_filename(thistrack_id3->codectype);
612 if (codec_fn)
614 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
615 queue_post(&codec_queue, Q_CODEC_LOAD_DISK,
616 (intptr_t)codec_fn);
620 break;
622 #ifdef AUDIO_HAVE_RECORDING
623 case Q_ENCODER_LOAD_DISK:
624 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
626 if (status == CODEC_OK)
627 break;
629 logf("Encoder failure");
630 splash(HZ*2, "Encoder failure");
632 if (ci.enc_codec_loaded < 0)
633 break;
635 logf("Encoder failed to load");
636 ci.enc_codec_loaded = -1;
637 break;
638 #endif /* AUDIO_HAVE_RECORDING */
640 default:
641 LOGFQUEUE("codec < default");
643 } /* end switch */
647 void make_codec_thread(void)
649 codec_thread_id = create_thread(
650 codec_thread, codec_stack, sizeof(codec_stack),
651 CREATE_THREAD_FROZEN,
652 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
653 IF_COP(, CPU));
654 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
655 codec_thread_id);