diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2002-09-27 09:44:51 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2002-09-27 09:44:51 +0000 |
commit | f11702921fc137fb9eb08b3311bc4264ed6a2fe2 (patch) | |
tree | d4547128f405131cdd2a67f5d2ae00fe10b547bd /apps/recorder | |
parent | bb572c44121e48d9d54411dff9e0f8dce015e550 (diff) | |
download | rockbox-f11702921fc137fb9eb08b3311bc4264ed6a2fe2.tar.gz rockbox-f11702921fc137fb9eb08b3311bc4264ed6a2fe2.zip |
Philip Pertermanns peak meter
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@2437 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/recorder')
-rw-r--r-- | apps/recorder/peakmeter.c | 292 | ||||
-rw-r--r-- | apps/recorder/peakmeter.h | 29 |
2 files changed, 321 insertions, 0 deletions
diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c new file mode 100644 index 0000000000..d184641c67 --- /dev/null +++ b/apps/recorder/peakmeter.c | |||
@@ -0,0 +1,292 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Philipp Pertermann | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "mas.h" | ||
20 | #include "thread.h" | ||
21 | #include "kernel.h" | ||
22 | #include "settings.h" | ||
23 | #include "lcd.h" | ||
24 | #include "wps-display.h" | ||
25 | #include "sprintf.h" | ||
26 | #include "button.h" | ||
27 | #include "system.h" | ||
28 | #include "font.h" | ||
29 | #include "icons.h" | ||
30 | #include "lang.h" | ||
31 | #include "peakmeter.h" | ||
32 | |||
33 | /* buffer the read peak value */ | ||
34 | static int peak_meter_max_l; | ||
35 | static int peak_meter_max_r; | ||
36 | |||
37 | /* point in time when peak_meter_max_x becomes invalid */ | ||
38 | static long peak_meter_timeout_l; | ||
39 | static long peak_meter_timeout_r; | ||
40 | |||
41 | /* when true a clip has occurred */ | ||
42 | static bool peak_meter_l_clip = false; | ||
43 | static bool peak_meter_r_clip = false; | ||
44 | |||
45 | /* point in time when peak_meter_x_oveflow becomes invalid */ | ||
46 | static long peak_meter_clip_timeout_l; | ||
47 | static long peak_meter_clip_timeout_r; | ||
48 | |||
49 | /* if set to true clip timeout is disabled */ | ||
50 | static bool peak_meter_clip_eternal = false; | ||
51 | |||
52 | #ifndef SIMULATOR | ||
53 | static int peak_meter_src_l = MAS_REG_DQPEAK_L; | ||
54 | static int peak_meter_src_r = MAS_REG_DQPEAK_R; | ||
55 | #endif | ||
56 | |||
57 | /* temporarily en- / disables peak meter. This is | ||
58 | especially for external applications to detect | ||
59 | if the peak_meter is in use and needs drawing at all */ | ||
60 | bool peak_meter_enabled = true; | ||
61 | |||
62 | static int peak_meter_l; | ||
63 | static int peak_meter_r; | ||
64 | |||
65 | /* time out values for max */ | ||
66 | static long max_time_out[] = { | ||
67 | 0 * HZ, HZ / 5, 30, HZ / 2, HZ, 2 * HZ, | ||
68 | 3 * HZ, 4 * HZ, 5 * HZ, 6 * HZ, 7 * HZ, 8 * HZ, | ||
69 | 9 * HZ, 10 * HZ, 15 * HZ, 20 * HZ, 30 * HZ, 60 * HZ | ||
70 | }; | ||
71 | |||
72 | /* time out values for clip */ | ||
73 | static long clip_time_out[] = { | ||
74 | 0 * HZ, 1 * HZ, 2 * HZ, 3 * HZ, 4 * HZ, 5 * HZ, | ||
75 | 6 * HZ, 7 * HZ, 8 * HZ, 9 * HZ, 10 * HZ, 15 * HZ, | ||
76 | 20 * HZ, 25 * HZ, 30 * HZ, 45 * HZ, 60 * HZ, 90 * HZ, | ||
77 | 120 * HZ, 180 * HZ, 300 * HZ, 600 * HZ, 1200 * HZ, | ||
78 | 2700 * HZ, 5400 * HZ | ||
79 | }; | ||
80 | |||
81 | /** | ||
82 | * Set the source of the peak meter to playback or to | ||
83 | * record. | ||
84 | * @param: bool playback - If true playback peak meter is used. | ||
85 | * If false recording peak meter is used. | ||
86 | */ | ||
87 | void peak_meter_playback(bool playback) { | ||
88 | #ifdef SIMULATOR | ||
89 | (void)playback; | ||
90 | #else | ||
91 | if (playback) { | ||
92 | peak_meter_src_l = MAS_REG_DQPEAK_L; | ||
93 | peak_meter_src_r = MAS_REG_DQPEAK_R; | ||
94 | } else { | ||
95 | peak_meter_src_l = MAS_REG_QPEAK_L; | ||
96 | peak_meter_src_r = MAS_REG_QPEAK_R; | ||
97 | } | ||
98 | #endif | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * Reads peak values from the MAS, and detects clips. The | ||
103 | * values are stored in peak_meter_l peak_meter_r for later | ||
104 | * evauluation. Consecutive calls to peak_meter_peek detect | ||
105 | * that ocurred. This function could be used by a thread for | ||
106 | * busy reading the MAS. | ||
107 | */ | ||
108 | static void peak_meter_peek(void) { | ||
109 | #ifdef SIMULATOR | ||
110 | int left = 8000; | ||
111 | int right = 9000; | ||
112 | #else | ||
113 | /* read the peak values */ | ||
114 | int left = mas_codec_readreg(peak_meter_src_l); | ||
115 | int right = mas_codec_readreg(peak_meter_src_r); | ||
116 | #endif | ||
117 | |||
118 | /* check for clips | ||
119 | An clip is assumed when two consecutive readouts | ||
120 | of the volume are at full scale. This is proven | ||
121 | to be inaccurate in both ways: it may detect clips | ||
122 | when no clip occurred and it may fail to detect | ||
123 | a real clip. */ | ||
124 | if ((left == peak_meter_l) && | ||
125 | (left == MAX_PEAK - 1)) { | ||
126 | peak_meter_l_clip = true; | ||
127 | peak_meter_clip_timeout_l = | ||
128 | current_tick + clip_time_out[global_settings.peak_meter_clip_hold]; | ||
129 | } | ||
130 | |||
131 | if ((right == peak_meter_r) && | ||
132 | (right == MAX_PEAK - 1)) { | ||
133 | peak_meter_r_clip = true; | ||
134 | peak_meter_clip_timeout_r = | ||
135 | current_tick + clip_time_out[global_settings.peak_meter_clip_hold]; | ||
136 | } | ||
137 | |||
138 | /* peaks are searched -> we have to find the maximum */ | ||
139 | peak_meter_l = MAX(peak_meter_l, left); | ||
140 | peak_meter_r = MAX(peak_meter_r, right); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Reads out the peak volume of the left channel. | ||
145 | * @return int - The maximum value that has been detected | ||
146 | * since the last call of peak_meter_read_l. The value | ||
147 | * is in the range 0 <= value < MAX_PEAK. | ||
148 | */ | ||
149 | static int peak_meter_read_l (void) { | ||
150 | int retval = peak_meter_l; | ||
151 | #ifdef SIMULATOR | ||
152 | peak_meter_l = 8000; | ||
153 | #else | ||
154 | peak_meter_l = mas_codec_readreg(peak_meter_src_l); | ||
155 | #endif | ||
156 | return retval; | ||
157 | } | ||
158 | |||
159 | /** | ||
160 | * Reads out the peak volume of the right channel. | ||
161 | * @return int - The maximum value that has been detected | ||
162 | * since the last call of peak_meter_read_l. The value | ||
163 | * is in the range 0 <= value < MAX_PEAK. | ||
164 | */ | ||
165 | static int peak_meter_read_r (void) { | ||
166 | int retval = peak_meter_r; | ||
167 | #ifdef SIMULATOR | ||
168 | peak_meter_l = 8000; | ||
169 | #else | ||
170 | peak_meter_r = mas_codec_readreg(peak_meter_src_r); | ||
171 | #endif | ||
172 | return retval; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * Reset the detected clips. This method is for | ||
177 | * use by the user interface. | ||
178 | * @param int unused - This parameter was added to | ||
179 | * make the function compatible with set_int | ||
180 | */ | ||
181 | void peak_meter_set_clip_hold(int time) { | ||
182 | peak_meter_clip_eternal = false; | ||
183 | |||
184 | if (time <= 0) { | ||
185 | peak_meter_l_clip = false; | ||
186 | peak_meter_r_clip = false; | ||
187 | peak_meter_clip_eternal = true; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | |||
192 | /** | ||
193 | * Draws a peak meter in the specified size at the specified position. | ||
194 | * @param int x - The x coordinate. | ||
195 | * Make sure that 0 <= x and x + width < LCD_WIDTH | ||
196 | * @param int y - The y coordinate. | ||
197 | * Make sure that 0 <= y and y + height < LCD_HEIGHT | ||
198 | * @param int width - The width of the peak meter. Note that for display | ||
199 | * of clips a 3 pixel wide area is used -> | ||
200 | * width > 3 | ||
201 | * @param int height - The height of the peak meter. height > 3 | ||
202 | */ | ||
203 | void peak_meter_draw(int x, int y, int width, int height) { | ||
204 | int left = 0, right = 0; | ||
205 | static int last_left = 0, last_right = 0; | ||
206 | int meterwidth = width - 3; | ||
207 | int i; | ||
208 | |||
209 | /* if disabled only draw the peak meter */ | ||
210 | if (peak_meter_enabled) { | ||
211 | /* read the volume info from MAS */ | ||
212 | left = peak_meter_read_l(); | ||
213 | right = peak_meter_read_r(); | ||
214 | peak_meter_peek(); | ||
215 | |||
216 | |||
217 | /* scale the samples */ | ||
218 | left /= (MAX_PEAK / meterwidth); | ||
219 | right /= (MAX_PEAK / meterwidth); | ||
220 | |||
221 | /* apply release */ | ||
222 | left = MAX(left , last_left - global_settings.peak_meter_release); | ||
223 | right = MAX(right, last_right - global_settings.peak_meter_release); | ||
224 | |||
225 | /* reset max values after timeout */ | ||
226 | if (TIME_AFTER(current_tick, peak_meter_timeout_l)){ | ||
227 | peak_meter_max_l = 0; | ||
228 | } | ||
229 | |||
230 | if (TIME_AFTER(current_tick, peak_meter_timeout_r)){ | ||
231 | peak_meter_max_r = 0; | ||
232 | } | ||
233 | |||
234 | if (!peak_meter_clip_eternal) { | ||
235 | if (peak_meter_l_clip && | ||
236 | TIME_AFTER(current_tick, peak_meter_clip_timeout_l)){ | ||
237 | peak_meter_l_clip = false; | ||
238 | } | ||
239 | |||
240 | if (peak_meter_r_clip && | ||
241 | TIME_AFTER(current_tick, peak_meter_clip_timeout_r)){ | ||
242 | peak_meter_r_clip = false; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | /* check for new max values */ | ||
247 | if (left > peak_meter_max_l) { | ||
248 | peak_meter_max_l = left - 1; | ||
249 | peak_meter_timeout_l = | ||
250 | current_tick + max_time_out[global_settings.peak_meter_hold]; | ||
251 | } | ||
252 | |||
253 | if (right > peak_meter_max_r) { | ||
254 | peak_meter_max_r = right - 1; | ||
255 | peak_meter_timeout_r = | ||
256 | current_tick + max_time_out[global_settings.peak_meter_hold]; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | /* draw the peak meter */ | ||
261 | lcd_clearrect(x, y, width, height); | ||
262 | |||
263 | /* draw left */ | ||
264 | lcd_fillrect (x, y, left, height / 2 - 2 ); | ||
265 | if (peak_meter_max_l > 0) { | ||
266 | lcd_drawline(x + peak_meter_max_l, y, | ||
267 | x + peak_meter_max_l, y + height / 2 - 2 ); | ||
268 | } | ||
269 | if (peak_meter_l_clip) { | ||
270 | lcd_fillrect(x + meterwidth, y, 3, height / 2 - 1); | ||
271 | } | ||
272 | |||
273 | /* draw right */ | ||
274 | lcd_fillrect(x, y + height / 2 + 1, right, height / 2 - 2); | ||
275 | if (peak_meter_max_r > 0) { | ||
276 | lcd_drawline( x + peak_meter_max_r, y + height / 2, | ||
277 | x + peak_meter_max_r, y + height - 2); | ||
278 | } | ||
279 | if (peak_meter_r_clip) { | ||
280 | lcd_fillrect(x + meterwidth, y + height / 2, 3, height / 2 - 1); | ||
281 | } | ||
282 | |||
283 | /* draw scale */ | ||
284 | lcd_drawline(x + meterwidth, y, | ||
285 | x + meterwidth, y + height - 2); | ||
286 | for (i = 0; i < 10; i++) { | ||
287 | lcd_invertpixel(x + meterwidth * i / 10, y + height / 2 - 1); | ||
288 | } | ||
289 | |||
290 | last_left = left; | ||
291 | last_right = right; | ||
292 | } | ||
diff --git a/apps/recorder/peakmeter.h b/apps/recorder/peakmeter.h new file mode 100644 index 0000000000..8d894cd920 --- /dev/null +++ b/apps/recorder/peakmeter.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Philipp Pertermann | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #ifndef __PEAKMETER_H__ | ||
20 | #define __PEAKMETER_H__ | ||
21 | |||
22 | extern bool peak_meter_enabled; | ||
23 | |||
24 | extern void peak_meter_init(void); | ||
25 | extern void peak_meter_playback(bool playback); | ||
26 | extern void peak_meter_draw(int x, int y, int width, int height); | ||
27 | extern void peak_meter_set_clip_hold(int time); | ||
28 | |||
29 | #endif /* __PEAKMETER_H__ */ | ||