Index: trunk/apps/onplay.c =================================================================== --- trunk.orig/apps/onplay.c +++ trunk/apps/onplay.c @@ -68,6 +68,7 @@ #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1)) #include "backdrop.h" #endif +#include "metadata.h" static int context; static char* selected_file = NULL; @@ -283,6 +284,241 @@ static int cat_playlist_callback(int act return action; } +/* 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); +} + +int playing_time_speak_info(int selected_item, void * data) +{ + char buffer[MAX_PATH]; + playing_time_get_or_speak_info(selected_item, data, buffer, true); + return 0; +} + +/* playing time screen: shows total and elapsed playlist duration and + other stats */ +bool playing_time(void) +{ + unsigned long talked_tick = current_tick; + struct playing_time_info pti; + struct playlist_track_info pltrack; + struct mp3entry id3; + 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, 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 id: LANG_LOADING_PERCENT desc: splash number of percents loaded user: *: "Loading... %d%% done (%s)" *: "Loading... %d%% done (%s)" - *: "" + *: "Loading" id: LANG_SCANNING_DISK desc: when booting up and rebuilding the cache and calculating free space user: *: "Scanning disk..." *: "Scanning disk..." @@ -11390,12 +11390,138 @@ *: none radio: "Preset" *: none radio: "Preset" *: none radio: "Preset mode" + + 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" + +