application: Enable chipper and fft plugins.
[maemo-rb.git] / apps / plugins / fft / fft.c
blob1c4e1b4c20a662b33d9088fa5fd14140496afdf9
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2009 Delyan Kratunov
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
23 #include "lib/helper.h"
24 #include "lib/xlcd.h"
25 #include "math.h"
26 #include "fracmul.h"
27 #ifndef HAVE_LCD_COLOR
28 #include "lib/grey.h"
29 #endif
30 #include "lib/mylcd.h"
34 #ifndef HAVE_LCD_COLOR
35 GREY_INFO_STRUCT
36 #endif
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_AMP_SCALE BUTTON_UP
44 # define FFT_QUIT BUTTON_OFF
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_AMP_SCALE BUTTON_UP
53 # define FFT_FREQ_SCALE BUTTON_DOWN
54 # define FFT_QUIT BUTTON_OFF
56 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
57 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
58 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
59 # define MINESWP_SCROLLWHEEL
60 # define FFT_PREV_GRAPH BUTTON_LEFT
61 # define FFT_NEXT_GRAPH BUTTON_RIGHT
62 # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
63 # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
64 # define FFT_AMP_SCALE BUTTON_MENU
65 # define FFT_FREQ_SCALE BUTTON_PLAY
66 # define FFT_QUIT (BUTTON_SELECT | BUTTON_MENU)
68 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
69 # define FFT_PREV_GRAPH BUTTON_LEFT
70 # define FFT_NEXT_GRAPH BUTTON_RIGHT
71 # define FFT_ORIENTATION BUTTON_SELECT
72 # define FFT_WINDOW BUTTON_PLAY
73 # define FFT_AMP_SCALE BUTTON_UP
74 # define FFT_FREQ_SCALE BUTTON_DOWN
75 # define FFT_QUIT BUTTON_POWER
77 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
78 # define FFT_PREV_GRAPH BUTTON_LEFT
79 # define FFT_NEXT_GRAPH BUTTON_RIGHT
80 # define FFT_AMP_SCALE BUTTON_UP
81 # define FFT_FREQ_SCALE BUTTON_DOWN
82 # define FFT_ORIENTATION BUTTON_SELECT
83 # define FFT_WINDOW BUTTON_A
84 # define FFT_QUIT BUTTON_POWER
86 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
87 # define FFT_PREV_GRAPH BUTTON_LEFT
88 # define FFT_NEXT_GRAPH BUTTON_RIGHT
89 # define FFT_ORIENTATION BUTTON_SELECT
90 # define FFT_WINDOW BUTTON_REC
91 # define FFT_AMP_SCALE BUTTON_UP
92 # define FFT_FREQ_SCALE BUTTON_DOWN
93 # define FFT_QUIT BUTTON_POWER
95 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
96 # define FFT_PREV_GRAPH BUTTON_LEFT
97 # define FFT_NEXT_GRAPH BUTTON_RIGHT
98 # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
99 # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
100 # define FFT_AMP_SCALE BUTTON_UP
101 # define FFT_FREQ_SCALE BUTTON_DOWN
102 # define FFT_QUIT (BUTTON_HOME|BUTTON_REPEAT)
104 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
105 # define FFT_PREV_GRAPH BUTTON_LEFT
106 # define FFT_NEXT_GRAPH BUTTON_RIGHT
107 # define FFT_ORIENTATION BUTTON_UP
108 # define FFT_WINDOW BUTTON_REC
109 # define FFT_AMP_SCALE BUTTON_SELECT
110 # define FFT_QUIT BUTTON_POWER
111 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
112 # define FFT_PREV_GRAPH BUTTON_LEFT
113 # define FFT_NEXT_GRAPH BUTTON_RIGHT
114 # define FFT_ORIENTATION BUTTON_UP
115 # define FFT_WINDOW BUTTON_DOWN
116 # define FFT_AMP_SCALE BUTTON_SELECT
117 # define FFT_QUIT BUTTON_POWER
118 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
119 # define FFT_PREV_GRAPH BUTTON_LEFT
120 # define FFT_NEXT_GRAPH BUTTON_RIGHT
121 # define FFT_ORIENTATION BUTTON_UP
122 # define FFT_WINDOW BUTTON_HOME
123 # define FFT_AMP_SCALE BUTTON_SELECT
124 # define FFT_QUIT BUTTON_POWER
126 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
127 # define FFT_PREV_GRAPH BUTTON_LEFT
128 # define FFT_NEXT_GRAPH BUTTON_RIGHT
129 # define FFT_ORIENTATION BUTTON_FF
130 # define FFT_WINDOW BUTTON_SCROLL_UP
131 # define FFT_AMP_SCALE BUTTON_REW
132 # define FFT_FREQ_SCALE BUTTON_PLAY
133 # define FFT_QUIT BUTTON_POWER
135 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
136 # define FFT_PREV_GRAPH BUTTON_LEFT
137 # define FFT_NEXT_GRAPH BUTTON_RIGHT
138 # define FFT_ORIENTATION BUTTON_MENU
139 # define FFT_WINDOW BUTTON_PREV
140 # define FFT_AMP_SCALE BUTTON_UP
141 # define FFT_FREQ_SCALE BUTTON_DOWN
142 # define FFT_QUIT BUTTON_BACK
144 #elif (CONFIG_KEYPAD == MROBE100_PAD)
145 # define FFT_PREV_GRAPH BUTTON_LEFT
146 # define FFT_NEXT_GRAPH BUTTON_RIGHT
147 # define FFT_ORIENTATION BUTTON_PLAY
148 # define FFT_WINDOW BUTTON_SELECT
149 # define FFT_AMP_SCALE BUTTON_UP
150 # define FFT_FREQ_SCALE BUTTON_DOWN
151 # define FFT_QUIT BUTTON_POWER
153 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
154 # define FFT_PREV_GRAPH BUTTON_RC_REW
155 # define FFT_NEXT_GRAPH BUTTON_RC_FF
156 # define FFT_ORIENTATION BUTTON_RC_MODE
157 # define FFT_WINDOW BUTTON_RC_PLAY
158 # define FFT_AMP_SCALE BUTTON_RC_VOL_UP
159 # define FFT_QUIT BUTTON_RC_REC
161 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
162 # define FFT_QUIT BUTTON_POWER
163 # define FFT_PREV_GRAPH BUTTON_PLUS
164 # define FFT_NEXT_GRAPH BUTTON_MINUS
166 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
167 # define FFT_PREV_GRAPH BUTTON_LEFT
168 # define FFT_NEXT_GRAPH BUTTON_RIGHT
169 # define FFT_ORIENTATION BUTTON_MENU
170 # define FFT_WINDOW BUTTON_SELECT
171 # define FFT_AMP_SCALE BUTTON_UP
172 # define FFT_FREQ_SCALE BUTTON_DOWN
173 # define FFT_QUIT BUTTON_BACK
175 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
176 # define FFT_PREV_GRAPH BUTTON_LEFT
177 # define FFT_NEXT_GRAPH BUTTON_RIGHT
178 # define FFT_ORIENTATION BUTTON_SELECT
179 # define FFT_WINDOW BUTTON_MENU
180 # define FFT_AMP_SCALE BUTTON_UP
181 # define FFT_FREQ_SCALE BUTTON_DOWN
182 # define FFT_QUIT BUTTON_POWER
184 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
185 # define FFT_PREV_GRAPH BUTTON_PREV
186 # define FFT_NEXT_GRAPH BUTTON_NEXT
187 # define FFT_ORIENTATION BUTTON_PLAY
188 # define FFT_WINDOW BUTTON_MENU
189 # define FFT_AMP_SCALE BUTTON_UP
190 # define FFT_FREQ_SCALE BUTTON_DOWN
191 # define FFT_QUIT BUTTON_POWER
193 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
194 # define FFT_PREV_GRAPH BUTTON_PREV
195 # define FFT_NEXT_GRAPH BUTTON_NEXT
196 # define FFT_ORIENTATION BUTTON_PLAY
197 # define FFT_WINDOW BUTTON_MENU
198 # define FFT_AMP_SCALE BUTTON_UP
199 # define FFT_FREQ_SCALE BUTTON_DOWN
200 # define FFT_QUIT BUTTON_POWER
202 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
203 # define FFT_PREV_GRAPH BUTTON_LEFT
204 # define FFT_NEXT_GRAPH BUTTON_RIGHT
205 # define FFT_ORIENTATION BUTTON_UP
206 # define FFT_WINDOW BUTTON_DOWN
207 # define FFT_AMP_SCALE BUTTON_FFWD
208 # define FFT_QUIT BUTTON_PLAY
210 #elif (CONFIG_KEYPAD == MROBE500_PAD)
211 # define FFT_QUIT BUTTON_POWER
213 #elif (CONFIG_KEYPAD == ONDAVX747_PAD)
214 # define FFT_QUIT BUTTON_POWER
216 #elif (CONFIG_KEYPAD == ONDAVX777_PAD)
217 # define FFT_QUIT BUTTON_POWER
219 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
220 # define FFT_PREV_GRAPH BUTTON_PREV
221 # define FFT_NEXT_GRAPH BUTTON_NEXT
222 # define FFT_ORIENTATION BUTTON_MENU
223 # define FFT_WINDOW BUTTON_OK
224 # define FFT_AMP_SCALE BUTTON_PLAY
225 # define FFT_QUIT BUTTON_REC
227 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
228 # define FFT_PREV_GRAPH BUTTON_REW
229 # define FFT_NEXT_GRAPH BUTTON_FF
230 # define FFT_ORIENTATION BUTTON_REC
231 # define FFT_WINDOW BUTTON_FUNC
232 # define FFT_AMP_SCALE BUTTON_PLAY
233 # define FFT_QUIT (BUTTON_REC | BUTTON_PLAY)
235 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
236 # define FFT_PREV_GRAPH BUTTON_REW
237 # define FFT_NEXT_GRAPH BUTTON_FF
238 # define FFT_ORIENTATION BUTTON_REC
239 # define FFT_WINDOW BUTTON_ENTER
240 # define FFT_AMP_SCALE BUTTON_PLAY
241 # define FFT_QUIT (BUTTON_REC | BUTTON_REPEAT)
243 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
244 # define FFT_PREV_GRAPH BUTTON_LEFT
245 # define FFT_NEXT_GRAPH BUTTON_RIGHT
246 # define FFT_ORIENTATION BUTTON_BACK
247 # define FFT_WINDOW BUTTON_SELECT
248 # define FFT_AMP_SCALE BUTTON_PLAYPAUSE
249 # define FFT_QUIT BUTTON_POWER
251 #elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
252 # define FFT_PREV_GRAPH BUTTON_LEFT
253 # define FFT_NEXT_GRAPH BUTTON_RIGHT
254 # define FFT_ORIENTATION BUTTON_SELECT
255 # define FFT_WINDOW BUTTON_VOL_DOWN
256 # define FFT_AMP_SCALE BUTTON_UP
257 # define FFT_FREQ_SCALE BUTTON_DOWN
258 # define FFT_QUIT BUTTON_POWER
260 #elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
261 # define FFT_PREV_GRAPH BUTTON_LEFT
262 # define FFT_NEXT_GRAPH BUTTON_RIGHT
263 # define FFT_ORIENTATION BUTTON_USER
264 # define FFT_WINDOW BUTTON_MENU
265 # define FFT_AMP_SCALE BUTTON_SELECT
266 # define FFT_FREQ_SCALE BUTTON_DOWN
267 # define FFT_QUIT BUTTON_BACK
269 #elif !defined(HAVE_TOUCHSCREEN)
270 #error No keymap defined!
271 #endif
273 #ifdef HAVE_TOUCHSCREEN
274 #ifndef FFT_PREV_GRAPH
275 # define FFT_PREV_GRAPH BUTTON_MIDLEFT
276 #endif
277 #ifndef FFT_NEXT_GRAPH
278 # define FFT_NEXT_GRAPH BUTTON_MIDRIGHT
279 #endif
280 #ifndef FFT_ORIENTATION
281 # define FFT_ORIENTATION BUTTON_CENTER
282 #endif
283 #ifndef FFT_WINDOW
284 # define FFT_WINDOW BUTTON_TOPLEFT
285 #endif
286 #ifndef FFT_AMP_SCALE
287 # define FFT_AMP_SCALE BUTTON_TOPRIGHT
288 #endif
289 #ifndef FFT_QUIT
290 # define FFT_QUIT BUTTON_BOTTOMLEFT
291 #endif
292 #endif /* HAVE_TOUCHSCREEN */
294 #ifdef HAVE_LCD_COLOR
295 #include "pluginbitmaps/fft_colors.h"
296 #endif
298 #include "kiss_fftr.h"
299 #include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */
300 #include "const.h"
302 #define LCD_SIZE MAX(LCD_WIDTH, LCD_HEIGHT)
304 #if (LCD_SIZE <= 511)
305 #define FFT_SIZE 1024 /* 512*2 */
306 #elif (LCD_SIZE <= 1023)
307 #define FFT_SIZE 2048 /* 1024*2 */
308 #else
309 #define FFT_SIZE 4096 /* 2048*2 */
310 #endif
312 #define ARRAYLEN_IN (FFT_SIZE)
313 #define ARRAYLEN_OUT (FFT_SIZE)
314 #define ARRAYLEN_PLOT (FFT_SIZE/2-1) /* FFT is symmetric, ignore DC */
315 #define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE-1))
317 #define __COEFF(type,size) type##_##size
318 #define _COEFF(x, y) __COEFF(x,y) /* force the preprocessor to evaluate FFT_SIZE) */
319 #define HANN_COEFF _COEFF(hann, FFT_SIZE)
320 #define HAMMING_COEFF _COEFF(hamming, FFT_SIZE)
322 /****************************** Globals ****************************/
323 /* cacheline-aligned buffers with COP, otherwise word-aligned */
324 /* CPU/COP only applies when compiled for more than one core */
326 #define CACHEALIGN_UP_SIZE(type, len) \
327 (CACHEALIGN_UP((len)*sizeof(type) + (sizeof(type)-1)) / sizeof(type))
328 /* Shared */
329 /* COP + CPU PCM */
330 static kiss_fft_cpx input[CACHEALIGN_UP_SIZE(kiss_fft_scalar, ARRAYLEN_IN)]
331 CACHEALIGN_AT_LEAST_ATTR(4);
332 /* CPU+COP */
333 #if NUM_CORES > 1
334 /* Output queue indexes */
335 static volatile int output_head SHAREDBSS_ATTR = 0;
336 static volatile int output_tail SHAREDBSS_ATTR = 0;
337 /* The result is nfft/2 complex frequency bins from DC to Nyquist. */
338 static kiss_fft_cpx output[2][CACHEALIGN_UP_SIZE(kiss_fft_cpx, ARRAYLEN_OUT)]
339 SHAREDBSS_ATTR;
340 #else
341 /* Only one output buffer */
342 #define output_head 0
343 #define output_tail 0
344 /* The result is nfft/2 complex frequency bins from DC to Nyquist. */
345 static kiss_fft_cpx output[1][ARRAYLEN_OUT];
346 #endif
348 /* Unshared */
349 /* COP */
350 static kiss_fft_cfg fft_state SHAREDBSS_ATTR;
351 static char fft_buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE_FFT)]
352 CACHEALIGN_AT_LEAST_ATTR(4);
353 /* CPU */
354 static int32_t plot_history[ARRAYLEN_PLOT];
355 static int32_t plot[ARRAYLEN_PLOT];
356 static struct
358 int16_t bin; /* integer bin number */
359 uint16_t frac; /* interpolation fraction */
360 } binlog[ARRAYLEN_PLOT] __attribute__((aligned(4)));
362 enum fft_window_func
364 FFT_WF_FIRST = 0,
365 FFT_WF_HAMMING = 0,
366 FFT_WF_HANN,
368 #define FFT_WF_COUNT (FFT_WF_HANN+1)
370 enum fft_display_mode
372 FFT_DM_FIRST = 0,
373 FFT_DM_LINES = 0,
374 FFT_DM_BARS,
375 FFT_DM_SPECTROGRAPH,
377 #define FFT_DM_COUNT (FFT_DM_SPECTROGRAPH+1)
379 static const unsigned char* const modes_text[FFT_DM_COUNT] =
380 { "Lines", "Bars", "Spectrogram" };
382 static const unsigned char* const amp_scales_text[2] =
383 { "Linear amplitude", "Logarithmic amplitude" };
385 static const unsigned char* const freq_scales_text[2] =
386 { "Linear frequency", "Logarithmic frequency" };
388 static const unsigned char* const window_text[FFT_WF_COUNT] =
389 { "Hamming window", "Hann window" };
391 static struct {
392 bool orientation_vertical;
393 enum fft_display_mode mode;
394 bool logarithmic_amp;
395 bool logarithmic_freq;
396 enum fft_window_func window_func;
397 int spectrogram_pos; /* row or column - only used by one at a time */
398 union
400 struct
402 bool orientation : 1;
403 bool mode : 1;
404 bool amp_scale : 1;
405 bool freq_scale : 1;
406 bool window_func : 1;
407 bool do_clear : 1;
409 bool clear_all; /* Write 'false' to clear all above */
410 } changed;
411 } graph_settings SHAREDDATA_ATTR =
413 /* Defaults */
414 .orientation_vertical = true,
415 .mode = FFT_DM_LINES,
416 .logarithmic_amp = true,
417 .logarithmic_freq = true,
418 .window_func = FFT_WF_HAMMING,
419 .spectrogram_pos = 0,
420 .changed = { .clear_all = false },
423 #ifdef HAVE_LCD_COLOR
424 #define SHADES BMPWIDTH_fft_colors
425 #define SPECTROGRAPH_PALETTE(index) (fft_colors[index])
426 #else
427 #define SHADES 256
428 #define SPECTROGRAPH_PALETTE(index) (255 - (index))
429 #endif
431 /************************* End of globals *************************/
433 /************************* Math functions *************************/
435 /* Based on feeding-in a 0db sinewave at FS/4 */
436 #define QLOG_MAX 0x0009154B
437 /* fudge it a little or it's not very visbile */
438 #define QLIN_MAX (0x00002266 >> 1)
440 /* Apply window function to input */
441 static void apply_window_func(enum fft_window_func mode)
443 int i;
445 switch(mode)
447 case FFT_WF_HAMMING:
448 for(i = 0; i < ARRAYLEN_IN; ++i)
450 input[i].r = (input[i].r * HAMMING_COEFF[i] + 16384) >> 15;
452 break;
454 case FFT_WF_HANN:
455 for(i = 0; i < ARRAYLEN_IN; ++i)
457 input[i].r = (input[i].r * HANN_COEFF[i] + 16384) >> 15;
459 break;
463 /* Calculates the magnitudes from complex numbers and returns the maximum */
464 static int32_t calc_magnitudes(bool logarithmic_amp)
466 /* A major assumption made when calculating the Q*MAX constants
467 * is that the maximum magnitude is 29 bits long. */
468 uint32_t max = 0;
469 kiss_fft_cpx *this_output = output[output_head] + 1; /* skip DC */
470 int i;
472 /* Calculate the magnitude, discarding the phase. */
473 for(i = 0; i < ARRAYLEN_PLOT; ++i)
475 int32_t re = this_output[i].r;
476 int32_t im = this_output[i].i;
478 uint32_t tmp = re*re + im*im;
480 if(tmp > 0)
482 if(tmp > 0x7FFFFFFF) /* clip */
484 tmp = 0x7FFFFFFF; /* if our assumptions are correct,
485 this should never happen. It's just
486 a safeguard. */
489 if(logarithmic_amp)
491 if(tmp < 0x8000) /* be more precise */
493 /* ln(x ^ .5) = .5*ln(x) */
494 tmp = fp16_log(tmp << 16) >> 1;
496 else
498 tmp = isqrt(tmp); /* linear scaling, nothing
499 bad should happen */
500 tmp = fp16_log(tmp << 16); /* the log function
501 expects s15.16 values */
504 else
506 tmp = isqrt(tmp); /* linear scaling, nothing
507 bad should happen */
511 /* Length 2 moving average - last transform and this one */
512 tmp = (plot_history[i] + tmp) >> 1;
513 plot[i] = tmp;
514 plot_history[i] = tmp;
516 if(tmp > max)
517 max = tmp;
520 return max;
523 /* Move plot bins into a logarithmic scale by sliding them towards the
524 * Nyquist bin according to the translation in the binlog array. */
525 static void logarithmic_plot_translate(void)
527 int i;
529 for(i = ARRAYLEN_PLOT-1; i > 0; --i)
531 int bin;
532 int s = binlog[i].bin;
533 int e = binlog[i-1].bin;
534 int frac = binlog[i].frac;
536 bin = plot[s];
538 if(frac)
540 /* slope < 1, Interpolate stretched bins (linear for now) */
541 int diff = plot[s+1] - bin;
545 plot[i] = bin + FRACMUL(frac << 15, diff);
546 frac = binlog[--i].frac;
548 while(frac);
550 else
552 /* slope > 1, Find peak of two or more bins */
553 while(--s > e)
555 int val = plot[s];
557 if (val > bin)
558 bin = val;
562 plot[i] = bin;
566 /* Calculates the translation for logarithmic plot bins */
567 static void logarithmic_plot_init(void)
569 int i, j;
571 * log: y = round(n * ln(x) / ln(n))
572 * anti: y = round(exp(x * ln(n) / n))
574 j = fp16_log((ARRAYLEN_PLOT - 1) << 16);
575 for(i = 0; i < ARRAYLEN_PLOT; ++i)
577 binlog[i].bin = (fp16_exp(i * j / (ARRAYLEN_PLOT - 1)) + 32768) >> 16;
580 /* setup fractions for interpolation of stretched bins */
581 for(i = 0; i < ARRAYLEN_PLOT-1; i = j)
583 j = i + 1;
585 /* stop when we have two different values */
586 while(binlog[j].bin == binlog[i].bin)
587 j++; /* if here, local slope of curve is < 1 */
589 if(j > i + 1)
591 /* distribute pieces evenly over stretched interval */
592 int diff = j - i;
593 int x = 0;
596 binlog[i].frac = (x++ << 16) / diff;
598 while(++i < j);
603 /************************ End of math functions ***********************/
605 /********************* Plotting functions (modes) *********************/
606 static void draw_lines_vertical(void);
607 static void draw_lines_horizontal(void);
608 static void draw_bars_vertical(void);
609 static void draw_bars_horizontal(void);
610 static void draw_spectrogram_vertical(void);
611 static void draw_spectrogram_horizontal(void);
613 #define COLOR_DEFAULT_FG MYLCD_DEFAULT_FG
614 #define COLOR_DEFAULT_BG MYLCD_DEFAULT_BG
616 #ifdef HAVE_LCD_COLOR
617 #define COLOR_MESSAGE_FRAME LCD_RGBPACK(0xc6, 0x00, 0x00)
618 #define COLOR_MESSAGE_BG LCD_BLACK
619 #define COLOR_MESSAGE_FG LCD_WHITE
620 #else
621 #define COLOR_MESSAGE_FRAME GREY_DARKGRAY
622 #define COLOR_MESSAGE_BG GREY_WHITE
623 #define COLOR_MESSAGE_FG GREY_BLACK
624 #endif
626 #define POPUP_HPADDING 3 /* 3 px of horizontal padding and */
627 #define POPUP_VPADDING 2 /* 2 px of vertical padding */
629 static void draw_message_string(const unsigned char *message, bool active)
631 int x, y;
632 mylcd_getstringsize(message, &x, &y);
634 /* x and y give the size of the box for the popup */
635 x += POPUP_HPADDING*2;
636 y += POPUP_VPADDING*2;
638 /* In vertical spectrogram mode, leave space for the popup
639 * before actually drawing it (if space is needed) */
640 if(active &&
641 graph_settings.mode == FFT_DM_SPECTROGRAPH &&
642 graph_settings.orientation_vertical &&
643 graph_settings.spectrogram_pos >= LCD_WIDTH - x)
645 mylcd_scroll_left(graph_settings.spectrogram_pos -
646 LCD_WIDTH + x);
647 graph_settings.spectrogram_pos = LCD_WIDTH - x - 1;
650 mylcd_set_foreground(COLOR_MESSAGE_FRAME);
651 mylcd_fillrect(LCD_WIDTH - x, 0, LCD_WIDTH - 1, y);
653 mylcd_set_foreground(COLOR_MESSAGE_FG);
654 mylcd_set_background(COLOR_MESSAGE_BG);
655 mylcd_putsxy(LCD_WIDTH - x + POPUP_HPADDING,
656 POPUP_VPADDING, message);
657 mylcd_set_foreground(COLOR_DEFAULT_FG);
658 mylcd_set_background(COLOR_DEFAULT_BG);
661 static void draw(const unsigned char* message)
663 static long show_message_tick = 0;
664 static const unsigned char* last_message = 0;
666 if(message != NULL)
668 last_message = message;
669 show_message_tick = (*rb->current_tick + HZ) | 1;
672 /* maybe take additional actions depending upon the changed setting */
673 if(graph_settings.changed.orientation)
675 graph_settings.changed.amp_scale = true;
676 graph_settings.changed.do_clear = true;
679 if(graph_settings.changed.mode)
681 graph_settings.changed.amp_scale = true;
682 graph_settings.changed.do_clear = true;
685 if(graph_settings.changed.amp_scale)
686 memset(plot_history, 0, sizeof (plot_history));
688 if(graph_settings.changed.freq_scale)
689 graph_settings.changed.freq_scale = true;
691 mylcd_set_foreground(COLOR_DEFAULT_FG);
692 mylcd_set_background(COLOR_DEFAULT_BG);
694 switch (graph_settings.mode)
696 default:
697 case FFT_DM_LINES: {
699 mylcd_clear_display();
701 if (graph_settings.orientation_vertical)
702 draw_lines_vertical();
703 else
704 draw_lines_horizontal();
705 break;
707 case FFT_DM_BARS: {
709 mylcd_clear_display();
711 if(graph_settings.orientation_vertical)
712 draw_bars_vertical();
713 else
714 draw_bars_horizontal();
716 break;
718 case FFT_DM_SPECTROGRAPH: {
720 if(graph_settings.changed.do_clear)
722 graph_settings.spectrogram_pos = 0;
723 mylcd_clear_display();
726 if(graph_settings.orientation_vertical)
727 draw_spectrogram_vertical();
728 else
729 draw_spectrogram_horizontal();
730 break;
734 if(show_message_tick != 0)
736 if(TIME_BEFORE(*rb->current_tick, show_message_tick))
738 /* We have a message to show */
739 draw_message_string(last_message, true);
741 else
743 /* Stop drawing message */
744 show_message_tick = 0;
747 else if(last_message != NULL)
749 if(graph_settings.mode == FFT_DM_SPECTROGRAPH)
751 /* Spectrogram mode - need to erase the popup */
752 int x, y;
753 mylcd_getstringsize(last_message, &x, &y);
754 /* Recalculate the size */
755 x += POPUP_HPADDING*2;
756 y += POPUP_VPADDING*2;
758 if(!graph_settings.orientation_vertical)
760 /* In horizontal spectrogram mode, just scroll up by Y lines */
761 mylcd_scroll_up(y);
762 graph_settings.spectrogram_pos -= y;
763 if(graph_settings.spectrogram_pos < 0)
764 graph_settings.spectrogram_pos = 0;
766 else
768 /* In vertical spectrogram mode, erase the popup */
769 mylcd_set_foreground(COLOR_DEFAULT_BG);
770 mylcd_fillrect(graph_settings.spectrogram_pos + 1, 0,
771 LCD_WIDTH, y);
772 mylcd_set_foreground(COLOR_DEFAULT_FG);
775 /* else These modes clear the screen themselves */
777 last_message = NULL;
780 mylcd_update();
782 graph_settings.changed.clear_all = false;
785 static void draw_lines_vertical(void)
787 static int max = 0;
789 #if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
790 const int offset = 0;
791 const int plotwidth = LCD_WIDTH;
792 #else
793 const int offset = (LCD_HEIGHT - ARRAYLEN_PLOT) / 2;
794 const int plotwidth = ARRAYLEN_PLOT;
795 #endif
797 int this_max;
798 int i, x;
800 if(graph_settings.changed.amp_scale)
801 max = 0; /* reset the graph on scaling mode change */
803 this_max = calc_magnitudes(graph_settings.logarithmic_amp);
805 if(this_max == 0)
807 mylcd_hline(0, LCD_WIDTH - 1, LCD_HEIGHT - 1); /* Draw all "zero" */
808 return;
811 if(graph_settings.logarithmic_freq)
812 logarithmic_plot_translate();
814 /* take the maximum of neighboring bins if we have to scale the graph
815 * horizontally */
816 if(LCD_WIDTH < ARRAYLEN_PLOT) /* graph compression */
818 int bins_acc = LCD_WIDTH / 2;
819 int bins_max = 0;
821 i = 0, x = 0;
823 for(;;)
825 int bin = plot[i++];
827 if(bin > bins_max)
828 bins_max = bin;
830 bins_acc += LCD_WIDTH;
832 if(bins_acc >= ARRAYLEN_PLOT)
834 plot[x] = bins_max;
836 if(bins_max > max)
837 max = bins_max;
839 if(++x >= LCD_WIDTH)
840 break;
842 bins_acc -= ARRAYLEN_PLOT;
843 bins_max = 0;
847 else
849 if(this_max > max)
850 max = this_max;
853 for(x = 0; x < plotwidth; ++x)
855 int h = LCD_HEIGHT*plot[x] / max;
856 mylcd_vline(x + offset, LCD_HEIGHT - h, LCD_HEIGHT-1);
860 static void draw_lines_horizontal(void)
862 static int max = 0;
864 #if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
865 const int offset = 0;
866 const int plotwidth = LCD_HEIGHT;
867 #else
868 const int offset = (LCD_HEIGHT - ARRAYLEN_PLOT) / 2;
869 const int plotwidth = ARRAYLEN_PLOT;
870 #endif
872 int this_max;
873 int y;
875 if(graph_settings.changed.amp_scale)
876 max = 0; /* reset the graph on scaling mode change */
878 this_max = calc_magnitudes(graph_settings.logarithmic_amp);
880 if(this_max == 0)
882 mylcd_vline(0, 0, LCD_HEIGHT-1); /* Draw all "zero" */
883 return;
886 if(graph_settings.logarithmic_freq)
887 logarithmic_plot_translate();
889 /* take the maximum of neighboring bins if we have to scale the graph
890 * horizontally */
891 if(LCD_HEIGHT < ARRAYLEN_PLOT) /* graph compression */
893 int bins_acc = LCD_HEIGHT / 2;
894 int bins_max = 0;
895 int i = 0;
897 y = 0;
899 for(;;)
901 int bin = plot[i++];
903 if (bin > bins_max)
904 bins_max = bin;
906 bins_acc += LCD_HEIGHT;
908 if(bins_acc >= ARRAYLEN_PLOT)
910 plot[y] = bins_max;
912 if(bins_max > max)
913 max = bins_max;
915 if(++y >= LCD_HEIGHT)
916 break;
918 bins_acc -= ARRAYLEN_PLOT;
919 bins_max = 0;
923 else
925 if(this_max > max)
926 max = this_max;
929 for(y = 0; y < plotwidth; ++y)
931 int w = LCD_WIDTH*plot[y] / max;
932 mylcd_hline(0, w - 1, y + offset);
936 static void draw_bars_vertical(void)
938 static int max = 0;
940 #if LCD_WIDTH < LCD_HEIGHT
941 const int bars = 15;
942 #else
943 const int bars = 20;
944 #endif
945 const int border = 2;
946 const int barwidth = LCD_WIDTH / (bars + border);
947 const int width = barwidth - border;
948 const int offset = (LCD_WIDTH - bars*barwidth) / 2;
950 if(graph_settings.changed.amp_scale)
951 max = 0; /* reset the graph on scaling mode change */
953 mylcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-1); /* Draw baseline */
955 if(calc_magnitudes(graph_settings.logarithmic_amp) == 0)
956 return; /* nothing more to draw */
958 if(graph_settings.logarithmic_freq)
959 logarithmic_plot_translate();
961 int bins_acc = bars / 2;
962 int bins_max = 0;
963 int x = 0, i = 0;
965 for(;;)
967 int bin = plot[i++];
969 if(bin > bins_max)
970 bins_max = bin;
972 bins_acc += bars;
974 if(bins_acc >= ARRAYLEN_PLOT)
976 plot[x] = bins_max;
978 if(bins_max > max)
979 max = bins_max;
981 if(++x >= bars)
982 break;
984 bins_acc -= ARRAYLEN_PLOT;
985 bins_max = 0;
989 for(i = 0, x = offset; i < bars; ++i, x += barwidth)
991 int h = LCD_HEIGHT * plot[i] / max;
992 mylcd_fillrect(x, LCD_HEIGHT - h, width, h - 1);
996 static void draw_bars_horizontal(void)
998 static int max = 0;
1000 #if LCD_WIDTH < LCD_HEIGHT
1001 const int bars = 20;
1002 #else
1003 const int bars = 15;
1004 #endif
1005 const int border = 2;
1006 const int barwidth = LCD_HEIGHT / (bars + border);
1007 const int height = barwidth - border;
1008 const int offset = (LCD_HEIGHT - bars*barwidth) / 2;
1010 if(graph_settings.changed.amp_scale)
1011 max = 0; /* reset the graph on scaling mode change */
1013 mylcd_vline(0, 0, LCD_HEIGHT-1); /* Draw baseline */
1015 if(calc_magnitudes(graph_settings.logarithmic_amp) == 0)
1016 return; /* nothing more to draw */
1018 if(graph_settings.logarithmic_freq)
1019 logarithmic_plot_translate();
1021 int bins_acc = bars / 2;
1022 int bins_max = 0;
1023 int y = 0, i = 0;
1025 for(;;)
1027 int bin = plot[i++];
1029 if (bin > bins_max)
1030 bins_max = bin;
1032 bins_acc += bars;
1034 if(bins_acc >= ARRAYLEN_PLOT)
1036 plot[y] = bins_max;
1038 if(bins_max > max)
1039 max = bins_max;
1041 if(++y >= bars)
1042 break;
1044 bins_acc -= ARRAYLEN_PLOT;
1045 bins_max = 0;
1049 for(i = 0, y = offset; i < bars; ++i, y += barwidth)
1051 int w = LCD_WIDTH * plot[i] / max;
1052 mylcd_fillrect(1, y, w, height);
1056 static void draw_spectrogram_vertical(void)
1058 const int32_t scale_factor = MIN(LCD_HEIGHT, ARRAYLEN_PLOT);
1060 calc_magnitudes(graph_settings.logarithmic_amp);
1062 if(graph_settings.logarithmic_freq)
1063 logarithmic_plot_translate();
1065 int bins_acc = scale_factor / 2;
1066 int bins_max = 0;
1067 int y = 0, i = 0;
1069 for(;;)
1071 int bin = plot[i++];
1073 if(bin > bins_max)
1074 bins_max = bin;
1076 bins_acc += scale_factor;
1078 if(bins_acc >= ARRAYLEN_PLOT)
1080 unsigned index;
1082 if(graph_settings.logarithmic_amp)
1083 index = (SHADES-1)*bins_max / QLOG_MAX;
1084 else
1085 index = (SHADES-1)*bins_max / QLIN_MAX;
1087 /* These happen because we exaggerate the graph a little for
1088 * linear mode */
1089 if(index >= SHADES)
1090 index = SHADES-1;
1092 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index));
1093 mylcd_drawpixel(graph_settings.spectrogram_pos,
1094 scale_factor-1 - y);
1096 if(++y >= scale_factor)
1097 break;
1099 bins_acc -= ARRAYLEN_PLOT;
1100 bins_max = 0;
1104 if(graph_settings.spectrogram_pos < LCD_WIDTH-1)
1105 graph_settings.spectrogram_pos++;
1106 else
1107 mylcd_scroll_left(1);
1110 static void draw_spectrogram_horizontal(void)
1112 const int32_t scale_factor = MIN(LCD_WIDTH, ARRAYLEN_PLOT);
1114 calc_magnitudes(graph_settings.logarithmic_amp);
1116 if(graph_settings.logarithmic_freq)
1117 logarithmic_plot_translate();
1119 int bins_acc = scale_factor / 2;
1120 int bins_max = 0;
1121 int x = 0, i = 0;
1123 for(;;)
1125 int bin = plot[i++];
1127 if(bin > bins_max)
1128 bins_max = bin;
1130 bins_acc += scale_factor;
1132 if(bins_acc >= ARRAYLEN_PLOT)
1134 unsigned index;
1136 if(graph_settings.logarithmic_amp)
1137 index = (SHADES-1)*bins_max / QLOG_MAX;
1138 else
1139 index = (SHADES-1)*bins_max / QLIN_MAX;
1141 /* These happen because we exaggerate the graph a little for
1142 * linear mode */
1143 if(index >= SHADES)
1144 index = SHADES-1;
1146 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index));
1147 mylcd_drawpixel(x, graph_settings.spectrogram_pos);
1149 if(++x >= scale_factor)
1150 break;
1152 bins_acc -= ARRAYLEN_PLOT;
1153 bins_max = 0;
1157 if(graph_settings.spectrogram_pos < LCD_HEIGHT-1)
1158 graph_settings.spectrogram_pos++;
1159 else
1160 mylcd_scroll_up(1);
1163 /********************* End of plotting functions (modes) *********************/
1165 /****************************** FFT functions ********************************/
1166 static bool is_playing(void)
1168 return rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_PLAYING;
1171 /** functions use in single/multi configuration **/
1172 static inline bool fft_init_fft_lib(void)
1174 size_t size = sizeof(fft_buffer);
1175 fft_state = kiss_fft_alloc(FFT_SIZE, 0, fft_buffer, &size);
1177 if(fft_state == NULL)
1179 DEBUGF("needed data: %i", (int) size);
1180 return false;
1183 return true;
1186 static inline bool fft_get_fft(void)
1188 int count;
1189 int16_t *value =
1190 (int16_t *) rb->mixer_channel_get_buffer(PCM_MIXER_CHAN_PLAYBACK, &count);
1191 /* This block can introduce discontinuities in our data. Meaning, the
1192 * FFT will not be done a continuous segment of the signal. Which can
1193 * be bad. Or not.
1195 * Anyway, this is a demo, not a scientific tool. If you want accuracy,
1196 * do a proper spectrum analysis.*/
1198 /* there are cases when we don't have enough data to fill the buffer */
1199 if(count != ARRAYLEN_IN)
1201 if(count < ARRAYLEN_IN)
1202 return false;
1204 count = ARRAYLEN_IN; /* too much - limit */
1207 int fft_idx = 0; /* offset in 'input' */
1211 kiss_fft_scalar left = *value++;
1212 kiss_fft_scalar right = *value++;
1213 input[fft_idx].r = (left + right) >> 1; /* to mono */
1214 } while (fft_idx++, --count > 0);
1216 apply_window_func(graph_settings.window_func);
1218 rb->yield();
1220 kiss_fft(fft_state, input, output[output_tail]);
1222 rb->yield();
1224 return true;
1227 #if NUM_CORES > 1
1228 /* use a worker thread if there is another processor core */
1229 static volatile bool fft_thread_run SHAREDDATA_ATTR = false;
1230 static unsigned long fft_thread;
1232 static long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))]
1233 CACHEALIGN_AT_LEAST_ATTR(4);
1235 static void fft_thread_entry(void)
1237 if (!fft_init_fft_lib())
1239 output_tail = -1; /* tell that we bailed */
1240 fft_thread_run = true;
1241 return;
1244 fft_thread_run = true;
1246 while(fft_thread_run)
1248 if (!is_playing())
1250 rb->sleep(HZ/5);
1251 continue;
1254 if (!fft_get_fft())
1256 rb->sleep(0); /* not enough - ease up */
1257 continue;
1260 /* write back output for other processor and invalidate for next frame read */
1261 rb->commit_discard_dcache();
1263 int new_tail = output_tail ^ 1;
1265 /* if full, block waiting until reader has freed a slot */
1266 while(fft_thread_run)
1268 if(new_tail != output_head)
1270 output_tail = new_tail;
1271 break;
1274 rb->sleep(0);
1279 static bool fft_have_fft(void)
1281 return output_head != output_tail;
1284 /* Call only after fft_have_fft() has returned true */
1285 static inline void fft_free_fft_output(void)
1287 output_head ^= 1; /* finished with this */
1290 static bool fft_init_fft(void)
1292 /* create worker thread - on the COP for dual-core targets */
1293 fft_thread = rb->create_thread(fft_thread_entry,
1294 fft_thread_stack, sizeof(fft_thread_stack), 0, "fft output thread"
1295 IF_PRIO(, PRIORITY_USER_INTERFACE+1) IF_COP(, COP));
1297 if(fft_thread == 0)
1299 rb->splash(HZ, "FFT thread failed create");
1300 return false;
1303 /* wait for it to indicate 'ready' */
1304 while(fft_thread_run == false)
1305 rb->sleep(0);
1307 if(output_tail == -1)
1309 /* FFT thread bailed-out like The Fed */
1310 rb->thread_wait(fft_thread);
1311 rb->splash(HZ, "FFT thread failed to init");
1312 return false;
1315 return true;
1318 static void fft_close_fft(void)
1320 /* Handle our FFT thread. */
1321 fft_thread_run = false;
1322 rb->thread_wait(fft_thread);
1323 rb->commit_discard_dcache();
1325 #else /* NUM_CORES == 1 */
1326 /* everything serialize on single-core and FFT gets to use IRAM main stack if
1327 * target uses IRAM */
1328 static bool fft_have_fft(void)
1330 return is_playing() && fft_get_fft();
1333 static inline void fft_free_fft_output(void)
1335 /* nothing to do */
1338 static bool fft_init_fft(void)
1340 return fft_init_fft_lib();
1343 static inline void fft_close_fft(void)
1345 /* nothing to do */
1347 #endif /* NUM_CORES */
1348 /*************************** End of FFT functions ****************************/
1350 enum plugin_status plugin_start(const void* parameter)
1352 /* Defaults */
1353 bool run = true;
1354 bool showing_warning = false;
1356 if (!fft_init_fft())
1357 return PLUGIN_ERROR;
1359 #ifndef HAVE_LCD_COLOR
1360 unsigned char *gbuf;
1361 size_t gbuf_size = 0;
1362 /* get the remainder of the plugin buffer */
1363 gbuf = (unsigned char *) rb->plugin_get_buffer(&gbuf_size);
1365 /* initialize the greyscale buffer.*/
1366 if (!grey_init(gbuf, gbuf_size, GREY_ON_COP | GREY_BUFFERED,
1367 LCD_WIDTH, LCD_HEIGHT, NULL))
1369 rb->splash(HZ, "Couldn't init greyscale display");
1370 fft_close_fft();
1371 return PLUGIN_ERROR;
1373 grey_show(true);
1374 #endif
1376 logarithmic_plot_init();
1378 #if LCD_DEPTH > 1
1379 rb->lcd_set_backdrop(NULL);
1380 mylcd_clear_display();
1381 mylcd_update();
1382 #endif
1383 backlight_ignore_timeout();
1385 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1386 rb->cpu_boost(true);
1387 #endif
1389 while (run)
1391 /* Unless otherwise specified, HZ/50 is around the window length
1392 * and quite fast. We want to be done with drawing by this time. */
1393 long next_frame_tick = *rb->current_tick + HZ/50;
1394 int button;
1396 while (!fft_have_fft())
1398 int timeout;
1400 if(!is_playing())
1402 showing_warning = true;
1403 mylcd_clear_display();
1404 draw_message_string("No audio playing", false);
1405 mylcd_update();
1406 timeout = HZ/5;
1408 else
1410 if(showing_warning)
1412 showing_warning = false;
1413 mylcd_clear_display();
1414 mylcd_update();
1417 timeout = HZ/100; /* 'till end of curent tick, don't use 100% CPU */
1420 /* Make sure the FFT has produced something before doing anything
1421 * but watching for buttons. Music might not be playing or things
1422 * just aren't going well for picking up buffers so keys are
1423 * scanned to avoid lockup. */
1424 button = rb->button_get_w_tmo(timeout);
1425 if (button != BUTTON_NONE)
1426 goto read_button;
1429 draw(NULL);
1431 fft_free_fft_output(); /* COP only */
1433 long tick = *rb->current_tick;
1434 if(TIME_BEFORE(tick, next_frame_tick))
1436 tick = next_frame_tick - tick;
1438 else
1440 rb->yield(); /* tmo = 0 won't yield */
1441 tick = 0;
1444 button = rb->button_get_w_tmo(tick);
1445 read_button:
1446 switch (button)
1448 case FFT_QUIT:
1449 run = false;
1450 break;
1451 case FFT_PREV_GRAPH: {
1452 if (graph_settings.mode-- <= FFT_DM_FIRST)
1453 graph_settings.mode = FFT_DM_COUNT-1;
1454 graph_settings.changed.mode = true;
1455 draw(modes_text[graph_settings.mode]);
1456 break;
1458 case FFT_NEXT_GRAPH: {
1459 if (++graph_settings.mode >= FFT_DM_COUNT)
1460 graph_settings.mode = FFT_DM_FIRST;
1461 graph_settings.changed.mode = true;
1462 draw(modes_text[graph_settings.mode]);
1463 break;
1465 case FFT_WINDOW: {
1466 if(++graph_settings.window_func >= FFT_WF_COUNT)
1467 graph_settings.window_func = FFT_WF_FIRST;
1468 graph_settings.changed.window_func = true;
1469 draw(window_text[graph_settings.window_func]);
1470 break;
1472 case FFT_AMP_SCALE: {
1473 graph_settings.logarithmic_amp = !graph_settings.logarithmic_amp;
1474 graph_settings.changed.amp_scale = true;
1475 draw(amp_scales_text[graph_settings.logarithmic_amp ? 1 : 0]);
1476 break;
1478 #ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
1479 case FFT_FREQ_SCALE: {
1480 graph_settings.logarithmic_freq = !graph_settings.logarithmic_freq;
1481 graph_settings.changed.freq_scale = true;
1482 draw(freq_scales_text[graph_settings.logarithmic_freq ? 1 : 0]);
1483 break;
1485 #endif
1486 case FFT_ORIENTATION: {
1487 graph_settings.orientation_vertical =
1488 !graph_settings.orientation_vertical;
1489 graph_settings.changed.orientation = true;
1490 draw(NULL);
1491 break;
1493 default: {
1494 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1495 return PLUGIN_USB_CONNECTED;
1501 fft_close_fft();
1503 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1504 rb->cpu_boost(false);
1505 #endif
1506 #ifndef HAVE_LCD_COLOR
1507 grey_release();
1508 #endif
1509 backlight_use_settings();
1510 return PLUGIN_OK;
1511 (void)parameter;