From a6409c868663a90673147e6f778ea310f08d4f24 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Tue, 18 Dec 2012 20:32:23 -0500 Subject: Adapt FFT plugin to use floating on-screen display. Helps appearance especially with the spectrogram view since it no longer must be scrolled out of the way to avoid artifacts. Additionally, allow settings to be saved and clean up some misellaneous stuff to help graphics performance. Still other "pointless" work to do on it... :-) Change-Id: Iff2f725b0237ffb430e85f591675c2062b159abc --- apps/plugins/fft/fft.c | 1235 +++++++++++++++++++++++++----------------------- 1 file changed, 652 insertions(+), 583 deletions(-) diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c index 1e323ec7bd..709fbf9f46 100644 --- a/apps/plugins/fft/fft.c +++ b/apps/plugins/fft/fft.c @@ -1,5 +1,5 @@ /*************************************************************************** -* __________ __ ___. + * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < @@ -21,6 +21,8 @@ #include "plugin.h" #include "lib/helper.h" +#include "lib/pluginlib_exit.h" +#include "lib/configfile.h" #include "lib/xlcd.h" #include "math.h" #include "fracmul.h" @@ -28,6 +30,7 @@ #include "lib/grey.h" #endif #include "lib/mylcd.h" +#include "lib/osd.h" @@ -318,6 +321,9 @@ GREY_INFO_STRUCT #include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */ #include "const.h" + +/******************************* FFT globals *******************************/ + #define LCD_SIZE MAX(LCD_WIDTH, LCD_HEIGHT) #if (LCD_SIZE <= 511) @@ -331,14 +337,14 @@ GREY_INFO_STRUCT #define ARRAYLEN_IN (FFT_SIZE) #define ARRAYLEN_OUT (FFT_SIZE) #define ARRAYLEN_PLOT (FFT_SIZE/2-1) /* FFT is symmetric, ignore DC */ -#define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE-1)) +#define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+\ + sizeof(kiss_fft_cpx)*(FFT_SIZE-1)) #define __COEFF(type,size) type##_##size -#define _COEFF(x, y) __COEFF(x,y) /* force the preprocessor to evaluate FFT_SIZE) */ +#define _COEFF(x, y) __COEFF(x,y) /* force CPP evaluation of FFT_SIZE */ #define HANN_COEFF _COEFF(hann, FFT_SIZE) #define HAMMING_COEFF _COEFF(hamming, FFT_SIZE) -/****************************** Globals ****************************/ /* cacheline-aligned buffers with COP, otherwise word-aligned */ /* CPU/COP only applies when compiled for more than one core */ @@ -370,75 +376,156 @@ static kiss_fft_cfg fft_state SHAREDBSS_ATTR; static char fft_buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE_FFT)] CACHEALIGN_AT_LEAST_ATTR(4); /* CPU */ -static int32_t plot_history[ARRAYLEN_PLOT]; -static int32_t plot[ARRAYLEN_PLOT]; +static uint32_t linf_magnitudes[ARRAYLEN_PLOT]; /* ling freq bin plot */ +static uint32_t logf_magnitudes[ARRAYLEN_PLOT]; /* log freq plot output */ +static uint32_t *plot; /* use this to plot */ static struct { int16_t bin; /* integer bin number */ uint16_t frac; /* interpolation fraction */ } binlog[ARRAYLEN_PLOT] __attribute__((aligned(4))); -enum fft_window_func +/**************************** End of FFT globals ***************************/ + + +/********************************* Settings ********************************/ + +enum fft_orientation { - FFT_WF_FIRST = 0, - FFT_WF_HAMMING = 0, - FFT_WF_HANN, + FFT_MIN_OR = 0, + FFT_OR_VERT = 0, /* Amplitude vertical, frequency horizontal * */ + FFT_OR_HORZ, /* Amplitude horizontal, frequency vertical */ + FFT_MAX_OR, }; -#define FFT_WF_COUNT (FFT_WF_HANN+1) enum fft_display_mode { - FFT_DM_FIRST = 0, - FFT_DM_LINES = 0, - FFT_DM_BARS, - FFT_DM_SPECTROGRAPH, + FFT_MIN_DM = 0, + FFT_DM_LINES = 0, /* Bands are displayed as single-pixel lines * */ + FFT_DM_BARS, /* Bands are combined into wide bars */ + FFT_DM_SPECTROGRAM, /* Band amplitudes are denoted by color */ + FFT_MAX_DM, }; -#define FFT_DM_COUNT (FFT_DM_SPECTROGRAPH+1) - -static const unsigned char* const modes_text[FFT_DM_COUNT] = -{ "Lines", "Bars", "Spectrogram" }; -static const unsigned char* const amp_scales_text[2] = -{ "Linear amplitude", "Logarithmic amplitude" }; +enum fft_amp_scale +{ + FFT_MIN_AS = 0, + FFT_AS_LOG = 0, /* Amplitude is plotted on log scale * */ + FFT_AS_LIN, /* Amplitude is plotted on linear scale */ + FFT_MAX_AS, +}; -static const unsigned char* const freq_scales_text[2] = -{ "Linear frequency", "Logarithmic frequency" }; +enum fft_freq_scale +{ + FFT_MIN_FS = 0, + FFT_FS_LOG = 0, /* Frequency is plotted on log scale * */ + FFT_FS_LIN, /* Frequency is plotted on linear scale */ + FFT_MAX_FS +}; -static const unsigned char* const window_text[FFT_WF_COUNT] = -{ "Hamming window", "Hann window" }; +enum fft_window_func +{ + FFT_MIN_WF = 0, + FFT_WF_HAMMING = 0, /* Hamming window applied to each input frame * */ + FFT_WF_HANN, /* Hann window applied to each input frame */ + FFT_MAX_WF, +}; -static struct { - bool orientation_vertical; - enum fft_display_mode mode; - bool logarithmic_amp; - bool logarithmic_freq; - enum fft_window_func window_func; - int spectrogram_pos; /* row or column - only used by one at a time */ - union - { - struct - { - bool orientation : 1; - bool mode : 1; - bool amp_scale : 1; - bool freq_scale : 1; - bool window_func : 1; - bool do_clear : 1; - }; - bool clear_all; /* Write 'false' to clear all above */ - } changed; -} graph_settings SHAREDDATA_ATTR = +static struct fft_config +{ + int orientation; + int drawmode; + int amp_scale; + int freq_scale; + int window_func; +} fft_disk = { /* Defaults */ - .orientation_vertical = true, - .mode = FFT_DM_LINES, - .logarithmic_amp = true, - .logarithmic_freq = true, - .window_func = FFT_WF_HAMMING, - .spectrogram_pos = 0, - .changed = { .clear_all = false }, + .orientation = FFT_OR_VERT, + .drawmode = FFT_DM_LINES, + .amp_scale = FFT_AS_LOG, + .freq_scale = FFT_FS_LOG, + .window_func = FFT_WF_HAMMING, +}; + +#define CFGFILE_VERSION 0 +#define CFGFILE_MINVERSION 0 + +static const char cfg_filename[] = "fft.cfg"; +static struct configdata disk_config[] = +{ + { TYPE_ENUM, FFT_MIN_OR, FFT_MAX_OR, + { .int_p = &fft_disk.orientation }, "orientation", + (char * []){ [FFT_OR_VERT] = "vertical", + [FFT_OR_HORZ] = "horizontal" } }, + { TYPE_ENUM, FFT_MIN_DM, FFT_MAX_DM, + { .int_p = &fft_disk.drawmode }, "drawmode", + (char * []){ [FFT_DM_LINES] = "lines", + [FFT_DM_BARS] = "bars", + [FFT_DM_SPECTROGRAM] = "spectrogram" } }, + { TYPE_ENUM, FFT_MIN_AS, FFT_MAX_AS, + { .int_p = &fft_disk.amp_scale }, "amp scale", + (char * []){ [FFT_AS_LOG] = "logarithmic", + [FFT_AS_LIN] = "linear" } }, + { TYPE_ENUM, FFT_MIN_FS, FFT_MAX_FS, + { .int_p = &fft_disk.freq_scale }, "freq scale", + (char * []){ [FFT_FS_LOG] = "logarithmic", + [FFT_FS_LIN] = "linear" } }, + { TYPE_ENUM, FFT_MIN_WF, FFT_MAX_WF, + { .int_p = &fft_disk.window_func }, "window function", + (char * []){ [FFT_WF_HAMMING] = "hamming", + [FFT_WF_HANN] = "hann" } }, }; +/* Hint flags for setting changes */ +enum fft_setting_flags +{ + FFT_SETF_OR = 1 << 0, + FFT_SETF_DM = 1 << 1, + FFT_SETF_AS = 1 << 2, +#ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */ + FFT_SETF_FS = 1 << 3, +#endif + FFT_SETF_WF = 1 << 4, + FFT_SETF_ALL = 0x1f +}; + +/***************************** End of settings *****************************/ + + +/**************************** Operational data *****************************/ + +#define COLOR_DEFAULT_FG MYLCD_DEFAULT_FG +#define COLOR_DEFAULT_BG MYLCD_DEFAULT_BG + +#ifdef HAVE_LCD_COLOR +#define COLOR_MESSAGE_FRAME LCD_RGBPACK(0xc6, 0x00, 0x00) +#define COLOR_MESSAGE_BG LCD_BLACK +#define COLOR_MESSAGE_FG LCD_WHITE +#else +#define COLOR_MESSAGE_FRAME GREY_DARKGRAY +#define COLOR_MESSAGE_BG GREY_WHITE +#define COLOR_MESSAGE_FG GREY_BLACK +#endif + +#define FFT_OSD_MARGIN_SIZE 1 + +#define FFT_PERIOD (HZ/50) /* How fast to try to go */ + +/* Based on feeding-in a 0db sinewave at FS/4 */ +#define QLOG_MAX 0x0009154B +/* Fudge it a little or it's not very visbile */ +#define QLIN_MAX (0x00002266 >> 1) + +static struct fft_config fft; +typedef void (* fft_drawfn_t)(unsigned, unsigned); +static fft_drawfn_t fft_drawfn = NULL; /* plotting function */ +static int fft_spectrogram_pos = -1; /* row or column - only used by one at a time */ +static uint32_t fft_graph_scale = 0; /* max level over time, for scaling display */ +static int fft_message_id = -1; /* current message id displayed */ +static char fft_osd_message[32]; /* current message string displayed */ +static long fft_next_frame_tick = 0; /* next tick to attempt drawing */ + #ifdef HAVE_LCD_COLOR #define SHADES BMPWIDTH_fft_colors #define SPECTROGRAPH_PALETTE(index) (fft_colors[index]) @@ -447,121 +534,103 @@ static struct { #define SPECTROGRAPH_PALETTE(index) (255 - (index)) #endif -/************************* End of globals *************************/ +/************************* End of operational data *************************/ -/************************* Math functions *************************/ -/* Based on feeding-in a 0db sinewave at FS/4 */ -#define QLOG_MAX 0x0009154B -/* fudge it a little or it's not very visbile */ -#define QLIN_MAX (0x00002266 >> 1) +/***************************** Math functions ******************************/ /* Apply window function to input */ static void apply_window_func(enum fft_window_func mode) { - int i; - - switch(mode) + static const int16_t * const coefs[] = { - case FFT_WF_HAMMING: - for(i = 0; i < ARRAYLEN_IN; ++i) - { - input[i].r = (input[i].r * HAMMING_COEFF[i] + 16384) >> 15; - } - break; + [FFT_WF_HAMMING] = HAMMING_COEFF, + [FFT_WF_HANN] = HANN_COEFF, + }; - case FFT_WF_HANN: - for(i = 0; i < ARRAYLEN_IN; ++i) - { - input[i].r = (input[i].r * HANN_COEFF[i] + 16384) >> 15; - } - break; - } + const int16_t * const c = coefs[mode]; + + for(int i = 0; i < ARRAYLEN_IN; ++i) + input[i].r = (input[i].r * c[i] + 16384) >> 15; } /* Calculates the magnitudes from complex numbers and returns the maximum */ -static int32_t calc_magnitudes(bool logarithmic_amp) +static unsigned calc_magnitudes(enum fft_amp_scale scale) { /* A major assumption made when calculating the Q*MAX constants * is that the maximum magnitude is 29 bits long. */ - uint32_t max = 0; + unsigned this_max = 0; kiss_fft_cpx *this_output = output[output_head] + 1; /* skip DC */ - int i; /* Calculate the magnitude, discarding the phase. */ - for(i = 0; i < ARRAYLEN_PLOT; ++i) + for(int i = 0; i < ARRAYLEN_PLOT; ++i) { int32_t re = this_output[i].r; int32_t im = this_output[i].i; - uint32_t tmp = re*re + im*im; + uint32_t d = re*re + im*im; - if(tmp > 0) + if(d > 0) { - if(tmp > 0x7FFFFFFF) /* clip */ + if(d > 0x7FFFFFFF) /* clip */ { - tmp = 0x7FFFFFFF; /* if our assumptions are correct, - this should never happen. It's just - a safeguard. */ + d = 0x7FFFFFFF; /* if our assumptions are correct, + this should never happen. It's just + a safeguard. */ } - if(logarithmic_amp) + if(scale == FFT_AS_LOG) { - if(tmp < 0x8000) /* be more precise */ + if(d < 0x8000) /* be more precise */ { /* ln(x ^ .5) = .5*ln(x) */ - tmp = fp16_log(tmp << 16) >> 1; + d = fp16_log(d << 16) >> 1; } else { - tmp = isqrt(tmp); /* linear scaling, nothing - bad should happen */ - tmp = fp16_log(tmp << 16); /* the log function - expects s15.16 values */ + d = isqrt(d); /* linear scaling, nothing + bad should happen */ + d = fp16_log(d << 16); /* the log function + expects s15.16 values */ } } else { - tmp = isqrt(tmp); /* linear scaling, nothing - bad should happen */ + d = isqrt(d); /* linear scaling, nothing + bad should happen */ } } /* Length 2 moving average - last transform and this one */ - tmp = (plot_history[i] + tmp) >> 1; - plot[i] = tmp; - plot_history[i] = tmp; + linf_magnitudes[i] = (linf_magnitudes[i] + d) >> 1; - if(tmp > max) - max = tmp; + if(d > this_max) + this_max = d; } - return max; + return this_max; } /* Move plot bins into a logarithmic scale by sliding them towards the * Nyquist bin according to the translation in the binlog array. */ -static void logarithmic_plot_translate(void) +static void log_plot_translate(void) { - int i; - - for(i = ARRAYLEN_PLOT-1; i > 0; --i) + for(int i = ARRAYLEN_PLOT-1; i > 0; --i) { - int bin; int s = binlog[i].bin; int e = binlog[i-1].bin; - int frac = binlog[i].frac; + unsigned frac = binlog[i].frac; - bin = plot[s]; + int bin = linf_magnitudes[s]; if(frac) { /* slope < 1, Interpolate stretched bins (linear for now) */ - int diff = plot[s+1] - bin; + int diff = linf_magnitudes[s+1] - bin; do { - plot[i] = bin + FRACMUL(frac << 15, diff); + logf_magnitudes[i] = bin + FRACMUL(frac << 15, diff); frac = binlog[--i].frac; } while(frac); @@ -571,33 +640,32 @@ static void logarithmic_plot_translate(void) /* slope > 1, Find peak of two or more bins */ while(--s > e) { - int val = plot[s]; + int val = linf_magnitudes[s]; if (val > bin) bin = val; } } - plot[i] = bin; + logf_magnitudes[i] = bin; } } /* Calculates the translation for logarithmic plot bins */ static void logarithmic_plot_init(void) { - int i, j; /* * log: y = round(n * ln(x) / ln(n)) * anti: y = round(exp(x * ln(n) / n)) */ - j = fp16_log((ARRAYLEN_PLOT - 1) << 16); - for(i = 0; i < ARRAYLEN_PLOT; ++i) + int j = fp16_log((ARRAYLEN_PLOT - 1) << 16); + for(int i = 0; i < ARRAYLEN_PLOT; ++i) { binlog[i].bin = (fp16_exp(i * j / (ARRAYLEN_PLOT - 1)) + 32768) >> 16; } /* setup fractions for interpolation of stretched bins */ - for(i = 0; i < ARRAYLEN_PLOT-1; i = j) + for(int i = 0; i < ARRAYLEN_PLOT-1; i = j) { j = i + 1; @@ -619,192 +687,13 @@ static void logarithmic_plot_init(void) } } -/************************ End of math functions ***********************/ - -/********************* Plotting functions (modes) *********************/ -static void draw_lines_vertical(void); -static void draw_lines_horizontal(void); -static void draw_bars_vertical(void); -static void draw_bars_horizontal(void); -static void draw_spectrogram_vertical(void); -static void draw_spectrogram_horizontal(void); - -#define COLOR_DEFAULT_FG MYLCD_DEFAULT_FG -#define COLOR_DEFAULT_BG MYLCD_DEFAULT_BG - -#ifdef HAVE_LCD_COLOR -#define COLOR_MESSAGE_FRAME LCD_RGBPACK(0xc6, 0x00, 0x00) -#define COLOR_MESSAGE_BG LCD_BLACK -#define COLOR_MESSAGE_FG LCD_WHITE -#else -#define COLOR_MESSAGE_FRAME GREY_DARKGRAY -#define COLOR_MESSAGE_BG GREY_WHITE -#define COLOR_MESSAGE_FG GREY_BLACK -#endif - -#define POPUP_HPADDING 3 /* 3 px of horizontal padding and */ -#define POPUP_VPADDING 2 /* 2 px of vertical padding */ - -static void draw_message_string(const unsigned char *message, bool active) -{ - int x, y; - mylcd_getstringsize(message, &x, &y); - - /* x and y give the size of the box for the popup */ - x += POPUP_HPADDING*2; - y += POPUP_VPADDING*2; - - /* In vertical spectrogram mode, leave space for the popup - * before actually drawing it (if space is needed) */ - if(active && - graph_settings.mode == FFT_DM_SPECTROGRAPH && - graph_settings.orientation_vertical && - graph_settings.spectrogram_pos >= LCD_WIDTH - x) - { - mylcd_scroll_left(graph_settings.spectrogram_pos - - LCD_WIDTH + x); - graph_settings.spectrogram_pos = LCD_WIDTH - x - 1; - } - - mylcd_set_foreground(COLOR_MESSAGE_FRAME); - mylcd_fillrect(LCD_WIDTH - x, 0, LCD_WIDTH - 1, y); - - mylcd_set_foreground(COLOR_MESSAGE_FG); - mylcd_set_background(COLOR_MESSAGE_BG); - mylcd_putsxy(LCD_WIDTH - x + POPUP_HPADDING, - POPUP_VPADDING, message); - mylcd_set_foreground(COLOR_DEFAULT_FG); - mylcd_set_background(COLOR_DEFAULT_BG); -} - -static void draw(const unsigned char* message) -{ - static long show_message_tick = 0; - static const unsigned char* last_message = 0; - - if(message != NULL) - { - last_message = message; - show_message_tick = (*rb->current_tick + HZ) | 1; - } - - /* maybe take additional actions depending upon the changed setting */ - if(graph_settings.changed.orientation) - { - graph_settings.changed.amp_scale = true; - graph_settings.changed.do_clear = true; - } - - if(graph_settings.changed.mode) - { - graph_settings.changed.amp_scale = true; - graph_settings.changed.do_clear = true; - } - - if(graph_settings.changed.amp_scale) - memset(plot_history, 0, sizeof (plot_history)); - - if(graph_settings.changed.freq_scale) - graph_settings.changed.freq_scale = true; - - mylcd_set_foreground(COLOR_DEFAULT_FG); - mylcd_set_background(COLOR_DEFAULT_BG); +/************************** End of math functions **************************/ - switch (graph_settings.mode) - { - default: - case FFT_DM_LINES: { - - mylcd_clear_display(); - - if (graph_settings.orientation_vertical) - draw_lines_vertical(); - else - draw_lines_horizontal(); - break; - } - case FFT_DM_BARS: { - mylcd_clear_display(); - - if(graph_settings.orientation_vertical) - draw_bars_vertical(); - else - draw_bars_horizontal(); - - break; - } - case FFT_DM_SPECTROGRAPH: { - - if(graph_settings.changed.do_clear) - { - graph_settings.spectrogram_pos = 0; - mylcd_clear_display(); - } - - if(graph_settings.orientation_vertical) - draw_spectrogram_vertical(); - else - draw_spectrogram_horizontal(); - break; - } - } +/*********************** Plotting functions (modes) ************************/ - if(show_message_tick != 0) - { - if(TIME_BEFORE(*rb->current_tick, show_message_tick)) - { - /* We have a message to show */ - draw_message_string(last_message, true); - } - else - { - /* Stop drawing message */ - show_message_tick = 0; - } - } - else if(last_message != NULL) - { - if(graph_settings.mode == FFT_DM_SPECTROGRAPH) - { - /* Spectrogram mode - need to erase the popup */ - int x, y; - mylcd_getstringsize(last_message, &x, &y); - /* Recalculate the size */ - x += POPUP_HPADDING*2; - y += POPUP_VPADDING*2; - - if(!graph_settings.orientation_vertical) - { - /* In horizontal spectrogram mode, just scroll up by Y lines */ - mylcd_scroll_up(y); - graph_settings.spectrogram_pos -= y; - if(graph_settings.spectrogram_pos < 0) - graph_settings.spectrogram_pos = 0; - } - else - { - /* In vertical spectrogram mode, erase the popup */ - mylcd_set_foreground(COLOR_DEFAULT_BG); - mylcd_fillrect(graph_settings.spectrogram_pos + 1, 0, - LCD_WIDTH, y); - mylcd_set_foreground(COLOR_DEFAULT_FG); - } - } - /* else These modes clear the screen themselves */ - - last_message = NULL; - } - - mylcd_update(); - - graph_settings.changed.clear_all = false; -} - -static void draw_lines_vertical(void) +static void draw_lines_vertical(unsigned this_max, unsigned graph_max) { - static int max = 0; - #if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */ const int offset = 0; const int plotwidth = LCD_WIDTH; @@ -813,13 +702,7 @@ static void draw_lines_vertical(void) const int plotwidth = ARRAYLEN_PLOT; #endif - int this_max; - int i, x; - - if(graph_settings.changed.amp_scale) - max = 0; /* reset the graph on scaling mode change */ - - this_max = calc_magnitudes(graph_settings.logarithmic_amp); + mylcd_clear_display(); if(this_max == 0) { @@ -827,21 +710,16 @@ static void draw_lines_vertical(void) return; } - if(graph_settings.logarithmic_freq) - logarithmic_plot_translate(); - - /* take the maximum of neighboring bins if we have to scale the graph - * horizontally */ + /* take the maximum of neighboring bins if we have to scale down the + * graph horizontally */ if(LCD_WIDTH < ARRAYLEN_PLOT) /* graph compression */ { int bins_acc = LCD_WIDTH / 2; - int bins_max = 0; + unsigned bins_max = 0; - i = 0, x = 0; - - for(;;) + for(int i = 0, x = 0; i < ARRAYLEN_PLOT; ++i) { - int bin = plot[i++]; + unsigned bin = plot[i]; if(bin > bins_max) bins_max = bin; @@ -850,14 +728,10 @@ static void draw_lines_vertical(void) if(bins_acc >= ARRAYLEN_PLOT) { - plot[x] = bins_max; - - if(bins_max > max) - max = bins_max; - - if(++x >= LCD_WIDTH) - break; + int h = LCD_HEIGHT*bins_max / graph_max; + mylcd_vline(x, LCD_HEIGHT - h, LCD_HEIGHT-1); + x++; bins_acc -= ARRAYLEN_PLOT; bins_max = 0; } @@ -865,21 +739,16 @@ static void draw_lines_vertical(void) } else { - if(this_max > max) - max = this_max; - } - - for(x = 0; x < plotwidth; ++x) - { - int h = LCD_HEIGHT*plot[x] / max; - mylcd_vline(x + offset, LCD_HEIGHT - h, LCD_HEIGHT-1); + for(int i = 0; i < plotwidth; ++i) + { + int h = LCD_HEIGHT*plot[i] / graph_max; + mylcd_vline(i + offset, LCD_HEIGHT - h, LCD_HEIGHT-1); + } } } -static void draw_lines_horizontal(void) +static void draw_lines_horizontal(unsigned this_max, unsigned graph_max) { - static int max = 0; - #if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */ const int offset = 0; const int plotwidth = LCD_HEIGHT; @@ -888,13 +757,7 @@ static void draw_lines_horizontal(void) const int plotwidth = ARRAYLEN_PLOT; #endif - int this_max; - int y; - - if(graph_settings.changed.amp_scale) - max = 0; /* reset the graph on scaling mode change */ - - this_max = calc_magnitudes(graph_settings.logarithmic_amp); + mylcd_clear_display(); if(this_max == 0) { @@ -902,38 +765,28 @@ static void draw_lines_horizontal(void) return; } - if(graph_settings.logarithmic_freq) - logarithmic_plot_translate(); - /* take the maximum of neighboring bins if we have to scale the graph * horizontally */ if(LCD_HEIGHT < ARRAYLEN_PLOT) /* graph compression */ { int bins_acc = LCD_HEIGHT / 2; - int bins_max = 0; - int i = 0; - - y = 0; + unsigned bins_max = 0; - for(;;) + for(int i = 0, y = 0; i < ARRAYLEN_PLOT; ++i) { - int bin = plot[i++]; + unsigned bin = plot[i]; - if (bin > bins_max) + if(bin > bins_max) bins_max = bin; bins_acc += LCD_HEIGHT; if(bins_acc >= ARRAYLEN_PLOT) { - plot[y] = bins_max; - - if(bins_max > max) - max = bins_max; - - if(++y >= LCD_HEIGHT) - break; + int w = LCD_WIDTH*bins_max / graph_max; + mylcd_hline(0, w - 1, y); + y++; bins_acc -= ARRAYLEN_PLOT; bins_max = 0; } @@ -941,21 +794,16 @@ static void draw_lines_horizontal(void) } else { - if(this_max > max) - max = this_max; - } - - for(y = 0; y < plotwidth; ++y) - { - int w = LCD_WIDTH*plot[y] / max; - mylcd_hline(0, w - 1, y + offset); + for(int i = 0; i < plotwidth; ++i) + { + int w = LCD_WIDTH*plot[i] / graph_max; + mylcd_hline(0, w - 1, i + offset); + } } } -static void draw_bars_vertical(void) +static void draw_bars_vertical(unsigned this_max, unsigned graph_max) { - static int max = 0; - #if LCD_WIDTH < LCD_HEIGHT const int bars = 15; #else @@ -964,26 +812,20 @@ static void draw_bars_vertical(void) const int border = 2; const int barwidth = LCD_WIDTH / (bars + border); const int width = barwidth - border; - const int offset = (LCD_WIDTH - bars*barwidth) / 2; - - if(graph_settings.changed.amp_scale) - max = 0; /* reset the graph on scaling mode change */ + const int offset = (LCD_WIDTH - bars*barwidth + border) / 2; + mylcd_clear_display(); mylcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-1); /* Draw baseline */ - if(calc_magnitudes(graph_settings.logarithmic_amp) == 0) + if(this_max == 0) return; /* nothing more to draw */ - if(graph_settings.logarithmic_freq) - logarithmic_plot_translate(); - int bins_acc = bars / 2; - int bins_max = 0; - int x = 0, i = 0; + unsigned bins_max = 0; - for(;;) + for(int i = 0, x = offset;; ++i) { - int bin = plot[i++]; + unsigned bin = plot[i]; if(bin > bins_max) bins_max = bin; @@ -992,30 +834,21 @@ static void draw_bars_vertical(void) if(bins_acc >= ARRAYLEN_PLOT) { - plot[x] = bins_max; + int h = LCD_HEIGHT*bins_max / graph_max; + mylcd_fillrect(x, LCD_HEIGHT - h, width, h - 1); - if(bins_max > max) - max = bins_max; - - if(++x >= bars) + if(i >= ARRAYLEN_PLOT-1) break; + x += barwidth; bins_acc -= ARRAYLEN_PLOT; bins_max = 0; } } - - for(i = 0, x = offset; i < bars; ++i, x += barwidth) - { - int h = LCD_HEIGHT * plot[i] / max; - mylcd_fillrect(x, LCD_HEIGHT - h, width, h - 1); - } } -static void draw_bars_horizontal(void) +static void draw_bars_horizontal(unsigned this_max, unsigned graph_max) { - static int max = 0; - #if LCD_WIDTH < LCD_HEIGHT const int bars = 20; #else @@ -1024,70 +857,56 @@ static void draw_bars_horizontal(void) const int border = 2; const int barwidth = LCD_HEIGHT / (bars + border); const int height = barwidth - border; - const int offset = (LCD_HEIGHT - bars*barwidth) / 2; - - if(graph_settings.changed.amp_scale) - max = 0; /* reset the graph on scaling mode change */ + const int offset = (LCD_HEIGHT - bars*barwidth + border) / 2; + mylcd_clear_display(); mylcd_vline(0, 0, LCD_HEIGHT-1); /* Draw baseline */ - if(calc_magnitudes(graph_settings.logarithmic_amp) == 0) + if(this_max == 0) return; /* nothing more to draw */ - if(graph_settings.logarithmic_freq) - logarithmic_plot_translate(); - int bins_acc = bars / 2; - int bins_max = 0; - int y = 0, i = 0; + unsigned bins_max = 0; - for(;;) + for(int i = 0, y = offset;; ++i) { - int bin = plot[i++]; + unsigned bin = plot[i]; - if (bin > bins_max) + if(bin > bins_max) bins_max = bin; bins_acc += bars; if(bins_acc >= ARRAYLEN_PLOT) { - plot[y] = bins_max; - - if(bins_max > max) - max = bins_max; + int w = LCD_WIDTH*bins_max / graph_max; + mylcd_fillrect(1, y, w, height); - if(++y >= bars) + if(i >= ARRAYLEN_PLOT-1) break; + y += barwidth; bins_acc -= ARRAYLEN_PLOT; bins_max = 0; } } - - for(i = 0, y = offset; i < bars; ++i, y += barwidth) - { - int w = LCD_WIDTH * plot[i] / max; - mylcd_fillrect(1, y, w, height); - } } -static void draw_spectrogram_vertical(void) +static void draw_spectrogram_vertical(unsigned this_max, unsigned graph_max) { - const int32_t scale_factor = MIN(LCD_HEIGHT, ARRAYLEN_PLOT); - - calc_magnitudes(graph_settings.logarithmic_amp); + const int scale_factor = MIN(LCD_HEIGHT, ARRAYLEN_PLOT); - if(graph_settings.logarithmic_freq) - logarithmic_plot_translate(); + if(fft_spectrogram_pos < LCD_WIDTH-1) + fft_spectrogram_pos++; + else + mylcd_scroll_left(1); int bins_acc = scale_factor / 2; - int bins_max = 0; - int y = 0, i = 0; + unsigned bins_max = 0; - for(;;) + for(int i = 0, y = LCD_HEIGHT-1;; ++i) { - int bin = plot[i++]; + unsigned bin = plot[i]; if(bin > bins_max) bins_max = bin; @@ -1096,12 +915,7 @@ static void draw_spectrogram_vertical(void) if(bins_acc >= ARRAYLEN_PLOT) { - unsigned index; - - if(graph_settings.logarithmic_amp) - index = (SHADES-1)*bins_max / QLOG_MAX; - else - index = (SHADES-1)*bins_max / QLIN_MAX; + unsigned index = (SHADES-1)*bins_max / graph_max; /* These happen because we exaggerate the graph a little for * linear mode */ @@ -1109,10 +923,9 @@ static void draw_spectrogram_vertical(void) index = SHADES-1; mylcd_set_foreground(SPECTROGRAPH_PALETTE(index)); - mylcd_drawpixel(graph_settings.spectrogram_pos, - scale_factor-1 - y); + mylcd_drawpixel(fft_spectrogram_pos, y); - if(++y >= scale_factor) + if(--y < 0) break; bins_acc -= ARRAYLEN_PLOT; @@ -1120,28 +933,24 @@ static void draw_spectrogram_vertical(void) } } - if(graph_settings.spectrogram_pos < LCD_WIDTH-1) - graph_settings.spectrogram_pos++; - else - mylcd_scroll_left(1); + (void)this_max; } -static void draw_spectrogram_horizontal(void) +static void draw_spectrogram_horizontal(unsigned this_max, unsigned graph_max) { - const int32_t scale_factor = MIN(LCD_WIDTH, ARRAYLEN_PLOT); + const int scale_factor = MIN(LCD_WIDTH, ARRAYLEN_PLOT); - calc_magnitudes(graph_settings.logarithmic_amp); - - if(graph_settings.logarithmic_freq) - logarithmic_plot_translate(); + if(fft_spectrogram_pos < LCD_HEIGHT-1) + fft_spectrogram_pos++; + else + mylcd_scroll_up(1); int bins_acc = scale_factor / 2; - int bins_max = 0; - int x = 0, i = 0; + unsigned bins_max = 0; - for(;;) + for(int i = 0, x = 0;; ++i) { - int bin = plot[i++]; + unsigned bin = plot[i]; if(bin > bins_max) bins_max = bin; @@ -1150,12 +959,7 @@ static void draw_spectrogram_horizontal(void) if(bins_acc >= ARRAYLEN_PLOT) { - unsigned index; - - if(graph_settings.logarithmic_amp) - index = (SHADES-1)*bins_max / QLOG_MAX; - else - index = (SHADES-1)*bins_max / QLIN_MAX; + unsigned index = (SHADES-1)*bins_max / graph_max; /* These happen because we exaggerate the graph a little for * linear mode */ @@ -1163,9 +967,9 @@ static void draw_spectrogram_horizontal(void) index = SHADES-1; mylcd_set_foreground(SPECTROGRAPH_PALETTE(index)); - mylcd_drawpixel(x, graph_settings.spectrogram_pos); + mylcd_drawpixel(x, fft_spectrogram_pos); - if(++x >= scale_factor) + if(++x >= LCD_WIDTH) break; bins_acc -= ARRAYLEN_PLOT; @@ -1173,15 +977,14 @@ static void draw_spectrogram_horizontal(void) } } - if(graph_settings.spectrogram_pos < LCD_HEIGHT-1) - graph_settings.spectrogram_pos++; - else - mylcd_scroll_up(1); + (void)this_max; } -/********************* End of plotting functions (modes) *********************/ +/******************** End of plotting functions (modes) ********************/ + + +/***************************** FFT functions *******************************/ -/****************************** FFT functions ********************************/ static bool is_playing(void) { return rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_PLAYING; @@ -1232,7 +1035,7 @@ static inline bool fft_get_fft(void) input[fft_idx].r = (left + right) >> 1; /* to mono */ } while (fft_idx++, --count > 0); - apply_window_func(graph_settings.window_func); + apply_window_func(fft.window_func); rb->yield(); @@ -1246,14 +1049,14 @@ static inline bool fft_get_fft(void) #if NUM_CORES > 1 /* use a worker thread if there is another processor core */ static volatile bool fft_thread_run SHAREDDATA_ATTR = false; -static unsigned long fft_thread; +static unsigned long fft_thread = 0; static long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))] CACHEALIGN_AT_LEAST_ATTR(4); static void fft_thread_entry(void) { - if (!fft_init_fft_lib()) + if(!fft_init_fft_lib()) { output_tail = -1; /* tell that we bailed */ fft_thread_run = true; @@ -1276,7 +1079,8 @@ static void fft_thread_entry(void) continue; } - /* write back output for other processor and invalidate for next frame read */ + /* write back output for other processor and invalidate for next + frame read */ rb->commit_discard_dcache(); int new_tail = output_tail ^ 1; @@ -1364,168 +1168,433 @@ static inline void fft_close_fft(void) /* nothing to do */ } #endif /* NUM_CORES */ -/*************************** End of FFT functions ****************************/ -enum plugin_status plugin_start(const void* parameter) +/************************** End of FFT functions ***************************/ + + +/****************************** OSD functions ******************************/ + +/* Format a message to display */ +static void fft_osd_format_message(enum fft_setting_flags id) { - /* Defaults */ - bool run = true; - bool showing_warning = false; + const char *msg = ""; - if (!fft_init_fft()) - return PLUGIN_ERROR; + switch (id) + { + case FFT_SETF_DM: + msg = (const char * [FFT_MAX_DM]) { + [FFT_DM_LINES] = "Lines", + [FFT_DM_BARS] = "Bars", + [FFT_DM_SPECTROGRAM] = "Spectrogram", + }[fft.drawmode]; + break; + + case FFT_SETF_WF: + msg = (const char * [FFT_MAX_WF]) { + [FFT_WF_HAMMING] = "Hamming window", + [FFT_WF_HANN] = "Hann window", + }[fft.window_func]; + break; + + case FFT_SETF_AS: + msg = (const char * [FFT_MAX_AS]) { + [FFT_AS_LOG] = "Logarithmic amplitude", + [FFT_AS_LIN] = "Linear amplitude" + }[fft.amp_scale]; + break; + +#ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */ + case FFT_SETF_FS: + msg = (const char * [FFT_MAX_FS]) { + [FFT_FS_LOG] = "Logarithmic frequency", + [FFT_FS_LIN] = "Linear frequency", + }[fft.freq_scale]; + break; +#endif + + case FFT_SETF_OR: + rb->snprintf(fft_osd_message, sizeof (fft_osd_message), + (const char * [FFT_MAX_OR]) { + [FFT_OR_VERT] = "Vertical %s", + [FFT_OR_HORZ] = "Horizontal %s", + }[fft.orientation], + (const char * [FFT_MAX_DM]) { + [FFT_DM_LINES ... FFT_DM_BARS] = "amplitude", + [FFT_DM_SPECTROGRAM] = "frequency" + }[fft.drawmode]); + return; + +#if 0 + /* Pertentially */ + case FFT_SETF_VOLUME: + rb->snprintf(fft_osd_message, sizeof (fft_osd_message), + "Volume: %d%s", + rb->sound_val2phys(SOUND_VOLUME, global_settings.volume), + rb->sound_unit(SOUND_VOLUME)); + return; +#endif + + default: + break; + } + + /* Default action: copy string */ + rb->strlcpy(fft_osd_message, msg, sizeof (fft_osd_message)); +} + +static void fft_osd_draw_cb(int x, int y, int width, int height) +{ +#if LCD_DEPTH > 1 + mylcd_set_foreground(COLOR_MESSAGE_FG); + mylcd_set_background(COLOR_MESSAGE_BG); +#endif +#if FFT_OSD_MARGIN_SIZE != 0 + mylcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); + mylcd_fillrect(1, 1, width - 2, height - 2); + mylcd_set_drawmode(DRMODE_SOLID); +#endif + mylcd_putsxy(1+FFT_OSD_MARGIN_SIZE, 1+FFT_OSD_MARGIN_SIZE, + fft_osd_message); +#if LCD_DEPTH > 1 + mylcd_set_foreground(COLOR_MESSAGE_FRAME); +#endif + + mylcd_drawrect(0, 0, width, height); + + (void)x; (void)y; +} + +static void fft_osd_show_message(enum fft_setting_flags id) +{ + fft_osd_format_message(id); + + if(!myosd_enabled()) + return; + + int width, height; + int maxwidth, maxheight; + + mylcd_set_viewport(myosd_get_viewport()); + myosd_get_max_dims(&maxwidth, &maxheight); + mylcd_setfont(FONT_UI); + mylcd_getstringsize(fft_osd_message, &width, &height); + mylcd_set_viewport(NULL); + + width += 2 + 2*FFT_OSD_MARGIN_SIZE; + if(width > maxwidth) + width = maxwidth; + + height += 2 + 2*FFT_OSD_MARGIN_SIZE; + if(height > maxheight) + height = maxheight; + + bool drawn = myosd_update_pos((LCD_WIDTH - width) / 2, + (LCD_HEIGHT - height) / 2, + width, height); + + myosd_show(OSD_SHOW | (drawn ? 0 : OSD_UPDATENOW)); +} + +static void fft_popupmsg(enum fft_setting_flags id) +{ + fft_message_id = id; +} + +/************************** End of OSD functions ***************************/ + + +static void fft_setting_update(unsigned which) +{ + static fft_drawfn_t fft_drawfns[FFT_MAX_DM][FFT_MAX_OR] = + { + [FFT_DM_LINES] = + { + [FFT_OR_HORZ] = draw_lines_horizontal, + [FFT_OR_VERT] = draw_lines_vertical, + }, + [FFT_DM_BARS] = + { + [FFT_OR_HORZ] = draw_bars_horizontal, + [FFT_OR_VERT] = draw_bars_vertical, + }, + [FFT_DM_SPECTROGRAM] = + { + [FFT_OR_HORZ] = draw_spectrogram_horizontal, + [FFT_OR_VERT] = draw_spectrogram_vertical, + }, + }; + + if(which & (FFT_SETF_DM | FFT_SETF_OR)) + { + fft_drawfn = fft_drawfns[fft.drawmode] + [fft.orientation]; + + if(fft.drawmode == FFT_DM_SPECTROGRAM) + { + fft_spectrogram_pos = -1; + myosd_lcd_update_prepare(); + mylcd_clear_display(); + myosd_lcd_update(); + } + } + + if(which & (FFT_SETF_DM | FFT_SETF_AS)) + { + if(fft.drawmode == FFT_DM_SPECTROGRAM) + { + fft_graph_scale = fft.amp_scale == FFT_AS_LIN ? + QLIN_MAX : QLOG_MAX; + } + else + { + fft_graph_scale = 0; + } + } + +#ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */ + if(which & FFT_SETF_FS) + { + plot = fft.freq_scale == FFT_FS_LIN ? + linf_magnitudes : logf_magnitudes; + } +#endif + + if(which & FFT_SETF_AS) + { + memset(linf_magnitudes, 0, sizeof (linf_magnitudes)); + memset(logf_magnitudes, 0, sizeof (logf_magnitudes)); + } +} + +static long fft_draw(void) +{ + long tick = *rb->current_tick; + + if(fft_message_id != -1) + { + /* Show a new message */ + fft_osd_show_message((enum fft_setting_flags)fft_message_id); + fft_message_id = -1; + } + else + { + /* Monitor OSD timeout */ + myosd_monitor_timeout(); + } + + if(TIME_BEFORE(tick, fft_next_frame_tick)) + return fft_next_frame_tick - tick; /* Too early */ + + unsigned this_max; + + if(!fft_have_fft()) + { + if(is_playing()) + return HZ/100; + + /* All magnitudes == 0 thus this_max == 0 */ + for(int i = 0; i < ARRAYLEN_PLOT; i++) + linf_magnitudes[i] >>= 1; /* decay */ + this_max = 0; + } + else + { + this_max = calc_magnitudes(fft.amp_scale); + + fft_free_fft_output(); /* COP only */ + + if(fft.drawmode != FFT_DM_SPECTROGRAM && + this_max > fft_graph_scale) + { + fft_graph_scale = this_max; + } + } + + if (fft.freq_scale == FFT_FS_LOG) + log_plot_translate(); + + myosd_lcd_update_prepare(); + + mylcd_set_foreground(COLOR_DEFAULT_FG); + mylcd_set_background(COLOR_DEFAULT_BG); + + fft_drawfn(this_max, fft_graph_scale); + + myosd_lcd_update(); + + fft_next_frame_tick = tick + FFT_PERIOD; + return fft_next_frame_tick - *rb->current_tick; +} + +static void fft_osd_init(void *buf, size_t bufsize) +{ + int width, height; + mylcd_setfont(FONT_UI); + mylcd_getstringsize("M", NULL, &height); + width = LCD_WIDTH; + height += 2 + 2*FFT_OSD_MARGIN_SIZE; + myosd_init(OSD_INIT_MAJOR_HEIGHT | OSD_INIT_MINOR_MAX, buf, bufsize, + fft_osd_draw_cb, &width, &height, NULL); + myosd_set_timeout(HZ); +} + +static void fft_cleanup(void) +{ + myosd_destroy(); + + fft_close_fft(); + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cancel_cpu_boost(); +#endif #ifndef HAVE_LCD_COLOR - unsigned char *gbuf; - size_t gbuf_size = 0; - /* get the remainder of the plugin buffer */ - gbuf = (unsigned char *) rb->plugin_get_buffer(&gbuf_size); + grey_release(); +#endif + backlight_use_settings(); + + /* save settings if changed */ + if (rb->memcmp(&fft, &fft_disk, sizeof(fft))) + { + fft_disk = fft; + configfile_save(cfg_filename, disk_config, ARRAYLEN(disk_config), + CFGFILE_VERSION); + } +} + +static bool fft_setup(void) +{ + atexit(fft_cleanup); + configfile_load(cfg_filename, disk_config, ARRAYLEN(disk_config), + CFGFILE_MINVERSION); + fft = fft_disk; /* copy to running config */ + + if(!fft_init_fft()) + return false; + + /* get the remainder of the plugin buffer for OSD and perhaps + greylib */ + size_t bufsize = 0; + unsigned char *buf = rb->plugin_get_buffer(&bufsize); + +#ifndef HAVE_LCD_COLOR /* initialize the greyscale buffer.*/ - if (!grey_init(gbuf, gbuf_size, GREY_ON_COP | GREY_BUFFERED, - LCD_WIDTH, LCD_HEIGHT, NULL)) + long grey_size; + if(!grey_init(buf, bufsize, GREY_ON_COP | GREY_BUFFERED, + LCD_WIDTH, LCD_HEIGHT, &grey_size)) { rb->splash(HZ, "Couldn't init greyscale display"); - fft_close_fft(); - return PLUGIN_ERROR; + return false; } - grey_show(true); -#endif - logarithmic_plot_init(); + grey_show(true); + + buf += grey_size; + bufsize -= grey_size; +#endif /* !HAVE_LCD_COLOR */ + + fft_osd_init(buf, bufsize); #if LCD_DEPTH > 1 + myosd_lcd_update_prepare(); rb->lcd_set_backdrop(NULL); mylcd_clear_display(); - mylcd_update(); + myosd_lcd_update(); #endif backlight_ignore_timeout(); #ifdef HAVE_ADJUSTABLE_CPU_FREQ - rb->cpu_boost(true); + rb->trigger_cpu_boost(); #endif - while (run) - { - /* Unless otherwise specified, HZ/50 is around the window length - * and quite fast. We want to be done with drawing by this time. */ - long next_frame_tick = *rb->current_tick + HZ/50; - int button; - - while (!fft_have_fft()) - { - int timeout; - - if(!is_playing()) - { - showing_warning = true; - mylcd_clear_display(); - draw_message_string("No audio playing", false); - mylcd_update(); - timeout = HZ/5; - } - else - { - if(showing_warning) - { - showing_warning = false; - mylcd_clear_display(); - mylcd_update(); - } + logarithmic_plot_init(); + fft_setting_update(FFT_SETF_ALL); + fft_next_frame_tick = *rb->current_tick; - timeout = HZ/100; /* 'till end of curent tick, don't use 100% CPU */ - } + return true; +} - /* Make sure the FFT has produced something before doing anything - * but watching for buttons. Music might not be playing or things - * just aren't going well for picking up buffers so keys are - * scanned to avoid lockup. */ - button = rb->button_get_w_tmo(timeout); - if (button != BUTTON_NONE) - goto read_button; - } +enum plugin_status plugin_start(const void* parameter) +{ + bool run = true; - draw(NULL); + if(!fft_setup()) + return PLUGIN_ERROR; - fft_free_fft_output(); /* COP only */ + while(run) + { + long delay = fft_draw(); - long tick = *rb->current_tick; - if(TIME_BEFORE(tick, next_frame_tick)) - { - tick = next_frame_tick - tick; - } - else + if(delay <= 0) { + delay = 0; rb->yield(); /* tmo = 0 won't yield */ - tick = 0; } - button = rb->button_get_w_tmo(tick); - read_button: + int button = rb->button_get_w_tmo(delay); + switch (button) { case FFT_QUIT: run = false; break; - case FFT_PREV_GRAPH: { - if (graph_settings.mode-- <= FFT_DM_FIRST) - graph_settings.mode = FFT_DM_COUNT-1; - graph_settings.changed.mode = true; - draw(modes_text[graph_settings.mode]); + + case FFT_ORIENTATION: + if (++fft.orientation >= FFT_MAX_OR) + fft.orientation = FFT_MIN_OR; + + fft_setting_update(FFT_SETF_OR); + fft_popupmsg(FFT_SETF_OR); break; - } - case FFT_NEXT_GRAPH: { - if (++graph_settings.mode >= FFT_DM_COUNT) - graph_settings.mode = FFT_DM_FIRST; - graph_settings.changed.mode = true; - draw(modes_text[graph_settings.mode]); + + case FFT_PREV_GRAPH: + if (fft.drawmode-- <= FFT_MIN_DM) + fft.drawmode = FFT_MAX_DM-1; + + fft_setting_update(FFT_SETF_DM); + fft_popupmsg(FFT_SETF_DM); break; - } - case FFT_WINDOW: { - if(++graph_settings.window_func >= FFT_WF_COUNT) - graph_settings.window_func = FFT_WF_FIRST; - graph_settings.changed.window_func = true; - draw(window_text[graph_settings.window_func]); + + case FFT_NEXT_GRAPH: + if (++fft.drawmode >= FFT_MAX_DM) + fft.drawmode = FFT_MIN_DM; + + fft_setting_update(FFT_SETF_DM); + fft_popupmsg(FFT_SETF_DM); break; - } - case FFT_AMP_SCALE: { - graph_settings.logarithmic_amp = !graph_settings.logarithmic_amp; - graph_settings.changed.amp_scale = true; - draw(amp_scales_text[graph_settings.logarithmic_amp ? 1 : 0]); + + case FFT_AMP_SCALE: + if (++fft.amp_scale >= FFT_MAX_AS) + fft.amp_scale = FFT_MIN_AS; + + fft_setting_update(FFT_SETF_AS); + fft_popupmsg(FFT_SETF_AS); break; - } + #ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */ - case FFT_FREQ_SCALE: { - graph_settings.logarithmic_freq = !graph_settings.logarithmic_freq; - graph_settings.changed.freq_scale = true; - draw(freq_scales_text[graph_settings.logarithmic_freq ? 1 : 0]); + case FFT_FREQ_SCALE: + if (++fft.freq_scale >= FFT_MAX_FS) + fft.freq_scale = FFT_MIN_FS; + + fft_setting_update(FFT_SETF_FS); + fft_popupmsg(FFT_SETF_FS); break; - } #endif - case FFT_ORIENTATION: { - graph_settings.orientation_vertical = - !graph_settings.orientation_vertical; - graph_settings.changed.orientation = true; - draw(NULL); + case FFT_WINDOW: + if(++fft.window_func >= FFT_MAX_WF) + fft.window_func = FFT_MIN_WF; + + fft_setting_update(FFT_SETF_WF); + fft_popupmsg(FFT_SETF_WF); break; - } - default: { - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - return PLUGIN_USB_CONNECTED; - } + default: + exit_on_usb(button); + break; } } - fft_close_fft(); - -#ifdef HAVE_ADJUSTABLE_CPU_FREQ - rb->cpu_boost(false); -#endif -#ifndef HAVE_LCD_COLOR - grey_release(); -#endif - backlight_use_settings(); return PLUGIN_OK; (void)parameter; } -- cgit v1.2.3