summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2002-10-29 12:09:15 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2002-10-29 12:09:15 +0000
commitfd0cc3b2b1302d77f3404861509e75c64fd505af (patch)
treeacdb25b87a549908424e052c04cc323d02b681f4 /apps
parent8f11dc00ac1a0a5fe009d1d07d9a1378c3300ba8 (diff)
downloadrockbox-fd0cc3b2b1302d77f3404861509e75c64fd505af.tar.gz
rockbox-fd0cc3b2b1302d77f3404861509e75c64fd505af.zip
Phil Pertermann's dB peak meter patch
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@2774 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/debug_menu.c7
-rw-r--r--apps/lang/english.lang40
-rw-r--r--apps/recorder/peakmeter.c631
-rw-r--r--apps/recorder/peakmeter.h22
-rw-r--r--apps/settings.c86
-rw-r--r--apps/settings.h5
-rw-r--r--apps/settings_menu.c194
-rw-r--r--apps/wps-display.c72
-rw-r--r--apps/wps-display.h13
-rw-r--r--apps/wps.c62
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
866eng: "Units Per Read" 866eng: "Units Per Read"
867new: 867new:
868 868
869id: LANG_PM_PERFORMANCE
870desc: in the peak meter menu
871eng: "Performance"
872new:
873
874id: LANG_PM_HIGH_PERFORMANCE
875desc: in the peak meter menu
876eng: "High performance"
877new:
878
879id: LANG_PM_ENERGY_SAVER
880desc: in the peak meter menu
881eng: "Save energy"
882new:
883
884id: LANG_PM_SCALE
885desc: in the peak meter menu
886eng: "dBfs <-> linear"
887new:
888
889id: LANG_PM_DBFS
890desc: in the peak meter menu
891eng: "dBfs"
892new:
893
894id: LANG_PM_LINEAR
895desc: in the peak meter menu
896eng: "linear"
897new:
898
899id: LANG_PM_MIN
900desc: in the peak meter menu
901eng: "Minimum of range"
902new:
903
904id: LANG_PM_MAX
905desc: in the peak meter menu
906eng: "Maximum of range"
907new:
908
869id: LANG_BACKLIGHT_ON_WHEN_CHARGING 909id: LANG_BACKLIGHT_ON_WHEN_CHARGING
870desc: in display_settings_menu 910desc: in display_settings_menu
871eng: "Backlight On When Plugged" 911eng: "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;
46static long peak_meter_clip_timeout_l; 46static long peak_meter_clip_timeout_l;
47static long peak_meter_clip_timeout_r; 47static long peak_meter_clip_timeout_r;
48 48
49static int peak_meter_clip_hold;
50
51/* specifies the value range in peak volume values */
52unsigned short peak_meter_range_min;
53unsigned short peak_meter_range_max;
54
49/* if set to true clip timeout is disabled */ 55/* if set to true clip timeout is disabled */
50static bool peak_meter_clip_eternal = false; 56static bool peak_meter_clip_eternal = false;
51 57
58static bool peak_meter_use_dbfs = true;
59
60
52#ifndef SIMULATOR 61#ifndef SIMULATOR
53static int peak_meter_src_l = MAS_REG_DQPEAK_L; 62static int peak_meter_src_l = MAS_REG_DQPEAK_L;
54static int peak_meter_src_r = MAS_REG_DQPEAK_R; 63static 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 */
62bool peak_meter_enabled = true; 69bool peak_meter_enabled = true;
63 70
71/*
72bool peak_meter_use_thread = false;
73static char peak_meter_stack[DEFAULT_STACK_SIZE];
74*/
75/* used in wps.c to set the display frame rate of the peak meter */
76int peak_meter_fps = 20;
77
64static int peak_meter_l; 78static int peak_meter_l;
65static int peak_meter_r; 79static int peak_meter_r;
66 80
81static int peak_meter_hold = 1;
82static int peak_meter_release = 8;
83
84/* debug only */
85#ifdef PM_DEBUG
86static int peek_calls = 0;
87
88#define PEEKS_PER_DRAW_SIZE 40
89static unsigned int peeks_per_redraw[PEEKS_PER_DRAW_SIZE];
90
91#define TICKS_PER_DRAW_SIZE 20
92static unsigned int ticks_per_redraw[TICKS_PER_DRAW_SIZE];
93#endif
94
67/* time out values for max */ 95/* time out values for max */
68static long max_time_out[] = { 96static 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
114static 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
128static 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
143int 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 */
147static bool db_scale_valid = false;
148
149/* contains the lcd x coordinates of the magical
150 scale values in db_scale_src_values */
151static 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
172int 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 */
273static 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 */
300static 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 */
337void 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 */
356int 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 */
373void 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 */
391int 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 */
407int 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 */
417void 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 */
435void 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 */
452void 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/*
534void 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/*
549void 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 */
151static int peak_meter_read_l (void) { 564static 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 */
167static int peak_meter_read_r (void) { 590static 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 */
634unsigned 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
844static 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
855bool 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
24extern bool peak_meter_histogramm(void);
25#endif
26
27
22extern bool peak_meter_enabled; 28extern bool peak_meter_enabled;
29extern int peak_meter_fps;
23 30
24extern void peak_meter_init(void);
25extern void peak_meter_playback(bool playback); 31extern void peak_meter_playback(bool playback);
26extern void peak_meter_draw(int x, int y, int width, int height); 32extern void peak_meter_draw(int x, int y, int width, int height);
27extern void peak_meter_set_clip_hold(int time); 33extern void peak_meter_set_clip_hold(int time);
28extern void peak_meter_peek(void); 34extern void peak_meter_peek(void);
35extern void peak_meter_init_range( bool dbfs, int range_min, int range_max);
36extern void peak_meter_init_times(int release, int hold, int clip_hold);
37
38extern void peak_meter_set_min(int newmin);
39extern int peak_meter_get_min(void);
40extern void peak_meter_set_max(int newmax);
41extern int peak_meter_get_max(void);
42extern void peak_meter_set_use_dbfs(int use);
43extern int peak_meter_get_use_dbfs(void);
44extern int calc_db (int isample);
45extern unsigned short peak_meter_scale_value(unsigned short val, int meterwidth);
46
47extern unsigned short peak_meter_range_min;
48extern 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 @@
52struct user_settings global_settings; 53struct user_settings global_settings;
53char rockboxdir[] = ROCKBOX_DIR; /* config/font/data file directory */ 54char 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
890x16 0x2a <(int) Byte offset into resume file> 900x16 0x2a <(int) Byte offset into resume file>
900x1a 0x2e <time until disk spindown> 910x1a 0x2e <time until disk spindown>
910x1b 0x2f <browse current, play selected> 920x1b 0x2f <browse current, play selected>
920x1c 0x30 <peak meter hold timeout (bit 0-4)> 930x1c 0x30 <peak meter hold timeout (bit 0-4)>,
930x1d 0x31 <peak meter clip hold timeout (bit 0-4)> 94 peak_meter_performance (bit 7)
940x1e 0x32 <peak meter release step size> 950x1d 0x31 <peak meter clip hold timeout (bit 0-4)
950x1f 0x33 <repeat mode> 960x1e 0x32 <peak meter release step size,
97 peak_meter_dbfs (bit 7)
980x1f 0x33 <peak meter min either in -db or in percent>
990x20 0x34 <peak meter max either in -db or in percent>
1000x21 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 */
372void 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
358void settings_apply(void) 396void 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);
121void settings_load(void); 125void settings_load(void);
122void settings_reset(void); 126void settings_reset(void);
123void settings_apply(void); 127void settings_apply(void);
128void settings_apply_pm_range(void);
124void settings_display(void); 129void settings_display(void);
125 130
126bool settings_load_config(char* file); 131bool 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
78static 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 */
80static bool peak_meter_hold(void) 90static 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 */
96static bool peak_meter_clip_hold(void) 112static 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 */
114static bool peak_meter_release(void) 137static 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 */
159static 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 */
204static 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 */
238static 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 */
272static 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
142static bool shuffle(void) 314static 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
58struct format_flags
59{
60 bool dynamic;
61 bool scroll;
62 bool player_progress;
63 bool peak_meter;
64};
65 58
66static char format_buffer[FORMAT_BUFFER_SIZE]; 59static char format_buffer[FORMAT_BUFFER_SIZE];
67static char* format_lines[MAX_LINES]; 60static char* format_lines[MAX_LINES];
68static bool dynamic_lines[MAX_LINES]; 61static unsigned char line_type[MAX_LINES];
69static int ff_rewind_count; 62static int ff_rewind_count;
70bool wps_time_countup = true; 63bool wps_time_countup = true;
71static bool wps_loaded = false; 64static 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 */
445static void format_display(char* buf, 438static 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
529bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_all) 522bool 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
25bool 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
36bool wps_refresh(struct mp3entry* id3, int ffwd_offset, unsigned char refresh_mode);
26bool wps_display(struct mp3entry* id3); 37bool wps_display(struct mp3entry* id3);
27bool wps_load(char* file, bool display); 38bool wps_load(char* file, bool display);
28void wps_reset(void); 39void 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;