709fbf9f4649c684a03702ab8c01eddaee12e449
[maemo-rb.git] / apps / plugins / fft / fft.c
blob709fbf9f4649c684a03702ab8c01eddaee12e449
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
49 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
50 (CONFIG_KEYPAD == IRIVER_H300_PAD)
51 # define FFT_PREV_GRAPH BUTTON_LEFT
52 # define FFT_NEXT_GRAPH BUTTON_RIGHT
53 # define FFT_ORIENTATION BUTTON_REC
54 # define FFT_WINDOW BUTTON_SELECT
55 # define FFT_AMP_SCALE BUTTON_UP
56 # define FFT_FREQ_SCALE BUTTON_DOWN
57 # define FFT_QUIT BUTTON_OFF
59 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
60 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
61 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
62 # define MINESWP_SCROLLWHEEL
63 # define FFT_PREV_GRAPH BUTTON_LEFT
64 # define FFT_NEXT_GRAPH BUTTON_RIGHT
65 # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
66 # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
67 # define FFT_AMP_SCALE BUTTON_MENU
68 # define FFT_FREQ_SCALE BUTTON_PLAY
69 # define FFT_QUIT (BUTTON_SELECT | BUTTON_MENU)
71 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
72 # define FFT_PREV_GRAPH BUTTON_LEFT
73 # define FFT_NEXT_GRAPH BUTTON_RIGHT
74 # define FFT_ORIENTATION BUTTON_SELECT
75 # define FFT_WINDOW BUTTON_PLAY
76 # define FFT_AMP_SCALE BUTTON_UP
77 # define FFT_FREQ_SCALE BUTTON_DOWN
78 # define FFT_QUIT BUTTON_POWER
80 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
81 # define FFT_PREV_GRAPH BUTTON_LEFT
82 # define FFT_NEXT_GRAPH BUTTON_RIGHT
83 # define FFT_AMP_SCALE BUTTON_UP
84 # define FFT_FREQ_SCALE BUTTON_DOWN
85 # define FFT_ORIENTATION BUTTON_SELECT
86 # define FFT_WINDOW BUTTON_A
87 # define FFT_QUIT BUTTON_POWER
89 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
90 # define FFT_PREV_GRAPH BUTTON_LEFT
91 # define FFT_NEXT_GRAPH BUTTON_RIGHT
92 # define FFT_ORIENTATION BUTTON_SELECT
93 # define FFT_WINDOW BUTTON_REC
94 # define FFT_AMP_SCALE BUTTON_UP
95 # define FFT_FREQ_SCALE BUTTON_DOWN
96 # define FFT_QUIT BUTTON_POWER
98 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
99 # define FFT_PREV_GRAPH BUTTON_LEFT
100 # define FFT_NEXT_GRAPH BUTTON_RIGHT
101 # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
102 # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
103 # define FFT_AMP_SCALE BUTTON_UP
104 # define FFT_FREQ_SCALE BUTTON_DOWN
105 # define FFT_QUIT (BUTTON_HOME|BUTTON_REPEAT)
107 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
108 # define FFT_PREV_GRAPH BUTTON_LEFT
109 # define FFT_NEXT_GRAPH BUTTON_RIGHT
110 # define FFT_ORIENTATION BUTTON_UP
111 # define FFT_WINDOW BUTTON_REC
112 # define FFT_AMP_SCALE BUTTON_SELECT
113 # define FFT_QUIT BUTTON_POWER
114 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
115 # define FFT_PREV_GRAPH BUTTON_LEFT
116 # define FFT_NEXT_GRAPH BUTTON_RIGHT
117 # define FFT_ORIENTATION BUTTON_UP
118 # define FFT_WINDOW BUTTON_DOWN
119 # define FFT_AMP_SCALE BUTTON_SELECT
120 # define FFT_QUIT BUTTON_POWER
121 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
122 # define FFT_PREV_GRAPH BUTTON_LEFT
123 # define FFT_NEXT_GRAPH BUTTON_RIGHT
124 # define FFT_ORIENTATION BUTTON_UP
125 # define FFT_WINDOW BUTTON_HOME
126 # define FFT_AMP_SCALE BUTTON_SELECT
127 # define FFT_QUIT BUTTON_POWER
129 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
130 # define FFT_PREV_GRAPH BUTTON_LEFT
131 # define FFT_NEXT_GRAPH BUTTON_RIGHT
132 # define FFT_ORIENTATION BUTTON_FF
133 # define FFT_WINDOW BUTTON_SCROLL_UP
134 # define FFT_AMP_SCALE BUTTON_REW
135 # define FFT_FREQ_SCALE BUTTON_PLAY
136 # define FFT_QUIT BUTTON_POWER
138 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
139 # define FFT_PREV_GRAPH BUTTON_LEFT
140 # define FFT_NEXT_GRAPH BUTTON_RIGHT
141 # define FFT_ORIENTATION BUTTON_MENU
142 # define FFT_WINDOW BUTTON_PREV
143 # define FFT_AMP_SCALE BUTTON_UP
144 # define FFT_FREQ_SCALE BUTTON_DOWN
145 # define FFT_QUIT BUTTON_BACK
147 #elif (CONFIG_KEYPAD == MROBE100_PAD)
148 # define FFT_PREV_GRAPH BUTTON_LEFT
149 # define FFT_NEXT_GRAPH BUTTON_RIGHT
150 # define FFT_ORIENTATION BUTTON_PLAY
151 # define FFT_WINDOW BUTTON_SELECT
152 # define FFT_AMP_SCALE BUTTON_UP
153 # define FFT_FREQ_SCALE BUTTON_DOWN
154 # define FFT_QUIT BUTTON_POWER
156 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
157 # define FFT_PREV_GRAPH BUTTON_RC_REW
158 # define FFT_NEXT_GRAPH BUTTON_RC_FF
159 # define FFT_ORIENTATION BUTTON_RC_MODE
160 # define FFT_WINDOW BUTTON_RC_PLAY
161 # define FFT_AMP_SCALE BUTTON_RC_VOL_UP
162 # define FFT_QUIT BUTTON_RC_REC
164 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
165 # define FFT_QUIT BUTTON_POWER
166 # define FFT_PREV_GRAPH BUTTON_PLUS
167 # define FFT_NEXT_GRAPH BUTTON_MINUS
169 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
170 # define FFT_PREV_GRAPH BUTTON_LEFT
171 # define FFT_NEXT_GRAPH BUTTON_RIGHT
172 # define FFT_ORIENTATION BUTTON_MENU
173 # define FFT_WINDOW BUTTON_SELECT
174 # define FFT_AMP_SCALE BUTTON_UP
175 # define FFT_FREQ_SCALE BUTTON_DOWN
176 # define FFT_QUIT BUTTON_BACK
178 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
179 # define FFT_PREV_GRAPH BUTTON_LEFT
180 # define FFT_NEXT_GRAPH BUTTON_RIGHT
181 # define FFT_ORIENTATION BUTTON_SELECT
182 # define FFT_WINDOW BUTTON_MENU
183 # define FFT_AMP_SCALE BUTTON_UP
184 # define FFT_FREQ_SCALE BUTTON_DOWN
185 # define FFT_QUIT BUTTON_POWER
187 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
188 # define FFT_PREV_GRAPH BUTTON_PREV
189 # define FFT_NEXT_GRAPH BUTTON_NEXT
190 # define FFT_ORIENTATION BUTTON_PLAY
191 # define FFT_WINDOW BUTTON_MENU
192 # define FFT_AMP_SCALE BUTTON_UP
193 # define FFT_FREQ_SCALE BUTTON_DOWN
194 # define FFT_QUIT BUTTON_POWER
196 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
197 # define FFT_PREV_GRAPH BUTTON_PREV
198 # define FFT_NEXT_GRAPH BUTTON_NEXT
199 # define FFT_ORIENTATION BUTTON_PLAY
200 # define FFT_WINDOW BUTTON_MENU
201 # define FFT_AMP_SCALE BUTTON_UP
202 # define FFT_FREQ_SCALE BUTTON_DOWN
203 # define FFT_QUIT BUTTON_POWER
205 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
206 # define FFT_PREV_GRAPH BUTTON_LEFT
207 # define FFT_NEXT_GRAPH BUTTON_RIGHT
208 # define FFT_ORIENTATION BUTTON_UP
209 # define FFT_WINDOW BUTTON_DOWN
210 # define FFT_AMP_SCALE BUTTON_FFWD
211 # define FFT_QUIT BUTTON_PLAY
213 #elif (CONFIG_KEYPAD == MROBE500_PAD)
214 # define FFT_QUIT BUTTON_POWER
216 #elif (CONFIG_KEYPAD == ONDAVX747_PAD)
217 # define FFT_QUIT BUTTON_POWER
219 #elif (CONFIG_KEYPAD == ONDAVX777_PAD)
220 # define FFT_QUIT BUTTON_POWER
222 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
223 # define FFT_PREV_GRAPH BUTTON_PREV
224 # define FFT_NEXT_GRAPH BUTTON_NEXT
225 # define FFT_ORIENTATION BUTTON_MENU
226 # define FFT_WINDOW BUTTON_OK
227 # define FFT_AMP_SCALE BUTTON_PLAY
228 # define FFT_QUIT BUTTON_REC
230 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
231 # define FFT_PREV_GRAPH BUTTON_REW
232 # define FFT_NEXT_GRAPH BUTTON_FF
233 # define FFT_ORIENTATION BUTTON_REC
234 # define FFT_WINDOW BUTTON_FUNC
235 # define FFT_AMP_SCALE BUTTON_PLAY
236 # define FFT_QUIT (BUTTON_REC | BUTTON_PLAY)
238 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
239 # define FFT_PREV_GRAPH BUTTON_REW
240 # define FFT_NEXT_GRAPH BUTTON_FF
241 # define FFT_ORIENTATION BUTTON_REC
242 # define FFT_WINDOW BUTTON_ENTER
243 # define FFT_AMP_SCALE BUTTON_PLAY
244 # define FFT_QUIT (BUTTON_REC | BUTTON_REPEAT)
246 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
247 # define FFT_PREV_GRAPH BUTTON_LEFT
248 # define FFT_NEXT_GRAPH BUTTON_RIGHT
249 # define FFT_ORIENTATION BUTTON_PLAYPAUSE
250 # define FFT_WINDOW BUTTON_SELECT
251 # define FFT_AMP_SCALE BUTTON_BOTTOMLEFT
252 # define FFT_FREQ_SCALE BUTTON_BOTTOMRIGHT
253 # define FFT_QUIT BUTTON_POWER
255 #elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
256 # define FFT_PREV_GRAPH BUTTON_LEFT
257 # define FFT_NEXT_GRAPH BUTTON_RIGHT
258 # define FFT_ORIENTATION BUTTON_SELECT
259 # define FFT_WINDOW BUTTON_VOL_DOWN
260 # define FFT_AMP_SCALE BUTTON_UP
261 # define FFT_FREQ_SCALE BUTTON_DOWN
262 # define FFT_QUIT BUTTON_POWER
264 #elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
265 # define FFT_PREV_GRAPH BUTTON_LEFT
266 # define FFT_NEXT_GRAPH BUTTON_RIGHT
267 # define FFT_ORIENTATION BUTTON_USER
268 # define FFT_WINDOW BUTTON_MENU
269 # define FFT_AMP_SCALE BUTTON_SELECT
270 # define FFT_FREQ_SCALE BUTTON_DOWN
271 # define FFT_QUIT BUTTON_BACK
273 #elif (CONFIG_KEYPAD == HM60X_PAD)
274 # define FFT_PREV_GRAPH BUTTON_LEFT
275 # define FFT_NEXT_GRAPH BUTTON_RIGHT
276 # define FFT_AMP_SCALE BUTTON_UP
277 # define FFT_FREQ_SCALE BUTTON_DOWN
278 # define FFT_ORIENTATION BUTTON_SELECT
279 # define FFT_WINDOW (BUTTON_POWER|BUTTON_SELECT)
280 # define FFT_QUIT BUTTON_POWER
282 #elif (CONFIG_KEYPAD == HM801_PAD)
283 # define FFT_PREV_GRAPH BUTTON_LEFT
284 # define FFT_NEXT_GRAPH BUTTON_RIGHT
285 # define FFT_AMP_SCALE BUTTON_UP
286 # define FFT_FREQ_SCALE BUTTON_DOWN
287 # define FFT_ORIENTATION BUTTON_SELECT
288 # define FFT_WINDOW BUTTON_PLAY
289 # define FFT_QUIT BUTTON_POWER
291 #elif !defined(HAVE_TOUCHSCREEN)
292 #error No keymap defined!
293 #endif
295 #ifdef HAVE_TOUCHSCREEN
296 #ifndef FFT_PREV_GRAPH
297 # define FFT_PREV_GRAPH BUTTON_MIDLEFT
298 #endif
299 #ifndef FFT_NEXT_GRAPH
300 # define FFT_NEXT_GRAPH BUTTON_MIDRIGHT
301 #endif
302 #ifndef FFT_ORIENTATION
303 # define FFT_ORIENTATION BUTTON_CENTER
304 #endif
305 #ifndef FFT_WINDOW
306 # define FFT_WINDOW BUTTON_TOPLEFT
307 #endif
308 #ifndef FFT_AMP_SCALE
309 # define FFT_AMP_SCALE BUTTON_TOPRIGHT
310 #endif
311 #ifndef FFT_QUIT
312 # define FFT_QUIT BUTTON_BOTTOMLEFT
313 #endif
314 #endif /* HAVE_TOUCHSCREEN */
316 #ifdef HAVE_LCD_COLOR
317 #include "pluginbitmaps/fft_colors.h"
318 #endif
320 #include "kiss_fftr.h"
321 #include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */
322 #include "const.h"
325 /******************************* FFT globals *******************************/
327 #define LCD_SIZE MAX(LCD_WIDTH, LCD_HEIGHT)
329 #if (LCD_SIZE <= 511)
330 #define FFT_SIZE 1024 /* 512*2 */
331 #elif (LCD_SIZE <= 1023)
332 #define FFT_SIZE 2048 /* 1024*2 */
333 #else
334 #define FFT_SIZE 4096 /* 2048*2 */
335 #endif
337 #define ARRAYLEN_IN (FFT_SIZE)
338 #define ARRAYLEN_OUT (FFT_SIZE)
339 #define ARRAYLEN_PLOT (FFT_SIZE/2-1) /* FFT is symmetric, ignore DC */
340 #define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+\
341 sizeof(kiss_fft_cpx)*(FFT_SIZE-1))
343 #define __COEFF(type,size) type##_##size
344 #define _COEFF(x, y) __COEFF(x,y) /* force CPP evaluation of FFT_SIZE */
345 #define HANN_COEFF _COEFF(hann, FFT_SIZE)
346 #define HAMMING_COEFF _COEFF(hamming, FFT_SIZE)
348 /* cacheline-aligned buffers with COP, otherwise word-aligned */
349 /* CPU/COP only applies when compiled for more than one core */
351 #define CACHEALIGN_UP_SIZE(type, len) \
352 (CACHEALIGN_UP((len)*sizeof(type) + (sizeof(type)-1)) / sizeof(type))
353 /* Shared */
354 /* COP + CPU PCM */
355 static kiss_fft_cpx input[CACHEALIGN_UP_SIZE(kiss_fft_scalar, ARRAYLEN_IN)]
356 CACHEALIGN_AT_LEAST_ATTR(4);
357 /* CPU+COP */
358 #if NUM_CORES > 1
359 /* Output queue indexes */
360 static volatile int output_head SHAREDBSS_ATTR = 0;
361 static volatile int output_tail SHAREDBSS_ATTR = 0;
362 /* The result is nfft/2 complex frequency bins from DC to Nyquist. */
363 static kiss_fft_cpx output[2][CACHEALIGN_UP_SIZE(kiss_fft_cpx, ARRAYLEN_OUT)]
364 SHAREDBSS_ATTR;
365 #else
366 /* Only one output buffer */
367 #define output_head 0
368 #define output_tail 0
369 /* The result is nfft/2 complex frequency bins from DC to Nyquist. */
370 static kiss_fft_cpx output[1][ARRAYLEN_OUT];
371 #endif
373 /* Unshared */
374 /* COP */
375 static kiss_fft_cfg fft_state SHAREDBSS_ATTR;
376 static char fft_buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE_FFT)]
377 CACHEALIGN_AT_LEAST_ATTR(4);
378 /* CPU */
379 static uint32_t linf_magnitudes[ARRAYLEN_PLOT]; /* ling freq bin plot */
380 static uint32_t logf_magnitudes[ARRAYLEN_PLOT]; /* log freq plot output */
381 static uint32_t *plot; /* use this to plot */
382 static struct
384 int16_t bin; /* integer bin number */
385 uint16_t frac; /* interpolation fraction */
386 } binlog[ARRAYLEN_PLOT] __attribute__((aligned(4)));
388 /**************************** End of FFT globals ***************************/
391 /********************************* Settings ********************************/
393 enum fft_orientation
395 FFT_MIN_OR = 0,
396 FFT_OR_VERT = 0, /* Amplitude vertical, frequency horizontal * */
397 FFT_OR_HORZ, /* Amplitude horizontal, frequency vertical */
398 FFT_MAX_OR,
401 enum fft_display_mode
403 FFT_MIN_DM = 0,
404 FFT_DM_LINES = 0, /* Bands are displayed as single-pixel lines * */
405 FFT_DM_BARS, /* Bands are combined into wide bars */
406 FFT_DM_SPECTROGRAM, /* Band amplitudes are denoted by color */
407 FFT_MAX_DM,
410 enum fft_amp_scale
412 FFT_MIN_AS = 0,
413 FFT_AS_LOG = 0, /* Amplitude is plotted on log scale * */
414 FFT_AS_LIN, /* Amplitude is plotted on linear scale */
415 FFT_MAX_AS,
418 enum fft_freq_scale
420 FFT_MIN_FS = 0,
421 FFT_FS_LOG = 0, /* Frequency is plotted on log scale * */
422 FFT_FS_LIN, /* Frequency is plotted on linear scale */
423 FFT_MAX_FS
426 enum fft_window_func
428 FFT_MIN_WF = 0,
429 FFT_WF_HAMMING = 0, /* Hamming window applied to each input frame * */
430 FFT_WF_HANN, /* Hann window applied to each input frame */
431 FFT_MAX_WF,
434 static struct fft_config
436 int orientation;
437 int drawmode;
438 int amp_scale;
439 int freq_scale;
440 int window_func;
441 } fft_disk =
443 /* Defaults */
444 .orientation = FFT_OR_VERT,
445 .drawmode = FFT_DM_LINES,
446 .amp_scale = FFT_AS_LOG,
447 .freq_scale = FFT_FS_LOG,
448 .window_func = FFT_WF_HAMMING,
451 #define CFGFILE_VERSION 0
452 #define CFGFILE_MINVERSION 0
454 static const char cfg_filename[] = "fft.cfg";
455 static struct configdata disk_config[] =
457 { TYPE_ENUM, FFT_MIN_OR, FFT_MAX_OR,
458 { .int_p = &fft_disk.orientation }, "orientation",
459 (char * []){ [FFT_OR_VERT] = "vertical",
460 [FFT_OR_HORZ] = "horizontal" } },
461 { TYPE_ENUM, FFT_MIN_DM, FFT_MAX_DM,
462 { .int_p = &fft_disk.drawmode }, "drawmode",
463 (char * []){ [FFT_DM_LINES] = "lines",
464 [FFT_DM_BARS] = "bars",
465 [FFT_DM_SPECTROGRAM] = "spectrogram" } },
466 { TYPE_ENUM, FFT_MIN_AS, FFT_MAX_AS,
467 { .int_p = &fft_disk.amp_scale }, "amp scale",
468 (char * []){ [FFT_AS_LOG] = "logarithmic",
469 [FFT_AS_LIN] = "linear" } },
470 { TYPE_ENUM, FFT_MIN_FS, FFT_MAX_FS,
471 { .int_p = &fft_disk.freq_scale }, "freq scale",
472 (char * []){ [FFT_FS_LOG] = "logarithmic",
473 [FFT_FS_LIN] = "linear" } },
474 { TYPE_ENUM, FFT_MIN_WF, FFT_MAX_WF,
475 { .int_p = &fft_disk.window_func }, "window function",
476 (char * []){ [FFT_WF_HAMMING] = "hamming",
477 [FFT_WF_HANN] = "hann" } },
480 /* Hint flags for setting changes */
481 enum fft_setting_flags
483 FFT_SETF_OR = 1 << 0,
484 FFT_SETF_DM = 1 << 1,
485 FFT_SETF_AS = 1 << 2,
486 #ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
487 FFT_SETF_FS = 1 << 3,
488 #endif
489 FFT_SETF_WF = 1 << 4,
490 FFT_SETF_ALL = 0x1f
493 /***************************** End of settings *****************************/
496 /**************************** Operational data *****************************/
498 #define COLOR_DEFAULT_FG MYLCD_DEFAULT_FG
499 #define COLOR_DEFAULT_BG MYLCD_DEFAULT_BG
501 #ifdef HAVE_LCD_COLOR
502 #define COLOR_MESSAGE_FRAME LCD_RGBPACK(0xc6, 0x00, 0x00)
503 #define COLOR_MESSAGE_BG LCD_BLACK
504 #define COLOR_MESSAGE_FG LCD_WHITE
505 #else
506 #define COLOR_MESSAGE_FRAME GREY_DARKGRAY
507 #define COLOR_MESSAGE_BG GREY_WHITE
508 #define COLOR_MESSAGE_FG GREY_BLACK
509 #endif
511 #define FFT_OSD_MARGIN_SIZE 1
513 #define FFT_PERIOD (HZ/50) /* How fast to try to go */
515 /* Based on feeding-in a 0db sinewave at FS/4 */
516 #define QLOG_MAX 0x0009154B
517 /* Fudge it a little or it's not very visbile */
518 #define QLIN_MAX (0x00002266 >> 1)
520 static struct fft_config fft;
521 typedef void (* fft_drawfn_t)(unsigned, unsigned);
522 static fft_drawfn_t fft_drawfn = NULL; /* plotting function */
523 static int fft_spectrogram_pos = -1; /* row or column - only used by one at a time */
524 static uint32_t fft_graph_scale = 0; /* max level over time, for scaling display */
525 static int fft_message_id = -1; /* current message id displayed */
526 static char fft_osd_message[32]; /* current message string displayed */
527 static long fft_next_frame_tick = 0; /* next tick to attempt drawing */
529 #ifdef HAVE_LCD_COLOR
530 #define SHADES BMPWIDTH_fft_colors
531 #define SPECTROGRAPH_PALETTE(index) (fft_colors[index])
532 #else
533 #define SHADES 256
534 #define SPECTROGRAPH_PALETTE(index) (255 - (index))
535 #endif
537 /************************* End of operational data *************************/
540 /***************************** Math functions ******************************/
542 /* Apply window function to input */
543 static void apply_window_func(enum fft_window_func mode)
545 static const int16_t * const coefs[] =
547 [FFT_WF_HAMMING] = HAMMING_COEFF,
548 [FFT_WF_HANN] = HANN_COEFF,
551 const int16_t * const c = coefs[mode];
553 for(int i = 0; i < ARRAYLEN_IN; ++i)
554 input[i].r = (input[i].r * c[i] + 16384) >> 15;
557 /* Calculates the magnitudes from complex numbers and returns the maximum */
558 static unsigned calc_magnitudes(enum fft_amp_scale scale)
560 /* A major assumption made when calculating the Q*MAX constants
561 * is that the maximum magnitude is 29 bits long. */
562 unsigned this_max = 0;
563 kiss_fft_cpx *this_output = output[output_head] + 1; /* skip DC */
565 /* Calculate the magnitude, discarding the phase. */
566 for(int i = 0; i < ARRAYLEN_PLOT; ++i)
568 int32_t re = this_output[i].r;
569 int32_t im = this_output[i].i;
571 uint32_t d = re*re + im*im;
573 if(d > 0)
575 if(d > 0x7FFFFFFF) /* clip */
577 d = 0x7FFFFFFF; /* if our assumptions are correct,
578 this should never happen. It's just
579 a safeguard. */
582 if(scale == FFT_AS_LOG)
584 if(d < 0x8000) /* be more precise */
586 /* ln(x ^ .5) = .5*ln(x) */
587 d = fp16_log(d << 16) >> 1;
589 else
591 d = isqrt(d); /* linear scaling, nothing
592 bad should happen */
593 d = fp16_log(d << 16); /* the log function
594 expects s15.16 values */
597 else
599 d = isqrt(d); /* linear scaling, nothing
600 bad should happen */
604 /* Length 2 moving average - last transform and this one */
605 linf_magnitudes[i] = (linf_magnitudes[i] + d) >> 1;
607 if(d > this_max)
608 this_max = d;
611 return this_max;
614 /* Move plot bins into a logarithmic scale by sliding them towards the
615 * Nyquist bin according to the translation in the binlog array. */
616 static void log_plot_translate(void)
618 for(int i = ARRAYLEN_PLOT-1; i > 0; --i)
620 int s = binlog[i].bin;
621 int e = binlog[i-1].bin;
622 unsigned frac = binlog[i].frac;
624 int bin = linf_magnitudes[s];
626 if(frac)
628 /* slope < 1, Interpolate stretched bins (linear for now) */
629 int diff = linf_magnitudes[s+1] - bin;
633 logf_magnitudes[i] = bin + FRACMUL(frac << 15, diff);
634 frac = binlog[--i].frac;
636 while(frac);
638 else
640 /* slope > 1, Find peak of two or more bins */
641 while(--s > e)
643 int val = linf_magnitudes[s];
645 if (val > bin)
646 bin = val;
650 logf_magnitudes[i] = bin;
654 /* Calculates the translation for logarithmic plot bins */
655 static void logarithmic_plot_init(void)
658 * log: y = round(n * ln(x) / ln(n))
659 * anti: y = round(exp(x * ln(n) / n))
661 int j = fp16_log((ARRAYLEN_PLOT - 1) << 16);
662 for(int i = 0; i < ARRAYLEN_PLOT; ++i)
664 binlog[i].bin = (fp16_exp(i * j / (ARRAYLEN_PLOT - 1)) + 32768) >> 16;
667 /* setup fractions for interpolation of stretched bins */
668 for(int i = 0; i < ARRAYLEN_PLOT-1; i = j)
670 j = i + 1;
672 /* stop when we have two different values */
673 while(binlog[j].bin == binlog[i].bin)
674 j++; /* if here, local slope of curve is < 1 */
676 if(j > i + 1)
678 /* distribute pieces evenly over stretched interval */
679 int diff = j - i;
680 int x = 0;
683 binlog[i].frac = (x++ << 16) / diff;
685 while(++i < j);
690 /************************** End of math functions **************************/
693 /*********************** Plotting functions (modes) ************************/
695 static void draw_lines_vertical(unsigned this_max, unsigned graph_max)
697 #if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
698 const int offset = 0;
699 const int plotwidth = LCD_WIDTH;
700 #else
701 const int offset = (LCD_HEIGHT - ARRAYLEN_PLOT) / 2;
702 const int plotwidth = ARRAYLEN_PLOT;
703 #endif
705 mylcd_clear_display();
707 if(this_max == 0)
709 mylcd_hline(0, LCD_WIDTH - 1, LCD_HEIGHT - 1); /* Draw all "zero" */
710 return;
713 /* take the maximum of neighboring bins if we have to scale down the
714 * graph horizontally */
715 if(LCD_WIDTH < ARRAYLEN_PLOT) /* graph compression */
717 int bins_acc = LCD_WIDTH / 2;
718 unsigned bins_max = 0;
720 for(int i = 0, x = 0; i < ARRAYLEN_PLOT; ++i)
722 unsigned bin = plot[i];
724 if(bin > bins_max)
725 bins_max = bin;
727 bins_acc += LCD_WIDTH;
729 if(bins_acc >= ARRAYLEN_PLOT)
731 int h = LCD_HEIGHT*bins_max / graph_max;
732 mylcd_vline(x, LCD_HEIGHT - h, LCD_HEIGHT-1);
734 x++;
735 bins_acc -= ARRAYLEN_PLOT;
736 bins_max = 0;
740 else
742 for(int i = 0; i < plotwidth; ++i)
744 int h = LCD_HEIGHT*plot[i] / graph_max;
745 mylcd_vline(i + offset, LCD_HEIGHT - h, LCD_HEIGHT-1);
750 static void draw_lines_horizontal(unsigned this_max, unsigned graph_max)
752 #if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
753 const int offset = 0;
754 const int plotwidth = LCD_HEIGHT;
755 #else
756 const int offset = (LCD_HEIGHT - ARRAYLEN_PLOT) / 2;
757 const int plotwidth = ARRAYLEN_PLOT;
758 #endif
760 mylcd_clear_display();
762 if(this_max == 0)
764 mylcd_vline(0, 0, LCD_HEIGHT-1); /* Draw all "zero" */
765 return;
768 /* take the maximum of neighboring bins if we have to scale the graph
769 * horizontally */
770 if(LCD_HEIGHT < ARRAYLEN_PLOT) /* graph compression */
772 int bins_acc = LCD_HEIGHT / 2;
773 unsigned bins_max = 0;
775 for(int i = 0, y = 0; i < ARRAYLEN_PLOT; ++i)
777 unsigned bin = plot[i];
779 if(bin > bins_max)
780 bins_max = bin;
782 bins_acc += LCD_HEIGHT;
784 if(bins_acc >= ARRAYLEN_PLOT)
786 int w = LCD_WIDTH*bins_max / graph_max;
787 mylcd_hline(0, w - 1, y);
789 y++;
790 bins_acc -= ARRAYLEN_PLOT;
791 bins_max = 0;
795 else
797 for(int i = 0; i < plotwidth; ++i)
799 int w = LCD_WIDTH*plot[i] / graph_max;
800 mylcd_hline(0, w - 1, i + offset);
805 static void draw_bars_vertical(unsigned this_max, unsigned graph_max)
807 #if LCD_WIDTH < LCD_HEIGHT
808 const int bars = 15;
809 #else
810 const int bars = 20;
811 #endif
812 const int border = 2;
813 const int barwidth = LCD_WIDTH / (bars + border);
814 const int width = barwidth - border;
815 const int offset = (LCD_WIDTH - bars*barwidth + border) / 2;
817 mylcd_clear_display();
818 mylcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-1); /* Draw baseline */
820 if(this_max == 0)
821 return; /* nothing more to draw */
823 int bins_acc = bars / 2;
824 unsigned bins_max = 0;
826 for(int i = 0, x = offset;; ++i)
828 unsigned bin = plot[i];
830 if(bin > bins_max)
831 bins_max = bin;
833 bins_acc += bars;
835 if(bins_acc >= ARRAYLEN_PLOT)
837 int h = LCD_HEIGHT*bins_max / graph_max;
838 mylcd_fillrect(x, LCD_HEIGHT - h, width, h - 1);
840 if(i >= ARRAYLEN_PLOT-1)
841 break;
843 x += barwidth;
844 bins_acc -= ARRAYLEN_PLOT;
845 bins_max = 0;
850 static void draw_bars_horizontal(unsigned this_max, unsigned graph_max)
852 #if LCD_WIDTH < LCD_HEIGHT
853 const int bars = 20;
854 #else
855 const int bars = 15;
856 #endif
857 const int border = 2;
858 const int barwidth = LCD_HEIGHT / (bars + border);
859 const int height = barwidth - border;
860 const int offset = (LCD_HEIGHT - bars*barwidth + border) / 2;
862 mylcd_clear_display();
863 mylcd_vline(0, 0, LCD_HEIGHT-1); /* Draw baseline */
865 if(this_max == 0)
866 return; /* nothing more to draw */
868 int bins_acc = bars / 2;
869 unsigned bins_max = 0;
871 for(int i = 0, y = offset;; ++i)
873 unsigned bin = plot[i];
875 if(bin > bins_max)
876 bins_max = bin;
878 bins_acc += bars;
880 if(bins_acc >= ARRAYLEN_PLOT)
882 int w = LCD_WIDTH*bins_max / graph_max;
883 mylcd_fillrect(1, y, w, height);
885 if(i >= ARRAYLEN_PLOT-1)
886 break;
888 y += barwidth;
889 bins_acc -= ARRAYLEN_PLOT;
890 bins_max = 0;
895 static void draw_spectrogram_vertical(unsigned this_max, unsigned graph_max)
897 const int scale_factor = MIN(LCD_HEIGHT, ARRAYLEN_PLOT);
899 if(fft_spectrogram_pos < LCD_WIDTH-1)
900 fft_spectrogram_pos++;
901 else
902 mylcd_scroll_left(1);
904 int bins_acc = scale_factor / 2;
905 unsigned bins_max = 0;
907 for(int i = 0, y = LCD_HEIGHT-1;; ++i)
909 unsigned bin = plot[i];
911 if(bin > bins_max)
912 bins_max = bin;
914 bins_acc += scale_factor;
916 if(bins_acc >= ARRAYLEN_PLOT)
918 unsigned index = (SHADES-1)*bins_max / graph_max;
920 /* These happen because we exaggerate the graph a little for
921 * linear mode */
922 if(index >= SHADES)
923 index = SHADES-1;
925 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index));
926 mylcd_drawpixel(fft_spectrogram_pos, y);
928 if(--y < 0)
929 break;
931 bins_acc -= ARRAYLEN_PLOT;
932 bins_max = 0;
936 (void)this_max;
939 static void draw_spectrogram_horizontal(unsigned this_max, unsigned graph_max)
941 const int scale_factor = MIN(LCD_WIDTH, ARRAYLEN_PLOT);
943 if(fft_spectrogram_pos < LCD_HEIGHT-1)
944 fft_spectrogram_pos++;
945 else
946 mylcd_scroll_up(1);
948 int bins_acc = scale_factor / 2;
949 unsigned bins_max = 0;
951 for(int i = 0, x = 0;; ++i)
953 unsigned bin = plot[i];
955 if(bin > bins_max)
956 bins_max = bin;
958 bins_acc += scale_factor;
960 if(bins_acc >= ARRAYLEN_PLOT)
962 unsigned index = (SHADES-1)*bins_max / graph_max;
964 /* These happen because we exaggerate the graph a little for
965 * linear mode */
966 if(index >= SHADES)
967 index = SHADES-1;
969 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index));
970 mylcd_drawpixel(x, fft_spectrogram_pos);
972 if(++x >= LCD_WIDTH)
973 break;
975 bins_acc -= ARRAYLEN_PLOT;
976 bins_max = 0;
980 (void)this_max;
983 /******************** End of plotting functions (modes) ********************/
986 /***************************** FFT functions *******************************/
988 static bool is_playing(void)
990 return rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_PLAYING;
993 /** functions use in single/multi configuration **/
994 static inline bool fft_init_fft_lib(void)
996 size_t size = sizeof(fft_buffer);
997 fft_state = kiss_fft_alloc(FFT_SIZE, 0, fft_buffer, &size);
999 if(fft_state == NULL)
1001 DEBUGF("needed data: %i", (int) size);
1002 return false;
1005 return true;
1008 static inline bool fft_get_fft(void)
1010 int count;
1011 const int16_t *value =
1012 rb->mixer_channel_get_buffer(PCM_MIXER_CHAN_PLAYBACK, &count);
1013 /* This block can introduce discontinuities in our data. Meaning, the
1014 * FFT will not be done a continuous segment of the signal. Which can
1015 * be bad. Or not.
1017 * Anyway, this is a demo, not a scientific tool. If you want accuracy,
1018 * do a proper spectrum analysis.*/
1020 /* there are cases when we don't have enough data to fill the buffer */
1021 if(count != ARRAYLEN_IN)
1023 if(count < ARRAYLEN_IN)
1024 return false;
1026 count = ARRAYLEN_IN; /* too much - limit */
1029 int fft_idx = 0; /* offset in 'input' */
1033 kiss_fft_scalar left = *value++;
1034 kiss_fft_scalar right = *value++;
1035 input[fft_idx].r = (left + right) >> 1; /* to mono */
1036 } while (fft_idx++, --count > 0);
1038 apply_window_func(fft.window_func);
1040 rb->yield();
1042 kiss_fft(fft_state, input, output[output_tail]);
1044 rb->yield();
1046 return true;
1049 #if NUM_CORES > 1
1050 /* use a worker thread if there is another processor core */
1051 static volatile bool fft_thread_run SHAREDDATA_ATTR = false;
1052 static unsigned long fft_thread = 0;
1054 static long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))]
1055 CACHEALIGN_AT_LEAST_ATTR(4);
1057 static void fft_thread_entry(void)
1059 if(!fft_init_fft_lib())
1061 output_tail = -1; /* tell that we bailed */
1062 fft_thread_run = true;
1063 return;
1066 fft_thread_run = true;
1068 while(fft_thread_run)
1070 if (!is_playing())
1072 rb->sleep(HZ/5);
1073 continue;
1076 if (!fft_get_fft())
1078 rb->sleep(0); /* not enough - ease up */
1079 continue;
1082 /* write back output for other processor and invalidate for next
1083 frame read */
1084 rb->commit_discard_dcache();
1086 int new_tail = output_tail ^ 1;
1088 /* if full, block waiting until reader has freed a slot */
1089 while(fft_thread_run)
1091 if(new_tail != output_head)
1093 output_tail = new_tail;
1094 break;
1097 rb->sleep(0);
1102 static bool fft_have_fft(void)
1104 return output_head != output_tail;
1107 /* Call only after fft_have_fft() has returned true */
1108 static inline void fft_free_fft_output(void)
1110 output_head ^= 1; /* finished with this */
1113 static bool fft_init_fft(void)
1115 /* create worker thread - on the COP for dual-core targets */
1116 fft_thread = rb->create_thread(fft_thread_entry,
1117 fft_thread_stack, sizeof(fft_thread_stack), 0, "fft output thread"
1118 IF_PRIO(, PRIORITY_USER_INTERFACE+1) IF_COP(, COP));
1120 if(fft_thread == 0)
1122 rb->splash(HZ, "FFT thread failed create");
1123 return false;
1126 /* wait for it to indicate 'ready' */
1127 while(fft_thread_run == false)
1128 rb->sleep(0);
1130 if(output_tail == -1)
1132 /* FFT thread bailed-out like The Fed */
1133 rb->thread_wait(fft_thread);
1134 rb->splash(HZ, "FFT thread failed to init");
1135 return false;
1138 return true;
1141 static void fft_close_fft(void)
1143 /* Handle our FFT thread. */
1144 fft_thread_run = false;
1145 rb->thread_wait(fft_thread);
1146 rb->commit_discard_dcache();
1148 #else /* NUM_CORES == 1 */
1149 /* everything serialize on single-core and FFT gets to use IRAM main stack if
1150 * target uses IRAM */
1151 static bool fft_have_fft(void)
1153 return is_playing() && fft_get_fft();
1156 static inline void fft_free_fft_output(void)
1158 /* nothing to do */
1161 static bool fft_init_fft(void)
1163 return fft_init_fft_lib();
1166 static inline void fft_close_fft(void)
1168 /* nothing to do */
1170 #endif /* NUM_CORES */
1172 /************************** End of FFT functions ***************************/
1175 /****************************** OSD functions ******************************/
1177 /* Format a message to display */
1178 static void fft_osd_format_message(enum fft_setting_flags id)
1180 const char *msg = "";
1182 switch (id)
1184 case FFT_SETF_DM:
1185 msg = (const char * [FFT_MAX_DM]) {
1186 [FFT_DM_LINES] = "Lines",
1187 [FFT_DM_BARS] = "Bars",
1188 [FFT_DM_SPECTROGRAM] = "Spectrogram",
1189 }[fft.drawmode];
1190 break;
1192 case FFT_SETF_WF:
1193 msg = (const char * [FFT_MAX_WF]) {
1194 [FFT_WF_HAMMING] = "Hamming window",
1195 [FFT_WF_HANN] = "Hann window",
1196 }[fft.window_func];
1197 break;
1199 case FFT_SETF_AS:
1200 msg = (const char * [FFT_MAX_AS]) {
1201 [FFT_AS_LOG] = "Logarithmic amplitude",
1202 [FFT_AS_LIN] = "Linear amplitude"
1203 }[fft.amp_scale];
1204 break;
1206 #ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
1207 case FFT_SETF_FS:
1208 msg = (const char * [FFT_MAX_FS]) {
1209 [FFT_FS_LOG] = "Logarithmic frequency",
1210 [FFT_FS_LIN] = "Linear frequency",
1211 }[fft.freq_scale];
1212 break;
1213 #endif
1215 case FFT_SETF_OR:
1216 rb->snprintf(fft_osd_message, sizeof (fft_osd_message),
1217 (const char * [FFT_MAX_OR]) {
1218 [FFT_OR_VERT] = "Vertical %s",
1219 [FFT_OR_HORZ] = "Horizontal %s",
1220 }[fft.orientation],
1221 (const char * [FFT_MAX_DM]) {
1222 [FFT_DM_LINES ... FFT_DM_BARS] = "amplitude",
1223 [FFT_DM_SPECTROGRAM] = "frequency"
1224 }[fft.drawmode]);
1225 return;
1227 #if 0
1228 /* Pertentially */
1229 case FFT_SETF_VOLUME:
1230 rb->snprintf(fft_osd_message, sizeof (fft_osd_message),
1231 "Volume: %d%s",
1232 rb->sound_val2phys(SOUND_VOLUME, global_settings.volume),
1233 rb->sound_unit(SOUND_VOLUME));
1234 return;
1235 #endif
1237 default:
1238 break;
1241 /* Default action: copy string */
1242 rb->strlcpy(fft_osd_message, msg, sizeof (fft_osd_message));
1245 static void fft_osd_draw_cb(int x, int y, int width, int height)
1247 #if LCD_DEPTH > 1
1248 mylcd_set_foreground(COLOR_MESSAGE_FG);
1249 mylcd_set_background(COLOR_MESSAGE_BG);
1250 #endif
1251 #if FFT_OSD_MARGIN_SIZE != 0
1252 mylcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1253 mylcd_fillrect(1, 1, width - 2, height - 2);
1254 mylcd_set_drawmode(DRMODE_SOLID);
1255 #endif
1256 mylcd_putsxy(1+FFT_OSD_MARGIN_SIZE, 1+FFT_OSD_MARGIN_SIZE,
1257 fft_osd_message);
1258 #if LCD_DEPTH > 1
1259 mylcd_set_foreground(COLOR_MESSAGE_FRAME);
1260 #endif
1262 mylcd_drawrect(0, 0, width, height);
1264 (void)x; (void)y;
1267 static void fft_osd_show_message(enum fft_setting_flags id)
1269 fft_osd_format_message(id);
1271 if(!myosd_enabled())
1272 return;
1274 int width, height;
1275 int maxwidth, maxheight;
1277 mylcd_set_viewport(myosd_get_viewport());
1278 myosd_get_max_dims(&maxwidth, &maxheight);
1279 mylcd_setfont(FONT_UI);
1280 mylcd_getstringsize(fft_osd_message, &width, &height);
1281 mylcd_set_viewport(NULL);
1283 width += 2 + 2*FFT_OSD_MARGIN_SIZE;
1284 if(width > maxwidth)
1285 width = maxwidth;
1287 height += 2 + 2*FFT_OSD_MARGIN_SIZE;
1288 if(height > maxheight)
1289 height = maxheight;
1291 bool drawn = myosd_update_pos((LCD_WIDTH - width) / 2,
1292 (LCD_HEIGHT - height) / 2,
1293 width, height);
1295 myosd_show(OSD_SHOW | (drawn ? 0 : OSD_UPDATENOW));
1298 static void fft_popupmsg(enum fft_setting_flags id)
1300 fft_message_id = id;
1303 /************************** End of OSD functions ***************************/
1306 static void fft_setting_update(unsigned which)
1308 static fft_drawfn_t fft_drawfns[FFT_MAX_DM][FFT_MAX_OR] =
1310 [FFT_DM_LINES] =
1312 [FFT_OR_HORZ] = draw_lines_horizontal,
1313 [FFT_OR_VERT] = draw_lines_vertical,
1315 [FFT_DM_BARS] =
1317 [FFT_OR_HORZ] = draw_bars_horizontal,
1318 [FFT_OR_VERT] = draw_bars_vertical,
1320 [FFT_DM_SPECTROGRAM] =
1322 [FFT_OR_HORZ] = draw_spectrogram_horizontal,
1323 [FFT_OR_VERT] = draw_spectrogram_vertical,
1327 if(which & (FFT_SETF_DM | FFT_SETF_OR))
1329 fft_drawfn = fft_drawfns[fft.drawmode]
1330 [fft.orientation];
1332 if(fft.drawmode == FFT_DM_SPECTROGRAM)
1334 fft_spectrogram_pos = -1;
1335 myosd_lcd_update_prepare();
1336 mylcd_clear_display();
1337 myosd_lcd_update();
1341 if(which & (FFT_SETF_DM | FFT_SETF_AS))
1343 if(fft.drawmode == FFT_DM_SPECTROGRAM)
1345 fft_graph_scale = fft.amp_scale == FFT_AS_LIN ?
1346 QLIN_MAX : QLOG_MAX;
1348 else
1350 fft_graph_scale = 0;
1354 #ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
1355 if(which & FFT_SETF_FS)
1357 plot = fft.freq_scale == FFT_FS_LIN ?
1358 linf_magnitudes : logf_magnitudes;
1360 #endif
1362 if(which & FFT_SETF_AS)
1364 memset(linf_magnitudes, 0, sizeof (linf_magnitudes));
1365 memset(logf_magnitudes, 0, sizeof (logf_magnitudes));
1369 static long fft_draw(void)
1371 long tick = *rb->current_tick;
1373 if(fft_message_id != -1)
1375 /* Show a new message */
1376 fft_osd_show_message((enum fft_setting_flags)fft_message_id);
1377 fft_message_id = -1;
1379 else
1381 /* Monitor OSD timeout */
1382 myosd_monitor_timeout();
1385 if(TIME_BEFORE(tick, fft_next_frame_tick))
1386 return fft_next_frame_tick - tick; /* Too early */
1388 unsigned this_max;
1390 if(!fft_have_fft())
1392 if(is_playing())
1393 return HZ/100;
1395 /* All magnitudes == 0 thus this_max == 0 */
1396 for(int i = 0; i < ARRAYLEN_PLOT; i++)
1397 linf_magnitudes[i] >>= 1; /* decay */
1399 this_max = 0;
1401 else
1403 this_max = calc_magnitudes(fft.amp_scale);
1405 fft_free_fft_output(); /* COP only */
1407 if(fft.drawmode != FFT_DM_SPECTROGRAM &&
1408 this_max > fft_graph_scale)
1410 fft_graph_scale = this_max;
1414 if (fft.freq_scale == FFT_FS_LOG)
1415 log_plot_translate();
1417 myosd_lcd_update_prepare();
1419 mylcd_set_foreground(COLOR_DEFAULT_FG);
1420 mylcd_set_background(COLOR_DEFAULT_BG);
1422 fft_drawfn(this_max, fft_graph_scale);
1424 myosd_lcd_update();
1426 fft_next_frame_tick = tick + FFT_PERIOD;
1427 return fft_next_frame_tick - *rb->current_tick;
1430 static void fft_osd_init(void *buf, size_t bufsize)
1432 int width, height;
1433 mylcd_setfont(FONT_UI);
1434 mylcd_getstringsize("M", NULL, &height);
1435 width = LCD_WIDTH;
1436 height += 2 + 2*FFT_OSD_MARGIN_SIZE;
1437 myosd_init(OSD_INIT_MAJOR_HEIGHT | OSD_INIT_MINOR_MAX, buf, bufsize,
1438 fft_osd_draw_cb, &width, &height, NULL);
1439 myosd_set_timeout(HZ);
1442 static void fft_cleanup(void)
1444 myosd_destroy();
1446 fft_close_fft();
1448 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1449 rb->cancel_cpu_boost();
1450 #endif
1451 #ifndef HAVE_LCD_COLOR
1452 grey_release();
1453 #endif
1454 backlight_use_settings();
1456 /* save settings if changed */
1457 if (rb->memcmp(&fft, &fft_disk, sizeof(fft)))
1459 fft_disk = fft;
1460 configfile_save(cfg_filename, disk_config, ARRAYLEN(disk_config),
1461 CFGFILE_VERSION);
1465 static bool fft_setup(void)
1467 atexit(fft_cleanup);
1469 configfile_load(cfg_filename, disk_config, ARRAYLEN(disk_config),
1470 CFGFILE_MINVERSION);
1471 fft = fft_disk; /* copy to running config */
1473 if(!fft_init_fft())
1474 return false;
1476 /* get the remainder of the plugin buffer for OSD and perhaps
1477 greylib */
1478 size_t bufsize = 0;
1479 unsigned char *buf = rb->plugin_get_buffer(&bufsize);
1481 #ifndef HAVE_LCD_COLOR
1482 /* initialize the greyscale buffer.*/
1483 long grey_size;
1484 if(!grey_init(buf, bufsize, GREY_ON_COP | GREY_BUFFERED,
1485 LCD_WIDTH, LCD_HEIGHT, &grey_size))
1487 rb->splash(HZ, "Couldn't init greyscale display");
1488 return false;
1491 grey_show(true);
1493 buf += grey_size;
1494 bufsize -= grey_size;
1495 #endif /* !HAVE_LCD_COLOR */
1497 fft_osd_init(buf, bufsize);
1499 #if LCD_DEPTH > 1
1500 myosd_lcd_update_prepare();
1501 rb->lcd_set_backdrop(NULL);
1502 mylcd_clear_display();
1503 myosd_lcd_update();
1504 #endif
1505 backlight_ignore_timeout();
1507 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1508 rb->trigger_cpu_boost();
1509 #endif
1511 logarithmic_plot_init();
1512 fft_setting_update(FFT_SETF_ALL);
1513 fft_next_frame_tick = *rb->current_tick;
1515 return true;
1518 enum plugin_status plugin_start(const void* parameter)
1520 bool run = true;
1522 if(!fft_setup())
1523 return PLUGIN_ERROR;
1525 while(run)
1527 long delay = fft_draw();
1529 if(delay <= 0)
1531 delay = 0;
1532 rb->yield(); /* tmo = 0 won't yield */
1535 int button = rb->button_get_w_tmo(delay);
1537 switch (button)
1539 case FFT_QUIT:
1540 run = false;
1541 break;
1543 case FFT_ORIENTATION:
1544 if (++fft.orientation >= FFT_MAX_OR)
1545 fft.orientation = FFT_MIN_OR;
1547 fft_setting_update(FFT_SETF_OR);
1548 fft_popupmsg(FFT_SETF_OR);
1549 break;
1551 case FFT_PREV_GRAPH:
1552 if (fft.drawmode-- <= FFT_MIN_DM)
1553 fft.drawmode = FFT_MAX_DM-1;
1555 fft_setting_update(FFT_SETF_DM);
1556 fft_popupmsg(FFT_SETF_DM);
1557 break;
1559 case FFT_NEXT_GRAPH:
1560 if (++fft.drawmode >= FFT_MAX_DM)
1561 fft.drawmode = FFT_MIN_DM;
1563 fft_setting_update(FFT_SETF_DM);
1564 fft_popupmsg(FFT_SETF_DM);
1565 break;
1567 case FFT_AMP_SCALE:
1568 if (++fft.amp_scale >= FFT_MAX_AS)
1569 fft.amp_scale = FFT_MIN_AS;
1571 fft_setting_update(FFT_SETF_AS);
1572 fft_popupmsg(FFT_SETF_AS);
1573 break;
1575 #ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
1576 case FFT_FREQ_SCALE:
1577 if (++fft.freq_scale >= FFT_MAX_FS)
1578 fft.freq_scale = FFT_MIN_FS;
1580 fft_setting_update(FFT_SETF_FS);
1581 fft_popupmsg(FFT_SETF_FS);
1582 break;
1583 #endif
1584 case FFT_WINDOW:
1585 if(++fft.window_func >= FFT_MAX_WF)
1586 fft.window_func = FFT_MIN_WF;
1588 fft_setting_update(FFT_SETF_WF);
1589 fft_popupmsg(FFT_SETF_WF);
1590 break;
1592 default:
1593 exit_on_usb(button);
1594 break;
1598 return PLUGIN_OK;
1599 (void)parameter;