From d54811fe22c274ed31d897864ac0a33349f460e0 Mon Sep 17 00:00:00 2001 From: Miika Pekkarinen Date: Sat, 2 Jul 2005 16:52:30 +0000 Subject: Fixed mono playback support for mp3. Added two other event handlers in playback.c. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6986 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/mpa.c | 56 +++++++++++++++++--------------- apps/dsp.c | 20 +++++++----- apps/playback.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- apps/playback.h | 5 +++ 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 -- cgit v1.2.3