Remove major_version define from rpm .spec file, it's not needed
[maemo-rb.git] / apps / codec_thread.c
blob9b17d7cf420db1383a9eff2a69632c3feba5b329
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 ssize_t s = CODEC_SIZE - codec_size;
197 void *buf = &codecbuf[codec_size];
198 ALIGN_BUFFER(buf, s, CACHEALIGN_SIZE);
200 if (s <= 0)
201 return NULL;
203 *size = s;
204 return buf;
207 static void codec_pcmbuf_insert_callback(
208 const void *ch1, const void *ch2, int count)
210 const char *src[2] = { ch1, ch2 };
212 while (count > 0)
214 int out_count = dsp_output_count(ci.dsp, count);
215 int inp_count;
216 char *dest;
218 /* Prevent audio from a previous track from playing */
219 if (ci.new_track || ci.stop_codec)
220 return;
222 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
224 cancel_cpu_boost();
225 sleep(1);
226 if (ci.seek_time || ci.new_track || ci.stop_codec)
227 return;
230 /* Get the real input_size for output_size bytes, guarding
231 * against resampling buffer overflows. */
232 inp_count = dsp_input_count(ci.dsp, out_count);
234 if (inp_count <= 0)
235 return;
237 /* Input size has grown, no error, just don't write more than length */
238 if (inp_count > count)
239 inp_count = count;
241 out_count = dsp_process(ci.dsp, dest, src, inp_count);
243 if (out_count <= 0)
244 return;
246 pcmbuf_write_complete(out_count);
248 count -= inp_count;
250 } /* codec_pcmbuf_insert_callback */
252 static void codec_set_elapsed_callback(unsigned long value)
254 if (ci.seek_time)
255 return;
257 #ifdef AB_REPEAT_ENABLE
258 ab_position_report(value);
259 #endif
261 unsigned long latency = pcmbuf_get_latency();
262 if (value < latency)
263 thistrack_id3->elapsed = 0;
264 else
266 unsigned long elapsed = value - latency;
267 if (elapsed > thistrack_id3->elapsed ||
268 elapsed < thistrack_id3->elapsed - 2)
270 thistrack_id3->elapsed = elapsed;
275 static void codec_set_offset_callback(size_t value)
277 if (ci.seek_time)
278 return;
280 unsigned long latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
281 if (value < latency)
282 thistrack_id3->offset = 0;
283 else
284 thistrack_id3->offset = value - latency;
287 /* helper function, not a callback */
288 static void codec_advance_buffer_counters(size_t amount)
290 bufadvance(get_audio_hid(), amount);
291 ci.curpos += amount;
294 /* copy up-to size bytes into ptr and return the actual size copied */
295 static size_t codec_filebuf_callback(void *ptr, size_t size)
297 ssize_t copy_n;
299 if (ci.stop_codec)
300 return 0;
302 copy_n = bufread(get_audio_hid(), size, ptr);
304 /* Nothing requested OR nothing left */
305 if (copy_n == 0)
306 return 0;
308 /* Update read and other position pointers */
309 codec_advance_buffer_counters(copy_n);
311 /* Return the actual amount of data copied to the buffer */
312 return copy_n;
313 } /* codec_filebuf_callback */
315 static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
317 size_t copy_n = reqsize;
318 ssize_t ret;
319 void *ptr;
321 ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
322 if (ret >= 0)
323 copy_n = MIN((size_t)ret, reqsize);
324 else
325 copy_n = 0;
327 if (copy_n == 0)
328 ptr = NULL;
330 *realsize = copy_n;
331 return ptr;
332 } /* codec_request_buffer_callback */
334 static void codec_advance_buffer_callback(size_t amount)
336 codec_advance_buffer_counters(amount);
337 codec_set_offset_callback(ci.curpos);
340 static void codec_advance_buffer_loc_callback(void *ptr)
342 size_t amount = buf_get_offset(get_audio_hid(), ptr);
343 codec_advance_buffer_callback(amount);
346 static bool codec_seek_buffer_callback(size_t newpos)
348 logf("codec_seek_buffer_callback");
350 int ret = bufseek(get_audio_hid(), newpos);
351 if (ret == 0) {
352 ci.curpos = newpos;
353 return true;
355 else {
356 return false;
360 static void codec_seek_complete_callback(void)
362 struct queue_event ev;
364 logf("seek_complete");
366 /* Clear DSP */
367 dsp_configure(ci.dsp, DSP_FLUSH, 0);
369 /* Post notification to audio thread */
370 LOGFQUEUE("audio > Q_AUDIO_SEEK_COMPLETE");
371 queue_post(&audio_queue, Q_AUDIO_SEEK_COMPLETE, 0);
373 /* Wait for ACK */
374 queue_wait(&codec_queue, &ev);
376 /* ACK back in context */
377 codec_queue_ack(Q_AUDIO_SEEK_COMPLETE);
380 static bool codec_request_next_track_callback(void)
382 struct queue_event ev;
384 logf("Request new track");
386 audio_set_prev_elapsed(thistrack_id3->elapsed);
388 #ifdef AB_REPEAT_ENABLE
389 ab_end_of_track_report();
390 #endif
392 if (ci.stop_codec)
394 /* Handle ACK in outer loop */
395 LOGFQUEUE("codec: already stopping");
396 return false;
399 trigger_cpu_boost();
401 /* Post request to audio thread */
402 LOGFQUEUE("codec > audio Q_AUDIO_CHECK_NEW_TRACK");
403 queue_post(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
405 /* Wait for ACK */
406 queue_wait(&codec_queue, &ev);
408 if (ev.data == Q_CODEC_REQUEST_COMPLETE)
410 /* Seek to the beginning of the new track because if the struct
411 mp3entry was buffered, "elapsed" might not be zero (if the track has
412 been played already but not unbuffered) */
413 codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
416 /* ACK back in context */
417 codec_queue_ack(Q_AUDIO_CHECK_NEW_TRACK);
419 if (ev.data != Q_CODEC_REQUEST_COMPLETE || ci.stop_codec)
421 LOGFQUEUE("codec <= request failed (%d)", ev.data);
422 return false;
425 LOGFQUEUE("codec <= Q_CODEC_REQEST_COMPLETE");
426 return true;
429 static void codec_configure_callback(int setting, intptr_t value)
431 if (!dsp_configure(ci.dsp, setting, value))
432 { logf("Illegal key:%d", setting); }
435 /* Initialize codec API */
436 void codec_init_codec_api(void)
438 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
439 CODEC_IDX_AUDIO);
440 ci.codec_get_buffer = codec_get_buffer;
441 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
442 ci.set_elapsed = codec_set_elapsed_callback;
443 ci.read_filebuf = codec_filebuf_callback;
444 ci.request_buffer = codec_request_buffer_callback;
445 ci.advance_buffer = codec_advance_buffer_callback;
446 ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
447 ci.seek_buffer = codec_seek_buffer_callback;
448 ci.seek_complete = codec_seek_complete_callback;
449 ci.request_next_track = codec_request_next_track_callback;
450 ci.set_offset = codec_set_offset_callback;
451 ci.configure = codec_configure_callback;
455 /* track change */
457 /** CODEC THREAD */
458 static void codec_thread(void)
460 struct queue_event ev;
463 while (1)
465 int status = CODEC_OK;
466 void *handle = NULL;
467 int hid;
468 const char *codec_fn;
470 #ifdef HAVE_CROSSFADE
471 if (!pcmbuf_is_crossfade_active())
472 #endif
474 cancel_cpu_boost();
477 queue_wait(&codec_queue, &ev);
479 switch (ev.id)
481 case Q_CODEC_LOAD_DISK:
482 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
483 codec_fn = get_codec_filename(ev.data);
484 if (!codec_fn)
485 break;
486 #ifdef AUDIO_HAVE_RECORDING
487 if (ev.data & CODEC_TYPE_ENCODER)
489 ev.id = Q_ENCODER_LOAD_DISK;
490 handle = codec_load_file(codec_fn, &ci);
491 if (handle)
492 codec_queue_ack(Q_ENCODER_LOAD_DISK);
494 else
495 #endif
497 codec_queue_ack(Q_CODEC_LOAD_DISK);
498 handle = codec_load_file(codec_fn, &ci);
500 break;
502 case Q_CODEC_LOAD:
503 LOGFQUEUE("codec < Q_CODEC_LOAD");
504 codec_queue_ack(Q_CODEC_LOAD);
505 hid = (int)ev.data;
506 handle = codec_load_buf(hid, &ci);
507 bufclose(hid);
508 break;
510 case Q_CODEC_DO_CALLBACK:
511 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
512 codec_queue_ack(Q_CODEC_DO_CALLBACK);
513 if ((void*)ev.data != NULL)
515 cpucache_commit_discard();
516 ((void (*)(void))ev.data)();
517 cpucache_commit();
519 break;
521 default:
522 LOGFQUEUE("codec < default : %ld", ev.id);
525 if (handle)
527 /* Codec loaded - call the entrypoint */
528 yield();
529 logf("codec running");
530 status = codec_begin(handle);
531 logf("codec stopped");
532 codec_close(handle);
533 current_codectype = AFMT_UNKNOWN;
535 if (ci.stop_codec)
536 status = CODEC_OK;
539 switch (ev.id)
541 #ifdef AUDIO_HAVE_RECORDING
542 case Q_ENCODER_LOAD_DISK:
543 #endif
544 case Q_CODEC_LOAD_DISK:
545 case Q_CODEC_LOAD:
546 /* Notify about the status */
547 if (!handle)
548 status = CODEC_ERROR;
549 LOGFQUEUE("codec > audio notify status: %d", status);
550 queue_post(&audio_queue, ev.id, status);
551 break;
556 void make_codec_thread(void)
558 queue_init(&codec_queue, false);
559 codec_thread_id = create_thread(
560 codec_thread, codec_stack, sizeof(codec_stack),
561 CREATE_THREAD_FROZEN,
562 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
563 IF_COP(, CPU));
564 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
565 codec_thread_id);
568 void codec_thread_resume(void)
570 thread_thaw(codec_thread_id);
573 bool is_codec_thread(void)
575 return thread_self() == codec_thread_id;
578 #ifdef HAVE_PRIORITY_SCHEDULING
579 int codec_thread_get_priority(void)
581 return thread_get_priority(codec_thread_id);
584 int codec_thread_set_priority(int priority)
586 return thread_set_priority(codec_thread_id, priority);
588 #endif /* HAVE_PRIORITY_SCHEDULING */
590 /* functions for audio thread use */
591 intptr_t codec_ack_msg(intptr_t data, bool stop_codec)
593 intptr_t resp;
594 LOGFQUEUE("codec >| Q_CODEC_ACK: %d", data);
595 if (stop_codec)
596 ci.stop_codec = true;
597 resp = codec_queue_send(Q_CODEC_ACK, data);
598 if (stop_codec)
599 codec_stop();
600 LOGFQUEUE(" ack: %ld", resp);
601 return resp;
604 bool codec_load(int hid, int cod_spec)
606 bool retval = false;
608 ci.stop_codec = false;
609 current_codectype = cod_spec;
611 if (hid >= 0)
613 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d", hid);
614 retval = codec_queue_send(Q_CODEC_LOAD, hid) != Q_NULL;
616 else
618 LOGFQUEUE("audio >| codec Q_CODEC_LOAD_DISK: %d", cod_spec);
619 retval = codec_queue_send(Q_CODEC_LOAD_DISK, cod_spec) != Q_NULL;
622 if (!retval)
624 ci.stop_codec = true;
625 current_codectype = AFMT_UNKNOWN;
628 return retval;
631 void codec_stop(void)
633 ci.stop_codec = true;
634 /* Wait until it's in the main loop */
635 while (codec_ack_msg(0, false) != Q_NULL);
636 current_codectype = AFMT_UNKNOWN;
639 int codec_loaded(void)
641 return current_codectype;