diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2010-06-02 08:34:10 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2010-06-02 08:34:10 +0000 |
commit | 30e2f42c82c6c558497aceb5ab0bea24ffeffa3a (patch) | |
tree | efdd88db6d56a941f11ee2c7d3a687287ca267ec /apps/plugins | |
parent | bbe6c5a5e20b95eff80881cee1277a0e259cbcd4 (diff) | |
download | rockbox-30e2f42c82c6c558497aceb5ab0bea24ffeffa3a.tar.gz rockbox-30e2f42c82c6c558497aceb5ab0bea24ffeffa3a.zip |
FFT Plugin: Revamp the main code to rid it of 64-bit math. Use 32-bit kiss_fft_scalar because 16-bit integers are generally a poor choice for computation on-target. Simplify display code to speed it up. Add logarithmic frequency display (need keymappings, guessed on some). On dual-core, perform FFT on COP. Add some support function to fixedpoint.c. ... and stuff.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26470 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins')
-rw-r--r-- | apps/plugins/fft/fft.c | 1688 | ||||
-rw-r--r-- | apps/plugins/fft/kiss_fft.c | 8 | ||||
-rw-r--r-- | apps/plugins/fft/kiss_fft.h | 2 | ||||
-rw-r--r-- | apps/plugins/fft/kiss_fftr.c | 7 | ||||
-rw-r--r-- | apps/plugins/lib/fixedpoint.h | 3 |
5 files changed, 926 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 | ||
270 | static kiss_fft_scalar input[ARRAYSIZE_IN]; | 291 | static volatile int output_head SHAREDBSS_ATTR = 0; |
271 | static kiss_fft_cpx output[ARRAYSIZE_OUT]; | 292 | static volatile int output_tail SHAREDBSS_ATTR = 0; |
272 | static int32_t plot[ARRAYSIZE_PLOT]; | 293 | /* cacheline-aligned buffers with COP, otherwise word-aligned */ |
273 | static 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 */ | ||
299 | static 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. */ | ||
304 | static 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 */ | ||
309 | static char buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE)] | ||
310 | CACHEALIGN_AT_LEAST_ATTR(4); | ||
311 | /* CPU */ | ||
312 | static int32_t plot_history[ARRAYLEN_PLOT]; | ||
313 | static int32_t plot[ARRAYLEN_PLOT]; | ||
314 | static struct | ||
315 | { | ||
316 | int16_t bin; /* integer bin number */ | ||
317 | uint16_t frac; /* interpolation fraction */ | ||
318 | } binlog[ARRAYLEN_PLOT] __attribute__((aligned(4))); | ||
319 | |||
320 | static volatile bool fft_thread_run SHAREDDATA_ATTR = false; | ||
274 | 321 | ||
275 | #define MODES_COUNT 3 | 322 | enum 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 | |||
330 | enum 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 | ||
277 | const unsigned char* modes_text[] = { "Lines", "Bars", "Spectrogram" }; | 339 | static const unsigned char* const modes_text[FFT_DM_COUNT] = |
278 | const unsigned char* scales_text[] = { "Linear scale", "Logarithmic scale" }; | 340 | { "Lines", "Bars", "Spectrogram" }; |
279 | const unsigned char* window_text[] = { "Hamming window", "Hann window" }; | ||
280 | 341 | ||
281 | struct mutex input_mutex; | 342 | static const unsigned char* const amp_scales_text[2] = |
282 | bool input_thread_run = true; | 343 | { "Linear amplitude", "Logarithmic amplitude" }; |
283 | bool input_thread_has_data = false; | ||
284 | 344 | ||
285 | struct { | 345 | static const unsigned char* const freq_scales_text[2] = |
286 | int32_t mode; | 346 | { "Linear frequency", "Logarithmic frequency" }; |
287 | bool logarithmic; | 347 | |
348 | static const unsigned char* const window_text[FFT_WF_COUNT] = | ||
349 | { "Hamming window", "Hann window" }; | ||
350 | |||
351 | static 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 */ |
312 | inline 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 | 401 | void apply_window_func(enum fft_window_func mode) |
319 | * 1 - Hann window */ | ||
320 | #define WINDOW_COUNT 2 | ||
321 | void 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 */ |
347 | int32_t calc_magnitudes(bool logarithmic) | 424 | int32_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. */ | ||
485 | void 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 */ | ||
527 | void 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); | |||
388 | void draw_spectrogram_vertical(void); | 570 | void draw_spectrogram_vertical(void); |
389 | void draw_spectrogram_horizontal(void); | 571 | void draw_spectrogram_horizontal(void); |
390 | 572 | ||
391 | void 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) | 590 | void 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 | |||
622 | void 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 | ||
578 | void draw_lines_vertical(void) | 746 | void 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 | ||
719 | void draw_bars_vertical(void) | 897 | void 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 | ||
768 | void draw_bars_horizontal(void) | 957 | void 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 | ||
818 | void draw_spectrogram_vertical(void) | 1017 | void 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 | ||
925 | void draw_spectrogram_horizontal(void) | 1071 | void 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 | ||
1034 | static long thread_stack[DEFAULT_STACK_SIZE/sizeof(long)]; | 1126 | /* TODO: Only have this thread for multicore, otherwise it serves no purpose */ |
1035 | void input_thread_entry(void) | 1127 | long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))] |
1128 | CACHEALIGN_AT_LEAST_ATTR(4); | ||
1129 | void 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 | |||
1087 | enum plugin_status plugin_start(const void* parameter) | 1200 | enum 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 | ||
21 | static kiss_fft_cpx *scratchbuf=NULL; | 21 | static kiss_fft_cpx *scratchbuf SHAREDBSS_ATTR = NULL; |
22 | static size_t nscratchbuf=0; | 22 | static size_t nscratchbuf SHAREDBSS_ATTR = 0; |
23 | static kiss_fft_cpx *tmpbuf=NULL; | 23 | static kiss_fft_cpx *tmpbuf SHAREDBSS_ATTR = NULL; |
24 | static size_t ntmpbuf=0; | 24 | static 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 | } |
diff --git a/apps/plugins/lib/fixedpoint.h b/apps/plugins/lib/fixedpoint.h index ef50dd0085..7cb0098129 100644 --- a/apps/plugins/lib/fixedpoint.h +++ b/apps/plugins/lib/fixedpoint.h | |||
@@ -59,6 +59,9 @@ long fp_sqrt(long a, unsigned int fracbits); | |||
59 | long fp14_cos(int val); | 59 | long fp14_cos(int val); |
60 | long fp14_sin(int val); | 60 | long fp14_sin(int val); |
61 | long fp16_log(int x); | 61 | long fp16_log(int x); |
62 | long fp16_exp(int x); | ||
63 | |||
64 | unsigned long isqrt(unsigned long x); | ||
62 | 65 | ||
63 | /* fast unsigned multiplication (16x16bit->32bit or 32x32bit->32bit, | 66 | /* fast unsigned multiplication (16x16bit->32bit or 32x32bit->32bit, |
64 | * whichever is faster for the architecture) */ | 67 | * whichever is faster for the architecture) */ |