summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/codecs/mpa.c56
-rw-r--r--apps/dsp.c20
-rw-r--r--apps/playback.c96
-rw-r--r--apps/playback.h5
4 files changed, 139 insertions, 38 deletions
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c
index f2fd8a6e2b..a52dc12a40 100644
--- a/apps/codecs/mpa.c
+++ b/apps/codecs/mpa.c
@@ -57,15 +57,6 @@ extern char iramstart[];
extern char iramend[];
#endif
-/*
-long resample(long *in, long *out, int num, struct resampler *s)
-{
- if (s->delta >= (1 << 16))
- return downsample(in, out, num, s);
- else
- return upsample(in, out, num, s);
-}
-*/
/* this is the codec entry point */
enum codec_status codec_start(struct codec_api* api)
{
@@ -79,9 +70,10 @@ enum codec_status codec_start(struct codec_api* api)
unsigned int samplesdone;
bool first_frame;
int stop_skip, start_skip;
- // struct resampler lr = { 0, 0, 0 }, rr = { 0, 0, 0 };
+ int current_stereo_mode = -1;
+ int frequency_divider;
+
/* Generic codec inititialisation */
-
TEST_CODEC_API(api);
#ifdef USE_IRAM
@@ -102,7 +94,6 @@ enum codec_status codec_start(struct codec_api* api)
ci->configure(DSP_SET_CLIP_MAX, (int *)(MAD_F_ONE - 1));
ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(MAD_F_FRACBITS));
ci->configure(DSP_DITHER, (bool *)false);
- ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED);
ci->configure(CODEC_DSP_ENABLE, (bool *)true);
ci->memset(&Stream, 0, sizeof(struct mad_stream));
@@ -128,6 +119,7 @@ enum codec_status codec_start(struct codec_api* api)
first_frame = false;
file_end = 0;
OutputPtr = OutputBuffer;
+ frequency_divider = ci->id3->frequency / 100;
while (!*ci->taginfo_ready)
ci->yield();
@@ -154,16 +146,16 @@ enum codec_status codec_start(struct codec_api* api)
/* TODO: 1152 is the frame size in samples for MPEG1 layer 2 and layer 3,
it's probably not correct at all for MPEG2 and layer 1 */
samplecount = info->frame_count*1152 - (start_skip + stop_skip);
- samplesdone = ci->id3->elapsed * (ci->id3->frequency / 100) / 10;
+ samplesdone = ci->id3->elapsed * frequency_divider / 10;
} else {
- samplecount = ci->id3->length * (ci->id3->frequency / 100) / 10;
- samplesdone = ci->id3->elapsed * (ci->id3->frequency / 100) / 10;
+ samplecount = ci->id3->length * frequency_divider / 10;
+ samplesdone = ci->id3->elapsed * frequency_divider / 10;
}
/* This is the decoding loop. */
while (1) {
ci->yield();
- if (ci->stop_codec || ci->reload_codec) {
+ if (ci->stop_codec || ci->reload_codec) {
break ;
}
@@ -171,11 +163,12 @@ enum codec_status codec_start(struct codec_api* api)
unsigned int sample_loc;
int newpos;
- sample_loc = ci->seek_time/1000 * ci->id3->frequency;
+ sample_loc = ci->seek_time * frequency_divider / 10;
newpos = ci->mp3_get_filepos(ci->seek_time-1);
+ if (sample_loc >= samplecount + samplesdone)
+ break ;
+
if (ci->seek_buffer(newpos)) {
- if (sample_loc >= samplecount + samplesdone)
- break ;
samplecount += samplesdone - sample_loc;
samplesdone = sample_loc;
}
@@ -232,17 +225,28 @@ enum codec_status codec_start(struct codec_api* api)
/* We skip start_skip number of samples here, this should only happen for
very first frame in the stream. */
/* TODO: possible for start_skip to exceed one frames worth of samples? */
- //length = resample((long *)&Synth.pcm.samples[0][start_skip], resampled_data[0], Synth.pcm.length, &lr);
- //if (MAD_NCHANNELS(&Frame.header) == 2)
- // resample((long *)&Synth.pcm.samples[1][start_skip], resampled_data[1], Synth.pcm.length, &rr);
- ci->audiobuffer_insert_split(&Synth.pcm.samples[0][start_skip],
- &Synth.pcm.samples[1][start_skip],
- (Synth.pcm.length - start_skip) * 4);
+
+ if (MAD_NCHANNELS(&Frame.header) == 2) {
+ if (current_stereo_mode != STEREO_NONINTERLEAVED) {
+ ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED);
+ current_stereo_mode = STEREO_NONINTERLEAVED;
+ }
+ ci->audiobuffer_insert_split(&Synth.pcm.samples[0][start_skip],
+ &Synth.pcm.samples[1][start_skip],
+ (Synth.pcm.length - start_skip) * 4);
+ } else {
+ if (current_stereo_mode != STEREO_MONO) {
+ ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO);
+ current_stereo_mode = STEREO_MONO;
+ }
+ ci->audiobuffer_insert((char *)&Synth.pcm.samples[0][start_skip],
+ (Synth.pcm.length - start_skip) * 4);
+ }
start_skip = 0; /* not very elegant, and might want to keep this value */
samplesdone += Synth.pcm.length;
samplecount -= Synth.pcm.length;
- ci->set_elapsed(samplesdone / (ci->id3->frequency/1000));
+ ci->set_elapsed(samplesdone / (frequency_divider / 10));
}
Stream.error = 0;
diff --git a/apps/dsp.c b/apps/dsp.c
index 56ebacb24c..b24e261371 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -269,7 +269,6 @@ void convert_stereo_mode(long *dest, long *src, int samplecount)
}
}
-/* Not yet functional. */
void scale_up(long *dest, short *src, int samplecount)
{
int i;
@@ -287,8 +286,6 @@ void scale_up_convert_stereo_mode(long *dest, short *src, int samplecount)
for (i = 0; i < samplecount; i++) {
dest[i] = (long)(src[i*2+0] << SAMPLE_DEPTH);
dest[i+samplecount] = (long)(src[i*2+1] << SAMPLE_DEPTH);
- //dest[i] = (long)(((src[i*2 + 0] << 8)&0x7fff) | ((1L << 31) & src[i*2+0]<<15));
- //dest[i+samplecount] = (long)(((src[i*2 + 1] << 8)&0x7fff) | ((1L << 31) & src[i*2+1]<<15));
}
}
@@ -307,21 +304,22 @@ int dsp_process(char *dest, char *src, int samplecount)
p = src;
/* Scale up to 32-bit samples. */
if (dsp_config.sample_depth <= SAMPLE_DEPTH) {
- if (dsp_config.stereo_mode == STEREO_INTERLEAVED)
+ if (dsp_config.stereo_mode == STEREO_INTERLEAVED) {
scale_up_convert_stereo_mode((long *)samplebuf,
(short *)p, copy_n);
- else
+ } else {
scale_up((long *)samplebuf, (short *)p, copy_n);
+ }
p = samplebuf;
fracbits = 31;
}
/* Convert to non-interleaved stereo. */
else if (dsp_config.stereo_mode == STEREO_INTERLEAVED) {
- convert_stereo_mode((long *)samplebuf, (long *)p, copy_n);
+ convert_stereo_mode((long *)samplebuf, (long *)p, copy_n / 2);
p = samplebuf;
- }
-
+ }
+
/* Apply DSP functions. */
if (dsp_config.stereo_mode == STEREO_INTERLEAVED) {
channel = 0;
@@ -330,6 +328,12 @@ int dsp_process(char *dest, char *src, int samplecount)
channel = 1;
process((short *)dest, (long *)p, copy_n / 2);
dest += rc;
+ } else if (dsp_config.stereo_mode == STEREO_MONO) {
+ channel = 0;
+ rc = process((short *)dest, (long *)p, copy_n) * 4;
+ channel = 1;
+ process((short *)dest, (long *)p, copy_n);
+ dest += rc;
} else {
rc = process((short *)dest, (long *)p, copy_n) * 2;
dest += rc * 2;
diff --git a/apps/playback.c b/apps/playback.c
index 70cb36341b..3a5eeb328f 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -165,6 +165,8 @@ static int new_track;
/* Callback function to call when current track has really changed. */
void (*track_changed_callback)(struct track_info *ti);
+void (*track_buffer_callback)(struct mp3entry *id3, bool last_track);
+void (*track_unbuffer_callback)(struct mp3entry *id3, bool disk_spinning);
/* Configuration */
static int conf_bufferlimit;
@@ -266,10 +268,14 @@ bool codec_audiobuffer_insert_callback(char *buf, long length)
int factor;
int next_channel = 0;
int processed_length;
+ int mono = 0;
/* If non-interleaved stereo mode. */
- if (dsp_config.stereo_mode == STEREO_NONINTERLEAVED) {
+ if (dsp_config.stereo_mode == STEREO_NONINTERLEAVED)
next_channel = length / 2;
+ else if (dsp_config.stereo_mode == STEREO_MONO) {
+ length *= 2;
+ mono = 1;
}
if (dsp_config.sample_depth > 16) {
@@ -296,11 +302,11 @@ bool codec_audiobuffer_insert_callback(char *buf, long length)
processed_length = dsp_process(dest, buf, realsize / 4) * 2;
dsp_process(dest, buf + next_channel, realsize / 4);
} else {
- processed_length = dsp_process(dest, buf, realsize / 2);
+ processed_length = dsp_process(dest, buf, realsize >> (mono + 1));
}
pcm_flush_buffer(processed_length);
length -= realsize;
- buf += realsize << factor;
+ buf += realsize << (factor + mono);
}
return true;
@@ -589,6 +595,18 @@ void codec_configure_callback(int setting, void *value)
}
}
+void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3,
+ bool last_track))
+{
+ track_buffer_callback = handler;
+}
+
+void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3,
+ bool disk_spinning))
+{
+ track_unbuffer_callback = handler;
+}
+
void audio_set_track_changed_event(void (*handler)(struct track_info *ti))
{
track_changed_callback = handler;
@@ -934,6 +952,10 @@ void audio_play_start(int offset)
last_peek_offset = 0;
if (audio_load_track(offset, true, 0)) {
last_peek_offset++;
+ if (track_buffer_callback) {
+ cur_ti->event_sent = true;
+ track_buffer_callback(&cur_ti->id3, true);
+ }
ata_sleep();
} else {
logf("Failure");
@@ -950,10 +972,46 @@ void audio_clear_track_entries(void)
for (i = 0; i < MAX_TRACK - track_count; i++) {
if (++cur_idx >= MAX_TRACK)
cur_idx = 0;
+
+ /* Send event to notify that track has finished. */
+ if (track_unbuffer_callback && tracks[cur_idx].event_sent)
+ track_unbuffer_callback(&tracks[cur_idx].id3, filling);
memset(&tracks[cur_idx], 0, sizeof(struct track_info));
}
}
+/* Send callback events to notify about new tracks. */
+static void generate_postbuffer_events(void)
+{
+ int i;
+ int cur_ridx, event_count;
+
+ if (!track_buffer_callback)
+ return ;
+
+ /* At first determine how many unsent events we have. */
+ cur_ridx = track_ridx;
+ event_count = 0;
+ for (i = 0; i < track_count; i++) {
+ if (!tracks[cur_ridx].event_sent)
+ event_count++;
+ if (++cur_ridx >= MAX_TRACK)
+ cur_ridx -= MAX_TRACK;
+ }
+
+ /* Now sent these events. */
+ cur_ridx = track_ridx;
+ for (i = 0; i < track_count; i++) {
+ if (!tracks[cur_ridx].event_sent) {
+ tracks[cur_ridx].event_sent = true;
+ event_count--;
+ track_buffer_callback(&tracks[cur_ridx].id3, event_count == 0);
+ }
+ if (++cur_ridx >= MAX_TRACK)
+ cur_ridx -= MAX_TRACK;
+ }
+}
+
void initialize_buffer_fill(void)
{
int cur_idx, i;
@@ -1014,6 +1072,7 @@ void audio_check_buffer(void)
if (audio_load_track(0, false, last_peek_offset)) {
last_peek_offset++;
} else if (tracks[track_widx].filerem == 0 || fill_bytesleft == 0) {
+ generate_postbuffer_events();
filling = false;
conf_bufferlimit = 0;
pcm_set_boost_mode(false);
@@ -1069,6 +1128,8 @@ void audio_change_track(void)
logf("No more tracks");
while (pcm_is_playing())
yield();
+ track_count = 0;
+ audio_clear_track_entries();
playing = false;
return ;
}
@@ -1206,6 +1267,8 @@ void audio_thread(void)
}
pcm_play_stop();
pcm_play_pause(true);
+ track_count = 0;
+ audio_clear_track_entries();
break ;
case AUDIO_PAUSE:
@@ -1233,6 +1296,8 @@ void audio_thread(void)
#ifndef SIMULATOR
case SYS_USB_CONNECTED:
+ track_count = 0;
+ audio_clear_track_entries();
playing = false;
filling = false;
ci.stop_codec = true;
@@ -1269,6 +1334,8 @@ void codec_thread(void)
codecsize = cur_ti->codecsize;
if (codecsize == 0) {
logf("Codec slot is empty!");
+ track_count = 0;
+ audio_clear_track_entries();
playing = false;
break ;
}
@@ -1296,6 +1363,8 @@ void codec_thread(void)
if (status != CODEC_OK) {
logf("Codec failure");
splash(HZ*2, true, "Codec failure");
+ track_count = 0;
+ audio_clear_track_entries();
playing = false;
} else {
logf("Codec finished");
@@ -1597,6 +1666,18 @@ void mpeg_id3_options(bool _v1first)
v1first = _v1first;
}
+/*
+void test_buffer_event(struct mp3entry *id3, bool last_track)
+{
+ logf("be:%d%s", last_track, id3->title);
+}
+
+void test_unbuffer_event(struct mp3entry *id3, bool disk_spinning)
+{
+ logf("ube:%d%s", disk_spinning, id3->title);
+}
+*/
+
void audio_init(void)
{
logf("audio api init");
@@ -1611,12 +1692,19 @@ void audio_init(void)
paused = false;
track_changed = false;
current_fd = -1;
+ track_buffer_callback = NULL;
+ track_unbuffer_callback = NULL;
track_changed_callback = NULL;
logf("abuf:%0x", PCMBUF_SIZE);
logf("fbuf:%0x", codecbuflen);
logf("mbuf:%0x", MALLOC_BUFSIZE);
-
+
+ /*
+ audio_set_track_buffer_event(test_buffer_event);
+ audio_set_track_unbuffer_event(test_unbuffer_event);
+ */
+
/* Initialize codec api. */
ci.read_filebuf = codec_filebuf_callback;
ci.audiobuffer_insert = pcm_insert_buffer;
diff --git a/apps/playback.h b/apps/playback.h
index 5cebfa84b3..8148a3445c 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -58,10 +58,15 @@ struct track_info {
volatile int available; /* Available bytes to read from buffer */
bool taginfo_ready; /* Is metadata read */
int playlist_offset; /* File location in playlist */
+ bool event_sent; /* Has event callback functions been called? */
};
/* Functions */
void audio_set_track_changed_event(void (*handler)(struct track_info *ti));
+void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3,
+ bool last_track));
+void audio_set_track_unbufer_event(void (*handler)(struct mp3entry *id3,
+ bool disk_spinning));
void audio_invalidate_tracks(void);
#endif