RaaA: Create full config directory tree during initialization
[maemo-rb.git] / apps / codec_thread.c
blobf166f2ba18c37e4033c8b3e5965643929e6c3593
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 ****************************************************************************/
22 #include "config.h"
23 #include "system.h"
24 #include "playback.h"
25 #include "codec_thread.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 */
71 /* Type of codec loaded? (C/A) */
72 static int current_codectype SHAREDBSS_ATTR = AFMT_UNKNOWN;
74 extern struct mp3entry *thistrack_id3, /* the currently playing track */
75 *othertrack_id3; /* prev track during track-change-transition, or end of playlist,
76 * next track otherwise */
78 /* Track change controls */
79 extern struct event_queue audio_queue SHAREDBSS_ATTR;
82 extern struct codec_api ci; /* from codecs.c */
84 /* Codec thread */
85 static unsigned int codec_thread_id; /* For modifying thread priority later */
86 static struct event_queue codec_queue SHAREDBSS_ATTR;
87 static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR;
88 static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
89 IBSS_ATTR;
90 static const char codec_thread_name[] = "codec";
92 /* static routines */
93 static void codec_queue_ack(intptr_t ackme)
95 queue_reply(&codec_queue, ackme);
98 static intptr_t codec_queue_send(long id, intptr_t data)
100 return queue_send(&codec_queue, id, data);
103 /**************************************/
105 /** misc external functions */
107 /* Used to check whether a new codec must be loaded. See array audio_formats[]
108 * in metadata.c */
109 int get_codec_base_type(int type)
111 int base_type = type;
112 switch (type) {
113 case AFMT_MPA_L1:
114 case AFMT_MPA_L2:
115 case AFMT_MPA_L3:
116 base_type = AFMT_MPA_L3;
117 break;
118 case AFMT_MPC_SV7:
119 case AFMT_MPC_SV8:
120 base_type = AFMT_MPC_SV7;
121 break;
122 case AFMT_MP4_AAC:
123 case AFMT_MP4_AAC_HE:
124 base_type = AFMT_MP4_AAC;
125 break;
126 case AFMT_SAP:
127 case AFMT_CMC:
128 case AFMT_CM3:
129 case AFMT_CMR:
130 case AFMT_CMS:
131 case AFMT_DMC:
132 case AFMT_DLT:
133 case AFMT_MPT:
134 case AFMT_MPD:
135 case AFMT_RMT:
136 case AFMT_TMC:
137 case AFMT_TM8:
138 case AFMT_TM2:
139 base_type = AFMT_SAP;
140 break;
141 default:
142 break;
145 return base_type;
148 const char *get_codec_filename(int cod_spec)
150 const char *fname;
152 #ifdef HAVE_RECORDING
153 /* Can choose decoder or encoder if one available */
154 int type = cod_spec & CODEC_TYPE_MASK;
155 int afmt = cod_spec & CODEC_AFMT_MASK;
157 if ((unsigned)afmt >= AFMT_NUM_CODECS)
158 type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
160 fname = (type == CODEC_TYPE_ENCODER) ?
161 audio_formats[afmt].codec_enc_root_fn :
162 audio_formats[afmt].codec_root_fn;
164 logf("%s: %d - %s",
165 (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
166 afmt, fname ? fname : "<unknown>");
167 #else /* !HAVE_RECORDING */
168 /* Always decoder */
169 if ((unsigned)cod_spec >= AFMT_NUM_CODECS)
170 cod_spec = AFMT_UNKNOWN;
171 fname = audio_formats[cod_spec].codec_root_fn;
172 logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>");
173 #endif /* HAVE_RECORDING */
175 return fname;
176 } /* get_codec_filename */
178 /* Borrow the codec thread and return the ID */
179 void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
181 /* Set id before telling thread to call something; it may be
182 * needed before this function returns. */
183 if (id != NULL)
184 *id = codec_thread_id;
186 /* Codec thread will signal just before entering callback */
187 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
188 codec_queue_send(Q_CODEC_DO_CALLBACK, (intptr_t)fn);
192 /** codec API callbacks */
194 static void* codec_get_buffer(size_t *size)
196 if (codec_size >= CODEC_SIZE)
197 return NULL;
198 *size = CODEC_SIZE - codec_size;
199 return &codecbuf[codec_size];
202 static void codec_pcmbuf_insert_callback(
203 const void *ch1, const void *ch2, int count)
205 const char *src[2] = { ch1, ch2 };
207 while (count > 0)
209 int out_count = dsp_output_count(ci.dsp, count);
210 int inp_count;
211 char *dest;
213 /* Prevent audio from a previous track from playing */
214 if (ci.new_track || ci.stop_codec)
215 return;
217 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
219 cancel_cpu_boost();
220 sleep(1);
221 if (ci.seek_time || ci.new_track || ci.stop_codec)
222 return;
225 /* Get the real input_size for output_size bytes, guarding
226 * against resampling buffer overflows. */
227 inp_count = dsp_input_count(ci.dsp, out_count);
229 if (inp_count <= 0)
230 return;
232 /* Input size has grown, no error, just don't write more than length */
233 if (inp_count > count)
234 inp_count = count;
236 out_count = dsp_process(ci.dsp, dest, src, inp_count);
238 if (out_count <= 0)
239 return;
241 pcmbuf_write_complete(out_count);
243 count -= inp_count;
245 } /* codec_pcmbuf_insert_callback */
247 static void codec_set_elapsed_callback(unsigned long value)
249 if (ci.seek_time)
250 return;
252 #ifdef AB_REPEAT_ENABLE
253 ab_position_report(value);
254 #endif
256 unsigned long latency = pcmbuf_get_latency();
257 if (value < latency)
258 thistrack_id3->elapsed = 0;
259 else
261 unsigned long elapsed = value - latency;
262 if (elapsed > thistrack_id3->elapsed ||
263 elapsed < thistrack_id3->elapsed - 2)
265 thistrack_id3->elapsed = elapsed;
270 static void codec_set_offset_callback(size_t value)
272 if (ci.seek_time)
273 return;
275 unsigned long latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
276 if (value < latency)
277 thistrack_id3->offset = 0;
278 else
279 thistrack_id3->offset = value - latency;
282 /* helper function, not a callback */
283 static void codec_advance_buffer_counters(size_t amount)
285 bufadvance(get_audio_hid(), amount);
286 ci.curpos += amount;
289 /* copy up-to size bytes into ptr and return the actual size copied */
290 static size_t codec_filebuf_callback(void *ptr, size_t size)
292 ssize_t copy_n;
294 if (ci.stop_codec)
295 return 0;
297 copy_n = bufread(get_audio_hid(), size, ptr);
299 /* Nothing requested OR nothing left */
300 if (copy_n == 0)
301 return 0;
303 /* Update read and other position pointers */
304 codec_advance_buffer_counters(copy_n);
306 /* Return the actual amount of data copied to the buffer */
307 return copy_n;
308 } /* codec_filebuf_callback */
310 static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
312 size_t copy_n = reqsize;
313 ssize_t ret;
314 void *ptr;
316 ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
317 if (ret >= 0)
318 copy_n = MIN((size_t)ret, reqsize);
319 else
320 copy_n = 0;
322 if (copy_n == 0)
323 ptr = NULL;
325 *realsize = copy_n;
326 return ptr;
327 } /* codec_request_buffer_callback */
329 static void codec_advance_buffer_callback(size_t amount)
331 codec_advance_buffer_counters(amount);
332 codec_set_offset_callback(ci.curpos);
335 static void codec_advance_buffer_loc_callback(void *ptr)
337 size_t amount = buf_get_offset(get_audio_hid(), ptr);
338 codec_advance_buffer_callback(amount);
341 static bool codec_seek_buffer_callback(size_t newpos)
343 logf("codec_seek_buffer_callback");
345 int ret = bufseek(get_audio_hid(), newpos);
346 if (ret == 0) {
347 ci.curpos = newpos;
348 return true;
350 else {
351 return false;
355 static void codec_seek_complete_callback(void)
357 struct queue_event ev;
359 logf("seek_complete");
361 /* Clear DSP */
362 dsp_configure(ci.dsp, DSP_FLUSH, 0);
364 /* Post notification to audio thread */
365 LOGFQUEUE("audio > Q_AUDIO_SEEK_COMPLETE");
366 queue_post(&audio_queue, Q_AUDIO_SEEK_COMPLETE, 0);
368 /* Wait for ACK */
369 queue_wait(&codec_queue, &ev);
371 /* ACK back in context */
372 codec_queue_ack(Q_AUDIO_SEEK_COMPLETE);
375 static bool codec_request_next_track_callback(void)
377 struct queue_event ev;
379 logf("Request new track");
381 audio_set_prev_elapsed(thistrack_id3->elapsed);
383 #ifdef AB_REPEAT_ENABLE
384 ab_end_of_track_report();
385 #endif
387 if (ci.stop_codec)
389 /* Handle ACK in outer loop */
390 LOGFQUEUE("codec: already stopping");
391 return false;
394 trigger_cpu_boost();
396 /* Post request to audio thread */
397 LOGFQUEUE("codec > audio Q_AUDIO_CHECK_NEW_TRACK");
398 queue_post(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
400 /* Wait for ACK */
401 queue_wait(&codec_queue, &ev);
403 if (ev.data == Q_CODEC_REQUEST_COMPLETE)
405 /* Seek to the beginning of the new track because if the struct
406 mp3entry was buffered, "elapsed" might not be zero (if the track has
407 been played already but not unbuffered) */
408 codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
411 /* ACK back in context */
412 codec_queue_ack(Q_AUDIO_CHECK_NEW_TRACK);
414 if (ev.data != Q_CODEC_REQUEST_COMPLETE || ci.stop_codec)
416 LOGFQUEUE("codec <= request failed (%d)", ev.data);
417 return false;
420 LOGFQUEUE("codec <= Q_CODEC_REQEST_COMPLETE");
421 return true;
424 static void codec_configure_callback(int setting, intptr_t value)
426 if (!dsp_configure(ci.dsp, setting, value))
427 { logf("Illegal key:%d", setting); }
430 /* Initialize codec API */
431 void codec_init_codec_api(void)
433 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
434 CODEC_IDX_AUDIO);
435 ci.codec_get_buffer = codec_get_buffer;
436 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
437 ci.set_elapsed = codec_set_elapsed_callback;
438 ci.read_filebuf = codec_filebuf_callback;
439 ci.request_buffer = codec_request_buffer_callback;
440 ci.advance_buffer = codec_advance_buffer_callback;
441 ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
442 ci.seek_buffer = codec_seek_buffer_callback;
443 ci.seek_complete = codec_seek_complete_callback;
444 ci.request_next_track = codec_request_next_track_callback;
445 ci.set_offset = codec_set_offset_callback;
446 ci.configure = codec_configure_callback;
450 /* track change */
452 /** CODEC THREAD */
453 static void codec_thread(void)
455 struct queue_event ev;
458 while (1)
460 int status = CODEC_OK;
461 void *handle = NULL;
462 int hid;
463 const char *codec_fn;
465 #ifdef HAVE_CROSSFADE
466 if (!pcmbuf_is_crossfade_active())
467 #endif
469 cancel_cpu_boost();
472 queue_wait(&codec_queue, &ev);
474 switch (ev.id)
476 case Q_CODEC_LOAD_DISK:
477 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
478 codec_fn = get_codec_filename(ev.data);
479 if (!codec_fn)
480 break;
481 #ifdef AUDIO_HAVE_RECORDING
482 if (ev.data & CODEC_TYPE_ENCODER)
484 ev.id = Q_ENCODER_LOAD_DISK;
485 handle = codec_load_file(codec_fn, &ci);
486 if (handle)
487 codec_queue_ack(Q_ENCODER_LOAD_DISK);
489 else
490 #endif
492 codec_queue_ack(Q_CODEC_LOAD_DISK);
493 handle = codec_load_file(codec_fn, &ci);
495 break;
497 case Q_CODEC_LOAD:
498 LOGFQUEUE("codec < Q_CODEC_LOAD");
499 codec_queue_ack(Q_CODEC_LOAD);
500 hid = (int)ev.data;
501 handle = codec_load_buf(hid, &ci);
502 bufclose(hid);
503 break;
505 case Q_CODEC_DO_CALLBACK:
506 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
507 codec_queue_ack(Q_CODEC_DO_CALLBACK);
508 if ((void*)ev.data != NULL)
510 cpucache_commit_discard();
511 ((void (*)(void))ev.data)();
512 cpucache_commit();
514 break;
516 default:
517 LOGFQUEUE("codec < default : %ld", ev.id);
520 if (handle)
522 /* Codec loaded - call the entrypoint */
523 yield();
524 logf("codec running");
525 status = codec_begin(handle);
526 logf("codec stopped");
527 codec_close(handle);
528 current_codectype = AFMT_UNKNOWN;
530 if (ci.stop_codec)
531 status = CODEC_OK;
534 switch (ev.id)
536 #ifdef AUDIO_HAVE_RECORDING
537 case Q_ENCODER_LOAD_DISK:
538 #endif
539 case Q_CODEC_LOAD_DISK:
540 case Q_CODEC_LOAD:
541 /* Notify about the status */
542 if (!handle)
543 status = CODEC_ERROR;
544 LOGFQUEUE("codec > audio notify status: %d", status);
545 queue_post(&audio_queue, ev.id, status);
546 break;
551 void make_codec_thread(void)
553 queue_init(&codec_queue, false);
554 codec_thread_id = create_thread(
555 codec_thread, codec_stack, sizeof(codec_stack),
556 CREATE_THREAD_FROZEN,
557 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
558 IF_COP(, CPU));
559 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
560 codec_thread_id);
563 void codec_thread_resume(void)
565 thread_thaw(codec_thread_id);
568 bool is_codec_thread(void)
570 return thread_get_current() == codec_thread_id;
573 #ifdef HAVE_PRIORITY_SCHEDULING
574 int codec_thread_get_priority(void)
576 return thread_get_priority(codec_thread_id);
579 int codec_thread_set_priority(int priority)
581 return thread_set_priority(codec_thread_id, priority);
583 #endif /* HAVE_PRIORITY_SCHEDULING */
585 /* functions for audio thread use */
586 intptr_t codec_ack_msg(intptr_t data, bool stop_codec)
588 intptr_t resp;
589 LOGFQUEUE("codec >| Q_CODEC_ACK: %d", data);
590 if (stop_codec)
591 ci.stop_codec = true;
592 resp = codec_queue_send(Q_CODEC_ACK, data);
593 if (stop_codec)
594 codec_stop();
595 LOGFQUEUE(" ack: %ld", resp);
596 return resp;
599 bool codec_load(int hid, int cod_spec)
601 bool retval = false;
603 ci.stop_codec = false;
604 current_codectype = cod_spec;
606 if (hid >= 0)
608 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d", hid);
609 retval = codec_queue_send(Q_CODEC_LOAD, hid) != Q_NULL;
611 else
613 LOGFQUEUE("audio >| codec Q_CODEC_LOAD_DISK: %d", cod_spec);
614 retval = codec_queue_send(Q_CODEC_LOAD_DISK, cod_spec) != Q_NULL;
617 if (!retval)
619 ci.stop_codec = true;
620 current_codectype = AFMT_UNKNOWN;
623 return retval;
626 void codec_stop(void)
628 ci.stop_codec = true;
629 /* Wait until it's in the main loop */
630 while (codec_ack_msg(0, false) != Q_NULL);
631 current_codectype = AFMT_UNKNOWN;
634 int codec_loaded(void)
636 return current_codectype;