summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Goode <jeffg7@gmail.com>2009-10-31 19:17:36 +0000
committerJeffrey Goode <jeffg7@gmail.com>2009-10-31 19:17:36 +0000
commit9a4420bf96f2fb105369106f7f6049985d8ee703 (patch)
treee27ce51a67127a6ed35920a45def4f1ad3f4fcb5
parent15ea6e663f1b8ef006d4662d33c2dba1d85d2ddf (diff)
downloadrockbox-9a4420bf96f2fb105369106f7f6049985d8ee703.tar.gz
rockbox-9a4420bf96f2fb105369106f7f6049985d8ee703.zip
FS#10739: playback.c code split
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23444 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/codec_thread.c784
-rw-r--r--apps/codec_thread.h35
-rw-r--r--apps/debug_menu.c5
-rw-r--r--apps/metadata.c1
-rw-r--r--apps/mpeg.c2
-rw-r--r--apps/playback.c858
-rw-r--r--apps/playback.h51
-rw-r--r--apps/plugin.h1
-rw-r--r--apps/recorder/recording.c1
-rw-r--r--apps/scrobbler.c3
-rw-r--r--apps/tagtree.c1
-rw-r--r--apps/talk.c3
13 files changed, 978 insertions, 768 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 6c0b12c88f..3f44fddf67 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -134,6 +134,7 @@ audio_path.c
134#endif /* INPUT_SRC_CAPS != 0 */ 134#endif /* INPUT_SRC_CAPS != 0 */
135fixedpoint.c 135fixedpoint.c
136pcmbuf.c 136pcmbuf.c
137codec_thread.c
137playback.c 138playback.c
138codecs.c 139codecs.c
139dsp.c 140dsp.c
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}
diff --git a/apps/codec_thread.h b/apps/codec_thread.h
new file mode 100644
index 0000000000..26a0f1fdd8
--- /dev/null
+++ b/apps/codec_thread.h
@@ -0,0 +1,35 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Miika Pekkarinen
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef _CODEC_THREAD_H
23#define _CODEC_THREAD_H
24
25#include <stdbool.h>
26
27const char *get_codec_filename(int cod_spec);
28void codec_thread_do_callback(void (*fn)(void),
29 unsigned int *codec_thread_id);
30void make_codec_thread(void);
31int get_codec_base_type(int type);
32void codec_init_codec_api(void);
33bool codec_is_loaded(void);
34
35#endif
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index a9c4435e9b..f9fb7a3bb7 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -272,9 +272,6 @@ static bool dbg_audio_thread(void)
272} 272}
273#endif /* !SIMULATOR */ 273#endif /* !SIMULATOR */
274#else /* CONFIG_CODEC == SWCODEC */ 274#else /* CONFIG_CODEC == SWCODEC */
275extern size_t filebuflen;
276/* This is a size_t, but call it a long so it puts a - when it's bad. */
277
278static unsigned int ticks, boost_ticks, freq_sum; 275static unsigned int ticks, boost_ticks, freq_sum;
279 276
280static void dbg_audio_task(void) 277static void dbg_audio_task(void)
@@ -296,6 +293,8 @@ static bool dbg_buffering_thread(void)
296 size_t bufsize = pcmbuf_get_bufsize(); 293 size_t bufsize = pcmbuf_get_bufsize();
297 int pcmbufdescs = pcmbuf_descs(); 294 int pcmbufdescs = pcmbuf_descs();
298 struct buffering_debug d; 295 struct buffering_debug d;
296 size_t filebuflen = audio_get_filebuflen();
297 /* This is a size_t, but call it a long so it puts a - when it's bad. */
299 298
300 ticks = boost_ticks = freq_sum = 0; 299 ticks = boost_ticks = freq_sum = 0;
301 300
diff --git a/apps/metadata.c b/apps/metadata.c
index 56ab6c16e6..7913226779 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -24,7 +24,6 @@
24#include <ctype.h> 24#include <ctype.h>
25#include <inttypes.h> 25#include <inttypes.h>
26 26
27#include "playback.h"
28#include "debug.h" 27#include "debug.h"
29#include "logf.h" 28#include "logf.h"
30#include "cuesheet.h" 29#include "cuesheet.h"
diff --git a/apps/mpeg.c b/apps/mpeg.c
index e28260b6a7..0b6a5894bd 100644
--- a/apps/mpeg.c
+++ b/apps/mpeg.c
@@ -111,7 +111,7 @@ static struct trackdata trackdata[MAX_TRACK_ENTRIES];
111static unsigned int current_track_counter = 0; 111static unsigned int current_track_counter = 0;
112 112
113/* Play time of the previous track */ 113/* Play time of the previous track */
114unsigned long prev_track_elapsed; 114static unsigned long prev_track_elapsed;
115 115
116#ifndef SIMULATOR 116#ifndef SIMULATOR
117static int track_read_idx = 0; 117static int track_read_idx = 0;
diff --git a/apps/playback.c b/apps/playback.c
index 7b614cd0d0..b38ae7acf1 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -23,67 +23,67 @@
23/* TODO: Pause should be handled in here, rather than PCMBUF so that voice can 23/* TODO: Pause should be handled in here, rather than PCMBUF so that voice can
24 * play whilst audio is paused */ 24 * play whilst audio is paused */
25 25
26#include <stdio.h> 26//#include <stdio.h>
27#include <string.h> 27//#include <string.h>
28#include <stdlib.h> 28//#include <ctype.h>
29#include <ctype.h> 29
30 30#include "playback.h"
31#include "system.h" 31#include "codec_thread.h"
32#include "thread.h" 32//#include "system.h"
33#include "file.h" 33//#include "thread.h"
34#include "panic.h" 34//#include "file.h"
35#include "memory.h" 35//#include "panic.h"
36#include "lcd.h" 36//#include "memory.h"
37#include "font.h" 37//#include "lcd.h"
38#include "button.h" 38//#include "font.h"
39//#include "button.h"
39#include "kernel.h" 40#include "kernel.h"
40#include "tree.h" 41//#include "tree.h"
41#include "debug.h" 42//#include "debug.h"
42#include "sprintf.h" 43//#include "sprintf.h"
43#include "settings.h" 44//#include "settings.h"
44#include "codecs.h" 45#include "codecs.h"
45#include "audio.h" 46//#include "audio.h"
46#include "buffering.h" 47#include "buffering.h"
47#include "appevents.h" 48//#include "appevents.h"
48#include "voice_thread.h" 49#include "voice_thread.h"
49#include "mp3_playback.h" 50//#include "mp3_playback.h"
50#include "usb.h" 51#include "usb.h"
51#include "storage.h" 52//#include "storage.h"
52#include "screens.h" 53//#include "screens.h"
53#include "playlist.h" 54#include "playlist.h"
54#include "playback.h"
55#include "pcmbuf.h" 55#include "pcmbuf.h"
56#include "buffer.h" 56#include "buffer.h"
57#include "dsp.h" 57//#include "dsp.h"
58#include "abrepeat.h" 58//#include "abrepeat.h"
59#include "cuesheet.h" 59#include "cuesheet.h"
60#ifdef HAVE_TAGCACHE 60#ifdef HAVE_TAGCACHE
61#include "tagcache.h" 61#include "tagcache.h"
62#endif 62#endif
63#ifdef HAVE_LCD_BITMAP 63#ifdef HAVE_LCD_BITMAP
64#include "icons.h" 64//#include "icons.h"
65#include "peakmeter.h" 65//#include "peakmeter.h"
66#include "action.h" 66//#include "action.h"
67#ifdef HAVE_ALBUMART 67#ifdef HAVE_ALBUMART
68#include "albumart.h" 68#include "albumart.h"
69#include "bmp.h" 69//#include "bmp.h"
70#endif 70#endif
71#endif 71#endif
72#include "lang.h" 72//#include "lang.h"
73#include "misc.h" 73//#include "misc.h"
74#include "sound.h" 74#include "sound.h"
75#include "metadata.h" 75#include "metadata.h"
76#include "splash.h" 76#include "splash.h"
77#include "talk.h" 77#include "talk.h"
78#include "ata_idle_notify.h" 78//#include "ata_idle_notify.h"
79 79
80#ifdef HAVE_RECORDING 80#ifdef HAVE_RECORDING
81#include "recording.h" 81//#include "recording.h"
82#include "pcm_record.h" 82#include "pcm_record.h"
83#endif 83#endif
84 84
85#ifdef IPOD_ACCESSORY_PROTOCOL 85#ifdef IPOD_ACCESSORY_PROTOCOL
86#include "iap.h" 86//#include "iap.h"
87#endif 87#endif
88 88
89#define PLAYBACK_VOICE 89#define PLAYBACK_VOICE
@@ -118,52 +118,13 @@
118#endif 118#endif
119 119
120 120
121/* Define one constant that includes recording related functionality */ 121static enum filling_state {
122#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
123#define AUDIO_HAVE_RECORDING
124#endif
125
126enum {
127 Q_NULL = 0,
128 Q_AUDIO_PLAY = 1,
129 Q_AUDIO_STOP,
130 Q_AUDIO_PAUSE,
131 Q_AUDIO_SKIP,
132 Q_AUDIO_PRE_FF_REWIND,
133 Q_AUDIO_FF_REWIND,
134 Q_AUDIO_CHECK_NEW_TRACK,
135 Q_AUDIO_FLUSH,
136 Q_AUDIO_TRACK_CHANGED,
137 Q_AUDIO_DIR_SKIP,
138 Q_AUDIO_POSTINIT,
139 Q_AUDIO_FILL_BUFFER,
140 Q_AUDIO_FINISH_LOAD,
141 Q_CODEC_REQUEST_COMPLETE,
142 Q_CODEC_REQUEST_FAILED,
143
144 Q_CODEC_LOAD,
145 Q_CODEC_LOAD_DISK,
146
147#ifdef AUDIO_HAVE_RECORDING
148 Q_ENCODER_LOAD_DISK,
149 Q_ENCODER_RECORD,
150#endif
151
152 Q_CODEC_DO_CALLBACK,
153};
154
155enum filling_state {
156 STATE_IDLE, /* audio is stopped: nothing to do */ 122 STATE_IDLE, /* audio is stopped: nothing to do */
157 STATE_FILLING, /* adding tracks to the buffer */ 123 STATE_FILLING, /* adding tracks to the buffer */
158 STATE_FULL, /* can't add any more tracks */ 124 STATE_FULL, /* can't add any more tracks */
159 STATE_END_OF_PLAYLIST, /* all remaining tracks have been added */ 125 STATE_END_OF_PLAYLIST, /* all remaining tracks have been added */
160 STATE_FINISHED, /* all remaining tracks are fully buffered */ 126 STATE_FINISHED, /* all remaining tracks are fully buffered */
161}; 127} filling;
162
163#define MAX_TRACK 128
164#define MAX_MULTIPLE_AA 2
165
166#define MAX_TRACK_MASK (MAX_TRACK-1)
167 128
168/* As defined in plugins/lib/xxx2wav.h */ 129/* As defined in plugins/lib/xxx2wav.h */
169#define GUARD_BUFSIZE (32*1024) 130#define GUARD_BUFSIZE (32*1024)
@@ -177,15 +138,13 @@ static bool audio_thread_ready SHAREDBSS_ATTR = false;
177/* TBD: Split out "audio" and "playback" (ie. calling) threads */ 138/* TBD: Split out "audio" and "playback" (ie. calling) threads */
178 139
179/* Main state control */ 140/* Main state control */
180static volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */
181static volatile bool playing SHAREDBSS_ATTR = false; /* Is audio playing? (A) */ 141static volatile bool playing SHAREDBSS_ATTR = false; /* Is audio playing? (A) */
182static volatile bool paused SHAREDBSS_ATTR = false; /* Is audio paused? (A/C-) */ 142static volatile bool paused SHAREDBSS_ATTR = false; /* Is audio paused? (A/C-) */
183 143
184/* Ring buffer where compressed audio and codecs are loaded */ 144/* Ring buffer where compressed audio and codecs are loaded */
185static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */ 145static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */
186static unsigned char *malloc_buf = NULL; /* Start of malloc buffer (A/C-) */ 146static unsigned char *malloc_buf = NULL; /* Start of malloc buffer (A/C-) */
187/* FIXME: make filebuflen static */ 147static size_t filebuflen = 0; /* Size of buffer (A/C-) */
188size_t filebuflen = 0; /* Size of buffer (A/C-) */
189/* FIXME: make buf_ridx (C/A-) */ 148/* FIXME: make buf_ridx (C/A-) */
190 149
191/* Possible arrangements of the buffer */ 150/* Possible arrangements of the buffer */
@@ -196,16 +155,32 @@ static int buffer_state = AUDIOBUF_STATE_TRASHED; /* Buffer state */
196 * by audio_current/next_track will be valid for the full duration of the 155 * by audio_current/next_track will be valid for the full duration of the
197 * currently playing track */ 156 * currently playing track */
198static struct mp3entry mp3entry_buf[2]; 157static struct mp3entry mp3entry_buf[2];
199static struct mp3entry *thistrack_id3, /* the currently playing track */ 158struct mp3entry *thistrack_id3, /* the currently playing track */
200 *othertrack_id3; /* prev track during track-change-transition, or end of playlist, 159 *othertrack_id3; /* prev track during track-change-transition, or end of playlist,
201 * next track otherwise */ 160 * next track otherwise */
202static struct mp3entry unbuffered_id3; /* the id3 for the first unbuffered track */ 161static struct mp3entry unbuffered_id3; /* the id3 for the first unbuffered track */
203 162
204/* for cuesheet support */ 163/* for cuesheet support */
205static struct cuesheet *curr_cue = NULL; 164static struct cuesheet *curr_cue = NULL;
206 165
166
167#define MAX_MULTIPLE_AA 2
168
169#ifdef HAVE_ALBUMART
170static struct albumart_slot {
171 struct dim dim; /* holds width, height of the albumart */
172 int used; /* counter, increments if something uses it */
173} albumart_slots[MAX_MULTIPLE_AA];
174
175#define FOREACH_ALBUMART(i) for(i = 0;i < MAX_MULTIPLE_AA; i++)
176#endif
177
178
179#define MAX_TRACK 128
180#define MAX_TRACK_MASK (MAX_TRACK-1)
181
207/* Track info structure about songs in the file buffer (A/C-) */ 182/* Track info structure about songs in the file buffer (A/C-) */
208struct track_info { 183static struct track_info {
209 int audio_hid; /* The ID for the track's buffer handle */ 184 int audio_hid; /* The ID for the track's buffer handle */
210 int id3_hid; /* The ID for the track's metadata handle */ 185 int id3_hid; /* The ID for the track's metadata handle */
211 int codec_hid; /* The ID for the track's codec handle */ 186 int codec_hid; /* The ID for the track's codec handle */
@@ -217,23 +192,13 @@ struct track_info {
217 size_t filesize; /* File total length */ 192 size_t filesize; /* File total length */
218 193
219 bool taginfo_ready; /* Is metadata read */ 194 bool taginfo_ready; /* Is metadata read */
220}; 195
221 196} tracks[MAX_TRACK];
222
223#ifdef HAVE_ALBUMART
224struct albumart_slot {
225 struct dim dim; /* holds width, height of the albumart */
226 int used; /* counter, increments if something uses it */
227} albumart_slots[MAX_MULTIPLE_AA];
228
229#define FOREACH_ALBUMART(i) for(i = 0;i < MAX_MULTIPLE_AA; i++)
230#endif
231 197
232static struct track_info tracks[MAX_TRACK];
233static volatile int track_ridx = 0; /* Track being decoded (A/C-) */ 198static volatile int track_ridx = 0; /* Track being decoded (A/C-) */
234static int track_widx = 0; /* Track being buffered (A) */ 199static int track_widx = 0; /* Track being buffered (A) */
235
236#define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */ 200#define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */
201
237static struct track_info *prev_ti = NULL; /* Pointer to the previously played 202static struct track_info *prev_ti = NULL; /* Pointer to the previously played
238 track */ 203 track */
239 204
@@ -244,10 +209,8 @@ static int last_peek_offset = 0;
244/* Scrobbler support */ 209/* Scrobbler support */
245static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/ 210static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/
246 211
247static enum filling_state filling;
248
249/* Track change controls */ 212/* Track change controls */
250static bool automatic_skip = false; /* Who initiated in-progress skip? (C/A-) */ 213bool automatic_skip = false; /* Who initiated in-progress skip? (C/A-) */
251static bool dir_skip = false; /* Is a directory skip pending? (A) */ 214static bool dir_skip = false; /* Is a directory skip pending? (A) */
252static bool new_playlist = false; /* Are we starting a new playlist? (A) */ 215static bool new_playlist = false; /* Are we starting a new playlist? (A) */
253static int wps_offset = 0; /* Pending track change offset, to keep WPS responsive (A) */ 216static int wps_offset = 0; /* Pending track change offset, to keep WPS responsive (A) */
@@ -262,21 +225,23 @@ static bool start_play_g = false; /* Used by audio_load_track to notify
262 */ 225 */
263static bool track_load_started = false; 226static bool track_load_started = false;
264 227
265/* Set to true if the codec thread should send an audio stop request
266 * (typically because the end of the playlist has been reached).
267 */
268static bool codec_requested_stop = false;
269
270#ifdef HAVE_DISK_STORAGE 228#ifdef HAVE_DISK_STORAGE
271static size_t buffer_margin = 5; /* Buffer margin aka anti-skip buffer (A/C-) */ 229static size_t buffer_margin = 5; /* Buffer margin aka anti-skip buffer (A/C-) */
272#endif 230#endif
273 231
232/* Event queues */
233struct event_queue audio_queue SHAREDBSS_ATTR;
234struct event_queue codec_queue SHAREDBSS_ATTR;
235struct event_queue pcmbuf_queue SHAREDBSS_ATTR;
236
237extern struct codec_api ci;
238extern unsigned int codec_thread_id;
239
274/* Multiple threads */ 240/* Multiple threads */
275/* Set the watermark to trigger buffer fill (A/C) */ 241/* Set the watermark to trigger buffer fill (A/C) */
276static void set_filebuf_watermark(void); 242static void set_filebuf_watermark(void);
277 243
278/* Audio thread */ 244/* Audio thread */
279static struct event_queue audio_queue SHAREDBSS_ATTR;
280static struct queue_sender_list audio_queue_sender_list SHAREDBSS_ATTR; 245static struct queue_sender_list audio_queue_sender_list SHAREDBSS_ATTR;
281static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)]; 246static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
282static const char audio_thread_name[] = "audio"; 247static const char audio_thread_name[] = "audio";
@@ -287,26 +252,8 @@ static bool audio_have_tracks(void);
287static void audio_reset_buffer(void); 252static void audio_reset_buffer(void);
288static void audio_stop_playback(void); 253static void audio_stop_playback(void);
289 254
290/* Codec thread */ 255
291extern struct codec_api ci; 256/**************************************/
292static struct event_queue codec_queue SHAREDBSS_ATTR;
293static struct queue_sender_list codec_queue_sender_list;
294static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
295IBSS_ATTR;
296static const char codec_thread_name[] = "codec";
297unsigned int codec_thread_id; /* For modifying thread priority later. */
298
299/* PCM buffer messaging */
300static struct event_queue pcmbuf_queue SHAREDBSS_ATTR;
301
302/* Function to be called by pcm buffer callbacks.
303 * Permissible Context(s): Audio interrupt
304 */
305static void pcmbuf_callback_queue_post(long id, intptr_t data)
306{
307 /* No lock since we're already in audio interrupt context */
308 queue_post(&pcmbuf_queue, id, data);
309}
310 257
311/* Scan the pcmbuf queue and return true if a message pulled. 258/* Scan the pcmbuf queue and return true if a message pulled.
312 * Permissible Context(s): Thread 259 * Permissible Context(s): Thread
@@ -850,6 +797,7 @@ int audio_status(void)
850 797
851#ifdef HAVE_RECORDING 798#ifdef HAVE_RECORDING
852 /* Do this here for constitency with mpeg.c version */ 799 /* Do this here for constitency with mpeg.c version */
800 /* FIXME: pcm_rec_status() is deprecated */
853 ret |= pcm_rec_status(); 801 ret |= pcm_rec_status();
854#endif 802#endif
855 803
@@ -947,600 +895,6 @@ static void set_filebuf_watermark(void)
947 logf("fwmark: %d", bytes); 895 logf("fwmark: %d", bytes);
948} 896}
949 897
950const char *get_codec_filename(int cod_spec)
951{
952 const char *fname;
953
954#ifdef HAVE_RECORDING
955 /* Can choose decoder or encoder if one available */
956 int type = cod_spec & CODEC_TYPE_MASK;
957 int afmt = cod_spec & CODEC_AFMT_MASK;
958
959 if ((unsigned)afmt >= AFMT_NUM_CODECS)
960 type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
961
962 fname = (type == CODEC_TYPE_ENCODER) ?
963 audio_formats[afmt].codec_enc_root_fn :
964 audio_formats[afmt].codec_root_fn;
965
966 logf("%s: %d - %s",
967 (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
968 afmt, fname ? fname : "<unknown>");
969#else /* !HAVE_RECORDING */
970 /* Always decoder */
971 if ((unsigned)cod_spec >= AFMT_NUM_CODECS)
972 cod_spec = AFMT_UNKNOWN;
973 fname = audio_formats[cod_spec].codec_root_fn;
974 logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>");
975#endif /* HAVE_RECORDING */
976
977 return fname;
978} /* get_codec_filename */
979
980/* --- Codec thread --- */
981static bool codec_pcmbuf_insert_callback(
982 const void *ch1, const void *ch2, int count)
983{
984 const char *src[2] = { ch1, ch2 };
985
986 while (count > 0)
987 {
988 int out_count = dsp_output_count(ci.dsp, count);
989 int inp_count;
990 char *dest;
991
992 /* Prevent audio from a previous track from playing */
993 if (ci.new_track || ci.stop_codec)
994 return true;
995
996 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
997 {
998 cancel_cpu_boost();
999 sleep(1);
1000 if (ci.seek_time || ci.new_track || ci.stop_codec)
1001 return true;
1002 }
1003
1004 /* Get the real input_size for output_size bytes, guarding
1005 * against resampling buffer overflows. */
1006 inp_count = dsp_input_count(ci.dsp, out_count);
1007
1008 if (inp_count <= 0)
1009 return true;
1010
1011 /* Input size has grown, no error, just don't write more than length */
1012 if (inp_count > count)
1013 inp_count = count;
1014
1015 out_count = dsp_process(ci.dsp, dest, src, inp_count);
1016
1017 if (out_count <= 0)
1018 return true;
1019
1020 pcmbuf_write_complete(out_count);
1021
1022 count -= inp_count;
1023 }
1024
1025 return true;
1026} /* codec_pcmbuf_insert_callback */
1027
1028static void* codec_get_buffer(size_t *size)
1029{
1030 if (codec_size >= CODEC_SIZE)
1031 return NULL;
1032 *size = CODEC_SIZE - codec_size;
1033 return &codecbuf[codec_size];
1034}
1035
1036/* Between the codec and PCM track change, we need to keep updating the
1037 "elapsed" value of the previous (to the codec, but current to the
1038 user/PCM/WPS) track, so that the progressbar reaches the end.
1039 During that transition, the WPS will display prevtrack_id3. */
1040static void codec_pcmbuf_position_callback(size_t size) ICODE_ATTR;
1041static void codec_pcmbuf_position_callback(size_t size)
1042{
1043 /* This is called from an ISR, so be quick */
1044 unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
1045 othertrack_id3->elapsed;
1046
1047 if (time >= othertrack_id3->length)
1048 {
1049 pcmbuf_set_position_callback(NULL);
1050 othertrack_id3->elapsed = othertrack_id3->length;
1051 }
1052 else
1053 othertrack_id3->elapsed = time;
1054}
1055
1056static void codec_set_elapsed_callback(unsigned int value)
1057{
1058 unsigned int latency;
1059 if (ci.seek_time)
1060 return;
1061
1062#ifdef AB_REPEAT_ENABLE
1063 ab_position_report(value);
1064#endif
1065
1066 latency = pcmbuf_get_latency();
1067 if (value < latency)
1068 thistrack_id3->elapsed = 0;
1069 else if (value - latency > thistrack_id3->elapsed ||
1070 value - latency < thistrack_id3->elapsed - 2)
1071 {
1072 thistrack_id3->elapsed = value - latency;
1073 }
1074}
1075
1076static void codec_set_offset_callback(size_t value)
1077{
1078 unsigned int latency;
1079
1080 if (ci.seek_time)
1081 return;
1082
1083 latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
1084 if (value < latency)
1085 thistrack_id3->offset = 0;
1086 else
1087 thistrack_id3->offset = value - latency;
1088}
1089
1090static void codec_advance_buffer_counters(size_t amount)
1091{
1092 bufadvance(CUR_TI->audio_hid, amount);
1093 ci.curpos += amount;
1094}
1095
1096/* copy up-to size bytes into ptr and return the actual size copied */
1097static size_t codec_filebuf_callback(void *ptr, size_t size)
1098{
1099 ssize_t copy_n;
1100
1101 if (ci.stop_codec || !playing)
1102 return 0;
1103
1104 copy_n = bufread(CUR_TI->audio_hid, size, ptr);
1105
1106 /* Nothing requested OR nothing left */
1107 if (copy_n == 0)
1108 return 0;
1109
1110 /* Update read and other position pointers */
1111 codec_advance_buffer_counters(copy_n);
1112
1113 /* Return the actual amount of data copied to the buffer */
1114 return copy_n;
1115} /* codec_filebuf_callback */
1116
1117static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
1118{
1119 size_t copy_n = reqsize;
1120 ssize_t ret;
1121 void *ptr;
1122
1123 if (!playing)
1124 {
1125 *realsize = 0;
1126 return NULL;
1127 }
1128
1129 ret = bufgetdata(CUR_TI->audio_hid, reqsize, &ptr);
1130 if (ret >= 0)
1131 copy_n = MIN((size_t)ret, reqsize);
1132
1133 if (copy_n == 0)
1134 {
1135 *realsize = 0;
1136 return NULL;
1137 }
1138
1139 *realsize = copy_n;
1140
1141 return ptr;
1142} /* codec_request_buffer_callback */
1143
1144static int get_codec_base_type(int type)
1145{
1146 switch (type) {
1147 case AFMT_MPA_L1:
1148 case AFMT_MPA_L2:
1149 case AFMT_MPA_L3:
1150 return AFMT_MPA_L3;
1151 }
1152
1153 return type;
1154}
1155
1156static void codec_advance_buffer_callback(size_t amount)
1157{
1158 codec_advance_buffer_counters(amount);
1159 codec_set_offset_callback(ci.curpos);
1160}
1161
1162static void codec_advance_buffer_loc_callback(void *ptr)
1163{
1164 size_t amount = buf_get_offset(CUR_TI->audio_hid, ptr);
1165 codec_advance_buffer_callback(amount);
1166}
1167
1168static void codec_seek_complete_callback(void)
1169{
1170 logf("seek_complete");
1171 /* If seeking-while-playing, pcm playback is actually paused (pcm_is_paused())
1172 * but the paused flag is not set. If seeking-while-paused, the (paused) flag is
1173 * set, but pcm playback may have actually stopped due to a previous buffer clear.
1174 * The buffer clear below occurs with either condition. A seemless seek skips
1175 * this section and no buffer clear occurs.
1176 */
1177 if (pcm_is_paused() || paused)
1178 {
1179 /* Clear the buffer */
1180 pcmbuf_play_stop();
1181 dsp_configure(ci.dsp, DSP_FLUSH, 0);
1182
1183 /* If seeking-while-playing, resume pcm playback */
1184 if (!paused)
1185 pcmbuf_pause(false);
1186 }
1187 ci.seek_time = 0;
1188}
1189
1190static bool codec_seek_buffer_callback(size_t newpos)
1191{
1192 logf("codec_seek_buffer_callback");
1193
1194 int ret = bufseek(CUR_TI->audio_hid, newpos);
1195 if (ret == 0) {
1196 ci.curpos = newpos;
1197 return true;
1198 }
1199 else {
1200 return false;
1201 }
1202}
1203
1204static void codec_configure_callback(int setting, intptr_t value)
1205{
1206 switch (setting) {
1207 default:
1208 if (!dsp_configure(ci.dsp, setting, value))
1209 { logf("Illegal key:%d", setting); }
1210 }
1211}
1212
1213static void codec_track_changed(void)
1214{
1215 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
1216 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
1217}
1218
1219static void codec_pcmbuf_track_changed_callback(void)
1220{
1221 pcmbuf_set_position_callback(NULL);
1222 pcmbuf_callback_queue_post(Q_AUDIO_TRACK_CHANGED, 0);
1223}
1224
1225static void codec_discard_codec_callback(void)
1226{
1227 if (CUR_TI->codec_hid >= 0)
1228 {
1229 bufclose(CUR_TI->codec_hid);
1230 CUR_TI->codec_hid = -1;
1231 }
1232}
1233
1234static inline void codec_gapless_track_change(void)
1235{
1236 /* callback keeps the progress bar moving while the pcmbuf empties */
1237 pcmbuf_set_position_callback(codec_pcmbuf_position_callback);
1238 /* set the pcmbuf callback for when the track really changes */
1239 pcmbuf_set_event_handler(codec_pcmbuf_track_changed_callback);
1240}
1241
1242static inline void codec_crossfade_track_change(void)
1243{
1244 /* Initiate automatic crossfade mode */
1245 pcmbuf_crossfade_init(false);
1246 /* Notify the wps that the track change starts now */
1247 codec_track_changed();
1248}
1249
1250static void codec_track_skip_done(bool was_manual)
1251{
1252 /* Manual track change (always crossfade or flush audio). */
1253 if (was_manual)
1254 {
1255 pcmbuf_crossfade_init(true);
1256 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
1257 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
1258 }
1259 /* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
1260 else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
1261 && global_settings.crossfade != CROSSFADE_ENABLE_TRACKSKIP)
1262 {
1263 if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP)
1264 {
1265 if (global_settings.playlist_shuffle)
1266 /* shuffle mode is on, so crossfade: */
1267 codec_crossfade_track_change();
1268 else
1269 /* shuffle mode is off, so do a gapless track change */
1270 codec_gapless_track_change();
1271 }
1272 else
1273 /* normal crossfade: */
1274 codec_crossfade_track_change();
1275 }
1276 else
1277 /* normal gapless playback. */
1278 codec_gapless_track_change();
1279}
1280
1281static bool codec_load_next_track(void)
1282{
1283 intptr_t result = Q_CODEC_REQUEST_FAILED;
1284
1285 prev_track_elapsed = thistrack_id3->elapsed;
1286
1287#ifdef AB_REPEAT_ENABLE
1288 ab_end_of_track_report();
1289#endif
1290
1291 logf("Request new track");
1292
1293 if (ci.new_track == 0)
1294 {
1295 ci.new_track++;
1296 automatic_skip = true;
1297 }
1298
1299 if (!ci.stop_codec)
1300 {
1301 trigger_cpu_boost();
1302 LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK");
1303 result = queue_send(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
1304 }
1305
1306 switch (result)
1307 {
1308 case Q_CODEC_REQUEST_COMPLETE:
1309 LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
1310 codec_track_skip_done(!automatic_skip);
1311 return true;
1312
1313 case Q_CODEC_REQUEST_FAILED:
1314 LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED");
1315 ci.new_track = 0;
1316 ci.stop_codec = true;
1317 codec_requested_stop = true;
1318 return false;
1319
1320 default:
1321 LOGFQUEUE("codec |< default");
1322 ci.stop_codec = true;
1323 codec_requested_stop = true;
1324 return false;
1325 }
1326}
1327
1328static bool codec_request_next_track_callback(void)
1329{
1330 int prev_codectype;
1331
1332 if (ci.stop_codec || !playing)
1333 return false;
1334
1335 prev_codectype = get_codec_base_type(thistrack_id3->codectype);
1336 if (!codec_load_next_track())
1337 return false;
1338
1339 /* Seek to the beginning of the new track because if the struct
1340 mp3entry was buffered, "elapsed" might not be zero (if the track has
1341 been played already but not unbuffered) */
1342 codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
1343 /* Check if the next codec is the same file. */
1344 if (prev_codectype == get_codec_base_type(thistrack_id3->codectype))
1345 {
1346 logf("New track loaded");
1347 codec_discard_codec_callback();
1348 return true;
1349 }
1350 else
1351 {
1352 logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype);
1353 return false;
1354 }
1355}
1356
1357static void codec_thread(void)
1358{
1359 struct queue_event ev;
1360 int status;
1361
1362 while (1) {
1363 status = 0;
1364
1365 if (!pcmbuf_is_crossfade_active()) {
1366 cancel_cpu_boost();
1367 }
1368
1369 queue_wait(&codec_queue, &ev);
1370 codec_requested_stop = false;
1371
1372 switch (ev.id) {
1373 case Q_CODEC_LOAD_DISK:
1374 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
1375 queue_reply(&codec_queue, 1);
1376 audio_codec_loaded = true;
1377 ci.stop_codec = false;
1378 status = codec_load_file((const char *)ev.data, &ci);
1379 LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev.data, status);
1380 break;
1381
1382 case Q_CODEC_LOAD:
1383 LOGFQUEUE("codec < Q_CODEC_LOAD");
1384 if (CUR_TI->codec_hid < 0) {
1385 logf("Codec slot is empty!");
1386 /* Wait for the pcm buffer to go empty */
1387 while (pcm_is_playing())
1388 yield();
1389 /* This must be set to prevent an infinite loop */
1390 ci.stop_codec = true;
1391 LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
1392 queue_post(&codec_queue, Q_AUDIO_PLAY, 0);
1393 break;
1394 }
1395
1396 audio_codec_loaded = true;
1397 ci.stop_codec = false;
1398 status = codec_load_buf(CUR_TI->codec_hid, &ci);
1399 LOGFQUEUE("codec_load_buf %d\n", status);
1400 break;
1401
1402 case Q_CODEC_DO_CALLBACK:
1403 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
1404 queue_reply(&codec_queue, 1);
1405 if ((void*)ev.data != NULL)
1406 {
1407 cpucache_invalidate();
1408 ((void (*)(void))ev.data)();
1409 cpucache_flush();
1410 }
1411 break;
1412
1413#ifdef AUDIO_HAVE_RECORDING
1414 case Q_ENCODER_LOAD_DISK:
1415 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
1416 audio_codec_loaded = false; /* Not audio codec! */
1417 logf("loading encoder");
1418 ci.stop_encoder = false;
1419 status = codec_load_file((const char *)ev.data, &ci);
1420 logf("encoder stopped");
1421 break;
1422#endif /* AUDIO_HAVE_RECORDING */
1423
1424 default:
1425 LOGFQUEUE("codec < default");
1426 }
1427
1428 if (audio_codec_loaded)
1429 {
1430 if (ci.stop_codec)
1431 {
1432 status = CODEC_OK;
1433 if (!playing)
1434 pcmbuf_play_stop();
1435
1436 }
1437 audio_codec_loaded = false;
1438 }
1439
1440 switch (ev.id) {
1441 case Q_CODEC_LOAD_DISK:
1442 case Q_CODEC_LOAD:
1443 LOGFQUEUE("codec < Q_CODEC_LOAD");
1444 if (playing)
1445 {
1446 if (ci.new_track || status != CODEC_OK)
1447 {
1448 if (!ci.new_track)
1449 {
1450 logf("Codec failure, %d %d", ci.new_track, status);
1451 splash(HZ*2, "Codec failure");
1452 }
1453
1454 if (!codec_load_next_track())
1455 {
1456 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
1457 /* End of playlist */
1458 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
1459 break;
1460 }
1461 }
1462 else
1463 {
1464 logf("Codec finished");
1465 if (ci.stop_codec)
1466 {
1467 /* Wait for the audio to stop playing before
1468 * triggering the WPS exit */
1469 while(pcm_is_playing())
1470 {
1471 /* There has been one too many struct pointer swaps by now
1472 * so even though it says othertrack_id3, its the correct one! */
1473 othertrack_id3->elapsed =
1474 othertrack_id3->length - pcmbuf_get_latency();
1475 sleep(1);
1476 }
1477
1478 if (codec_requested_stop)
1479 {
1480 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
1481 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
1482 }
1483 break;
1484 }
1485 }
1486
1487 if (CUR_TI->codec_hid >= 0)
1488 {
1489 LOGFQUEUE("codec > codec Q_CODEC_LOAD");
1490 queue_post(&codec_queue, Q_CODEC_LOAD, 0);
1491 }
1492 else
1493 {
1494 const char *codec_fn =
1495 get_codec_filename(thistrack_id3->codectype);
1496 if (codec_fn)
1497 {
1498 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
1499 queue_post(&codec_queue, Q_CODEC_LOAD_DISK,
1500 (intptr_t)codec_fn);
1501 }
1502 }
1503 }
1504 break;
1505
1506#ifdef AUDIO_HAVE_RECORDING
1507 case Q_ENCODER_LOAD_DISK:
1508 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
1509
1510 if (status == CODEC_OK)
1511 break;
1512
1513 logf("Encoder failure");
1514 splash(HZ*2, "Encoder failure");
1515
1516 if (ci.enc_codec_loaded < 0)
1517 break;
1518
1519 logf("Encoder failed to load");
1520 ci.enc_codec_loaded = -1;
1521 break;
1522#endif /* AUDIO_HAVE_RECORDING */
1523
1524 default:
1525 LOGFQUEUE("codec < default");
1526
1527 } /* end switch */
1528 }
1529}
1530
1531/* Borrow the codec thread and return the ID */
1532void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
1533{
1534 /* Set id before telling thread to call something; it may be
1535 * needed before this function returns. */
1536 if (id != NULL)
1537 *id = codec_thread_id;
1538
1539 /* Codec thread will signal just before entering callback */
1540 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
1541 queue_send(&codec_queue, Q_CODEC_DO_CALLBACK, (intptr_t)fn);
1542}
1543
1544/* --- Buffering callbacks --- */ 898/* --- Buffering callbacks --- */
1545 899
1546static void buffering_low_buffer_callback(void *data) 900static void buffering_low_buffer_callback(void *data)
@@ -1722,7 +1076,7 @@ static bool audio_loadcodec(bool start_play)
1722 if (id3 && prev_id3 && 1076 if (id3 && prev_id3 &&
1723 get_codec_base_type(id3->codectype) == 1077 get_codec_base_type(id3->codectype) ==
1724 get_codec_base_type(prev_id3->codectype) 1078 get_codec_base_type(prev_id3->codectype)
1725 && audio_codec_loaded) 1079 && codec_is_loaded())
1726 { 1080 {
1727 logf("Reusing prev. codec"); 1081 logf("Reusing prev. codec");
1728 return true; 1082 return true;
@@ -2269,12 +1623,17 @@ unsigned long audio_prev_elapsed(void)
2269 return prev_track_elapsed; 1623 return prev_track_elapsed;
2270} 1624}
2271 1625
1626void audio_set_prev_elapsed(unsigned long setting)
1627{
1628 prev_track_elapsed = setting;
1629}
1630
2272static void audio_stop_codec_flush(void) 1631static void audio_stop_codec_flush(void)
2273{ 1632{
2274 ci.stop_codec = true; 1633 ci.stop_codec = true;
2275 pcmbuf_pause(true); 1634 pcmbuf_pause(true);
2276 1635
2277 while (audio_codec_loaded) 1636 while (codec_is_loaded())
2278 yield(); 1637 yield();
2279 1638
2280 /* If the audio codec is not loaded any more, and the audio is still 1639 /* If the audio codec is not loaded any more, and the audio is still
@@ -2292,9 +1651,9 @@ static void audio_stop_playback(void)
2292{ 1651{
2293 if (playing) 1652 if (playing)
2294 { 1653 {
2295 /* If still actively playing here, play out the last samples in the track 1654 /* If still actively playing here, play out the last samples in the
2296 * before stopping. A manual stop is actually paused at this point, so 1655 * pcm buffer before stopping. If a manual stop has occurred, the
2297 * don't continue playback. 1656 * paused flag is set, so don't continue playback.
2298 */ 1657 */
2299 if (!paused) 1658 if (!paused)
2300 pcmbuf_play_remainder(); 1659 pcmbuf_play_remainder();
@@ -2686,23 +2045,8 @@ void audio_init(void)
2686 2045
2687 pcm_init(); 2046 pcm_init();
2688 2047
2689 /* Initialize codec api. */ 2048 codec_init_codec_api();
2690 ci.read_filebuf = codec_filebuf_callback; 2049
2691 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
2692 ci.codec_get_buffer = codec_get_buffer;
2693 ci.request_buffer = codec_request_buffer_callback;
2694 ci.advance_buffer = codec_advance_buffer_callback;
2695 ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
2696 ci.request_next_track = codec_request_next_track_callback;
2697 ci.seek_buffer = codec_seek_buffer_callback;
2698 ci.seek_complete = codec_seek_complete_callback;
2699 ci.set_elapsed = codec_set_elapsed_callback;
2700 ci.set_offset = codec_set_offset_callback;
2701 ci.configure = codec_configure_callback;
2702 ci.discard_codec = codec_discard_codec_callback;
2703 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
2704 CODEC_IDX_AUDIO);
2705
2706 thistrack_id3 = &mp3entry_buf[0]; 2050 thistrack_id3 = &mp3entry_buf[0];
2707 othertrack_id3 = &mp3entry_buf[1]; 2051 othertrack_id3 = &mp3entry_buf[1];
2708 2052
@@ -2717,14 +2061,7 @@ void audio_init(void)
2717 talk first */ 2061 talk first */
2718 talk_init(); 2062 talk_init();
2719 2063
2720 codec_thread_id = create_thread( 2064 make_codec_thread();
2721 codec_thread, codec_stack, sizeof(codec_stack),
2722 CREATE_THREAD_FROZEN,
2723 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
2724 IF_COP(, CPU));
2725
2726 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
2727 codec_thread_id);
2728 2065
2729 audio_thread_id = create_thread(audio_thread, audio_stack, 2066 audio_thread_id = create_thread(audio_thread, audio_stack,
2730 sizeof(audio_stack), CREATE_THREAD_FROZEN, 2067 sizeof(audio_stack), CREATE_THREAD_FROZEN,
@@ -2790,3 +2127,28 @@ bool audio_is_thread_ready(void)
2790{ 2127{
2791 return audio_thread_ready; 2128 return audio_thread_ready;
2792} 2129}
2130
2131size_t audio_get_filebuflen(void)
2132{
2133 return filebuflen;
2134}
2135
2136int get_audio_hid()
2137{
2138 return CUR_TI->audio_hid;
2139}
2140
2141int *get_codec_hid()
2142{
2143 return &tracks[track_ridx].codec_hid;
2144}
2145
2146bool audio_is_playing(void)
2147{
2148 return playing;
2149}
2150
2151bool audio_is_paused(void)
2152{
2153 return paused;
2154}
diff --git a/apps/playback.h b/apps/playback.h
index add11e296b..a53d2bb2e7 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -23,6 +23,7 @@
23#define _PLAYBACK_H 23#define _PLAYBACK_H
24 24
25#include <stdbool.h> 25#include <stdbool.h>
26#include <stdlib.h>
26#include "config.h" 27#include "config.h"
27 28
28#ifdef HAVE_ALBUMART 29#ifdef HAVE_ALBUMART
@@ -52,7 +53,6 @@ void playback_release_aa_slot(int slot);
52#endif 53#endif
53 54
54/* Functions */ 55/* Functions */
55const char *get_codec_filename(int cod_spec);
56void voice_wait(void); 56void voice_wait(void);
57bool audio_is_thread_ready(void); 57bool audio_is_thread_ready(void);
58int audio_track_count(void); 58int audio_track_count(void);
@@ -68,18 +68,53 @@ enum
68 AUDIO_WANT_VOICE, 68 AUDIO_WANT_VOICE,
69}; 69};
70bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */ 70bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
71void codec_thread_do_callback(void (*fn)(void), 71size_t audio_get_filebuflen(void);
72 unsigned int *codec_thread_id); 72int get_audio_hid(void);
73int *get_codec_hid(void);
74void audio_set_prev_elapsed(unsigned long setting);
75bool audio_is_playing(void);
76bool audio_is_paused(void);
73 77
78/* Define one constant that includes recording related functionality */
79#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
80#define AUDIO_HAVE_RECORDING
81#endif
82
83enum {
84 Q_NULL = 0,
85 Q_AUDIO_PLAY = 1,
86 Q_AUDIO_STOP,
87 Q_AUDIO_PAUSE,
88 Q_AUDIO_SKIP,
89 Q_AUDIO_PRE_FF_REWIND,
90 Q_AUDIO_FF_REWIND,
91 Q_AUDIO_CHECK_NEW_TRACK,
92 Q_AUDIO_FLUSH,
93 Q_AUDIO_TRACK_CHANGED,
94 Q_AUDIO_DIR_SKIP,
95 Q_AUDIO_POSTINIT,
96 Q_AUDIO_FILL_BUFFER,
97 Q_AUDIO_FINISH_LOAD,
98 Q_CODEC_REQUEST_COMPLETE,
99 Q_CODEC_REQUEST_FAILED,
100
101 Q_CODEC_LOAD,
102 Q_CODEC_LOAD_DISK,
103
104#ifdef AUDIO_HAVE_RECORDING
105 Q_ENCODER_LOAD_DISK,
106 Q_ENCODER_RECORD,
107#endif
74 108
75#if CONFIG_CODEC == SWCODEC /* This #ifdef is better here than gui/wps.c */ 109 Q_CODEC_DO_CALLBACK,
76extern void audio_next_dir(void); 110};
77extern void audio_prev_dir(void); 111
112#if CONFIG_CODEC == SWCODEC
113void audio_next_dir(void);
114void audio_prev_dir(void);
78#else 115#else
79#define audio_next_dir() ({ }) 116#define audio_next_dir() ({ })
80#define audio_prev_dir() ({ }) 117#define audio_prev_dir() ({ })
81#endif 118#endif
82 119
83#endif 120#endif
84
85
diff --git a/apps/plugin.h b/apps/plugin.h
index 129fd9b389..227fe5ccbb 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -67,6 +67,7 @@ void* plugin_get_buffer(size_t *buffer_size);
67#include "dsp.h" 67#include "dsp.h"
68#include "codecs.h" 68#include "codecs.h"
69#include "playback.h" 69#include "playback.h"
70#include "codec_thread.h"
70#ifdef HAVE_RECORDING 71#ifdef HAVE_RECORDING
71#include "recording.h" 72#include "recording.h"
72#endif 73#endif
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 7e24ab9a30..fa747b7c61 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -34,7 +34,6 @@
34#include "audio.h" 34#include "audio.h"
35#if CONFIG_CODEC == SWCODEC 35#if CONFIG_CODEC == SWCODEC
36#include "thread.h" 36#include "thread.h"
37#include "playback.h"
38#include "enc_config.h" 37#include "enc_config.h"
39#if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT) 38#if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT)
40#include "spdif.h" 39#include "spdif.h"
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
index 83f546b59f..1b19be7eb5 100644
--- a/apps/scrobbler.c
+++ b/apps/scrobbler.c
@@ -25,7 +25,6 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging
25 25
26#include "file.h" 26#include "file.h"
27#include "sprintf.h" 27#include "sprintf.h"
28#include "playback.h"
29#include "logf.h" 28#include "logf.h"
30#include "metadata.h" 29#include "metadata.h"
31#include "kernel.h" 30#include "kernel.h"
@@ -72,7 +71,7 @@ static unsigned long timestamp;
72 71
73/* Crude work-around for Archos Sims - return a set amount */ 72/* Crude work-around for Archos Sims - return a set amount */
74#if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR) 73#if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR)
75unsigned long audio_prev_elapsed(void) 74static unsigned long audio_prev_elapsed(void)
76{ 75{
77 return 120000; 76 return 120000;
78} 77}
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 832a49e359..3e2fc75617 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -45,7 +45,6 @@
45#include "keyboard.h" 45#include "keyboard.h"
46#include "gui/list.h" 46#include "gui/list.h"
47#include "buffer.h" 47#include "buffer.h"
48#include "playback.h"
49#include "yesno.h" 48#include "yesno.h"
50#include "misc.h" 49#include "misc.h"
51#include "filetypes.h" 50#include "filetypes.h"
diff --git a/apps/talk.c b/apps/talk.c
index b2e25e1f8f..c7a7eab50a 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -40,9 +40,6 @@
40#include "logf.h" 40#include "logf.h"
41#include "bitswap.h" 41#include "bitswap.h"
42#include "structec.h" 42#include "structec.h"
43#if CONFIG_CODEC == SWCODEC
44#include "playback.h"
45#endif
46#include "debug.h" 43#include "debug.h"
47 44
48 45