diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 0635f7b569..ada1129a43 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12614,16 +12614,16 @@
id: LANG_PLAYTIME_REMAINING
- desc: playing time screen
+ desc: deprecated
user: core
- *: "Playlist remaining:"
+ *: ""
- *: "Playlist remaining:"
+ *: ""
- *: "Playlist remaining"
+ *: ""
@@ -16500,3 +16500,17 @@
*: "Choose File"
+
+ id: LANG_REMAINING
+ desc: Playing Time
+ user: core
+
+ *: "Remaining"
+
+
+ *: "Remaining"
+
+
+ *: "Remaining"
+
+
diff --git a/apps/plugins/playing_time.c b/apps/plugins/playing_time.c
index 5916e4e58f..54752e987a 100644
--- a/apps/plugins/playing_time.c
+++ b/apps/plugins/playing_time.c
@@ -32,10 +32,10 @@ const unsigned char * const byte_units[] =
};
const int menu_items[] = {
- LANG_PLAYTIME_ELAPSED,
- LANG_PLAYTIME_REMAINING,
- LANG_PLAYTIME_TRK_ELAPSED,
+ LANG_REMAINING,
+ LANG_ELAPSED,
LANG_PLAYTIME_TRK_REMAINING,
+ LANG_PLAYTIME_TRK_ELAPSED,
LANG_PLAYTIME_TRACK,
LANG_PLAYTIME_STORAGE,
LANG_PLAYTIME_AVG_TRACK_SIZE,
@@ -54,14 +54,61 @@ enum ePT_SUM {
};
struct playing_time_info {
- int nb_tracks; /* number of tracks in playlist */
- int curr_track_index; /* index of currently playing track in playlist */
- int curr_display_index; /* display index of currently playing track */
- unsigned long curr_track_length[ePT_COUNT]; /* current track length */
- unsigned long long length[ePT_COUNT]; /* length of all tracks */
- unsigned long long size[ePT_COUNT]; /* file size of all tracks */
+ char single_mode_tag[MAX_PATH]; /* Relevant tag when single mode enabled */
+ unsigned long long size[ePT_COUNT]; /* File size of tracks */
+ unsigned long long length[ePT_COUNT]; /* Length of tracks */
+ unsigned long curr_track_length[ePT_COUNT]; /* Current track length */
+ int curr_track_index; /* Index of currently playing track in playlist */
+ int curr_display_index; /* Display index of currently playing track */
+ int actual_index; /* Display index in actually counted tracks */
+ int counted; /* Number of tracks already added up */
+ int nb_tracks; /* Number of tracks in playlist */
+ int error_count; /* Number of tracks whose data couldn't be retrieved */
+ bool remaining_only; /* Whether to ignore elapsed tracks */
};
+static int32_t single_mode_lang(void)
+{
+ switch (rb->global_settings->single_mode)
+ {
+ case SINGLE_MODE_ALBUM:
+ return LANG_ID3_ALBUM;
+ case SINGLE_MODE_ALBUM_ARTIST:
+ return LANG_ID3_ALBUMARTIST;
+ case SINGLE_MODE_ARTIST:
+ return LANG_ID3_ARTIST;
+ case SINGLE_MODE_COMPOSER:
+ return LANG_ID3_COMPOSER;
+ case SINGLE_MODE_GROUPING:
+ return LANG_ID3_GROUPING;
+ case SINGLE_MODE_GENRE:
+ return LANG_ID3_GENRE;
+ case SINGLE_MODE_TRACK:
+ return LANG_TRACK;
+ }
+ return LANG_OFF;
+}
+
+static char* single_mode_id3_tag(struct mp3entry *id3)
+{
+ switch (rb->global_settings->single_mode)
+ {
+ case SINGLE_MODE_ALBUM:
+ return id3->album;
+ case SINGLE_MODE_ALBUM_ARTIST:
+ return id3->albumartist;
+ case SINGLE_MODE_ARTIST:
+ return id3->artist;
+ case SINGLE_MODE_COMPOSER:
+ return id3->composer;
+ case SINGLE_MODE_GROUPING:
+ return id3->grouping;
+ case SINGLE_MODE_GENRE:
+ return id3->genre_string;
+ }
+ return NULL;
+}
+
static char* get_percent_str(long percents)
{
static char val[10];
@@ -97,7 +144,18 @@ static const char * pt_get_or_speak_info(int selected_item, void * data,
/* data */
switch(info_no) {
- case 0: { /* elapsed and total time */
+ case 0: { /* playlist remaining time */
+ char timestr[25];
+ rb->format_time_auto(timestr, sizeof(timestr),
+ pti->length[ePT_REMAINING], UNIT_SEC, false);
+ rb->snprintf(buf, buffer_len, "%s", timestr);
+
+ if (say_it)
+ rb_talk_ids(false, menu_name_id,
+ TALK_ID(pti->length[ePT_REMAINING], UNIT_TIME));
+ break;
+ }
+ case 1: { /* elapsed and total time */
char timestr1[25], timestr2[25];
rb->format_time_auto(timestr1, sizeof(timestr1),
pti->length[ePT_ELAPSED], UNIT_SEC, true);
@@ -128,18 +186,18 @@ static const char * pt_get_or_speak_info(int selected_item, void * data,
TALK_ID(elapsed_pct, UNIT_PERCENT));
break;
}
- case 1: { /* playlist remaining time */
+ case 2: { /* track remaining time */
char timestr[25];
rb->format_time_auto(timestr, sizeof(timestr),
- pti->length[ePT_REMAINING], UNIT_SEC, false);
+ pti->curr_track_length[ePT_REMAINING], UNIT_SEC, false);
rb->snprintf(buf, buffer_len, "%s", timestr);
if (say_it)
rb_talk_ids(false, menu_name_id,
- TALK_ID(pti->length[ePT_REMAINING], UNIT_TIME));
+ TALK_ID(pti->curr_track_length[ePT_REMAINING], UNIT_TIME));
break;
}
- case 2: { /* track elapsed and duration */
+ case 3: { /* track elapsed and duration */
char timestr1[25], timestr2[25];
rb->format_time_auto(timestr1, sizeof(timestr1),
@@ -170,32 +228,21 @@ static const char * pt_get_or_speak_info(int selected_item, void * data,
TALK_ID(elapsed_pct, UNIT_PERCENT));
break;
}
- case 3: { /* track remaining time */
- char timestr[25];
- rb->format_time_auto(timestr, sizeof(timestr),
- pti->curr_track_length[ePT_REMAINING], UNIT_SEC, false);
- rb->snprintf(buf, buffer_len, "%s", timestr);
-
- if (say_it)
- rb_talk_ids(false, menu_name_id,
- TALK_ID(pti->curr_track_length[ePT_REMAINING], UNIT_TIME));
- break;
- }
case 4: { /* track index */
- int track_pct = pti->curr_display_index * 100 / pti->nb_tracks;
+ int track_pct = pti->actual_index * 100 / pti->counted;
if (rb->lang_is_rtl())
rb->snprintf(buf, buffer_len, "%s %d / %d", get_percent_str(track_pct),
- pti->nb_tracks, pti->curr_display_index);
+ pti->counted, pti->actual_index);
else
- rb->snprintf(buf, buffer_len, "%d / %d %s", pti->curr_display_index,
- pti->nb_tracks, get_percent_str(track_pct));
+ rb->snprintf(buf, buffer_len, "%d / %d %s", pti->actual_index,
+ pti->counted, get_percent_str(track_pct));
if (say_it)
rb_talk_ids(false, menu_name_id,
- TALK_ID(pti->curr_display_index, UNIT_INT),
+ TALK_ID(pti->actual_index, UNIT_INT),
VOICE_OF,
- TALK_ID(pti->nb_tracks, UNIT_INT),
+ TALK_ID(pti->counted, UNIT_INT),
VOICE_PAUSE,
TALK_ID(track_pct, UNIT_PERCENT));
break;
@@ -215,7 +262,7 @@ static const char * pt_get_or_speak_info(int selected_item, void * data,
int32_t voice_ids[ePT_COUNT];
voice_ids[ePT_TOTAL] = menu_name_id;
voice_ids[ePT_ELAPSED] = VOICE_PLAYTIME_DONE;
- voice_ids[ePT_REMAINING] = LANG_PLAYTIME_REMAINING;
+ voice_ids[ePT_REMAINING] = LANG_REMAINING;
for (i = 0; i < ePT_COUNT; i++)
{
@@ -227,7 +274,7 @@ static const char * pt_get_or_speak_info(int selected_item, void * data,
}
case 6: { /* Average track file size */
char str[20];
- long avg_track_size = pti->size[ePT_TOTAL] / pti->nb_tracks;
+ long avg_track_size = pti->size[ePT_TOTAL] / pti->counted;
rb->output_dyn_value(str, sizeof(str), avg_track_size, kibyte_units, 3, true);
rb->snprintf(buf, buffer_len, "%s", str);
@@ -266,116 +313,21 @@ static int pt_speak_info(int selected_item, void * data)
return 0;
}
-/* playing time screen: shows total and elapsed playlist duration and
- other stats */
-static bool playing_time(void)
+static bool pt_display_stats(struct playing_time_info *pti)
{
- struct playing_time_info pti;
- struct playlist_track_info pl_track;
- struct mp3entry id3;
- struct mp3entry *curr_id3;
struct gui_synclist pt_lists;
- unsigned long talked_tick = *rb->current_tick;
- int action, i, index, section, error_count = 0;
-
- pti.nb_tracks = rb->playlist_amount();
- rb->playlist_get_resume_info(&pti.curr_track_index);
- curr_id3 = rb->audio_current_track();
-
- if (pti.curr_track_index == -1 || !curr_id3)
- return false;
-
- pti.curr_display_index = rb->playlist_get_display_index();
-
- pti.length[ePT_ELAPSED] = pti.curr_track_length[ePT_ELAPSED]
- = curr_id3->elapsed;
- pti.length[ePT_REMAINING] = pti.curr_track_length[ePT_REMAINING]
- = curr_id3->length - curr_id3->elapsed;
-
- pti.size[ePT_ELAPSED] = curr_id3->offset;
- pti.size[ePT_REMAINING] = curr_id3->filesize - curr_id3->offset;
-
- rb->splash_progress_set_delay(HZ/2);
-
-#ifdef HAVE_ADJUSTABLE_CPU_FREQ
- rb->cpu_boost(true);
-#endif
- /* Go through each file in the playlist and get its stats. For
- huge playlists this can take a while... The reference position
- is the position at the moment this function was invoked,
- although playback continues forward. */
- index = rb->playlist_get_first_index(NULL);
- for (i = 0; i < pti.nb_tracks; i++, index++) {
-
- if (index == pti.nb_tracks)
- index = 0;
-
- rb->splash_progress(i, pti.nb_tracks, "%s (%s)",
- rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
-
- if (TIME_AFTER(*rb->current_tick, talked_tick + HZ*5))
- {
- talked_tick = *rb->current_tick;
- rb_talk_ids(false, LANG_LOADING_PERCENT,
- TALK_ID(i * 100 / pti.nb_tracks, UNIT_PERCENT));
- }
- if (rb->action_userabort(TIMEOUT_NOBLOCK))
- {
-#ifdef HAVE_ADJUSTABLE_CPU_FREQ
- rb->cpu_boost(false);
-#endif
- goto exit;
- }
-
- if (index == pti.curr_track_index)
- continue;
-
- if (rb->playlist_get_track_info(NULL, index, &pl_track) < 0
- || !rb->get_metadata(&id3, -1, pl_track.filename))
- {
- error_count++;
- continue;
- }
-
- section = pl_track.display_index < pti.curr_display_index ?
- ePT_ELAPSED : ePT_REMAINING;
- pti.length[section] += id3.length;
- pti.size[section] += id3.filesize;
- }
-#ifdef HAVE_ADJUSTABLE_CPU_FREQ
- rb->cpu_boost(false);
-#endif
-
- if (error_count > 0)
- rb->splash(HZ, ID2P(LANG_PLAYTIME_ERROR));
-
- pti.nb_tracks -= error_count;
-
- /* convert units from ms to s */
- pti.length[ePT_ELAPSED] /= 1000;
- pti.length[ePT_REMAINING] /= 1000;
- pti.curr_track_length[ePT_ELAPSED] /= 1000;
- pti.curr_track_length[ePT_REMAINING] /= 1000;
- /* convert units from Bytes to KiB */
- pti.size[ePT_ELAPSED] >>= 10;
- pti.size[ePT_REMAINING] >>= 10;
-
- /* calculate totals */
- pti.length[ePT_TOTAL] = pti.length[ePT_ELAPSED] + pti.length[ePT_REMAINING];
- pti.curr_track_length[ePT_TOTAL] = pti.curr_track_length[ePT_ELAPSED]
- + pti.curr_track_length[ePT_REMAINING];
- pti.size[ePT_TOTAL] = pti.size[ePT_ELAPSED] + pti.size[ePT_REMAINING];
-
- rb->gui_synclist_init(&pt_lists, &pt_get_info, &pti, true, 2, NULL);
+ rb->gui_synclist_init(&pt_lists, &pt_get_info, pti, true, 2, NULL);
if (rb->global_settings->talk_menu)
rb->gui_synclist_set_voice_callback(&pt_lists, pt_speak_info);
- rb->gui_synclist_set_nb_items(&pt_lists, 16);
- rb->gui_synclist_set_title(&pt_lists, rb->str(LANG_PLAYING_TIME), NOICON);
+ rb->gui_synclist_set_nb_items(&pt_lists, pti->remaining_only ? 2 : 8*2);
+ rb->gui_synclist_set_title(&pt_lists, *pti->single_mode_tag ?
+ rb->str(single_mode_lang()) :
+ rb->str(LANG_PLAYLIST), NOICON);
rb->gui_synclist_draw(&pt_lists);
rb->gui_synclist_speak_item(&pt_lists);
while (true)
{
- action = rb->get_action(CONTEXT_LIST, HZ/2);
+ int action = rb->get_action(CONTEXT_LIST, HZ/2);
if (rb->gui_synclist_do_button(&pt_lists, &action) == 0
&& action != ACTION_NONE && action != ACTION_UNKNOWN)
{
@@ -388,16 +340,213 @@ static bool playing_time(void)
return usb;
}
}
- exit:
return false;
}
+static const char *pt_options_name(int selected_item, void * data,
+ char *buf, size_t buf_size)
+{
+ (void) data;
+ (void) buf;
+ (void) buf_size;
+ return selected_item == 0 ? rb->str(LANG_ALL) :
+ selected_item == 1 ? rb->str(LANG_REMAINING) :
+ rb->str(single_mode_lang());
+}
+
+static int pt_options_speak(int selected_item, void * data)
+{
+ (void) data;
+ rb->talk_id(selected_item == 0 ? LANG_ALL :
+ selected_item == 1 ? LANG_REMAINING :
+ single_mode_lang(), false);
+ return 0;
+}
+
+static int pt_options(struct playing_time_info *pti)
+{
+ struct gui_synclist pt_options;
+ rb->gui_synclist_init(&pt_options, &pt_options_name, NULL, true, 1, NULL);
+ if (rb->global_settings->talk_menu)
+ rb->gui_synclist_set_voice_callback(&pt_options, pt_options_speak);
+ rb->gui_synclist_set_nb_items(&pt_options, *pti->single_mode_tag ? 3 : 2);
+ rb->gui_synclist_set_title(&pt_options, rb->str(LANG_PLAYING_TIME), NOICON);
+ rb->gui_synclist_draw(&pt_options);
+ rb->gui_synclist_speak_item(&pt_options);
+
+ while(true)
+ {
+ int button = rb->get_action(CONTEXT_LIST, HZ);
+ if (rb->gui_synclist_do_button(&pt_options, &button))
+ continue;
+ switch(button)
+ {
+ case ACTION_STD_OK:
+ {
+ int sel = rb->gui_synclist_get_sel_pos(&pt_options);
+ if (sel < 2)
+ *pti->single_mode_tag = 0;
+ if (sel == 1)
+ pti->remaining_only = true;
+ return -1;
+ }
+ case ACTION_STD_CANCEL:
+ return 0;
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ return 1;
+ }
+ }
+}
+
+static void pt_store_converted_totals(struct playing_time_info *pti)
+{
+ /* convert units from ms to s */
+ pti->length[ePT_ELAPSED] /= 1000;
+ pti->length[ePT_REMAINING] /= 1000;
+ pti->curr_track_length[ePT_ELAPSED] /= 1000;
+ pti->curr_track_length[ePT_REMAINING] /= 1000;
+ /* convert units from Bytes to KiB */
+ pti->size[ePT_ELAPSED] >>= 10;
+ pti->size[ePT_REMAINING] >>= 10;
+
+ pti->length[ePT_TOTAL] = pti->length[ePT_ELAPSED] + pti->length[ePT_REMAINING];
+ pti->curr_track_length[ePT_TOTAL] = pti->curr_track_length[ePT_ELAPSED]
+ + pti->curr_track_length[ePT_REMAINING];
+ pti->size[ePT_TOTAL] = pti->size[ePT_ELAPSED] + pti->size[ePT_REMAINING];
+}
+
+static int pt_add_track(int i, enum ePT_SUM section, struct playing_time_info *pti)
+{
+ struct mp3entry id3;
+ struct playlist_track_info pl_track;
+ static unsigned long talked_tick;
+ int progress_total = pti->remaining_only ?
+ pti->nb_tracks - pti->curr_display_index :
+ pti->nb_tracks;
+
+ rb->splash_progress(pti->counted, progress_total, "%s (%s)",
+ rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
+
+ if (TIME_AFTER(*rb->current_tick, talked_tick + HZ*5))
+ {
+ talked_tick = *rb->current_tick;
+ rb_talk_ids(false, LANG_LOADING_PERCENT,
+ TALK_ID(pti->counted * 100 / progress_total,
+ UNIT_PERCENT));
+ }
+
+ if (rb->action_userabort(TIMEOUT_NOBLOCK))
+ return -1;
+ else if (rb->playlist_get_track_info(NULL, i, &pl_track) < 0
+ || !rb->get_metadata(&id3, -1, pl_track.filename))
+ {
+ pti->error_count++;
+ return -2;
+ }
+ else if(*pti->single_mode_tag && /* single mode tag doesn't match */
+ rb->strcmp(pti->single_mode_tag, single_mode_id3_tag(&id3) ?: ""))
+ return 1;
+
+ pti->length[section] += id3.length;
+ pti->size[section] += id3.filesize;
+ pti->counted++;
+ return 0;
+}
+
+static bool pt_add_remaining(struct playing_time_info *pti)
+{
+ int display_index = pti->curr_display_index + 1;
+ for (int i = pti->curr_track_index + 1; display_index <= pti->nb_tracks; i++, display_index++)
+ {
+ if (i == pti->nb_tracks)
+ i = 0;
+
+ int ret = pt_add_track(i, ePT_REMAINING, pti);
+ if (ret == 1)
+ break;
+ else if (ret == -1)
+ return false;
+ }
+ return true;
+}
+
+static bool pt_add_elapsed(struct playing_time_info *pti)
+{
+ int display_index = pti->curr_display_index - 1;
+ for (int i = pti->curr_track_index - 1; display_index > 0; i--, display_index--)
+ {
+ if (i < 0)
+ i = pti->nb_tracks - 1;
+
+ int ret = pt_add_track(i, ePT_ELAPSED, pti);
+ if (ret == 1)
+ break;
+ else if (ret == -1)
+ return false;
+ else if (ret == 0)
+ pti->actual_index++;
+ }
+ return true;
+}
+
+static bool pt_add_curr_track(struct playing_time_info *pti)
+{
+ struct mp3entry *curr_id3 = rb->audio_current_track();
+ rb->playlist_get_resume_info(&pti->curr_track_index);
+
+ if (pti->curr_track_index == -1 || !curr_id3)
+ return false;
+
+ pti->curr_display_index = rb->playlist_get_display_index();
+ pti->length[ePT_ELAPSED] = pti->curr_track_length[ePT_ELAPSED]
+ = curr_id3->elapsed;
+ pti->length[ePT_REMAINING] = pti->curr_track_length[ePT_REMAINING]
+ = curr_id3->length - curr_id3->elapsed;
+ pti->size[ePT_ELAPSED] = curr_id3->offset;
+ pti->size[ePT_REMAINING] = curr_id3->filesize - curr_id3->offset;
+ pti->actual_index = pti->counted = 1;
+ rb->strlcpy(pti->single_mode_tag, single_mode_id3_tag(curr_id3) ?: "",
+ sizeof(pti->single_mode_tag));
+ return true;
+}
+
+/* playing time screen: shows total and elapsed playlist duration and
+ other stats */
+static bool playing_time(void)
+{
+ struct playing_time_info pti = {0};
+
+ if (!pt_add_curr_track(&pti))
+ return false;
+
+ int opt = pt_options(&pti);
+ if (opt > -1)
+ return opt;
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ rb->cpu_boost(true);
+#endif
+ rb->splash_progress_set_delay(HZ/2);
+ pti.nb_tracks = rb->playlist_amount();
+ int success = (pti.remaining_only || pt_add_elapsed(&pti)) && pt_add_remaining(&pti);
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ rb->cpu_boost(false);
+#endif
+ if (!success)
+ return false;
+ if (pti.error_count > 0)
+ rb->splash(HZ, ID2P(LANG_PLAYTIME_ERROR));
+
+ pt_store_converted_totals(&pti);
+ return pt_display_stats(&pti);
+}
+
/* this is the plugin entry point */
enum plugin_status plugin_start(const void* parameter)
{
- enum plugin_status status = PLUGIN_OK;
-
(void)parameter;
+ enum plugin_status status = PLUGIN_OK;
if (!rb->audio_status())
{