diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2002-10-29 12:09:15 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2002-10-29 12:09:15 +0000 |
commit | fd0cc3b2b1302d77f3404861509e75c64fd505af (patch) | |
tree | acdb25b87a549908424e052c04cc323d02b681f4 /apps/recorder | |
parent | 8f11dc00ac1a0a5fe009d1d07d9a1378c3300ba8 (diff) | |
download | rockbox-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/recorder')
-rw-r--r-- | apps/recorder/peakmeter.c | 631 | ||||
-rw-r--r-- | apps/recorder/peakmeter.h | 22 |
2 files changed, 636 insertions, 17 deletions
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__ */ |