Fix yellow, again
[kugel-rb.git] / apps / codec_thread.c
blob22c177589f3520e4ec6d44b92d8b5100ecb50f65
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 ****************************************************************************/
23 //#include <stdio.h>
24 //#include <string.h>
25 //#include <stdlib.h>
26 //#include <ctype.h>
28 #include "playback.h"
29 #include "codec_thread.h"
30 #include "system.h"
31 //#include "thread.h"
32 //#include "file.h"
33 //#include "panic.h"
34 //#include "memory.h"
35 //#include "lcd.h"
36 //#include "font.h"
37 //#include "button.h"
38 #include "kernel.h"
39 //#include "tree.h"
40 //#include "debug.h"
41 //#include "sprintf.h"
42 //#include "settings.h"
43 #include "codecs.h"
44 //#include "audio.h"
45 #include "buffering.h"
46 //#include "appevents.h"
47 //#include "voice_thread.h"
48 //#include "mp3_playback.h"
49 //#include "usb.h"
50 //#include "storage.h"
51 //#include "screens.h"
52 //#include "playlist.h"
53 #include "pcmbuf.h"
54 //#include "buffer.h"
55 #include "dsp.h"
56 #include "abrepeat.h"
57 //#include "cuesheet.h"
58 #ifdef HAVE_TAGCACHE
59 //#include "tagcache.h"
60 #endif
61 #ifdef HAVE_LCD_BITMAP
62 //#include "icons.h"
63 //#include "peakmeter.h"
64 //#include "action.h"
65 #ifdef HAVE_ALBUMART
66 //#include "albumart.h"
67 //#include "bmp.h"
68 #endif
69 #endif
70 //#include "lang.h"
71 //#include "misc.h"
72 //#include "sound.h"
73 #include "metadata.h"
74 #include "splash.h"
75 //#include "talk.h"
76 //#include "ata_idle_notify.h"
78 #ifdef HAVE_RECORDING
79 //#include "recording.h"
80 //#include "pcm_record.h"
81 #endif
83 #ifdef IPOD_ACCESSORY_PROTOCOL
84 //#include "iap.h"
85 #endif
87 /* Define LOGF_ENABLE to enable logf output in this file */
88 /*#define LOGF_ENABLE*/
89 #include "logf.h"
91 /* macros to enable logf for queues
92 logging on SYS_TIMEOUT can be disabled */
93 #ifdef SIMULATOR
94 /* Define this for logf output of all queuing except SYS_TIMEOUT */
95 #define PLAYBACK_LOGQUEUES
96 /* Define this to logf SYS_TIMEOUT messages */
97 /*#define PLAYBACK_LOGQUEUES_SYS_TIMEOUT*/
98 #endif
100 #ifdef PLAYBACK_LOGQUEUES
101 #define LOGFQUEUE logf
102 #else
103 #define LOGFQUEUE(...)
104 #endif
106 #ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT
107 #define LOGFQUEUE_SYS_TIMEOUT logf
108 #else
109 #define LOGFQUEUE_SYS_TIMEOUT(...)
110 #endif
113 /* Variables are commented with the threads that use them: *
114 * A=audio, C=codec, V=voice. A suffix of - indicates that *
115 * the variable is read but not updated on that thread. */
117 /* Main state control */
118 static volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */
120 extern struct mp3entry *thistrack_id3, /* the currently playing track */
121 *othertrack_id3; /* prev track during track-change-transition, or end of playlist,
122 * next track otherwise */
124 /* Track change controls */
125 extern bool automatic_skip; /* Who initiated in-progress skip? (C/A-) */
127 /* Set to true if the codec thread should send an audio stop request
128 * (typically because the end of the playlist has been reached).
130 static bool codec_requested_stop = false;
132 extern struct event_queue audio_queue;
133 extern struct event_queue codec_queue;
134 extern struct event_queue pcmbuf_queue;
136 /* Codec thread */
137 extern struct codec_api ci;
138 unsigned int codec_thread_id; /* For modifying thread priority later. */
139 static struct queue_sender_list codec_queue_sender_list;
140 static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
141 IBSS_ATTR;
142 static const char codec_thread_name[] = "codec";
144 /**************************************/
146 /* Function to be called by pcm buffer callbacks.
147 * Permissible Context(s): Audio interrupt
149 static void pcmbuf_callback_queue_post(long id, intptr_t data)
151 /* No lock since we're already in audio interrupt context */
152 queue_post(&pcmbuf_queue, id, data);
155 const char *get_codec_filename(int cod_spec)
157 const char *fname;
159 #ifdef HAVE_RECORDING
160 /* Can choose decoder or encoder if one available */
161 int type = cod_spec & CODEC_TYPE_MASK;
162 int afmt = cod_spec & CODEC_AFMT_MASK;
164 if ((unsigned)afmt >= AFMT_NUM_CODECS)
165 type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
167 fname = (type == CODEC_TYPE_ENCODER) ?
168 audio_formats[afmt].codec_enc_root_fn :
169 audio_formats[afmt].codec_root_fn;
171 logf("%s: %d - %s",
172 (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
173 afmt, fname ? fname : "<unknown>");
174 #else /* !HAVE_RECORDING */
175 /* Always decoder */
176 if ((unsigned)cod_spec >= AFMT_NUM_CODECS)
177 cod_spec = AFMT_UNKNOWN;
178 fname = audio_formats[cod_spec].codec_root_fn;
179 logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>");
180 #endif /* HAVE_RECORDING */
182 return fname;
183 } /* get_codec_filename */
185 /* --- Codec thread --- */
186 static bool codec_pcmbuf_insert_callback(
187 const void *ch1, const void *ch2, int count)
189 const char *src[2] = { ch1, ch2 };
191 while (count > 0)
193 int out_count = dsp_output_count(ci.dsp, count);
194 int inp_count;
195 char *dest;
197 /* Prevent audio from a previous track from playing */
198 if (ci.new_track || ci.stop_codec)
199 return true;
201 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
203 cancel_cpu_boost();
204 sleep(1);
205 if (ci.seek_time || ci.new_track || ci.stop_codec)
206 return true;
209 /* Get the real input_size for output_size bytes, guarding
210 * against resampling buffer overflows. */
211 inp_count = dsp_input_count(ci.dsp, out_count);
213 if (inp_count <= 0)
214 return true;
216 /* Input size has grown, no error, just don't write more than length */
217 if (inp_count > count)
218 inp_count = count;
220 out_count = dsp_process(ci.dsp, dest, src, inp_count);
222 if (out_count <= 0)
223 return true;
225 pcmbuf_write_complete(out_count);
227 count -= inp_count;
230 return true;
231 } /* codec_pcmbuf_insert_callback */
233 static void* codec_get_buffer(size_t *size)
235 if (codec_size >= CODEC_SIZE)
236 return NULL;
237 *size = CODEC_SIZE - codec_size;
238 return &codecbuf[codec_size];
241 /* Between the codec and PCM track change, we need to keep updating the
242 "elapsed" value of the previous (to the codec, but current to the
243 user/PCM/WPS) track, so that the progressbar reaches the end.
244 During that transition, the WPS will display prevtrack_id3. */
245 static void codec_pcmbuf_position_callback(size_t size) ICODE_ATTR;
246 static void codec_pcmbuf_position_callback(size_t size)
248 /* This is called from an ISR, so be quick */
249 unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
250 othertrack_id3->elapsed;
252 if (time >= othertrack_id3->length)
254 pcmbuf_set_position_callback(NULL);
255 othertrack_id3->elapsed = othertrack_id3->length;
257 else
258 othertrack_id3->elapsed = time;
261 static void codec_set_elapsed_callback(unsigned int value)
263 unsigned int latency;
264 if (ci.seek_time)
265 return;
267 #ifdef AB_REPEAT_ENABLE
268 ab_position_report(value);
269 #endif
271 latency = pcmbuf_get_latency();
272 if (value < latency)
273 thistrack_id3->elapsed = 0;
274 else if (value - latency > thistrack_id3->elapsed ||
275 value - latency < thistrack_id3->elapsed - 2)
277 thistrack_id3->elapsed = value - latency;
281 static void codec_set_offset_callback(size_t value)
283 unsigned int latency;
285 if (ci.seek_time)
286 return;
288 latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
289 if (value < latency)
290 thistrack_id3->offset = 0;
291 else
292 thistrack_id3->offset = value - latency;
295 static void codec_advance_buffer_counters(size_t amount)
297 bufadvance(get_audio_hid(), amount);
298 ci.curpos += amount;
301 /* copy up-to size bytes into ptr and return the actual size copied */
302 static size_t codec_filebuf_callback(void *ptr, size_t size)
304 ssize_t copy_n;
306 if (ci.stop_codec || !audio_is_playing())
307 return 0;
309 copy_n = bufread(get_audio_hid(), size, ptr);
311 /* Nothing requested OR nothing left */
312 if (copy_n == 0)
313 return 0;
315 /* Update read and other position pointers */
316 codec_advance_buffer_counters(copy_n);
318 /* Return the actual amount of data copied to the buffer */
319 return copy_n;
320 } /* codec_filebuf_callback */
322 static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
324 size_t copy_n = reqsize;
325 ssize_t ret;
326 void *ptr;
328 if (!audio_is_playing())
330 *realsize = 0;
331 return NULL;
334 ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
335 if (ret >= 0)
336 copy_n = MIN((size_t)ret, reqsize);
338 if (copy_n == 0)
340 *realsize = 0;
341 return NULL;
344 *realsize = copy_n;
346 return ptr;
347 } /* codec_request_buffer_callback */
349 int get_codec_base_type(int type)
351 switch (type) {
352 case AFMT_MPA_L1:
353 case AFMT_MPA_L2:
354 case AFMT_MPA_L3:
355 return AFMT_MPA_L3;
358 return type;
361 static void codec_advance_buffer_callback(size_t amount)
363 codec_advance_buffer_counters(amount);
364 codec_set_offset_callback(ci.curpos);
367 static void codec_advance_buffer_loc_callback(void *ptr)
369 size_t amount = buf_get_offset(get_audio_hid(), ptr);
370 codec_advance_buffer_callback(amount);
373 static void codec_seek_complete_callback(void)
375 logf("seek_complete");
376 /* If seeking-while-playing, pcm playback is actually paused (pcm_is_paused())
377 * but audio_is_paused() is false. If seeking-while-paused, audio_is_paused() is
378 * true, but pcm playback may have actually stopped due to a previous buffer clear.
379 * The buffer clear below occurs with either condition. A seemless seek skips
380 * this section and no buffer clear occurs.
382 if (pcm_is_paused() || audio_is_paused())
384 /* Clear the buffer */
385 pcmbuf_play_stop();
386 dsp_configure(ci.dsp, DSP_FLUSH, 0);
388 /* If seeking-while-playing, resume pcm playback */
389 if (!audio_is_paused())
390 pcmbuf_pause(false);
392 ci.seek_time = 0;
395 static bool codec_seek_buffer_callback(size_t newpos)
397 logf("codec_seek_buffer_callback");
399 int ret = bufseek(get_audio_hid(), newpos);
400 if (ret == 0) {
401 ci.curpos = newpos;
402 return true;
404 else {
405 return false;
409 static void codec_configure_callback(int setting, intptr_t value)
411 switch (setting) {
412 default:
413 if (!dsp_configure(ci.dsp, setting, value))
414 { logf("Illegal key:%d", setting); }
418 static void codec_track_changed(void)
420 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
421 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
424 static void codec_pcmbuf_track_changed_callback(void)
426 pcmbuf_set_position_callback(NULL);
427 pcmbuf_callback_queue_post(Q_AUDIO_TRACK_CHANGED, 0);
430 static void codec_discard_codec_callback(void)
432 int *codec_hid = get_codec_hid();
433 if (*codec_hid >= 0)
435 bufclose(*codec_hid);
436 *codec_hid = -1;
440 static inline void codec_gapless_track_change(void)
442 /* callback keeps the progress bar moving while the pcmbuf empties */
443 pcmbuf_set_position_callback(codec_pcmbuf_position_callback);
444 /* set the pcmbuf callback for when the track really changes */
445 pcmbuf_set_event_handler(codec_pcmbuf_track_changed_callback);
448 static inline void codec_crossfade_track_change(void)
450 /* Initiate automatic crossfade mode */
451 pcmbuf_crossfade_init(false);
452 /* Notify the wps that the track change starts now */
453 codec_track_changed();
456 static void codec_track_skip_done(bool was_manual)
458 /* Manual track change (always crossfade or flush audio). */
459 if (was_manual)
461 pcmbuf_crossfade_init(true);
462 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
463 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
465 /* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
466 else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
467 && global_settings.crossfade != CROSSFADE_ENABLE_TRACKSKIP)
469 if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP)
471 if (global_settings.playlist_shuffle)
472 /* shuffle mode is on, so crossfade: */
473 codec_crossfade_track_change();
474 else
475 /* shuffle mode is off, so do a gapless track change */
476 codec_gapless_track_change();
478 else
479 /* normal crossfade: */
480 codec_crossfade_track_change();
482 else
483 /* normal gapless playback. */
484 codec_gapless_track_change();
487 static bool codec_load_next_track(void)
489 intptr_t result = Q_CODEC_REQUEST_FAILED;
491 audio_set_prev_elapsed(thistrack_id3->elapsed);
493 #ifdef AB_REPEAT_ENABLE
494 ab_end_of_track_report();
495 #endif
497 logf("Request new track");
499 if (ci.new_track == 0)
501 ci.new_track++;
502 automatic_skip = true;
505 if (!ci.stop_codec)
507 trigger_cpu_boost();
508 LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK");
509 result = queue_send(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
512 switch (result)
514 case Q_CODEC_REQUEST_COMPLETE:
515 LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
516 codec_track_skip_done(!automatic_skip);
517 return true;
519 case Q_CODEC_REQUEST_FAILED:
520 LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED");
521 ci.new_track = 0;
522 ci.stop_codec = true;
523 codec_requested_stop = true;
524 return false;
526 default:
527 LOGFQUEUE("codec |< default");
528 ci.stop_codec = true;
529 codec_requested_stop = true;
530 return false;
534 static bool codec_request_next_track_callback(void)
536 int prev_codectype;
538 if (ci.stop_codec || !audio_is_playing())
539 return false;
541 prev_codectype = get_codec_base_type(thistrack_id3->codectype);
542 if (!codec_load_next_track())
543 return false;
545 /* Seek to the beginning of the new track because if the struct
546 mp3entry was buffered, "elapsed" might not be zero (if the track has
547 been played already but not unbuffered) */
548 codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
549 /* Check if the next codec is the same file. */
550 if (prev_codectype == get_codec_base_type(thistrack_id3->codectype))
552 logf("New track loaded");
553 codec_discard_codec_callback();
554 return true;
556 else
558 logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype);
559 return false;
563 static void codec_thread(void)
565 struct queue_event ev;
566 int status;
568 while (1) {
569 status = 0;
571 if (!pcmbuf_is_crossfade_active()) {
572 cancel_cpu_boost();
575 queue_wait(&codec_queue, &ev);
576 codec_requested_stop = false;
578 switch (ev.id) {
579 case Q_CODEC_LOAD_DISK:
580 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
581 queue_reply(&codec_queue, 1);
582 audio_codec_loaded = true;
583 ci.stop_codec = false;
584 status = codec_load_file((const char *)ev.data, &ci);
585 LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev.data, status);
586 break;
588 case Q_CODEC_LOAD:
589 LOGFQUEUE("codec < Q_CODEC_LOAD");
590 if (*get_codec_hid() < 0) {
591 logf("Codec slot is empty!");
592 /* Wait for the pcm buffer to go empty */
593 while (pcm_is_playing())
594 yield();
595 /* This must be set to prevent an infinite loop */
596 ci.stop_codec = true;
597 LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
598 queue_post(&codec_queue, Q_AUDIO_PLAY, 0);
599 break;
602 audio_codec_loaded = true;
603 ci.stop_codec = false;
604 status = codec_load_buf(*get_codec_hid(), &ci);
605 LOGFQUEUE("codec_load_buf %d\n", status);
606 break;
608 case Q_CODEC_DO_CALLBACK:
609 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
610 queue_reply(&codec_queue, 1);
611 if ((void*)ev.data != NULL)
613 cpucache_invalidate();
614 ((void (*)(void))ev.data)();
615 cpucache_flush();
617 break;
619 #ifdef AUDIO_HAVE_RECORDING
620 case Q_ENCODER_LOAD_DISK:
621 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
622 audio_codec_loaded = false; /* Not audio codec! */
623 logf("loading encoder");
624 ci.stop_encoder = false;
625 status = codec_load_file((const char *)ev.data, &ci);
626 logf("encoder stopped");
627 break;
628 #endif /* AUDIO_HAVE_RECORDING */
630 default:
631 LOGFQUEUE("codec < default");
634 if (audio_codec_loaded)
636 if (ci.stop_codec)
638 status = CODEC_OK;
639 if (!audio_is_playing())
640 pcmbuf_play_stop();
643 audio_codec_loaded = false;
646 switch (ev.id) {
647 case Q_CODEC_LOAD_DISK:
648 case Q_CODEC_LOAD:
649 LOGFQUEUE("codec < Q_CODEC_LOAD");
650 if (audio_is_playing())
652 if (ci.new_track || status != CODEC_OK)
654 if (!ci.new_track)
656 logf("Codec failure, %d %d", ci.new_track, status);
657 splash(HZ*2, "Codec failure");
660 if (!codec_load_next_track())
662 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
663 /* End of playlist */
664 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
665 break;
668 else
670 logf("Codec finished");
671 if (ci.stop_codec)
673 /* Wait for the audio to stop playing before
674 * triggering the WPS exit */
675 while(pcm_is_playing())
677 /* There has been one too many struct pointer swaps by now
678 * so even though it says othertrack_id3, its the correct one! */
679 othertrack_id3->elapsed =
680 othertrack_id3->length - pcmbuf_get_latency();
681 sleep(1);
684 if (codec_requested_stop)
686 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
687 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
689 break;
693 if (*get_codec_hid() >= 0)
695 LOGFQUEUE("codec > codec Q_CODEC_LOAD");
696 queue_post(&codec_queue, Q_CODEC_LOAD, 0);
698 else
700 const char *codec_fn =
701 get_codec_filename(thistrack_id3->codectype);
702 if (codec_fn)
704 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
705 queue_post(&codec_queue, Q_CODEC_LOAD_DISK,
706 (intptr_t)codec_fn);
710 break;
712 #ifdef AUDIO_HAVE_RECORDING
713 case Q_ENCODER_LOAD_DISK:
714 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
716 if (status == CODEC_OK)
717 break;
719 logf("Encoder failure");
720 splash(HZ*2, "Encoder failure");
722 if (ci.enc_codec_loaded < 0)
723 break;
725 logf("Encoder failed to load");
726 ci.enc_codec_loaded = -1;
727 break;
728 #endif /* AUDIO_HAVE_RECORDING */
730 default:
731 LOGFQUEUE("codec < default");
733 } /* end switch */
737 /* Borrow the codec thread and return the ID */
738 void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
740 /* Set id before telling thread to call something; it may be
741 * needed before this function returns. */
742 if (id != NULL)
743 *id = codec_thread_id;
745 /* Codec thread will signal just before entering callback */
746 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
747 queue_send(&codec_queue, Q_CODEC_DO_CALLBACK, (intptr_t)fn);
750 void codec_init_codec_api(void)
752 /* Initialize codec api. */
753 ci.read_filebuf = codec_filebuf_callback;
754 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
755 ci.codec_get_buffer = codec_get_buffer;
756 ci.request_buffer = codec_request_buffer_callback;
757 ci.advance_buffer = codec_advance_buffer_callback;
758 ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
759 ci.request_next_track = codec_request_next_track_callback;
760 ci.seek_buffer = codec_seek_buffer_callback;
761 ci.seek_complete = codec_seek_complete_callback;
762 ci.set_elapsed = codec_set_elapsed_callback;
763 ci.set_offset = codec_set_offset_callback;
764 ci.configure = codec_configure_callback;
765 ci.discard_codec = codec_discard_codec_callback;
766 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
767 CODEC_IDX_AUDIO);
770 bool codec_is_loaded(void)
772 return audio_codec_loaded;
775 void make_codec_thread(void)
777 codec_thread_id = create_thread(
778 codec_thread, codec_stack, sizeof(codec_stack),
779 CREATE_THREAD_FROZEN,
780 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
781 IF_COP(, CPU));
782 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
783 codec_thread_id);