1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
23 #include "lib/helper.h"
28 #ifndef HAVE_LCD_COLOR
34 #ifndef HAVE_LCD_COLOR
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
182 #error No keymap defined!
185 #ifdef HAVE_LCD_COLOR
186 #include "pluginbitmaps/fft_colors.h"
189 #include "kiss_fftr.h"
190 #include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */
193 #define FFT_SIZE 2048
194 #define ARRAYSIZE_IN (FFT_SIZE)
195 #define ARRAYSIZE_OUT (FFT_SIZE/2)
196 #define ARRAYSIZE_PLOT (FFT_SIZE/4)
197 #define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE-1))
198 #define BUFSIZE_FFTR (BUFSIZE_FFT+sizeof(struct kiss_fftr_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE*3/2))
199 #define BUFSIZE BUFSIZE_FFTR
200 #define FFT_ALLOC kiss_fftr_alloc
201 #define FFT_FFT kiss_fftr
202 #define FFT_CFG kiss_fftr_cfg
204 #define __COEFF(type,size) type##_##size
205 #define _COEFF(x, y) __COEFF(x,y) /* force the preprocessor to evaluate FFT_SIZE) */
206 #define HANN_COEFF _COEFF(hann, FFT_SIZE)
207 #define HAMMING_COEFF _COEFF(hamming, FFT_SIZE)
209 /****************************** Globals ****************************/
211 static kiss_fft_scalar input
[ARRAYSIZE_IN
];
212 static kiss_fft_cpx output
[ARRAYSIZE_OUT
];
213 static int32_t plot
[ARRAYSIZE_PLOT
];
214 static char buffer
[BUFSIZE
];
216 #if LCD_DEPTH > 1 /* greyscale or color, enable spectrogram */
217 #define MODES_COUNT 3
219 #define MODES_COUNT 2
222 const unsigned char* modes_text
[] = { "Lines", "Bars", "Spectrogram" };
223 const unsigned char* scales_text
[] = { "Linear scale", "Logarithmic scale" };
224 const unsigned char* window_text
[] = { "Hamming window", "Hann window" };
226 struct mutex input_mutex
;
227 bool input_thread_run
= true;
228 bool input_thread_has_data
= false;
233 bool orientation_vertical
;
246 #define COLORS BMPWIDTH_fft_colors
248 /************************* End of globals *************************/
250 /************************* Math functions *************************/
251 #define QLOG_MAX 286286
252 #define QLIN_MAX 1534588906
253 #define QLN_10 float_q16(2.302585093)
254 #define LIN_MAX (QLIN_MAX >> 16)
256 /* Returns logarithmically scaled values in S15.16 format */
257 inline int32_t get_log_value(int32_t value
)
259 return Q16_DIV(fp16_log(value
), QLN_10
);
262 /* Apply window function to input
265 #define WINDOW_COUNT 2
266 void apply_window_func(char mode
)
270 case 0: /* Hamming window */
273 for (i
= 0; i
< ARRAYSIZE_IN
; ++i
)
275 input
[i
] = Q15_MUL(input
[i
] << 15, HAMMING_COEFF
[i
]) >> 15;
279 case 1: /* Hann window */
282 for (i
= 0; i
< ARRAYSIZE_IN
; ++i
)
284 input
[i
] = Q15_MUL(input
[i
] << 15, HANN_COEFF
[i
]) >> 15;
291 /* Calculates the magnitudes from complex numbers and returns the maximum */
292 int32_t calc_magnitudes(bool logarithmic
)
297 int32_t max
= -2147483647;
299 /* Calculate the magnitude, discarding the phase.
300 * The sum of the squares can easily overflow the 15-bit (s15.16)
301 * requirement for fsqrt, so we scale the data down */
302 for (i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
304 tmp
= output
[i
].r
* output
[i
].r
+ output
[i
].i
* output
[i
].i
;
307 tmp
= fsqrt64(tmp
, 16);
310 tmp
= get_log_value(tmp
& 0x7FFFFFFF);
319 /************************ End of math functions ***********************/
321 /********************* Plotting functions (modes) *********************/
322 void draw_lines_vertical(void);
323 void draw_lines_horizontal(void);
324 void draw_bars_vertical(void);
325 void draw_bars_horizontal(void);
326 void draw_spectrogram_vertical(void);
327 void draw_spectrogram_horizontal(void);
329 void draw(const unsigned char* message
)
331 static uint32_t show_message
= 0;
332 static unsigned char* last_message
= 0;
334 static char last_mode
= 0;
335 static bool last_orientation
= true, last_scale
= true;
339 last_message
= (unsigned char*) message
;
343 if(last_mode
!= graph_settings
.mode
)
345 last_mode
= graph_settings
.mode
;
346 graph_settings
.changed
.mode
= true;
348 if(last_scale
!= graph_settings
.logarithmic
)
350 last_scale
= graph_settings
.logarithmic
;
351 graph_settings
.changed
.scale
= true;
353 if(last_orientation
!= graph_settings
.orientation_vertical
)
355 last_orientation
= graph_settings
.orientation_vertical
;
356 graph_settings
.changed
.orientation
= true;
358 #ifdef HAVE_LCD_COLOR
359 rb
->lcd_set_foreground(LCD_DEFAULT_FG
);
360 rb
->lcd_set_background(LCD_DEFAULT_BG
);
362 grey_set_foreground(GREY_BLACK
);
363 grey_set_background(GREY_WHITE
);
366 switch (graph_settings
.mode
)
371 #ifdef HAVE_LCD_COLOR
372 rb
->lcd_clear_display();
374 grey_clear_display();
377 if (graph_settings
.orientation_vertical
)
378 draw_lines_vertical();
380 draw_lines_horizontal();
385 #ifdef HAVE_LCD_COLOR
386 rb
->lcd_clear_display();
388 grey_clear_display();
391 if(graph_settings
.orientation_vertical
)
392 draw_bars_vertical();
394 draw_bars_horizontal();
399 if(graph_settings
.orientation_vertical
)
400 draw_spectrogram_vertical();
402 draw_spectrogram_horizontal();
407 if (show_message
> 0)
409 /* We have a message to show */
412 #ifdef HAVE_LCD_COLOR
413 rb
->lcd_getstringsize(last_message
, &x
, &y
);
415 grey_getstringsize(last_message
, &x
, &y
);
417 /* x and y give the size of the box for the popup */
418 x
+= 6; /* 3 px of horizontal padding and */
419 y
+= 4; /* 2 px of vertical padding */
421 /* In vertical spectrogram mode, leave space for the popup
422 * before actually drawing it (if space is needed) */
423 if(graph_settings
.mode
== 2 &&
424 graph_settings
.orientation_vertical
&&
425 graph_settings
.spectrogram
.column
> LCD_WIDTH
-x
-2)
427 #ifdef HAVE_LCD_COLOR
428 xlcd_scroll_left(graph_settings
.spectrogram
.column
-
429 (LCD_WIDTH
- x
- 1));
431 grey_scroll_left(graph_settings
.spectrogram
.column
-
432 (LCD_WIDTH
- x
- 1));
434 graph_settings
.spectrogram
.column
= LCD_WIDTH
- x
- 2;
437 #ifdef HAVE_LCD_COLOR
438 rb
->lcd_set_foreground(LCD_DARKGRAY
);
439 rb
->lcd_fillrect(LCD_WIDTH
-1-x
, 0, LCD_WIDTH
-1, y
);
441 rb
->lcd_set_foreground(LCD_DEFAULT_FG
);
442 rb
->lcd_set_background(LCD_DARKGRAY
);
443 rb
->lcd_putsxy(LCD_WIDTH
-1-x
+3, 2, last_message
);
444 rb
->lcd_set_background(LCD_DEFAULT_BG
);
446 grey_set_foreground(GREY_LIGHTGRAY
);
447 grey_fillrect(LCD_WIDTH
-1-x
, 0, LCD_WIDTH
-1, y
);
449 grey_set_foreground(GREY_BLACK
);
450 grey_set_background(GREY_LIGHTGRAY
);
451 grey_putsxy(LCD_WIDTH
-1-x
+3, 2, last_message
);
452 grey_set_background(GREY_WHITE
);
457 else if(last_message
!= 0)
459 if(graph_settings
.mode
!= 2)
461 /* These modes clear the screen themselves */
464 else /* Spectrogram mode - need to erase the popup */
467 #ifdef HAVE_LCD_COLOR
468 rb
->lcd_getstringsize(last_message
, &x
, &y
);
470 grey_getstringsize(last_message
, &x
, &y
);
472 /* Recalculate the size */
473 x
+= 6; /* 3 px of horizontal padding and */
474 y
+= 4; /* 2 px of vertical padding */
476 if(!graph_settings
.orientation_vertical
)
478 /* In horizontal spectrogram mode, just scroll up by Y lines */
479 #ifdef HAVE_LCD_COLOR
484 graph_settings
.spectrogram
.row
-= y
;
485 if(graph_settings
.spectrogram
.row
< 0)
486 graph_settings
.spectrogram
.row
= 0;
490 /* In vertical spectrogram mode, erase the popup */
491 #ifdef HAVE_LCD_COLOR
492 rb
->lcd_set_foreground(LCD_DEFAULT_BG
);
493 rb
->lcd_fillrect(LCD_WIDTH
-2-x
, 0, LCD_WIDTH
-1, y
);
494 rb
->lcd_set_foreground(LCD_DEFAULT_FG
);
496 grey_set_foreground(GREY_WHITE
);
497 grey_fillrect(LCD_WIDTH
-2-x
, 0, LCD_WIDTH
-1, y
);
498 grey_set_foreground(GREY_BLACK
);
505 #ifdef HAVE_LCD_COLOR
511 graph_settings
.changed
.mode
= false;
512 graph_settings
.changed
.orientation
= false;
513 graph_settings
.changed
.scale
= false;
516 void draw_lines_vertical(void)
518 static int32_t max
= 0, vfactor
= 0, vfactor_count
= 0;
519 static const int32_t hfactor
=
520 Q16_DIV(LCD_WIDTH
<< 16, (ARRAYSIZE_PLOT
) << 16),
521 bins_per_pixel
= (ARRAYSIZE_PLOT
) / LCD_WIDTH
;
522 static bool old_scale
= true;
524 if (old_scale
!= graph_settings
.logarithmic
)
525 old_scale
= graph_settings
.logarithmic
, max
= 0; /* reset the graph on scaling mode change */
527 int32_t new_max
= calc_magnitudes(graph_settings
.logarithmic
);
532 vfactor
= Q16_DIV(LCD_HEIGHT
<< 16, max
); /* s15.16 */
533 vfactor_count
= Q16_DIV(vfactor
, bins_per_pixel
<< 16); /* s15.16 */
536 if (new_max
== 0 || max
== 0) /* nothing to draw */
539 /* take the average of neighboring bins
540 * if we have to scale the graph horizontally */
541 int64_t bins_avg
= 0;
544 for (i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
546 int32_t x
= 0, y
= 0;
548 x
= Q16_MUL(hfactor
, i
<< 16) >> 16;
549 //x = (x + (1 << 15)) >> 16;
551 if (hfactor
< 65536) /* hfactor < 0, graph compression */
556 /* fix the division by zero warning:
557 * bins_per_pixel is zero when the graph is expanding;
558 * execution won't even reach this point - this is a dummy constant
560 const int32_t div
= bins_per_pixel
> 0 ? bins_per_pixel
: 1;
561 if ((i
+ 1) % div
== 0)
563 y
= Q16_MUL(vfactor_count
, bins_avg
) >> 16;
571 y
= Q16_MUL(vfactor
, plot
[i
]) >> 16;
577 #ifdef HAVE_LCD_COLOR
578 rb
->lcd_vline(x
, LCD_HEIGHT
-1, LCD_HEIGHT
-y
-1);
580 grey_vline(x
, LCD_HEIGHT
-1, LCD_HEIGHT
-y
-1);
586 void draw_lines_horizontal(void)
590 static const int32_t vfactor
=
591 Q16_DIV(LCD_HEIGHT
<< 16, (ARRAYSIZE_PLOT
) << 16),
592 bins_per_pixel
= (ARRAYSIZE_PLOT
) / LCD_HEIGHT
;
594 if (graph_settings
.changed
.scale
)
595 max
= 0; /* reset the graph on scaling mode change */
597 int32_t new_max
= calc_magnitudes(graph_settings
.logarithmic
);
602 if (new_max
== 0 || max
== 0) /* nothing to draw */
607 hfactor
= Q16_DIV((LCD_WIDTH
- 1) << 16, max
); /* s15.16 */
609 /* take the average of neighboring bins
610 * if we have to scale the graph horizontally */
611 int64_t bins_avg
= 0;
614 for (i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
616 int32_t x
= 0, y
= 0;
618 y
= Q16_MUL(vfactor
, i
<< 16) + (1 << 15);
621 if (vfactor
< 65536) /* vfactor < 0, graph compression */
626 /* fix the division by zero warning:
627 * bins_per_pixel is zero when the graph is expanding;
628 * execution won't even reach this point - this is a dummy constant
630 const int32_t div
= bins_per_pixel
> 0 ? bins_per_pixel
: 1;
631 if ((i
+ 1) % div
== 0)
633 bins_avg
= Q16_DIV(bins_avg
, div
<< 16);
634 x
= Q16_MUL(hfactor
, bins_avg
) >> 16;
642 y
= Q16_MUL(hfactor
, plot
[i
]) >> 16;
648 #ifdef HAVE_LCD_COLOR
649 rb
->lcd_hline(0, x
, y
);
657 void draw_bars_vertical(void)
659 static const unsigned int bars
= 20, border
= 2, items
= ARRAYSIZE_PLOT
660 / bars
, width
= (LCD_WIDTH
- ((bars
- 1) * border
)) / bars
;
662 calc_magnitudes(graph_settings
.logarithmic
);
664 uint64_t bars_values
[bars
], bars_max
= 0, avg
= 0;
665 unsigned int i
, bars_idx
= 0;
666 for (i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
669 if ((i
+ 1) % items
== 0)
671 /* Calculate the average value and keep the fractional part
672 * for some added precision */
673 avg
= Q16_DIV(avg
, items
<< 16);
674 bars_values
[bars_idx
] = avg
;
676 if (bars_values
[bars_idx
] > bars_max
)
677 bars_max
= bars_values
[bars_idx
];
684 if(bars_max
== 0) /* nothing to draw */
687 /* Give the graph some headroom */
688 bars_max
= Q16_MUL(bars_max
, float_q16(1.1));
690 uint64_t vfactor
= Q16_DIV(LCD_HEIGHT
<< 16, bars_max
);
692 for (i
= 0; i
< bars
; ++i
)
694 int x
= (i
) * (border
+ width
);
696 y
= Q16_MUL(vfactor
, bars_values
[i
]) + (1 << 15);
698 #ifdef HAVE_LCD_COLOR
699 rb
->lcd_fillrect(x
, LCD_HEIGHT
- y
- 1, width
, y
);
701 grey_fillrect(x
, LCD_HEIGHT
- y
- 1, width
, y
);
706 void draw_bars_horizontal(void)
708 static const unsigned int bars
= 14, border
= 3, items
= ARRAYSIZE_PLOT
709 / bars
, height
= (LCD_HEIGHT
- ((bars
- 1) * border
)) / bars
;
711 calc_magnitudes(graph_settings
.logarithmic
);
713 int64_t bars_values
[bars
], bars_max
= 0, avg
= 0;
714 unsigned int i
, bars_idx
= 0;
715 for (i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
718 if ((i
+ 1) % items
== 0)
720 /* Calculate the average value and keep the fractional part
721 * for some added precision */
722 avg
= Q16_DIV(avg
, items
<< 16); /* s15.16 */
723 bars_values
[bars_idx
] = avg
;
725 if (bars_values
[bars_idx
] > bars_max
)
726 bars_max
= bars_values
[bars_idx
];
733 if(bars_max
== 0) /* nothing to draw */
736 /* Give the graph some headroom */
737 bars_max
= Q16_MUL(bars_max
, float_q16(1.1));
739 int64_t hfactor
= Q16_DIV(LCD_WIDTH
<< 16, bars_max
);
741 for (i
= 0; i
< bars
; ++i
)
743 int y
= (i
) * (border
+ height
);
745 x
= Q16_MUL(hfactor
, bars_values
[i
]) + (1 << 15);
748 #ifdef HAVE_LCD_COLOR
749 rb
->lcd_fillrect(0, y
, x
, height
);
751 grey_fillrect(0, y
, x
, height
);
756 void draw_spectrogram_vertical(void)
758 const int32_t scale_factor
= ARRAYSIZE_PLOT
/ LCD_HEIGHT
759 #ifdef HAVE_LCD_COLOR
760 ,colors_per_val_log
= Q16_DIV((COLORS
-1) << 16, QLOG_MAX
),
761 colors_per_val_lin
= Q16_DIV((COLORS
-1) << 16, QLIN_MAX
)
763 ,grey_vals_per_val_log
= Q16_DIV(255 << 16, QLOG_MAX
),
764 grey_vals_per_val_lin
= Q16_DIV(255 << 16, QLIN_MAX
)
768 const int32_t remaining_div
=
769 (ARRAYSIZE_PLOT
-scale_factor
*LCD_HEIGHT
) > 0 ?
770 ( Q16_DIV((scale_factor
*LCD_HEIGHT
) << 16,
771 (ARRAYSIZE_PLOT
-scale_factor
*LCD_HEIGHT
) << 16)
772 + (1<<15) ) >> 16 : 0;
774 calc_magnitudes(graph_settings
.logarithmic
);
775 if(graph_settings
.changed
.mode
|| graph_settings
.changed
.orientation
)
777 graph_settings
.spectrogram
.column
= 0;
778 #ifdef HAVE_LCD_COLOR
779 rb
->lcd_clear_display();
781 grey_clear_display();
785 int i
, y
= LCD_HEIGHT
-1, count
= 0, rem_count
= 0;
787 bool added_extra_value
= false;
788 for(i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
795 /* Kinda hacky - due to the rounding in scale_factor, we try to
796 * uniformly interweave the extra values in our calculations */
797 if(remaining_div
> 0 && rem_count
>= remaining_div
&&
798 i
< (ARRAYSIZE_PLOT
-1))
804 added_extra_value
= true;
807 if(count
>= scale_factor
)
809 if(added_extra_value
)
810 { ++count
; added_extra_value
= false; }
814 avg
= Q16_DIV(avg
, count
<< 16);
816 #ifdef HAVE_LCD_COLOR
817 if(graph_settings
.logarithmic
)
818 color
= Q16_MUL(avg
, colors_per_val_log
) >> 16;
820 color
= Q16_MUL(avg
, colors_per_val_lin
) >> 16;
821 if(color
>= COLORS
) /* TODO These happen because we don't normalize the values to be above 1 and log() returns negative numbers. I think. */
827 if(graph_settings
.logarithmic
)
828 color
= Q16_MUL(avg
, grey_vals_per_val_log
) >> 16;
830 color
= Q16_MUL(avg
, grey_vals_per_val_lin
) >> 16;
837 #ifdef HAVE_LCD_COLOR
838 rb
->lcd_set_foreground(fft_colors
[color
]);
839 rb
->lcd_drawpixel(graph_settings
.spectrogram
.column
, y
);
841 grey_set_foreground(255 - color
);
842 grey_drawpixel(graph_settings
.spectrogram
.column
, y
);
853 if(graph_settings
.spectrogram
.column
!= LCD_WIDTH
-1)
854 graph_settings
.spectrogram
.column
++;
856 #ifdef HAVE_LCD_COLOR
863 void draw_spectrogram_horizontal(void)
865 const int32_t scale_factor
= ARRAYSIZE_PLOT
/ LCD_WIDTH
866 #ifdef HAVE_LCD_COLOR
867 ,colors_per_val_log
= Q16_DIV((COLORS
-1) << 16, QLOG_MAX
),
868 colors_per_val_lin
= Q16_DIV((COLORS
-1) << 16, QLIN_MAX
)
870 ,grey_vals_per_val_log
= Q16_DIV(255 << 16, QLOG_MAX
),
871 grey_vals_per_val_lin
= Q16_DIV(255 << 16, QLIN_MAX
)
875 const int32_t remaining_div
=
876 (ARRAYSIZE_PLOT
-scale_factor
*LCD_WIDTH
) > 0 ?
877 ( Q16_DIV((scale_factor
*LCD_WIDTH
) << 16,
878 (ARRAYSIZE_PLOT
-scale_factor
*LCD_WIDTH
) << 16)
879 + (1<<15) ) >> 16 : 0;
881 calc_magnitudes(graph_settings
.logarithmic
);
882 if(graph_settings
.changed
.mode
|| graph_settings
.changed
.orientation
)
884 graph_settings
.spectrogram
.row
= 0;
885 #ifdef HAVE_LCD_COLOR
886 rb
->lcd_clear_display();
888 grey_clear_display();
892 int i
, x
= 0, count
= 0, rem_count
= 0;
894 bool added_extra_value
= false;
895 for(i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
902 /* Kinda hacky - due to the rounding in scale_factor, we try to
903 * uniformly interweave the extra values in our calculations */
904 if(remaining_div
> 0 && rem_count
>= remaining_div
&&
905 i
< (ARRAYSIZE_PLOT
-1))
911 added_extra_value
= true;
914 if(count
>= scale_factor
)
916 if(added_extra_value
)
917 { ++count
; added_extra_value
= false; }
921 avg
= Q16_DIV(avg
, count
<< 16);
923 #ifdef HAVE_LCD_COLOR
924 if(graph_settings
.logarithmic
)
925 color
= Q16_MUL(avg
, colors_per_val_log
) >> 16;
927 color
= Q16_MUL(avg
, colors_per_val_lin
) >> 16;
928 if(color
>= COLORS
) /* TODO same as _vertical */
934 if(graph_settings
.logarithmic
)
935 color
= Q16_MUL(avg
, grey_vals_per_val_log
) >> 16;
937 color
= Q16_MUL(avg
, grey_vals_per_val_lin
) >> 16;
944 #ifdef HAVE_LCD_COLOR
945 rb
->lcd_set_foreground(fft_colors
[color
]);
946 rb
->lcd_drawpixel(x
, graph_settings
.spectrogram
.row
);
948 grey_set_foreground(255 - color
);
949 grey_drawpixel(x
, graph_settings
.spectrogram
.row
);
960 if(graph_settings
.spectrogram
.row
!= LCD_HEIGHT
-1)
961 graph_settings
.spectrogram
.row
++;
963 #ifdef HAVE_LCD_COLOR
970 /********************* End of plotting functions (modes) *********************/
972 static long thread_stack
[DEFAULT_STACK_SIZE
/sizeof(long)];
973 void input_thread_entry(void)
975 kiss_fft_scalar
* value
;
976 kiss_fft_scalar left
;
978 int idx
= 0; /* offset in the buffer */
979 int fft_idx
= 0; /* offset in input */
982 rb
->mutex_lock(&input_mutex
);
983 if(!input_thread_run
)
986 value
= (kiss_fft_scalar
*) rb
->pcm_get_peak_buffer(&count
);
988 if (value
== 0 || count
== 0)
990 rb
->mutex_unlock(&input_mutex
);
993 /* This block can introduce discontinuities in our data. Meaning, the FFT
994 * will not be done a continuous segment of the signal. Which can be bad. Or not.
996 * Anyway, this is a demo, not a scientific tool. If you want accuracy, do a proper
997 * spectrum analysis.*/
1004 left
= *(value
+ idx
);
1007 input
[fft_idx
] = left
;
1010 if (fft_idx
== ARRAYSIZE_IN
)
1012 } while (idx
< count
);
1014 if(fft_idx
== ARRAYSIZE_IN
) /* there are cases when we don't have enough data to fill the buffer */
1015 input_thread_has_data
= true;
1017 rb
->mutex_unlock(&input_mutex
);
1023 enum plugin_status
plugin_start(const void* parameter
)
1026 if ((rb
->audio_status() & AUDIO_STATUS_PLAY
) == 0)
1028 rb
->splash(HZ
* 2, "No track playing. Exiting..");
1031 #ifndef HAVE_LCD_COLOR
1032 unsigned char *gbuf
;
1033 size_t gbuf_size
= 0;
1034 /* get the remainder of the plugin buffer */
1035 gbuf
= (unsigned char *) rb
->plugin_get_buffer(&gbuf_size
);
1037 /* initialize the greyscale buffer.*/
1038 if (!grey_init(gbuf
, gbuf_size
, GREY_ON_COP
| GREY_BUFFERED
,
1039 LCD_WIDTH
, LCD_HEIGHT
, NULL
))
1041 rb
->splash(HZ
, "Couldn't init greyscale display");
1042 return PLUGIN_ERROR
;
1048 rb
->lcd_set_backdrop(NULL
);
1050 backlight_force_on();
1052 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1053 rb
->cpu_boost(true);
1058 graph_settings
.mode
= 0;
1059 graph_settings
.logarithmic
= true;
1060 graph_settings
.orientation_vertical
= true;
1061 graph_settings
.window_func
= 0;
1062 graph_settings
.changed
.mode
= false;
1063 graph_settings
.changed
.scale
= false;
1064 graph_settings
.changed
.orientation
= false;
1065 graph_settings
.spectrogram
.row
= 0;
1066 graph_settings
.spectrogram
.column
= 0;
1068 bool changed_window
= false;
1070 size_t size
= sizeof(buffer
);
1071 FFT_CFG state
= FFT_ALLOC(FFT_SIZE
, 0, buffer
, &size
);
1075 DEBUGF("needed data: %i", (int) size
);
1076 return PLUGIN_ERROR
;
1079 unsigned int input_thread
= rb
->create_thread(&input_thread_entry
, thread_stack
, sizeof(thread_stack
), 0, "fft input thread" IF_PRIO(, PRIORITY_BACKGROUND
) IF_COP(, CPU
));
1083 rb
->mutex_lock(&input_mutex
);
1084 if(!input_thread_has_data
)
1086 /* Make sure the input thread has started before doing anything else */
1087 rb
->mutex_unlock(&input_mutex
);
1091 apply_window_func(graph_settings
.window_func
);
1092 FFT_FFT(state
, input
, output
);
1096 draw(window_text
[graph_settings
.window_func
]);
1097 changed_window
= false;
1102 input_thread_has_data
= false;
1103 rb
->mutex_unlock(&input_mutex
);
1106 int button
= rb
->button_get(false);
1112 case FFT_PREV_GRAPH
: {
1113 graph_settings
.mode
--;
1114 if (graph_settings
.mode
< 0)
1115 graph_settings
.mode
= MODES_COUNT
-1;
1116 draw(modes_text
[graph_settings
.mode
]);
1119 case FFT_NEXT_GRAPH
: {
1120 graph_settings
.mode
++;
1121 if (graph_settings
.mode
>= MODES_COUNT
)
1122 graph_settings
.mode
= 0;
1123 draw(modes_text
[graph_settings
.mode
]);
1127 changed_window
= true;
1128 graph_settings
.window_func
++;
1129 if(graph_settings
.window_func
>= WINDOW_COUNT
)
1130 graph_settings
.window_func
= 0;
1134 graph_settings
.logarithmic
= !graph_settings
.logarithmic
;
1135 draw(scales_text
[graph_settings
.logarithmic
? 1 : 0]);
1138 case FFT_ORIENTATION
: {
1139 graph_settings
.orientation_vertical
= !graph_settings
.orientation_vertical
;
1144 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
1145 return PLUGIN_USB_CONNECTED
;
1151 /* Handle our input thread. We haven't yield()'d since our last mutex_unlock, so we know we have the mutex */
1152 rb
->mutex_lock(&input_mutex
);
1153 input_thread_run
= false;
1154 rb
->mutex_unlock(&input_mutex
);
1155 rb
->thread_wait(input_thread
);
1157 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1158 rb
->cpu_boost(false);
1160 #ifndef HAVE_LCD_COLOR
1163 backlight_use_settings();