Fix reds. No need for #ifdef to save buttons anymore.
[maemo-rb.git] / apps / codec_thread.c
blob39db7410549ef96f14aa0dfc9d13454d677fef17
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"
33 #include "settings.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
60 /* Variables are commented with the threads that use them:
61 * A=audio, C=codec
62 * - = reads only
64 * Unless otherwise noted, the extern variables are located
65 * in playback.c.
68 /* Q_LOAD_CODEC parameter data */
69 struct codec_load_info
71 int hid; /* audio handle id (specify < 0 to use afmt) */
72 int afmt; /* codec specification (AFMT_*) */
76 /** --- Main state control --- **/
78 static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */
80 /* Private interfaces to main playback control */
81 extern void audio_codec_update_elapsed(unsigned long elapsed);
82 extern void audio_codec_update_offset(size_t offset);
83 extern void audio_codec_complete(int status);
84 extern void audio_codec_seek_complete(void);
85 extern struct codec_api ci; /* from codecs.c */
87 /* Codec thread */
88 static unsigned int codec_thread_id; /* For modifying thread priority later */
89 static struct event_queue codec_queue SHAREDBSS_ATTR;
90 static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR;
91 static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] IBSS_ATTR;
92 static const char codec_thread_name[] = "codec";
94 static void unload_codec(void);
96 /* Messages are only ever sent one at a time to the codec from the audio
97 thread. This is important for correct operation unless playback is
98 stopped. */
100 /* static routines */
101 static void codec_queue_ack(intptr_t ackme)
103 queue_reply(&codec_queue, ackme);
106 static intptr_t codec_queue_send(long id, intptr_t data)
108 return queue_send(&codec_queue, id, data);
111 /* Poll the state of the codec queue. Returns < 0 if the message is urgent
112 and any state should exit, > 0 if it's a run message (and it was
113 scrubbed), 0 if message was ignored. */
114 static int codec_check_queue__have_msg(void)
116 struct queue_event ev;
118 queue_peek(&codec_queue, &ev);
120 /* Seek, pause or stop? Just peek and return if so. Codec
121 must handle the command after returing. Inserts will not
122 be allowed until it complies. */
123 switch (ev.id)
125 case Q_CODEC_SEEK:
126 LOGFQUEUE("codec - Q_CODEC_SEEK", ev.id);
127 return -1;
128 case Q_CODEC_PAUSE:
129 LOGFQUEUE("codec - Q_CODEC_PAUSE", ev.id);
130 return -1;
131 case Q_CODEC_STOP:
132 LOGFQUEUE("codec - Q_CODEC_STOP", ev.id);
133 return -1;
136 /* This is in error in this context unless it's "go, go, go!" */
137 queue_wait(&codec_queue, &ev);
139 if (ev.id == Q_CODEC_RUN)
141 logf("codec < Q_CODEC_RUN: already running!");
142 codec_queue_ack(Q_CODEC_RUN);
143 return 1;
146 /* Ignore it */
147 logf("codec < bad req %ld (%s)", ev.id, __func__);
148 codec_queue_ack(Q_NULL);
149 return 0;
152 /* Does the audio format type equal CODEC_TYPE_ENCODER? */
153 static inline bool type_is_encoder(int afmt)
155 #ifdef AUDIO_HAVE_RECORDING
156 return (afmt & CODEC_TYPE_MASK) == CODEC_TYPE_ENCODER;
157 #else
158 return false;
159 (void)afmt;
160 #endif
163 /**************************************/
166 /** --- Miscellaneous external functions --- **/
167 const char * get_codec_filename(int cod_spec)
169 const char *fname;
171 #ifdef HAVE_RECORDING
172 /* Can choose decoder or encoder if one available */
173 int type = cod_spec & CODEC_TYPE_MASK;
174 int afmt = cod_spec & CODEC_AFMT_MASK;
176 if ((unsigned)afmt >= AFMT_NUM_CODECS)
177 type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
179 fname = (type == CODEC_TYPE_ENCODER) ?
180 audio_formats[afmt].codec_enc_root_fn :
181 audio_formats[afmt].codec_root_fn;
183 logf("%s: %d - %s",
184 (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
185 afmt, fname ? fname : "<unknown>");
186 #else /* !HAVE_RECORDING */
187 /* Always decoder */
188 if ((unsigned)cod_spec >= AFMT_NUM_CODECS)
189 cod_spec = AFMT_UNKNOWN;
190 fname = audio_formats[cod_spec].codec_root_fn;
191 logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>");
192 #endif /* HAVE_RECORDING */
194 return fname;
197 /* Borrow the codec thread and return the ID */
198 void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
200 /* Set id before telling thread to call something; it may be
201 * needed before this function returns. */
202 if (id != NULL)
203 *id = codec_thread_id;
205 /* Codec thread will signal just before entering callback */
206 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
207 codec_queue_send(Q_CODEC_DO_CALLBACK, (intptr_t)fn);
211 /** --- codec API callbacks --- **/
213 static void codec_pcmbuf_insert_callback(
214 const void *ch1, const void *ch2, int count)
216 const char *src[2] = { ch1, ch2 };
218 while (count > 0)
220 int out_count = dsp_output_count(ci.dsp, count);
221 int inp_count;
222 char *dest;
224 while (1)
226 if ((dest = pcmbuf_request_buffer(&out_count)) != NULL)
227 break;
229 cancel_cpu_boost();
231 /* It will be awhile before space is available but we want
232 "instant" response to any message */
233 queue_wait_w_tmo(&codec_queue, NULL, HZ/20);
235 if (!queue_empty(&codec_queue) &&
236 codec_check_queue__have_msg() < 0)
237 return;
240 /* Get the real input_size for output_size bytes, guarding
241 * against resampling buffer overflows. */
242 inp_count = dsp_input_count(ci.dsp, out_count);
244 if (inp_count <= 0)
245 return;
247 /* Input size has grown, no error, just don't write more than length */
248 if (inp_count > count)
249 inp_count = count;
251 out_count = dsp_process(ci.dsp, dest, src, inp_count);
253 if (out_count <= 0)
254 return;
256 pcmbuf_write_complete(out_count, ci.id3->elapsed, ci.id3->offset);
258 count -= inp_count;
262 /* helper function, not a callback */
263 static bool codec_advance_buffer_counters(size_t amount)
265 if (bufadvance(ci.audio_hid, amount) < 0)
267 ci.curpos = ci.filesize;
268 return false;
271 ci.curpos += amount;
272 return true;
275 /* copy up-to size bytes into ptr and return the actual size copied */
276 static size_t codec_filebuf_callback(void *ptr, size_t size)
278 ssize_t copy_n = bufread(ci.audio_hid, size, ptr);
280 /* Nothing requested OR nothing left */
281 if (copy_n <= 0)
282 return 0;
284 /* Update read and other position pointers */
285 codec_advance_buffer_counters(copy_n);
287 /* Return the actual amount of data copied to the buffer */
288 return copy_n;
291 static void * codec_request_buffer_callback(size_t *realsize, size_t reqsize)
293 size_t copy_n = reqsize;
294 ssize_t ret;
295 void *ptr;
297 ret = bufgetdata(ci.audio_hid, reqsize, &ptr);
298 if (ret >= 0)
299 copy_n = MIN((size_t)ret, reqsize);
300 else
301 copy_n = 0;
303 if (copy_n == 0)
304 ptr = NULL;
306 *realsize = copy_n;
307 return ptr;
310 static void codec_advance_buffer_callback(size_t amount)
312 if (!codec_advance_buffer_counters(amount))
313 return;
315 audio_codec_update_offset(ci.curpos);
318 static bool codec_seek_buffer_callback(size_t newpos)
320 logf("codec_seek_buffer_callback");
322 int ret = bufseek(ci.audio_hid, newpos);
323 if (ret == 0)
325 ci.curpos = newpos;
326 return true;
329 return false;
332 static void codec_seek_complete_callback(void)
334 logf("seek_complete");
336 /* Clear DSP */
337 dsp_configure(ci.dsp, DSP_FLUSH, 0);
339 /* Sync position */
340 audio_codec_update_offset(ci.curpos);
342 /* Post notification to audio thread */
343 audio_codec_seek_complete();
345 /* Wait for urgent or go message */
348 queue_wait(&codec_queue, NULL);
350 while (codec_check_queue__have_msg() == 0);
353 static void codec_configure_callback(int setting, intptr_t value)
355 if (!dsp_configure(ci.dsp, setting, value))
357 logf("Illegal key: %d", setting);
361 static enum codec_command_action
362 codec_get_command_callback(intptr_t *param)
364 yield();
366 if (LIKELY(queue_empty(&codec_queue)))
367 return CODEC_ACTION_NULL; /* As you were */
369 /* Process the message - return requested action and data (if any should
370 be expected) */
371 while (1)
373 enum codec_command_action action = CODEC_ACTION_NULL;
374 struct queue_event ev;
375 queue_wait(&codec_queue, &ev);
377 switch (ev.id)
379 case Q_CODEC_RUN: /* Already running */
380 LOGFQUEUE("codec < Q_CODEC_RUN");
381 break;
383 case Q_CODEC_PAUSE: /* Stay here and wait */
384 LOGFQUEUE("codec < Q_CODEC_PAUSE");
385 codec_queue_ack(Q_CODEC_PAUSE);
386 continue;
388 case Q_CODEC_SEEK: /* Audio wants codec to seek */
389 LOGFQUEUE("codec < Q_CODEC_SEEK %ld", ev.data);
390 *param = ev.data;
391 action = CODEC_ACTION_SEEK_TIME;
392 break;
394 case Q_CODEC_STOP: /* Must only return 0 in main loop */
395 LOGFQUEUE("codec < Q_CODEC_STOP");
396 action = CODEC_ACTION_HALT;
397 break;
399 default: /* This is in error in this context. */
400 ev.id = Q_NULL;
401 logf("codec bad req %ld (%s)", ev.id, __func__);
404 codec_queue_ack(ev.id);
405 return action;
409 static bool codec_loop_track_callback(void)
411 return global_settings.repeat_mode == REPEAT_ONE;
415 /** --- CODEC THREAD --- **/
417 /* Handle Q_CODEC_LOAD */
418 static void load_codec(const struct codec_load_info *ev_data)
420 int status = CODEC_ERROR;
421 /* Save a local copy so we can let the audio thread go ASAP */
422 struct codec_load_info data = *ev_data;
423 bool const encoder = type_is_encoder(data.afmt);
425 if (codec_type != AFMT_UNKNOWN)
427 /* Must have unloaded it first */
428 logf("a codec is already loaded");
429 if (data.hid >= 0)
430 bufclose(data.hid);
431 return;
434 trigger_cpu_boost();
436 if (!encoder)
438 /* Do this now because codec may set some things up at load time */
439 dsp_configure(ci.dsp, DSP_RESET, 0);
442 if (data.hid >= 0)
444 /* First try buffer load */
445 status = codec_load_buf(data.hid, &ci);
446 bufclose(data.hid);
449 if (status < 0)
451 /* Either not a valid handle or the buffer method failed */
452 const char *codec_fn = get_codec_filename(data.afmt);
453 if (codec_fn)
455 #ifdef HAVE_IO_PRIORITY
456 buf_back_off_storage(true);
457 #endif
458 status = codec_load_file(codec_fn, &ci);
459 #ifdef HAVE_IO_PRIORITY
460 buf_back_off_storage(false);
461 #endif
465 if (status >= 0)
467 codec_type = data.afmt;
468 codec_queue_ack(Q_CODEC_LOAD);
469 return;
472 /* Failed - get rid of it */
473 unload_codec();
476 /* Handle Q_CODEC_RUN */
477 static void run_codec(void)
479 bool const encoder = type_is_encoder(codec_type);
480 int status;
482 if (codec_type == AFMT_UNKNOWN)
484 logf("no codec to run");
485 return;
488 codec_queue_ack(Q_CODEC_RUN);
490 trigger_cpu_boost();
492 if (!encoder)
494 /* This will be either the initial buffered offset or where it left off
495 if it remained buffered and we're skipping back to it and it is best
496 to have ci.curpos in sync with the handle's read position - it's the
497 codec's responsibility to ensure it has the correct positions -
498 playback is sorta dumb and only has a vague idea about what to
499 buffer based upon what metadata has to say */
500 ci.curpos = bufftell(ci.audio_hid);
502 /* Pin the codec's audio data in place */
503 buf_pin_handle(ci.audio_hid, true);
506 status = codec_run_proc();
508 if (!encoder)
510 /* Codec is done with it - let it move */
511 buf_pin_handle(ci.audio_hid, false);
513 /* Notify audio that we're done for better or worse - advise of the
514 status */
515 audio_codec_complete(status);
519 /* Handle Q_CODEC_SEEK */
520 static void seek_codec(unsigned long time)
522 if (codec_type == AFMT_UNKNOWN)
524 logf("no codec to seek");
525 codec_queue_ack(Q_CODEC_SEEK);
526 codec_seek_complete_callback();
527 return;
530 /* Post it up one level */
531 queue_post(&codec_queue, Q_CODEC_SEEK, time);
532 codec_queue_ack(Q_CODEC_SEEK);
534 /* Have to run it again */
535 run_codec();
538 /* Handle Q_CODEC_UNLOAD */
539 static void unload_codec(void)
541 /* Tell codec to clean up */
542 codec_type = AFMT_UNKNOWN;
543 codec_close();
546 /* Handle Q_CODEC_DO_CALLBACK */
547 static void do_callback(void (* callback)(void))
549 codec_queue_ack(Q_CODEC_DO_CALLBACK);
551 if (callback)
553 commit_discard_idcache();
554 callback();
555 commit_dcache();
559 /* Codec thread function */
560 static void NORETURN_ATTR codec_thread(void)
562 struct queue_event ev;
564 while (1)
566 cancel_cpu_boost();
568 queue_wait(&codec_queue, &ev);
570 switch (ev.id)
572 case Q_CODEC_LOAD:
573 LOGFQUEUE("codec < Q_CODEC_LOAD");
574 load_codec((const struct codec_load_info *)ev.data);
575 break;
577 case Q_CODEC_RUN:
578 LOGFQUEUE("codec < Q_CODEC_RUN");
579 run_codec();
580 break;
582 case Q_CODEC_PAUSE:
583 LOGFQUEUE("codec < Q_CODEC_PAUSE");
584 break;
586 case Q_CODEC_SEEK:
587 LOGFQUEUE("codec < Q_CODEC_SEEK: %lu", (unsigned long)ev.data);
588 seek_codec(ev.data);
589 break;
591 case Q_CODEC_UNLOAD:
592 LOGFQUEUE("codec < Q_CODEC_UNLOAD");
593 unload_codec();
594 break;
596 case Q_CODEC_DO_CALLBACK:
597 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
598 do_callback((void (*)(void))ev.data);
599 break;
601 default:
602 LOGFQUEUE("codec < default : %ld", ev.id);
608 /** --- Miscellaneous external interfaces -- **/
610 /* Initialize playback's codec interface */
611 void codec_thread_init(void)
613 /* Init API */
614 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
615 CODEC_IDX_AUDIO);
616 ci.codec_get_buffer = codec_get_buffer_callback;
617 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
618 ci.set_elapsed = audio_codec_update_elapsed;
619 ci.read_filebuf = codec_filebuf_callback;
620 ci.request_buffer = codec_request_buffer_callback;
621 ci.advance_buffer = codec_advance_buffer_callback;
622 ci.seek_buffer = codec_seek_buffer_callback;
623 ci.seek_complete = codec_seek_complete_callback;
624 ci.set_offset = audio_codec_update_offset;
625 ci.configure = codec_configure_callback;
626 ci.get_command = codec_get_command_callback;
627 ci.loop_track = codec_loop_track_callback;
629 /* Init threading */
630 queue_init(&codec_queue, false);
631 codec_thread_id = create_thread(
632 codec_thread, codec_stack, sizeof(codec_stack), 0,
633 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
634 IF_COP(, CPU));
635 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
636 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;