Fix FS#12824 : Malfunctioning FFT plugin in Sansa Clip Zip
[maemo-rb.git] / apps / plugins / fft / fft.c
blob2b72d63188bd84a04995074c551b00bc014c3dd0
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/pluginlib_exit.h"
25 #include "lib/configfile.h"
26 #include "lib/xlcd.h"
27 #include "math.h"
28 #include "fracmul.h"
29 #ifndef HAVE_LCD_COLOR
30 #include "lib/grey.h"
31 #endif
32 #include "lib/mylcd.h"
33 #include "lib/osd.h"
37 #ifndef HAVE_LCD_COLOR
38 GREY_INFO_STRUCT
39 #endif
41 #if CONFIG_KEYPAD == ARCHOS_AV300_PAD
42 # define FFT_PREV_GRAPH BUTTON_LEFT
43 # define FFT_NEXT_GRAPH BUTTON_RIGHT
44 # define FFT_ORIENTATION BUTTON_F3
45 # define FFT_WINDOW BUTTON_F1
46 # define FFT_AMP_SCALE BUTTON_UP
47 # define FFT_QUIT BUTTON_OFF
48 /* Need FFT_FREQ_SCALE key */
50 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
51 (CONFIG_KEYPAD == IRIVER_H300_PAD)
52 # define FFT_PREV_GRAPH BUTTON_LEFT
53 # define FFT_NEXT_GRAPH BUTTON_RIGHT
54 # define FFT_ORIENTATION BUTTON_REC
55 # define FFT_WINDOW BUTTON_SELECT
56 # define FFT_AMP_SCALE BUTTON_UP
57 # define FFT_FREQ_SCALE BUTTON_DOWN
58 # define FFT_QUIT BUTTON_OFF
60 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
61 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
62 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
63 # define MINESWP_SCROLLWHEEL
64 # define FFT_PREV_GRAPH BUTTON_LEFT
65 # define FFT_NEXT_GRAPH BUTTON_RIGHT
66 # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
67 # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
68 # define FFT_AMP_SCALE BUTTON_MENU
69 # define FFT_FREQ_SCALE BUTTON_PLAY
70 # define FFT_QUIT (BUTTON_SELECT | BUTTON_MENU)
72 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
73 # define FFT_PREV_GRAPH BUTTON_LEFT
74 # define FFT_NEXT_GRAPH BUTTON_RIGHT
75 # define FFT_ORIENTATION BUTTON_SELECT
76 # define FFT_WINDOW BUTTON_PLAY
77 # define FFT_AMP_SCALE BUTTON_UP
78 # define FFT_FREQ_SCALE BUTTON_DOWN
79 # define FFT_QUIT BUTTON_POWER
81 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
82 # define FFT_PREV_GRAPH BUTTON_LEFT
83 # define FFT_NEXT_GRAPH BUTTON_RIGHT
84 # define FFT_AMP_SCALE BUTTON_UP
85 # define FFT_FREQ_SCALE BUTTON_DOWN
86 # define FFT_ORIENTATION BUTTON_SELECT
87 # define FFT_WINDOW BUTTON_A
88 # define FFT_QUIT BUTTON_POWER
90 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
91 # define FFT_PREV_GRAPH BUTTON_LEFT
92 # define FFT_NEXT_GRAPH BUTTON_RIGHT
93 # define FFT_ORIENTATION BUTTON_SELECT
94 # define FFT_WINDOW BUTTON_REC
95 # define FFT_AMP_SCALE BUTTON_UP
96 # define FFT_FREQ_SCALE BUTTON_DOWN
97 # define FFT_QUIT BUTTON_POWER
99 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
100 # define FFT_PREV_GRAPH BUTTON_LEFT
101 # define FFT_NEXT_GRAPH BUTTON_RIGHT
102 # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
103 # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
104 # define FFT_AMP_SCALE BUTTON_UP
105 # define FFT_FREQ_SCALE BUTTON_DOWN
106 # define FFT_QUIT (BUTTON_HOME|BUTTON_REPEAT)
108 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
109 # define FFT_PREV_GRAPH BUTTON_LEFT
110 # define FFT_NEXT_GRAPH BUTTON_RIGHT
111 # define FFT_ORIENTATION BUTTON_UP
112 # define FFT_WINDOW BUTTON_REC
113 # define FFT_AMP_SCALE BUTTON_SELECT
114 # define FFT_QUIT BUTTON_POWER
115 /* Need FFT_FREQ_SCALE key */
116 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
117 # define FFT_PREV_GRAPH BUTTON_LEFT
118 # define FFT_NEXT_GRAPH BUTTON_RIGHT
119 # define FFT_ORIENTATION BUTTON_UP
120 # define FFT_WINDOW BUTTON_DOWN
121 # define FFT_AMP_SCALE BUTTON_SELECT
122 # define FFT_QUIT BUTTON_POWER
123 /* Need FFT_FREQ_SCALE key */
124 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
125 # define FFT_PREV_GRAPH BUTTON_LEFT
126 # define FFT_NEXT_GRAPH BUTTON_RIGHT
127 # define FFT_ORIENTATION BUTTON_UP
128 # define FFT_FREQ_SCALE BUTTON_DOWN
129 # define FFT_WINDOW BUTTON_HOME
130 # define FFT_AMP_SCALE BUTTON_SELECT
131 # define FFT_QUIT BUTTON_POWER
132 /* Need FFT_FREQ_SCALE key */
133 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
134 # define FFT_PREV_GRAPH BUTTON_LEFT
135 # define FFT_NEXT_GRAPH BUTTON_RIGHT
136 # define FFT_ORIENTATION BUTTON_FF
137 # define FFT_WINDOW BUTTON_SCROLL_UP
138 # define FFT_AMP_SCALE BUTTON_REW
139 # define FFT_FREQ_SCALE BUTTON_PLAY
140 # define FFT_QUIT BUTTON_POWER
142 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
143 # define FFT_PREV_GRAPH BUTTON_LEFT
144 # define FFT_NEXT_GRAPH BUTTON_RIGHT
145 # define FFT_ORIENTATION BUTTON_MENU
146 # define FFT_WINDOW BUTTON_PREV
147 # define FFT_AMP_SCALE BUTTON_UP
148 # define FFT_FREQ_SCALE BUTTON_DOWN
149 # define FFT_QUIT BUTTON_BACK
151 #elif (CONFIG_KEYPAD == MROBE100_PAD)
152 # define FFT_PREV_GRAPH BUTTON_LEFT
153 # define FFT_NEXT_GRAPH BUTTON_RIGHT
154 # define FFT_ORIENTATION BUTTON_PLAY
155 # define FFT_WINDOW BUTTON_SELECT
156 # define FFT_AMP_SCALE BUTTON_UP
157 # define FFT_FREQ_SCALE BUTTON_DOWN
158 # define FFT_QUIT BUTTON_POWER
160 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
161 # define FFT_PREV_GRAPH BUTTON_RC_REW
162 # define FFT_NEXT_GRAPH BUTTON_RC_FF
163 # define FFT_ORIENTATION BUTTON_RC_MODE
164 # define FFT_WINDOW BUTTON_RC_PLAY
165 # define FFT_AMP_SCALE BUTTON_RC_VOL_UP
166 # define FFT_QUIT BUTTON_RC_REC
167 /* Need FFT_FREQ_SCALE key */
168 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
169 # define FFT_QUIT BUTTON_POWER
170 # define FFT_PREV_GRAPH BUTTON_PLUS
171 # define FFT_NEXT_GRAPH BUTTON_MINUS
173 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
174 # define FFT_PREV_GRAPH BUTTON_LEFT
175 # define FFT_NEXT_GRAPH BUTTON_RIGHT
176 # define FFT_ORIENTATION BUTTON_MENU
177 # define FFT_WINDOW BUTTON_SELECT
178 # define FFT_AMP_SCALE BUTTON_UP
179 # define FFT_FREQ_SCALE BUTTON_DOWN
180 # define FFT_QUIT BUTTON_BACK
182 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
183 # define FFT_PREV_GRAPH BUTTON_LEFT
184 # define FFT_NEXT_GRAPH BUTTON_RIGHT
185 # define FFT_ORIENTATION BUTTON_SELECT
186 # define FFT_WINDOW BUTTON_MENU
187 # define FFT_AMP_SCALE BUTTON_UP
188 # define FFT_FREQ_SCALE BUTTON_DOWN
189 # define FFT_QUIT BUTTON_POWER
191 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
192 # define FFT_PREV_GRAPH BUTTON_PREV
193 # define FFT_NEXT_GRAPH BUTTON_NEXT
194 # define FFT_ORIENTATION BUTTON_PLAY
195 # define FFT_WINDOW BUTTON_MENU
196 # define FFT_AMP_SCALE BUTTON_UP
197 # define FFT_FREQ_SCALE BUTTON_DOWN
198 # define FFT_QUIT BUTTON_POWER
200 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
201 # define FFT_PREV_GRAPH BUTTON_PREV
202 # define FFT_NEXT_GRAPH BUTTON_NEXT
203 # define FFT_ORIENTATION BUTTON_PLAY
204 # define FFT_WINDOW BUTTON_MENU
205 # define FFT_AMP_SCALE BUTTON_UP
206 # define FFT_FREQ_SCALE BUTTON_DOWN
207 # define FFT_QUIT BUTTON_POWER
209 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
210 # define FFT_PREV_GRAPH BUTTON_LEFT
211 # define FFT_NEXT_GRAPH BUTTON_RIGHT
212 # define FFT_ORIENTATION BUTTON_UP
213 # define FFT_WINDOW BUTTON_DOWN
214 # define FFT_AMP_SCALE BUTTON_FFWD
215 # define FFT_QUIT BUTTON_PLAY
216 /* Need FFT_FREQ_SCALE key */
217 #elif (CONFIG_KEYPAD == MROBE500_PAD)
218 # define FFT_QUIT BUTTON_POWER
220 #elif (CONFIG_KEYPAD == ONDAVX747_PAD)
221 # define FFT_QUIT BUTTON_POWER
223 #elif (CONFIG_KEYPAD == ONDAVX777_PAD)
224 # define FFT_QUIT BUTTON_POWER
226 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
227 # define FFT_PREV_GRAPH BUTTON_PREV
228 # define FFT_NEXT_GRAPH BUTTON_NEXT
229 # define FFT_ORIENTATION BUTTON_MENU
230 # define FFT_WINDOW BUTTON_OK
231 # define FFT_AMP_SCALE BUTTON_PLAY
232 # define FFT_QUIT BUTTON_REC
233 /* Need FFT_FREQ_SCALE key */
234 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
235 # define FFT_PREV_GRAPH BUTTON_REW
236 # define FFT_NEXT_GRAPH BUTTON_FF
237 # define FFT_ORIENTATION BUTTON_REC
238 # define FFT_WINDOW BUTTON_FUNC
239 # define FFT_AMP_SCALE BUTTON_PLAY
240 # define FFT_QUIT (BUTTON_REC | BUTTON_PLAY)
241 /* Need FFT_FREQ_SCALE key */
242 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
243 # define FFT_PREV_GRAPH BUTTON_REW
244 # define FFT_NEXT_GRAPH BUTTON_FF
245 # define FFT_ORIENTATION BUTTON_REC
246 # define FFT_WINDOW BUTTON_ENTER
247 # define FFT_AMP_SCALE BUTTON_PLAY
248 # define FFT_QUIT (BUTTON_REC | BUTTON_REPEAT)
249 /* Need FFT_FREQ_SCALE key */
250 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
251 # define FFT_PREV_GRAPH BUTTON_LEFT
252 # define FFT_NEXT_GRAPH BUTTON_RIGHT
253 # define FFT_ORIENTATION BUTTON_PLAYPAUSE
254 # define FFT_WINDOW BUTTON_SELECT
255 # define FFT_AMP_SCALE BUTTON_BOTTOMLEFT
256 # define FFT_FREQ_SCALE BUTTON_BOTTOMRIGHT
257 # define FFT_QUIT BUTTON_POWER
259 #elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
260 # define FFT_PREV_GRAPH BUTTON_LEFT
261 # define FFT_NEXT_GRAPH BUTTON_RIGHT
262 # define FFT_ORIENTATION BUTTON_SELECT
263 # define FFT_WINDOW BUTTON_VOL_DOWN
264 # define FFT_AMP_SCALE BUTTON_UP
265 # define FFT_FREQ_SCALE BUTTON_DOWN
266 # define FFT_QUIT BUTTON_POWER
268 #elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
269 # define FFT_PREV_GRAPH BUTTON_LEFT
270 # define FFT_NEXT_GRAPH BUTTON_RIGHT
271 # define FFT_ORIENTATION BUTTON_USER
272 # define FFT_WINDOW BUTTON_MENU
273 # define FFT_AMP_SCALE BUTTON_SELECT
274 # define FFT_FREQ_SCALE BUTTON_DOWN
275 # define FFT_QUIT BUTTON_BACK
277 #elif (CONFIG_KEYPAD == HM60X_PAD)
278 # define FFT_PREV_GRAPH BUTTON_LEFT
279 # define FFT_NEXT_GRAPH BUTTON_RIGHT
280 # define FFT_AMP_SCALE BUTTON_UP
281 # define FFT_FREQ_SCALE BUTTON_DOWN
282 # define FFT_ORIENTATION BUTTON_SELECT
283 # define FFT_WINDOW (BUTTON_POWER|BUTTON_SELECT)
284 # define FFT_QUIT BUTTON_POWER
286 #elif (CONFIG_KEYPAD == HM801_PAD)
287 # define FFT_PREV_GRAPH BUTTON_LEFT
288 # define FFT_NEXT_GRAPH BUTTON_RIGHT
289 # define FFT_AMP_SCALE BUTTON_UP
290 # define FFT_FREQ_SCALE BUTTON_DOWN
291 # define FFT_ORIENTATION BUTTON_SELECT
292 # define FFT_WINDOW BUTTON_PLAY
293 # define FFT_QUIT BUTTON_POWER
295 #elif !defined(HAVE_TOUCHSCREEN)
296 #error No keymap defined!
297 #endif
299 #ifdef HAVE_TOUCHSCREEN
300 #ifndef FFT_PREV_GRAPH
301 # define FFT_PREV_GRAPH BUTTON_MIDLEFT
302 #endif
303 #ifndef FFT_NEXT_GRAPH
304 # define FFT_NEXT_GRAPH BUTTON_MIDRIGHT
305 #endif
306 #ifndef FFT_ORIENTATION
307 # define FFT_ORIENTATION BUTTON_CENTER
308 #endif
309 #ifndef FFT_WINDOW
310 # define FFT_WINDOW BUTTON_TOPLEFT
311 #endif
312 #ifndef FFT_AMP_SCALE
313 # define FFT_AMP_SCALE BUTTON_TOPRIGHT
314 #endif
315 #ifndef FFT_QUIT
316 # define FFT_QUIT BUTTON_BOTTOMLEFT
317 #endif
318 /* Need FFT_FREQ_SCALE key */
319 #endif /* HAVE_TOUCHSCREEN */
321 #ifdef HAVE_LCD_COLOR
322 #include "pluginbitmaps/fft_colors.h"
323 #endif
325 #include "kiss_fftr.h"
326 #include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */
327 #include "const.h"
330 /******************************* FFT globals *******************************/
332 #define LCD_SIZE MAX(LCD_WIDTH, LCD_HEIGHT)
334 #if (LCD_SIZE <= 511)
335 #define FFT_SIZE 1024 /* 512*2 */
336 #elif (LCD_SIZE <= 1023)
337 #define FFT_SIZE 2048 /* 1024*2 */
338 #else
339 #define FFT_SIZE 4096 /* 2048*2 */
340 #endif
342 #define ARRAYLEN_IN (FFT_SIZE)
343 #define ARRAYLEN_OUT (FFT_SIZE)
344 #define ARRAYLEN_PLOT (FFT_SIZE/2-1) /* FFT is symmetric, ignore DC */
345 #define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+\
346 sizeof(kiss_fft_cpx)*(FFT_SIZE-1))
348 #define __COEFF(type,size) type##_##size
349 #define _COEFF(x, y) __COEFF(x,y) /* force CPP evaluation of FFT_SIZE */
350 #define HANN_COEFF _COEFF(hann, FFT_SIZE)
351 #define HAMMING_COEFF _COEFF(hamming, FFT_SIZE)
353 /* cacheline-aligned buffers with COP, otherwise word-aligned */
354 /* CPU/COP only applies when compiled for more than one core */
356 #define CACHEALIGN_UP_SIZE(type, len) \
357 (CACHEALIGN_UP((len)*sizeof(type) + (sizeof(type)-1)) / sizeof(type))
358 /* Shared */
359 /* COP + CPU PCM */
360 static kiss_fft_cpx input[CACHEALIGN_UP_SIZE(kiss_fft_scalar, ARRAYLEN_IN)]
361 CACHEALIGN_AT_LEAST_ATTR(4);
362 /* CPU+COP */
363 #if NUM_CORES > 1
364 /* Output queue indexes */
365 static volatile int output_head SHAREDBSS_ATTR = 0;
366 static volatile int output_tail SHAREDBSS_ATTR = 0;
367 /* The result is nfft/2 complex frequency bins from DC to Nyquist. */
368 static kiss_fft_cpx output[2][CACHEALIGN_UP_SIZE(kiss_fft_cpx, ARRAYLEN_OUT)]
369 SHAREDBSS_ATTR;
370 #else
371 /* Only one output buffer */
372 #define output_head 0
373 #define output_tail 0
374 /* The result is nfft/2 complex frequency bins from DC to Nyquist. */
375 static kiss_fft_cpx output[1][ARRAYLEN_OUT];
376 #endif
378 /* Unshared */
379 /* COP */
380 static kiss_fft_cfg fft_state SHAREDBSS_ATTR;
381 static char fft_buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE_FFT)]
382 CACHEALIGN_AT_LEAST_ATTR(4);
383 /* CPU */
384 static uint32_t linf_magnitudes[ARRAYLEN_PLOT]; /* ling freq bin plot */
385 static uint32_t logf_magnitudes[ARRAYLEN_PLOT]; /* log freq plot output */
386 static uint32_t *plot; /* use this to plot */
387 static struct
389 int16_t bin; /* integer bin number */
390 uint16_t frac; /* interpolation fraction */
391 } binlog[ARRAYLEN_PLOT] __attribute__((aligned(4)));
393 /**************************** End of FFT globals ***************************/
396 /********************************* Settings ********************************/
398 enum fft_orientation
400 FFT_MIN_OR = 0,
401 FFT_OR_VERT = 0, /* Amplitude vertical, frequency horizontal * */
402 FFT_OR_HORZ, /* Amplitude horizontal, frequency vertical */
403 FFT_MAX_OR,
406 enum fft_display_mode
408 FFT_MIN_DM = 0,
409 FFT_DM_LINES = 0, /* Bands are displayed as single-pixel lines * */
410 FFT_DM_BARS, /* Bands are combined into wide bars */
411 FFT_DM_SPECTROGRAM, /* Band amplitudes are denoted by color */
412 FFT_MAX_DM,
415 enum fft_amp_scale
417 FFT_MIN_AS = 0,
418 FFT_AS_LOG = 0, /* Amplitude is plotted on log scale * */
419 FFT_AS_LIN, /* Amplitude is plotted on linear scale */
420 FFT_MAX_AS,
423 enum fft_freq_scale
425 FFT_MIN_FS = 0,
426 FFT_FS_LOG = 0, /* Frequency is plotted on log scale * */
427 FFT_FS_LIN, /* Frequency is plotted on linear scale */
428 FFT_MAX_FS
431 enum fft_window_func
433 FFT_MIN_WF = 0,
434 FFT_WF_HAMMING = 0, /* Hamming window applied to each input frame * */
435 FFT_WF_HANN, /* Hann window applied to each input frame */
436 FFT_MAX_WF,
439 static struct fft_config
441 int orientation;
442 int drawmode;
443 int amp_scale;
444 int freq_scale;
445 int window_func;
446 } fft_disk =
448 /* Defaults */
449 .orientation = FFT_OR_VERT,
450 .drawmode = FFT_DM_LINES,
451 .amp_scale = FFT_AS_LOG,
452 .freq_scale = FFT_FS_LOG,
453 .window_func = FFT_WF_HAMMING,
456 #define CFGFILE_VERSION 0
457 #define CFGFILE_MINVERSION 0
459 static const char cfg_filename[] = "fft.cfg";
460 static struct configdata disk_config[] =
462 { TYPE_ENUM, FFT_MIN_OR, FFT_MAX_OR,
463 { .int_p = &fft_disk.orientation }, "orientation",
464 (char * []){ [FFT_OR_VERT] = "vertical",
465 [FFT_OR_HORZ] = "horizontal" } },
466 { TYPE_ENUM, FFT_MIN_DM, FFT_MAX_DM,
467 { .int_p = &fft_disk.drawmode }, "drawmode",
468 (char * []){ [FFT_DM_LINES] = "lines",
469 [FFT_DM_BARS] = "bars",
470 [FFT_DM_SPECTROGRAM] = "spectrogram" } },
471 { TYPE_ENUM, FFT_MIN_AS, FFT_MAX_AS,
472 { .int_p = &fft_disk.amp_scale }, "amp scale",
473 (char * []){ [FFT_AS_LOG] = "logarithmic",
474 [FFT_AS_LIN] = "linear" } },
475 { TYPE_ENUM, FFT_MIN_FS, FFT_MAX_FS,
476 { .int_p = &fft_disk.freq_scale }, "freq scale",
477 (char * []){ [FFT_FS_LOG] = "logarithmic",
478 [FFT_FS_LIN] = "linear" } },
479 { TYPE_ENUM, FFT_MIN_WF, FFT_MAX_WF,
480 { .int_p = &fft_disk.window_func }, "window function",
481 (char * []){ [FFT_WF_HAMMING] = "hamming",
482 [FFT_WF_HANN] = "hann" } },
485 /* Hint flags for setting changes */
486 enum fft_setting_flags
488 FFT_SETF_OR = 1 << 0,
489 FFT_SETF_DM = 1 << 1,
490 FFT_SETF_AS = 1 << 2,
491 FFT_SETF_FS = 1 << 3,
492 FFT_SETF_WF = 1 << 4,
493 FFT_SETF_ALL = 0x1f
496 /***************************** End of settings *****************************/
499 /**************************** Operational data *****************************/
501 #define COLOR_DEFAULT_FG MYLCD_DEFAULT_FG
502 #define COLOR_DEFAULT_BG MYLCD_DEFAULT_BG
504 #ifdef HAVE_LCD_COLOR
505 #define COLOR_MESSAGE_FRAME LCD_RGBPACK(0xc6, 0x00, 0x00)
506 #define COLOR_MESSAGE_BG LCD_BLACK
507 #define COLOR_MESSAGE_FG LCD_WHITE
508 #else
509 #define COLOR_MESSAGE_FRAME GREY_DARKGRAY
510 #define COLOR_MESSAGE_BG GREY_WHITE
511 #define COLOR_MESSAGE_FG GREY_BLACK
512 #endif
514 #define FFT_OSD_MARGIN_SIZE 1
516 #define FFT_PERIOD (HZ/50) /* How fast to try to go */
518 /* Based on feeding-in a 0db sinewave at FS/4 */
519 #define QLOG_MAX 0x0009154B
520 /* Fudge it a little or it's not very visbile */
521 #define QLIN_MAX (0x00002266 >> 1)
523 static struct fft_config fft;
524 typedef void (* fft_drawfn_t)(unsigned, unsigned);
525 static fft_drawfn_t fft_drawfn = NULL; /* plotting function */
526 static int fft_spectrogram_pos = -1; /* row or column - only used by one at a time */
527 static uint32_t fft_graph_scale = 0; /* max level over time, for scaling display */
528 static int fft_message_id = -1; /* current message id displayed */
529 static char fft_osd_message[32]; /* current message string displayed */
530 static long fft_next_frame_tick = 0; /* next tick to attempt drawing */
532 #ifdef HAVE_LCD_COLOR
533 #define SHADES BMPWIDTH_fft_colors
534 #define SPECTROGRAPH_PALETTE(index) (fft_colors[index])
535 #else
536 #define SHADES 256
537 #define SPECTROGRAPH_PALETTE(index) (255 - (index))
538 #endif
540 /************************* End of operational data *************************/
543 /***************************** Math functions ******************************/
545 /* Apply window function to input */
546 static void apply_window_func(enum fft_window_func mode)
548 static const int16_t * const coefs[] =
550 [FFT_WF_HAMMING] = HAMMING_COEFF,
551 [FFT_WF_HANN] = HANN_COEFF,
554 const int16_t * const c = coefs[mode];
556 for(int i = 0; i < ARRAYLEN_IN; ++i)
557 input[i].r = (input[i].r * c[i] + 16384) >> 15;
560 /* Calculates the magnitudes from complex numbers and returns the maximum */
561 static unsigned calc_magnitudes(enum fft_amp_scale scale)
563 /* A major assumption made when calculating the Q*MAX constants
564 * is that the maximum magnitude is 29 bits long. */
565 unsigned this_max = 0;
566 kiss_fft_cpx *this_output = output[output_head] + 1; /* skip DC */
568 /* Calculate the magnitude, discarding the phase. */
569 for(int i = 0; i < ARRAYLEN_PLOT; ++i)
571 int32_t re = this_output[i].r;
572 int32_t im = this_output[i].i;
574 uint32_t d = re*re + im*im;
576 if(d > 0)
578 if(d > 0x7FFFFFFF) /* clip */
580 d = 0x7FFFFFFF; /* if our assumptions are correct,
581 this should never happen. It's just
582 a safeguard. */
585 if(scale == FFT_AS_LOG)
587 if(d < 0x8000) /* be more precise */
589 /* ln(x ^ .5) = .5*ln(x) */
590 d = fp16_log(d << 16) >> 1;
592 else
594 d = isqrt(d); /* linear scaling, nothing
595 bad should happen */
596 d = fp16_log(d << 16); /* the log function
597 expects s15.16 values */
600 else
602 d = isqrt(d); /* linear scaling, nothing
603 bad should happen */
607 /* Length 2 moving average - last transform and this one */
608 linf_magnitudes[i] = (linf_magnitudes[i] + d) >> 1;
610 if(d > this_max)
611 this_max = d;
614 return this_max;
617 /* Move plot bins into a logarithmic scale by sliding them towards the
618 * Nyquist bin according to the translation in the binlog array. */
619 static void log_plot_translate(void)
621 for(int i = ARRAYLEN_PLOT-1; i > 0; --i)
623 int s = binlog[i].bin;
624 int e = binlog[i-1].bin;
625 unsigned frac = binlog[i].frac;
627 int bin = linf_magnitudes[s];
629 if(frac)
631 /* slope < 1, Interpolate stretched bins (linear for now) */
632 int diff = linf_magnitudes[s+1] - bin;
636 logf_magnitudes[i] = bin + FRACMUL(frac << 15, diff);
637 frac = binlog[--i].frac;
639 while(frac);
641 else
643 /* slope > 1, Find peak of two or more bins */
644 while(--s > e)
646 int val = linf_magnitudes[s];
648 if (val > bin)
649 bin = val;
653 logf_magnitudes[i] = bin;
657 /* Calculates the translation for logarithmic plot bins */
658 static void logarithmic_plot_init(void)
661 * log: y = round(n * ln(x) / ln(n))
662 * anti: y = round(exp(x * ln(n) / n))
664 int j = fp16_log((ARRAYLEN_PLOT - 1) << 16);
665 for(int i = 0; i < ARRAYLEN_PLOT; ++i)
667 binlog[i].bin = (fp16_exp(i * j / (ARRAYLEN_PLOT - 1)) + 32768) >> 16;
670 /* setup fractions for interpolation of stretched bins */
671 for(int i = 0; i < ARRAYLEN_PLOT-1; i = j)
673 j = i + 1;
675 /* stop when we have two different values */
676 while(binlog[j].bin == binlog[i].bin)
677 j++; /* if here, local slope of curve is < 1 */
679 if(j > i + 1)
681 /* distribute pieces evenly over stretched interval */
682 int diff = j - i;
683 int x = 0;
686 binlog[i].frac = (x++ << 16) / diff;
688 while(++i < j);
693 /************************** End of math functions **************************/
696 /*********************** Plotting functions (modes) ************************/
698 static void draw_lines_vertical(unsigned this_max, unsigned graph_max)
700 #if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
701 const int offset = 0;
702 const int plotwidth = LCD_WIDTH;
703 #else
704 const int offset = (LCD_HEIGHT - ARRAYLEN_PLOT) / 2;
705 const int plotwidth = ARRAYLEN_PLOT;
706 #endif
708 mylcd_clear_display();
710 if(this_max == 0)
712 mylcd_hline(0, LCD_WIDTH - 1, LCD_HEIGHT - 1); /* Draw all "zero" */
713 return;
716 /* take the maximum of neighboring bins if we have to scale down the
717 * graph horizontally */
718 if(LCD_WIDTH < ARRAYLEN_PLOT) /* graph compression */
720 int bins_acc = LCD_WIDTH / 2;
721 unsigned bins_max = 0;
723 for(int i = 0, x = 0; i < ARRAYLEN_PLOT; ++i)
725 unsigned bin = plot[i];
727 if(bin > bins_max)
728 bins_max = bin;
730 bins_acc += LCD_WIDTH;
732 if(bins_acc >= ARRAYLEN_PLOT)
734 int h = LCD_HEIGHT*bins_max / graph_max;
735 mylcd_vline(x, LCD_HEIGHT - h, LCD_HEIGHT-1);
737 x++;
738 bins_acc -= ARRAYLEN_PLOT;
739 bins_max = 0;
743 else
745 for(int i = 0; i < plotwidth; ++i)
747 int h = LCD_HEIGHT*plot[i] / graph_max;
748 mylcd_vline(i + offset, LCD_HEIGHT - h, LCD_HEIGHT-1);
753 static void draw_lines_horizontal(unsigned this_max, unsigned graph_max)
755 #if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
756 const int offset = 0;
757 const int plotwidth = LCD_HEIGHT;
758 #else
759 const int offset = (LCD_HEIGHT - ARRAYLEN_PLOT) / 2;
760 const int plotwidth = ARRAYLEN_PLOT;
761 #endif
763 mylcd_clear_display();
765 if(this_max == 0)
767 mylcd_vline(0, 0, LCD_HEIGHT-1); /* Draw all "zero" */
768 return;
771 /* take the maximum of neighboring bins if we have to scale the graph
772 * horizontally */
773 if(LCD_HEIGHT < ARRAYLEN_PLOT) /* graph compression */
775 int bins_acc = LCD_HEIGHT / 2;
776 unsigned bins_max = 0;
778 for(int i = 0, y = 0; i < ARRAYLEN_PLOT; ++i)
780 unsigned bin = plot[i];
782 if(bin > bins_max)
783 bins_max = bin;
785 bins_acc += LCD_HEIGHT;
787 if(bins_acc >= ARRAYLEN_PLOT)
789 int w = LCD_WIDTH*bins_max / graph_max;
790 mylcd_hline(0, w - 1, y);
792 y++;
793 bins_acc -= ARRAYLEN_PLOT;
794 bins_max = 0;
798 else
800 for(int i = 0; i < plotwidth; ++i)
802 int w = LCD_WIDTH*plot[i] / graph_max;
803 mylcd_hline(0, w - 1, i + offset);
808 static void draw_bars_vertical(unsigned this_max, unsigned graph_max)
810 #if LCD_WIDTH < LCD_HEIGHT
811 const int bars = 15;
812 #else
813 const int bars = 20;
814 #endif
815 const int border = 2;
816 const int barwidth = LCD_WIDTH / (bars + border);
817 const int width = barwidth - border;
818 const int offset = (LCD_WIDTH - bars*barwidth + border) / 2;
820 mylcd_clear_display();
821 mylcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-1); /* Draw baseline */
823 if(this_max == 0)
824 return; /* nothing more to draw */
826 int bins_acc = bars / 2;
827 unsigned bins_max = 0;
829 for(int i = 0, x = offset;; ++i)
831 unsigned bin = plot[i];
833 if(bin > bins_max)
834 bins_max = bin;
836 bins_acc += bars;
838 if(bins_acc >= ARRAYLEN_PLOT)
840 int h = LCD_HEIGHT*bins_max / graph_max;
841 mylcd_fillrect(x, LCD_HEIGHT - h, width, h - 1);
843 if(i >= ARRAYLEN_PLOT-1)
844 break;
846 x += barwidth;
847 bins_acc -= ARRAYLEN_PLOT;
848 bins_max = 0;
853 static void draw_bars_horizontal(unsigned this_max, unsigned graph_max)
855 #if LCD_WIDTH < LCD_HEIGHT
856 const int bars = 20;
857 #else
858 const int bars = 15;
859 #endif
860 const int border = 2;
861 const int barwidth = LCD_HEIGHT / (bars + border);
862 const int height = barwidth - border;
863 const int offset = (LCD_HEIGHT - bars*barwidth + border) / 2;
865 mylcd_clear_display();
866 mylcd_vline(0, 0, LCD_HEIGHT-1); /* Draw baseline */
868 if(this_max == 0)
869 return; /* nothing more to draw */
871 int bins_acc = bars / 2;
872 unsigned bins_max = 0;
874 for(int i = 0, y = offset;; ++i)
876 unsigned bin = plot[i];
878 if(bin > bins_max)
879 bins_max = bin;
881 bins_acc += bars;
883 if(bins_acc >= ARRAYLEN_PLOT)
885 int w = LCD_WIDTH*bins_max / graph_max;
886 mylcd_fillrect(1, y, w, height);
888 if(i >= ARRAYLEN_PLOT-1)
889 break;
891 y += barwidth;
892 bins_acc -= ARRAYLEN_PLOT;
893 bins_max = 0;
898 static void draw_spectrogram_vertical(unsigned this_max, unsigned graph_max)
900 const int scale_factor = MIN(LCD_HEIGHT, ARRAYLEN_PLOT);
902 if(fft_spectrogram_pos < LCD_WIDTH-1)
903 fft_spectrogram_pos++;
904 else
905 mylcd_scroll_left(1);
907 int bins_acc = scale_factor / 2;
908 unsigned bins_max = 0;
910 for(int i = 0, y = LCD_HEIGHT-1;; ++i)
912 unsigned bin = plot[i];
914 if(bin > bins_max)
915 bins_max = bin;
917 bins_acc += scale_factor;
919 if(bins_acc >= ARRAYLEN_PLOT)
921 unsigned index = (SHADES-1)*bins_max / graph_max;
923 /* These happen because we exaggerate the graph a little for
924 * linear mode */
925 if(index >= SHADES)
926 index = SHADES-1;
928 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index));
929 mylcd_drawpixel(fft_spectrogram_pos, y);
931 if(--y < 0)
932 break;
934 bins_acc -= ARRAYLEN_PLOT;
935 bins_max = 0;
939 (void)this_max;
942 static void draw_spectrogram_horizontal(unsigned this_max, unsigned graph_max)
944 const int scale_factor = MIN(LCD_WIDTH, ARRAYLEN_PLOT);
946 if(fft_spectrogram_pos < LCD_HEIGHT-1)
947 fft_spectrogram_pos++;
948 else
949 mylcd_scroll_up(1);
951 int bins_acc = scale_factor / 2;
952 unsigned bins_max = 0;
954 for(int i = 0, x = 0;; ++i)
956 unsigned bin = plot[i];
958 if(bin > bins_max)
959 bins_max = bin;
961 bins_acc += scale_factor;
963 if(bins_acc >= ARRAYLEN_PLOT)
965 unsigned index = (SHADES-1)*bins_max / graph_max;
967 /* These happen because we exaggerate the graph a little for
968 * linear mode */
969 if(index >= SHADES)
970 index = SHADES-1;
972 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index));
973 mylcd_drawpixel(x, fft_spectrogram_pos);
975 if(++x >= LCD_WIDTH)
976 break;
978 bins_acc -= ARRAYLEN_PLOT;
979 bins_max = 0;
983 (void)this_max;
986 /******************** End of plotting functions (modes) ********************/
989 /***************************** FFT functions *******************************/
991 static bool is_playing(void)
993 return rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_PLAYING;
996 /** functions use in single/multi configuration **/
997 static inline bool fft_init_fft_lib(void)
999 size_t size = sizeof(fft_buffer);
1000 fft_state = kiss_fft_alloc(FFT_SIZE, 0, fft_buffer, &size);
1002 if(fft_state == NULL)
1004 DEBUGF("needed data: %i", (int) size);
1005 return false;
1008 return true;
1011 static inline bool fft_get_fft(void)
1013 int count;
1014 const int16_t *value =
1015 rb->mixer_channel_get_buffer(PCM_MIXER_CHAN_PLAYBACK, &count);
1016 /* This block can introduce discontinuities in our data. Meaning, the
1017 * FFT will not be done a continuous segment of the signal. Which can
1018 * be bad. Or not.
1020 * Anyway, this is a demo, not a scientific tool. If you want accuracy,
1021 * do a proper spectrum analysis.*/
1023 /* there are cases when we don't have enough data to fill the buffer */
1024 if(count != ARRAYLEN_IN)
1026 if(count < ARRAYLEN_IN)
1027 return false;
1029 count = ARRAYLEN_IN; /* too much - limit */
1032 int fft_idx = 0; /* offset in 'input' */
1036 kiss_fft_scalar left = *value++;
1037 kiss_fft_scalar right = *value++;
1038 input[fft_idx].r = (left + right) >> 1; /* to mono */
1039 } while (fft_idx++, --count > 0);
1041 apply_window_func(fft.window_func);
1043 rb->yield();
1045 kiss_fft(fft_state, input, output[output_tail]);
1047 rb->yield();
1049 return true;
1052 #if NUM_CORES > 1
1053 /* use a worker thread if there is another processor core */
1054 static volatile bool fft_thread_run SHAREDDATA_ATTR = false;
1055 static unsigned long fft_thread = 0;
1057 static long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))]
1058 CACHEALIGN_AT_LEAST_ATTR(4);
1060 static void fft_thread_entry(void)
1062 if(!fft_init_fft_lib())
1064 output_tail = -1; /* tell that we bailed */
1065 fft_thread_run = true;
1066 return;
1069 fft_thread_run = true;
1071 while(fft_thread_run)
1073 if (!is_playing())
1075 rb->sleep(HZ/5);
1076 continue;
1079 if (!fft_get_fft())
1081 rb->sleep(0); /* not enough - ease up */
1082 continue;
1085 /* write back output for other processor and invalidate for next
1086 frame read */
1087 rb->commit_discard_dcache();
1089 int new_tail = output_tail ^ 1;
1091 /* if full, block waiting until reader has freed a slot */
1092 while(fft_thread_run)
1094 if(new_tail != output_head)
1096 output_tail = new_tail;
1097 break;
1100 rb->sleep(0);
1105 static bool fft_have_fft(void)
1107 return output_head != output_tail;
1110 /* Call only after fft_have_fft() has returned true */
1111 static inline void fft_free_fft_output(void)
1113 output_head ^= 1; /* finished with this */
1116 static bool fft_init_fft(void)
1118 /* create worker thread - on the COP for dual-core targets */
1119 fft_thread = rb->create_thread(fft_thread_entry,
1120 fft_thread_stack, sizeof(fft_thread_stack), 0, "fft output thread"
1121 IF_PRIO(, PRIORITY_USER_INTERFACE+1) IF_COP(, COP));
1123 if(fft_thread == 0)
1125 rb->splash(HZ, "FFT thread failed create");
1126 return false;
1129 /* wait for it to indicate 'ready' */
1130 while(fft_thread_run == false)
1131 rb->sleep(0);
1133 if(output_tail == -1)
1135 /* FFT thread bailed-out like The Fed */
1136 rb->thread_wait(fft_thread);
1137 rb->splash(HZ, "FFT thread failed to init");
1138 return false;
1141 return true;
1144 static void fft_close_fft(void)
1146 /* Handle our FFT thread. */
1147 fft_thread_run = false;
1148 rb->thread_wait(fft_thread);
1149 rb->commit_discard_dcache();
1151 #else /* NUM_CORES == 1 */
1152 /* everything serialize on single-core and FFT gets to use IRAM main stack if
1153 * target uses IRAM */
1154 static bool fft_have_fft(void)
1156 return is_playing() && fft_get_fft();
1159 static inline void fft_free_fft_output(void)
1161 /* nothing to do */
1164 static bool fft_init_fft(void)
1166 return fft_init_fft_lib();
1169 static inline void fft_close_fft(void)
1171 /* nothing to do */
1173 #endif /* NUM_CORES */
1175 /************************** End of FFT functions ***************************/
1178 /****************************** OSD functions ******************************/
1180 /* Format a message to display */
1181 static void fft_osd_format_message(enum fft_setting_flags id)
1183 const char *msg = "";
1185 switch (id)
1187 case FFT_SETF_DM:
1188 msg = (const char * [FFT_MAX_DM]) {
1189 [FFT_DM_LINES] = "Lines",
1190 [FFT_DM_BARS] = "Bars",
1191 [FFT_DM_SPECTROGRAM] = "Spectrogram",
1192 }[fft.drawmode];
1193 break;
1195 case FFT_SETF_WF:
1196 msg = (const char * [FFT_MAX_WF]) {
1197 [FFT_WF_HAMMING] = "Hamming window",
1198 [FFT_WF_HANN] = "Hann window",
1199 }[fft.window_func];
1200 break;
1202 case FFT_SETF_AS:
1203 msg = (const char * [FFT_MAX_AS]) {
1204 [FFT_AS_LOG] = "Logarithmic amplitude",
1205 [FFT_AS_LIN] = "Linear amplitude"
1206 }[fft.amp_scale];
1207 break;
1209 case FFT_SETF_FS:
1210 msg = (const char * [FFT_MAX_FS]) {
1211 [FFT_FS_LOG] = "Logarithmic frequency",
1212 [FFT_FS_LIN] = "Linear frequency",
1213 }[fft.freq_scale];
1214 break;
1216 case FFT_SETF_OR:
1217 rb->snprintf(fft_osd_message, sizeof (fft_osd_message),
1218 (const char * [FFT_MAX_OR]) {
1219 [FFT_OR_VERT] = "Vertical %s",
1220 [FFT_OR_HORZ] = "Horizontal %s",
1221 }[fft.orientation],
1222 (const char * [FFT_MAX_DM]) {
1223 [FFT_DM_LINES ... FFT_DM_BARS] = "amplitude",
1224 [FFT_DM_SPECTROGRAM] = "frequency"
1225 }[fft.drawmode]);
1226 return;
1228 #if 0
1229 /* Pertentially */
1230 case FFT_SETF_VOLUME:
1231 rb->snprintf(fft_osd_message, sizeof (fft_osd_message),
1232 "Volume: %d%s",
1233 rb->sound_val2phys(SOUND_VOLUME, global_settings.volume),
1234 rb->sound_unit(SOUND_VOLUME));
1235 return;
1236 #endif
1238 default:
1239 break;
1242 /* Default action: copy string */
1243 rb->strlcpy(fft_osd_message, msg, sizeof (fft_osd_message));
1246 static void fft_osd_draw_cb(int x, int y, int width, int height)
1248 #if LCD_DEPTH > 1
1249 mylcd_set_foreground(COLOR_MESSAGE_FG);
1250 mylcd_set_background(COLOR_MESSAGE_BG);
1251 #endif
1252 #if FFT_OSD_MARGIN_SIZE != 0
1253 mylcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1254 mylcd_fillrect(1, 1, width - 2, height - 2);
1255 mylcd_set_drawmode(DRMODE_SOLID);
1256 #endif
1257 mylcd_putsxy(1+FFT_OSD_MARGIN_SIZE, 1+FFT_OSD_MARGIN_SIZE,
1258 fft_osd_message);
1259 #if LCD_DEPTH > 1
1260 mylcd_set_foreground(COLOR_MESSAGE_FRAME);
1261 #endif
1263 mylcd_drawrect(0, 0, width, height);
1265 (void)x; (void)y;
1268 static void fft_osd_show_message(enum fft_setting_flags id)
1270 fft_osd_format_message(id);
1272 if(!myosd_enabled())
1273 return;
1275 int width, height;
1276 int maxwidth, maxheight;
1278 mylcd_set_viewport(myosd_get_viewport());
1279 myosd_get_max_dims(&maxwidth, &maxheight);
1280 mylcd_setfont(FONT_UI);
1281 mylcd_getstringsize(fft_osd_message, &width, &height);
1282 mylcd_set_viewport(NULL);
1284 width += 2 + 2*FFT_OSD_MARGIN_SIZE;
1285 if(width > maxwidth)
1286 width = maxwidth;
1288 height += 2 + 2*FFT_OSD_MARGIN_SIZE;
1289 if(height > maxheight)
1290 height = maxheight;
1292 bool drawn = myosd_update_pos((LCD_WIDTH - width) / 2,
1293 (LCD_HEIGHT - height) / 2,
1294 width, height);
1296 myosd_show(OSD_SHOW | (drawn ? 0 : OSD_UPDATENOW));
1299 static void fft_popupmsg(enum fft_setting_flags id)
1301 fft_message_id = id;
1304 /************************** End of OSD functions ***************************/
1307 static void fft_setting_update(unsigned which)
1309 static fft_drawfn_t fft_drawfns[FFT_MAX_DM][FFT_MAX_OR] =
1311 [FFT_DM_LINES] =
1313 [FFT_OR_HORZ] = draw_lines_horizontal,
1314 [FFT_OR_VERT] = draw_lines_vertical,
1316 [FFT_DM_BARS] =
1318 [FFT_OR_HORZ] = draw_bars_horizontal,
1319 [FFT_OR_VERT] = draw_bars_vertical,
1321 [FFT_DM_SPECTROGRAM] =
1323 [FFT_OR_HORZ] = draw_spectrogram_horizontal,
1324 [FFT_OR_VERT] = draw_spectrogram_vertical,
1328 if(which & (FFT_SETF_DM | FFT_SETF_OR))
1330 fft_drawfn = fft_drawfns[fft.drawmode]
1331 [fft.orientation];
1333 if(fft.drawmode == FFT_DM_SPECTROGRAM)
1335 fft_spectrogram_pos = -1;
1336 myosd_lcd_update_prepare();
1337 mylcd_clear_display();
1338 myosd_lcd_update();
1342 if(which & (FFT_SETF_DM | FFT_SETF_AS))
1344 if(fft.drawmode == FFT_DM_SPECTROGRAM)
1346 fft_graph_scale = fft.amp_scale == FFT_AS_LIN ?
1347 QLIN_MAX : QLOG_MAX;
1349 else
1351 fft_graph_scale = 0;
1355 if(which & FFT_SETF_FS)
1357 plot = fft.freq_scale == FFT_FS_LIN ?
1358 linf_magnitudes : logf_magnitudes;
1361 if(which & FFT_SETF_AS)
1363 memset(linf_magnitudes, 0, sizeof (linf_magnitudes));
1364 memset(logf_magnitudes, 0, sizeof (logf_magnitudes));
1368 static long fft_draw(void)
1370 long tick = *rb->current_tick;
1372 if(fft_message_id != -1)
1374 /* Show a new message */
1375 fft_osd_show_message((enum fft_setting_flags)fft_message_id);
1376 fft_message_id = -1;
1378 else
1380 /* Monitor OSD timeout */
1381 myosd_monitor_timeout();
1384 if(TIME_BEFORE(tick, fft_next_frame_tick))
1385 return fft_next_frame_tick - tick; /* Too early */
1387 unsigned this_max;
1389 if(!fft_have_fft())
1391 if(is_playing())
1392 return HZ/100;
1394 /* All magnitudes == 0 thus this_max == 0 */
1395 for(int i = 0; i < ARRAYLEN_PLOT; i++)
1396 linf_magnitudes[i] >>= 1; /* decay */
1398 this_max = 0;
1400 else
1402 this_max = calc_magnitudes(fft.amp_scale);
1404 fft_free_fft_output(); /* COP only */
1406 if(fft.drawmode != FFT_DM_SPECTROGRAM &&
1407 this_max > fft_graph_scale)
1409 fft_graph_scale = this_max;
1413 if (fft.freq_scale == FFT_FS_LOG)
1414 log_plot_translate();
1416 myosd_lcd_update_prepare();
1418 mylcd_set_foreground(COLOR_DEFAULT_FG);
1419 mylcd_set_background(COLOR_DEFAULT_BG);
1421 fft_drawfn(this_max, fft_graph_scale);
1423 myosd_lcd_update();
1425 fft_next_frame_tick = tick + FFT_PERIOD;
1426 return fft_next_frame_tick - *rb->current_tick;
1429 static void fft_osd_init(void *buf, size_t bufsize)
1431 int width, height;
1432 mylcd_setfont(FONT_UI);
1433 mylcd_getstringsize("M", NULL, &height);
1434 width = LCD_WIDTH;
1435 height += 2 + 2*FFT_OSD_MARGIN_SIZE;
1436 myosd_init(OSD_INIT_MAJOR_HEIGHT | OSD_INIT_MINOR_MAX, buf, bufsize,
1437 fft_osd_draw_cb, &width, &height, NULL);
1438 myosd_set_timeout(HZ);
1441 static void fft_cleanup(void)
1443 myosd_destroy();
1445 fft_close_fft();
1447 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1448 rb->cancel_cpu_boost();
1449 #endif
1450 #ifndef HAVE_LCD_COLOR
1451 grey_release();
1452 #endif
1453 backlight_use_settings();
1455 /* save settings if changed */
1456 if (rb->memcmp(&fft, &fft_disk, sizeof(fft)))
1458 fft_disk = fft;
1459 configfile_save(cfg_filename, disk_config, ARRAYLEN(disk_config),
1460 CFGFILE_VERSION);
1464 static bool fft_setup(void)
1466 atexit(fft_cleanup);
1468 configfile_load(cfg_filename, disk_config, ARRAYLEN(disk_config),
1469 CFGFILE_MINVERSION);
1470 fft = fft_disk; /* copy to running config */
1472 if(!fft_init_fft())
1473 return false;
1475 /* get the remainder of the plugin buffer for OSD and perhaps
1476 greylib */
1477 size_t bufsize = 0;
1478 unsigned char *buf = rb->plugin_get_buffer(&bufsize);
1480 #ifndef HAVE_LCD_COLOR
1481 /* initialize the greyscale buffer.*/
1482 long grey_size;
1483 if(!grey_init(buf, bufsize, GREY_ON_COP | GREY_BUFFERED,
1484 LCD_WIDTH, LCD_HEIGHT, &grey_size))
1486 rb->splash(HZ, "Couldn't init greyscale display");
1487 return false;
1490 grey_show(true);
1492 buf += grey_size;
1493 bufsize -= grey_size;
1494 #endif /* !HAVE_LCD_COLOR */
1496 fft_osd_init(buf, bufsize);
1498 #if LCD_DEPTH > 1
1499 myosd_lcd_update_prepare();
1500 rb->lcd_set_backdrop(NULL);
1501 mylcd_clear_display();
1502 myosd_lcd_update();
1503 #endif
1504 backlight_ignore_timeout();
1506 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1507 rb->trigger_cpu_boost();
1508 #endif
1510 logarithmic_plot_init();
1511 fft_setting_update(FFT_SETF_ALL);
1512 fft_next_frame_tick = *rb->current_tick;
1514 return true;
1517 enum plugin_status plugin_start(const void* parameter)
1519 bool run = true;
1521 if(!fft_setup())
1522 return PLUGIN_ERROR;
1524 while(run)
1526 long delay = fft_draw();
1528 if(delay <= 0)
1530 delay = 0;
1531 rb->yield(); /* tmo = 0 won't yield */
1534 int button = rb->button_get_w_tmo(delay);
1536 switch (button)
1538 case FFT_QUIT:
1539 run = false;
1540 break;
1542 case FFT_ORIENTATION:
1543 if (++fft.orientation >= FFT_MAX_OR)
1544 fft.orientation = FFT_MIN_OR;
1546 fft_setting_update(FFT_SETF_OR);
1547 fft_popupmsg(FFT_SETF_OR);
1548 break;
1550 case FFT_PREV_GRAPH:
1551 if (fft.drawmode-- <= FFT_MIN_DM)
1552 fft.drawmode = FFT_MAX_DM-1;
1554 fft_setting_update(FFT_SETF_DM);
1555 fft_popupmsg(FFT_SETF_DM);
1556 break;
1558 case FFT_NEXT_GRAPH:
1559 if (++fft.drawmode >= FFT_MAX_DM)
1560 fft.drawmode = FFT_MIN_DM;
1562 fft_setting_update(FFT_SETF_DM);
1563 fft_popupmsg(FFT_SETF_DM);
1564 break;
1566 case FFT_AMP_SCALE:
1567 if (++fft.amp_scale >= FFT_MAX_AS)
1568 fft.amp_scale = FFT_MIN_AS;
1570 fft_setting_update(FFT_SETF_AS);
1571 fft_popupmsg(FFT_SETF_AS);
1572 break;
1574 #ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
1575 case FFT_FREQ_SCALE:
1576 if (++fft.freq_scale >= FFT_MAX_FS)
1577 fft.freq_scale = FFT_MIN_FS;
1579 fft_setting_update(FFT_SETF_FS);
1580 fft_popupmsg(FFT_SETF_FS);
1581 break;
1582 #endif
1583 case FFT_WINDOW:
1584 if(++fft.window_func >= FFT_MAX_WF)
1585 fft.window_func = FFT_MIN_WF;
1587 fft_setting_update(FFT_SETF_WF);
1588 fft_popupmsg(FFT_SETF_WF);
1589 break;
1591 default:
1592 exit_on_usb(button);
1593 break;
1597 return PLUGIN_OK;
1598 (void)parameter;