summaryrefslogtreecommitdiff
path: root/apps/plugins/playing_time.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/playing_time.c')
-rw-r--r--apps/plugins/playing_time.c592
1 files changed, 361 insertions, 231 deletions
diff --git a/apps/plugins/playing_time.c b/apps/plugins/playing_time.c
index e465c35a18..47784cf85e 100644
--- a/apps/plugins/playing_time.c
+++ b/apps/plugins/playing_time.c
@@ -32,10 +32,10 @@ const unsigned char * const byte_units[] =
32}; 32};
33 33
34const int menu_items[] = { 34const int menu_items[] = {
35 LANG_PLAYTIME_ELAPSED, 35 LANG_REMAINING,
36 LANG_PLAYTIME_REMAINING, 36 LANG_ELAPSED,
37 LANG_PLAYTIME_TRK_ELAPSED,
38 LANG_PLAYTIME_TRK_REMAINING, 37 LANG_PLAYTIME_TRK_REMAINING,
38 LANG_PLAYTIME_TRK_ELAPSED,
39 LANG_PLAYTIME_TRACK, 39 LANG_PLAYTIME_TRACK,
40 LANG_PLAYTIME_STORAGE, 40 LANG_PLAYTIME_STORAGE,
41 LANG_PLAYTIME_AVG_TRACK_SIZE, 41 LANG_PLAYTIME_AVG_TRACK_SIZE,
@@ -44,212 +44,237 @@ const int menu_items[] = {
44 44
45const unsigned char * const * const kibyte_units = &byte_units[1]; 45const unsigned char * const * const kibyte_units = &byte_units[1];
46 46
47enum ePT_SECS { 47enum ePT_SUM {
48 ePT_SECS_TTL = 0,
49 ePT_SECS_BEF,
50 ePT_SECS_AFT,
51 ePT_SECS_COUNT
52};
53
54enum ePT_KBS {
55 /* Note: Order matters (voicing order of LANG_PLAYTIME_STORAGE) */ 48 /* Note: Order matters (voicing order of LANG_PLAYTIME_STORAGE) */
56 ePT_KBS_TTL = 0, 49 ePT_TOTAL = 0,
57 ePT_KBS_BEF, 50 ePT_ELAPSED,
58 ePT_KBS_AFT, 51 ePT_REMAINING,
59 ePT_KBS_COUNT 52
53 ePT_COUNT
60}; 54};
61 55
62/* playing_time screen context */
63struct playing_time_info { 56struct playing_time_info {
64 int curr_index; /* index of currently playing track in playlist */ 57 char single_mode_tag[MAX_PATH]; /* Relevant tag when single mode enabled */
65 int curr_display_index; /* display index of currently playing track in playlist */ 58 unsigned long long size[ePT_COUNT]; /* File size of tracks */
66 int nb_tracks; /* how many tracks in playlist */ 59 unsigned long long length[ePT_COUNT]; /* Length of tracks */
67 60 unsigned long curr_track_length[ePT_COUNT]; /* Current track length */
68 /* seconds total, before, and after current position. Datatype 61 int curr_track_index; /* Index of currently playing track in playlist */
69 allows for values up to 68years. If I had kept it in ms 62 int curr_display_index; /* Display index of currently playing track */
70 though, it would have overflowed at 24days, which takes 63 int actual_index; /* Display index in actually counted tracks */
71 something like 8.5GB at 32kbps, and so we could conceivably 64 int counted; /* Number of tracks already added up */
72 have playlists lasting longer than that. */ 65 int nb_tracks; /* Number of tracks in playlist */
73 long secs[ePT_SECS_COUNT]; 66 int error_count; /* Number of tracks whose data couldn't be retrieved */
74 long trk_secs[ePT_SECS_COUNT]; 67 bool remaining_only; /* Whether to ignore elapsed tracks */
75
76 /* kilobytes played total, before, and after current pos.
77 Kilobytes because bytes would overflow. Data type range is up
78 to 2TB. */
79 long kbs[ePT_KBS_COUNT];
80}; 68};
81 69
70static int32_t single_mode_lang(void)
71{
72 switch (rb->global_settings->single_mode)
73 {
74 case SINGLE_MODE_ALBUM:
75 return LANG_ID3_ALBUM;
76 case SINGLE_MODE_ALBUM_ARTIST:
77 return LANG_ID3_ALBUMARTIST;
78 case SINGLE_MODE_ARTIST:
79 return LANG_ID3_ARTIST;
80 case SINGLE_MODE_COMPOSER:
81 return LANG_ID3_COMPOSER;
82 case SINGLE_MODE_GROUPING:
83 return LANG_ID3_GROUPING;
84 case SINGLE_MODE_GENRE:
85 return LANG_ID3_GENRE;
86 case SINGLE_MODE_TRACK:
87 return LANG_TRACK;
88 }
89 return LANG_OFF;
90}
91
92static char* single_mode_id3_tag(struct mp3entry *id3)
93{
94 switch (rb->global_settings->single_mode)
95 {
96 case SINGLE_MODE_ALBUM:
97 return id3->album;
98 case SINGLE_MODE_ALBUM_ARTIST:
99 return id3->albumartist;
100 case SINGLE_MODE_ARTIST:
101 return id3->artist;
102 case SINGLE_MODE_COMPOSER:
103 return id3->composer;
104 case SINGLE_MODE_GROUPING:
105 return id3->grouping;
106 case SINGLE_MODE_GENRE:
107 return id3->genre_string;
108 }
109 return NULL;
110}
111
82static char* get_percent_str(long percents) 112static char* get_percent_str(long percents)
83{ 113{
84 static char val[10]; 114 static char val[10];
85 rb->snprintf(val, 10, rb->str(LANG_PERCENT_FORMAT), percents); 115 rb->snprintf(val, sizeof(val), rb->str(LANG_PERCENT_FORMAT), percents);
86 return val; 116 return val;
87} 117}
88 118
89static inline void prepare_time_string(char *buf, size_t buffer_len, long elapsed_pct, const char *timestr1, const char *timestr2) 119static inline void prepare_time_string(char *buf, size_t buffer_len,
120 long elapsed_pct, const char *timestr1,
121 const char *timestr2)
90{ 122{
91 if (rb->lang_is_rtl()) 123 if (rb->lang_is_rtl())
92 {
93 rb->snprintf(buf, buffer_len, "%s %s / %s", 124 rb->snprintf(buf, buffer_len, "%s %s / %s",
94 get_percent_str(elapsed_pct), timestr2, timestr1); 125 get_percent_str(elapsed_pct), timestr2, timestr1);
95 }
96 else 126 else
97 {
98 rb->snprintf(buf, buffer_len, "%s / %s %s", 127 rb->snprintf(buf, buffer_len, "%s / %s %s",
99 timestr1, timestr2, get_percent_str(elapsed_pct)); 128 timestr1, timestr2, get_percent_str(elapsed_pct));
100 }
101} 129}
102 130
103/* list callback for playing_time screen */ 131/* list callback for playing_time screen */
104static const char * playing_time_get_or_speak_info(int selected_item, void * data, 132static const char * pt_get_or_speak_info(int selected_item, void * data,
105 char *buf, size_t buffer_len, 133 char *buf, size_t buffer_len,
106 bool say_it) 134 bool say_it)
107{ 135{
108 long elapsed_pct; /* percentage of duration elapsed */ 136 long elapsed_pct; /* percentage of duration elapsed */
109 struct playing_time_info *pti = (struct playing_time_info *)data; 137 struct playing_time_info *pti = (struct playing_time_info *)data;
110 int info_no = selected_item/2; 138 int info_no = selected_item/2;
111 const int menu_name_id = menu_items[info_no]; 139 const int menu_name_id = menu_items[info_no];
112 140
113 if (!(selected_item%2)) 141 /* header */
114 {/* header */ 142 if (!say_it && !(selected_item % 2))
115 return rb->str(menu_name_id); 143 return rb->str(menu_name_id);
116 }
117 144
145 /* data */
118 switch(info_no) { 146 switch(info_no) {
119 case 0: { /* elapsed and total time */ 147 case 0: { /* playlist remaining time */
148 char timestr[25];
149 rb->format_time_auto(timestr, sizeof(timestr),
150 pti->length[ePT_REMAINING], UNIT_SEC, false);
151 rb->snprintf(buf, buffer_len, "%s", timestr);
152
153 if (say_it)
154 rb_talk_ids(false, menu_name_id,
155 TALK_ID(pti->length[ePT_REMAINING], UNIT_TIME));
156 break;
157 }
158 case 1: { /* elapsed and total time */
120 char timestr1[25], timestr2[25]; 159 char timestr1[25], timestr2[25];
121 rb->format_time_auto(timestr1, sizeof(timestr1), 160 rb->format_time_auto(timestr1, sizeof(timestr1),
122 pti->secs[ePT_SECS_BEF], UNIT_SEC, true); 161 pti->length[ePT_ELAPSED], UNIT_SEC, true);
123 162
124 rb->format_time_auto(timestr2, sizeof(timestr2), 163 rb->format_time_auto(timestr2, sizeof(timestr2),
125 pti->secs[ePT_SECS_TTL], UNIT_SEC, true); 164 pti->length[ePT_TOTAL], UNIT_SEC, true);
126 165
127 if (pti->secs[ePT_SECS_TTL] == 0) 166 if (pti->length[ePT_TOTAL] == 0)
128 elapsed_pct = 0; 167 elapsed_pct = 0;
129 else if (pti->secs[ePT_SECS_TTL] <= 0xFFFFFF) 168 else if (pti->length[ePT_TOTAL] <= 0xFFFFFF)
130 { 169 {
131 elapsed_pct = (pti->secs[ePT_SECS_BEF] * 100 170 elapsed_pct = (pti->length[ePT_ELAPSED] * 100
132 / pti->secs[ePT_SECS_TTL]); 171 / pti->length[ePT_TOTAL]);
133 } 172 }
134 else /* sacrifice some precision to avoid overflow */ 173 else /* sacrifice some precision to avoid overflow */
135 { 174 {
136 elapsed_pct = (pti->secs[ePT_SECS_BEF] >> 7) * 100 175 elapsed_pct = (pti->length[ePT_ELAPSED] >> 7) * 100
137 / (pti->secs[ePT_SECS_TTL] >> 7); 176 / (pti->length[ePT_TOTAL] >> 7);
138 } 177 }
139 prepare_time_string(buf, buffer_len, elapsed_pct, timestr1, timestr2); 178 prepare_time_string(buf, buffer_len, elapsed_pct, timestr1, timestr2);
140 179
141 if (say_it) 180 if (say_it)
142 rb_talk_ids(false, menu_name_id, 181 rb_talk_ids(false, menu_name_id,
143 TALK_ID(pti->secs[ePT_SECS_BEF], UNIT_TIME), 182 TALK_ID(pti->length[ePT_ELAPSED], UNIT_TIME),
144 VOICE_OF, 183 VOICE_OF,
145 TALK_ID(pti->secs[ePT_SECS_TTL], UNIT_TIME), 184 TALK_ID(pti->length[ePT_TOTAL], UNIT_TIME),
146 VOICE_PAUSE, 185 VOICE_PAUSE,
147 TALK_ID(elapsed_pct, UNIT_PERCENT)); 186 TALK_ID(elapsed_pct, UNIT_PERCENT));
148 break; 187 break;
149 } 188 }
150 case 1: { /* playlist remaining time */ 189 case 2: { /* track remaining time */
151 char timestr[25]; 190 char timestr[25];
152 rb->format_time_auto(timestr, sizeof(timestr), pti->secs[ePT_SECS_AFT], 191 rb->format_time_auto(timestr, sizeof(timestr),
153 UNIT_SEC, false); 192 pti->curr_track_length[ePT_REMAINING], UNIT_SEC, false);
154 rb->snprintf(buf, buffer_len, "%s", timestr); 193 rb->snprintf(buf, buffer_len, "%s", timestr);
155 194
156 if (say_it) 195 if (say_it)
157 rb_talk_ids(false, menu_name_id, 196 rb_talk_ids(false, menu_name_id,
158 TALK_ID(pti->secs[ePT_SECS_AFT], UNIT_TIME)); 197 TALK_ID(pti->curr_track_length[ePT_REMAINING], UNIT_TIME));
159 break; 198 break;
160 } 199 }
161 case 2: { /* track elapsed and duration */ 200 case 3: { /* track elapsed and duration */
162 char timestr1[25], timestr2[25]; 201 char timestr1[25], timestr2[25];
163 202
164 rb->format_time_auto(timestr1, sizeof(timestr1), pti->trk_secs[ePT_SECS_BEF], 203 rb->format_time_auto(timestr1, sizeof(timestr1),
165 UNIT_SEC, true); 204 pti->curr_track_length[ePT_ELAPSED], UNIT_SEC, true);
166 rb->format_time_auto(timestr2, sizeof(timestr2), pti->trk_secs[ePT_SECS_TTL], 205 rb->format_time_auto(timestr2, sizeof(timestr2),
167 UNIT_SEC, true); 206 pti->curr_track_length[ePT_TOTAL], UNIT_SEC, true);
168 207
169 if (pti->trk_secs[ePT_SECS_TTL] == 0) 208 if (pti->curr_track_length[ePT_TOTAL] == 0)
170 elapsed_pct = 0; 209 elapsed_pct = 0;
171 else if (pti->trk_secs[ePT_SECS_TTL] <= 0xFFFFFF) 210 else if (pti->curr_track_length[ePT_TOTAL] <= 0xFFFFFF)
172 { 211 {
173 elapsed_pct = (pti->trk_secs[ePT_SECS_BEF] * 100 212 elapsed_pct = (pti->curr_track_length[ePT_ELAPSED] * 100
174 / pti->trk_secs[ePT_SECS_TTL]); 213 / pti->curr_track_length[ePT_TOTAL]);
175 } 214 }
176 else /* sacrifice some precision to avoid overflow */ 215 else /* sacrifice some precision to avoid overflow */
177 { 216 {
178 elapsed_pct = (pti->trk_secs[ePT_SECS_BEF] >> 7) * 100 217 elapsed_pct = (pti->curr_track_length[ePT_ELAPSED] >> 7) * 100
179 / (pti->trk_secs[ePT_SECS_TTL] >> 7); 218 / (pti->curr_track_length[ePT_TOTAL] >> 7);
180 } 219 }
181 prepare_time_string(buf, buffer_len, elapsed_pct, timestr1, timestr2); 220 prepare_time_string(buf, buffer_len, elapsed_pct, timestr1, timestr2);
182 221
183 if (say_it) 222 if (say_it)
184 rb_talk_ids(false, menu_name_id, 223 rb_talk_ids(false, menu_name_id,
185 TALK_ID(pti->trk_secs[ePT_SECS_BEF], UNIT_TIME), 224 TALK_ID(pti->curr_track_length[ePT_ELAPSED], UNIT_TIME),
186 VOICE_OF, 225 VOICE_OF,
187 TALK_ID(pti->trk_secs[ePT_SECS_TTL], UNIT_TIME), 226 TALK_ID(pti->curr_track_length[ePT_TOTAL], UNIT_TIME),
188 VOICE_PAUSE, 227 VOICE_PAUSE,
189 TALK_ID(elapsed_pct, UNIT_PERCENT)); 228 TALK_ID(elapsed_pct, UNIT_PERCENT));
190 break;
191 }
192 case 3: { /* track remaining time */
193 char timestr[25];
194 rb->format_time_auto(timestr, sizeof(timestr), pti->trk_secs[ePT_SECS_AFT],
195 UNIT_SEC, false);
196 rb->snprintf(buf, buffer_len, "%s", timestr);
197
198 if (say_it)
199 rb_talk_ids(false, menu_name_id,
200 TALK_ID(pti->trk_secs[ePT_SECS_AFT], UNIT_TIME));
201 break; 229 break;
202 } 230 }
203 case 4: { /* track index */ 231 case 4: { /* track index */
204 int track_pct = pti->curr_display_index * 100 / pti->nb_tracks; 232 int track_pct = pti->actual_index * 100 / pti->counted;
205 233
206 if (rb->lang_is_rtl()) 234 if (rb->lang_is_rtl())
207 { 235 rb->snprintf(buf, buffer_len, "%s %d / %d", get_percent_str(track_pct),
208 rb->snprintf(buf, buffer_len, "%s %d / %d", 236 pti->counted, pti->actual_index);
209 get_percent_str(track_pct), pti->nb_tracks, pti->curr_display_index);
210 }
211 else 237 else
212 { 238 rb->snprintf(buf, buffer_len, "%d / %d %s", pti->actual_index,
213 rb->snprintf(buf, buffer_len, "%d / %d %s", 239 pti->counted, get_percent_str(track_pct));
214 pti->curr_display_index, pti->nb_tracks, get_percent_str(track_pct));
215 }
216 240
217 if (say_it) 241 if (say_it)
218 rb_talk_ids(false, menu_name_id, 242 rb_talk_ids(false, menu_name_id,
219 TALK_ID(pti->curr_display_index, UNIT_INT), 243 TALK_ID(pti->actual_index, UNIT_INT),
220 VOICE_OF, 244 VOICE_OF,
221 TALK_ID(pti->nb_tracks, UNIT_INT), 245 TALK_ID(pti->counted, UNIT_INT),
222 VOICE_PAUSE, 246 VOICE_PAUSE,
223 TALK_ID(track_pct, UNIT_PERCENT)); 247 TALK_ID(track_pct, UNIT_PERCENT));
224 break; 248 break;
225 } 249 }
226 case 5: { /* storage size */ 250 case 5: { /* storage size */
227 int i; 251 int i;
228 char kbstr[ePT_KBS_COUNT][20]; 252 char kbstr[ePT_COUNT][20];
229 253
230 for (i = 0; i < ePT_KBS_COUNT; i++) { 254 for (i = 0; i < ePT_COUNT; i++) {
231 rb->output_dyn_value(kbstr[i], sizeof(kbstr[i]), 255 rb->output_dyn_value(kbstr[i], sizeof(kbstr[i]),
232 pti->kbs[i], kibyte_units, 3, true); 256 pti->size[i], kibyte_units, 3, true);
233 } 257 }
234 rb->snprintf(buf, buffer_len, "%s (%s / %s)", 258 rb->snprintf(buf, buffer_len, "%s (%s / %s)", kbstr[ePT_TOTAL],
235 kbstr[ePT_KBS_TTL], kbstr[ePT_KBS_BEF],kbstr[ePT_KBS_AFT]); 259 kbstr[ePT_ELAPSED], kbstr[ePT_REMAINING]);
236 260
237 if (say_it) { 261 if (say_it) {
238 int32_t voice_ids[ePT_KBS_COUNT]; 262 int32_t voice_ids[ePT_COUNT];
239 voice_ids[ePT_KBS_TTL] = menu_name_id; 263 voice_ids[ePT_TOTAL] = menu_name_id;
240 voice_ids[ePT_KBS_BEF] = VOICE_PLAYTIME_DONE; 264 voice_ids[ePT_ELAPSED] = VOICE_PLAYTIME_DONE;
241 voice_ids[ePT_KBS_AFT] = LANG_PLAYTIME_REMAINING; 265 voice_ids[ePT_REMAINING] = LANG_REMAINING;
242 266
243 for (i = 0; i < ePT_KBS_COUNT; i++) { 267 for (i = 0; i < ePT_COUNT; i++)
268 {
244 rb_talk_ids(i > 0, VOICE_PAUSE, voice_ids[i]); 269 rb_talk_ids(i > 0, VOICE_PAUSE, voice_ids[i]);
245 rb->output_dyn_value(NULL, 0, pti->kbs[i], kibyte_units, 3, true); 270 rb->output_dyn_value(NULL, 0, pti->size[i], kibyte_units, 3, true);
246 } 271 }
247 } 272 }
248 break; 273 break;
249 } 274 }
250 case 6: { /* Average track file size */ 275 case 6: { /* Average track file size */
251 char str[20]; 276 char str[20];
252 long avg_track_size = pti->kbs[ePT_KBS_TTL] / pti->nb_tracks; 277 long avg_track_size = pti->size[ePT_TOTAL] / pti->counted;
253 rb->output_dyn_value(str, sizeof(str), avg_track_size, kibyte_units, 3, true); 278 rb->output_dyn_value(str, sizeof(str), avg_track_size, kibyte_units, 3, true);
254 rb->snprintf(buf, buffer_len, "%s", str); 279 rb->snprintf(buf, buffer_len, "%s", str);
255 280
@@ -261,163 +286,268 @@ static const char * playing_time_get_or_speak_info(int selected_item, void * dat
261 } 286 }
262 case 7: { /* Average bitrate */ 287 case 7: { /* Average bitrate */
263 /* Convert power of 2 kilobytes to power of 10 kilobits */ 288 /* Convert power of 2 kilobytes to power of 10 kilobits */
264 long avg_bitrate = (pti->kbs[ePT_KBS_TTL] / pti->secs[ePT_SECS_TTL] 289 long avg_bitrate = (pti->size[ePT_TOTAL] / pti->length[ePT_TOTAL]
265 * 1024 * 8 / 1000); 290 * 1024 * 8 / 1000);
266 rb->snprintf(buf, buffer_len, "%ld kbps", avg_bitrate); 291 rb->snprintf(buf, buffer_len, "%ld kbps", avg_bitrate);
267 292
268 if (say_it) 293 if (say_it)
269 rb_talk_ids(false, menu_name_id, 294 rb_talk_ids(false, menu_name_id,
270 TALK_ID(avg_bitrate, UNIT_KBIT)); 295 TALK_ID(avg_bitrate, UNIT_KBIT));
271 break; 296 break;
272 } 297 }
273 } 298 }
274 return buf; 299 return buf;
275} 300}
276 301
277static const char * playing_time_get_info(int selected_item, void * data, 302static const char * pt_get_info(int selected_item, void * data,
278 char *buffer, size_t buffer_len) 303 char *buffer, size_t buffer_len)
279{ 304{
280 return playing_time_get_or_speak_info(selected_item, data, 305 return pt_get_or_speak_info(selected_item, data,
281 buffer, buffer_len, false); 306 buffer, buffer_len, false);
282} 307}
283 308
284static int playing_time_speak_info(int selected_item, void * data) 309static int pt_speak_info(int selected_item, void * data)
285{ 310{
286 static char buffer[MAX_PATH]; 311 static char buffer[MAX_PATH];
287 playing_time_get_or_speak_info(selected_item, data, 312 pt_get_or_speak_info(selected_item, data, buffer, sizeof(buffer), true);
288 buffer, MAX_PATH, true);
289 return 0; 313 return 0;
290} 314}
291 315
292/* playing time screen: shows total and elapsed playlist duration and 316static bool pt_display_stats(struct playing_time_info *pti)
293 other stats */
294static bool playing_time(void)
295{ 317{
296 int error_count = 0; 318 struct gui_synclist pt_lists;
297 unsigned long talked_tick = *rb->current_tick; 319 rb->gui_synclist_init(&pt_lists, &pt_get_info, pti, true, 2, NULL);
298 struct playing_time_info pti; 320 if (rb->global_settings->talk_menu)
299 struct playlist_track_info pltrack; 321 rb->gui_synclist_set_voice_callback(&pt_lists, pt_speak_info);
300 struct mp3entry id3; 322 rb->gui_synclist_set_nb_items(&pt_lists, pti->remaining_only ? 2 : 8*2);
301 int i, index, fd; 323 rb->gui_synclist_set_title(&pt_lists, *pti->single_mode_tag ?
324 rb->str(single_mode_lang()) :
325 rb->str(LANG_PLAYLIST), NOICON);
326 rb->gui_synclist_draw(&pt_lists);
327 rb->gui_synclist_speak_item(&pt_lists);
328 while (true)
329 {
330 int action = rb->get_action(CONTEXT_LIST, HZ/2);
331 if (rb->gui_synclist_do_button(&pt_lists, &action) == 0
332 && action != ACTION_NONE && action != ACTION_UNKNOWN)
333 {
334 bool usb = rb->default_event_handler(action) == SYS_USB_CONNECTED;
302 335
303 pti.nb_tracks = rb->playlist_amount(); 336 if (!usb && IS_SYSEVENT(action))
304 rb->playlist_get_resume_info(&pti.curr_index); 337 continue;
305 struct mp3entry *curr_id3 = rb->audio_current_track(); 338
306 if (pti.curr_index == -1 || !curr_id3) 339 rb->talk_force_shutup();
307 return false; 340 return usb;
308 pti.curr_display_index = rb->playlist_get_display_index();
309
310 pti.secs[ePT_SECS_BEF] = pti.trk_secs[ePT_SECS_BEF] = curr_id3->elapsed / 1000;
311 pti.secs[ePT_SECS_AFT] = pti.trk_secs[ePT_SECS_AFT]
312 = (curr_id3->length -curr_id3->elapsed) / 1000;
313 pti.kbs[ePT_KBS_BEF] = curr_id3->offset / 1024;
314 pti.kbs[ePT_KBS_AFT] = (curr_id3->filesize -curr_id3->offset) / 1024;
315
316 rb->splash(0, ID2P(LANG_WAIT));
317 rb->splash_progress_set_delay(5 * HZ);
318 /* Go through each file in the playlist and get its stats. For
319 huge playlists this can take a while... The reference position
320 is the position at the moment this function was invoked,
321 although playback continues forward. */
322 index = rb->playlist_get_first_index(NULL);
323 for (i = 0; i < pti.nb_tracks; i++, index++) {
324
325 if (index == pti.nb_tracks)
326 index = 0;
327
328 /* Show a splash while we are loading. */
329 rb->splash_progress(i, pti.nb_tracks,
330 "%s (%s)", rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
331
332 /* Voice equivalent */
333 if (TIME_AFTER(*rb->current_tick, talked_tick + 5 * HZ)) {
334 talked_tick = *rb->current_tick;
335 rb_talk_ids(false, LANG_LOADING_PERCENT,
336 TALK_ID(i * 100 / pti.nb_tracks, UNIT_PERCENT));
337 } 341 }
338 if (rb->action_userabort(TIMEOUT_NOBLOCK)) 342 }
339 goto exit; 343 return false;
344}
340 345
341 if (index == pti.curr_index) 346static const char *pt_options_name(int selected_item, void * data,
342 continue; 347 char *buf, size_t buf_size)
348{
349 (void) data;
350 (void) buf;
351 (void) buf_size;
352 return selected_item == 0 ? rb->str(LANG_ALL) :
353 selected_item == 1 ? rb->str(LANG_REMAINING) :
354 rb->str(single_mode_lang());
355}
343 356
344 if (rb->playlist_get_track_info(NULL, index, &pltrack) >= 0) 357static int pt_options_speak(int selected_item, void * data)
345 { 358{
346 bool ret = false; 359 (void) data;
347 if ((fd = rb->open(pltrack.filename, O_RDONLY)) >= 0) 360 rb->talk_id(selected_item == 0 ? LANG_ALL :
348 { 361 selected_item == 1 ? LANG_REMAINING :
349 ret = rb->get_metadata(&id3, fd, pltrack.filename); 362 single_mode_lang(), false);
350 rb->close(fd); 363 return 0;
351 if (ret) 364}
352 {
353 if (pltrack.display_index < pti.curr_display_index) {
354 pti.secs[ePT_SECS_BEF] += id3.length / 1000;
355 pti.kbs[ePT_KBS_BEF] += id3.filesize / 1024;
356 } else {
357 pti.secs[ePT_SECS_AFT] += id3.length / 1000;
358 pti.kbs[ePT_KBS_AFT] += id3.filesize / 1024;
359 }
360 }
361 }
362 365
363 if (!ret) 366static int pt_options(struct playing_time_info *pti)
367{
368 struct gui_synclist pt_options;
369 rb->gui_synclist_init(&pt_options, &pt_options_name, NULL, true, 1, NULL);
370 if (rb->global_settings->talk_menu)
371 rb->gui_synclist_set_voice_callback(&pt_options, pt_options_speak);
372 rb->gui_synclist_set_nb_items(&pt_options, *pti->single_mode_tag ? 3 : 2);
373 rb->gui_synclist_set_title(&pt_options, rb->str(LANG_PLAYING_TIME), NOICON);
374 rb->gui_synclist_draw(&pt_options);
375 rb->gui_synclist_speak_item(&pt_options);
376
377 while(true)
378 {
379 int button = rb->get_action(CONTEXT_LIST, HZ);
380 if (rb->gui_synclist_do_button(&pt_options, &button))
381 continue;
382 switch(button)
383 {
384 case ACTION_STD_OK:
364 { 385 {
365 error_count++; 386 int sel = rb->gui_synclist_get_sel_pos(&pt_options);
366 continue; 387 if (sel < 2)
388 *pti->single_mode_tag = 0;
389 if (sel == 1)
390 pti->remaining_only = true;
391 return -1;
367 } 392 }
368 } 393 case ACTION_STD_CANCEL:
369 else 394 return 0;
370 { 395 default:
371 error_count++; 396 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
372 break; 397 return 1;
373 } 398 }
374 } 399 }
400}
401
402static void pt_store_converted_totals(struct playing_time_info *pti)
403{
404 /* convert units from ms to s */
405 pti->length[ePT_ELAPSED] /= 1000;
406 pti->length[ePT_REMAINING] /= 1000;
407 pti->curr_track_length[ePT_ELAPSED] /= 1000;
408 pti->curr_track_length[ePT_REMAINING] /= 1000;
409 /* convert units from Bytes to KiB */
410 pti->size[ePT_ELAPSED] >>= 10;
411 pti->size[ePT_REMAINING] >>= 10;
412
413 pti->length[ePT_TOTAL] = pti->length[ePT_ELAPSED] + pti->length[ePT_REMAINING];
414 pti->curr_track_length[ePT_TOTAL] = pti->curr_track_length[ePT_ELAPSED]
415 + pti->curr_track_length[ePT_REMAINING];
416 pti->size[ePT_TOTAL] = pti->size[ePT_ELAPSED] + pti->size[ePT_REMAINING];
417}
375 418
376 if (error_count > 0) 419static int pt_add_track(int i, enum ePT_SUM section, struct playing_time_info *pti)
420{
421 struct mp3entry id3;
422 struct playlist_track_info pl_track;
423 static unsigned long talked_tick;
424 int progress_total = pti->remaining_only ?
425 pti->nb_tracks - pti->curr_display_index :
426 pti->nb_tracks;
427
428 rb->splash_progress(pti->counted, progress_total, "%s (%s)",
429 rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
430
431 if (TIME_AFTER(*rb->current_tick, talked_tick + HZ*5))
377 { 432 {
378 rb->splash(HZ, ID2P(LANG_PLAYTIME_ERROR)); 433 talked_tick = *rb->current_tick;
434 rb_talk_ids(false, LANG_LOADING_PERCENT,
435 TALK_ID(pti->counted * 100 / progress_total,
436 UNIT_PERCENT));
379 } 437 }
380 438
381 pti.nb_tracks -= error_count; 439 if (rb->action_userabort(TIMEOUT_NOBLOCK))
382 pti.secs[ePT_SECS_TTL] = pti.secs[ePT_SECS_BEF] + pti.secs[ePT_SECS_AFT]; 440 return -1;
383 pti.trk_secs[ePT_SECS_TTL] = pti.trk_secs[ePT_SECS_BEF] + pti.trk_secs[ePT_SECS_AFT]; 441 else if (rb->playlist_get_track_info(NULL, i, &pl_track) < 0
384 pti.kbs[ePT_KBS_TTL] = pti.kbs[ePT_KBS_BEF] + pti.kbs[ePT_KBS_AFT]; 442 || !rb->get_metadata(&id3, -1, pl_track.filename))
443 {
444 pti->error_count++;
445 return -2;
446 }
447 else if(*pti->single_mode_tag && /* single mode tag doesn't match */
448 rb->strcmp(pti->single_mode_tag, single_mode_id3_tag(&id3) ?: ""))
449 return 1;
385 450
386 struct gui_synclist pt_lists; 451 pti->length[section] += id3.length;
387 int key; 452 pti->size[section] += id3.filesize;
453 pti->counted++;
454 return 0;
455}
388 456
389 rb->gui_synclist_init(&pt_lists, &playing_time_get_info, &pti, true, 2, NULL); 457static bool pt_add_remaining(struct playing_time_info *pti)
390 if (rb->global_settings->talk_menu) 458{
391 rb->gui_synclist_set_voice_callback(&pt_lists, playing_time_speak_info); 459 int display_index = pti->curr_display_index + 1;
392 rb->gui_synclist_set_nb_items(&pt_lists, 16); 460 for (int i = pti->curr_track_index + 1; display_index <= pti->nb_tracks; i++, display_index++)
393 rb->gui_synclist_set_title(&pt_lists, rb->str(LANG_PLAYING_TIME), NOICON); 461 {
394 rb->gui_synclist_draw(&pt_lists); 462 if (i == pti->nb_tracks)
395 rb->gui_synclist_speak_item(&pt_lists); 463 i = 0;
396 while (true) {
397 if (rb->list_do_action(CONTEXT_LIST, HZ/2, &pt_lists, &key) == 0
398 && key!=ACTION_NONE && key!=ACTION_UNKNOWN)
399 {
400 bool usb = rb->default_event_handler(key) == SYS_USB_CONNECTED;
401 464
402 if (!usb && IS_SYSEVENT(key)) 465 int ret = pt_add_track(i, ePT_REMAINING, pti);
403 continue; 466 if (ret == 1)
467 break;
468 else if (ret == -1)
469 return false;
470 }
471 return true;
472}
404 473
405 rb->talk_force_shutup(); 474static bool pt_add_elapsed(struct playing_time_info *pti)
406 return usb; 475{
407 } 476 int display_index = pti->curr_display_index - 1;
477 for (int i = pti->curr_track_index - 1; display_index > 0; i--, display_index--)
478 {
479 if (i < 0)
480 i = pti->nb_tracks - 1;
408 481
482 int ret = pt_add_track(i, ePT_ELAPSED, pti);
483 if (ret == 1)
484 break;
485 else if (ret == -1)
486 return false;
487 else if (ret == 0)
488 pti->actual_index++;
409 } 489 }
490 return true;
491}
410 492
411 exit: 493static bool pt_add_curr_track(struct playing_time_info *pti)
412 return false; 494{
495 struct mp3entry *curr_id3 = rb->audio_current_track();
496 rb->playlist_get_resume_info(&pti->curr_track_index);
497
498 if (pti->curr_track_index == -1 || !curr_id3)
499 return false;
500
501 pti->curr_display_index = rb->playlist_get_display_index();
502 pti->length[ePT_ELAPSED] = pti->curr_track_length[ePT_ELAPSED]
503 = curr_id3->elapsed;
504 pti->length[ePT_REMAINING] = pti->curr_track_length[ePT_REMAINING]
505 = curr_id3->length - curr_id3->elapsed;
506 pti->size[ePT_ELAPSED] = curr_id3->offset;
507 pti->size[ePT_REMAINING] = curr_id3->filesize - curr_id3->offset;
508 pti->actual_index = pti->counted = 1;
509 rb->strlcpy(pti->single_mode_tag, single_mode_id3_tag(curr_id3) ?: "",
510 sizeof(pti->single_mode_tag));
511 return true;
512}
513
514/* playing time screen: shows total and elapsed playlist duration and
515 other stats */
516static bool playing_time(void)
517{
518 struct playing_time_info pti;
519 rb->memset(&pti, 0, sizeof(struct playing_time_info));
520
521 if (!pt_add_curr_track(&pti))
522 return false;
523
524 int opt = pt_options(&pti);
525 if (opt > -1)
526 return opt;
527
528#ifdef HAVE_ADJUSTABLE_CPU_FREQ
529 rb->cpu_boost(true);
530#endif
531 rb->splash_progress_set_delay(HZ/2);
532 pti.nb_tracks = rb->playlist_amount();
533 int success = (pti.remaining_only || pt_add_elapsed(&pti)) && pt_add_remaining(&pti);
534#ifdef HAVE_ADJUSTABLE_CPU_FREQ
535 rb->cpu_boost(false);
536#endif
537 if (!success)
538 return false;
539 if (pti.error_count > 0)
540 rb->splash(HZ, ID2P(LANG_PLAYTIME_ERROR));
541
542 pt_store_converted_totals(&pti);
543 return pt_display_stats(&pti);
413} 544}
414 545
415/* this is the plugin entry point */ 546/* this is the plugin entry point */
416enum plugin_status plugin_start(const void* parameter) 547enum plugin_status plugin_start(const void* parameter)
417{ 548{
418 enum plugin_status status = PLUGIN_OK;
419
420 (void)parameter; 549 (void)parameter;
550 enum plugin_status status = PLUGIN_OK;
421 551
422 if (!rb->audio_status()) 552 if (!rb->audio_status())
423 { 553 {