summaryrefslogtreecommitdiff
path: root/apps/plugins/fft/fft.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2010-06-03 00:59:05 +0000
committerMichael Sevakis <jethead71@rockbox.org>2010-06-03 00:59:05 +0000
commitcc38700b7a8541a6a9285e1fae473863993d3227 (patch)
tree1972ffb3953de0cfc9be5343546555674cb51444 /apps/plugins/fft/fft.c
parentc1ae4414d4ac6504992434b949b252c30daf0c48 (diff)
downloadrockbox-cc38700b7a8541a6a9285e1fae473863993d3227.tar.gz
rockbox-cc38700b7a8541a6a9285e1fae473863993d3227.zip
FFT plugin: Use worker thread only on multiprocessor targets.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26498 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/fft/fft.c')
-rw-r--r--apps/plugins/fft/fft.c230
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
291static volatile int output_head SHAREDBSS_ATTR = 0;
292static 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;
299static kiss_fft_scalar input[CACHEALIGN_UP_SIZE(kiss_fft_scalar, ARRAYLEN_IN)] 297static 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 */
302static volatile int output_head SHAREDBSS_ATTR = 0;
303static 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. */
304static kiss_fft_cpx output[2][CACHEALIGN_UP_SIZE(kiss_fft_cpx, ARRAYLEN_OUT+1)] 305static 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. */
312static kiss_fft_cpx output[1][ARRAYLEN_OUT+1];
313#endif
306 314
307/* Unshared */ 315/* Unshared */
308/* COP */ 316/* COP */
317static FFT_CFG fft_state SHAREDBSS_ATTR;
309static char buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE)] 318static 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
320static volatile bool fft_thread_run SHAREDDATA_ATTR = false;
321
322enum fft_window_func 329enum 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 ********************************/
1127long 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 **/
1129void fft_thread_entry(void) 1136static 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
1150static 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 */
1193static volatile bool fft_thread_run SHAREDDATA_ATTR = false;
1194static unsigned long fft_thread;
1195
1196static long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))]
1197 CACHEALIGN_AT_LEAST_ATTR(4);
1198
1199static 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
1200enum plugin_status plugin_start(const void* parameter) 1244static 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 */
1250static inline void fft_free_fft_output(void)
1251{
1252 output_head ^= 1; /* finished with this */
1253}
1254
1255static 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
1283static 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 */
1295static bool fft_have_fft(void)
1296{
1297 return fft_get_fft();
1298}
1299
1300static inline void fft_free_fft_output(void)
1301{
1302 /* nothing to do */
1303}
1304
1305static bool fft_init_fft(void)
1306{
1307 return fft_init_fft_lib();
1308}
1309
1310static inline void fft_close_fft(void)
1311{
1312 /* nothing to do */
1313}
1314#endif /* NUM_CORES */
1315/*************************** End of FFT functions ****************************/
1316
1317enum 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);