summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2012-12-18 20:32:23 -0500
committerMichael Sevakis <jethead71@rockbox.org>2012-12-18 20:32:23 -0500
commita6409c868663a90673147e6f778ea310f08d4f24 (patch)
tree6c8d7474c6f162735d6bba7f6ef12b567c2b8dff
parent371c86bf3f4d1708fc40db2aa7fa572eb429d0b4 (diff)
downloadrockbox-a6409c868663a90673147e6f778ea310f08d4f24.tar.gz
rockbox-a6409c868663a90673147e6f778ea310f08d4f24.zip
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
-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}