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:
Brandon Low 2006-02-07 20:38:55 +00:00
parent 566ce5f951
commit 413da2a3d9
13 changed files with 667 additions and 531 deletions

View file

@ -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);

View file

@ -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();
}

View file

@ -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;

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -19,36 +19,48 @@
#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
#define pcmbuf_boost(state) do { } while(0)
#define pcmbuf_boost(state) do { } while(0)
#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

View file

@ -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)
return true;
while ((dest = pcmbuf_request_buffer(dsp_output_size(length),
&output_size)) == NULL) {
sleep(1);
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(est_output_size,
&output_size)) == NULL) {
sleep(1);
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);
}
#ifdef AB_REPEAT_ENABLE
ab_end_of_track_report();
#endif
pcmbuf_set_position_callback(pcmbuf_position_callback);
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);
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;

View file

@ -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,37 +400,7 @@ 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
};
int plugin_load(const char* plugin, void* parameter)

View file

@ -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);
@ -406,13 +445,12 @@ struct plugin_api {
int (*count_mp3_frames)(int fd, int startpos, int filesize,
void (*progressfunc)(int));
int (*create_xing_header)(int fd, long startpos, long filesize,
unsigned char *buf, unsigned long num_frames,
unsigned long rec_time, unsigned long header_template,
void (*progressfunc)(int), bool generate_toc);
unsigned char *buf, unsigned long num_frames,
unsigned long rec_time, unsigned long header_template,
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);
long max_offset, unsigned long last_header);
#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 */

View file

@ -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++;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
dma_start(start, 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,19 +171,32 @@ void pcm_play_pause(bool play)
if (!pcm_playing)
return ;
if(pcm_paused && play && next_size)
if(pcm_paused && play)
{
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);
if (BCR0 & 0xffffff)
{
logf("unpause");
/* Enable the FIFO and force one write to it */
IIS2CONFIG = IIS_DEFPARM(pcm_freq);
#ifdef HAVE_SPDIF_OUT
EBU1CONFIG = EBU_DEFPARM;
EBU1CONFIG = EBU_DEFPARM;
#endif
DCR0 |= DMA_EEXT | DMA_START;
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)
{
@ -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)