summaryrefslogtreecommitdiff
path: root/apps/codec_thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codec_thread.c')
-rw-r--r--apps/codec_thread.c784
1 files changed, 784 insertions, 0 deletions
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
new file mode 100644
index 0000000000..22c177589f
--- /dev/null
+++ b/apps/codec_thread.c
@@ -0,0 +1,784 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005-2007 Miika Pekkarinen
11 * Copyright (C) 2007-2008 Nicolas Pennequin
12 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22
23//#include <stdio.h>
24//#include <string.h>
25//#include <stdlib.h>
26//#include <ctype.h>
27
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"
77
78#ifdef HAVE_RECORDING
79//#include "recording.h"
80//#include "pcm_record.h"
81#endif
82
83#ifdef IPOD_ACCESSORY_PROTOCOL
84//#include "iap.h"
85#endif
86
87/* Define LOGF_ENABLE to enable logf output in this file */
88/*#define LOGF_ENABLE*/
89#include "logf.h"
90
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
99
100#ifdef PLAYBACK_LOGQUEUES
101#define LOGFQUEUE logf
102#else
103#define LOGFQUEUE(...)
104#endif
105
106#ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT
107#define LOGFQUEUE_SYS_TIMEOUT logf
108#else
109#define LOGFQUEUE_SYS_TIMEOUT(...)
110#endif
111
112
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. */
116
117/* Main state control */
118static volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */
119
120extern 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 */
123
124/* Track change controls */
125extern bool automatic_skip; /* Who initiated in-progress skip? (C/A-) */
126
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).
129 */
130static bool codec_requested_stop = false;
131
132extern struct event_queue audio_queue;
133extern struct event_queue codec_queue;
134extern struct event_queue pcmbuf_queue;
135
136/* Codec thread */
137extern struct codec_api ci;
138unsigned int codec_thread_id; /* For modifying thread priority later. */
139static struct queue_sender_list codec_queue_sender_list;
140static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
141IBSS_ATTR;
142static const char codec_thread_name[] = "codec";
143
144/**************************************/
145
146/* Function to be called by pcm buffer callbacks.
147 * Permissible Context(s): Audio interrupt
148 */
149static void pcmbuf_callback_queue_post(long id, intptr_t data)
150{
151 /* No lock since we're already in audio interrupt context */
152 queue_post(&pcmbuf_queue, id, data);
153}
154
155const char *get_codec_filename(int cod_spec)
156{
157 const char *fname;
158
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;
163
164 if ((unsigned)afmt >= AFMT_NUM_CODECS)
165 type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
166
167 fname = (type == CODEC_TYPE_ENCODER) ?
168 audio_formats[afmt].codec_enc_root_fn :
169 audio_formats[afmt].codec_root_fn;
170
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 */
181
182 return fname;
183} /* get_codec_filename */
184
185/* --- Codec thread --- */
186static bool codec_pcmbuf_insert_callback(
187 const void *ch1, const void *ch2, int count)
188{
189 const char *src[2] = { ch1, ch2 };
190
191 while (count > 0)
192 {
193 int out_count = dsp_output_count(ci.dsp, count);
194 int inp_count;
195 char *dest;
196
197 /* Prevent audio from a previous track from playing */
198 if (ci.new_track || ci.stop_codec)
199 return true;
200
201 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
202 {
203 cancel_cpu_boost();
204 sleep(1);
205 if (ci.seek_time || ci.new_track || ci.stop_codec)
206 return true;
207 }
208
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);
212
213 if (inp_count <= 0)
214 return true;
215
216 /* Input size has grown, no error, just don't write more than length */
217 if (inp_count > count)
218 inp_count = count;
219
220 out_count = dsp_process(ci.dsp, dest, src, inp_count);
221
222 if (out_count <= 0)
223 return true;
224
225 pcmbuf_write_complete(out_count);
226
227 count -= inp_count;
228 }
229
230 return true;
231} /* codec_pcmbuf_insert_callback */
232
233static void* codec_get_buffer(size_t *size)
234{
235 if (codec_size >= CODEC_SIZE)
236 return NULL;
237 *size = CODEC_SIZE - codec_size;
238 return &codecbuf[codec_size];
239}
240
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. */
245static void codec_pcmbuf_position_callback(size_t size) ICODE_ATTR;
246static void codec_pcmbuf_position_callback(size_t size)
247{
248 /* This is called from an ISR, so be quick */
249 unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
250 othertrack_id3->elapsed;
251
252 if (time >= othertrack_id3->length)
253 {
254 pcmbuf_set_position_callback(NULL);
255 othertrack_id3->elapsed = othertrack_id3->length;
256 }
257 else
258 othertrack_id3->elapsed = time;
259}
260
261static void codec_set_elapsed_callback(unsigned int value)
262{
263 unsigned int latency;
264 if (ci.seek_time)
265 return;
266
267#ifdef AB_REPEAT_ENABLE
268 ab_position_report(value);
269#endif
270
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)
276 {
277 thistrack_id3->elapsed = value - latency;
278 }
279}
280
281static void codec_set_offset_callback(size_t value)
282{
283 unsigned int latency;
284
285 if (ci.seek_time)
286 return;
287
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;
293}
294
295static void codec_advance_buffer_counters(size_t amount)
296{
297 bufadvance(get_audio_hid(), amount);
298 ci.curpos += amount;
299}
300
301/* copy up-to size bytes into ptr and return the actual size copied */
302static size_t codec_filebuf_callback(void *ptr, size_t size)
303{
304 ssize_t copy_n;
305
306 if (ci.stop_codec || !audio_is_playing())
307 return 0;
308
309 copy_n = bufread(get_audio_hid(), size, ptr);
310
311 /* Nothing requested OR nothing left */
312 if (copy_n == 0)
313 return 0;
314
315 /* Update read and other position pointers */
316 codec_advance_buffer_counters(copy_n);
317
318 /* Return the actual amount of data copied to the buffer */
319 return copy_n;
320} /* codec_filebuf_callback */
321
322static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
323{
324 size_t copy_n = reqsize;
325 ssize_t ret;
326 void *ptr;
327
328 if (!audio_is_playing())
329 {
330 *realsize = 0;
331 return NULL;
332 }
333
334 ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
335 if (ret >= 0)
336 copy_n = MIN((size_t)ret, reqsize);
337
338 if (copy_n == 0)
339 {
340 *realsize = 0;
341 return NULL;
342 }
343
344 *realsize = copy_n;
345
346 return ptr;
347} /* codec_request_buffer_callback */
348
349int get_codec_base_type(int type)
350{
351 switch (type) {
352 case AFMT_MPA_L1:
353 case AFMT_MPA_L2:
354 case AFMT_MPA_L3:
355 return AFMT_MPA_L3;
356 }
357
358 return type;
359}
360
361static void codec_advance_buffer_callback(size_t amount)
362{
363 codec_advance_buffer_counters(amount);
364 codec_set_offset_callback(ci.curpos);
365}
366
367static void codec_advance_buffer_loc_callback(void *ptr)
368{
369 size_t amount = buf_get_offset(get_audio_hid(), ptr);
370 codec_advance_buffer_callback(amount);
371}
372
373static void codec_seek_complete_callback(void)
374{
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.
381 */
382 if (pcm_is_paused() || audio_is_paused())
383 {
384 /* Clear the buffer */
385 pcmbuf_play_stop();
386 dsp_configure(ci.dsp, DSP_FLUSH, 0);
387
388 /* If seeking-while-playing, resume pcm playback */
389 if (!audio_is_paused())
390 pcmbuf_pause(false);
391 }
392 ci.seek_time = 0;
393}
394
395static bool codec_seek_buffer_callback(size_t newpos)
396{
397 logf("codec_seek_buffer_callback");
398
399 int ret = bufseek(get_audio_hid(), newpos);
400 if (ret == 0) {
401 ci.curpos = newpos;
402 return true;
403 }
404 else {
405 return false;
406 }
407}
408
409static void codec_configure_callback(int setting, intptr_t value)
410{
411 switch (setting) {
412 default:
413 if (!dsp_configure(ci.dsp, setting, value))
414 { logf("Illegal key:%d", setting); }
415 }
416}
417
418static void codec_track_changed(void)
419{
420 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
421 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
422}
423
424static void codec_pcmbuf_track_changed_callback(void)
425{
426 pcmbuf_set_position_callback(NULL);
427 pcmbuf_callback_queue_post(Q_AUDIO_TRACK_CHANGED, 0);
428}
429
430static void codec_discard_codec_callback(void)
431{
432 int *codec_hid = get_codec_hid();
433 if (*codec_hid >= 0)
434 {
435 bufclose(*codec_hid);
436 *codec_hid = -1;
437 }
438}
439
440static inline void codec_gapless_track_change(void)
441{
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);
446}
447
448static inline void codec_crossfade_track_change(void)
449{
450 /* Initiate automatic crossfade mode */
451 pcmbuf_crossfade_init(false);
452 /* Notify the wps that the track change starts now */
453 codec_track_changed();
454}
455
456static void codec_track_skip_done(bool was_manual)
457{
458 /* Manual track change (always crossfade or flush audio). */
459 if (was_manual)
460 {
461 pcmbuf_crossfade_init(true);
462 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
463 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
464 }
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)
468 {
469 if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP)
470 {
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();
477 }
478 else
479 /* normal crossfade: */
480 codec_crossfade_track_change();
481 }
482 else
483 /* normal gapless playback. */
484 codec_gapless_track_change();
485}
486
487static bool codec_load_next_track(void)
488{
489 intptr_t result = Q_CODEC_REQUEST_FAILED;
490
491 audio_set_prev_elapsed(thistrack_id3->elapsed);
492
493#ifdef AB_REPEAT_ENABLE
494 ab_end_of_track_report();
495#endif
496
497 logf("Request new track");
498
499 if (ci.new_track == 0)
500 {
501 ci.new_track++;
502 automatic_skip = true;
503 }
504
505 if (!ci.stop_codec)
506 {
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);
510 }
511
512 switch (result)
513 {
514 case Q_CODEC_REQUEST_COMPLETE:
515 LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
516 codec_track_skip_done(!automatic_skip);
517 return true;
518
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;
525
526 default:
527 LOGFQUEUE("codec |< default");
528 ci.stop_codec = true;
529 codec_requested_stop = true;
530 return false;
531 }
532}
533
534static bool codec_request_next_track_callback(void)
535{
536 int prev_codectype;
537
538 if (ci.stop_codec || !audio_is_playing())
539 return false;
540
541 prev_codectype = get_codec_base_type(thistrack_id3->codectype);
542 if (!codec_load_next_track())
543 return false;
544
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))
551 {
552 logf("New track loaded");
553 codec_discard_codec_callback();
554 return true;
555 }
556 else
557 {
558 logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype);
559 return false;
560 }
561}
562
563static void codec_thread(void)
564{
565 struct queue_event ev;
566 int status;
567
568 while (1) {
569 status = 0;
570
571 if (!pcmbuf_is_crossfade_active()) {
572 cancel_cpu_boost();
573 }
574
575 queue_wait(&codec_queue, &ev);
576 codec_requested_stop = false;
577
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;
587
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;
600 }
601
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;
607
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)
612 {
613 cpucache_invalidate();
614 ((void (*)(void))ev.data)();
615 cpucache_flush();
616 }
617 break;
618
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 */
629
630 default:
631 LOGFQUEUE("codec < default");
632 }
633
634 if (audio_codec_loaded)
635 {
636 if (ci.stop_codec)
637 {
638 status = CODEC_OK;
639 if (!audio_is_playing())
640 pcmbuf_play_stop();
641
642 }
643 audio_codec_loaded = false;
644 }
645
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())
651 {
652 if (ci.new_track || status != CODEC_OK)
653 {
654 if (!ci.new_track)
655 {
656 logf("Codec failure, %d %d", ci.new_track, status);
657 splash(HZ*2, "Codec failure");
658 }
659
660 if (!codec_load_next_track())
661 {
662 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
663 /* End of playlist */
664 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
665 break;
666 }
667 }
668 else
669 {
670 logf("Codec finished");
671 if (ci.stop_codec)
672 {
673 /* Wait for the audio to stop playing before
674 * triggering the WPS exit */
675 while(pcm_is_playing())
676 {
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);
682 }
683
684 if (codec_requested_stop)
685 {
686 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
687 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
688 }
689 break;
690 }
691 }
692
693 if (*get_codec_hid() >= 0)
694 {
695 LOGFQUEUE("codec > codec Q_CODEC_LOAD");
696 queue_post(&codec_queue, Q_CODEC_LOAD, 0);
697 }
698 else
699 {
700 const char *codec_fn =
701 get_codec_filename(thistrack_id3->codectype);
702 if (codec_fn)
703 {
704 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
705 queue_post(&codec_queue, Q_CODEC_LOAD_DISK,
706 (intptr_t)codec_fn);
707 }
708 }
709 }
710 break;
711
712#ifdef AUDIO_HAVE_RECORDING
713 case Q_ENCODER_LOAD_DISK:
714 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
715
716 if (status == CODEC_OK)
717 break;
718
719 logf("Encoder failure");
720 splash(HZ*2, "Encoder failure");
721
722 if (ci.enc_codec_loaded < 0)
723 break;
724
725 logf("Encoder failed to load");
726 ci.enc_codec_loaded = -1;
727 break;
728#endif /* AUDIO_HAVE_RECORDING */
729
730 default:
731 LOGFQUEUE("codec < default");
732
733 } /* end switch */
734 }
735}
736
737/* Borrow the codec thread and return the ID */
738void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
739{
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;
744
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);
748}
749
750void codec_init_codec_api(void)
751{
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);
768}
769
770bool codec_is_loaded(void)
771{
772 return audio_codec_loaded;
773}
774
775void make_codec_thread(void)
776{
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);
784}