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
This commit is contained in:
Michael Giacomelli 2011-01-02 02:49:13 +00:00
parent 4048cf27a2
commit 66e8fc0f0d
8 changed files with 150 additions and 31 deletions

View file

@ -12685,3 +12685,31 @@
*: "Filesize"
</voice>
</phrase>
<phrase>
id: LANG_AUTORESUME_ENABLE
desc: resume settings menu
user: core
<source>
*: "Enable automatic resume"
</source>
<dest>
*: "Enable automatic resume"
</dest>
<voice>
*: "Enable automatic resume"
</voice>
</phrase>
<phrase>
id: LANG_AUTORESUME_ENABLE_YES
desc: resume settings menu
user: core
<source>
*: "Yes (requires initialized database)"
</source>
<dest>
*: "Yes (requires initialized database)"
</dest>
<voice>
*: "Yes (requires initialized database)"
</voice>
</phrase>

View file

@ -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

View file

@ -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);

View file

@ -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 */

View file

@ -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

View file

@ -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;

View file

@ -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))

View file

@ -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);
if (global_settings.runtimedb)
{
long playcount;
long playtime;
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));
playcount = id3->playcount + 1;
/* 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);
/* 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);
}
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)