forked from len0rd/rockbox
Code cleanup in codec_thread, playback and pcmbuf; more elegant solution to leftover samples
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23471 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
bcadf96066
commit
e8eefe98bf
6 changed files with 197 additions and 283 deletions
|
@ -20,69 +20,17 @@
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
//#include <stdio.h>
|
|
||||||
//#include <string.h>
|
|
||||||
//#include <stdlib.h>
|
|
||||||
//#include <ctype.h>
|
|
||||||
|
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include "codec_thread.h"
|
#include "codec_thread.h"
|
||||||
#include "system.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 "kernel.h"
|
||||||
//#include "tree.h"
|
|
||||||
//#include "debug.h"
|
|
||||||
//#include "sprintf.h"
|
|
||||||
//#include "settings.h"
|
|
||||||
#include "codecs.h"
|
#include "codecs.h"
|
||||||
//#include "audio.h"
|
|
||||||
#include "buffering.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 "pcmbuf.h"
|
||||||
//#include "buffer.h"
|
|
||||||
#include "dsp.h"
|
#include "dsp.h"
|
||||||
#include "abrepeat.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 "metadata.h"
|
||||||
#include "splash.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 to enable logf output in this file */
|
||||||
/*#define LOGF_ENABLE*/
|
/*#define LOGF_ENABLE*/
|
||||||
|
@ -110,12 +58,16 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Variables are commented with the threads that use them: *
|
/* Variables are commented with the threads that use them:
|
||||||
* A=audio, C=codec, V=voice. A suffix of - indicates that *
|
* A=audio, C=codec, V=voice. A suffix of - indicates that
|
||||||
* the variable is read but not updated on that thread. */
|
* the variable is read but not updated on that thread.
|
||||||
|
|
||||||
|
* Unless otherwise noted, the extern variables are located
|
||||||
|
* in playback.c.
|
||||||
|
*/
|
||||||
|
|
||||||
/* Main state control */
|
/* Main state control */
|
||||||
static volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */
|
volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */
|
||||||
|
|
||||||
extern struct mp3entry *thistrack_id3, /* the currently playing track */
|
extern struct mp3entry *thistrack_id3, /* the currently playing track */
|
||||||
*othertrack_id3; /* prev track during track-change-transition, or end of playlist,
|
*othertrack_id3; /* prev track during track-change-transition, or end of playlist,
|
||||||
|
@ -131,25 +83,35 @@ static bool codec_requested_stop = false;
|
||||||
|
|
||||||
extern struct event_queue audio_queue;
|
extern struct event_queue audio_queue;
|
||||||
extern struct event_queue codec_queue;
|
extern struct event_queue codec_queue;
|
||||||
extern struct event_queue pcmbuf_queue;
|
|
||||||
|
extern struct codec_api ci; /* from codecs.c */
|
||||||
|
|
||||||
/* Codec thread */
|
/* Codec thread */
|
||||||
extern struct codec_api ci;
|
unsigned int codec_thread_id; /* For modifying thread priority later.
|
||||||
unsigned int codec_thread_id; /* For modifying thread priority later. */
|
Used by playback.c and pcmbuf.c */
|
||||||
static struct queue_sender_list codec_queue_sender_list;
|
static struct queue_sender_list codec_queue_sender_list;
|
||||||
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
|
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
|
||||||
IBSS_ATTR;
|
IBSS_ATTR;
|
||||||
static const char codec_thread_name[] = "codec";
|
static const char codec_thread_name[] = "codec";
|
||||||
|
|
||||||
|
/* function prototypes */
|
||||||
|
static bool codec_load_next_track(void);
|
||||||
|
|
||||||
|
|
||||||
/**************************************/
|
/**************************************/
|
||||||
|
|
||||||
/* Function to be called by pcm buffer callbacks.
|
/** misc external functions */
|
||||||
* Permissible Context(s): Audio interrupt
|
|
||||||
*/
|
int get_codec_base_type(int type)
|
||||||
static void pcmbuf_callback_queue_post(long id, intptr_t data)
|
|
||||||
{
|
{
|
||||||
/* No lock since we're already in audio interrupt context */
|
switch (type) {
|
||||||
queue_post(&pcmbuf_queue, id, data);
|
case AFMT_MPA_L1:
|
||||||
|
case AFMT_MPA_L2:
|
||||||
|
case AFMT_MPA_L3:
|
||||||
|
return AFMT_MPA_L3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *get_codec_filename(int cod_spec)
|
const char *get_codec_filename(int cod_spec)
|
||||||
|
@ -182,7 +144,30 @@ const char *get_codec_filename(int cod_spec)
|
||||||
return fname;
|
return fname;
|
||||||
} /* get_codec_filename */
|
} /* get_codec_filename */
|
||||||
|
|
||||||
/* --- Codec thread --- */
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** codec API callbacks */
|
||||||
|
|
||||||
|
static void* codec_get_buffer(size_t *size)
|
||||||
|
{
|
||||||
|
if (codec_size >= CODEC_SIZE)
|
||||||
|
return NULL;
|
||||||
|
*size = CODEC_SIZE - codec_size;
|
||||||
|
return &codecbuf[codec_size];
|
||||||
|
}
|
||||||
|
|
||||||
static bool codec_pcmbuf_insert_callback(
|
static bool codec_pcmbuf_insert_callback(
|
||||||
const void *ch1, const void *ch2, int count)
|
const void *ch1, const void *ch2, int count)
|
||||||
{
|
{
|
||||||
|
@ -230,37 +215,8 @@ static bool codec_pcmbuf_insert_callback(
|
||||||
return true;
|
return true;
|
||||||
} /* codec_pcmbuf_insert_callback */
|
} /* 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)
|
static void codec_set_elapsed_callback(unsigned int value)
|
||||||
{
|
{
|
||||||
unsigned int latency;
|
|
||||||
if (ci.seek_time)
|
if (ci.seek_time)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -268,30 +224,33 @@ static void codec_set_elapsed_callback(unsigned int value)
|
||||||
ab_position_report(value);
|
ab_position_report(value);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
latency = pcmbuf_get_latency();
|
unsigned int latency = pcmbuf_get_latency();
|
||||||
if (value < latency)
|
if (value < latency)
|
||||||
thistrack_id3->elapsed = 0;
|
thistrack_id3->elapsed = 0;
|
||||||
else if (value - latency > thistrack_id3->elapsed ||
|
else
|
||||||
value - latency < thistrack_id3->elapsed - 2)
|
|
||||||
{
|
{
|
||||||
thistrack_id3->elapsed = value - latency;
|
unsigned int elapsed = value - latency;
|
||||||
|
if (elapsed > thistrack_id3->elapsed ||
|
||||||
|
elapsed < thistrack_id3->elapsed - 2)
|
||||||
|
{
|
||||||
|
thistrack_id3->elapsed = elapsed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void codec_set_offset_callback(size_t value)
|
static void codec_set_offset_callback(size_t value)
|
||||||
{
|
{
|
||||||
unsigned int latency;
|
|
||||||
|
|
||||||
if (ci.seek_time)
|
if (ci.seek_time)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
|
unsigned int latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
|
||||||
if (value < latency)
|
if (value < latency)
|
||||||
thistrack_id3->offset = 0;
|
thistrack_id3->offset = 0;
|
||||||
else
|
else
|
||||||
thistrack_id3->offset = value - latency;
|
thistrack_id3->offset = value - latency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* helper function, not a callback */
|
||||||
static void codec_advance_buffer_counters(size_t amount)
|
static void codec_advance_buffer_counters(size_t amount)
|
||||||
{
|
{
|
||||||
bufadvance(get_audio_hid(), amount);
|
bufadvance(get_audio_hid(), amount);
|
||||||
|
@ -346,18 +305,6 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||||
return ptr;
|
return ptr;
|
||||||
} /* codec_request_buffer_callback */
|
} /* 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)
|
static void codec_advance_buffer_callback(size_t amount)
|
||||||
{
|
{
|
||||||
codec_advance_buffer_counters(amount);
|
codec_advance_buffer_counters(amount);
|
||||||
|
@ -370,28 +317,6 @@ static void codec_advance_buffer_loc_callback(void *ptr)
|
||||||
codec_advance_buffer_callback(amount);
|
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)
|
static bool codec_seek_buffer_callback(size_t newpos)
|
||||||
{
|
{
|
||||||
logf("codec_seek_buffer_callback");
|
logf("codec_seek_buffer_callback");
|
||||||
|
@ -406,25 +331,23 @@ static bool codec_seek_buffer_callback(size_t newpos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void codec_configure_callback(int setting, intptr_t value)
|
static void codec_seek_complete_callback(void)
|
||||||
{
|
{
|
||||||
switch (setting) {
|
logf("seek_complete");
|
||||||
default:
|
/* If seeking-while-playing, pcm_is_paused() is true.
|
||||||
if (!dsp_configure(ci.dsp, setting, value))
|
* If seeking-while-paused, audio_is_paused() is true.
|
||||||
{ logf("Illegal key:%d", setting); }
|
* A seamless seek skips this section. */
|
||||||
|
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 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)
|
static void codec_discard_codec_callback(void)
|
||||||
|
@ -437,6 +360,94 @@ static void codec_discard_codec_callback(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_configure_callback(int setting, intptr_t value)
|
||||||
|
{
|
||||||
|
if (!dsp_configure(ci.dsp, setting, value))
|
||||||
|
{ logf("Illegal key:%d", setting); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize codec API */
|
||||||
|
void codec_init_codec_api(void)
|
||||||
|
{
|
||||||
|
ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
|
||||||
|
CODEC_IDX_AUDIO);
|
||||||
|
ci.codec_get_buffer = codec_get_buffer;
|
||||||
|
ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
|
||||||
|
ci.set_elapsed = codec_set_elapsed_callback;
|
||||||
|
ci.read_filebuf = codec_filebuf_callback;
|
||||||
|
ci.request_buffer = codec_request_buffer_callback;
|
||||||
|
ci.advance_buffer = codec_advance_buffer_callback;
|
||||||
|
ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
|
||||||
|
ci.seek_buffer = codec_seek_buffer_callback;
|
||||||
|
ci.seek_complete = codec_seek_complete_callback;
|
||||||
|
ci.request_next_track = codec_request_next_track_callback;
|
||||||
|
ci.discard_codec = codec_discard_codec_callback;
|
||||||
|
ci.set_offset = codec_set_offset_callback;
|
||||||
|
ci.configure = codec_configure_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** pcmbuf track change callbacks */
|
||||||
|
|
||||||
|
/* 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_pcmbuf_track_changed_callback(void)
|
||||||
|
{
|
||||||
|
LOGFQUEUE("codec > pcmbuf/audio Q_AUDIO_TRACK_CHANGED");
|
||||||
|
pcmbuf_set_position_callback(NULL);
|
||||||
|
audio_post_track_change();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** track change functions */
|
||||||
|
|
||||||
static inline void codec_gapless_track_change(void)
|
static inline void codec_gapless_track_change(void)
|
||||||
{
|
{
|
||||||
/* callback keeps the progress bar moving while the pcmbuf empties */
|
/* callback keeps the progress bar moving while the pcmbuf empties */
|
||||||
|
@ -450,7 +461,8 @@ static inline void codec_crossfade_track_change(void)
|
||||||
/* Initiate automatic crossfade mode */
|
/* Initiate automatic crossfade mode */
|
||||||
pcmbuf_crossfade_init(false);
|
pcmbuf_crossfade_init(false);
|
||||||
/* Notify the wps that the track change starts now */
|
/* Notify the wps that the track change starts now */
|
||||||
codec_track_changed();
|
LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
|
||||||
|
queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void codec_track_skip_done(bool was_manual)
|
static void codec_track_skip_done(bool was_manual)
|
||||||
|
@ -531,35 +543,7 @@ static bool codec_load_next_track(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool codec_request_next_track_callback(void)
|
/** CODEC THREAD */
|
||||||
{
|
|
||||||
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)
|
static void codec_thread(void)
|
||||||
{
|
{
|
||||||
struct queue_event ev;
|
struct queue_event ev;
|
||||||
|
@ -734,44 +718,6 @@ static void codec_thread(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
void make_codec_thread(void)
|
||||||
{
|
{
|
||||||
codec_thread_id = create_thread(
|
codec_thread_id = create_thread(
|
||||||
|
|
|
@ -24,12 +24,11 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
int get_codec_base_type(int type);
|
||||||
const char *get_codec_filename(int cod_spec);
|
const char *get_codec_filename(int cod_spec);
|
||||||
void codec_thread_do_callback(void (*fn)(void),
|
void codec_thread_do_callback(void (*fn)(void),
|
||||||
unsigned int *codec_thread_id);
|
unsigned int *codec_thread_id);
|
||||||
void make_codec_thread(void);
|
|
||||||
int get_codec_base_type(int type);
|
|
||||||
void codec_init_codec_api(void);
|
void codec_init_codec_api(void);
|
||||||
bool codec_is_loaded(void);
|
void make_codec_thread(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -135,12 +135,13 @@ static void pcmbuf_under_watermark(bool under);
|
||||||
static bool pcmbuf_flush_fillpos(void);
|
static bool pcmbuf_flush_fillpos(void);
|
||||||
|
|
||||||
#define CALL_IF_EXISTS(function, args...) if (function) function(args)
|
#define CALL_IF_EXISTS(function, args...) if (function) function(args)
|
||||||
/* This function has 2 major logical parts (separated by brackets both for
|
/* This function has 3 major logical parts (separated by brackets both for
|
||||||
* readability and variable scoping). The first part performs the
|
* readability and variable scoping). The first part performs the
|
||||||
* operastions related to finishing off the last buffer we fed to the DMA.
|
* operations related to finishing off the last buffer we fed to the DMA.
|
||||||
* The second part performs the operations involved in sending a new buffer
|
* The second part detects the end of playlist condition when the pcm
|
||||||
* to the DMA. Finally the function checks the status of the buffer and
|
* buffer is empty except for uncommitted samples. Then they are committed.
|
||||||
* boosts if necessary */
|
* The third part performs the operations involved in sending a new buffer
|
||||||
|
* to the DMA. */
|
||||||
static void pcmbuf_callback(unsigned char** start, size_t* size) ICODE_ATTR;
|
static void pcmbuf_callback(unsigned char** start, size_t* size) ICODE_ATTR;
|
||||||
static void pcmbuf_callback(unsigned char** start, size_t* size)
|
static void pcmbuf_callback(unsigned char** start, size_t* size)
|
||||||
{
|
{
|
||||||
|
@ -164,6 +165,15 @@ static void pcmbuf_callback(unsigned char** start, size_t* size)
|
||||||
if (pcmbuf_current == crossfade_chunk)
|
if (pcmbuf_current == crossfade_chunk)
|
||||||
crossfade_chunk = pcmbuf_read;
|
crossfade_chunk = pcmbuf_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
/* Commit last samples at end of playlist */
|
||||||
|
if (audiobuffer_fillpos && !pcmbuf_read)
|
||||||
|
{
|
||||||
|
logf("pcmbuf callback: commit last samples");
|
||||||
|
pcmbuf_flush_fillpos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
/* Send the new buffer to the pcm */
|
/* Send the new buffer to the pcm */
|
||||||
|
@ -1109,11 +1119,3 @@ bool pcmbuf_is_crossfade_enabled(void)
|
||||||
|
|
||||||
return crossfade_enabled;
|
return crossfade_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** PLAY LAST REMAINING SAMPLES AT END OF PLAYBACK
|
|
||||||
* Commit any remaining samples in the PCM buffer for playback. */
|
|
||||||
void pcmbuf_play_remainder(void)
|
|
||||||
{
|
|
||||||
if (audiobuffer_fillpos)
|
|
||||||
pcmbuf_flush_fillpos();
|
|
||||||
}
|
|
||||||
|
|
|
@ -75,6 +75,5 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
|
||||||
|
|
||||||
int pcmbuf_used_descs(void);
|
int pcmbuf_used_descs(void);
|
||||||
int pcmbuf_descs(void);
|
int pcmbuf_descs(void);
|
||||||
void pcmbuf_play_remainder(void);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,70 +23,36 @@
|
||||||
/* TODO: Pause should be handled in here, rather than PCMBUF so that voice can
|
/* TODO: Pause should be handled in here, rather than PCMBUF so that voice can
|
||||||
* play whilst audio is paused */
|
* play whilst audio is paused */
|
||||||
|
|
||||||
//#include <stdio.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
//#include <ctype.h>
|
|
||||||
|
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include "codec_thread.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 "kernel.h"
|
||||||
//#include "tree.h"
|
|
||||||
//#include "debug.h"
|
|
||||||
//#include "sprintf.h"
|
|
||||||
//#include "settings.h"
|
|
||||||
#include "codecs.h"
|
#include "codecs.h"
|
||||||
//#include "audio.h"
|
|
||||||
#include "buffering.h"
|
#include "buffering.h"
|
||||||
//#include "appevents.h"
|
|
||||||
#include "voice_thread.h"
|
#include "voice_thread.h"
|
||||||
//#include "mp3_playback.h"
|
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
//#include "storage.h"
|
|
||||||
#include "ata.h"
|
#include "ata.h"
|
||||||
//#include "screens.h"
|
|
||||||
#include "playlist.h"
|
#include "playlist.h"
|
||||||
#include "pcmbuf.h"
|
#include "pcmbuf.h"
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
//#include "dsp.h"
|
|
||||||
//#include "abrepeat.h"
|
|
||||||
#include "cuesheet.h"
|
#include "cuesheet.h"
|
||||||
#ifdef HAVE_TAGCACHE
|
#ifdef HAVE_TAGCACHE
|
||||||
#include "tagcache.h"
|
#include "tagcache.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LCD_BITMAP
|
#ifdef HAVE_LCD_BITMAP
|
||||||
//#include "icons.h"
|
|
||||||
//#include "peakmeter.h"
|
|
||||||
//#include "action.h"
|
|
||||||
#ifdef HAVE_ALBUMART
|
#ifdef HAVE_ALBUMART
|
||||||
#include "albumart.h"
|
#include "albumart.h"
|
||||||
//#include "bmp.h"
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
//#include "lang.h"
|
|
||||||
//#include "misc.h"
|
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
#include "splash.h"
|
#include "splash.h"
|
||||||
#include "talk.h"
|
#include "talk.h"
|
||||||
//#include "ata_idle_notify.h"
|
|
||||||
|
|
||||||
#ifdef HAVE_RECORDING
|
#ifdef HAVE_RECORDING
|
||||||
//#include "recording.h"
|
|
||||||
#include "pcm_record.h"
|
#include "pcm_record.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef IPOD_ACCESSORY_PROTOCOL
|
|
||||||
//#include "iap.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define PLAYBACK_VOICE
|
#define PLAYBACK_VOICE
|
||||||
|
|
||||||
/* amount of guess-space to allow for codecs that must hunt and peck
|
/* amount of guess-space to allow for codecs that must hunt and peck
|
||||||
|
@ -139,8 +105,9 @@ static bool audio_thread_ready SHAREDBSS_ATTR = false;
|
||||||
/* TBD: Split out "audio" and "playback" (ie. calling) threads */
|
/* TBD: Split out "audio" and "playback" (ie. calling) threads */
|
||||||
|
|
||||||
/* Main state control */
|
/* Main state control */
|
||||||
static volatile bool playing SHAREDBSS_ATTR = false; /* Is audio playing? (A) */
|
static volatile bool playing SHAREDBSS_ATTR = false;/* Is audio playing? (A) */
|
||||||
static volatile bool paused SHAREDBSS_ATTR = false; /* Is audio paused? (A/C-) */
|
static volatile bool paused SHAREDBSS_ATTR = false; /* Is audio paused? (A/C-) */
|
||||||
|
extern volatile bool audio_codec_loaded; /* Codec loaded? (C/A-) */
|
||||||
|
|
||||||
/* Ring buffer where compressed audio and codecs are loaded */
|
/* Ring buffer where compressed audio and codecs are loaded */
|
||||||
static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */
|
static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */
|
||||||
|
@ -233,7 +200,7 @@ static size_t buffer_margin = 5; /* Buffer margin aka anti-skip buffer (A/C-) *
|
||||||
/* Event queues */
|
/* Event queues */
|
||||||
struct event_queue audio_queue SHAREDBSS_ATTR;
|
struct event_queue audio_queue SHAREDBSS_ATTR;
|
||||||
struct event_queue codec_queue SHAREDBSS_ATTR;
|
struct event_queue codec_queue SHAREDBSS_ATTR;
|
||||||
struct event_queue pcmbuf_queue SHAREDBSS_ATTR;
|
static struct event_queue pcmbuf_queue SHAREDBSS_ATTR;
|
||||||
|
|
||||||
extern struct codec_api ci;
|
extern struct codec_api ci;
|
||||||
extern unsigned int codec_thread_id;
|
extern unsigned int codec_thread_id;
|
||||||
|
@ -256,6 +223,13 @@ static void audio_stop_playback(void);
|
||||||
|
|
||||||
/**************************************/
|
/**************************************/
|
||||||
|
|
||||||
|
/* Post message from pcmbuf callback in the codec thread that
|
||||||
|
* the end of the previous track has just been played. */
|
||||||
|
void audio_post_track_change(void)
|
||||||
|
{
|
||||||
|
queue_post(&pcmbuf_queue, Q_AUDIO_TRACK_CHANGED, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Scan the pcmbuf queue and return true if a message pulled.
|
/* Scan the pcmbuf queue and return true if a message pulled.
|
||||||
* Permissible Context(s): Thread
|
* Permissible Context(s): Thread
|
||||||
*/
|
*/
|
||||||
|
@ -1077,7 +1051,7 @@ static bool audio_loadcodec(bool start_play)
|
||||||
if (id3 && prev_id3 &&
|
if (id3 && prev_id3 &&
|
||||||
get_codec_base_type(id3->codectype) ==
|
get_codec_base_type(id3->codectype) ==
|
||||||
get_codec_base_type(prev_id3->codectype)
|
get_codec_base_type(prev_id3->codectype)
|
||||||
&& codec_is_loaded())
|
&& audio_codec_loaded)
|
||||||
{
|
{
|
||||||
logf("Reusing prev. codec");
|
logf("Reusing prev. codec");
|
||||||
return true;
|
return true;
|
||||||
|
@ -1634,7 +1608,7 @@ static void audio_stop_codec_flush(void)
|
||||||
ci.stop_codec = true;
|
ci.stop_codec = true;
|
||||||
pcmbuf_pause(true);
|
pcmbuf_pause(true);
|
||||||
|
|
||||||
while (codec_is_loaded())
|
while (audio_codec_loaded)
|
||||||
yield();
|
yield();
|
||||||
|
|
||||||
/* If the audio codec is not loaded any more, and the audio is still
|
/* If the audio codec is not loaded any more, and the audio is still
|
||||||
|
@ -1652,13 +1626,6 @@ static void audio_stop_playback(void)
|
||||||
{
|
{
|
||||||
if (playing)
|
if (playing)
|
||||||
{
|
{
|
||||||
/* If still actively playing here, play out the last samples in the
|
|
||||||
* pcm buffer before stopping. If a manual stop has occurred, the
|
|
||||||
* paused flag is set, so don't continue playback.
|
|
||||||
*/
|
|
||||||
if (!paused)
|
|
||||||
pcmbuf_play_remainder();
|
|
||||||
|
|
||||||
/* If we were playing, save resume information */
|
/* If we were playing, save resume information */
|
||||||
struct mp3entry *id3 = NULL;
|
struct mp3entry *id3 = NULL;
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ enum
|
||||||
};
|
};
|
||||||
bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
|
bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
|
||||||
size_t audio_get_filebuflen(void);
|
size_t audio_get_filebuflen(void);
|
||||||
|
void audio_post_track_change(void);
|
||||||
int get_audio_hid(void);
|
int get_audio_hid(void);
|
||||||
int *get_codec_hid(void);
|
int *get_codec_hid(void);
|
||||||
void audio_set_prev_elapsed(unsigned long setting);
|
void audio_set_prev_elapsed(unsigned long setting);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue