From 66e8fc0f0d05d926a90e8d9991f78b7a855eb7f3 Mon Sep 17 00:00:00 2001 From: Michael Giacomelli Date: Sun, 2 Jan 2011 02:49:13 +0000 Subject: Commit part of FS#11748 by Michael Hohmuth. Adds support for automatically resuming any song that is not played to completion at any point later in time, regardless of how many intermediate tracks are played. This is accomplished by expanding the database to record incompletely played tracks. Currently, the feature is simply on or off, in which case all tracks automatically resume, or they do not. The remainder of patches in the task expand this feature by allowing only certain file to automatically resume, only resuming in certain circumstances, etc but are not included until we reach agreement on what should be included. Additionally, the manual will need to be updated once we agree on the available settings. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28942 a1c6a512-1295-4272-9138-f99709370657 --- apps/lang/english.lang | 28 ++++++++++++++++ apps/menus/settings_menu.c | 10 +++++- apps/playback.c | 40 +++++++++++++++++++--- apps/settings.h | 1 + apps/settings_list.c | 3 ++ apps/tagcache.c | 10 +++++- apps/tagcache.h | 5 +-- apps/tagtree.c | 84 ++++++++++++++++++++++++++++++++++------------ 8 files changed, 150 insertions(+), 31 deletions(-) (limited to 'apps') diff --git a/apps/lang/english.lang b/apps/lang/english.lang index b2e54da8e9..0fad5bf5b0 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -12685,3 +12685,31 @@ *: "Filesize" + + id: LANG_AUTORESUME_ENABLE + desc: resume settings menu + user: core + + *: "Enable automatic resume" + + + *: "Enable automatic resume" + + + *: "Enable automatic resume" + + + + id: LANG_AUTORESUME_ENABLE_YES + desc: resume settings menu + user: core + + *: "Yes (requires initialized database)" + + + *: "Yes (requires initialized database)" + + + *: "Yes (requires initialized database)" + + diff --git a/apps/menus/settings_menu.c b/apps/menus/settings_menu.c index 5de3a305f9..e609a4032e 100644 --- a/apps/menus/settings_menu.c +++ b/apps/menus/settings_menu.c @@ -424,6 +424,10 @@ MAKE_MENU(hotkey_menu, ID2P(LANG_HOTKEY), 0, Icon_NOICON, /***********************************/ /* SETTINGS MENU */ +#ifdef HAVE_TAGCACHE +MENUITEM_SETTING(autoresume_enable, &global_settings.autoresume_enable, NULL); +#endif + static struct browse_folder_info langs = { LANG_DIR, SHOW_LNG }; MENUITEM_FUNCTION(browse_langs, MENU_FUNC_USEPARAM, ID2P(LANG_LANGUAGE), @@ -436,7 +440,11 @@ MAKE_MENU(settings_menu_item, ID2P(LANG_GENERAL_SETTINGS), 0, &tagcache_menu, #endif &display_menu, &system_menu, - &bookmark_settings_menu, &browse_langs, &voice_settings_menu, + &bookmark_settings_menu, +#ifdef HAVE_TAGCACHE + &autoresume_enable, +#endif + &browse_langs, &voice_settings_menu, #ifdef HAVE_HOTKEY &hotkey_menu, #endif diff --git a/apps/playback.c b/apps/playback.c index b18ba14e9f..e71e06b92c 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -992,13 +992,27 @@ long audio_filebufused(void) /* Update track info after successful a codec track change */ static void audio_update_trackinfo(void) { + bool resume = false; + /* Load the curent track's metadata into curtrack_id3 */ if (CUR_TI->id3_hid >= 0) copy_mp3entry(thistrack_id3, bufgetid3(CUR_TI->id3_hid)); /* Reset current position */ thistrack_id3->elapsed = 0; - thistrack_id3->offset = 0; + +#ifdef HAVE_TAGCACHE + /* Resume all manually selected tracks if so configured */ + resume = global_settings.autoresume_enable && !automatic_skip; +#endif + + if (!resume) + { + thistrack_id3->offset = 0; + } + + logf("audio_update_trackinfo: Set offset for %s to %lX\n", + thistrack_id3->title, thistrack_id3->offset); /* Update the codec API */ ci.filesize = CUR_TI->filesize; @@ -1210,6 +1224,9 @@ static bool audio_load_track(size_t offset, bool start_play) { copy_mp3entry(thistrack_id3, id3); thistrack_id3->offset = offset; + logf("audio_load_track: set offset for %s to %lX\n", + thistrack_id3->title, + offset); } else memset(thistrack_id3, 0, sizeof(struct mp3entry)); @@ -1415,8 +1432,14 @@ static void audio_finish_load_track(void) return; } - /* All required data is now available for the codec. */ - tracks[track_widx].taginfo_ready = true; + /* All required data is now available for the codec -- unless the + autoresume feature is in effect. In the latter case, the codec + must wait until after PLAYBACK_EVENT_TRACK_BUFFER, which may + generate a resume position. */ +#ifdef HAVE_TAGCACHE + if (! global_settings.autoresume_enable) +#endif + tracks[track_widx].taginfo_ready = true; if (start_play) { @@ -1424,10 +1447,17 @@ static void audio_finish_load_track(void) buf_request_buffer_handle(tracks[track_widx].audio_hid); } - track_widx = (track_widx + 1) & MAX_TRACK_MASK; - send_event(PLAYBACK_EVENT_TRACK_BUFFER, track_id3); +#ifdef HAVE_TAGCACHE + /* In case the autoresume feature has been enabled, finally all + required data is available for the codec. */ + if (global_settings.autoresume_enable) + tracks[track_widx].taginfo_ready = true; +#endif + + track_widx = (track_widx + 1) & MAX_TRACK_MASK; + /* load next track */ LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER"); queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); diff --git a/apps/settings.h b/apps/settings.h index fac2c9634f..4c2875392b 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -576,6 +576,7 @@ struct user_settings bool tagcache_ram; /* load tagcache to ram? */ #endif bool tagcache_autoupdate; /* automatically keep tagcache in sync? */ + bool autoresume_enable; /* enable autoupdate feature? */ bool runtimedb; /* runtime database active? */ #endif /* HAVE_TAGCACHE */ diff --git a/apps/settings_list.c b/apps/settings_list.c index 80000ab23f..3da5f1db62 100644 --- a/apps/settings_list.c +++ b/apps/settings_list.c @@ -1257,6 +1257,9 @@ const struct settings_list settings[] = { ID2P(LANG_RANDOM)), #ifdef HAVE_TAGCACHE + BOOL_SETTING(0, autoresume_enable, LANG_AUTORESUME_ENABLE, false, + "autoresume enable", off_on, + LANG_AUTORESUME_ENABLE_YES, LANG_SET_BOOL_NO, NULL), OFFON_SETTING(0, runtimedb, LANG_RUNTIMEDB_ACTIVE, false, "gather runtime data", NULL), #endif diff --git a/apps/tagcache.c b/apps/tagcache.c index 0831bab32d..1d90eee24b 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -195,7 +195,7 @@ static const char *tagfile_entry_ec = "ll"; /** Note: This should be (1 + TAG_COUNT) amount of l's. */ -static const char *index_entry_ec = "lllllllllllllllllllll"; +static const char *index_entry_ec = "llllllllllllllllllllll"; static const char *tagcache_header_ec = "lll"; static const char *master_header_ec = "llllll"; @@ -1695,6 +1695,13 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename) if (id3->bitrate == 0) id3->bitrate = 1; + if (global_settings.autoresume_enable) + { + id3->offset = get_tag_numeric(entry, tag_lastoffset, idx_id); + logf("tagcache_fill_tags: Set offset for %s to %lX\n", + id3->title, id3->offset); + } + return true; } #endif @@ -2315,6 +2322,7 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd) tmpdb_copy_tag(tag_playtime); tmpdb_copy_tag(tag_lastplayed); tmpdb_copy_tag(tag_commitid); + tmpdb_copy_tag(tag_lastoffset); /* Avoid processing this entry again. */ idx.flag |= FLAG_RESURRECTED; diff --git a/apps/tagcache.h b/apps/tagcache.h index b52da76a0f..4fffccae2f 100644 --- a/apps/tagcache.h +++ b/apps/tagcache.h @@ -32,7 +32,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, tag_filename, tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_year, tag_discnumber, tag_tracknumber, tag_bitrate, tag_length, tag_playcount, tag_rating, - tag_playtime, tag_lastplayed, tag_commitid, tag_mtime, + tag_playtime, tag_lastplayed, tag_commitid, tag_mtime, tag_lastoffset, /* Real tags end here, count them. */ TAG_COUNT, /* Virtual tags */ @@ -50,7 +50,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, #define IDX_BUF_DEPTH 64 /* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ -#define TAGCACHE_MAGIC 0x5443480d +#define TAGCACHE_MAGIC 0x5443480e /* How much to allocate extra space for ramcache. */ #define TAGCACHE_RESERVE 32768 @@ -103,6 +103,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, (1LU << tag_tracknumber) | (1LU << tag_length) | (1LU << tag_bitrate) | \ (1LU << tag_playcount) | (1LU << tag_rating) | (1LU << tag_playtime) | \ (1LU << tag_lastplayed) | (1LU << tag_commitid) | (1LU << tag_mtime) | \ + (1LU << tag_lastoffset) | \ (1LU << tag_virt_length_min) | (1LU << tag_virt_length_sec) | \ (1LU << tag_virt_playtime_min) | (1LU << tag_virt_playtime_sec) | \ (1LU << tag_virt_entryage) | (1LU << tag_virt_autoscore)) diff --git a/apps/tagtree.c b/apps/tagtree.c index 75caab01d4..8ebac0b3a5 100644 --- a/apps/tagtree.c +++ b/apps/tagtree.c @@ -53,6 +53,8 @@ #include "storage.h" #include "dir.h" +#define str_or_empty(x) (x ? x : "(NULL)") + #define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config" static int tagtree_play_folder(struct tree_context* c); @@ -168,6 +170,8 @@ static int current_entry_count; static struct tree_context *tc; +extern bool automatic_skip; /* Who initiated in-progress skip? (C/A-) */ + static int get_token_str(char *buf, int size) { /* Find the start. */ @@ -239,6 +243,7 @@ static int get_tag(int *tag) MATCH(tag, buf, "playcount", tag_playcount); MATCH(tag, buf, "rating", tag_rating); MATCH(tag, buf, "lastplayed", tag_lastplayed); + MATCH(tag, buf, "lastoffset", tag_lastoffset); MATCH(tag, buf, "commitid", tag_commitid); MATCH(tag, buf, "entryage", tag_virt_entryage); MATCH(tag, buf, "autoscore", tag_virt_autoscore); @@ -647,7 +652,7 @@ static void tagtree_buffer_event(void *data) struct mp3entry *id3 = (struct mp3entry*)data; /* Do not gather data unless proper setting has been enabled. */ - if (!global_settings.runtimedb) + if (!global_settings.runtimedb && !global_settings.autoresume_enable) return; logf("be:%s", id3->path); @@ -661,12 +666,30 @@ static void tagtree_buffer_event(void *data) return; } - id3->playcount = tagcache_get_numeric(&tcs, tag_playcount); - if (!id3->rating) - id3->rating = tagcache_get_numeric(&tcs, tag_rating); - id3->lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed); - id3->score = tagcache_get_numeric(&tcs, tag_virt_autoscore) / 10; - id3->playtime = tagcache_get_numeric(&tcs, tag_playtime); + if (global_settings.runtimedb) + { + id3->playcount = tagcache_get_numeric(&tcs, tag_playcount); + if (!id3->rating) + id3->rating = tagcache_get_numeric(&tcs, tag_rating); + id3->lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed); + id3->score = tagcache_get_numeric(&tcs, tag_virt_autoscore) / 10; + id3->playtime = tagcache_get_numeric(&tcs, tag_playtime); + + logf("-> %ld/%ld", id3->playcount, id3->playtime); + } + + if (global_settings.autoresume_enable) + { + /* Load current file resume offset if not already defined (by + another resume mechanism) */ + if (id3->offset == 0) + { + id3->offset = tagcache_get_numeric(&tcs, tag_lastoffset); + + logf("tagtree_buffer_event: Set offset for %s to %lX\n", + str_or_empty(id3->title), id3->offset); + } + } /* Store our tagcache index pointer. */ id3->tagcache_idx = tcs.idx_id+1; @@ -676,16 +699,14 @@ static void tagtree_buffer_event(void *data) static void tagtree_track_finish_event(void *data) { - long playcount; - long playtime; long lastplayed; long tagcache_idx; struct mp3entry *id3 = (struct mp3entry*)data; /* Do not gather data unless proper setting has been enabled. */ - if (!global_settings.runtimedb) + if (!global_settings.runtimedb && !global_settings.autoresume_enable) { - logf("runtimedb gathering not enabled"); + logf("runtimedb gathering and autoresume not enabled"); return; } @@ -704,7 +725,6 @@ static void tagtree_track_finish_event(void *data) return; } - playcount = id3->playcount + 1; lastplayed = tagcache_increase_serial(); if (lastplayed < 0) { @@ -712,17 +732,37 @@ static void tagtree_track_finish_event(void *data) return; } - /* Ignore the last 15s (crossfade etc.) */ - playtime = id3->playtime + MIN(id3->length, id3->elapsed + 15 * 1000); - - logf("ube:%s", id3->path); - logf("-> %ld/%ld", playcount, playtime); - logf("-> %ld/%ld/%ld", id3->elapsed, id3->length, MIN(id3->length, id3->elapsed + 15 * 1000)); + if (global_settings.runtimedb) + { + long playcount; + long playtime; + + playcount = id3->playcount + 1; + + /* Ignore the last 15s (crossfade etc.) */ + playtime = id3->playtime + MIN(id3->length, id3->elapsed + 15 * 1000); + + logf("ube:%s", id3->path); + logf("-> %ld/%ld", playcount, playtime); + logf("-> %ld/%ld/%ld", id3->elapsed, id3->length, + MIN(id3->length, id3->elapsed + 15 * 1000)); - /* Queue the updates to the tagcache system. */ - tagcache_update_numeric(tagcache_idx, tag_playcount, playcount); - tagcache_update_numeric(tagcache_idx, tag_playtime, playtime); - tagcache_update_numeric(tagcache_idx, tag_lastplayed, lastplayed); + /* Queue the updates to the tagcache system. */ + tagcache_update_numeric(tagcache_idx, tag_playcount, playcount); + tagcache_update_numeric(tagcache_idx, tag_playtime, playtime); + tagcache_update_numeric(tagcache_idx, tag_lastplayed, lastplayed); + } + + if (global_settings.autoresume_enable) + { + unsigned long offset + = automatic_skip ? 0 : id3->offset; + + tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset); + + logf("tagtree_track_finish_event: Save offset for %s: %lX", + str_or_empty(id3->title), offset); + } } bool tagtree_export(void) -- cgit