diff options
Diffstat (limited to 'apps/plugins/fft/fft.c')
-rw-r--r-- | apps/plugins/fft/fft.c | 1165 |
1 files changed, 1165 insertions, 0 deletions
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c new file mode 100644 index 0000000000..531c9af4cb --- /dev/null +++ b/apps/plugins/fft/fft.c | |||
@@ -0,0 +1,1165 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009 Delyan Kratunov | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "plugin.h" | ||
22 | |||
23 | #include "lib/helper.h" | ||
24 | #include "lib/xlcd.h" | ||
25 | #include "math.h" | ||
26 | #include "thread.h" | ||
27 | |||
28 | #ifndef HAVE_LCD_COLOR | ||
29 | #include "lib/grey.h" | ||
30 | #endif | ||
31 | |||
32 | PLUGIN_HEADER | ||
33 | |||
34 | #ifndef HAVE_LCD_COLOR | ||
35 | GREY_INFO_STRUCT | ||
36 | #endif | ||
37 | |||
38 | #if CONFIG_KEYPAD == ARCHOS_AV300_PAD | ||
39 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
40 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
41 | # define FFT_ORIENTATION BUTTON_F3 | ||
42 | # define FFT_WINDOW BUTTON_F1 | ||
43 | # define FFT_SCALE BUTTON_UP | ||
44 | # define FFT_QUIT BUTTON_OFF | ||
45 | |||
46 | #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ | ||
47 | (CONFIG_KEYPAD == IRIVER_H300_PAD) | ||
48 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
49 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
50 | # define FFT_ORIENTATION BUTTON_REC | ||
51 | # define FFT_WINDOW BUTTON_SELECT | ||
52 | # define FFT_SCALE BUTTON_UP | ||
53 | # define FFT_QUIT BUTTON_OFF | ||
54 | |||
55 | #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ | ||
56 | (CONFIG_KEYPAD == IPOD_3G_PAD) || \ | ||
57 | (CONFIG_KEYPAD == IPOD_1G2G_PAD) | ||
58 | # define MINESWP_SCROLLWHEEL | ||
59 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
60 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
61 | # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT) | ||
62 | # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT) | ||
63 | # define FFT_SCALE BUTTON_MENU | ||
64 | # define FFT_QUIT (BUTTON_SELECT | BUTTON_MENU) | ||
65 | |||
66 | #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) | ||
67 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
68 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
69 | # define FFT_ORIENTATION BUTTON_SELECT | ||
70 | # define FFT_WINDOW BUTTON_PLAY | ||
71 | # define FFT_SCALE BUTTON_UP | ||
72 | # define FFT_QUIT BUTTON_POWER | ||
73 | |||
74 | #elif (CONFIG_KEYPAD == GIGABEAT_PAD) | ||
75 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
76 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
77 | # define FFT_SCALE BUTTON_UP | ||
78 | # define FFT_ORIENTATION BUTTON_SELECT | ||
79 | # define FFT_WINDOW BUTTON_A | ||
80 | # define FFT_QUIT BUTTON_POWER | ||
81 | |||
82 | #elif (CONFIG_KEYPAD == SANSA_E200_PAD) | ||
83 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
84 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
85 | # define FFT_ORIENTATION BUTTON_SELECT | ||
86 | # define FFT_WINDOW BUTTON_REC | ||
87 | # define FFT_SCALE BUTTON_UP | ||
88 | # define FFT_QUIT BUTTON_POWER | ||
89 | |||
90 | #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD) | ||
91 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
92 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
93 | # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT) | ||
94 | # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT) | ||
95 | # define FFT_SCALE BUTTON_UP | ||
96 | # define FFT_QUIT BUTTON_POWER | ||
97 | |||
98 | #elif (CONFIG_KEYPAD == SANSA_C200_PAD) | ||
99 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
100 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
101 | # define FFT_ORIENTATION BUTTON_UP | ||
102 | # define FFT_WINDOW BUTTON_REC | ||
103 | # define FFT_SCALE BUTTON_SELECT | ||
104 | # define FFT_QUIT BUTTON_POWER | ||
105 | #elif (CONFIG_KEYPAD == SANSA_M200_PAD) | ||
106 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
107 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
108 | # define FFT_ORIENTATION BUTTON_UP | ||
109 | # define FFT_WINDOW BUTTON_DOWN | ||
110 | # define FFT_SCALE BUTTON_SELECT | ||
111 | # define FFT_QUIT BUTTON_POWER | ||
112 | #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD) | ||
113 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
114 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
115 | # define FFT_ORIENTATION BUTTON_UP | ||
116 | # define FFT_WINDOW BUTTON_HOME | ||
117 | # define FFT_SCALE BUTTON_SELECT | ||
118 | # define FFT_QUIT BUTTON_POWER | ||
119 | |||
120 | #elif (CONFIG_KEYPAD == IRIVER_H10_PAD) | ||
121 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
122 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
123 | # define FFT_ORIENTATION BUTTON_FF | ||
124 | # define FFT_WINDOW BUTTON_SCROLL_UP | ||
125 | # define FFT_SCALE BUTTON_REW | ||
126 | # define FFT_QUIT BUTTON_POWER | ||
127 | |||
128 | #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) | ||
129 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
130 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
131 | # define FFT_ORIENTATION BUTTON_MENU | ||
132 | # define FFT_WINDOW BUTTON_PREV | ||
133 | # define FFT_SCALE BUTTON_UP | ||
134 | # define FFT_QUIT BUTTON_BACK | ||
135 | |||
136 | #elif (CONFIG_KEYPAD == MROBE100_PAD) | ||
137 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
138 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
139 | # define FFT_ORIENTATION BUTTON_PLAY | ||
140 | # define FFT_WINDOW BUTTON_SELECT | ||
141 | # define FFT_SCALE BUTTON_UP | ||
142 | # define FFT_QUIT BUTTON_POWER | ||
143 | |||
144 | #elif CONFIG_KEYPAD == IAUDIO_M3_PAD | ||
145 | # define FFT_PREV_GRAPH BUTTON_RC_REW | ||
146 | # define FFT_NEXT_GRAPH BUTTON_RC_FF | ||
147 | # define FFT_ORIENTATION BUTTON_RC_MODE | ||
148 | # define FFT_WINDOW BUTTON_RC_PLAY | ||
149 | # define FFT_SCALE BUTTON_RC_VOL_UP | ||
150 | # define FFT_QUIT BUTTON_RC_REC | ||
151 | |||
152 | #elif (CONFIG_KEYPAD == COWON_D2_PAD) | ||
153 | # define FFT_QUIT BUTTON_POWER | ||
154 | # define FFT_PREV_GRAPH BUTTON_PLUS | ||
155 | # define FFT_NEXT_GRAPH BUTTON_MINUS | ||
156 | |||
157 | #elif CONFIG_KEYPAD == CREATIVEZVM_PAD | ||
158 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
159 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
160 | # define FFT_ORIENTATION BUTTON_MENU | ||
161 | # define FFT_WINDOW BUTTON_SELECT | ||
162 | # define FFT_SCALE BUTTON_UP | ||
163 | # define FFT_QUIT BUTTON_BACK | ||
164 | |||
165 | #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD | ||
166 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
167 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
168 | # define FFT_ORIENTATION BUTTON_SELECT | ||
169 | # define FFT_WINDOW BUTTON_MENU | ||
170 | # define FFT_SCALE BUTTON_UP | ||
171 | # define FFT_QUIT BUTTON_POWER | ||
172 | |||
173 | #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD) | ||
174 | # define FFT_PREV_GRAPH BUTTON_LEFT | ||
175 | # define FFT_NEXT_GRAPH BUTTON_RIGHT | ||
176 | # define FFT_ORIENTATION BUTTON_UP | ||
177 | # define FFT_WINDOW BUTTON_DOWN | ||
178 | # define FFT_SCALE BUTTON_FFWD | ||
179 | # define FFT_QUIT BUTTON_PLAY | ||
180 | |||
181 | #else | ||
182 | #error No keymap defined! | ||
183 | #endif | ||
184 | |||
185 | #ifdef HAVE_LCD_COLOR | ||
186 | #include "pluginbitmaps/fft_colors.h" | ||
187 | #endif | ||
188 | |||
189 | #include "kiss_fftr.h" | ||
190 | #include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */ | ||
191 | #include "const.h" | ||
192 | |||
193 | #define FFT_SIZE 2048 | ||
194 | #define ARRAYSIZE_IN (FFT_SIZE) | ||
195 | #define ARRAYSIZE_OUT (FFT_SIZE/2) | ||
196 | #define ARRAYSIZE_PLOT (FFT_SIZE/4) | ||
197 | #define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE-1)) | ||
198 | #define BUFSIZE_FFTR (BUFSIZE_FFT+sizeof(struct kiss_fftr_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE*3/2)) | ||
199 | #define BUFSIZE BUFSIZE_FFTR | ||
200 | #define FFT_ALLOC kiss_fftr_alloc | ||
201 | #define FFT_FFT kiss_fftr | ||
202 | #define FFT_CFG kiss_fftr_cfg | ||
203 | |||
204 | #define __COEFF(type,size) type##_##size | ||
205 | #define _COEFF(x, y) __COEFF(x,y) /* force the preprocessor to evaluate FFT_SIZE) */ | ||
206 | #define HANN_COEFF _COEFF(hann, FFT_SIZE) | ||
207 | #define HAMMING_COEFF _COEFF(hamming, FFT_SIZE) | ||
208 | |||
209 | /****************************** Globals ****************************/ | ||
210 | |||
211 | static kiss_fft_scalar input[ARRAYSIZE_IN]; | ||
212 | static kiss_fft_cpx output[ARRAYSIZE_OUT]; | ||
213 | static int32_t plot[ARRAYSIZE_PLOT]; | ||
214 | static char buffer[BUFSIZE]; | ||
215 | |||
216 | #if LCD_DEPTH > 1 /* greyscale or color, enable spectrogram */ | ||
217 | #define MODES_COUNT 3 | ||
218 | #else | ||
219 | #define MODES_COUNT 2 | ||
220 | #endif | ||
221 | |||
222 | const unsigned char* modes_text[] = { "Lines", "Bars", "Spectrogram" }; | ||
223 | const unsigned char* scales_text[] = { "Linear scale", "Logarithmic scale" }; | ||
224 | const unsigned char* window_text[] = { "Hamming window", "Hann window" }; | ||
225 | |||
226 | struct mutex input_mutex; | ||
227 | bool input_thread_run = true; | ||
228 | bool input_thread_has_data = false; | ||
229 | |||
230 | struct { | ||
231 | int32_t mode; | ||
232 | bool logarithmic; | ||
233 | bool orientation_vertical; | ||
234 | int window_func; | ||
235 | struct { | ||
236 | int column; | ||
237 | int row; | ||
238 | } spectrogram; | ||
239 | struct { | ||
240 | bool orientation; | ||
241 | bool mode; | ||
242 | bool scale; | ||
243 | } changed; | ||
244 | } graph_settings; | ||
245 | |||
246 | #define COLORS BMPWIDTH_fft_colors | ||
247 | |||
248 | /************************* End of globals *************************/ | ||
249 | |||
250 | /************************* Math functions *************************/ | ||
251 | #define QLOG_MAX 286286 | ||
252 | #define QLIN_MAX 1534588906 | ||
253 | #define QLN_10 float_q16(2.302585093) | ||
254 | #define LIN_MAX (QLIN_MAX >> 16) | ||
255 | |||
256 | /* Returns logarithmically scaled values in S15.16 format */ | ||
257 | inline int32_t get_log_value(int32_t value) | ||
258 | { | ||
259 | return Q16_DIV(fp16_log(value), QLN_10); | ||
260 | } | ||
261 | |||
262 | /* Apply window function to input | ||
263 | * 0 - Hamming window | ||
264 | * 1 - Hann window */ | ||
265 | #define WINDOW_COUNT 2 | ||
266 | void apply_window_func(char mode) | ||
267 | { | ||
268 | switch(mode) | ||
269 | { | ||
270 | case 0: /* Hamming window */ | ||
271 | { | ||
272 | size_t i; | ||
273 | for (i = 0; i < ARRAYSIZE_IN; ++i) | ||
274 | { | ||
275 | input[i] = Q15_MUL(input[i] << 15, HAMMING_COEFF[i]) >> 15; | ||
276 | } | ||
277 | break; | ||
278 | } | ||
279 | case 1: /* Hann window */ | ||
280 | { | ||
281 | size_t i; | ||
282 | for (i = 0; i < ARRAYSIZE_IN; ++i) | ||
283 | { | ||
284 | input[i] = Q15_MUL(input[i] << 15, HANN_COEFF[i]) >> 15; | ||
285 | } | ||
286 | break; | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 | /* Calculates the magnitudes from complex numbers and returns the maximum */ | ||
292 | int32_t calc_magnitudes(bool logarithmic) | ||
293 | { | ||
294 | int64_t tmp; | ||
295 | size_t i; | ||
296 | |||
297 | int32_t max = -2147483647; | ||
298 | |||
299 | /* Calculate the magnitude, discarding the phase. | ||
300 | * The sum of the squares can easily overflow the 15-bit (s15.16) | ||
301 | * requirement for fsqrt, so we scale the data down */ | ||
302 | for (i = 0; i < ARRAYSIZE_PLOT; ++i) | ||
303 | { | ||
304 | tmp = output[i].r * output[i].r + output[i].i * output[i].i; | ||
305 | tmp <<= 16; | ||
306 | |||
307 | tmp = fsqrt64(tmp, 16); | ||
308 | |||
309 | if (logarithmic) | ||
310 | tmp = get_log_value(tmp & 0x7FFFFFFF); | ||
311 | |||
312 | plot[i] = tmp; | ||
313 | |||
314 | if (plot[i] > max) | ||
315 | max = plot[i]; | ||
316 | } | ||
317 | return max; | ||
318 | } | ||
319 | /************************ End of math functions ***********************/ | ||
320 | |||
321 | /********************* Plotting functions (modes) *********************/ | ||
322 | void draw_lines_vertical(void); | ||
323 | void draw_lines_horizontal(void); | ||
324 | void draw_bars_vertical(void); | ||
325 | void draw_bars_horizontal(void); | ||
326 | void draw_spectrogram_vertical(void); | ||
327 | void draw_spectrogram_horizontal(void); | ||
328 | |||
329 | void draw(const unsigned char* message) | ||
330 | { | ||
331 | static uint32_t show_message = 0; | ||
332 | static unsigned char* last_message = 0; | ||
333 | |||
334 | static char last_mode = 0; | ||
335 | static bool last_orientation = true, last_scale = true; | ||
336 | |||
337 | if (message != 0) | ||
338 | { | ||
339 | last_message = (unsigned char*) message; | ||
340 | show_message = 5; | ||
341 | } | ||
342 | |||
343 | if(last_mode != graph_settings.mode) | ||
344 | { | ||
345 | last_mode = graph_settings.mode; | ||
346 | graph_settings.changed.mode = true; | ||
347 | } | ||
348 | if(last_scale != graph_settings.logarithmic) | ||
349 | { | ||
350 | last_scale = graph_settings.logarithmic; | ||
351 | graph_settings.changed.scale = true; | ||
352 | } | ||
353 | if(last_orientation != graph_settings.orientation_vertical) | ||
354 | { | ||
355 | last_orientation = graph_settings.orientation_vertical; | ||
356 | graph_settings.changed.orientation = true; | ||
357 | } | ||
358 | #ifdef HAVE_LCD_COLOR | ||
359 | rb->lcd_set_foreground(LCD_DEFAULT_FG); | ||
360 | rb->lcd_set_background(LCD_DEFAULT_BG); | ||
361 | #else | ||
362 | grey_set_foreground(GREY_BLACK); | ||
363 | grey_set_background(GREY_WHITE); | ||
364 | #endif | ||
365 | |||
366 | switch (graph_settings.mode) | ||
367 | { | ||
368 | default: | ||
369 | case 0: { | ||
370 | |||
371 | #ifdef HAVE_LCD_COLOR | ||
372 | rb->lcd_clear_display(); | ||
373 | #else | ||
374 | grey_clear_display(); | ||
375 | #endif | ||
376 | |||
377 | if (graph_settings.orientation_vertical) | ||
378 | draw_lines_vertical(); | ||
379 | else | ||
380 | draw_lines_horizontal(); | ||
381 | break; | ||
382 | } | ||
383 | case 1: { | ||
384 | |||
385 | #ifdef HAVE_LCD_COLOR | ||
386 | rb->lcd_clear_display(); | ||
387 | #else | ||
388 | grey_clear_display(); | ||
389 | #endif | ||
390 | |||
391 | if(graph_settings.orientation_vertical) | ||
392 | draw_bars_vertical(); | ||
393 | else | ||
394 | draw_bars_horizontal(); | ||
395 | |||
396 | break; | ||
397 | } | ||
398 | case 2: { | ||
399 | if(graph_settings.orientation_vertical) | ||
400 | draw_spectrogram_vertical(); | ||
401 | else | ||
402 | draw_spectrogram_horizontal(); | ||
403 | break; | ||
404 | } | ||
405 | } | ||
406 | |||
407 | if (show_message > 0) | ||
408 | { | ||
409 | /* We have a message to show */ | ||
410 | |||
411 | int x, y; | ||
412 | #ifdef HAVE_LCD_COLOR | ||
413 | rb->lcd_getstringsize(last_message, &x, &y); | ||
414 | #else | ||
415 | grey_getstringsize(last_message, &x, &y); | ||
416 | #endif | ||
417 | /* x and y give the size of the box for the popup */ | ||
418 | x += 6; /* 3 px of horizontal padding and */ | ||
419 | y += 4; /* 2 px of vertical padding */ | ||
420 | |||
421 | /* In vertical spectrogram mode, leave space for the popup | ||
422 | * before actually drawing it (if space is needed) */ | ||
423 | if(graph_settings.mode == 2 && | ||
424 | graph_settings.orientation_vertical && | ||
425 | graph_settings.spectrogram.column > LCD_WIDTH-x-2) | ||
426 | { | ||
427 | #ifdef HAVE_LCD_COLOR | ||
428 | xlcd_scroll_left(graph_settings.spectrogram.column - | ||
429 | (LCD_WIDTH - x - 1)); | ||
430 | #else | ||
431 | grey_scroll_left(graph_settings.spectrogram.column - | ||
432 | (LCD_WIDTH - x - 1)); | ||
433 | #endif | ||
434 | graph_settings.spectrogram.column = LCD_WIDTH - x - 2; | ||
435 | } | ||
436 | |||
437 | #ifdef HAVE_LCD_COLOR | ||
438 | rb->lcd_set_foreground(LCD_DARKGRAY); | ||
439 | rb->lcd_fillrect(LCD_WIDTH-1-x, 0, LCD_WIDTH-1, y); | ||
440 | |||
441 | rb->lcd_set_foreground(LCD_DEFAULT_FG); | ||
442 | rb->lcd_set_background(LCD_DARKGRAY); | ||
443 | rb->lcd_putsxy(LCD_WIDTH-1-x+3, 2, last_message); | ||
444 | rb->lcd_set_background(LCD_DEFAULT_BG); | ||
445 | #else | ||
446 | grey_set_foreground(GREY_LIGHTGRAY); | ||
447 | grey_fillrect(LCD_WIDTH-1-x, 0, LCD_WIDTH-1, y); | ||
448 | |||
449 | grey_set_foreground(GREY_BLACK); | ||
450 | grey_set_background(GREY_LIGHTGRAY); | ||
451 | grey_putsxy(LCD_WIDTH-1-x+3, 2, last_message); | ||
452 | grey_set_background(GREY_WHITE); | ||
453 | #endif | ||
454 | |||
455 | show_message--; | ||
456 | } | ||
457 | else if(last_message != 0) | ||
458 | { | ||
459 | if(graph_settings.mode != 2) | ||
460 | { | ||
461 | /* These modes clear the screen themselves */ | ||
462 | last_message = 0; | ||
463 | } | ||
464 | else /* Spectrogram mode - need to erase the popup */ | ||
465 | { | ||
466 | int x, y; | ||
467 | #ifdef HAVE_LCD_COLOR | ||
468 | rb->lcd_getstringsize(last_message, &x, &y); | ||
469 | #else | ||
470 | grey_getstringsize(last_message, &x, &y); | ||
471 | #endif | ||
472 | /* Recalculate the size */ | ||
473 | x += 6; /* 3 px of horizontal padding and */ | ||
474 | y += 4; /* 2 px of vertical padding */ | ||
475 | |||
476 | if(!graph_settings.orientation_vertical) | ||
477 | { | ||
478 | /* In horizontal spectrogram mode, just scroll up by Y lines */ | ||
479 | #ifdef HAVE_LCD_COLOR | ||
480 | xlcd_scroll_up(y); | ||
481 | #else | ||
482 | grey_scroll_up(y); | ||
483 | #endif | ||
484 | graph_settings.spectrogram.row -= y; | ||
485 | if(graph_settings.spectrogram.row < 0) | ||
486 | graph_settings.spectrogram.row = 0; | ||
487 | } | ||
488 | else | ||
489 | { | ||
490 | /* In vertical spectrogram mode, erase the popup */ | ||
491 | #ifdef HAVE_LCD_COLOR | ||
492 | rb->lcd_set_foreground(LCD_DEFAULT_BG); | ||
493 | rb->lcd_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y); | ||
494 | rb->lcd_set_foreground(LCD_DEFAULT_FG); | ||
495 | #else | ||
496 | grey_set_foreground(GREY_WHITE); | ||
497 | grey_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y); | ||
498 | grey_set_foreground(GREY_BLACK); | ||
499 | #endif | ||
500 | } | ||
501 | |||
502 | last_message = 0; | ||
503 | } | ||
504 | } | ||
505 | #ifdef HAVE_LCD_COLOR | ||
506 | rb->lcd_update(); | ||
507 | #else | ||
508 | grey_update(); | ||
509 | #endif | ||
510 | |||
511 | graph_settings.changed.mode = false; | ||
512 | graph_settings.changed.orientation = false; | ||
513 | graph_settings.changed.scale = false; | ||
514 | } | ||
515 | |||
516 | void draw_lines_vertical(void) | ||
517 | { | ||
518 | static int32_t max = 0, vfactor = 0, vfactor_count = 0; | ||
519 | static const int32_t hfactor = | ||
520 | Q16_DIV(LCD_WIDTH << 16, (ARRAYSIZE_PLOT) << 16), | ||
521 | bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_WIDTH; | ||
522 | static bool old_scale = true; | ||
523 | |||
524 | if (old_scale != graph_settings.logarithmic) | ||
525 | old_scale = graph_settings.logarithmic, max = 0; /* reset the graph on scaling mode change */ | ||
526 | |||
527 | int32_t new_max = calc_magnitudes(graph_settings.logarithmic); | ||
528 | |||
529 | if (new_max > max) | ||
530 | { | ||
531 | max = new_max; | ||
532 | vfactor = Q16_DIV(LCD_HEIGHT << 16, max); /* s15.16 */ | ||
533 | vfactor_count = Q16_DIV(vfactor, bins_per_pixel << 16); /* s15.16 */ | ||
534 | } | ||
535 | |||
536 | if (new_max == 0 || max == 0) /* nothing to draw */ | ||
537 | return; | ||
538 | |||
539 | /* take the average of neighboring bins | ||
540 | * if we have to scale the graph horizontally */ | ||
541 | int64_t bins_avg = 0; | ||
542 | bool draw = true; | ||
543 | int32_t i; | ||
544 | for (i = 0; i < ARRAYSIZE_PLOT; ++i) | ||
545 | { | ||
546 | int32_t x = 0, y = 0; | ||
547 | |||
548 | x = Q16_MUL(hfactor, i << 16) >> 16; | ||
549 | //x = (x + (1 << 15)) >> 16; | ||
550 | |||
551 | if (hfactor < 65536) /* hfactor < 0, graph compression */ | ||
552 | { | ||
553 | draw = false; | ||
554 | bins_avg += plot[i]; | ||
555 | |||
556 | /* fix the division by zero warning: | ||
557 | * bins_per_pixel is zero when the graph is expanding; | ||
558 | * execution won't even reach this point - this is a dummy constant | ||
559 | */ | ||
560 | const int32_t div = bins_per_pixel > 0 ? bins_per_pixel : 1; | ||
561 | if ((i + 1) % div == 0) | ||
562 | { | ||
563 | y = Q16_MUL(vfactor_count, bins_avg) >> 16; | ||
564 | |||
565 | bins_avg = 0; | ||
566 | draw = true; | ||
567 | } | ||
568 | } | ||
569 | else | ||
570 | { | ||
571 | y = Q16_MUL(vfactor, plot[i]) >> 16; | ||
572 | draw = true; | ||
573 | } | ||
574 | |||
575 | if (draw) | ||
576 | { | ||
577 | #ifdef HAVE_LCD_COLOR | ||
578 | rb->lcd_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1); | ||
579 | #else | ||
580 | grey_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1); | ||
581 | #endif | ||
582 | } | ||
583 | } | ||
584 | } | ||
585 | |||
586 | void draw_lines_horizontal(void) | ||
587 | { | ||
588 | static int max = 0; | ||
589 | |||
590 | static const int32_t vfactor = | ||
591 | Q16_DIV(LCD_HEIGHT << 16, (ARRAYSIZE_PLOT) << 16), | ||
592 | bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_HEIGHT; | ||
593 | |||
594 | if (graph_settings.changed.scale) | ||
595 | max = 0; /* reset the graph on scaling mode change */ | ||
596 | |||
597 | int32_t new_max = calc_magnitudes(graph_settings.logarithmic); | ||
598 | |||
599 | if (new_max > max) | ||
600 | max = new_max; | ||
601 | |||
602 | if (new_max == 0 || max == 0) /* nothing to draw */ | ||
603 | return; | ||
604 | |||
605 | int32_t hfactor; | ||
606 | |||
607 | hfactor = Q16_DIV((LCD_WIDTH - 1) << 16, max); /* s15.16 */ | ||
608 | |||
609 | /* take the average of neighboring bins | ||
610 | * if we have to scale the graph horizontally */ | ||
611 | int64_t bins_avg = 0; | ||
612 | bool draw = true; | ||
613 | int32_t i; | ||
614 | for (i = 0; i < ARRAYSIZE_PLOT; ++i) | ||
615 | { | ||
616 | int32_t x = 0, y = 0; | ||
617 | |||
618 | y = Q16_MUL(vfactor, i << 16) + (1 << 15); | ||
619 | y >>= 16; | ||
620 | |||
621 | if (vfactor < 65536) /* vfactor < 0, graph compression */ | ||
622 | { | ||
623 | draw = false; | ||
624 | bins_avg += plot[i]; | ||
625 | |||
626 | /* fix the division by zero warning: | ||
627 | * bins_per_pixel is zero when the graph is expanding; | ||
628 | * execution won't even reach this point - this is a dummy constant | ||
629 | */ | ||
630 | const int32_t div = bins_per_pixel > 0 ? bins_per_pixel : 1; | ||
631 | if ((i + 1) % div == 0) | ||
632 | { | ||
633 | bins_avg = Q16_DIV(bins_avg, div << 16); | ||
634 | x = Q16_MUL(hfactor, bins_avg) >> 16; | ||
635 | |||
636 | bins_avg = 0; | ||
637 | draw = true; | ||
638 | } | ||
639 | } | ||
640 | else | ||
641 | { | ||
642 | y = Q16_MUL(hfactor, plot[i]) >> 16; | ||
643 | draw = true; | ||
644 | } | ||
645 | |||
646 | if (draw) | ||
647 | { | ||
648 | #ifdef HAVE_LCD_COLOR | ||
649 | rb->lcd_hline(0, x, y); | ||
650 | #else | ||
651 | grey_hline(0, x, y); | ||
652 | #endif | ||
653 | } | ||
654 | } | ||
655 | } | ||
656 | |||
657 | void draw_bars_vertical(void) | ||
658 | { | ||
659 | static const unsigned int bars = 20, border = 2, items = ARRAYSIZE_PLOT | ||
660 | / bars, width = (LCD_WIDTH - ((bars - 1) * border)) / bars; | ||
661 | |||
662 | calc_magnitudes(graph_settings.logarithmic); | ||
663 | |||
664 | uint64_t bars_values[bars], bars_max = 0, avg = 0; | ||
665 | unsigned int i, bars_idx = 0; | ||
666 | for (i = 0; i < ARRAYSIZE_PLOT; ++i) | ||
667 | { | ||
668 | avg += plot[i]; | ||
669 | if ((i + 1) % items == 0) | ||
670 | { | ||
671 | /* Calculate the average value and keep the fractional part | ||
672 | * for some added precision */ | ||
673 | avg = Q16_DIV(avg, items << 16); | ||
674 | bars_values[bars_idx] = avg; | ||
675 | |||
676 | if (bars_values[bars_idx] > bars_max) | ||
677 | bars_max = bars_values[bars_idx]; | ||
678 | |||
679 | bars_idx++; | ||
680 | avg = 0; | ||
681 | } | ||
682 | } | ||
683 | |||
684 | if(bars_max == 0) /* nothing to draw */ | ||
685 | return; | ||
686 | |||
687 | /* Give the graph some headroom */ | ||
688 | bars_max = Q16_MUL(bars_max, float_q16(1.1)); | ||
689 | |||
690 | uint64_t vfactor = Q16_DIV(LCD_HEIGHT << 16, bars_max); | ||
691 | |||
692 | for (i = 0; i < bars; ++i) | ||
693 | { | ||
694 | int x = (i) * (border + width); | ||
695 | int y; | ||
696 | y = Q16_MUL(vfactor, bars_values[i]) + (1 << 15); | ||
697 | y >>= 16; | ||
698 | #ifdef HAVE_LCD_COLOR | ||
699 | rb->lcd_fillrect(x, LCD_HEIGHT - y - 1, width, y); | ||
700 | #else | ||
701 | grey_fillrect(x, LCD_HEIGHT - y - 1, width, y); | ||
702 | #endif | ||
703 | } | ||
704 | } | ||
705 | |||
706 | void draw_bars_horizontal(void) | ||
707 | { | ||
708 | static const unsigned int bars = 14, border = 3, items = ARRAYSIZE_PLOT | ||
709 | / bars, height = (LCD_HEIGHT - ((bars - 1) * border)) / bars; | ||
710 | |||
711 | calc_magnitudes(graph_settings.logarithmic); | ||
712 | |||
713 | int64_t bars_values[bars], bars_max = 0, avg = 0; | ||
714 | unsigned int i, bars_idx = 0; | ||
715 | for (i = 0; i < ARRAYSIZE_PLOT; ++i) | ||
716 | { | ||
717 | avg += plot[i]; | ||
718 | if ((i + 1) % items == 0) | ||
719 | { | ||
720 | /* Calculate the average value and keep the fractional part | ||
721 | * for some added precision */ | ||
722 | avg = Q16_DIV(avg, items << 16); /* s15.16 */ | ||
723 | bars_values[bars_idx] = avg; | ||
724 | |||
725 | if (bars_values[bars_idx] > bars_max) | ||
726 | bars_max = bars_values[bars_idx]; | ||
727 | |||
728 | bars_idx++; | ||
729 | avg = 0; | ||
730 | } | ||
731 | } | ||
732 | |||
733 | if(bars_max == 0) /* nothing to draw */ | ||
734 | return; | ||
735 | |||
736 | /* Give the graph some headroom */ | ||
737 | bars_max = Q16_MUL(bars_max, float_q16(1.1)); | ||
738 | |||
739 | int64_t hfactor = Q16_DIV(LCD_WIDTH << 16, bars_max); | ||
740 | |||
741 | for (i = 0; i < bars; ++i) | ||
742 | { | ||
743 | int y = (i) * (border + height); | ||
744 | int x; | ||
745 | x = Q16_MUL(hfactor, bars_values[i]) + (1 << 15); | ||
746 | x >>= 16; | ||
747 | |||
748 | #ifdef HAVE_LCD_COLOR | ||
749 | rb->lcd_fillrect(0, y, x, height); | ||
750 | #else | ||
751 | grey_fillrect(0, y, x, height); | ||
752 | #endif | ||
753 | } | ||
754 | } | ||
755 | |||
756 | void draw_spectrogram_vertical(void) | ||
757 | { | ||
758 | const int32_t scale_factor = ARRAYSIZE_PLOT / LCD_HEIGHT | ||
759 | #ifdef HAVE_LCD_COLOR | ||
760 | ,colors_per_val_log = Q16_DIV((COLORS-1) << 16, QLOG_MAX), | ||
761 | colors_per_val_lin = Q16_DIV((COLORS-1) << 16, QLIN_MAX) | ||
762 | #else | ||
763 | ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX), | ||
764 | grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX) | ||
765 | #endif | ||
766 | ; | ||
767 | |||
768 | const int32_t remaining_div = | ||
769 | (ARRAYSIZE_PLOT-scale_factor*LCD_HEIGHT) > 0 ? | ||
770 | ( Q16_DIV((scale_factor*LCD_HEIGHT) << 16, | ||
771 | (ARRAYSIZE_PLOT-scale_factor*LCD_HEIGHT) << 16) | ||
772 | + (1<<15) ) >> 16 : 0; | ||
773 | |||
774 | calc_magnitudes(graph_settings.logarithmic); | ||
775 | if(graph_settings.changed.mode || graph_settings.changed.orientation) | ||
776 | { | ||
777 | graph_settings.spectrogram.column = 0; | ||
778 | #ifdef HAVE_LCD_COLOR | ||
779 | rb->lcd_clear_display(); | ||
780 | #else | ||
781 | grey_clear_display(); | ||
782 | #endif | ||
783 | } | ||
784 | |||
785 | int i, y = LCD_HEIGHT-1, count = 0, rem_count = 0; | ||
786 | uint64_t avg = 0; | ||
787 | bool added_extra_value = false; | ||
788 | for(i = 0; i < ARRAYSIZE_PLOT; ++i) | ||
789 | { | ||
790 | if(plot[i] > 0) | ||
791 | avg += plot[i]; | ||
792 | ++count; | ||
793 | ++rem_count; | ||
794 | |||
795 | /* Kinda hacky - due to the rounding in scale_factor, we try to | ||
796 | * uniformly interweave the extra values in our calculations */ | ||
797 | if(remaining_div > 0 && rem_count >= remaining_div && | ||
798 | i < (ARRAYSIZE_PLOT-1)) | ||
799 | { | ||
800 | ++i; | ||
801 | if(plot[i] > 0) | ||
802 | avg += plot[i]; | ||
803 | rem_count = 0; | ||
804 | added_extra_value = true; | ||
805 | } | ||
806 | |||
807 | if(count >= scale_factor) | ||
808 | { | ||
809 | if(added_extra_value) | ||
810 | { ++count; added_extra_value = false; } | ||
811 | |||
812 | int32_t color; | ||
813 | |||
814 | avg = Q16_DIV(avg, count << 16); | ||
815 | |||
816 | #ifdef HAVE_LCD_COLOR | ||
817 | if(graph_settings.logarithmic) | ||
818 | color = Q16_MUL(avg, colors_per_val_log) >> 16; | ||
819 | else | ||
820 | color = Q16_MUL(avg, colors_per_val_lin) >> 16; | ||
821 | if(color >= COLORS) /* TODO These happen because we don't normalize the values to be above 1 and log() returns negative numbers. I think. */ | ||
822 | color = COLORS-1; | ||
823 | else if (color < 0) | ||
824 | color = 0; | ||
825 | |||
826 | #else | ||
827 | if(graph_settings.logarithmic) | ||
828 | color = Q16_MUL(avg, grey_vals_per_val_log) >> 16; | ||
829 | else | ||
830 | color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16; | ||
831 | if(color > 255) | ||
832 | color = 255; | ||
833 | else if (color < 0) | ||
834 | color = 0; | ||
835 | #endif | ||
836 | |||
837 | #ifdef HAVE_LCD_COLOR | ||
838 | rb->lcd_set_foreground(fft_colors[color]); | ||
839 | rb->lcd_drawpixel(graph_settings.spectrogram.column, y); | ||
840 | #else | ||
841 | grey_set_foreground(255 - color); | ||
842 | grey_drawpixel(graph_settings.spectrogram.column, y); | ||
843 | #endif | ||
844 | |||
845 | y--; | ||
846 | |||
847 | avg = 0; | ||
848 | count = 0; | ||
849 | } | ||
850 | if(y < 0) | ||
851 | break; | ||
852 | } | ||
853 | if(graph_settings.spectrogram.column != LCD_WIDTH-1) | ||
854 | graph_settings.spectrogram.column++; | ||
855 | else | ||
856 | #ifdef HAVE_LCD_COLOR | ||
857 | xlcd_scroll_left(1); | ||
858 | #else | ||
859 | grey_scroll_left(1); | ||
860 | #endif | ||
861 | } | ||
862 | |||
863 | void draw_spectrogram_horizontal(void) | ||
864 | { | ||
865 | const int32_t scale_factor = ARRAYSIZE_PLOT / LCD_WIDTH | ||
866 | #ifdef HAVE_LCD_COLOR | ||
867 | ,colors_per_val_log = Q16_DIV((COLORS-1) << 16, QLOG_MAX), | ||
868 | colors_per_val_lin = Q16_DIV((COLORS-1) << 16, QLIN_MAX) | ||
869 | #else | ||
870 | ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX), | ||
871 | grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX) | ||
872 | #endif | ||
873 | ; | ||
874 | |||
875 | const int32_t remaining_div = | ||
876 | (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) > 0 ? | ||
877 | ( Q16_DIV((scale_factor*LCD_WIDTH) << 16, | ||
878 | (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) << 16) | ||
879 | + (1<<15) ) >> 16 : 0; | ||
880 | |||
881 | calc_magnitudes(graph_settings.logarithmic); | ||
882 | if(graph_settings.changed.mode || graph_settings.changed.orientation) | ||
883 | { | ||
884 | graph_settings.spectrogram.row = 0; | ||
885 | #ifdef HAVE_LCD_COLOR | ||
886 | rb->lcd_clear_display(); | ||
887 | #else | ||
888 | grey_clear_display(); | ||
889 | #endif | ||
890 | } | ||
891 | |||
892 | int i, x = 0, count = 0, rem_count = 0; | ||
893 | uint64_t avg = 0; | ||
894 | bool added_extra_value = false; | ||
895 | for(i = 0; i < ARRAYSIZE_PLOT; ++i) | ||
896 | { | ||
897 | if(plot[i] > 0) | ||
898 | avg += plot[i]; | ||
899 | ++count; | ||
900 | ++rem_count; | ||
901 | |||
902 | /* Kinda hacky - due to the rounding in scale_factor, we try to | ||
903 | * uniformly interweave the extra values in our calculations */ | ||
904 | if(remaining_div > 0 && rem_count >= remaining_div && | ||
905 | i < (ARRAYSIZE_PLOT-1)) | ||
906 | { | ||
907 | ++i; | ||
908 | if(plot[i] > 0) | ||
909 | avg += plot[i]; | ||
910 | rem_count = 0; | ||
911 | added_extra_value = true; | ||
912 | } | ||
913 | |||
914 | if(count >= scale_factor) | ||
915 | { | ||
916 | if(added_extra_value) | ||
917 | { ++count; added_extra_value = false; } | ||
918 | |||
919 | int32_t color; | ||
920 | |||
921 | avg = Q16_DIV(avg, count << 16); | ||
922 | |||
923 | #ifdef HAVE_LCD_COLOR | ||
924 | if(graph_settings.logarithmic) | ||
925 | color = Q16_MUL(avg, colors_per_val_log) >> 16; | ||
926 | else | ||
927 | color = Q16_MUL(avg, colors_per_val_lin) >> 16; | ||
928 | if(color >= COLORS) /* TODO same as _vertical */ | ||
929 | color = COLORS-1; | ||
930 | else if (color < 0) | ||
931 | color = 0; | ||
932 | |||
933 | #else | ||
934 | if(graph_settings.logarithmic) | ||
935 | color = Q16_MUL(avg, grey_vals_per_val_log) >> 16; | ||
936 | else | ||
937 | color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16; | ||
938 | if(color > 255) | ||
939 | color = 255; | ||
940 | else if (color < 0) | ||
941 | color = 0; | ||
942 | #endif | ||
943 | |||
944 | #ifdef HAVE_LCD_COLOR | ||
945 | rb->lcd_set_foreground(fft_colors[color]); | ||
946 | rb->lcd_drawpixel(x, graph_settings.spectrogram.row); | ||
947 | #else | ||
948 | grey_set_foreground(255 - color); | ||
949 | grey_drawpixel(x, graph_settings.spectrogram.row); | ||
950 | #endif | ||
951 | |||
952 | x++; | ||
953 | |||
954 | avg = 0; | ||
955 | count = 0; | ||
956 | } | ||
957 | if(x >= LCD_WIDTH) | ||
958 | break; | ||
959 | } | ||
960 | if(graph_settings.spectrogram.row != LCD_HEIGHT-1) | ||
961 | graph_settings.spectrogram.row++; | ||
962 | else | ||
963 | #ifdef HAVE_LCD_COLOR | ||
964 | xlcd_scroll_up(1); | ||
965 | #else | ||
966 | grey_scroll_up(1); | ||
967 | #endif | ||
968 | } | ||
969 | |||
970 | /********************* End of plotting functions (modes) *********************/ | ||
971 | |||
972 | static long thread_stack[DEFAULT_STACK_SIZE/sizeof(long)]; | ||
973 | void input_thread_entry(void) | ||
974 | { | ||
975 | kiss_fft_scalar * value; | ||
976 | kiss_fft_scalar left; | ||
977 | int count; | ||
978 | int idx = 0; /* offset in the buffer */ | ||
979 | int fft_idx = 0; /* offset in input */ | ||
980 | while(true) | ||
981 | { | ||
982 | rb->mutex_lock(&input_mutex); | ||
983 | if(!input_thread_run) | ||
984 | rb->thread_exit(); | ||
985 | |||
986 | value = (kiss_fft_scalar*) rb->pcm_get_peak_buffer(&count); | ||
987 | |||
988 | if (value == 0 || count == 0) | ||
989 | { | ||
990 | rb->mutex_unlock(&input_mutex); | ||
991 | rb->yield(); | ||
992 | continue; | ||
993 | /* This block can introduce discontinuities in our data. Meaning, the FFT | ||
994 | * will not be done a continuous segment of the signal. Which can be bad. Or not. | ||
995 | * | ||
996 | * Anyway, this is a demo, not a scientific tool. If you want accuracy, do a proper | ||
997 | * spectrum analysis.*/ | ||
998 | } | ||
999 | else | ||
1000 | { | ||
1001 | idx = fft_idx = 0; | ||
1002 | do | ||
1003 | { | ||
1004 | left = *(value + idx); | ||
1005 | idx += 2; | ||
1006 | |||
1007 | input[fft_idx] = left; | ||
1008 | fft_idx++; | ||
1009 | |||
1010 | if (fft_idx == ARRAYSIZE_IN) | ||
1011 | break; | ||
1012 | } while (idx < count); | ||
1013 | } | ||
1014 | if(fft_idx == ARRAYSIZE_IN) /* there are cases when we don't have enough data to fill the buffer */ | ||
1015 | input_thread_has_data = true; | ||
1016 | |||
1017 | rb->mutex_unlock(&input_mutex); | ||
1018 | rb->yield(); | ||
1019 | } | ||
1020 | } | ||
1021 | |||
1022 | |||
1023 | enum plugin_status plugin_start(const void* parameter) | ||
1024 | { | ||
1025 | (void) parameter; | ||
1026 | if ((rb->audio_status() & AUDIO_STATUS_PLAY) == 0) | ||
1027 | { | ||
1028 | rb->splash(HZ * 2, "No track playing. Exiting.."); | ||
1029 | return PLUGIN_OK; | ||
1030 | } | ||
1031 | #ifndef HAVE_LCD_COLOR | ||
1032 | unsigned char *gbuf; | ||
1033 | size_t gbuf_size = 0; | ||
1034 | /* get the remainder of the plugin buffer */ | ||
1035 | gbuf = (unsigned char *) rb->plugin_get_buffer(&gbuf_size); | ||
1036 | |||
1037 | /* initialize the greyscale buffer.*/ | ||
1038 | if (!grey_init(gbuf, gbuf_size, GREY_ON_COP | GREY_BUFFERED, | ||
1039 | LCD_WIDTH, LCD_HEIGHT, NULL)) | ||
1040 | { | ||
1041 | rb->splash(HZ, "Couldn't init greyscale display"); | ||
1042 | return PLUGIN_ERROR; | ||
1043 | } | ||
1044 | grey_show(true); | ||
1045 | #endif | ||
1046 | |||
1047 | #if LCD_DEPTH > 1 | ||
1048 | rb->lcd_set_backdrop(NULL); | ||
1049 | #endif | ||
1050 | backlight_force_on(); | ||
1051 | |||
1052 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
1053 | rb->cpu_boost(true); | ||
1054 | #endif | ||
1055 | |||
1056 | /* Defaults */ | ||
1057 | bool run = true; | ||
1058 | graph_settings.mode = 0; | ||
1059 | graph_settings.logarithmic = true; | ||
1060 | graph_settings.orientation_vertical = true; | ||
1061 | graph_settings.window_func = 0; | ||
1062 | graph_settings.changed.mode = false; | ||
1063 | graph_settings.changed.scale = false; | ||
1064 | graph_settings.changed.orientation = false; | ||
1065 | graph_settings.spectrogram.row = 0; | ||
1066 | graph_settings.spectrogram.column = 0; | ||
1067 | |||
1068 | bool changed_window = false; | ||
1069 | |||
1070 | size_t size = sizeof(buffer); | ||
1071 | FFT_CFG state = FFT_ALLOC(FFT_SIZE, 0, buffer, &size); | ||
1072 | |||
1073 | if (state == 0) | ||
1074 | { | ||
1075 | DEBUGF("needed data: %i", (int) size); | ||
1076 | return PLUGIN_ERROR; | ||
1077 | } | ||
1078 | |||
1079 | unsigned int input_thread = rb->create_thread(&input_thread_entry, thread_stack, sizeof(thread_stack), 0, "fft input thread" IF_PRIO(, PRIORITY_BACKGROUND) IF_COP(, CPU)); | ||
1080 | rb->yield(); | ||
1081 | while (run) | ||
1082 | { | ||
1083 | rb->mutex_lock(&input_mutex); | ||
1084 | if(!input_thread_has_data) | ||
1085 | { | ||
1086 | /* Make sure the input thread has started before doing anything else */ | ||
1087 | rb->mutex_unlock(&input_mutex); | ||
1088 | rb->yield(); | ||
1089 | continue; | ||
1090 | } | ||
1091 | apply_window_func(graph_settings.window_func); | ||
1092 | FFT_FFT(state, input, output); | ||
1093 | |||
1094 | if(changed_window) | ||
1095 | { | ||
1096 | draw(window_text[graph_settings.window_func]); | ||
1097 | changed_window = false; | ||
1098 | } | ||
1099 | else | ||
1100 | draw(0); | ||
1101 | |||
1102 | input_thread_has_data = false; | ||
1103 | rb->mutex_unlock(&input_mutex); | ||
1104 | rb->yield(); | ||
1105 | |||
1106 | int button = rb->button_get(false); | ||
1107 | switch (button) | ||
1108 | { | ||
1109 | case FFT_QUIT: | ||
1110 | run = false; | ||
1111 | break; | ||
1112 | case FFT_PREV_GRAPH: { | ||
1113 | graph_settings.mode--; | ||
1114 | if (graph_settings.mode < 0) | ||
1115 | graph_settings.mode = MODES_COUNT-1; | ||
1116 | draw(modes_text[graph_settings.mode]); | ||
1117 | break; | ||
1118 | } | ||
1119 | case FFT_NEXT_GRAPH: { | ||
1120 | graph_settings.mode++; | ||
1121 | if (graph_settings.mode >= MODES_COUNT) | ||
1122 | graph_settings.mode = 0; | ||
1123 | draw(modes_text[graph_settings.mode]); | ||
1124 | break; | ||
1125 | } | ||
1126 | case FFT_WINDOW: { | ||
1127 | changed_window = true; | ||
1128 | graph_settings.window_func ++; | ||
1129 | if(graph_settings.window_func >= WINDOW_COUNT) | ||
1130 | graph_settings.window_func = 0; | ||
1131 | break; | ||
1132 | } | ||
1133 | case FFT_SCALE: { | ||
1134 | graph_settings.logarithmic = !graph_settings.logarithmic; | ||
1135 | draw(scales_text[graph_settings.logarithmic ? 1 : 0]); | ||
1136 | break; | ||
1137 | } | ||
1138 | case FFT_ORIENTATION: { | ||
1139 | graph_settings.orientation_vertical = !graph_settings.orientation_vertical; | ||
1140 | draw(0); | ||
1141 | break; | ||
1142 | } | ||
1143 | default: { | ||
1144 | if (rb->default_event_handler(button) == SYS_USB_CONNECTED) | ||
1145 | return PLUGIN_USB_CONNECTED; | ||
1146 | } | ||
1147 | |||
1148 | } | ||
1149 | } | ||
1150 | |||
1151 | /* Handle our input thread. We haven't yield()'d since our last mutex_unlock, so we know we have the mutex */ | ||
1152 | rb->mutex_lock(&input_mutex); | ||
1153 | input_thread_run = false; | ||
1154 | rb->mutex_unlock(&input_mutex); | ||
1155 | rb->thread_wait(input_thread); | ||
1156 | |||
1157 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
1158 | rb->cpu_boost(false); | ||
1159 | #endif | ||
1160 | #ifndef HAVE_LCD_COLOR | ||
1161 | grey_release(); | ||
1162 | #endif | ||
1163 | backlight_use_settings(); | ||
1164 | return PLUGIN_OK; | ||
1165 | } | ||