view VibratoDemoUnit.cpp @ 0:a6a46af64546

first upload
author wenx <xue.wen@eecs.qmul.ac.uk>
date Wed, 10 Aug 2011 14:55:38 +0100
parents
children
line wrap: on
line source
/*
    Harmonic Visualiser

    An audio file viewer and editor.
    Centre for Digital Music, Queen Mary, University of London.
    This file copyright 2011 Wen Xue.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version. 
*/
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <Math.hpp>
#include <math.h>
#include "VibratoDemoUnit.h"
#include "Matrix.h"
#include "Unit1.h"
#include "splines.h"
#include "hssf.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TVibratoDemoForm *VibratoDemoForm;
//---------------------------------------------------------------------------
const double updb=10, downdb=-80, dbrange=updb-downdb;
const double log10e=Log10(exp(1));
const Bdw=5, Bdwc=2;


__fastcall TVibratoDemoForm::TVibratoDemoForm(TComponent* Owner)
  : TForm(Owner)
{
  ForceUpdate=false;

	SUThread=0;
	pThread=0;
	memset(ThreadList, 0, sizeof(TSUThread*)*ThreadCaps);

  WaveAudio1=new TWaveAudio(NULL);
  WaveAudio1->UseMemoryStream=true;
	WaveAudio1->Channels=1;
  WaveView1=new TWaveView(NULL);
  WaveView1->Parent=Panel2;
  WaveView1->Align=alClient;
  WaveView1->WaveAudio=WaveAudio1;
  WaveView1->OnGetOpMode=WaveView1OpMode;
  WaveView1->CreatePanes(1, 1);
  WaveView1->SetContent(0, 0, 0, 1);
  WaveView1->ClickFocus=true;
  WaveView1->DefaultPopupMenu=false;
  WaveView1->CustomInfo=WaveView1CustomInfo;
  WaveView1->InfoLeft=5; WaveView1->InfoTop=5;

  WaveAudio2=new TWaveAudio(NULL);
  WaveAudio2->UseMemoryStream=true;
  WaveAudio2->Channels=1;
  WaveView2=new TWaveView(NULL);
  WaveView2->Parent=Panel3;
  WaveView2->Align=alClient;
  WaveView2->WaveAudio=WaveAudio2;
  WaveView2->CreatePanes(1, 1);
  WaveView2->SetContent(0, 0, 0, 1);
  WaveView2->ClickFocus=true;
  WaveView2->DefaultPopupMenu=false;
  WaveView2->CustomInfo=WaveView1CustomInfo;
  WaveView2->InfoLeft=5; WaveView2->InfoTop=5;


  HS=0;
  CurrentModCycle=-1;
  peaky=0;
  cyclefrs=0;
  cyclefs=0;

  datain=0;
}

__fastcall TVibratoDemoForm::~TVibratoDemoForm()
{
	delete WaveAudio1;
	delete WaveAudio2;
  delete WaveView1;
  delete WaveView2;
  delete HS;
	delete[] peaky;
  delete[] cyclefrs;
	delete[] cyclefs;
	for (int i=0; i<ThreadCaps; i++) if (ThreadList[i]) delete ThreadList[i];
  delete[] datain;
}
//---------------------------------------------------------------------------
void TVibratoDemoForm::FindNote(){}

void TVibratoDemoForm::Reset()
{
	if (!HS) return;
	if (HS->M<=0 || HS->Fr<=0) return;

	if (ListBox1->ItemIndex<0) ListBox1->ItemIndex=0;
	double Fs=WaveView1->SamplesPerSec;
	int FSMode=ListBox1->ItemIndex;
	double FSF=FEdit->Text.ToDouble();
	int FSFScale=FScaleCombo->ItemIndex;
	if (FSFScale==0) FSF/=Fs;
  else FSF/=Amel*log(1+Fs/700);
	double FStheta=ThetaEdit->Text.ToDouble();

  double sps=WaveView1->SamplesPerSec;
  double h=WaveView1->SpecOffst;
	AnalyzeV(*HS, V, peaky, cyclefrs, cyclefs, sps, h, &cyclefrcount, FSMode, FSF, FSFScale, FStheta);
	SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
  SaveV();
  UpdateDisplay();
}

void TVibratoDemoForm::Copydata()
{
  int dst, den;
  double* xrec;
  if (HS)
  {
    xrec=SynthesisHSp(HS, dst, den);
    if (dst<0) dst=0;

    delete[] datain;
    datain=new __int16[den-dst];

    DoubleToInt(datain, sizeof(__int16), xrec, den-dst);

    free8(xrec);
  }
}

void TVibratoDemoForm::Synthesize()
{
  int dst, den;
  double* xrec;
  if (HS)
  {
    double t=GetTickCount();
    xrec=SynthesisHS(HS, dst, den);
    t=GetTickCount()-t;
    Label13->Caption=t;
  }
  int bips=WaveAudio1->BitsPerSample;
	double max=(1<<(bips-1)); for (int i=dst; i<den; i++) if (xrec[i]>max) max=xrec[i]; else if (xrec[i]<-max) max=-xrec[i];
	if (max>(1<<(bips-1)))
	{
		max=(1<<(bips-1))/max;
		for (int i=dst; i<den; i++) xrec[i]*=max;
	}
  int bps=bips/8;
  WaveAudio2->Clear(NULL);
  WaveAudio2->GetWaveProperties(WaveAudio1);
  if (HS)
  {
    if (dst<0) dst=0;
    void* data=new char[(den-dst)*bps];
    DoubleToInt(data, bps, xrec, den-dst);

    WaveAudio2->Clear(NULL);
    WaveAudio2->WriteSamples(data, den-dst);
    free8(xrec);
    delete[] data;
  }
}

  //for frequency modulator shape
  void DrawBarMap(Graphics::TBitmap* Bitmap, TRect Rect, int Count, double* data, bool res, int* KX1=0, int* KX2=0)
  {
    TCanvas* Canvas=Bitmap->Canvas;
    Bitmap->Width=Rect.Width();
    Bitmap->Height=Rect.Height();

    TFont* F=Canvas->Font; F->Color=clWhite; F->Height=12; F->Name="Ariel";
    int fh=Canvas->TextHeight("0");
    int DrawCount=res?(Count+1):Count;

    double XX=Rect.right-Bdw*2, YY=Rect.bottom-Bdw-Bdwc-fh;
    Canvas->Brush->Color=clBlack; Canvas->Brush->Style=bsSolid; Canvas->FillRect(Rect);
    Canvas->Brush->Color=clAqua; Canvas->Pen->Color=clAqua; Canvas->Brush->Style=bsSolid;
    int empspace=XX/(DrawCount+1)/4;
    double r=0; for (int i=0; i<Count; i++) r+=data[i]*data[i];
    double sqrtr=sqrt(r);
    double res_amp=(r>1)?0:sqrt(1-r);
    for (int i=0; i<DrawCount; i++)
    {
      double ldata;
      if (res)
      {
        if (i==0) ldata=data[i];
        else if (i<Count) ldata=data[i];
        else ldata=res_amp;
      }
      else ldata=data[i]/sqrtr;
      int X=floor(Bdw+XX*(i+1)/(DrawCount+1)+0.5);
      int Y=floor(Bdw+YY*(1-ldata)+0.5);
      if (empspace>=1)
      {
        Canvas->Brush->Style=bsSolid; Canvas->Brush->Color=TColor(0xFFFF00);
        Canvas->Rectangle(X-empspace, Y, X+empspace, Bdw+YY);
        if (KX1) KX1[i]=X-empspace, KX2[i]=X+empspace-1;
      }
      else
      {
        Canvas->MoveTo(X, Y); Canvas->LineTo(X, Bdw+YY);
        if (KX1) KX1[i]=X-1, KX2[i]=X+1;
      }
      AnsiString S=i; if (i>=Count) S="R"; int fw=Canvas->TextWidth(S);
      Canvas->Brush->Style=bsClear;
      Canvas->TextOut(X-fw/2, Bdw+YY, S);
      S.sprintf("%.3g", 100*ldata*ldata); if (S[1]=='0') S=S.SubString(2, S.Length()-1); fw=Canvas->TextWidth(S);
      if (Y-Bdw>fh) Canvas->TextOut(X-fw/2, Y-fh, S);
      else Canvas->TextOut(X+empspace+1, Y, S);
    }
    Canvas->Brush->Color=clBlack; Canvas->Brush->Style=bsClear; Canvas->Pen->Color=clWhite;
    Canvas->Rectangle(Bdw, Bdw, Bdw+XX, Bdw+YY);
  }

  TColor RotateColors[10]={clWhite, clAqua, clLime, clYellow, clRed, clTeal, clGray, clGreen, clFuchsia, clBlue};
  //draw A-F distribution
	void DrawAF(Graphics::TBitmap* Bitmap, TRect Rect, double xst, double xen, double dbst, double dben, int M, int Fr, atom** Partials, double* A0C, bool MA, bool bars, int FrStart=0, int TextHeight=12)
  {
    Bitmap->Width=Rect.Width();
    Bitmap->Height=Rect.Height();
    TCanvas* Canvas=Bitmap->Canvas;
    double idbrange=1.0/(dbst-dben);
    TFont* F=Canvas->Font; F->Color=clWhite; F->Height=TextHeight; F->Name="Ariel";
    int fh=Canvas->TextHeight("0");

    double XX=Rect.right-Bdw*2, YY=Rect.bottom-Bdw-Bdwc-fh;
    Canvas->Brush->Color=clBlack; Canvas->Brush->Style=bsSolid; Canvas->FillRect(Rect);

    Canvas->Pen->Color=clSilver; Canvas->MoveTo(Bdw, Bdw+YY*0.5); Canvas->LineTo(Bdw+XX, Bdw+YY*0.5);

    double *x=new double[(Fr-FrStart)*4], *y=&x[Fr-FrStart], *newa=&x[(Fr-FrStart)*2], *neww=&x[(Fr-FrStart)*3];

    for (int m=0; m<M; m++)
    {
      Canvas->Pen->Color=RotateColors[m%10]; Canvas->Brush->Color=RotateColors[m%10];

      int Sc=0;
      bool visible=false;
      atom* Partialsm=Partials[m];
      for (int fr=FrStart; fr<Fr; fr++)
      {
        if (Partialsm[fr].f>0 && Partialsm[fr].a>0)
        {
          x[Sc]=Partials[m][fr].f;
          y[Sc]=20*Log10(Partialsm[fr].a/A0C[fr]);
          if (x[Sc]<xen) visible=true;
          Sc++;
        }
      }
      if (!visible) break;

      if (MA)
      {
        double fw;
        for (int c=0; c<Sc; c++) newa[c]=y[c], neww[c]=1;
        for (int c=0; c<Sc; c++)
        {
          fw=0.002; if (fw>x[c]*0.05) fw=x[c]*0.05;

          for (int c1=0; c1<c; c1++)
          {
            double df=fabs(x[c]-x[c1]);
            if (df<fw)
            {
              double w=0.5+0.5*cos(M_PI*df/fw);
              newa[c]+=y[c1]*w; neww[c]+=w;
              newa[c1]+=y[c]*w; neww[c1]+=w;
            }
          }
        }
        for (int c=0; c<Sc; c++) y[c]=newa[c]/neww[c];
      }

      for (int c=0; c<Sc; c++)
      {
        if (x[c]<xst || x[c]>xen) continue;
        double ldata=(y[c]-dben)*idbrange;
        int X=floor(Bdw+XX*(x[c]-xst)/(xen-xst)+0.5);
        int Y=floor(Bdw+YY*(1-ldata)+0.5);
        if (bars) {Canvas->MoveTo(X, Y); Canvas->LineTo(X, Bdw+YY);}
        else {Canvas->MoveTo(X-1, Y); Canvas->LineTo(X+2, Y); Canvas->MoveTo(X, Y-1); Canvas->LineTo(X, Y+2);}
      }
    }

    AnsiString S="0hz"; Canvas->Brush->Style=bsClear; Canvas->TextOut(Bdw, Bdw+YY, S);
    S="5.5khz"; int fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+(XX-fw)/2, Bdw+YY, S);
    S="11khz"; fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+YY, S);
    S.sprintf("%gdB", dbst); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw, S);
    S.sprintf("%gdB", (dbst+dben)/2); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+(YY-fh)/2, S);
    S.sprintf("%gdB", dben); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+YY-fh, S);

    Canvas->Brush->Color=clBlack; Canvas->Brush->Style=bsClear; Canvas->Pen->Color=clWhite;
    Canvas->Rectangle(Bdw, Bdw, Bdw+XX, Bdw+YY);

    delete[] x;

  }   //*/
  //for source-filter results
	void DrawSFr(Graphics::TBitmap* Bitmap, TRect Rect, double xst, double xen, double dbst, double dben, int M, int Fr, int afres, atom** Partials, double* LogAF, double* LogAS, int TextHeight=12)
  {
    Bitmap->Width=Rect.Width();
    Bitmap->Height=Rect.Height();
    TCanvas* Canvas=Bitmap->Canvas;
		TFont* F=Canvas->Font; F->Color=clWhite; F->Height=TextHeight; F->Name="Ariel";
    int fh=Canvas->TextHeight("0");

    double XX=Rect.right-Bdw*2, YY=Rect.bottom-Bdw-Bdwc-fh;
    Canvas->Brush->Color=clBlack; Canvas->Brush->Style=bsSolid; Canvas->FillRect(Rect);

    Canvas->Pen->Color=clSilver; Canvas->MoveTo(Bdw, Bdw+YY*0.5); Canvas->LineTo(Bdw+XX, Bdw+YY*0.5);

    double xrange=xen-xst, XXixrange=XX/xrange, xstXXixrange=xst*XXixrange,
           xrangeiXX=xrange/XX, xstafres=xst*afres, xrangeiXXafres=xrangeiXX*afres,
           YYidbrange=YY/(dbst-dben);

    int *XRec=new int[XX];
    for (int m=0; m<M; m++)
    {
      bool visible=false;
      atom* Partialsm=Partials[m];
      Canvas->Brush->Color=RotateColors[m%10]; Canvas->Pen->Color=RotateColors[m%10];
      memset(XRec, 0, sizeof(int)*XX);

      for (int fr=0; fr<Fr; fr++)
      {
        if (Partialsm[fr].f>0 && Partialsm[fr].a>0)
        {
          double xx=Partialsm[fr].f;
          if (xx<xen) visible=true;
          if (xx<xst || xx>xen) continue;
          int X=floor(XXixrange*xx-xstXXixrange+0.5);
          if (X>=0 && X<XX && !XRec[X])
          {
            XRec[X]=true;
            double yy=20*log10e*(LogAF[int(xstafres+X*xrangeiXXafres)]+LogAS[m]);
            int Y=Bdw+floor((YY-YYidbrange*(yy-dben))+0.5);
            X+=Bdw;
            Canvas->MoveTo(X-1, Y); Canvas->LineTo(X+2, Y);
            Canvas->MoveTo(X, Y-1); Canvas->LineTo(X, Y+2);
          }
        }
      }
      if (!visible) break;
    }
    delete[] XRec;

    AnsiString S="0hz"; Canvas->Brush->Style=bsClear; Canvas->TextOut(Bdw, Bdw+YY, S);
    S="5.5khz"; int fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+(XX-fw)/2, Bdw+YY, S);
    S="11khz"; fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+YY, S);
    S.sprintf("%gdB", dbst); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw, S);
    S.sprintf("%gdB", (dbst+dben)/2); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+(YY-fh)/2, S);
    S.sprintf("%gdB", dben); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+YY-fh, S);

    Canvas->Brush->Color=clBlack; Canvas->Brush->Style=bsClear; Canvas->Pen->Color=clWhite;
    Canvas->Rectangle(Bdw, Bdw, Bdw+XX, Bdw+YY);
  }   
  //*for source-filter model
	int DrawSF(Graphics::TBitmap* Bitmap, TRect Rect, double xst, double xen, double dbst, double dben, double f0, int SCount, double* LogAS, double fc0, int FCount, double* LogAF, int* SX=0, TColor* Colors=0, int TextHeight=12)
  {
    Bitmap->Width=Rect.Width();
    Bitmap->Height=Rect.Height();
    TCanvas* Canvas=Bitmap->Canvas;
    double *Sx=new double[SCount];
    for (int i=0; i<SCount; i++) Sx[i]=20*log10e*LogAS[i];

    int result=0;
		TFont* F=Canvas->Font; F->Color=clWhite; F->Height=TextHeight; F->Name="Ariel";
    int fh=Canvas->TextHeight("0");

    double XX=Rect.right-Bdw*2, YY=Rect.bottom-Bdw-Bdwc-fh;
    Canvas->Brush->Color=clBlack; Canvas->Brush->Style=bsSolid; Canvas->FillRect(Rect);
    Canvas->Pen->Color=clSilver; Canvas->MoveTo(Bdw, Bdw+YY*0.5); Canvas->LineTo(Bdw+XX, Bdw+YY*0.5);

    double idbrange=1.0/(dbst-dben), xrange=xen-xst, XXixrange=XX/xrange, xstXXixrange=xst*XXixrange,
      YY20log10eidbrange=YY*20*log10e*idbrange, BdwpYYpYYdbenidbrange=Bdw+YY+YY*dben*idbrange,
      Bdw_xstXXixrange=Bdw-xstXXixrange, YYidbrange=YY*idbrange;

    double st=xst/fc0; int ist=ceil(st); if (ist<0) ist=0;
    Canvas->Pen->Color=clWhite;
    Canvas->MoveTo(Bdw+XX*(fc0*ist-xst)/xrange+0.5, BdwpYYpYYdbenidbrange-YY20log10eidbrange*LogAF[ist]+0.5);
    //draw filter contour
    int LineCount=xrange/fc0;
    if (LineCount<XX)
    {
      for (int i=ist+1; i<FCount; i++)
      {
        double xi=fc0*i;
        if (xi>xen) break;
        int X=floor(Bdw_xstXXixrange+xi*XXixrange+0.5);
        int Y=floor(BdwpYYpYYdbenidbrange-YY20log10eidbrange*LogAF[i]+0.5);
        Canvas->LineTo(X, Y);
      }
    }
    else
    {
      double di=xrange/(XX*fc0);
      for (int x=0; x<XX; x++)
      {
        int i=st+x*di;
        int Y=floor(BdwpYYpYYdbenidbrange-YY20log10eidbrange*LogAF[i]+0.5);
        Canvas->LineTo(Bdw+x, Y);
      }
    }
    //draw source lines
    for (int i=0; i<SCount; i++)
    {
      double xi=f0*(i+1);
      if (xi<xst || xi>xen) continue;
      int X=floor(Bdw_xstXXixrange+XXixrange*xi+0.5);
      int Y=floor(BdwpYYpYYdbenidbrange-YYidbrange*Sx[i]+0.5);
      if (Colors) Canvas->Pen->Color=Colors[i];
      else Canvas->Pen->Color=RotateColors[i%10];

      Canvas->MoveTo(X, Y); Canvas->LineTo(X, Bdw+YY);

      if (SX) SX[i]=X, result++;
    }
    AnsiString S="0hz"; Canvas->Brush->Style=bsClear; Canvas->TextOut(Bdw, Bdw+YY, S);
    S="5.5khz"; int fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+(XX-fw)/2, Bdw+YY, S);
    S="11khz"; fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+YY, S);
    S.sprintf("%gdB", dbst); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw, S);
    S.sprintf("%gdB", (dbst+dben)/2); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+(YY-fh)/2, S);
    S.sprintf("%gdB", dben); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+YY-fh, S);

    Canvas->Brush->Color=clBlack; Canvas->Brush->Style=bsClear; Canvas->Pen->Color=clWhite;
    Canvas->Rectangle(Bdw, Bdw, Bdw+XX, Bdw+YY);
    delete[] Sx;
    return result;
  }   //*/

	void DrawF0(Graphics::TBitmap* Bitmap, TCanvas* Canvas, TRect Rect, double* lp, int npfr, double* F0, double f_c, double f_ex, double sps, double F0Overall, int Fr, atom** Partials, double L, bool Captions=true, TColor F0Color=clLime, bool peakmark=false, TColor peakmarkcolor=clYellow, double* peaky=0, TColor peakycolor=clYellow, double* frs=0, double* fs=0)
  {
    Canvas->Brush->Color=cl3DDkShadow; Canvas->FillRect(Rect);
    int Width=Rect.Width(), Height=Rect.Height();
    Bitmap->Width=Width; Bitmap->Height=Height;
    TPen* P=Canvas->Pen;
    TFont* F=Canvas->Font;

    int X, Y, Y1=0, Y2=Height, Y_;
    AnsiString S, as;

    P->Color=clBlack; P->Style=psDot; F->Color=clBlack; F->Height=12; F->Name="Ariel";
    double f0max=-0.5, f0min=0.5; for (int fr=lp[0]; fr<lp[npfr-1]; fr++){if (f0max<F0[fr]) f0max=F0[fr]; if (f0min>F0[fr]) f0min=F0[fr];}
    Y1=Height*(0.5-(f0max-f_c)/f_ex); Canvas->MoveTo(0, Y1); Canvas->LineTo(Width, Y1);
    if (Captions){as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(f0max*sps)/C4)); S.sprintf("%.4ghz (%s)", f0max*sps, as.c_str()); Canvas->TextOut(1, Y1-Canvas->TextHeight(S)-1, S);}
    Y2=Height*(0.5-(f0min-f_c)/f_ex); Canvas->MoveTo(0, Y2); Canvas->LineTo(Width, Y2);
    if (Captions){as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(f0min*sps)/C4)); S.sprintf("%.4ghz (%s)", f0min*sps, as.c_str()); Canvas->TextOut(1, Y2+1, S);}

    if (peakmark)
    {
      int Y0=0.5*(Y1+Y2), Y0ex=0.5*(Y1-Y2);
      if (peaky) {P->Color=peakmarkcolor; P->Style=psDot; Canvas->MoveTo(0, Y0); Canvas->LineTo(Width, Y0);}

      for (int pfr=0; pfr<npfr; pfr++)
      {
        int fr=floor(lp[pfr]);
        X=Width*(Partials[1][fr].t*(fr+1-lp[pfr])+Partials[1][fr+1].t*(lp[pfr]-fr))/L;
        P->Color=peakmarkcolor; P->Style=psDot; Canvas->MoveTo(X, Y1); Canvas->LineTo(X, Y2);
        if (peaky)
        {
          Y=Y0ex*peaky[pfr];
          P->Color=peakycolor; P->Style=psSolid; Canvas->MoveTo(X, Y0); Canvas->LineTo(X, Y0+Y);
        }
      }
    }

    if (Captions) {Y_=Height*(0.5-(F0Overall-f_c)/f_ex); P->Color=clWhite; Canvas->MoveTo(0, Y_); Canvas->LineTo(Width, Y_);}

    P->Style=psSolid; P->Color=F0Color;
    for (int fr=0; fr<Fr; fr++)
    {
      if (fr>0) Canvas->MoveTo(X, Y);
      X=Width*Partials[1][fr].t/L, Y=Height*(0.5-(F0[fr]-f_c)/f_ex);
      if (fr>0) Canvas->LineTo(X, Y);
    }

    if (frs)
    {
      P->Color=clRed;
      for (int pfr=0; pfr<npfr-1; pfr++)
      {
        int ifrs=frs[pfr]; double rfrs=frs[pfr]-ifrs;
        X=(Width*Partials[1][ifrs].t*(1-rfrs)+Width*Partials[1][ifrs+1].t*rfrs)/L, Y=Height*(0.5-(fs[pfr]-f_c)/f_ex);
        Canvas->MoveTo(X-4, Y); Canvas->LineTo(X+5, Y);
        Canvas->MoveTo(X, Y-4); Canvas->LineTo(X, Y+5);
      }
    }

    if (Captions)
    {
      as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(F0Overall*sps)/C4)); S.sprintf("%.4ghz (%s)", F0Overall*sps, as.c_str());
      F->Color=clWhite; F->Style=F->Style<<fsBold; Canvas->TextOut(Width-Canvas->TextWidth(S), Y_+1, S); F->Style=F->Style>>fsBold;
      F->Color=clSilver; S="F0"; Canvas->TextOut(1, Height-Canvas->TextHeight(S), S);
      double f=(f_c+f_ex/2)*sps; as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(f)/C4)); S.sprintf("%.4ghz (%s)", f, as.c_str()); Canvas->TextOut(Width-Canvas->TextWidth(S), 0, S);
      f=(f_c-f_ex/2)*sps; as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(f)/C4)); S.sprintf("%.4ghz (%s)", f, as.c_str()); Canvas->TextOut(Width-Canvas->TextWidth(S), Height-Canvas->TextHeight(S), S);
    }
  }

	void DrawF0C(Graphics::TBitmap* Bitmap, TCanvas* Canvas, TRect Rect, double* F0C, double& F0Cmax, double& F0Cmin, double f_c, double f_ex, double sps, double F0Overall, int Fr, atom** Partials, double L, bool clearbackground=true, int frcount=0, double* frs=0, double* fs=0, int* CX1=0, int* CX2=0, int* CY1=0, int* CY2=0)
  {
    //Draw the F0 carrier
    Canvas->Brush->Color=cl3DDkShadow; if (clearbackground) Canvas->FillRect(Rect);
    int Width=Rect.Width(), Height=Rect.Height();
    Bitmap->Width=Width; Bitmap->Height=Height;
    TPen* P=Canvas->Pen;
    TFont* F=Canvas->Font;

    P->Color=clBlack;
    F0Cmax=f_c-f_ex, F0Cmin=f_c+f_ex;
    for (int fr=0; fr<Fr; fr++){if (F0Cmax<F0C[fr]) F0Cmax=F0C[fr]; if (F0Cmin>F0C[fr]) F0Cmin=F0C[fr];}
    P->Style=psDot;
    int X, Y=Height*(0.5-(F0Cmax-f_c)/f_ex);
    Canvas->MoveTo(0, Y); Canvas->LineTo(Width, Y);
    AnsiString as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(F0Cmax*sps)/C4)); AnsiString S; S.sprintf("%.4ghz (%s)", F0Cmax*sps, as.c_str());
    F->Color=clBlack; F->Height=12; F->Name="Ariel"; Canvas->TextOut(1, Y-Canvas->TextHeight(S)-1, S);
    Y=Height*(0.5-(F0Cmin-f_c)/f_ex);
    Canvas->MoveTo(0, Y); Canvas->LineTo(Width, Y);
    as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(F0Cmin*sps)/C4)); S.sprintf("%.4ghz (%s)", F0Cmin*sps, as.c_str());
    Canvas->TextOut(1, Y+1, S);
    P->Color=clWhite;
    as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(F0Overall*sps)/C4)); S.sprintf("%.4ghz (%s)", F0Overall*sps, as.c_str());
    int Y_=Height*(0.5-(F0Overall-f_c)/f_ex);
    Canvas->MoveTo(0, Y_); Canvas->LineTo(Width, Y_);
    F->Color=clWhite; F->Style=F->Style<<fsBold;
    if (Y_+1+Canvas->TextHeight(S)<Y) Canvas->TextOut(Width-Canvas->TextWidth(S), Y_+1, S);
    else Canvas->TextOut(Width-Canvas->TextWidth(S), Y+1, S);
    F->Style=F->Style>>fsBold;

    P->Style=psSolid; P->Color=clLime;
    for (int fr=0; fr<Fr; fr++)
    {
      if (fr>0) Canvas->MoveTo(X, Y);
      X=Width*Partials[1][fr].t/L, Y=Height*(0.5-(F0C[fr]-f_c)/f_ex);
      if (fr>0) Canvas->LineTo(X, Y);
    }

    if (frcount)
    {
      P->Color=clRed;
      for (int p=0; p<frcount; p++)
      {
        int ifrs=frs[p]; double rfrs=frs[p]-ifrs;
        X=(Width*Partials[1][ifrs].t*(1-rfrs)+Width*Partials[1][ifrs+1].t*rfrs)/L, Y=Height*(0.5-(fs[p]-f_c)/f_ex);
        Canvas->MoveTo(X-4, Y); Canvas->LineTo(X+5, Y);
        Canvas->MoveTo(X, Y-4); Canvas->LineTo(X, Y+5);
        if (CX1) CX1[p]=X-4;
        if (CX2) CX2[p]=X+4;
        if (CY1) CY1[p]=Y-4;
        if (CY2) CY2[p]=Y+4;
      }
    }

		double f=(f_c+f_ex/2)*sps;
		F->Color=clSilver;
		as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(f)/C4)); S.sprintf("%.4ghz (%s)", f, as.c_str());
		Canvas->TextOut(Width-Canvas->TextWidth(S), 0, S);
		f=(f_c-f_ex/2)*sps; as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(f)/C4)); S.sprintf("%.4ghz (%s)", f, as.c_str());
		Canvas->TextOut(Width-Canvas->TextWidth(S), Height-Canvas->TextHeight(S), S);
		F->Color=clSilver; S="F0 carrier";
		Canvas->TextOut(1, Height-Canvas->TextHeight(S), S);
	}

	void DrawF0D(Graphics::TBitmap* Bitmap, TCanvas* Canvas, TRect Rect, double* lp, int npfr, double* F0D, double& F0Dmax, double& F0Dmin, double f_c, double f_ex, double sps, double F0Overall, int Fr, atom** Partials, double L)
	{
		int Width=Rect.Width(), Height=Rect.Height();
		Bitmap->Width=Width; Bitmap->Height=Height;

		Canvas->Brush->Style=bsSolid; Canvas->Brush->Color=cl3DDkShadow; Canvas->FillRect(Rect);

		//this part draws the modulator itself
		F0Dmax=-f_ex, F0Dmin=f_ex;
		for (int fr=lp[0]; fr<lp[npfr-1]; fr++){if (F0Dmax<F0D[fr]) F0Dmax=F0D[fr]; if (F0Dmin>F0D[fr]) F0Dmin=F0D[fr];}
		TPen* P=Canvas->Pen; P->Color=clBlack; P->Style=psDot;
		int X, Y=Height*(0.5-F0Dmax/f_ex); Canvas->MoveTo(0, Y); Canvas->LineTo(Width, Y);
		AnsiString S; S.sprintf("%.4ghz (%.3g%%)", F0Dmax*sps, F0Dmax/F0Overall*100);
		TFont* F=Canvas->Font; F->Color=clBlack; F->Height=12; F->Name="Ariel";
		Canvas->TextOut(1, Y-Canvas->TextHeight(S)-1, S);
		Y=Height*(0.5-F0Dmin/f_ex);
		Canvas->MoveTo(0, Y); Canvas->LineTo(Width, Y);
		S.sprintf("%.4ghz (%.3g%%)", F0Dmin*sps, F0Dmin/F0Overall*100); Canvas->TextOut(1, Y+1, S);

		P->Style=psSolid; P->Color=clSilver; Canvas->MoveTo(0, Height/2); Canvas->LineTo(Width, Height/2);
		Canvas->Pen->Color=clLime;
		for (int fr=0; fr<Fr; fr++)
		{
			if (fr>0) Canvas->MoveTo(X, Y);
			X=Width*Partials[1][fr].t/L, Y=Height*(0.5-F0D[fr]/f_ex);
			if (fr>0) Canvas->LineTo(X, Y);
		}
		P->Color=clBlue;
		for (int fr=0; fr<Fr; fr++)
		{
			X=Width*Partials[1][fr].t/L, Y=Height*(0.5-F0D[fr]/f_ex);
			Canvas->MoveTo(X-1, Y); Canvas->LineTo(X+2, Y); Canvas->MoveTo(X, Y-1); Canvas->LineTo(X, Y+2);
		}
		F->Color=clSilver; F->Height=12; F->Name="Ariel";
		S.sprintf("%.4ghz (%.3g%%)", f_ex*sps/2, f_ex/2/F0Overall*100); Canvas->TextOut(Width-Canvas->TextWidth(S), 0, S);
		S.sprintf("%.4ghz (%.3g%%)", -f_ex*sps/2, -f_ex/2/F0Overall*100); Canvas->TextOut(Width-Canvas->TextWidth(S), Height-Canvas->TextHeight(S), S);

		F->Color=clSilver; S="F0 modulator"; Canvas->TextOut(1, Height-Canvas->TextHeight(S), S);
	}

	//y=ax+b
	void linefit(int n, double* x, double* y, double* indi, double& a, double& b)
  {
    double sx=0, sy=0, sxy=0, sxx=0, m=0;
    for (int i=0; i<n; i++) if (indi[i]>0) sx+=x[i], sy+=y[i], sxx+=x[i]*x[i], sxy+=x[i]*y[i], m++;
    if (m<=0) {a=-1; b=-1; return;}
    double id=1.0/(m*sxx-sx*sx);
    a=(m*sxy-sx*sy)*id;
    b=(sxx*sy-sx*sxy)*id;
  }

void TVibratoDemoForm::UpdateDisplay(bool f0, bool f0c, bool f0d, bool sf)
{
  int Fr=HS->Fr;
  atom** Partials=HS->Partials;
  //find the highest point of the 2nd partial
  double f2min=1, f2max=0, fst=0, fen=0.25;
  for (int fr=0; fr<Fr; fr++)
  {
		if (f2max<Partials[1][fr].f) f2max=Partials[1][fr].f;
		if (f2min>Partials[1][fr].f) f2min=Partials[1][fr].f;
	}
	f_c=(f2min+f2max)/4; f_ex=f2max-f2min;

	if (f0) DrawF0(Image0->Picture->Bitmap, Image0->Canvas, Image0->ClientRect, V.lp, V.P, V.F0, f_c, f_ex, WaveView1->SamplesPerSec, V.F0Overall, Fr, Partials, WaveView2->Length, true, clLime, PeakMarksCheck->Checked, clYellow, 0, TColor(0), 0/*cyclefrs*/, cyclefs);
  if (f0c)
  {
    memset(CX1, 0xFF, sizeof(int)*128); memset(CX2, 0xFF, sizeof(int)*128); memset(CY1, 0xFF, sizeof(int)*128); memset(CY1, 0xFF, sizeof(int)*128);
		DrawF0C(Image1->Picture->Bitmap, Image1->Canvas, Image1->ClientRect, V.F0C, V.F0Cmax, V.F0Cmin, f_c, f_ex, WaveView1->SamplesPerSec, V.F0Overall, Fr, Partials, WaveView2->Length, true, PeakMarksCheck->Checked?cyclefrcount:0, cyclefrs, cyclefs, CX1, CX2, CY1, CY2);
  }
  if (f0d)
  {
    //Draw the F0 modulator
    DrawModulator();
  //
    double sps=WaveView1->SamplesPerSec;
    DurationEdit->Text=AnsiString().sprintf("%.4gs", WaveView2->Length/sps);
    RegEdit->Text=AnsiString().sprintf("%.4g", V.regularity);
    RateEdit->Text=AnsiString().sprintf("%.4ghz", V.rate);
    if (ResidueCheck->Checked) {FXX[0]=sqrt(1-V.FRes[0]); for (int i=1; i<V.K; i++) FXX[i]=sqrt(V.FRes[i-1]-V.FRes[i]);}
    else {FXX[0]=fabs(V.FXR[0]); for (int i=1; i<V.K; i++) FXX[i]=2*sqrt(V.FXR[2*i]*V.FXR[2*i]+V.FXR[2*i-1]*V.FXR[2*i-1]);}
    DrawBarMap(MImage1->Picture->Bitmap, MImage1->ClientRect, V.K, FXX, ResidueCheck->Checked, KX1, KX2);
  }

  if (sf)
  {
    if (SFCheck->Checked)
			DrawSFr(AImage1->Picture->Bitmap, AImage1->ClientRect, fst, fen, updb, downdb, V.M, Fr, V.afres, Partials, V.LogAF, V.LogAS);
		else
      DrawAF(AImage1->Picture->Bitmap, AImage1->ClientRect, fst, fen, updb, downdb, V.M, Fr, Partials, V.A0C, MACheck->Checked, false);
    SXc=DrawSF(AImage3->Picture->Bitmap, AImage3->ClientRect, fst, fen, updb, downdb, V.F0Overall, V.M, V.LogAS, 1.0/V.afres, V.afres/2, V.LogAF, SX);
  }

  ForceUpdate=false;
}

void __fastcall TVibratoDemoForm::WaveView1OpMode(TObject* Sender, TShiftState Shift, int& OpMode)
{
  if (Shift.Contains(ssShift)) OpMode=0; //drag mode
  else OpMode=1; //select mode
}

int __fastcall TVibratoDemoForm::WaveView1CustomInfo(TObject* Sender)
{
  TWaveView* WV=(TWaveView*)Sender;
  TStringList* List=new TStringList;
  double fs=WV->StartPos*1.0/WV->SamplesPerSec, fe=WV->EndPos*1.0/WV->SamplesPerSec;
  List->Add(AnsiString().sprintf(" Time (%.4gs): from %.4gs to %.4gs. ", fe-fs, fs, fe));
  List->Add(AnsiString().sprintf(" Frequency: from %.1fhz to %.1fhz. ", WV->StartDigiFreq*WV->SamplesPerSec, WV->EndDigiFreq*WV->SamplesPerSec));
  return int(List);
}

void __fastcall TVibratoDemoForm::Image1MouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{
  if (!HS) return;
  TImage* Image=(TImage*)Sender;
  Image->Parent->SetFocus();

  double t, sps=WaveView1->SamplesPerSec, f; AnsiString S, as; TFont* F=Image->Canvas->Font;

  if (Sender==Image1)
  {
    if (Shift.Contains(ssLeft))
    {
      if (CurrentC<0 || CurrentC>=cyclefrcount) return;
      double df=-(Y-StartDrag)*f_ex/Image->Height;
      cyclefs[CurrentC]+=df;
      StartDrag=Y;
      Smooth_Interpolate(V.F0C, V.L, cyclefrcount+1, cyclefs, cyclefrs);
      SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
      UpdateDisplay(true, true, true, false);

        t=HS->Partials[0][0].t+V.h*cyclefrs[CurrentC];
        f=cyclefs[CurrentC]*sps;
        as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(f)/C4));
        S.sprintf("%.3gs, %.4ghz (%s)     ", t/sps, f, as.c_str());
        F->Color=clRed;
        Image->Canvas->TextOut(1, Image->Canvas->TextHeight("0"), S);

    }
    else
    {
      CurrentC=-1;
      for (int i=0; i<cyclefrcount; i++)
        if (X>=CX1[i] && X<=CX2[i] && Y>=CY1[i] && Y<=CY2[i]) {CurrentC=i; break;}
    }
      if (CurrentC>=0 && CurrentC<cyclefrcount)
      {
        t=HS->Partials[0][0].t+V.h*cyclefrs[CurrentC];
        f=cyclefs[CurrentC]*sps;
        as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(f)/C4));
        S.sprintf("%.3gs, %.4ghz (%s)     ", t/sps, f, as.c_str());
        F->Color=clRed;
        Image->Canvas->TextOut(1, Image->Canvas->TextHeight("0"), S);
      }
      else
      {
        S.sprintf("                                        ", t/sps, f, as.c_str());
        Image->Canvas->TextOut(1, Image->Canvas->TextHeight("0"), S);
      }
  }

  t=WaveView2->Length*1.0*(X+0.5)/Image->Width;
  f=(f_c-(Y-0.5*Image->Height)*f_ex/Image->Height)*sps;
  as=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(f)/C4));
  S.sprintf("%.3gs, %.4ghz (%s)       ", t/sps, f, as.c_str());
  F->Color=clAqua; F->Height=12; F->Name="Ariel";
  Image->Canvas->TextOut(1, 0, S);
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::Image2MouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{
  if (!HS) return;
  if (Shift.Contains(ssLeft)) return;
  atom** Partials=HS->Partials;
  Image2->Parent->SetFocus();
  double t=WaveView2->Length*1.0*(X+0.5)/Image2->Width, sps=WaveView1->SamplesPerSec;
  double f=-(Y-0.5*Image2->Height)*f_ex/Image2->Height;

  if (true || Shift.Contains(ssShift))
  {
    int modcycle=0;
    if (t<Partials[0][0].t+V.lp[0]*V.h || t>Partials[0][0].t+V.lp[V.P-1]*V.h) modcycle=-1;
    else while (t>Partials[0][0].t+V.lp[modcycle]*V.h) modcycle++;
    if (CurrentModCycle!=modcycle)
    {
      CurrentModCycle=modcycle;

      if (CurrentModCycle==-1)
      {
        if (PageControl1->ActivePage==ModCycleSheet)
        {
          PageControl1->ActivePage=ModOverSheet;
          ResidueCheck->Parent=ModOverSheet;
        }
        else if (PageControl1->ActivePage==AmpCycleSheet)
        {
          PageControl1->ActivePage=AmpOverSheet;
          SFCheck->Parent=AmpOverSheet;
          MACheck->Parent=AmpOverSheet;
        }
      }
      else
      {
        if (PageControl1->ActivePage==ModOverSheet)
        {
          PageControl1->ActivePage=ModCycleSheet;
          ResidueCheck->Parent=ModCycleSheet;
        }
        else if (PageControl1->ActivePage==AmpOverSheet)
        {
          PageControl1->ActivePage=AmpCycleSheet;
          SFCheck->Parent=AmpCycleSheet;
          MACheck->Parent=AmpCycleSheet;
        }
        ModCycleSheet->Caption="Modulator Cycle "+AnsiString(CurrentModCycle);
        AmpCycleSheet->Caption="Amplitudes Cycle "+AnsiString(CurrentModCycle);
      }
      DrawModulator();
    }
  }

  AnsiString S;
  S.sprintf("%.3gs, %.4ghz (%.3g%%)       ", t/sps, f*sps, f/V.F0Overall*100);
  TFont* F=Image2->Canvas->Font; F->Color=clAqua; F->Height=12; F->Name="Ariel";
  Image2->Canvas->TextOut(1, 0, S);
}
//---------------------------------------------------------------------------
void TVibratoDemoForm::DrawModulator()
{
  double fst=0, fen=0.25;

  Image2->Picture->Bitmap->Width=Image2->Width;
  Image2->Picture->Bitmap->Height=Image2->Height;

  int X, Y, L=WaveView2->Length, Fr=HS->Fr;
  double sps=WaveView2->SamplesPerSec;
  atom** Partials=HS->Partials;
  TCanvas* I2Canvas=Image2->Canvas; I2Canvas->Brush->Style=bsSolid; I2Canvas->Brush->Color=cl3DDkShadow; I2Canvas->FillRect(Image2->ClientRect);
  if (CurrentModCycle>=1 && CurrentModCycle<V.P)
  {
		CurrentModCycleStart=Partials[0][0].t+V.lp[CurrentModCycle-1]*WaveView1->SpecOffst;
		CurrentModCycleEnd=Partials[0][0].t+V.lp[CurrentModCycle]*WaveView1->SpecOffst;

		int X1=Image2->Width*CurrentModCycleStart/L;
		int X2=Image2->Width*CurrentModCycleEnd/L;
		I2Canvas->Brush->Color=clDkGray; I2Canvas->FillRect(TRect(X1, 0, X2, Image2->Height));
		I2Canvas->Brush->Color=cl3DDkShadow;

    //this part update the tab sheet
    int frst=V.peakfr[CurrentModCycle-1], fren=V.peakfr[CurrentModCycle];
    if (PageControl1->ActivePage==ModCycleSheet)
    {
      CycleDurationEdit->Text=AnsiString().sprintf("%.4gs (%.4gs~%.4gs)", (CurrentModCycleEnd-CurrentModCycleStart)/sps, CurrentModCycleStart/sps, CurrentModCycleEnd/sps);
      CycleRateEdit->Text=AnsiString().sprintf("%.4ghz", sps/(CurrentModCycleEnd-CurrentModCycleStart));

      double af0c=(V.F0C[frst]+V.F0C[fren])*0.5, f0dmax=V.F0D[frst], f0dmin=V.F0D[frst];
      double np=1, nn=0;
      double pdp=(V.F0D[frst]*V.F0D[frst]+V.F0D[fren]*V.F0D[fren])*0.5, pdn=0;
      for (int fr=frst+1; fr<fren; fr++)
      {
        af0c+=V.F0C[fr];
        if (V.F0D[fr]>f0dmax) f0dmax=V.F0D[fr];
        if (V.F0D[fr]<f0dmin) f0dmin=V.F0D[fr];
        if (V.F0D[fr]>0) pdp+=V.F0D[fr]*V.F0D[fr], np+=1;
        else if (V.F0D[fr]<0) pdn+=V.F0D[fr]*V.F0D[fr], nn+=1;
      }
      af0c/=(fren-frst);
      pdp=(np>0)?sqrt(pdp/np):0, pdn=(nn>0)?sqrt(pdn/nn):0;
      AnsiString S=SemitoneToPitch(12*Log2(WV2_LOG_FREQ(af0c*sps)/C4));
      CyclePitchEdit->Text=AnsiString().sprintf("%.4ghz (%s)", af0c*sps, S.c_str());
      CycleExtentEdit->Text=AnsiString().sprintf("%.3g%%~%.3g%%", f0dmin/af0c*100, f0dmax/af0c*100);
      CycleAvgPDEdit->Text=AnsiString().sprintf("-%.3g%%~%.3g%%", pdn/af0c*100, pdp/af0c*100);


      if (ResidueCheck->Checked) {FXX[0]=sqrt(1-V.fres[CurrentModCycle][0]); for (int i=1; i<V.Kp[CurrentModCycle]; i++) FXX[i]=sqrt(V.fres[CurrentModCycle][i-1]-V.fres[CurrentModCycle][i]); for (int i=V.Kp[CurrentModCycle]; i<V.K; i++) FXX[i]=0;}
      else {FXX[0]=fabs(V.fxr[CurrentModCycle][0]); for (int i=1; i<V.Kp[CurrentModCycle]; i++) FXX[i]=2*sqrt(V.fxr[CurrentModCycle][2*i]*V.fxr[CurrentModCycle][2*i]+V.fxr[CurrentModCycle][2*i-1]*V.fxr[CurrentModCycle][2*i-1]); for (int i=V.Kp[CurrentModCycle]; i<V.K; i++) FXX[i]=0;}
      DrawBarMap(MImage2->Picture->Bitmap, MImage2->ClientRect, V.K, FXX, ResidueCheck->Checked, KX1, KX2);
    }
    else if (PageControl1->ActivePage==AmpCycleSheet)
    {
      if (SFCheck->Checked)
        DrawSFr(AImage2->Picture->Bitmap, AImage2->ClientRect, fst, fen, updb, downdb, V.M, Fr, V.afres, Partials, V.LogAF, V.LogASp[CurrentModCycle]);
      else
        DrawAF(AImage2->Picture->Bitmap, AImage2->ClientRect, fst, fen, updb, downdb, V.M, V.peakfr[CurrentModCycle], Partials, V.A0C, MACheck->Checked, false, V.peakfr[CurrentModCycle-1]);
			double cf0=0; for (int i=frst; i<fren; i++) cf0+=V.F0C[i]; cf0/=(fren-frst);
      SXcyclec=DrawSF(AImage4->Picture->Bitmap, AImage4->ClientRect, 0, 0.25, updb, downdb, cf0, V.M, V.LogASp[CurrentModCycle], 1.0/V.afres, V.afres/2, V.LogAF, SXcycle);
    }
  }
  else //CurrentCycle==-1
  {
  }

  //this part draws the modulator itself
  V.F0Dmax=-f_ex, V.F0Dmin=f_ex;
  for (int fr=V.peakfr[0]; fr<=V.peakfr[V.P-1]; fr++){if (V.F0Dmax<V.F0D[fr]) V.F0Dmax=V.F0D[fr]; if (V.F0Dmin>V.F0D[fr]) V.F0Dmin=V.F0D[fr];}
  I2Canvas->Pen->Color=clBlack; I2Canvas->Pen->Style=psDot;
  Y=Image2->Height*(0.5-V.F0Dmax/f_ex);
  I2Canvas->MoveTo(0, Y); I2Canvas->LineTo(Image2->Width, Y);
  AnsiString S; S.sprintf("%.4ghz (%.3g%%)", V.F0Dmax*sps, V.F0Dmax/V.F0Overall*100);
  TFont* F=I2Canvas->Font; F->Color=clBlack; F->Height=12; F->Name="Ariel";
  I2Canvas->TextOut(1, Y-I2Canvas->TextHeight(S)-1, S);
  Y=Image2->Height*(0.5-V.F0Dmin/f_ex);
  I2Canvas->MoveTo(0, Y); I2Canvas->LineTo(Image2->Width, Y);
  S.sprintf("%.4ghz (%.3g%%)", V.F0Dmin*sps, V.F0Dmin/V.F0Overall*100);
  I2Canvas->TextOut(1, Y+1, S);
  I2Canvas->Pen->Style=psSolid;

  I2Canvas->Pen->Color=clSilver;
  I2Canvas->MoveTo(0, Image2->Height/2); I2Canvas->LineTo(Image2->Width, Image2->Height/2);
  I2Canvas->Pen->Color=clBlue;
  for (int fr=0; fr<Fr; fr++)
  {
    if (fr>0) I2Canvas->MoveTo(X, Y);
    X=Image2->Width*Partials[1][fr].t/L, Y=Image2->Height*(0.5-V.F0D[fr]/f_ex);
    if (fr>0) I2Canvas->LineTo(X, Y);
  }
  I2Canvas->Pen->Color=clLime;
  for (int fr=0; fr<Fr; fr++)
  {
    X=Image2->Width*Partials[1][fr].t/L, Y=Image2->Height*(0.5-V.F0D[fr]/f_ex);
    I2Canvas->MoveTo(X-1, Y); I2Canvas->LineTo(X+2, Y); I2Canvas->MoveTo(X, Y-1); I2Canvas->LineTo(X, Y+2);
  }
  F->Color=clSilver; F->Height=12; F->Name="Ariel";
  S.sprintf("%.4ghz (%.3g%%)", f_ex*sps/2, f_ex/2/V.F0Overall*100);
  I2Canvas->TextOut(Image2->Width-I2Canvas->TextWidth(S), 0, S);
  S.sprintf("%.4ghz (%.3g%%)", -f_ex*sps/2, -f_ex/2/V.F0Overall*100);
  I2Canvas->TextOut(Image2->Width-I2Canvas->TextWidth(S), Image2->Height-I2Canvas->TextHeight(S), S);

  F->Color=clSilver; S="F0 modulator";
  I2Canvas->TextOut(1, Image2->Height-I2Canvas->TextHeight(S), S);

}


void __fastcall TVibratoDemoForm::SFCheckClick(TObject *Sender)
{
  if (Sender==SFCheck)
  {
    MACheck->Visible=!SFCheck->Checked;
  }
  UpdateDisplay();
}
//---------------------------------------------------------------------------


void __fastcall TVibratoDemoForm::RegButtonClick(TObject *Sender)
{
  RegularizeV(*HS, V, WaveView1->SamplesPerSec, WaveView1->SpecOffst);
  AnalyzeV(*HS, V, peaky, cyclefrs, cyclefs, WaveView1->SamplesPerSec, WaveView1->SpecOffst, &cyclefrcount);
  SaveV();
  Synthesize();
  UpdateDisplay();
}
//---------------------------------------------------------------------------


void __fastcall TVibratoDemoForm::ExternalInput()
{
  AnsiString FileName=ExtractFilePath(Application->ExeName)+"tvoin";
  FILE* file;
  if ((file=fopen(FileName.c_str(), "rb"))!=NULL)
  {
    V.LoadFromFileHandle(file);
    fclose(file);
    DeleteFile(FileName);
    SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
    SaveV();
    Synthesize();
    UpdateDisplay();
  }
}

//---------------------------------------------------------------------------
void __fastcall TVibratoDemoForm::SaveV()
{
  FILE* file;
  if ((file=fopen((ExtractFilePath(Application->ExeName)+"tvoout").c_str(), "wb"))!=NULL)
  {
    V.SaveToFileHandle(file);
    fclose(file);
  }
}

//---------------------------------------------------------------------------
void __fastcall TVibratoDemoForm::MouseWheelHandler(TMessage& Msg)
{
  TWMMouseWheel* WMsg=(TWMMouseWheel*)&Msg;

  bool Handled=false;
  TMouseWheelEvent Event=NULL;
  TControl* Sender;
  TShiftState Shift; memcpy(&Shift, &WMsg->Keys, 1);

  if (ActiveControl==Image1->Parent) Event=Image1MouseWheel, Sender=Image1;
  else if (ActiveControl==Image2->Parent) Event=Image2MouseWheel, Sender=Image2;
  else if (ActiveControl==AImage3->Parent) Event=AImage3MouseWheel, Sender=AImage3;
	else if (ActiveControl==AImage1->Parent) Event=AImage3MouseWheel, Sender=AImage1;
  else if (ActiveControl==AImage4->Parent) Event=AImage4MouseWheel, Sender=AImage4;
  else if (ActiveControl==AImage2->Parent) Event=AImage4MouseWheel, Sender=AImage2;
  else if (ActiveControl==MImage1->Parent) Event=MImage1MouseWheel, Sender=MImage1;
  else if (ActiveControl==MImage2->Parent) Event=MImage1MouseWheel, Sender=MImage2;
  else if (ActiveControl==RateEdit || ActiveControl==CycleRateEdit) Event=RateEditMouseWheel, Sender=ActiveControl;
  else if (ActiveControl==DurationEdit || ActiveControl==CycleDurationEdit) Event=DurationEditMouseWheel, Sender=ActiveControl;
  else if (ActiveControl==CyclePitchEdit) Event=PitchEditMouseWheel, Sender=ActiveControl;

  if (Event) Event(Sender, Shift, WMsg->WheelDelta, Sender->ScreenToClient(TPoint(WMsg->Pos.x, WMsg->Pos.y)), Handled);
  WMsg->Result=Handled;

  if (!Handled) TCustomForm::MouseWheelHandler(Msg);
}

//---------------------------------------------------------------------------
void __fastcall TVibratoDemoForm::Image1MouseWheel(TObject* Sender, TShiftState Shift, int WheelDelta, const TPoint &MousePos, bool &Handled)
{
  double cent;
  if (Shift.Contains(ssShift)) cent=1;
  else if (Shift.Contains(ssCtrl)) cent=100;
  else cent=10;

  if (WheelDelta<0) cent=-cent;
  double rate=pow(2, cent/1200);

  for (int l=0; l<V.L; l++) V.F0C[l]*=rate; V.F0Overall*=rate;
  for (int fr=0; fr<cyclefrcount; fr++) cyclefs[fr]*=rate;

  SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
  SaveV();
  S_U();
  Handled=true;
}

void __fastcall TVibratoDemoForm::Image2MouseWheel(TObject* Sender, TShiftState Shift, int WheelDelta, const TPoint &MousePos, bool &Handled)
{
  double rate;
  if (Shift.Contains(ssShift)) rate=1.01;
  else if (Shift.Contains(ssCtrl)) rate=1.282432;
  else rate=1.05101;
  if (WheelDelta<0) rate=1.0/rate;
  if (CurrentModCycle>0) V.Dp[CurrentModCycle]=V.Dp[CurrentModCycle]*rate;
  else for (int p=1; p<V.P; p++) V.Dp[p]=V.Dp[p]*rate;

  SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
  SaveV();
  S_U();
  Handled=true;
}

void __fastcall TVibratoDemoForm::AImage3MouseWheel(TObject* Sender, TShiftState Shift, int WheelDelta, const TPoint &MousePos, bool &Handled)
{
  double ddb;
  if (Shift.Contains(ssShift)) ddb=0.2;
  else if (Shift.Contains(ssCtrl)) ddb=5;
  else ddb=1;
  double db=20*log10e*V.LogAS[CurrentPartial];
  if (WheelDelta<0) ddb=-ddb;

  db+=ddb;
  AnsiString S; S.sprintf("Partial %d: %.2fdB", CurrentPartial+1, db);
  if (Sender==AImage3) Label7->Caption=S;
  else Label8->Caption=S;
  ddb=ddb/(20*log10e);

  for (int p=1; p<V.P; p++) V.LogASp[p][CurrentPartial]+=ddb;
  V.LogAS[CurrentPartial]+=ddb;

  SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
  SaveV();
  S_U(true);
  Handled=true;
}

void __fastcall TVibratoDemoForm::AImage4MouseWheel(TObject* Sender, TShiftState Shift, int WheelDelta, const TPoint &MousePos, bool &Handled)
{
  double ddb;
  if (Shift.Contains(ssShift)) ddb=0.2;
  else if (Shift.Contains(ssCtrl)) ddb=5;
  else ddb=1;
  double db=20*log10e*V.LogASp[CurrentModCycle][CurrentPartial];
  if (WheelDelta<0) ddb=-ddb;

  db+=ddb;
  AnsiString S; S.sprintf("Partial %d: %.2fdB", CurrentPartial+1, db);
  if (Sender==AImage4) Label9->Caption=S;
  else Label10->Caption=S;
  ddb=ddb/(20*log10e);

  V.LogASp[CurrentModCycle][CurrentPartial]+=ddb;

  SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
  SaveV();
  S_U(true);
  Handled=true;
}

void __fastcall TVibratoDemoForm::MImage1MouseWheel(TObject* Sender, TShiftState Shift, int WheelDelta, const TPoint &MousePos, bool &Handled)
{
  double rate;
  if (Shift.Contains(ssShift)) rate=1.0192448764914566206520666016133;
  else if (Shift.Contains(ssCtrl)) rate=1.61051;
  else rate=1.1;
  if (WheelDelta<0) rate=1.0/rate;

  double* FXR;
  TLabel* Label;
  if (Sender==MImage1) FXR=V.FXR, Label=Label11;
  else if (ActiveControl==MImage2->Parent) FXR=V.fxr[CurrentModCycle], Label=Label12;

  if (CurrentK==0)
  {
    FXR[0]*=rate;
    if (Sender==MImage1) for (int p=1; p<V.P; p++) V.fxr[p][0]*=rate;
  }
  else
  {
    FXR[2*CurrentK-1]*=rate, FXR[2*CurrentK]*=rate;
    if (Sender==MImage1) for (int p=1; p<V.P; p++) V.fxr[p][2*CurrentK-1]*=rate, V.fxr[p][2*CurrentK]*=rate;
  }

  AnsiString S;
  if (CurrentK>0) S.sprintf("%d: %.3g", CurrentK, sqrt(FXR[CurrentK*2-1]*FXR[CurrentK*2-1]+FXR[CurrentK*2]*FXR[CurrentK*2]));
  else if (CurrentK==0) S.sprintf("%d: %.3g", CurrentK, fabs(FXR[0]));
  Label->Caption=S;
  Label->Left=ActiveControl->Left+0.5*(KX1[CurrentK]+KX2[CurrentK]-Label->Width);

  SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
  SaveV();
  S_U();
  Handled=true;
}

void __fastcall TVibratoDemoForm::RateEditMouseWheel(TObject* Sender, TShiftState Shift, int WheelDelta, const TPoint &MousePos, bool &Handled)
{
  double rate, irate;
  if (Shift.Contains(ssShift)) rate=1.01;
  else if (Shift.Contains(ssCtrl)) rate=1.282432;
  else rate=1.05101;
  if (WheelDelta<0) {irate=rate; rate=1.0/rate;}
  else irate=1.0/rate;

  if (Sender==CycleRateEdit)
  {
    if (CurrentModCycle>0)
    {
      double dlp=(V.lp[CurrentModCycle]-V.lp[CurrentModCycle-1])*(irate-1);
      for (int p=CurrentModCycle; p<V.P; p++)
      {
        V.lp[p]+=dlp;
        if (V.lp[p]>V.L) {V.P=p; break;}
      }
    }
  }
  else if (Sender==RateEdit)
  {
    double newP=V.GetOldP()*rate;
    if (newP>2.5)
    {
      TVo* newV=InterpolateV(newP, rate, V);
      V.~TVo();
      memcpy(&V, newV, sizeof(TVo)); memset(newV, 0, sizeof(TVo));
      delete newV;
    }
  }
  SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
  RateAndReg(V.rate, V.regularity, V.F0D, V.peakfr[0], V.peakfr[V.P-1], 8, WaveView1->SamplesPerSec, WaveView1->SpecOffst);
  SaveV();
  S_U();
  Handled=true;
}

void __fastcall TVibratoDemoForm::DurationEditMouseWheel(TObject* Sender, TShiftState Shift, int WheelDelta, const TPoint &MousePos, bool &Handled)
{
  double rate;
  if (Shift.Contains(ssShift)) rate=1.01;
  else if (Shift.Contains(ssCtrl)) rate=1.282432;
  else rate=1.05101;
  if (WheelDelta<0) rate=1.0/rate;

  if (Sender==CycleDurationEdit)
  {
    if (CurrentModCycle>0)
    {
      double dlp=(V.lp[CurrentModCycle]-V.lp[CurrentModCycle-1])*(rate-1);

      for (int p=CurrentModCycle; p<V.P; p++)
      {
        V.lp[p]+=dlp;
      }
      int oldL=V.L, newL=V.L+dlp+0.5;

      double *F0C=new double[V.L*2], *A0C=&F0C[V.L];
      memcpy(F0C, V.F0C, sizeof(double)*oldL), memcpy(A0C, V.A0C, sizeof(double)*oldL);
      V.AllocateL(newL);
      for (int l=0; l<newL; l++)
      {
        double rl=1.0*l*oldL/newL;
        int il=floor(rl); rl-=il;
        if (fabs(rl)<1e-16)
        {
          V.F0C[l]=F0C[il];
          V.A0C[l]=A0C[il];
        }
        else if (il>=oldL-1)
        {
          V.F0C[l]=F0C[oldL-1];
          V.A0C[l]=A0C[oldL-1];
        }
        else
        {
          V.F0C[l]=F0C[il]*(1-rl)+F0C[il+1]*rl;
          V.A0C[l]=A0C[il]*(1-rl)+A0C[il+1]*rl;
        }
      }

      for (int c=0; c<cyclefrcount; c++)
      {
        cyclefrs[c]*=(1.0*newL/oldL);
        int ic=floor(cyclefrs[c]);
        if (ic<newL-1) cyclefs[c]=F0C[ic]*(ic+1-cyclefrs[c])+F0C[ic+1]*(cyclefrs[c]-ic);
        else cyclefs[c]=F0C[newL-1];
      }

      delete[] F0C;
    }
  }
  else if (Sender==DurationEdit)
  {
    double newP=V.GetOldP()*rate;
    if (newP>2.5)
    {
      TVo* newV=InterpolateV(newP, 1, V); V.~TVo();
      memcpy(&V, newV, sizeof(TVo)); memset(newV, 0, sizeof(TVo));
      delete newV;

      for (int c=0; c<cyclefrcount; c++)
      {
        cyclefrs[c]*=rate;
        int ic=floor(cyclefrs[c]);
        if (ic<V.L-1) cyclefs[c]=V.F0C[ic]*(ic+1-cyclefrs[c])+V.F0C[ic+1]*(cyclefrs[c]-ic);
        else cyclefs[c]=V.F0C[V.L-1];
      }
    }
  }
  SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
  RateAndReg(V.rate, V.regularity, V.F0D, V.peakfr[0], V.peakfr[V.P-1], 8, WaveView1->SamplesPerSec, WaveView1->SpecOffst);
  SaveV();
  S_U();
  Handled=true;
}

void __fastcall TVibratoDemoForm::PitchEditMouseWheel(TObject* Sender, TShiftState Shift, int WheelDelta, const TPoint &MousePos, bool &Handled)
{
  if (CurrentModCycle<=0) return;

  double cent;
  if (Shift.Contains(ssShift)) cent=1;
  else if (Shift.Contains(ssCtrl)) cent=100;
  else cent=10;

  if (WheelDelta<0) cent=-cent;
  double rate_1=pow(2, cent/1200)-1;

  int frst=ceil(V.lp[CurrentModCycle-1]), freninc=floor(V.lp[CurrentModCycle]);
  for (int fr=frst; fr<=freninc; fr++) V.F0C[fr]=V.F0C[fr]*(1+rate_1);//(1+rate_1*0.5*(1-cos(2*M_PI*(fr-V.lp[CurrentModCycle-1])/(V.lp[CurrentModCycle]-V.lp[CurrentModCycle-1]))));


  SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
  SaveV();
  S_U();
  Handled=true;
}
//---------------------------------------------------------------------------


void __fastcall TVibratoDemoForm::AImage3MouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{
  if (!HS) return;
  TImage* Image=(TImage*)Sender;

  if (Shift.Contains(ssLeft))
  {
    if (CurrentPartial<0) return;
    int fh=Image->Canvas->TextHeight("0");
    int YY=Image->Height-Bdw-Bdwc-fh;
    double dbperpixel=dbrange/YY;
    int dY=Y-StartDrag;
    StartDrag=Y;
    double ddb=-dY*dbperpixel;

    if (Sender==AImage1 || Sender==AImage3)
    {
      double db=20*log10e*V.LogAS[CurrentPartial];
      db+=ddb;
      AnsiString S; S.sprintf("Partial %d: %.2fdB", CurrentPartial+1, db);
      if (Sender==AImage3) Label7->Caption=S;
      else Label8->Caption=S;
      ddb=ddb/(20*log10e);

      for (int p=1; p<V.P; p++) V.LogASp[p][CurrentPartial]+=ddb;
      V.LogAS[CurrentPartial]+=ddb;
    }
    else if (Sender==AImage2 || Sender==AImage4)
    {
      double db=20*log10e*V.LogASp[CurrentModCycle][CurrentPartial];
      db+=ddb;
      AnsiString S; S.sprintf("Partial %d: %.2fdB", CurrentPartial+1, db);
      if (Sender==AImage4) Label9->Caption=S;
      else Label10->Caption=S;
      ddb=ddb/(20*log10e);

      V.LogASp[CurrentModCycle][CurrentPartial]+=ddb;
    }

    SynthesizeV(HS, &V, WaveView1->SamplesPerSec);
    UpdateDisplay();
  }
  else
  {
    Image->Parent->SetFocus();

    int lSXc, *lSX;
    double* LogAS;
    if (Sender==AImage1 || Sender==AImage3) lSXc=SXc, lSX=SX, LogAS=V.LogAS, X=Bdw+(X-Bdw)*AImage3->Width/((TImage*)Sender)->Width;
    else if (Sender==AImage2 || Sender==AImage4) lSXc=SXcyclec, lSX=SXcycle, LogAS=V.LogASp[CurrentModCycle], X=Bdw+(X-Bdw)*AImage4->Width/((TImage*)Sender)->Width;

    if (lSXc<=0) return;


    CurrentPartial=0; while (CurrentPartial<lSXc && lSX[CurrentPartial]<X) CurrentPartial++;
    if (CurrentPartial>=lSXc) CurrentPartial=lSXc-1;
    if (CurrentPartial>=1 && X-lSX[CurrentPartial-1]<lSX[CurrentPartial]-X) CurrentPartial--;

    double dB=20*log10e*LogAS[CurrentPartial];
    AnsiString S; S.sprintf("Partial %d: %.2fdB", CurrentPartial+1, dB);

    TLabel* Label;
    if (Sender==AImage1) Label=Label8;
    else if (Sender==AImage2) Label=Label10;
    else if (Sender==AImage4) Label=Label9;
    else Label=Label7;
    Label->Font->Color=RotateColors[CurrentPartial%10]; Label->Caption=S;
  }
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::MImage1MouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{
  if (!HS) return;
  if (Shift.Contains(ssLeft))
  {
    if (CurrentK<0) return;

  }
  else
  {
    TImage* Image=(TImage*)Sender;
    Image->Parent->SetFocus();
    CurrentK=-1; for (int i=0; i<V.K; i++) if (X>=KX1[i] && X<=KX2[i]) {CurrentK=i; break;}
    if (CurrentK<0) return;

    double* FXR;
    TLabel* Label;
    if (Sender==MImage1) FXR=V.FXR, Label=Label11;
    else if (Sender==MImage2)
    {
      if (CurrentModCycle<0) return;
      FXR=V.fxr[CurrentModCycle], Label=Label12;
    }

    AnsiString S;
    if (CurrentK>0) S.sprintf("%d: %.3g", CurrentK, sqrt(FXR[CurrentK*2-1]*FXR[CurrentK*2-1]+FXR[CurrentK*2]*FXR[CurrentK*2]));
    else if (CurrentK==0) S.sprintf("%d: %.3g", CurrentK, fabs(FXR[0]));
    Label->Caption=S; Label->Left=Image->Parent->Left+0.5*(KX1[CurrentK]+KX2[CurrentK]-Label->Width);
  }
}
//---------------------------------------------------------------------------


void __fastcall TVibratoDemoForm::Image2DblClick(TObject *Sender)
{
  TPoint P1; GetCursorPos(&P1);
  TPoint P2=MImage1->ClientToScreen(TPoint(0, MImage1->Height*0.3819));
  SetCursorPos(P1.x, P2.y);
}
//---------------------------------------------------------------------------



void __fastcall TVibratoDemoForm::AImage1MouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
  FirstStartDrag=StartDrag=Y;
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::AImage1MouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
  if (Y!=FirstStartDrag)
  {
    SaveV();
    Synthesize();
    UpdateDisplay();
  }
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::CycleRateEditMouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{
  TEdit* Edit=(TEdit*)Sender;
  Edit->SetFocus();
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::PageControl1Change(TObject *Sender)
{
  if (PageControl1->ActivePage==ModOverSheet || PageControl1->ActivePage==ModCycleSheet)
    ResidueCheck->Parent=PageControl1->ActivePage;
  else if (PageControl1->ActivePage==AmpOverSheet || PageControl1->ActivePage==AmpCycleSheet)
   {SFCheck->Parent=PageControl1->ActivePage; MACheck->Parent=PageControl1->ActivePage;}
  UpdateDisplay();
}
//---------------------------------------------------------------------------


  void SourceResponse(double* SV, TVo* V)
  {
    int N=V->afres/2;
    memset(SV, 0, sizeof(double)*N);
    for (int p=1; p<V->P; p++)
    {
      double F0p=0;
      int frst=V->lp[p-1], fren=V->lp[p];
      for (int fr=frst; fr<fren; fr++) F0p+=V->F0C[fr];
      F0p/=(fren-frst);
      F0p*=V->afres;
      int lastfp;
      double lastsv;
      for (int m=0; m<V->M; m++)
      {
        int fp=F0p*(m+1);
        double sv=V->LogASp[p][m];
        if (m==0) for (int n=0; n<fp; n++) SV[n]+=sv;
        else for (int n=lastfp; n<fp; n++) SV[n]+=(sv*(n-lastfp)+lastsv*(fp-n))/(fp-lastfp);
        lastfp=fp, lastsv=sv;
      }
      for (int n=lastfp; n<N; n++) SV[n]+=lastsv;
    }
    for (int n=0; n<N; n++) SV[n]/=(V->P-1);
  }

//S_U implements delayed updating of lengthy synthesizing to speed up operation
//  in case the HS is updated frequently, s.a. during mouse wheeling.
//This allows a later synthesis to start before an earlier synthesis finishes,
//  at which the latter is terminated and its incomplete result is discarded.
void __fastcall TVibratoDemoForm::S_U(bool sf)
{
  if (SUThread)
  {
    SUThread->Terminate();
  }
	SUThread=new TSUThread(true);
	SUThread->sf=sf;
	SUThread->OnTerminate=SUTerminate;
	SUThread->HS=new THS(HS);
	ThreadList[pThread++]=SUThread;
	if (pThread==ThreadCaps/2 && ThreadList[pThread]) 
		for (int i=pThread; i<ThreadCaps; i++) {delete ThreadList[i]; ThreadList[i]=0;}
	else if (pThread==ThreadCaps)
	{
		for (int i=0; i<ThreadCaps/2; i++) {delete ThreadList[i]; ThreadList[i]=0;}
		pThread=0;
	}
	SUThread->Resume();
}

void __fastcall TVibratoDemoForm::SUTerminate(TObject* Sender)
{
	TSUThread* CurrentThread=(TSUThread*)Sender;
  if (CurrentThread==SUThread)
  {
    double* xrec=CurrentThread->xrec;
    if (xrec)
    {
      int dst=CurrentThread->dst, den=CurrentThread->den;
      if (dst<0) dst=0;
      __int16* data=new __int16[den];
      memset(data, 0, sizeof(__int16)*dst);
      DoubleToInt(&data[dst], sizeof(__int16), xrec, den-dst);
      WaveAudio2->Clear(NULL);
      WaveAudio2->WriteSamples(data, den-dst);
      delete[] data;
      UpdateDisplay();
    }
    SUThread=0;
  }
  else
  {
    UpdateDisplay(true, true, true, CurrentThread->sf);
  }
  free8(CurrentThread->xrec); CurrentThread->xrec=0;
}
//---------------------------------------------------------------------------
void __fastcall TVibratoDemoForm::Panel4Resize(TObject *Sender)
{
  Panel10->Height=Panel4->Height/3;
  ForceUpdate=true;
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::Panel8Resize(TObject *Sender)
{
  int H=(Panel8->Height-Splitter4->Height-Splitter5->Height)/3;
  Panel9->Height=H; Panel7->Height=H;
  ForceUpdate=true;
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::Panel1Resize(TObject *Sender)
{
  Panel2->Height=(Panel1->Height-Splitter1->Height)/2;
}
//---------------------------------------------------------------------------


void __fastcall TVibratoDemoForm::AmpOverSheetResize(TObject *Sender)
{
  double W=(AmpOverSheet->Width-24)/20.0;
  AImage3->Parent->Width=W*9;
  Label7->Width=AImage3->Parent->Width;
  AImage1->Parent->Left=AImage3->Parent->Left+AImage3->Parent->Width+8;
  AImage1->Parent->Width=W*11;
  Label8->Left=AImage1->Parent->Left;
  Label8->Width=AImage1->Parent->Width;
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::AmpCycleSheetResize(TObject *Sender)
{
  double W=(AmpCycleSheet->Width-24)/20.0;
  AImage4->Parent->Width=W*9;
  Label9->Width=AImage4->Parent->Width;
  AImage2->Parent->Left=AImage4->Parent->Left+AImage4->Parent->Width+8;
  AImage2->Parent->Width=W*11;
  Label10->Left=AImage2->Parent->Left;
  Label10->Width=AImage2->Parent->Width;
}
//---------------------------------------------------------------------------


void __fastcall TVibratoDemoForm::ListBox1Click(TObject *Sender)
{
  if (ListBox1->ItemIndex==1) ThetaEdit->Enabled=true;
  else ThetaEdit->Enabled=false;
	Reset();	
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::Button1Click(TObject *Sender)
{
  int N=WaveView1->Length, Channel=Form1->HS->Channel;

  if (GetKeyState(VK_SHIFT)<0)
  {
    __int16* d2=WaveView2->Data16[0];
    Form1->PostWaveViewData(d2, Channel, StartPos, StartPos+N, Form1->FadeInCheck->Checked, Form1->FadeInCombo->Text.ToInt());
  }
  else
  {
    __int16 *data=new __int16[N], *data1=WaveView1->Data16[0], *data2=WaveView2->Data16[0];
    int NN=N;
    if (NN>WaveView2->Length) NN=WaveView2->Length;
    for (int n=0; n<NN; n++) data[n]=data1[n]-datain[n]+data2[n];
    for (int n=NN; n<N; n++) data[n]=data1[n]-datain[n];
    Form1->PostWaveViewData(data, Channel, StartPos, StartPos+NN, Form1->FadeInCheck->Checked, Form1->FadeInCombo->Text.ToInt());
    if (NN!=N) Form1->PostWaveViewData(&data[NN], Channel, StartPos+NN, StartPos+N, Form1->FadeInCheck->Checked, Form1->FadeInCombo->Text.ToInt());
    delete[] data;
  }
}
//---------------------------------------------------------------------------



void __fastcall TVibratoDemoForm::Panel9Resize(TObject *Sender)
{
  ForceUpdate=true;
}
//---------------------------------------------------------------------------

void __fastcall TVibratoDemoForm::Panel7Resize(TObject *Sender)
{
  ForceUpdate=true;
}
//---------------------------------------------------------------------------