Add option to resume next track on automatic track change
Move autoresume setting into its own menu. Add option to customize which tracks should be resumed on automatic track change. Tracks can be selected based on their their file location or genre tag (comma-separated list of filename / genre substrings). git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29251 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
4844142e16
commit
7141ff4513
7 changed files with 165 additions and 28 deletions
|
@ -12716,3 +12716,31 @@
|
||||||
*: "Automatic resume"
|
*: "Automatic resume"
|
||||||
</voice>
|
</voice>
|
||||||
</phrase>
|
</phrase>
|
||||||
|
<phrase>
|
||||||
|
id: LANG_AUTORESUME_AUTOMATIC
|
||||||
|
desc: resume on automatic track change
|
||||||
|
user: core
|
||||||
|
<source>
|
||||||
|
*: "Resume on automatic track change"
|
||||||
|
</source>
|
||||||
|
<dest>
|
||||||
|
*: "Resume on automatic track change"
|
||||||
|
</dest>
|
||||||
|
<voice>
|
||||||
|
*: "Resume on automatic track change"
|
||||||
|
</voice>
|
||||||
|
</phrase>
|
||||||
|
<phrase>
|
||||||
|
id: LANG_AUTORESUME_CUSTOM
|
||||||
|
desc: enable customization of resume on automatic track change
|
||||||
|
user: core
|
||||||
|
<source>
|
||||||
|
*: "Custom path/genre substrings (comma-separated)"
|
||||||
|
</source>
|
||||||
|
<dest>
|
||||||
|
*: "Custom path/genre substrings (comma-separated)"
|
||||||
|
</dest>
|
||||||
|
<voice>
|
||||||
|
*: "Custom path/genre substrings (comma-separated)"
|
||||||
|
</voice>
|
||||||
|
</phrase>
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "action.h"
|
#include "action.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
#include "keyboard.h"
|
||||||
#include "sound_menu.h"
|
#include "sound_menu.h"
|
||||||
#include "exported_menus.h"
|
#include "exported_menus.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
@ -367,6 +368,64 @@ MAKE_MENU(bookmark_settings_menu, ID2P(LANG_BOOKMARK_SETTINGS), 0,
|
||||||
/* BOOKMARK MENU */
|
/* BOOKMARK MENU */
|
||||||
/***********************************/
|
/***********************************/
|
||||||
|
|
||||||
|
/***********************************/
|
||||||
|
/* AUTORESUME MENU */
|
||||||
|
#ifdef HAVE_TAGCACHE
|
||||||
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
|
||||||
|
static int autoresume_callback(int action, const struct menu_item_ex *this_item)
|
||||||
|
{
|
||||||
|
(void)this_item;
|
||||||
|
|
||||||
|
if (action == ACTION_EXIT_MENUITEM /* on exit */
|
||||||
|
&& global_settings.autoresume_enable
|
||||||
|
&& !tagcache_is_usable())
|
||||||
|
{
|
||||||
|
static const char *lines[] = {ID2P(LANG_TAGCACHE_BUSY),
|
||||||
|
ID2P(LANG_TAGCACHE_FORCE_UPDATE)};
|
||||||
|
static const struct text_message message = {lines, 2};
|
||||||
|
|
||||||
|
if (gui_syncyesno_run(&message, NULL, NULL) == YESNO_YES)
|
||||||
|
tagcache_rebuild_with_splash();
|
||||||
|
}
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int autoresume_nexttrack_callback(int action,
|
||||||
|
const struct menu_item_ex *this_item)
|
||||||
|
{
|
||||||
|
(void)this_item;
|
||||||
|
static int oldval = 0;
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case ACTION_ENTER_MENUITEM:
|
||||||
|
oldval = global_settings.autoresume_automatic;
|
||||||
|
break;
|
||||||
|
case ACTION_EXIT_MENUITEM:
|
||||||
|
if (global_settings.autoresume_automatic == AUTORESUME_NEXTTRACK_CUSTOM
|
||||||
|
&& kbd_input ((char*) &global_settings.autoresume_strpat,
|
||||||
|
MAX_PATHNAME+1) < 0)
|
||||||
|
{
|
||||||
|
global_settings.autoresume_automatic = oldval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
MENUITEM_SETTING(autoresume_enable, &global_settings.autoresume_enable,
|
||||||
|
autoresume_callback);
|
||||||
|
MENUITEM_SETTING(autoresume_automatic, &global_settings.autoresume_automatic,
|
||||||
|
autoresume_nexttrack_callback);
|
||||||
|
|
||||||
|
MAKE_MENU(autoresume_menu, ID2P(LANG_AUTORESUME),
|
||||||
|
0, Icon_NOICON,
|
||||||
|
&autoresume_enable, &autoresume_automatic);
|
||||||
|
|
||||||
|
#endif /* CONFIG_CODEC == SWCODEC */
|
||||||
|
#endif /* HAVE_TAGCACHE */
|
||||||
|
/* AUTORESUME MENU */
|
||||||
|
/***********************************/
|
||||||
|
|
||||||
/***********************************/
|
/***********************************/
|
||||||
/* VOICE MENU */
|
/* VOICE MENU */
|
||||||
static int talk_callback(int action,const struct menu_item_ex *this_item);
|
static int talk_callback(int action,const struct menu_item_ex *this_item);
|
||||||
|
@ -425,31 +484,6 @@ MAKE_MENU(hotkey_menu, ID2P(LANG_HOTKEY), 0, Icon_NOICON,
|
||||||
/***********************************/
|
/***********************************/
|
||||||
/* SETTINGS MENU */
|
/* SETTINGS MENU */
|
||||||
|
|
||||||
#ifdef HAVE_TAGCACHE
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
|
||||||
static int autoresume_callback(int action, const struct menu_item_ex *this_item)
|
|
||||||
{
|
|
||||||
(void)this_item;
|
|
||||||
|
|
||||||
if (action == ACTION_EXIT_MENUITEM /* on exit */
|
|
||||||
&& global_settings.autoresume_enable
|
|
||||||
&& !tagcache_is_usable())
|
|
||||||
{
|
|
||||||
static const char *lines[] = {ID2P(LANG_TAGCACHE_BUSY),
|
|
||||||
ID2P(LANG_TAGCACHE_FORCE_UPDATE)};
|
|
||||||
static const struct text_message message = {lines, 2};
|
|
||||||
|
|
||||||
if (gui_syncyesno_run(&message, NULL, NULL) == YESNO_YES)
|
|
||||||
tagcache_rebuild_with_splash();
|
|
||||||
}
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
MENUITEM_SETTING(autoresume_enable, &global_settings.autoresume_enable,
|
|
||||||
autoresume_callback);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct browse_folder_info langs = { LANG_DIR, SHOW_LNG };
|
static struct browse_folder_info langs = { LANG_DIR, SHOW_LNG };
|
||||||
|
|
||||||
MENUITEM_FUNCTION(browse_langs, MENU_FUNC_USEPARAM, ID2P(LANG_LANGUAGE),
|
MENUITEM_FUNCTION(browse_langs, MENU_FUNC_USEPARAM, ID2P(LANG_LANGUAGE),
|
||||||
|
@ -465,7 +499,7 @@ MAKE_MENU(settings_menu_item, ID2P(LANG_GENERAL_SETTINGS), 0,
|
||||||
&bookmark_settings_menu,
|
&bookmark_settings_menu,
|
||||||
#ifdef HAVE_TAGCACHE
|
#ifdef HAVE_TAGCACHE
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
&autoresume_enable,
|
&autoresume_menu,
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
&browse_langs, &voice_settings_menu,
|
&browse_langs, &voice_settings_menu,
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "logf.h"
|
#include "logf.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "cuesheet.h"
|
#include "cuesheet.h"
|
||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
|
|
||||||
|
@ -425,3 +426,49 @@ void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig)
|
||||||
memcpy(dest, orig, sizeof(struct mp3entry));
|
memcpy(dest, orig, sizeof(struct mp3entry));
|
||||||
adjust_mp3entry(dest, dest, orig);
|
adjust_mp3entry(dest, dest, orig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_TAGCACHE
|
||||||
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
|
||||||
|
enum { AUTORESUMABLE_UNKNOWN = 0, AUTORESUMABLE_TRUE, AUTORESUMABLE_FALSE };
|
||||||
|
|
||||||
|
bool autoresumable(struct mp3entry *id3)
|
||||||
|
{
|
||||||
|
unsigned char search[MAX_PATHNAME+1];
|
||||||
|
char *saveptr, *substr;
|
||||||
|
bool is_resumable;
|
||||||
|
|
||||||
|
if (id3->autoresumable) /* result cached? */
|
||||||
|
return id3->autoresumable == AUTORESUMABLE_TRUE;
|
||||||
|
|
||||||
|
is_resumable = true;
|
||||||
|
|
||||||
|
strcpy(search, global_settings.autoresume_strpat);
|
||||||
|
|
||||||
|
for (substr = strtok_r(search, ",", &saveptr);
|
||||||
|
substr;
|
||||||
|
substr = strtok_r(NULL, ",", &saveptr))
|
||||||
|
{
|
||||||
|
if (id3->path && strcasestr(id3->path, substr))
|
||||||
|
goto out;
|
||||||
|
if (id3->genre_string && strcasestr(id3->genre_string, substr))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_resumable = false;
|
||||||
|
|
||||||
|
out:
|
||||||
|
/* cache result */
|
||||||
|
id3->autoresumable =
|
||||||
|
is_resumable ? AUTORESUMABLE_TRUE : AUTORESUMABLE_FALSE;
|
||||||
|
|
||||||
|
logf("autoresumable: %s with genre %s is%s resumable",
|
||||||
|
id3->path,
|
||||||
|
id3->genre_string ? id3->genre_string : "(NULL)",
|
||||||
|
is_resumable ? "" : " not");
|
||||||
|
|
||||||
|
return is_resumable;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* SWCODEC */
|
||||||
|
#endif /* HAVE_TAGCACHE */
|
||||||
|
|
|
@ -255,6 +255,8 @@ struct mp3entry {
|
||||||
int index; /* playlist index */
|
int index; /* playlist index */
|
||||||
|
|
||||||
#ifdef HAVE_TAGCACHE
|
#ifdef HAVE_TAGCACHE
|
||||||
|
unsigned char autoresumable; /* caches result of autoresumable() */
|
||||||
|
|
||||||
/* runtime database fields */
|
/* runtime database fields */
|
||||||
long tagcache_idx; /* 0=invalid, otherwise idx+1 */
|
long tagcache_idx; /* 0=invalid, otherwise idx+1 */
|
||||||
int rating;
|
int rating;
|
||||||
|
@ -287,10 +289,15 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname);
|
||||||
bool mp3info(struct mp3entry *entry, const char *filename);
|
bool mp3info(struct mp3entry *entry, const char *filename);
|
||||||
void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig);
|
void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig);
|
||||||
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig);
|
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig);
|
||||||
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
void strip_tags(int handle_id);
|
void strip_tags(int handle_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_TAGCACHE
|
||||||
|
bool autoresumable(struct mp3entry *id3);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1002,8 +1002,13 @@ static void audio_update_trackinfo(void)
|
||||||
thistrack_id3->elapsed = 0;
|
thistrack_id3->elapsed = 0;
|
||||||
|
|
||||||
#ifdef HAVE_TAGCACHE
|
#ifdef HAVE_TAGCACHE
|
||||||
/* Resume all manually selected tracks if so configured */
|
/* Ignoring resume position for automatic track change if so configured */
|
||||||
resume = global_settings.autoresume_enable && !automatic_skip;
|
resume = global_settings.autoresume_enable &&
|
||||||
|
(!automatic_skip /* Resume all manually selected tracks */
|
||||||
|
|| global_settings.autoresume_automatic == AUTORESUME_NEXTTRACK_ALWAYS
|
||||||
|
|| (global_settings.autoresume_automatic != AUTORESUME_NEXTTRACK_NEVER
|
||||||
|
/* Not never resume? */
|
||||||
|
&& autoresumable(thistrack_id3))); /* Pass Resume filter? */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!resume)
|
if (!resume)
|
||||||
|
|
|
@ -125,6 +125,10 @@ enum { SHOW_PATH_OFF = 0, SHOW_PATH_CURRENT, SHOW_PATH_FULL };
|
||||||
/* scrollbar visibility/position */
|
/* scrollbar visibility/position */
|
||||||
enum { SCROLLBAR_OFF = 0, SCROLLBAR_LEFT, SCROLLBAR_RIGHT };
|
enum { SCROLLBAR_OFF = 0, SCROLLBAR_LEFT, SCROLLBAR_RIGHT };
|
||||||
|
|
||||||
|
/* autoresume settings */
|
||||||
|
enum { AUTORESUME_NEXTTRACK_NEVER = 0, AUTORESUME_NEXTTRACK_ALWAYS,
|
||||||
|
AUTORESUME_NEXTTRACK_CUSTOM};
|
||||||
|
|
||||||
/* Alarm settings */
|
/* Alarm settings */
|
||||||
#ifdef HAVE_RTC_ALARM
|
#ifdef HAVE_RTC_ALARM
|
||||||
enum { ALARM_START_WPS = 0,
|
enum { ALARM_START_WPS = 0,
|
||||||
|
@ -577,6 +581,9 @@ struct user_settings
|
||||||
#endif
|
#endif
|
||||||
bool tagcache_autoupdate; /* automatically keep tagcache in sync? */
|
bool tagcache_autoupdate; /* automatically keep tagcache in sync? */
|
||||||
bool autoresume_enable; /* enable auto-resume feature? */
|
bool autoresume_enable; /* enable auto-resume feature? */
|
||||||
|
int autoresume_automatic; /* resume next track? 0=never, 1=always,
|
||||||
|
2=custom */
|
||||||
|
unsigned char autoresume_strpat[MAX_PATHNAME+1]; /* comma-separated list */
|
||||||
bool runtimedb; /* runtime database active? */
|
bool runtimedb; /* runtime database active? */
|
||||||
#endif /* HAVE_TAGCACHE */
|
#endif /* HAVE_TAGCACHE */
|
||||||
|
|
||||||
|
|
|
@ -1261,6 +1261,15 @@ const struct settings_list settings[] = {
|
||||||
BOOL_SETTING(0, autoresume_enable, LANG_AUTORESUME, false,
|
BOOL_SETTING(0, autoresume_enable, LANG_AUTORESUME, false,
|
||||||
"autoresume enable", off_on,
|
"autoresume enable", off_on,
|
||||||
LANG_SET_BOOL_YES, LANG_SET_BOOL_NO, NULL),
|
LANG_SET_BOOL_YES, LANG_SET_BOOL_NO, NULL),
|
||||||
|
CHOICE_SETTING(0, autoresume_automatic, LANG_AUTORESUME_AUTOMATIC,
|
||||||
|
AUTORESUME_NEXTTRACK_NEVER,
|
||||||
|
"autoresume next track", "never,all,custom",
|
||||||
|
NULL, 3,
|
||||||
|
ID2P(LANG_SET_BOOL_NO),
|
||||||
|
ID2P(LANG_ALWAYS),
|
||||||
|
ID2P(LANG_AUTORESUME_CUSTOM)),
|
||||||
|
TEXT_SETTING(0, autoresume_strpat, "autoresume next track patterns",
|
||||||
|
"podcast", NULL, NULL),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
OFFON_SETTING(0, runtimedb, LANG_RUNTIMEDB_ACTIVE, false,
|
OFFON_SETTING(0, runtimedb, LANG_RUNTIMEDB_ACTIVE, false,
|
||||||
|
|
Loading…
Reference in a new issue