Mercurial > hg > hv
diff SFDemoUnit.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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SFDemoUnit.cpp Wed Aug 10 14:55:38 2011 +0100 @@ -0,0 +1,1474 @@ +/* + 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 "SFDemoUnit.h" +#include "Matrix.h" +#include "Unit1.h" +#include "splines.h" +//--------------------------------------------------------------------------- +#pragma package(smart_init) +#pragma resource "*.dfm" +TSFDemoForm *SFDemoForm; +//--------------------------------------------------------------------------- +const double log10e=Log10(exp(1)); +const Bdw=5, Bdwc=2; //border width of charts +const ftrr=5; //filter control point radius + + +__fastcall TSFDemoForm::TSFDemoForm(TComponent* Owner) + : TForm(Owner) +{ + ForceUpdate=false; + + double updb=updbEdit->Text.ToDouble(), downdb=downdbEdit->Text.ToDouble(); + dbrange=updb-downdb; + + 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; + WaveView2->LoopPlay=CheckBox1->Checked; + + + HS=0; + cyclefrs=0; + cyclefs=0; + + MethodListBox->ItemIndex=0; + + analyze2=false; + + AnsiString Path=ExtractFilePath(Application->ExeName)+"*.sf"; + TSearchRec sr; + if (FindFirst(Path, faAnyFile, sr)==0) + { + do + { + TmplListBox->Items->Add(sr.Name); + } + while (FindNext(sr) == 0); + FindClose(sr); + } + Wave1=0; + CurrentP=-1; + + memset(&SF, 0, sizeof(TSF)); + memset(&SF0, 0, sizeof(TSF)); + + datain=0; +} + +__fastcall TSFDemoForm::~TSFDemoForm() +{ + delete WaveAudio1; + delete WaveAudio2; + delete WaveView1; + delete WaveView2; + delete HS; + delete[] cyclefrs; + delete[] cyclefs; + for (int i=0; i<ThreadCaps; i++) if (ThreadList[i]) delete ThreadList[i]; + delete[] Wave1; + delete[] datain; +} +//--------------------------------------------------------------------------- + +void TSFDemoForm::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 TSFDemoForm::FindNote() +{ +} + +void TSFDemoForm::Reset() +{ + if (!HS) return; + if (HS->M<=0 || HS->Fr<=0) return; + + if (MethodListBox->ItemIndex<0) MethodListBox->ItemIndex=0; + + double sps=WaveView1->SamplesPerSec; + double h=WaveView1->SpecOffst; + int FSMode=MethodListBox->ItemIndex; + double FSF=FEdit->Text.ToDouble(); + int FSFScale=FScaleCombo->ItemIndex; + if (FSFScale==0) FSF/=sps; + else FSF/=Amel*log(1+sps/700); + double FStheta=ThetaEdit->Text.ToDouble(); + + AnalyzeSF_1(*HS, SF, sps, h); + AnalyzeSF_2(*HS, SF, cyclefrs, cyclefs, sps, &cyclefrcount, FSMode, FSF, FSFScale, FStheta); + + SaveSF(); + SaveSF0(); + + UpdateDisplay(); +} + +void TSFDemoForm::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 DrawsBarMap(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 frst, int fren, atom** Partials, bool MA, bool bars, double sps, int TextHeight=12) + { + double log10e=1.0/log(10); + 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[(fren-frst)*4], *y=&x[fren-frst], *newa=&x[(fren-frst)*2], *neww=&x[(fren-frst)*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=frst; fr<fren; fr++) + { + if (Partialsm[fr].f>0 && Partialsm[fr].a>0) + { + x[Sc]=Partials[m][fr].f; + y[Sc]=20*(Log10(Partialsm[fr].a)); + 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=AnsiString().sprintf("%.2gkhz", sps*xst/1000); Canvas->Brush->Style=bsClear; Canvas->TextOut(Bdw, Bdw+YY, S); + S=AnsiString().sprintf("%.2gkhz", sps*(xst+xen)/2000); int fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+(XX-fw)/2, Bdw+YY, S); + S=AnsiString().sprintf("%.2gkhz", sps*xen/1000); 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 frst, int fren, atom** Partials, double* logA0C, double* b, double F, int K, double* h, int FScaleMode, double Fs, int TextHeight=12) + { + Bitmap->Width=Rect.Width(); + Bitmap->Height=Rect.Height(); + TCanvas* Canvas=Bitmap->Canvas; + TFont* Ft=Canvas->Font; Ft->Color=clWhite; Ft->Height=TextHeight; Ft->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, + 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=frst; fr<fren; fr++) + { + try{ + 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 logaf; + //calculate LogAF + if (FScaleMode) xx=log(1+xx*Fs/700)/log(1+Fs/700); + int k=floor(xx/F); double f_plus=xx/F-k; + if (k<K+1) logaf=h[k]*(1-f_plus)+h[k+1]*f_plus; + else logaf=h[K+1]; + + double yy=20*log10e*(logaf+b[m]+logA0C[fr]); + 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); + } + }}catch(...) + {int k=0;} + } + if (!visible) break; + } + delete[] XRec; + + AnsiString S=AnsiString().sprintf("%.2gkhz", Fs*xst/1000); Canvas->Brush->Style=bsClear; Canvas->TextOut(Bdw, Bdw+YY, S); + S=AnsiString().sprintf("%.2gkhz", Fs*(xst+xen)/2000); int fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+(XX-fw)/2, Bdw+YY, S); + S=AnsiString().sprintf("%.2gkhz", Fs*xen/1000); 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 updb, double downdb, double f0, int M, double* b, double F, int K, double* h, int FScaleMode, double Fs=44100, int* SX=0, int* FX=0, int* FY=0, TColor* Colors=0, int TextHeight=12, int FShiftdB=0) + { + double FShift=FShiftdB/(20.0*log10e); //allowing the filter to be drawn with a vertical shift, and source an opposite shift + Bitmap->Width=Rect.Width(); + Bitmap->Height=Rect.Height(); + TCanvas* Canvas=Bitmap->Canvas; + double *Sx=new double[M]; + for (int i=0; i<M; i++) Sx[i]=20*log10e*(b[i]-FShift); + + int result=0; + TFont* Ft=Canvas->Font; Ft->Color=clWhite; Ft->Height=TextHeight; Ft->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/(updb-downdb), xrange=xen-xst, XXixrange=XX/xrange, xstXXixrange=xst*XXixrange, + YY20log10eidbrange=YY*20*log10e*idbrange, BdwpYYpYYdbenidbrange=Bdw+YY+YY*downdb*idbrange, + Bdw_xstXXixrange=Bdw-xstXXixrange, YYidbrange=YY*idbrange; + + double st=xst/F; int ist=ceil(st); if (ist<0) ist=0; + Canvas->Pen->Color=clWhite; + int X=Bdw+XX*(F*ist-xst)/xrange+0.5, Y=BdwpYYpYYdbenidbrange-YY20log10eidbrange*(h[ist]+FShift)+0.5; + if (FX){FX[ist]=X, FY[ist]=Y; Canvas->MoveTo(X-1, Y); Canvas->LineTo(X+2, Y); Canvas->MoveTo(X, Y-1); Canvas->LineTo(X, Y+2);} + Canvas->MoveTo(X, Y); + //draw filter contour + int LineCount=xrange/F; + if (LineCount<XX) + { + for (int i=ist+1; i<K+2; i++) + { + double xi=F*i; + if (FScaleMode) + { + double ximel=Amel*log(1+Fs/700)*xi; + double xihz=700*(exp(ximel/Amel)-1); + xi=xihz/Fs; + } + X=floor(Bdw_xstXXixrange+xi*XXixrange+0.5); + Y=floor(BdwpYYpYYdbenidbrange-YY20log10eidbrange*(h[i]+FShift)+0.5); + Canvas->LineTo(X, Y); + if (FX){FX[i]=X, FY[i]=Y; Canvas->MoveTo(X-1, Y); Canvas->LineTo(X+2, Y); Canvas->MoveTo(X, Y-1); Canvas->LineTo(X, Y+2); Canvas->MoveTo(X, Y);} + } + } + else + { + double di=xrange/(XX*F); + for (int x=0; x<XX; x++) + { + int i; + if (FScaleMode) i=log(1+(xst+xrange*x/XX)*Fs/700)/log(1+Fs/700)/F; + else i=st+x*di; + X=Bdw+x; + Y=floor(BdwpYYpYYdbenidbrange-YY20log10eidbrange*(h[i]+FShift)+0.5); + Canvas->LineTo(Bdw+x, Y); + if (FX){FX[i]=X, FY[i]=Y; Canvas->MoveTo(X-1, Y); Canvas->LineTo(X+2, Y); Canvas->MoveTo(X, Y-1); Canvas->LineTo(X, Y+2); Canvas->MoveTo(X, Y);} + } + } + //draw source lines + for (int i=0; i<M; 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=AnsiString().sprintf("%.2gkhz", Fs*xst/1000); Canvas->Brush->Style=bsClear; Canvas->TextOut(Bdw, Bdw+YY, S); + S=AnsiString().sprintf("%.2gkhz", Fs*(xst+xen)/2000); int fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+(XX-fw)/2, Bdw+YY, S); + S=AnsiString().sprintf("%.2gkhz", Fs*xen/1000); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+YY, S); + S.sprintf("%gdB", updb); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw, S); + S.sprintf("%gdB", (updb+downdb)/2); fw=Canvas->TextWidth(S); Canvas->TextOut(Bdw+XX-fw, Bdw+(YY-fh)/2, S); + S.sprintf("%gdB", downdb); 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 P, 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* frs=0, double* fs=0, int* BX1=0, int* BX2=0, int HLX1=0, int HLX2=0) + { + if (f_ex==0) f_ex=0.01; + Canvas->Brush->Color=cl3DDkShadow; Canvas->FillRect(Rect); + int Width=Rect.Width(), Height=Rect.Height(); + Bitmap->Width=Width; Bitmap->Height=Height; + TPen* Pn=Canvas->Pen; + TFont* F=Canvas->Font; + + if (HLX1<HLX2) + { + Canvas->Brush->Color=clDkGray; Canvas->FillRect(TRect(HLX1, 0, HLX2, Height)); + Canvas->Brush->Color=cl3DDkShadow; + } + + int X, Y, Y1=0, Y2=Height, Y_; + AnsiString S, as; + + Pn->Color=clBlack; Pn->Style=psDot; F->Color=clBlack; F->Height=12; F->Name="Ariel"; + int lp0=ceil(lp[0]), lpp=ceil(lp[P-1]); + double f0max=-0.5, f0min=0.5; for (int fr=lp0; fr<lpp; 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); + + for (int pfr=0; pfr<P; pfr++) + { + int fr=floor(lp[pfr]); double frplus=lp[pfr]-fr; + X=Width*(Partials[1][fr].t*(1-frplus)+Partials[1][fr+1].t*frplus)/L; + Pn->Color=peakmarkcolor; Pn->Style=psDot; Canvas->MoveTo(X, Y1); Canvas->LineTo(X, Y2); + if (BX1) BX1[pfr]=X-1; + if (BX2) BX2[pfr]=X+1; + } + } + + if (Captions) {Y_=Height*(0.5-(F0Overall-f_c)/f_ex); Pn->Color=clWhite; Canvas->MoveTo(0, Y_); Canvas->LineTo(Width, Y_);} + + Pn->Style=psSolid; Pn->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) + { + Pn->Color=clRed; + for (int pfr=0; pfr<P-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) + { + if (f_ex==0) f_ex=f_c*0.005; + //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); + } + + //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 TSFDemoForm::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=fmaxEdit->Text.ToDouble(); + 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) + { + int HLX1=0, HLX2=0; + if (CurrentP>=0) HLX1=BX1[CurrentP]+1, HLX2=BX1[CurrentP+1]+1; + DrawF0(Image0->Picture->Bitmap, Image0->Canvas, Image0->ClientRect, SF.lp, SF.P, SF.F0, f_c, f_ex, WaveView1->SamplesPerSec, SF.F0Overall, Fr, Partials, WaveView2->Length, true, clLime, true, clYellow, 0, cyclefs, BX1, BX2, HLX1, HLX2); + } + 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, SF.F0C, SF.F0Cmax, SF.F0Cmin, f_c, f_ex, WaveView1->SamplesPerSec, SF.F0Overall, Fr, Partials, WaveView2->Length, true, cyclefrcount, cyclefrs, cyclefs, CX1, CX2, CY1, CY2); + } + if (f0d) + { + //Draw the F0 modulator + DrawModulator(); + } + + if (sf) + { + int lp0=ceil(SF.lp[0]), lpp=ceil(SF.lp[SF.P-1]); + double updb=updbEdit->Text.ToDouble(), downdb=downdbEdit->Text.ToDouble(), dbshift=SFPicShift->Text.ToDouble(); + dbrange=updb-downdb; + if (SFCheck->Checked) + DrawSFr(AImage1->Picture->Bitmap, AImage1->ClientRect, fst, fen, updb, downdb, SF.M, lp0, lpp, Partials, useA0?SF.logA0:SF.logA0C, SF.avgb, SF.F, SF.K, SF.avgh, SF.FScaleMode, SF.Fs); + else + DrawAF(AImage1->Picture->Bitmap, AImage1->ClientRect, fst, fen, updb, downdb, SF.M, lp0, lpp, Partials, MACheck->Checked, false, WaveView1->SamplesPerSec); + dbrange=updb-downdb; + SXc=DrawSF(AImage3->Picture->Bitmap, AImage3->ClientRect, fst, fen, updb+dbshift, downdb+dbshift, SF.F0Overall, SF.M, SF.avgb, SF.F, SF.K, SF.avgh, SF.FScaleMode, 44100, SX, FX, FY, 0, 12, FShiftdBEdit->Text.ToInt()); + } + + ForceUpdate=false; +} + +void __fastcall TSFDemoForm::WaveView1OpMode(TObject* Sender, TShiftState Shift, int& OpMode) +{ + if (Shift.Contains(ssShift)) OpMode=0; //drag mode + else OpMode=1; //select mode +} + +int __fastcall TSFDemoForm::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 TSFDemoForm::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==Image0) + { + if (Shift.Contains(ssLeft)) + { + if (CurrentB<0 || CurrentB>=SF.P) return; + if (X<0 || X>=Image->Width || CurrentB>0 && X<=BX2[CurrentB-1] || CurrentB<SF.P-1 && X>=BX1[CurrentB+1]){} + else + { + double dlp=1.0*(X-StartDragX)*SF.L/Image->Width; + SF.lp[CurrentB]+=dlp; + StartDragX=X; + } + } + else + { + CurrentB=-1; + for (int i=0; i<SF.P; i++) if (X>=BX1[i] && X<=BX2[i]) {CurrentB=i; break;} + } + } + else 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(SF.F0C, SF.L, cyclefrcount+1, cyclefs, cyclefrs); + SynthesizeSF(HS, &SF, WaveView1->SamplesPerSec); + UpdateDisplay(true, true, true, false); + + t=HS->Partials[0][0].t+SF.offst*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+SF.offst*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); + + + if (Sender==Image0) + { + int Wid=HS->Partials[0][1].s, hWid=Wid/2, Offst=HS->Partials[0][1].t-HS->Partials[0][0].t; + CurrentFr=(t-hWid)/Offst+0.5; + if (!Shift.Contains(ssLeft)) CurrentP=-1; while (CurrentP<SF.P-1 && CurrentFr>SF.lp[CurrentP+1]) CurrentP++; if (CurrentP>=SF.P-1) CurrentP=-1; + + if (CurrentP>=0) + { + PageControl1->ActivePage=AmpCycleSheet; + SFCheck->Parent=Panel14; + MACheck->Parent=Panel14; + } + else + { + PageControl1->ActivePage=AmpOverSheet; + SFCheck->Parent=Panel12; + MACheck->Parent=Panel12; + } + UpdateDisplay(true, 0, CurrentP>=0, 0); + + if (CurrentB>=0 && CurrentB<SF.P) + { + t=HS->Partials[0][0].t+SF.offst*SF.lp[CurrentB]; + S.sprintf("%.3gs", t/sps); + F->Color=clYellow; + Image->Canvas->TextOut(1, Image->Canvas->TextHeight("0"), S); + } + else + { + S.sprintf(" "); + Image->Canvas->TextOut(1, Image->Canvas->TextHeight("0"), S); + } + } +} +//--------------------------------------------------------------------------- + +void TSFDemoForm::DrawModulator() +{ + if (PageControl1->ActivePage==AmpCycleSheet && CurrentP>=0 && CurrentP<SF.P-1) + { + double fst=0, fen=fmaxEdit->Text.ToDouble(); + int lp0=ceil(SF.lp[0]); + double updb=updbEdit->Text.ToDouble(), downdb=downdbEdit->Text.ToDouble(), dbshift=SFPicShift->Text.ToDouble(); + dbrange=updb-downdb; + if (SFCheck->Checked) + DrawSFr(AImage2->Picture->Bitmap, AImage2->ClientRect, fst, fen, updb, downdb, SF.M, ceil(SF.lp[CurrentP]), ceil(SF.lp[CurrentP+1]), HS->Partials, useA0?SF.logA0:SF.logA0C, SF.b[CurrentFr-lp0], SF.F, SF.K, SF.h[CurrentFr-lp0], SF.FScaleMode, SF.Fs); + else + DrawAF(AImage2->Picture->Bitmap, AImage2->ClientRect, fst, fen, updb, downdb, SF.M, ceil(SF.lp[CurrentP]), ceil(SF.lp[CurrentP+1]), HS->Partials, MACheck->Checked, false, WaveView1->SamplesPerSec); + dbrange=updb-downdb; + SXcyclec=DrawSF(AImage4->Picture->Bitmap, AImage4->ClientRect, fst, fen, updb+dbshift, downdb+dbshift, SF.F0[CurrentFr], SF.M, SF.b[CurrentFr-lp0], SF.F, SF.K, SF.h[CurrentFr-lp0], SF.FScaleMode, SF.Fs, SXcycle, 0, 0, 0, 12, FShiftdBEdit->Text.ToInt()); + } +} + + +void __fastcall TSFDemoForm::SFCheckClick(TObject *Sender) +{ + if (Sender==SFCheck) + { + MACheck->Visible=!SFCheck->Checked; + } + UpdateDisplay(); +} +//--------------------------------------------------------------------------- + + +void __fastcall TSFDemoForm::ExternalInput() +{ + AnsiString FileName=ExtractFilePath(Application->ExeName)+"tsfin"; + FILE* file; + if (file=fopen(FileName.c_str(), "rb")) + { + SF.LoadFromFileHandle(file); + fclose(file); + DeleteFile(FileName); + SynthesizeSF(HS, &SF, WaveView1->SamplesPerSec); + + SaveSF(); + Synthesize(); + UpdateDisplay(); + } +} + +//--------------------------------------------------------------------------- +void __fastcall TSFDemoForm::SaveSF() +{ + FILE* file; + if (file=fopen((ExtractFilePath(Application->ExeName)+"tsfout").c_str(), "wb")) + { + SF.SaveToFileHandle(file); + fclose(file); + } +} + +void __fastcall TSFDemoForm::SaveSF0() +{ + SF0.Duplicate(SF); +} + +void __fastcall TSFDemoForm::SaveWave() +{ + THS* HS=Form1->HS; int dst, den; + double* xrec=SynthesisHSp(HS, dst, den); + delete[] Wave1; Wave1=new __int16[WaveView2->Length]; + DoubleToInt(Wave1, 2, xrec, den-dst); + free8(xrec); +} +//--------------------------------------------------------------------------- +void __fastcall TSFDemoForm::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==AImage3->Parent) Event=AImage3MouseWheel, Sender=AImage3; + else if (ActiveControl==AImage1->Parent) Event=AImage3MouseWheel, Sender=AImage1; + + 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 TSFDemoForm::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<SF.L; l++) SF.F0C[l]*=rate, SF.F0D[l]*=rate; SF.F0Overall*=rate; + for (int fr=0; fr<cyclefrcount; fr++) cyclefs[fr]*=rate; + + SynthesizeSF(HS, &SF, WaveView1->SamplesPerSec); + SaveSF(); + S_U(); + Handled=true; +} + +void __fastcall TSFDemoForm::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*SF.avgb[CurrentPartial]; + if (WheelDelta<0) ddb=-ddb; + + db+=ddb; + AnsiString S; S.sprintf("Partial %d: %.2fdB", CurrentPartial+1, db); + Label7->Caption=S; + ddb=ddb/(20*log10e); + + int lp0=ceil(SF.lp[0]), lpp=ceil(SF.lp[SF.P-1]); + for (int l=0; l<lpp-lp0; l++) SF.b[l][CurrentPartial]+=ddb; + SF.avgb[CurrentPartial]+=ddb; + + SynthesizeSF(HS, &SF, WaveView1->SamplesPerSec); + SaveSF(); + S_U(true); + Handled=true; +} + + +//--------------------------------------------------------------------------- +void __fastcall TSFDemoForm::AImage3MouseMove(TObject *Sender, + TShiftState Shift, int X, int Y) +{ + if (!HS) return; + TImage* Image=(TImage*)Sender; + int lp0=ceil(SF.lp[0]), lpp=ceil(SF.lp[SF.P-1]); + + if (Shift.Contains(ssLeft)) + { + if (CurrentFB<0 && CurrentPartial<0) return; + int fh=Image->Canvas->TextHeight("0"); + int YY=Image->Height-Bdw-Bdwc-fh; + double dbperpixel=dbrange/YY; + int dY=Y-StartDrag; + if (dY==0) return; + StartDrag=Y; + double ddb=-dY*dbperpixel; + + if (Sender==AImage3 && CurrentFB>=0) + { + double db=20*log10e*SF.avgh[CurrentPartial]; + db+=ddb; + AnsiString S; S.sprintf("Filter control %d: %.2fdB", CurrentFB, db); + Label7->Caption=S; + ddb=ddb/(20*log10e); + + for (int l=0; l<lpp-lp0; l++) SF.h[l][CurrentFB]+=ddb; + SF.avgh[CurrentFB]+=ddb; + + if (Shift.Contains(ssShift)) + for (int k=CurrentFB+1; k<=SF.K+1; k++) {for (int l=0; l<lpp-lp0; l++) SF.h[l][k]+=ddb; SF.avgh[k]+=ddb;} + if (Shift.Contains(ssCtrl)) {S_F_b(SF, HS->Partials); SaveSF0();} + } + else if (Sender==AImage1 || Sender==AImage3) + { + double db=20*log10e*SF.avgb[CurrentPartial]; + db+=ddb; + AnsiString S; S.sprintf("Partial %d: %.2fdB", CurrentPartial+1, db); + Label7->Caption=S; + ddb=ddb/(20*log10e); + + for (int l=0; l<lpp-lp0; l++) SF.b[l][CurrentPartial]+=ddb; + SF.avgb[CurrentPartial]+=ddb; + + int dm=0; for (int i=0x31; i<0x3a; i++) if (GetKeyState(i)<0) dm+=(i-0x30); + if (dm==0 && Shift.Contains(ssShift)) dm=CurrentPartial+1; + if (dm>0) for (int m=CurrentPartial+dm; m<SF.M; m+=dm){for (int l=0; l<lpp-lp0; l++) SF.b[l][m]+=ddb; SF.avgb[m]+=ddb;} + } + else if (Sender==AImage2 || Sender==AImage4) + { + if (CurrentFr>=lp0 && CurrentFr<lpp) + { + double db=20*log10e*SF.b[CurrentFr-lp0][CurrentPartial]; + db+=ddb; + AnsiString S; S.sprintf("Partial %d: %.2fdB", CurrentPartial+1, db); + Label8->Caption=S; + ddb=ddb/(20*log10e); + + SF.b[CurrentFr-lp0][CurrentPartial]+=ddb; + } + } + + SynthesizeSF(HS, &SF, WaveView1->SamplesPerSec); + UpdateDisplay(); + } + else + { + Image->Parent->SetFocus(); + + int lSXc, *lSX; + double* LogAS; + if (Sender==AImage1 || Sender==AImage3) lSXc=SXc, lSX=SX, LogAS=SF.avgb, X=Bdw+(X-Bdw)*AImage3->Width/((TImage*)Sender)->Width; + else if (Sender==AImage2 || Sender==AImage4) + { + if (CurrentFr<lp0 || CurrentFr>=lpp) return; + lSXc=SXcyclec, lSX=SXcycle, LogAS=SF.b[CurrentFr-lp0], 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--; + CurrentFB=-1; int lfb=0; while (lfb<=SF.K && X>FX[lfb]) lfb++; + if (Sender==AImage3) + { + if (X>=FX[lfb]-ftrr && X<=FX[lfb]+ftrr && Y>=FY[lfb]-ftrr && Y<=FY[lfb]+ftrr) CurrentFB=lfb; + if (X>=FX[lfb-1]-ftrr && X<=FX[lfb-1]+ftrr && Y>=FY[lfb-1]-ftrr && Y<=FY[lfb-1]+ftrr) + { + if (CurrentFB==-1) CurrentFB=lfb-1; + else if (abs(X-FX[lfb-1])<abs(X-FX[lfb])) CurrentFB=lfb-1; + } + } + + TLabel* Label; AnsiString S; + if (Sender==AImage1 || Sender==AImage3) Label=Label7; + else if (Sender==AImage2 || Sender==AImage4) Label=Label8; + + if (CurrentFB>=0) + { + S.sprintf("Filter control %d: %.2fdB", CurrentFB, 20*log10e*SF.avgh[CurrentFB]); + Label->Font->Color=clWhite; + } + else + { + double dB=20*log10e*LogAS[CurrentPartial]; + S.sprintf("Partial %d: %.2fdB", CurrentPartial+1, dB); + Label->Font->Color=RotateColors[CurrentPartial%10]; + } + Label->Caption=S; + } +} +//--------------------------------------------------------------------------- + + +void __fastcall TSFDemoForm::AImage1MouseDown(TObject *Sender, + TMouseButton Button, TShiftState Shift, int X, int Y) +{ + FirstStartDrag=StartDrag=Y; + FirstStartDragX=StartDragX=X; + if (Sender==Image0 && Shift.Contains(ssShift)) + { + if (CurrentB>=0) //delete current peakmark + { + for (int p=CurrentB; p<SF.P-1; p++) SF.lp[p]=SF.lp[p+1]; + SF.P--; + CurrentB=-1; + UpdateDisplay(1, 0, 0, 0); + analyze2=true; + } + else + { + double newlpb=1.0*X/Image0->Width*SF.L, *newlp=new double[SF.P+3]; + int newb=0; while (newb<SF.P && X>BX1[newb]) newb++; + + if (newb>0) memcpy(newlp, SF.lp, sizeof(double)*newb); + if (newb<SF.P) memcpy(&newlp[newb+1], &SF.lp[newb], sizeof(double)*(SF.P-newb)); + newlp[newb]=newlpb; + CurrentB=newb; + SF.P++; + delete[] SF.lp; SF.lp=newlp; + UpdateDisplay(1, 0, 0, 0); + analyze2=true; + } + } + if ((Sender==AImage2 || Sender==AImage4) && Shift.Contains(ssRight)) + PageControl1->ActivePage=AmpOverSheet; +} +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::AImage1MouseUp(TObject *Sender, + TMouseButton Button, TShiftState Shift, int X, int Y) +{ + if (Sender==Image0 && (CurrentB>=0 && X!=FirstStartDragX || analyze2)) + { + //do analysis using the new segmentation + double sps=WaveView1->SamplesPerSec; + double h=WaveView1->SpecOffst; + int FSMode=MethodListBox->ItemIndex; + double FSF=FEdit->Text.ToDouble(); + int FSFScale=FScaleCombo->ItemIndex; + if (FSFScale==0) FSF/=sps; + else FSF/=Amel*log(1+sps/700); + double FStheta=ThetaEdit->Text.ToDouble(); + AnalyzeSF_2(*HS, SF, cyclefrs, cyclefs, sps, &cyclefrcount, FSMode, FSF, FSFScale, FStheta); + SaveSF0(); + SaveSF(); + S_U(true); + UpdateDisplay(); + analyze2=false; + } + else if (Y!=FirstStartDrag) + { + SaveSF(); + S_U(); + UpdateDisplay(); + } +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::PageControl1Change(TObject *Sender) +{ + if (PageControl1->ActivePage==AmpOverSheet) + { + SFCheck->Parent=Panel12; + MACheck->Parent=Panel12; + } + else + { + SFCheck->Parent=Panel14; + MACheck->Parent=Panel14; + } + UpdateDisplay(); +} +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::SaveButtonClick(TObject *Sender) +{ + int k=1; AnsiString FileName="1.sf"; + while (TmplListBox->Items->IndexOf(FileName)>=0) {k++; FileName=AnsiString(k)+".sf";} + if (GetKeyState(VK_SHIFT)<0) + { + AnsiString oldpath=SaveDialog1->InitialDir; + SaveDialog1->InitialDir=ExtractFileDir(Application->ExeName); + if (SaveDialog1->Execute()) FileName=SaveDialog1->FileName; + SaveDialog1->InitialDir=oldpath; + } + else FileName=ExtractFilePath(Application->ExeName)+FileName; + SF.SaveToFile(FileName.c_str()); + TmplListBox->Items->Add(ExtractFileName(FileName)); +} +//--------------------------------------------------------------------------- + + + //calculates increment dh on sf1.avgh so that it becomes sf2.avgh + // dh should have the same dimenstions as sf1.avgh + void* CompareFilters(double* dh, TSF& sf1, TSF& sf2) + { + int K1=sf1.K, FScale1=sf1.FScaleMode; + double F1=sf1.F, Fs=sf1.Fs; + for (int k=0; k<K1+2; k++) + { + double h1=sf1.avgh[k], f=F1*k; + if (FScale1) + { + double fmel=Amel*log(1+Fs/700)*f; + double fhz=700*(exp(fmel/Amel)-1); + f=fhz/Fs; + } + double h2=sf2.LogAF(f); + dh[k]=h2-h1; + } + } + double* CompareFilters(TSF& sf1, TSF& sf2) + { + double* dh=new double[sf1.K+2]; + CompareFilters(dh, sf1, sf2); + return dh; + } + +void __fastcall TSFDemoForm::UseButtonClick(TObject *Sender) +{ + if (TmplListBox->ItemIndex<0) return; + AnsiString FileName=ExtractFilePath(Application->ExeName)+TmplListBox->Items->Strings[TmplListBox->ItemIndex]; + if (!FileExists(FileName)) return; + double dbamp=TrackBar1->Position/10.0; + double dbgain=TrackBar2->Position/5.0-10; + + TSF* sf=new TSF; + FILE* file; + if (file=fopen(FileName.c_str(), "rb")) + { + sf->LoadFromFileHandle(file); + fclose(file); + } + if (dbgain!=0) sf->ShiftFilterByDB(dbgain); + int L=ceil(SF.lp[SF.P-1])-ceil(SF.lp[0]), K=SF.K, M=SF.M; + double avgbend=SF0.avgb[sf->M-1]; + switch (ComboBox1->ItemIndex) + { + case 0: //use source + { + for (int l=0; l<L; l++) memcpy(SF.h[l], SF0.h[l], sizeof(double)*(K+2)); + memcpy (SF.avgh, SF0.avgh, sizeof(double)*(K+2)); + for (int m=0; m<M; m++) + { + double sfb=(m<sf->M)?sf->avgb[m]:(sf->avgb[sf->M-1]-avgbend+SF0.avgb[m]); + double db=(sfb-SF0.avgb[m])*dbamp; + for (int l=0; l<L; l++) SF.b[l][m]=SF0.b[l][m]+db; + SF.avgb[m]=SF0.avgb[m]+db; + } + break; + } + case 1: //use filter + { + for (int l=0; l<L; l++) memcpy(SF.b[l], SF0.b[l], sizeof(double)*M); + memcpy(SF.avgb, SF0.avgb, sizeof(double)*M); + double* dh=CompareFilters(SF0, *sf); + + for (int k=0; k<K+2; k++){double dhk=dh[k]*dbamp; + for (int l=0; l<L; l++) SF.h[l][k]=SF0.h[l][k]+dhk; SF.avgh[k]=SF0.avgh[k]+dhk;} + delete[] dh; + break; + } + case 2: //source and filter + { + for (int m=0; m<M; m++) + { + double sfb=(m<sf->M)?sf->avgb[m]:(sf->avgb[sf->M-1]-avgbend+SF0.avgb[m]); + double db=(sfb-SF0.avgb[m])*dbamp; + for (int l=0; l<L; l++) SF.b[l][m]=SF0.b[l][m]+db; SF.avgb[m]=SF0.avgb[m]+db; + } + double* dh=CompareFilters(SF0, *sf); + + for (int k=0; k<K+2; k++){double dhk=dh[k]*dbamp; + for (int l=0; l<L; l++) SF.h[l][k]=SF0.h[l][k]+dhk; SF.avgh[k]=SF0.avgh[k]+dhk;} + delete[] dh; + break; + } + case 3: //towards + { + for (int m=0; m<M; m++) + { + double sfb=(m<sf->M)?sf->avgb[m]:(sf->avgb[sf->M-1]-avgbend+SF0.avgb[m]); + double db=(sfb-SF0.avgb[m])*dbamp, avgb=0; + for (int l=0; l<L; l++){SF.b[l][m]=SF0.b[l][m]+db*l/(L-1); avgb+=SF.b[l][m];} + SF.avgb[m]=avgb/L; + } + double *dh=CompareFilters(SF0, *sf); + + for (int k=0; k<K+2; k++){double dhk=dh[k]*dbamp, avgh=0; + for (int l=0; l<L; l++){SF.h[l][k]=SF0.h[l][k]+dhk*l/(L-1); avgh+=SF.h[l][k];} SF.avgh[k]=avgh/L;} + delete[] dh; + break; + } + case 4: //towards and back + { + for (int m=0; m<M; m++) + { + double sfb=(m<sf->M)?sf->avgb[m]:(sf->avgb[sf->M-1]-avgbend+SF0.avgb[m]); + double db=(sfb-SF0.avgb[m])*dbamp, avgb=0; + for (int l=0; l<L; l++){SF.b[l][m]=SF0.b[l][m]+db*(1-fabs(l*2.0/(L-1)-1)); avgb+=SF.b[l][m];} + SF.avgb[m]=avgb/L; + } + double *dh=CompareFilters(SF0, *sf); + + for (int k=0; k<K+2; k++){double dhk=dh[k]*dbamp, avgh=0; + for (int l=0; l<L; l++){SF.h[l][k]=SF0.h[l][k]+dhk*(1-fabs(l*2.0/(L-1)-1)); avgh+=SF.h[l][k];} SF.avgh[k]=avgh/L;} + delete[] dh; + break; + } + break; + } + + delete sf; + + SynthesizeSF(HS, &SF, WaveView1->SamplesPerSec); + SaveSF(); + S_U(true); + UpdateDisplay(); +} +//--------------------------------------------------------------------------- +//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 TSFDemoForm::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 TSFDemoForm::SUTerminate(TObject* Sender) +{ + TSUThread* CurrentThread=(TSUThread*)Sender; + if (CurrentThread==SUThread) + { + double* xrec=CurrentThread->xrec; + if (xrec) + { + int dst=CurrentThread->dst, den=CurrentThread->den, bps=WaveAudio2->BitsPerSample; + if (dst<0) dst=0; + double max=1<<(bps-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<<(bps-1)) + { + max=(1<<(bps-1))/max; + for (int i=dst; i<den; i++) xrec[i]*=max; + } + void* data=new char[(den-dst)*bps]; + DoubleToInt(data, bps>>3, 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 TSFDemoForm::Panel4Resize(TObject *Sender) +{ + ForceUpdate=true; +} +//--------------------------------------------------------------------------- + + +void __fastcall TSFDemoForm::Panel1Resize(TObject *Sender) +{ + Panel2->Height=(Panel1->Height-Splitter1->Height)/2; +} +//--------------------------------------------------------------------------- + + + +void __fastcall TSFDemoForm::AmpCycleSheetResize(TObject *Sender) +{ + UpdateDisplay(); +} +//--------------------------------------------------------------------------- + + +void __fastcall TSFDemoForm::MethodListBoxClick(TObject *Sender) +{ + Reset(); +} +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::AmpOverSheetResize(TObject *Sender) +{ + UpdateDisplay(); +} +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::Splitter7Moved(TObject *Sender) +{ + if (Sender==Splitter7) AImage4->Parent->Parent->Height=AImage3->Parent->Parent->Height; + else if (Sender==Splitter8) AImage3->Parent->Parent->Height=AImage4->Parent->Parent->Height; + UpdateDisplay(); +} +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::AImage3DblClick(TObject *Sender) +{ + Graphics::TBitmap* bmp=new Graphics::TBitmap; + bmp->Width=800; bmp->Height=600; TRect Rect=TRect(0, 0, 800, 600); + + + double fst=0, fen=fmaxEdit->Text.ToDouble(); + + double updb=updbEdit->Text.ToDouble(), downdb=downdbEdit->Text.ToDouble(), dbshift=SFPicShift->Text.ToDouble(); + dbrange=updb-downdb; + + if (Sender==AImage3) DrawSF(bmp, Rect, fst, fen, updb+dbshift, downdb=dbshift, SF.F0Overall, SF.M, SF.avgb, SF.F, SF.K, SF.avgh, SF.FScaleMode, 44100, SX, 0, 0, 0, 36, FShiftdBEdit->Text.ToInt()); + else if (Sender==AImage1) + { + int lp0=ceil(SF.lp[0]), lpp=ceil(SF.lp[SF.P-1]); + if (!SFCheck->Checked) + DrawAF(bmp, Rect, fst, fen, updb, downdb, SF.M, lp0, lpp, HS->Partials, MACheck->Checked, false, WaveView1->SamplesPerSec, 36); + else + DrawSFr(bmp, Rect, fst, fen, updb, downdb, SF.M, lp0, lpp, HS->Partials, useA0?SF.logA0:SF.logA0C, SF.avgb, SF.F, SF.K, SF.avgh, SF.FScaleMode, SF.Fs, 36); + } + Form1->SaveDialog1->FileName="waveview.waveview1.bmp"; + Form1->SaveDialog1->FilterIndex=2; + if (Form1->SaveDialog1->Execute()) bmp->SaveToFile(Form1->SaveDialog1->FileName); + delete bmp; +} +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::Panel7Resize(TObject *Sender) +{ + UpdateDisplay(1, 0, 0, 0); +} +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::Panel8Resize(TObject *Sender) +{ + UpdateDisplay(0, 1, 0, 0); +} +//--------------------------------------------------------------------------- + + +void __fastcall TSFDemoForm::CheckBox1Click(TObject *Sender) +{ + WaveView2->LoopPlay=CheckBox1->Checked; +} +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::fmaxEditKeyPress(TObject *Sender, char &Key) +{ + if (Key==VK_RETURN) UpdateDisplay(0, 0, 0, 1); +} +//--------------------------------------------------------------------------- + +void __fastcall TSFDemoForm::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]; + for (int n=0; n<N; n++) data[n]=data1[n]-datain[n]+data2[n]; + Form1->PostWaveViewData(data, Channel, StartPos, StartPos+N, Form1->FadeInCheck->Checked, Form1->FadeInCombo->Text.ToInt()); + delete[] data; + } +} +//--------------------------------------------------------------------------- + + + +