FS#10739: playback.c code split
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23444 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
15ea6e663f
commit
9a4420bf96
13 changed files with 981 additions and 771 deletions
|
@ -134,6 +134,7 @@ audio_path.c
|
|||
#endif /* INPUT_SRC_CAPS != 0 */
|
||||
fixedpoint.c
|
||||
pcmbuf.c
|
||||
codec_thread.c
|
||||
playback.c
|
||||
codecs.c
|
||||
dsp.c
|
||||
|
|
784
apps/codec_thread.c
Normal file
784
apps/codec_thread.c
Normal file
|
@ -0,0 +1,784 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2005-2007 Miika Pekkarinen
|
||||
* Copyright (C) 2007-2008 Nicolas Pennequin
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
//#include <stdio.h>
|
||||
//#include <string.h>
|
||||
//#include <stdlib.h>
|
||||
//#include <ctype.h>
|
||||
|
||||
#include "playback.h"
|
||||
#include "codec_thread.h"
|
||||
#include "system.h"
|
||||
//#include "thread.h"
|
||||
//#include "file.h"
|
||||
//#include "panic.h"
|
||||
//#include "memory.h"
|
||||
//#include "lcd.h"
|
||||
//#include "font.h"
|
||||
//#include "button.h"
|
||||
#include "kernel.h"
|
||||
//#include "tree.h"
|
||||
//#include "debug.h"
|
||||
//#include "sprintf.h"
|
||||
//#include "settings.h"
|
||||
#include "codecs.h"
|
||||
//#include "audio.h"
|
||||
#include "buffering.h"
|
||||
//#include "appevents.h"
|
||||
//#include "voice_thread.h"
|
||||
//#include "mp3_playback.h"
|
||||
//#include "usb.h"
|
||||
//#include "storage.h"
|
||||
//#include "screens.h"
|
||||
//#include "playlist.h"
|
||||
#include "pcmbuf.h"
|
||||
//#include "buffer.h"
|
||||
#include "dsp.h"
|
||||
#include "abrepeat.h"
|
||||
//#include "cuesheet.h"
|
||||
#ifdef HAVE_TAGCACHE
|
||||
//#include "tagcache.h"
|
||||
#endif
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
//#include "icons.h"
|
||||
//#include "peakmeter.h"
|
||||
//#include "action.h"
|
||||
#ifdef HAVE_ALBUMART
|
||||
//#include "albumart.h"
|
||||
//#include "bmp.h"
|
||||
#endif
|
||||
#endif
|
||||
//#include "lang.h"
|
||||
//#include "misc.h"
|
||||
//#include "sound.h"
|
||||
#include "metadata.h"
|
||||
#include "splash.h"
|
||||
//#include "talk.h"
|
||||
//#include "ata_idle_notify.h"
|
||||
|
||||
#ifdef HAVE_RECORDING
|
||||
//#include "recording.h"
|
||||
//#include "pcm_record.h"
|
||||
#endif
|
||||
|
||||
#ifdef IPOD_ACCESSORY_PROTOCOL
|
||||
//#include "iap.h"
|
||||
#endif
|
||||
|
||||
/* Define LOGF_ENABLE to enable logf output in this file */
|
||||
/*#define LOGF_ENABLE*/
|
||||
#include "logf.h"
|
||||
|
||||
/* macros to enable logf for queues
|
||||
logging on SYS_TIMEOUT can be disabled */
|
||||
#ifdef SIMULATOR
|
||||
/* Define this for logf output of all queuing except SYS_TIMEOUT */
|
||||
#define PLAYBACK_LOGQUEUES
|
||||
/* Define this to logf SYS_TIMEOUT messages */
|
||||
/*#define PLAYBACK_LOGQUEUES_SYS_TIMEOUT*/
|
||||
#endif
|
||||
|
||||
#ifdef PLAYBACK_LOGQUEUES
|
||||
#define LOGFQUEUE logf
|
||||
#else
|
||||
#define LOGFQUEUE(...)
|
||||
#endif
|
||||
|
||||
#ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT
|
||||
#define LOGFQUEUE_SYS_TIMEOUT logf
|
||||
#else
|
||||
#define LOGFQUEUE_SYS_TIMEOUT(...)
|
||||
#endif
|
||||
|
||||
|
||||
/* Variables are commented with the threads that use them: *
|
||||
* A=audio, C=codec, V=voice. A suffix of - indicates that *
|
||||
* the variable is read but not updated on that thread. */
|
||||
|
||||
/* Main state control */
|
||||
static volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */
|
||||
|
||||
extern struct mp3entry *thistrack_id3, /* the currently playing track */
|
||||
*othertrack_id3; /* prev track during track-change-transition, or end of playlist,
|
||||
* next track otherwise */
|
||||
|
||||
/* Track change controls */
|
||||
extern bool automatic_skip; /* Who initiated in-progress skip? (C/A-) */
|
||||
|
||||
/* Set to true if the codec thread should send an audio stop request
|
||||
* (typically because the end of the playlist has been reached).
|
||||
*/
|
||||
static bool codec_requested_stop = false;
|
||||
|
||||
extern struct event_queue audio_queue;
|
||||
extern struct event_queue codec_queue;
|
||||
extern struct event_queue pcmbuf_queue;
|
||||
|
||||
/* Codec thread */
|
||||
extern struct codec_api ci;
|
||||
unsigned int codec_thread_id; /* For modifying thread priority later. */
|
||||
static struct queue_sender_list codec_queue_sender_list;
|
||||
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
|
||||
IBSS_ATTR;
|
||||
static const char codec_thread_name[] = "codec";
|
||||
|
||||
/**************************************/
|
||||
|
||||
/* Function to be called by pcm buffer callbacks.
|
||||
* Permissible Context(s): Audio interrupt
|
||||
*/
|
||||
static void pcmbuf_callback_queue_post(long id, intptr_t data)
|
||||
{
|
||||
/* No lock since we're already in audio interrupt context */
|
||||
queue_post(&pcmbuf_queue, id, data);
|
||||
}
|
||||
|
||||
const char *get_codec_filename(int cod_spec)
|
||||
{
|
||||
const char *fname;
|
||||
|
||||
#ifdef HAVE_RECORDING
|
||||
/* Can choose decoder or encoder if one available */
|
||||
int type = cod_spec & CODEC_TYPE_MASK;
|
||||
int afmt = cod_spec & CODEC_AFMT_MASK;
|
||||
|
||||
if ((unsigned)afmt >= AFMT_NUM_CODECS)
|
||||
type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
|
||||
|
||||
fname = (type == CODEC_TYPE_ENCODER) ?
|
||||
audio_formats[afmt].codec_enc_root_fn :
|
||||
audio_formats[afmt].codec_root_fn;
|
||||
|
||||
logf("%s: %d - %s",
|
||||
(type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
|
||||
afmt, fname ? fname : "<unknown>");
|
||||
#else /* !HAVE_RECORDING */
|
||||
/* Always decoder */
|
||||
if ((unsigned)cod_spec >= AFMT_NUM_CODECS)
|
||||
cod_spec = AFMT_UNKNOWN;
|
||||
fname = audio_formats[cod_spec].codec_root_fn;
|
||||
logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>");
|
||||
#endif /* HAVE_RECORDING */
|
||||
|
||||
return fname;
|
||||
} /* get_codec_filename */
|
||||
|
||||
/* --- Codec thread --- */
|
||||
static bool codec_pcmbuf_insert_callback(
|
||||
const void *ch1, const void *ch2, int count)
|
||||
{
|
||||
const char *src[2] = { ch1, ch2 };
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
int out_count = dsp_output_count(ci.dsp, count);
|
||||
int inp_count;
|
||||
char *dest;
|
||||
|
||||
/* Prevent audio from a previous track from playing */
|
||||
if (ci.new_track || ci.stop_codec)
|
||||
return true;
|
||||
|
||||
while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
|
||||
{
|
||||
cancel_cpu_boost();
|
||||
sleep(1);
|
||||
if (ci.seek_time || ci.new_track || ci.stop_codec)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Get the real input_size for output_size bytes, guarding
|
||||
* against resampling buffer overflows. */
|
||||
inp_count = dsp_input_count(ci.dsp, out_count);
|
||||
|
||||
if (inp_count <= 0)
|
||||
return true;
|
||||
|
||||
/* Input size has grown, no error, just don't write more than length */
|
||||
if (inp_count > count)
|
||||
inp_count = count;
|
||||
|
||||
out_count = dsp_process(ci.dsp, dest, src, inp_count);
|
||||
|
||||
if (out_count <= 0)
|
||||
return true;
|
||||
|
||||
pcmbuf_write_complete(out_count);
|
||||
|
||||
count -= inp_count;
|
||||
}
|
||||
|
||||
return true;
|
||||
} /* codec_pcmbuf_insert_callback */
|
||||
|
||||
static void* codec_get_buffer(size_t *size)
|
||||
{
|
||||
if (codec_size >= CODEC_SIZE)
|
||||
return NULL;
|
||||
*size = CODEC_SIZE - codec_size;
|
||||
return &codecbuf[codec_size];
|
||||
}
|
||||
|
||||
/* Between the codec and PCM track change, we need to keep updating the
|
||||
"elapsed" value of the previous (to the codec, but current to the
|
||||
user/PCM/WPS) track, so that the progressbar reaches the end.
|
||||
During that transition, the WPS will display prevtrack_id3. */
|
||||
static void codec_pcmbuf_position_callback(size_t size) ICODE_ATTR;
|
||||
static void codec_pcmbuf_position_callback(size_t size)
|
||||
{
|
||||
/* This is called from an ISR, so be quick */
|
||||
unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
|
||||
othertrack_id3->elapsed;
|
||||
|
||||
if (time >= othertrack_id3->length)
|
||||
{
|
||||
pcmbuf_set_position_callback(NULL);
|
||||
othertrack_id3->elapsed = othertrack_id3->length;
|
||||
}
|
||||
else
|
||||
othertrack_id3->elapsed = time;
|
||||
}
|
||||
|
||||
static void codec_set_elapsed_callback(unsigned int value)
|
||||
{
|
||||
unsigned int latency;
|
||||
if (ci.seek_time)
|
||||
return;
|
||||
|
||||
#ifdef AB_REPEAT_ENABLE
|
||||
ab_position_report(value);
|
||||
#endif
|
||||
|
||||
latency = pcmbuf_get_latency();
|
||||
if (value < latency)
|
||||
thistrack_id3->elapsed = 0;
|
||||
else if (value - latency > thistrack_id3->elapsed ||
|
||||
value - latency < thistrack_id3->elapsed - 2)
|
||||
{
|
||||
thistrack_id3->elapsed = value - latency;
|
||||
}
|
||||
}
|
||||
|
||||
static void codec_set_offset_callback(size_t value)
|
||||
{
|
||||
unsigned int latency;
|
||||
|
||||
if (ci.seek_time)
|
||||
return;
|
||||
|
||||
latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
|
||||
if (value < latency)
|
||||
thistrack_id3->offset = 0;
|
||||
else
|
||||
thistrack_id3->offset = value - latency;
|
||||
}
|
||||
|
||||
static void codec_advance_buffer_counters(size_t amount)
|
||||
{
|
||||
bufadvance(get_audio_hid(), amount);
|
||||
ci.curpos += amount;
|
||||
}
|
||||
|
||||
/* copy up-to size bytes into ptr and return the actual size copied */
|
||||
static size_t codec_filebuf_callback(void *ptr, size_t size)
|
||||
{
|
||||
ssize_t copy_n;
|
||||
|
||||
if (ci.stop_codec || !audio_is_playing())
|
||||
return 0;
|
||||
|
||||
copy_n = bufread(get_audio_hid(), size, ptr);
|
||||
|
||||
/* Nothing requested OR nothing left */
|
||||
if (copy_n == 0)
|
||||
return 0;
|
||||
|
||||
/* Update read and other position pointers */
|
||||
codec_advance_buffer_counters(copy_n);
|
||||
|
||||
/* Return the actual amount of data copied to the buffer */
|
||||
return copy_n;
|
||||
} /* codec_filebuf_callback */
|
||||
|
||||
static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||
{
|
||||
size_t copy_n = reqsize;
|
||||
ssize_t ret;
|
||||
void *ptr;
|
||||
|
||||
if (!audio_is_playing())
|
||||
{
|
||||
*realsize = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
|
||||
if (ret >= 0)
|
||||
copy_n = MIN((size_t)ret, reqsize);
|
||||
|
||||
if (copy_n == 0)
|
||||
{
|
||||
*realsize = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*realsize = copy_n;
|
||||
|
||||
return ptr;
|
||||
} /* codec_request_buffer_callback */
|
||||
|
||||
int get_codec_base_type(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case AFMT_MPA_L1:
|
||||
case AFMT_MPA_L2:
|
||||
case AFMT_MPA_L3:
|
||||
return AFMT_MPA_L3;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static void codec_advance_buffer_callback(size_t amount)
|
||||
{
|
||||
codec_advance_buffer_counters(amount);
|
||||
codec_set_offset_callback(ci.curpos);
|
||||
}
|
||||
|
||||
static void codec_advance_buffer_loc_callback(void *ptr)
|
||||
{
|
||||
size_t amount = buf_get_offset(get_audio_hid(), ptr);
|
||||
codec_advance_buffer_callback(amount);
|
||||
}
|
||||
|
||||
static void codec_seek_complete_callback(void)
|
||||
{
|
||||
logf("seek_complete");
|
||||
/* If seeking-while-playing, pcm playback is actually paused (pcm_is_paused())
|
||||
* but audio_is_paused() is false. If seeking-while-paused, audio_is_paused() is
|
||||
* true, but pcm playback may have actually stopped due to a previous buffer clear.
|
||||
* The buffer clear below occurs with either condition. A seemless seek skips
|
||||
* this section and no buffer clear occurs.
|
||||
*/
|
||||
if (pcm_is_paused() || audio_is_paused())
|
||||
{
|
||||
/* Clear the buffer */
|
||||
pcmbuf_play_stop();
|
||||
dsp_configure(ci.dsp, DSP_FLUSH, 0);
|
||||
|
||||
/* If seeking-while-playing, resume pcm playback */
|
||||
if (!audio_is_paused())
|
||||
pcmbuf_pause(false);
|
||||
}
|
||||
ci.seek_time = 0;
|
||||
}
|
||||
|
||||
static bool codec_seek_buffer_callback(size_t newpos)
|
||||
{
|
||||
logf("codec_seek_buffer_callback");
|
||||
|
||||
int ret = bufseek(get_audio_hid(), newpos);
|
||||
if (ret == 0) {
|
||||
ci.curpos = newpos;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void codec_configure_callback(int setting, intptr_t value)
|
||||
{
|
||||
switch (setting) {
|
||||
default:
|
||||
if (!dsp_configure(ci.dsp, setting, value))
|
||||
{ logf("Illegal key:%d", setting); }
|
||||
}
|
||||
}
|
||||
|
||||
static void codec_track_changed(void)
|
||||
{
|
||||
LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
|
||||
queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
|
||||
}
|
||||
|
||||
static void codec_pcmbuf_track_changed_callback(void)
|
||||
{
|
||||
pcmbuf_set_position_callback(NULL);
|
||||
pcmbuf_callback_queue_post(Q_AUDIO_TRACK_CHANGED, 0);
|
||||
}
|
||||
|
||||
static void codec_discard_codec_callback(void)
|
||||
{
|
||||
int *codec_hid = get_codec_hid();
|
||||
if (*codec_hid >= 0)
|
||||
{
|
||||
bufclose(*codec_hid);
|
||||
*codec_hid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void codec_gapless_track_change(void)
|
||||
{
|
||||
/* callback keeps the progress bar moving while the pcmbuf empties */
|
||||
pcmbuf_set_position_callback(codec_pcmbuf_position_callback);
|
||||
/* set the pcmbuf callback for when the track really changes */
|
||||
pcmbuf_set_event_handler(codec_pcmbuf_track_changed_callback);
|
||||
}
|
||||
|
||||
static inline void codec_crossfade_track_change(void)
|
||||
{
|
||||
/* Initiate automatic crossfade mode */
|
||||
pcmbuf_crossfade_init(false);
|
||||
/* Notify the wps that the track change starts now */
|
||||
codec_track_changed();
|
||||
}
|
||||
|
||||
static void codec_track_skip_done(bool was_manual)
|
||||
{
|
||||
/* Manual track change (always crossfade or flush audio). */
|
||||
if (was_manual)
|
||||
{
|
||||
pcmbuf_crossfade_init(true);
|
||||
LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
|
||||
queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
|
||||
}
|
||||
/* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
|
||||
else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
|
||||
&& global_settings.crossfade != CROSSFADE_ENABLE_TRACKSKIP)
|
||||
{
|
||||
if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP)
|
||||
{
|
||||
if (global_settings.playlist_shuffle)
|
||||
/* shuffle mode is on, so crossfade: */
|
||||
codec_crossfade_track_change();
|
||||
else
|
||||
/* shuffle mode is off, so do a gapless track change */
|
||||
codec_gapless_track_change();
|
||||
}
|
||||
else
|
||||
/* normal crossfade: */
|
||||
codec_crossfade_track_change();
|
||||
}
|
||||
else
|
||||
/* normal gapless playback. */
|
||||
codec_gapless_track_change();
|
||||
}
|
||||
|
||||
static bool codec_load_next_track(void)
|
||||
{
|
||||
intptr_t result = Q_CODEC_REQUEST_FAILED;
|
||||
|
||||
audio_set_prev_elapsed(thistrack_id3->elapsed);
|
||||
|
||||
#ifdef AB_REPEAT_ENABLE
|
||||
ab_end_of_track_report();
|
||||
#endif
|
||||
|
||||
logf("Request new track");
|
||||
|
||||
if (ci.new_track == 0)
|
||||
{
|
||||
ci.new_track++;
|
||||
automatic_skip = true;
|
||||
}
|
||||
|
||||
if (!ci.stop_codec)
|
||||
{
|
||||
trigger_cpu_boost();
|
||||
LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK");
|
||||
result = queue_send(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
|
||||
}
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case Q_CODEC_REQUEST_COMPLETE:
|
||||
LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
|
||||
codec_track_skip_done(!automatic_skip);
|
||||
return true;
|
||||
|
||||
case Q_CODEC_REQUEST_FAILED:
|
||||
LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED");
|
||||
ci.new_track = 0;
|
||||
ci.stop_codec = true;
|
||||
codec_requested_stop = true;
|
||||
return false;
|
||||
|
||||
default:
|
||||
LOGFQUEUE("codec |< default");
|
||||
ci.stop_codec = true;
|
||||
codec_requested_stop = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool codec_request_next_track_callback(void)
|
||||
{
|
||||
int prev_codectype;
|
||||
|
||||
if (ci.stop_codec || !audio_is_playing())
|
||||
return false;
|
||||
|
||||
prev_codectype = get_codec_base_type(thistrack_id3->codectype);
|
||||
if (!codec_load_next_track())
|
||||
return false;
|
||||
|
||||
/* Seek to the beginning of the new track because if the struct
|
||||
mp3entry was buffered, "elapsed" might not be zero (if the track has
|
||||
been played already but not unbuffered) */
|
||||
codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
|
||||
/* Check if the next codec is the same file. */
|
||||
if (prev_codectype == get_codec_base_type(thistrack_id3->codectype))
|
||||
{
|
||||
logf("New track loaded");
|
||||
codec_discard_codec_callback();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void codec_thread(void)
|
||||
{
|
||||
struct queue_event ev;
|
||||
int status;
|
||||
|
||||
while (1) {
|
||||
status = 0;
|
||||
|
||||
if (!pcmbuf_is_crossfade_active()) {
|
||||
cancel_cpu_boost();
|
||||
}
|
||||
|
||||
queue_wait(&codec_queue, &ev);
|
||||
codec_requested_stop = false;
|
||||
|
||||
switch (ev.id) {
|
||||
case Q_CODEC_LOAD_DISK:
|
||||
LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
|
||||
queue_reply(&codec_queue, 1);
|
||||
audio_codec_loaded = true;
|
||||
ci.stop_codec = false;
|
||||
status = codec_load_file((const char *)ev.data, &ci);
|
||||
LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev.data, status);
|
||||
break;
|
||||
|
||||
case Q_CODEC_LOAD:
|
||||
LOGFQUEUE("codec < Q_CODEC_LOAD");
|
||||
if (*get_codec_hid() < 0) {
|
||||
logf("Codec slot is empty!");
|
||||
/* Wait for the pcm buffer to go empty */
|
||||
while (pcm_is_playing())
|
||||
yield();
|
||||
/* This must be set to prevent an infinite loop */
|
||||
ci.stop_codec = true;
|
||||
LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
|
||||
queue_post(&codec_queue, Q_AUDIO_PLAY, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
audio_codec_loaded = true;
|
||||
ci.stop_codec = false;
|
||||
status = codec_load_buf(*get_codec_hid(), &ci);
|
||||
LOGFQUEUE("codec_load_buf %d\n", status);
|
||||
break;
|
||||
|
||||
case Q_CODEC_DO_CALLBACK:
|
||||
LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
|
||||
queue_reply(&codec_queue, 1);
|
||||
if ((void*)ev.data != NULL)
|
||||
{
|
||||
cpucache_invalidate();
|
||||
((void (*)(void))ev.data)();
|
||||
cpucache_flush();
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef AUDIO_HAVE_RECORDING
|
||||
case Q_ENCODER_LOAD_DISK:
|
||||
LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
|
||||
audio_codec_loaded = false; /* Not audio codec! */
|
||||
logf("loading encoder");
|
||||
ci.stop_encoder = false;
|
||||
status = codec_load_file((const char *)ev.data, &ci);
|
||||
logf("encoder stopped");
|
||||
break;
|
||||
#endif /* AUDIO_HAVE_RECORDING */
|
||||
|
||||
default:
|
||||
LOGFQUEUE("codec < default");
|
||||
}
|
||||
|
||||
if (audio_codec_loaded)
|
||||
{
|
||||
if (ci.stop_codec)
|
||||
{
|
||||
status = CODEC_OK;
|
||||
if (!audio_is_playing())
|
||||
pcmbuf_play_stop();
|
||||
|
||||
}
|
||||
audio_codec_loaded = false;
|
||||
}
|
||||
|
||||
switch (ev.id) {
|
||||
case Q_CODEC_LOAD_DISK:
|
||||
case Q_CODEC_LOAD:
|
||||
LOGFQUEUE("codec < Q_CODEC_LOAD");
|
||||
if (audio_is_playing())
|
||||
{
|
||||
if (ci.new_track || status != CODEC_OK)
|
||||
{
|
||||
if (!ci.new_track)
|
||||
{
|
||||
logf("Codec failure, %d %d", ci.new_track, status);
|
||||
splash(HZ*2, "Codec failure");
|
||||
}
|
||||
|
||||
if (!codec_load_next_track())
|
||||
{
|
||||
LOGFQUEUE("codec > audio Q_AUDIO_STOP");
|
||||
/* End of playlist */
|
||||
queue_post(&audio_queue, Q_AUDIO_STOP, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logf("Codec finished");
|
||||
if (ci.stop_codec)
|
||||
{
|
||||
/* Wait for the audio to stop playing before
|
||||
* triggering the WPS exit */
|
||||
while(pcm_is_playing())
|
||||
{
|
||||
/* There has been one too many struct pointer swaps by now
|
||||
* so even though it says othertrack_id3, its the correct one! */
|
||||
othertrack_id3->elapsed =
|
||||
othertrack_id3->length - pcmbuf_get_latency();
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (codec_requested_stop)
|
||||
{
|
||||
LOGFQUEUE("codec > audio Q_AUDIO_STOP");
|
||||
queue_post(&audio_queue, Q_AUDIO_STOP, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*get_codec_hid() >= 0)
|
||||
{
|
||||
LOGFQUEUE("codec > codec Q_CODEC_LOAD");
|
||||
queue_post(&codec_queue, Q_CODEC_LOAD, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *codec_fn =
|
||||
get_codec_filename(thistrack_id3->codectype);
|
||||
if (codec_fn)
|
||||
{
|
||||
LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
|
||||
queue_post(&codec_queue, Q_CODEC_LOAD_DISK,
|
||||
(intptr_t)codec_fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef AUDIO_HAVE_RECORDING
|
||||
case Q_ENCODER_LOAD_DISK:
|
||||
LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
|
||||
|
||||
if (status == CODEC_OK)
|
||||
break;
|
||||
|
||||
logf("Encoder failure");
|
||||
splash(HZ*2, "Encoder failure");
|
||||
|
||||
if (ci.enc_codec_loaded < 0)
|
||||
break;
|
||||
|
||||
logf("Encoder failed to load");
|
||||
ci.enc_codec_loaded = -1;
|
||||
break;
|
||||
#endif /* AUDIO_HAVE_RECORDING */
|
||||
|
||||
default:
|
||||
LOGFQUEUE("codec < default");
|
||||
|
||||
} /* end switch */
|
||||
}
|
||||
}
|
||||
|
||||
/* Borrow the codec thread and return the ID */
|
||||
void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
|
||||
{
|
||||
/* Set id before telling thread to call something; it may be
|
||||
* needed before this function returns. */
|
||||
if (id != NULL)
|
||||
*id = codec_thread_id;
|
||||
|
||||
/* Codec thread will signal just before entering callback */
|
||||
LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
|
||||
queue_send(&codec_queue, Q_CODEC_DO_CALLBACK, (intptr_t)fn);
|
||||
}
|
||||
|
||||
void codec_init_codec_api(void)
|
||||
{
|
||||
/* Initialize codec api. */
|
||||
ci.read_filebuf = codec_filebuf_callback;
|
||||
ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
|
||||
ci.codec_get_buffer = codec_get_buffer;
|
||||
ci.request_buffer = codec_request_buffer_callback;
|
||||
ci.advance_buffer = codec_advance_buffer_callback;
|
||||
ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
|
||||
ci.request_next_track = codec_request_next_track_callback;
|
||||
ci.seek_buffer = codec_seek_buffer_callback;
|
||||
ci.seek_complete = codec_seek_complete_callback;
|
||||
ci.set_elapsed = codec_set_elapsed_callback;
|
||||
ci.set_offset = codec_set_offset_callback;
|
||||
ci.configure = codec_configure_callback;
|
||||
ci.discard_codec = codec_discard_codec_callback;
|
||||
ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
|
||||
CODEC_IDX_AUDIO);
|
||||
}
|
||||
|
||||
bool codec_is_loaded(void)
|
||||
{
|
||||
return audio_codec_loaded;
|
||||
}
|
||||
|
||||
void make_codec_thread(void)
|
||||
{
|
||||
codec_thread_id = create_thread(
|
||||
codec_thread, codec_stack, sizeof(codec_stack),
|
||||
CREATE_THREAD_FROZEN,
|
||||
codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
|
||||
IF_COP(, CPU));
|
||||
queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
|
||||
codec_thread_id);
|
||||
}
|
35
apps/codec_thread.h
Normal file
35
apps/codec_thread.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2005 Miika Pekkarinen
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _CODEC_THREAD_H
|
||||
#define _CODEC_THREAD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
const char *get_codec_filename(int cod_spec);
|
||||
void codec_thread_do_callback(void (*fn)(void),
|
||||
unsigned int *codec_thread_id);
|
||||
void make_codec_thread(void);
|
||||
int get_codec_base_type(int type);
|
||||
void codec_init_codec_api(void);
|
||||
bool codec_is_loaded(void);
|
||||
|
||||
#endif
|
|
@ -272,9 +272,6 @@ static bool dbg_audio_thread(void)
|
|||
}
|
||||
#endif /* !SIMULATOR */
|
||||
#else /* CONFIG_CODEC == SWCODEC */
|
||||
extern size_t filebuflen;
|
||||
/* This is a size_t, but call it a long so it puts a - when it's bad. */
|
||||
|
||||
static unsigned int ticks, boost_ticks, freq_sum;
|
||||
|
||||
static void dbg_audio_task(void)
|
||||
|
@ -296,6 +293,8 @@ static bool dbg_buffering_thread(void)
|
|||
size_t bufsize = pcmbuf_get_bufsize();
|
||||
int pcmbufdescs = pcmbuf_descs();
|
||||
struct buffering_debug d;
|
||||
size_t filebuflen = audio_get_filebuflen();
|
||||
/* This is a size_t, but call it a long so it puts a - when it's bad. */
|
||||
|
||||
ticks = boost_ticks = freq_sum = 0;
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "playback.h"
|
||||
#include "debug.h"
|
||||
#include "logf.h"
|
||||
#include "cuesheet.h"
|
||||
|
|
|
@ -111,7 +111,7 @@ static struct trackdata trackdata[MAX_TRACK_ENTRIES];
|
|||
static unsigned int current_track_counter = 0;
|
||||
|
||||
/* Play time of the previous track */
|
||||
unsigned long prev_track_elapsed;
|
||||
static unsigned long prev_track_elapsed;
|
||||
|
||||
#ifndef SIMULATOR
|
||||
static int track_read_idx = 0;
|
||||
|
|
856
apps/playback.c
856
apps/playback.c
File diff suppressed because it is too large
Load diff
|
@ -23,6 +23,7 @@
|
|||
#define _PLAYBACK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_ALBUMART
|
||||
|
@ -52,7 +53,6 @@ void playback_release_aa_slot(int slot);
|
|||
#endif
|
||||
|
||||
/* Functions */
|
||||
const char *get_codec_filename(int cod_spec);
|
||||
void voice_wait(void);
|
||||
bool audio_is_thread_ready(void);
|
||||
int audio_track_count(void);
|
||||
|
@ -68,18 +68,53 @@ enum
|
|||
AUDIO_WANT_VOICE,
|
||||
};
|
||||
bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
|
||||
void codec_thread_do_callback(void (*fn)(void),
|
||||
unsigned int *codec_thread_id);
|
||||
size_t audio_get_filebuflen(void);
|
||||
int get_audio_hid(void);
|
||||
int *get_codec_hid(void);
|
||||
void audio_set_prev_elapsed(unsigned long setting);
|
||||
bool audio_is_playing(void);
|
||||
bool audio_is_paused(void);
|
||||
|
||||
/* Define one constant that includes recording related functionality */
|
||||
#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
|
||||
#define AUDIO_HAVE_RECORDING
|
||||
#endif
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC /* This #ifdef is better here than gui/wps.c */
|
||||
extern void audio_next_dir(void);
|
||||
extern void audio_prev_dir(void);
|
||||
enum {
|
||||
Q_NULL = 0,
|
||||
Q_AUDIO_PLAY = 1,
|
||||
Q_AUDIO_STOP,
|
||||
Q_AUDIO_PAUSE,
|
||||
Q_AUDIO_SKIP,
|
||||
Q_AUDIO_PRE_FF_REWIND,
|
||||
Q_AUDIO_FF_REWIND,
|
||||
Q_AUDIO_CHECK_NEW_TRACK,
|
||||
Q_AUDIO_FLUSH,
|
||||
Q_AUDIO_TRACK_CHANGED,
|
||||
Q_AUDIO_DIR_SKIP,
|
||||
Q_AUDIO_POSTINIT,
|
||||
Q_AUDIO_FILL_BUFFER,
|
||||
Q_AUDIO_FINISH_LOAD,
|
||||
Q_CODEC_REQUEST_COMPLETE,
|
||||
Q_CODEC_REQUEST_FAILED,
|
||||
|
||||
Q_CODEC_LOAD,
|
||||
Q_CODEC_LOAD_DISK,
|
||||
|
||||
#ifdef AUDIO_HAVE_RECORDING
|
||||
Q_ENCODER_LOAD_DISK,
|
||||
Q_ENCODER_RECORD,
|
||||
#endif
|
||||
|
||||
Q_CODEC_DO_CALLBACK,
|
||||
};
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
void audio_next_dir(void);
|
||||
void audio_prev_dir(void);
|
||||
#else
|
||||
#define audio_next_dir() ({ })
|
||||
#define audio_prev_dir() ({ })
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ void* plugin_get_buffer(size_t *buffer_size);
|
|||
#include "dsp.h"
|
||||
#include "codecs.h"
|
||||
#include "playback.h"
|
||||
#include "codec_thread.h"
|
||||
#ifdef HAVE_RECORDING
|
||||
#include "recording.h"
|
||||
#endif
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include "audio.h"
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "thread.h"
|
||||
#include "playback.h"
|
||||
#include "enc_config.h"
|
||||
#if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT)
|
||||
#include "spdif.h"
|
||||
|
|
|
@ -25,7 +25,6 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging
|
|||
|
||||
#include "file.h"
|
||||
#include "sprintf.h"
|
||||
#include "playback.h"
|
||||
#include "logf.h"
|
||||
#include "metadata.h"
|
||||
#include "kernel.h"
|
||||
|
@ -72,7 +71,7 @@ static unsigned long timestamp;
|
|||
|
||||
/* Crude work-around for Archos Sims - return a set amount */
|
||||
#if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR)
|
||||
unsigned long audio_prev_elapsed(void)
|
||||
static unsigned long audio_prev_elapsed(void)
|
||||
{
|
||||
return 120000;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
#include "keyboard.h"
|
||||
#include "gui/list.h"
|
||||
#include "buffer.h"
|
||||
#include "playback.h"
|
||||
#include "yesno.h"
|
||||
#include "misc.h"
|
||||
#include "filetypes.h"
|
||||
|
|
|
@ -40,9 +40,6 @@
|
|||
#include "logf.h"
|
||||
#include "bitswap.h"
|
||||
#include "structec.h"
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "playback.h"
|
||||
#endif
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue