desc:SonicAnalyzer

filename:0,na_sa_disp.png
filename:1,ns_sa_frame.png
filename:2,ns_sa_disp_on.png

slider1:0<0,1,{Log,Lin}>Scale
slider2:0<0,1,{Normal,Inverted}>Signal
slider3:0.0<0,1,0.001>Brightness
slider5:0<-1,1,0.01>Color

@serialize
  file_var(0, spectrum_MODE); 
  file_var(0, spectrogram_FOCUS); 
  file_var(0, spectrogram_BRIGHTNESS); 
  file_var(0, spectrogram_COLOR_SHIFT); 

@init
  TWELFTH_ROOT_OF_TWO = 2^(1/12);
  TUNING = 440;

  function logB(x b)                ( log(x)/log(b); );

  function clamp(x xMin xMax)            ( min(xMax,max(xMin,x)); );
  function clampNorm(x)              ( min(1,max(0,x));  );

  function hz2pxLog(hz maxPx)            ( 0.166834573783595 * log(800 * hz / srate + 1) * maxPx; );
  function px2hzLog(px maxPx)            ( 0.00125 * exp(5.99396142730657 * px / maxPx) * srate - 0.00125 * srate; );
  function px2binLog(px fftsize maxPx)      ( 2 * (0.00125 * exp(5.99396142730657 * px / maxPx) * fftsize - 0.00125 * fftsize); );
  
  function hz2pxLin(hz maxPx)            ( 2.0 * hz * maxPx / srate; );
  function px2hzLin(px maxPx)            ( 0.5 * px * srate / maxPx; );
  function px2binLin(px fftsize maxPx)      ( px * fftsize / maxPx; );

  function scaleValue(blin blog linLogRatio)    ( blin - ((blin - blog) * (1 - linLogRatio)); );
  function px2bin(px fftsize maxPx linLogRatio)  ( scaleValue(px2binLin(px, fftsize, maxPx), px2binLog(px, fftsize, maxPx), linLogRatio); );
  function hz2px(hz maxPx linLogRatio)      ( scaleValue(hz2pxLin(hz, maxPx), hz2pxLog(hz, maxPx), linLogRatio); );
  function px2hz(px maxPx linLogRatio)      ( scaleValue(px2hzLin(px, maxPx), px2hzLog(px, maxPx), linLogRatio); );

  function hz2semitones(hz)            ( -logB(TUNING, TWELFTH_ROOT_OF_TWO) + logB(hz, TWELFTH_ROOT_OF_TWO); );

  function semitones2octaveFloat(semitones)    ( semitones/12+4; );
  function semitones2octave(semitones)      ( floor(semitones2octaveFloat(semitones)); );
  function semitones2keyFloat(semitones)      ( o = semitones2octaveFloat(semitones); (o-floor(o))*12; );
  function semitones2key(semitones)        ( floor(semitones2keyFloat(semitones)); );
  function semitones2centsFloat(semitones)    ( k = semitones2keyFloat(semitones); (k-floor(k))*100; );
  function semitones2cents(semitones)        ( floor(semitones2centsFloat(semitones)); );

  function hz2keyName(hz)
  (
    semitones = hz2semitones(hz);
    octaveFloat = semitones/12+4;
    octave = floor(octaveFloat);
    keyFloat = (octaveFloat-octave)*12;
    key = floor(keyFloat);
    centsFloat = (keyFloat-key)*100;
    cents = floor(centsFloat);

    (key >= 3) ? octave += 1;

    keyName = #;
    keyName =   (key ==  0) ? "A" :
            ((key ==  1) ? "A#" :
              ((key ==  2) ? "B" :
                ((key ==  3) ? "C" :
                  ((key ==  4) ? "C#" :
                    ((key ==  5) ? "D" :
                      ((key ==  6) ? "D#" :
                        ((key ==  7) ? "E" :
                          ((key ==  8) ? "F" :
                            ((key == 9) ? "F#" :
                              ((key == 10) ? "G" :
                                ((key == 11) ? "G#" : "-")))))))))));
    str = #;
    sprintf(str, "%s%d+%03d", keyName, octave, cents);
    sprintf(str, "%8s", str);
    str;
  );

  function getPartial(f n)             ( n * f; );

  function setColor(r g b a)
  (    
    gfx_r = r;
    gfx_g = g;
    gfx_b = b;
    gfx_a = a;
  );

  function blitGfx(id sx sy sw sh tx ty tw th)
  (
    coordinatelist=128*1024*2+id*11; 
    coordinatelist[0]=sx;
    coordinatelist[1]=sy;
    coordinatelist[2]=sw;
    coordinatelist[3]=sh;
    coordinatelist[4]=tx;
    coordinatelist[5]=ty;
    coordinatelist[6]=tw;
    coordinatelist[7]=th;
    coordinatelist[8]=0;
    coordinatelist[9]=0;
    gfx_blitext(id, coordinatelist, 0);
  );

  function blitOffScreen(idSrc idDest sx sy sw sh tx ty tw th)
  (
    gfx_dest = idDest;
    coordinatelist=128*1024*2+idSrc*11; 
    coordinatelist[0]=sx;
    coordinatelist[1]=sy;
    coordinatelist[2]=sw;
    coordinatelist[3]=sh;
    coordinatelist[4]=tx;
    coordinatelist[5]=ty;
    coordinatelist[6]=tw;
    coordinatelist[7]=th;
    coordinatelist[8]=0;
    coordinatelist[9]=0;
    gfx_blitext(idSrc, coordinatelist, 0);
    gfx_dest = -1;
  );

  function blitTiled(src tx ty tw th)
  (
    sw = 0;
    sh = 0;
    sx = 0;
    sy = 0;
    gfx_getimgdim(src, sw, sh);
    gfx_setimgdim(127, tw, th);

    tilesX = ceil(tw/sw);
    tilesY = ceil(th/sh);

    tileX = 0;
    while (        
      tileY = 0;
      sy = 0;
      while (
        blitOffScreen(src, 127, 0, 0, sw, sh, sx, sy, sw, sh);
        blitOffScreen(src, 127, 0, 0, sw, sh, sx, sy, sw, sh);
        sy += sh;
        tileY += 1;
        tileY < tilesY;
      );    
      sx += sw;
      tileX += 1;
      tileX < tilesX;
    );

    blitGfx(127, 0, 0, tw, th, tx, ty, tw, th);
  );

  function blitDisplay(tx ty tw th)
  (
    blitTiled(0, tx, ty, tw, th)
  );

  function blitBlock(sx sy ox oy sw sh ow oh)
  (
    gfx_a = 1;
    coordinatelist=128*1024*2+22;
    coordinatelist[0]=sx;
    coordinatelist[1]=sy;
    coordinatelist[2]=sw-ow;
    coordinatelist[3]=sh-oh;
    coordinatelist[4]=sx+ox;
    coordinatelist[5]=sy+oy;
    coordinatelist[6]=sw-ow;
    coordinatelist[7]=sh-oh;
    coordinatelist[8]=0;
    coordinatelist[9]=0;
    gfx_blitext(-1, coordinatelist, 0);
  );

  function blitBlock2(img sx sy sw sh ox oy ow oh)
  (
    coordinatelist=128*1024*2+22;
    coordinatelist[0]=sx;
    coordinatelist[1]=sy;
    coordinatelist[2]=sw;
    coordinatelist[3]=sh;
    coordinatelist[4]=ox;
    coordinatelist[5]=oy;
    coordinatelist[6]=ow;
    coordinatelist[7]=oh;
    coordinatelist[8]=0;
    coordinatelist[9]=0;
    gfx_blitext(img, coordinatelist, 0);
  );

  function blitPixel(sx sy enabled salpha)
  (
    gfx_a = salpha;
    blitDisplay(sx, sy, 1, 1);
  );

  function blitVerticalPixelLine(sx sy ty enabled salpha)
  (
    (sy > ty) ?
    (
      sy2 = ty;
      ty = sy;
      sy = sy2;
    );

    sx = sx|0;
    sy = sy|0;
    tx = sx;
    ty = ty|0;

    gfx_a = salpha;

    blitDisplay(sx, sy, 1, ty-sy);
  );

  function blitFrame(img sx sy sw sh tx ty tw th sa_l sa_r sa_t sa_b b_l b_r b_t b_b)
  (
    tl_x = sx;
    tl_y = sy;
    br_x = sx+sw;
    br_y = sy+sh;

    dtl_x = tx;
    dtl_y = ty;
    dbr_x = tx+tw;
    dbr_y = ty+th;

    sw_middle = sw - sa_l - sa_r;
    tw_middle = tw - sa_l - sa_r;

    sh_middle = sh - sa_t - sa_b;
    th_middle = th - sa_t - sa_b;

    sx_r = br_x - sa_r;
    sx_l = tl_x + sa_l;
    tx_r = dbr_x - sa_r;
    tx_l = dtl_x + sa_l;
    sy_t = tl_y + sa_t;
    sy_b = br_y - sa_b;
    ty_t = dtl_y + sa_t;
    ty_b = dbr_y - sa_b;
    
    gfx_a = 1;
     
     blitBlock2(img, tl_x,       tl_y,      sa_l,     sa_t,   
            dtl_x,       dtl_y,       sa_l,     sa_t); 
     blitBlock2(img, sx_r,       tl_y,       sa_r,     sa_t,   
            tx_r,       dtl_y,       sa_r,     sa_t); 
     blitBlock2(img, tl_x,       sy_b,       sa_l,     sa_b,   
            dtl_x,       ty_b,       sa_l,     sa_b); 
     blitBlock2(img, sx_r,       sy_b,       sa_r,     sa_b,    
            tx_r,       ty_b,       sa_r,     sa_b); 
     blitBlock2(img, sx_l,       tl_y,       sw_middle,   b_t, 
            tx_l,       dtl_y,       tw_middle,   b_t);
     blitBlock2(img, sx_l,       br_y - b_b,   sw_middle,   b_b, 
            tx_l,       dbr_y - b_b,   tw_middle,   b_b); 
     blitBlock2(img, tl_x,       sy_t,       b_l,     sh_middle, 
            dtl_x,       ty_t,       b_l,     th_middle);
     blitBlock2(img, br_x - b_r,   sy_t,       b_r,     sh_middle, 
            dbr_x - b_r,   ty_t,       b_r,     th_middle); 
  );

  function blitFrame(img tx ty tw th sa_l sa_r sa_t sa_b b_l b_r b_t b_b)
  (
    sx = 0;
    sy = 0;
    sw = 0;
    sh = 0;
    gfx_getimgdim(img, sw, sh);
    blitFrame(img, sx, sy, sw, sh, tx, ty, tw, th, sa_l, sa_r, sa_t, sa_b, b_l, b_r, b_t, b_b)
  );

  function blitFrame(tx ty tw th)( blitFrame(NS_UI_FRAME_IMG, tx, ty, tw, th, ui_safe_area_L, ui_safe_area_R, ui_safe_area_T, ui_safe_area_B, ui_border_L, ui_border_R, ui_border_T, ui_border_B) );

  function blitMoveBlock(sx sy sw sh dx)(  blitBlock(sx, sy, dx, 0, sw, sh, 0, 0);  );

  function colorPixelAdd(x y r g b useFrameBuffer)
  (
    gfx_x = x;
    gfx_y = y;
      useFrameBuffer == 1 ? blitPixel(gfx_x, gfx_y, 0, 1);
    gfx_getpixel(gfx_r, gfx_g, gfx_b);
    gfx_setpixel(clampNorm(gfx_r+r), clampNorm(gfx_g+g), clampNorm(gfx_b+b));
  );

  function colorPixelAdd(x y r g b)(  colorPixelAdd(x, y, r, g, b, 0); );

  function colorBlockAdd(x y w h r g b useFrameBuffer)
  (
    i = 0;
    l = w;
    j = 0;
    k = h;
    gfx_x = x;
    gfx_y = y;

    (w != 0 && h != 0) ?
    (
      while
      (
        j = 0;
        gfx_y = y;
          useFrameBuffer == 1 ? blitDisplay(gfx_x, gfx_y, 1, h);
        while
        (
          gfx_getpixel(gfx_r, gfx_g, gfx_b);
          gfx_setpixel(clampNorm(gfx_r+r), clampNorm(gfx_g+g), clampNorm(gfx_b+b));
          j += 1;
          gfx_y += 1;
          j <= k;
        );

        i += 1;
        gfx_x += 1;
        i <= l;
      );
    );
  );

  function colorBlockAdd(x y w h r g b) ( colorBlockAdd(x, y, w, h, r, g, b, 0);  );

  function blitSpectrumVerticalZeroBased(x y h v r g b baseEnabled)
  (
    blitVerticalPixelLine(x, y, y+h, baseEnabled, 1);

    v = clampNorm(v);
    yA = h-v*h;
    yB = h;

    colorBlockAdd(x, y+yA, 1, yB-yA, r, g, b);
  );

  function blurArea(x y w h n)
  (    
    loop(n,
      gfx_x = x;
      gfx_y = y;
      gfx_blurto(gfx_x+w, gfx_y+h);
    );
  );

  function mapSpectrogramColors(mv scale1 scale2 scale3)
  (
  	spectrogram_FOCUS ? mv = 168+mv;
    modr = log(168)-log(abs(mv));
    modr *= modr;
    modr *= modr;
    modr /= phi2;


    gfx_r = modr-(1-goniometer_ph)/4; 
    gfx_g = modr-goniometer_ph/4; 
    gfx_b = (modr*modr)/phi2;

    gfx_r = clampNorm(gfx_r*spectrogram_BRIGHTNESS_RED);
    gfx_g = clampNorm(gfx_g*spectrogram_BRIGHTNESS_GREEN);
    gfx_b = clampNorm(gfx_b*spectrogram_BRIGHTNESS_BLUE);
  );

  function drawString(x y str)
  (
    gfx_x = x;
    gfx_y = y;
    gfx_drawstr(str);
  );

  function drawString(x y str w h)
  (
    drawString(x, y, str);
    gfx_measurestr(str, w, h);
  );

  function drawNumber(x y num precision unit maxLen)
  (
    gfx_x = x;
    gfx_y = y;
    str = #;
    format = #;
    precision > 0   ? sprintf(format, "%%.%d%s%%s", precision, "f")
            : sprintf(format, "%%%d%s%%s", precision, "d");            
    sprintf(str, format, num, unit);
    sprintf(format, "%%%ds", maxLen);
    sprintf(str, format, str);

    gfx_drawstr(str);
  );

  function mouseInArea(x y w h)
  (
    area_x = x;
    area_y = y;
    area_w = w;
    area_h = h;

    mouse_y >= y && mouse_y <= (y+h) && (mouse_x >= x && mouse_x <= (x+w));
  );

  function detectBeat()
  (
    ((beat_position-0.5) % ts_denom == 0) ?
    (
      (beatDetected == -1) ?
      (
        baseEnabled = 1;
        beatDetected = 1;
      ) :
      (
        baseEnabled = 0;
      );
    ) :
    (
      baseEnabled = 0;
      beatDetected = -1;
    );
  );

  goniometer_normalize = 1;

  function initGoniometer()
  (
    goniometer_xPos = 500000; // memory offset 1
    goniometer_yPos = 500000*2; // memory offset 2
    goniometer_rot = -$pi/4; 
    goniometer_MAXDRAWSPLS = 10000;
    goniometer_PHASEUPDATERATE = srate/8;

    goniometer_n = 0;
    goniometer_phC = 0;
    goniometer_ph = 0;
  );

  function sampleGoniometer(NS_SAMPLE_0 NS_SAMPLE_1)
  (
    goniometer_s0 = sign(NS_SAMPLE_0);
    goniometer_s1 = sign(NS_SAMPLE_1);
    
    goniometer_angle = atan(NS_SAMPLE_0/NS_SAMPLE_1);
    
    ((goniometer_s0 == 1 && goniometer_s1 == -1) || (goniometer_s0 == -1 && goniometer_s1 == -1)) ? ( goniometer_angle += $pi; );
    (goniometer_s0 == -1 && goniometer_s1 == 1) ? (  goniometer_angle += $pi*2;  );

    (NS_SAMPLE_1 == 0) ? ( (NS_SAMPLE_0 > 0) ? ( goniometer_angle = $pi/2;  ) : ( goniometer_angle = $pi*1.5; ); );
    (NS_SAMPLE_0 == 0) ? ((NS_SAMPLE_1 > 0) ? ( goniometer_angle = 0; ) : ( goniometer_angle = $pi; ); );
    
    goniometer_radius = sqrt(sqr(NS_SAMPLE_0)+sqr(NS_SAMPLE_1));    
    goniometer_angle -= goniometer_rot;
    
    goniometer_xPos[goniometer_b] = cos(goniometer_angle)*goniometer_radius;
    goniometer_yPos[goniometer_b] = sin(goniometer_angle)*goniometer_radius;
    
    (goniometer_b < goniometer_MAXDRAWSPLS) ? (  goniometer_b += 1; );
    (goniometer_s0 != goniometer_s1) ? ( goniometer_phC -= 1; ) : (  goniometer_phC += 1; );

    ((goniometer_n += 1) > goniometer_PHASEUPDATERATE) ? 
    (
      goniometer_avgCorrelation = goniometer_phC / goniometer_n;
      goniometer_ph = (1-goniometer_avgCorrelation)/2;
      goniometer_phC = goniometer_n = 0;
    );
  );

  function normalizeGoniometerSamples()
  (
    goniometer_i = min(goniometer_b, goniometer_MAXDRAWSPLS);

    g_min = 0;
    g_max = 0;

    while 
    (
      gx = goniometer_xPos[goniometer_i];
      gy = goniometer_yPos[goniometer_i];

      g_min = min(g_min, min(gx, gy));
      g_max = max(g_max, max(gx, gy));

      (goniometer_i -= 1) > 0;
    );

    g_norm = max(abs(g_min), g_max)*2;
  );

  function blitGoniometer()
  (
    goniometer_half_size = goniometer_SIZE/2;
    goniometer_sizeDSqr05 = goniometer_half_size * 0.70710681;

    (goniometer_normalize == 1) ? normalizeGoniometerSamples();

    goniometer_i = min(goniometer_b, goniometer_MAXDRAWSPLS);
    
    while 
    (
      gfx_a = 1;
      gw = ((goniometer_xPos[goniometer_i] / g_norm)*2.7)*goniometer_sizeDSqr05;
      gh = ((goniometer_yPos[goniometer_i] / g_norm)*2.7)*goniometer_sizeDSqr05;
      gfx_x = (goniometer_half_size+gw+goniometer_X)-2;
      gfx_y = (goniometer_half_size-gh+goniometer_Y)-1;

      (gfx_x > goniometer_X-5 && gfx_x < goniometer_X+goniometer_SIZE+10 &&
       gfx_y > goniometer_Y-5 && gfx_y < goniometer_Y+goniometer_SIZE+10) ?
        colorPixelAdd(gfx_x, gfx_y, 1-(0.5+0.5*goniometer_avgCorrelation), (0.5+0.5*goniometer_avgCorrelation), 0);

      (goniometer_i -= 1) > 0;
    );

    blurArea(goniometer_X-10, goniometer_Y-10, goniometer_SIZE+15, goniometer_SIZE+15, 1);
    goniometer_b = 0;
  );

  phi = 1.6803398875;
  phi2 = 1.6803398875+1.6803398875;

  spectrum_minVol = 10^(-192*8);
  spectrum_gainScale = 10/log(10);

  spectrum_24th = 1/24;
  spectrum_scl = spectrum_24th*0.125;
  spectrum_scl2 = spectrum_24th*0.25;
  spectrum_scl3 = spectrum_24th*0.5;

  function initSpectrumMeter(resolution)
  (
    spectrum_recpos = 0;
    spectrum_fftsize = resolution;
    spectrum_ifftsize = (1/spectrum_fftsize)*0.5;
    spectrum_histsize = 128*1024;
    spectrum_window = spectrum_histsize;
    spectrum_fftworkspace = spectrum_window+resolution*2;
    spectrum_lrecpos = 0;
  );

  function updateSizes()
  (
    NS_UI_FRAME_IMG = 1;

    ui_safe_area_L = 35;
    ui_safe_area_R = 35;
    ui_safe_area_T = 25;
    ui_safe_area_B = 25;

    ui_border_L = ui_safe_area_L;
    ui_border_R = ui_safe_area_R;
    ui_border_T = 5;
    ui_border_B = 5;

    ui_frame_left = 5;
    ui_frame_middle = gfx_w - 235;
    ui_frame_right = gfx_w - 5;

    goniometer_SIZE = 140;
    goniometer_X = gfx_w-230+45;
    goniometer_Y = 20;

    spectrum_X = gfx_w-230+35;
    spectrum_Y = 175;
    spectrum_W = 155;
    spectrum_H = gfx_h-185;
    spectrum_R = spectrum_X+spectrum_W;
    spectrum_B = spectrum_Y+spectrum_H;

    spectrogram_X = 40;
    spectrogram_Y = 175;
    spectrogram_W = gfx_w-230-40-35;
    spectrogram_H = gfx_h-185;
    spectrogram_R = spectrogram_X+spectrogram_W;
    spectrogram_B = spectrogram_Y+spectrogram_H;

    volume_L_x = spectrogram_X;
    volume_L_y = 10;
    volume_L_w = spectrogram_W;
    volume_L_h = 44;

    volume_R_x = spectrogram_X;
    volume_R_y = 65;
    volume_R_w = spectrogram_W;
    volume_R_h = volume_L_h;

    phase_x = spectrogram_X;
    phase_y = 120;
    phase_w = spectrogram_W;
    phase_h = volume_L_h;

    marker_horizontal_W = spectrogram_W+spectrum_W+ui_safe_area_L+ui_safe_area_R;

    gfx_setimgdim(126, spectrogram_W, gfx_h);

    initSpectrumMeter(2^15);
  );

  function drawHorizontalGridLine(f w h x y drawLabel labelOffset linear)
  (
    gfx_x = x;
    gfx_y = y + h - hz2px(f, h, spectrum_MODE);

    r = gfx_r;
    g = gfx_g;
    b = gfx_b;

    colorBlockAdd(gfx_x, gfx_y, w-1, 1, gfx_r, gfx_g, gfx_b);
    
    setColor(r, g, b, gfx_a);

    (drawLabel == 1) ?
    (
      gfx_y = y + h - hz2px(f, h, spectrum_MODE) - 8;    
      (f < 1000)   ? ( drawNumber(gfx_x - labelOffset, gfx_y, f, 0, "", 5); ) 
            : ( drawNumber(gfx_x - labelOffset, gfx_y, f/1000, 1, "k", 5); );
    );
  );

  function drawVerticalGridLine(value)
  (
    gfx_a = 0.75;
    gfx_g = 0.5;
    gfx_x = spectrum_R-spectrum_W*value;
    gfx_y = spectrum_Y;
    gfx_lineto(gfx_x, spectrum_B, 1);

    gfx_a = 0.5;
    gfx_x += 2;
    gfx_y = spectrum_B - 10;

    (value == -7/-64) ?  ( gfx_drawstr("0");  ) :
      ((value == -23/-64) ? (  gfx_drawstr("-24"); ) :
        ((value == -39/-64) ? (  gfx_drawstr("-48"); ) :
          ((value == -56/-64) ? ( gfx_drawstr("-72"); ))));
  );

  function drawFreqMarkers(f baseBands octaveBands x y w h linear)
  (
    (baseBands && (f == 20 || f == 100 || f == 500 || f == 1000 || f == 5000 || f == 10000 || f == 20000)) ?
    (
      setColor(1, 1, 0, 1);
      drawHorizontalGridLine(f, w, h, x, y, 1, 60, linear);
    );

    (octaveBands && (f == 7 || f == 14 || f == 27 || f == 55 || f == 110 || f == 220 || f == 440 || f == 880 || f == 1760 || f == 3520 || f == 7040 || f == 14080 || f == 28160)) ?
    (
      setColor(1, 0, 0, 1);
      drawHorizontalGridLine(f, w, h, x, y, 1, 60, linear);
    );
  );

  function drawOvertoneMarker(freq n label)
  (
    p = spectrum_B-hz2px(getPartial(freq, n), spectrogram_H, spectrum_MODE);
    p > spectrum_Y ?
    (    
      setColor(1, 0, 1, 0.75);
      colorBlockAdd(spectrogram_X, p, marker_horizontal_W, 1, gfx_r, gfx_g, gfx_b);
      setColor(1, 1, 1, 0.75);
      drawString(bx+25, p-10, label);    
    );
  );

  function showToolTip(x y w h str)
  (
 	mouseInArea(x, y, w, h) ?
    (
    	strW = 0;
    	strH = 0;
    	gfx_measurestr(str, strW, strH);

    	bx = mouse_x + 10;
    	by = mouse_y - strH - 15;
    	bw = strW + 5;
    	bh = strH + 5;

    	bx < x ? bx = x;
    	by < y ? by = y;
    	bx + bw >= x+w ? bx = x+w-bw;
    	by + bh >= y+h ? by = y+h-bh;

      	blurArea(bx, by, bw, bh, 16);
	    setColor(0, 0, 0, 0.5);
      	gfx_rect(bx, by,   bw,   bh);
      	setColor(1, 1, 1, 1);
      	gfx_rect(bx-1, by-1,     bw+2,     bh+2,   -1);
     
		drawString(bx+2.5, by+2.5, str);
    );
  );

  function drawFreqGrid(x y w h linear)
  (
    setColor(0.95, 0.05, 0, 0.25);
    px = hz2px(20, h, spectrum_MODE);
    gfx_rect(x, y+h-px, w, px);
    px = hz2px(20000, h, spectrum_MODE);
    gfx_rect(x, y, w, h-px);

    f = 0;
    mod = 10;

    while
    (
      ((f % mod) == 0) ?
      (
        setColor(0.0, 0.25, 0, 0.5);
        drawHorizontalGridLine(f, w, h, x, y, 0, 0, linear);
      );
      drawFreqMarkers(f, mouse_cap == 0, mouse_cap > 0, x, y, w, h, linear);
      f += 1;
      (f == 100) ? mod = 100 : ((f == 1000) ?  mod = 1000; );
      f < srate / 2;
    );

    drawVerticalGridLine(-7 / -64); // 0dB
    drawVerticalGridLine(-23 / -64); // 24db
    drawVerticalGridLine(-39 / -64); // 48dB
    drawVerticalGridLine(-56 / -64); // 72dB

    mouseOverSpectrogram = mouseInArea(spectrogram_X, spectrogram_Y, spectrogram_W, spectrogram_H);
    mouseOverSpectrum = mouseInArea(spectrum_X, spectrum_Y, spectrum_W, spectrum_H);
    mouseOverVolumeOrPhase = mouseInArea(volume_L_x, volume_L_y, volume_L_w, volume_L_h) || mouseInArea(volume_R_x, volume_R_y, volume_R_w, volume_R_h) || mouseInArea(phase_x, phase_y, phase_w, phase_h);

    mouseOverSpectrogram || mouseOverSpectrum || mouseOverVolumeOrPhase?
    (
      bx = gfx_w - 115;
      by = mouse_y - (mouse_y <= spectrum_Y+40 ? 0 : (mouse_y <= spectrum_B-40 ? 15 : 32));
      bw = 78;
      bh = 17*2;

      freq = px2hz(spectrogram_H - (mouse_y - spectrogram_Y), spectrogram_H, spectrum_MODE);

      drawOvertoneMarker(freq, 1, "");
      setColor(1, 0, 1, 0.75);
      mouseOverSpectrogram || mouseOverVolumeOrPhase  ?	colorBlockAdd(mouse_x, 5, 1, gfx_h-11, gfx_r, gfx_g, gfx_b);
      mouseOverSpectrum ? 								colorBlockAdd(mouse_x, spectrum_Y, 1, spectrum_H, gfx_r, gfx_g, gfx_b);
      mouseOverVolumeOrPhase ? 
      (
		setColor(1, 0, 1, 0.75);
		colorBlockAdd(volume_L_x, mouse_y, volume_L_w, 1, gfx_r, gfx_g, gfx_b);
	  );
      mouse_cap ?
      (
        drawOvertoneMarker(freq, 2, "2nd");
        drawOvertoneMarker(freq, 3, "3rd");
        drawOvertoneMarker(freq, 4, "4th");
        drawOvertoneMarker(freq, 5, "5th");
        drawOvertoneMarker(freq, 6, "6th");
        drawOvertoneMarker(freq, 7, "7th");
        drawOvertoneMarker(freq, 8, "8th");
        drawOvertoneMarker(freq, 9, "9th");
        drawOvertoneMarker(freq, 10, "10th");
      );

      str = #;
      sprintf(str, "%5d Hz\n%8s", freq, hz2keyName(freq));
      showToolTip(spectrogram_X, spectrogram_Y, spectrogram_W, spectrogram_H, str);   
      showToolTip(spectrum_X, spectrum_Y, spectrum_W, spectrum_H, str);   
    );
  );

  function gfxDrawGoniometerCross(tlx tly brx bry gc roh)
  (
    gfx_line(0,             0,     goniometer_SIZE+roh,     goniometer_SIZE+roh,     0);
    gfx_line(1,             1,     goniometer_SIZE+roh-1,     goniometer_SIZE+roh-1,     0);
    gfx_line(goniometer_SIZE+roh*2,    0,     0,               goniometer_SIZE+roh*2,     0);
    gfx_line(goniometer_SIZE+roh*2-1,  1,     1,               goniometer_SIZE+roh*2-1,  0);
    gfx_line(roh,             gc,    goniometer_SIZE+roh,     gc,             0);
    gfx_line(gc,             roh,   gc,             goniometer_SIZE+roh,     0); 
  );

  function gfxDrawGoniometerCircles(gc ghw)
  (
    gfx_circle(gc, gc, ghw/3.6,   -1, 0); 
    gfx_circle(gc, gc, ghw/3.59,   -1, 0); 
    gfx_circle(gc, gc, ghw/1.9,   -1, 0); 
    gfx_circle(gc, gc, ghw/1.89,   -1, 0); 
    gfx_circle(gc, gc, ghw/1.3,   -1, 0); 
    gfx_circle(gc, gc, ghw/1.29,   -1, 0); 
    gfx_circle(gc, gc, ghw,     -1, 0); 
    gfx_circle(gc, gc, ghw/1.01,   -1, 0);
  );

  function gfxDrawGoniometer()
  (
    ro = 15;
    roh = ro / 2;
    gs = goniometer_SIZE + ro;
    gx = goniometer_X - 10;
    gy = goniometer_Y - 10;
    gc = goniometer_half_size + ro / 2;
    ghw = goniometer_half_size;

    tlx = ro;
    tly = ro;
    brx = goniometer_SIZE;
    bry = goniometer_SIZE;

    gfx_dest = 127;
    gfx_setimgdim(127, gs, gs);
    
    setColor(1, 1, 1, 0.5);

    gfxDrawGoniometerCross(tlx, tly, brx, bry, gc, roh);
    gfxDrawGoniometerCircles(gc, ghw);
    blurArea(0, 0, gs, gs, 2);
    gfxDrawGoniometerCross(tlx, tly, brx, bry, gc, roh);
    gfxDrawGoniometerCircles(gc, ghw);
    blurArea(0, 0, gs, gs, 1);

    gfx_dest = -1;

    gfx_a = 0.9;
    blitGfx(127, 0, 0, gs, gs, gx, gy, gs, gs);

    gfx_a = 0.65;
    blitTiled(0, gx, gy, gs, gs);
    gfx_mode = 0.0;

    gfx_a = 0.6;
    drawString(gx+5,     gy+5,     "+L");
    drawString(gx+5,     gy+gs-10,   "+R");
    drawString(gx+gs-20,   gy+5,     "-R");
    drawString(gx+gs-20,   gy+gs-10,   "-L");
  );

  function gfxSonicAnalyzerSpectrum2()
  (
    gfx_a = 1;
    blitTiled(0, spectrum_X, spectrum_Y, spectrum_W, gfx_h - spectrum_Y);    
    spectrogram_RESTORE ?  blitOffScreen(126, -1, 0, 0, spectrogram_W-1, gfx_h, spectrogram_X, 0, spectrogram_W-1, gfx_h);
    (baseEnabled && beatDetected) ? blitTiled(2, spectrogram_W+38, 5, 1, gfx_h - 10);

    (play_state == 1 || play_state == 5) ?
    (
      gfxDrawGoniometer();
      
      gfx_a = 1;
      gfx_mode = 0.0;

      xA = spectrogram_R-1;
      abs0 = abs(spl0);
      abs1 = abs(spl1);
      
      blitSpectrumVerticalZeroBased(xA,   10,  45, abs0, abs0, 1-abs0, 0.128, baseEnabled);
      blitSpectrumVerticalZeroBased(xA,   65,  45, abs1, abs1, 1-abs1, 0.128, baseEnabled);
      blitSpectrumVerticalZeroBased(xA,   110,  55, goniometer_ph, goniometer_ph, 1-goniometer_ph, 0.0, baseEnabled);
      blitMoveBlock(spectrogram_X+1, spectrogram_Y, spectrogram_W, spectrogram_H, -1);      
      blitMoveBlock(spectrogram_X+1, 10, spectrogram_W, 45, -1);
      blitMoveBlock(spectrogram_X+1, 65, spectrogram_W, 45, -1);
      blitMoveBlock(spectrogram_X+1, 120, spectrogram_W, 45, -1);
      blitGoniometer();

      spectrum_recpos != spectrum_lrecpos ?
      (
        (spectrum_windowsize != spectrum_fftsize) ?
        (
          multiplier = $pi; 
          spectrum_windowsize = (spectrum_fftsize-1);
          spectrum_i = 0;
          spectrum_deltawinpos = ($pi * 2) / spectrum_windowsize;
          spectrum_winpos = 0;

          loop(
            spectrum_fftsize,
            spectrum_window[spectrum_i] = (0.35875 - 0.48829 * cos(spectrum_winpos) + 0.14128 * cos(2 * spectrum_winpos) - 0.01168 * cos(6 * spectrum_winpos)) * multiplier * spectrum_ifftsize; // blackman-harris
            spectrum_winpos += spectrum_deltawinpos;
            spectrum_i += 1;
          );
        );

        spectrum_lrecpos = spectrum_recpos;

        spectrum_buf1 = spectrum_lrecpos - spectrum_fftsize;
        (spectrum_buf1 < 0) ? spectrum_buf1 += spectrum_histsize;
        spectrum_buf2 = spectrum_window;
        spectrum_buf3 = spectrum_fftworkspace;

        loop(
          spectrum_fftsize,
          spectrum_buf3[] = spectrum_buf1[] * spectrum_buf2[];
          spectrum_buf3[1] = 0;
          spectrum_buf3 += 2;
          spectrum_buf2 += 1;
          (spectrum_buf1 += 1) >= spectrum_histsize ? spectrum_buf1 -= spectrum_histsize;
        );

        fft(spectrum_fftworkspace, spectrum_fftsize);
        fft_permute(spectrum_fftworkspace, spectrum_fftsize);

        spectrum_i=0;

        loop(spectrogram_H,
          spectrum_tpos = (px2bin(spectrum_i, spectrum_fftsize, spectrogram_H, spectrum_MODE)/2)|0; 
          spectrum_tpos >= spectrum_fftsize*0.5 ? spectrum_tpos = spectrum_fftsize*0.5;
          spectrum_lfftpos >= spectrum_tpos ? ( spectrum_lfftpos = spectrum_tpos-1; );
          spectrum_mv = 0;

          loop(spectrum_tpos - spectrum_lfftpos,
            spectrum_j = max(spectrum_lfftpos,0) * 2.0;             
            spectrum_spl_a = spectrum_fftworkspace[spectrum_j];
            spectrum_spl_b = spectrum_fftworkspace[spectrum_j + 1];
            spectrum_dv = spectrum_spl_a*spectrum_spl_a + spectrum_spl_b*spectrum_spl_b;
            spectrum_dv /= 20;             
            spectrum_dv < 0.00000000000000000000001 ? spectrum_dv = 0.0000000000000000000000001;
            spectrum_dv > spectrum_mv ? spectrum_mv = spectrum_dv;
            spectrum_lfftpos += 1;
          );

          spectrum_mv = spectrum_mv <= spectrum_minVol ? -500 : log(spectrum_mv)*spectrum_gainScale;      
          
          gfx_y = spectrogram_Y + (spectrogram_H - spectrum_i-1);
          xv = spectrum_W*(spectrum_mv/-96);
          y = gfx_y-1;
          w = spectrum_W-xv;
          h = 1;

          mapSpectrogramColors(spectrum_mv, spectrum_scl, spectrum_scl2, spectrum_scl3);
          gfx_r += 0.125;
          mr = gfx_r;
          mg = gfx_g;
          mb = gfx_b;

          colorPixelAdd(spectrogram_R - 2, gfx_y, mr, mg, mb, 1);
          colorBlockAdd(spectrum_X, y, w, h, mr, mg, mb, 1);

          spectrum_i+=1;
        );
      );
    );

    blitOffScreen(-1, 126, spectrogram_X, 0, spectrogram_W-1, gfx_h, 0, 0, spectrogram_W-1, gfx_h);
    spectrogram_RESTORE = 1;
    
    drawFreqGrid(spectrum_X, spectrum_Y, spectrum_W, spectrum_H, spectrum_MODE);
  );

  ext_noinit = 1.0; 
  gfx_clear = -1;

  initGoniometer();
  updateSizes();

@slider
  spectrum_MODE = slider1;
  spectrogram_FOCUS = slider2;
  spectrogram_BRIGHTNESS = (1-slider3)/2;
  spectrogram_RED_INTENSITY = 1+slider5*1;
  spectrogram_GREEN_INTENSITY = 0.8+slider5*0.8;
  spectrogram_BLUE_INTENSITY = 1.4-slider5*1.4;
  spectrogram_COLOR_SHIFT = slider5;
  spectrogram_BRIGHTNESS_RED = spectrogram_RED_INTENSITY/spectrogram_BRIGHTNESS;
  spectrogram_BRIGHTNESS_GREEN = spectrogram_GREEN_INTENSITY/spectrogram_BRIGHTNESS;
  spectrogram_BRIGHTNESS_BLUE = spectrogram_BLUE_INTENSITY/spectrogram_BRIGHTNESS;

@sample
  spectrum_recpos[] = spl0+spl1;
  spectrum_recpos+=1;
  spectrum_recpos >= spectrum_histsize ? spectrum_recpos=0;

  sampleGoniometer(spl0, spl1);

@gfx 800 600
  gfx_mode = 0.0; 
  gfx_a = 1;     

  gfx_w != last_w || gfx_h != last_h ? 
  (
    updateSizes();
    blitTiled(0, 0, 0, gfx_w, gfx_h);
  );  

  detectBeat();
  gfxSonicAnalyzerSpectrum2();

  blitFrame(5,            5,  ui_frame_middle,  55);
  blitFrame(5,           60,  ui_frame_middle,  55);
  blitFrame(5,          115,  ui_frame_middle,  55);
  blitFrame(5,          170,  ui_frame_middle,  spectrum_H+10);
  blitFrame(ui_frame_middle+5,  170,  225,        spectrum_H+10);
  blitFrame(ui_frame_middle+5,    5,  225,        165);

	showToolTip(volume_R_x, volume_R_y, volume_R_w, volume_R_h, "Volume right");
	showToolTip(volume_L_x, volume_L_y, volume_L_w, volume_L_h, "Volume left");
	showToolTip(phase_x, phase_y, phase_w, phase_h, "Phase correlation");

  last_w = gfx_w;
  last_h = gfx_h;
