Fix oops in r30242. I didn't want to change/reduce the buffer size.
[kugel-rb.git] / apps / codec_thread.c
blob945f0b0605e9fc471eb4bbf9d232fd2943ba8b11
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_pcmbuf_insert_callback(
212 const void *ch1, const void *ch2, int count)
214 const char *src[2] = { ch1, ch2 };
216 while (count > 0)
218 int out_count = dsp_output_count(ci.dsp, count);
219 int inp_count;
220 char *dest;
222 while (1)
224 if ((dest = pcmbuf_request_buffer(&out_count)) != NULL)
225 break;
227 cancel_cpu_boost();
229 /* It will be awhile before space is available but we want
230 "instant" response to any message */
231 queue_wait_w_tmo(&codec_queue, NULL, HZ/20);
233 if (!queue_empty(&codec_queue) &&
234 codec_check_queue__have_msg() < 0)
235 return;
238 /* Get the real input_size for output_size bytes, guarding
239 * against resampling buffer overflows. */
240 inp_count = dsp_input_count(ci.dsp, out_count);
242 if (inp_count <= 0)
243 return;
245 /* Input size has grown, no error, just don't write more than length */
246 if (inp_count > count)
247 inp_count = count;
249 out_count = dsp_process(ci.dsp, dest, src, inp_count);
251 if (out_count <= 0)
252 return;
254 pcmbuf_write_complete(out_count);
256 count -= inp_count;
260 /* helper function, not a callback */
261 static bool codec_advance_buffer_counters(size_t amount)
263 if (bufadvance(ci.audio_hid, amount) < 0)
265 ci.curpos = ci.filesize;
266 return false;
269 ci.curpos += amount;
270 return true;
273 /* copy up-to size bytes into ptr and return the actual size copied */
274 static size_t codec_filebuf_callback(void *ptr, size_t size)
276 ssize_t copy_n = bufread(ci.audio_hid, size, ptr);
278 /* Nothing requested OR nothing left */
279 if (copy_n <= 0)
280 return 0;
282 /* Update read and other position pointers */
283 codec_advance_buffer_counters(copy_n);
285 /* Return the actual amount of data copied to the buffer */
286 return copy_n;
289 static void * codec_request_buffer_callback(size_t *realsize, size_t reqsize)
291 size_t copy_n = reqsize;
292 ssize_t ret;
293 void *ptr;
295 ret = bufgetdata(ci.audio_hid, reqsize, &ptr);
296 if (ret >= 0)
297 copy_n = MIN((size_t)ret, reqsize);
298 else
299 copy_n = 0;
301 if (copy_n == 0)
302 ptr = NULL;
304 *realsize = copy_n;
305 return ptr;
308 static void codec_advance_buffer_callback(size_t amount)
310 if (!codec_advance_buffer_counters(amount))
311 return;
313 audio_codec_update_offset(ci.curpos);
316 static bool codec_seek_buffer_callback(size_t newpos)
318 logf("codec_seek_buffer_callback");
320 int ret = bufseek(ci.audio_hid, newpos);
321 if (ret == 0)
323 ci.curpos = newpos;
324 return true;
327 return false;
330 static void codec_seek_complete_callback(void)
332 logf("seek_complete");
334 /* Clear DSP */
335 dsp_configure(ci.dsp, DSP_FLUSH, 0);
337 /* Post notification to audio thread */
338 LOGFQUEUE("audio > Q_AUDIO_CODEC_SEEK_COMPLETE");
339 audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
341 /* Wait for urgent or go message */
344 queue_wait(&codec_queue, NULL);
346 while (codec_check_queue__have_msg() == 0);
349 static void codec_configure_callback(int setting, intptr_t value)
351 if (!dsp_configure(ci.dsp, setting, value))
353 logf("Illegal key: %d", setting);
357 static enum codec_command_action
358 codec_get_command_callback(intptr_t *param)
360 yield();
362 if (LIKELY(queue_empty(&codec_queue)))
363 return CODEC_ACTION_NULL; /* As you were */
365 /* Process the message - return requested action and data (if any should
366 be expected) */
367 while (1)
369 enum codec_command_action action = CODEC_ACTION_NULL;
370 struct queue_event ev;
371 queue_wait(&codec_queue, &ev);
373 switch (ev.id)
375 case Q_CODEC_RUN: /* Already running */
376 LOGFQUEUE("codec < Q_CODEC_RUN");
377 break;
379 case Q_CODEC_PAUSE: /* Stay here and wait */
380 LOGFQUEUE("codec < Q_CODEC_PAUSE");
381 codec_queue_ack(Q_CODEC_PAUSE);
382 continue;
384 case Q_CODEC_SEEK: /* Audio wants codec to seek */
385 LOGFQUEUE("codec < Q_CODEC_SEEK %ld", ev.data);
386 *param = ev.data;
387 action = CODEC_ACTION_SEEK_TIME;
388 break;
390 case Q_CODEC_STOP: /* Must only return 0 in main loop */
391 LOGFQUEUE("codec < Q_CODEC_STOP");
392 action = CODEC_ACTION_HALT;
393 break;
395 default: /* This is in error in this context. */
396 ev.id = Q_NULL;
397 logf("codec bad req %ld (%s)", ev.id, __func__);
400 codec_queue_ack(ev.id);
401 return action;
405 /* Initialize codec API */
406 void codec_init_codec_api(void)
408 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
409 CODEC_IDX_AUDIO);
410 ci.codec_get_buffer = codec_get_buffer_callback;
411 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
412 ci.set_elapsed = audio_codec_update_elapsed;
413 ci.read_filebuf = codec_filebuf_callback;
414 ci.request_buffer = codec_request_buffer_callback;
415 ci.advance_buffer = codec_advance_buffer_callback;
416 ci.seek_buffer = codec_seek_buffer_callback;
417 ci.seek_complete = codec_seek_complete_callback;
418 ci.set_offset = audio_codec_update_offset;
419 ci.configure = codec_configure_callback;
420 ci.get_command = codec_get_command_callback;
424 /** --- CODEC THREAD --- **/
426 /* Handle Q_CODEC_LOAD */
427 static void load_codec(const struct codec_load_info *ev_data)
429 int status = CODEC_ERROR;
430 /* Save a local copy so we can let the audio thread go ASAP */
431 struct codec_load_info data = *ev_data;
432 bool const encoder = type_is_encoder(data.afmt);
434 if (codec_type != AFMT_UNKNOWN)
436 /* Must have unloaded it first */
437 logf("a codec is already loaded");
438 if (data.hid >= 0)
439 bufclose(data.hid);
440 return;
443 trigger_cpu_boost();
445 if (!encoder)
447 /* Do this now because codec may set some things up at load time */
448 dsp_configure(ci.dsp, DSP_RESET, 0);
451 if (data.hid >= 0)
453 /* First try buffer load */
454 status = codec_load_buf(data.hid, &ci);
455 bufclose(data.hid);
458 if (status < 0)
460 /* Either not a valid handle or the buffer method failed */
461 const char *codec_fn = get_codec_filename(data.afmt);
462 if (codec_fn)
464 #ifdef HAVE_IO_PRIORITY
465 buf_back_off_storage(true);
466 #endif
467 status = codec_load_file(codec_fn, &ci);
468 #ifdef HAVE_IO_PRIORITY
469 buf_back_off_storage(false);
470 #endif
474 if (status >= 0)
476 codec_type = data.afmt;
477 codec_queue_ack(Q_CODEC_LOAD);
478 return;
481 /* Failed - get rid of it */
482 unload_codec();
485 /* Handle Q_CODEC_RUN */
486 static void run_codec(void)
488 bool const encoder = type_is_encoder(codec_type);
489 int status;
491 if (codec_type == AFMT_UNKNOWN)
493 logf("no codec to run");
494 return;
497 codec_queue_ack(Q_CODEC_RUN);
499 trigger_cpu_boost();
501 if (!encoder)
503 /* This will be either the initial buffered offset or where it left off
504 if it remained buffered and we're skipping back to it and it is best
505 to have ci.curpos in sync with the handle's read position - it's the
506 codec's responsibility to ensure it has the correct positions -
507 playback is sorta dumb and only has a vague idea about what to
508 buffer based upon what metadata has to say */
509 ci.curpos = bufftell(ci.audio_hid);
511 /* Pin the codec's audio data in place */
512 buf_pin_handle(ci.audio_hid, true);
515 status = codec_run_proc();
517 if (!encoder)
519 /* Codec is done with it - let it move */
520 buf_pin_handle(ci.audio_hid, false);
522 /* Notify audio that we're done for better or worse - advise of the
523 status */
524 LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status);
525 audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
529 /* Handle Q_CODEC_SEEK */
530 static void seek_codec(unsigned long time)
532 if (codec_type == AFMT_UNKNOWN)
534 logf("no codec to seek");
535 codec_queue_ack(Q_CODEC_SEEK);
536 codec_seek_complete_callback();
537 return;
540 /* Post it up one level */
541 queue_post(&codec_queue, Q_CODEC_SEEK, time);
542 codec_queue_ack(Q_CODEC_SEEK);
544 /* Have to run it again */
545 run_codec();
548 /* Handle Q_CODEC_UNLOAD */
549 static void unload_codec(void)
551 /* Tell codec to clean up */
552 codec_type = AFMT_UNKNOWN;
553 codec_close();
556 /* Handle Q_CODEC_DO_CALLBACK */
557 static void do_callback(void (* callback)(void))
559 codec_queue_ack(Q_CODEC_DO_CALLBACK);
561 if (callback)
563 cpucache_commit_discard();
564 callback();
565 cpucache_commit();
569 /* Codec thread function */
570 static void NORETURN_ATTR codec_thread(void)
572 struct queue_event ev;
574 while (1)
576 cancel_cpu_boost();
578 queue_wait(&codec_queue, &ev);
580 switch (ev.id)
582 case Q_CODEC_LOAD:
583 LOGFQUEUE("codec < Q_CODEC_LOAD");
584 load_codec((const struct codec_load_info *)ev.data);
585 break;
587 case Q_CODEC_RUN:
588 LOGFQUEUE("codec < Q_CODEC_RUN");
589 run_codec();
590 break;
592 case Q_CODEC_PAUSE:
593 LOGFQUEUE("codec < Q_CODEC_PAUSE");
594 break;
596 case Q_CODEC_SEEK:
597 LOGFQUEUE("codec < Q_CODEC_SEEK: %lu", (unsigned long)ev.data);
598 seek_codec(ev.data);
599 break;
601 case Q_CODEC_UNLOAD:
602 LOGFQUEUE("codec < Q_CODEC_UNLOAD");
603 unload_codec();
604 break;
606 case Q_CODEC_DO_CALLBACK:
607 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
608 do_callback((void (*)(void))ev.data);
609 break;
611 default:
612 LOGFQUEUE("codec < default : %ld", ev.id);
618 /** --- Miscellaneous external interfaces -- **/
620 /* Create the codec thread and init kernel objects */
621 void make_codec_thread(void)
623 queue_init(&codec_queue, false);
624 codec_thread_id = create_thread(
625 codec_thread, codec_stack, sizeof(codec_stack),
626 CREATE_THREAD_FROZEN,
627 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
628 IF_COP(, CPU));
629 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
630 codec_thread_id);
633 /* Unfreeze the codec thread */
634 void codec_thread_resume(void)
636 thread_thaw(codec_thread_id);
639 #ifdef HAVE_PRIORITY_SCHEDULING
640 /* Obtain codec thread's current priority */
641 int codec_thread_get_priority(void)
643 return thread_get_priority(codec_thread_id);
646 /* Set the codec thread's priority and return the old value */
647 int codec_thread_set_priority(int priority)
649 return thread_set_priority(codec_thread_id, priority);
651 #endif /* HAVE_PRIORITY_SCHEDULING */
654 /** --- Functions for audio thread use --- **/
656 /* Load a decoder or encoder and set the format type */
657 bool codec_load(int hid, int cod_spec)
659 struct codec_load_info parm = { hid, cod_spec };
661 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d, %d", hid, cod_spec);
662 return codec_queue_send(Q_CODEC_LOAD, (intptr_t)&parm) != 0;
665 /* Begin decoding the current file */
666 void codec_go(void)
668 LOGFQUEUE("audio >| codec Q_CODEC_RUN");
669 codec_queue_send(Q_CODEC_RUN, 0);
672 /* Instruct the codec to seek to the specified time (should be properly
673 paused or stopped first to avoid possible buffering deadlock) */
674 void codec_seek(long time)
676 LOGFQUEUE("audio > codec Q_CODEC_SEEK: %ld", time);
677 codec_queue_send(Q_CODEC_SEEK, time);
680 /* Pause the codec and make it wait for further instructions inside the
681 command callback */
682 bool codec_pause(void)
684 LOGFQUEUE("audio >| codec Q_CODEC_PAUSE");
685 return codec_queue_send(Q_CODEC_PAUSE, 0) != Q_NULL;
688 /* Stop codec if running - codec stays resident if loaded */
689 void codec_stop(void)
691 /* Wait until it's in the main loop */
692 LOGFQUEUE("audio >| codec Q_CODEC_STOP");
693 while (codec_queue_send(Q_CODEC_STOP, 0) != Q_NULL);
696 /* Call the codec's exit routine and close all references */
697 void codec_unload(void)
699 codec_stop();
700 LOGFQUEUE("audio >| codec Q_CODEC_UNLOAD");
701 codec_queue_send(Q_CODEC_UNLOAD, 0);
704 /* Return the afmt type of the loaded codec - sticks until calling
705 codec_unload unless initial load failed */
706 int codec_loaded(void)
708 return codec_type;