Index: rockbox-devel/apps/lang/english.lang
===================================================================
--- rockbox-devel.orig/apps/lang/english.lang
+++ rockbox-devel/apps/lang/english.lang
@@ -9568,7 +9568,7 @@
*: "Loading... %d%% done (%s)"
- *: ""
+ *: "Loading"
@@ -11316,3 +11316,129 @@
*: ""
+
+ id: LANG_PLAYTIME_ELAPSED
+ desc: playing time screen
+ user:
+
+ *: "Elapsed: %s / %s %ld%%"
+
+
+ *: "Elapsed: %s / %s %ld%%"
+
+
+ *: "Elapsed"
+
+
+
+ id: LANG_PLAYTIME_REMAINING
+ desc: playing time screen
+ user:
+
+ *: "Remaining: %s"
+
+
+ *: "Remaining: %s"
+
+
+ *: "Remaining"
+
+
+
+ id: LANG_PLAYTIME_TRACK
+ desc: playing time screen
+ user:
+
+ *: "Track %d / %d %d%%"
+
+
+ *: "Track %d / %d %d%%"
+
+
+ *: "Track"
+
+
+
+ id: LANG_PLAYTIME_STORAGE
+ desc: playing time screen
+ user:
+
+ *: "Storage: %s (done %s, remaining %s)"
+
+
+ *: "Storage: %s (done %s, remaining %s)"
+
+
+ *: "Storage"
+
+
+
+ id: VOICE_PLAYTIME_DONE
+ desc: playing time screen
+ user:
+
+ *: ""
+
+
+ *: ""
+
+
+ *: "Done"
+
+
+
+ id: LANG_PLAYTIME_AVG_TRACK_SIZE
+ desc: playing time screen
+ user:
+
+ *: "Average track size: %s"
+
+
+ *: "Average track size: %s"
+
+
+ *: "Average track size"
+
+
+
+ id: LANG_PLAYTIME_AVG_BITRATE
+ desc: playing time screen
+ user:
+
+ *: "Average bitrate: %ld kbps"
+
+
+ *: "Average bitrate: %ld kbps"
+
+
+ *: "Average bit rate"
+
+
+
+ id: LANG_PLAYTIME_ERROR
+ desc: playing time screen
+ user:
+
+ *: "Error while gathering info"
+
+
+ *: "Error while gathering info"
+
+
+ *: "Error while gathering info"
+
+
+
+ id: LANG_PLAYING_TIME
+ desc: onplay menu
+ user:
+
+ *: "Playing time"
+
+
+ *: "Playing time"
+
+
+ *: "Playing time"
+
+
Index: rockbox-devel/apps/onplay.c
===================================================================
--- rockbox-devel.orig/apps/onplay.c
+++ rockbox-devel/apps/onplay.c
@@ -67,6 +67,7 @@
#ifdef HAVE_TAGCACHE
#include "tagtree.h"
#endif
+#include "metadata.h"
static int context;
static char* selected_file = NULL;
@@ -285,6 +286,257 @@ static bool cat_playlist_options(void)
return ret;
}
+/* Format time in seconds to string like 00:00:00. This version takes
+ seconds instead of ms so that a bigger max value fits the data
+ type. */
+void format_time_secs(char* buf, int buf_size, long time)
+{
+ if ( time < 3600 ) {
+ snprintf(buf, buf_size, "%d:%02d",
+ (int) (time / 60), (int) (time % 60));
+ } else {
+ snprintf(buf, buf_size, "%d:%02d:%02d",
+ (int) (time / 3600), (int) (time % 3600 / 60),
+ (int) (time % 60));
+ }
+}
+
+/* playing_time screen context */
+struct playing_time_info {
+ int curr_playing; /* index of currently playing track in playlist */
+ int nb_tracks; /* how many tracks in playlist */
+ /* seconds before and after current position, and total. Datatype
+ allows for values up to 68years. If I had kept it in ms
+ though, it would have overflowed at 24days, which takes
+ something like 8.5GB at 32kbps, and so we could conceivably
+ have playlists lasting longer than that. */
+ long secs_bef, secs_aft, secs_ttl;
+ /* kilobytes played before and after current pos, and total.
+ Kilobytes because bytes would overflow. Data type range is up
+ to 2TB. */
+ long kbs_bef, kbs_aft, kbs_ttl;
+};
+
+/* list callback for playing_time screen */
+char * playing_time_get_or_speak_info(int selected_item, void * data,
+ char *buf, bool say_it)
+{
+ struct playing_time_info *pti = (struct playing_time_info *)data;
+ const unsigned char *kbyte_units[] = {
+ ID2P(LANG_KILOBYTE),
+ ID2P(LANG_MEGABYTE),
+ ID2P(LANG_GIGABYTE)
+ };
+ switch(selected_item) {
+ case 0: { /* elapsed and total time */
+ char timestr1[15], timestr2[15];
+ format_time_secs(timestr1, sizeof(timestr1), pti->secs_bef);
+ format_time_secs(timestr2, sizeof(timestr2), pti->secs_ttl);
+ long elapsed_perc; /* percentage of duration elapsed */
+ if(pti->secs_ttl == 0)
+ elapsed_perc = 0;
+ else if(pti->secs_ttl <= 0xFFFFFF)
+ elapsed_perc = pti->secs_bef *100 / pti->secs_ttl;
+ else /* sacrifice some precision to avoid overflow */
+ elapsed_perc = (pti->secs_bef>>7) *100 /(pti->secs_ttl>>7);
+ snprintf(buf, MAX_PATH, str(LANG_PLAYTIME_ELAPSED),
+ timestr1, timestr2, elapsed_perc);
+ if(say_it)
+ talk_ids(false, LANG_PLAYTIME_ELAPSED,
+ TALK_ID(pti->secs_bef, UNIT_TIME),
+ VOICE_OF,
+ TALK_ID(pti->secs_ttl, UNIT_TIME),
+ VOICE_PAUSE,
+ TALK_ID(elapsed_perc, UNIT_PERCENT));
+ break;
+ }
+ case 1: { /* remaining time */
+ char timestr[15];
+ format_time_secs(timestr, sizeof(timestr), pti->secs_aft);
+ snprintf(buf, MAX_PATH, str(LANG_PLAYTIME_REMAINING),
+ timestr);
+ if(say_it)
+ talk_ids(false, LANG_PLAYTIME_REMAINING,
+ TALK_ID(pti->secs_aft, UNIT_TIME));
+ break;
+ }
+ case 2: { /* track index */
+ int track_perc = (pti->curr_playing+1) *100 / pti->nb_tracks;
+ snprintf(buf, MAX_PATH, str(LANG_PLAYTIME_TRACK),
+ pti->curr_playing, pti->nb_tracks, track_perc);
+ if(say_it)
+ talk_ids(false, LANG_PLAYTIME_TRACK,
+ TALK_ID(pti->curr_playing+1, UNIT_INT),
+ VOICE_OF,
+ TALK_ID(pti->nb_tracks, UNIT_INT),
+ VOICE_PAUSE,
+ TALK_ID(track_perc, UNIT_PERCENT));
+ break;
+ }
+ case 3: { /* storage size */
+ char str1[10], str2[10], str3[10];
+ output_dyn_value(str1, sizeof(str1), pti->kbs_ttl, kbyte_units, true);
+ output_dyn_value(str2, sizeof(str2), pti->kbs_bef, kbyte_units, true);
+ output_dyn_value(str3, sizeof(str3), pti->kbs_aft, kbyte_units, true);
+ snprintf(buf, MAX_PATH, str(LANG_PLAYTIME_STORAGE),
+ str1,str2,str3);
+ if(say_it) {
+ talk_id(LANG_PLAYTIME_STORAGE, false);
+ output_dyn_value(NULL, 0, pti->kbs_ttl, kbyte_units, true);
+ talk_id(VOICE_PAUSE, true);
+ talk_id(VOICE_PLAYTIME_DONE, true);
+ output_dyn_value(NULL, 0, pti->kbs_bef, kbyte_units, true);
+ talk_id(LANG_PLAYTIME_REMAINING, true);
+ output_dyn_value(NULL, 0, pti->kbs_aft, kbyte_units, true);
+ }
+ break;
+ }
+ case 4: { /* Average track file size */
+ char str[10];
+ long avg_track_size = pti->kbs_ttl /pti->nb_tracks;
+ output_dyn_value(str, sizeof(str), avg_track_size, kbyte_units, true);
+ snprintf(buf, MAX_PATH, str(LANG_PLAYTIME_AVG_TRACK_SIZE),
+ str);
+ if(say_it) {
+ talk_id(LANG_PLAYTIME_AVG_TRACK_SIZE, false);
+ output_dyn_value(NULL, 0, avg_track_size, kbyte_units, true);
+ }
+ break;
+ }
+ case 5: { /* Average bitrate */
+ /* Convert power of 2 kilobytes to power of 10 kilobits */
+ long avg_bitrate = pti->kbs_ttl / pti->secs_ttl *1024 *8 /1000;
+ snprintf(buf, MAX_PATH, str(LANG_PLAYTIME_AVG_BITRATE),
+ avg_bitrate);
+ if(say_it)
+ talk_ids(false, LANG_PLAYTIME_AVG_BITRATE,
+ TALK_ID(avg_bitrate, UNIT_KBIT));
+ break;
+ }
+ }
+ return buf;
+}
+
+char * playing_time_get_info(int selected_item, void * data,
+ char *buffer)
+{
+ return playing_time_get_or_speak_info(selected_item, data,
+ buffer, false);
+}
+
+/* playing time screen: shows total and elapsed playlist duration and
+ other stats */
+bool playing_time(void)
+{
+ unsigned long last_tick = current_tick, talked_tick = current_tick;
+ struct playing_time_info pti;
+ struct playlist_track_info pltrack;
+ struct track_info t;
+ int i, fd, ret;
+
+ pti.secs_bef = pti.secs_aft = 0;
+ pti.kbs_bef = pti.kbs_aft = 0;
+
+ pti.nb_tracks = playlist_amount();
+ playlist_get_resume_info(&pti.curr_playing);
+ struct mp3entry *curr_id3 = audio_current_track();
+ if(pti.curr_playing == -1 || !curr_id3)
+ return false;
+ pti.secs_bef += curr_id3->elapsed/1000;
+ pti.secs_aft += (curr_id3->length -curr_id3->elapsed)/1000;
+ pti.kbs_bef += curr_id3->offset/1024;
+ pti.kbs_aft += (curr_id3->filesize -curr_id3->offset)/1024;
+
+ gui_syncsplash(0, true, ID2P(LANG_WAIT));
+
+ /* 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. */
+ for(i=0; i