Rework PCM buffer
* Linked list instead of static array buffer pointers * Variable sized chunks * Improved mix handling * Reduction in duplicated code * Reduced IRAM usage w/o sacrificing performance * Converted to almost entirely unsigned math * Add pause function to reduce pcm_* exposure to playback. This WILL break playback on the iPod until linuxstb makes a followup commit. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8612 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
566ce5f951
commit
413da2a3d9
13 changed files with 667 additions and 531 deletions
|
@ -128,8 +128,8 @@ struct codec_api {
|
|||
void* (*get_codec_memory)(long *size);
|
||||
/* Insert PCM data into audio buffer for playback. Playback will start
|
||||
automatically. */
|
||||
bool (*pcmbuf_insert)(char *data, long length);
|
||||
bool (*pcmbuf_insert_split)(void *ch1, void *ch2, long length);
|
||||
bool (*pcmbuf_insert)(const char *data, size_t length);
|
||||
bool (*pcmbuf_insert_split)(const void *ch1, const void *ch2, size_t length);
|
||||
/* Set song position in WPS (value in ms). */
|
||||
void (*set_elapsed)(unsigned int value);
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ extern int filebuflen;
|
|||
extern int filebufused;
|
||||
extern int track_count;
|
||||
|
||||
static int ticks, boost_ticks;
|
||||
static unsigned int ticks, boost_ticks;
|
||||
|
||||
void dbg_audio_task(void)
|
||||
{
|
||||
|
@ -225,7 +225,8 @@ bool dbg_audio_thread(void)
|
|||
int button;
|
||||
int line;
|
||||
bool done = false;
|
||||
int bufsize = pcmbuf_get_bufsize();
|
||||
size_t bufsize = pcmbuf_get_bufsize();
|
||||
int pcmbufdescs = pcmbuf_descs();
|
||||
|
||||
ticks = boost_ticks = 0;
|
||||
|
||||
|
@ -239,6 +240,12 @@ bool dbg_audio_thread(void)
|
|||
button = button_get_w_tmo(HZ/5);
|
||||
switch(button)
|
||||
{
|
||||
case SETTINGS_NEXT:
|
||||
audio_next();
|
||||
break;
|
||||
case SETTINGS_PREV:
|
||||
audio_prev();
|
||||
break;
|
||||
case SETTINGS_CANCEL:
|
||||
done = true;
|
||||
break;
|
||||
|
@ -248,8 +255,8 @@ bool dbg_audio_thread(void)
|
|||
|
||||
lcd_clear_display();
|
||||
|
||||
snprintf(buf, sizeof(buf), "pcm: %d/%d",
|
||||
bufsize-(int)audiobuffer_free, bufsize);
|
||||
snprintf(buf, sizeof(buf), "pcm: %7ld/%7ld",
|
||||
bufsize-audiobuffer_free, bufsize);
|
||||
lcd_puts(0, line++, buf);
|
||||
|
||||
/* Playable space left */
|
||||
|
@ -257,7 +264,7 @@ bool dbg_audio_thread(void)
|
|||
bufsize-audiobuffer_free, HORIZONTAL);
|
||||
line++;
|
||||
|
||||
snprintf(buf, sizeof(buf), "codec: %d/%d", filebufused, filebuflen);
|
||||
snprintf(buf, sizeof(buf), "codec: %8d/%8d", filebufused, filebuflen);
|
||||
lcd_puts(0, line++, buf);
|
||||
|
||||
/* Playable space left */
|
||||
|
@ -265,17 +272,21 @@ bool dbg_audio_thread(void)
|
|||
filebufused, HORIZONTAL);
|
||||
line++;
|
||||
|
||||
snprintf(buf, sizeof(buf), "track count: %d", track_count);
|
||||
snprintf(buf, sizeof(buf), "track count: %2d", track_count);
|
||||
lcd_puts(0, line++, buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "cpu freq: %dMHz",
|
||||
snprintf(buf, sizeof(buf), "cpu freq: %3dMHz",
|
||||
(int)((FREQ + 500000) / 1000000));
|
||||
lcd_puts(0, line++, buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "boost ratio: %d%%",
|
||||
snprintf(buf, sizeof(buf), "boost ratio: %3d%%",
|
||||
boost_ticks * 100 / ticks);
|
||||
lcd_puts(0, line++, buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "pcmbufdesc: %2d/%2d",
|
||||
pcmbuf_used_descs(), pcmbufdescs);
|
||||
lcd_puts(0, line++, buf);
|
||||
|
||||
lcd_update();
|
||||
}
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ void sound_set_pitch(int permille)
|
|||
* consume. Note that for mono, dst[0] equals dst[1], as there is no point
|
||||
* in processing the same data twice.
|
||||
*/
|
||||
static int convert_to_internal(char* src[], int count, long* dst[])
|
||||
static int convert_to_internal(const char* src[], int count, long* dst[])
|
||||
{
|
||||
count = MIN(SAMPLE_BUF_SIZE / 2, count);
|
||||
|
||||
|
@ -773,7 +773,7 @@ static void write_samples(short* dst, long* src[], int count)
|
|||
* pointers, one for each audio channel. Returns number of bytes written to
|
||||
* dest.
|
||||
*/
|
||||
long dsp_process(char* dst, char* src[], long size)
|
||||
long dsp_process(char* dst, const char* src[], long size)
|
||||
{
|
||||
long* tmp[2];
|
||||
long written = 0;
|
||||
|
|
|
@ -47,7 +47,7 @@ enum {
|
|||
DSP_CROSSFEED
|
||||
};
|
||||
|
||||
long dsp_process(char *dest, char *src[], long size);
|
||||
long dsp_process(char *dest, const char *src[], long size);
|
||||
long dsp_input_size(long size);
|
||||
long dsp_output_size(long size);
|
||||
int dsp_stereo_mode(void);
|
||||
|
|
677
apps/pcmbuf.c
677
apps/pcmbuf.c
File diff suppressed because it is too large
Load diff
|
@ -19,19 +19,30 @@
|
|||
#ifndef PCMBUF_H
|
||||
#define PCMBUF_H
|
||||
|
||||
/* Guard buffer for crossfader when dsp is enabled. */
|
||||
#define PCMBUF_GUARD 32768
|
||||
#define PCMBUF_TARGET_CHUNK 32768 /* This is the target fill size of chunks
|
||||
on the pcm buffer */
|
||||
#define PCMBUF_MINAVG_CHUNK 24576 /* This is the minimum average size of
|
||||
chunks on the pcm buffer (or we run out
|
||||
of buffer descriptors, which is
|
||||
non-fatal) */
|
||||
#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than
|
||||
this to the DMA */
|
||||
#define PCMBUF_FADE_CHUNK 8192 /* This is the maximum size of one packet
|
||||
for mixing (crossfade or voice) */
|
||||
|
||||
void pcmbuf_init(long bufsize);
|
||||
long pcmbuf_get_bufsize(void);
|
||||
/* Returns true if the buffer needs to change size */
|
||||
bool pcmbuf_is_same_size(size_t bufsize);
|
||||
void pcmbuf_init(size_t bufsize);
|
||||
/* Size in bytes used by the pcmbuffer */
|
||||
size_t pcmbuf_get_bufsize(void);
|
||||
size_t get_pcmbuf_descsize(void);
|
||||
|
||||
void pcmbuf_pause(bool pause);
|
||||
void pcmbuf_play_stop(void);
|
||||
bool pcmbuf_is_crossfade_active(void);
|
||||
|
||||
/* These functions are for playing chained buffers of PCM data */
|
||||
bool pcmbuf_add_chunk(void *addr, int size, void (*callback)(void));
|
||||
|
||||
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
||||
#if defined(HAVE_ADJUSTABLE_CPU_FREQ) && !defined(SIMULATOR)
|
||||
void pcmbuf_boost(bool state);
|
||||
void pcmbuf_set_boost_mode(bool state);
|
||||
#else
|
||||
|
@ -39,16 +50,17 @@ void pcmbuf_set_boost_mode(bool state);
|
|||
#define pcmbuf_set_boost_mode(state) do { } while(0)
|
||||
#endif
|
||||
bool pcmbuf_is_lowdata(void);
|
||||
void pcmbuf_flush_audio(void);
|
||||
void pcmbuf_play_start(void);
|
||||
bool pcmbuf_crossfade_init(bool manual_skip);
|
||||
void pcmbuf_add_event(void (*event_handler)(void));
|
||||
void pcmbuf_set_position_callback(void (*callback)(int size));
|
||||
void pcmbuf_set_event_handler(void (*callback)(void));
|
||||
void pcmbuf_set_position_callback(void (*callback)(size_t size));
|
||||
unsigned int pcmbuf_get_latency(void);
|
||||
void pcmbuf_set_low_latency(bool state);
|
||||
bool pcmbuf_insert_buffer(char *buf, long length);
|
||||
void pcmbuf_flush_buffer(long length);
|
||||
void* pcmbuf_request_buffer(long length, long *realsize);
|
||||
bool pcmbuf_insert_buffer(const char *buf, size_t length);
|
||||
void pcmbuf_write_complete(size_t length);
|
||||
void pcmbuf_write_voice(size_t length);
|
||||
void* pcmbuf_request_buffer(size_t length, size_t *realsize);
|
||||
void* pcmbuf_request_voice_buffer(size_t length, size_t *realsize, bool mix);
|
||||
bool pcmbuf_is_crossfade_enabled(void);
|
||||
void pcmbuf_crossfade_enable(bool on_off);
|
||||
|
||||
|
@ -56,6 +68,9 @@ int pcmbuf_usage(void);
|
|||
int pcmbuf_mix_usage(void);
|
||||
void pcmbuf_beep(int frequency, int duration, int amplitude);
|
||||
void pcmbuf_reset_mixpos(void);
|
||||
void pcmbuf_mix(char *buf, long length);
|
||||
void pcmbuf_mix(char *buf, size_t length);
|
||||
|
||||
int pcmbuf_used_descs(void);
|
||||
int pcmbuf_descs(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -216,6 +216,13 @@ static bool v1first = false;
|
|||
static void mp3_set_elapsed(struct mp3entry* id3);
|
||||
int mp3_get_file_pos(void);
|
||||
|
||||
#ifdef TIME_CODEC
|
||||
bool is_filling(void)
|
||||
{
|
||||
return filling;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void do_swap(int idx_old, int idx_new)
|
||||
{
|
||||
#ifndef SIMULATOR
|
||||
|
@ -287,13 +294,13 @@ static void voice_boost_cpu(bool state)
|
|||
#define voice_boost_cpu(state) do { } while(0)
|
||||
#endif
|
||||
|
||||
bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2,
|
||||
long length)
|
||||
bool codec_pcmbuf_insert_split_callback(const void *ch1, const void *ch2,
|
||||
size_t length)
|
||||
{
|
||||
char* src[2];
|
||||
const char* src[2];
|
||||
char *dest;
|
||||
long input_size;
|
||||
long output_size;
|
||||
size_t output_size;
|
||||
|
||||
src[0] = ch1;
|
||||
src[1] = ch2;
|
||||
|
@ -311,47 +318,50 @@ bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2,
|
|||
}
|
||||
|
||||
while (length > 0) {
|
||||
long est_output_size = dsp_output_size(length);
|
||||
/* This will prevent old audio from playing when skipping tracks. */
|
||||
if ((ci.reload_codec || ci.stop_codec) &&
|
||||
current_codec != CODEC_IDX_VOICE)
|
||||
if (current_codec == CODEC_IDX_VOICE) {
|
||||
while ((dest = pcmbuf_request_voice_buffer(est_output_size,
|
||||
&output_size, audio_codec_loaded)) == NULL)
|
||||
sleep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ci.reload_codec || ci.stop_codec)
|
||||
return true;
|
||||
|
||||
while ((dest = pcmbuf_request_buffer(dsp_output_size(length),
|
||||
while ((dest = pcmbuf_request_buffer(est_output_size,
|
||||
&output_size)) == NULL) {
|
||||
sleep(1);
|
||||
if ((ci.reload_codec || ci.stop_codec) &&
|
||||
current_codec != CODEC_IDX_VOICE)
|
||||
if (ci.reload_codec || ci.stop_codec)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the real input_size for output_size bytes, guarding
|
||||
* against resampling buffer overflows. */
|
||||
input_size = dsp_input_size(output_size);
|
||||
if (input_size > length) {
|
||||
|
||||
if (input_size <= 0) {
|
||||
DEBUGF("Warning: dsp_input_size(%ld=dsp_output_size(%ld))=%ld <= 0\n",
|
||||
output_size, length, input_size);
|
||||
/* this cannot happen */
|
||||
break;
|
||||
}
|
||||
|
||||
if ((size_t)input_size > length) {
|
||||
DEBUGF("Error: dsp_input_size(%ld=dsp_output_size(%ld))=%ld > %ld\n",
|
||||
output_size, length, input_size, length);
|
||||
input_size = length;
|
||||
}
|
||||
|
||||
if (input_size <= 0) {
|
||||
pcmbuf_flush_buffer(0);
|
||||
DEBUGF("Warning: dsp_input_size(%ld=dsp_output_size(%ld))=%ld <= 0\n",
|
||||
output_size, length, input_size);
|
||||
/* should we really continue, or should we break?
|
||||
* We should probably continue because calling
|
||||
* pcmbuf_flush_buffer(0) will wrap the buffer if it was fully
|
||||
* filled and so next call to pcmbuf_request_buffer should give
|
||||
* the requested output_size. */
|
||||
continue;
|
||||
}
|
||||
|
||||
output_size = dsp_process(dest, src, input_size);
|
||||
|
||||
/* Hotswap between audio and voice codecs as necessary. */
|
||||
switch (current_codec)
|
||||
{
|
||||
case CODEC_IDX_AUDIO:
|
||||
pcmbuf_flush_buffer(output_size);
|
||||
pcmbuf_write_complete(output_size);
|
||||
if (voice_is_playing && pcmbuf_usage() > 30
|
||||
&& pcmbuf_mix_usage() < 20)
|
||||
{
|
||||
|
@ -368,7 +378,7 @@ bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2,
|
|||
|| pcmbuf_mix_usage() > 70)
|
||||
swap_codec();
|
||||
} else {
|
||||
pcmbuf_flush_buffer(output_size);
|
||||
pcmbuf_write_complete(output_size);
|
||||
}
|
||||
break ;
|
||||
}
|
||||
|
@ -379,7 +389,7 @@ bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool codec_pcmbuf_insert_callback(char *buf, long length)
|
||||
bool codec_pcmbuf_insert_callback(const char *buf, size_t length)
|
||||
{
|
||||
/* TODO: The audiobuffer API should probably be updated, and be based on
|
||||
* pcmbuf_insert_split().
|
||||
|
@ -405,9 +415,10 @@ void* get_codec_memory_callback(long *size)
|
|||
return &audiobuf[0];
|
||||
}
|
||||
|
||||
static void pcmbuf_position_callback(int size) ICODE_ATTR;
|
||||
static void pcmbuf_position_callback(int size) {
|
||||
unsigned int time = size * 1000 / 4 / 44100 + prev_ti->id3.elapsed;
|
||||
static void pcmbuf_position_callback(size_t size) ICODE_ATTR;
|
||||
static void pcmbuf_position_callback(size_t size) {
|
||||
unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
|
||||
prev_ti->id3.elapsed;
|
||||
if (time >= prev_ti->id3.length) {
|
||||
pcmbuf_set_position_callback(NULL);
|
||||
prev_ti->id3.elapsed = prev_ti->id3.length;
|
||||
|
@ -785,6 +796,13 @@ static void codec_track_changed(void)
|
|||
queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
|
||||
}
|
||||
|
||||
static void pcmbuf_track_changed_callback(void)
|
||||
{
|
||||
track_changed = true;
|
||||
pcmbuf_set_position_callback(NULL);
|
||||
queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
|
||||
}
|
||||
|
||||
/* Give codecs or file buffering the right amount of processing time
|
||||
to prevent pcm audio buffer from going empty. */
|
||||
static void yield_codecs(void)
|
||||
|
@ -1529,7 +1547,7 @@ static void audio_update_trackinfo(void)
|
|||
/* Gapless playback. */
|
||||
else
|
||||
{
|
||||
pcmbuf_add_event(codec_track_changed);
|
||||
pcmbuf_set_event_handler(pcmbuf_track_changed_callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1675,15 +1693,17 @@ bool codec_request_next_track_callback(void)
|
|||
the core has been requested the codec to be terminated. */
|
||||
return !ci_voice.stop_codec && queue_empty(&voice_codec_queue);
|
||||
}
|
||||
|
||||
if (ci.stop_codec || !playing)
|
||||
return false;
|
||||
|
||||
#ifdef AB_REPEAT_ENABLE
|
||||
ab_end_of_track_report();
|
||||
#endif
|
||||
|
||||
if (!new_track)
|
||||
pcmbuf_set_position_callback(pcmbuf_position_callback);
|
||||
|
||||
if (ci.stop_codec || !playing)
|
||||
return false;
|
||||
|
||||
logf("Request new track");
|
||||
|
||||
/* Advance to next track. */
|
||||
|
@ -1856,15 +1876,13 @@ void audio_thread(void)
|
|||
|
||||
case Q_AUDIO_PAUSE:
|
||||
logf("audio_pause");
|
||||
pcm_mute(true);
|
||||
pcm_play_pause(false);
|
||||
pcmbuf_pause(true);
|
||||
paused = true;
|
||||
break ;
|
||||
|
||||
case Q_AUDIO_RESUME:
|
||||
logf("audio_resume");
|
||||
pcm_play_pause(true);
|
||||
pcm_mute(false);
|
||||
pcmbuf_pause(false);
|
||||
paused = false;
|
||||
break ;
|
||||
|
||||
|
@ -2022,8 +2040,9 @@ void codec_thread(void)
|
|||
static void reset_buffer(void)
|
||||
{
|
||||
filebuf = (char *)&audiobuf[MALLOC_BUFSIZE];
|
||||
filebuflen = audiobufend - audiobuf - pcmbuf_get_bufsize()
|
||||
- PCMBUF_GUARD - MALLOC_BUFSIZE - GUARD_BUFSIZE;
|
||||
filebuflen = audiobufend - audiobuf - MALLOC_BUFSIZE - GUARD_BUFSIZE -
|
||||
(pcmbuf_get_bufsize() + get_pcmbuf_descsize() + PCMBUF_FADE_CHUNK);
|
||||
|
||||
|
||||
if (talk_get_bufsize() && voice_codec_loaded)
|
||||
{
|
||||
|
@ -2422,7 +2441,7 @@ void audio_set_buffer_margin(int setting)
|
|||
/* Set crossfade & PCM buffer length. */
|
||||
void audio_set_crossfade(int enable)
|
||||
{
|
||||
long size;
|
||||
size_t size;
|
||||
bool was_playing = playing;
|
||||
int offset = 0;
|
||||
int seconds = 1;
|
||||
|
|
|
@ -123,6 +123,10 @@ static const struct plugin_api rockbox_api = {
|
|||
lcd_get_background,
|
||||
lcd_bitmap_part,
|
||||
lcd_bitmap,
|
||||
#endif
|
||||
#if LCD_DEPTH == 16
|
||||
lcd_bitmap_transparent_part,
|
||||
lcd_bitmap_transparent,
|
||||
#endif
|
||||
lcd_putsxy,
|
||||
lcd_puts_style,
|
||||
|
@ -198,6 +202,7 @@ static const struct plugin_api rockbox_api = {
|
|||
settings_parseline,
|
||||
#ifndef SIMULATOR
|
||||
ata_sleep,
|
||||
ata_disk_is_active,
|
||||
#endif
|
||||
|
||||
/* dir */
|
||||
|
@ -225,6 +230,17 @@ static const struct plugin_api rockbox_api = {
|
|||
timer_unregister,
|
||||
timer_set_period,
|
||||
#endif
|
||||
queue_init,
|
||||
queue_delete,
|
||||
queue_post,
|
||||
queue_wait_w_tmo,
|
||||
usb_acknowledge,
|
||||
#ifdef RB_PROFILE
|
||||
profile_thread,
|
||||
profstop,
|
||||
profile_func_enter,
|
||||
profile_func_exit,
|
||||
#endif
|
||||
|
||||
/* strings and memory */
|
||||
snprintf,
|
||||
|
@ -238,6 +254,7 @@ static const struct plugin_api rockbox_api = {
|
|||
strncasecmp,
|
||||
memset,
|
||||
memcpy,
|
||||
memmove,
|
||||
_ctype_,
|
||||
atoi,
|
||||
strchr,
|
||||
|
@ -332,6 +349,23 @@ static const struct plugin_api rockbox_api = {
|
|||
menu_insert,
|
||||
menu_set_cursor,
|
||||
|
||||
/* power */
|
||||
battery_level,
|
||||
battery_level_safe,
|
||||
battery_time,
|
||||
#ifndef SIMULATOR
|
||||
battery_voltage,
|
||||
#endif
|
||||
#ifdef HAVE_CHARGING
|
||||
charger_inserted,
|
||||
# ifdef HAVE_CHARGE_STATE
|
||||
charging_state,
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_USB_POWER
|
||||
usb_powered,
|
||||
#endif
|
||||
|
||||
/* misc */
|
||||
srand,
|
||||
rand,
|
||||
|
@ -353,8 +387,6 @@ static const struct plugin_api rockbox_api = {
|
|||
count_mp3_frames,
|
||||
create_xing_header,
|
||||
find_next_frame,
|
||||
battery_level,
|
||||
battery_level_safe,
|
||||
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
|
||||
peak_meter_scale_value,
|
||||
peak_meter_set_use_dbfs,
|
||||
|
@ -368,36 +400,6 @@ static const struct plugin_api rockbox_api = {
|
|||
|
||||
/* new stuff at the end, sort into place next time
|
||||
the API gets incompatible */
|
||||
#ifdef RB_PROFILE
|
||||
profile_thread,
|
||||
profstop,
|
||||
profile_func_enter,
|
||||
profile_func_exit,
|
||||
#endif
|
||||
battery_time,
|
||||
#ifndef SIMULATOR
|
||||
ata_disk_is_active,
|
||||
battery_voltage,
|
||||
#endif
|
||||
queue_init,
|
||||
queue_delete,
|
||||
queue_post,
|
||||
queue_wait_w_tmo,
|
||||
usb_acknowledge,
|
||||
#if LCD_DEPTH == 16
|
||||
lcd_bitmap_transparent_part,
|
||||
lcd_bitmap_transparent,
|
||||
#endif
|
||||
memmove,
|
||||
#ifdef HAVE_CHARGING
|
||||
charger_inserted,
|
||||
# ifdef HAVE_CHARGE_STATE
|
||||
charging_state,
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_USB_POWER
|
||||
usb_powered,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -97,12 +97,12 @@
|
|||
#define PLUGIN_MAGIC 0x526F634B /* RocK */
|
||||
|
||||
/* increase this every time the api struct changes */
|
||||
#define PLUGIN_API_VERSION 6
|
||||
#define PLUGIN_API_VERSION 7
|
||||
|
||||
/* update this to latest version if a change to the api struct breaks
|
||||
backwards compatibility (and please take the opportunity to sort in any
|
||||
new function which are "waiting" at the end of the function table) */
|
||||
#define PLUGIN_MIN_API_VERSION 2
|
||||
#define PLUGIN_MIN_API_VERSION 7
|
||||
|
||||
/* plugin return codes */
|
||||
enum plugin_status {
|
||||
|
@ -161,6 +161,13 @@ struct plugin_api {
|
|||
int stride, int x, int y, int width, int height);
|
||||
void (*lcd_bitmap)(const fb_data *src, int x, int y, int width,
|
||||
int height);
|
||||
#endif
|
||||
#if LCD_DEPTH == 16
|
||||
void (*lcd_bitmap_transparent_part)(const fb_data *src,
|
||||
int src_x, int src_y, int stride,
|
||||
int x, int y, int width, int height);
|
||||
void (*lcd_bitmap_transparent)(const fb_data *src, int x, int y,
|
||||
int width, int height);
|
||||
#endif
|
||||
void (*lcd_putsxy)(int x, int y, const unsigned char *string);
|
||||
void (*lcd_puts_style)(int x, int y, const unsigned char *str, int style);
|
||||
|
@ -246,6 +253,7 @@ struct plugin_api {
|
|||
bool (*settings_parseline)(char* line, char** name, char** value);
|
||||
#ifndef SIMULATOR
|
||||
void (*ata_sleep)(void);
|
||||
bool (*ata_disk_is_active)(void);
|
||||
#endif
|
||||
|
||||
/* dir */
|
||||
|
@ -275,6 +283,18 @@ struct plugin_api {
|
|||
void (*timer_unregister)(void);
|
||||
bool (*timer_set_period)(long count);
|
||||
#endif
|
||||
void (*queue_init)(struct event_queue *q);
|
||||
void (*queue_delete)(struct event_queue *q);
|
||||
void (*queue_post)(struct event_queue *q, long id, void *data);
|
||||
void (*queue_wait_w_tmo)(struct event_queue *q, struct event *ev,
|
||||
int ticks);
|
||||
void (*usb_acknowledge)(long id);
|
||||
#ifdef RB_PROFILE
|
||||
void (*profile_thread)(void);
|
||||
void (*profstop)(void);
|
||||
void (*profile_func_enter)(void *this_fn, void *call_site);
|
||||
void (*profile_func_exit)(void *this_fn, void *call_site);
|
||||
#endif
|
||||
|
||||
/* strings and memory */
|
||||
int (*snprintf)(char *buf, size_t size, const char *fmt, ...);
|
||||
|
@ -288,6 +308,7 @@ struct plugin_api {
|
|||
int (*strncasecmp)(const char *s1, const char *s2, size_t n);
|
||||
void* (*memset)(void *dst, int c, size_t length);
|
||||
void* (*memcpy)(void *out, const void *in, size_t n);
|
||||
void* (*memmove)(void *out, const void *in, size_t n);
|
||||
const unsigned char *_ctype_;
|
||||
int (*atoi)(const char *str);
|
||||
char *(*strchr)(const char *s, int c);
|
||||
|
@ -315,7 +336,8 @@ struct plugin_api {
|
|||
void (*bitswap)(unsigned char *data, int length);
|
||||
#endif
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
void (*pcm_play_data)(void (*get_more)(unsigned char** start, long*size));
|
||||
void (*pcm_play_data)(void (*get_more)(unsigned char** start, size_t*size),
|
||||
unsigned char* start, size_t size);
|
||||
void (*pcm_play_stop)(void);
|
||||
void (*pcm_set_frequency)(unsigned int frequency);
|
||||
bool (*pcm_is_playing)(void);
|
||||
|
@ -384,6 +406,23 @@ struct plugin_api {
|
|||
void (*menu_insert)(int menu, int position, char *desc, bool (*function) (void));
|
||||
void (*menu_set_cursor)(int menu, int position);
|
||||
|
||||
/* power */
|
||||
int (*battery_level)(void);
|
||||
bool (*battery_level_safe)(void);
|
||||
int (*battery_time)(void);
|
||||
#ifndef SIMULATOR
|
||||
unsigned int (*battery_voltage)(void);
|
||||
#endif
|
||||
#ifdef HAVE_CHARGING
|
||||
bool (*charger_inserted)(void);
|
||||
# ifdef HAVE_CHARGE_STATE
|
||||
bool (*charging_state)(void);
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_USB_POWER
|
||||
bool (*usb_powered)(void);
|
||||
#endif
|
||||
|
||||
/* misc */
|
||||
void (*srand)(unsigned int seed);
|
||||
int (*rand)(void);
|
||||
|
@ -411,8 +450,7 @@ struct plugin_api {
|
|||
void (*progressfunc)(int), bool generate_toc);
|
||||
unsigned long (*find_next_frame)(int fd, long *offset,
|
||||
long max_offset, unsigned long last_header);
|
||||
int (*battery_level)(void);
|
||||
bool (*battery_level_safe)(void);
|
||||
|
||||
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
|
||||
unsigned short (*peak_meter_scale_value)(unsigned short val,
|
||||
int meterwidth);
|
||||
|
@ -429,40 +467,6 @@ struct plugin_api {
|
|||
/* new stuff at the end, sort into place next time
|
||||
the API gets incompatible */
|
||||
|
||||
#ifdef RB_PROFILE
|
||||
void (*profile_thread)(void);
|
||||
void (*profstop)(void);
|
||||
void (*profile_func_enter)(void *this_fn, void *call_site);
|
||||
void (*profile_func_exit)(void *this_fn, void *call_site);
|
||||
#endif
|
||||
int (*battery_time)(void);
|
||||
#ifndef SIMULATOR
|
||||
bool (*ata_disk_is_active)(void);
|
||||
unsigned int (*battery_voltage)(void);
|
||||
#endif
|
||||
void (*queue_init)(struct event_queue *q);
|
||||
void (*queue_delete)(struct event_queue *q);
|
||||
void (*queue_post)(struct event_queue *q, long id, void *data);
|
||||
void (*queue_wait_w_tmo)(struct event_queue *q, struct event *ev, int ticks);
|
||||
void (*usb_acknowledge)(long id);
|
||||
#if LCD_DEPTH == 16
|
||||
void (*lcd_bitmap_transparent_part)(const fb_data *src, int src_x, int src_y,
|
||||
int stride, int x, int y, int width, int height);
|
||||
void (*lcd_bitmap_transparent)(const fb_data *src, int x, int y,
|
||||
int width, int height);
|
||||
#endif
|
||||
void* (*memmove)(void *out, const void *in, size_t n);
|
||||
|
||||
#ifdef HAVE_CHARGING
|
||||
bool (*charger_inserted)(void);
|
||||
# ifdef HAVE_CHARGE_STATE
|
||||
bool (*charging_state)(void);
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_USB_POWER
|
||||
bool (*usb_powered)(void);
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
/* plugin header */
|
||||
|
|
|
@ -736,18 +736,8 @@ void prepare_tock(void)
|
|||
}
|
||||
}
|
||||
|
||||
void callback_pcm(unsigned char** start, long* size)
|
||||
{
|
||||
if(sound_active) {
|
||||
*start = (unsigned char *)sndbuf;
|
||||
*size = sizeof(sndbuf);
|
||||
sound_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void play_tock(void) {
|
||||
sound_active = true;
|
||||
rb->pcm_play_data(callback_pcm);
|
||||
rb->pcm_play_data(NULL,(unsigned char *)sndbuf,sizeof(sndbuf));
|
||||
tock++;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ static unsigned short *gmbuf;
|
|||
|
||||
static bool newly_started;
|
||||
|
||||
void get_more(unsigned char** start, long* size)
|
||||
void get_more(unsigned char** start, size_t* size)
|
||||
{
|
||||
#ifdef ONEBUF
|
||||
doneplay=1;
|
||||
|
@ -108,7 +108,7 @@ int pcm_submit(void)
|
|||
|
||||
if(newly_started)
|
||||
{
|
||||
rb->pcm_play_data(&get_more);
|
||||
rb->pcm_play_data(&get_more,NULL,0);
|
||||
newly_started = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,11 @@ void pcm_init(void);
|
|||
void pcm_set_frequency(unsigned int frequency);
|
||||
|
||||
/* This is for playing "raw" PCM data */
|
||||
void pcm_play_data(void (*get_more)(unsigned char** start, long* size));
|
||||
void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size),
|
||||
unsigned char* start, size_t size);
|
||||
|
||||
void pcm_calculate_peaks(int *left, int *right);
|
||||
long pcm_get_bytes_waiting(void);
|
||||
size_t pcm_get_bytes_waiting(void);
|
||||
|
||||
void pcm_play_stop(void);
|
||||
void pcm_mute(bool mute);
|
||||
|
|
|
@ -61,11 +61,11 @@ static bool pcm_playing;
|
|||
static bool pcm_paused;
|
||||
static int pcm_freq = 0x6; /* 44.1 is default */
|
||||
|
||||
static unsigned char *next_start IDATA_ATTR;
|
||||
static long next_size IDATA_ATTR;
|
||||
size_t next_size IBSS_ATTR;
|
||||
unsigned char *next_start IBSS_ATTR;
|
||||
|
||||
/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
|
||||
static void dma_start(const void *addr, long size)
|
||||
static void dma_start(const void *addr, size_t size)
|
||||
{
|
||||
pcm_playing = true;
|
||||
|
||||
|
@ -104,8 +104,6 @@ static void dma_stop(void)
|
|||
EBU1CONFIG = IIS_RESET | EBU_DEFPARM;
|
||||
#endif
|
||||
|
||||
next_start = NULL;
|
||||
next_size = 0;
|
||||
pcm_paused = false;
|
||||
}
|
||||
|
||||
|
@ -131,23 +129,27 @@ void pcm_set_frequency(unsigned int frequency)
|
|||
}
|
||||
|
||||
/* the registered callback function to ask for more mp3 data */
|
||||
static void (*callback_for_more)(unsigned char**, long*) IDATA_ATTR = NULL;
|
||||
static void (*callback_for_more)(unsigned char**, size_t*) IDATA_ATTR = NULL;
|
||||
|
||||
void pcm_play_data(void (*get_more)(unsigned char** start, long* size))
|
||||
void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size),
|
||||
unsigned char* start, size_t size)
|
||||
{
|
||||
unsigned char *start;
|
||||
long size;
|
||||
|
||||
callback_for_more = get_more;
|
||||
|
||||
get_more((unsigned char **)&start, (long *)&size);
|
||||
get_more(&next_start, &next_size);
|
||||
if (!(start && size))
|
||||
{
|
||||
if (get_more)
|
||||
get_more(&start, &size);
|
||||
else
|
||||
return;
|
||||
}
|
||||
if (start && size)
|
||||
dma_start(start, size);
|
||||
}
|
||||
|
||||
long pcm_get_bytes_waiting(void)
|
||||
size_t pcm_get_bytes_waiting(void)
|
||||
{
|
||||
return next_size + (BCR0 & 0xffffff);
|
||||
return (BCR0 & 0xffffff);
|
||||
}
|
||||
|
||||
void pcm_mute(bool mute)
|
||||
|
@ -169,13 +171,11 @@ void pcm_play_pause(bool play)
|
|||
if (!pcm_playing)
|
||||
return ;
|
||||
|
||||
if(pcm_paused && play && next_size)
|
||||
if(pcm_paused && play)
|
||||
{
|
||||
if (BCR0 & 0xffffff)
|
||||
{
|
||||
logf("unpause");
|
||||
/* Reset chunk size so dma has enough data to fill the fifo. */
|
||||
/* This shouldn't be needed anymore. */
|
||||
//SAR0 = (unsigned long)next_start;
|
||||
//BCR0 = next_size;
|
||||
/* Enable the FIFO and force one write to it */
|
||||
IIS2CONFIG = IIS_DEFPARM(pcm_freq);
|
||||
#ifdef HAVE_SPDIF_OUT
|
||||
|
@ -183,6 +183,21 @@ void pcm_play_pause(bool play)
|
|||
#endif
|
||||
DCR0 |= DMA_EEXT | DMA_START;
|
||||
}
|
||||
else
|
||||
{
|
||||
logf("unpause, no data waiting");
|
||||
void (*get_more)(unsigned char**, size_t*) = callback_for_more;
|
||||
if (get_more)
|
||||
get_more(&next_start, &next_size);
|
||||
if (next_start && next_size)
|
||||
dma_start(next_start, next_size);
|
||||
else
|
||||
{
|
||||
dma_stop();
|
||||
logf("unpause attempted, no data");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!pcm_paused && !play)
|
||||
{
|
||||
logf("pause");
|
||||
|
@ -224,13 +239,22 @@ void DMA0(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
void (*get_more)(unsigned char**, size_t*) = callback_for_more;
|
||||
if (get_more)
|
||||
get_more(&next_start, &next_size);
|
||||
else
|
||||
{
|
||||
next_size = 0;
|
||||
next_start = NULL;
|
||||
}
|
||||
}
|
||||
if(next_size)
|
||||
{
|
||||
SAR0 = (unsigned long)next_start; /* Source address */
|
||||
BCR0 = next_size; /* Bytes to transfer */
|
||||
DCR0 |= DMA_EEXT;
|
||||
if (callback_for_more)
|
||||
callback_for_more(&next_start, &next_size);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -301,9 +325,9 @@ static bool pcm_paused;
|
|||
static int pcm_freq = 0x6; /* 44.1 is default */
|
||||
|
||||
/* the registered callback function to ask for more mp3 data */
|
||||
static void (*callback_for_more)(unsigned char**, long*) = NULL;
|
||||
static void (*callback_for_more)(unsigned char**, size_t*) = NULL;
|
||||
static unsigned short *p IBSS_ATTR;
|
||||
static long size IBSS_ATTR;
|
||||
static size_t size IBSS_ATTR;
|
||||
|
||||
/* Stops the DMA transfer and interrupt */
|
||||
static void dma_stop(void)
|
||||
|
@ -353,7 +377,7 @@ void fiq(void)
|
|||
IISCONFIG &= ~0x2;
|
||||
|
||||
if ((size==0) && (callback_for_more)) {
|
||||
callback_for_more((unsigned char **)&p, (long *)&size);
|
||||
callback_for_more((unsigned char **)&p, &size);
|
||||
}
|
||||
|
||||
while (size > 0) {
|
||||
|
@ -368,20 +392,22 @@ void fiq(void)
|
|||
size-=4;
|
||||
|
||||
if ((size==0) && (callback_for_more)) {
|
||||
callback_for_more((unsigned char **)&p, (long *)&size);
|
||||
callback_for_more((unsigned char **)&p, &size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pcm_play_data(void (*get_more)(unsigned char** start, long* size))
|
||||
void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size),
|
||||
unsigned char* _p, size_t _size)
|
||||
{
|
||||
int free_count;
|
||||
size_t free_count;
|
||||
|
||||
callback_for_more = get_more;
|
||||
|
||||
if (size > 0) { return; }
|
||||
|
||||
get_more((unsigned char **)&p, (long *)&size);
|
||||
p = (unsigned short *)_p;
|
||||
size = _size;
|
||||
|
||||
/* setup I2S interrupt for FIQ */
|
||||
outl(inl(0x6000402c) | I2S_MASK, 0x6000402c);
|
||||
|
@ -406,7 +432,7 @@ void pcm_play_data(void (*get_more)(unsigned char** start, long* size))
|
|||
size-=4;
|
||||
|
||||
if ((size==0) && (get_more)) {
|
||||
get_more((unsigned char **)&p, (long *)&size);
|
||||
get_more((unsigned char **)&p, &size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -448,7 +474,7 @@ bool pcm_is_playing(void)
|
|||
return pcm_playing;
|
||||
}
|
||||
|
||||
long pcm_get_bytes_waiting(void)
|
||||
size_t pcm_get_bytes_waiting(void)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
@ -608,9 +634,12 @@ void pcm_set_frequency(unsigned int frequency)
|
|||
(void)frequency;
|
||||
}
|
||||
|
||||
void pcm_play_data(void (*get_more)(unsigned char** start, long* size))
|
||||
void pcm_play_data(void (*get_more)(unsigned char** start, long* size),
|
||||
unsigned char* start, long size)
|
||||
{
|
||||
(void)get_more;
|
||||
(void)start;
|
||||
(void)size;
|
||||
}
|
||||
|
||||
void pcm_play_stop(void)
|
||||
|
|
Loading…
Reference in a new issue