diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2012-12-18 20:32:23 -0500 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2012-12-18 20:32:23 -0500 |
commit | a6409c868663a90673147e6f778ea310f08d4f24 (patch) | |
tree | 6c8d7474c6f162735d6bba7f6ef12b567c2b8dff | |
parent | 371c86bf3f4d1708fc40db2aa7fa572eb429d0b4 (diff) | |
download | rockbox-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.c | 1235 |
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; | |||
370 | static char fft_buffer[CACHEALIGN_UP_SIZE(char, BUFSIZE_FFT)] | 376 | static 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 */ |
373 | static int32_t plot_history[ARRAYLEN_PLOT]; | 379 | static uint32_t linf_magnitudes[ARRAYLEN_PLOT]; /* ling freq bin plot */ |
374 | static int32_t plot[ARRAYLEN_PLOT]; | 380 | static uint32_t logf_magnitudes[ARRAYLEN_PLOT]; /* log freq plot output */ |
381 | static uint32_t *plot; /* use this to plot */ | ||
375 | static struct | 382 | static 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 | ||
381 | enum fft_window_func | 388 | /**************************** End of FFT globals ***************************/ |
389 | |||
390 | |||
391 | /********************************* Settings ********************************/ | ||
392 | |||
393 | enum 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 | ||
389 | enum fft_display_mode | 401 | enum 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 | |||
398 | static const unsigned char* const modes_text[FFT_DM_COUNT] = | ||
399 | { "Lines", "Bars", "Spectrogram" }; | ||
400 | 409 | ||
401 | static const unsigned char* const amp_scales_text[2] = | 410 | enum 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 | ||
404 | static const unsigned char* const freq_scales_text[2] = | 418 | enum 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 | ||
407 | static const unsigned char* const window_text[FFT_WF_COUNT] = | 426 | enum 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 | ||
410 | static struct { | 434 | static 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 | |||
454 | static const char cfg_filename[] = "fft.cfg"; | ||
455 | static 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 */ | ||
481 | enum 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 | |||
520 | static struct fft_config fft; | ||
521 | typedef void (* fft_drawfn_t)(unsigned, unsigned); | ||
522 | static fft_drawfn_t fft_drawfn = NULL; /* plotting function */ | ||
523 | static int fft_spectrogram_pos = -1; /* row or column - only used by one at a time */ | ||
524 | static uint32_t fft_graph_scale = 0; /* max level over time, for scaling display */ | ||
525 | static int fft_message_id = -1; /* current message id displayed */ | ||
526 | static char fft_osd_message[32]; /* current message string displayed */ | ||
527 | static 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 */ |
460 | static void apply_window_func(enum fft_window_func mode) | 543 | static 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 */ |
483 | static int32_t calc_magnitudes(bool logarithmic_amp) | 558 | static 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. */ |
544 | static void logarithmic_plot_translate(void) | 616 | static 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 */ |
586 | static void logarithmic_plot_init(void) | 655 | static 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) *********************/ | ||
625 | static void draw_lines_vertical(void); | ||
626 | static void draw_lines_horizontal(void); | ||
627 | static void draw_bars_vertical(void); | ||
628 | static void draw_bars_horizontal(void); | ||
629 | static void draw_spectrogram_vertical(void); | ||
630 | static 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 | |||
648 | static 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 | |||
680 | static 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) | 695 | static 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 | |||
804 | static 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 | ||
879 | static void draw_lines_horizontal(void) | 750 | static 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 | ||
955 | static void draw_bars_vertical(void) | 805 | static 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 | ||
1015 | static void draw_bars_horizontal(void) | 850 | static 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 | ||
1075 | static void draw_spectrogram_vertical(void) | 895 | static 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 | ||
1129 | static void draw_spectrogram_horizontal(void) | 939 | static 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 ********************************/ | ||
1185 | static bool is_playing(void) | 988 | static 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 */ |
1248 | static volatile bool fft_thread_run SHAREDDATA_ATTR = false; | 1051 | static volatile bool fft_thread_run SHAREDDATA_ATTR = false; |
1249 | static unsigned long fft_thread; | 1052 | static unsigned long fft_thread = 0; |
1250 | 1053 | ||
1251 | static long fft_thread_stack[CACHEALIGN_UP(DEFAULT_STACK_SIZE*4/sizeof(long))] | 1054 | static 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 | ||
1254 | static void fft_thread_entry(void) | 1057 | static 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 | ||
1369 | enum plugin_status plugin_start(const void* parameter) | 1172 | /************************** End of FFT functions ***************************/ |
1173 | |||
1174 | |||
1175 | /****************************** OSD functions ******************************/ | ||
1176 | |||
1177 | /* Format a message to display */ | ||
1178 | static 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 | |||
1245 | static 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 | |||
1267 | static 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 | |||
1298 | static void fft_popupmsg(enum fft_setting_flags id) | ||
1299 | { | ||
1300 | fft_message_id = id; | ||
1301 | } | ||
1302 | |||
1303 | /************************** End of OSD functions ***************************/ | ||
1304 | |||
1305 | |||
1306 | static 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 | |||
1369 | static 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 | |||
1430 | static 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 | |||
1442 | static 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 | |||
1465 | static 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 | 1518 | enum 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 | } |