diff options
Diffstat (limited to 'apps/plugins/fft/fft.c')
-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 | } |