diff options
Diffstat (limited to 'apps/plugins/playing_time.c')
-rw-r--r-- | apps/plugins/playing_time.c | 592 |
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 | ||
34 | const int menu_items[] = { | 34 | const 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 | ||
45 | const unsigned char * const * const kibyte_units = &byte_units[1]; | 45 | const unsigned char * const * const kibyte_units = &byte_units[1]; |
46 | 46 | ||
47 | enum ePT_SECS { | 47 | enum ePT_SUM { |
48 | ePT_SECS_TTL = 0, | ||
49 | ePT_SECS_BEF, | ||
50 | ePT_SECS_AFT, | ||
51 | ePT_SECS_COUNT | ||
52 | }; | ||
53 | |||
54 | enum 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 */ | ||
63 | struct playing_time_info { | 56 | struct 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 | ||
70 | static 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 | |||
92 | static 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 | |||
82 | static char* get_percent_str(long percents) | 112 | static 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 | ||
89 | static inline void prepare_time_string(char *buf, size_t buffer_len, long elapsed_pct, const char *timestr1, const char *timestr2) | 119 | static 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 */ |
104 | static const char * playing_time_get_or_speak_info(int selected_item, void * data, | 132 | static 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 | ||
277 | static const char * playing_time_get_info(int selected_item, void * data, | 302 | static 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 | ||
284 | static int playing_time_speak_info(int selected_item, void * data) | 309 | static 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 | 316 | static bool pt_display_stats(struct playing_time_info *pti) |
293 | other stats */ | ||
294 | static 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) | 346 | static 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) | 357 | static 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) | 366 | static 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 | |||
402 | static 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) | 419 | static 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); | 457 | static 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(); | 474 | static 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: | 493 | static 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 */ | ||
516 | static 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 */ |
416 | enum plugin_status plugin_start(const void* parameter) | 547 | enum 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 | { |