Prepare new maemo release
[maemo-rb.git] / apps / codec_thread.c
blob308b2ff9824ad51a49a89e6d61cf1f546415c821
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_core.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 struct dsp_buffer src;
217 src.remcount = count;
218 src.pin[0] = ch1;
219 src.pin[1] = ch2;
220 src.proc_mask = 0;
222 while (LIKELY(queue_empty(&codec_queue)) ||
223 codec_check_queue__have_msg() >= 0)
225 struct dsp_buffer dst;
226 dst.remcount = 0;
227 dst.bufcount = MAX(src.remcount, 1024); /* Arbitrary min request */
229 if ((dst.p16out = pcmbuf_request_buffer(&dst.bufcount)) == NULL)
231 cancel_cpu_boost();
233 /* It may be awhile before space is available but we want
234 "instant" response to any message */
235 queue_wait_w_tmo(&codec_queue, NULL, HZ/20);
237 else
239 dsp_process(ci.dsp, &src, &dst);
241 if (dst.remcount > 0)
243 pcmbuf_write_complete(dst.remcount, ci.id3->elapsed,
244 ci.id3->offset);
246 else if (src.remcount <= 0)
248 return; /* No input remains and DSP purged */
254 /* helper function, not a callback */
255 static bool codec_advance_buffer_counters(size_t amount)
257 if (bufadvance(ci.audio_hid, amount) < 0)
259 ci.curpos = ci.filesize;
260 return false;
263 ci.curpos += amount;
264 return true;
267 /* copy up-to size bytes into ptr and return the actual size copied */
268 static size_t codec_filebuf_callback(void *ptr, size_t size)
270 ssize_t copy_n = bufread(ci.audio_hid, size, ptr);
272 /* Nothing requested OR nothing left */
273 if (copy_n <= 0)
274 return 0;
276 /* Update read and other position pointers */
277 codec_advance_buffer_counters(copy_n);
279 /* Return the actual amount of data copied to the buffer */
280 return copy_n;
283 static void * codec_request_buffer_callback(size_t *realsize, size_t reqsize)
285 size_t copy_n = reqsize;
286 ssize_t ret;
287 void *ptr;
289 ret = bufgetdata(ci.audio_hid, reqsize, &ptr);
290 if (ret >= 0)
291 copy_n = MIN((size_t)ret, reqsize);
292 else
293 copy_n = 0;
295 if (copy_n == 0)
296 ptr = NULL;
298 *realsize = copy_n;
299 return ptr;
302 static void codec_advance_buffer_callback(size_t amount)
304 if (!codec_advance_buffer_counters(amount))
305 return;
307 audio_codec_update_offset(ci.curpos);
310 static bool codec_seek_buffer_callback(size_t newpos)
312 logf("codec_seek_buffer_callback");
314 int ret = bufseek(ci.audio_hid, newpos);
315 if (ret == 0)
317 ci.curpos = newpos;
318 return true;
321 return false;
324 static void codec_seek_complete_callback(void)
326 logf("seek_complete");
328 /* Clear DSP */
329 dsp_configure(ci.dsp, DSP_FLUSH, 0);
331 /* Sync position */
332 audio_codec_update_offset(ci.curpos);
334 /* Post notification to audio thread */
335 audio_codec_seek_complete();
337 /* Wait for urgent or go message */
340 queue_wait(&codec_queue, NULL);
342 while (codec_check_queue__have_msg() == 0);
345 static void codec_configure_callback(int setting, intptr_t value)
347 dsp_configure(ci.dsp, setting, value);
350 static enum codec_command_action
351 codec_get_command_callback(intptr_t *param)
353 yield();
355 if (LIKELY(queue_empty(&codec_queue)))
356 return CODEC_ACTION_NULL; /* As you were */
358 /* Process the message - return requested action and data (if any should
359 be expected) */
360 while (1)
362 enum codec_command_action action = CODEC_ACTION_NULL;
363 struct queue_event ev;
365 queue_peek(&codec_queue, &ev); /* Find out what it is */
367 long id = ev.id;
369 switch (id)
371 case Q_CODEC_RUN: /* Already running */
372 LOGFQUEUE("codec < Q_CODEC_RUN");
373 break;
375 case Q_CODEC_PAUSE: /* Stay here and wait */
376 LOGFQUEUE("codec < Q_CODEC_PAUSE");
377 queue_wait(&codec_queue, &ev); /* Remove message */
378 codec_queue_ack(Q_CODEC_PAUSE);
379 queue_wait(&codec_queue, NULL); /* Wait for next (no remove) */
380 continue;
382 case Q_CODEC_SEEK: /* Audio wants codec to seek */
383 LOGFQUEUE("codec < Q_CODEC_SEEK %ld", ev.data);
384 *param = ev.data;
385 action = CODEC_ACTION_SEEK_TIME;
386 trigger_cpu_boost();
387 break;
389 case Q_CODEC_STOP: /* Must only return 0 in main loop */
390 LOGFQUEUE("codec < Q_CODEC_STOP");
391 dsp_configure(ci.dsp, DSP_FLUSH, 0); /* Discontinuity */
392 return CODEC_ACTION_HALT; /* Leave in queue */
394 default: /* This is in error in this context. */
395 logf("codec bad req %ld (%s)", ev.id, __func__);
396 id = Q_NULL;
399 queue_wait(&codec_queue, &ev); /* Actually remove it */
400 codec_queue_ack(id);
401 return action;
405 static bool codec_loop_track_callback(void)
407 return global_settings.repeat_mode == REPEAT_ONE;
411 /** --- CODEC THREAD --- **/
413 /* Handle Q_CODEC_LOAD */
414 static void load_codec(const struct codec_load_info *ev_data)
416 int status = CODEC_ERROR;
417 /* Save a local copy so we can let the audio thread go ASAP */
418 struct codec_load_info data = *ev_data;
419 bool const encoder = type_is_encoder(data.afmt);
421 if (codec_type != AFMT_UNKNOWN)
423 /* Must have unloaded it first */
424 logf("a codec is already loaded");
425 if (data.hid >= 0)
426 bufclose(data.hid);
427 return;
430 trigger_cpu_boost();
432 if (!encoder)
434 /* Do this now because codec may set some things up at load time */
435 dsp_configure(ci.dsp, DSP_RESET, 0);
438 if (data.hid >= 0)
440 /* First try buffer load */
441 status = codec_load_buf(data.hid, &ci);
442 bufclose(data.hid);
445 if (status < 0)
447 /* Either not a valid handle or the buffer method failed */
448 const char *codec_fn = get_codec_filename(data.afmt);
449 if (codec_fn)
451 #ifdef HAVE_IO_PRIORITY
452 buf_back_off_storage(true);
453 #endif
454 status = codec_load_file(codec_fn, &ci);
455 #ifdef HAVE_IO_PRIORITY
456 buf_back_off_storage(false);
457 #endif
461 if (status >= 0)
463 codec_type = data.afmt;
464 codec_queue_ack(Q_CODEC_LOAD);
465 return;
468 /* Failed - get rid of it */
469 unload_codec();
472 /* Handle Q_CODEC_RUN */
473 static void run_codec(void)
475 bool const encoder = type_is_encoder(codec_type);
476 int status;
478 if (codec_type == AFMT_UNKNOWN)
480 logf("no codec to run");
481 return;
484 codec_queue_ack(Q_CODEC_RUN);
486 trigger_cpu_boost();
488 if (!encoder)
490 /* This will be either the initial buffered offset or where it left off
491 if it remained buffered and we're skipping back to it and it is best
492 to have ci.curpos in sync with the handle's read position - it's the
493 codec's responsibility to ensure it has the correct positions -
494 playback is sorta dumb and only has a vague idea about what to
495 buffer based upon what metadata has to say */
496 ci.curpos = bufftell(ci.audio_hid);
498 /* Pin the codec's audio data in place */
499 buf_pin_handle(ci.audio_hid, true);
502 status = codec_run_proc();
504 if (!encoder)
506 /* Codec is done with it - let it move */
507 buf_pin_handle(ci.audio_hid, false);
509 /* Notify audio that we're done for better or worse - advise of the
510 status */
511 audio_codec_complete(status);
515 /* Handle Q_CODEC_SEEK */
516 static void seek_codec(unsigned long time)
518 if (codec_type == AFMT_UNKNOWN)
520 logf("no codec to seek");
521 codec_queue_ack(Q_CODEC_SEEK);
522 codec_seek_complete_callback();
523 return;
526 /* Post it up one level */
527 queue_post(&codec_queue, Q_CODEC_SEEK, time);
528 codec_queue_ack(Q_CODEC_SEEK);
530 /* Have to run it again */
531 run_codec();
534 /* Handle Q_CODEC_UNLOAD */
535 static void unload_codec(void)
537 /* Tell codec to clean up */
538 codec_type = AFMT_UNKNOWN;
539 codec_close();
542 /* Handle Q_CODEC_DO_CALLBACK */
543 static void do_callback(void (* callback)(void))
545 codec_queue_ack(Q_CODEC_DO_CALLBACK);
547 if (callback)
549 commit_discard_idcache();
550 callback();
551 commit_dcache();
555 /* Codec thread function */
556 static void NORETURN_ATTR codec_thread(void)
558 struct queue_event ev;
560 while (1)
562 cancel_cpu_boost();
564 queue_wait(&codec_queue, &ev);
566 switch (ev.id)
568 case Q_CODEC_LOAD:
569 LOGFQUEUE("codec < Q_CODEC_LOAD");
570 load_codec((const struct codec_load_info *)ev.data);
571 break;
573 case Q_CODEC_RUN:
574 LOGFQUEUE("codec < Q_CODEC_RUN");
575 run_codec();
576 break;
578 case Q_CODEC_PAUSE:
579 LOGFQUEUE("codec < Q_CODEC_PAUSE");
580 break;
582 case Q_CODEC_SEEK:
583 LOGFQUEUE("codec < Q_CODEC_SEEK: %lu", (unsigned long)ev.data);
584 seek_codec(ev.data);
585 break;
587 case Q_CODEC_UNLOAD:
588 LOGFQUEUE("codec < Q_CODEC_UNLOAD");
589 unload_codec();
590 break;
592 case Q_CODEC_DO_CALLBACK:
593 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
594 do_callback((void (*)(void))ev.data);
595 break;
597 default:
598 LOGFQUEUE("codec < default : %ld", ev.id);
604 /** --- Miscellaneous external interfaces -- **/
606 /* Initialize playback's codec interface */
607 void codec_thread_init(void)
609 /* Init API */
610 ci.dsp = dsp_get_config(CODEC_IDX_AUDIO);
611 ci.codec_get_buffer = codec_get_buffer_callback;
612 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
613 ci.set_elapsed = audio_codec_update_elapsed;
614 ci.read_filebuf = codec_filebuf_callback;
615 ci.request_buffer = codec_request_buffer_callback;
616 ci.advance_buffer = codec_advance_buffer_callback;
617 ci.seek_buffer = codec_seek_buffer_callback;
618 ci.seek_complete = codec_seek_complete_callback;
619 ci.set_offset = audio_codec_update_offset;
620 ci.configure = codec_configure_callback;
621 ci.get_command = codec_get_command_callback;
622 ci.loop_track = codec_loop_track_callback;
624 /* Init threading */
625 queue_init(&codec_queue, false);
626 codec_thread_id = create_thread(
627 codec_thread, codec_stack, sizeof(codec_stack), 0,
628 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
629 IF_COP(, CPU));
630 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
631 codec_thread_id);
634 #ifdef HAVE_PRIORITY_SCHEDULING
635 /* Obtain codec thread's current priority */
636 int codec_thread_get_priority(void)
638 return thread_get_priority(codec_thread_id);
641 /* Set the codec thread's priority and return the old value */
642 int codec_thread_set_priority(int priority)
644 return thread_set_priority(codec_thread_id, priority);
646 #endif /* HAVE_PRIORITY_SCHEDULING */
649 /** --- Functions for audio thread use --- **/
651 /* Load a decoder or encoder and set the format type */
652 bool codec_load(int hid, int cod_spec)
654 struct codec_load_info parm = { hid, cod_spec };
656 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d, %d", hid, cod_spec);
657 return codec_queue_send(Q_CODEC_LOAD, (intptr_t)&parm) != 0;
660 /* Begin decoding the current file */
661 void codec_go(void)
663 LOGFQUEUE("audio >| codec Q_CODEC_RUN");
664 codec_queue_send(Q_CODEC_RUN, 0);
667 /* Instruct the codec to seek to the specified time (should be properly
668 paused or stopped first to avoid possible buffering deadlock) */
669 void codec_seek(long time)
671 LOGFQUEUE("audio > codec Q_CODEC_SEEK: %ld", time);
672 codec_queue_send(Q_CODEC_SEEK, time);
675 /* Pause the codec and make it wait for further instructions inside the
676 command callback */
677 bool codec_pause(void)
679 LOGFQUEUE("audio >| codec Q_CODEC_PAUSE");
680 return codec_queue_send(Q_CODEC_PAUSE, 0) != Q_NULL;
683 /* Stop codec if running - codec stays resident if loaded */
684 void codec_stop(void)
686 /* Wait until it's in the main loop */
687 LOGFQUEUE("audio >| codec Q_CODEC_STOP");
688 while (codec_queue_send(Q_CODEC_STOP, 0) != Q_NULL);
691 /* Call the codec's exit routine and close all references */
692 void codec_unload(void)
694 codec_stop();
695 LOGFQUEUE("audio >| codec Q_CODEC_UNLOAD");
696 codec_queue_send(Q_CODEC_UNLOAD, 0);
699 /* Return the afmt type of the loaded codec - sticks until calling
700 codec_unload unless initial load failed */
701 int codec_loaded(void)
703 return codec_type;