diff options
Diffstat (limited to 'apps/plugins/fft/fft.c')
-rw-r--r-- | apps/plugins/fft/fft.c | 230 |
1 files changed, 162 insertions, 68 deletions
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c index d6a8f016b3..3884f2c413 100644 --- a/apps/plugins/fft/fft.c +++ b/apps/plugins/fft/fft.c | |||
@@ -287,10 +287,8 @@ GREY_INFO_STRUCT | |||
287 | #define HAMMING_COEFF _COEFF(hamming, FFT_SIZE) | 287 | #define HAMMING_COEFF _COEFF(hamming, FFT_SIZE) |
288 | 288 | ||
289 | /****************************** Globals ****************************/ | 289 | /****************************** Globals ****************************/ |
290 | |||
291 | static volatile int output_head SHAREDBSS_ATTR = 0; | ||
292 | static volatile int output_tail SHAREDBSS_ATTR = 0; | ||
293 | /* cacheline-aligned buffers with COP, otherwise word-aligned */ | 290 | /* cacheline-aligned buffers with COP, otherwise word-aligned */ |
291 | /* CPU/COP only applies when compiled for more than one core */ | ||
294 | 292 | ||
295 | #define CACHEALIGN_UP_SIZE(type, len) \ | 293 | #define CACHEALIGN_UP_SIZE(type, len) \ |
296 | (CACHEALIGN_UP((len)*sizeof(type) + (sizeof(type)-1)) / sizeof(type)) | 294 | (CACHEALIGN_UP((len)*sizeof(type) + (sizeof(type)-1)) / sizeof(type)) |
@@ -299,13 +297,24 @@ static volatile int output_tail SHAREDBSS_ATTR = 0; | |||
299 | static kiss_fft_scalar input[CACHEALIGN_UP_SIZE(kiss_fft_scalar, ARRAYLEN_IN)] | 297 | static kiss_fft_scalar input[CACHEALIGN_UP_SIZE(kiss_fft_scalar, ARRAYLEN_IN)] |
300 | CACHEALIGN_AT_LEAST_ATTR(4); | 298 | CACHEALIGN_AT_LEAST_ATTR(4); |
301 | /* CPU+COP */ | 299 | /* CPU+COP */ |
302 | 300 | #if NUM_CORES > 1 | |
301 | /* Output queue indexes */ | ||
302 | static volatile int output_head SHAREDBSS_ATTR = 0; | ||
303 | static volatile int output_tail SHAREDBSS_ATTR = 0; | ||
303 | /* The result is nfft/2+1 complex frequency bins from DC to Nyquist. */ | 304 | /* 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 | static kiss_fft_cpx output[2][CACHEALIGN_UP_SIZE(kiss_fft_cpx, ARRAYLEN_OUT+1)] |
305 | __attribute__((aligned(4))) SHAREDBSS_ATTR; | 306 | SHAREDBSS_ATTR; |
307 | #else | ||
308 | /* Only one output buffer */ | ||
309 | #define output_head 0 | ||
310 | #define output_tail 0 | ||
311 | /* The result is nfft/2+1 complex frequency bins from DC to Nyquist. */ | ||
312 | static kiss_fft_cpx output[1][ARRAYLEN_OUT+1]; | ||
313 | #endif | ||
306 | 314 | ||
307 | /* Unshared */ | 315 | /* Unshared */ |
308 | /* COP */ | 316 | /* COP */ |
317 | static FFT_CFG fft_state SHAREDBSS_ATTR; | ||
309 | static char buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE)] | 318 | static char buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE)] |
310 | CACHEALIGN_AT_LEAST_ATTR(4); | 319 | CACHEALIGN_AT_LEAST_ATTR(4); |
311 | /* CPU */ | 320 | /* CPU */ |
@@ -317,8 +326,6 @@ static struct | |||
317 | uint16_t frac; /* interpolation fraction */ | 326 | uint16_t frac; /* interpolation fraction */ |
318 | } binlog[ARRAYLEN_PLOT] __attribute__((aligned(4))); | 327 | } binlog[ARRAYLEN_PLOT] __attribute__((aligned(4))); |
319 | 328 | ||
320 | static volatile bool fft_thread_run SHAREDDATA_ATTR = false; | ||
321 | |||
322 | enum fft_window_func | 329 | enum fft_window_func |
323 | { | 330 | { |
324 | FFT_WF_FIRST = 0, | 331 | FFT_WF_FIRST = 0, |
@@ -1123,18 +1130,76 @@ void draw_spectrogram_horizontal(void) | |||
1123 | 1130 | ||
1124 | /********************* End of plotting functions (modes) *********************/ | 1131 | /********************* End of plotting functions (modes) *********************/ |
1125 | 1132 | ||
1126 | /* TODO: Only have this thread for multicore, otherwise it serves no purpose */ | 1133 | /****************************** FFT functions ********************************/ |
1127 | long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))] | 1134 | |
1128 | CACHEALIGN_AT_LEAST_ATTR(4); | 1135 | /** functions use in single/multi configuration **/ |
1129 | void fft_thread_entry(void) | 1136 | static inline bool fft_init_fft_lib(void) |
1130 | { | 1137 | { |
1131 | size_t size = sizeof(buffer); | 1138 | size_t size = sizeof(buffer); |
1132 | FFT_CFG state = FFT_ALLOC(FFT_SIZE, 0, buffer, &size); | 1139 | fft_state = FFT_ALLOC(FFT_SIZE, 0, buffer, &size); |
1133 | int count; | ||
1134 | 1140 | ||
1135 | if(state == 0) | 1141 | if(fft_state == NULL) |
1136 | { | 1142 | { |
1137 | DEBUGF("needed data: %i", (int) size); | 1143 | DEBUGF("needed data: %i", (int) size); |
1144 | return false; | ||
1145 | } | ||
1146 | |||
1147 | return true; | ||
1148 | } | ||
1149 | |||
1150 | static inline bool fft_get_fft(void) | ||
1151 | { | ||
1152 | int count; | ||
1153 | int16_t *value = (int16_t *) rb->pcm_get_peak_buffer(&count); | ||
1154 | /* This block can introduce discontinuities in our data. Meaning, the | ||
1155 | * FFT will not be done a continuous segment of the signal. Which can | ||
1156 | * be bad. Or not. | ||
1157 | * | ||
1158 | * Anyway, this is a demo, not a scientific tool. If you want accuracy, | ||
1159 | * do a proper spectrum analysis.*/ | ||
1160 | |||
1161 | /* there are cases when we don't have enough data to fill the buffer */ | ||
1162 | if(count != ARRAYLEN_IN/2) | ||
1163 | { | ||
1164 | if(count < ARRAYLEN_IN/2) | ||
1165 | return false; | ||
1166 | |||
1167 | count = ARRAYLEN_IN/2; /* too much - limit */ | ||
1168 | } | ||
1169 | |||
1170 | int fft_idx = 0; /* offset in 'input' */ | ||
1171 | |||
1172 | do | ||
1173 | { | ||
1174 | kiss_fft_scalar left = *value++; | ||
1175 | kiss_fft_scalar right = *value++; | ||
1176 | input[fft_idx++] = (left + right) >> 1; /* to mono */ | ||
1177 | input[fft_idx++] = 0; | ||
1178 | } while (--count > 0); | ||
1179 | |||
1180 | apply_window_func(graph_settings.window_func); | ||
1181 | |||
1182 | rb->yield(); | ||
1183 | |||
1184 | FFT_FFT(fft_state, input, output[output_tail]); | ||
1185 | |||
1186 | rb->yield(); | ||
1187 | |||
1188 | return true; | ||
1189 | } | ||
1190 | |||
1191 | #if NUM_CORES > 1 | ||
1192 | /* use a worker thread if there is another processor core */ | ||
1193 | static volatile bool fft_thread_run SHAREDDATA_ATTR = false; | ||
1194 | static unsigned long fft_thread; | ||
1195 | |||
1196 | static long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))] | ||
1197 | CACHEALIGN_AT_LEAST_ATTR(4); | ||
1198 | |||
1199 | static void fft_thread_entry(void) | ||
1200 | { | ||
1201 | if (!fft_init_fft_lib()) | ||
1202 | { | ||
1138 | output_tail = -1; /* tell that we bailed */ | 1203 | output_tail = -1; /* tell that we bailed */ |
1139 | fft_thread_run = true; | 1204 | fft_thread_run = true; |
1140 | return; | 1205 | return; |
@@ -1144,45 +1209,18 @@ void fft_thread_entry(void) | |||
1144 | 1209 | ||
1145 | while(fft_thread_run) | 1210 | while(fft_thread_run) |
1146 | { | 1211 | { |
1147 | int16_t *value = (int16_t *) rb->pcm_get_peak_buffer(&count); | ||
1148 | /* This block can introduce discontinuities in our data. Meaning, the | ||
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()) | 1212 | if (!rb->pcm_is_playing()) |
1157 | { | 1213 | { |
1158 | rb->sleep(HZ/5); | 1214 | rb->sleep(HZ/5); |
1159 | output_tail = output_head; /* set empty */ | ||
1160 | continue; | 1215 | continue; |
1161 | } | 1216 | } |
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 | } | ||
1169 | 1217 | ||
1170 | count = ARRAYLEN_IN/2; /* too much - limit */ | 1218 | if (!fft_get_fft()) |
1219 | { | ||
1220 | rb->sleep(0); /* not enough - ease up */ | ||
1221 | continue; | ||
1171 | } | 1222 | } |
1172 | 1223 | ||
1173 | int fft_idx = 0; /* offset in 'input' */ | ||
1174 | |||
1175 | do | ||
1176 | { | ||
1177 | kiss_fft_scalar left = *value++; | ||
1178 | kiss_fft_scalar right = *value++; | ||
1179 | input[fft_idx++] = (left + right) >> 1; /* to mono */ | ||
1180 | input[fft_idx++] = 0; | ||
1181 | } while (--count > 0); | ||
1182 | |||
1183 | apply_window_func(graph_settings.window_func); | ||
1184 | FFT_FFT(state, input, output[output_tail]); | ||
1185 | rb->yield(); | ||
1186 | #if NUM_CORES > 1 | 1224 | #if NUM_CORES > 1 |
1187 | /* write back output for other processor and invalidate for next frame read */ | 1225 | /* write back output for other processor and invalidate for next frame read */ |
1188 | rb->cpucache_invalidate(); | 1226 | rb->cpucache_invalidate(); |
@@ -1190,29 +1228,41 @@ void fft_thread_entry(void) | |||
1190 | int new_tail = output_tail ^ 1; | 1228 | int new_tail = output_tail ^ 1; |
1191 | 1229 | ||
1192 | /* if full, block waiting until reader has freed a slot */ | 1230 | /* if full, block waiting until reader has freed a slot */ |
1193 | while(new_tail == output_head && fft_thread_run) | 1231 | while(fft_thread_run) |
1194 | rb->sleep(0); | 1232 | { |
1233 | if(new_tail != output_head) | ||
1234 | { | ||
1235 | output_tail = new_tail; | ||
1236 | break; | ||
1237 | } | ||
1195 | 1238 | ||
1196 | output_tail = new_tail; | 1239 | rb->sleep(0); |
1240 | } | ||
1197 | } | 1241 | } |
1198 | } | 1242 | } |
1199 | 1243 | ||
1200 | enum plugin_status plugin_start(const void* parameter) | 1244 | static bool fft_have_fft(void) |
1201 | { | 1245 | { |
1202 | /* Defaults */ | 1246 | return output_head != output_tail; |
1203 | bool run = true; | 1247 | } |
1204 | bool showing_warning = false; | ||
1205 | int timeout = HZ/100; | ||
1206 | 1248 | ||
1249 | /* Call only after fft_have_fft() has returned true */ | ||
1250 | static inline void fft_free_fft_output(void) | ||
1251 | { | ||
1252 | output_head ^= 1; /* finished with this */ | ||
1253 | } | ||
1254 | |||
1255 | static bool fft_init_fft(void) | ||
1256 | { | ||
1207 | /* create worker thread - on the COP for dual-core targets */ | 1257 | /* create worker thread - on the COP for dual-core targets */ |
1208 | unsigned int fft_thread = rb->create_thread(fft_thread_entry, | 1258 | fft_thread = rb->create_thread(fft_thread_entry, |
1209 | fft_thread_stack, sizeof(fft_thread_stack), 0, "fft output thread" | 1259 | fft_thread_stack, sizeof(fft_thread_stack), 0, "fft output thread" |
1210 | IF_PRIO(, PRIORITY_USER_INTERFACE+1) IF_COP(, COP)); | 1260 | IF_PRIO(, PRIORITY_USER_INTERFACE+1) IF_COP(, COP)); |
1211 | 1261 | ||
1212 | if(fft_thread == 0) | 1262 | if(fft_thread == 0) |
1213 | { | 1263 | { |
1214 | rb->splash(HZ, "FFT thread failed create"); | 1264 | rb->splash(HZ, "FFT thread failed create"); |
1215 | return PLUGIN_ERROR; | 1265 | return false; |
1216 | } | 1266 | } |
1217 | 1267 | ||
1218 | /* wait for it to indicate 'ready' */ | 1268 | /* wait for it to indicate 'ready' */ |
@@ -1224,9 +1274,55 @@ enum plugin_status plugin_start(const void* parameter) | |||
1224 | /* FFT thread bailed-out like The Fed */ | 1274 | /* FFT thread bailed-out like The Fed */ |
1225 | rb->thread_wait(fft_thread); | 1275 | rb->thread_wait(fft_thread); |
1226 | rb->splash(HZ, "FFT thread failed to init"); | 1276 | rb->splash(HZ, "FFT thread failed to init"); |
1227 | return PLUGIN_ERROR; | 1277 | return false; |
1228 | } | 1278 | } |
1229 | 1279 | ||
1280 | return true; | ||
1281 | } | ||
1282 | |||
1283 | static void fft_close_fft(void) | ||
1284 | { | ||
1285 | /* Handle our FFT thread. */ | ||
1286 | fft_thread_run = false; | ||
1287 | rb->thread_wait(fft_thread); | ||
1288 | #if NUM_CORES > 1 | ||
1289 | rb->cpucache_invalidate(); | ||
1290 | #endif | ||
1291 | } | ||
1292 | #else /* NUM_CORES == 1 */ | ||
1293 | /* everything serialize on single-core and FFT gets to use IRAM main stack if | ||
1294 | * target uses IRAM */ | ||
1295 | static bool fft_have_fft(void) | ||
1296 | { | ||
1297 | return fft_get_fft(); | ||
1298 | } | ||
1299 | |||
1300 | static inline void fft_free_fft_output(void) | ||
1301 | { | ||
1302 | /* nothing to do */ | ||
1303 | } | ||
1304 | |||
1305 | static bool fft_init_fft(void) | ||
1306 | { | ||
1307 | return fft_init_fft_lib(); | ||
1308 | } | ||
1309 | |||
1310 | static inline void fft_close_fft(void) | ||
1311 | { | ||
1312 | /* nothing to do */ | ||
1313 | } | ||
1314 | #endif /* NUM_CORES */ | ||
1315 | /*************************** End of FFT functions ****************************/ | ||
1316 | |||
1317 | enum plugin_status plugin_start(const void* parameter) | ||
1318 | { | ||
1319 | /* Defaults */ | ||
1320 | bool run = true; | ||
1321 | bool showing_warning = false; | ||
1322 | |||
1323 | if (!fft_init_fft()) | ||
1324 | return PLUGIN_ERROR; | ||
1325 | |||
1230 | #ifndef HAVE_LCD_COLOR | 1326 | #ifndef HAVE_LCD_COLOR |
1231 | unsigned char *gbuf; | 1327 | unsigned char *gbuf; |
1232 | size_t gbuf_size = 0; | 1328 | size_t gbuf_size = 0; |
@@ -1238,8 +1334,7 @@ enum plugin_status plugin_start(const void* parameter) | |||
1238 | LCD_WIDTH, LCD_HEIGHT, NULL)) | 1334 | LCD_WIDTH, LCD_HEIGHT, NULL)) |
1239 | { | 1335 | { |
1240 | rb->splash(HZ, "Couldn't init greyscale display"); | 1336 | rb->splash(HZ, "Couldn't init greyscale display"); |
1241 | fft_thread_run = false; | 1337 | fft_close_fft(); |
1242 | rb->thread_wait(fft_thread); | ||
1243 | return PLUGIN_ERROR; | 1338 | return PLUGIN_ERROR; |
1244 | } | 1339 | } |
1245 | grey_show(true); | 1340 | grey_show(true); |
@@ -1262,8 +1357,10 @@ enum plugin_status plugin_start(const void* parameter) | |||
1262 | { | 1357 | { |
1263 | int button; | 1358 | int button; |
1264 | 1359 | ||
1265 | while(output_head == output_tail) | 1360 | while (!fft_have_fft()) |
1266 | { | 1361 | { |
1362 | int timeout; | ||
1363 | |||
1267 | if(!rb->pcm_is_playing()) | 1364 | if(!rb->pcm_is_playing()) |
1268 | { | 1365 | { |
1269 | showing_warning = true; | 1366 | showing_warning = true; |
@@ -1279,8 +1376,9 @@ enum plugin_status plugin_start(const void* parameter) | |||
1279 | showing_warning = false; | 1376 | showing_warning = false; |
1280 | lcd_(clear_display)(); | 1377 | lcd_(clear_display)(); |
1281 | lcd_(update)(); | 1378 | lcd_(update)(); |
1282 | timeout = HZ/100; | ||
1283 | } | 1379 | } |
1380 | |||
1381 | timeout = HZ/100; | ||
1284 | } | 1382 | } |
1285 | 1383 | ||
1286 | /* Make sure the input thread has produced something before doing | 1384 | /* Make sure the input thread has produced something before doing |
@@ -1294,7 +1392,8 @@ enum plugin_status plugin_start(const void* parameter) | |||
1294 | 1392 | ||
1295 | draw(NULL); | 1393 | draw(NULL); |
1296 | 1394 | ||
1297 | output_head ^= 1; /* done drawing, free this buffer */ | 1395 | fft_free_fft_output(); /* COP only */ |
1396 | |||
1298 | rb->yield(); | 1397 | rb->yield(); |
1299 | 1398 | ||
1300 | button = rb->button_get(false); | 1399 | button = rb->button_get(false); |
@@ -1353,13 +1452,8 @@ enum plugin_status plugin_start(const void* parameter) | |||
1353 | 1452 | ||
1354 | } | 1453 | } |
1355 | } | 1454 | } |
1356 | 1455 | ||
1357 | /* Handle our FFT thread. */ | 1456 | fft_close_fft(); |
1358 | fft_thread_run = false; | ||
1359 | rb->thread_wait(fft_thread); | ||
1360 | #if NUM_CORES > 1 | ||
1361 | rb->cpucache_flush(); | ||
1362 | #endif | ||
1363 | 1457 | ||
1364 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 1458 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
1365 | rb->cpu_boost(false); | 1459 | rb->cpu_boost(false); |