summaryrefslogtreecommitdiff
path: root/apps/plugins/fft/fft.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/fft/fft.c')
-rw-r--r--apps/plugins/fft/fft.c1235
1 files 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 @@
1/*************************************************************************** 1/***************************************************************************
2* __________ __ ___. 2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
@@ -21,6 +21,8 @@
21#include "plugin.h" 21#include "plugin.h"
22 22
23#include "lib/helper.h" 23#include "lib/helper.h"
24#include "lib/pluginlib_exit.h"
25#include "lib/configfile.h"
24#include "lib/xlcd.h" 26#include "lib/xlcd.h"
25#include "math.h" 27#include "math.h"
26#include "fracmul.h" 28#include "fracmul.h"
@@ -28,6 +30,7 @@
28#include "lib/grey.h" 30#include "lib/grey.h"
29#endif 31#endif
30#include "lib/mylcd.h" 32#include "lib/mylcd.h"
33#include "lib/osd.h"
31 34
32 35
33 36
@@ -318,6 +321,9 @@ GREY_INFO_STRUCT
318#include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */ 321#include "_kiss_fft_guts.h" /* sizeof(struct kiss_fft_state) */
319#include "const.h" 322#include "const.h"
320 323
324
325/******************************* FFT globals *******************************/
326
321#define LCD_SIZE MAX(LCD_WIDTH, LCD_HEIGHT) 327#define LCD_SIZE MAX(LCD_WIDTH, LCD_HEIGHT)
322 328
323#if (LCD_SIZE <= 511) 329#if (LCD_SIZE <= 511)
@@ -331,14 +337,14 @@ GREY_INFO_STRUCT
331#define ARRAYLEN_IN (FFT_SIZE) 337#define ARRAYLEN_IN (FFT_SIZE)
332#define ARRAYLEN_OUT (FFT_SIZE) 338#define ARRAYLEN_OUT (FFT_SIZE)
333#define ARRAYLEN_PLOT (FFT_SIZE/2-1) /* FFT is symmetric, ignore DC */ 339#define ARRAYLEN_PLOT (FFT_SIZE/2-1) /* FFT is symmetric, ignore DC */
334#define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+sizeof(kiss_fft_cpx)*(FFT_SIZE-1)) 340#define BUFSIZE_FFT (sizeof(struct kiss_fft_state)+\
341 sizeof(kiss_fft_cpx)*(FFT_SIZE-1))
335 342
336#define __COEFF(type,size) type##_##size 343#define __COEFF(type,size) type##_##size
337#define _COEFF(x, y) __COEFF(x,y) /* force the preprocessor to evaluate FFT_SIZE) */ 344#define _COEFF(x, y) __COEFF(x,y) /* force CPP evaluation of FFT_SIZE */
338#define HANN_COEFF _COEFF(hann, FFT_SIZE) 345#define HANN_COEFF _COEFF(hann, FFT_SIZE)
339#define HAMMING_COEFF _COEFF(hamming, FFT_SIZE) 346#define HAMMING_COEFF _COEFF(hamming, FFT_SIZE)
340 347
341/****************************** Globals ****************************/
342/* cacheline-aligned buffers with COP, otherwise word-aligned */ 348/* cacheline-aligned buffers with COP, otherwise word-aligned */
343/* CPU/COP only applies when compiled for more than one core */ 349/* CPU/COP only applies when compiled for more than one core */
344 350
@@ -370,75 +376,156 @@ static kiss_fft_cfg fft_state SHAREDBSS_ATTR;
370static char fft_buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE_FFT)] 376static char fft_buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE_FFT)]
371 CACHEALIGN_AT_LEAST_ATTR(4); 377 CACHEALIGN_AT_LEAST_ATTR(4);
372/* CPU */ 378/* CPU */
373static int32_t plot_history[ARRAYLEN_PLOT]; 379static uint32_t linf_magnitudes[ARRAYLEN_PLOT]; /* ling freq bin plot */
374static int32_t plot[ARRAYLEN_PLOT]; 380static uint32_t logf_magnitudes[ARRAYLEN_PLOT]; /* log freq plot output */
381static uint32_t *plot; /* use this to plot */
375static struct 382static struct
376{ 383{
377 int16_t bin; /* integer bin number */ 384 int16_t bin; /* integer bin number */
378 uint16_t frac; /* interpolation fraction */ 385 uint16_t frac; /* interpolation fraction */
379} binlog[ARRAYLEN_PLOT] __attribute__((aligned(4))); 386} binlog[ARRAYLEN_PLOT] __attribute__((aligned(4)));
380 387
381enum fft_window_func 388/**************************** End of FFT globals ***************************/
389
390
391/********************************* Settings ********************************/
392
393enum fft_orientation
382{ 394{
383 FFT_WF_FIRST = 0, 395 FFT_MIN_OR = 0,
384 FFT_WF_HAMMING = 0, 396 FFT_OR_VERT = 0, /* Amplitude vertical, frequency horizontal * */
385 FFT_WF_HANN, 397 FFT_OR_HORZ, /* Amplitude horizontal, frequency vertical */
398 FFT_MAX_OR,
386}; 399};
387#define FFT_WF_COUNT (FFT_WF_HANN+1)
388 400
389enum fft_display_mode 401enum fft_display_mode
390{ 402{
391 FFT_DM_FIRST = 0, 403 FFT_MIN_DM = 0,
392 FFT_DM_LINES = 0, 404 FFT_DM_LINES = 0, /* Bands are displayed as single-pixel lines * */
393 FFT_DM_BARS, 405 FFT_DM_BARS, /* Bands are combined into wide bars */
394 FFT_DM_SPECTROGRAPH, 406 FFT_DM_SPECTROGRAM, /* Band amplitudes are denoted by color */
407 FFT_MAX_DM,
395}; 408};
396#define FFT_DM_COUNT (FFT_DM_SPECTROGRAPH+1)
397
398static const unsigned char* const modes_text[FFT_DM_COUNT] =
399{ "Lines", "Bars", "Spectrogram" };
400 409
401static const unsigned char* const amp_scales_text[2] = 410enum fft_amp_scale
402{ "Linear amplitude", "Logarithmic amplitude" }; 411{
412 FFT_MIN_AS = 0,
413 FFT_AS_LOG = 0, /* Amplitude is plotted on log scale * */
414 FFT_AS_LIN, /* Amplitude is plotted on linear scale */
415 FFT_MAX_AS,
416};
403 417
404static const unsigned char* const freq_scales_text[2] = 418enum fft_freq_scale
405{ "Linear frequency", "Logarithmic frequency" }; 419{
420 FFT_MIN_FS = 0,
421 FFT_FS_LOG = 0, /* Frequency is plotted on log scale * */
422 FFT_FS_LIN, /* Frequency is plotted on linear scale */
423 FFT_MAX_FS
424};
406 425
407static const unsigned char* const window_text[FFT_WF_COUNT] = 426enum fft_window_func
408{ "Hamming window", "Hann window" }; 427{
428 FFT_MIN_WF = 0,
429 FFT_WF_HAMMING = 0, /* Hamming window applied to each input frame * */
430 FFT_WF_HANN, /* Hann window applied to each input frame */
431 FFT_MAX_WF,
432};
409 433
410static struct { 434static struct fft_config
411 bool orientation_vertical; 435{
412 enum fft_display_mode mode; 436 int orientation;
413 bool logarithmic_amp; 437 int drawmode;
414 bool logarithmic_freq; 438 int amp_scale;
415 enum fft_window_func window_func; 439 int freq_scale;
416 int spectrogram_pos; /* row or column - only used by one at a time */ 440 int window_func;
417 union 441} fft_disk =
418 {
419 struct
420 {
421 bool orientation : 1;
422 bool mode : 1;
423 bool amp_scale : 1;
424 bool freq_scale : 1;
425 bool window_func : 1;
426 bool do_clear : 1;
427 };
428 bool clear_all; /* Write 'false' to clear all above */
429 } changed;
430} graph_settings SHAREDDATA_ATTR =
431{ 442{
432 /* Defaults */ 443 /* Defaults */
433 .orientation_vertical = true, 444 .orientation = FFT_OR_VERT,
434 .mode = FFT_DM_LINES, 445 .drawmode = FFT_DM_LINES,
435 .logarithmic_amp = true, 446 .amp_scale = FFT_AS_LOG,
436 .logarithmic_freq = true, 447 .freq_scale = FFT_FS_LOG,
437 .window_func = FFT_WF_HAMMING, 448 .window_func = FFT_WF_HAMMING,
438 .spectrogram_pos = 0, 449};
439 .changed = { .clear_all = false }, 450
451#define CFGFILE_VERSION 0
452#define CFGFILE_MINVERSION 0
453
454static const char cfg_filename[] = "fft.cfg";
455static struct configdata disk_config[] =
456{
457 { TYPE_ENUM, FFT_MIN_OR, FFT_MAX_OR,
458 { .int_p = &fft_disk.orientation }, "orientation",
459 (char * []){ [FFT_OR_VERT] = "vertical",
460 [FFT_OR_HORZ] = "horizontal" } },
461 { TYPE_ENUM, FFT_MIN_DM, FFT_MAX_DM,
462 { .int_p = &fft_disk.drawmode }, "drawmode",
463 (char * []){ [FFT_DM_LINES] = "lines",
464 [FFT_DM_BARS] = "bars",
465 [FFT_DM_SPECTROGRAM] = "spectrogram" } },
466 { TYPE_ENUM, FFT_MIN_AS, FFT_MAX_AS,
467 { .int_p = &fft_disk.amp_scale }, "amp scale",
468 (char * []){ [FFT_AS_LOG] = "logarithmic",
469 [FFT_AS_LIN] = "linear" } },
470 { TYPE_ENUM, FFT_MIN_FS, FFT_MAX_FS,
471 { .int_p = &fft_disk.freq_scale }, "freq scale",
472 (char * []){ [FFT_FS_LOG] = "logarithmic",
473 [FFT_FS_LIN] = "linear" } },
474 { TYPE_ENUM, FFT_MIN_WF, FFT_MAX_WF,
475 { .int_p = &fft_disk.window_func }, "window function",
476 (char * []){ [FFT_WF_HAMMING] = "hamming",
477 [FFT_WF_HANN] = "hann" } },
440}; 478};
441 479
480/* Hint flags for setting changes */
481enum fft_setting_flags
482{
483 FFT_SETF_OR = 1 << 0,
484 FFT_SETF_DM = 1 << 1,
485 FFT_SETF_AS = 1 << 2,
486#ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
487 FFT_SETF_FS = 1 << 3,
488#endif
489 FFT_SETF_WF = 1 << 4,
490 FFT_SETF_ALL = 0x1f
491};
492
493/***************************** End of settings *****************************/
494
495
496/**************************** Operational data *****************************/
497
498#define COLOR_DEFAULT_FG MYLCD_DEFAULT_FG
499#define COLOR_DEFAULT_BG MYLCD_DEFAULT_BG
500
501#ifdef HAVE_LCD_COLOR
502#define COLOR_MESSAGE_FRAME LCD_RGBPACK(0xc6, 0x00, 0x00)
503#define COLOR_MESSAGE_BG LCD_BLACK
504#define COLOR_MESSAGE_FG LCD_WHITE
505#else
506#define COLOR_MESSAGE_FRAME GREY_DARKGRAY
507#define COLOR_MESSAGE_BG GREY_WHITE
508#define COLOR_MESSAGE_FG GREY_BLACK
509#endif
510
511#define FFT_OSD_MARGIN_SIZE 1
512
513#define FFT_PERIOD (HZ/50) /* How fast to try to go */
514
515/* Based on feeding-in a 0db sinewave at FS/4 */
516#define QLOG_MAX 0x0009154B
517/* Fudge it a little or it's not very visbile */
518#define QLIN_MAX (0x00002266 >> 1)
519
520static struct fft_config fft;
521typedef void (* fft_drawfn_t)(unsigned, unsigned);
522static fft_drawfn_t fft_drawfn = NULL; /* plotting function */
523static int fft_spectrogram_pos = -1; /* row or column - only used by one at a time */
524static uint32_t fft_graph_scale = 0; /* max level over time, for scaling display */
525static int fft_message_id = -1; /* current message id displayed */
526static char fft_osd_message[32]; /* current message string displayed */
527static long fft_next_frame_tick = 0; /* next tick to attempt drawing */
528
442#ifdef HAVE_LCD_COLOR 529#ifdef HAVE_LCD_COLOR
443#define SHADES BMPWIDTH_fft_colors 530#define SHADES BMPWIDTH_fft_colors
444#define SPECTROGRAPH_PALETTE(index) (fft_colors[index]) 531#define SPECTROGRAPH_PALETTE(index) (fft_colors[index])
@@ -447,121 +534,103 @@ static struct {
447#define SPECTROGRAPH_PALETTE(index) (255 - (index)) 534#define SPECTROGRAPH_PALETTE(index) (255 - (index))
448#endif 535#endif
449 536
450/************************* End of globals *************************/ 537/************************* End of operational data *************************/
451 538
452/************************* Math functions *************************/
453 539
454/* Based on feeding-in a 0db sinewave at FS/4 */ 540/***************************** Math functions ******************************/
455#define QLOG_MAX 0x0009154B
456/* fudge it a little or it's not very visbile */
457#define QLIN_MAX (0x00002266 >> 1)
458 541
459/* Apply window function to input */ 542/* Apply window function to input */
460static void apply_window_func(enum fft_window_func mode) 543static void apply_window_func(enum fft_window_func mode)
461{ 544{
462 int i; 545 static const int16_t * const coefs[] =
463
464 switch(mode)
465 { 546 {
466 case FFT_WF_HAMMING: 547 [FFT_WF_HAMMING] = HAMMING_COEFF,
467 for(i = 0; i < ARRAYLEN_IN; ++i) 548 [FFT_WF_HANN] = HANN_COEFF,
468 { 549 };
469 input[i].r = (input[i].r * HAMMING_COEFF[i] + 16384) >> 15;
470 }
471 break;
472 550
473 case FFT_WF_HANN: 551 const int16_t * const c = coefs[mode];
474 for(i = 0; i < ARRAYLEN_IN; ++i) 552
475 { 553 for(int i = 0; i < ARRAYLEN_IN; ++i)
476 input[i].r = (input[i].r * HANN_COEFF[i] + 16384) >> 15; 554 input[i].r = (input[i].r * c[i] + 16384) >> 15;
477 }
478 break;
479 }
480} 555}
481 556
482/* Calculates the magnitudes from complex numbers and returns the maximum */ 557/* Calculates the magnitudes from complex numbers and returns the maximum */
483static int32_t calc_magnitudes(bool logarithmic_amp) 558static unsigned calc_magnitudes(enum fft_amp_scale scale)
484{ 559{
485 /* A major assumption made when calculating the Q*MAX constants 560 /* A major assumption made when calculating the Q*MAX constants
486 * is that the maximum magnitude is 29 bits long. */ 561 * is that the maximum magnitude is 29 bits long. */
487 uint32_t max = 0; 562 unsigned this_max = 0;
488 kiss_fft_cpx *this_output = output[output_head] + 1; /* skip DC */ 563 kiss_fft_cpx *this_output = output[output_head] + 1; /* skip DC */
489 int i;
490 564
491 /* Calculate the magnitude, discarding the phase. */ 565 /* Calculate the magnitude, discarding the phase. */
492 for(i = 0; i < ARRAYLEN_PLOT; ++i) 566 for(int i = 0; i < ARRAYLEN_PLOT; ++i)
493 { 567 {
494 int32_t re = this_output[i].r; 568 int32_t re = this_output[i].r;
495 int32_t im = this_output[i].i; 569 int32_t im = this_output[i].i;
496 570
497 uint32_t tmp = re*re + im*im; 571 uint32_t d = re*re + im*im;
498 572
499 if(tmp > 0) 573 if(d > 0)
500 { 574 {
501 if(tmp > 0x7FFFFFFF) /* clip */ 575 if(d > 0x7FFFFFFF) /* clip */
502 { 576 {
503 tmp = 0x7FFFFFFF; /* if our assumptions are correct, 577 d = 0x7FFFFFFF; /* if our assumptions are correct,
504 this should never happen. It's just 578 this should never happen. It's just
505 a safeguard. */ 579 a safeguard. */
506 } 580 }
507 581
508 if(logarithmic_amp) 582 if(scale == FFT_AS_LOG)
509 { 583 {
510 if(tmp < 0x8000) /* be more precise */ 584 if(d < 0x8000) /* be more precise */
511 { 585 {
512 /* ln(x ^ .5) = .5*ln(x) */ 586 /* ln(x ^ .5) = .5*ln(x) */
513 tmp = fp16_log(tmp << 16) >> 1; 587 d = fp16_log(d << 16) >> 1;
514 } 588 }
515 else 589 else
516 { 590 {
517 tmp = isqrt(tmp); /* linear scaling, nothing 591 d = isqrt(d); /* linear scaling, nothing
518 bad should happen */ 592 bad should happen */
519 tmp = fp16_log(tmp << 16); /* the log function 593 d = fp16_log(d << 16); /* the log function
520 expects s15.16 values */ 594 expects s15.16 values */
521 } 595 }
522 } 596 }
523 else 597 else
524 { 598 {
525 tmp = isqrt(tmp); /* linear scaling, nothing 599 d = isqrt(d); /* linear scaling, nothing
526 bad should happen */ 600 bad should happen */
527 } 601 }
528 } 602 }
529 603
530 /* Length 2 moving average - last transform and this one */ 604 /* Length 2 moving average - last transform and this one */
531 tmp = (plot_history[i] + tmp) >> 1; 605 linf_magnitudes[i] = (linf_magnitudes[i] + d) >> 1;
532 plot[i] = tmp;
533 plot_history[i] = tmp;
534 606
535 if(tmp > max) 607 if(d > this_max)
536 max = tmp; 608 this_max = d;
537 } 609 }
538 610
539 return max; 611 return this_max;
540} 612}
541 613
542/* Move plot bins into a logarithmic scale by sliding them towards the 614/* Move plot bins into a logarithmic scale by sliding them towards the
543 * Nyquist bin according to the translation in the binlog array. */ 615 * Nyquist bin according to the translation in the binlog array. */
544static void logarithmic_plot_translate(void) 616static void log_plot_translate(void)
545{ 617{
546 int i; 618 for(int i = ARRAYLEN_PLOT-1; i > 0; --i)
547
548 for(i = ARRAYLEN_PLOT-1; i > 0; --i)
549 { 619 {
550 int bin;
551 int s = binlog[i].bin; 620 int s = binlog[i].bin;
552 int e = binlog[i-1].bin; 621 int e = binlog[i-1].bin;
553 int frac = binlog[i].frac; 622 unsigned frac = binlog[i].frac;
554 623
555 bin = plot[s]; 624 int bin = linf_magnitudes[s];
556 625
557 if(frac) 626 if(frac)
558 { 627 {
559 /* slope < 1, Interpolate stretched bins (linear for now) */ 628 /* slope < 1, Interpolate stretched bins (linear for now) */
560 int diff = plot[s+1] - bin; 629 int diff = linf_magnitudes[s+1] - bin;
561 630
562 do 631 do
563 { 632 {
564 plot[i] = bin + FRACMUL(frac << 15, diff); 633 logf_magnitudes[i] = bin + FRACMUL(frac << 15, diff);
565 frac = binlog[--i].frac; 634 frac = binlog[--i].frac;
566 } 635 }
567 while(frac); 636 while(frac);
@@ -571,33 +640,32 @@ static void logarithmic_plot_translate(void)
571 /* slope > 1, Find peak of two or more bins */ 640 /* slope > 1, Find peak of two or more bins */
572 while(--s > e) 641 while(--s > e)
573 { 642 {
574 int val = plot[s]; 643 int val = linf_magnitudes[s];
575 644
576 if (val > bin) 645 if (val > bin)
577 bin = val; 646 bin = val;
578 } 647 }
579 } 648 }
580 649
581 plot[i] = bin; 650 logf_magnitudes[i] = bin;
582 } 651 }
583} 652}
584 653
585/* Calculates the translation for logarithmic plot bins */ 654/* Calculates the translation for logarithmic plot bins */
586static void logarithmic_plot_init(void) 655static void logarithmic_plot_init(void)
587{ 656{
588 int i, j;
589 /* 657 /*
590 * log: y = round(n * ln(x) / ln(n)) 658 * log: y = round(n * ln(x) / ln(n))
591 * anti: y = round(exp(x * ln(n) / n)) 659 * anti: y = round(exp(x * ln(n) / n))
592 */ 660 */
593 j = fp16_log((ARRAYLEN_PLOT - 1) << 16); 661 int j = fp16_log((ARRAYLEN_PLOT - 1) << 16);
594 for(i = 0; i < ARRAYLEN_PLOT; ++i) 662 for(int i = 0; i < ARRAYLEN_PLOT; ++i)
595 { 663 {
596 binlog[i].bin = (fp16_exp(i * j / (ARRAYLEN_PLOT - 1)) + 32768) >> 16; 664 binlog[i].bin = (fp16_exp(i * j / (ARRAYLEN_PLOT - 1)) + 32768) >> 16;
597 } 665 }
598 666
599 /* setup fractions for interpolation of stretched bins */ 667 /* setup fractions for interpolation of stretched bins */
600 for(i = 0; i < ARRAYLEN_PLOT-1; i = j) 668 for(int i = 0; i < ARRAYLEN_PLOT-1; i = j)
601 { 669 {
602 j = i + 1; 670 j = i + 1;
603 671
@@ -619,192 +687,13 @@ static void logarithmic_plot_init(void)
619 } 687 }
620} 688}
621 689
622/************************ End of math functions ***********************/ 690/************************** End of math functions **************************/
623
624/********************* Plotting functions (modes) *********************/
625static void draw_lines_vertical(void);
626static void draw_lines_horizontal(void);
627static void draw_bars_vertical(void);
628static void draw_bars_horizontal(void);
629static void draw_spectrogram_vertical(void);
630static void draw_spectrogram_horizontal(void);
631
632#define COLOR_DEFAULT_FG MYLCD_DEFAULT_FG
633#define COLOR_DEFAULT_BG MYLCD_DEFAULT_BG
634
635#ifdef HAVE_LCD_COLOR
636#define COLOR_MESSAGE_FRAME LCD_RGBPACK(0xc6, 0x00, 0x00)
637#define COLOR_MESSAGE_BG LCD_BLACK
638#define COLOR_MESSAGE_FG LCD_WHITE
639#else
640#define COLOR_MESSAGE_FRAME GREY_DARKGRAY
641#define COLOR_MESSAGE_BG GREY_WHITE
642#define COLOR_MESSAGE_FG GREY_BLACK
643#endif
644
645#define POPUP_HPADDING 3 /* 3 px of horizontal padding and */
646#define POPUP_VPADDING 2 /* 2 px of vertical padding */
647
648static void draw_message_string(const unsigned char *message, bool active)
649{
650 int x, y;
651 mylcd_getstringsize(message, &x, &y);
652
653 /* x and y give the size of the box for the popup */
654 x += POPUP_HPADDING*2;
655 y += POPUP_VPADDING*2;
656
657 /* In vertical spectrogram mode, leave space for the popup
658 * before actually drawing it (if space is needed) */
659 if(active &&
660 graph_settings.mode == FFT_DM_SPECTROGRAPH &&
661 graph_settings.orientation_vertical &&
662 graph_settings.spectrogram_pos >= LCD_WIDTH - x)
663 {
664 mylcd_scroll_left(graph_settings.spectrogram_pos -
665 LCD_WIDTH + x);
666 graph_settings.spectrogram_pos = LCD_WIDTH - x - 1;
667 }
668
669 mylcd_set_foreground(COLOR_MESSAGE_FRAME);
670 mylcd_fillrect(LCD_WIDTH - x, 0, LCD_WIDTH - 1, y);
671
672 mylcd_set_foreground(COLOR_MESSAGE_FG);
673 mylcd_set_background(COLOR_MESSAGE_BG);
674 mylcd_putsxy(LCD_WIDTH - x + POPUP_HPADDING,
675 POPUP_VPADDING, message);
676 mylcd_set_foreground(COLOR_DEFAULT_FG);
677 mylcd_set_background(COLOR_DEFAULT_BG);
678}
679
680static void draw(const unsigned char* message)
681{
682 static long show_message_tick = 0;
683 static const unsigned char* last_message = 0;
684
685 if(message != NULL)
686 {
687 last_message = message;
688 show_message_tick = (*rb->current_tick + HZ) | 1;
689 }
690
691 /* maybe take additional actions depending upon the changed setting */
692 if(graph_settings.changed.orientation)
693 {
694 graph_settings.changed.amp_scale = true;
695 graph_settings.changed.do_clear = true;
696 }
697
698 if(graph_settings.changed.mode)
699 {
700 graph_settings.changed.amp_scale = true;
701 graph_settings.changed.do_clear = true;
702 }
703
704 if(graph_settings.changed.amp_scale)
705 memset(plot_history, 0, sizeof (plot_history));
706
707 if(graph_settings.changed.freq_scale)
708 graph_settings.changed.freq_scale = true;
709
710 mylcd_set_foreground(COLOR_DEFAULT_FG);
711 mylcd_set_background(COLOR_DEFAULT_BG);
712 691
713 switch (graph_settings.mode)
714 {
715 default:
716 case FFT_DM_LINES: {
717
718 mylcd_clear_display();
719
720 if (graph_settings.orientation_vertical)
721 draw_lines_vertical();
722 else
723 draw_lines_horizontal();
724 break;
725 }
726 case FFT_DM_BARS: {
727 692
728 mylcd_clear_display(); 693/*********************** Plotting functions (modes) ************************/
729
730 if(graph_settings.orientation_vertical)
731 draw_bars_vertical();
732 else
733 draw_bars_horizontal();
734
735 break;
736 }
737 case FFT_DM_SPECTROGRAPH: {
738
739 if(graph_settings.changed.do_clear)
740 {
741 graph_settings.spectrogram_pos = 0;
742 mylcd_clear_display();
743 }
744
745 if(graph_settings.orientation_vertical)
746 draw_spectrogram_vertical();
747 else
748 draw_spectrogram_horizontal();
749 break;
750 }
751 }
752 694
753 if(show_message_tick != 0) 695static void draw_lines_vertical(unsigned this_max, unsigned graph_max)
754 {
755 if(TIME_BEFORE(*rb->current_tick, show_message_tick))
756 {
757 /* We have a message to show */
758 draw_message_string(last_message, true);
759 }
760 else
761 {
762 /* Stop drawing message */
763 show_message_tick = 0;
764 }
765 }
766 else if(last_message != NULL)
767 {
768 if(graph_settings.mode == FFT_DM_SPECTROGRAPH)
769 {
770 /* Spectrogram mode - need to erase the popup */
771 int x, y;
772 mylcd_getstringsize(last_message, &x, &y);
773 /* Recalculate the size */
774 x += POPUP_HPADDING*2;
775 y += POPUP_VPADDING*2;
776
777 if(!graph_settings.orientation_vertical)
778 {
779 /* In horizontal spectrogram mode, just scroll up by Y lines */
780 mylcd_scroll_up(y);
781 graph_settings.spectrogram_pos -= y;
782 if(graph_settings.spectrogram_pos < 0)
783 graph_settings.spectrogram_pos = 0;
784 }
785 else
786 {
787 /* In vertical spectrogram mode, erase the popup */
788 mylcd_set_foreground(COLOR_DEFAULT_BG);
789 mylcd_fillrect(graph_settings.spectrogram_pos + 1, 0,
790 LCD_WIDTH, y);
791 mylcd_set_foreground(COLOR_DEFAULT_FG);
792 }
793 }
794 /* else These modes clear the screen themselves */
795
796 last_message = NULL;
797 }
798
799 mylcd_update();
800
801 graph_settings.changed.clear_all = false;
802}
803
804static void draw_lines_vertical(void)
805{ 696{
806 static int max = 0;
807
808#if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */ 697#if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
809 const int offset = 0; 698 const int offset = 0;
810 const int plotwidth = LCD_WIDTH; 699 const int plotwidth = LCD_WIDTH;
@@ -813,13 +702,7 @@ static void draw_lines_vertical(void)
813 const int plotwidth = ARRAYLEN_PLOT; 702 const int plotwidth = ARRAYLEN_PLOT;
814#endif 703#endif
815 704
816 int this_max; 705 mylcd_clear_display();
817 int i, x;
818
819 if(graph_settings.changed.amp_scale)
820 max = 0; /* reset the graph on scaling mode change */
821
822 this_max = calc_magnitudes(graph_settings.logarithmic_amp);
823 706
824 if(this_max == 0) 707 if(this_max == 0)
825 { 708 {
@@ -827,21 +710,16 @@ static void draw_lines_vertical(void)
827 return; 710 return;
828 } 711 }
829 712
830 if(graph_settings.logarithmic_freq) 713 /* take the maximum of neighboring bins if we have to scale down the
831 logarithmic_plot_translate(); 714 * graph horizontally */
832
833 /* take the maximum of neighboring bins if we have to scale the graph
834 * horizontally */
835 if(LCD_WIDTH < ARRAYLEN_PLOT) /* graph compression */ 715 if(LCD_WIDTH < ARRAYLEN_PLOT) /* graph compression */
836 { 716 {
837 int bins_acc = LCD_WIDTH / 2; 717 int bins_acc = LCD_WIDTH / 2;
838 int bins_max = 0; 718 unsigned bins_max = 0;
839 719
840 i = 0, x = 0; 720 for(int i = 0, x = 0; i < ARRAYLEN_PLOT; ++i)
841
842 for(;;)
843 { 721 {
844 int bin = plot[i++]; 722 unsigned bin = plot[i];
845 723
846 if(bin > bins_max) 724 if(bin > bins_max)
847 bins_max = bin; 725 bins_max = bin;
@@ -850,14 +728,10 @@ static void draw_lines_vertical(void)
850 728
851 if(bins_acc >= ARRAYLEN_PLOT) 729 if(bins_acc >= ARRAYLEN_PLOT)
852 { 730 {
853 plot[x] = bins_max; 731 int h = LCD_HEIGHT*bins_max / graph_max;
854 732 mylcd_vline(x, LCD_HEIGHT - h, LCD_HEIGHT-1);
855 if(bins_max > max)
856 max = bins_max;
857
858 if(++x >= LCD_WIDTH)
859 break;
860 733
734 x++;
861 bins_acc -= ARRAYLEN_PLOT; 735 bins_acc -= ARRAYLEN_PLOT;
862 bins_max = 0; 736 bins_max = 0;
863 } 737 }
@@ -865,21 +739,16 @@ static void draw_lines_vertical(void)
865 } 739 }
866 else 740 else
867 { 741 {
868 if(this_max > max) 742 for(int i = 0; i < plotwidth; ++i)
869 max = this_max; 743 {
870 } 744 int h = LCD_HEIGHT*plot[i] / graph_max;
871 745 mylcd_vline(i + offset, LCD_HEIGHT - h, LCD_HEIGHT-1);
872 for(x = 0; x < plotwidth; ++x) 746 }
873 {
874 int h = LCD_HEIGHT*plot[x] / max;
875 mylcd_vline(x + offset, LCD_HEIGHT - h, LCD_HEIGHT-1);
876 } 747 }
877} 748}
878 749
879static void draw_lines_horizontal(void) 750static void draw_lines_horizontal(unsigned this_max, unsigned graph_max)
880{ 751{
881 static int max = 0;
882
883#if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */ 752#if LCD_WIDTH < ARRAYLEN_PLOT /* graph compression */
884 const int offset = 0; 753 const int offset = 0;
885 const int plotwidth = LCD_HEIGHT; 754 const int plotwidth = LCD_HEIGHT;
@@ -888,13 +757,7 @@ static void draw_lines_horizontal(void)
888 const int plotwidth = ARRAYLEN_PLOT; 757 const int plotwidth = ARRAYLEN_PLOT;
889#endif 758#endif
890 759
891 int this_max; 760 mylcd_clear_display();
892 int y;
893
894 if(graph_settings.changed.amp_scale)
895 max = 0; /* reset the graph on scaling mode change */
896
897 this_max = calc_magnitudes(graph_settings.logarithmic_amp);
898 761
899 if(this_max == 0) 762 if(this_max == 0)
900 { 763 {
@@ -902,38 +765,28 @@ static void draw_lines_horizontal(void)
902 return; 765 return;
903 } 766 }
904 767
905 if(graph_settings.logarithmic_freq)
906 logarithmic_plot_translate();
907
908 /* take the maximum of neighboring bins if we have to scale the graph 768 /* take the maximum of neighboring bins if we have to scale the graph
909 * horizontally */ 769 * horizontally */
910 if(LCD_HEIGHT < ARRAYLEN_PLOT) /* graph compression */ 770 if(LCD_HEIGHT < ARRAYLEN_PLOT) /* graph compression */
911 { 771 {
912 int bins_acc = LCD_HEIGHT / 2; 772 int bins_acc = LCD_HEIGHT / 2;
913 int bins_max = 0; 773 unsigned bins_max = 0;
914 int i = 0;
915
916 y = 0;
917 774
918 for(;;) 775 for(int i = 0, y = 0; i < ARRAYLEN_PLOT; ++i)
919 { 776 {
920 int bin = plot[i++]; 777 unsigned bin = plot[i];
921 778
922 if (bin > bins_max) 779 if(bin > bins_max)
923 bins_max = bin; 780 bins_max = bin;
924 781
925 bins_acc += LCD_HEIGHT; 782 bins_acc += LCD_HEIGHT;
926 783
927 if(bins_acc >= ARRAYLEN_PLOT) 784 if(bins_acc >= ARRAYLEN_PLOT)
928 { 785 {
929 plot[y] = bins_max; 786 int w = LCD_WIDTH*bins_max / graph_max;
930 787 mylcd_hline(0, w - 1, y);
931 if(bins_max > max)
932 max = bins_max;
933
934 if(++y >= LCD_HEIGHT)
935 break;
936 788
789 y++;
937 bins_acc -= ARRAYLEN_PLOT; 790 bins_acc -= ARRAYLEN_PLOT;
938 bins_max = 0; 791 bins_max = 0;
939 } 792 }
@@ -941,21 +794,16 @@ static void draw_lines_horizontal(void)
941 } 794 }
942 else 795 else
943 { 796 {
944 if(this_max > max) 797 for(int i = 0; i < plotwidth; ++i)
945 max = this_max; 798 {
946 } 799 int w = LCD_WIDTH*plot[i] / graph_max;
947 800 mylcd_hline(0, w - 1, i + offset);
948 for(y = 0; y < plotwidth; ++y) 801 }
949 {
950 int w = LCD_WIDTH*plot[y] / max;
951 mylcd_hline(0, w - 1, y + offset);
952 } 802 }
953} 803}
954 804
955static void draw_bars_vertical(void) 805static void draw_bars_vertical(unsigned this_max, unsigned graph_max)
956{ 806{
957 static int max = 0;
958
959#if LCD_WIDTH < LCD_HEIGHT 807#if LCD_WIDTH < LCD_HEIGHT
960 const int bars = 15; 808 const int bars = 15;
961#else 809#else
@@ -964,26 +812,20 @@ static void draw_bars_vertical(void)
964 const int border = 2; 812 const int border = 2;
965 const int barwidth = LCD_WIDTH / (bars + border); 813 const int barwidth = LCD_WIDTH / (bars + border);
966 const int width = barwidth - border; 814 const int width = barwidth - border;
967 const int offset = (LCD_WIDTH - bars*barwidth) / 2; 815 const int offset = (LCD_WIDTH - bars*barwidth + border) / 2;
968
969 if(graph_settings.changed.amp_scale)
970 max = 0; /* reset the graph on scaling mode change */
971 816
817 mylcd_clear_display();
972 mylcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-1); /* Draw baseline */ 818 mylcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-1); /* Draw baseline */
973 819
974 if(calc_magnitudes(graph_settings.logarithmic_amp) == 0) 820 if(this_max == 0)
975 return; /* nothing more to draw */ 821 return; /* nothing more to draw */
976 822
977 if(graph_settings.logarithmic_freq)
978 logarithmic_plot_translate();
979
980 int bins_acc = bars / 2; 823 int bins_acc = bars / 2;
981 int bins_max = 0; 824 unsigned bins_max = 0;
982 int x = 0, i = 0;
983 825
984 for(;;) 826 for(int i = 0, x = offset;; ++i)
985 { 827 {
986 int bin = plot[i++]; 828 unsigned bin = plot[i];
987 829
988 if(bin > bins_max) 830 if(bin > bins_max)
989 bins_max = bin; 831 bins_max = bin;
@@ -992,30 +834,21 @@ static void draw_bars_vertical(void)
992 834
993 if(bins_acc >= ARRAYLEN_PLOT) 835 if(bins_acc >= ARRAYLEN_PLOT)
994 { 836 {
995 plot[x] = bins_max; 837 int h = LCD_HEIGHT*bins_max / graph_max;
838 mylcd_fillrect(x, LCD_HEIGHT - h, width, h - 1);
996 839
997 if(bins_max > max) 840 if(i >= ARRAYLEN_PLOT-1)
998 max = bins_max;
999
1000 if(++x >= bars)
1001 break; 841 break;
1002 842
843 x += barwidth;
1003 bins_acc -= ARRAYLEN_PLOT; 844 bins_acc -= ARRAYLEN_PLOT;
1004 bins_max = 0; 845 bins_max = 0;
1005 } 846 }
1006 } 847 }
1007
1008 for(i = 0, x = offset; i < bars; ++i, x += barwidth)
1009 {
1010 int h = LCD_HEIGHT * plot[i] / max;
1011 mylcd_fillrect(x, LCD_HEIGHT - h, width, h - 1);
1012 }
1013} 848}
1014 849
1015static void draw_bars_horizontal(void) 850static void draw_bars_horizontal(unsigned this_max, unsigned graph_max)
1016{ 851{
1017 static int max = 0;
1018
1019#if LCD_WIDTH < LCD_HEIGHT 852#if LCD_WIDTH < LCD_HEIGHT
1020 const int bars = 20; 853 const int bars = 20;
1021#else 854#else
@@ -1024,70 +857,56 @@ static void draw_bars_horizontal(void)
1024 const int border = 2; 857 const int border = 2;
1025 const int barwidth = LCD_HEIGHT / (bars + border); 858 const int barwidth = LCD_HEIGHT / (bars + border);
1026 const int height = barwidth - border; 859 const int height = barwidth - border;
1027 const int offset = (LCD_HEIGHT - bars*barwidth) / 2; 860 const int offset = (LCD_HEIGHT - bars*barwidth + border) / 2;
1028
1029 if(graph_settings.changed.amp_scale)
1030 max = 0; /* reset the graph on scaling mode change */
1031 861
862 mylcd_clear_display();
1032 mylcd_vline(0, 0, LCD_HEIGHT-1); /* Draw baseline */ 863 mylcd_vline(0, 0, LCD_HEIGHT-1); /* Draw baseline */
1033 864
1034 if(calc_magnitudes(graph_settings.logarithmic_amp) == 0) 865 if(this_max == 0)
1035 return; /* nothing more to draw */ 866 return; /* nothing more to draw */
1036 867
1037 if(graph_settings.logarithmic_freq)
1038 logarithmic_plot_translate();
1039
1040 int bins_acc = bars / 2; 868 int bins_acc = bars / 2;
1041 int bins_max = 0; 869 unsigned bins_max = 0;
1042 int y = 0, i = 0;
1043 870
1044 for(;;) 871 for(int i = 0, y = offset;; ++i)
1045 { 872 {
1046 int bin = plot[i++]; 873 unsigned bin = plot[i];
1047 874
1048 if (bin > bins_max) 875 if(bin > bins_max)
1049 bins_max = bin; 876 bins_max = bin;
1050 877
1051 bins_acc += bars; 878 bins_acc += bars;
1052 879
1053 if(bins_acc >= ARRAYLEN_PLOT) 880 if(bins_acc >= ARRAYLEN_PLOT)
1054 { 881 {
1055 plot[y] = bins_max; 882 int w = LCD_WIDTH*bins_max / graph_max;
1056 883 mylcd_fillrect(1, y, w, height);
1057 if(bins_max > max)
1058 max = bins_max;
1059 884
1060 if(++y >= bars) 885 if(i >= ARRAYLEN_PLOT-1)
1061 break; 886 break;
1062 887
888 y += barwidth;
1063 bins_acc -= ARRAYLEN_PLOT; 889 bins_acc -= ARRAYLEN_PLOT;
1064 bins_max = 0; 890 bins_max = 0;
1065 } 891 }
1066 } 892 }
1067
1068 for(i = 0, y = offset; i < bars; ++i, y += barwidth)
1069 {
1070 int w = LCD_WIDTH * plot[i] / max;
1071 mylcd_fillrect(1, y, w, height);
1072 }
1073} 893}
1074 894
1075static void draw_spectrogram_vertical(void) 895static void draw_spectrogram_vertical(unsigned this_max, unsigned graph_max)
1076{ 896{
1077 const int32_t scale_factor = MIN(LCD_HEIGHT, ARRAYLEN_PLOT); 897 const int scale_factor = MIN(LCD_HEIGHT, ARRAYLEN_PLOT);
1078
1079 calc_magnitudes(graph_settings.logarithmic_amp);
1080 898
1081 if(graph_settings.logarithmic_freq) 899 if(fft_spectrogram_pos < LCD_WIDTH-1)
1082 logarithmic_plot_translate(); 900 fft_spectrogram_pos++;
901 else
902 mylcd_scroll_left(1);
1083 903
1084 int bins_acc = scale_factor / 2; 904 int bins_acc = scale_factor / 2;
1085 int bins_max = 0; 905 unsigned bins_max = 0;
1086 int y = 0, i = 0;
1087 906
1088 for(;;) 907 for(int i = 0, y = LCD_HEIGHT-1;; ++i)
1089 { 908 {
1090 int bin = plot[i++]; 909 unsigned bin = plot[i];
1091 910
1092 if(bin > bins_max) 911 if(bin > bins_max)
1093 bins_max = bin; 912 bins_max = bin;
@@ -1096,12 +915,7 @@ static void draw_spectrogram_vertical(void)
1096 915
1097 if(bins_acc >= ARRAYLEN_PLOT) 916 if(bins_acc >= ARRAYLEN_PLOT)
1098 { 917 {
1099 unsigned index; 918 unsigned index = (SHADES-1)*bins_max / graph_max;
1100
1101 if(graph_settings.logarithmic_amp)
1102 index = (SHADES-1)*bins_max / QLOG_MAX;
1103 else
1104 index = (SHADES-1)*bins_max / QLIN_MAX;
1105 919
1106 /* These happen because we exaggerate the graph a little for 920 /* These happen because we exaggerate the graph a little for
1107 * linear mode */ 921 * linear mode */
@@ -1109,10 +923,9 @@ static void draw_spectrogram_vertical(void)
1109 index = SHADES-1; 923 index = SHADES-1;
1110 924
1111 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index)); 925 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index));
1112 mylcd_drawpixel(graph_settings.spectrogram_pos, 926 mylcd_drawpixel(fft_spectrogram_pos, y);
1113 scale_factor-1 - y);
1114 927
1115 if(++y >= scale_factor) 928 if(--y < 0)
1116 break; 929 break;
1117 930
1118 bins_acc -= ARRAYLEN_PLOT; 931 bins_acc -= ARRAYLEN_PLOT;
@@ -1120,28 +933,24 @@ static void draw_spectrogram_vertical(void)
1120 } 933 }
1121 } 934 }
1122 935
1123 if(graph_settings.spectrogram_pos < LCD_WIDTH-1) 936 (void)this_max;
1124 graph_settings.spectrogram_pos++;
1125 else
1126 mylcd_scroll_left(1);
1127} 937}
1128 938
1129static void draw_spectrogram_horizontal(void) 939static void draw_spectrogram_horizontal(unsigned this_max, unsigned graph_max)
1130{ 940{
1131 const int32_t scale_factor = MIN(LCD_WIDTH, ARRAYLEN_PLOT); 941 const int scale_factor = MIN(LCD_WIDTH, ARRAYLEN_PLOT);
1132 942
1133 calc_magnitudes(graph_settings.logarithmic_amp); 943 if(fft_spectrogram_pos < LCD_HEIGHT-1)
1134 944 fft_spectrogram_pos++;
1135 if(graph_settings.logarithmic_freq) 945 else
1136 logarithmic_plot_translate(); 946 mylcd_scroll_up(1);
1137 947
1138 int bins_acc = scale_factor / 2; 948 int bins_acc = scale_factor / 2;
1139 int bins_max = 0; 949 unsigned bins_max = 0;
1140 int x = 0, i = 0;
1141 950
1142 for(;;) 951 for(int i = 0, x = 0;; ++i)
1143 { 952 {
1144 int bin = plot[i++]; 953 unsigned bin = plot[i];
1145 954
1146 if(bin > bins_max) 955 if(bin > bins_max)
1147 bins_max = bin; 956 bins_max = bin;
@@ -1150,12 +959,7 @@ static void draw_spectrogram_horizontal(void)
1150 959
1151 if(bins_acc >= ARRAYLEN_PLOT) 960 if(bins_acc >= ARRAYLEN_PLOT)
1152 { 961 {
1153 unsigned index; 962 unsigned index = (SHADES-1)*bins_max / graph_max;
1154
1155 if(graph_settings.logarithmic_amp)
1156 index = (SHADES-1)*bins_max / QLOG_MAX;
1157 else
1158 index = (SHADES-1)*bins_max / QLIN_MAX;
1159 963
1160 /* These happen because we exaggerate the graph a little for 964 /* These happen because we exaggerate the graph a little for
1161 * linear mode */ 965 * linear mode */
@@ -1163,9 +967,9 @@ static void draw_spectrogram_horizontal(void)
1163 index = SHADES-1; 967 index = SHADES-1;
1164 968
1165 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index)); 969 mylcd_set_foreground(SPECTROGRAPH_PALETTE(index));
1166 mylcd_drawpixel(x, graph_settings.spectrogram_pos); 970 mylcd_drawpixel(x, fft_spectrogram_pos);
1167 971
1168 if(++x >= scale_factor) 972 if(++x >= LCD_WIDTH)
1169 break; 973 break;
1170 974
1171 bins_acc -= ARRAYLEN_PLOT; 975 bins_acc -= ARRAYLEN_PLOT;
@@ -1173,15 +977,14 @@ static void draw_spectrogram_horizontal(void)
1173 } 977 }
1174 } 978 }
1175 979
1176 if(graph_settings.spectrogram_pos < LCD_HEIGHT-1) 980 (void)this_max;
1177 graph_settings.spectrogram_pos++;
1178 else
1179 mylcd_scroll_up(1);
1180} 981}
1181 982
1182/********************* End of plotting functions (modes) *********************/ 983/******************** End of plotting functions (modes) ********************/
984
985
986/***************************** FFT functions *******************************/
1183 987
1184/****************************** FFT functions ********************************/
1185static bool is_playing(void) 988static bool is_playing(void)
1186{ 989{
1187 return rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_PLAYING; 990 return rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_PLAYING;
@@ -1232,7 +1035,7 @@ static inline bool fft_get_fft(void)
1232 input[fft_idx].r = (left + right) >> 1; /* to mono */ 1035 input[fft_idx].r = (left + right) >> 1; /* to mono */
1233 } while (fft_idx++, --count > 0); 1036 } while (fft_idx++, --count > 0);
1234 1037
1235 apply_window_func(graph_settings.window_func); 1038 apply_window_func(fft.window_func);
1236 1039
1237 rb->yield(); 1040 rb->yield();
1238 1041
@@ -1246,14 +1049,14 @@ static inline bool fft_get_fft(void)
1246#if NUM_CORES > 1 1049#if NUM_CORES > 1
1247/* use a worker thread if there is another processor core */ 1050/* use a worker thread if there is another processor core */
1248static volatile bool fft_thread_run SHAREDDATA_ATTR = false; 1051static volatile bool fft_thread_run SHAREDDATA_ATTR = false;
1249static unsigned long fft_thread; 1052static unsigned long fft_thread = 0;
1250 1053
1251static long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))] 1054static long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))]
1252 CACHEALIGN_AT_LEAST_ATTR(4); 1055 CACHEALIGN_AT_LEAST_ATTR(4);
1253 1056
1254static void fft_thread_entry(void) 1057static void fft_thread_entry(void)
1255{ 1058{
1256 if (!fft_init_fft_lib()) 1059 if(!fft_init_fft_lib())
1257 { 1060 {
1258 output_tail = -1; /* tell that we bailed */ 1061 output_tail = -1; /* tell that we bailed */
1259 fft_thread_run = true; 1062 fft_thread_run = true;
@@ -1276,7 +1079,8 @@ static void fft_thread_entry(void)
1276 continue; 1079 continue;
1277 } 1080 }
1278 1081
1279 /* write back output for other processor and invalidate for next frame read */ 1082 /* write back output for other processor and invalidate for next
1083 frame read */
1280 rb->commit_discard_dcache(); 1084 rb->commit_discard_dcache();
1281 1085
1282 int new_tail = output_tail ^ 1; 1086 int new_tail = output_tail ^ 1;
@@ -1364,168 +1168,433 @@ static inline void fft_close_fft(void)
1364 /* nothing to do */ 1168 /* nothing to do */
1365} 1169}
1366#endif /* NUM_CORES */ 1170#endif /* NUM_CORES */
1367/*************************** End of FFT functions ****************************/
1368 1171
1369enum plugin_status plugin_start(const void* parameter) 1172/************************** End of FFT functions ***************************/
1173
1174
1175/****************************** OSD functions ******************************/
1176
1177/* Format a message to display */
1178static void fft_osd_format_message(enum fft_setting_flags id)
1370{ 1179{
1371 /* Defaults */ 1180 const char *msg = "";
1372 bool run = true;
1373 bool showing_warning = false;
1374 1181
1375 if (!fft_init_fft()) 1182 switch (id)
1376 return PLUGIN_ERROR; 1183 {
1184 case FFT_SETF_DM:
1185 msg = (const char * [FFT_MAX_DM]) {
1186 [FFT_DM_LINES] = "Lines",
1187 [FFT_DM_BARS] = "Bars",
1188 [FFT_DM_SPECTROGRAM] = "Spectrogram",
1189 }[fft.drawmode];
1190 break;
1191
1192 case FFT_SETF_WF:
1193 msg = (const char * [FFT_MAX_WF]) {
1194 [FFT_WF_HAMMING] = "Hamming window",
1195 [FFT_WF_HANN] = "Hann window",
1196 }[fft.window_func];
1197 break;
1198
1199 case FFT_SETF_AS:
1200 msg = (const char * [FFT_MAX_AS]) {
1201 [FFT_AS_LOG] = "Logarithmic amplitude",
1202 [FFT_AS_LIN] = "Linear amplitude"
1203 }[fft.amp_scale];
1204 break;
1205
1206#ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
1207 case FFT_SETF_FS:
1208 msg = (const char * [FFT_MAX_FS]) {
1209 [FFT_FS_LOG] = "Logarithmic frequency",
1210 [FFT_FS_LIN] = "Linear frequency",
1211 }[fft.freq_scale];
1212 break;
1213#endif
1214
1215 case FFT_SETF_OR:
1216 rb->snprintf(fft_osd_message, sizeof (fft_osd_message),
1217 (const char * [FFT_MAX_OR]) {
1218 [FFT_OR_VERT] = "Vertical %s",
1219 [FFT_OR_HORZ] = "Horizontal %s",
1220 }[fft.orientation],
1221 (const char * [FFT_MAX_DM]) {
1222 [FFT_DM_LINES ... FFT_DM_BARS] = "amplitude",
1223 [FFT_DM_SPECTROGRAM] = "frequency"
1224 }[fft.drawmode]);
1225 return;
1226
1227#if 0
1228 /* Pertentially */
1229 case FFT_SETF_VOLUME:
1230 rb->snprintf(fft_osd_message, sizeof (fft_osd_message),
1231 "Volume: %d%s",
1232 rb->sound_val2phys(SOUND_VOLUME, global_settings.volume),
1233 rb->sound_unit(SOUND_VOLUME));
1234 return;
1235#endif
1236
1237 default:
1238 break;
1239 }
1240
1241 /* Default action: copy string */
1242 rb->strlcpy(fft_osd_message, msg, sizeof (fft_osd_message));
1243}
1244
1245static void fft_osd_draw_cb(int x, int y, int width, int height)
1246{
1247#if LCD_DEPTH > 1
1248 mylcd_set_foreground(COLOR_MESSAGE_FG);
1249 mylcd_set_background(COLOR_MESSAGE_BG);
1250#endif
1251#if FFT_OSD_MARGIN_SIZE != 0
1252 mylcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1253 mylcd_fillrect(1, 1, width - 2, height - 2);
1254 mylcd_set_drawmode(DRMODE_SOLID);
1255#endif
1256 mylcd_putsxy(1+FFT_OSD_MARGIN_SIZE, 1+FFT_OSD_MARGIN_SIZE,
1257 fft_osd_message);
1258#if LCD_DEPTH > 1
1259 mylcd_set_foreground(COLOR_MESSAGE_FRAME);
1260#endif
1261
1262 mylcd_drawrect(0, 0, width, height);
1263
1264 (void)x; (void)y;
1265}
1266
1267static void fft_osd_show_message(enum fft_setting_flags id)
1268{
1269 fft_osd_format_message(id);
1270
1271 if(!myosd_enabled())
1272 return;
1273
1274 int width, height;
1275 int maxwidth, maxheight;
1276
1277 mylcd_set_viewport(myosd_get_viewport());
1278 myosd_get_max_dims(&maxwidth, &maxheight);
1279 mylcd_setfont(FONT_UI);
1280 mylcd_getstringsize(fft_osd_message, &width, &height);
1281 mylcd_set_viewport(NULL);
1282
1283 width += 2 + 2*FFT_OSD_MARGIN_SIZE;
1284 if(width > maxwidth)
1285 width = maxwidth;
1286
1287 height += 2 + 2*FFT_OSD_MARGIN_SIZE;
1288 if(height > maxheight)
1289 height = maxheight;
1290
1291 bool drawn = myosd_update_pos((LCD_WIDTH - width) / 2,
1292 (LCD_HEIGHT - height) / 2,
1293 width, height);
1294
1295 myosd_show(OSD_SHOW | (drawn ? 0 : OSD_UPDATENOW));
1296}
1297
1298static void fft_popupmsg(enum fft_setting_flags id)
1299{
1300 fft_message_id = id;
1301}
1302
1303/************************** End of OSD functions ***************************/
1304
1305
1306static void fft_setting_update(unsigned which)
1307{
1308 static fft_drawfn_t fft_drawfns[FFT_MAX_DM][FFT_MAX_OR] =
1309 {
1310 [FFT_DM_LINES] =
1311 {
1312 [FFT_OR_HORZ] = draw_lines_horizontal,
1313 [FFT_OR_VERT] = draw_lines_vertical,
1314 },
1315 [FFT_DM_BARS] =
1316 {
1317 [FFT_OR_HORZ] = draw_bars_horizontal,
1318 [FFT_OR_VERT] = draw_bars_vertical,
1319 },
1320 [FFT_DM_SPECTROGRAM] =
1321 {
1322 [FFT_OR_HORZ] = draw_spectrogram_horizontal,
1323 [FFT_OR_VERT] = draw_spectrogram_vertical,
1324 },
1325 };
1326
1327 if(which & (FFT_SETF_DM | FFT_SETF_OR))
1328 {
1329 fft_drawfn = fft_drawfns[fft.drawmode]
1330 [fft.orientation];
1331
1332 if(fft.drawmode == FFT_DM_SPECTROGRAM)
1333 {
1334 fft_spectrogram_pos = -1;
1335 myosd_lcd_update_prepare();
1336 mylcd_clear_display();
1337 myosd_lcd_update();
1338 }
1339 }
1340
1341 if(which & (FFT_SETF_DM | FFT_SETF_AS))
1342 {
1343 if(fft.drawmode == FFT_DM_SPECTROGRAM)
1344 {
1345 fft_graph_scale = fft.amp_scale == FFT_AS_LIN ?
1346 QLIN_MAX : QLOG_MAX;
1347 }
1348 else
1349 {
1350 fft_graph_scale = 0;
1351 }
1352 }
1353
1354#ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
1355 if(which & FFT_SETF_FS)
1356 {
1357 plot = fft.freq_scale == FFT_FS_LIN ?
1358 linf_magnitudes : logf_magnitudes;
1359 }
1360#endif
1361
1362 if(which & FFT_SETF_AS)
1363 {
1364 memset(linf_magnitudes, 0, sizeof (linf_magnitudes));
1365 memset(logf_magnitudes, 0, sizeof (logf_magnitudes));
1366 }
1367}
1368
1369static long fft_draw(void)
1370{
1371 long tick = *rb->current_tick;
1372
1373 if(fft_message_id != -1)
1374 {
1375 /* Show a new message */
1376 fft_osd_show_message((enum fft_setting_flags)fft_message_id);
1377 fft_message_id = -1;
1378 }
1379 else
1380 {
1381 /* Monitor OSD timeout */
1382 myosd_monitor_timeout();
1383 }
1384
1385 if(TIME_BEFORE(tick, fft_next_frame_tick))
1386 return fft_next_frame_tick - tick; /* Too early */
1387
1388 unsigned this_max;
1389
1390 if(!fft_have_fft())
1391 {
1392 if(is_playing())
1393 return HZ/100;
1394
1395 /* All magnitudes == 0 thus this_max == 0 */
1396 for(int i = 0; i < ARRAYLEN_PLOT; i++)
1397 linf_magnitudes[i] >>= 1; /* decay */
1377 1398
1399 this_max = 0;
1400 }
1401 else
1402 {
1403 this_max = calc_magnitudes(fft.amp_scale);
1404
1405 fft_free_fft_output(); /* COP only */
1406
1407 if(fft.drawmode != FFT_DM_SPECTROGRAM &&
1408 this_max > fft_graph_scale)
1409 {
1410 fft_graph_scale = this_max;
1411 }
1412 }
1413
1414 if (fft.freq_scale == FFT_FS_LOG)
1415 log_plot_translate();
1416
1417 myosd_lcd_update_prepare();
1418
1419 mylcd_set_foreground(COLOR_DEFAULT_FG);
1420 mylcd_set_background(COLOR_DEFAULT_BG);
1421
1422 fft_drawfn(this_max, fft_graph_scale);
1423
1424 myosd_lcd_update();
1425
1426 fft_next_frame_tick = tick + FFT_PERIOD;
1427 return fft_next_frame_tick - *rb->current_tick;
1428}
1429
1430static void fft_osd_init(void *buf, size_t bufsize)
1431{
1432 int width, height;
1433 mylcd_setfont(FONT_UI);
1434 mylcd_getstringsize("M", NULL, &height);
1435 width = LCD_WIDTH;
1436 height += 2 + 2*FFT_OSD_MARGIN_SIZE;
1437 myosd_init(OSD_INIT_MAJOR_HEIGHT | OSD_INIT_MINOR_MAX, buf, bufsize,
1438 fft_osd_draw_cb, &width, &height, NULL);
1439 myosd_set_timeout(HZ);
1440}
1441
1442static void fft_cleanup(void)
1443{
1444 myosd_destroy();
1445
1446 fft_close_fft();
1447
1448#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1449 rb->cancel_cpu_boost();
1450#endif
1378#ifndef HAVE_LCD_COLOR 1451#ifndef HAVE_LCD_COLOR
1379 unsigned char *gbuf; 1452 grey_release();
1380 size_t gbuf_size = 0; 1453#endif
1381 /* get the remainder of the plugin buffer */ 1454 backlight_use_settings();
1382 gbuf = (unsigned char *) rb->plugin_get_buffer(&gbuf_size); 1455
1456 /* save settings if changed */
1457 if (rb->memcmp(&fft, &fft_disk, sizeof(fft)))
1458 {
1459 fft_disk = fft;
1460 configfile_save(cfg_filename, disk_config, ARRAYLEN(disk_config),
1461 CFGFILE_VERSION);
1462 }
1463}
1464
1465static bool fft_setup(void)
1466{
1467 atexit(fft_cleanup);
1383 1468
1469 configfile_load(cfg_filename, disk_config, ARRAYLEN(disk_config),
1470 CFGFILE_MINVERSION);
1471 fft = fft_disk; /* copy to running config */
1472
1473 if(!fft_init_fft())
1474 return false;
1475
1476 /* get the remainder of the plugin buffer for OSD and perhaps
1477 greylib */
1478 size_t bufsize = 0;
1479 unsigned char *buf = rb->plugin_get_buffer(&bufsize);
1480
1481#ifndef HAVE_LCD_COLOR
1384 /* initialize the greyscale buffer.*/ 1482 /* initialize the greyscale buffer.*/
1385 if (!grey_init(gbuf, gbuf_size, GREY_ON_COP | GREY_BUFFERED, 1483 long grey_size;
1386 LCD_WIDTH, LCD_HEIGHT, NULL)) 1484 if(!grey_init(buf, bufsize, GREY_ON_COP | GREY_BUFFERED,
1485 LCD_WIDTH, LCD_HEIGHT, &grey_size))
1387 { 1486 {
1388 rb->splash(HZ, "Couldn't init greyscale display"); 1487 rb->splash(HZ, "Couldn't init greyscale display");
1389 fft_close_fft(); 1488 return false;
1390 return PLUGIN_ERROR;
1391 } 1489 }
1392 grey_show(true);
1393#endif
1394 1490
1395 logarithmic_plot_init(); 1491 grey_show(true);
1492
1493 buf += grey_size;
1494 bufsize -= grey_size;
1495#endif /* !HAVE_LCD_COLOR */
1496
1497 fft_osd_init(buf, bufsize);
1396 1498
1397#if LCD_DEPTH > 1 1499#if LCD_DEPTH > 1
1500 myosd_lcd_update_prepare();
1398 rb->lcd_set_backdrop(NULL); 1501 rb->lcd_set_backdrop(NULL);
1399 mylcd_clear_display(); 1502 mylcd_clear_display();
1400 mylcd_update(); 1503 myosd_lcd_update();
1401#endif 1504#endif
1402 backlight_ignore_timeout(); 1505 backlight_ignore_timeout();
1403 1506
1404#ifdef HAVE_ADJUSTABLE_CPU_FREQ 1507#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1405 rb->cpu_boost(true); 1508 rb->trigger_cpu_boost();
1406#endif 1509#endif
1407 1510
1408 while (run) 1511 logarithmic_plot_init();
1409 { 1512 fft_setting_update(FFT_SETF_ALL);
1410 /* Unless otherwise specified, HZ/50 is around the window length 1513 fft_next_frame_tick = *rb->current_tick;
1411 * and quite fast. We want to be done with drawing by this time. */
1412 long next_frame_tick = *rb->current_tick + HZ/50;
1413 int button;
1414
1415 while (!fft_have_fft())
1416 {
1417 int timeout;
1418
1419 if(!is_playing())
1420 {
1421 showing_warning = true;
1422 mylcd_clear_display();
1423 draw_message_string("No audio playing", false);
1424 mylcd_update();
1425 timeout = HZ/5;
1426 }
1427 else
1428 {
1429 if(showing_warning)
1430 {
1431 showing_warning = false;
1432 mylcd_clear_display();
1433 mylcd_update();
1434 }
1435 1514
1436 timeout = HZ/100; /* 'till end of curent tick, don't use 100% CPU */ 1515 return true;
1437 } 1516}
1438 1517
1439 /* Make sure the FFT has produced something before doing anything 1518enum plugin_status plugin_start(const void* parameter)
1440 * but watching for buttons. Music might not be playing or things 1519{
1441 * just aren't going well for picking up buffers so keys are 1520 bool run = true;
1442 * scanned to avoid lockup. */
1443 button = rb->button_get_w_tmo(timeout);
1444 if (button != BUTTON_NONE)
1445 goto read_button;
1446 }
1447 1521
1448 draw(NULL); 1522 if(!fft_setup())
1523 return PLUGIN_ERROR;
1449 1524
1450 fft_free_fft_output(); /* COP only */ 1525 while(run)
1526 {
1527 long delay = fft_draw();
1451 1528
1452 long tick = *rb->current_tick; 1529 if(delay <= 0)
1453 if(TIME_BEFORE(tick, next_frame_tick))
1454 {
1455 tick = next_frame_tick - tick;
1456 }
1457 else
1458 { 1530 {
1531 delay = 0;
1459 rb->yield(); /* tmo = 0 won't yield */ 1532 rb->yield(); /* tmo = 0 won't yield */
1460 tick = 0;
1461 } 1533 }
1462 1534
1463 button = rb->button_get_w_tmo(tick); 1535 int button = rb->button_get_w_tmo(delay);
1464 read_button: 1536
1465 switch (button) 1537 switch (button)
1466 { 1538 {
1467 case FFT_QUIT: 1539 case FFT_QUIT:
1468 run = false; 1540 run = false;
1469 break; 1541 break;
1470 case FFT_PREV_GRAPH: { 1542
1471 if (graph_settings.mode-- <= FFT_DM_FIRST) 1543 case FFT_ORIENTATION:
1472 graph_settings.mode = FFT_DM_COUNT-1; 1544 if (++fft.orientation >= FFT_MAX_OR)
1473 graph_settings.changed.mode = true; 1545 fft.orientation = FFT_MIN_OR;
1474 draw(modes_text[graph_settings.mode]); 1546
1547 fft_setting_update(FFT_SETF_OR);
1548 fft_popupmsg(FFT_SETF_OR);
1475 break; 1549 break;
1476 } 1550
1477 case FFT_NEXT_GRAPH: { 1551 case FFT_PREV_GRAPH:
1478 if (++graph_settings.mode >= FFT_DM_COUNT) 1552 if (fft.drawmode-- <= FFT_MIN_DM)
1479 graph_settings.mode = FFT_DM_FIRST; 1553 fft.drawmode = FFT_MAX_DM-1;
1480 graph_settings.changed.mode = true; 1554
1481 draw(modes_text[graph_settings.mode]); 1555 fft_setting_update(FFT_SETF_DM);
1556 fft_popupmsg(FFT_SETF_DM);
1482 break; 1557 break;
1483 } 1558
1484 case FFT_WINDOW: { 1559 case FFT_NEXT_GRAPH:
1485 if(++graph_settings.window_func >= FFT_WF_COUNT) 1560 if (++fft.drawmode >= FFT_MAX_DM)
1486 graph_settings.window_func = FFT_WF_FIRST; 1561 fft.drawmode = FFT_MIN_DM;
1487 graph_settings.changed.window_func = true; 1562
1488 draw(window_text[graph_settings.window_func]); 1563 fft_setting_update(FFT_SETF_DM);
1564 fft_popupmsg(FFT_SETF_DM);
1489 break; 1565 break;
1490 } 1566
1491 case FFT_AMP_SCALE: { 1567 case FFT_AMP_SCALE:
1492 graph_settings.logarithmic_amp = !graph_settings.logarithmic_amp; 1568 if (++fft.amp_scale >= FFT_MAX_AS)
1493 graph_settings.changed.amp_scale = true; 1569 fft.amp_scale = FFT_MIN_AS;
1494 draw(amp_scales_text[graph_settings.logarithmic_amp ? 1 : 0]); 1570
1571 fft_setting_update(FFT_SETF_AS);
1572 fft_popupmsg(FFT_SETF_AS);
1495 break; 1573 break;
1496 } 1574
1497#ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */ 1575#ifdef FFT_FREQ_SCALE /* 'Till all keymaps are defined */
1498 case FFT_FREQ_SCALE: { 1576 case FFT_FREQ_SCALE:
1499 graph_settings.logarithmic_freq = !graph_settings.logarithmic_freq; 1577 if (++fft.freq_scale >= FFT_MAX_FS)
1500 graph_settings.changed.freq_scale = true; 1578 fft.freq_scale = FFT_MIN_FS;
1501 draw(freq_scales_text[graph_settings.logarithmic_freq ? 1 : 0]); 1579
1580 fft_setting_update(FFT_SETF_FS);
1581 fft_popupmsg(FFT_SETF_FS);
1502 break; 1582 break;
1503 }
1504#endif 1583#endif
1505 case FFT_ORIENTATION: { 1584 case FFT_WINDOW:
1506 graph_settings.orientation_vertical = 1585 if(++fft.window_func >= FFT_MAX_WF)
1507 !graph_settings.orientation_vertical; 1586 fft.window_func = FFT_MIN_WF;
1508 graph_settings.changed.orientation = true; 1587
1509 draw(NULL); 1588 fft_setting_update(FFT_SETF_WF);
1589 fft_popupmsg(FFT_SETF_WF);
1510 break; 1590 break;
1511 }
1512 default: {
1513 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1514 return PLUGIN_USB_CONNECTED;
1515 }
1516 1591
1592 default:
1593 exit_on_usb(button);
1594 break;
1517 } 1595 }
1518 } 1596 }
1519 1597
1520 fft_close_fft();
1521
1522#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1523 rb->cpu_boost(false);
1524#endif
1525#ifndef HAVE_LCD_COLOR
1526 grey_release();
1527#endif
1528 backlight_use_settings();
1529 return PLUGIN_OK; 1598 return PLUGIN_OK;
1530 (void)parameter; 1599 (void)parameter;
1531} 1600}