Minor loop optimization in libfaad's is/ms decoding.
[kugel-rb.git] / apps / codec_thread.c
blob7cf45c34905c5ee96cbcf13507904f1b7e2c9aaf
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
12 * Copyright (C) 2011 Michael Sevakis
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "config.h"
24 #include "system.h"
25 #include "kernel.h"
26 #include "codecs.h"
27 #include "codec_thread.h"
28 #include "pcmbuf.h"
29 #include "playback.h"
30 #include "buffering.h"
31 #include "dsp.h"
32 #include "metadata.h"
34 /* Define LOGF_ENABLE to enable logf output in this file */
35 /*#define LOGF_ENABLE*/
36 #include "logf.h"
38 /* macros to enable logf for queues
39 logging on SYS_TIMEOUT can be disabled */
40 #ifdef SIMULATOR
41 /* Define this for logf output of all queuing except SYS_TIMEOUT */
42 #define PLAYBACK_LOGQUEUES
43 /* Define this to logf SYS_TIMEOUT messages */
44 /*#define PLAYBACK_LOGQUEUES_SYS_TIMEOUT*/
45 #endif
47 #ifdef PLAYBACK_LOGQUEUES
48 #define LOGFQUEUE logf
49 #else
50 #define LOGFQUEUE(...)
51 #endif
53 #ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT
54 #define LOGFQUEUE_SYS_TIMEOUT logf
55 #else
56 #define LOGFQUEUE_SYS_TIMEOUT(...)
57 #endif
59 /* Variables are commented with the threads that use them:
60 * A=audio, C=codec
61 * - = reads only
63 * Unless otherwise noted, the extern variables are located
64 * in playback.c.
67 /* Q_LOAD_CODEC parameter data */
68 struct codec_load_info
70 int hid; /* audio handle id (specify < 0 to use afmt) */
71 int afmt; /* codec specification (AFMT_*) */
75 /** --- Main state control --- **/
77 static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */
79 /* Private interfaces to main playback control */
80 extern void audio_codec_update_elapsed(unsigned long value);
81 extern void audio_codec_update_offset(size_t value);
82 extern void audio_queue_post(long id, intptr_t data);
83 extern struct codec_api ci; /* from codecs.c */
85 /* Codec thread */
86 static unsigned int codec_thread_id; /* For modifying thread priority later */
87 static struct event_queue codec_queue SHAREDBSS_ATTR;
88 static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR;
89 static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] IBSS_ATTR;
90 static const char codec_thread_name[] = "codec";
92 static void unload_codec(void);
94 /* Messages are only ever sent one at a time to the codec from the audio
95 thread. This is important for correct operation unless playback is
96 stopped. */
98 /* static routines */
99 static void codec_queue_ack(intptr_t ackme)
101 queue_reply(&codec_queue, ackme);
104 static intptr_t codec_queue_send(long id, intptr_t data)
106 return queue_send(&codec_queue, id, data);
109 /* Poll the state of the codec queue. Returns < 0 if the message is urgent
110 and any state should exit, > 0 if it's a run message (and it was
111 scrubbed), 0 if message was ignored. */
112 static int codec_check_queue__have_msg(void)
114 struct queue_event ev;
116 queue_peek(&codec_queue, &ev);
118 /* Seek, pause or stop? Just peek and return if so. Codec
119 must handle the command after returing. Inserts will not
120 be allowed until it complies. */
121 switch (ev.id)
123 case Q_CODEC_SEEK:
124 LOGFQUEUE("codec - Q_CODEC_SEEK", ev.id);
125 return -1;
126 case Q_CODEC_PAUSE:
127 LOGFQUEUE("codec - Q_CODEC_PAUSE", ev.id);
128 return -1;
129 case Q_CODEC_STOP:
130 LOGFQUEUE("codec - Q_CODEC_STOP", ev.id);
131 return -1;
134 /* This is in error in this context unless it's "go, go, go!" */
135 queue_wait(&codec_queue, &ev);
137 if (ev.id == Q_CODEC_RUN)
139 logf("codec < Q_CODEC_RUN: already running!");
140 codec_queue_ack(Q_CODEC_RUN);
141 return 1;
144 /* Ignore it */
145 logf("codec < bad req %ld (%s)", ev.id, __func__);
146 codec_queue_ack(Q_NULL);
147 return 0;
150 /* Does the audio format type equal CODEC_TYPE_ENCODER? */
151 static inline bool type_is_encoder(int afmt)
153 #ifdef AUDIO_HAVE_RECORDING
154 return (afmt & CODEC_TYPE_MASK) == CODEC_TYPE_ENCODER;
155 #else
156 return false;
157 (void)afmt;
158 #endif
161 /**************************************/
164 /** --- Miscellaneous external functions --- **/
165 const char * get_codec_filename(int cod_spec)
167 const char *fname;
169 #ifdef HAVE_RECORDING
170 /* Can choose decoder or encoder if one available */
171 int type = cod_spec & CODEC_TYPE_MASK;
172 int afmt = cod_spec & CODEC_AFMT_MASK;
174 if ((unsigned)afmt >= AFMT_NUM_CODECS)
175 type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
177 fname = (type == CODEC_TYPE_ENCODER) ?
178 audio_formats[afmt].codec_enc_root_fn :
179 audio_formats[afmt].codec_root_fn;
181 logf("%s: %d - %s",
182 (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
183 afmt, fname ? fname : "<unknown>");
184 #else /* !HAVE_RECORDING */
185 /* Always decoder */
186 if ((unsigned)cod_spec >= AFMT_NUM_CODECS)
187 cod_spec = AFMT_UNKNOWN;
188 fname = audio_formats[cod_spec].codec_root_fn;
189 logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>");
190 #endif /* HAVE_RECORDING */
192 return fname;
195 /* Borrow the codec thread and return the ID */
196 void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
198 /* Set id before telling thread to call something; it may be
199 * needed before this function returns. */
200 if (id != NULL)
201 *id = codec_thread_id;
203 /* Codec thread will signal just before entering callback */
204 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
205 codec_queue_send(Q_CODEC_DO_CALLBACK, (intptr_t)fn);
209 /** --- codec API callbacks --- **/
211 static void * codec_get_buffer(size_t *size)
213 ssize_t s = CODEC_SIZE - codec_size;
214 void *buf = &codecbuf[codec_size];
215 ALIGN_BUFFER(buf, s, CACHEALIGN_SIZE);
217 if (s <= 0)
218 return NULL;
220 *size = s;
221 return buf;
224 static void codec_pcmbuf_insert_callback(
225 const void *ch1, const void *ch2, int count)
227 const char *src[2] = { ch1, ch2 };
229 while (count > 0)
231 int out_count = dsp_output_count(ci.dsp, count);
232 int inp_count;
233 char *dest;
235 while (1)
237 if ((dest = pcmbuf_request_buffer(&out_count)) != NULL)
238 break;
240 cancel_cpu_boost();
242 /* It will be awhile before space is available but we want
243 "instant" response to any message */
244 queue_wait_w_tmo(&codec_queue, NULL, HZ/20);
246 if (!queue_empty(&codec_queue) &&
247 codec_check_queue__have_msg() < 0)
248 return;
251 /* Get the real input_size for output_size bytes, guarding
252 * against resampling buffer overflows. */
253 inp_count = dsp_input_count(ci.dsp, out_count);
255 if (inp_count <= 0)
256 return;
258 /* Input size has grown, no error, just don't write more than length */
259 if (inp_count > count)
260 inp_count = count;
262 out_count = dsp_process(ci.dsp, dest, src, inp_count);
264 if (out_count <= 0)
265 return;
267 pcmbuf_write_complete(out_count);
269 count -= inp_count;
273 /* helper function, not a callback */
274 static bool codec_advance_buffer_counters(size_t amount)
276 if (bufadvance(ci.audio_hid, amount) < 0)
278 ci.curpos = ci.filesize;
279 return false;
282 ci.curpos += amount;
283 return true;
286 /* copy up-to size bytes into ptr and return the actual size copied */
287 static size_t codec_filebuf_callback(void *ptr, size_t size)
289 ssize_t copy_n = bufread(ci.audio_hid, size, ptr);
291 /* Nothing requested OR nothing left */
292 if (copy_n <= 0)
293 return 0;
295 /* Update read and other position pointers */
296 codec_advance_buffer_counters(copy_n);
298 /* Return the actual amount of data copied to the buffer */
299 return copy_n;
302 static void * codec_request_buffer_callback(size_t *realsize, size_t reqsize)
304 size_t copy_n = reqsize;
305 ssize_t ret;
306 void *ptr;
308 ret = bufgetdata(ci.audio_hid, reqsize, &ptr);
309 if (ret >= 0)
310 copy_n = MIN((size_t)ret, reqsize);
311 else
312 copy_n = 0;
314 if (copy_n == 0)
315 ptr = NULL;
317 *realsize = copy_n;
318 return ptr;
321 static void codec_advance_buffer_callback(size_t amount)
323 if (!codec_advance_buffer_counters(amount))
324 return;
326 audio_codec_update_offset(ci.curpos);
329 static bool codec_seek_buffer_callback(size_t newpos)
331 logf("codec_seek_buffer_callback");
333 int ret = bufseek(ci.audio_hid, newpos);
334 if (ret == 0)
336 ci.curpos = newpos;
337 return true;
340 return false;
343 static void codec_seek_complete_callback(void)
345 logf("seek_complete");
347 /* Clear DSP */
348 dsp_configure(ci.dsp, DSP_FLUSH, 0);
350 /* Post notification to audio thread */
351 LOGFQUEUE("audio > Q_AUDIO_CODEC_SEEK_COMPLETE");
352 audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
354 /* Wait for urgent or go message */
357 queue_wait(&codec_queue, NULL);
359 while (codec_check_queue__have_msg() == 0);
362 static void codec_configure_callback(int setting, intptr_t value)
364 if (!dsp_configure(ci.dsp, setting, value))
366 logf("Illegal key: %d", setting);
370 static enum codec_command_action
371 codec_get_command_callback(intptr_t *param)
373 yield();
375 if (LIKELY(queue_empty(&codec_queue)))
376 return CODEC_ACTION_NULL; /* As you were */
378 /* Process the message - return requested action and data (if any should
379 be expected) */
380 while (1)
382 enum codec_command_action action = CODEC_ACTION_NULL;
383 struct queue_event ev;
384 queue_wait(&codec_queue, &ev);
386 switch (ev.id)
388 case Q_CODEC_RUN: /* Already running */
389 LOGFQUEUE("codec < Q_CODEC_RUN");
390 break;
392 case Q_CODEC_PAUSE: /* Stay here and wait */
393 LOGFQUEUE("codec < Q_CODEC_PAUSE");
394 codec_queue_ack(Q_CODEC_PAUSE);
395 continue;
397 case Q_CODEC_SEEK: /* Audio wants codec to seek */
398 LOGFQUEUE("codec < Q_CODEC_SEEK %ld", ev.data);
399 *param = ev.data;
400 action = CODEC_ACTION_SEEK_TIME;
401 break;
403 case Q_CODEC_STOP: /* Must only return 0 in main loop */
404 LOGFQUEUE("codec < Q_CODEC_STOP");
405 action = CODEC_ACTION_HALT;
406 break;
408 default: /* This is in error in this context. */
409 ev.id = Q_NULL;
410 logf("codec bad req %ld (%s)", ev.id, __func__);
413 codec_queue_ack(ev.id);
414 return action;
418 /* Initialize codec API */
419 void codec_init_codec_api(void)
421 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
422 CODEC_IDX_AUDIO);
423 ci.codec_get_buffer = codec_get_buffer;
424 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
425 ci.set_elapsed = audio_codec_update_elapsed;
426 ci.read_filebuf = codec_filebuf_callback;
427 ci.request_buffer = codec_request_buffer_callback;
428 ci.advance_buffer = codec_advance_buffer_callback;
429 ci.seek_buffer = codec_seek_buffer_callback;
430 ci.seek_complete = codec_seek_complete_callback;
431 ci.set_offset = audio_codec_update_offset;
432 ci.configure = codec_configure_callback;
433 ci.get_command = codec_get_command_callback;
437 /** --- CODEC THREAD --- **/
439 /* Handle Q_CODEC_LOAD */
440 static void load_codec(const struct codec_load_info *ev_data)
442 int status = CODEC_ERROR;
443 /* Save a local copy so we can let the audio thread go ASAP */
444 struct codec_load_info data = *ev_data;
445 bool const encoder = type_is_encoder(data.afmt);
447 if (codec_type != AFMT_UNKNOWN)
449 /* Must have unloaded it first */
450 logf("a codec is already loaded");
451 if (data.hid >= 0)
452 bufclose(data.hid);
453 return;
456 trigger_cpu_boost();
458 if (!encoder)
460 /* Do this now because codec may set some things up at load time */
461 dsp_configure(ci.dsp, DSP_RESET, 0);
464 if (data.hid >= 0)
466 /* First try buffer load */
467 status = codec_load_buf(data.hid, &ci);
468 bufclose(data.hid);
471 if (status < 0)
473 /* Either not a valid handle or the buffer method failed */
474 const char *codec_fn = get_codec_filename(data.afmt);
475 if (codec_fn)
477 #ifdef HAVE_IO_PRIORITY
478 buf_back_off_storage(true);
479 #endif
480 status = codec_load_file(codec_fn, &ci);
481 #ifdef HAVE_IO_PRIORITY
482 buf_back_off_storage(false);
483 #endif
487 if (status >= 0)
489 codec_type = data.afmt;
490 codec_queue_ack(Q_CODEC_LOAD);
491 return;
494 /* Failed - get rid of it */
495 unload_codec();
498 /* Handle Q_CODEC_RUN */
499 static void run_codec(void)
501 bool const encoder = type_is_encoder(codec_type);
502 int status;
504 if (codec_type == AFMT_UNKNOWN)
506 logf("no codec to run");
507 return;
510 codec_queue_ack(Q_CODEC_RUN);
512 trigger_cpu_boost();
514 if (!encoder)
516 /* This will be either the initial buffered offset or where it left off
517 if it remained buffered and we're skipping back to it and it is best
518 to have ci.curpos in sync with the handle's read position - it's the
519 codec's responsibility to ensure it has the correct positions -
520 playback is sorta dumb and only has a vague idea about what to
521 buffer based upon what metadata has to say */
522 ci.curpos = bufftell(ci.audio_hid);
524 /* Pin the codec's audio data in place */
525 buf_pin_handle(ci.audio_hid, true);
528 status = codec_run_proc();
530 if (!encoder)
532 /* Codec is done with it - let it move */
533 buf_pin_handle(ci.audio_hid, false);
535 /* Notify audio that we're done for better or worse - advise of the
536 status */
537 LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status);
538 audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
542 /* Handle Q_CODEC_SEEK */
543 static void seek_codec(unsigned long time)
545 if (codec_type == AFMT_UNKNOWN)
547 logf("no codec to seek");
548 codec_queue_ack(Q_CODEC_SEEK);
549 codec_seek_complete_callback();
550 return;
553 /* Post it up one level */
554 queue_post(&codec_queue, Q_CODEC_SEEK, time);
555 codec_queue_ack(Q_CODEC_SEEK);
557 /* Have to run it again */
558 run_codec();
561 /* Handle Q_CODEC_UNLOAD */
562 static void unload_codec(void)
564 /* Tell codec to clean up */
565 codec_type = AFMT_UNKNOWN;
566 codec_close();
569 /* Handle Q_CODEC_DO_CALLBACK */
570 static void do_callback(void (* callback)(void))
572 codec_queue_ack(Q_CODEC_DO_CALLBACK);
574 if (callback)
576 cpucache_commit_discard();
577 callback();
578 cpucache_commit();
582 /* Codec thread function */
583 static void NORETURN_ATTR codec_thread(void)
585 struct queue_event ev;
587 while (1)
589 cancel_cpu_boost();
591 queue_wait(&codec_queue, &ev);
593 switch (ev.id)
595 case Q_CODEC_LOAD:
596 LOGFQUEUE("codec < Q_CODEC_LOAD");
597 load_codec((const struct codec_load_info *)ev.data);
598 break;
600 case Q_CODEC_RUN:
601 LOGFQUEUE("codec < Q_CODEC_RUN");
602 run_codec();
603 break;
605 case Q_CODEC_PAUSE:
606 LOGFQUEUE("codec < Q_CODEC_PAUSE");
607 break;
609 case Q_CODEC_SEEK:
610 LOGFQUEUE("codec < Q_CODEC_SEEK: %lu", (unsigned long)ev.data);
611 seek_codec(ev.data);
612 break;
614 case Q_CODEC_UNLOAD:
615 LOGFQUEUE("codec < Q_CODEC_UNLOAD");
616 unload_codec();
617 break;
619 case Q_CODEC_DO_CALLBACK:
620 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
621 do_callback((void (*)(void))ev.data);
622 break;
624 default:
625 LOGFQUEUE("codec < default : %ld", ev.id);
631 /** --- Miscellaneous external interfaces -- **/
633 /* Create the codec thread and init kernel objects */
634 void make_codec_thread(void)
636 queue_init(&codec_queue, false);
637 codec_thread_id = create_thread(
638 codec_thread, codec_stack, sizeof(codec_stack),
639 CREATE_THREAD_FROZEN,
640 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
641 IF_COP(, CPU));
642 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
643 codec_thread_id);
646 /* Unfreeze the codec thread */
647 void codec_thread_resume(void)
649 thread_thaw(codec_thread_id);
652 /* Is the current thread the codec thread? */
653 bool is_codec_thread(void)
655 return thread_self() == codec_thread_id;
658 #ifdef HAVE_PRIORITY_SCHEDULING
659 /* Obtain codec thread's current priority */
660 int codec_thread_get_priority(void)
662 return thread_get_priority(codec_thread_id);
665 /* Set the codec thread's priority and return the old value */
666 int codec_thread_set_priority(int priority)
668 return thread_set_priority(codec_thread_id, priority);
670 #endif /* HAVE_PRIORITY_SCHEDULING */
673 /** --- Functions for audio thread use --- **/
675 /* Load a decoder or encoder and set the format type */
676 bool codec_load(int hid, int cod_spec)
678 struct codec_load_info parm = { hid, cod_spec };
680 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d, %d", hid, cod_spec);
681 return codec_queue_send(Q_CODEC_LOAD, (intptr_t)&parm) != 0;
684 /* Begin decoding the current file */
685 void codec_go(void)
687 LOGFQUEUE("audio >| codec Q_CODEC_RUN");
688 codec_queue_send(Q_CODEC_RUN, 0);
691 /* Instruct the codec to seek to the specified time (should be properly
692 paused or stopped first to avoid possible buffering deadlock) */
693 void codec_seek(long time)
695 LOGFQUEUE("audio > codec Q_CODEC_SEEK: %ld", time);
696 codec_queue_send(Q_CODEC_SEEK, time);
699 /* Pause the codec and make it wait for further instructions inside the
700 command callback */
701 bool codec_pause(void)
703 LOGFQUEUE("audio >| codec Q_CODEC_PAUSE");
704 return codec_queue_send(Q_CODEC_PAUSE, 0) != Q_NULL;
707 /* Stop codec if running - codec stays resident if loaded */
708 void codec_stop(void)
710 /* Wait until it's in the main loop */
711 LOGFQUEUE("audio >| codec Q_CODEC_STOP");
712 while (codec_queue_send(Q_CODEC_STOP, 0) != Q_NULL);
715 /* Call the codec's exit routine and close all references */
716 void codec_unload(void)
718 codec_stop();
719 LOGFQUEUE("audio >| codec Q_CODEC_UNLOAD");
720 codec_queue_send(Q_CODEC_UNLOAD, 0);
723 /* Return the afmt type of the loaded codec - sticks until calling
724 codec_unload unless initial load failed */
725 int codec_loaded(void)
727 return codec_type;