summaryrefslogtreecommitdiff
path: root/apps/plugins/fft
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/fft')
-rw-r--r--apps/plugins/fft/fft.c1688
-rw-r--r--apps/plugins/fft/kiss_fft.c8
-rw-r--r--apps/plugins/fft/kiss_fft.h2
-rw-r--r--apps/plugins/fft/kiss_fftr.c7
4 files changed, 923 insertions, 782 deletions
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c
index 713fa236f6..2b9e541f3e 100644
--- a/apps/plugins/fft/fft.c
+++ b/apps/plugins/fft/fft.c
@@ -23,7 +23,7 @@
23#include "lib/helper.h" 23#include "lib/helper.h"
24#include "lib/xlcd.h" 24#include "lib/xlcd.h"
25#include "math.h" 25#include "math.h"
26#include "thread.h" 26#include "fracmul.h"
27 27
28#ifndef HAVE_LCD_COLOR 28#ifndef HAVE_LCD_COLOR
29#include "lib/grey.h" 29#include "lib/grey.h"
@@ -36,172 +36,183 @@ GREY_INFO_STRUCT
36#endif 36#endif
37 37
38#if CONFIG_KEYPAD == ARCHOS_AV300_PAD 38#if CONFIG_KEYPAD == ARCHOS_AV300_PAD
39# define FFT_PREV_GRAPH BUTTON_LEFT 39# define FFT_PREV_GRAPH BUTTON_LEFT
40# define FFT_NEXT_GRAPH BUTTON_RIGHT 40# define FFT_NEXT_GRAPH BUTTON_RIGHT
41# define FFT_ORIENTATION BUTTON_F3 41# define FFT_ORIENTATION BUTTON_F3
42# define FFT_WINDOW BUTTON_F1 42# define FFT_WINDOW BUTTON_F1
43# define FFT_SCALE BUTTON_UP 43# define FFT_AMP_SCALE BUTTON_UP
44# define FFT_QUIT BUTTON_OFF 44# define FFT_QUIT BUTTON_OFF
45 45
46#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ 46#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
47 (CONFIG_KEYPAD == IRIVER_H300_PAD) 47 (CONFIG_KEYPAD == IRIVER_H300_PAD)
48# define FFT_PREV_GRAPH BUTTON_LEFT 48# define FFT_PREV_GRAPH BUTTON_LEFT
49# define FFT_NEXT_GRAPH BUTTON_RIGHT 49# define FFT_NEXT_GRAPH BUTTON_RIGHT
50# define FFT_ORIENTATION BUTTON_REC 50# define FFT_ORIENTATION BUTTON_REC
51# define FFT_WINDOW BUTTON_SELECT 51# define FFT_WINDOW BUTTON_SELECT
52# define FFT_SCALE BUTTON_UP 52# define FFT_AMP_SCALE BUTTON_UP
53# define FFT_QUIT BUTTON_OFF 53# define FFT_FREQ_SCALE BUTTON_DOWN
54# define FFT_QUIT BUTTON_OFF
54 55
55#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ 56#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
56 (CONFIG_KEYPAD == IPOD_3G_PAD) || \ 57 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
57 (CONFIG_KEYPAD == IPOD_1G2G_PAD) 58 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
58# define MINESWP_SCROLLWHEEL 59# define MINESWP_SCROLLWHEEL
59# define FFT_PREV_GRAPH BUTTON_LEFT 60# define FFT_PREV_GRAPH BUTTON_LEFT
60# define FFT_NEXT_GRAPH BUTTON_RIGHT 61# define FFT_NEXT_GRAPH BUTTON_RIGHT
61# define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT) 62# define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
62# define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT) 63# define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
63# define FFT_SCALE BUTTON_MENU 64# define FFT_AMP_SCALE BUTTON_MENU
64# define FFT_QUIT (BUTTON_SELECT | BUTTON_MENU) 65# define FFT_FREQ_SCALE BUTTON_PLAY
66# define FFT_QUIT (BUTTON_SELECT | BUTTON_MENU)
65 67
66#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) 68#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
67# define FFT_PREV_GRAPH BUTTON_LEFT 69# define FFT_PREV_GRAPH BUTTON_LEFT
68# define FFT_NEXT_GRAPH BUTTON_RIGHT 70# define FFT_NEXT_GRAPH BUTTON_RIGHT
69# define FFT_ORIENTATION BUTTON_SELECT 71# define FFT_ORIENTATION BUTTON_SELECT
70# define FFT_WINDOW BUTTON_PLAY 72# define FFT_WINDOW BUTTON_PLAY
71# define FFT_SCALE BUTTON_UP 73# define FFT_AMP_SCALE BUTTON_UP
72# define FFT_QUIT BUTTON_POWER 74# define FFT_FREQ_SCALE BUTTON_DOWN
75# define FFT_QUIT BUTTON_POWER
73 76
74#elif (CONFIG_KEYPAD == GIGABEAT_PAD) 77#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
75# define FFT_PREV_GRAPH BUTTON_LEFT 78# define FFT_PREV_GRAPH BUTTON_LEFT
76# define FFT_NEXT_GRAPH BUTTON_RIGHT 79# define FFT_NEXT_GRAPH BUTTON_RIGHT
77# define FFT_SCALE BUTTON_UP 80# define FFT_AMP_SCALE BUTTON_UP
81# define FFT_FREQ_SCALE BUTTON_DOWN
78# define FFT_ORIENTATION BUTTON_SELECT 82# define FFT_ORIENTATION BUTTON_SELECT
79# define FFT_WINDOW BUTTON_A 83# define FFT_WINDOW BUTTON_A
80# define FFT_QUIT BUTTON_POWER 84# define FFT_QUIT BUTTON_POWER
81 85
82#elif (CONFIG_KEYPAD == SANSA_E200_PAD) 86#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
83# define FFT_PREV_GRAPH BUTTON_LEFT 87# define FFT_PREV_GRAPH BUTTON_LEFT
84# define FFT_NEXT_GRAPH BUTTON_RIGHT 88# define FFT_NEXT_GRAPH BUTTON_RIGHT
85# define FFT_ORIENTATION BUTTON_SELECT 89# define FFT_ORIENTATION BUTTON_SELECT
86# define FFT_WINDOW BUTTON_REC 90# define FFT_WINDOW BUTTON_REC
87# define FFT_SCALE BUTTON_UP 91# define FFT_AMP_SCALE BUTTON_UP
88# define FFT_QUIT BUTTON_POWER 92# define FFT_FREQ_SCALE BUTTON_DOWN
93# define FFT_QUIT BUTTON_POWER
89 94
90#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD) 95#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
91# define FFT_PREV_GRAPH BUTTON_LEFT 96# define FFT_PREV_GRAPH BUTTON_LEFT
92# define FFT_NEXT_GRAPH BUTTON_RIGHT 97# define FFT_NEXT_GRAPH BUTTON_RIGHT
93# define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT) 98# define FFT_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
94# define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT) 99# define FFT_WINDOW (BUTTON_SELECT | BUTTON_RIGHT)
95# define FFT_SCALE BUTTON_UP 100# define FFT_AMP_SCALE BUTTON_UP
101# define FFT_FREQ_SCALE BUTTON_DOWN
96# define FFT_QUIT (BUTTON_HOME|BUTTON_REPEAT) 102# define FFT_QUIT (BUTTON_HOME|BUTTON_REPEAT)
97 103
98#elif (CONFIG_KEYPAD == SANSA_C200_PAD) 104#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
99# define FFT_PREV_GRAPH BUTTON_LEFT 105# define FFT_PREV_GRAPH BUTTON_LEFT
100# define FFT_NEXT_GRAPH BUTTON_RIGHT 106# define FFT_NEXT_GRAPH BUTTON_RIGHT
101# define FFT_ORIENTATION BUTTON_UP 107# define FFT_ORIENTATION BUTTON_UP
102# define FFT_WINDOW BUTTON_REC 108# define FFT_WINDOW BUTTON_REC
103# define FFT_SCALE BUTTON_SELECT 109# define FFT_AMP_SCALE BUTTON_SELECT
104# define FFT_QUIT BUTTON_POWER 110# define FFT_QUIT BUTTON_POWER
105#elif (CONFIG_KEYPAD == SANSA_M200_PAD) 111#elif (CONFIG_KEYPAD == SANSA_M200_PAD)
106# define FFT_PREV_GRAPH BUTTON_LEFT 112# define FFT_PREV_GRAPH BUTTON_LEFT
107# define FFT_NEXT_GRAPH BUTTON_RIGHT 113# define FFT_NEXT_GRAPH BUTTON_RIGHT
108# define FFT_ORIENTATION BUTTON_UP 114# define FFT_ORIENTATION BUTTON_UP
109# define FFT_WINDOW BUTTON_DOWN 115# define FFT_WINDOW BUTTON_DOWN
110# define FFT_SCALE BUTTON_SELECT 116# define FFT_AMP_SCALE BUTTON_SELECT
111# define FFT_QUIT BUTTON_POWER 117# define FFT_QUIT BUTTON_POWER
112#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD) 118#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
113# define FFT_PREV_GRAPH BUTTON_LEFT 119# define FFT_PREV_GRAPH BUTTON_LEFT
114# define FFT_NEXT_GRAPH BUTTON_RIGHT 120# define FFT_NEXT_GRAPH BUTTON_RIGHT
115# define FFT_ORIENTATION BUTTON_UP 121# define FFT_ORIENTATION BUTTON_UP
116# define FFT_WINDOW BUTTON_HOME 122# define FFT_WINDOW BUTTON_HOME
117# define FFT_SCALE BUTTON_SELECT 123# define FFT_AMP_SCALE BUTTON_SELECT
118# define FFT_QUIT BUTTON_POWER 124# define FFT_QUIT BUTTON_POWER
119 125
120#elif (CONFIG_KEYPAD == IRIVER_H10_PAD) 126#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
121# define FFT_PREV_GRAPH BUTTON_LEFT 127# define FFT_PREV_GRAPH BUTTON_LEFT
122# define FFT_NEXT_GRAPH BUTTON_RIGHT 128# define FFT_NEXT_GRAPH BUTTON_RIGHT
123# define FFT_ORIENTATION BUTTON_FF 129# define FFT_ORIENTATION BUTTON_FF
124# define FFT_WINDOW BUTTON_SCROLL_UP 130# define FFT_WINDOW BUTTON_SCROLL_UP
125# define FFT_SCALE BUTTON_REW 131# define FFT_AMP_SCALE BUTTON_REW
126# define FFT_QUIT BUTTON_POWER 132# define FFT_FREQ_SCALE BUTTON_PLAY
133# define FFT_QUIT BUTTON_POWER
127 134
128#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) 135#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
129# define FFT_PREV_GRAPH BUTTON_LEFT 136# define FFT_PREV_GRAPH BUTTON_LEFT
130# define FFT_NEXT_GRAPH BUTTON_RIGHT 137# define FFT_NEXT_GRAPH BUTTON_RIGHT
131# define FFT_ORIENTATION BUTTON_MENU 138# define FFT_ORIENTATION BUTTON_MENU
132# define FFT_WINDOW BUTTON_PREV 139# define FFT_WINDOW BUTTON_PREV
133# define FFT_SCALE BUTTON_UP 140# define FFT_AMP_SCALE BUTTON_UP
134# define FFT_QUIT BUTTON_BACK 141# define FFT_FREQ_SCALE BUTTON_DOWN
142# define FFT_QUIT BUTTON_BACK
135 143
136#elif (CONFIG_KEYPAD == MROBE100_PAD) 144#elif (CONFIG_KEYPAD == MROBE100_PAD)
137# define FFT_PREV_GRAPH BUTTON_LEFT 145# define FFT_PREV_GRAPH BUTTON_LEFT
138# define FFT_NEXT_GRAPH BUTTON_RIGHT 146# define FFT_NEXT_GRAPH BUTTON_RIGHT
139# define FFT_ORIENTATION BUTTON_PLAY 147# define FFT_ORIENTATION BUTTON_PLAY
140# define FFT_WINDOW BUTTON_SELECT 148# define FFT_WINDOW BUTTON_SELECT
141# define FFT_SCALE BUTTON_UP 149# define FFT_AMP_SCALE BUTTON_UP
142# define FFT_QUIT BUTTON_POWER 150# define FFT_FREQ_SCALE BUTTON_DOWN
151# define FFT_QUIT BUTTON_POWER
143 152
144#elif CONFIG_KEYPAD == IAUDIO_M3_PAD 153#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
145# define FFT_PREV_GRAPH BUTTON_RC_REW 154# define FFT_PREV_GRAPH BUTTON_RC_REW
146# define FFT_NEXT_GRAPH BUTTON_RC_FF 155# define FFT_NEXT_GRAPH BUTTON_RC_FF
147# define FFT_ORIENTATION BUTTON_RC_MODE 156# define FFT_ORIENTATION BUTTON_RC_MODE
148# define FFT_WINDOW BUTTON_RC_PLAY 157# define FFT_WINDOW BUTTON_RC_PLAY
149# define FFT_SCALE BUTTON_RC_VOL_UP 158# define FFT_AMP_SCALE BUTTON_RC_VOL_UP
150# define FFT_QUIT BUTTON_RC_REC 159# define FFT_QUIT BUTTON_RC_REC
151 160
152#elif (CONFIG_KEYPAD == COWON_D2_PAD) 161#elif (CONFIG_KEYPAD == COWON_D2_PAD)
153# define FFT_QUIT BUTTON_POWER 162# define FFT_QUIT BUTTON_POWER
154# define FFT_PREV_GRAPH BUTTON_PLUS 163# define FFT_PREV_GRAPH BUTTON_PLUS
155# define FFT_NEXT_GRAPH BUTTON_MINUS 164# define FFT_NEXT_GRAPH BUTTON_MINUS
156 165
157#elif CONFIG_KEYPAD == CREATIVEZVM_PAD 166#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
158# define FFT_PREV_GRAPH BUTTON_LEFT 167# define FFT_PREV_GRAPH BUTTON_LEFT
159# define FFT_NEXT_GRAPH BUTTON_RIGHT 168# define FFT_NEXT_GRAPH BUTTON_RIGHT
160# define FFT_ORIENTATION BUTTON_MENU 169# define FFT_ORIENTATION BUTTON_MENU
161# define FFT_WINDOW BUTTON_SELECT 170# define FFT_WINDOW BUTTON_SELECT
162# define FFT_SCALE BUTTON_UP 171# define FFT_AMP_SCALE BUTTON_UP
163# define FFT_QUIT BUTTON_BACK 172# define FFT_FREQ_SCALE BUTTON_DOWN
173# define FFT_QUIT BUTTON_BACK
164 174
165#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD 175#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
166# define FFT_PREV_GRAPH BUTTON_LEFT 176# define FFT_PREV_GRAPH BUTTON_LEFT
167# define FFT_NEXT_GRAPH BUTTON_RIGHT 177# define FFT_NEXT_GRAPH BUTTON_RIGHT
168# define FFT_ORIENTATION BUTTON_SELECT 178# define FFT_ORIENTATION BUTTON_SELECT
169# define FFT_WINDOW BUTTON_MENU 179# define FFT_WINDOW BUTTON_MENU
170# define FFT_SCALE BUTTON_UP 180# define FFT_AMP_SCALE BUTTON_UP
171# define FFT_QUIT BUTTON_POWER 181# define FFT_FREQ_SCALE BUTTON_DOWN
182# define FFT_QUIT BUTTON_POWER
172 183
173#elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD) 184#elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
174# define FFT_PREV_GRAPH BUTTON_LEFT 185# define FFT_PREV_GRAPH BUTTON_LEFT
175# define FFT_NEXT_GRAPH BUTTON_RIGHT 186# define FFT_NEXT_GRAPH BUTTON_RIGHT
176# define FFT_ORIENTATION BUTTON_UP 187# define FFT_ORIENTATION BUTTON_UP
177# define FFT_WINDOW BUTTON_DOWN 188# define FFT_WINDOW BUTTON_DOWN
178# define FFT_SCALE BUTTON_FFWD 189# define FFT_AMP_SCALE BUTTON_FFWD
179# define FFT_QUIT BUTTON_PLAY 190# define FFT_QUIT BUTTON_PLAY
180 191
181#elif (CONFIG_KEYPAD == MROBE500_PAD) 192#elif (CONFIG_KEYPAD == MROBE500_PAD)
182# define FFT_QUIT BUTTON_POWER 193# define FFT_QUIT BUTTON_POWER
183 194
184#elif (CONFIG_KEYPAD == ONDAVX747_PAD) 195#elif (CONFIG_KEYPAD == ONDAVX747_PAD)
185# define FFT_QUIT BUTTON_POWER 196# define FFT_QUIT BUTTON_POWER
186 197
187#elif (CONFIG_KEYPAD == ONDAVX777_PAD) 198#elif (CONFIG_KEYPAD == ONDAVX777_PAD)
188# define FFT_QUIT BUTTON_POWER 199# define FFT_QUIT BUTTON_POWER
189 200
190#elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD) 201#elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
191# define FFT_PREV_GRAPH BUTTON_PREV 202# define FFT_PREV_GRAPH BUTTON_PREV
192# define FFT_NEXT_GRAPH BUTTON_NEXT 203# define FFT_NEXT_GRAPH BUTTON_NEXT
193# define FFT_ORIENTATION BUTTON_MENU 204# define FFT_ORIENTATION BUTTON_MENU
194# define FFT_WINDOW BUTTON_OK 205# define FFT_WINDOW BUTTON_OK
195# define FFT_SCALE BUTTON_PLAY 206# define FFT_AMP_SCALE BUTTON_PLAY
196# define FFT_QUIT BUTTON_REC 207# define FFT_QUIT BUTTON_REC
197 208
198#elif CONFIG_KEYPAD == MPIO_HD200_PAD 209#elif CONFIG_KEYPAD == MPIO_HD200_PAD
199# define FFT_PREV_GRAPH BUTTON_PREV 210# define FFT_PREV_GRAPH BUTTON_PREV
200# define FFT_NEXT_GRAPH BUTTON_NEXT 211# define FFT_NEXT_GRAPH BUTTON_NEXT
201# define FFT_ORIENTATION BUTTON_REC 212# define FFT_ORIENTATION BUTTON_REC
202# define FFT_WINDOW BUTTON_SELECT 213# define FFT_WINDOW BUTTON_SELECT
203# define FFT_SCALE BUTTON_PLAY 214# define FFT_AMP_SCALE BUTTON_PLAY
204# define FFT_QUIT (BUTTON_REC | BUTTON_PLAY) 215# define FFT_QUIT (BUTTON_REC | BUTTON_PLAY)
205 216
206#else 217#else
207#error No keymap defined! 218#error No keymap defined!
@@ -209,24 +220,24 @@ GREY_INFO_STRUCT
209 220
210#ifdef HAVE_TOUCHSCREEN 221#ifdef HAVE_TOUCHSCREEN
211#ifndef FFT_PREV_GRAPH 222#ifndef FFT_PREV_GRAPH
212# define FFT_PREV_GRAPH BUTTON_MIDLEFT 223# define FFT_PREV_GRAPH BUTTON_MIDLEFT
213#endif 224#endif
214#ifndef FFT_NEXT_GRAPH 225#ifndef FFT_NEXT_GRAPH
215# define FFT_NEXT_GRAPH BUTTON_MIDRIGHT 226# define FFT_NEXT_GRAPH BUTTON_MIDRIGHT
216#endif 227#endif
217#ifndef FFT_ORIENTATION 228#ifndef FFT_ORIENTATION
218# define FFT_ORIENTATION BUTTON_CENTER 229# define FFT_ORIENTATION BUTTON_CENTER
219#endif 230#endif
220#ifndef FFT_WINDOW 231#ifndef FFT_WINDOW
221# define FFT_WINDOW BUTTON_TOPLEFT 232# define FFT_WINDOW BUTTON_TOPLEFT
222#endif 233#endif
223#ifndef FFT_SCALE 234#ifndef FFT_AMP_SCALE
224# define FFT_SCALE BUTTON_TOPRIGHT 235# define FFT_AMP_SCALE BUTTON_TOPRIGHT
225#endif 236#endif
226#ifndef FFT_QUIT 237#ifndef FFT_QUIT
227# define FFT_QUIT BUTTON_BOTTOMLEFT 238# define FFT_QUIT BUTTON_BOTTOMLEFT
228#endif
229#endif 239#endif
240#endif /* HAVE_TOUCHSCREEN */
230 241
231#ifdef HAVE_LCD_COLOR 242#ifdef HAVE_LCD_COLOR
232#include "pluginbitmaps/fft_colors.h" 243#include "pluginbitmaps/fft_colors.h"
@@ -250,9 +261,19 @@ GREY_INFO_STRUCT
250#define FFT_SIZE 8192 /* 2048*4 */ 261#define FFT_SIZE 8192 /* 2048*4 */
251#endif 262#endif
252 263
253#define ARRAYSIZE_IN (FFT_SIZE) 264#ifdef HAVE_LCD_COLOR
254#define ARRAYSIZE_OUT (FFT_SIZE/2) 265#define lcd_(fn) rb->lcd_##fn
255#define ARRAYSIZE_PLOT (FFT_SIZE/4) 266#define lcd_scroll_up xlcd_scroll_up
267#define lcd_scroll_left xlcd_scroll_left
268#else
269#define lcd_(fn) grey_##fn
270#define lcd_scroll_up grey_scroll_up
271#define lcd_scroll_left grey_scroll_left
272#endif
273
274#define ARRAYLEN_IN (FFT_SIZE)
275#define ARRAYLEN_OUT (FFT_SIZE/2)
276#define ARRAYLEN_PLOT ((FFT_SIZE/4)-1) /* -1 to ignore DC bin */
256#define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE-1)) 277#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)) 278#define BUFSIZE_FFTR (BUFSIZE_FFT+sizeof(struct kiss_fftr_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE*3/2))
258#define BUFSIZE BUFSIZE_FFTR 279#define BUFSIZE BUFSIZE_FFTR
@@ -267,117 +288,278 @@ GREY_INFO_STRUCT
267 288
268/****************************** Globals ****************************/ 289/****************************** Globals ****************************/
269 290
270static kiss_fft_scalar input[ARRAYSIZE_IN]; 291static volatile int output_head SHAREDBSS_ATTR = 0;
271static kiss_fft_cpx output[ARRAYSIZE_OUT]; 292static volatile int output_tail SHAREDBSS_ATTR = 0;
272static int32_t plot[ARRAYSIZE_PLOT]; 293/* cacheline-aligned buffers with COP, otherwise word-aligned */
273static char buffer[BUFSIZE]; 294
295#define CACHEALIGN_UP_SIZE(type, len) \
296 (CACHEALIGN_UP((len)*sizeof(type) + (sizeof(type)-1)) / sizeof(type))
297/* Shared */
298/* COP + CPU PCM */
299static kiss_fft_scalar input[CACHEALIGN_UP_SIZE(kiss_fft_scalar, ARRAYLEN_IN)]
300 CACHEALIGN_AT_LEAST_ATTR(4);
301/* CPU+COP */
302
303/* The result is nfft/2+1 complex frequency bins from DC to Nyquist. */
304static kiss_fft_cpx output[2][CACHEALIGN_UP_SIZE(kiss_fft_cpx, ARRAYLEN_OUT+1)]
305 __attribute__((aligned(4))) SHAREDBSS_ATTR;
306
307/* Unshared */
308/* COP */
309static char buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE)]
310 CACHEALIGN_AT_LEAST_ATTR(4);
311/* CPU */
312static int32_t plot_history[ARRAYLEN_PLOT];
313static int32_t plot[ARRAYLEN_PLOT];
314static struct
315{
316 int16_t bin; /* integer bin number */
317 uint16_t frac; /* interpolation fraction */
318} binlog[ARRAYLEN_PLOT] __attribute__((aligned(4)));
319
320static volatile bool fft_thread_run SHAREDDATA_ATTR = false;
274 321
275#define MODES_COUNT 3 322enum fft_window_func
323{
324 FFT_WF_FIRST = 0,
325 FFT_WF_HAMMING = 0,
326 FFT_WF_HANN,
327};
328#define FFT_WF_COUNT (FFT_WF_HANN+1)
329
330enum fft_display_mode
331{
332 FFT_DM_FIRST = 0,
333 FFT_DM_LINES = 0,
334 FFT_DM_BARS,
335 FFT_DM_SPECTROGRAPH,
336};
337#define FFT_DM_COUNT (FFT_DM_SPECTROGRAPH+1)
276 338
277const unsigned char* modes_text[] = { "Lines", "Bars", "Spectrogram" }; 339static const unsigned char* const modes_text[FFT_DM_COUNT] =
278const unsigned char* scales_text[] = { "Linear scale", "Logarithmic scale" }; 340{ "Lines", "Bars", "Spectrogram" };
279const unsigned char* window_text[] = { "Hamming window", "Hann window" };
280 341
281struct mutex input_mutex; 342static const unsigned char* const amp_scales_text[2] =
282bool input_thread_run = true; 343{ "Linear amplitude", "Logarithmic amplitude" };
283bool input_thread_has_data = false;
284 344
285struct { 345static const unsigned char* const freq_scales_text[2] =
286 int32_t mode; 346{ "Linear frequency", "Logarithmic frequency" };
287 bool logarithmic; 347
348static const unsigned char* const window_text[FFT_WF_COUNT] =
349{ "Hamming window", "Hann window" };
350
351static struct {
288 bool orientation_vertical; 352 bool orientation_vertical;
289 int window_func; 353 enum fft_display_mode mode;
290 struct { 354 bool logarithmic_amp;
291 int column; 355 bool logarithmic_freq;
292 int row; 356 enum fft_window_func window_func;
293 } spectrogram; 357 int spectrogram_pos; /* row or column - only used by one at a time */
294 struct { 358 union
295 bool orientation; 359 {
296 bool mode; 360 struct
297 bool scale; 361 {
362 bool orientation : 1;
363 bool mode : 1;
364 bool amp_scale : 1;
365 bool freq_scale : 1;
366 bool window_func : 1;
367 bool do_clear : 1;
368 };
369 bool clear_all; /* Write 'false' to clear all above */
298 } changed; 370 } changed;
299} graph_settings; 371} graph_settings SHAREDDATA_ATTR =
372{
373 /* Defaults */
374 .orientation_vertical = true,
375 .mode = FFT_DM_LINES,
376 .logarithmic_amp = true,
377 .logarithmic_freq = true,
378 .window_func = FFT_WF_HAMMING,
379 .spectrogram_pos = 0,
380 .changed = { .clear_all = false },
381};
300 382
301#define COLORS BMPWIDTH_fft_colors 383#ifdef HAVE_LCD_COLOR
384#define SHADES BMPWIDTH_fft_colors
385#define SPECTROGRAPH_PALETTE(index) (fft_colors[index])
386#else
387#define SHADES 256
388#define SPECTROGRAPH_PALETTE(index) (255 - (index))
389#endif
302 390
303/************************* End of globals *************************/ 391/************************* End of globals *************************/
304 392
305/************************* Math functions *************************/ 393/************************* Math functions *************************/
306#define QLOG_MAX 0x00040000
307#define QLIN_MAX 0x5B000000
308#define QLN_10 float_q16(2.302585093)
309#define LIN_MAX (QLIN_MAX >> 16)
310 394
311/* Returns logarithmically scaled values in S15.16 format */ 395/* Based on playing back a 0dB sweep tone */
312inline int32_t get_log_value(int32_t value) 396#define QLOG_MAX 0x000865EF
313{ 397/* fudge it a little or it's not very visbile */
314 return Q16_DIV(fp16_log(value), QLN_10); 398#define QLIN_MAX (0x00001157 >> 1)
315}
316 399
317/* Apply window function to input 400/* Apply window function to input */
318 * 0 - Hamming window 401void apply_window_func(enum fft_window_func mode)
319 * 1 - Hann window */
320#define WINDOW_COUNT 2
321void apply_window_func(char mode)
322{ 402{
403 int i;
404
323 switch(mode) 405 switch(mode)
324 { 406 {
325 case 0: /* Hamming window */ 407 case FFT_WF_HAMMING:
326 { 408 for(i = 0; i < ARRAYLEN_IN; ++i)
327 size_t i;
328 for (i = 0; i < ARRAYSIZE_IN; ++i)
329 { 409 {
330 input[i] = Q15_MUL(input[i] << 15, HAMMING_COEFF[i]) >> 15; 410 input[i] = (input[i] * HAMMING_COEFF[i] + 16384) >> 15;
331 } 411 }
332 break; 412 break;
333 } 413
334 case 1: /* Hann window */ 414 case FFT_WF_HANN:
335 { 415 for(i = 0; i < ARRAYLEN_IN; ++i)
336 size_t i;
337 for (i = 0; i < ARRAYSIZE_IN; ++i)
338 { 416 {
339 input[i] = Q15_MUL(input[i] << 15, HANN_COEFF[i]) >> 15; 417 input[i] = (input[i] * HANN_COEFF[i] + 16384) >> 15;
340 } 418 }
341 break; 419 break;
342 }
343 } 420 }
344} 421}
345 422
346/* Calculates the magnitudes from complex numbers and returns the maximum */ 423/* Calculates the magnitudes from complex numbers and returns the maximum */
347int32_t calc_magnitudes(bool logarithmic) 424int32_t calc_magnitudes(bool logarithmic_amp)
348{ 425{
349 /* A major assumption made when calculating the Q*MAX constants 426 /* A major assumption made when calculating the Q*MAX constants
350 * is that the maximum magnitude is 29 bits long. */ 427 * is that the maximum magnitude is 29 bits long. */
351 428 uint32_t max = 0;
352 uint32_t tmp; 429 kiss_fft_cpx *this_output = output[output_head] + 1; /* skip DC */
353 size_t i; 430 int i;
354
355 int32_t max = 0;
356 431
357 /* Calculate the magnitude, discarding the phase. */ 432 /* Calculate the magnitude, discarding the phase. */
358 for (i = 0; i < ARRAYSIZE_PLOT; ++i) 433 for(i = 0; i < ARRAYLEN_PLOT; ++i)
359 { 434 {
360 tmp = output[i].r * output[i].r + output[i].i * output[i].i; 435 int32_t re = this_output[i].r;
436 int32_t im = this_output[i].i;
437
438 uint32_t tmp = re*re + im*im;
361 439
362 if (tmp > 0x7FFFFFFF) tmp >>= 1; /* if our assumptions are correct, 440 if(tmp > 0)
363 this should never happen. It's just
364 a safeguard. */
365 if (tmp > 0)
366 { 441 {
367 tmp = fp_sqrt(tmp, 0); /* linear scaling, nothing 442 if(tmp > 0x7FFFFFFF) /* clip */
368 bad should happen */ 443 {
369 tmp <<= 16; 444 tmp = 0x7FFFFFFF; /* if our assumptions are correct,
370 if (logarithmic) 445 this should never happen. It's just
371 tmp = get_log_value(tmp);/* the log function 446 a safeguard. */
372 expects s15.16 values */ 447 }
448
449 if(logarithmic_amp)
450 {
451 if(tmp < 0x8000) /* be more precise */
452 {
453 /* ln(x ^ .5) = .5*ln(x) */
454 tmp = fp16_log(tmp << 16) >> 1;
455 }
456 else
457 {
458 tmp = isqrt(tmp); /* linear scaling, nothing
459 bad should happen */
460 tmp = fp16_log(tmp << 16); /* the log function
461 expects s15.16 values */
462 }
463 }
464 else
465 {
466 tmp = isqrt(tmp); /* linear scaling, nothing
467 bad should happen */
468 }
373 } 469 }
470
471 /* Length 2 moving average - last transform and this one */
472 tmp = (plot_history[i] + tmp) >> 1;
374 plot[i] = tmp; 473 plot[i] = tmp;
474 plot_history[i] = tmp;
375 475
376 if (plot[i] > max) 476 if(tmp > max)
377 max = plot[i]; 477 max = tmp;
378 } 478 }
479
379 return max; 480 return max;
380} 481}
482
483/* Move plot bins into a logarithmic scale by sliding them towards the
484 * Nyquist bin according to the translation in the binlog array. */
485void logarithmic_plot_translate(void)
486{
487 int i;
488
489 for(i = ARRAYLEN_PLOT-1; i > 0; --i)
490 {
491 int bin;
492 int s = binlog[i].bin;
493 int e = binlog[i-1].bin;
494 int frac = binlog[i].frac;
495
496 bin = plot[s];
497
498 if(frac)
499 {
500 /* slope < 1, Interpolate stretched bins (linear for now) */
501 int diff = plot[s+1] - bin;
502
503 do
504 {
505 plot[i] = bin + FRACMUL(frac << 15, diff);
506 frac = binlog[--i].frac;
507 }
508 while(frac);
509 }
510 else
511 {
512 /* slope > 1, Find peak of two or more bins */
513 while(--s > e)
514 {
515 int val = plot[s];
516
517 if (val > bin)
518 bin = val;
519 }
520 }
521
522 plot[i] = bin;
523 }
524}
525
526/* Calculates the translation for logarithmic plot bins */
527void logarithmic_plot_init(void)
528{
529 int i, j;
530 /*
531 * log: y = round(n * ln(x) / ln(n))
532 * anti: y = round(exp(x * ln(n) / n))
533 */
534 j = fp16_log((ARRAYLEN_PLOT - 1) << 16);
535 for(i = 0; i < ARRAYLEN_PLOT; ++i)
536 {
537 binlog[i].bin = (fp16_exp(i * j / (ARRAYLEN_PLOT - 1)) + 32768) >> 16;
538 }
539
540 /* setup fractions for interpolation of stretched bins */
541 for(i = 0; i < ARRAYLEN_PLOT-1; i = j)
542 {
543 j = i + 1;
544
545 /* stop when we have two different values */
546 while(binlog[j].bin == binlog[i].bin)
547 j++; /* if here, local slope of curve is < 1 */
548
549 if(j > i + 1)
550 {
551 /* distribute pieces evenly over stretched interval */
552 int diff = j - i;
553 int x = 0;
554 do
555 {
556 binlog[i].frac = (x++ << 16) / diff;
557 }
558 while(++i < j);
559 }
560 }
561}
562
381/************************ End of math functions ***********************/ 563/************************ End of math functions ***********************/
382 564
383/********************* Plotting functions (modes) *********************/ 565/********************* Plotting functions (modes) *********************/
@@ -388,53 +570,94 @@ void draw_bars_horizontal(void);
388void draw_spectrogram_vertical(void); 570void draw_spectrogram_vertical(void);
389void draw_spectrogram_horizontal(void); 571void draw_spectrogram_horizontal(void);
390 572
391void draw(const unsigned char* message) 573#ifdef HAVE_LCD_COLOR
392{ 574#define COLOR_DEFAULT_FG LCD_DEFAULT_FG
393 static uint32_t show_message = 0; 575#define COLOR_DEFAULT_BG LCD_DEFAULT_BG
394 static unsigned char* last_message = 0; 576#define COLOR_MESSAGE_FRAME LCD_RGBPACK(0xc6, 0x00, 0x00)
577#define COLOR_MESSAGE_BG LCD_BLACK
578#define COLOR_MESSAGE_FG LCD_WHITE
579#else
580#define COLOR_DEFAULT_FG GREY_BLACK
581#define COLOR_DEFAULT_BG GREY_WHITE
582#define COLOR_MESSAGE_FRAME GREY_DARKGRAY
583#define COLOR_MESSAGE_BG GREY_WHITE
584#define COLOR_MESSAGE_FG GREY_BLACK
585#endif
395 586
396 static char last_mode = 0; 587#define POPUP_HPADDING 3 /* 3 px of horizontal padding and */
397 static bool last_orientation = true, last_scale = true; 588#define POPUP_VPADDING 2 /* 2 px of vertical padding */
398 589
399 if (message != 0) 590void draw_message_string(const unsigned char *message, bool active)
591{
592 int x, y;
593 lcd_(getstringsize)(message, &x, &y);
594
595 /* x and y give the size of the box for the popup */
596 x += POPUP_HPADDING*2;
597 y += POPUP_VPADDING*2;
598
599 /* In vertical spectrogram mode, leave space for the popup
600 * before actually drawing it (if space is needed) */
601 if(active &&
602 graph_settings.mode == FFT_DM_SPECTROGRAPH &&
603 graph_settings.orientation_vertical &&
604 graph_settings.spectrogram_pos >= LCD_WIDTH - x)
400 { 605 {
401 last_message = (unsigned char*) message; 606 lcd_scroll_left(graph_settings.spectrogram_pos -
402 show_message = 5; 607 LCD_WIDTH + x);
608 graph_settings.spectrogram_pos = LCD_WIDTH - x - 1;
403 } 609 }
404 610
405 if(last_mode != graph_settings.mode) 611 lcd_(set_foreground)(COLOR_MESSAGE_FRAME);
612 lcd_(fillrect)(LCD_WIDTH - x, 0, LCD_WIDTH - 1, y);
613
614 lcd_(set_foreground)(COLOR_MESSAGE_FG);
615 lcd_(set_background)(COLOR_MESSAGE_BG);
616 lcd_(putsxy)(LCD_WIDTH - x + POPUP_HPADDING,
617 POPUP_VPADDING, message);
618 lcd_(set_foreground)(COLOR_DEFAULT_FG);
619 lcd_(set_background)(COLOR_DEFAULT_BG);
620}
621
622void draw(const unsigned char* message)
623{
624 static long show_message_tick = 0;
625 static const unsigned char* last_message = 0;
626
627 if(message != NULL)
406 { 628 {
407 last_mode = graph_settings.mode; 629 last_message = message;
408 graph_settings.changed.mode = true; 630 show_message_tick = (*rb->current_tick + HZ) | 1;
409 } 631 }
410 if(last_scale != graph_settings.logarithmic) 632
633 /* maybe take additional actions depending upon the changed setting */
634 if(graph_settings.changed.orientation)
411 { 635 {
412 last_scale = graph_settings.logarithmic; 636 graph_settings.changed.amp_scale = true;
413 graph_settings.changed.scale = true; 637 graph_settings.changed.do_clear = true;
414 } 638 }
415 if(last_orientation != graph_settings.orientation_vertical) 639
640 if(graph_settings.changed.mode)
416 { 641 {
417 last_orientation = graph_settings.orientation_vertical; 642 graph_settings.changed.amp_scale = true;
418 graph_settings.changed.orientation = true; 643 graph_settings.changed.do_clear = true;
419 } 644 }
420#ifdef HAVE_LCD_COLOR 645
421 rb->lcd_set_foreground(LCD_DEFAULT_FG); 646 if(graph_settings.changed.amp_scale)
422 rb->lcd_set_background(LCD_DEFAULT_BG); 647 memset(plot_history, 0, sizeof (plot_history));
423#else 648
424 grey_set_foreground(GREY_BLACK); 649 if(graph_settings.changed.freq_scale)
425 grey_set_background(GREY_WHITE); 650 graph_settings.changed.freq_scale = true;
426#endif 651
652 lcd_(set_foreground)(COLOR_DEFAULT_FG);
653 lcd_(set_background)(COLOR_DEFAULT_BG);
427 654
428 switch (graph_settings.mode) 655 switch (graph_settings.mode)
429 { 656 {
430 default: 657 default:
431 case 0: { 658 case FFT_DM_LINES: {
432 659
433#ifdef HAVE_LCD_COLOR 660 lcd_(clear_display)();
434 rb->lcd_clear_display();
435#else
436 grey_clear_display();
437#endif
438 661
439 if (graph_settings.orientation_vertical) 662 if (graph_settings.orientation_vertical)
440 draw_lines_vertical(); 663 draw_lines_vertical();
@@ -442,13 +665,9 @@ void draw(const unsigned char* message)
442 draw_lines_horizontal(); 665 draw_lines_horizontal();
443 break; 666 break;
444 } 667 }
445 case 1: { 668 case FFT_DM_BARS: {
446 669
447#ifdef HAVE_LCD_COLOR 670 lcd_(clear_display());
448 rb->lcd_clear_display();
449#else
450 grey_clear_display();
451#endif
452 671
453 if(graph_settings.orientation_vertical) 672 if(graph_settings.orientation_vertical)
454 draw_bars_vertical(); 673 draw_bars_vertical();
@@ -457,7 +676,14 @@ void draw(const unsigned char* message)
457 676
458 break; 677 break;
459 } 678 }
460 case 2: { 679 case FFT_DM_SPECTROGRAPH: {
680
681 if(graph_settings.changed.do_clear)
682 {
683 graph_settings.spectrogram_pos = 0;
684 lcd_(clear_display)();
685 }
686
461 if(graph_settings.orientation_vertical) 687 if(graph_settings.orientation_vertical)
462 draw_spectrogram_vertical(); 688 draw_spectrogram_vertical();
463 else 689 else
@@ -466,182 +692,129 @@ void draw(const unsigned char* message)
466 } 692 }
467 } 693 }
468 694
469 if (show_message > 0) 695 if(show_message_tick != 0)
470 { 696 {
471 /* We have a message to show */ 697 if(TIME_BEFORE(*rb->current_tick, show_message_tick))
472 698 {
473 int x, y; 699 /* We have a message to show */
474#ifdef HAVE_LCD_COLOR 700 draw_message_string(last_message, true);
475 rb->lcd_getstringsize(last_message, &x, &y); 701 }
476#else 702 else
477 grey_getstringsize(last_message, &x, &y); 703 {
478#endif 704 /* Stop drawing message */
479 /* x and y give the size of the box for the popup */ 705 show_message_tick = 0;
480 x += 6; /* 3 px of horizontal padding and */ 706 }
481 y += 4; /* 2 px of vertical padding */
482
483 /* In vertical spectrogram mode, leave space for the popup
484 * before actually drawing it (if space is needed) */
485 if(graph_settings.mode == 2 &&
486 graph_settings.orientation_vertical &&
487 graph_settings.spectrogram.column > LCD_WIDTH-x-2)
488 {
489#ifdef HAVE_LCD_COLOR
490 xlcd_scroll_left(graph_settings.spectrogram.column -
491 (LCD_WIDTH - x - 1));
492#else
493 grey_scroll_left(graph_settings.spectrogram.column -
494 (LCD_WIDTH - x - 1));
495#endif
496 graph_settings.spectrogram.column = LCD_WIDTH - x - 2;
497 }
498
499#ifdef HAVE_LCD_COLOR
500 rb->lcd_set_foreground(LCD_DARKGRAY);
501 rb->lcd_fillrect(LCD_WIDTH-1-x, 0, LCD_WIDTH-1, y);
502
503 rb->lcd_set_foreground(LCD_DEFAULT_FG);
504 rb->lcd_set_background(LCD_DARKGRAY);
505 rb->lcd_putsxy(LCD_WIDTH-1-x+3, 2, last_message);
506 rb->lcd_set_background(LCD_DEFAULT_BG);
507#else
508 grey_set_foreground(GREY_LIGHTGRAY);
509 grey_fillrect(LCD_WIDTH-1-x, 0, LCD_WIDTH-1, y);
510
511 grey_set_foreground(GREY_BLACK);
512 grey_set_background(GREY_LIGHTGRAY);
513 grey_putsxy(LCD_WIDTH-1-x+3, 2, last_message);
514 grey_set_background(GREY_WHITE);
515#endif
516
517 show_message--;
518 } 707 }
519 else if(last_message != 0) 708 else if(last_message != NULL)
520 { 709 {
521 if(graph_settings.mode != 2) 710 if(graph_settings.mode == FFT_DM_SPECTROGRAPH)
522 { 711 {
523 /* These modes clear the screen themselves */ 712 /* Spectrogram mode - need to erase the popup */
524 last_message = 0;
525 }
526 else /* Spectrogram mode - need to erase the popup */
527 {
528 int x, y; 713 int x, y;
529#ifdef HAVE_LCD_COLOR 714 lcd_(getstringsize)(last_message, &x, &y);
530 rb->lcd_getstringsize(last_message, &x, &y);
531#else
532 grey_getstringsize(last_message, &x, &y);
533#endif
534 /* Recalculate the size */ 715 /* Recalculate the size */
535 x += 6; /* 3 px of horizontal padding and */ 716 x += POPUP_HPADDING*2;
536 y += 4; /* 2 px of vertical padding */ 717 y += POPUP_VPADDING*2;
537 718
538 if(!graph_settings.orientation_vertical) 719 if(!graph_settings.orientation_vertical)
539 { 720 {
540 /* In horizontal spectrogram mode, just scroll up by Y lines */ 721 /* In horizontal spectrogram mode, just scroll up by Y lines */
541#ifdef HAVE_LCD_COLOR 722 lcd_scroll_up(y);
542 xlcd_scroll_up(y); 723 graph_settings.spectrogram_pos -= y;
543#else 724 if(graph_settings.spectrogram_pos < 0)
544 grey_scroll_up(y); 725 graph_settings.spectrogram_pos = 0;
545#endif
546 graph_settings.spectrogram.row -= y;
547 if(graph_settings.spectrogram.row < 0)
548 graph_settings.spectrogram.row = 0;
549 } 726 }
550 else 727 else
551 { 728 {
552 /* In vertical spectrogram mode, erase the popup */ 729 /* In vertical spectrogram mode, erase the popup */
553#ifdef HAVE_LCD_COLOR 730 lcd_(set_foreground)(COLOR_DEFAULT_BG);
554 rb->lcd_set_foreground(LCD_DEFAULT_BG); 731 lcd_(fillrect)(graph_settings.spectrogram_pos + 1, 0,
555 rb->lcd_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y); 732 LCD_WIDTH, y);
556 rb->lcd_set_foreground(LCD_DEFAULT_FG); 733 lcd_(set_foreground)(COLOR_DEFAULT_FG);
557#else
558 grey_set_foreground(GREY_WHITE);
559 grey_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y);
560 grey_set_foreground(GREY_BLACK);
561#endif
562 } 734 }
735 }
736 /* else These modes clear the screen themselves */
563 737
564 last_message = 0; 738 last_message = NULL;
565 }
566 } 739 }
567#ifdef HAVE_LCD_COLOR
568 rb->lcd_update();
569#else
570 grey_update();
571#endif
572 740
573 graph_settings.changed.mode = false; 741 lcd_(update)();
574 graph_settings.changed.orientation = false; 742
575 graph_settings.changed.scale = false; 743 graph_settings.changed.clear_all = false;
576} 744}
577 745
578void draw_lines_vertical(void) 746void draw_lines_vertical(void)
579{ 747{
580 static int32_t max = 0, vfactor = 0, vfactor_count = 0; 748 static int max = 0;
581 static const int32_t hfactor =
582 Q16_DIV(LCD_WIDTH << 16, (ARRAYSIZE_PLOT) << 16),
583 bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_WIDTH;
584 static bool old_scale = true;
585 749
586 if (old_scale != graph_settings.logarithmic) 750#if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
587 old_scale = graph_settings.logarithmic, max = 0; /* reset the graph on scaling mode change */ 751 const int offset = 0;
752 const int plotwidth = LCD_WIDTH;
753#else
754 const int offset = (LCD_HEIGHT - ARRAYLEN_PLOT) / 2;
755 const int plotwidth = ARRAYLEN_PLOT;
756#endif
588 757
589 int32_t new_max = calc_magnitudes(graph_settings.logarithmic); 758 int this_max;
759 int i, x;
590 760
591 if (new_max > max) 761 if(graph_settings.changed.amp_scale)
762 max = 0; /* reset the graph on scaling mode change */
763
764 this_max = calc_magnitudes(graph_settings.logarithmic_amp);
765
766 if(this_max == 0)
592 { 767 {
593 max = new_max; 768 lcd_(hline)(0, LCD_WIDTH - 1, LCD_HEIGHT - 1); /* Draw all "zero" */
594 vfactor = Q16_DIV(LCD_HEIGHT << 16, max); /* s15.16 */ 769 return;
595 vfactor_count = Q16_DIV(vfactor, bins_per_pixel << 16); /* s15.16 */
596 } 770 }
597 771
598 if (new_max == 0 || max == 0) /* nothing to draw */ 772 if(graph_settings.logarithmic_freq)
599 return; 773 logarithmic_plot_translate();
600 774
601 /* take the average of neighboring bins 775 /* take the maximum of neighboring bins if we have to scale the graph
602 * if we have to scale the graph horizontally */ 776 * horizontally */
603 int64_t bins_avg = 0; 777 if(LCD_WIDTH < ARRAYLEN_PLOT) /* graph compression */
604 bool draw = true;
605 int32_t i;
606 for (i = 0; i < ARRAYSIZE_PLOT; ++i)
607 { 778 {
608 int32_t x = 0, y = 0; 779 int bins_acc = LCD_WIDTH / 2;
609 780 int bins_max = 0;
610 x = Q16_MUL(hfactor, i << 16) >> 16; 781
611 //x = (x + (1 << 15)) >> 16; 782 i = 0, x = 0;
612 783
613 if (hfactor < 65536) /* hfactor < 0, graph compression */ 784 for(;;)
614 { 785 {
615 draw = false; 786 int bin = plot[i++];
616 bins_avg += plot[i]; 787
617 788 if(bin > bins_max)
618 /* fix the division by zero warning: 789 bins_max = bin;
619 * bins_per_pixel is zero when the graph is expanding; 790
620 * execution won't even reach this point - this is a dummy constant 791 bins_acc += LCD_WIDTH;
621 */ 792
622 const int32_t div = bins_per_pixel > 0 ? bins_per_pixel : 1; 793 if(bins_acc >= ARRAYLEN_PLOT)
623 if ((i + 1) % div == 0)
624 { 794 {
625 y = Q16_MUL(vfactor_count, bins_avg) >> 16; 795 plot[x] = bins_max;
796
797 if(bins_max > max)
798 max = bins_max;
626 799
627 bins_avg = 0; 800 if(++x >= LCD_WIDTH)
628 draw = true; 801 break;
802
803 bins_acc -= ARRAYLEN_PLOT;
804 bins_max = 0;
629 } 805 }
630 } 806 }
631 else 807 }
632 { 808 else
633 y = Q16_MUL(vfactor, plot[i]) >> 16; 809 {
634 draw = true; 810 if(this_max > max)
635 } 811 max = this_max;
812 }
636 813
637 if (draw) 814 for(x = 0; x < plotwidth; ++x)
638 { 815 {
639#ifdef HAVE_LCD_COLOR 816 int h = LCD_HEIGHT*plot[x] / max;
640 rb->lcd_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1); 817 lcd_(vline)(x + offset, LCD_HEIGHT - h, LCD_HEIGHT-1);
641#else
642 grey_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1);
643#endif
644 }
645 } 818 }
646} 819}
647 820
@@ -649,449 +822,411 @@ void draw_lines_horizontal(void)
649{ 822{
650 static int max = 0; 823 static int max = 0;
651 824
652 static const int32_t vfactor = 825#if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
653 Q16_DIV(LCD_HEIGHT << 16, (ARRAYSIZE_PLOT) << 16), 826 const int offset = 0;
654 bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_HEIGHT; 827 const int plotwidth = LCD_HEIGHT;
828#else
829 const int offset = (LCD_HEIGHT - ARRAYLEN_PLOT) / 2;
830 const int plotwidth = ARRAYLEN_PLOT;
831#endif
655 832
656 if (graph_settings.changed.scale) 833 int this_max;
657 max = 0; /* reset the graph on scaling mode change */ 834 int y;
658 835
659 int32_t new_max = calc_magnitudes(graph_settings.logarithmic); 836 if(graph_settings.changed.amp_scale)
837 max = 0; /* reset the graph on scaling mode change */
660 838
661 if (new_max > max) 839 this_max = calc_magnitudes(graph_settings.logarithmic_amp);
662 max = new_max;
663 840
664 if (new_max == 0 || max == 0) /* nothing to draw */ 841 if(this_max == 0)
842 {
843 lcd_(vline)(0, 0, LCD_HEIGHT-1); /* Draw all "zero" */
665 return; 844 return;
845 }
666 846
667 int32_t hfactor; 847 if(graph_settings.logarithmic_freq)
668 848 logarithmic_plot_translate();
669 hfactor = Q16_DIV((LCD_WIDTH - 1) << 16, max); /* s15.16 */
670 849
671 /* take the average of neighboring bins 850 /* take the maximum of neighboring bins if we have to scale the graph
672 * if we have to scale the graph horizontally */ 851 * horizontally */
673 int64_t bins_avg = 0; 852 if(LCD_HEIGHT < ARRAYLEN_PLOT) /* graph compression */
674 bool draw = true;
675 int32_t i;
676 for (i = 0; i < ARRAYSIZE_PLOT; ++i)
677 { 853 {
678 int32_t x = 0, y = 0; 854 int bins_acc = LCD_HEIGHT / 2;
855 int bins_max = 0;
856 int i = 0;
679 857
680 y = Q16_MUL(vfactor, i << 16) + (1 << 15); 858 y = 0;
681 y >>= 16;
682 859
683 if (vfactor < 65536) /* vfactor < 0, graph compression */ 860 for(;;)
684 { 861 {
685 draw = false; 862 int bin = plot[i++];
686 bins_avg += plot[i]; 863
687 864 if (bin > bins_max)
688 /* fix the division by zero warning: 865 bins_max = bin;
689 * bins_per_pixel is zero when the graph is expanding; 866
690 * execution won't even reach this point - this is a dummy constant 867 bins_acc += LCD_HEIGHT;
691 */ 868
692 const int32_t div = bins_per_pixel > 0 ? bins_per_pixel : 1; 869 if(bins_acc >= ARRAYLEN_PLOT)
693 if ((i + 1) % div == 0)
694 { 870 {
695 bins_avg = Q16_DIV(bins_avg, div << 16); 871 plot[y] = bins_max;
696 x = Q16_MUL(hfactor, bins_avg) >> 16; 872
873 if(bins_max > max)
874 max = bins_max;
697 875
698 bins_avg = 0; 876 if(++y >= LCD_HEIGHT)
699 draw = true; 877 break;
878
879 bins_acc -= ARRAYLEN_PLOT;
880 bins_max = 0;
700 } 881 }
701 } 882 }
702 else 883 }
703 { 884 else
704 y = Q16_MUL(hfactor, plot[i]) >> 16; 885 {
705 draw = true; 886 if(this_max > max)
706 } 887 max = this_max;
888 }
707 889
708 if (draw) 890 for(y = 0; y < plotwidth; ++y)
709 { 891 {
710#ifdef HAVE_LCD_COLOR 892 int w = LCD_WIDTH*plot[y] / max;
711 rb->lcd_hline(0, x, y); 893 lcd_(hline)(0, w - 1, y + offset);
712#else
713 grey_hline(0, x, y);
714#endif
715 }
716 } 894 }
717} 895}
718 896
719void draw_bars_vertical(void) 897void draw_bars_vertical(void)
720{ 898{
721 static const unsigned int bars = 20, border = 2, items = ARRAYSIZE_PLOT 899 static int max = 0;
722 / bars, width = (LCD_WIDTH - ((bars - 1) * border)) / bars;
723 900
724 calc_magnitudes(graph_settings.logarithmic); 901#if LCD_WIDTH < LCD_HEIGHT
902 const int bars = 15;
903#else
904 const int bars = 20;
905#endif
906 const int border = 2;
907 const int barwidth = LCD_WIDTH / (bars + border);
908 const int width = barwidth - border;
909 const int offset = (LCD_WIDTH - bars*barwidth) / 2;
725 910
726 uint64_t bars_values[bars], bars_max = 0, avg = 0; 911 if(graph_settings.changed.amp_scale)
727 unsigned int i, bars_idx = 0; 912 max = 0; /* reset the graph on scaling mode change */
728 for (i = 0; i < ARRAYSIZE_PLOT; ++i) 913
914 lcd_(hline)(0, LCD_WIDTH-1, LCD_HEIGHT-1); /* Draw baseline */
915
916 if(calc_magnitudes(graph_settings.logarithmic_amp) == 0)
917 return; /* nothing more to draw */
918
919 if(graph_settings.logarithmic_freq)
920 logarithmic_plot_translate();
921
922 int bins_acc = bars / 2;
923 int bins_max = 0;
924 int x = 0, i = 0;
925
926 for(;;)
729 { 927 {
730 avg += plot[i]; 928 int bin = plot[i++];
731 if ((i + 1) % items == 0)
732 {
733 /* Calculate the average value and keep the fractional part
734 * for some added precision */
735 avg = Q16_DIV(avg, items << 16);
736 bars_values[bars_idx] = avg;
737 929
738 if (bars_values[bars_idx] > bars_max) 930 if(bin > bins_max)
739 bars_max = bars_values[bars_idx]; 931 bins_max = bin;
740 932
741 bars_idx++; 933 bins_acc += bars;
742 avg = 0;
743 }
744 }
745 934
746 if(bars_max == 0) /* nothing to draw */ 935 if(bins_acc >= ARRAYLEN_PLOT)
747 return; 936 {
937 plot[x] = bins_max;
748 938
749 /* Give the graph some headroom */ 939 if(bins_max > max)
750 bars_max = Q16_MUL(bars_max, float_q16(1.1)); 940 max = bins_max;
751 941
752 uint64_t vfactor = Q16_DIV(LCD_HEIGHT << 16, bars_max); 942 if(++x >= bars)
943 break;
944
945 bins_acc -= ARRAYLEN_PLOT;
946 bins_max = 0;
947 }
948 }
753 949
754 for (i = 0; i < bars; ++i) 950 for(i = 0, x = offset; i < bars; ++i, x += barwidth)
755 { 951 {
756 int x = (i) * (border + width); 952 int h = LCD_HEIGHT * plot[i] / max;
757 int y; 953 lcd_(fillrect)(x, LCD_HEIGHT - h, width, h - 1);
758 y = Q16_MUL(vfactor, bars_values[i]) + (1 << 15);
759 y >>= 16;
760#ifdef HAVE_LCD_COLOR
761 rb->lcd_fillrect(x, LCD_HEIGHT - y - 1, width, y);
762#else
763 grey_fillrect(x, LCD_HEIGHT - y - 1, width, y);
764#endif
765 } 954 }
766} 955}
767 956
768void draw_bars_horizontal(void) 957void draw_bars_horizontal(void)
769{ 958{
770 static const unsigned int bars = 14, border = 3, items = ARRAYSIZE_PLOT 959 static int max = 0;
771 / bars, height = (LCD_HEIGHT - ((bars - 1) * border)) / bars;
772 960
773 calc_magnitudes(graph_settings.logarithmic); 961#if LCD_WIDTH < LCD_HEIGHT
962 const int bars = 20;
963#else
964 const int bars = 15;
965#endif
966 const int border = 2;
967 const int barwidth = LCD_HEIGHT / (bars + border);
968 const int height = barwidth - border;
969 const int offset = (LCD_HEIGHT - bars*barwidth) / 2;
774 970
775 int64_t bars_values[bars], bars_max = 0, avg = 0; 971 if(graph_settings.changed.amp_scale)
776 unsigned int i, bars_idx = 0; 972 max = 0; /* reset the graph on scaling mode change */
777 for (i = 0; i < ARRAYSIZE_PLOT; ++i) 973
974 lcd_(vline)(0, 0, LCD_HEIGHT-1); /* Draw baseline */
975
976 if(calc_magnitudes(graph_settings.logarithmic_amp) == 0)
977 return; /* nothing more to draw */
978
979 if(graph_settings.logarithmic_freq)
980 logarithmic_plot_translate();
981
982 int bins_acc = bars / 2;
983 int bins_max = 0;
984 int y = 0, i = 0;
985
986 for(;;)
778 { 987 {
779 avg += plot[i]; 988 int bin = plot[i++];
780 if ((i + 1) % items == 0)
781 {
782 /* Calculate the average value and keep the fractional part
783 * for some added precision */
784 avg = Q16_DIV(avg, items << 16); /* s15.16 */
785 bars_values[bars_idx] = avg;
786 989
787 if (bars_values[bars_idx] > bars_max) 990 if (bin > bins_max)
788 bars_max = bars_values[bars_idx]; 991 bins_max = bin;
789 992
790 bars_idx++; 993 bins_acc += bars;
791 avg = 0;
792 }
793 }
794 994
795 if(bars_max == 0) /* nothing to draw */ 995 if(bins_acc >= ARRAYLEN_PLOT)
796 return; 996 {
997 plot[y] = bins_max;
998
999 if(bins_max > max)
1000 max = bins_max;
797 1001
798 /* Give the graph some headroom */ 1002 if(++y >= bars)
799 bars_max = Q16_MUL(bars_max, float_q16(1.1)); 1003 break;
800 1004
801 int64_t hfactor = Q16_DIV(LCD_WIDTH << 16, bars_max); 1005 bins_acc -= ARRAYLEN_PLOT;
1006 bins_max = 0;
1007 }
1008 }
802 1009
803 for (i = 0; i < bars; ++i) 1010 for(i = 0, y = offset; i < bars; ++i, y += barwidth)
804 { 1011 {
805 int y = (i) * (border + height); 1012 int w = LCD_WIDTH * plot[i] / max;
806 int x; 1013 lcd_(fillrect)(1, y, w, height);
807 x = Q16_MUL(hfactor, bars_values[i]) + (1 << 15);
808 x >>= 16;
809
810#ifdef HAVE_LCD_COLOR
811 rb->lcd_fillrect(0, y, x, height);
812#else
813 grey_fillrect(0, y, x, height);
814#endif
815 } 1014 }
816} 1015}
817 1016
818void draw_spectrogram_vertical(void) 1017void draw_spectrogram_vertical(void)
819{ 1018{
820 const int32_t scale_factor = ARRAYSIZE_PLOT / LCD_HEIGHT 1019 const int32_t scale_factor = MIN(LCD_HEIGHT, ARRAYLEN_PLOT);
821#ifdef HAVE_LCD_COLOR
822 ,colors_per_val_log = Q16_DIV((COLORS-1) << 16, QLOG_MAX),
823 colors_per_val_lin = Q16_DIV((COLORS-1) << 16, QLIN_MAX)
824#else
825 ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX),
826 grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX)
827#endif
828 ;
829 1020
830 const int32_t remaining_div = 1021 calc_magnitudes(graph_settings.logarithmic_amp);
831 (ARRAYSIZE_PLOT-scale_factor*LCD_HEIGHT) > 0 ?
832 ( Q16_DIV((scale_factor*LCD_HEIGHT) << 16,
833 (ARRAYSIZE_PLOT-scale_factor*LCD_HEIGHT) << 16)
834 + (1<<15) ) >> 16 : 0;
835 1022
836 calc_magnitudes(graph_settings.logarithmic); 1023 if(graph_settings.logarithmic_freq)
837 if(graph_settings.changed.mode || graph_settings.changed.orientation) 1024 logarithmic_plot_translate();
838 { 1025
839 graph_settings.spectrogram.column = 0; 1026 int bins_acc = scale_factor / 2;
840#ifdef HAVE_LCD_COLOR 1027 int bins_max = 0;
841 rb->lcd_clear_display(); 1028 int y = 0, i = 0;
842#else
843 grey_clear_display();
844#endif
845 }
846 1029
847 int i, y = LCD_HEIGHT-1, count = 0, rem_count = 0; 1030 for(;;)
848 uint64_t avg = 0;
849 bool added_extra_value = false;
850 for(i = 0; i < ARRAYSIZE_PLOT; ++i)
851 { 1031 {
852 if(plot[i] > 0) 1032 int bin = plot[i++];
853 avg += plot[i];
854 ++count;
855 ++rem_count;
856
857 /* Kinda hacky - due to the rounding in scale_factor, we try to
858 * uniformly interweave the extra values in our calculations */
859 if(remaining_div > 0 && rem_count >= remaining_div &&
860 i < (ARRAYSIZE_PLOT-1))
861 {
862 ++i;
863 if(plot[i] > 0)
864 avg += plot[i];
865 rem_count = 0;
866 added_extra_value = true;
867 }
868 1033
869 if(count >= scale_factor) 1034 if(bin > bins_max)
870 { 1035 bins_max = bin;
871 if(added_extra_value)
872 { ++count; added_extra_value = false; }
873 1036
874 int32_t color; 1037 bins_acc += scale_factor;
875 1038
876 avg = Q16_DIV(avg, count << 16); 1039 if(bins_acc >= ARRAYLEN_PLOT)
1040 {
1041 unsigned index;
877 1042
878#ifdef HAVE_LCD_COLOR 1043 if(graph_settings.logarithmic_amp)
879 if(graph_settings.logarithmic) 1044 index = (SHADES-1)*bins_max / QLOG_MAX;
880 color = Q16_MUL(avg, colors_per_val_log) >> 16;
881 else 1045 else
882 color = Q16_MUL(avg, colors_per_val_lin) >> 16; 1046 index = (SHADES-1)*bins_max / QLIN_MAX;
883 if(color >= COLORS) /* TODO These happen because we don't normalize the values to be above 1 and log() returns negative numbers. I think. */
884 color = COLORS-1;
885 else if (color < 0)
886 color = 0;
887 1047
888#else 1048 /* These happen because we exaggerate the graph a little for
889 if(graph_settings.logarithmic) 1049 * linear mode */
890 color = Q16_MUL(avg, grey_vals_per_val_log) >> 16; 1050 if(index >= SHADES)
891 else 1051 index = SHADES-1;
892 color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16;
893 if(color > 255)
894 color = 255;
895 else if (color < 0)
896 color = 0;
897#endif
898 1052
899#ifdef HAVE_LCD_COLOR 1053 lcd_(set_foreground)(SPECTROGRAPH_PALETTE(index));
900 rb->lcd_set_foreground(fft_colors[color]); 1054 lcd_(drawpixel)(graph_settings.spectrogram_pos,
901 rb->lcd_drawpixel(graph_settings.spectrogram.column, y); 1055 scale_factor-1 - y);
902#else
903 grey_set_foreground(255 - color);
904 grey_drawpixel(graph_settings.spectrogram.column, y);
905#endif
906 1056
907 y--; 1057 if(++y >= LCD_HEIGHT)
1058 break;
908 1059
909 avg = 0; 1060 bins_acc -= ARRAYLEN_PLOT;
910 count = 0; 1061 bins_max = 0;
911 } 1062 }
912 if(y < 0)
913 break;
914 } 1063 }
915 if(graph_settings.spectrogram.column != LCD_WIDTH-1) 1064
916 graph_settings.spectrogram.column++; 1065 if(graph_settings.spectrogram_pos < LCD_WIDTH-1)
1066 graph_settings.spectrogram_pos++;
917 else 1067 else
918#ifdef HAVE_LCD_COLOR 1068 lcd_scroll_left(1);
919 xlcd_scroll_left(1);
920#else
921 grey_scroll_left(1);
922#endif
923} 1069}
924 1070
925void draw_spectrogram_horizontal(void) 1071void draw_spectrogram_horizontal(void)
926{ 1072{
927 const int32_t scale_factor = ARRAYSIZE_PLOT / LCD_WIDTH 1073 const int32_t scale_factor = MIN(LCD_WIDTH, ARRAYLEN_PLOT);
928#ifdef HAVE_LCD_COLOR
929 ,colors_per_val_log = Q16_DIV((COLORS-1) << 16, QLOG_MAX),
930 colors_per_val_lin = Q16_DIV((COLORS-1) << 16, QLIN_MAX)
931#else
932 ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX),
933 grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX)
934#endif
935 ;
936 1074
937 const int32_t remaining_div = 1075 calc_magnitudes(graph_settings.logarithmic_amp);
938 (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) > 0 ?
939 ( Q16_DIV((scale_factor*LCD_WIDTH) << 16,
940 (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) << 16)
941 + (1<<15) ) >> 16 : 0;
942 1076
943 calc_magnitudes(graph_settings.logarithmic); 1077 if(graph_settings.logarithmic_freq)
944 if(graph_settings.changed.mode || graph_settings.changed.orientation) 1078 logarithmic_plot_translate();
945 { 1079
946 graph_settings.spectrogram.row = 0; 1080 int bins_acc = scale_factor / 2;
947#ifdef HAVE_LCD_COLOR 1081 int bins_max = 0;
948 rb->lcd_clear_display(); 1082 int x = 0, i = 0;
949#else
950 grey_clear_display();
951#endif
952 }
953 1083
954 int i, x = 0, count = 0, rem_count = 0; 1084 for(;;)
955 uint64_t avg = 0;
956 bool added_extra_value = false;
957 for(i = 0; i < ARRAYSIZE_PLOT; ++i)
958 { 1085 {
959 if(plot[i] > 0) 1086 int bin = plot[i++];
960 avg += plot[i];
961 ++count;
962 ++rem_count;
963
964 /* Kinda hacky - due to the rounding in scale_factor, we try to
965 * uniformly interweave the extra values in our calculations */
966 if(remaining_div > 0 && rem_count >= remaining_div &&
967 i < (ARRAYSIZE_PLOT-1))
968 {
969 ++i;
970 if(plot[i] > 0)
971 avg += plot[i];
972 rem_count = 0;
973 added_extra_value = true;
974 }
975 1087
976 if(count >= scale_factor) 1088 if(bin > bins_max)
977 { 1089 bins_max = bin;
978 if(added_extra_value)
979 { ++count; added_extra_value = false; }
980 1090
981 int32_t color; 1091 bins_acc += scale_factor;
982 1092
983 avg = Q16_DIV(avg, count << 16); 1093 if(bins_acc >= ARRAYLEN_PLOT)
1094 {
1095 unsigned index;
984 1096
985#ifdef HAVE_LCD_COLOR 1097 if(graph_settings.logarithmic_amp)
986 if(graph_settings.logarithmic) 1098 index = (SHADES-1)*bins_max / QLOG_MAX;
987 color = Q16_MUL(avg, colors_per_val_log) >> 16;
988 else 1099 else
989 color = Q16_MUL(avg, colors_per_val_lin) >> 16; 1100 index = (SHADES-1)*bins_max / QLIN_MAX;
990 if(color >= COLORS) /* TODO same as _vertical */
991 color = COLORS-1;
992 else if (color < 0)
993 color = 0;
994 1101
995#else 1102 /* These happen because we exaggerate the graph a little for
996 if(graph_settings.logarithmic) 1103 * linear mode */
997 color = Q16_MUL(avg, grey_vals_per_val_log) >> 16; 1104 if(index >= SHADES)
998 else 1105 index = SHADES-1;
999 color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16;
1000 if(color > 255)
1001 color = 255;
1002 else if (color < 0)
1003 color = 0;
1004#endif
1005 1106
1006#ifdef HAVE_LCD_COLOR 1107 lcd_(set_foreground)(SPECTROGRAPH_PALETTE(index));
1007 rb->lcd_set_foreground(fft_colors[color]); 1108 lcd_(drawpixel)(x, graph_settings.spectrogram_pos);
1008 rb->lcd_drawpixel(x, graph_settings.spectrogram.row);
1009#else
1010 grey_set_foreground(255 - color);
1011 grey_drawpixel(x, graph_settings.spectrogram.row);
1012#endif
1013 1109
1014 x++; 1110 if(++x >= LCD_WIDTH)
1111 break;
1015 1112
1016 avg = 0; 1113 bins_acc -= ARRAYLEN_PLOT;
1017 count = 0; 1114 bins_max = 0;
1018 } 1115 }
1019 if(x >= LCD_WIDTH)
1020 break;
1021 } 1116 }
1022 if(graph_settings.spectrogram.row != LCD_HEIGHT-1) 1117
1023 graph_settings.spectrogram.row++; 1118 if(graph_settings.spectrogram_pos < LCD_HEIGHT-1)
1119 graph_settings.spectrogram_pos++;
1024 else 1120 else
1025#ifdef HAVE_LCD_COLOR 1121 lcd_scroll_up(1);
1026 xlcd_scroll_up(1);
1027#else
1028 grey_scroll_up(1);
1029#endif
1030} 1122}
1031 1123
1032/********************* End of plotting functions (modes) *********************/ 1124/********************* End of plotting functions (modes) *********************/
1033 1125
1034static long thread_stack[DEFAULT_STACK_SIZE/sizeof(long)]; 1126/* TODO: Only have this thread for multicore, otherwise it serves no purpose */
1035void input_thread_entry(void) 1127long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))]
1128 CACHEALIGN_AT_LEAST_ATTR(4);
1129void fft_thread_entry(void)
1036{ 1130{
1037 kiss_fft_scalar * value; 1131 size_t size = sizeof(buffer);
1038 kiss_fft_scalar left; 1132 FFT_CFG state = FFT_ALLOC(FFT_SIZE, 0, buffer, &size);
1039 int count; 1133 int count;
1040 int idx = 0; /* offset in the buffer */ 1134
1041 int fft_idx = 0; /* offset in input */ 1135 if(state == 0)
1042 while(true) 1136 {
1137 DEBUGF("needed data: %i", (int) size);
1138 output_tail = -1; /* tell that we bailed */
1139 fft_thread_run = true;
1140 return;
1141 }
1142
1143 fft_thread_run = true;
1144
1145 while(fft_thread_run)
1043 { 1146 {
1044 rb->mutex_lock(&input_mutex); 1147 int16_t *value = (int16_t *) rb->pcm_get_peak_buffer(&count);
1045 if(!input_thread_run) 1148 /* This block can introduce discontinuities in our data. Meaning, the
1046 rb->thread_exit(); 1149 * FFT will not be done a continuous segment of the signal. Which can
1150 * be bad. Or not.
1151 *
1152 * Anyway, this is a demo, not a scientific tool. If you want accuracy,
1153 * do a proper spectrum analysis.*/
1154
1155 /* there are cases when we don't have enough data to fill the buffer */
1156 if (!rb->pcm_is_playing())
1157 {
1158 rb->sleep(HZ/5);
1159 output_tail = output_head; /* set empty */
1160 continue;
1161 }
1162 else if(count != ARRAYLEN_IN/2)
1163 {
1164 if(count < ARRAYLEN_IN/2)
1165 {
1166 rb->sleep(0); /* not enough - ease up */
1167 continue;
1168 }
1047 1169
1048 value = (kiss_fft_scalar*) rb->pcm_get_peak_buffer(&count); 1170 count = ARRAYLEN_IN/2; /* too much - limit */
1049 1171 }
1050 if (value == 0 || count == 0)
1051 {
1052 rb->mutex_unlock(&input_mutex);
1053 rb->yield();
1054 continue;
1055 /* This block can introduce discontinuities in our data. Meaning, the FFT
1056 * will not be done a continuous segment of the signal. Which can be bad. Or not.
1057 *
1058 * Anyway, this is a demo, not a scientific tool. If you want accuracy, do a proper
1059 * spectrum analysis.*/
1060 }
1061 else
1062 {
1063 idx = fft_idx = 0;
1064 do
1065 {
1066 left = *(value + idx);
1067 idx += 2;
1068 1172
1069 input[fft_idx] = left; 1173 int fft_idx = 0; /* offset in 'input' */
1070 fft_idx++;
1071 input[fft_idx] = 0;
1072 fft_idx++;
1073 1174
1074 if (fft_idx == ARRAYSIZE_IN) 1175 do
1075 break; 1176 {
1076 } while (idx < count); 1177 kiss_fft_scalar left = *value++;
1077 } 1178 kiss_fft_scalar right = *value++;
1078 if(fft_idx == ARRAYSIZE_IN) /* there are cases when we don't have enough data to fill the buffer */ 1179 input[fft_idx++] = (left + right) >> 1; /* to mono */
1079 input_thread_has_data = true; 1180 input[fft_idx++] = 0;
1080 1181 } while (--count > 0);
1081 rb->mutex_unlock(&input_mutex); 1182
1082 rb->yield(); 1183 apply_window_func(graph_settings.window_func);
1184 FFT_FFT(state, input, output[output_tail]);
1185 rb->yield();
1186#if NUM_CORES > 1
1187 /* write back output for other processor and invalidate for next frame read */
1188 rb->cpucache_invalidate();
1189#endif
1190 int new_tail = output_tail ^ 1;
1191
1192 /* if full, block waiting until reader has freed a slot */
1193 while(new_tail == output_head && fft_thread_run)
1194 rb->sleep(0);
1195
1196 output_tail = new_tail;
1083 } 1197 }
1084} 1198}
1085 1199
1086
1087enum plugin_status plugin_start(const void* parameter) 1200enum plugin_status plugin_start(const void* parameter)
1088{ 1201{
1089 (void) parameter; 1202 /* Defaults */
1090 if ((rb->audio_status() & AUDIO_STATUS_PLAY) == 0) 1203 bool run = true;
1204 bool showing_warning = false;
1205 int timeout = HZ/100;
1206
1207 /* create worker thread - on the COP for dual-core targets */
1208 unsigned int fft_thread = rb->create_thread(fft_thread_entry,
1209 fft_thread_stack, sizeof(fft_thread_stack), 0, "fft output thread"
1210 IF_PRIO(, PRIORITY_USER_INTERFACE+1) IF_COP(, COP));
1211
1212 if(fft_thread == 0)
1091 { 1213 {
1092 rb->splash(HZ * 2, "No track playing. Exiting.."); 1214 rb->splash(HZ, "FFT thread failed create");
1093 return PLUGIN_OK; 1215 return PLUGIN_ERROR;
1216 }
1217
1218 /* wait for it to indicate 'ready' */
1219 while(fft_thread_run == false)
1220 rb->sleep(0);
1221
1222 if(output_tail == -1)
1223 {
1224 /* FFT thread bailed-out like The Fed */
1225 rb->thread_wait(fft_thread);
1226 rb->splash(HZ, "FFT thread failed to init");
1227 return PLUGIN_ERROR;
1094 } 1228 }
1229
1095#ifndef HAVE_LCD_COLOR 1230#ifndef HAVE_LCD_COLOR
1096 unsigned char *gbuf; 1231 unsigned char *gbuf;
1097 size_t gbuf_size = 0; 1232 size_t gbuf_size = 0;
@@ -1103,13 +1238,19 @@ enum plugin_status plugin_start(const void* parameter)
1103 LCD_WIDTH, LCD_HEIGHT, NULL)) 1238 LCD_WIDTH, LCD_HEIGHT, NULL))
1104 { 1239 {
1105 rb->splash(HZ, "Couldn't init greyscale display"); 1240 rb->splash(HZ, "Couldn't init greyscale display");
1241 fft_thread_run = false;
1242 rb->thread_wait(fft_thread);
1106 return PLUGIN_ERROR; 1243 return PLUGIN_ERROR;
1107 } 1244 }
1108 grey_show(true); 1245 grey_show(true);
1109#endif 1246#endif
1110 1247
1248 logarithmic_plot_init();
1249
1111#if LCD_DEPTH > 1 1250#if LCD_DEPTH > 1
1112 rb->lcd_set_backdrop(NULL); 1251 rb->lcd_set_backdrop(NULL);
1252 lcd_(clear_display)();
1253 lcd_(update)();
1113#endif 1254#endif
1114 backlight_force_on(); 1255 backlight_force_on();
1115 1256
@@ -1117,93 +1258,92 @@ enum plugin_status plugin_start(const void* parameter)
1117 rb->cpu_boost(true); 1258 rb->cpu_boost(true);
1118#endif 1259#endif
1119 1260
1120 rb->mutex_init(&input_mutex);
1121
1122 /* Defaults */
1123 bool run = true;
1124 graph_settings.mode = 0;
1125 graph_settings.logarithmic = true;
1126 graph_settings.orientation_vertical = true;
1127 graph_settings.window_func = 0;
1128 graph_settings.changed.mode = false;
1129 graph_settings.changed.scale = false;
1130 graph_settings.changed.orientation = false;
1131 graph_settings.spectrogram.row = 0;
1132 graph_settings.spectrogram.column = 0;
1133
1134 bool changed_window = false;
1135
1136 size_t size = sizeof(buffer);
1137 FFT_CFG state = FFT_ALLOC(FFT_SIZE, 0, buffer, &size);
1138
1139 if (state == 0)
1140 {
1141 DEBUGF("needed data: %i", (int) size);
1142 return PLUGIN_ERROR;
1143 }
1144
1145 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->yield();
1147 while (run) 1261 while (run)
1148 { 1262 {
1149 rb->mutex_lock(&input_mutex); 1263 int button;
1150 if(!input_thread_has_data)
1151 {
1152 /* Make sure the input thread has started before doing anything else */
1153 rb->mutex_unlock(&input_mutex);
1154 rb->yield();
1155 continue;
1156 }
1157 apply_window_func(graph_settings.window_func);
1158 FFT_FFT(state, input, output);
1159 1264
1160 if(changed_window) 1265 while(output_head == output_tail)
1161 { 1266 {
1162 draw(window_text[graph_settings.window_func]); 1267 if(!rb->pcm_is_playing())
1163 changed_window = false; 1268 {
1269 showing_warning = true;
1270 lcd_(clear_display)();
1271 draw_message_string("No audio playing", false);
1272 lcd_(update)();
1273 timeout = HZ/5;
1274 }
1275 else
1276 {
1277 if(showing_warning)
1278 {
1279 showing_warning = false;
1280 lcd_(clear_display)();
1281 lcd_(update)();
1282 timeout = HZ/100;
1283 }
1284 }
1285
1286 /* Make sure the input thread has produced something before doing
1287 * anything but watching for buttons. Music might not be playing
1288 * or things just aren't going well for picking up buffers so keys
1289 * are scanned to avoid lockup. */
1290 button = rb->button_get_w_tmo(timeout);
1291 if (button != BUTTON_NONE)
1292 goto read_button;
1164 } 1293 }
1165 else
1166 draw(0);
1167 1294
1168 input_thread_has_data = false; 1295 draw(NULL);
1169 rb->mutex_unlock(&input_mutex);
1170 rb->yield();
1171 1296
1172 int button = rb->button_get(false); 1297 output_head ^= 1; /* done drawing, free this buffer */
1298 rb->yield();
1299
1300 button = rb->button_get(false);
1301 read_button:
1173 switch (button) 1302 switch (button)
1174 { 1303 {
1175 case FFT_QUIT: 1304 case FFT_QUIT:
1176 run = false; 1305 run = false;
1177 break; 1306 break;
1178 case FFT_PREV_GRAPH: { 1307 case FFT_PREV_GRAPH: {
1179 graph_settings.mode--; 1308 if (graph_settings.mode-- <= FFT_DM_FIRST)
1180 if (graph_settings.mode < 0) 1309 graph_settings.mode = FFT_DM_COUNT-1;
1181 graph_settings.mode = MODES_COUNT-1; 1310 graph_settings.changed.mode = true;
1182 draw(modes_text[graph_settings.mode]); 1311 draw(modes_text[graph_settings.mode]);
1183 break; 1312 break;
1184 } 1313 }
1185 case FFT_NEXT_GRAPH: { 1314 case FFT_NEXT_GRAPH: {
1186 graph_settings.mode++; 1315 if (++graph_settings.mode >= FFT_DM_COUNT)
1187 if (graph_settings.mode >= MODES_COUNT) 1316 graph_settings.mode = FFT_DM_FIRST;
1188 graph_settings.mode = 0; 1317 graph_settings.changed.mode = true;
1189 draw(modes_text[graph_settings.mode]); 1318 draw(modes_text[graph_settings.mode]);
1190 break; 1319 break;
1191 } 1320 }
1192 case FFT_WINDOW: { 1321 case FFT_WINDOW: {
1193 changed_window = true; 1322 if(++graph_settings.window_func >= FFT_WF_COUNT)
1194 graph_settings.window_func ++; 1323 graph_settings.window_func = FFT_WF_FIRST;
1195 if(graph_settings.window_func >= WINDOW_COUNT) 1324 graph_settings.changed.window_func = true;
1196 graph_settings.window_func = 0; 1325 draw(window_text[graph_settings.window_func]);
1326 break;
1327 }
1328 case FFT_AMP_SCALE: {
1329 graph_settings.logarithmic_amp = !graph_settings.logarithmic_amp;
1330 graph_settings.changed.amp_scale = true;
1331 draw(amp_scales_text[graph_settings.logarithmic_amp ? 1 : 0]);
1197 break; 1332 break;
1198 } 1333 }
1199 case FFT_SCALE: { 1334#ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
1200 graph_settings.logarithmic = !graph_settings.logarithmic; 1335 case FFT_FREQ_SCALE: {
1201 draw(scales_text[graph_settings.logarithmic ? 1 : 0]); 1336 graph_settings.logarithmic_freq = !graph_settings.logarithmic_freq;
1337 graph_settings.changed.freq_scale = true;
1338 draw(freq_scales_text[graph_settings.logarithmic_freq ? 1 : 0]);
1202 break; 1339 break;
1203 } 1340 }
1341#endif
1204 case FFT_ORIENTATION: { 1342 case FFT_ORIENTATION: {
1205 graph_settings.orientation_vertical = !graph_settings.orientation_vertical; 1343 graph_settings.orientation_vertical =
1206 draw(0); 1344 !graph_settings.orientation_vertical;
1345 graph_settings.changed.orientation = true;
1346 draw(NULL);
1207 break; 1347 break;
1208 } 1348 }
1209 default: { 1349 default: {
@@ -1214,11 +1354,12 @@ enum plugin_status plugin_start(const void* parameter)
1214 } 1354 }
1215 } 1355 }
1216 1356
1217 /* Handle our input thread. We haven't yield()'d since our last mutex_unlock, so we know we have the mutex */ 1357 /* Handle our FFT thread. */
1218 rb->mutex_lock(&input_mutex); 1358 fft_thread_run = false;
1219 input_thread_run = false; 1359 rb->thread_wait(fft_thread);
1220 rb->mutex_unlock(&input_mutex); 1360#if NUM_CORES > 1
1221 rb->thread_wait(input_thread); 1361 rb->cpucache_flush();
1362#endif
1222 1363
1223#ifdef HAVE_ADJUSTABLE_CPU_FREQ 1364#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1224 rb->cpu_boost(false); 1365 rb->cpu_boost(false);
@@ -1228,4 +1369,5 @@ enum plugin_status plugin_start(const void* parameter)
1228#endif 1369#endif
1229 backlight_use_settings(); 1370 backlight_use_settings();
1230 return PLUGIN_OK; 1371 return PLUGIN_OK;
1372 (void)parameter;
1231} 1373}
diff --git a/apps/plugins/fft/kiss_fft.c b/apps/plugins/fft/kiss_fft.c
index 05db6288fe..33837f7da8 100644
--- a/apps/plugins/fft/kiss_fft.c
+++ b/apps/plugins/fft/kiss_fft.c
@@ -18,10 +18,10 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 fixed or floating point complex numbers. It also delares the kf_ internal functions. 18 fixed or floating point complex numbers. It also delares the kf_ internal functions.
19 */ 19 */
20 20
21static kiss_fft_cpx *scratchbuf=NULL; 21static kiss_fft_cpx *scratchbuf SHAREDBSS_ATTR = NULL;
22static size_t nscratchbuf=0; 22static size_t nscratchbuf SHAREDBSS_ATTR = 0;
23static kiss_fft_cpx *tmpbuf=NULL; 23static kiss_fft_cpx *tmpbuf SHAREDBSS_ATTR = NULL;
24static size_t ntmpbuf=0; 24static size_t ntmpbuf SHAREDBSS_ATTR = 0;
25 25
26#define CHECKBUF(buf,nbuf,n) \ 26#define CHECKBUF(buf,nbuf,n) \
27 do { \ 27 do { \
diff --git a/apps/plugins/fft/kiss_fft.h b/apps/plugins/fft/kiss_fft.h
index 35c864832a..ac673c9094 100644
--- a/apps/plugins/fft/kiss_fft.h
+++ b/apps/plugins/fft/kiss_fft.h
@@ -31,7 +31,7 @@ extern "C" {
31 31
32#ifdef FIXED_POINT 32#ifdef FIXED_POINT
33#include <inttypes.h> 33#include <inttypes.h>
34# if (FIXED_POINT == 32) 34# if 1 /* 16-bit data is _slow_ on devices (FIXED_POINT == 32) */
35# define kiss_fft_scalar int32_t 35# define kiss_fft_scalar int32_t
36# else 36# else
37# define kiss_fft_scalar int16_t 37# define kiss_fft_scalar int16_t
diff --git a/apps/plugins/fft/kiss_fftr.c b/apps/plugins/fft/kiss_fftr.c
index 7fa29b98e8..dc7eab756a 100644
--- a/apps/plugins/fft/kiss_fftr.c
+++ b/apps/plugins/fft/kiss_fftr.c
@@ -48,12 +48,11 @@ kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenme
48 for (i = 0; i < nfft/2; ++i) { 48 for (i = 0; i < nfft/2; ++i) {
49 /*double phase = 49 /*double phase =
50 -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);*/ 50 -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);*/
51 if (inverse_fft) 51 if (inverse_fft) {
52 {
53 DEBUGF("Inverse FFT not implemented!"); /*phase *= -1;*/ 52 DEBUGF("Inverse FFT not implemented!"); /*phase *= -1;*/
54 } 53 }
55 54
56 kf_cexp_round (st->super_twiddles+i, i+1, nfft); 55 kf_cexp_round (st->super_twiddles+i, i+1, nfft);
57 } 56 }
58 return st; 57 return st;
59} 58}