From 84d6f9e89bf1bae7e3669e487541f91f27a86b0a Mon Sep 17 00:00:00 2001 From: Miika Pekkarinen Date: Wed, 29 Jun 2005 20:50:58 +0000 Subject: [PATCH] Fixed slow track switching and track pre-buffering. Fixed rockboy crash while audio is playing. Some buffering adjustments made. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6930 a1c6a512-1295-4272-9138-f99709370657 --- apps/playback.c | 29 ++++++++-- firmware/export/pcm_playback.h | 2 + firmware/pcm_playback.c | 97 +++++++++++++++++++++++++--------- 3 files changed, 97 insertions(+), 31 deletions(-) diff --git a/apps/playback.c b/apps/playback.c index e6cc68af6b..dccf632dc9 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -61,6 +61,7 @@ #include "sound.h" #include "metadata.h" +static volatile bool codec_loaded; static volatile bool playing; static volatile bool paused; @@ -213,11 +214,20 @@ bool pcm_is_playing(void) return false; } +bool pcm_is_crossfade_active(void) +{ + return false; +} + bool pcm_is_lowdata(void) { return false; } +void pcm_flush_audio(void) +{ +} + bool pcm_crossfade_init(void) { return false; @@ -487,7 +497,8 @@ bool codec_seek_buffer_callback(off_t newpos) if (difference >= 0) { logf("seek: +%d", difference); codec_advance_buffer_callback(difference); - pcm_play_stop(); + if (!pcm_is_crossfade_active()) + pcm_play_stop(); return true; } @@ -508,6 +519,7 @@ bool codec_seek_buffer_callback(off_t newpos) if (buf_ridx < 0) buf_ridx = codecbuflen + buf_ridx; ci.curpos -= difference; + if (!pcm_is_crossfade_active()) pcm_play_stop(); return true; @@ -554,7 +566,7 @@ void yield_codecs(void) if (!pcm_is_playing()) sleep(5); while (pcm_is_lowdata() && !ci.stop_codec && - playing && queue_empty(&audio_queue)) + playing && queue_empty(&audio_queue) && codecbufused > (128*1024)) yield(); } @@ -937,8 +949,7 @@ void audio_check_buffer(void) /* Limit buffering size at first run. */ if (conf_bufferlimit && (int)fill_bytesleft >= conf_bufferlimit) { - fill_bytesleft = conf_bufferlimit; - conf_bufferlimit = 0; + fill_bytesleft = conf_bufferlimit - codecbufused; } /* Try to load remainings of the file. */ @@ -956,6 +967,7 @@ void audio_check_buffer(void) last_peek_offset++; } else if (tracks[track_widx].filerem == 0 || fill_bytesleft == 0) { filling = false; + conf_bufferlimit = 0; pcm_set_boost_mode(false); if (playing) ata_sleep(); @@ -1128,7 +1140,7 @@ void audio_thread(void) ci.stop_codec = true; ci.reload_codec = false; ci.seek_time = 0; - //pcm_play_stop(); + pcm_flush_audio(); audio_play_start((int)ev.data); break ; @@ -1195,6 +1207,7 @@ void codec_thread(void) switch (ev.id) { case CODEC_LOAD_DISK: ci.stop_codec = false; + codec_loaded = true; status = codec_load_file((char *)ev.data); break ; @@ -1209,6 +1222,7 @@ void codec_thread(void) ci.stop_codec = false; wrap = (int)&codecbuf[codecbuflen] - (int)cur_ti->codecbuf; + codec_loaded = true; status = codec_load_ram(cur_ti->codecbuf, codecsize, &codecbuf[0], wrap); break ; @@ -1220,6 +1234,8 @@ void codec_thread(void) break ; #endif } + + codec_loaded = false; switch (ev.id) { case CODEC_LOAD_DISK: @@ -1297,6 +1313,8 @@ void audio_stop(void) { logf("audio_stop"); queue_post(&audio_queue, AUDIO_STOP, 0); + while (playing || codec_loaded) + yield(); } void audio_pause(void) @@ -1524,6 +1542,7 @@ void audio_init(void) filling = false; codecbuf = &audiobuf[MALLOC_BUFSIZE]; playing = false; + codec_loaded = false; paused = false; track_changed = false; current_fd = -1; diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h index 07e33e96bf..554e975354 100644 --- a/firmware/export/pcm_playback.h +++ b/firmware/export/pcm_playback.h @@ -35,6 +35,7 @@ void pcm_play_data(const unsigned char* start, int size, void pcm_play_stop(void); void pcm_play_pause(bool play); bool pcm_is_playing(void); +bool pcm_is_crossfade_active(void); /* These functions are for playing chained buffers of PCM data */ void pcm_play_init(void); @@ -45,6 +46,7 @@ void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left)); void pcm_set_boost_mode(bool state); bool pcm_is_lowdata(void); +void pcm_flush_buffer(long length); bool pcm_crossfade_init(void); void audiobuffer_add_event(void (*event_handler)(void)); unsigned int audiobuffer_get_latency(void); diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index b0bdfbbb32..03cc106016 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c @@ -47,7 +47,7 @@ /* Must be a power of 2 */ #define NUM_PCM_BUFFERS (PCMBUF_SIZE / CHUNK_SIZE) #define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1) -#define PCM_WATERMARK (CHUNK_SIZE * 4) +#define PCM_WATERMARK (CHUNK_SIZE * 6) #define PCM_CF_WATERMARK (PCMBUF_SIZE - CHUNK_SIZE*8) static bool pcm_playing; @@ -60,6 +60,16 @@ long audiobuffer_free; static long audiobuffer_fillpos; static bool boost_mode; +/* Crossfade modes. If CFM_CROSSFADE is selected, normal + * crossfader will activate. Selecting CFM_FLUSH is a special + * operation that only overwrites the pcm buffer without crossfading. + */ +enum { + CFM_CROSSFADE, + CFM_FLUSH +}; + +static int crossfade_mode; static bool crossfade_enabled; static bool crossfade_active; static bool crossfade_init; @@ -346,8 +356,6 @@ bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void)) void pcm_watermark_callback(int bytes_left) { - (void)bytes_left; - /* Fill audio buffer by boosting cpu */ pcm_boost(true); if (bytes_left <= CHUNK_SIZE * 2) @@ -395,12 +403,25 @@ bool pcm_crossfade_init(void) return false; } logf("crossfading!"); + crossfade_mode = CFM_CROSSFADE; crossfade_init = true; return true; } +/** Initialize a track switch so that audio playback will not stop but + * the switch to next track would happen as soon as possible. + */ +void pcm_flush_audio(void) +{ + if (crossfade_init || crossfade_active) + return ; + + crossfade_mode = CFM_FLUSH; + crossfade_init = true; +} + void pcm_flush_fillpos(void) { if (audiobuffer_fillpos) { @@ -419,19 +440,29 @@ void pcm_flush_fillpos(void) static void crossfade_start(void) { - if (!crossfade_init) - return ; - crossfade_init = 0; - if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 6) + if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 4) { + if (crossfade_mode == CFM_FLUSH) + pcm_play_stop(); return ; - + } + pcm_flush_fillpos(); pcm_boost(true); crossfade_active = true; crossfade_pos = audiobuffer_pos; - crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - (CHUNK_SIZE * 2))/2; - crossfade_rem = crossfade_amount; + + switch (crossfade_mode) { + case CFM_CROSSFADE: + crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - (CHUNK_SIZE * 2))/2; + crossfade_rem = crossfade_amount; + break ; + + case CFM_FLUSH: + crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - (CHUNK_SIZE * 2))/2; + crossfade_rem = crossfade_amount; + break ; + } crossfade_pos -= crossfade_amount*2; if (crossfade_pos < 0) @@ -441,25 +472,40 @@ static void crossfade_start(void) static __inline int crossfade(short *buf, const short *buf2, int length) { - int i, size; - int val1 = (crossfade_rem<<10)/crossfade_amount; - int val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount; + int size, i; + int val1, val2; - // logf("cfi: %d/%d", length, crossfade_rem); size = MIN(length, crossfade_rem); - for (i = 0; i < size; i++) { - buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10; + switch (crossfade_mode) { + case CFM_CROSSFADE: + val1 = (crossfade_rem<<10)/crossfade_amount; + val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount; + + for (i = 0; i < size; i++) { + buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10; + } + break ; + + case CFM_FLUSH: + for (i = 0; i < size; i++) { + buf[i] = buf2[i]; + } + //memcpy((char *)buf, (char *)buf2, size*2); + break ; } - crossfade_rem -= i; + + crossfade_rem -= size; if (crossfade_rem <= 0) crossfade_active = false; - + return size; } inline static bool prepare_insert(long length) { - crossfade_start(); + if (crossfade_init) + crossfade_start(); + if (audiobuffer_free < length + audiobuffer_fillpos + CHUNK_SIZE && !crossfade_active) { pcm_boost(false); @@ -487,15 +533,12 @@ void* pcm_request_buffer(long length, long *realsize) if (crossfade_active) { *realsize = MIN(length, PCMBUF_GUARD); - //logf("cfb:%d/%d", *realsize, length); ptr = &guardbuf[0]; } else { *realsize = MIN(length, PCMBUF_SIZE - audiobuffer_pos - audiobuffer_fillpos); if (*realsize < length) { - //logf("gbr1:%d/%d", *realsize, length); *realsize += MIN((long)(length - *realsize), PCMBUF_GUARD); - //logf("gbr2:%d/%d", *realsize, length); } ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos]; } @@ -503,17 +546,20 @@ void* pcm_request_buffer(long length, long *realsize) return ptr; } +bool pcm_is_crossfade_active(void) +{ + return crossfade_active; +} + void pcm_flush_buffer(long length) { int copy_n; char *buf; if (crossfade_active) { - //logf("cfbf"); buf = &guardbuf[0]; length = MIN(length, PCMBUF_GUARD); while (length > 0 && crossfade_active) { - //logf("cfl:%d", length); copy_n = MIN(length, PCMBUF_SIZE - crossfade_pos); copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos], (const short *)buf, copy_n/2); @@ -525,7 +571,6 @@ void pcm_flush_buffer(long length) } while (length > 0) { - //logf("cfl2:%d", length); copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos); memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n); audiobuffer_fillpos = copy_n; @@ -545,7 +590,6 @@ void pcm_flush_buffer(long length) copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos); if (copy_n > 0) { - //logf("gbu:%d/%d/%d", copy_n, audiobuffer_fillpos, audiobuffer_pos); audiobuffer_fillpos -= copy_n; pcm_flush_fillpos(); copy_n = MIN(copy_n, PCMBUF_GUARD); @@ -652,6 +696,7 @@ void pcm_play_start(void) pcm_play_set_watermark(PCM_WATERMARK, pcm_watermark_callback); } crossfade_active = false; + if(!pcm_is_playing()) { size = MIN(desc->size, 32768);