diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c index e62c919..91891c5 100644 --- a/apps/plugins/fft/fft.c +++ b/apps/plugins/fft/fft.c @@ -1,5 +1,5 @@ /*************************************************************************** -* __________ __ ___. + * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < @@ -18,6 +18,64 @@ * KIND, either express or implied. * ****************************************************************************/ + +/**************************************************************************** + * OVERVIEW OF THE PLUGIN + * + * There are two input modes available: + * 1) Playback mode - analyzes the currently playing track + * 2) Recording mode (if supported by hardware) - analyzes input from + * user-selected source (mic, linein, spdif, taken from Recording Settings) + * + * Recording mode is triggered if playback is stopped. + * + * 1) Playback mode + * * This mode uses a 2-thread model. The main thread waits for the input + * thread to fetch new data. Synchronization is done through input_mutex + * * The input thread always fills the entire buffer, so it may have the mutex + * even if it yield()s due to insufficient data. + * It will release the mutex iff the buffer is full + * * The input thread has a lower priority to make sure that playback runs + * smoothly + * * The input thread must run on the same core as the main thread as + * doing pcm_* calls from the second core may be risky + * + * 2) Recording mode + * * This mode uses a callback model. The callback is called when more data + * is available + * * To avoid using double buffering, recording is stopped in the callback, + * so that the buffer is not filled with new data during the transform. + * Recording is restarted before the buffer is drawn + * + * The workflow is as follows: + * + * input ready -> apply_window_func -> FFT_FFT (do the transform) -> + * -> draw -> draw_*_(vertical|horizontal) + * + * Drawing & calculation + * + * * Drawing is done by individual functions draw_*_(vertical|horizontal) + * * The auxiliary drawing (i.e., popups) is done in draw() + * + * * calc_magnitudes calculates the magnitudes of the complex results + * * Since we don't have a fast fixed-point 64-bit log function, + * the values are CLIPPED, which results in the ragged appearance of + * lines mode when using logarithmic scale. + * + * * apply_window_func applies a window function to the input. + * The coefficients are precalculated and appear in const.h. + * Tables are available for 512-,1024-,2048-,4096-, and 8192-point + * transforms. If a larger transform is used, new tables will have to be + * created. + * + * Other + * + * * Every mode averages groups of bins to find the value of the pixel/line/bar + * to be drawn. + * * To do that, we use #bins/LCD_WIDTH/HEIGHT. Since that's an integer, + * special measures are implemented to handle the remaining pixels. + * + ****************************************************************************/ #include "plugin.h" #include "lib/helper.h" @@ -44,7 +102,7 @@ GREY_INFO_STRUCT # define FFT_QUIT BUTTON_OFF #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ - (CONFIG_KEYPAD == IRIVER_H300_PAD) + (CONFIG_KEYPAD == IRIVER_H300_PAD) # define FFT_PREV_GRAPH BUTTON_LEFT # define FFT_NEXT_GRAPH BUTTON_RIGHT # define FFT_ORIENTATION BUTTON_REC @@ -53,8 +111,8 @@ GREY_INFO_STRUCT # define FFT_QUIT BUTTON_OFF #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ - (CONFIG_KEYPAD == IPOD_3G_PAD) || \ - (CONFIG_KEYPAD == IPOD_1G2G_PAD) + (CONFIG_KEYPAD == IPOD_3G_PAD) || \ +(CONFIG_KEYPAD == IPOD_1G2G_PAD) # define MINESWP_SCROLLWHEEL # define FFT_PREV_GRAPH BUTTON_LEFT # define FFT_NEXT_GRAPH BUTTON_RIGHT @@ -101,6 +159,7 @@ GREY_INFO_STRUCT # define FFT_ORIENTATION BUTTON_UP # define FFT_WINDOW BUTTON_REC # define FFT_SCALE BUTTON_SELECT + # define FFT_QUIT BUTTON_POWER #elif (CONFIG_KEYPAD == SANSA_M200_PAD) # define FFT_PREV_GRAPH BUTTON_LEFT @@ -228,6 +287,14 @@ GREY_INFO_STRUCT #include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */ #include "const.h" +#if (REC_SAMPR_CAPS & SAMPR_CAP_44) +#define SAMPLE_RATE SAMPR_44 +#elif (REC_SAMPR_CAPS & SAMPR_CAP_22) +#define SAMPLE_RATE SAMPR_22 +#elif (REC_SAMPR_CAPS & SAMPR_CAP_11) +#define SAMPLE_RATE SAMPR_11 +#endif + #if (LCD_WIDTH < LCD_HEIGHT) #define LCD_SIZE LCD_HEIGHT #else @@ -319,23 +386,23 @@ void apply_window_func(char mode) switch(mode) { case 0: /* Hamming window */ - { - size_t i; - for (i = 0; i < ARRAYSIZE_IN; ++i) { - input[i] = Q15_MUL(input[i] << 15, HAMMING_COEFF[i]) >> 15; - } - break; - } + size_t i; + for (i = 0; i < ARRAYSIZE_IN; ++i) + { + input[i] = Q15_MUL(input[i] << 15, HAMMING_COEFF[i]) >> 15; + } + break; + } case 1: /* Hann window */ - { - size_t i; - for (i = 0; i < ARRAYSIZE_IN; ++i) { - input[i] = Q15_MUL(input[i] << 15, HANN_COEFF[i]) >> 15; + size_t i; + for (i = 0; i < ARRAYSIZE_IN; ++i) + { + input[i] = Q15_MUL(input[i] << 15, HANN_COEFF[i]) >> 15; + } + break; } - break; - } } } @@ -346,7 +413,7 @@ int32_t calc_magnitudes(bool logarithmic) size_t i; int32_t max = -2147483647; - + /* Calculate the magnitude, discarding the phase. * The sum of the squares can easily overflow the 15-bit (s15.16) * requirement for fsqrt, so we scale the data down */ @@ -420,44 +487,44 @@ void draw(const unsigned char* message) case 0: { #ifdef HAVE_LCD_COLOR - rb->lcd_clear_display(); + rb->lcd_clear_display(); #else - grey_clear_display(); + grey_clear_display(); #endif - if (graph_settings.orientation_vertical) - draw_lines_vertical(); - else - draw_lines_horizontal(); - break; - } + if (graph_settings.orientation_vertical) + draw_lines_vertical(); + else + draw_lines_horizontal(); + break; + } case 1: { #ifdef HAVE_LCD_COLOR - rb->lcd_clear_display(); + rb->lcd_clear_display(); #else - grey_clear_display(); + grey_clear_display(); #endif - if(graph_settings.orientation_vertical) - draw_bars_vertical(); - else - draw_bars_horizontal(); + if(graph_settings.orientation_vertical) + draw_bars_vertical(); + else + draw_bars_horizontal(); - break; - } + break; + } case 2: { - if(graph_settings.orientation_vertical) - draw_spectrogram_vertical(); - else - draw_spectrogram_horizontal(); - break; - } + if(graph_settings.orientation_vertical) + draw_spectrogram_vertical(); + else + draw_spectrogram_horizontal(); + break; + } } if (show_message > 0) { - /* We have a message to show */ + /* We have a message to show */ int x, y; #ifdef HAVE_LCD_COLOR @@ -465,25 +532,25 @@ void draw(const unsigned char* message) #else grey_getstringsize(last_message, &x, &y); #endif - /* x and y give the size of the box for the popup */ + /* x and y give the size of the box for the popup */ x += 6; /* 3 px of horizontal padding and */ y += 4; /* 2 px of vertical padding */ /* In vertical spectrogram mode, leave space for the popup - * before actually drawing it (if space is needed) */ + * before actually drawing it (if space is needed) */ if(graph_settings.mode == 2 && - graph_settings.orientation_vertical && - graph_settings.spectrogram.column > LCD_WIDTH-x-2) - { + graph_settings.orientation_vertical && + graph_settings.spectrogram.column > LCD_WIDTH-x-2) + { #ifdef HAVE_LCD_COLOR - xlcd_scroll_left(graph_settings.spectrogram.column - - (LCD_WIDTH - x - 1)); + xlcd_scroll_left(graph_settings.spectrogram.column - + (LCD_WIDTH - x - 1)); #else - grey_scroll_left(graph_settings.spectrogram.column - - (LCD_WIDTH - x - 1)); + grey_scroll_left(graph_settings.spectrogram.column - + (LCD_WIDTH - x - 1)); #endif - graph_settings.spectrogram.column = LCD_WIDTH - x - 2; - } + graph_settings.spectrogram.column = LCD_WIDTH - x - 2; + } #ifdef HAVE_LCD_COLOR rb->lcd_set_foreground(LCD_DARKGRAY); @@ -507,51 +574,51 @@ void draw(const unsigned char* message) } else if(last_message != 0) { - if(graph_settings.mode != 2) - { - /* These modes clear the screen themselves */ - last_message = 0; - } - else /* Spectrogram mode - need to erase the popup */ - { - int x, y; + if(graph_settings.mode != 2) + { + /* These modes clear the screen themselves */ + last_message = 0; + } + else /* Spectrogram mode - need to erase the popup */ + { + int x, y; #ifdef HAVE_LCD_COLOR - rb->lcd_getstringsize(last_message, &x, &y); + rb->lcd_getstringsize(last_message, &x, &y); #else - grey_getstringsize(last_message, &x, &y); + grey_getstringsize(last_message, &x, &y); #endif - /* Recalculate the size */ - x += 6; /* 3 px of horizontal padding and */ - y += 4; /* 2 px of vertical padding */ + /* Recalculate the size */ + x += 6; /* 3 px of horizontal padding and */ + y += 4; /* 2 px of vertical padding */ - if(!graph_settings.orientation_vertical) - { - /* In horizontal spectrogram mode, just scroll up by Y lines */ + if(!graph_settings.orientation_vertical) + { + /* In horizontal spectrogram mode, just scroll up by Y lines */ #ifdef HAVE_LCD_COLOR - xlcd_scroll_up(y); + xlcd_scroll_up(y); #else - grey_scroll_up(y); + grey_scroll_up(y); #endif - graph_settings.spectrogram.row -= y; - if(graph_settings.spectrogram.row < 0) - graph_settings.spectrogram.row = 0; - } - else - { - /* In vertical spectrogram mode, erase the popup */ + graph_settings.spectrogram.row -= y; + if(graph_settings.spectrogram.row < 0) + graph_settings.spectrogram.row = 0; + } + else + { + /* In vertical spectrogram mode, erase the popup */ #ifdef HAVE_LCD_COLOR - rb->lcd_set_foreground(LCD_DEFAULT_BG); - rb->lcd_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y); - rb->lcd_set_foreground(LCD_DEFAULT_FG); + rb->lcd_set_foreground(LCD_DEFAULT_BG); + rb->lcd_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y); + rb->lcd_set_foreground(LCD_DEFAULT_FG); #else - grey_set_foreground(GREY_WHITE); - grey_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y); - grey_set_foreground(GREY_BLACK); + grey_set_foreground(GREY_WHITE); + grey_fillrect(LCD_WIDTH-2-x, 0, LCD_WIDTH-1, y); + grey_set_foreground(GREY_BLACK); #endif - } - - last_message = 0; - } + } + + last_message = 0; + } } #ifdef HAVE_LCD_COLOR rb->lcd_update(); @@ -568,8 +635,8 @@ void draw_lines_vertical(void) { static int32_t max = 0, vfactor = 0, vfactor_count = 0; static const int32_t hfactor = - Q16_DIV(LCD_WIDTH << 16, (ARRAYSIZE_PLOT) << 16), - bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_WIDTH; + Q16_DIV(LCD_WIDTH << 16, (ARRAYSIZE_PLOT) << 16), + bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_WIDTH; static bool old_scale = true; if (old_scale != graph_settings.logarithmic) @@ -626,9 +693,9 @@ void draw_lines_vertical(void) if (draw) { #ifdef HAVE_LCD_COLOR - rb->lcd_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1); + rb->lcd_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1); #else - grey_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1); + grey_vline(x, LCD_HEIGHT-1, LCD_HEIGHT-y-1); #endif } } @@ -639,8 +706,8 @@ void draw_lines_horizontal(void) static int max = 0; static const int32_t vfactor = - Q16_DIV(LCD_HEIGHT << 16, (ARRAYSIZE_PLOT) << 16), - bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_HEIGHT; + Q16_DIV(LCD_HEIGHT << 16, (ARRAYSIZE_PLOT) << 16), + bins_per_pixel = (ARRAYSIZE_PLOT) / LCD_HEIGHT; if (graph_settings.changed.scale) max = 0; /* reset the graph on scaling mode change */ @@ -697,9 +764,9 @@ void draw_lines_horizontal(void) if (draw) { #ifdef HAVE_LCD_COLOR - rb->lcd_hline(0, x, y); + rb->lcd_hline(0, x, y); #else - grey_hline(0, x, y); + grey_hline(0, x, y); #endif } } @@ -708,7 +775,7 @@ void draw_lines_horizontal(void) void draw_bars_vertical(void) { static const unsigned int bars = 20, border = 2, items = ARRAYSIZE_PLOT - / bars, width = (LCD_WIDTH - ((bars - 1) * border)) / bars; + / bars, width = (LCD_WIDTH - ((bars - 1) * border)) / bars; calc_magnitudes(graph_settings.logarithmic); @@ -757,7 +824,7 @@ void draw_bars_vertical(void) void draw_bars_horizontal(void) { static const unsigned int bars = 14, border = 3, items = ARRAYSIZE_PLOT - / bars, height = (LCD_HEIGHT - ((bars - 1) * border)) / bars; + / bars, height = (LCD_HEIGHT - ((bars - 1) * border)) / bars; calc_magnitudes(graph_settings.logarithmic); @@ -811,16 +878,16 @@ void draw_spectrogram_vertical(void) ,colors_per_val_log = Q16_DIV((COLORS-1) << 16, QLOG_MAX), colors_per_val_lin = Q16_DIV((COLORS-1) << 16, QLIN_MAX) #else - ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX), - grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX) + ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX), + grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX) #endif - ; + ; const int32_t remaining_div = (ARRAYSIZE_PLOT-scale_factor*LCD_HEIGHT) > 0 ? ( Q16_DIV((scale_factor*LCD_HEIGHT) << 16, (ARRAYSIZE_PLOT-scale_factor*LCD_HEIGHT) << 16) - + (1<<15) ) >> 16 : 0; + + (1<<15) ) >> 16 : 0; calc_magnitudes(graph_settings.logarithmic); if(graph_settings.changed.mode || graph_settings.changed.orientation) @@ -858,7 +925,7 @@ void draw_spectrogram_vertical(void) if(count >= scale_factor) { if(added_extra_value) - { ++count; added_extra_value = false; } + { ++count; added_extra_value = false; } int32_t color; @@ -875,10 +942,10 @@ void draw_spectrogram_vertical(void) color = 0; #else - if(graph_settings.logarithmic) - color = Q16_MUL(avg, grey_vals_per_val_log) >> 16; - else - color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16; + if(graph_settings.logarithmic) + color = Q16_MUL(avg, grey_vals_per_val_log) >> 16; + else + color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16; if(color > 255) color = 255; else if (color < 0) @@ -907,7 +974,7 @@ void draw_spectrogram_vertical(void) #ifdef HAVE_LCD_COLOR xlcd_scroll_left(1); #else - grey_scroll_left(1); + grey_scroll_left(1); #endif } @@ -918,16 +985,16 @@ void draw_spectrogram_horizontal(void) ,colors_per_val_log = Q16_DIV((COLORS-1) << 16, QLOG_MAX), colors_per_val_lin = Q16_DIV((COLORS-1) << 16, QLIN_MAX) #else - ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX), - grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX) + ,grey_vals_per_val_log = Q16_DIV(255 << 16, QLOG_MAX), + grey_vals_per_val_lin = Q16_DIV(255 << 16, QLIN_MAX) #endif - ; + ; const int32_t remaining_div = - (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) > 0 ? - ( Q16_DIV((scale_factor*LCD_WIDTH) << 16, - (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) << 16) - + (1<<15) ) >> 16 : 0; + (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) > 0 ? + ( Q16_DIV((scale_factor*LCD_WIDTH) << 16, + (ARRAYSIZE_PLOT-scale_factor*LCD_WIDTH) << 16) + + (1<<15) ) >> 16 : 0; calc_magnitudes(graph_settings.logarithmic); if(graph_settings.changed.mode || graph_settings.changed.orientation) @@ -965,7 +1032,7 @@ void draw_spectrogram_horizontal(void) if(count >= scale_factor) { if(added_extra_value) - { ++count; added_extra_value = false; } + { ++count; added_extra_value = false; } int32_t color; @@ -982,12 +1049,12 @@ void draw_spectrogram_horizontal(void) color = 0; #else - if(graph_settings.logarithmic) - color = Q16_MUL(avg, grey_vals_per_val_log) >> 16; - else - color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16; + if(graph_settings.logarithmic) + color = Q16_MUL(avg, grey_vals_per_val_log) >> 16; + else + color = Q16_MUL(avg, grey_vals_per_val_lin) >> 16; if(color > 255) - color = 255; + color = 255; else if (color < 0) color = 0; #endif @@ -1014,7 +1081,7 @@ void draw_spectrogram_horizontal(void) #ifdef HAVE_LCD_COLOR xlcd_scroll_up(1); #else - grey_scroll_up(1); + grey_scroll_up(1); #endif } @@ -1023,64 +1090,114 @@ void draw_spectrogram_horizontal(void) static long thread_stack[DEFAULT_STACK_SIZE/sizeof(long)]; void input_thread_entry(void) { - kiss_fft_scalar * value; - kiss_fft_scalar left; - int count; - int idx = 0; /* offset in the buffer */ - int fft_idx = 0; /* offset in input */ - while(true) - { - rb->mutex_lock(&input_mutex); - if(!input_thread_run) - rb->thread_exit(); - - value = (kiss_fft_scalar*) rb->pcm_get_peak_buffer(&count); - - if (value == 0 || count == 0) - { - rb->mutex_unlock(&input_mutex); - rb->yield(); - continue; - /* This block can introduce discontinuities in our data. Meaning, the FFT - * will not be done a continuous segment of the signal. Which can be bad. Or not. - * - * Anyway, this is a demo, not a scientific tool. If you want accuracy, do a proper - * spectrum analysis.*/ - } - else - { - idx = fft_idx = 0; - do - { - left = *(value + idx); - idx += 2; - - input[fft_idx] = left; - fft_idx++; - input[fft_idx] = 0; - fft_idx++; - - if (fft_idx == ARRAYSIZE_IN) - break; - } while (idx < count); - } - if(fft_idx == ARRAYSIZE_IN) /* there are cases when we don't have enough data to fill the buffer */ - input_thread_has_data = true; - - rb->mutex_unlock(&input_mutex); - rb->yield(); - } + kiss_fft_scalar * value; + kiss_fft_scalar left; + int count; + int idx = 0; /* offset in the buffer */ + int fft_idx = 0; /* offset in input */ + while(true) + { + rb->mutex_lock(&input_mutex); + if(!input_thread_run) + rb->thread_exit(); + + value = (kiss_fft_scalar*) rb->pcm_get_peak_buffer(&count); + + if (value == 0 || count == 0) + { + rb->mutex_unlock(&input_mutex); + rb->yield(); + continue; + /* This block can introduce discontinuities in our data. Meaning, the FFT + * will not be done a continuous segment of the signal. Which can be bad. Or not. + * + * Anyway, this is a demo, not a scientific tool. If you want accuracy, do a proper + * spectrum analysis.*/ + } + else + { + idx = fft_idx = 0; + do + { + left = *(value + idx); + idx += 2; + + input[fft_idx] = left; + fft_idx++; + input[fft_idx] = 0; + fft_idx++; + + if (fft_idx == ARRAYSIZE_IN) + break; + } while (idx < count); + } + if(fft_idx == ARRAYSIZE_IN) /* there are cases when we don't have enough data to fill the buffer */ + input_thread_has_data = true; + + rb->mutex_unlock(&input_mutex); + rb->yield(); + } +} + +#ifdef HAVE_RECORDING +static int recording_entry(int status) +{ + if (status >= 0) + input_thread_has_data = true; + else + input_thread_has_data = false; + + rb->pcm_stop_recording(); + + rb->logf("FFT: More data available, status: %d", status); + return -1; +} + +void recording_init(void) +{ + rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); + rb->audio_set_input_source(rb->global_settings->rec_source, SRCF_RECORDING); + + /* set to maximum gain */ + if (rb->global_settings->rec_source == AUDIO_SRC_MIC) + rb->audio_set_recording_gain(rb->global_settings->rec_mic_gain, + rb->global_settings->rec_mic_gain, + AUDIO_GAIN_MIC); + else + rb->audio_set_recording_gain(rb->global_settings->rec_left_gain, + rb->global_settings->rec_right_gain, + AUDIO_GAIN_LINEIN); + + rb->pcm_set_frequency(SAMPLE_RATE); + rb->pcm_apply_settings(); + + rb->pcm_init_recording(); + rb->logf("FFT: Recording initialized"); } +#endif enum plugin_status plugin_start(const void* parameter) { (void) parameter; + +#ifdef HAVE_RECORDING + bool recording_mode = false; +#endif if ((rb->audio_status() & AUDIO_STATUS_PLAY) == 0) { +#ifdef HAVE_RECORDING + rb->splash(HZ * 2, "No track playing. Input mode."); + recording_mode = true; + recording_init(); +#else rb->splash(HZ * 2, "No track playing. Exiting.."); return PLUGIN_OK; +#endif } + + rb->lcd_clear_display(); + #ifndef HAVE_LCD_COLOR unsigned char *gbuf; size_t gbuf_size = 0; @@ -1089,7 +1206,7 @@ enum plugin_status plugin_start(const void* parameter) /* initialize the greyscale buffer.*/ if (!grey_init(gbuf, gbuf_size, GREY_ON_COP | GREY_BUFFERED, - LCD_WIDTH, LCD_HEIGHT, NULL)) + LCD_WIDTH, LCD_HEIGHT, NULL)) { rb->splash(HZ, "Couldn't init greyscale display"); return PLUGIN_ERROR; @@ -1127,87 +1244,124 @@ enum plugin_status plugin_start(const void* parameter) if (state == 0) { - DEBUGF("needed data: %i", (int) size); + DEBUGF("needed data: %l", (int) size); return PLUGIN_ERROR; } - - 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)); - rb->yield(); + unsigned int input_thread = 0; +#ifdef HAVE_RECORDING + if (recording_mode) + rb->pcm_record_data(recording_entry, (void *) input, + (size_t) ARRAYSIZE_IN * sizeof(kiss_fft_scalar)); + else +#endif + input_thread = rb->create_thread(&input_thread_entry, thread_stack, sizeof(thread_stack), 0, "fft input thread" IF_PRIO(, PRIORITY_BACKGROUND) IF_COP(, CPU)); + + rb->yield(); while (run) { - rb->mutex_lock(&input_mutex); - if(!input_thread_has_data) - { - /* Make sure the input thread has started before doing anything else */ - rb->mutex_unlock(&input_mutex); - rb->yield(); - continue; - } - apply_window_func(graph_settings.window_func); - FFT_FFT(state, input, output); - - if(changed_window) - { - draw(window_text[graph_settings.window_func]); - changed_window = false; - } - else - draw(0); - - input_thread_has_data = false; - rb->mutex_unlock(&input_mutex); - rb->yield(); + if(!recording_mode) + rb->mutex_lock(&input_mutex); + if(!input_thread_has_data) + { + /* Make sure the input thread has started before doing anything else */ + if(!recording_mode) + rb->mutex_unlock(&input_mutex); + else + { + if(rb->button_get(false) == FFT_QUIT) + { + rb->logf("Aborting wait-for-data cycle"); + run = false; + break; + } + } + + rb->yield(); + continue; + } + apply_window_func(graph_settings.window_func); + FFT_FFT(state, input, output); + + /* We're done with the input */ + if(recording_mode) + { + rb->logf("FFT: call pcm_record_data from main loop"); + rb->pcm_record_data(recording_entry, (void *) input, (size_t) ARRAYSIZE_IN * sizeof(kiss_fft_scalar)); + } - int button = rb->button_get(false); + if(changed_window) + { + draw(window_text[graph_settings.window_func]); + changed_window = false; + } + else + draw(0); + + input_thread_has_data = false; + if(!recording_mode) + rb->mutex_unlock(&input_mutex); + rb->yield(); + + int button = rb->button_get(false); switch (button) { case FFT_QUIT: run = false; break; case FFT_PREV_GRAPH: { - graph_settings.mode--; - if (graph_settings.mode < 0) - graph_settings.mode = MODES_COUNT-1; - draw(modes_text[graph_settings.mode]); - break; - } + graph_settings.mode--; + if (graph_settings.mode < 0) + graph_settings.mode = MODES_COUNT-1; + draw(modes_text[graph_settings.mode]); + break; + } case FFT_NEXT_GRAPH: { - graph_settings.mode++; - if (graph_settings.mode >= MODES_COUNT) - graph_settings.mode = 0; - draw(modes_text[graph_settings.mode]); - break; - } + graph_settings.mode++; + if (graph_settings.mode >= MODES_COUNT) + graph_settings.mode = 0; + draw(modes_text[graph_settings.mode]); + break; + } case FFT_WINDOW: { - changed_window = true; - graph_settings.window_func ++; - if(graph_settings.window_func >= WINDOW_COUNT) - graph_settings.window_func = 0; - break; - } + changed_window = true; + graph_settings.window_func ++; + if(graph_settings.window_func >= WINDOW_COUNT) + graph_settings.window_func = 0; + break; + } case FFT_SCALE: { - graph_settings.logarithmic = !graph_settings.logarithmic; - draw(scales_text[graph_settings.logarithmic ? 1 : 0]); - break; - } + graph_settings.logarithmic = !graph_settings.logarithmic; + draw(scales_text[graph_settings.logarithmic ? 1 : 0]); + break; + } case FFT_ORIENTATION: { - graph_settings.orientation_vertical = !graph_settings.orientation_vertical; - draw(0); - break; - } + graph_settings.orientation_vertical = !graph_settings.orientation_vertical; + draw(0); + break; + } default: { - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - return PLUGIN_USB_CONNECTED; - } + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + return PLUGIN_USB_CONNECTED; + } } } - - /* Handle our input thread. We haven't yield()'d since our last mutex_unlock, so we know we have the mutex */ - rb->mutex_lock(&input_mutex); - input_thread_run = false; - rb->mutex_unlock(&input_mutex); - rb->thread_wait(input_thread); + +#ifdef HAVE_RECORDING + if (recording_mode) + { + rb->pcm_stop_recording(); + rb->pcm_close_recording(); + } + else +#endif + { + /* Handle our input thread. We haven't yield()'d since our last mutex_unlock, so we know we have the mutex */ + rb->mutex_lock(&input_mutex); + input_thread_run = false; + rb->mutex_unlock(&input_mutex); + rb->thread_wait(input_thread); + } #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(false);