From 6a4bfb5dcf03d78718436045b8629454b6f2d282 Mon Sep 17 00:00:00 2001 From: Miika Pekkarinen Date: Thu, 1 Dec 2005 18:44:11 +0000 Subject: [PATCH] iRiver: Fixed several problems with playback on track skipping & loading: * Correctly detecting codec type from metadata. * Skipping incorrect tracks correctly if there are more than one unloadable track. * Fixed internal ram skipping when codec switching is required. * Fixed hang if no files in playlist is playable. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8116 a1c6a512-1295-4272-9138-f99709370657 --- apps/metadata.c | 3 + apps/playback.c | 414 +++++++++++++++++++++++++----------------------- apps/playlist.c | 38 ++--- 3 files changed, 242 insertions(+), 213 deletions(-) diff --git a/apps/metadata.c b/apps/metadata.c index 5ed16e5847..49780b5860 100644 --- a/apps/metadata.c +++ b/apps/metadata.c @@ -1365,6 +1365,9 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname, unsigned long totalsamples; int i; + /* We should detect the codec type here. */ + track->id3.codectype = probe_file_format(trackname); + /* Load codec specific track tag information. */ switch (track->id3.codectype) diff --git a/apps/playback.c b/apps/playback.c index 563fb741ed..5ac70d17f7 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -705,7 +705,7 @@ static void set_filebuf_watermark(int seconds) conf_watermark = bytes; } -void codec_configure_callback(int setting, void *value) +static void codec_configure_callback(int setting, void *value) { switch (setting) { case CODEC_SET_FILEBUF_WATERMARK: @@ -748,7 +748,7 @@ void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3)) track_changed_callback = handler; } -void codec_track_changed(void) +static void codec_track_changed(void) { track_changed = true; queue_post(&audio_queue, AUDIO_TRACK_CHANGED, 0); @@ -756,7 +756,7 @@ void codec_track_changed(void) /* Give codecs or file buffering the right amount of processing time to prevent pcm audio buffer from going empty. */ -void yield_codecs(void) +static void yield_codecs(void) { yield(); if (!pcm_is_playing() && !paused) @@ -806,7 +806,7 @@ void strip_id3v1_tag(void) } } -void audio_fill_file_buffer(void) +static void audio_fill_file_buffer(void) { long i, size; int rc; @@ -852,20 +852,28 @@ void audio_fill_file_buffer(void) tracks[track_widx].filerem);*/ } -bool loadcodec(const char *trackname, bool start_play) +static int get_codec_base_type(int type) +{ + switch (type) { + case AFMT_MPA_L1: + case AFMT_MPA_L2: + case AFMT_MPA_L3: + return AFMT_MPA_L3; + } + + return type; +} + +static bool loadcodec(bool start_play) { - char msgbuf[80]; off_t size; - unsigned int filetype; int fd; int i, rc; const char *codec_path; int copy_n; int prev_track; - filetype = probe_file_format(trackname); - - switch (filetype) { + switch (tracks[track_widx].id3.codectype) { case AFMT_OGG_VORBIS: logf("Codec: Vorbis"); codec_path = CODEC_VORBIS; @@ -910,20 +918,20 @@ bool loadcodec(const char *trackname, bool start_play) break; default: logf("Codec: Unsupported"); - snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", trackname); - gui_syncsplash(HZ*2, true, msgbuf); codec_path = NULL; return false; } - tracks[track_widx].id3.codectype = filetype; tracks[track_widx].codecsize = 0; if (!start_play) { prev_track = track_widx - 1; if (prev_track < 0) prev_track = MAX_TRACK-1; - if (track_count > 0 && filetype == tracks[prev_track].id3.codectype) { + if (track_count > 0 && + get_codec_base_type(tracks[track_widx].id3.codectype) == + get_codec_base_type(tracks[prev_track].id3.codectype)) + { logf("Reusing prev. codec"); return true; } @@ -943,8 +951,6 @@ bool loadcodec(const char *trackname, bool start_play) fd = open(codec_path, O_RDONLY); if (fd < 0) { logf("Codec doesn't exist!"); - snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", codec_path); - gui_syncsplash(HZ*2, true, msgbuf); return false; } @@ -981,7 +987,7 @@ bool loadcodec(const char *trackname, bool start_play) return true; } -bool read_next_metadata(void) +static bool read_next_metadata(void) { int fd; char *trackname; @@ -1010,22 +1016,21 @@ bool read_next_metadata(void) * In fact, it might be better not to start filling here, because if user * is manipulating the playlist a lot, we will just lose battery. */ // filling = true; - tracks[next_track].id3.codectype = probe_file_format(trackname); status = get_metadata(&tracks[next_track],fd,trackname,v1first); - tracks[next_track].id3.codectype = 0; track_changed = true; close(fd); return status; } -bool audio_load_track(int offset, bool start_play, int peek_offset) +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 @@ -1087,10 +1092,28 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) current_codec = last_codec; } + /* Get track metadata if we don't already have it. */ + if (!tracks[track_widx].taginfo_ready) { + if (!get_metadata(&tracks[track_widx],fd,trackname,v1first)) { + logf("Metadata error!"); + tracks[track_widx].filesize = 0; + tracks[track_widx].filerem = 0; + tracks[track_widx].taginfo_ready = false; + close(fd); + /* Skip invalid entry from playlist. */ + playlist_skip_entry(NULL, peek_offset); + goto peek_again; + } + } + /* Load the codec. */ tracks[track_widx].codecbuf = &filebuf[buf_widx]; - if (!loadcodec(trackname, start_play)) { + if (!loadcodec(start_play)) { + /* We should not use gui_syncplash from audio thread! */ + snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", trackname); + gui_syncsplash(HZ*2, true, msgbuf); close(fd); + /* Set filesize to zero to indicate no file was loaded. */ tracks[track_widx].filesize = 0; tracks[track_widx].filerem = 0; @@ -1104,21 +1127,8 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) } return false; } - // tracks[track_widx].filebuf = &filebuf[buf_widx]; + tracks[track_widx].start_pos = 0; - - /* Get track metadata if we don't already have it. */ - if (!tracks[track_widx].taginfo_ready) { - if (!get_metadata(&tracks[track_widx],fd,trackname,v1first)) { - logf("Metadata error!"); - tracks[track_widx].filesize = 0; - tracks[track_widx].filerem = 0; - close(fd); - /* Skip invalid entry from playlist. */ - playlist_skip_entry(NULL, peek_offset); - goto peek_again; - } - } set_filebuf_watermark(buffer_margin); tracks[track_widx].id3.elapsed = 0; @@ -1220,40 +1230,7 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) return true; } -void audio_play_start(int offset) -{ - if (current_fd >= 0) { - close(current_fd); - current_fd = -1; - } - - memset(&tracks, 0, sizeof(struct track_info) * MAX_TRACK); - sound_set_volume(global_settings.volume); - track_count = 0; - track_widx = 0; - track_ridx = 0; - buf_ridx = 0; - buf_widx = 0; - filebufused = 0; - pcmbuf_set_boost_mode(true); - - fill_bytesleft = filebuflen; - filling = true; - last_peek_offset = -1; - - if (audio_load_track(offset, true, 0)) { - if (track_buffer_callback) { - cur_ti->event_sent = true; - track_buffer_callback(&cur_ti->id3, true); - } - } else { - logf("Failure"); - } - - pcmbuf_set_boost_mode(false); -} - -void audio_clear_track_entries(bool buffered_only) +static void audio_clear_track_entries(bool buffered_only) { int cur_idx, event_count; int i; @@ -1290,6 +1267,61 @@ void audio_clear_track_entries(bool buffered_only) } } +static void audio_stop_playback(bool resume) +{ + paused = false; + if (playing) + playlist_update_resume_info(resume ? audio_current_track() : NULL); + playing = false; + filling = false; + ci.stop_codec = true; + if (current_fd >= 0) { + close(current_fd); + current_fd = -1; + } + pcmbuf_play_stop(); + while (audio_codec_loaded) + yield(); + + track_count = 0; + /* Mark all entries null. */ + audio_clear_track_entries(false); +} + +static void audio_play_start(int offset) +{ + if (current_fd >= 0) { + close(current_fd); + current_fd = -1; + } + + memset(&tracks, 0, sizeof(struct track_info) * MAX_TRACK); + sound_set_volume(global_settings.volume); + track_count = 0; + track_widx = 0; + track_ridx = 0; + buf_ridx = 0; + buf_widx = 0; + filebufused = 0; + pcmbuf_set_boost_mode(true); + + fill_bytesleft = filebuflen; + filling = true; + last_peek_offset = -1; + + if (audio_load_track(offset, true, 0)) { + if (track_buffer_callback) { + cur_ti->event_sent = true; + track_buffer_callback(&cur_ti->id3, true); + } + } else { + logf("Failure"); + audio_stop_playback(false); + } + + pcmbuf_set_boost_mode(false); +} + /* Send callback events to notify about new tracks. */ static void generate_postbuffer_events(void) { @@ -1322,7 +1354,7 @@ static void generate_postbuffer_events(void) } } -void initialize_buffer_fill(void) +static void initialize_buffer_fill(void) { int cur_idx, i; @@ -1362,7 +1394,7 @@ void initialize_buffer_fill(void) audio_clear_track_entries(true); } -void audio_check_buffer(void) +static void audio_check_buffer(void) { /* Start buffer filling as necessary. */ if ((!conf_watermark || filebufused > conf_watermark @@ -1415,33 +1447,8 @@ void audio_check_buffer(void) } } -void audio_update_trackinfo(void) +static void audio_update_trackinfo(void) { - if (new_track >= 0) { - buf_ridx += cur_ti->available; - filebufused -= cur_ti->available; - - cur_ti = &tracks[track_ridx]; - buf_ridx += cur_ti->codecsize; - filebufused -= cur_ti->codecsize; - if (buf_ridx >= filebuflen) - buf_ridx -= filebuflen; - - if (!filling) - pcmbuf_set_boost_mode(false); - } else { - buf_ridx -= ci.curpos + cur_ti->codecsize; - filebufused += ci.curpos + cur_ti->codecsize; - cur_ti->available = cur_ti->filesize - cur_ti->filerem; - - cur_ti = &tracks[track_ridx]; - buf_ridx -= cur_ti->filesize; - filebufused += cur_ti->filesize; - cur_ti->available = cur_ti->filesize; - if (buf_ridx < 0) - buf_ridx = filebuflen + buf_ridx; - } - ci.filesize = cur_ti->filesize; cur_ti->id3.elapsed = 0; cur_ti->id3.offset = 0; @@ -1461,42 +1468,90 @@ void audio_update_trackinfo(void) codec_track_changed(); } -static void audio_stop_playback(bool resume) -{ - paused = false; - if (playing) - playlist_update_resume_info(resume ? audio_current_track() : NULL); - playing = false; - filling = false; - ci.stop_codec = true; - if (current_fd >= 0) { - close(current_fd); - current_fd = -1; - } - pcmbuf_play_stop(); - while (audio_codec_loaded) - yield(); - - track_count = 0; - /* Mark all entries null. */ - audio_clear_track_entries(false); -} +enum { + SKIP_FAIL, + SKIP_OK_DISK, + SKIP_OK_RAM, +}; -static bool advance_next_track(void) +/* Should handle all situations. */ +static int skip_next_track(void) { + logf("skip next"); + /* Manual track skipping. */ + if (new_track > 0) + last_peek_offset--; + + /* Automatic track skipping. */ + else + { + if (!playlist_check(1)) { + ci.reload_codec = false; + return SKIP_FAIL; + } + last_peek_offset--; + playlist_next(1); + } + if (++track_ridx >= MAX_TRACK) track_ridx = 0; - - /* Wait for new track data (codectype 0 is invalid). When a correct - codectype is set, we can assume that the filesize is correct. */ - while (tracks[track_ridx].id3.codectype == 0 && filling + + /* Wait for new track data. */ + while (tracks[track_ridx].filesize == 0 && filling && !ci.stop_codec) yield(); - if (tracks[track_ridx].filesize > 0) - return true; + if (tracks[track_ridx].filesize <= 0) + { + logf("Loading from disk..."); + queue_post(&audio_queue, AUDIO_PLAY, 0); + return SKIP_OK_DISK; + } + + buf_ridx += cur_ti->available; + filebufused -= cur_ti->available; + + cur_ti = &tracks[track_ridx]; + buf_ridx += cur_ti->codecsize; + filebufused -= cur_ti->codecsize; + if (buf_ridx >= filebuflen) + buf_ridx -= filebuflen; + audio_update_trackinfo(); + + if (!filling) + pcmbuf_set_boost_mode(false); + + return SKIP_OK_RAM; +} - return false; +static int skip_previous_track(void) +{ + logf("skip previous"); + last_peek_offset++; + if (--track_ridx < 0) + track_ridx += MAX_TRACK; + + if (tracks[track_ridx].filesize == 0 || + filebufused+ci.curpos+tracks[track_ridx].filesize + /*+ (off_t)tracks[track_ridx].codecsize*/ > filebuflen) { + logf("Loading from disk..."); + queue_post(&audio_queue, AUDIO_PLAY, 0); + return SKIP_OK_DISK; + } + + buf_ridx -= ci.curpos + cur_ti->codecsize; + filebufused += ci.curpos + cur_ti->codecsize; + cur_ti->available = cur_ti->filesize - cur_ti->filerem; + + cur_ti = &tracks[track_ridx]; + buf_ridx -= cur_ti->filesize; + filebufused += cur_ti->filesize; + cur_ti->available = cur_ti->filesize; + if (buf_ridx < 0) + buf_ridx += filebuflen; + audio_update_trackinfo(); + + return SKIP_OK_RAM; } /* Request the next track with new codec. */ @@ -1504,33 +1559,28 @@ static void audio_change_track(void) { logf("change track"); - if (!advance_next_track()) + if (!ci.reload_codec) { - logf("No more tracks"); - while (pcm_is_playing()) + if (skip_next_track() == SKIP_FAIL) + { + logf("No more tracks"); + while (pcm_is_playing()) sleep(1); - audio_stop_playback(false); - return ; + audio_stop_playback(false); + return ; + } } - audio_update_trackinfo(); - queue_post(&codec_queue, CODEC_LOAD, 0); -} - -static int get_codec_base_type(int type) -{ - switch (type) { - case AFMT_MPA_L1: - case AFMT_MPA_L2: - case AFMT_MPA_L3: - return AFMT_MPA_L3; - } - - return type; + ci.reload_codec = false; + /* Needed for fast skipping. */ + if (cur_ti->codecsize > 0) + queue_post(&codec_queue, CODEC_LOAD, 0); } bool codec_request_next_track_callback(void) { + struct track_info *prev_ti = cur_ti; + if (current_codec == CODEC_IDX_VOICE) { voice_remaining = 0; /* Terminate the codec if there are messages waiting on the queue or @@ -1544,67 +1594,40 @@ bool codec_request_next_track_callback(void) logf("Request new track"); /* Advance to next track. */ - if (ci.reload_codec && new_track > 0) { - last_peek_offset--; - - if (!advance_next_track()) { - logf("Loading from disk..."); - new_track = 0; - queue_post(&audio_queue, AUDIO_PLAY, 0); + if (new_track >= 0 || !ci.reload_codec) { + if (skip_next_track() != SKIP_OK_RAM) return false; - } } /* Advance to previous track. */ - else if (ci.reload_codec && new_track < 0) { - last_peek_offset++; - if (--track_ridx < 0) - track_ridx = MAX_TRACK-1; - if (tracks[track_ridx].filesize == 0 || - filebufused+ci.curpos+tracks[track_ridx].filesize - /*+ (off_t)tracks[track_ridx].codecsize*/ > filebuflen) { - logf("Loading from disk..."); - new_track = 0; - queue_post(&audio_queue, AUDIO_PLAY, 0); + else { + if (skip_previous_track() != SKIP_OK_RAM) return false; - } - } - - /* Codec requested track change (next track). */ - else { - if (!playlist_check(1)) { - ci.reload_codec = false; - return false; - } - last_peek_offset--; - playlist_next(1); - - if (!advance_next_track()) { - logf("No more tracks [2]"); - ci.stop_codec = true; - new_track = 0; - queue_post(&audio_queue, AUDIO_PLAY, 0); - return false; - } } + new_track = 0; ci.reload_codec = false; + logf("On-the-fly change"); + /* Check if the next codec is the same file. */ - if (get_codec_base_type(cur_ti->id3.codectype) != - get_codec_base_type(tracks[track_ridx].id3.codectype)) { + if (get_codec_base_type(prev_ti->id3.codectype) != + get_codec_base_type(cur_ti->id3.codectype)) + { logf("New codec:%d/%d", cur_ti->id3.codectype, tracks[track_ridx].id3.codectype); - if (--track_ridx < 0) - track_ridx = MAX_TRACK-1; - new_track = 0; + + if (cur_ti->codecsize == 0) + { + logf("Loading from disk [2]..."); + queue_post(&audio_queue, AUDIO_PLAY, 0); + } + else + ci.reload_codec = true; + return false; } - - logf("On-the-fly change"); - audio_update_trackinfo(); - new_track = 0; - + return true; } @@ -1632,8 +1655,8 @@ void audio_invalidate_tracks(void) static void initiate_track_change(int peek_index) { - /* Detect if disk is spinning.. */ - if (filling) { + /* Detect if disk is spinning or already loading. */ + if (filling || ci.reload_codec || !audio_codec_loaded) { queue_post(&audio_queue, AUDIO_PLAY, 0); } else { new_track = peek_index; @@ -1808,6 +1831,8 @@ void codec_thread(void) while (1) { status = 0; queue_wait(&codec_queue, &ev); + new_track = 0; + switch (ev.id) { case CODEC_LOAD_DISK: ci.stop_codec = false; @@ -1862,12 +1887,13 @@ void codec_thread(void) if (status != CODEC_OK) { logf("Codec failure"); // audio_stop_playback(); + ci.reload_codec = false; gui_syncsplash(HZ*2, true, "Codec failure"); } else { logf("Codec finished"); } - if (playing && !ci.stop_codec && !ci.reload_codec) + if (playing && !ci.stop_codec) audio_change_track(); // queue_post(&audio_queue, AUDIO_CODEC_DONE, (void *)status); diff --git a/apps/playlist.c b/apps/playlist.c index 81265813c4..e0a2b9ba6e 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -888,25 +888,6 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current, return 0; } -/* Marks the index of the track to be skipped that is "steps" away from - * current playing track. - */ -void playlist_skip_entry(struct playlist_info *playlist, int steps) -{ - int index; - - if (playlist == NULL) - playlist = ¤t_playlist; - - index = rotate_index(playlist, playlist->index); - index += steps; - if (index < 0 || index >= playlist->amount) - return ; - - index = (index+playlist->first_index) % playlist->amount; - playlist->indices[index] |= PLAYLIST_SKIPPED; -} - /* Calculate how many steps we have to really step when skipping entries * marked as bad. */ @@ -953,6 +934,25 @@ static int calculate_step_count(const struct playlist_info *playlist, int steps) return steps; } +/* Marks the index of the track to be skipped that is "steps" away from + * current playing track. + */ +void playlist_skip_entry(struct playlist_info *playlist, int steps) +{ + int index; + + if (playlist == NULL) + playlist = ¤t_playlist; + + index = rotate_index(playlist, playlist->index); + /* We should also skip already skipped entries before the entry to be skipepd. */ + index += calculate_step_count(playlist, steps); + if (index < 0 || index >= playlist->amount) + return ; + + index = (index+playlist->first_index) % playlist->amount; + playlist->indices[index] |= PLAYLIST_SKIPPED; +} /* * returns the index of the track that is "steps" away from current playing