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
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)
207 #error No keymap defined!
210 #ifdef HAVE_TOUCHSCREEN
211 #ifndef FFT_PREV_GRAPH
212 # define FFT_PREV_GRAPH BUTTON_MIDLEFT
214 #ifndef FFT_NEXT_GRAPH
215 # define FFT_NEXT_GRAPH BUTTON_MIDRIGHT
217 #ifndef FFT_ORIENTATION
218 # define FFT_ORIENTATION BUTTON_CENTER
221 # define FFT_WINDOW BUTTON_TOPLEFT
224 # define FFT_SCALE BUTTON_TOPRIGHT
227 # define FFT_QUIT BUTTON_BOTTOMLEFT
231 #ifdef HAVE_LCD_COLOR
232 #include "pluginbitmaps/fft_colors.h"
235 #include "kiss_fftr.h"
236 #include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */
239 #if (LCD_WIDTH < LCD_HEIGHT)
240 #define LCD_SIZE LCD_HEIGHT
242 #define LCD_SIZE LCD_WIDTH
246 #define FFT_SIZE 2048 /* 512*4 */
247 #elif (LCD_SIZE < 1024)
248 #define FFT_SIZE 4096 /* 1024*4 */
250 #define FFT_SIZE 8192 /* 2048*4 */
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
278 #define MODES_COUNT 2
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;
292 bool orientation_vertical
;
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
324 #define WINDOW_COUNT 2
325 void apply_window_func(char mode
)
329 case 0: /* Hamming window */
332 for (i
= 0; i
< ARRAYSIZE_IN
; ++i
)
334 input
[i
] = Q15_MUL(input
[i
] << 15, HAMMING_COEFF
[i
]) >> 15;
338 case 1: /* Hann window */
341 for (i
= 0; i
< ARRAYSIZE_IN
; ++i
)
343 input
[i
] = Q15_MUL(input
[i
] << 15, HANN_COEFF
[i
]) >> 15;
350 /* Calculates the magnitudes from complex numbers and returns the maximum */
351 int32_t calc_magnitudes(bool logarithmic
)
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
;
366 tmp
= fsqrt64(tmp
, 16);
369 tmp
= get_log_value(tmp
& 0x7FFFFFFF);
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;
398 last_message
= (unsigned char*) message
;
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
);
421 grey_set_foreground(GREY_BLACK
);
422 grey_set_background(GREY_WHITE
);
425 switch (graph_settings
.mode
)
430 #ifdef HAVE_LCD_COLOR
431 rb
->lcd_clear_display();
433 grey_clear_display();
436 if (graph_settings
.orientation_vertical
)
437 draw_lines_vertical();
439 draw_lines_horizontal();
444 #ifdef HAVE_LCD_COLOR
445 rb
->lcd_clear_display();
447 grey_clear_display();
450 if(graph_settings
.orientation_vertical
)
451 draw_bars_vertical();
453 draw_bars_horizontal();
458 if(graph_settings
.orientation_vertical
)
459 draw_spectrogram_vertical();
461 draw_spectrogram_horizontal();
466 if (show_message
> 0)
468 /* We have a message to show */
471 #ifdef HAVE_LCD_COLOR
472 rb
->lcd_getstringsize(last_message
, &x
, &y
);
474 grey_getstringsize(last_message
, &x
, &y
);
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));
490 grey_scroll_left(graph_settings
.spectrogram
.column
-
491 (LCD_WIDTH
- x
- 1));
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
);
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
);
516 else if(last_message
!= 0)
518 if(graph_settings
.mode
!= 2)
520 /* These modes clear the screen themselves */
523 else /* Spectrogram mode - need to erase the popup */
526 #ifdef HAVE_LCD_COLOR
527 rb
->lcd_getstringsize(last_message
, &x
, &y
);
529 grey_getstringsize(last_message
, &x
, &y
);
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
543 graph_settings
.spectrogram
.row
-= y
;
544 if(graph_settings
.spectrogram
.row
< 0)
545 graph_settings
.spectrogram
.row
= 0;
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
);
555 grey_set_foreground(GREY_WHITE
);
556 grey_fillrect(LCD_WIDTH
-2-x
, 0, LCD_WIDTH
-1, y
);
557 grey_set_foreground(GREY_BLACK
);
564 #ifdef HAVE_LCD_COLOR
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
);
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 */
598 /* take the average of neighboring bins
599 * if we have to scale the graph horizontally */
600 int64_t bins_avg
= 0;
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 */
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;
630 y
= Q16_MUL(vfactor
, plot
[i
]) >> 16;
636 #ifdef HAVE_LCD_COLOR
637 rb
->lcd_vline(x
, LCD_HEIGHT
-1, LCD_HEIGHT
-y
-1);
639 grey_vline(x
, LCD_HEIGHT
-1, LCD_HEIGHT
-y
-1);
645 void draw_lines_horizontal(void)
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
);
661 if (new_max
== 0 || max
== 0) /* nothing to draw */
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;
673 for (i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
675 int32_t x
= 0, y
= 0;
677 y
= Q16_MUL(vfactor
, i
<< 16) + (1 << 15);
680 if (vfactor
< 65536) /* vfactor < 0, graph compression */
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;
701 y
= Q16_MUL(hfactor
, plot
[i
]) >> 16;
707 #ifdef HAVE_LCD_COLOR
708 rb
->lcd_hline(0, x
, y
);
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
)
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
];
743 if(bars_max
== 0) /* nothing to draw */
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
);
755 y
= Q16_MUL(vfactor
, bars_values
[i
]) + (1 << 15);
757 #ifdef HAVE_LCD_COLOR
758 rb
->lcd_fillrect(x
, LCD_HEIGHT
- y
- 1, width
, y
);
760 grey_fillrect(x
, LCD_HEIGHT
- y
- 1, width
, y
);
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
)
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
];
792 if(bars_max
== 0) /* nothing to draw */
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
);
804 x
= Q16_MUL(hfactor
, bars_values
[i
]) + (1 << 15);
807 #ifdef HAVE_LCD_COLOR
808 rb
->lcd_fillrect(0, y
, x
, height
);
810 grey_fillrect(0, y
, x
, height
);
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
)
822 ,grey_vals_per_val_log
= Q16_DIV(255 << 16, QLOG_MAX
),
823 grey_vals_per_val_lin
= Q16_DIV(255 << 16, QLIN_MAX
)
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();
840 grey_clear_display();
844 int i
, y
= LCD_HEIGHT
-1, count
= 0, rem_count
= 0;
846 bool added_extra_value
= false;
847 for(i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
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))
863 added_extra_value
= true;
866 if(count
>= scale_factor
)
868 if(added_extra_value
)
869 { ++count
; added_extra_value
= false; }
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;
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. */
886 if(graph_settings
.logarithmic
)
887 color
= Q16_MUL(avg
, grey_vals_per_val_log
) >> 16;
889 color
= Q16_MUL(avg
, grey_vals_per_val_lin
) >> 16;
896 #ifdef HAVE_LCD_COLOR
897 rb
->lcd_set_foreground(fft_colors
[color
]);
898 rb
->lcd_drawpixel(graph_settings
.spectrogram
.column
, y
);
900 grey_set_foreground(255 - color
);
901 grey_drawpixel(graph_settings
.spectrogram
.column
, y
);
912 if(graph_settings
.spectrogram
.column
!= LCD_WIDTH
-1)
913 graph_settings
.spectrogram
.column
++;
915 #ifdef HAVE_LCD_COLOR
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
)
929 ,grey_vals_per_val_log
= Q16_DIV(255 << 16, QLOG_MAX
),
930 grey_vals_per_val_lin
= Q16_DIV(255 << 16, QLIN_MAX
)
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();
947 grey_clear_display();
951 int i
, x
= 0, count
= 0, rem_count
= 0;
953 bool added_extra_value
= false;
954 for(i
= 0; i
< ARRAYSIZE_PLOT
; ++i
)
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))
970 added_extra_value
= true;
973 if(count
>= scale_factor
)
975 if(added_extra_value
)
976 { ++count
; added_extra_value
= false; }
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;
986 color
= Q16_MUL(avg
, colors_per_val_lin
) >> 16;
987 if(color
>= COLORS
) /* TODO same as _vertical */
993 if(graph_settings
.logarithmic
)
994 color
= Q16_MUL(avg
, grey_vals_per_val_log
) >> 16;
996 color
= Q16_MUL(avg
, grey_vals_per_val_lin
) >> 16;
1003 #ifdef HAVE_LCD_COLOR
1004 rb
->lcd_set_foreground(fft_colors
[color
]);
1005 rb
->lcd_drawpixel(x
, graph_settings
.spectrogram
.row
);
1007 grey_set_foreground(255 - color
);
1008 grey_drawpixel(x
, graph_settings
.spectrogram
.row
);
1019 if(graph_settings
.spectrogram
.row
!= LCD_HEIGHT
-1)
1020 graph_settings
.spectrogram
.row
++;
1022 #ifdef HAVE_LCD_COLOR
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
;
1037 int idx
= 0; /* offset in the buffer */
1038 int fft_idx
= 0; /* offset in input */
1041 rb
->mutex_lock(&input_mutex
);
1042 if(!input_thread_run
)
1045 value
= (kiss_fft_scalar
*) rb
->pcm_get_peak_buffer(&count
);
1047 if (value
== 0 || count
== 0)
1049 rb
->mutex_unlock(&input_mutex
);
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.*/
1063 left
= *(value
+ idx
);
1066 input
[fft_idx
] = left
;
1071 if (fft_idx
== ARRAYSIZE_IN
)
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
);
1084 enum plugin_status
plugin_start(const void* parameter
)
1087 if ((rb
->audio_status() & AUDIO_STATUS_PLAY
) == 0)
1089 rb
->splash(HZ
* 2, "No track playing. Exiting..");
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
;
1109 rb
->lcd_set_backdrop(NULL
);
1111 backlight_force_on();
1113 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1114 rb
->cpu_boost(true);
1117 rb
->mutex_init(&input_mutex
);
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
);
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
));
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
);
1154 apply_window_func(graph_settings
.window_func
);
1155 FFT_FFT(state
, input
, output
);
1159 draw(window_text
[graph_settings
.window_func
]);
1160 changed_window
= false;
1165 input_thread_has_data
= false;
1166 rb
->mutex_unlock(&input_mutex
);
1169 int button
= rb
->button_get(false);
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
]);
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
]);
1190 changed_window
= true;
1191 graph_settings
.window_func
++;
1192 if(graph_settings
.window_func
>= WINDOW_COUNT
)
1193 graph_settings
.window_func
= 0;
1197 graph_settings
.logarithmic
= !graph_settings
.logarithmic
;
1198 draw(scales_text
[graph_settings
.logarithmic
? 1 : 0]);
1201 case FFT_ORIENTATION
: {
1202 graph_settings
.orientation_vertical
= !graph_settings
.orientation_vertical
;
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);
1223 #ifndef HAVE_LCD_COLOR
1226 backlight_use_settings();