From 857db456affc0b65e4fa3db1020117f547bcade5 Mon Sep 17 00:00:00 2001 From: Brandon Low Date: Thu, 6 Apr 2006 04:07:06 +0000 Subject: Fix seeking on swcodec, but probably break some cases of skipping. Another important rework here, buffer management is serialized along with most other operations on the audio thread. This has some minor performance issues on ipod that can lead to audio skips during buffer fill as well. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9529 a1c6a512-1295-4272-9138-f99709370657 --- apps/gui/gwps-common.c | 6 ++ apps/playback.c | 194 +++++++++++++++++++++++------------------------- firmware/export/audio.h | 1 + 3 files changed, 98 insertions(+), 103 deletions(-) diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c index 231aa28162..41e20ecadd 100644 --- a/apps/gui/gwps-common.c +++ b/apps/gui/gwps-common.c @@ -2347,7 +2347,11 @@ bool ffwd_rew(int button) wps_state.id3 && wps_state.id3->length ) { if (!wps_state.paused) +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else audio_pause(); +#endif #if CONFIG_KEYPAD == PLAYER_PAD FOR_NB_SCREENS(i) gui_wps[i].display->stop_scroll(); @@ -2399,8 +2403,10 @@ bool ffwd_rew(int button) ff_rewind_count = 0; wps_state.ff_rewind = false; status_set_ffmode(0); +#if (CONFIG_CODEC != SWCODEC) if (!wps_state.paused) audio_resume(); +#endif #ifdef HAVE_LCD_CHARCELLS gui_wps_display(); #endif diff --git a/apps/playback.c b/apps/playback.c index aa48dd9bae..6baf683342 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -70,7 +70,7 @@ static volatile bool audio_codec_loaded; static volatile bool voice_codec_loaded; static volatile bool playing; -static volatile bool seeking; +static volatile bool paused; #define CODEC_VORBIS "/.rockbox/codecs/vorbis.codec" #define CODEC_MPA_L3 "/.rockbox/codecs/mpa.codec" @@ -104,6 +104,7 @@ enum { Q_AUDIO_DIR_NEXT, Q_AUDIO_DIR_PREV, Q_AUDIO_POSTINIT, + Q_AUDIO_CHECK_BUFFER, Q_CODEC_LOAD, Q_CODEC_LOAD_DISK, @@ -454,6 +455,9 @@ static void advance_buffer_counters(size_t amount) { ci.curpos += amount; cur_ti->available -= amount; filebufused -= amount; + + if (!pcmbuf_is_lowdata() && !mutex_bufferfill.locked) + queue_post(&audio_queue, Q_AUDIO_CHECK_BUFFER, 0); } /* copy up-to size bytes into ptr and return the actual size copied */ @@ -476,6 +480,7 @@ size_t codec_filebuf_callback(void *ptr, size_t size) /* Let the disk buffer catch fill until enough data is available */ while (copy_n > cur_ti->available) { + queue_post(&audio_queue, Q_AUDIO_CHECK_BUFFER, 0); yield(); if (ci.stop_codec || ci.reload_codec) return 0; @@ -550,7 +555,7 @@ void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) return voice_request_data(realsize, reqsize); } - if (ci.stop_codec || !playing) { + if (!playing) { *realsize = 0; return NULL; } @@ -562,6 +567,7 @@ void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) } while (copy_n > cur_ti->available) { + queue_post(&audio_queue, Q_AUDIO_CHECK_BUFFER, 0); yield(); if (ci.stop_codec || ci.reload_codec) { *realsize = 0; @@ -618,11 +624,7 @@ static bool rebuffer_and_seek(size_t newpos) mutex_unlock(&mutex_bufferfill); - while (cur_ti->available == 0 && cur_ti->filerem > 0) { - sleep(1); - if (ci.stop_codec || ci.reload_codec || !queue_empty(&audio_queue)) - return false; - } + queue_post(&audio_queue, Q_AUDIO_CHECK_BUFFER, 0); return true; } @@ -644,6 +646,7 @@ void codec_advance_buffer_callback(size_t amount) while (amount > cur_ti->available && filling) sleep(1); + /* This should not happen */ if (amount > cur_ti->available) { if (!rebuffer_and_seek(ci.curpos + amount)) ci.stop_codec = true; @@ -682,9 +685,11 @@ void codec_seek_complete_callback(void) if (pcm_is_paused()) { /* If this is not a seamless seek, clear the buffer */ pcmbuf_play_stop(); + /* If playback was not 'deliberately' paused, unpause now */ + if (!paused) + pcmbuf_pause(false); } ci.seek_time = 0; - seeking = false; } bool codec_seek_buffer_callback(size_t newpos) @@ -852,7 +857,6 @@ void strip_id3v1_tag(void) static void audio_fill_file_buffer(void) { - unsigned long i; size_t size; size_t copy_n; int rc; @@ -865,9 +869,8 @@ static void audio_fill_file_buffer(void) tracks[track_widx].codecsize = 0; mutex_lock(&mutex_bufferfill); - i = 0; size = MIN(tracks[track_widx].filerem, AUDIO_FILL_CYCLE); - while (i < size) { + while (size > 0) { /* Give codecs some processing time. */ yield_codecs(); @@ -884,12 +887,16 @@ static void audio_fill_file_buffer(void) buf_widx += rc; if (buf_widx >= filebuflen) buf_widx -= filebuflen; - i += rc; tracks[track_widx].available += rc; tracks[track_widx].filerem -= rc; tracks[track_widx].filepos += rc; filebufused += rc; fill_bytesleft -= rc; + if ((unsigned)rc > size) { + rc = size; + logf("audio_fill_file_buffer read past end of file\n"); + } + size -= rc; } if (tracks[track_widx].filerem == 0) { @@ -1092,15 +1099,12 @@ static bool audio_load_track(int offset, bool start_play, int peek_offset) char *trackname; int fd = -1; off_t size; - int rc, i; - int copy_n; char msgbuf[80]; /* Stop buffer filling if there is no free track entries. Don't fill up the last track entry (we wan't to store next track metadata there). */ if (track_count >= MAX_TRACK - 1) { - fill_bytesleft = 0; return false; } @@ -1196,7 +1200,6 @@ static bool audio_load_track(int offset, bool start_play, int peek_offset) set_filebuf_watermark(buffer_margin); tracks[track_widx].id3.elapsed = 0; - /* Starting playback from an offset is only support in MPA at the moment */ if (offset > 0) { switch (tracks[track_widx].id3.codectype) { case AFMT_MPA_L2: @@ -1231,64 +1234,27 @@ static bool audio_load_track(int offset, bool start_play, int peek_offset) codec_track_changed(); } - /* Do some initial file buffering. */ - mutex_lock(&mutex_bufferfill); - i = tracks[track_widx].start_pos; - size = MIN(size, AUDIO_FILL_CYCLE); - while (i < size) { - /* Give codecs some processing time to prevent glitches. */ - yield_codecs(); - - if (fill_bytesleft == 0) - break ; - - copy_n = MIN(conf_filechunk, filebuflen - buf_widx); - copy_n = MIN(size - i, copy_n); - copy_n = MIN((int)fill_bytesleft, copy_n); - rc = read(fd, &filebuf[buf_widx], copy_n); - if (rc < copy_n) { - logf("File error!"); - tracks[track_widx].filesize = 0; - tracks[track_widx].filerem = 0; - close(fd); - mutex_unlock(&mutex_bufferfill); - return false; - } - buf_widx += rc; - if (buf_widx >= filebuflen) - buf_widx -= filebuflen; - i += rc; - tracks[track_widx].available += rc; - tracks[track_widx].filerem -= rc; - filebufused += rc; - fill_bytesleft -= rc; + if (current_fd >= 0) { + close(current_fd); } - mutex_unlock(&mutex_bufferfill); + current_fd = fd; + + audio_fill_file_buffer(); if (!start_play) track_count++; - tracks[track_widx].filepos = i; - - if (current_fd >= 0) { - close(current_fd); - current_fd = -1; - } - /* Leave the file handle open for faster buffer refill. */ if (tracks[track_widx].filerem != 0) { - current_fd = fd; logf("Partially buf:%d", tracks[track_widx].available); } else { logf("Completely buf."); close(fd); - strip_id3v1_tag(); - if (++track_widx >= MAX_TRACK) { track_widx = 0; } - tracks[track_widx].filerem = 0; + tracks[track_widx].filesize = 0; } return true; @@ -1337,7 +1303,7 @@ static void stop_codec_flush(void) pcmbuf_pause(true); while (audio_codec_loaded) yield(); - pcmbuf_pause(false); + pcmbuf_pause(paused); } static void audio_stop_playback(bool resume) @@ -1348,6 +1314,7 @@ static void audio_stop_playback(bool resume) playing = false; filling = false; stop_codec_flush(); + paused = false; if (current_fd >= 0) { close(current_fd); current_fd = -1; @@ -1467,10 +1434,10 @@ static void initialize_buffer_fill(void) static void audio_check_buffer(void) { /* Start buffer filling as necessary. */ - if ((!conf_watermark || filebufused > conf_watermark - || !queue_empty(&audio_queue) || !playing || ci.stop_codec - || ci.reload_codec || playlist_end) && !filling) - return ; + if ((!conf_watermark || filebufused > conf_watermark || !playing + || ci.stop_codec || ci.reload_codec || playlist_end) + && !filling) + return; mutex_lock(&mutex_bufferfill); initialize_buffer_fill(); @@ -1485,23 +1452,33 @@ static void audio_check_buffer(void) fill_bytesleft = 0; } - /* Try to load remainings of the file. */ - if (tracks[track_widx].filerem > 0) - audio_fill_file_buffer(); - - /* Increase track write index as necessary. */ - if (tracks[track_widx].filerem == 0 && tracks[track_widx].filesize != 0) { - if (++track_widx == MAX_TRACK) - track_widx = 0; + /* If the current track has been started */ + if (tracks[track_widx].filesize > 0) + { + /* Try to load remainings of the file. */ + if (tracks[track_widx].filerem > 0) + { + audio_fill_file_buffer(); + } + if (tracks[track_widx].filerem == 0) { + if (++track_widx == MAX_TRACK) + track_widx = 0; + tracks[track_widx].filesize = 0; + } + else + { + /* Done filling, couldn't read that whole file */ + fill_bytesleft = 0; + } } - - /* Load new files to fill the entire buffer. */ - if (audio_load_track(0, false, last_peek_offset + 1)) { + /* Otherwise, load a new track */ + else if (audio_load_track(0, false, last_peek_offset + 1)) + { if (conf_bufferlimit) fill_bytesleft = 0; - } - else if (tracks[track_widx].filerem == 0) + } else { fill_bytesleft = 0; + } if (fill_bytesleft <= 0) { @@ -1581,9 +1558,12 @@ static int skip_next_track(bool inside_codec_thread) track_ridx = 0; /* Wait for new track data. */ - while (tracks[track_ridx].filesize == 0 && filling - && !ci.stop_codec) + while (tracks[track_ridx].filesize == 0 && filling) + { yield(); + if (ci.stop_codec) + return SKIP_FAIL; + } if (tracks[track_ridx].filesize <= 0) { @@ -1604,18 +1584,21 @@ static int skip_next_track(bool inside_codec_thread) return SKIP_OK_DISK; } + /* Wind the buffer forward to the end of the current track */ buf_ridx += cur_ti->available; filebufused -= cur_ti->available; + /* Move to the new track */ cur_ti = &tracks[track_ridx]; + /* Wind past the new track's codec */ buf_ridx += cur_ti->codecsize; filebufused -= cur_ti->codecsize; + + /* Check and handle buffer wrapping */ if (buf_ridx >= filebuflen) buf_ridx -= filebuflen; - audio_update_trackinfo(); - if (!filling) - pcmbuf_set_boost_mode(false); + audio_update_trackinfo(); return SKIP_OK_RAM; } @@ -1628,8 +1611,8 @@ static int skip_previous_track(bool inside_codec_thread) track_ridx += MAX_TRACK; if (tracks[track_ridx].filesize == 0 || - filebufused+ci.curpos+tracks[track_ridx].filesize - /*+ (off_t)tracks[track_ridx].codecsize*/ > filebuflen) { + filebufused+ci.curpos + tracks[track_ridx].filesize > filebuflen) + { logf("Loading from disk..."); ci.reload_codec = true; /* Stop playback. */ @@ -1645,16 +1628,23 @@ static int skip_previous_track(bool inside_codec_thread) return SKIP_OK_DISK; } + /* Rewind the buffer to the beginning of the playing track */ buf_ridx -= ci.curpos + cur_ti->codecsize; filebufused += ci.curpos + cur_ti->codecsize; + /* Rewind the track to its beginning */ cur_ti->available = cur_ti->filesize - cur_ti->filerem; + /* Move to the new track */ cur_ti = &tracks[track_ridx]; + /* Rewind the buffer to the beginning of the new track */ + buf_ridx -= cur_ti->filesize; filebufused += cur_ti->filesize; + /* Reset to the beginning of the new track */ cur_ti->available = cur_ti->filesize; - if (buf_ridx < cur_ti->filesize) + + /* Check and handle buffer wrapping */ + if (buf_ridx <= 0) buf_ridx += filebuflen; - buf_ridx -= cur_ti->filesize; audio_update_trackinfo(); @@ -1806,18 +1796,11 @@ void audio_thread(void) playback_init(); while (1) { - if (!play_pending && queue_empty(&audio_queue)) - { - yield_codecs(); - audio_check_buffer(); - } + if (play_pending) + queue_wait_w_tmo(&audio_queue, &ev, 0); else - { - // ata_spin(); - sleep(1); - } + queue_wait(&audio_queue, &ev); - queue_wait_w_tmo(&audio_queue, &ev, 0); if (ev.id == SYS_TIMEOUT && play_pending) { ev.id = Q_AUDIO_PLAY; @@ -1825,6 +1808,9 @@ void audio_thread(void) } switch (ev.id) { + case Q_AUDIO_CHECK_BUFFER: + audio_check_buffer(); + break; case Q_AUDIO_PLAY: /* Don't start playing immediately if user is skipping tracks * fast to prevent UI lag. */ @@ -1875,11 +1861,13 @@ void audio_thread(void) case Q_AUDIO_PAUSE: logf("audio_pause"); pcmbuf_pause(true); + paused = true; break ; case Q_AUDIO_RESUME: logf("audio_resume"); pcmbuf_pause(false); + paused = false; break ; case Q_AUDIO_NEXT: @@ -2264,16 +2252,15 @@ void audio_prev_dir(void) queue_post(&audio_queue, Q_AUDIO_DIR_PREV, 0); } +void audio_pre_ff_rewind(void) { + logf("pre ff/rewind"); + pcmbuf_pause(true); +} + void audio_ff_rewind(long newpos) { logf("ff/rewind: %d", newpos); - seeking = true; queue_post(&audio_queue, Q_AUDIO_FF_REWIND, (int *)newpos); - /* This is a hack, the correct solution is to report back to - * the caller when the seek is complete. */ - while (seeking) { - yield(); - } } void audio_flush_and_reload_tracks(void) @@ -2293,7 +2280,7 @@ int audio_status(void) if (playing) ret |= AUDIO_STATUS_PLAY; - if (pcm_is_paused()) + if (paused) ret |= AUDIO_STATUS_PAUSE; return ret; @@ -2596,6 +2583,7 @@ void audio_preinit(void) filling = false; current_codec = CODEC_IDX_AUDIO; playing = false; + paused = false; audio_codec_loaded = false; voice_is_playing = false; track_changed = false; diff --git a/firmware/export/audio.h b/firmware/export/audio.h index d1421ce9a3..0bc47c51f8 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h @@ -70,6 +70,7 @@ void audio_resume(void); void audio_next(void); void audio_prev(void); int audio_status(void); +void audio_pre_ff_rewind(void); /* SWCODEC only */ void audio_ff_rewind(long newtime); void audio_flush_and_reload_tracks(void); struct mp3entry* audio_current_track(void); -- cgit v1.2.3