Fuzev2: revert r25741 and r25746
[kugel-rb.git] / apps / plugins / fft / fft.c
blobae071795571003a41a511271853c775703c69da5
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 "thread.h"
28 #ifndef HAVE_LCD_COLOR
29 #include "lib/grey.h"
30 #endif
32 PLUGIN_HEADER
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_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_SCALE BUTTON_UP
53 # define FFT_QUIT BUTTON_OFF
55 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
56 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
57 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
58 # define MINESWP_SCROLLWHEEL
59 # define FFT_PREV_GRAPH BUTTON_LEFT
60 # define FFT_NEXT_GRAPH BUTTON_RIGHT
61 # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
62 # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
63 # define FFT_SCALE BUTTON_MENU
64 # define FFT_QUIT (BUTTON_SELECT | BUTTON_MENU)
66 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
67 # define FFT_PREV_GRAPH BUTTON_LEFT
68 # define FFT_NEXT_GRAPH BUTTON_RIGHT
69 # define FFT_ORIENTATION BUTTON_SELECT
70 # define FFT_WINDOW BUTTON_PLAY
71 # define FFT_SCALE BUTTON_UP
72 # define FFT_QUIT BUTTON_POWER
74 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
75 # define FFT_PREV_GRAPH BUTTON_LEFT
76 # define FFT_NEXT_GRAPH BUTTON_RIGHT
77 # define FFT_SCALE BUTTON_UP
78 # define FFT_ORIENTATION BUTTON_SELECT
79 # define FFT_WINDOW BUTTON_A
80 # define FFT_QUIT BUTTON_POWER
82 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
83 # define FFT_PREV_GRAPH BUTTON_LEFT
84 # define FFT_NEXT_GRAPH BUTTON_RIGHT
85 # define FFT_ORIENTATION BUTTON_SELECT
86 # define FFT_WINDOW BUTTON_REC
87 # define FFT_SCALE BUTTON_UP
88 # define FFT_QUIT BUTTON_POWER
90 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
91 # define FFT_PREV_GRAPH BUTTON_LEFT
92 # define FFT_NEXT_GRAPH BUTTON_RIGHT
93 # define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
94 # define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
95 # define FFT_SCALE BUTTON_UP
96 # define FFT_QUIT BUTTON_POWER
98 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
99 # define FFT_PREV_GRAPH BUTTON_LEFT
100 # define FFT_NEXT_GRAPH BUTTON_RIGHT
101 # define FFT_ORIENTATION BUTTON_UP
102 # define FFT_WINDOW BUTTON_REC
103 # define FFT_SCALE BUTTON_SELECT
104 # define FFT_QUIT BUTTON_POWER
105 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
106 # define FFT_PREV_GRAPH BUTTON_LEFT
107 # define FFT_NEXT_GRAPH BUTTON_RIGHT
108 # define FFT_ORIENTATION BUTTON_UP
109 # define FFT_WINDOW BUTTON_DOWN
110 # define FFT_SCALE BUTTON_SELECT
111 # define FFT_QUIT BUTTON_POWER
112 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
113 # define FFT_PREV_GRAPH BUTTON_LEFT
114 # define FFT_NEXT_GRAPH BUTTON_RIGHT
115 # define FFT_ORIENTATION BUTTON_UP
116 # define FFT_WINDOW BUTTON_HOME
117 # define FFT_SCALE BUTTON_SELECT
118 # define FFT_QUIT BUTTON_POWER
120 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
121 # define FFT_PREV_GRAPH BUTTON_LEFT
122 # define FFT_NEXT_GRAPH BUTTON_RIGHT
123 # define FFT_ORIENTATION BUTTON_FF
124 # define FFT_WINDOW BUTTON_SCROLL_UP
125 # define FFT_SCALE BUTTON_REW
126 # define FFT_QUIT BUTTON_POWER
128 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
129 # define FFT_PREV_GRAPH BUTTON_LEFT
130 # define FFT_NEXT_GRAPH BUTTON_RIGHT
131 # define FFT_ORIENTATION BUTTON_MENU
132 # define FFT_WINDOW BUTTON_PREV
133 # define FFT_SCALE BUTTON_UP
134 # define FFT_QUIT BUTTON_BACK
136 #elif (CONFIG_KEYPAD == MROBE100_PAD)
137 # define FFT_PREV_GRAPH BUTTON_LEFT
138 # define FFT_NEXT_GRAPH BUTTON_RIGHT
139 # define FFT_ORIENTATION BUTTON_PLAY
140 # define FFT_WINDOW BUTTON_SELECT
141 # define FFT_SCALE BUTTON_UP
142 # define FFT_QUIT BUTTON_POWER
144 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
145 # define FFT_PREV_GRAPH BUTTON_RC_REW
146 # define FFT_NEXT_GRAPH BUTTON_RC_FF
147 # define FFT_ORIENTATION BUTTON_RC_MODE
148 # define FFT_WINDOW BUTTON_RC_PLAY
149 # define FFT_SCALE BUTTON_RC_VOL_UP
150 # define FFT_QUIT BUTTON_RC_REC
152 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
153 # define FFT_QUIT BUTTON_POWER
154 # define FFT_PREV_GRAPH BUTTON_PLUS
155 # define FFT_NEXT_GRAPH BUTTON_MINUS
157 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
158 # define FFT_PREV_GRAPH BUTTON_LEFT
159 # define FFT_NEXT_GRAPH BUTTON_RIGHT
160 # define FFT_ORIENTATION BUTTON_MENU
161 # define FFT_WINDOW BUTTON_SELECT
162 # define FFT_SCALE BUTTON_UP
163 # define FFT_QUIT BUTTON_BACK
165 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
166 # define FFT_PREV_GRAPH BUTTON_LEFT
167 # define FFT_NEXT_GRAPH BUTTON_RIGHT
168 # define FFT_ORIENTATION BUTTON_SELECT
169 # define FFT_WINDOW BUTTON_MENU
170 # define FFT_SCALE BUTTON_UP
171 # define FFT_QUIT BUTTON_POWER
173 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
174 # define FFT_PREV_GRAPH BUTTON_LEFT
175 # define FFT_NEXT_GRAPH BUTTON_RIGHT
176 # define FFT_ORIENTATION BUTTON_UP
177 # define FFT_WINDOW BUTTON_DOWN
178 # define FFT_SCALE BUTTON_FFWD
179 # define FFT_QUIT BUTTON_PLAY
181 #elif (CONFIG_KEYPAD == MROBE500_PAD)
182 # define FFT_QUIT BUTTON_POWER
184 #elif (CONFIG_KEYPAD == ONDAVX747_PAD)
185 # define FFT_QUIT BUTTON_POWER
187 #elif (CONFIG_KEYPAD == ONDAVX777_PAD)
188 # define FFT_QUIT BUTTON_POWER
190 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
191 # define FFT_PREV_GRAPH BUTTON_PREV
192 # define FFT_NEXT_GRAPH BUTTON_NEXT
193 # define FFT_ORIENTATION BUTTON_MENU
194 # define FFT_WINDOW BUTTON_OK
195 # define FFT_SCALE BUTTON_PLAY
196 # define FFT_QUIT BUTTON_REC
198 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
199 # define FFT_PREV_GRAPH BUTTON_PREV
200 # define FFT_NEXT_GRAPH BUTTON_NEXT
201 # define FFT_ORIENTATION BUTTON_REC
202 # define FFT_WINDOW BUTTON_SELECT
203 # define FFT_SCALE BUTTON_PLAY
204 # define FFT_QUIT (BUTTON_REC | BUTTON_PLAY)
206 #else
207 #error No keymap defined!
208 #endif
210 #ifdef HAVE_TOUCHSCREEN
211 #ifndef FFT_PREV_GRAPH
212 # define FFT_PREV_GRAPH BUTTON_MIDLEFT
213 #endif
214 #ifndef FFT_NEXT_GRAPH
215 # define FFT_NEXT_GRAPH BUTTON_MIDRIGHT
216 #endif
217 #ifndef FFT_ORIENTATION
218 # define FFT_ORIENTATION BUTTON_CENTER
219 #endif
220 #ifndef FFT_WINDOW
221 # define FFT_WINDOW BUTTON_TOPLEFT
222 #endif
223 #ifndef FFT_SCALE
224 # define FFT_SCALE BUTTON_TOPRIGHT
225 #endif
226 #ifndef FFT_QUIT
227 # define FFT_QUIT BUTTON_BOTTOMLEFT
228 #endif
229 #endif
231 #ifdef HAVE_LCD_COLOR
232 #include "pluginbitmaps/fft_colors.h"
233 #endif
235 #include "kiss_fftr.h"
236 #include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */
237 #include "const.h"
239 #if (LCD_WIDTH < LCD_HEIGHT)
240 #define LCD_SIZE LCD_HEIGHT
241 #else
242 #define LCD_SIZE LCD_WIDTH
243 #endif
245 #if (LCD_SIZE < 512)
246 #define FFT_SIZE 2048 /* 512*4 */
247 #elif (LCD_SIZE < 1024)
248 #define FFT_SIZE 4096 /* 1024*4 */
249 #else
250 #define FFT_SIZE 8192 /* 2048*4 */
251 #endif
253 #define ARRAYSIZE_IN (FFT_SIZE)
254 #define ARRAYSIZE_OUT (FFT_SIZE/2)
255 #define ARRAYSIZE_PLOT (FFT_SIZE/4)
256 #define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE-1))
257 #define BUFSIZE_FFTR (BUFSIZE_FFT+sizeof(struct kiss_fftr_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE*3/2))
258 #define BUFSIZE BUFSIZE_FFTR
259 #define FFT_ALLOC kiss_fftr_alloc
260 #define FFT_FFT kiss_fftr
261 #define FFT_CFG kiss_fftr_cfg
263 #define __COEFF(type,size) type##_##size
264 #define _COEFF(x, y) __COEFF(x,y) /* force the preprocessor to evaluate FFT_SIZE) */
265 #define HANN_COEFF _COEFF(hann, FFT_SIZE)
266 #define HAMMING_COEFF _COEFF(hamming, FFT_SIZE)
268 /****************************** Globals ****************************/
270 static kiss_fft_scalar input[ARRAYSIZE_IN];
271 static kiss_fft_cpx output[ARRAYSIZE_OUT];
272 static int32_t plot[ARRAYSIZE_PLOT];
273 static char buffer[BUFSIZE];
275 #if LCD_DEPTH > 1 /* greyscale or color, enable spectrogram */
276 #define MODES_COUNT 3
277 #else
278 #define MODES_COUNT 2
279 #endif
281 const unsigned char* modes_text[] = { "Lines", "Bars", "Spectrogram" };
282 const unsigned char* scales_text[] = { "Linear scale", "Logarithmic scale" };
283 const unsigned char* window_text[] = { "Hamming window", "Hann window" };
285 struct mutex input_mutex;
286 bool input_thread_run = true;
287 bool input_thread_has_data = false;
289 struct {
290 int32_t mode;
291 bool logarithmic;
292 bool orientation_vertical;
293 int window_func;
294 struct {
295 int column;
296 int row;
297 } spectrogram;
298 struct {
299 bool orientation;
300 bool mode;
301 bool scale;
302 } changed;
303 } graph_settings;
305 #define COLORS BMPWIDTH_fft_colors
307 /************************* End of globals *************************/
309 /************************* Math functions *************************/
310 #define QLOG_MAX 286286
311 #define QLIN_MAX 1534588906
312 #define QLN_10 float_q16(2.302585093)
313 #define LIN_MAX (QLIN_MAX >> 16)
315 /* Returns logarithmically scaled values in S15.16 format */
316 inline int32_t get_log_value(int32_t value)
318 return Q16_DIV(fp16_log(value), QLN_10);
321 /* Apply window function to input
322 * 0 - Hamming window
323 * 1 - Hann window */
324 #define WINDOW_COUNT 2
325 void apply_window_func(char mode)
327 switch(mode)
329 case 0: /* Hamming window */
331 size_t i;
332 for (i = 0; i < ARRAYSIZE_IN; ++i)
334 input[i] = Q15_MUL(input[i] << 15, HAMMING_COEFF[i]) >> 15;
336 break;
338 case 1: /* Hann window */
340 size_t i;
341 for (i = 0; i < ARRAYSIZE_IN; ++i)
343 input[i] = Q15_MUL(input[i] << 15, HANN_COEFF[i]) >> 15;
345 break;
350 /* Calculates the magnitudes from complex numbers and returns the maximum */
351 int32_t calc_magnitudes(bool logarithmic)
353 int64_t tmp;
354 size_t i;
356 int32_t max = -2147483647;
358 /* Calculate the magnitude, discarding the phase.
359 * The sum of the squares can easily overflow the 15-bit (s15.16)
360 * requirement for fsqrt, so we scale the data down */
361 for (i = 0; i < ARRAYSIZE_PLOT; ++i)
363 tmp = output[i].r * output[i].r + output[i].i * output[i].i;
364 tmp <<= 16;
366 tmp = fsqrt64(tmp, 16);
368 if (logarithmic)
369 tmp = get_log_value(tmp & 0x7FFFFFFF);
371 plot[i] = tmp;
373 if (plot[i] > max)
374 max = plot[i];
376 return max;
378 /************************ End of math functions ***********************/
380 /********************* Plotting functions (modes) *********************/
381 void draw_lines_vertical(void);
382 void draw_lines_horizontal(void);
383 void draw_bars_vertical(void);
384 void draw_bars_horizontal(void);
385 void draw_spectrogram_vertical(void);
386 void draw_spectrogram_horizontal(void);
388 void draw(const unsigned char* message)
390 static uint32_t show_message = 0;
391 static unsigned char* last_message = 0;
393 static char last_mode = 0;
394 static bool last_orientation = true, last_scale = true;
396 if (message != 0)
398 last_message = (unsigned char*) message;
399 show_message = 5;
402 if(last_mode != graph_settings.mode)
404 last_mode = graph_settings.mode;
405 graph_settings.changed.mode = true;
407 if(last_scale != graph_settings.logarithmic)
409 last_scale = graph_settings.logarithmic;
410 graph_settings.changed.scale = true;
412 if(last_orientation != graph_settings.orientation_vertical)
414 last_orientation = graph_settings.orientation_vertical;
415 graph_settings.changed.orientation = true;
417 #ifdef HAVE_LCD_COLOR
418 rb->lcd_set_foreground(LCD_DEFAULT_FG);
419 rb->lcd_set_background(LCD_DEFAULT_BG);
420 #else
421 grey_set_foreground(GREY_BLACK);
422 grey_set_background(GREY_WHITE);
423 #endif
425 switch (graph_settings.mode)
427 default:
428 case 0: {
430 #ifdef HAVE_LCD_COLOR
431 rb->lcd_clear_display();
432 #else
433 grey_clear_display();
434 #endif
436 if (graph_settings.orientation_vertical)
437 draw_lines_vertical();
438 else
439 draw_lines_horizontal();
440 break;
442 case 1: {
444 #ifdef HAVE_LCD_COLOR
445 rb->lcd_clear_display();
446 #else
447 grey_clear_display();
448 #endif
450 if(graph_settings.orientation_vertical)
451 draw_bars_vertical();
452 else
453 draw_bars_horizontal();
455 break;
457 case 2: {
458 if(graph_settings.orientation_vertical)
459 draw_spectrogram_vertical();
460 else
461 draw_spectrogram_horizontal();
462 break;
466 if (show_message > 0)
468 /* We have a message to show */
470 int x, y;
471 #ifdef HAVE_LCD_COLOR
472 rb->lcd_getstringsize(last_message, &x, &y);
473 #else
474 grey_getstringsize(last_message, &x, &y);
475 #endif
476 /* x and y give the size of the box for the popup */
477 x += 6; /* 3 px of horizontal padding and */
478 y += 4; /* 2 px of vertical padding */
480 /* In vertical spectrogram mode, leave space for the popup
481 * before actually drawing it (if space is needed) */
482 if(graph_settings.mode == 2 &&
483 graph_settings.orientation_vertical &&
484 graph_settings.spectrogram.column > LCD_WIDTH-x-2)
486 #ifdef HAVE_LCD_COLOR
487 xlcd_scroll_left(graph_settings.spectrogram.column -
488 (LCD_WIDTH - x - 1));
489 #else
490 grey_scroll_left(graph_settings.spectrogram.column -
491 (LCD_WIDTH - x - 1));
492 #endif
493 graph_settings.spectrogram.column = LCD_WIDTH - x - 2;
496 #ifdef HAVE_LCD_COLOR
497 rb->lcd_set_foreground(LCD_DARKGRAY);
498 rb->lcd_fillrect(LCD_WIDTH-1-x, 0, LCD_WIDTH-1, y);
500 rb->lcd_set_foreground(LCD_DEFAULT_FG);
501 rb->lcd_set_background(LCD_DARKGRAY);
502 rb->lcd_putsxy(LCD_WIDTH-1-x+3, 2, last_message);
503 rb->lcd_set_background(LCD_DEFAULT_BG);
504 #else
505 grey_set_foreground(GREY_LIGHTGRAY);
506 grey_fillrect(LCD_WIDTH-1-x, 0, LCD_WIDTH-1, y);
508 grey_set_foreground(GREY_BLACK);
509 grey_set_background(GREY_LIGHTGRAY);
510 grey_putsxy(LCD_WIDTH-1-x+3, 2, last_message);
511 grey_set_background(GREY_WHITE);
512 #endif
514 show_message--;
516 else if(last_message != 0)
518 if(graph_settings.mode != 2)
520 /* These modes clear the screen themselves */
521 last_message = 0;
523 else /* Spectrogram mode - need to erase the popup */
525 int x, y;
526 #ifdef HAVE_LCD_COLOR
527 rb->lcd_getstringsize(last_message, &x, &y);
528 #else
529 grey_getstringsize(last_message, &x, &y);
530 #endif
531 /* Recalculate the size */
532 x += 6; /* 3 px of horizontal padding and */
533 y += 4; /* 2 px of vertical padding */
535 if(!graph_settings.orientation_vertical)
537 /* In horizontal spectrogram mode, just scroll up by Y lines */
538 #ifdef HAVE_LCD_COLOR
539 xlcd_scroll_up(y);
540 #else
541 grey_scroll_up(y);
542 #endif
543 graph_settings.spectrogram.row -= y;
544 if(graph_settings.spectrogram.row < 0)
545 graph_settings.spectrogram.row = 0;
547 else
549 /* In vertical spectrogram mode, erase the popup */
550 #ifdef HAVE_LCD_COLOR
551 rb->lcd_set_foreground(LCD_DEFAULT_BG);
552 rb->lcd_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y);
553 rb->lcd_set_foreground(LCD_DEFAULT_FG);
554 #else
555 grey_set_foreground(GREY_WHITE);
556 grey_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y);
557 grey_set_foreground(GREY_BLACK);
558 #endif
561 last_message = 0;
564 #ifdef HAVE_LCD_COLOR
565 rb->lcd_update();
566 #else
567 grey_update();
568 #endif
570 graph_settings.changed.mode = false;
571 graph_settings.changed.orientation = false;
572 graph_settings.changed.scale = false;
575 void draw_lines_vertical(void)
577 static int32_t max = 0, vfactor = 0, vfactor_count = 0;
578 static const int32_t hfactor =
579 Q16_DIV(LCD_WIDTH << 16, (ARRAYSIZE_PLOT) << 16),
580 bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_WIDTH;
581 static bool old_scale = true;
583 if (old_scale != graph_settings.logarithmic)
584 old_scale = graph_settings.logarithmic, max = 0; /* reset the graph on scaling mode change */
586 int32_t new_max = calc_magnitudes(graph_settings.logarithmic);
588 if (new_max > max)
590 max = new_max;
591 vfactor = Q16_DIV(LCD_HEIGHT << 16, max); /* s15.16 */
592 vfactor_count = Q16_DIV(vfactor, bins_per_pixel << 16); /* s15.16 */
595 if (new_max == 0 || max == 0) /* nothing to draw */
596 return;
598 /* take the average of neighboring bins
599 * if we have to scale the graph horizontally */
600 int64_t bins_avg = 0;
601 bool draw = true;
602 int32_t i;
603 for (i = 0; i < ARRAYSIZE_PLOT; ++i)
605 int32_t x = 0, y = 0;
607 x = Q16_MUL(hfactor, i << 16) >> 16;
608 //x = (x + (1 << 15)) >> 16;
610 if (hfactor < 65536) /* hfactor < 0, graph compression */
612 draw = false;
613 bins_avg += plot[i];
615 /* fix the division by zero warning:
616 * bins_per_pixel is zero when the graph is expanding;
617 * execution won't even reach this point - this is a dummy constant
619 const int32_t div = bins_per_pixel > 0 ? bins_per_pixel : 1;
620 if ((i + 1) % div == 0)
622 y = Q16_MUL(vfactor_count, bins_avg) >> 16;
624 bins_avg = 0;
625 draw = true;
628 else
630 y = Q16_MUL(vfactor, plot[i]) >> 16;
631 draw = true;
634 if (draw)
636 #ifdef HAVE_LCD_COLOR
637 rb->lcd_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1);
638 #else
639 grey_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1);
640 #endif
645 void draw_lines_horizontal(void)
647 static int max = 0;
649 static const int32_t vfactor =
650 Q16_DIV(LCD_HEIGHT << 16, (ARRAYSIZE_PLOT) << 16),
651 bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_HEIGHT;
653 if (graph_settings.changed.scale)
654 max = 0; /* reset the graph on scaling mode change */
656 int32_t new_max = calc_magnitudes(graph_settings.logarithmic);
658 if (new_max > max)
659 max = new_max;
661 if (new_max == 0 || max == 0) /* nothing to draw */
662 return;
664 int32_t hfactor;
666 hfactor = Q16_DIV((LCD_WIDTH - 1) << 16, max); /* s15.16 */
668 /* take the average of neighboring bins
669 * if we have to scale the graph horizontally */
670 int64_t bins_avg = 0;
671 bool draw = true;
672 int32_t i;
673 for (i = 0; i < ARRAYSIZE_PLOT; ++i)
675 int32_t x = 0, y = 0;
677 y = Q16_MUL(vfactor, i << 16) + (1 << 15);
678 y >>= 16;
680 if (vfactor < 65536) /* vfactor < 0, graph compression */
682 draw = false;
683 bins_avg += plot[i];
685 /* fix the division by zero warning:
686 * bins_per_pixel is zero when the graph is expanding;
687 * execution won't even reach this point - this is a dummy constant
689 const int32_t div = bins_per_pixel > 0 ? bins_per_pixel : 1;
690 if ((i + 1) % div == 0)
692 bins_avg = Q16_DIV(bins_avg, div << 16);
693 x = Q16_MUL(hfactor, bins_avg) >> 16;
695 bins_avg = 0;
696 draw = true;
699 else
701 y = Q16_MUL(hfactor, plot[i]) >> 16;
702 draw = true;
705 if (draw)
707 #ifdef HAVE_LCD_COLOR
708 rb->lcd_hline(0, x, y);
709 #else
710 grey_hline(0, x, y);
711 #endif
716 void draw_bars_vertical(void)
718 static const unsigned int bars = 20, border = 2, items = ARRAYSIZE_PLOT
719 / bars, width = (LCD_WIDTH - ((bars - 1) * border)) / bars;
721 calc_magnitudes(graph_settings.logarithmic);
723 uint64_t bars_values[bars], bars_max = 0, avg = 0;
724 unsigned int i, bars_idx = 0;
725 for (i = 0; i < ARRAYSIZE_PLOT; ++i)
727 avg += plot[i];
728 if ((i + 1) % items == 0)
730 /* Calculate the average value and keep the fractional part
731 * for some added precision */
732 avg = Q16_DIV(avg, items << 16);
733 bars_values[bars_idx] = avg;
735 if (bars_values[bars_idx] > bars_max)
736 bars_max = bars_values[bars_idx];
738 bars_idx++;
739 avg = 0;
743 if(bars_max == 0) /* nothing to draw */
744 return;
746 /* Give the graph some headroom */
747 bars_max = Q16_MUL(bars_max, float_q16(1.1));
749 uint64_t vfactor = Q16_DIV(LCD_HEIGHT << 16, bars_max);
751 for (i = 0; i < bars; ++i)
753 int x = (i) * (border + width);
754 int y;
755 y = Q16_MUL(vfactor, bars_values[i]) + (1 << 15);
756 y >>= 16;
757 #ifdef HAVE_LCD_COLOR
758 rb->lcd_fillrect(x, LCD_HEIGHT - y - 1, width, y);
759 #else
760 grey_fillrect(x, LCD_HEIGHT - y - 1, width, y);
761 #endif
765 void draw_bars_horizontal(void)
767 static const unsigned int bars = 14, border = 3, items = ARRAYSIZE_PLOT
768 / bars, height = (LCD_HEIGHT - ((bars - 1) * border)) / bars;
770 calc_magnitudes(graph_settings.logarithmic);
772 int64_t bars_values[bars], bars_max = 0, avg = 0;
773 unsigned int i, bars_idx = 0;
774 for (i = 0; i < ARRAYSIZE_PLOT; ++i)
776 avg += plot[i];
777 if ((i + 1) % items == 0)
779 /* Calculate the average value and keep the fractional part
780 * for some added precision */
781 avg = Q16_DIV(avg, items << 16); /* s15.16 */
782 bars_values[bars_idx] = avg;
784 if (bars_values[bars_idx] > bars_max)
785 bars_max = bars_values[bars_idx];
787 bars_idx++;
788 avg = 0;
792 if(bars_max == 0) /* nothing to draw */
793 return;
795 /* Give the graph some headroom */
796 bars_max = Q16_MUL(bars_max, float_q16(1.1));
798 int64_t hfactor = Q16_DIV(LCD_WIDTH << 16, bars_max);
800 for (i = 0; i < bars; ++i)
802 int y = (i) * (border + height);
803 int x;
804 x = Q16_MUL(hfactor, bars_values[i]) + (1 << 15);
805 x >>= 16;
807 #ifdef HAVE_LCD_COLOR
808 rb->lcd_fillrect(0, y, x, height);
809 #else
810 grey_fillrect(0, y, x, height);
811 #endif
815 void draw_spectrogram_vertical(void)
817 const int32_t scale_factor = ARRAYSIZE_PLOT / LCD_HEIGHT
818 #ifdef HAVE_LCD_COLOR
819 ,colors_per_val_log = Q16_DIV((COLORS-1) << 16, QLOG_MAX),
820 colors_per_val_lin = Q16_DIV((COLORS-1) << 16, QLIN_MAX)
821 #else
822 ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX),
823 grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX)
824 #endif
827 const int32_t remaining_div =
828 (ARRAYSIZE_PLOT-scale_factor*LCD_HEIGHT) > 0 ?
829 ( Q16_DIV((scale_factor*LCD_HEIGHT) << 16,
830 (ARRAYSIZE_PLOT-scale_factor*LCD_HEIGHT) << 16)
831 + (1<<15) ) >> 16 : 0;
833 calc_magnitudes(graph_settings.logarithmic);
834 if(graph_settings.changed.mode || graph_settings.changed.orientation)
836 graph_settings.spectrogram.column = 0;
837 #ifdef HAVE_LCD_COLOR
838 rb->lcd_clear_display();
839 #else
840 grey_clear_display();
841 #endif
844 int i, y = LCD_HEIGHT-1, count = 0, rem_count = 0;
845 uint64_t avg = 0;
846 bool added_extra_value = false;
847 for(i = 0; i < ARRAYSIZE_PLOT; ++i)
849 if(plot[i] > 0)
850 avg += plot[i];
851 ++count;
852 ++rem_count;
854 /* Kinda hacky - due to the rounding in scale_factor, we try to
855 * uniformly interweave the extra values in our calculations */
856 if(remaining_div > 0 && rem_count >= remaining_div &&
857 i < (ARRAYSIZE_PLOT-1))
859 ++i;
860 if(plot[i] > 0)
861 avg += plot[i];
862 rem_count = 0;
863 added_extra_value = true;
866 if(count >= scale_factor)
868 if(added_extra_value)
869 { ++count; added_extra_value = false; }
871 int32_t color;
873 avg = Q16_DIV(avg, count << 16);
875 #ifdef HAVE_LCD_COLOR
876 if(graph_settings.logarithmic)
877 color = Q16_MUL(avg, colors_per_val_log) >> 16;
878 else
879 color = Q16_MUL(avg, colors_per_val_lin) >> 16;
880 if(color >= COLORS) /* TODO These happen because we don't normalize the values to be above 1 and log() returns negative numbers. I think. */
881 color = COLORS-1;
882 else if (color < 0)
883 color = 0;
885 #else
886 if(graph_settings.logarithmic)
887 color = Q16_MUL(avg, grey_vals_per_val_log) >> 16;
888 else
889 color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16;
890 if(color > 255)
891 color = 255;
892 else if (color < 0)
893 color = 0;
894 #endif
896 #ifdef HAVE_LCD_COLOR
897 rb->lcd_set_foreground(fft_colors[color]);
898 rb->lcd_drawpixel(graph_settings.spectrogram.column, y);
899 #else
900 grey_set_foreground(255 - color);
901 grey_drawpixel(graph_settings.spectrogram.column, y);
902 #endif
904 y--;
906 avg = 0;
907 count = 0;
909 if(y < 0)
910 break;
912 if(graph_settings.spectrogram.column != LCD_WIDTH-1)
913 graph_settings.spectrogram.column++;
914 else
915 #ifdef HAVE_LCD_COLOR
916 xlcd_scroll_left(1);
917 #else
918 grey_scroll_left(1);
919 #endif
922 void draw_spectrogram_horizontal(void)
924 const int32_t scale_factor = ARRAYSIZE_PLOT / LCD_WIDTH
925 #ifdef HAVE_LCD_COLOR
926 ,colors_per_val_log = Q16_DIV((COLORS-1) << 16, QLOG_MAX),
927 colors_per_val_lin = Q16_DIV((COLORS-1) << 16, QLIN_MAX)
928 #else
929 ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX),
930 grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX)
931 #endif
934 const int32_t remaining_div =
935 (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) > 0 ?
936 ( Q16_DIV((scale_factor*LCD_WIDTH) << 16,
937 (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) << 16)
938 + (1<<15) ) >> 16 : 0;
940 calc_magnitudes(graph_settings.logarithmic);
941 if(graph_settings.changed.mode || graph_settings.changed.orientation)
943 graph_settings.spectrogram.row = 0;
944 #ifdef HAVE_LCD_COLOR
945 rb->lcd_clear_display();
946 #else
947 grey_clear_display();
948 #endif
951 int i, x = 0, count = 0, rem_count = 0;
952 uint64_t avg = 0;
953 bool added_extra_value = false;
954 for(i = 0; i < ARRAYSIZE_PLOT; ++i)
956 if(plot[i] > 0)
957 avg += plot[i];
958 ++count;
959 ++rem_count;
961 /* Kinda hacky - due to the rounding in scale_factor, we try to
962 * uniformly interweave the extra values in our calculations */
963 if(remaining_div > 0 && rem_count >= remaining_div &&
964 i < (ARRAYSIZE_PLOT-1))
966 ++i;
967 if(plot[i] > 0)
968 avg += plot[i];
969 rem_count = 0;
970 added_extra_value = true;
973 if(count >= scale_factor)
975 if(added_extra_value)
976 { ++count; added_extra_value = false; }
978 int32_t color;
980 avg = Q16_DIV(avg, count << 16);
982 #ifdef HAVE_LCD_COLOR
983 if(graph_settings.logarithmic)
984 color = Q16_MUL(avg, colors_per_val_log) >> 16;
985 else
986 color = Q16_MUL(avg, colors_per_val_lin) >> 16;
987 if(color >= COLORS) /* TODO same as _vertical */
988 color = COLORS-1;
989 else if (color < 0)
990 color = 0;
992 #else
993 if(graph_settings.logarithmic)
994 color = Q16_MUL(avg, grey_vals_per_val_log) >> 16;
995 else
996 color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16;
997 if(color > 255)
998 color = 255;
999 else if (color < 0)
1000 color = 0;
1001 #endif
1003 #ifdef HAVE_LCD_COLOR
1004 rb->lcd_set_foreground(fft_colors[color]);
1005 rb->lcd_drawpixel(x, graph_settings.spectrogram.row);
1006 #else
1007 grey_set_foreground(255 - color);
1008 grey_drawpixel(x, graph_settings.spectrogram.row);
1009 #endif
1011 x++;
1013 avg = 0;
1014 count = 0;
1016 if(x >= LCD_WIDTH)
1017 break;
1019 if(graph_settings.spectrogram.row != LCD_HEIGHT-1)
1020 graph_settings.spectrogram.row++;
1021 else
1022 #ifdef HAVE_LCD_COLOR
1023 xlcd_scroll_up(1);
1024 #else
1025 grey_scroll_up(1);
1026 #endif
1029 /********************* End of plotting functions (modes) *********************/
1031 static long thread_stack[DEFAULT_STACK_SIZE/sizeof(long)];
1032 void input_thread_entry(void)
1034 kiss_fft_scalar * value;
1035 kiss_fft_scalar left;
1036 int count;
1037 int idx = 0; /* offset in the buffer */
1038 int fft_idx = 0; /* offset in input */
1039 while(true)
1041 rb->mutex_lock(&input_mutex);
1042 if(!input_thread_run)
1043 rb->thread_exit();
1045 value = (kiss_fft_scalar*) rb->pcm_get_peak_buffer(&count);
1047 if (value == 0 || count == 0)
1049 rb->mutex_unlock(&input_mutex);
1050 rb->yield();
1051 continue;
1052 /* This block can introduce discontinuities in our data. Meaning, the FFT
1053 * will not be done a continuous segment of the signal. Which can be bad. Or not.
1055 * Anyway, this is a demo, not a scientific tool. If you want accuracy, do a proper
1056 * spectrum analysis.*/
1058 else
1060 idx = fft_idx = 0;
1063 left = *(value + idx);
1064 idx += 2;
1066 input[fft_idx] = left;
1067 fft_idx++;
1068 input[fft_idx] = 0;
1069 fft_idx++;
1071 if (fft_idx == ARRAYSIZE_IN)
1072 break;
1073 } while (idx < count);
1075 if(fft_idx == ARRAYSIZE_IN) /* there are cases when we don't have enough data to fill the buffer */
1076 input_thread_has_data = true;
1078 rb->mutex_unlock(&input_mutex);
1079 rb->yield();
1084 enum plugin_status plugin_start(const void* parameter)
1086 (void) parameter;
1087 if ((rb->audio_status() & AUDIO_STATUS_PLAY) == 0)
1089 rb->splash(HZ * 2, "No track playing. Exiting..");
1090 return PLUGIN_OK;
1092 #ifndef HAVE_LCD_COLOR
1093 unsigned char *gbuf;
1094 size_t gbuf_size = 0;
1095 /* get the remainder of the plugin buffer */
1096 gbuf = (unsigned char *) rb->plugin_get_buffer(&gbuf_size);
1098 /* initialize the greyscale buffer.*/
1099 if (!grey_init(gbuf, gbuf_size, GREY_ON_COP | GREY_BUFFERED,
1100 LCD_WIDTH, LCD_HEIGHT, NULL))
1102 rb->splash(HZ, "Couldn't init greyscale display");
1103 return PLUGIN_ERROR;
1105 grey_show(true);
1106 #endif
1108 #if LCD_DEPTH > 1
1109 rb->lcd_set_backdrop(NULL);
1110 #endif
1111 backlight_force_on();
1113 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1114 rb->cpu_boost(true);
1115 #endif
1117 rb->mutex_init(&input_mutex);
1119 /* Defaults */
1120 bool run = true;
1121 graph_settings.mode = 0;
1122 graph_settings.logarithmic = true;
1123 graph_settings.orientation_vertical = true;
1124 graph_settings.window_func = 0;
1125 graph_settings.changed.mode = false;
1126 graph_settings.changed.scale = false;
1127 graph_settings.changed.orientation = false;
1128 graph_settings.spectrogram.row = 0;
1129 graph_settings.spectrogram.column = 0;
1131 bool changed_window = false;
1133 size_t size = sizeof(buffer);
1134 FFT_CFG state = FFT_ALLOC(FFT_SIZE, 0, buffer, &size);
1136 if (state == 0)
1138 DEBUGF("needed data: %i", (int) size);
1139 return PLUGIN_ERROR;
1142 unsigned int input_thread = rb->create_thread(&input_thread_entry, thread_stack, sizeof(thread_stack), 0, "fft input thread" IF_PRIO(, PRIORITY_BACKGROUND) IF_COP(, CPU));
1143 rb->yield();
1144 while (run)
1146 rb->mutex_lock(&input_mutex);
1147 if(!input_thread_has_data)
1149 /* Make sure the input thread has started before doing anything else */
1150 rb->mutex_unlock(&input_mutex);
1151 rb->yield();
1152 continue;
1154 apply_window_func(graph_settings.window_func);
1155 FFT_FFT(state, input, output);
1157 if(changed_window)
1159 draw(window_text[graph_settings.window_func]);
1160 changed_window = false;
1162 else
1163 draw(0);
1165 input_thread_has_data = false;
1166 rb->mutex_unlock(&input_mutex);
1167 rb->yield();
1169 int button = rb->button_get(false);
1170 switch (button)
1172 case FFT_QUIT:
1173 run = false;
1174 break;
1175 case FFT_PREV_GRAPH: {
1176 graph_settings.mode--;
1177 if (graph_settings.mode < 0)
1178 graph_settings.mode = MODES_COUNT-1;
1179 draw(modes_text[graph_settings.mode]);
1180 break;
1182 case FFT_NEXT_GRAPH: {
1183 graph_settings.mode++;
1184 if (graph_settings.mode >= MODES_COUNT)
1185 graph_settings.mode = 0;
1186 draw(modes_text[graph_settings.mode]);
1187 break;
1189 case FFT_WINDOW: {
1190 changed_window = true;
1191 graph_settings.window_func ++;
1192 if(graph_settings.window_func >= WINDOW_COUNT)
1193 graph_settings.window_func = 0;
1194 break;
1196 case FFT_SCALE: {
1197 graph_settings.logarithmic = !graph_settings.logarithmic;
1198 draw(scales_text[graph_settings.logarithmic ? 1 : 0]);
1199 break;
1201 case FFT_ORIENTATION: {
1202 graph_settings.orientation_vertical = !graph_settings.orientation_vertical;
1203 draw(0);
1204 break;
1206 default: {
1207 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1208 return PLUGIN_USB_CONNECTED;
1214 /* Handle our input thread. We haven't yield()'d since our last mutex_unlock, so we know we have the mutex */
1215 rb->mutex_lock(&input_mutex);
1216 input_thread_run = false;
1217 rb->mutex_unlock(&input_mutex);
1218 rb->thread_wait(input_thread);
1220 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1221 rb->cpu_boost(false);
1222 #endif
1223 #ifndef HAVE_LCD_COLOR
1224 grey_release();
1225 #endif
1226 backlight_use_settings();
1227 return PLUGIN_OK;