diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/debug_menu.c | 7 | ||||
-rw-r--r-- | apps/lang/english.lang | 40 | ||||
-rw-r--r-- | apps/recorder/peakmeter.c | 631 | ||||
-rw-r--r-- | apps/recorder/peakmeter.h | 22 | ||||
-rw-r--r-- | apps/settings.c | 86 | ||||
-rw-r--r-- | apps/settings.h | 5 | ||||
-rw-r--r-- | apps/settings_menu.c | 194 | ||||
-rw-r--r-- | apps/wps-display.c | 72 | ||||
-rw-r--r-- | apps/wps-display.h | 13 | ||||
-rw-r--r-- | apps/wps.c | 62 |
10 files changed, 1034 insertions, 98 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 97db40ee7d..4dd98e0498 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include "mpeg.h" | 40 | #include "mpeg.h" |
41 | #ifdef HAVE_LCD_BITMAP | 41 | #ifdef HAVE_LCD_BITMAP |
42 | #include "widgets.h" | 42 | #include "widgets.h" |
43 | #include "peakmeter.h" | ||
43 | #endif | 44 | #endif |
44 | 45 | ||
45 | /*---------------------------------------------------*/ | 46 | /*---------------------------------------------------*/ |
@@ -1031,7 +1032,10 @@ bool debug_menu(void) | |||
1031 | { "View partitions", dbg_partitions }, | 1032 | { "View partitions", dbg_partitions }, |
1032 | #ifdef HAVE_LCD_BITMAP | 1033 | #ifdef HAVE_LCD_BITMAP |
1033 | { "View mpeg thread", dbg_mpeg_thread }, | 1034 | { "View mpeg thread", dbg_mpeg_thread }, |
1034 | #endif | 1035 | #ifdef PM_DEBUG |
1036 | { "pm histogram", peak_meter_histogram}, | ||
1037 | #endif /* PM_DEBUG */ | ||
1038 | #endif /* HAVE_LCD_BITMAP */ | ||
1035 | }; | 1039 | }; |
1036 | 1040 | ||
1037 | m=menu_init( items, sizeof items / sizeof(struct menu_items) ); | 1041 | m=menu_init( items, sizeof items / sizeof(struct menu_items) ); |
@@ -1042,4 +1046,3 @@ bool debug_menu(void) | |||
1042 | } | 1046 | } |
1043 | 1047 | ||
1044 | #endif /* SIMULATOR */ | 1048 | #endif /* SIMULATOR */ |
1045 | |||
diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 3f1eb6efb8..373e072395 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang | |||
@@ -866,6 +866,46 @@ desc: in the peak meter menu | |||
866 | eng: "Units Per Read" | 866 | eng: "Units Per Read" |
867 | new: | 867 | new: |
868 | 868 | ||
869 | id: LANG_PM_PERFORMANCE | ||
870 | desc: in the peak meter menu | ||
871 | eng: "Performance" | ||
872 | new: | ||
873 | |||
874 | id: LANG_PM_HIGH_PERFORMANCE | ||
875 | desc: in the peak meter menu | ||
876 | eng: "High performance" | ||
877 | new: | ||
878 | |||
879 | id: LANG_PM_ENERGY_SAVER | ||
880 | desc: in the peak meter menu | ||
881 | eng: "Save energy" | ||
882 | new: | ||
883 | |||
884 | id: LANG_PM_SCALE | ||
885 | desc: in the peak meter menu | ||
886 | eng: "dBfs <-> linear" | ||
887 | new: | ||
888 | |||
889 | id: LANG_PM_DBFS | ||
890 | desc: in the peak meter menu | ||
891 | eng: "dBfs" | ||
892 | new: | ||
893 | |||
894 | id: LANG_PM_LINEAR | ||
895 | desc: in the peak meter menu | ||
896 | eng: "linear" | ||
897 | new: | ||
898 | |||
899 | id: LANG_PM_MIN | ||
900 | desc: in the peak meter menu | ||
901 | eng: "Minimum of range" | ||
902 | new: | ||
903 | |||
904 | id: LANG_PM_MAX | ||
905 | desc: in the peak meter menu | ||
906 | eng: "Maximum of range" | ||
907 | new: | ||
908 | |||
869 | id: LANG_BACKLIGHT_ON_WHEN_CHARGING | 909 | id: LANG_BACKLIGHT_ON_WHEN_CHARGING |
870 | desc: in display_settings_menu | 910 | desc: in display_settings_menu |
871 | eng: "Backlight On When Plugged" | 911 | eng: "Backlight On When Plugged" |
diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c index df7c672617..e1850a4bb0 100644 --- a/apps/recorder/peakmeter.c +++ b/apps/recorder/peakmeter.c | |||
@@ -46,24 +46,52 @@ static bool peak_meter_r_clip = false; | |||
46 | static long peak_meter_clip_timeout_l; | 46 | static long peak_meter_clip_timeout_l; |
47 | static long peak_meter_clip_timeout_r; | 47 | static long peak_meter_clip_timeout_r; |
48 | 48 | ||
49 | static int peak_meter_clip_hold; | ||
50 | |||
51 | /* specifies the value range in peak volume values */ | ||
52 | unsigned short peak_meter_range_min; | ||
53 | unsigned short peak_meter_range_max; | ||
54 | |||
49 | /* if set to true clip timeout is disabled */ | 55 | /* if set to true clip timeout is disabled */ |
50 | static bool peak_meter_clip_eternal = false; | 56 | static bool peak_meter_clip_eternal = false; |
51 | 57 | ||
58 | static bool peak_meter_use_dbfs = true; | ||
59 | |||
60 | |||
52 | #ifndef SIMULATOR | 61 | #ifndef SIMULATOR |
53 | static int peak_meter_src_l = MAS_REG_DQPEAK_L; | 62 | static int peak_meter_src_l = MAS_REG_DQPEAK_L; |
54 | static int peak_meter_src_r = MAS_REG_DQPEAK_R; | 63 | static int peak_meter_src_r = MAS_REG_DQPEAK_R; |
55 | #endif | 64 | #endif |
56 | 65 | ||
57 | /* temporarily en- / disables peak meter. This is | 66 | /* temporarily en- / disables peak meter. This is |
58 | |||
59 | especially for external applications to detect | 67 | especially for external applications to detect |
60 | |||
61 | if the peak_meter is in use and needs drawing at all */ | 68 | if the peak_meter is in use and needs drawing at all */ |
62 | bool peak_meter_enabled = true; | 69 | bool peak_meter_enabled = true; |
63 | 70 | ||
71 | /* | ||
72 | bool peak_meter_use_thread = false; | ||
73 | static char peak_meter_stack[DEFAULT_STACK_SIZE]; | ||
74 | */ | ||
75 | /* used in wps.c to set the display frame rate of the peak meter */ | ||
76 | int peak_meter_fps = 20; | ||
77 | |||
64 | static int peak_meter_l; | 78 | static int peak_meter_l; |
65 | static int peak_meter_r; | 79 | static int peak_meter_r; |
66 | 80 | ||
81 | static int peak_meter_hold = 1; | ||
82 | static int peak_meter_release = 8; | ||
83 | |||
84 | /* debug only */ | ||
85 | #ifdef PM_DEBUG | ||
86 | static int peek_calls = 0; | ||
87 | |||
88 | #define PEEKS_PER_DRAW_SIZE 40 | ||
89 | static unsigned int peeks_per_redraw[PEEKS_PER_DRAW_SIZE]; | ||
90 | |||
91 | #define TICKS_PER_DRAW_SIZE 20 | ||
92 | static unsigned int ticks_per_redraw[TICKS_PER_DRAW_SIZE]; | ||
93 | #endif | ||
94 | |||
67 | /* time out values for max */ | 95 | /* time out values for max */ |
68 | static long max_time_out[] = { | 96 | static long max_time_out[] = { |
69 | 0 * HZ, HZ / 5, 30, HZ / 2, HZ, 2 * HZ, | 97 | 0 * HZ, HZ / 5, 30, HZ / 2, HZ, 2 * HZ, |
@@ -80,6 +108,353 @@ static long clip_time_out[] = { | |||
80 | 2700 * HZ, 5400 * HZ | 108 | 2700 * HZ, 5400 * HZ |
81 | }; | 109 | }; |
82 | 110 | ||
111 | /* precalculated peak values that represent magical | ||
112 | dBfs values. Used to draw the scale */ | ||
113 | #if 0 | ||
114 | static int db_scale_src_values[] = { | ||
115 | 32767, /* 0 db */ | ||
116 | 23197, /* - 3 db */ | ||
117 | 16422, /* - 6 db */ | ||
118 | 11626, /* - 9 db */ | ||
119 | 8231, /* -12 db */ | ||
120 | 4125, /* -18 db */ | ||
121 | 2067, /* -24 db */ | ||
122 | 1036, /* -30 db */ | ||
123 | 328, /* -40 db */ | ||
124 | 104, /* -50 db */ | ||
125 | 33, /* -60 db */ | ||
126 | }; | ||
127 | #else | ||
128 | static int db_scale_src_values[] = { | ||
129 | 32752, /* 0 db */ | ||
130 | 22784, /* - 3 db */ | ||
131 | 14256, /* - 6 db */ | ||
132 | 11752, /* - 9 db */ | ||
133 | 9256, /* -12 db */ | ||
134 | 4256, /* -18 db */ | ||
135 | 2186, /* -24 db */ | ||
136 | 1186, /* -30 db */ | ||
137 | 373, /* -40 db */ | ||
138 | 102, /* -50 db */ | ||
139 | 33, /* -60 db */ | ||
140 | }; | ||
141 | #endif | ||
142 | |||
143 | int db_scale_count = sizeof db_scale_src_values / sizeof (int); | ||
144 | |||
145 | /* if db_scale_valid is false the content of | ||
146 | db_scale_lcd_coord needs recalculation */ | ||
147 | static bool db_scale_valid = false; | ||
148 | |||
149 | /* contains the lcd x coordinates of the magical | ||
150 | scale values in db_scale_src_values */ | ||
151 | static int db_scale_lcd_coord[sizeof db_scale_src_values / sizeof (int)]; | ||
152 | |||
153 | |||
154 | /** | ||
155 | * Calculates dB Value for the peak meter, uses peak value as input | ||
156 | * @param int sample - The input value | ||
157 | * Make sure that 0 <= value < SAMPLE_RANGE | ||
158 | * | ||
159 | * @return int - The 2 digit fixed comma result of the euation | ||
160 | * 20 * log (sample / SAMPLE_RANGE) + 90 | ||
161 | * Output range is 0-8961 (that is 0,0 - 89,6 dB). | ||
162 | * Normally 0dB is full scale, here it is shifted +90dB. | ||
163 | * The calculation is based on the results of a linear | ||
164 | * approximation tool written specifically for this problem | ||
165 | * by Andreas Zwirtes (radhard@gmx.de). The result hat an | ||
166 | * accurracy of better than 2%. It is highly runtime optimized, | ||
167 | * the cascading if-clauses do an successive approximation on | ||
168 | * the input value. This avoids big lookup-tables and | ||
169 | * for-loops. | ||
170 | */ | ||
171 | |||
172 | int calc_db (int isample) { | ||
173 | /* return n+m*(isample-istart)/100 */ | ||
174 | int n; | ||
175 | long m; | ||
176 | int istart; | ||
177 | |||
178 | /* Range 1-4 */ | ||
179 | if (isample < 119) { | ||
180 | |||
181 | /* Range 1-2 */ | ||
182 | if (isample < 5) { | ||
183 | |||
184 | /* Range 1 */ | ||
185 | if (isample < 1) { | ||
186 | istart = 0; | ||
187 | n = 0; | ||
188 | m = 5900; | ||
189 | } | ||
190 | |||
191 | /* Range 2 */ | ||
192 | else { | ||
193 | istart = 1; | ||
194 | n = 59; | ||
195 | m = 34950; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* Range 3-4 */ | ||
200 | else { | ||
201 | |||
202 | /* Range 3 */ | ||
203 | if (isample < 24) { | ||
204 | istart = 5; | ||
205 | n = 1457; | ||
206 | m = 7168; | ||
207 | } | ||
208 | |||
209 | /* Range 4 */ | ||
210 | else { | ||
211 | istart = 24; | ||
212 | n = 2819; | ||
213 | m = 1464; | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | |||
218 | /* Range 5-8 */ | ||
219 | else { | ||
220 | |||
221 | /* Range 5-6 */ | ||
222 | if (isample < 2918) { | ||
223 | |||
224 | /* Range 5 */ | ||
225 | if (isample < 592) { | ||
226 | istart = 119; | ||
227 | n = 4210; | ||
228 | m = 295; | ||
229 | } | ||
230 | |||
231 | /* Range 6 */ | ||
232 | else { | ||
233 | istart = 592; | ||
234 | n = 5605; | ||
235 | m = 60; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /* Range 7-8 */ | ||
240 | else { | ||
241 | |||
242 | /* Range 7 */ | ||
243 | if (isample < 15352) { | ||
244 | istart = 2918; | ||
245 | n = 7001; | ||
246 | m = 12; | ||
247 | } | ||
248 | |||
249 | /* Range 8 */ | ||
250 | else { | ||
251 | istart = 15352; | ||
252 | n = 8439; | ||
253 | m = 3; | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
258 | return n + (m * (long)(isample - istart)) / 100L; | ||
259 | } | ||
260 | |||
261 | |||
262 | /** | ||
263 | * A helper function for db_to_sample. Don't call it separately but | ||
264 | * use db_to_sample. If one or both of min and max are outside the | ||
265 | * range 0 <= min (or max) < 8961 the behaviour of this function is | ||
266 | * undefined. It may not return. | ||
267 | * @param int min - The minimum of the value range that is searched. | ||
268 | * @param int max - The maximum of the value range that is searched. | ||
269 | * @param int db - The value in dBfs * (-100) for which the according | ||
270 | * minimal peak sample is searched. | ||
271 | * @return int - A linear volume value with 0 <= value < MAX_PEAK | ||
272 | */ | ||
273 | static int db_to_sample_bin_search(int min, int max, int db){ | ||
274 | int test = min + (max - min) / 2; | ||
275 | |||
276 | if (min < max) { | ||
277 | if (calc_db(test) < db) { | ||
278 | test = db_to_sample_bin_search(test, max, db); | ||
279 | } else { | ||
280 | if (calc_db(test-1) > db) { | ||
281 | test = db_to_sample_bin_search(min, test, db); | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | return test; | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * Converts a value representing dBfs to a linear | ||
290 | * scaled volume info as it is used by the MAS. | ||
291 | * An incredibly inefficiant function which is | ||
292 | * the vague inverse of calc_db. This really | ||
293 | * should be replaced by something better soon. | ||
294 | * | ||
295 | * @param int db - A dBfs * 100 value with | ||
296 | * -9000 < value <= 0 | ||
297 | * @return int - The return value is in the range of | ||
298 | * 0 <= return value < MAX_PEAK | ||
299 | */ | ||
300 | static int db_to_sample(int db) { | ||
301 | int retval = 0; | ||
302 | |||
303 | /* what is the maximum pseudo db value */ | ||
304 | int max_peak_db = calc_db(MAX_PEAK - 1); | ||
305 | |||
306 | /* range check: db value to big */ | ||
307 | if (max_peak_db + db < 0) { | ||
308 | retval = 0; | ||
309 | } | ||
310 | |||
311 | /* range check: db value too small */ | ||
312 | else if (max_peak_db + db >= max_peak_db) { | ||
313 | retval = MAX_PEAK -1; | ||
314 | } | ||
315 | |||
316 | /* value in range: find the matching linear value */ | ||
317 | else { | ||
318 | retval = db_to_sample_bin_search(0, MAX_PEAK, max_peak_db + db); | ||
319 | |||
320 | /* as this is a dirty function anyway, we want to adjust the | ||
321 | full scale hit manually to avoid users complaining that when | ||
322 | they adjust maximum for 0 dBfs and display it in percent it | ||
323 | shows 99%. That is due to precision loss and this is the | ||
324 | optical fix */ | ||
325 | } | ||
326 | |||
327 | return retval; | ||
328 | } | ||
329 | |||
330 | /** | ||
331 | * Set the min value for restriction of the value range. | ||
332 | * @param int newmin - depending wether dBfs is used | ||
333 | * newmin is a value in dBfs * 100 or in linear percent values. | ||
334 | * for dBfs: -9000 < newmin <= 0 | ||
335 | * for linear: 0 <= newmin <= 100 | ||
336 | */ | ||
337 | void peak_meter_set_min(int newmin) { | ||
338 | if (peak_meter_use_dbfs) { | ||
339 | peak_meter_range_min = db_to_sample(newmin); | ||
340 | |||
341 | } else { | ||
342 | if (newmin < peak_meter_range_max) { | ||
343 | peak_meter_range_min = newmin * MAX_PEAK / 100; | ||
344 | } | ||
345 | } | ||
346 | db_scale_valid = false; | ||
347 | } | ||
348 | |||
349 | /** | ||
350 | * Returns the minimum value of the range the meter | ||
351 | * displays. If the scale is set to dBfs it returns | ||
352 | * dBfs values * 100 or linear percent values. | ||
353 | * @return: using dBfs : -9000 < value <= 0 | ||
354 | * using linear scale: 0 <= value <= 100 | ||
355 | */ | ||
356 | int peak_meter_get_min(void) { | ||
357 | int retval = 0; | ||
358 | if (peak_meter_use_dbfs) { | ||
359 | retval = calc_db(peak_meter_range_min) - calc_db(MAX_PEAK - 1); | ||
360 | } else { | ||
361 | retval = peak_meter_range_min * 100 / MAX_PEAK; | ||
362 | } | ||
363 | return retval; | ||
364 | } | ||
365 | |||
366 | /** | ||
367 | * Set the max value for restriction of the value range. | ||
368 | * @param int newmax - depending wether dBfs is used | ||
369 | * newmax is a value in dBfs * 100 or in linear percent values. | ||
370 | * for dBfs: -9000 < newmax <= 0 | ||
371 | * for linear: 0 <= newmax <= 100 | ||
372 | */ | ||
373 | void peak_meter_set_max(int newmax) { | ||
374 | if (peak_meter_use_dbfs) { | ||
375 | peak_meter_range_max = db_to_sample(newmax); | ||
376 | } else { | ||
377 | if (newmax > peak_meter_range_min) { | ||
378 | peak_meter_range_max = newmax * MAX_PEAK / 100; | ||
379 | } | ||
380 | } | ||
381 | db_scale_valid = false; | ||
382 | } | ||
383 | |||
384 | /** | ||
385 | * Returns the minimum value of the range the meter | ||
386 | * displays. If the scale is set to dBfs it returns | ||
387 | * dBfs values * 100 or linear percent values | ||
388 | * @return: using dBfs : -9000 < value <= 0 | ||
389 | * using linear scale: 0 <= value <= 100 | ||
390 | */ | ||
391 | int peak_meter_get_max(void) { | ||
392 | int retval = 0; | ||
393 | if (peak_meter_use_dbfs) { | ||
394 | retval = calc_db(peak_meter_range_max) - calc_db(MAX_PEAK - 1); | ||
395 | } else { | ||
396 | retval = peak_meter_range_max * 100 / MAX_PEAK; | ||
397 | } | ||
398 | return retval; | ||
399 | } | ||
400 | |||
401 | /** | ||
402 | * Returns 1 if the meter currently is | ||
403 | * displaying dBfs values, 0 if the meter | ||
404 | * displays percent values. | ||
405 | * @return int - returns 0 or 1. | ||
406 | */ | ||
407 | int peak_meter_get_use_dbfs(void) { | ||
408 | return peak_meter_use_dbfs ? 1 : 0; | ||
409 | } | ||
410 | |||
411 | /** | ||
412 | * Specifies wether the values displayed are scaled | ||
413 | * as dBfs or as linear percent values. | ||
414 | * @param int - Set to 0 for linear percent scale. Any other value | ||
415 | * switches on dBfs. | ||
416 | */ | ||
417 | void peak_meter_set_use_dbfs(int use){ | ||
418 | peak_meter_use_dbfs = ((use & 1) == 1); | ||
419 | db_scale_valid = false; | ||
420 | } | ||
421 | |||
422 | /** | ||
423 | * Initialize the range of the meter. Only values | ||
424 | * that are in the range of [range_min ... range_max] | ||
425 | * are displayed. | ||
426 | * @param bool dbfs - set to true for dBfs, | ||
427 | * set to false for linear scaling in percent | ||
428 | * @param int range_min - Specifies the lower value of the range. | ||
429 | * Pass a value dBfs * 100 when dbfs is set to true. | ||
430 | * Pass a percent value when dbfs is set to false. | ||
431 | * @param int range_max - Specifies the upper value of the range. | ||
432 | * Pass a value dBfs * 100 when dbfs is set to true. | ||
433 | * Pass a percent value when dbfs is set to false. | ||
434 | */ | ||
435 | void peak_meter_init_range( bool dbfs, int range_min, int range_max) { | ||
436 | peak_meter_use_dbfs = dbfs; | ||
437 | peak_meter_set_min(range_min); | ||
438 | peak_meter_set_max(range_max); | ||
439 | } | ||
440 | |||
441 | /** | ||
442 | * Initialize the peak meter with all relevant values concerning times. | ||
443 | * @param int release - Set the maximum amount of pixels the meter is allowed | ||
444 | * to decrease with each redraw | ||
445 | * @param int hold - Select the time preset for the time the peak indicator | ||
446 | * is reset after a peak occurred. The preset values are | ||
447 | * stored in max_time_out. | ||
448 | * @param int clip_hold - Select the time preset for the time the peak | ||
449 | * indicator is reset after a peak occurred. The preset | ||
450 | * values are stored in clip_time_out. | ||
451 | */ | ||
452 | void peak_meter_init_times(int release, int hold, int clip_hold) { | ||
453 | peak_meter_hold = hold; | ||
454 | peak_meter_release = release; | ||
455 | peak_meter_clip_hold = clip_hold; | ||
456 | } | ||
457 | |||
83 | /** | 458 | /** |
84 | * Set the source of the peak meter to playback or to | 459 | * Set the source of the peak meter to playback or to |
85 | * record. | 460 | * record. |
@@ -127,20 +502,58 @@ void peak_meter_peek(void) { | |||
127 | (left == MAX_PEAK - 1)) { | 502 | (left == MAX_PEAK - 1)) { |
128 | peak_meter_l_clip = true; | 503 | peak_meter_l_clip = true; |
129 | peak_meter_clip_timeout_l = | 504 | peak_meter_clip_timeout_l = |
130 | current_tick + clip_time_out[global_settings.peak_meter_clip_hold]; | 505 | current_tick + clip_time_out[peak_meter_clip_hold]; |
131 | } | 506 | } |
132 | 507 | ||
133 | if ((right == peak_meter_r) && | 508 | if ((right == peak_meter_r) && |
134 | (right == MAX_PEAK - 1)) { | 509 | (right == MAX_PEAK - 1)) { |
135 | peak_meter_r_clip = true; | 510 | peak_meter_r_clip = true; |
136 | peak_meter_clip_timeout_r = | 511 | peak_meter_clip_timeout_r = |
137 | current_tick + clip_time_out[global_settings.peak_meter_clip_hold]; | 512 | current_tick + clip_time_out[peak_meter_clip_hold]; |
138 | } | 513 | } |
139 | 514 | ||
140 | /* peaks are searched -> we have to find the maximum */ | 515 | /* peaks are searched -> we have to find the maximum. When |
516 | many calls of peak_meter_peek the maximum value will be | ||
517 | stored in peak_meter_x. This maximum is reset by the | ||
518 | functions peak_meter_read_x. */ | ||
141 | peak_meter_l = MAX(peak_meter_l, left); | 519 | peak_meter_l = MAX(peak_meter_l, left); |
142 | peak_meter_r = MAX(peak_meter_r, right); | 520 | peak_meter_r = MAX(peak_meter_r, right); |
521 | |||
522 | #ifdef PM_DEBUG | ||
523 | peek_calls++; | ||
524 | #endif | ||
525 | } | ||
526 | |||
527 | |||
528 | /** | ||
529 | * The thread function for the peak meter calls peak_meter_peek | ||
530 | * to reas out the mas and find maxima, clips, etc. No display | ||
531 | * is performed. | ||
532 | */ | ||
533 | /* | ||
534 | void peak_meter_thread(void) { | ||
535 | sleep(5000); | ||
536 | while (1) { | ||
537 | if (peak_meter_enabled && peak_meter_use_thread){ | ||
538 | peak_meter_peek(); | ||
539 | } | ||
540 | yield(); | ||
541 | } | ||
542 | } | ||
543 | */ | ||
544 | |||
545 | /** | ||
546 | * Creates the peak meter thread | ||
547 | */ | ||
548 | /* | ||
549 | void peak_meter_init(void) { | ||
550 | create_thread( | ||
551 | peak_meter_thread, | ||
552 | peak_meter_stack, | ||
553 | sizeof peak_meter_stack, | ||
554 | "peakmeter"); | ||
143 | } | 555 | } |
556 | */ | ||
144 | 557 | ||
145 | /** | 558 | /** |
146 | * Reads out the peak volume of the left channel. | 559 | * Reads out the peak volume of the left channel. |
@@ -149,10 +562,20 @@ void peak_meter_peek(void) { | |||
149 | * is in the range 0 <= value < MAX_PEAK. | 562 | * is in the range 0 <= value < MAX_PEAK. |
150 | */ | 563 | */ |
151 | static int peak_meter_read_l (void) { | 564 | static int peak_meter_read_l (void) { |
565 | /* peak_meter_l contains the maximum of | ||
566 | all peak values that were read by peak_meter_peek | ||
567 | since the last call of peak_meter_read_r */ | ||
152 | int retval = peak_meter_l; | 568 | int retval = peak_meter_l; |
569 | #ifdef PM_DEBUG | ||
570 | peek_calls = 0; | ||
571 | #endif | ||
572 | |||
153 | #ifdef SIMULATOR | 573 | #ifdef SIMULATOR |
154 | peak_meter_l = 8000; | 574 | peak_meter_l = 8000; |
155 | #else | 575 | #else |
576 | /* reset peak_meter_l so that subsequent calls of | ||
577 | peak_meter_peek doesn't get fooled by an old | ||
578 | maximum value */ | ||
156 | peak_meter_l = mas_codec_readreg(peak_meter_src_l); | 579 | peak_meter_l = mas_codec_readreg(peak_meter_src_l); |
157 | #endif | 580 | #endif |
158 | return retval; | 581 | return retval; |
@@ -165,10 +588,20 @@ static int peak_meter_read_l (void) { | |||
165 | * is in the range 0 <= value < MAX_PEAK. | 588 | * is in the range 0 <= value < MAX_PEAK. |
166 | */ | 589 | */ |
167 | static int peak_meter_read_r (void) { | 590 | static int peak_meter_read_r (void) { |
591 | /* peak_meter_r contains the maximum of | ||
592 | all peak values that were read by peak_meter_peek | ||
593 | since the last call of peak_meter_read_r */ | ||
168 | int retval = peak_meter_r; | 594 | int retval = peak_meter_r; |
595 | #ifdef PM_DEBUG | ||
596 | peek_calls = 0; | ||
597 | #endif | ||
598 | |||
169 | #ifdef SIMULATOR | 599 | #ifdef SIMULATOR |
170 | peak_meter_l = 8000; | 600 | peak_meter_l = 8000; |
171 | #else | 601 | #else |
602 | /* reset peak_meter_r so that subsequent calls of | ||
603 | peak_meter_peek doesn't get fooled by an old | ||
604 | maximum value */ | ||
172 | peak_meter_r = mas_codec_readreg(peak_meter_src_r); | 605 | peak_meter_r = mas_codec_readreg(peak_meter_src_r); |
173 | #endif | 606 | #endif |
174 | return retval; | 607 | return retval; |
@@ -190,6 +623,51 @@ void peak_meter_set_clip_hold(int time) { | |||
190 | } | 623 | } |
191 | } | 624 | } |
192 | 625 | ||
626 | /** | ||
627 | * Scales a peak value as read from the MAS to the range of meterwidth. | ||
628 | * The scaling is performed according to the scaling method (dBfs / linear) | ||
629 | * and the range (peak_meter_range_min .. peak_meter_range_max). | ||
630 | * @param unsigned short val - The volume value. Range: 0 <= val < MAX_PEAK | ||
631 | * @param int meterwidht - The widht of the meter in pixel | ||
632 | * @return unsigned short - A value 0 <= return value <= meterwidth | ||
633 | */ | ||
634 | unsigned short peak_meter_scale_value(unsigned short val, int meterwidth){ | ||
635 | int range; | ||
636 | int retval; | ||
637 | |||
638 | if (val <= peak_meter_range_min) { | ||
639 | return 0; | ||
640 | } | ||
641 | |||
642 | if (val >= peak_meter_range_max) { | ||
643 | return meterwidth; | ||
644 | } | ||
645 | |||
646 | retval = val; | ||
647 | |||
648 | /* different scaling is used for dBfs and linear percent */ | ||
649 | if (peak_meter_use_dbfs) { | ||
650 | |||
651 | /* needed the offset in 'zoomed' meters */ | ||
652 | int dbmin = calc_db(peak_meter_range_min); | ||
653 | |||
654 | range = calc_db(peak_meter_range_max) - dbmin; | ||
655 | |||
656 | /* scale the samples dBfs */ | ||
657 | retval = (calc_db(retval) - dbmin) * meterwidth / range; | ||
658 | } | ||
659 | |||
660 | /* Scale for linear percent display */ | ||
661 | else | ||
662 | { | ||
663 | range =(peak_meter_range_max - peak_meter_range_min); | ||
664 | |||
665 | /* scale the samples */ | ||
666 | retval = ((retval - peak_meter_range_min) * meterwidth) / range; | ||
667 | } | ||
668 | return retval; | ||
669 | } | ||
670 | |||
193 | 671 | ||
194 | /** | 672 | /** |
195 | * Draws a peak meter in the specified size at the specified position. | 673 | * Draws a peak meter in the specified size at the specified position. |
@@ -208,22 +686,66 @@ void peak_meter_draw(int x, int y, int width, int height) { | |||
208 | int meterwidth = width - 3; | 686 | int meterwidth = width - 3; |
209 | int i; | 687 | int i; |
210 | 688 | ||
689 | #ifdef PM_DEBUG | ||
690 | static long pm_tick = 0; | ||
691 | int tmp = peek_calls; | ||
692 | #endif | ||
693 | |||
211 | /* if disabled only draw the peak meter */ | 694 | /* if disabled only draw the peak meter */ |
212 | if (peak_meter_enabled) { | 695 | if (peak_meter_enabled) { |
696 | |||
213 | /* read the volume info from MAS */ | 697 | /* read the volume info from MAS */ |
214 | left = peak_meter_read_l(); | 698 | left = peak_meter_read_l(); |
215 | right = peak_meter_read_r(); | 699 | right = peak_meter_read_r(); |
216 | |||
217 | peak_meter_peek(); | 700 | peak_meter_peek(); |
218 | 701 | ||
702 | /* restrict the range to avoid drawing outside the lcd */ | ||
703 | left = MAX(peak_meter_range_min, left); | ||
704 | left = MIN(peak_meter_range_max, left); | ||
705 | |||
706 | right = MAX(peak_meter_range_min, right); | ||
707 | right = MIN(peak_meter_range_max, right); | ||
708 | |||
709 | /* scale the samples dBfs */ | ||
710 | left = peak_meter_scale_value(left, meterwidth); | ||
711 | right = peak_meter_scale_value(right, meterwidth); | ||
712 | |||
713 | /* if the scale has changed -> recalculate the scale | ||
714 | (The scale becomes invalid when the range changed.) */ | ||
715 | if (!db_scale_valid){ | ||
716 | |||
717 | if (peak_meter_use_dbfs) { | ||
718 | db_scale_count = sizeof db_scale_src_values / sizeof (int); | ||
719 | for (i = 0; i < db_scale_count; i++){ | ||
720 | /* find the real x-coords for predefined interesting | ||
721 | dBfs values. These only are recalculated when the | ||
722 | scaling of the meter changed. */ | ||
723 | db_scale_lcd_coord[i] = | ||
724 | peak_meter_scale_value( | ||
725 | db_scale_src_values[i], | ||
726 | meterwidth - 1); | ||
727 | } | ||
728 | } | ||
219 | 729 | ||
220 | /* scale the samples */ | 730 | /* when scaling linear we simly make 10% steps */ |
221 | left /= (MAX_PEAK / meterwidth); | 731 | else { |
222 | right /= (MAX_PEAK / meterwidth); | 732 | int range = peak_meter_range_max - peak_meter_range_min; |
733 | db_scale_count = 10; | ||
734 | for (i = 0; i < db_scale_count; i++) { | ||
735 | db_scale_lcd_coord[i] = | ||
736 | (i * (MAX_PEAK / 10) - peak_meter_range_min) * | ||
737 | meterwidth / range; | ||
738 | } | ||
739 | } | ||
740 | |||
741 | /* mark scale valid to avoid recalculating dBfs values | ||
742 | of the scale. */ | ||
743 | db_scale_valid = true; | ||
744 | } | ||
223 | 745 | ||
224 | /* apply release */ | 746 | /* apply release */ |
225 | left = MAX(left , last_left - global_settings.peak_meter_release); | 747 | left = MAX(left , last_left - peak_meter_release); |
226 | right = MAX(right, last_right - global_settings.peak_meter_release); | 748 | right = MAX(right, last_right - peak_meter_release); |
227 | 749 | ||
228 | /* reset max values after timeout */ | 750 | /* reset max values after timeout */ |
229 | if (TIME_AFTER(current_tick, peak_meter_timeout_l)){ | 751 | if (TIME_AFTER(current_tick, peak_meter_timeout_l)){ |
@@ -250,13 +772,13 @@ void peak_meter_draw(int x, int y, int width, int height) { | |||
250 | if (left > peak_meter_max_l) { | 772 | if (left > peak_meter_max_l) { |
251 | peak_meter_max_l = left - 1; | 773 | peak_meter_max_l = left - 1; |
252 | peak_meter_timeout_l = | 774 | peak_meter_timeout_l = |
253 | current_tick + max_time_out[global_settings.peak_meter_hold]; | 775 | current_tick + max_time_out[peak_meter_hold]; |
254 | } | 776 | } |
255 | 777 | ||
256 | if (right > peak_meter_max_r) { | 778 | if (right > peak_meter_max_r) { |
257 | peak_meter_max_r = right - 1; | 779 | peak_meter_max_r = right - 1; |
258 | peak_meter_timeout_r = | 780 | peak_meter_timeout_r = |
259 | current_tick + max_time_out[global_settings.peak_meter_hold]; | 781 | current_tick + max_time_out[peak_meter_hold]; |
260 | } | 782 | } |
261 | } | 783 | } |
262 | 784 | ||
@@ -283,13 +805,90 @@ void peak_meter_draw(int x, int y, int width, int height) { | |||
283 | lcd_fillrect(x + meterwidth, y + height / 2, 3, height / 2 - 1); | 805 | lcd_fillrect(x + meterwidth, y + height / 2, 3, height / 2 - 1); |
284 | } | 806 | } |
285 | 807 | ||
286 | /* draw scale */ | 808 | /* draw scale end */ |
287 | lcd_drawline(x + meterwidth, y, | 809 | lcd_drawline(x + meterwidth, y, |
288 | x + meterwidth, y + height - 2); | 810 | x + meterwidth, y + height - 2); |
289 | for (i = 0; i < 10; i++) { | 811 | |
290 | lcd_invertpixel(x + meterwidth * i / 10, y + height / 2 - 1); | 812 | /* draw dots for scale marks */ |
813 | for (i = 0; i < db_scale_count; i++) { | ||
814 | /* The x-coordinates of interesting scale mark points | ||
815 | have been calculated before */ | ||
816 | lcd_invertpixel(db_scale_lcd_coord[i], y + height / 2 - 1); | ||
817 | } | ||
818 | |||
819 | #ifdef PM_DEBUG | ||
820 | /* display a bar to show how many calls to peak_meter_peek | ||
821 | have ocurred since the last display */ | ||
822 | lcd_invertrect(x, y, tmp, 3); | ||
823 | |||
824 | if (tmp < PEEKS_PER_DRAW_SIZE) { | ||
825 | peeks_per_redraw[tmp]++; | ||
826 | } | ||
827 | |||
828 | tmp = current_tick - pm_tick; | ||
829 | if (tmp < TICKS_PER_DRAW_SIZE ){ | ||
830 | ticks_per_redraw[tmp] ++; | ||
291 | } | 831 | } |
292 | 832 | ||
833 | /* display a bar to show how many ticks have passed since | ||
834 | the last redraw */ | ||
835 | lcd_invertrect(x, y + height / 2, current_tick - pm_tick, 2); | ||
836 | pm_tick = current_tick; | ||
837 | #endif | ||
838 | |||
293 | last_left = left; | 839 | last_left = left; |
294 | last_right = right; | 840 | last_right = right; |
295 | } | 841 | } |
842 | |||
843 | #ifdef PM_DEBUG | ||
844 | static void peak_meter_clear_histogram(void) { | ||
845 | int i = 0; | ||
846 | for (i = 0; i < TICKS_PER_DRAW_SIZE; i++) { | ||
847 | ticks_per_redraw[i] = (unsigned int)0; | ||
848 | } | ||
849 | |||
850 | for (i = 0; i < PEEKS_PER_DRAW_SIZE; i++) { | ||
851 | peeks_per_redraw[i] = (unsigned int)0; | ||
852 | } | ||
853 | } | ||
854 | |||
855 | bool peak_meter_histogram(void) { | ||
856 | int i; | ||
857 | int btn = BUTTON_NONE; | ||
858 | while ((btn & BUTTON_OFF) != BUTTON_OFF ) | ||
859 | { | ||
860 | unsigned int max = 0; | ||
861 | int y = 0; | ||
862 | int x = 0; | ||
863 | lcd_clear_display(); | ||
864 | |||
865 | for (i = 0; i < PEEKS_PER_DRAW_SIZE; i++) { | ||
866 | max = MAX(max, peeks_per_redraw[i]); | ||
867 | } | ||
868 | |||
869 | for (i = 0; i < PEEKS_PER_DRAW_SIZE; i++) { | ||
870 | x = peeks_per_redraw[i] * (LCD_WIDTH - 1)/ max; | ||
871 | lcd_drawline(0, y + i, x, y + i); | ||
872 | } | ||
873 | |||
874 | y = PEEKS_PER_DRAW_SIZE + 1; | ||
875 | max = 0; | ||
876 | |||
877 | for (i = 0; i < TICKS_PER_DRAW_SIZE; i++) { | ||
878 | max = MAX(max, ticks_per_redraw[i]); | ||
879 | } | ||
880 | |||
881 | for (i = 0; i < TICKS_PER_DRAW_SIZE; i++) { | ||
882 | x = ticks_per_redraw[i] * (LCD_WIDTH - 1)/ max; | ||
883 | lcd_drawline(0, y + i, x, y + i); | ||
884 | } | ||
885 | lcd_update(); | ||
886 | |||
887 | btn = button_get(true); | ||
888 | if (btn == BUTTON_PLAY) { | ||
889 | peak_meter_clear_histogram(); | ||
890 | } | ||
891 | } | ||
892 | return false; | ||
893 | } | ||
894 | #endif | ||
diff --git a/apps/recorder/peakmeter.h b/apps/recorder/peakmeter.h index a5f25808e6..356926f9de 100644 --- a/apps/recorder/peakmeter.h +++ b/apps/recorder/peakmeter.h | |||
@@ -19,12 +19,32 @@ | |||
19 | #ifndef __PEAKMETER_H__ | 19 | #ifndef __PEAKMETER_H__ |
20 | #define __PEAKMETER_H__ | 20 | #define __PEAKMETER_H__ |
21 | 21 | ||
22 | /*#define PM_DEBUG */ | ||
23 | #ifdef PM_DEBUG | ||
24 | extern bool peak_meter_histogramm(void); | ||
25 | #endif | ||
26 | |||
27 | |||
22 | extern bool peak_meter_enabled; | 28 | extern bool peak_meter_enabled; |
29 | extern int peak_meter_fps; | ||
23 | 30 | ||
24 | extern void peak_meter_init(void); | ||
25 | extern void peak_meter_playback(bool playback); | 31 | extern void peak_meter_playback(bool playback); |
26 | extern void peak_meter_draw(int x, int y, int width, int height); | 32 | extern void peak_meter_draw(int x, int y, int width, int height); |
27 | extern void peak_meter_set_clip_hold(int time); | 33 | extern void peak_meter_set_clip_hold(int time); |
28 | extern void peak_meter_peek(void); | 34 | extern void peak_meter_peek(void); |
35 | extern void peak_meter_init_range( bool dbfs, int range_min, int range_max); | ||
36 | extern void peak_meter_init_times(int release, int hold, int clip_hold); | ||
37 | |||
38 | extern void peak_meter_set_min(int newmin); | ||
39 | extern int peak_meter_get_min(void); | ||
40 | extern void peak_meter_set_max(int newmax); | ||
41 | extern int peak_meter_get_max(void); | ||
42 | extern void peak_meter_set_use_dbfs(int use); | ||
43 | extern int peak_meter_get_use_dbfs(void); | ||
44 | extern int calc_db (int isample); | ||
45 | extern unsigned short peak_meter_scale_value(unsigned short val, int meterwidth); | ||
46 | |||
47 | extern unsigned short peak_meter_range_min; | ||
48 | extern unsigned short peak_meter_range_max; | ||
29 | 49 | ||
30 | #endif /* __PEAKMETER_H__ */ | 50 | #endif /* __PEAKMETER_H__ */ |
diff --git a/apps/settings.c b/apps/settings.c index cee7a286f6..c17f42fd52 100644 --- a/apps/settings.c +++ b/apps/settings.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #ifdef HAVE_LCD_BITMAP | 44 | #ifdef HAVE_LCD_BITMAP |
45 | #include "icons.h" | 45 | #include "icons.h" |
46 | #include "font.h" | 46 | #include "font.h" |
47 | #include "peakmeter.h" | ||
47 | #endif | 48 | #endif |
48 | #include "lang.h" | 49 | #include "lang.h" |
49 | #include "language.h" | 50 | #include "language.h" |
@@ -52,7 +53,7 @@ | |||
52 | struct user_settings global_settings; | 53 | struct user_settings global_settings; |
53 | char rockboxdir[] = ROCKBOX_DIR; /* config/font/data file directory */ | 54 | char rockboxdir[] = ROCKBOX_DIR; /* config/font/data file directory */ |
54 | 55 | ||
55 | #define CONFIG_BLOCK_VERSION 2 | 56 | #define CONFIG_BLOCK_VERSION 3 |
56 | #define CONFIG_BLOCK_SIZE 512 | 57 | #define CONFIG_BLOCK_SIZE 512 |
57 | #define RTC_BLOCK_SIZE 44 | 58 | #define RTC_BLOCK_SIZE 44 |
58 | 59 | ||
@@ -89,10 +90,14 @@ offset abs | |||
89 | 0x16 0x2a <(int) Byte offset into resume file> | 90 | 0x16 0x2a <(int) Byte offset into resume file> |
90 | 0x1a 0x2e <time until disk spindown> | 91 | 0x1a 0x2e <time until disk spindown> |
91 | 0x1b 0x2f <browse current, play selected> | 92 | 0x1b 0x2f <browse current, play selected> |
92 | 0x1c 0x30 <peak meter hold timeout (bit 0-4)> | 93 | 0x1c 0x30 <peak meter hold timeout (bit 0-4)>, |
93 | 0x1d 0x31 <peak meter clip hold timeout (bit 0-4)> | 94 | peak_meter_performance (bit 7) |
94 | 0x1e 0x32 <peak meter release step size> | 95 | 0x1d 0x31 <peak meter clip hold timeout (bit 0-4) |
95 | 0x1f 0x33 <repeat mode> | 96 | 0x1e 0x32 <peak meter release step size, |
97 | peak_meter_dbfs (bit 7) | ||
98 | 0x1f 0x33 <peak meter min either in -db or in percent> | ||
99 | 0x20 0x34 <peak meter max either in -db or in percent> | ||
100 | 0x21 0x35 <repeat mode> | ||
96 | 101 | ||
97 | <all unused space filled with 0xff> | 102 | <all unused space filled with 0xff> |
98 | 103 | ||
@@ -316,9 +321,13 @@ int settings_save( void ) | |||
316 | ((global_settings.play_selected & 1) << 1)); | 321 | ((global_settings.play_selected & 1) << 1)); |
317 | 322 | ||
318 | config_block[0x1c] = (unsigned char)global_settings.peak_meter_hold; | 323 | config_block[0x1c] = (unsigned char)global_settings.peak_meter_hold; |
319 | config_block[0x1d] = (unsigned char)global_settings.peak_meter_clip_hold; | 324 | config_block[0x1d] = (unsigned char)global_settings.peak_meter_clip_hold | |
320 | config_block[0x1e] = (unsigned char)global_settings.peak_meter_release; | 325 | (global_settings.peak_meter_performance ? 0x80 : 0); |
321 | config_block[0x1f] = (unsigned char)global_settings.repeat_mode; | 326 | config_block[0x1e] = global_settings.peak_meter_release | |
327 | (global_settings.peak_meter_dbfs ? 0x80 : 0); | ||
328 | config_block[0x1f] = (unsigned char)global_settings.peak_meter_min; | ||
329 | config_block[0x20] = (unsigned char)global_settings.peak_meter_max; | ||
330 | config_block[0x21] = (unsigned char)global_settings.repeat_mode; | ||
322 | 331 | ||
323 | memcpy(&config_block[0x24], &global_settings.total_uptime, 4); | 332 | memcpy(&config_block[0x24], &global_settings.total_uptime, 4); |
324 | 333 | ||
@@ -355,6 +364,35 @@ int settings_save( void ) | |||
355 | return 0; | 364 | return 0; |
356 | } | 365 | } |
357 | 366 | ||
367 | #ifdef HAVE_LCD_BITMAP | ||
368 | /** | ||
369 | * Applies the range infos stored in global_settings to | ||
370 | * the peak meter. | ||
371 | */ | ||
372 | void settings_apply_pm_range(void) | ||
373 | { | ||
374 | int pm_min, pm_max; | ||
375 | |||
376 | /* depending on the scale mode (dBfs or percent) the values | ||
377 | of global_settings.peak_meter_dbfs have different meanings */ | ||
378 | if (global_settings.peak_meter_dbfs) | ||
379 | { | ||
380 | /* convert to dBfs * 100 */ | ||
381 | pm_min = -(((int)global_settings.peak_meter_min) * 100); | ||
382 | pm_max = -(((int)global_settings.peak_meter_max) * 100); | ||
383 | } | ||
384 | else | ||
385 | { | ||
386 | /* percent is stored directly -> no conversion */ | ||
387 | pm_min = global_settings.peak_meter_min; | ||
388 | pm_max = global_settings.peak_meter_max; | ||
389 | } | ||
390 | |||
391 | /* apply the range */ | ||
392 | peak_meter_init_range(global_settings.peak_meter_dbfs, pm_min, pm_max); | ||
393 | } | ||
394 | #endif /* HAVE_LCD_BITMAP */ | ||
395 | |||
358 | void settings_apply(void) | 396 | void settings_apply(void) |
359 | { | 397 | { |
360 | char buf[64]; | 398 | char buf[64]; |
@@ -381,6 +419,13 @@ void settings_apply(void) | |||
381 | CHARGE_RESTART_LO : CHARGE_RESTART_HI; | 419 | CHARGE_RESTART_LO : CHARGE_RESTART_HI; |
382 | #endif | 420 | #endif |
383 | 421 | ||
422 | #ifdef HAVE_LCD_BITMAP | ||
423 | settings_apply_pm_range(); | ||
424 | peak_meter_init_times( | ||
425 | global_settings.peak_meter_release, global_settings.peak_meter_hold, | ||
426 | global_settings.peak_meter_clip_hold); | ||
427 | #endif | ||
428 | |||
384 | if ( global_settings.wps_file[0] && | 429 | if ( global_settings.wps_file[0] && |
385 | global_settings.wps_file[0] != 0xff ) { | 430 | global_settings.wps_file[0] != 0xff ) { |
386 | snprintf(buf, sizeof buf, ROCKBOX_DIR "/%s.wps", | 431 | snprintf(buf, sizeof buf, ROCKBOX_DIR "/%s.wps", |
@@ -500,14 +545,25 @@ void settings_load(void) | |||
500 | if (config_block[0x1c] != 0xFF) | 545 | if (config_block[0x1c] != 0xFF) |
501 | global_settings.peak_meter_hold = (config_block[0x1c]) & 0x1f; | 546 | global_settings.peak_meter_hold = (config_block[0x1c]) & 0x1f; |
502 | 547 | ||
503 | if (config_block[0x1d] != 0xFF) | 548 | if (config_block[0x1d] != 0xFF) { |
504 | global_settings.peak_meter_clip_hold = (config_block[0x1d]) & 0x1f; | 549 | global_settings.peak_meter_clip_hold = (config_block[0x1d]) & 0x1f; |
550 | global_settings.peak_meter_performance = | ||
551 | (config_block[0x1d] & 0x80) != 0; | ||
552 | } | ||
505 | 553 | ||
506 | if (config_block[0x1e] != 0xFF) | 554 | if (config_block[0x1e] != 0xFF) { |
507 | global_settings.peak_meter_release = config_block[0x1e]; | 555 | global_settings.peak_meter_release = config_block[0x1e] & 0x7f; |
556 | global_settings.peak_meter_dbfs = (config_block[0x1e] & 0x80) != 0; | ||
557 | } | ||
508 | 558 | ||
509 | if (config_block[0x1f] != 0xFF) | 559 | if (config_block[0x1f] != 0xFF) |
510 | global_settings.repeat_mode = config_block[0x1f]; | 560 | global_settings.peak_meter_min = config_block[0x1f]; |
561 | |||
562 | if (config_block[0x20] != 0xFF) | ||
563 | global_settings.peak_meter_max = config_block[0x20]; | ||
564 | |||
565 | if (config_block[0x21] != 0xFF) | ||
566 | global_settings.repeat_mode = config_block[0x21]; | ||
511 | 567 | ||
512 | if (config_block[0x24] != 0xFF) | 568 | if (config_block[0x24] != 0xFF) |
513 | memcpy(&global_settings.total_uptime, &config_block[0x24], 4); | 569 | memcpy(&global_settings.total_uptime, &config_block[0x24], 4); |
@@ -685,8 +741,12 @@ void settings_reset(void) { | |||
685 | global_settings.browse_current = false; | 741 | global_settings.browse_current = false; |
686 | global_settings.play_selected = true; | 742 | global_settings.play_selected = true; |
687 | global_settings.peak_meter_release = 8; | 743 | global_settings.peak_meter_release = 8; |
688 | global_settings.peak_meter_hold = 1; | 744 | global_settings.peak_meter_hold = 3; |
689 | global_settings.peak_meter_clip_hold = 16; | 745 | global_settings.peak_meter_clip_hold = 16; |
746 | global_settings.peak_meter_dbfs = true; | ||
747 | global_settings.peak_meter_min = 60; | ||
748 | global_settings.peak_meter_max = 0; | ||
749 | global_settings.peak_meter_performance = false; | ||
690 | global_settings.wps_file[0] = 0; | 750 | global_settings.wps_file[0] = 0; |
691 | global_settings.font_file[0] = 0; | 751 | global_settings.font_file[0] = 0; |
692 | global_settings.lang_file[0] = 0; | 752 | global_settings.lang_file[0] = 0; |
diff --git a/apps/settings.h b/apps/settings.h index bc8767d532..655b2e0245 100644 --- a/apps/settings.h +++ b/apps/settings.h | |||
@@ -100,6 +100,10 @@ struct user_settings | |||
100 | int peak_meter_release; /* units per read out */ | 100 | int peak_meter_release; /* units per read out */ |
101 | int peak_meter_hold; /* hold time for peak meter in 1/100 s */ | 101 | int peak_meter_hold; /* hold time for peak meter in 1/100 s */ |
102 | int peak_meter_clip_hold; /* hold time for clips */ | 102 | int peak_meter_clip_hold; /* hold time for clips */ |
103 | bool peak_meter_dbfs; /* show linear or dbfs values */ | ||
104 | bool peak_meter_performance; /* true: high performance, else save energy*/ | ||
105 | unsigned char peak_meter_min; /* range minimum */ | ||
106 | unsigned char peak_meter_max; /* range maximum */ | ||
103 | 107 | ||
104 | /* show status bar */ | 108 | /* show status bar */ |
105 | bool statusbar; /* 0=hide, 1=show */ | 109 | bool statusbar; /* 0=hide, 1=show */ |
@@ -121,6 +125,7 @@ int settings_save(void); | |||
121 | void settings_load(void); | 125 | void settings_load(void); |
122 | void settings_reset(void); | 126 | void settings_reset(void); |
123 | void settings_apply(void); | 127 | void settings_apply(void); |
128 | void settings_apply_pm_range(void); | ||
124 | void settings_display(void); | 129 | void settings_display(void); |
125 | 130 | ||
126 | bool settings_load_config(char* file); | 131 | bool settings_load_config(char* file); |
diff --git a/apps/settings_menu.c b/apps/settings_menu.c index 6f527f5224..ed2f97e390 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c | |||
@@ -74,27 +74,44 @@ static bool volume_type(void) | |||
74 | names, 2, NULL); | 74 | names, 2, NULL); |
75 | } | 75 | } |
76 | 76 | ||
77 | #ifdef PM_DEBUG | ||
78 | static bool peak_meter_fps_menu(void) { | ||
79 | bool retval = false; | ||
80 | retval = set_int( "Refresh rate", "/s", | ||
81 | &peak_meter_fps, | ||
82 | NULL, 1, 5, 40); | ||
83 | return retval; | ||
84 | } | ||
85 | #endif /* PM_DEBUG */ | ||
86 | |||
77 | /** | 87 | /** |
78 | * Menu to set the hold time of normal peaks. | 88 | * Menu to set the hold time of normal peaks. |
79 | */ | 89 | */ |
80 | static bool peak_meter_hold(void) | 90 | static bool peak_meter_hold(void) { |
81 | { | 91 | bool retval = false; |
82 | char* names[] = { str(LANG_OFF), | 92 | char* names[] = { str(LANG_OFF), |
83 | "200 ms ", "300 ms ", "500 ms ", "1 s ", "2 s ", | 93 | "200 ms ", "300 ms ", "500 ms ", "1 s ", "2 s ", |
84 | "3 s ", "4 s ", "5 s ", "6 s ", "7 s", | 94 | "3 s ", "4 s ", "5 s ", "6 s ", "7 s", |
85 | "8 s", "9 s", "10 s", "15 s", "20 s", | 95 | "8 s", "9 s", "10 s", "15 s", "20 s", |
86 | "30 s", "1 min" | 96 | "30 s", "1 min" |
87 | }; | 97 | }; |
88 | return set_option( str(LANG_PM_PEAK_HOLD), | 98 | retval = set_option( str(LANG_PM_PEAK_HOLD), |
89 | &global_settings.peak_meter_hold, names, | 99 | &global_settings.peak_meter_hold, names, |
90 | 18, NULL); | 100 | 18, NULL); |
101 | |||
102 | peak_meter_init_times(global_settings.peak_meter_release, | ||
103 | global_settings.peak_meter_hold, | ||
104 | global_settings.peak_meter_clip_hold); | ||
105 | |||
106 | return retval; | ||
91 | } | 107 | } |
92 | 108 | ||
93 | /** | 109 | /** |
94 | * Menu to set the hold time of clips. | 110 | * Menu to set the hold time of clips. |
95 | */ | 111 | */ |
96 | static bool peak_meter_clip_hold(void) | 112 | static bool peak_meter_clip_hold(void) { |
97 | { | 113 | bool retval = false; |
114 | |||
98 | char* names[] = { str(LANG_PM_ETERNAL), | 115 | char* names[] = { str(LANG_PM_ETERNAL), |
99 | "1s ", "2s ", "3s ", "4s ", "5s ", | 116 | "1s ", "2s ", "3s ", "4s ", "5s ", |
100 | "6s ", "7s ", "8s ", "9s ", "10s", | 117 | "6s ", "7s ", "8s ", "9s ", "10s", |
@@ -103,19 +120,167 @@ static bool peak_meter_clip_hold(void) | |||
103 | "10min", "20min", "45min", "90min" | 120 | "10min", "20min", "45min", "90min" |
104 | }; | 121 | }; |
105 | 122 | ||
106 | return set_option( str(LANG_PM_CLIP_HOLD), | 123 | retval = set_option( str(LANG_PM_CLIP_HOLD), |
107 | &global_settings.peak_meter_clip_hold, names, | 124 | &global_settings.peak_meter_clip_hold, names, |
108 | 25, peak_meter_set_clip_hold); | 125 | 25, peak_meter_set_clip_hold); |
126 | |||
127 | peak_meter_init_times(global_settings.peak_meter_release, | ||
128 | global_settings.peak_meter_hold, | ||
129 | global_settings.peak_meter_clip_hold); | ||
130 | |||
131 | return retval; | ||
109 | } | 132 | } |
110 | 133 | ||
111 | /** | 134 | /** |
112 | * Menu to set the release time of the peak meter. | 135 | * Menu to set the release time of the peak meter. |
113 | */ | 136 | */ |
114 | static bool peak_meter_release(void) | 137 | static bool peak_meter_release(void) { |
115 | { | 138 | bool retval = false; |
116 | return set_int( str(LANG_PM_RELEASE), str(LANG_PM_UNITS_PER_READ), | 139 | |
140 | /* The range of peak_meter_release is restricted so that it | ||
141 | fits into a 7 bit number. The 8th bit is used for storing | ||
142 | something else in the rtc ram. | ||
143 | Also, the max value is 0x7e, since the RTC value 0xff is reserved */ | ||
144 | retval = set_int( str(LANG_PM_RELEASE), str(LANG_PM_UNITS_PER_READ), | ||
117 | &global_settings.peak_meter_release, | 145 | &global_settings.peak_meter_release, |
118 | NULL, 1, 1, LCD_WIDTH); | 146 | NULL, 1, 1, 0x7e); |
147 | |||
148 | peak_meter_init_times(global_settings.peak_meter_release, | ||
149 | global_settings.peak_meter_hold, | ||
150 | global_settings.peak_meter_clip_hold); | ||
151 | |||
152 | return retval; | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * Menu to select wether the scale of the meter | ||
157 | * displays dBfs of linear values. | ||
158 | */ | ||
159 | static bool peak_meter_scale(void) { | ||
160 | bool retval = false; | ||
161 | bool use_dbfs = global_settings.peak_meter_dbfs; | ||
162 | retval = set_bool_options(str(LANG_PM_SCALE), | ||
163 | &use_dbfs, | ||
164 | str(LANG_PM_DBFS), str(LANG_PM_LINEAR)); | ||
165 | |||
166 | /* has the user really changed the scale? */ | ||
167 | if (use_dbfs != global_settings.peak_meter_dbfs) { | ||
168 | |||
169 | /* store the change */ | ||
170 | global_settings.peak_meter_dbfs = use_dbfs; | ||
171 | peak_meter_set_use_dbfs(use_dbfs); | ||
172 | |||
173 | /* If the user changed the scale mode the meaning of | ||
174 | peak_meter_min (peak_meter_max) has changed. Thus we have | ||
175 | to convert the values stored in global_settings. */ | ||
176 | if (use_dbfs) { | ||
177 | |||
178 | /* we only store -dBfs */ | ||
179 | global_settings.peak_meter_min = -peak_meter_get_min() / 100; | ||
180 | global_settings.peak_meter_max = -peak_meter_get_max() / 100; | ||
181 | } else { | ||
182 | int max; | ||
183 | |||
184 | /* linear percent */ | ||
185 | global_settings.peak_meter_min = peak_meter_get_min(); | ||
186 | |||
187 | /* converting dBfs -> percent results in a precision loss. | ||
188 | I assume that the user doesn't bother that conversion | ||
189 | dBfs <-> percent isn't symmetrical for odd values but that | ||
190 | he wants 0 dBfs == 100%. Thus I 'correct' the percent value | ||
191 | resulting from dBfs -> percent manually here */ | ||
192 | max = peak_meter_get_max(); | ||
193 | global_settings.peak_meter_max = max < 99 ? max : 100; | ||
194 | } | ||
195 | settings_apply_pm_range(); | ||
196 | } | ||
197 | return retval; | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * Adjust the min value of the value range that | ||
202 | * the peak meter shall visualize. | ||
203 | */ | ||
204 | static bool peak_meter_min(void) { | ||
205 | bool retval = false; | ||
206 | if (global_settings.peak_meter_dbfs) { | ||
207 | |||
208 | /* for dBfs scale */ | ||
209 | int range_max = -global_settings.peak_meter_max; | ||
210 | int min = -global_settings.peak_meter_min; | ||
211 | |||
212 | retval = set_int(str(LANG_PM_MIN), str(LANG_PM_DBFS), | ||
213 | &min, NULL, 1, -89, range_max); | ||
214 | |||
215 | global_settings.peak_meter_min = - min; | ||
216 | } | ||
217 | |||
218 | /* for linear scale */ | ||
219 | else { | ||
220 | int min = global_settings.peak_meter_min; | ||
221 | |||
222 | retval = set_int(str(LANG_PM_MIN), "%", | ||
223 | &min, NULL, | ||
224 | 1, 0, global_settings.peak_meter_max - 1); | ||
225 | |||
226 | global_settings.peak_meter_min = (unsigned char)min; | ||
227 | } | ||
228 | |||
229 | settings_apply_pm_range(); | ||
230 | return retval; | ||
231 | } | ||
232 | |||
233 | |||
234 | /** | ||
235 | * Adjust the max value of the value range that | ||
236 | * the peak meter shall visualize. | ||
237 | */ | ||
238 | static bool peak_meter_max(void) { | ||
239 | bool retval = false; | ||
240 | if (global_settings.peak_meter_dbfs) { | ||
241 | |||
242 | /* for dBfs scale */ | ||
243 | int range_min = -global_settings.peak_meter_min; | ||
244 | int max = -global_settings.peak_meter_max;; | ||
245 | |||
246 | retval = set_int(str(LANG_PM_MAX), str(LANG_PM_DBFS), | ||
247 | &max, NULL, 1, range_min, 0); | ||
248 | |||
249 | global_settings.peak_meter_max = - max; | ||
250 | |||
251 | } | ||
252 | |||
253 | /* for linear scale */ | ||
254 | else { | ||
255 | int max = global_settings.peak_meter_max; | ||
256 | |||
257 | retval = set_int(str(LANG_PM_MAX), "%", | ||
258 | &max, NULL, | ||
259 | 1, global_settings.peak_meter_min + 1, 100); | ||
260 | |||
261 | global_settings.peak_meter_max = (unsigned char)max; | ||
262 | } | ||
263 | |||
264 | settings_apply_pm_range(); | ||
265 | return retval; | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * Menu to select wether the meter is in | ||
270 | * precision or in energy saver mode | ||
271 | */ | ||
272 | static bool peak_meter_performance(void) { | ||
273 | bool retval = false; | ||
274 | retval = set_bool_options(str(LANG_PM_PERFORMANCE), | ||
275 | &global_settings.peak_meter_performance, | ||
276 | str(LANG_PM_HIGH_PERFORMANCE), str(LANG_PM_ENERGY_SAVER)); | ||
277 | |||
278 | if (global_settings.peak_meter_performance) { | ||
279 | peak_meter_fps = 25; | ||
280 | } else { | ||
281 | peak_meter_fps = 20; | ||
282 | } | ||
283 | return retval; | ||
119 | } | 284 | } |
120 | 285 | ||
121 | /** | 286 | /** |
@@ -130,6 +295,13 @@ static bool peak_meter_menu(void) | |||
130 | { str(LANG_PM_RELEASE) , peak_meter_release }, | 295 | { str(LANG_PM_RELEASE) , peak_meter_release }, |
131 | { str(LANG_PM_PEAK_HOLD), peak_meter_hold }, | 296 | { str(LANG_PM_PEAK_HOLD), peak_meter_hold }, |
132 | { str(LANG_PM_CLIP_HOLD), peak_meter_clip_hold }, | 297 | { str(LANG_PM_CLIP_HOLD), peak_meter_clip_hold }, |
298 | { str(LANG_PM_PERFORMANCE), peak_meter_performance }, | ||
299 | #ifdef PM_DEBUG | ||
300 | { "Refresh rate" , peak_meter_fps_menu }, | ||
301 | #endif | ||
302 | { str(LANG_PM_SCALE) , peak_meter_scale }, | ||
303 | { str(LANG_PM_MIN) , peak_meter_min }, | ||
304 | { str(LANG_PM_MAX) , peak_meter_max }, | ||
133 | }; | 305 | }; |
134 | 306 | ||
135 | m=menu_init( items, sizeof items / sizeof(struct menu_items) ); | 307 | m=menu_init( items, sizeof items / sizeof(struct menu_items) ); |
@@ -137,7 +309,7 @@ static bool peak_meter_menu(void) | |||
137 | menu_exit(m); | 309 | menu_exit(m); |
138 | return result; | 310 | return result; |
139 | } | 311 | } |
140 | #endif | 312 | #endif /* HAVE_LCD_BITMAP */ |
141 | 313 | ||
142 | static bool shuffle(void) | 314 | static bool shuffle(void) |
143 | { | 315 | { |
diff --git a/apps/wps-display.c b/apps/wps-display.c index 6bca8cb40d..b70c1359d1 100644 --- a/apps/wps-display.c +++ b/apps/wps-display.c | |||
@@ -55,17 +55,10 @@ | |||
55 | #endif | 55 | #endif |
56 | 56 | ||
57 | #define FORMAT_BUFFER_SIZE 300 | 57 | #define FORMAT_BUFFER_SIZE 300 |
58 | struct format_flags | ||
59 | { | ||
60 | bool dynamic; | ||
61 | bool scroll; | ||
62 | bool player_progress; | ||
63 | bool peak_meter; | ||
64 | }; | ||
65 | 58 | ||
66 | static char format_buffer[FORMAT_BUFFER_SIZE]; | 59 | static char format_buffer[FORMAT_BUFFER_SIZE]; |
67 | static char* format_lines[MAX_LINES]; | 60 | static char* format_lines[MAX_LINES]; |
68 | static bool dynamic_lines[MAX_LINES]; | 61 | static unsigned char line_type[MAX_LINES]; |
69 | static int ff_rewind_count; | 62 | static int ff_rewind_count; |
70 | bool wps_time_countup = true; | 63 | bool wps_time_countup = true; |
71 | static bool wps_loaded = false; | 64 | static bool wps_loaded = false; |
@@ -218,9 +211,7 @@ static char* get_dir(char* buf, int buf_size, char* path, int level) | |||
218 | * buf - buffer to certain tags, such as track number, play time or | 211 | * buf - buffer to certain tags, such as track number, play time or |
219 | * directory name. | 212 | * directory name. |
220 | * buf_size - size of buffer. | 213 | * buf_size - size of buffer. |
221 | * flags - flags in this struct will be set depending on the tag: | 214 | * flags - returns the type of the line. See constants i wps-display.h |
222 | * dynamic - if the tag data changes over time (like play time); | ||
223 | * player_progress - set if the tag is %pb. | ||
224 | * | 215 | * |
225 | * Returns the tag. NULL indicates the tag wasn't available. | 216 | * Returns the tag. NULL indicates the tag wasn't available. |
226 | */ | 217 | */ |
@@ -228,7 +219,7 @@ static char* get_tag(struct mp3entry* id3, | |||
228 | char* tag, | 219 | char* tag, |
229 | char* buf, | 220 | char* buf, |
230 | int buf_size, | 221 | int buf_size, |
231 | struct format_flags* flags) | 222 | unsigned char* flags) |
232 | { | 223 | { |
233 | if ((0 == tag[0]) || (0 == tag[1])) | 224 | if ((0 == tag[0]) || (0 == tag[1])) |
234 | { | 225 | { |
@@ -238,6 +229,7 @@ static char* get_tag(struct mp3entry* id3, | |||
238 | switch (tag[0]) | 229 | switch (tag[0]) |
239 | { | 230 | { |
240 | case 'i': /* ID3 Information */ | 231 | case 'i': /* ID3 Information */ |
232 | *flags |= WPS_REFRESH_STATIC; | ||
241 | switch (tag[1]) | 233 | switch (tag[1]) |
242 | { | 234 | { |
243 | case 't': /* ID3 Title */ | 235 | case 't': /* ID3 Title */ |
@@ -263,6 +255,7 @@ static char* get_tag(struct mp3entry* id3, | |||
263 | break; | 255 | break; |
264 | 256 | ||
265 | case 'f': /* File Information */ | 257 | case 'f': /* File Information */ |
258 | *flags |= WPS_REFRESH_STATIC; | ||
266 | switch(tag[1]) | 259 | switch(tag[1]) |
267 | { | 260 | { |
268 | case 'v': /* VBR file? */ | 261 | case 'v': /* VBR file? */ |
@@ -310,40 +303,42 @@ static char* get_tag(struct mp3entry* id3, | |||
310 | switch(tag[1]) | 303 | switch(tag[1]) |
311 | { | 304 | { |
312 | case 'b': /* progress bar */ | 305 | case 'b': /* progress bar */ |
313 | flags->player_progress = true; | 306 | *flags |= WPS_REFRESH_PLAYER_PROGRESS; |
314 | flags->dynamic = true; | ||
315 | return "\x01"; | 307 | return "\x01"; |
316 | 308 | ||
317 | case 'p': /* Playlist Position */ | 309 | case 'p': /* Playlist Position */ |
310 | *flags |= WPS_REFRESH_STATIC; | ||
318 | snprintf(buf, buf_size, "%d", id3->index + 1); | 311 | snprintf(buf, buf_size, "%d", id3->index + 1); |
319 | return buf; | 312 | return buf; |
320 | 313 | ||
321 | case 'n': /* Playlist Name (without path) */ | 314 | case 'n': /* Playlist Name (without path) */ |
315 | *flags |= WPS_REFRESH_STATIC; | ||
322 | return playlist_name(buf, buf_size); | 316 | return playlist_name(buf, buf_size); |
323 | 317 | ||
324 | case 'e': /* Playlist Total Entries */ | 318 | case 'e': /* Playlist Total Entries */ |
319 | *flags |= WPS_REFRESH_STATIC; | ||
325 | snprintf(buf, buf_size, "%d", playlist_amount()); | 320 | snprintf(buf, buf_size, "%d", playlist_amount()); |
326 | return buf; | 321 | return buf; |
327 | 322 | ||
328 | case 'c': /* Current Time in Song */ | 323 | case 'c': /* Current Time in Song */ |
329 | flags->dynamic = true; | 324 | *flags |= WPS_REFRESH_DYNAMIC; |
330 | format_time(buf, buf_size, id3->elapsed + ff_rewind_count); | 325 | format_time(buf, buf_size, id3->elapsed + ff_rewind_count); |
331 | return buf; | 326 | return buf; |
332 | 327 | ||
333 | case 'r': /* Remaining Time in Song */ | 328 | case 'r': /* Remaining Time in Song */ |
334 | flags->dynamic = true; | 329 | *flags |= WPS_REFRESH_DYNAMIC; |
335 | format_time(buf, buf_size, | 330 | format_time(buf, buf_size, |
336 | id3->length - id3->elapsed - ff_rewind_count); | 331 | id3->length - id3->elapsed - ff_rewind_count); |
337 | return buf; | 332 | return buf; |
338 | 333 | ||
339 | case 't': /* Total Time */ | 334 | case 't': /* Total Time */ |
335 | *flags |= WPS_REFRESH_STATIC; | ||
340 | format_time(buf, buf_size, id3->length); | 336 | format_time(buf, buf_size, id3->length); |
341 | return buf; | 337 | return buf; |
342 | 338 | ||
343 | #ifdef HAVE_LCD_BITMAP | 339 | #ifdef HAVE_LCD_BITMAP |
344 | case 'm': /* Peak Meter */ | 340 | case 'm': /* Peak Meter */ |
345 | flags->peak_meter = true; | 341 | *flags |= WPS_REFRESH_PEAK_METER; |
346 | flags->dynamic = true; | ||
347 | return "\x01"; | 342 | return "\x01"; |
348 | #endif | 343 | #endif |
349 | } | 344 | } |
@@ -351,6 +346,7 @@ static char* get_tag(struct mp3entry* id3, | |||
351 | 346 | ||
352 | case 'd': /* Directory path information */ | 347 | case 'd': /* Directory path information */ |
353 | { | 348 | { |
349 | *flags |= WPS_REFRESH_STATIC; | ||
354 | int level = tag[1] - '0'; | 350 | int level = tag[1] - '0'; |
355 | /* d1 through d9 */ | 351 | /* d1 through d9 */ |
356 | if ((0 < level) && (9 > level)) | 352 | if ((0 < level) && (9 > level)) |
@@ -437,16 +433,13 @@ static char* skip_conditional(char* fmt, bool to_else) | |||
437 | * buf_size - the size of buffer. | 433 | * buf_size - the size of buffer. |
438 | * id3 - the ID3 data to format with. | 434 | * id3 - the ID3 data to format with. |
439 | * fmt - format description. | 435 | * fmt - format description. |
440 | * flags - flags in this struct will be set depending on the tag: | 436 | * flags - returns the type of the line. See constants i wps-display.h |
441 | * dynamic - if the tag data changes over time (like play time); | ||
442 | * player_progress - set if the tag is %pb. | ||
443 | * scroll - if line scrolling is requested. | ||
444 | */ | 437 | */ |
445 | static void format_display(char* buf, | 438 | static void format_display(char* buf, |
446 | int buf_size, | 439 | int buf_size, |
447 | struct mp3entry* id3, | 440 | struct mp3entry* id3, |
448 | char* fmt, | 441 | char* fmt, |
449 | struct format_flags* flags) | 442 | unsigned char* flags) |
450 | { | 443 | { |
451 | char temp_buf[128]; | 444 | char temp_buf[128]; |
452 | char* buf_end = buf + buf_size - 1; /* Leave room for end null */ | 445 | char* buf_end = buf + buf_size - 1; /* Leave room for end null */ |
@@ -483,7 +476,7 @@ static void format_display(char* buf, | |||
483 | break; | 476 | break; |
484 | 477 | ||
485 | case 's': | 478 | case 's': |
486 | flags->scroll = true; | 479 | *flags |= WPS_REFRESH_SCROLL; |
487 | ++fmt; | 480 | ++fmt; |
488 | break; | 481 | break; |
489 | 482 | ||
@@ -526,10 +519,10 @@ static void format_display(char* buf, | |||
526 | *buf = 0; | 519 | *buf = 0; |
527 | } | 520 | } |
528 | 521 | ||
529 | bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_all) | 522 | bool wps_refresh(struct mp3entry* id3, int ffwd_offset, unsigned char refresh_mode) |
530 | { | 523 | { |
531 | char buf[MAX_PATH]; | 524 | char buf[MAX_PATH]; |
532 | struct format_flags flags; | 525 | unsigned char flags; |
533 | int i; | 526 | int i; |
534 | #ifdef HAVE_LCD_BITMAP | 527 | #ifdef HAVE_LCD_BITMAP |
535 | /* to find out wether the peak meter is enabled we | 528 | /* to find out wether the peak meter is enabled we |
@@ -554,16 +547,15 @@ bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_all) | |||
554 | if ( !format_lines[i] ) | 547 | if ( !format_lines[i] ) |
555 | break; | 548 | break; |
556 | 549 | ||
557 | if (dynamic_lines[i] || refresh_all) | 550 | if ((line_type[i] & refresh_mode) || |
551 | (refresh_mode == WPS_REFRESH_ALL)) | ||
558 | { | 552 | { |
559 | flags.dynamic = false; | 553 | flags = 0; |
560 | flags.scroll = false; | ||
561 | flags.player_progress = false; | ||
562 | flags.peak_meter = false; | ||
563 | format_display(buf, sizeof(buf), id3, format_lines[i], &flags); | 554 | format_display(buf, sizeof(buf), id3, format_lines[i], &flags); |
564 | dynamic_lines[i] = flags.dynamic; | 555 | line_type[i] = flags; |
565 | 556 | ||
566 | if (flags.player_progress) { | 557 | /* progress */ |
558 | if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) { | ||
567 | #ifdef HAVE_LCD_CHARCELLS | 559 | #ifdef HAVE_LCD_CHARCELLS |
568 | draw_player_progress(id3, ff_rewind_count); | 560 | draw_player_progress(id3, ff_rewind_count); |
569 | #else | 561 | #else |
@@ -578,11 +570,12 @@ bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_all) | |||
578 | } | 570 | } |
579 | 571 | ||
580 | #ifdef HAVE_LCD_BITMAP | 572 | #ifdef HAVE_LCD_BITMAP |
581 | if (flags.peak_meter) { | 573 | /* peak meter */ |
574 | if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) { | ||
582 | int peak_meter_y; | 575 | int peak_meter_y; |
583 | int w,h; | 576 | struct font *fnt = font_get(FONT_UI); |
577 | int h = fnt->height; | ||
584 | int offset = global_settings.statusbar ? STATUSBAR_HEIGHT : 0; | 578 | int offset = global_settings.statusbar ? STATUSBAR_HEIGHT : 0; |
585 | lcd_getstringsize("M",&w,&h); | ||
586 | 579 | ||
587 | peak_meter_y = i * h + offset; | 580 | peak_meter_y = i * h + offset; |
588 | 581 | ||
@@ -601,10 +594,13 @@ bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_all) | |||
601 | } | 594 | } |
602 | #endif | 595 | #endif |
603 | 596 | ||
604 | if (flags.scroll && !flags.dynamic) | 597 | /* static line */ |
598 | if (flags & WPS_REFRESH_SCROLL) | ||
605 | { | 599 | { |
600 | if (refresh_mode & WPS_REFRESH_SCROLL) { | ||
606 | lcd_puts_scroll(0, i, buf); | 601 | lcd_puts_scroll(0, i, buf); |
607 | } | 602 | } |
603 | } | ||
608 | else | 604 | else |
609 | { | 605 | { |
610 | lcd_puts(0, i, buf); | 606 | lcd_puts(0, i, buf); |
@@ -656,7 +652,7 @@ bool wps_display(struct mp3entry* id3) | |||
656 | } | 652 | } |
657 | } | 653 | } |
658 | } | 654 | } |
659 | wps_refresh(id3, 0, true); | 655 | wps_refresh(id3, 0, WPS_REFRESH_ALL); |
660 | status_draw(); | 656 | status_draw(); |
661 | lcd_update(); | 657 | lcd_update(); |
662 | return false; | 658 | return false; |
diff --git a/apps/wps-display.h b/apps/wps-display.h index fc40e19136..cda90ffffe 100644 --- a/apps/wps-display.h +++ b/apps/wps-display.h | |||
@@ -22,7 +22,18 @@ | |||
22 | #include <stdbool.h> | 22 | #include <stdbool.h> |
23 | #include "id3.h" | 23 | #include "id3.h" |
24 | 24 | ||
25 | bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_scroll); | 25 | /* constants used in line_type and as refresh_mode for wps_refresh */ |
26 | #define WPS_REFRESH_STATIC 1 /* line doesn't change over time */ | ||
27 | #define WPS_REFRESH_DYNAMIC 2 /* line may change (e.g. time flag) */ | ||
28 | #define WPS_REFRESH_SCROLL 4 /* line scrolls */ | ||
29 | #define WPS_REFRESH_PLAYER_PROGRESS 8 /* line contains a progress bar */ | ||
30 | #define WPS_REFRESH_PEAK_METER 16 /* line contains a peak meter */ | ||
31 | #define WPS_REFRESH_ALL 0xff /* to refresh all line types */ | ||
32 | /* to refresh only those lines that change over time */ | ||
33 | #define WPS_REFRESH_NON_STATIC (WPS_REFRESH_ALL & ~WPS_REFRESH_STATIC & ~WPS_REFRESH_SCROLL) | ||
34 | |||
35 | |||
36 | bool wps_refresh(struct mp3entry* id3, int ffwd_offset, unsigned char refresh_mode); | ||
26 | bool wps_display(struct mp3entry* id3); | 37 | bool wps_display(struct mp3entry* id3); |
27 | bool wps_load(char* file, bool display); | 38 | bool wps_load(char* file, bool display); |
28 | void wps_reset(void); | 39 | void wps_reset(void); |
diff --git a/apps/wps.c b/apps/wps.c index 3e48333818..8f9295a293 100644 --- a/apps/wps.c +++ b/apps/wps.c | |||
@@ -68,7 +68,7 @@ void player_change_volume(int button) | |||
68 | if(global_settings.volume > mpeg_sound_max(SOUND_VOLUME)) | 68 | if(global_settings.volume > mpeg_sound_max(SOUND_VOLUME)) |
69 | global_settings.volume = mpeg_sound_max(SOUND_VOLUME); | 69 | global_settings.volume = mpeg_sound_max(SOUND_VOLUME); |
70 | mpeg_sound_set(SOUND_VOLUME, global_settings.volume); | 70 | mpeg_sound_set(SOUND_VOLUME, global_settings.volume); |
71 | wps_refresh(id3,0,false); | 71 | wps_refresh(id3, 0, WPS_REFRESH_NON_STATIC); |
72 | settings_save(); | 72 | settings_save(); |
73 | break; | 73 | break; |
74 | 74 | ||
@@ -78,7 +78,7 @@ void player_change_volume(int button) | |||
78 | if(global_settings.volume < mpeg_sound_min(SOUND_VOLUME)) | 78 | if(global_settings.volume < mpeg_sound_min(SOUND_VOLUME)) |
79 | global_settings.volume = mpeg_sound_min(SOUND_VOLUME); | 79 | global_settings.volume = mpeg_sound_min(SOUND_VOLUME); |
80 | mpeg_sound_set(SOUND_VOLUME, global_settings.volume); | 80 | mpeg_sound_set(SOUND_VOLUME, global_settings.volume); |
81 | wps_refresh(id3,0,false); | 81 | wps_refresh(id3, 0, WPS_REFRESH_NON_STATIC); |
82 | settings_save(); | 82 | settings_save(); |
83 | break; | 83 | break; |
84 | 84 | ||
@@ -100,7 +100,7 @@ void player_change_volume(int button) | |||
100 | button = button_get(true); | 100 | button = button_get(true); |
101 | } | 101 | } |
102 | status_draw(); | 102 | status_draw(); |
103 | wps_refresh(id3,0,true); | 103 | wps_refresh(id3,0, WPS_REFRESH_ALL); |
104 | } | 104 | } |
105 | #endif | 105 | #endif |
106 | 106 | ||
@@ -366,9 +366,9 @@ static bool ffwd_rew(int button) | |||
366 | } | 366 | } |
367 | 367 | ||
368 | if(wps_time_countup == false) | 368 | if(wps_time_countup == false) |
369 | wps_refresh(id3, -ff_rewind_count, false); | 369 | wps_refresh(id3, -ff_rewind_count, WPS_REFRESH_PLAYER_PROGRESS); |
370 | else | 370 | else |
371 | wps_refresh(id3, ff_rewind_count, false); | 371 | wps_refresh(id3, ff_rewind_count, WPS_REFRESH_PLAYER_PROGRESS); |
372 | 372 | ||
373 | break; | 373 | break; |
374 | 374 | ||
@@ -399,7 +399,7 @@ static bool ffwd_rew(int button) | |||
399 | if (!exit) | 399 | if (!exit) |
400 | button = button_get(true); | 400 | button = button_get(true); |
401 | } | 401 | } |
402 | wps_refresh(id3,0,true); | 402 | wps_refresh(id3, 0, WPS_REFRESH_ALL); |
403 | return usb; | 403 | return usb; |
404 | } | 404 | } |
405 | 405 | ||
@@ -415,11 +415,11 @@ static bool update(void) | |||
415 | if (wps_display(id3)) | 415 | if (wps_display(id3)) |
416 | retcode = true; | 416 | retcode = true; |
417 | else | 417 | else |
418 | wps_refresh(id3,0,true); | 418 | wps_refresh(id3, 0, WPS_REFRESH_ALL); |
419 | } | 419 | } |
420 | 420 | ||
421 | if (id3) | 421 | if (id3) |
422 | wps_refresh(id3,0,false); | 422 | wps_refresh(id3, 0, WPS_REFRESH_NON_STATIC); |
423 | 423 | ||
424 | status_draw(); | 424 | status_draw(); |
425 | 425 | ||
@@ -453,7 +453,7 @@ static bool keylock(void) | |||
453 | #endif | 453 | #endif |
454 | display_keylock_text(true); | 454 | display_keylock_text(true); |
455 | keys_locked = true; | 455 | keys_locked = true; |
456 | wps_refresh(id3,0,true); | 456 | wps_refresh(id3, 0, WPS_REFRESH_ALL); |
457 | if (wps_display(id3)) { | 457 | if (wps_display(id3)) { |
458 | keys_locked = false; | 458 | keys_locked = false; |
459 | #ifdef HAVE_LCD_CHARCELLS | 459 | #ifdef HAVE_LCD_CHARCELLS |
@@ -511,7 +511,7 @@ static bool keylock(void) | |||
511 | default: | 511 | default: |
512 | display_keylock_text(true); | 512 | display_keylock_text(true); |
513 | while (button_get(false)); /* clear button queue */ | 513 | while (button_get(false)); /* clear button queue */ |
514 | wps_refresh(id3,0,true); | 514 | wps_refresh(id3, 0, WPS_REFRESH_ALL); |
515 | if (wps_display(id3)) { | 515 | if (wps_display(id3)) { |
516 | keys_locked = false; | 516 | keys_locked = false; |
517 | #ifdef HAVE_LCD_CHARCELLS | 517 | #ifdef HAVE_LCD_CHARCELLS |
@@ -628,7 +628,7 @@ static bool menu(void) | |||
628 | #endif | 628 | #endif |
629 | 629 | ||
630 | wps_display(id3); | 630 | wps_display(id3); |
631 | wps_refresh(id3,0,true); | 631 | wps_refresh(id3, 0, WPS_REFRESH_ALL); |
632 | return false; | 632 | return false; |
633 | } | 633 | } |
634 | 634 | ||
@@ -659,7 +659,7 @@ int wps_show(void) | |||
659 | if (id3) { | 659 | if (id3) { |
660 | if (wps_display(id3)) | 660 | if (wps_display(id3)) |
661 | return 0; | 661 | return 0; |
662 | wps_refresh(id3,0,true); | 662 | wps_refresh(id3, 0, WPS_REFRESH_ALL); |
663 | } | 663 | } |
664 | restore = true; | 664 | restore = true; |
665 | } | 665 | } |
@@ -674,14 +674,44 @@ int wps_show(void) | |||
674 | isn't displayed */ | 674 | isn't displayed */ |
675 | if (peak_meter_enabled) { | 675 | if (peak_meter_enabled) { |
676 | int i; | 676 | int i; |
677 | |||
678 | /* In high performance mode we read out the mas as | ||
679 | often as we can. There is no sleep for cpu */ | ||
680 | if (global_settings.peak_meter_performance) { | ||
681 | long next_refresh = current_tick; | ||
682 | long next_big_refresh = current_tick + HZ / 5; | ||
683 | button = BUTTON_NONE; | ||
684 | while (!TIME_AFTER(current_tick, next_big_refresh)) { | ||
685 | button = button_get(false); | ||
686 | if (button != BUTTON_NONE) { | ||
687 | break; | ||
688 | } | ||
689 | peak_meter_peek(); | ||
690 | yield(); | ||
691 | |||
692 | if (TIME_AFTER(current_tick, next_refresh)) { | ||
693 | wps_refresh(id3, 0, WPS_REFRESH_PEAK_METER); | ||
694 | next_refresh = current_tick + HZ / peak_meter_fps; | ||
695 | } | ||
696 | } | ||
697 | } | ||
698 | |||
699 | /* In energy saver mode the cpu may sleep a | ||
700 | little bit while waiting for buttons */ | ||
701 | else { | ||
677 | for (i = 0; i < 4; i++) { | 702 | for (i = 0; i < 4; i++) { |
678 | button = button_get_w_tmo(HZ / 20); | 703 | button = button_get_w_tmo(HZ / peak_meter_fps); |
679 | if (button != 0) { | 704 | if (button != 0) { |
680 | break; | 705 | break; |
681 | } | 706 | } |
682 | wps_refresh(id3, 0, false); | 707 | wps_refresh(id3, 0, WPS_REFRESH_PEAK_METER); |
708 | } | ||
683 | } | 709 | } |
684 | } else { | 710 | } |
711 | |||
712 | /* The peak meter is disabled | ||
713 | -> no additional screen updates needed */ | ||
714 | else { | ||
685 | button = button_get_w_tmo(HZ/5); | 715 | button = button_get_w_tmo(HZ/5); |
686 | } | 716 | } |
687 | #else | 717 | #else |
@@ -880,7 +910,7 @@ int wps_show(void) | |||
880 | if (wps_display(id3)) | 910 | if (wps_display(id3)) |
881 | return 0; | 911 | return 0; |
882 | if (id3) | 912 | if (id3) |
883 | wps_refresh(id3,0,false); | 913 | wps_refresh(id3, 0, WPS_REFRESH_NON_STATIC); |
884 | } | 914 | } |
885 | if(button != BUTTON_NONE) | 915 | if(button != BUTTON_NONE) |
886 | lastbutton = button; | 916 | lastbutton = button; |