Mercurial > hg > x
view hs.cpp @ 1:6422640a802f
first upload
author | Wen X <xue.wen@elec.qmul.ac.uk> |
---|---|
date | Tue, 05 Oct 2010 10:45:57 +0100 |
parents | |
children | fc19d45615d1 |
line wrap: on
line source
//--------------------------------------------------------------------------- #include <math.h> #include "hs.h" #include "align8.h" #include "arrayalloc.h" #include "procedures.h" #include "SinEst.h" #include "SinSyn.h" #include "splines.h" #include "xcomplex.h" #ifdef I #undef I #endif //--------------------------------------------------------------------------- //stiffcandid methods /* method stiffcandid::stiffcandid: creates a harmonic atom with minimal info, i.e. fundamantal frequency between 0 and Nyqvist frequency, stiffness coefficient between 0 and STIFF_B_MAX. In: Wid: DFT size (frame size, frequency resolution) */ stiffcandid::stiffcandid(int Wid) { F=new double[6]; G=&F[3]; double Fm=Wid*Wid/4; //NyqvistF^2 F[0]=0, G[0]=0; F[1]=Fm, G[1]=STIFF_B_MAX*Fm; F[2]=Fm, G[2]=0; N=3; P=0; s=0; p=NULL; f=NULL; }//stiffcandid /* method stiffcandid::stiffcandid: creates a harmonic atom with a frequency range for the $ap-th partial, without actually taking this partial as "known". In; Wid: DFT size ap: partial index af, errf: centre and half-width of the frequency range of the ap-th partial, in bins */ stiffcandid::stiffcandid(int Wid, int ap, double af, double errf) { F=new double[10]; G=&F[5]; double Fm=Wid*Wid/4; F[0]=0, G[0]=0; F[1]=Fm, G[1]=STIFF_B_MAX*Fm; F[2]=Fm, G[2]=0; N=3; P=0; s=0; p=NULL; f=NULL; double k=ap*ap-1; double g1=(af-errf)/ap, g2=(af+errf)/ap; if (g1<0) g1=0; g1=g1*g1, g2=g2*g2; //apply constraints F+kG-g2<0 and -F-kG+g1<0 cutcvpoly(N, F, G, 1, k, -g2); cutcvpoly(N, F, G, -1, -k, g1); }//stiffcandid /* method stiffcandid::stiffcandid: creates a harmonic atom from one atom. In; Wid: DFT size ap: partial index of the atom. af, errf: centre and half-width of the frequency range of the atom, in bins. ds: contribution to candidate score from this atom. */ stiffcandid::stiffcandid(int Wid, int ap, double af, double errf, double ds) { //the starting polygon implements the trivial constraints 0<f0<NyqvistF, // 0<B<Bmax. in terms of F and G, 0<F<NF^2, 0<G<FBmax. this is a triangle // with vertices (0, 0), (NF^2, Bmax*NF^2), (NF^2, 0) F=new double[12]; G=&F[5], f=&F[10], p=(int*)&F[11]; double Fm=Wid*Wid/4; //NyqvistF^2 F[0]=0, G[0]=0; F[1]=Fm, G[1]=STIFF_B_MAX*Fm; F[2]=Fm, G[2]=0; N=3; P=1; p[0]=ap; f[0]=af; s=ds; double k=ap*ap-1; double g1=(af-errf)/ap, g2=(af+errf)/ap; if (g1<0) g1=0; g1=g1*g1, g2=g2*g2; //apply constraints F+kG-g2<0 and -F-kG+g1<0 cutcvpoly(N, F, G, 1, k, -g2); cutcvpoly(N, F, G, -1, -k, g1); }//stiffcandid /* method stiffcandid::stiffcandid: creates a harmonic atom by adding a new partial to an existing harmonic atom. In; prev: initial harmonic atom ap: partial index of new atom af, errf: centre and half-width of the frequency range of the new atom, in bins. ds: contribution to candidate score from this atom. */ stiffcandid::stiffcandid(stiffcandid* prev, int ap, double af, double errf, double ds) { N=prev->N, P=prev->P, s=prev->s; int Np2=N+2, Pp1=P+1; F=new double[(Np2+Pp1)*2]; G=&F[Np2]; f=&G[Np2]; p=(int*)&f[Pp1]; memcpy(F, prev->F, sizeof(double)*N); memcpy(G, prev->G, sizeof(double)*N); if (P>0) { memcpy(f, prev->f, sizeof(double)*P); memcpy(p, prev->p, sizeof(int)*P); } //see if partial ap is already involved int i=0; while (i<P && p[i]!=ap) i++; //if it is not: if (i>=P) { p[P]=ap; f[P]=af; s+=ds; P++; double k=ap*ap-1; double g1=(af-errf)/ap, g2=(af+errf)/ap; if (g1<0) g1=0; g1=g1*g1, g2=g2*g2; //apply constraints F+kG-g2<0 and -F-kG+g1<0 cutcvpoly(N, F, G, 1, k, -g2); cutcvpoly(N, F, G, -1, -k, g1); } //otherwise, the new candid is simply a copy of prev }//stiffcandid //method stiffcandid::~stiffcandid: destructor stiffcandid::~stiffcandid() { delete[] F; }//~stiffcandid //--------------------------------------------------------------------------- //THS methods //method THS::THS: default constructor THS::THS() { memset(this, 0, sizeof(THS)); memset(BufM, 0, sizeof(void*)*128); }//THS /* method THS::THS: creates an HS with given number of partials and frames. In: aM, aFr: number of partials and frames */ THS::THS(int aM, int aFr) { memset(this, 0, sizeof(THS)); M=aM; Fr=aFr; Allocate2(atom, M, Fr, Partials); memset(Partials[0], 0, sizeof(atom)*M*Fr); memset(BufM, 0, sizeof(void*)*128); }//THS /* method THS::THS: creates a duplicate HS. This does not duplicate contents of BufM. In: HS: the HS to duplicate */ THS::THS(THS* HS) { memcpy(this, HS, sizeof(THS)); Allocate2(atom, M, Fr, Partials); for (int m=0; m<M; m++) memcpy(Partials[m], HS->Partials[m], sizeof(atom)*Fr); Allocate2(double, M, st_count, startamp); if (st_count) for (int m=0; m<M; m++) memcpy(startamp[m], HS->startamp[m], sizeof(double)*st_count); memset(BufM, 0, sizeof(void*)*128); }//THS /* method THS::THS: create a new HS which is a trunction of an existing HS. In: HS: the exinting HS from which a sub-HS is to be created start, end: truncation points. Atoms of *HS beyond these points are discarded. */ THS::THS(THS* HS, double start, double end) { memset(this, 0, sizeof(THS)); M=HS->M; Fr=HS->Fr; isconstf=HS->isconstf; int frst=0, fren=Fr; while (HS->Partials[0][frst].t<start && frst<fren) frst++; while (HS->Partials[0][fren-1].t>end && fren>frst) fren--; Fr=fren-frst; Allocate2(atom, M, Fr, Partials); for (int m=0; m<M; m++) { memcpy(Partials[m], &HS->Partials[m][frst], sizeof(atom)*Fr); for (int fr=0; fr<Fr; fr++) Partials[m][fr].t-=start; } if (frst>0){DeAlloc2(startamp); startamp=0;} }//THS //method THS::~THS: default descructor. THS::~THS() { DeAlloc2(Partials); DeAlloc2(startamp); ClearBufM(); }//~THS /* method THS::ClearBufM: free buffers pointed to by members of BufM. */ void THS::ClearBufM() { for (int m=0; m<128; m++) delete[] BufM[m]; memset(BufM, 0, sizeof(void*)*128); }//ClearBufM /* method THS::EndPos: the end position of an HS. Returns the time of the last frame of the first partial. */ int THS::EndPos() { return Partials[0][Fr-1].t; }//EndPos /* method THS::EndPosEx: the extended end position of an HS. Returns the time later than the last frame of the first partial by the interval between the first and second frames of that partial. */ int THS::EndPosEx() { return Partials[0][Fr-1].t+Partials[0][1].t-Partials[0][0].t; }//EndPosEx /* method THS::ReadAtomFromStream: reads an atom from a stream. Returns the number of bytes read, or 0 on failure. The stream pointer is shifted by this value. */ int THS::ReadAtomFromStream(TStream* Stream, atom* atm) { int StartPosition=Stream->Position, atomlen; char s[4]; if (Stream->Read(s, 4)!=4) goto ret0; if (strcmp(s, "ATM")) goto ret0; if (Stream->Read(&atomlen, sizeof(int))!=sizeof(int)) goto ret0; if (Stream->Read(atm, atomlen)!=atomlen) goto ret0; return atomlen+4+sizeof(int); ret0: Stream->Position=StartPosition; return 0; }//ReadAtomFromStream /* method THS::ReadFromStream: reads an HS from a stream. Returns the number of bytes read, or 0 on failure. The stream pointer is shifted by this value. */ int THS::ReadFromStream(TStream* Stream) { int StartPosition=Stream->Position, lenevt; char s[4]; if (Stream->Read(s, 4)!=4) goto ret0; if (strcmp(s, "EVT")) goto ret0; if (Stream->Read(&lenevt, sizeof(int))!=sizeof(int)) goto ret0; if (!ReadHdrFromStream(Stream)) goto ret0; Resize(M, Fr, false, true); for (int m=0; m<M; m++) for (int fr=0; fr<Fr; fr++) if (!ReadAtomFromStream(Stream, &Partials[m][fr])) goto ret0; lenevt=lenevt+4+sizeof(int); if (lenevt!=Stream->Position-StartPosition) goto ret0; return lenevt; ret0: Stream->Position=StartPosition; return 0; }//ReadFromStream /* method THS::ReadHdrFromStream: reads header from a stream into this HS. Returns the number of bytes read, or 0 on failure. The stream pointer is shifted by this value. */ int THS::ReadHdrFromStream(TStream* Stream) { int StartPosition=Stream->Position, hdrlen, tags; char s[4]; if (Stream->Read(s, 4)!=4) goto ret0; if (strcmp(s, "HDR")) goto ret0; if (Stream->Read(&hdrlen, sizeof(int))!=sizeof(int)) goto ret0; if (Stream->Read(&Channel, sizeof(int))!=sizeof(int)) goto ret0; if (Stream->Read(&M, sizeof(int))!=sizeof(int)) goto ret0; if (Stream->Read(&Fr, sizeof(int))!=sizeof(int)) goto ret0; if (Stream->Read(&tags, sizeof(int))!=sizeof(int)) goto ret0; isconstf=tags&HS_CONSTF; hdrlen=hdrlen+4+sizeof(int); //expected read count if (hdrlen!=Stream->Position-StartPosition) goto ret0; return hdrlen; ret0: Stream->Position=StartPosition; return 0; }//ReadHdrFromStream /* method THS::Resize: change the number of partials or frames of an HS structure. Unless forcealloc is set to true, this allocates new buffers to host HS data only when the new dimensions are larger than current ones. In: aM, aFr: new number of partials and frames copydata: specifies if HS data is to be copied to new buffers. forcealloc: specifies if new buffers are to be allocated in all cases. */ void THS::Resize(int aM, int aFr, bool copydata, bool forcealloc) { if (forcealloc || M<aM || Fr<aFr) { atom** Allocate2(atom, aM, aFr, NewPartials); if (copydata) { int minM=(M<aM)?M:aM, minFr=(Fr<aFr)?Fr:aFr; for (int m=0; m<minM; m++) memcpy(NewPartials[m], Partials[m], sizeof(atom)*minFr); } DeAlloc2(Partials); Partials=NewPartials; } if (forcealloc || M<aM) { double** Allocate2(double, aM, st_count, newstartamp); if (copydata) { for (int m=0; m<M; m++) memcpy(newstartamp[m], startamp[m], sizeof(double)*st_count); } DeAlloc2(startamp); startamp=newstartamp; } M=aM, Fr=aFr; }//Resize /* method THS::StartPos: the start position of an HS. Returns the time of the first frame of the first partial. */ int THS::StartPos() { return Partials[0][0].t; }//StartPos /* method THS::StdOffst: standard hop size of an HS. Returns the interval between the timing of the first and second frames of the first partial, which is the "standard" hop size if all measurement points are uniformly positioned. */ int THS::StdOffst() { return Partials[0][1].t-Partials[0][0].t; }//StdOffst /* method THS::WriteAtomToStream: writes an atom to a stream. An atom is stored in a stream as an RIFF chunk identified by "ATM\0". Returns the number of bytes written. The stream pointer is shifted by this value. */ int THS::WriteAtomToStream(TStream* Stream, atom* atm) { Stream->Write((void*)"ATM", 4); int atomlen=sizeof(atom); Stream->Write(&atomlen, sizeof(int)); atomlen=Stream->Write(atm, atomlen); return atomlen+4+sizeof(int); }//WriteAtomToStream /* method THS::WriteHdrToStream: writes header to a stream. HS header is stored in a stream as an RIFF chunk, identified by "HDR\0". Returns the number of bytes written. The stream pointer is shifted by this value. */ int THS::WriteHdrToStream(TStream* Stream) { Stream->Write((void*)"HDR", 4); int hdrlen=0, tags=0; if (isconstf) tags=tags|HS_CONSTF; Stream->Write(&hdrlen, sizeof(int)); Stream->Write(&Channel, sizeof(int)); hdrlen+=sizeof(int); Stream->Write(&M, sizeof(int)); hdrlen+=sizeof(int); Stream->Write(&Fr, sizeof(int)); hdrlen+=sizeof(int); Stream->Write(&tags, sizeof(int)); hdrlen+=sizeof(int); Stream->Seek(-hdrlen-sizeof(int), soFromCurrent); Stream->Write(&hdrlen, sizeof(int)); Stream->Seek(hdrlen, soFromCurrent); return hdrlen+4+sizeof(int); }//WriteHdrToStream /* method THS::WriteToStream: write an HS to a stream. An HS is stored in a stream as an RIFF chunk identified by "EVT\0". Its chunk data contains an HS header chunk followed by M*Fr atom chunks, in rising order of m then fr. Returns the number of bytes written. The stream pointer is shifted by this value. */ int THS::WriteToStream(TStream* Stream) { Stream->Write((void*)"EVT", 4); int lenevt=0; Stream->Write(&lenevt, sizeof(int)); lenevt+=WriteHdrToStream(Stream); for (int m=0; m<M; m++) for (int fr=0; fr<Fr; fr++) lenevt+=WriteAtomToStream(Stream, &Partials[m][fr]); Stream->Seek(-lenevt-sizeof(int), soFromCurrent); Stream->Write(&lenevt, sizeof(int)); Stream->Seek(lenevt, soFromCurrent); return lenevt+4+sizeof(int); }//WriteToStream //--------------------------------------------------------------------------- //TPolygon methods /* method TPolygon::TPolygon: create an empty polygon with a storage capacity In: cap: number of vertices to allocate memory for. */ TPolygon::TPolygon(int cap) { X=(double*)malloc8(sizeof(double)*cap*2); Y=&X[cap]; }//TPolygon /* method TPolygon::TPolygon: create a duplicate polygon. In: cap: number of vertices to allocate memory for. R: the polygon to duplicate. If cap is smaller than the number of vertices of R, the latter is used for memory allocation. */ TPolygon::TPolygon(int cap, TPolygon* R) { if (cap<R->N) cap=R->N; X=(double*)malloc8(sizeof(double)*cap*2); Y=&X[cap]; N=R->N; memcpy(X, R->X, sizeof(double)*R->N); memcpy(Y, R->Y, sizeof(double)*R->N); }//TPolygon //method TPolygon::~TPolygon: default destructor. TPolygon::~TPolygon() { free8(X); }//~TPolygon //--------------------------------------------------------------------------- //TTempAtom methods /* method TTempAtom::TTempAtom: initializes an empty atom with a fundamental frequency range. In: af, ef: centre and half width of the fundamental frequency maxB: upper bound of stiffness coefficient */ TTempAtom::TTempAtom(double af, double ef, double maxB) { Prev=NULL; pind=f=a=s=acce=0; R=new TPolygon(4); InitializeR(R, af, ef, maxB); }//TTempAtom /* method TTempAtom::TTempAtom: initialize an empty atom with a frequency range for a certain partial. In: apin: partial index af, ef: centre and half width of the fundamental frequency maxB: upper bound of stiffness coefficient */ TTempAtom::TTempAtom(int apin, double af, double ef, double maxB) { Prev=NULL; pind=f=a=s=acce=0; R=new TPolygon(4); InitializeR(R, apin, af, ef, maxB); }//TTempAtom /* method TTempAtom::TTempAtom: initialize an empty atom whose F-G polygon is the extension of AR In: AR: the F-G polygon to extend delf1, delf2: amount of extension, in semitones, of fundamental frequency minf: minimal fundamental frequency: extension will not go below this value */ TTempAtom::TTempAtom(TPolygon* AR, double delf1, double delf2, double minf) { Prev=NULL; pind=f=a=s=acce=0; R=new TPolygon(AR->N+2); R->N=AR->N; memcpy(R->X, AR->X, sizeof(double)*AR->N); memcpy(R->Y, AR->Y, sizeof(double)*AR->N); ExtendR(R, delf1, delf2, minf); }//TTempAtom /* method TTempAtom::TTempAtom: initialize a atom as the only partial. In: apind: partial index af, ef: partial frequency and its error range aa, as: partial amplitude and measurement scale maxB: stiffness coefficient upper bound */ TTempAtom::TTempAtom(int apind, double af, double ef, double aa, double as, double maxB) { Prev=NULL; pind=apind; f=af; a=aa; s=as; acce=aa*aa; R=new TPolygon(4); InitializeR(R, apind, af, ef, maxB); }//TTempAtom /* method TTempAtom::TTempAtom: creates a duplicate atom. R can be duplicated or snatched according to DupR. In: APrev: the atom to duplicate DupR: specifies if the newly created atom is to have its F-G polygon duplicated from that of APrev or to snatch the F-G polygon from APrev (in the latter case APrev losts its polygon). */ TTempAtom::TTempAtom(TTempAtom* APrev, bool DupR) { Prev=APrev; pind=APrev->pind; f=APrev->f; a=APrev->a; s=APrev->s; acce=APrev->acce; TPolygon* PrevR=APrev->R; if (DupR) {//duplicate R R=new TPolygon(PrevR->N); R->N=PrevR->N; memcpy(R->X, PrevR->X, sizeof(double)*PrevR->N); memcpy(R->Y, PrevR->Y, sizeof(double)*PrevR->N); } else {//snatch R from APrev R=APrev->R; APrev->R=0; } }//TTempAtom /* method TTempAtom::TTempAtom:: initializes an atom by adding a new partial to an existing group. In: APrev: the existing atom. apind: partial index af, ef: partial frequency and its error range aa, as: partial amplitude and measurement scale updateR: specifies if the F-G polygon is updated using (af, ef). */ TTempAtom::TTempAtom(TTempAtom* APrev, int apind, double af, double ef, double aa, double as, bool updateR) { Prev=APrev; pind=apind; f=af; a=aa; s=as; acce=APrev->acce+aa*aa; TPolygon* PrevR=APrev->R; R=new TPolygon(PrevR->N+2); // create R R->N=PrevR->N; // memcpy(R->X, PrevR->X, sizeof(double)*PrevR->N); // copy R memcpy(R->Y, PrevR->Y, sizeof(double)*PrevR->N); // if (updateR) CutR(R, apind, af, ef); }//TTempAtom //method TTempAtom::~TTempAtom: default destructor TTempAtom::~TTempAtom() { delete R; }//~TTempAtom //--------------------------------------------------------------------------- //functions /* function areaandcentroid: calculates the area and centroid of a convex polygon. In: x[N], y[N]: x- and y-coordinates of vertices of a polygon Out: A: area (cx, cy): coordinate of the centroid No return value. */ void areaandcentroid(double& A, double& cx, double& cy, int N, double* x, double* y) { if (N==0) {A=0;} else if (N==1) {A=0; cx=x[0], cy=y[0];} else if (N==2) {A=0; cx=(x[0]+x[1])/2, cy=(y[0]+y[1])/2;} A=cx=cy=0; for (int i=0; i<N; i++) { double xi1, yi1; //x[i+1], y[i+1], index modular N if (i+1==N) xi1=x[0], yi1=y[0]; else xi1=x[i+1], yi1=y[i+1]; double tmp=x[i]*yi1-xi1*y[i]; A+=tmp; cx+=(x[i]+xi1)*tmp; cy+=(y[i]+yi1)*tmp; } A/=2; cx=cx/6/A; cy=cy/6/A; }//areaandcentroid /* function AtomsToPartials: sort a list of atoms as a number of partials. It is assumed that the atoms are simply those from a harmonic sinusoid in arbitrary order with uniform timing and partial index clearly marked, so that it is easy to sort them into a list of partials. However, the sorting process does not check for harmonicity, missing or duplicate atoms, uniformity of timing, etc. In: part[k]: a list of atoms offst: interval between adjacent measurement points (hop size) Out: M, Fr, partials[M][Fr]: a list of partials containing the atoms. No return value. partials[][] is allocated anew and must be freed by caller. */ void AtomsToPartials(int k, atom* part, int& M, int& Fr, atom**& partials, int offst) { if (k>0) { int t1, t2, i=0; while (i<k && part[i].f<=0) i++; if (i<k) { t1=part[i].t, t2=part[i].t, M=part[i].pin; i++; while (i<k) { if (part[i].f>0) { if (M<part[i].pin) M=part[i].pin; if (t1>part[i].t) t1=part[i].t; if (t2<part[i].t) t2=part[i].t; } i++; } Fr=(t2-t1)/offst+1; Allocate2(atom, M, Fr, partials); memset(partials[0], 0, sizeof(atom)*M*Fr); for (int i=0; i<k; i++) if (part[i].f>0) { int fr=(part[i].t-t1)/offst; int pp=part[i].pin; partials[pp-1][fr]=part[i]; } } else M=0, Fr=0; } else M=0, Fr=0; }//AtomsToPartials /* function conta: a continuity measure between two set of amplitudes In: a1[N], a2[N]: the amplitudes Return the continuity measure, between 0 and 1 */ double conta(int N, double* a1, double* a2) { double e11=0, e22=0, e12=0; for (int n=0; n<N; n++) { double la1=(a1[n]>0)?a1[n]:0, la2=(a2[n]>0)?a2[n]:0; if (la1>1e10) la1=la2; e11+=la1*la1; e12+=la1*la2; e22+=la2*la2; } return 2*e12/(e11+e22); }//conta /* function cutcvpoly: severs a polygon by a straight line In: x[N], y[N]: x- and y-coordinates of vertices of a polygon, starting from the leftmost (x[0]= min x[n]) point clockwise; in case of two leftmost points, x[0] is the upper one and x[N-1] is the lower one. A, B, C: coefficients of a straight line Ax+By+C=0. protect: specifies what to do if the whole polygon satisfy Ax+By+C>0, true=do noting, false=eliminate. Out: N, x[N], y[N]: the polygon severed by the line, retaining that half for which Ax+By+C<=0. No return value. */ void cutcvpoly(int& N, double* x, double* y, double A, double B, double C, bool protect) { int n=0, ns; while (n<N && A*x[n]+B*y[n]+C<=0) n++; if (n==N) //all points satisfies the constraint, do nothing {} else if (n>0) //points 0, 1, ..., n-1 satisfies the constraint, point n does not { ns=n; n++; while (n<N && A*x[n]+B*y[n]+C>0) n++; //points 0, ..., ns-1 and n, ..., N-1 satisfy the constraint, //points ns, ..., n-1 do not satisfy the constraint, // ns>0, n>ns, however, it's possible that n-1=ns int pts, pte; //indicates (by 0/1) whether or now a new point is to be added for xs/xe double xs, ys, xe, ye; //find intersection of x:y[ns-1:ns] and A:B:C double x1=x[ns-1], x2=x[ns], y1=y[ns-1], y2=y[ns]; double z1=A*x1+B*y1+C, z2=A*x2+B*y2+C; //z1<=0, z2>0 if (z1==0) pts=0; //as point ns-1 IS point s else { pts=1, xs=(x1*z2-x2*z1)/(z2-z1), ys=(y1*z2-y2*z1)/(z2-z1); } //find intersection of x:y[n-1:n] and A:B:C x1=x[n-1], y1=y[n-1]; if (n==N) x2=x[0], y2=y[0]; else x2=x[n], y2=y[n]; z1=A*x1+B*y1+C, z2=A*x2+B*y2+C; //z1>0, z2<=0 if (z2==0) pte=0; //as point n is point e else { pte=1, xe=(x1*z2-x2*z1)/(z2-z1), ye=(y1*z2-y2*z1)/(z2-z1); } //the new polygon is formed by points 0, 1, ..., ns-1, (s), (e), n, ..., N-1 memmove(&x[ns+pts+pte], &x[n], sizeof(double)*(N-n)); memmove(&y[ns+pts+pte], &y[n], sizeof(double)*(N-n)); if (pts) x[ns]=xs, y[ns]=ys; if (pte) x[ns+pts]=xe, y[ns+pts]=ye; N=ns+pts+pte+N-n; } else //n=0, point 0 does not satisfy the constraint { n++; while (n<N && A*x[n]+B*y[n]+C>0) n++; if (n==N) //no point satisfies the constraint { if (!protect) N=0; } else { //points 0, 1, ..., n-1 do not satisfy the constraint, point n does ns=n; n++; while (n<N && A*x[n]+B*y[n]+C<=0) n++; //points 0, ..., ns-1 and n, ..., N-1 do not satisfy constraint, //points ns, ..., n-1 satisfy the constraint // ns>0, n>ns, however ns can be equal to n-1 int pts, pte; //indicates (by 0/1) whether or now a new point is to be added for xs/xe double xs, ys, xe, ye; //find intersection of x:y[ns-1:ns] and A:B:C double x1=x[ns-1], x2=x[ns], y1=y[ns-1], y2=y[ns]; double z1=A*x1+B*y1+C, z2=A*x2+B*y2+C; //z1>0, z2<=0 if (z2==0) pts=0; //as point ns IS point s else pts=1; xs=(x1*z2-x2*z1)/(z2-z1), ys=(y1*z2-y2*z1)/(z2-z1); //find intersection of x:y[n-1:n] and A:B:C x1=x[n-1], y1=y[n-1]; if (n==N) x2=x[0], y2=y[0]; else x2=x[n], y2=y[n]; z1=A*x1+B*y1+C, z2=A*x2+B*y2+C; //z1<=0, z2>0 if (z1==0) pte=0; //as point n-1 is point e else pte=1; xe=(x1*z2-x2*z1)/(z2-z1), ye=(y1*z2-y2*z1)/(z2-z1); //the new polygon is formed of points (s), ns, ..., n-1, (e) if (xs<=xe) { //the point sequence comes as (s), ns, ..., n-1, (e) memmove(&x[pts], &x[ns], sizeof(double)*(n-ns)); memmove(&y[pts], &y[ns], sizeof(double)*(n-ns)); if (pts) x[0]=xs, y[0]=ys; N=n-ns+pts+pte; if (pte) x[N-1]=xe, y[N-1]=ye; } else //xe<xs { //the sequence comes as e, (s), ns, ..., n-2 if pte=0 (notice point e<->n-1) //or e, (s), ns, ..., n-1 if pte=1 if (pte==0) { memmove(&x[pts+1], &x[ns], sizeof(double)*(n-ns-1)); memmove(&y[pts+1], &y[ns], sizeof(double)*(n-ns-1)); if (pts) x[1]=xs, y[1]=ys; } else { memmove(&x[pts+1], &x[ns], sizeof(double)*(n-ns)); memmove(&y[pts+1], &y[ns], sizeof(double)*(n-ns)); if (pts) x[1]=xs, y[1]=ys; } x[0]=xe, y[0]=ye; N=n-ns+pts+pte; } } } }//cutcvpoly /* funciton CutR: update an F-G polygon with an new partial In: R: F-G polygon apind: partial index af, ef: partial frequency and error bound protect: specifies what to do if the new partial has no intersection with R, true=do noting, false=eliminate R Out: R: the updated F-G polygon No return value. */ void CutR(TPolygon* R, int apind, double af, double ef, bool protect) { double k=apind*apind-1; double g1=(af-ef)/apind, g2=(af+ef)/apind; if (g1<0) g1=0; g1=g1*g1, g2=g2*g2; //apply constraints F+kG-g2<0 and -F-kG+g1<0 cutcvpoly(R->N, R->X, R->Y, 1, k, -g2, protect); // update R cutcvpoly(R->N, R->X, R->Y, -1, -k, g1, protect); // }//CutR /* function DeleteByHalf: reduces a list of stiffcandid objects by half, retaining those with higher s and delete the others, freeing their memory. In: cands[pcs]: the list of stiffcandid objects Out: cands[return value]: the list of retained ones Returns the size of the new list. */ int DeleteByHalf(stiffcandid** cands, int pcs) { int newpcs=pcs/2; double* tmp=new double[newpcs]; memset(tmp, 0, sizeof(double)*newpcs); for (int j=0; j<pcs; j++) InsertDec(cands[j]->s, tmp, newpcs); int k=0; for (int j=0; j<pcs; j++) { if (cands[j]->s>=tmp[newpcs-1]) cands[k++]=cands[j]; else delete cands[j]; } return k; }//DeleteByHalf /* function DeleteByHalf: reduces a list of TTempAtom objects by half, retaining those with higher s and delete the others, freeing their memory. In: cands[pcs]: the list of TTempAtom objects Out: cands[return value]: the list of retained ones Returns the size of the new list. */ int DeleteByHalf(TTempAtom** cands, int pcs) { int newpcs=pcs/2; double* tmp=new double[newpcs]; memset(tmp, 0, sizeof(double)*newpcs); for (int j=0; j<pcs; j++) InsertDec(cands[j]->s, tmp, newpcs); int k=0; for (int j=0; j<pcs; j++) { if (cands[j]->s>=tmp[newpcs-1]) cands[k++]=cands[j]; else delete cands[j]; } delete[] tmp; return k; }//DeleteByHalf /* function ds0: atom score 0 - energy In: a: atom amplitude Returns a^2, or s[0]+a^2 is s is not NULL, as atom score. */ double ds0(double a, void* s) { if (s) return *(double*)s+a*a; else return a*a; }//ds0 /* function ds2: atom score 1 - cross-correlation coefficient with another HA, e.g. from previous frame In: a: atom amplitude params: pointer to dsprams1 structure supplying other inputs. Out: cross-correlation coefficient as atom score. Returns the cross-correlation coefficient. */ double ds1(double a, void* params) { dsparams1* lparams=(dsparams1*)params; double hs=lparams->lastene+lparams->currentacce, hsaa=hs+a*a; if (lparams->p<=lparams->lastP) return (lparams->s*hs+a*lparams->lastvfp[lparams->p-1])/hsaa; else return (lparams->s*hs+a*a)/hsaa; }//ds1 /* function ExBStiff: finds the minimal and maximal values of stiffness coefficient B given a F-G polygon In: F[N], G[N]: vertices of a F-G polygon Out: Bmin, Bmax: minimal and maximal values of the stiffness coefficient No reutrn value. */ void ExBStiff(double& Bmin, double& Bmax, int N, double* F, double* G) { Bmax=G[0]/F[0]; Bmin=Bmax; for (int i=1; i<N; i++) { double vi=G[i]/F[i]; if (Bmin>vi) Bmin=vi; else if (Bmax<vi) Bmax=vi; } }//ExBStiff /* function ExFmStiff: finds the minimal and maximal frequecies of partial m given a F-G polygon In: F[N], G[N]: vertices of a F-G polygon m: partial index, fundamental=1 Out: Fmin, Fmax: minimal and maximal frequencies of partial m No return value. */ void ExFmStiff(double& Fmin, double& Fmax, int m, int N, double* F, double* G) { int k=m*m-1; double vmax=F[0]+k*G[0]; double vmin=vmax; for (int i=1; i<N; i++) { double vi=F[i]+k*G[i]; if (vmin>vi) vmin=vi; else if (vmax<vi) vmax=vi; } testnn(vmin); Fmin=m*sqrt(vmin); testnn(vmax); Fmax=m*sqrt(vmax); }//ExFmStiff /* function ExtendR: extends a F-G polygon on the f axis (to allow a larger pitch range) In: R: the F-G polygon delp1: amount of extension to the low end, in semitones delpe: amount of extension to the high end, in semitones minf: minimal fundamental frequency Out: R: the extended F-G polygon No return value. */ void ExtendR(TPolygon* R, double delp1, double delp2, double minf) { double mdelf1=pow(2, -delp1/12), mdelf2=pow(2, delp2/12); int N=R->N; double *G=R->Y, *F=R->X, slope, prevslope, f, ftmp; if (N==0) {} else if (N==1) { R->N=2; slope=G[0]/F[0]; testnn(F[0]); f=sqrt(F[0]); ftmp=f*mdelf2; F[1]=ftmp*ftmp; ftmp=f*mdelf1; if (ftmp<minf) ftmp=minf; F[0]=ftmp*ftmp; G[0]=F[0]*slope; G[1]=F[1]*slope; } else if (N==2) { prevslope=G[0]/F[0]; slope=G[1]/F[1]; if (fabs((slope-prevslope)/prevslope)<1e-10) { testnn(F[0]); ftmp=sqrt(F[0])*mdelf1; if (ftmp<0) ftmp=0; F[0]=ftmp*ftmp; G[0]=F[0]*prevslope; testnn(F[1]); ftmp=sqrt(F[1])*mdelf2; F[1]=ftmp*ftmp; G[1]=F[1]*slope; } else { if (prevslope>slope) { R->N=4; testnn(F[1]); f=sqrt(F[1]); ftmp=f*mdelf1; if (ftmp<minf) ftmp=minf; F[3]=ftmp*ftmp; G[3]=F[3]*slope; ftmp=f*mdelf2; F[2]=ftmp*ftmp; G[2]=F[2]*slope; testnn(F[0]); f=sqrt(F[0]); ftmp=f*mdelf1; if (ftmp<minf) ftmp=minf; F[0]=ftmp*ftmp; G[0]=F[0]*prevslope; ftmp=f*mdelf2; F[1]=ftmp*ftmp; G[1]=F[1]*prevslope; if (F[0]==0 && F[3]==0) R->N=3; } else { R->N=4; testnn(F[1]); f=sqrt(F[1]); ftmp=f*mdelf1; if (ftmp<minf) ftmp=minf; F[1]=ftmp*ftmp; G[1]=F[1]*slope; ftmp=f*mdelf2; F[2]=ftmp*ftmp; G[2]=F[2]*slope; testnn(F[0]); f=sqrt(F[0]); ftmp=f*mdelf1; if (ftmp<minf) ftmp=minf; F[0]=ftmp*ftmp; G[0]=F[0]*prevslope; ftmp=f*mdelf2; F[4]=ftmp*ftmp; G[4]=F[4]*prevslope; if (F[0]==0 && F[1]==0) {R->N=3; F[1]=F[2]; F[2]=F[3]; G[1]=G[2]; G[3]=G[3];} } } } else { //find the minimal and maximal angles of R //maximal slope is taken at Ms if Mt=1, or Ms-1 and Ms if Mt=0 //minimal slope is taken at ms if mt=1, or ms-1 and ms if mt=0 int Ms, ms, Mt=-1, mt=-1; double prevslope=G[0]/F[0], dslope; for (int n=1; n<N; n++) { double slope=G[n]/F[n]; dslope=atan(slope)-atan(prevslope); if (Mt==-1) { if (dslope<-1e-10) Ms=n-1, Mt=1; else if (dslope<1e-10) Ms=n, Mt=0; } else if (mt==-1) { if (dslope>1e-10) ms=n-1, mt=1; else if (dslope>-1e-10) ms=n, mt=0; } prevslope=slope; } if (mt==-1) { slope=G[0]/F[0]; dslope=atan(slope)-atan(prevslope); if (dslope>1e-10) ms=N-1, mt=1; else if (dslope>-1e-10) ms=0, mt=0; } R->N=N+mt+Mt; int newn=R->N-1; if (ms==0 && mt==1) { slope=G[0]/F[0]; testnn(F[0]); ftmp=sqrt(F[0])*mdelf2; F[newn]=ftmp*ftmp; G[newn]=F[newn]*slope; newn--; mt=-1; } else if (ms==0) mt=-1; for (int n=N-1; n>=0; n--) { if (mt!=-1) { slope=G[n]/F[n]; testnn(F[n]); f=sqrt(F[n]); ftmp=f*mdelf1; if (ftmp<minf) ftmp=minf; F[newn]=ftmp*ftmp; G[newn]=F[newn]*slope; newn--; if (ms==n && mt==1) { ftmp=f*mdelf2; F[newn]=ftmp*ftmp; G[newn]=F[newn]*slope; newn--; mt=-1; } else if (ms==n) //mt=0 mt=-1; } else if (Mt!=-1) { slope=G[n]/F[n]; testnn(F[n]); f=sqrt(F[n]); ftmp=f*mdelf2; F[newn]=ftmp*ftmp; G[newn]=F[newn]*slope; newn--; if (Ms==n && Mt==1) { ftmp=f*mdelf1; if (ftmp<minf) ftmp=minf; F[newn]=ftmp*ftmp; G[newn]=F[newn]*slope; newn--; Mt=-1; } else if (Ms==n) Mt=-1; } else { slope=G[n]/F[n]; testnn(F[n]); f=sqrt(F[n]); ftmp=f*mdelf1; if (ftmp<minf) ftmp=minf; F[newn]=ftmp*ftmp; G[newn]=F[newn]*slope; newn--; } } //merge multiple vertices at the origin N=R->N; while (N>0 && F[N-1]==0) N--; R->N=N; int n=1; while (n<N && F[n]==0) n++; if (n!=1) { R->N=N-(n-1); memcpy(&F[1], &F[n], sizeof(double)*(N-n)); memcpy(&G[1], &G[n], sizeof(double)*(N-n)); } } }//ExtendR /* function FGFrom2: solves the following equation system given m[2], f[2], norm[2]. This is interpreted as searching from (F0, G0) down direction (dF, dG) until the first equation is satisfied, where k[*]=m[*]^2-1. m[0](F+k[0]G)^0.5-f[0] m[1](F+k[1]G)^0.5-f[1] ------------------------ = ------------------------ >0 norm[0] norm[1] F=F0+lmd*dF G=G0+lmd*dG In: (F0, G0): search origin (dF, dG): search direction m[2], f[2], norm[2]: coefficients in the first equation Out: F[return value], G[return value]: solutions. Returns the number of solutions for (F, G). */ int FGFrom2(double* F, double* G, double F0, double dF, double G0, double dG, int* m, double* f, double* norm) { double m1=m[0]/norm[0], m2=m[1]/norm[1], f1=f[0]/norm[0], f2=f[1]/norm[1], k1=m[0]*m[0]-1, k2=m[1]*m[1]-1; double A=m1*m1-m2*m2*(dF+k2*dG)/(dF+k1*dG), B=2*m1*(f2-f1), C=(f2-f1)*(f2-f1)-(k1-k2)*(F0*dG-G0*dF)/(dF+k1*dG)*m2*m2; if (A==0) { double x=-C/B; if (x<0) return 0; else if (m1*x-f1<0) return 0; else { double lmd=(x*x-(F0+k1*G0))/(dF+k1*dG); F[0]=F0+lmd*dF; G[0]=G0+lmd*dG; return 1; } } else { double delta=B*B-4*A*C; if (delta<0) return 0; else if (delta==0) { double x=-B/2/A; if (x<0) return 0; else if (m1*x-f1<0) return 0; else { double lmd=(x*x-(F0+k1*G0))/(dF+k1*dG); F[0]=F0+lmd*dF; G[0]=G0+lmd*dG; return 1; } } else { int roots=0; double x=(-B+sqrt(delta))/2/A; if (x<0) {} else if (m1*x-f1<0) {} else { double lmd=(x*x-(F0+k1*G0))/(dF+k1*dG); F[0]=F0+lmd*dF; G[0]=G0+lmd*dG; roots++; } x=(-B-sqrt(delta))/2/A; if (x<0) {} else if (m1*x-f1<0) {} else { double lmd=(x*x-(F0+k1*G0))/(dF+k1*dG); F[roots]=F0+lmd*dF; G[roots]=G0+lmd*dG; roots++; } return roots; } } }//FGFrom2 /* function FGFrom2: solves the following equation system given m[2], f[2], k[2]. This is interpreted as searching from (F0, G0) down direction (dF, dG) until the first equation is satisfied. Functionally this is the same as the version using norm[2], with m[] anf f[] normalized by norm[] beforehand so that norm[] is no longer needed. However, k[2] cannot be computed from normalized m[2] so that it must be specified explicitly. m[0](F+k[0]G)^0.5-f[0] = m[1](F+k[1]G)^0.5-f[1] > 0 F=F0+lmd*dF G=G0+lmd*dG In: (F0, G0): search origin (dF, dG): search direction m[2], f[2], k[2]: coefficients in the first equation Out: F[return value], G[return value]: solutions. Returns the number of solutions for (F, G). */ int FGFrom2(double* F, double* G, double* lmd, double F0, double dF, double G0, double dG, double* m, double* f, int* k) { double m1=m[0], m2=m[1], f1=f[0], f2=f[1], k1=k[0], k2=k[1]; double A=m1*m1-m2*m2*(dF+k2*dG)/(dF+k1*dG), B=2*m1*(f2-f1), C=(f2-f1)*(f2-f1)-(k1-k2)*(F0*dG-G0*dF)/(dF+k1*dG)*m2*m2; //x is obtained by solving Axx+Bx+C=0 if (fabs(A)<1e-6) //A==0 { double x=-C/B; if (x<0) return 0; else if (m1*x-f1<0) return 0; else { lmd[0]=(x*x-(F0+k1*G0))/(dF+k1*dG); F[0]=F0+lmd[0]*dF; G[0]=G0+lmd[0]*dG; return 1; } } else { int roots=0; double delta=B*B-4*A*C; if (delta<0) return 0; else if (delta==0) { double x=-B/2/A; if (x<0) return 0; else if (m1*x-f1<0) return 0; else if ((m1*x-f1+f2)*m2<0) {} else { lmd[0]=(x*x-(F0+k1*G0))/(dF+k1*dG); F[0]=F0+lmd[0]*dF; G[0]=G0+lmd[0]*dG; roots++; } return roots; } else { int roots=0; double x=(-B+sqrt(delta))/2/A; if (x<0) {} else if (m1*x-f1<0) {} else if ((m1*x-f1+f2)*m2<0) {} else { lmd[0]=(x*x-(F0+k1*G0))/(dF+k1*dG); F[0]=F0+lmd[0]*dF; G[0]=G0+lmd[0]*dG; roots++; } x=(-B-sqrt(delta))/2/A; if (x<0) {} else if (m1*x-f1<0) {} else if ((m1*x-f1+f2)*m2<0) {} else { lmd[roots]=(x*x-(F0+k1*G0))/(dF+k1*dG); F[roots]=F0+lmd[roots]*dF; G[roots]=G0+lmd[roots]*dG; roots++; } return roots; } } }//FGFrom2 /* function FGFrom3: solves the following equation system given m[3], f[3], norm[3]. m[0](F+k[0]G)^0.5-f[0] m[1](F+k[1]G)^0.5-f[1] m[2](F+k[2]G)^0.5-f[2] ------------------------ = ------------------------ = ------------------------ > 0 norm[0] norm[1] norm[2] In: m[3], f[3], norm[3]: coefficients in the above Out: F[return value], G[return value]: solutions. Returns the number of solutions for (F, G). */ int FGFrom3(double* F, double* G, int* m, double* f, double* norm) { double m1=m[0]/norm[0], m2=m[1]/norm[1], m3=m[2]/norm[2], f1=f[0]/norm[0], f2=f[1]/norm[1], f3=f[2]/norm[2], k1=m[0]*m[0]-1, k2=m[1]*m[1]-1, k3=m[2]*m[2]-1; double h21=k2-k1, h31=k3-k1; //h21 and h31 double h12=m1*m1-m2*m2, h13=m1*m1-m3*m3; //h12' and h13' double a=m2*m2*h13*h21-m3*m3*h12*h31; if (a==0) { double x=h12*(f3-f1)*(f3-f1)-h13*(f2-f1)*(f2-f1), b=h13*(f2-f1)-h12*(f3-f1); x=x/(2*m1*b); if (x<0) return 0; //x must the square root of something else if (m1*x-f1<0) return 0; //discarded because we are solving e1=e2=e3>0 else { if (h21!=0) { G[0]=(h12*x*x+2*m1*(f2-f1)*x+(f2-f1)*(f2-f1))/(m2*m2*h21); } else { G[0]=(h13*x*x+2*m1*(f3-f1)*x+(f3-f1)*(f3-f1))/(m3*m3*h31); } F[0]=x*x-k1*G[0]; return 1; } } else { double b=(h13*(f2-f1)*(f2-f1)-h12*(f3-f1)*(f3-f1))/a; a=2*m1*(h13*(f2-f1)-h12*(f3-f1))/a; double A=h12, B=2*m1*(f2-f1)-m2*m2*h21*a, C=(f2-f1)*(f2-f1)-m2*m2*h21*b; double delta=B*B-4*A*C; if (delta<0) return 0; else if (delta==0) { double x=-B/2/A; if (x<0) return 0; //x must the square root of something else if (m1*x-f1<0) return 0; //discarded because we are solving e1=e2=e3>0 else { G[0]=a*x+b; F[0]=x*x-k1*G[0]; return 1; } } else { int roots=0; double x=(-B+sqrt(delta))/2/A; if (x<0) {} else if (m1*x-f1<0) {} else { G[0]=a*x+b; F[0]=x*x-k1*G[0]; roots++; } x=(-B-sqrt(delta))/2/A; if (x<0) {} else if (m1*x-f1<0) {} else { G[roots]=a*x+b; F[roots]=x*x-k1*G[roots]; roots++; } return roots; } } }//FGFrom3 /* function FGFrom3: solves the following equation system given m[3], f[3], k[3]. Functionally this is the same as the version using norm[3], with m[] anf f[] normalized by norm[] beforehand so that norm[] is no longer needed. However, k[3] cannot be computed from normalized m[3] so that it must be specified explicitly. m[0](F+k[0]G)^0.5-f[0] = m[1](F+k[1]G)^0.5-f[1] = m[2](F+k[2]G)^0.5-f[2] >0 In: m[3], f[3], k[3]: coefficients in the above Out: F[return value], G[return value]: solutions. Returns the number of solutions for (F, G). */ int FGFrom3(double* F, double* G, double* e, double* m, double* f, int* k) { double m1=m[0], m2=m[1], m3=m[2], f1=f[0], f2=f[1], f3=f[2], k1=k[0], k2=k[1], k3=k[2]; double h21=k2-k1, h31=k3-k1; //h21 and h31 double h12=m1*m1-m2*m2, h13=m1*m1-m3*m3; //h12' and h13' double a=m2*m2*h13*h21-m3*m3*h12*h31; if (a==0) { //a==0 implies two the e's are conjugates double x=h12*(f3-f1)*(f3-f1)-h13*(f2-f1)*(f2-f1), b=h13*(f2-f1)-h12*(f3-f1); x=x/(2*m1*b); if (x<0) return 0; //x must the square root of something else if (m1*x-f1<-1e-6) return 0; //discarded because we are solving e1=e2=e3>0 else if ((m1*x-f1+f2)*m2<0) return 0; else if ((m1*x-f1+f3)*m3<0) return 0; else { if (h21!=0) { G[0]=(h12*x*x+2*m1*(f2-f1)*x+(f2-f1)*(f2-f1))/(m2*m2*h21); } else { G[0]=(h13*x*x+2*m1*(f3-f1)*x+(f3-f1)*(f3-f1))/(m3*m3*h31); } F[0]=x*x-k1*G[0]; double tmp=F[0]+G[0]*k[0]; testnn(tmp); e[0]=m1*sqrt(tmp)-f[0]; return 1; } } else { double b=(h13*(f2-f1)*(f2-f1)-h12*(f3-f1)*(f3-f1))/a; a=2*m1*(h13*(f2-f1)-h12*(f3-f1))/a; double A=h12, B=2*m1*(f2-f1)-m2*m2*h21*a, C=(f2-f1)*(f2-f1)-m2*m2*h21*b; double delta=B*B-4*A*C; if (delta<0) return 0; else if (delta==0) { int roots=0; double x=-B/2/A; if (x<0) return 0; //x must the square root of something else if (m1*x-f1<0) return 0; //discarded because we are solving e1=e2=e3>0 else if ((m1*x-f1+f2)*m2<0) return 0; else if ((m1*x-f1+f3)*m3<0) return 0; else { G[0]=a*x+b; F[0]=x*x-k1*G[0]; double tmp=F[0]+G[0]*k[0]; testnn(tmp); e[0]=m1*sqrt(tmp)-f[0]; roots++; } return roots; } else { int roots=0; double x=(-B+sqrt(delta))/2/A; if (x<0) {} else if (m1*x-f1<0) {} else if ((m1*x-f1+f2)*m2<0) {} else if ((m1*x-f1+f3)*m3<0) {} else { G[0]=a*x+b; F[0]=x*x-k1*G[0]; double tmp=F[0]+G[0]*k1; testnn(tmp); e[0]=m1*sqrt(tmp)-f1; roots++; } x=(-B-sqrt(delta))/2/A; if (x<0) {} else if (m1*x-f1<0) {} else if ((m1*x-f1+f2)*m2<0) {} else if ((m1*x-f1+f3)*m3<0) {} else { G[roots]=a*x+b; F[roots]=x*x-k1*G[roots]; double tmp=F[roots]+G[roots]*k1; testnn(tmp); e[roots]=m1*sqrt(tmp)-f1; roots++; } return roots; } } }//FGFrom3 /* function FindNote: harmonic sinusoid tracking from a starting point in time-frequency plane forward and backward. In: _t, _f: start time and frequency frst, fren: tracking range, in frames Spec: spectrogram wid, offst: atom scale and hop size, must be consistent with Spec settings: note match settings brake: tracking termination threshold Out: M, Fr, partials[M][Fr]: HS partials Returns 0 if tracking is done by FindNoteF(), 1 if tracking is done by FindNoteConst() */ int FindNote(int _t, double _f, int& M, int& Fr, atom**& partials, int frst, int fren, int wid, int offst, TQuickSpectrogram* Spec, NMSettings settings) { double brake=0.02; if (settings.delp==0) return FindNoteConst(_t, _f, M, Fr, partials, frst, fren, wid, offst, Spec, settings, brake*5); atom* part=new atom[(fren-frst)*settings.maxp]; double fpp[1024]; double *vfpp=&fpp[256], *pfpp=&fpp[512]; atomtype *ptype=(atomtype*)&fpp[768]; NMResults results={fpp, vfpp, pfpp, ptype}; int trst=floor((_t-wid/2)*1.0/offst+0.5); if (trst<0) trst=0; TPolygon* R=new TPolygon(1024); R->N=0; cmplx<QSPEC_FORMAT>* spec=Spec->Spec(trst); double f0=_f*wid; cdouble *x=new cdouble[wid/2+1]; for (int i=0; i<=wid/2; i++) x[i]=spec[i]; settings.forcepin0=true; settings.pcount=0; settings.pin0asanchor=true; double B, starts=NoteMatchStiff3(R, f0, B, 1, &x, wid, 0, &settings, &results, 0, 0, ds0); int k=0; if (starts>0) { int startp=NMResultToAtoms(settings.maxp, part, trst*offst+wid/2, wid, results); settings.pin0asanchor=false; double* startvfp=new double[startp]; memcpy(startvfp, vfpp, sizeof(double)*startp); k=startp; k+=FindNoteF(&part[k], starts, R, startp, startvfp, trst, fren, wid, offst, Spec, settings, brake); k+=FindNoteF(&part[k], starts, R, startp, startvfp, trst, frst-1, wid, offst, Spec, settings, brake); delete[] startvfp; } delete R; delete[] x; AtomsToPartials(k, part, M, Fr, partials, offst); delete[] part; // ReEst1(M, frst, fren, partials, WV->Data16[channel], wid, offst); return 0; }//Findnote*/ /* function FindNoteConst: constant-pitch harmonic sinusoid tracking In: _t, _f: start time and frequency frst, fren: tracking range, in frames Spec: spectrogram wid, offst: atom scale and hop size, must be consistent with Spec settings: note match settings brake: tracking termination threshold Out: M, Fr, partials[M][Fr]: HS partials Returns 1. */ int FindNoteConst(int _t, double _f, int& M, int& Fr, atom**& partials, int frst, int fren, int wid, int offst, TQuickSpectrogram* Spec, NMSettings settings, double brake) { atom* part=new atom[(fren-frst)*settings.maxp]; double fpp[1024]; double *vfpp=&fpp[256], *pfpp=&fpp[512]; atomtype *ptype=(atomtype*)&fpp[768]; NMResults results={fpp, vfpp, pfpp, ptype}; //trst: the frame index to start tracking int trst=floor((_t-wid/2)*1.0/offst+0.5), maxp=settings.maxp; if (trst<0) trst=0; //find a note candidate for the starting frame at _t TPolygon* R=new TPolygon(1024); R->N=0; cmplx<QSPEC_FORMAT>* spec=Spec->Spec(trst); double f0=_f*wid; cdouble *x=new cdouble[wid/2+1]; for (int i=0; i<=wid/2; i++) x[i]=spec[i]; settings.forcepin0=true; settings.pcount=0; settings.pin0asanchor=true; double B, *starts=new double[maxp]; NoteMatchStiff3(R, f0, B, 1, &x, wid, 0, &settings, &results, 0, 0); //read the energy vector of this candidate HA to starts[]. starts[] will contain the highest single-frame //energy of each partial during the tracking. int P=0; while(P<maxp && f0*(P+1)*sqrt(1+B*((P+1)*(P+1)-1))<wid/2) P++; for (int m=0; m<P; m++) starts[m]=results.vfp[m]*results.vfp[m]; int atomcount=0; cdouble* ipw=new cdouble[maxp]; //find the ends of tracking by constant-pitch extension of the starting HA until //at a frame at least half of the partials fall below starts[]*brake. //first find end of the note by forward extension from the frame at _t int fr1=trst; while (fr1<fren) { spec=Spec->Spec(fr1); for (int i=0; i<=wid/2; i++) x[i]=spec[i]; double ls=0; for (int m=0; m<P; m++) { double fm=f0*(m+1)*sqrt(1+B*((m+1)*(m+1)-1)); int K1=floor(fm-settings.hB), K2=ceil(fm+settings.hB); if (K1<0) K1=0; if (K2>=wid/2) K2=wid/2-1; ipw[m]=IPWindowC(fm, x, wid, settings.M, settings.c, settings.iH2, K1, K2); ls+=~ipw[m]; if (starts[m]<~ipw[m]) starts[m]=~ipw[m]; } double sumstart=0, sumstop=0; for (int m=0; m<P; m++) { if (~ipw[m]<starts[m]*brake) sumstop+=1;// sqrt(starts[m]); sumstart+=1; //sqrt(starts[m]); } double lstop=sumstop/sumstart; if (lstop>0.5) break; fr1++; } //note find the start of note by backward extension from the frame at _t int fr0=trst-1; while(fr0>frst) { spec=Spec->Spec(fr0); for (int i=0; i<=wid/2; i++) x[i]=spec[i]; double ls=0; for (int m=0; m<P; m++) { double fm=f0*(m+1)*sqrt(1+B*((m+1)*(m+1)-1)); int K1=floor(fm-settings.hB), K2=ceil(fm+settings.hB); if (K1<0) K1=0; if (K2>=wid/2) K2=wid/2-1; ipw[m]=IPWindowC(fm, x, wid, settings.M, settings.c, settings.iH2, K1, K2); ls+=~ipw[m]; if (starts[m]<~ipw[m]) starts[m]=~ipw[m]; } double sumstart=0, sumstop=0; for (int m=0; m<P; m++) { if (~ipw[m]<starts[m]*brake) sumstop+=1; //sqrt(starts[m]); sumstart+=1; //sqrt(starts[m]); } double lstop=sumstop/sumstart; if (lstop>0.5) {fr0++; break;} fr0--; } //now fr0 and fr1 are the first and last (excl.) frame indices Fr=fr1-fr0; cdouble* ipfr=new cdouble[Fr]; double *as=new double[Fr*2], *phs=&as[Fr]; cdouble** Allocate2(cdouble, Fr, wid/2+1, xx); for (int fr=0; fr<Fr; fr++) { spec=Spec->Spec(fr0+fr); for (int i=0; i<=wid/2; i++) xx[fr][i]=spec[i]; } //reestimate partial frequencies, amplitudes and phase angles using all frames. In case of frequency estimation //failure, the original frequency is used and all atoms of that partial are typed "atInfered". for (int m=0; m<P; m++) { double fm=f0*(m+1)*sqrt(1+B*((m+1)*(m+1)-1)), dfm=(settings.delm<1)?settings.delm:1; double errf=LSESinusoidMP(fm, fm-dfm, fm+dfm, xx, Fr, wid, 3, settings.M, settings.c, settings.iH2, as, phs, 1e-6); // LSESinusoidMPC(fm, fm-1, fm+1, xx, Fr, wid, offst, 3, settings.M, settings.c, settings.iH2, as, phs, 1e-6); atomtype type=(errf<0)?atInfered:atPeak; for (int fr=0; fr<Fr; fr++) { double tfr=(fr0+fr)*offst+wid/2; atom tmpatom={tfr, wid, fm/wid, as[fr], phs[fr], m+1, type, 0}; part[atomcount++]=tmpatom; } } //Tag the atom at initial input (_t, _f) as anchor. AtomsToPartials(atomcount, part, M, Fr, partials, offst); partials[settings.pin0-1][trst-fr0].type=atAnchor; delete[] ipfr; delete[] ipw; delete[] part; delete R; delete[] x; delete[] as; DeAlloc2(xx); delete[] starts; return 1; }//FindNoteConst /* function FindNoteF: forward harmonic sinusoid tracking starting from a given harmonic atom until an endpoint is detected or a search boundary is reached. In: starts: harmonic atom score of the start HA startvfp[startp]: amplitudes of partials of start HA R: F-G polygon of the start HA frst, frst: frame index of the start and end frames of tracking. Spec: spectrogram wid, offst: atom scale and hop size, must be consistent with Spec settings: note match settings brake: tracking termination threshold. Out: part[return value]: list of atoms tracked. starts: maximal HA score of tracked frames. Its product with brake is used as the tracking threshold. Returns the number of atoms found. */ int FindNoteF(atom* part, double& starts, TPolygon* R, int startp, double* startvfp, int frst, int fren, int wid, int offst, TQuickSpectrogram* Spec, NMSettings settings, double brake) { settings.forcepin0=false, settings.pin0=0; int k=0, maxp=settings.maxp; TPolygon* RR=new TPolygon(1024, R); int lastp=startp; double *lastvfp=new double[settings.maxp]; memcpy(lastvfp, startvfp, sizeof(double)*startp); cdouble *x=new cdouble[wid/2+1]; double *fpp=new double[maxp*4], *vfpp=&fpp[maxp], *pfpp=&fpp[maxp*2]; atomtype* ptype=(atomtype*)&fpp[maxp*3]; NMResults results={fpp, vfpp, pfpp, ptype}; int step=(fren>frst)?1:(-1); for (int h=frst+step; (h-fren)*step<0; h+=step) { cmplx<QSPEC_FORMAT>* spec=Spec->Spec(h); for (int i=0; i<=wid/2; i++) x[i]=spec[i]; double f0=0, B=0; double tmp=NoteMatchStiff3(RR, f0, B, 1, &x, wid, 0, &settings, &results, lastp, lastvfp); if (starts<tmp) starts=tmp; if (tmp<starts*brake) break; //end condition: power drops by ??dB lastp=NMResultToAtoms(settings.maxp, &part[k], h*offst+wid/2, wid, results); k+=lastp; memcpy(lastvfp, vfpp, sizeof(double)*lastp); } delete RR; delete[] lastvfp; delete[] x; delete[] fpp; return k; }//FindNoteF /* function FindNoteFB: forward-backward harmonic sinusid tracking. In: frst, fren: index of start and end frames Rst, Ren: F-G polygons of start and end harmonic atoms vfpst[M], vfpen[M]: amplitude vectors of start and end harmonic atoms Spec: spectrogram wid, offst: atom scale and hop size, must be consistent with Spec settings: note match settings Out: partials[0:fren-frst][M], hosting tracked HS between frst and fren (inc). Returns 0 if successful, 1 if failure in pitch tracking stage (no feasible HA candidate at some frame). On start partials[0:fren-frst][M] shall be prepared to receive fren-frst+1 harmonic atoms */ int FindNoteFB(int frst, TPolygon* Rst, double* vfpst, int fren, TPolygon* Ren, double* vfpen, int M, atom** partials, int wid, int offst, TQuickSpectrogram* Spec, NMSettings settings) { //* if (frst>fren) return -1; int result=0; if (settings.maxp>M) settings.maxp=M; else M=settings.maxp; int Fr=fren-frst+1; double B, fpp[1024], delm=settings.delm, delp=settings.delp, iH2=settings.iH2; double *vfpp=&fpp[256], *pfpp=&fpp[512]; atomtype *ptype=(atomtype*)&fpp[768]; int maxpitch=50; NMResults results={fpp, vfpp, pfpp, ptype}; cmplx<QSPEC_FORMAT>* spec; cdouble *x=new cdouble[wid/2+1]; TPolygon **Ra=new TPolygon*[maxpitch], **Rb=new TPolygon*[maxpitch], ***R; memset(Ra, 0, sizeof(TPolygon*)*maxpitch), memset(Rb, 0, sizeof(TPolygon*)*maxpitch); double *sca=new double[maxpitch], *scb=new double[maxpitch], **sc; sca[0]=scb[0]=0; double** Allocate2(double, maxpitch, M, vfpa); memcpy(vfpa[0], vfpst, sizeof(double)*M); double** Allocate2(double, maxpitch, M, vfpb); memcpy(vfpb[0], vfpen, sizeof(double)*M); double*** vfp; double** Allocate2(double, Fr, wid/2, fps); double** Allocate2(double, Fr, wid/2, vps); int* pc=new int[Fr]; Ra[0]=new TPolygon(M*2+4, Rst); Rb[0]=new TPolygon(M*2+4, Ren); int pitchcounta=1, pitchcountb=1, pitchcount; //pitch tracking int** Allocate2(int, Fr, maxpitch, prev); int** Allocate2(int, Fr, maxpitch, pitches); int* pitchres=new int[Fr]; int fra=frst, frb=fren, fr; while (fra<frb-1) { if (pitchcounta<pitchcountb){fra++; fr=fra; pitchcount=pitchcounta; vfp=&vfpa; sc=&sca; R=&Ra;} else {frb--; fr=frb; pitchcount=pitchcountb; vfp=&vfpb; sc=&scb, R=&Rb;} int fr_frst=fr-frst; int absfr=(partials[0][fr].t-wid/2)/offst; spec=Spec->Spec(absfr); for (int i=0; i<=wid/2; i++) x[i]=spec[i]; pc[fr_frst]=QuickPeaks(fps[fr_frst], vps[fr_frst], wid, x, settings.M, settings.c, iH2, 0.0005); NoteMatchStiff3FB(pitchcount, *R, *vfp, *sc, pitches[fr_frst], prev[fr_frst], pc[fr_frst], fps[fr_frst], vps[fr_frst], x, wid, maxpitch, &settings); if (fr==fra) pitchcounta=pitchcount; else pitchcountb=pitchcount; if (pitchcount<=0) {result=1; goto cleanup;} } { //now fra=frb-1 int fra_frst=fra-frst, frb_frst=frb-frst, maxpitcha=0, maxpitchb=0; double maxs=0; for (int i=0; i<pitchcounta; i++) { double f0a, f0b; if (fra==frst) f0a=partials[0][frst].f*wid; else { int peak0a=((__int16*)&pitches[fra_frst][i])[0], pin0a=((__int16*)&pitches[fra_frst][i])[1]; f0a=fps[fra_frst][peak0a]/pin0a; } for (int j=0; j<pitchcountb; j++) { if (frb==fren) f0b=partials[0][fren].f*wid; else { int peak0b=((__int16*)&pitches[frb_frst][j])[0], pin0b=((__int16*)&pitches[frb_frst][j])[1]; f0b=fps[frb_frst][peak0b]/pin0b; } double pow2delp=pow(2, delp/12); if (f0b<f0a*pow2delp+delm && f0b>f0a/pow2delp-delm) { double s=sca[i]+scb[j]+conta(M, vfpa[i], vfpb[j]); if (maxs<s) maxs=s, maxpitcha=i, maxpitchb=j; } } } //get pitch tracking result pitchres[fra_frst]=pitches[fra_frst][maxpitcha]; for (int fr=fra; fr>frst; fr--) { maxpitcha=prev[fr-frst][maxpitcha]; pitchres[fr-frst-1]=pitches[fr-frst-1][maxpitcha]; } pitchres[frb_frst]=pitches[frb_frst][maxpitchb]; for (int fr=frb; fr<fren; fr++) { maxpitchb=prev[fr-frst][maxpitchb]; pitchres[fr-frst+1]=pitches[fr-frst+1][maxpitchb]; } } { double f0s[1024]; memset(f0s, 0, sizeof(double)*1024); for (int i=frst+1; i<fren; i++) { int pitch=pitchres[i-frst]; int peak0=((__int16*)&pitch)[0], pin0=((__int16*)&pitch)[1]; f0s[i]=fps[i-frst][peak0]/pin0; } f0s[frst]=sqrt(Rst->X[0]), f0s[fren]=sqrt(Ren->X[0]); //partial tracking int lastp=0; while (lastp<M && vfpst[lastp]>0) lastp++; double* lastvfp=new double[M]; memcpy(lastvfp, vfpst, sizeof(double)*M); delete Ra[0]; Ra[0]=new TPolygon(M*2+4, Rst); for (int h=frst+1; h<fren; h++) { int absfr=(partials[0][h].t-wid/2)/offst; int h_frst=h-frst, pitch=pitchres[h_frst]; int peak0=((__int16*)&pitch)[0], pin0=((__int16*)&pitch)[1]; spec=Spec->Spec(absfr); double f0=fpp[0];//, B=Form11->BEdit->Text.ToDouble(); //double deltaf=f0*(pow(2, settings.delp/12)-1); for (int i=0; i<=wid/2; i++) x[i]=spec[i]; memset(fpp, 0, sizeof(double)*1024); //settings.epf0=deltaf, settings.forcepin0=true; settings.pin0=pin0; f0=fps[h_frst][peak0]; settings.pin0asanchor=false; if (NoteMatchStiff3(Ra[0], f0, B, pc[h_frst], fps[h_frst], vps[h_frst], 1, &x, wid, 0, &settings, &results, lastp, lastvfp)>0) { lastp=NMResultToPartials(M, h, partials, partials[0][h].t, wid, results); memcpy(lastvfp, vfpp, sizeof(double)*lastp); } } delete[] lastvfp; } cleanup: delete[] x; for (int i=0; i<pitchcounta; i++) delete Ra[i]; delete[] Ra; for (int i=0; i<pitchcountb; i++) delete Rb[i]; delete[] Rb; delete[] sca; delete[] scb; DeAlloc2(vfpa); DeAlloc2(vfpb); DeAlloc2(fps); DeAlloc2(vps); DeAlloc2(pitches); DeAlloc2(prev); delete[] pitchres; delete[] pc; return result; //*/ }//FindNoteFB /* function GetResultsSingleFr: used by NoteMatchStiff3 to obtain final note match results after harmonic grouping of peaks with rough frequency estimates, including a screening of found peaks based on shape and consistency with other peak frequencies, reestimating sinusoid parameters using LSE, estimating atoms without peaks by inferring their frequencies, and translating internal peak type to atom type. In: R0: F-G polygon of primitive (=rough estimates) partial frequencies x[N]: spectrum ps[P], fs[P]; partial indices and frequencies, may miss partials rsrs[P]: peak shape factors, used for evaluating peak validity psb[P]: peak type flags, related to atom::ptype. settings: note match settings numsam: number of partials to evaluate (=number of atoms to return) Out: results: note match results as a NMResult structure f0, B: fundamental frequency and stiffness coefficient Rret: F-G polygon of reestimated set of partial frequencies Return the total energy of the harmonic atom. */ double GetResultsSingleFr(double& f0, double& B, TPolygon* Rret, TPolygon* R0, int P, int* ps, double* fs, double* rsrs, cdouble* x, int N, int* psb, int numsam, NMSettings* settings, NMResults* results) { Rret->N=0; double delm=settings->delm, *c=settings->c, iH2=settings->iH2, epf=settings->epf, maxB=settings->maxB, hB=settings->hB; int M=settings->M, maxp=settings->maxp; double *fp=results->fp; memset(fp, 0, sizeof(double)*maxp); double result=0; if (P>0) { double *vfp=results->vfp, *pfp=results->pfp; atomtype *ptype=results->ptype; //memset(ptype, 0, sizeof(int)*maxp); double *f1=new double[(maxp+1)*4], *f2=&f1[maxp+1], *ft=&f2[maxp+1], *fdep=&ft[maxp+1], tmpa, cF, cG; areaandcentroid(tmpa, cF, cG, R0->N, R0->X, R0->Y); for (int p=1; p<=maxp; p++) ExFmStiff(f1[p], f2[p], p, R0->N, R0->X, R0->Y), ft[p]=p*sqrt(cF+(p*p-1)*cG); //sort peaks by rsr and departure from model so that most reliable ones are found at the start double* values=new double[P]; int* indices=new int[P]; for (int i=0; i<P; i++) { values[i]=1e200; int lp=ps[i]; double lf=fs[i]; if (lf>=f1[lp] && lf<=f2[lp]) fdep[lp]=0; else if (lf<f1[lp]) fdep[lp]=f1[lp]-lf; else if (lf>f2[lp]) fdep[lp]=lf-f2[lp]; double tmpv=(fdep[lp]>0.5)?(fdep[lp]-0.5)*2:0; tmpv+=rsrs?rsrs[i]:0; tmpv*=pow(lp, 0.2); InsertInc(tmpv, i, values, indices, i+1); } for (int i=0; i<P; i++) { int ind=indices[i]; int lp=ps[ind]; //partial index //Get LSE estimation of atoms double lf=fs[ind], f1, f2, la, lph;//, lrsr=rsrs?rsrs[ind]:0 if (lp==0 || lf==0) continue; ExFmStiff(f1, f2, lp, R0->N, R0->X, R0->Y); LSESinusoid(lf, f1-delm, f2+delm, x, N, 3, M, c, iH2, la, lph, epf); //lrsr=PeakShapeC(lf, 1, N, &x, 6, M, c, iH2); if (Rret->N>0) ExFmStiff(f1, f2, lp, Rret->N, Rret->X, Rret->Y); if (Rret->N==0 || (lf>=f1 && lf<=f2)) { fp[lp-1]=lf; vfp[lp-1]=la; pfp[lp-1]=lph; if (psb[lp]==1 || (psb[lp]==2 && settings->pin0asanchor)) ptype[lp-1]=atAnchor; //0 for anchor points else ptype[lp-1]=atPeak; //1 for local maximum //update R using found partails with amplitude>1 if (Rret->N==0) InitializeR(Rret, lp, lf, delm, maxB); else if (la>1) CutR(Rret, lp, lf, delm, true); } } //* for (int p=1; p<=maxp; p++) { if (fp[p-1]>0) { double f1, f2; ExFmStiff(f1, f2, p, Rret->N, Rret->X, Rret->Y); if (fp[p-1]<f1-0.3 || fp[p-1]>f2+0.3) fp[p-1]=0; } }// */ //estimate f0 and B double norm[1024]; for (int i=0; i<1024; i++) norm[i]=1; areaandcentroid(tmpa, cF, cG, Rret->N, Rret->X, Rret->Y); //minimaxstiff(cF, cG, P, ps, fs, norm, R->N, R->X, R->Y); testnn(cF); f0=sqrt(cF); B=cG/cF; //Get LSE estimates for unfound partials for (int i=0; i<numsam; i++) { if (fp[i]==0) //no peak is found for this partial in lcand { int m=i+1; double tmp=cF+(m*m-1)*cG; testnn(tmp); double lf=m*sqrt(tmp); if (lf<N/2.1) { fp[i]=lf; ptype[i]=atInfered; //2 for non peak cdouble r=IPWindowC(lf, x, N, M, c, iH2, lf-hB, lf+hB); vfp[i]=sqrt(r.x*r.x+r.y*r.y); pfp[i]=Atan2(r.y, r.x);//*/ } } } if (f0>0) for (int i=0; i<numsam; i++) if (fp[i]>0) result+=vfp[i]*vfp[i]; delete[] f1; delete[] values; delete[] indices; } return result; }//GetResultsSingleFr /* function GetResultsMultiFr: the constant-pitch multi-frame version of GetResultsSingleFr. In: x[Fr][N]: spectrogram ps[P], fs[P]; partial indices and frequencies, may miss partials psb[P]: peak type flags, related to atom::ptype. settings: note match settings forceinputlocalfr: specifies if partial settings->pin0 is taken for granted ("pinned") numsam: number of partials to evaluate (=number of atoms to return) Out: results[Fr]: note match results as Fr NMResult structures f0, B: fundamental frequency and stiffness coefficient Rret: F-G polygon of reestimated set of partial frequencies Return the total energy of the harmonic sinusoid. */ double GetResultsMultiFr(double& f0, double& B, TPolygon* Rret, TPolygon* R0, int P, int* ps, double* fs, double* rsrs, int Fr, cdouble** x, int N, int offst, int* psb, int numsam, NMSettings* settings, NMResults* results, int forceinputlocalfr) { Rret->N=0; double delm=settings->delm, *c=settings->c, iH2=settings->iH2, epf=settings->epf, maxB=settings->maxB, hB=settings->hB;// *pinf=settings->pinf; int M=settings->M, maxp=settings->maxp, pincount=settings->pcount, *pin=settings->pin, *pinfr=settings->pinfr; // double *fp=results->fp, *vfp=results->vfp, *pfp=results->pfp; // int *ptype=results->ptype; for (int fr=0; fr<Fr; fr++) memset(results[fr].fp, 0, sizeof(double)*maxp); int* pclass=new int[maxp+1]; memset(pclass, 0, sizeof(int)*(maxp+1)); for (int i=0; i<pincount; i++) if (pinfr[i]>=0) pclass[pin[i]]=1; if (forceinputlocalfr>=0) pclass[settings->pin0]=1; double result=0; if (P>0) { double tmpa, cF, cG; //found atoms double *as=new double[Fr*2], *phs=&as[Fr]; for (int i=P-1; i>=0; i--) { int lp=ps[i]; double lf=fs[i]; if (lp==0 || lf==0) continue; if (!pclass[lp]) { //Get LSE estimation of atoms double startlf=lf; LSESinusoidMP(lf, lf-1, lf+1, x, Fr, N, 3, M, c, iH2, as, phs, epf); //LSESinusoidMPC(lf, lf-1, lf+1, x, Fr, N, offst, 3, M, c, iH2, as, phs, epf); if (fabs(lf-startlf)>0.6) { lf=startlf; for (int fr=0; fr<Fr; fr++) { cdouble r=IPWindowC(lf, x[fr], N, M, c, iH2, lf-settings->hB, lf+settings->hB); as[fr]=abs(r); phs[fr]=arg(r); } } } else //frequencies of local anchor atoms are not re-estimated { for (int fr=0; fr<Fr; fr++) { cdouble r=IPWindowC(lf, x[fr], N, M, c, iH2, lf-settings->hB, lf+settings->hB); as[fr]=abs(r); phs[fr]=arg(r); } } //LSESinusoid(lf, f1-delm, f2+delm, x, N, 3, M, c, iH2, la, lph, epf); //fp[lp-1]=lf; vfp[lp-1]=la; pfp[lp-1]=lph; for (int fr=0; fr<Fr; fr++) results[fr].fp[lp-1]=lf, results[fr].vfp[lp-1]=as[fr], results[fr].pfp[lp-1]=phs[fr]; if (psb[lp]==1 || (psb[lp]==2 && settings->pin0asanchor)) for (int fr=0; fr<Fr; fr++) results[fr].ptype[lp-1]=atAnchor; //0 for anchor points else for (int fr=0; fr<Fr; fr++) results[fr].ptype[lp-1]=atPeak; //1 for local maximum //update R using found partails with amplitude>1 if (Rret->N==0) { //temporary treatment: +0.5 should be +rsr or something similar InitializeR(Rret, lp, lf, delm+0.5, maxB); areaandcentroid(tmpa, cF, cG, Rret->N, Rret->X, Rret->Y); //minimaxstiff(cF, cG, P, ps, fs, norm, R->N, R->X, R->Y); } else //if (la>1) { CutR(Rret, lp, lf, delm+0.5, true); areaandcentroid(tmpa, cF, cG, Rret->N, Rret->X, Rret->Y); //minimaxstiff(cF, cG, P, ps, fs, norm, R->N, R->X, R->Y); } } //estimate f0 and B double norm[1024]; for (int i=0; i<1024; i++) norm[i]=1; areaandcentroid(tmpa, cF, cG, Rret->N, Rret->X, Rret->Y); //minimaxstiff(cF, cG, P, ps, fs, norm, R->N, R->X, R->Y); testnn(cF); f0=sqrt(cF); B=cG/cF; //Get LSE estimates for unfound partials for (int i=0; i<numsam; i++) { if (results[0].fp[i]==0) //no peak is found for this partial in lcand { int m=i+1; double tmp=cF+(m*m-1)*cG; testnn(tmp); double lf=m*sqrt(tmp); if (lf<N/2.1) { for (int fr=0; fr<Fr; fr++) { results[fr].fp[i]=lf, results[fr].ptype[i]=atInfered; //2 for non peak int k1=ceil(lf-hB); if (k1<0) k1=0; int k2=floor(lf+hB); if (k2>=N/2) k2=N/2-1; cdouble r=IPWindowC(lf, x[fr], N, M, c, iH2, k1, k2); results[fr].vfp[i]=abs(r); results[fr].pfp[i]=arg(r); } } } } delete[] as; if (f0>0) for (int fr=0; fr<Fr; fr++) for (int i=0; i<numsam; i++) if (results[fr].fp[i]>0) result+=results[fr].vfp[i]*results[fr].vfp[i]; result/=Fr; } delete[] pclass; return result; }//GetResultsMultiFr /* function InitailizeR: initializes a F-G polygon with a fundamental frequency range and stiffness coefficient bound In: af, ef: centre and half width of the fundamental frequency range maxB: maximal value of stiffness coefficient (the minimal is set to 0) Out: R: the initialized F-G polygon. No reutrn value. */ void InitializeR(TPolygon* R, double af, double ef, double maxB) { R->N=4; double *X=R->X, *Y=R->Y; double g1=af-ef, g2=af+ef; if (g1<0) g1=0; g1=g1*g1, g2=g2*g2; X[0]=X[3]=g1, X[1]=X[2]=g2; Y[0]=X[0]*maxB, Y[1]=X[1]*maxB, Y[2]=Y[3]=0; }//InitializeR /* function InitialzeR: initializes a F-G polygon with a frequency range for a given partial and stiffness coefficient bound In: apind: partial index af, ef: centre and half width of the frequency range of the apind-th partial maxB; maximal value of stiffness coefficient (the minimal is set to 0) Out: R: the initialized F-G polygon. No return value. */ void InitializeR(TPolygon* R, int apind, double af, double ef, double maxB) { R->N=4; double *X=R->X, *Y=R->Y; double k=apind*apind-1; double g1=(af-ef)/apind, g2=(af+ef)/apind; if (g1<0) g1=0; g1=g1*g1, g2=g2*g2; double kb1=1+k*maxB; X[0]=g1/kb1, X[1]=g2/kb1, X[2]=g2, X[3]=g1; Y[0]=X[0]*maxB, Y[1]=X[1]*maxB, Y[2]=Y[3]=0; }//InitializeR /* function maximalminimum: finds the point within a polygon that maximizes its minimal distance to the sides. In: sx[N], sy[N]: x- and y-coordinates of vertices of a polygon Out: (x, y): point within the polygon with maximal minimal distance to the sides Returns the maximial minimal distance. A circle centred at (x, y) with the return value as the radius is the maximum inscribed circle of the polygon. */ double maximalminimum(double& x, double& y, int N, double* sx, double* sy) { //at the beginning let (x, y) be (sx[0], sy[0]), then the mininum distance d is 0. double dm=0; x=sx[0], y=sy[0]; double *A=new double[N*3]; double *B=&A[N], *C=&A[N*2]; //calcualte equations of all sides A[k]x+B[k]y+C[k]=0, with signs adjusted so that for // any (x, y) within the polygon, A[k]x+B[k]y+C[k]>0. A[k]^2+B[k]^2=1. for (int n=0; n<N; n++) { double x1=sx[n], y1=sy[n], x2, y2, AA, BB, CC; if (n+1!=N) x2=sx[n+1], y2=sy[n+1]; else x2=sx[0], y2=sy[0]; double dx=x1-x2, dy=y1-y2; double ds=sqrt(dx*dx+dy*dy); AA=dy/ds, BB=-dx/ds; CC=-AA*x1-BB*y1; //adjust signs if (n+2<N) x2=sx[n+2], y2=sy[n+2]; else x2=sx[n+2-N], y2=sy[n+2-N]; if (AA*x2+BB*y2+CC<0) A[n]=-AA, B[n]=-BB, C[n]=-CC; else A[n]=AA, B[n]=BB, C[n]=CC; } //during the whole process (x, y) is always equal-distance to at least two sides, // namely l1--(l1+1) and l2--(l2+1). there equations are A1x+B1y+C1=0 and A2x+B2y+C2=0, // where A^2+B^2=1. int l1=0, l2=N-1; double a, b; b=A[l1]-A[l2], a=B[l2]-B[l1]; double ds=sqrt(a*a+b*b); a/=ds, b/=ds; //the line (x+at, y+bt) passes (x, y), and points on this line are equal-distance to l1 and l2. //along this line at point (x, y), we have dx=ads, dy=bds //now find the signs so that dm increases as ds>0 double ddmds=A[l1]*a+B[l1]*b; if (ddmds<0) a=-a, b=-b, ddmds=-ddmds; while (true) { //now the vector starting from (x, y) pointing in (a, b) is equi-distance-to-l1-and-l2 and // dm-increasing. actually at s from (x,y), d=dm+ddmds*s. //it is now guaranteed that the distance of (x, y) to l1 (=l2) is smaller than to any other // sides. along direction (A, B) the distance of (x, y) to l1 (=l2) is increasing and // the distance to at least one other sides is increasing, so that at some value of s the // distances equal. we find the smallest s>0 where this happens. int l3=-1; double s=-1; for (int n=0; n<N; n++) { if (n==l1 || n==l2) continue; //distance of (x,y) to the side double ldm=A[n]*x+B[n]*y+C[n]; //ldm>dm double dldmds=A[n]*a+B[n]*b; //so ld=ldm+lddmds*s, the equality is dm+ddmds*s=ldm+lddmds*s if (ddmds-dldmds>0) { ds=(ldm-dm)/(ddmds-dldmds); if (l3==-1) l3=n, s=ds; else if (ds<s) l3=n, s=ds; } } if (ddmds==0) s/=2; x=x+a*s, y=y+b*s; dm=A[l1]*x+B[l1]*y+C[l1]; // Form1->Canvas->Ellipse(x-dm, y-dm, x+dm, y+dm); //(x, y) is equal-distance to l1, l2 and l3 //try use l3 to substitute l2 b=A[l1]-A[l3], a=B[l3]-B[l1]; ds=sqrt(a*a+b*b); a/=ds, b/=ds; ddmds=A[l1]*a+B[l1]*b; if (ddmds<0) a=-a, b=-b, ddmds=-ddmds; if (ddmds==0 || A[l2]*a+B[l2]*b>0) { l2=l3; } else //l1<-l3 fails, then try use l3 to substitute l2 { b=A[l3]-A[l2], a=B[l2]-B[l3]; ds=sqrt(a*a+b*b); a/=ds, b/=ds; ddmds=A[l3]*a+B[l3]*b; if (ddmds<0) a=-a, b=-b, ddmds=-ddmds; if (ddmds==0 || A[l1]*a+B[l1]*b>0) { l1=l3; } else break; } } delete[] A; return dm; }//maximalminimum /* function minimaxstiff: finds the point in polygon (N; F, G) that minimizes the maximal value of the function | m[l]sqrt(F+(m[l]^2-1)*G)-f[l] | e_l = | ----------------------------- | regarding l=0, ..., L-1 | norm[l] | In: _m[L], _f[L], norm[L]: coefficients in the above F[N], G[N]: vertices of a F-G polygon Out: (F1, G1): the mini-maximum. Returns the minimized maximum value. Further reading: Wen X, "Estimating model parameters", Ch.3.2.6 from "Harmonic sinusoid modeling of tonal music events", PhD thesis, University of London, 2007. */ double minimaxstiff(double& F1, double& G1, int L, int* _m, double* _f, double* norm, int N, double* F, double* G) { if (L==0 || N<=2) { F1=F[0], G1=G[0]; return 0; } //normalizing double* m=(double*)malloc(sizeof(double)*L*6);//new double[L*6]; double* f=&m[L*2]; int* k=(int*)&m[L*4]; for (int l=0; l<L; l++) { k[2*l]=_m[l]*_m[l]-1; k[2*l+1]=k[2*l]; m[2*l]=_m[l]/norm[l]; m[2*l+1]=-m[2*l]; f[2*l]=_f[l]/norm[l]; f[2*l+1]=-f[2*l]; } //From this point on the L distance functions with absolute value is replace by 2L distance functions L*=2; double* vmnl=new double[N*2]; int* mnl=(int*)&vmnl[N]; start: //Initialize (F0, G0) to be the polygon vertex that has the minimal max_l(e_l) // maxn: the vertex index // maxl: the l that maximizes e_l at that vertex // maxsg: the sign of e_l before taking the abs. value int nc=-1, nd; int l1=-1, l2=0, l3=0; double vmax=0; for (int n=0; n<N; n++) { int lmax=-1; double lvmax=0; for (int l=0; l<L; l++) { double tmp=F[n]+k[l]*G[n]; testnn(tmp); double e=m[l]*sqrt(tmp)-f[l]; if (e>lvmax) lvmax=e, lmax=l; } mnl[n]=lmax, vmnl[n]=lvmax; if (n==0) { vmax=lvmax, nc=n, l1=lmax; } else { if (lvmax<vmax) vmax=lvmax, nc=n, l1=lmax; } } double F0=F[nc], G0=G[nc]; // start searching the the minimal maximum from (F0, G0) // // Each searching step starts from (F0, G0), ends at (F1, G1) // // Starting conditions of one step: // // (F0, G0) can be // (1)inside polygon R; // (2)on one side of R (nc:(nc+1) gives the side) // (3)a vertex of R (nc being the vertex index) // // The maximum at (F0, G0) can be // (1)vmax=e1=e2=e3>...; (l1, l2, l3) // (2)vmax=e1=e2>...; (l1, l2) // (3)vmax=e1>.... (l1) // // More complication arise if we have more than 3 equal maxima, i.e. // vmax=e1=e2=e3=e4>=.... CURRENTLY WE ASSUME THIS NEVER HAPPENS. // // These are also the ending conditions of one step. // // There are types of basic searching steps, i.e. // // (1) e1=e2 search: starting with e1=e2, search down the decreasing direction // of e1=e2 until at point (F1, G1) there is another l3 so that e1(F1, G1) // =e2(F1, G1)=e3(F1, G1), or until at point (F1, G1) the search is about // to go out of R. // Arguments: l1, l2, (F0, G0, vmax) // (2) e1!=e2 search: starting with e1=e2 and (F0, G0) being on one side, search // down the side in the decreasing direction of both e1 and e1 until at point // (F1, G1) there is a l3 so that e3(F1, G1)=max(e1, e2)(F1, G1), or // at a the search reaches a vertex of R. // Arguments: l1, l2, dF, dG, (F0, G0, vmax) // (3) e1 free search: starting with e1 being the only maximum, search down the decreasing // direction of e1 until at point (F1, G1) there is another l2 so that // e1(F1, G1)=e2(F1, G1), or until at point (F1, G1) the search is about // to go out of R. // Arguments: l1, dF, dG, (F0, G0, vmax) // (4) e1 side search: starting with e1 being the only maximum, search down the // a side of R in the decreasing direction of e1 until at point (F1, G1) there // is another l2 so that e1=e2, or until point (F1, G1) the search reaches // a vertex. // Arguments: l1, destimation vertex nd, (F0, G0, vmax) // // At the beginning of each searching step we check the conditions to see which // basic step to perform, and provide the starting conditions. At the very start of // the search, (F0, G0) is a vertex of R, e_l1 being the maximum at this point. // int condpos=3; //inside, on side, vertex int condmax=3; //triple max, duo max, solo max int searchmode; bool minimax=false; //set to true when the minimal maximum is met int iter=L*2; while (!minimax && iter>0) { iter--; double m1=m[l1], m2=m[l2], m3=m[l3], k1=k[l1], k2=k[l2], k3=k[l3]; double tmp, tmp1, tmp2, tmp3; switch (condmax) { case 1: tmp=F0+k3*G0; testnn(tmp); tmp3=sqrt(tmp); case 2: tmp=F0+k2*G0; testnn(tmp) tmp2=sqrt(tmp); case 3: tmp=F0+k1*G0; testnn(tmp); tmp1=sqrt(tmp); } double dF, dG; int n0=(nc==0)?(N-1):(nc-1), n1=(nc==N-1)?0:(nc+1); double x0, y0, x1, y1; if (n0>=0) x0=F[nc]-F[n0], y0=G[nc]-G[n0]; if (nc>=0) x1=F[n1]-F[nc], y1=G[n1]-G[nc]; if (condpos==1) //(F0, G0) being inside polygon { if (condmax==1) //e1=e2=e3 being the maximum { //vmax holds the maximum //l1, l2, l3 //now choose a searching direction, either e1=e2 or e1=e3 or e2=e3. // choose e1=e2 if (e3-e1) decreases along the decreasing direction of e1=e2 // choose e1=e3 if (e2-e1) decreases along the decreasing direction of e1=e3 // choose e2=e3 if (e1-e2) decreases along the decreasing directino of e2=e3 // if no condition is satisfied, then (F0, G0) is the minimal maximum. //calculate the decreasing direction of e1=e3 as (dF, dG) double den=m1*m3*(k1-k3)/2; dF=-(k1*m1*tmp3-k3*m3*tmp1)/den, dG=(m1*tmp3-m3*tmp1)/den; //the negative gradient of e2-e1 is calculated as (gF, gG) double gF=-(m2/tmp2-m1/tmp1)/2, gG=-(k2*m2/tmp2-k1*m1/tmp1)/2; if (dF*gF+dG*gG>0) //so that e2-e1 decreases in the decreasing direction of e1=e3 { l2=l3; searchmode=1; } else { //calculate the decreasing direction of e2=e3 as (dF, dG) den=m2*m3*(k2-k3)/2; dF=-(k2*m2*tmp3-k3*m3*tmp2)/den, dG=(m2*tmp3-m3*tmp2)/den; //calculate the negative gradient of e1-e2 as (gF, gG) gF=-gF, gG=-gG; if (dF*gF+dG*gG>0) //so that e1-e2 decreases in the decreasing direction of e2=e3 { l1=l3; searchmode=1; } else { //calculate the decreasing direction of e1=e2 as (dF, dG) den=m1*m2*(k1-k2)/2; dF=-(k1*m1*tmp2-k2*m2*tmp1)/den, dG=(m1*tmp2-m2*tmp1)/den; //calculate the negative gradient of (e3-e1) as (gF, gG) gF=-(m3/tmp3-m1/tmp1)/2, gG=-(k3*m3/tmp3-k1*m1/tmp1)/2; if (dF*gF+dG*gG>0) //so that e3-e1 decreases in the decreasing direction of e1=e2 { searchmode=1; } else { F1=F0, G1=G0; searchmode=0; //no search minimax=true; //quit loop } } } } // else if (condmax==2) //e1=e2 being the maximum { //vmax holds the maximum //l1, l2 searchmode=1; } else if (condmax==3) //e1 being the maximum { //the negative gradient of e1 dF=-0.5*m1/tmp1; dG=k1*dF; searchmode=3; } } else if (condpos==2) //(F0, G0) being on side nc:(nc+1) { //the vector nc->(nc+1) as (x1, y1) if (condmax==1) //e1=e2=e3 being the maximum { //This case rarely happens. //First see if a e1=e2 search is possible //calculate the decreasing direction of e1=e3 as (dF, dG) double den=m1*m3*(k1-k3)/2; double dF=-(k1*m1*tmp3-k3*m3*tmp1)/den, dG=(m1*tmp3-m3*tmp1)/den; //the negative gradient of e2-e1 is calculated as (gF, gG) double gF=-(m2/tmp2-m1/tmp1)/2, gG=-(k2*m2/tmp2-k1*m1/tmp1)/2; if (dF*gF+dG*gG>0 && x1*dG-y1*dF<0) //so that e2-e1 decreases in the decreasing direction of e1=e3 { //~~~~~~~~~~~~~and this direction points inward l2=l3; searchmode=1; } else { //calculate the decreasing direction of e2=e3 as (dF, dG) den=m2*m3*(k2-k3)/2; dF=-(k2*m2*tmp3-k3*m3*tmp2)/den, dG=(m2*tmp3-m3*tmp2)/den; //calculate the negative gradient of e1-e2 as (gF, gG) gF=-gF, gG=-gG; if (dF*gF+dG*gG>0 && x1*dG-y1*dF<0) //so that e1-e2 decreases in the decreasing direction of e2=e3 { l1=l3; searchmode=1; } else { //calculate the decreasing direction of e1=e2 as (dF, dG) den=m1*m2*(k1-k2)/2; dF=-(k1*m1*tmp2-k2*m2*tmp1)/den, dG=(m1*tmp2-m2*tmp1)/den; //calculate the negative gradient of (e3-e1) as (gF, gG) gF=-(m3/tmp3-m1/tmp1)/2, gG=-(k3*m3/tmp3-k1*m1/tmp1)/2; if (dF*gF+dG*gG>0 && x1*dG-y1*dF<0) //so that e3-e1 decreases in the decreasing direction of e1=e2 { searchmode=1; } else { //see the possibility of a e1!=e2 search //calcualte the dot product of the gradients and (x1, y1) double d1=m1/2/tmp1*(x1+k1*y1), d2=m2/2/tmp2*(x1+k2*y1), d3=m3/2/tmp3*(x1+k3*y1); //we can prove that if there is a direction pointing inward R in which // e1, e2, e2 decrease, and another direction pointing outside R in // which e1, e2, e3 decrease, then on one direction along the side // all the three decrease. (Even more, this direction must be inside // the <180 angle formed by the two directions.) // // On the contrary, if there is a direction // in which all the three decrease, with two equal, it has to point // outward for the program to get here. Then if along neither direction // of side R can the three all descend, then there doesn't exist any // direction inward R in which the three descend. In that case we // have a minimal maximum at (F0, G0). if (d1*d2<=0 || d1*d3<=0 || d2*d3<=0) //so that the three don't decrease in the same direction { F1=F0, G1=G0; searchmode=0; //no search minimax=true; //quit loop } else { if (d1>0) //so that d2>0, d3>0, all three decrease in the direction -(x1, y1) towards nc { if (d1>d2 && d1>d3) //e1 decreases fastest { l1=l3; //keep e2, e3 } else if (d2>=d1 && d2>d3) //e2 decreases fastest { l2=l3; //keep e1, e3 } else //d3>=d1 && d3>=d2, e3 decreases fastest { //keep e1, e2 } nd=nc; } else //d1<0, d2<0, d3<0, all three decrease in the direction (x1, y1) { if (d1<d2 && d1<d3) //e1 decreases fastest { l1=l3; } else if (d2<=d1 && d2<d3) //e2 decreases fastest { l2=l3; } else //d3<=d1 && d3<=d2 { //keep e1, e2 } nd=n1; } searchmode=2; } } } } } else if (condmax==2) //e1=e2 being the maximum { //first see if the decreasing direction of e1=e2 points to the inside //calculate the decreasing direction of e1=e2 as (dF, dG) double den=m1*m2*(k1-k2)/2; dF=-(k1*m1*tmp2-k2*m2*tmp1)/den, dG=(m1*tmp2-m2*tmp1)/den; if (x1*dG-y1*dF<0) //so that (dF, dG) points inward R { searchmode=1; } else { //calcualte the dot product of the gradients and (x1, y1) double d1=m1/2/tmp1*(x1+k1*y1), d2=m2/2/tmp2*(x1+k2*y1); if (d1*d2<=0) //so that along the side e1, e2 descend in opposite directions { F1=F0, G1=G0; searchmode=0; minimax=true; } else { if (d1>0) //so that both decrease in direction -(x1, y1) nd=nc; else nd=n1; searchmode=2; } } } else if (condmax==3) //e1 being the maximum { //calculate the negative gradient of e1 as (dF, dG) dF=-0.5*m1/tmp1; dG=k1*dF; if (x1*dG-y1*dF<0) //so the gradient points inward R searchmode=3; else { //calculate the dot product of the gradient and (x1, y1) double d1=m1/2/tmp1*(x1+k1*y1); if (d1>0) //so that e1 decreases in direction -(x1, y1) { nd=nc; searchmode=4; } else if (d1<0) { nd=n1; searchmode=4; } else //so that e1 does not change along side nc:(nc+1) { F1=F0, G1=G0; searchmode=0; minimax=true; } } } } else //condpos==3, (F0, G0) being vertex nc { //the vector nc->(nc+1) as (x1, y1) //the vector (nc-1)->nc as (x0, y0) if (condmax==1) //e1=e2=e3 being the maximum { //This case rarely happens. //First see if a e1=e2 search is possible //calculate the decreasing direction of e1=e3 as (dF, dG) double den=m1*m3*(k1-k3)/2; double dF=-(k1*m1*tmp3-k3*m3*tmp1)/den, dG=(m1*tmp3-m3*tmp1)/den; //the negative gradient of e2-e1 is calculated as (gF, gG) double gF=-(m2/tmp2-m1/tmp1)/2, gG=-(k2*m2/tmp2-k1*m1/tmp1)/2; if (dF*gF+dG*gG>0 && x1*dG-y1*dF<0 && x0*dG-y0*dF<0) //so that e2-e1 decreases in the decreasing direction of e1=e3 { //~~~~~~~~~~~~~and this direction points inward l2=l3; searchmode=1; } else { //calculate the decreasing direction of e2=e3 as (dF, dG) den=m2*m3*(k2-k3)/2; dF=-(k2*m2*tmp3-k3*m3*tmp2)/den, dG=(m2*tmp3-m3*tmp2)/den; //calculate the negative gradient of e1-e2 as (gF, gG) gF=-gF, gG=-gG; if (dF*gF+dG*gG>0 && x1*dG-y1*dF<0 && x0*dG-y0*dF<0) //so that e1-e2 decreases in the decreasing direction of e2=e3 { l1=l3; searchmode=1; } else { //calculate the decreasing direction of e1=e2 as (dF, dG) den=m1*m2*(k1-k2)/2; dF=-(k1*m1*tmp2-k2*m2*tmp1)/den, dG=(m1*tmp2-m2*tmp1)/den; //calculate the negative gradient of (e3-e1) as (gF, gG) gF=-(m3/tmp3-m1/tmp1)/2, gG=-(k3*m3/tmp3-k1*m1/tmp1)/2; if (dF*gF+dG*gG>0 && x1*dG-y1*dF<0 && x0*dG-y0*dF<0) //so that e3-e1 decreases in the decreasing direction of e1=e2 { searchmode=1; } else { //see the possibility of a e1!=e2 search //calcualte the dot product of the gradients and (x1, y1) double d1=m1/2/tmp1*(x1+k1*y1), d2=m2/2/tmp2*(x1+k2*y1), d3=m3/2/tmp3*(x1+k3*y1); //we can also prove that if there is a direction pointing inward R in which // e1, e2, e2 decrease, and another direction pointing outside R in // which e1, e2, e3 decrease, all the three decrease either on nc->(nc+1) // direction along side nc:(nc+1), or on nc->(nc-1) direction along side // nc:(nc-1). // // On the contrary, if there is a direction // in which all the three decrease, with two equal, it has to point // outward for the program to get here. Then if along neither direction // of side R can the three all descend, then there doesn't exist any // direction inward R in which the three descend. In that case we // have a minimal maximum at (F0, G0). if (d1<0 && d2<0 && d3<0) //so that all the three decrease in the direction (x1, y1) { if (d1<d2 && d1<d3) //e1 decreases fastest { l1=l3; } else if (d2<=d1 && d2<d3) //e2 decreases fastest { l2=l3; } else //d3<=d1 && d3<=d2 { //keep e1, e2 } nd=n1; searchmode=2; } else { d1=m1/2/tmp1*(x0+k1*y0), d2=m2/2/tmp2*(x0+k2*y0), d3=m3/2/tmp3*(x0+k3*y0); if (d1>0 && d2>0 && d3>0) //so that all the three decrease in the direction -(x0, y0) { if (d1>d2 && d1>d3) //e1 decreases fastest { l1=l3; //keep e2, e3 } else if (d2>=d1 && d2>d3) //e2 decreases fastest { l2=l3; //keep e1, e3 } else //d3>=d1 && d3>=d2, e3 decreases fastest { //keep e1, e2 } nd=n0; searchmode=2; } else { F1=F0, G1=G0; searchmode=0; minimax=true; } } } } } } else if (condmax==2) //e1=e2 being the maximum { //first see if the decreasing direction of e1=e2 points to the inside //calculate the decreasing direction of e1=e2 as (dF, dG) double den=m1*m2*(k1-k2)/2; dF=-(k1*m1*tmp2-k2*m2*tmp1)/den, dG=(m1*tmp2-m2*tmp1)/den; if (x1*dG-y1*dF<0 && x0*dG-y1*dF<0) //so that (dF, dG) points inward R { searchmode=1; } else { //calcualte the dot product of the gradients and (x1, y1) double d1=m1/2/tmp1*(x1+k1*y1), d2=m2/2/tmp2*(x1+k2*y1); if (d1<0 && d2<0) //so that along side nc:(nc+1) e1, e2 descend in direction (x1, y1) { nd=n1; searchmode=2; } else { d1=m1/2/tmp1*(x0+k1*y0), d2=m2/2/tmp2*(x0+k2*y0); if (d1>0 && d2>0) //so that slong the side (nc-1):nc e1, e2 decend in direction -(x0, y0) { nd=n0; searchmode=2; } else { F1=F0, G1=G0; searchmode=0; minimax=true; } } } } else //condmax==3, e1 being the solo maximum { //calculate the negative gradient of e1 as (dF, dG) dF=-0.5*m1/tmp1; dG=k1*dF; if (x1*dG-y1*dF<0 && x0*dG-y0*dF<0) //so the gradient points inward R searchmode=3; else { //calculate the dot product of the gradient and (x1, y1) double d1=m1/2/tmp1*(x1+k1*y1), d0=-m1/2/tmp1*(x0+k1*y0); if (d1<d0) //so that e1 decreases in direction (x1, y1) faster than in -(x0, y0) { if (d1<0) { nd=n1; searchmode=4; } else { F1=F0, G1=G0; searchmode=0; minimax=true; } } else //so that e1 decreases in direction -(x0, y0) faster than in (x1, y1) { if (d0<0) { nd=n0; searchmode=4; } else { F1=F0, G1=G0; searchmode=0; minimax=true; } } } } } // the conditioning stage ends here // the searching step starts here if (searchmode==0) {} else if (searchmode==1) { //Search mode 1: e1=e2 search // starting with e1=e2, search down the decreasing direction of // e1=e2 until at point (F1, G1) there is another l3 so that e1(F1, G1) // =e2(F1, G1)=e3(F1, G1), or until at point (F1, G1) the search is about // to go out of R. //Arguments: l1, l2, (F0, G0, vmax) double tmp=F0+k[l1]*G0; testnn(tmp); double e1=m[l1]*sqrt(tmp)-f[l1]; tmp=F0+k[l2]*G0; testnn(tmp); double e2=m[l2]*sqrt(tmp)-f[l2]; if (fabs(e1-e2)>1e-4) { // int kk=1; goto start; } //find intersections of e1=e2 with other ek's l3=-1; loopl3: double e=-1; for (int l=0; l<L; l++) { if (l==l1 || l==l2) continue; double lF1[2], lG1[2], lE[2]; double lm[3]={m[l1], m[l2], m[l]}; double lf[3]={f[l1], f[l2], f[l]}; int lk[3]={k[l1], k[l2], k[l]}; int r=FGFrom3(lF1, lG1, lE, lm, lf, lk); for (int i=0; i<r; i++) { if (lE[i]>e && lE[i]<vmax) l3=l, e=lE[i], F1=lF1[i], G1=lG1[i]; } } //l3 shall be >=0 at this point //find intersections of e1=e2 with polygon sides if (l3<0) { ///int kkk=1; goto loopl3; } nc=-1; double F2, G2; for (int n=0; n<N; n++) { int nn=(n==N-1)?0:(n+1); double xn1=F[nn]-F[n], yn1=G[nn]-G[n], xn2=F1-F[n], yn2=G1-G[n]; if (xn1*yn2-xn2*yn1>=0) //so that (F1, G1) is on or out of side n:(n+1) { nc=n; break; } } if (nc<0) //(F1, G1) being inside R { vmax=e; condpos=1; condmax=1; //l3 shall be >=0 at this point, so l1, l2, l3 are all ok } else //(F1, G1) being outside nc:(nc+1) //(F2, G2) being on side nc:(nc+1) { //find where e1=e2 crosses a side of R between (F0, G0) and (F1, G1) double lF2[2], lG2[2], lmd[2]; double lm[2]={m[l1], m[l2]}; double lf[2]={f[l1], f[l2]}; int lk[2]={k[l1], k[l2]}; int ncc=-1; while (ncc<0) { n1=(nc==N-1)?0:(nc+1); double xn1=F[n1]-F[nc], yn1=G[n1]-G[nc]; int r=FGFrom2(lF2, lG2, lmd, F[nc], xn1, G[nc], yn1, lm, lf, lk); bool once=false; for (int i=0; i<r; i++) { double tmp=lF2[i]+k[l1]*lG2[i]; testnn(tmp); double le=m[l1]*sqrt(tmp)-f[l1]; if (le<=vmax) //this shall be satisfied once { once=true; if (lmd[i]>=0 && lmd[i]<=1) //so that curve e1=e2 does cross the boundry within it ncc=nc, F2=lF2[i], G2=lG2[i], e=le; else if (lmd[i]>1) //so that the crossing point is out of vertex n1 nc=n1; else //lmd[i]<0, so that the crossing point is out of vertex nc-1 nc=(nc==0)?(N-1):(nc-1); } } if (!once) { //int kk=0; goto start; } } nc=ncc; vmax=e; condpos=2; if (F1==F2 && G1==G2) condmax=1; else condmax=2; F1=F2, G1=G2; } } else if (searchmode==2) { // Search mode 2 (e1!=e2 search): starting with e1=e2, and a linear equation // constraint, search down the decreasing direction until at point (F1, G1) // there is a l3 so that e3(F1, G1)=max(e1, e2)(F1, G1), or at point (F1, G1) // the search is about to go out of R. // Arguments: l1, l2, dF, dG, (F0, G0, vmax) //The searching direction (dF, dG) is always along some polygon side // If conpos==2, it is along nc:(nc+1) // If conpos==3, it is along nc:(nc+1) or (nc-1):nc // //first check whether e1>e2 or e2>e1 down (dF, dG) dF=F[nd]-F0, dG=G[nd]-G0; // //the negative gradient of e2-e1 is calculated as (gF, gG) double gF=-(m2/tmp2-m1/tmp1)/2, gG=-(k2*m2/tmp2-k1*m1/tmp1)/2; if (gF*dF+gG*dG>0) //e2-e1 decrease down direction (dF, dG), so that e1>e2 {} else //e1<e2 down (dF, dG) { int ll=l2; l2=l1; l1=ll; m1=m[l1], m2=m[l2], k1=k[l1], k2=k[l2]; tmp=F0+k1*G0; testnn(tmp); tmp1=sqrt(tmp); tmp=F0+k2*G0; testnn(tmp); tmp2=sqrt(tmp); } //now e1>e2 down (dF, dG) from (F0, G0) tmp=F[nd]+k1*G[nd]; testnn(tmp); double e1=m1*sqrt(tmp)-f[l1]; tmp=F[nd]+k2*G[nd]; testnn(tmp); double e2=m2*sqrt(tmp)-f[l2], FEx, GEx, eEx; int maxlmd=1; if (e1>=e2) { // then e1 is larger than e2 for the whole searching interval FEx=F[nd], GEx=G[nd]; eEx=e1; } else // the they swap somewhere in between, now find it and make it maxlmd { double lF1[2], lG1[2], lmd[2]; double lm[2]={m[l1], m[l2]}; double lf[2]={f[l1], f[l2]}; int lk[2]={k[l1], k[l2]}; int r=FGFrom2(lF1, lG1, lmd, F0, dF, G0, dG, lm, lf, lk); //process the results if (r==2) //must be so { if (lmd[0]<lmd[1]) lmd[0]=lmd[1], FEx=lF1[1], GEx=lG1[1]; else FEx=lF1[0], GEx=lG1[0]; } if (lmd[0]>0 && lmd[0]<1) //must be so maxlmd=lmd[0]; tmp=FEx+k1*GEx; testnn(tmp); eEx=m1*sqrt(tmp)-f[l1]; } l3=-1; // int l4; double e, llmd=maxlmd; int *tpl=new int[L], ctpl=0; for (int l=0; l<L; l++) { if (l==l1 || l==l2) continue; tmp=FEx+k[l]*GEx; testnn(tmp); double le=m[l]*sqrt(tmp)-f[l]; if (le<eEx) { tpl[ctpl]=l; ctpl++; continue; } //solve for the equation system e1(F1, G1)=el(F1, G1), with (F1, G1) on nc:n1 double lF1[2], lG1[2], lmd[2]; double lm[2]={m[l1], m[l]}; double lf[2]={f[l1], f[l]}; int lk[2]={k[l1], k[l]}; int r=FGFrom2(lF1, lG1, lmd, F0, dF, G0, dG, lm, lf, lk); //process the results for (int i=0; i<r; i++) { if (lmd[i]<0 || lmd[i]>maxlmd) continue; //the solution is in the wrong direction if (lmd[i]<llmd) l3=l, F1=lF1[i], G1=lG1[i], llmd=lmd[i]; } } if (ctpl==L-2) //so that at (FEx, GEx) e1 is maximal, equivalent to "l3<0" {} else { //at this point F1 and G1 must have valid values already tmp=F1+k[l1]*G1; testnn(tmp); e=m[l1]*sqrt(tmp)-f[l1]; //test if e1 is maximal at (F1, G1) bool lmax=true; for (int tp=0; tp<ctpl; tp++) { tmp=F1+k[tpl[tp]]*G1; testnn(tmp); double le=m[tpl[tp]]*sqrt(tmp)-f[tpl[tp]]; if (le>e) {lmax=false; break;} } if (!lmax) { for (int tp=0; tp<ctpl; tp++) { double lF1[2], lG1[2], lmd[2]; double lm[2]={m[l1], m[tpl[tp]]}; double lf[2]={f[l1], f[tpl[tp]]}; int lk[2]={k[l1], k[tpl[tp]]}; int r=FGFrom2(lF1, lG1, lmd, F0, dF, G0, dG, lm, lf, lk); //process the results for (int i=0; i<r; i++) { if (lmd[i]<0 || lmd[i]>maxlmd) continue; //the solution is in the wrong direction if (lmd[i]<=llmd) l3=tpl[tp], F1=lF1[i], G1=lG1[i], llmd=lmd[i]; } } tmp=F1+k[l1]*G1; testnn(tmp); e=m[l1]*sqrt(tmp)-f[l1]; } } delete[] tpl; if (l3>=0) //the solution is found in the right direction { vmax=e; if (llmd==1) //(F1, G1) is a vertex of R { nc=nd; condpos=3; condmax=2; l2=l3; } else { if (condpos==3 && nd==n0) nc=n0; condpos=2; condmax=2; l2=l3; } } else if (maxlmd<1) //so that e1=e2 remains maximal at maxlmd { if (condpos==3 && nd==n0) nc=n0; condpos=2; condmax=2; //l1, l2 remain unchanged F1=FEx, G1=GEx; vmax=eEx; } else //so that e1 remains maximal at vertex nd { condpos=3; condmax=3; nc=nd; F1=FEx, G1=GEx; //this shall equal to F0+dF, G0+dG vmax=eEx; } } else if (searchmode==3) { //Search mode 3 (e1 search): starting with e1 being the only maximum, search down // the decreasing direction of e1 until at point (F1, G1) there is another l2 so // that e1(F1, G1)=e2(F1, G1), or until at point (F1, G1) the search is about // to go out of R. // // Arguments: l1, dF, dG, (F0, G0, vmax) // //first calculate the value of lmd from (F0, G0) to get out of R double maxlmd=-1, FEx, GEx; double fdggdf=-F0*dG+G0*dF; nd=-1; for (int n=0; n<N; n++) { if (condpos==2 && n==nc) continue; if ((condpos==3 && n==nc) || n==n0) continue; int nn=(n==N-1)?0:n+1; double xn1=F[n], xn2=F[nn], yn1=G[n], yn2=G[nn]; double test1=xn1*dG-yn1*dF+fdggdf, test2=xn2*dG-yn2*dF+fdggdf; if (test1*test2<=0) { //the two following are equivalent. we use the larger denominator. FEx=(xn1*test2-xn2*test1)/(test2-test1); GEx=(yn1*test2-yn2*test1)/(test2-test1); if (fabs(dF)>fabs(dG)) maxlmd=(FEx-F0)/dF; else maxlmd=(GEx-G0)/dG; nd=n; } } //maxlmd must be >0 at this point. l2=-1; tmp=FEx+k[l1]*GEx; testnn(tmp); double e, eEx=m[l1]*sqrt(tmp)-f[l1], llmd=maxlmd; int *tpl=new int[L], ctpl=0; for (int l=0; l<L; l++) { if (l==l1) continue; //if el does not catch up with e1 at (FEx, GEx), then there is no hope this // happens in between (F0, G0) and (FEx, GEx) tmp=FEx+k[l]*GEx; testnn(tmp); double le=m[l]*sqrt(tmp)-f[l]; if (le<eEx) { tpl[ctpl]=l; ctpl++; continue; } //now the existence of (F1, G1) is guaranteed double lF1[2], lG1[2], lmd[2]; double lm[2]={m[l1], m[l]}; double lf[2]={f[l1], f[l]}; int lk[2]={k[l1], k[l]}; int r=FGFrom2(lF1, lG1, lmd, F0, dF, G0, dG, lm, lf, lk); for (int i=0; i<r; i++) { if (lmd[i]<0 || lmd[i]>maxlmd) continue; if (lmd[i]<=llmd) llmd=lmd[i], l2=l, F1=lF1[i], G1=lG1[i]; } } if (ctpl==L-1) //e1 is maximal at eEx, equivalent to "l2<0" {}//l2 must equal -1 at this point else { tmp=F1+k[l1]*G1; testnn(tmp); e=m[l1]*sqrt(tmp)-f[l1]; //test if e1 is maximal at (F1, G1) //e1 is already maximal of those l's not in tpl[ctpl] bool lmax=true; for (int tp=0; tp<ctpl; tp++) { tmp=F1+k[tpl[tp]]*G1; testnn(tmp); double le=m[tpl[tp]]*sqrt(tmp)-f[tpl[tp]]; if (le>e) {lmax=false; break;} } if (!lmax) { for (int tp=0; tp<ctpl; tp++) { double lF1[2], lG1[2], lmd[2]; double lm[2]={m[l1], m[tpl[tp]]}; double lf[2]={f[l1], f[tpl[tp]]}; int lk[2]={k[l1], k[tpl[tp]]}; int r=FGFrom2(lF1, lG1, lmd, F0, dF, G0, dG, lm, lf, lk); for (int i=0; i<r; i++) { if (lmd[i]<0 || lmd[i]>maxlmd) continue; if (llmd>=lmd[i]) llmd=lmd[i], l2=tpl[tp], F1=lF1[i], G1=lG1[i]; } } tmp=F1+k[l1]*G1; testnn(tmp); e=m[l1]*sqrt(tmp)-f[l1]; } } delete[] tpl; if (l2>=0) //so that e1 meets some other ek during the search { vmax=e; //this has to equal m[l2]*sqrt(F1+k[l2]*G1)-f[l2] if (vmax==eEx) { condpos=2; condmax=2; nc=nd; } else { condpos=1; condmax=2; } } else //l2==-1 { vmax=eEx; F1=FEx, G1=GEx; condpos=2; condmax=3; nc=nd; } } else //searchmode==4 { //Search mode 4: a special case of search mode 3 in which the boundry constraint // is simply 0<=lmd<=1. dF=F[nd]-F0, dG=G[nd]-G0; l2=-1; int *tpl=new int[L], ctpl=0; tmp=F[nd]+k[l1]*G[nd]; testnn(tmp); double e, eEx=m[l1]*sqrt(tmp)-f[l1], llmd=1; for (int l=0; l<L; l++) { if (l==l1) continue; //if el does not catch up with e1 at (F[nd], G[nd]), then there is no hope this // happens in between (F0, G0) and vertex nd. tmp=F[nd]+k[l]*G[nd]; testnn(tmp); double le=m[l]*sqrt(tmp)-f[l]; if (le<eEx) { tpl[ctpl]=l; ctpl++; continue; } //now the existence of (F1, G1) is guaranteed double lF1[2], lG1[2], lmd[2]; double lm[2]={m[l1], m[l]}; double lf[2]={f[l1], f[l]}; int lk[2]={k[l1], k[l]}; loop1: int r=FGFrom2(lF1, lG1, lmd, F0, dF, G0, dG, lm, lf, lk); for (int i=0; i<r; i++) { if (lmd[i]<0 || lmd[i]>1) continue; if (llmd>=lmd[i]) { tmp=lF1[i]+k[l1]*lG1[i]; testnn(tmp); double e1=m[l1]*sqrt(tmp)-f[l1]; tmp=lF1[i]+k[l]*lG1[i]; testnn(tmp); double e2=m[l]*sqrt(tmp)-f[l]; if (fabs(e1-e2)>1e-4) { //int kk=0; goto loop1; } llmd=lmd[i], l2=l, F1=lF1[i], G1=lG1[i]; } } } if (ctpl==N-1) //so e1 is maximal at (F[nd], G[nd]) {} else { tmp=F1+k[l1]*G1; testnn(tmp); e=m[l1]*sqrt(tmp)-f[l1]; //test if e1 is maximal at (F1, G1) bool lmax=true; for (int tp=0; tp<ctpl; tp++) { tmp=F1+k[tpl[tp]]*G1; testnn(tmp); double le=m[tpl[tp]]*sqrt(tmp)-f[tpl[tp]]; if (le>e) {lmax=false; break;} } if (!lmax) { for (int tp=0; tp<ctpl; tp++) { double lF1[2], lG1[2], lmd[2]; double lm[2]={m[l1], m[tpl[tp]]}; double lf[2]={f[l1], f[tpl[tp]]}; int lk[2]={k[l1], k[tpl[tp]]}; int r=FGFrom2(lF1, lG1, lmd, F0, dF, G0, dG, lm, lf, lk); for (int i=0; i<r; i++) { if (lmd[i]<0 || lmd[i]>1) continue; if (llmd>=lmd[i]) llmd=lmd[i], l2=tpl[tp], F1=lF1[i], G1=lG1[i]; } } // e=m[l1]*sqrt(F1+k[l1]*G1)-f[l1]; } } tmp=F1+k[l1]*G1; testnn(tmp); e=m[l1]*sqrt(tmp)-f[l1]; delete[] tpl; if (l2>=0) { vmax=e; if (vmax==eEx) { condpos=3; condmax=2; nc=nd; } else { condpos=2; condmax=2; //(F1, G1) lies on side n0:nc (nd=n0) or nc:n1 (nd=nc or nd=n1) if (nd==n0) nc=n0; //else nc=nc; } } else //l2==-1, { vmax=eEx; condpos=3; condmax=3; F1=F[nd], G1=G[nd]; nc=nd; // } } F0=F1, G0=G1; if (condmax==1 && ((l1^l2)==1 || (l1^l3)==1 || (l2^l3)==1)) minimax=true; else if (condmax==2 && ((l1^l2)==1)) minimax=true; else if (vmax<1e-6) minimax=true; } free(m);//delete[] m; delete[] vmnl; return vmax; }//minimaxstiff*/ /* function NMResultToAtoms: converts the note-match result hosted in a NMResults structure, i.e. parameters of a harmonic atom, to a list of atoms. The process retrieves atom parameters from low partials until a required number of atoms have been retrieved or a non-positive frequency is encountered (non-positive frequencies are returned by note-match to indicate high partials for which no informative estimates can be obtained). In: results: the NMResults structure hosting the harmonic atom M: number of atoms to request from &results t: time of this harmonic atom wid: scale of this harmonic atom with which the note-match was performed Out: HP[return value]: list of atoms retrieved from $result. Returns the number of atoms retrieved. */ int NMResultToAtoms(int M, atom* HP, int t, int wid, NMResults results) { double *fpp=results.fp, *vfpp=results.vfp, *pfpp=results.pfp; atomtype* ptype=results.ptype; for (int i=0; i<M; i++) { if (fpp[i]<=0) {memset(&HP[i], 0, sizeof(atom)*(M-i)); return i;} HP[i].t=t, HP[i].s=wid, HP[i].f=fpp[i]/wid, HP[i].a=vfpp[i]; HP[i].p=pfpp[i]; HP[i].type=ptype[i]; HP[i].pin=i+1; } return M; }//NMResultToAtoms /* function NMResultToPartials: reads atoms of a harmonic atom in a NMResult structure into HS partials. In: results: the NMResults structure hosting the harmonic atom M: number of atoms to request from &results fr: frame index of this harmonic atom t: time of this harmonic atom wid: scale of this harmonic atom with which the note-match was performed Out: Partials[M][]: HS partials whose fr-th frame is updated to the harmonic partial read from $results Returns the number of partials retrieved. */ int NMResultToPartials(int M, int fr, atom** Partials, int t, int wid, NMResults results) { double *fpp=results.fp, *vfpp=results.vfp, *pfpp=results.pfp; atomtype* ptype=results.ptype; for (int i=0; i<M; i++) { atom* part=&Partials[i][fr]; if (fpp[i]<=0) {for (int j=i; j<M; j++) memset(&Partials[j][fr], 0, sizeof(atom)); return i;} part->t=t, part->s=wid, part->f=fpp[i]/wid, part->a=vfpp[i]; part->p=pfpp[i]; part->type=ptype[i]; part->pin=i+1; } return M; }//NMResultToPartials /* function NoteMatchStiff3: finds harmonic atom from spectrum if Fr=1, or constant-pitch harmonic sinusoid from spectrogram if Fr>1. In: x[Fr][N/2+1]: spectrogram fps[pc], vps[pc]: primitive (rough) peak frequencies and amplitudes N, offst: atom scale and hop size R: initial F-G polygon constraint, optional settings: note match settings computes: pointer to a function that computes HA score, must be ds0 or ds1 lastvfp[lastp]: amplitude of the previous harmonic atom forceinputlocalfr: specifies if partial settings->pin0 is taken for granted ("pinned") Out: results: note match results f0, B: fundamental frequency and stiffness coefficient R: F-G polygon of harmonic atom Returns the total energy of HA or constant-pitch HS. */ double NoteMatchStiff3(TPolygon* R, double &f0, double& B, int pc, double* fps, double* vps, int Fr, cdouble** x, int N, int offst, NMSettings* settings, NMResults* results, int lastp, double* lastvfp, double (*computes)(double a, void* params), int forceinputlocalfr) { double result=0; double maxB=settings->maxB, minf0=settings->minf0, maxf0=settings->maxf0, delm=settings->delm, delp=settings->delp, *c=settings->c, iH2=settings->iH2, *pinf=settings->pinf, hB=settings->hB, epf0=settings->epf0;// epf=settings->epf, int maxp=settings->maxp, *pin=settings->pin, pin0=settings->pin0, *pinfr=settings->pinfr, pcount=settings->pcount, M=settings->M; // int *ptype=results->ptype; //calculate the energy of the last frame (hp) double lastene=0; for (int i=0; i<lastp; i++) lastene+=lastvfp[i]*lastvfp[i]; //fill frequency and amplitude buffers with 0 //now determine numsam (the maximal number of partials) bool inputpartial=(f0>0 && settings->pin0>0); if (!inputpartial) { if (pcount>0) f0=pinf[0], pin0=pin[0]; else f0=sqrt(R->X[0]), pin0=1; } int numsam=N*pin0/2.1/f0; if (numsam>maxp) numsam=maxp; //allocate buffer for candidate atoms TTempAtom*** Allocate2(TTempAtom*, numsam+1, MAX_CAND, cands); //pcs[p] records the number of candidates for partial index p //psb[p] = 1: anchor, 2: user input, 0: normal int *psb=new int[(numsam+1)*2], *pcs=&psb[numsam+1]; memset(psb, 0, sizeof(int)*(numsam+1)*2); //if R is not specified, initialize a trivial candidate with maxB if (R->N==0) cands[0][0]=new TTempAtom(1, (minf0+maxf0)*0.5, (maxf0-minf0)*0.5, maxB); //if R is, initialize a trivial candidate by extending R by delp else cands[0][0]=new TTempAtom(R, delp, delp, minf0); int pm=0, pM=0; int ind=1; pcs[0]=1; //anchor partials: highest priority atoms, must have spectral peaks for (int i=0; i<pcount; i++) { //skip out-of-scope atoms if (pin[i]>=numsam) continue; //skip user input atom if (inputpartial && pin[i]==pin0) continue; void* dsparams; if (computes==ds0) dsparams=&cands[ind-1][0]->s; else { dsparams1 params={cands[ind-1][0]->s, lastene, cands[ind-1][0]->acce, pin[i], lastp, lastvfp}; dsparams=¶ms; } if (pinfr[i]>0) //local anchor { double ls=0; for (int fr=0; fr<Fr; fr++) { cdouble r=IPWindowC(pinf[i], x[fr], N, M, c, iH2, pinf[i]-hB, pinf[i]+hB); ls+=~r; } ls=sqrt(ls/Fr); cands[ind][0]=new TTempAtom(cands[ind-1][0], pin[i], pinf[i], delm, ls, computes(ls, dsparams)); } else { //find the nearest peak to pinf[i] while (pm<pc-1 && fps[pm]<pinf[i]) pm++; while (pm>0 && fps[pm]>=pinf[i]) pm--; if (pm<pc-1 && pinf[i]-fps[pm]>fps[pm+1]-pinf[i]) pm++; cands[ind][0]=new TTempAtom(cands[ind-1][0], pin[i], fps[pm], delm, vps[pm], computes(vps[pm], dsparams)); } delete cands[ind-1][0]->R; cands[ind-1][0]->R=0; psb[pin[i]]=1; pcs[ind]=1; ind++; } //harmonic atom grouping fails if anchors conflict if (cands[ind-1][0]->R->N==0) {f0=0; goto cleanup;} //user input partial: must have spectral peak if (inputpartial) { //harmonic atom grouping fails if user partial is out of scope if (pin0>numsam){f0=0; goto cleanup;} TTempAtom* lcand=cands[ind-1][0]; //feasible frequency range for partial pin0 double f1, f2; ExFmStiff(f1, f2, pin0, lcand->R->N, lcand->R->X, lcand->R->Y); f1-=delm, f2+=delm; if (f1<0) f1=0; if (f1>N/2.1) f1=N/2.1; if (f2>N/2.1) f2=N/2.1; //forcepin0 being true means this partial have to be the peak nearest to f0 bool inputfound=false; if (forceinputlocalfr>=0) { int k1=floor(f0-epf0-1), k2=ceil(f0+epf0+1), k12=k2-k1+1, pk=-1; cdouble *lx=x[forceinputlocalfr], *lr=new cdouble[k12]; for (int k=0; k<k12; k++) lr[k]=IPWindowC(k1+k, lx, N, M, c, iH2, k1+k-hB, k1+k+hB); for (int k=1; k<k12-1; k++) if (~lr[k]>~lr[k-1] && ~lr[k]>~lr[k+1]) { if (pk==-1 || fabs(k1+pk-f0)>fabs(k1+k-f0)) pk=k; } if (pk>0) { double lf=k1+pk, la, lph, oldlf=lf; LSESinusoid(lf, lf-1, lf+1, lx, N, hB, M, c, iH2, la, lph, settings->epf); if (fabs(lf-oldlf)>0.6) { lf=oldlf; cdouble r=IPWindowC(lf, lx, N, M, c, iH2, lf-hB, lf+hB); la=abs(r); lph=arg(r); } if (lf>f1 && lf<f2) { void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } cands[ind][0]=new TTempAtom(lcand, pin0, lf, delm, la, computes(la, dsparams)); pcs[ind]=1; inputfound=true; } } } if (!inputfound && settings->forcepin0) { //find the nearest peak to f0 while (pm<pc-1 && fps[pm]<f0) pm++; while (pm>0 && fps[pm]>=f0) pm--; if (pm<pc-1 && f0-fps[pm]>fps[pm+1]-f0) pm++; if (fps[pm]>f1 && fps[pm]<f2) { void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } cands[ind][0]=new TTempAtom(lcand, pin0, fps[pm], delm, vps[pm], computes(vps[pm], dsparams)); pcs[ind]=1; } } else if (!inputfound) { double epf0=settings->epf0; //lpcs records number of candidates found for partial pin0 int lpcs=0; //lcoate peaks with the feasible frequency range, specified by f0 and epf0 while (pm>0 && fps[pm]>=f0-epf0) pm--; while (pm<pc-1 && fps[pm]<f0-epf0) pm++; while (pM<pc-1 && fps[pM]<=f0+epf0) pM++; while (pM>0 && fps[pM]>f0+epf0) pM--; //add peaks as candidates for (int j=pm; j<=pM; j++) if (fps[j]>f1 && fps[j]<f2) { void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } cands[ind][lpcs]=new TTempAtom(lcand, pin0, fps[j], delm, vps[j], computes(vps[j], dsparams)); lpcs++; } pcs[ind]=lpcs; } //harmonic atom grouping fails if user partial is not found if (pcs[ind]==0){f0=0; goto cleanup;} else {delete lcand->R; lcand->R=0;} psb[pin0]=2; ind++; } //normal partials (non-anchor, non-user) //p: partial index { int p=0; while (ind<=numsam) { p++; //skip anchor or user input partials if (psb[p]) continue; //loop over all candidates for (int i=0; i<pcs[ind-1]; i++) { //the ith candidate of last partial TTempAtom* lcand=cands[ind-1][i]; //lpcs records number of candidates found for partial p int lpcs=0; //the feasible frequency range for partial p double f1, f2; ExFmStiff(f1, f2, p, lcand->R->N, lcand->R->X, lcand->R->Y); //calculate the frequency range to search for peak f1-=delm, f2+=delm; //allow a frequency error for the new partial if (f1<0) f1=0; if (f1>N/2.1) f1=N/2.1; if (f2>N/2.1) f2=N/2.1; //locate peaks within this range while (pm>0 && fps[pm]>=f1) pm--; while (pm<pc-1 && fps[pm]<f1) pm++; while (pM<pc-1 && fps[pM]<=f2) pM++; while (pM>0 && fps[pM]>f2) pM--; //an index range for peaks //add peaks as candidates for (int j=pm; j<=pM; j++) { if (fps[j]>f1 && fps[j]<f2) //this peak frequency falls in the frequency range { void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } cands[ind][pcs[ind]+lpcs]=new TTempAtom(lcand, p, fps[j], delm, vps[j], computes(vps[j], dsparams)); lpcs++; if (pcs[ind]+lpcs>=MAX_CAND) {int newpcs=DeleteByHalf(cands[ind], pcs[ind]); memcpy(&cands[ind][newpcs], &cands[ind][pcs[ind]], sizeof(TTempAtom)*lpcs); pcs[ind]=newpcs;} } } //if there is no peak found for partial p if (lpcs==0) { cands[ind][pcs[ind]+lpcs]=lcand; lpcs++; cands[ind-1][i]=0; if (pcs[ind]+lpcs>=MAX_CAND){int newpcs=DeleteByHalf(cands[ind], pcs[ind]); memcpy(&cands[ind][newpcs], &cands[ind][pcs[ind]], sizeof(TTempAtom*)*lpcs); pcs[ind]=newpcs;} } else{delete lcand->R; lcand->R=0;} pcs[ind]+=lpcs; } ind++; } //select the candidate with maximal s int maxs=0; double vmax=cands[ind-1][0]->s; for (int i=1; i<pcs[ind-1]; i++) if (vmax<cands[ind-1][i]->s) maxs=i, vmax=cands[ind-1][i]->s; TTempAtom* lcand=cands[ind-1][maxs]; //get results //read frequencies of found partials int P=0; int* ps=new int[maxp]; double* fs=new double[maxp]; //these are used for evaluating f0 and B while (lcand){if (lcand->pind) {ps[P]=lcand->pind; fs[P]=lcand->f; P++;} lcand=lcand->Prev;} //read R of candidate with maximal s as R0 TPolygon* R0=cands[ind-1][maxs]->R; if (Fr==1) result=GetResultsSingleFr(f0, B, R, R0, P, ps, fs, 0, x[0], N, psb, numsam, settings, results); else result=GetResultsMultiFr(f0, B, R, R0, P, ps, fs, 0, Fr, x, N, offst, psb, numsam, settings, results, forceinputlocalfr); delete[] ps; delete[] fs; } cleanup: for (int i=0; i<=numsam; i++) for (int j=0; j<pcs[i]; j++) delete cands[i][j]; DeAlloc2(cands); delete[] psb; return result; }//NoteMatchStiff3 /* function NoteMatchStiff3: finds harmonic atom from spectrum if Fr=1, or constant-pitch harmonic sinusoid from spectrogram if Fr>1. This version uses residue-sinusoid ratio ("rsr") that measures how "good" a spectral peak is in terms of its shape. peaks with large rsr[] is likely to be contaminated and their frequency estimates are regarded unreliable. In: x[Fr][N/2+1]: spectrogram N, offst: atom scale and hop size fps[pc], vps[pc], rsr[pc]: primitive (rough) peak frequencies, amplitudes and shape factor R: initial F-G polygon constraint, optional settings: note match settings computes: pointer to a function that computes HA score, must be ds0 or ds1 lastvfp[lastp]: amplitude of the previous harmonic atom forceinputlocalfr: specifies if partial settings->pin0 is taken for granted ("pinned") Out: results: note match results f0, B: fundamental frequency and stiffness coefficient R: F-G polygon of harmonic atom Returns the total energy of HA or constant-pitch HS. */ double NoteMatchStiff3(TPolygon* R, double &f0, double& B, int pc, double* fps, double* vps, double* rsr, int Fr, cdouble** x, int N, int offst, NMSettings* settings, NMResults* results, int lastp, double* lastvfp, double (*computes)(double a, void* params), int forceinputlocalfr) { double result=0; double maxB=settings->maxB, minf0=settings->minf0, maxf0=settings->maxf0, delm=settings->delm, delp=settings->delp, *c=settings->c, iH2=settings->iH2, *pinf=settings->pinf, hB=settings->hB, epf0=settings->epf0;// epf=settings->epf, int maxp=settings->maxp, *pin=settings->pin, pin0=settings->pin0, *pinfr=settings->pinfr, pcount=settings->pcount, M=settings->M; // int *ptype=results->ptype; //calculate the energy of the last frame (hp) double lastene=0; for (int i=0; i<lastp; i++) lastene+=lastvfp[i]*lastvfp[i]; //fill frequency and amplitude buffers with 0 //now determine numsam (the maximal number of partials) bool inputpartial=(f0>0 && settings->pin0>0); if (!inputpartial) { if (pcount>0) f0=pinf[0], pin0=pin[0]; else f0=sqrt(R->X[0]), pin0=1; } int numsam=N*pin0/2.1/f0; if (numsam>maxp) numsam=maxp; //allocate buffer for candidate atoms TTempAtom*** Allocate2(TTempAtom*, numsam+1, MAX_CAND, cands); //pcs[p] records the number of candidates for partial index p //psb[p] = 1: anchor, 2: user input, 0: normal int *psb=new int[(numsam+1)*2], *pcs=&psb[numsam+1]; memset(psb, 0, sizeof(int)*(numsam+1)*2); //if R is not specified, initialize a trivial candidate with maxB if (R->N==0) cands[0][0]=new TTempAtom(1, (minf0+maxf0)*0.5, (maxf0-minf0)*0.5, maxB); //if R is, initialize a trivial candidate by extending R by delp else cands[0][0]=new TTempAtom(R, delp, delp, minf0); int pm=0, pM=0; int ind=1; pcs[0]=1; //anchor partials: highest priority atoms, must have spectral peaks for (int i=0; i<pcount; i++) { //skip out-of-scope atoms if (pin[i]>=numsam) continue; //skip user input atom if (inputpartial && pin[i]==pin0) continue; void* dsparams; if (computes==ds0) dsparams=&cands[ind-1][0]->s; else { dsparams1 params={cands[ind-1][0]->s, lastene, cands[ind-1][0]->acce, pin[i], lastp, lastvfp}; dsparams=¶ms; } if (pinfr[i]>0) //local anchor { double ls=0, lrsr=PeakShapeC(pinf[i], 1, N, &x[pinfr[i]], hB*2, M, c, iH2); for (int fr=0; fr<Fr; fr++) { cdouble r=IPWindowC(pinf[i], x[fr], N, M, c, iH2, pinf[i]-hB, pinf[i]+hB); ls+=~r; } ls=sqrt(ls/Fr); cands[ind][0]=new TTempAtom(cands[ind-1][0], pin[i], pinf[i], delm+lrsr, ls, computes(ls, dsparams)); cands[ind][0]->rsr=lrsr; } else { //find the nearest peak to pinf[i] while (pm<pc-1 && fps[pm]<pinf[i]) pm++; while (pm>0 && fps[pm]>=pinf[i]) pm--; if (pm<pc-1 && pinf[i]-fps[pm]>fps[pm+1]-pinf[i]) pm++; cands[ind][0]=new TTempAtom(cands[ind-1][0], pin[i], fps[pm], delm+rsr[pm], vps[pm], computes(vps[pm], dsparams)); cands[ind][0]->rsr=rsr[pm]; } delete cands[ind-1][0]->R; cands[ind-1][0]->R=0; psb[pin[i]]=1; pcs[ind]=1; ind++; } //harmonic atom grouping fails if anchors conflict if (cands[ind-1][0]->R->N==0) {f0=0; goto cleanup;} //user input partial: must have spectral peak if (inputpartial) { //harmonic atom grouping fails if user partial is out of scope if (pin0>numsam){f0=0; goto cleanup;} TTempAtom* lcand=cands[ind-1][0]; //feasible frequency range for partial pin0 double f1, f2; ExFmStiff(f1, f2, pin0, lcand->R->N, lcand->R->X, lcand->R->Y); f1-=delm, f2+=delm; if (f1<0) f1=0; if (f1>N/2.1) f1=N/2.1; if (f2>N/2.1) f2=N/2.1; bool inputfound=false; if (forceinputlocalfr>=0) { int k1=floor(f0-epf0-1), k2=ceil(f0+epf0+1), k12=k2-k1+1, pk=-1; cdouble *lx=x[forceinputlocalfr], *lr=new cdouble[k12]; for (int k=0; k<k12; k++) lr[k]=IPWindowC(k1+k, lx, N, M, c, iH2, k1+k-hB, k1+k+hB); for (int k=1; k<k12-1; k++) if (~lr[k]>~lr[k-1] && ~lr[k]>~lr[k+1]) { if (pk==-1 || fabs(k1+pk-f0)>fabs(k1+k-f0)) pk=k; } if (pk>0) { double lf=k1+pk, la, lph, oldlf=lf; LSESinusoid(lf, lf-1, lf+1, lx, N, hB, M, c, iH2, la, lph, settings->epf); if (fabs(lf-oldlf)>0.6) //by which we judge that the LS result is biased by interference { lf=oldlf; cdouble r=IPWindowC(lf, lx, N, M, c, iH2, lf-hB, lf+hB); la=abs(r); lph=arg(r); } if (lf>f1 && lf<f2) { void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } double lrsr=PeakShapeC(lf, 1, N, &x[forceinputlocalfr], hB*2, M, c, iH2); cands[ind][0]=new TTempAtom(lcand, pin0, lf, delm+lrsr, la, computes(la, dsparams)); cands[ind][0]->rsr=lrsr; pcs[ind]=1; inputfound=true; } } } //forcepin0 being true means this partial have to be the peak nearest to f0 if (!inputfound && settings->forcepin0) { //find the nearest peak to f0 while (pm<pc-1 && fps[pm]<f0) pm++; while (pm>0 && fps[pm]>=f0) pm--; if (pm<pc-1 && f0-fps[pm]>fps[pm+1]-f0) pm++; if (fps[pm]>f1 && fps[pm]<f2) { void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } cands[ind][0]=new TTempAtom(lcand, pin0, fps[pm], delm+rsr[pm], vps[pm], computes(vps[pm], dsparams)); cands[ind][0]->rsr=rsr[pm]; pcs[ind]=1; } } else if (!inputfound) { double epf0=settings->epf0; //lpcs records number of candidates found for partial pin0 int lpcs=0; //lcoate peaks with the feasible frequency range, specified by f0 and epf0 while (pm>0 && fps[pm]>=f0-epf0) pm--; while (pm<pc-1 && fps[pm]<f0-epf0) pm++; while (pM<pc-1 && fps[pM]<=f0+epf0) pM++; while (pM>0 && fps[pM]>f0+epf0) pM--; //add peaks as candidates for (int j=pm; j<=pM; j++) if (fps[j]>f1 && fps[j]<f2) { void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } cands[ind][lpcs]=new TTempAtom(lcand, pin0, fps[j], delm+rsr[j], vps[j], computes(vps[j], dsparams)); cands[ind][lpcs]->rsr=rsr[j]; lpcs++; } pcs[ind]=lpcs; } //harmonic atom grouping fails if user partial is not found if (pcs[ind]==0){f0=0; goto cleanup;} else {delete lcand->R; lcand->R=0;} psb[pin0]=2; ind++; } //normal partials (non-anchor, non-user) //p: partial index { int p=0; while (ind<=numsam) { p++; //skip anchor or user input partials if (psb[p]) continue; //loop over all candidates for (int i=0; i<pcs[ind-1]; i++) { int tightpks=0; //the ith candidate of last partial TTempAtom* lcand=cands[ind-1][i]; //lpcs records number of candidates found for partial p int lpcs=0; //the feasible frequency range for partial p double tightf1, tightf2; ExFmStiff(tightf1, tightf2, p, lcand->R->N, lcand->R->X, lcand->R->Y); //calculate the frequency range to search for peak double tightf=(tightf1+tightf2)*0.5, f1=tightf1-delm, f2=tightf2+delm; //allow a frequency error for the new partial if (f1<0) f1=0; if (f1>N/2.1) f1=N/2.1; if (f2>N/2.1) f2=N/2.1; //locate peaks within this range while (pm>0 && fps[pm]>=f1) pm--; while (pm<pc-1 && fps[pm]<f1) pm++; while (pM<pc-1 && fps[pM]<=f2) pM++; while (pM>0 && fps[pM]>f2) pM--; //an index range for peaks //add peaks as candidates for (int j=pm; j<=pM; j++) { if (fps[j]>f1 && fps[j]<f2) //this peak frequency falls in the frequency range { if ((fps[j]>tightf1 && fps[j]<tightf2) || fabs(fps[j]-tightf)<0.5) tightpks++; //indicates there are "tight" peaks found for this partial void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } cands[ind][pcs[ind]+lpcs]=new TTempAtom(lcand, p, fps[j], delm+rsr[j], vps[j], computes(vps[j], dsparams)); cands[ind][pcs[ind]+lpcs]->rsr=rsr[j]; lpcs++; if (pcs[ind]+lpcs>=MAX_CAND) {int newpcs=DeleteByHalf(cands[ind], pcs[ind]); memcpy(&cands[ind][newpcs], &cands[ind][pcs[ind]], sizeof(TTempAtom*)*lpcs); pcs[ind]=newpcs;} } } //if there is no peak found for partial p. if (!lpcs) { //use the lcand directly //BUT UPDATE accumulative energy (acce) and s using induced partial if (tightf<N/2-hB) { double la=0; for (int fr=0; fr<Fr; fr++) la+=~IPWindowC(tightf, x[fr], N, M, c, iH2, tightf-hB, tightf+hB); la=sqrt(la/Fr); void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } double ls=computes(la, dsparams); lcand->acce+=la*la; lcand->s=ls; } cands[ind][pcs[ind]+lpcs]=lcand; lpcs++; cands[ind-1][i]=0; if (pcs[ind]+lpcs>=MAX_CAND){int newpcs=DeleteByHalf(cands[ind], pcs[ind]); memcpy(&cands[ind][newpcs], &cands[ind][pcs[ind]], sizeof(TTempAtom*)*lpcs); pcs[ind]=newpcs;} } //if there are peaks but no tight peak found for partial p else if (!tightpks) { if (tightf<N/2-hB) { double la=0; for (int fr=0; fr<Fr; fr++) la+=~IPWindowC(tightf, x[fr], N, M, c, iH2, tightf-hB, tightf+hB); la=sqrt(la/Fr); void* dsparams; if (computes==ds0) dsparams=&lcand->s; else { dsparams1 params={lcand->s, lastene, lcand->acce, pin0, lastp, lastvfp}; dsparams=¶ms; } double ls=computes(la, dsparams); TTempAtom* lnewcand=new TTempAtom(lcand, true); lnewcand->f=0; lnewcand->acce+=la*la; lnewcand->s=ls; cands[ind][pcs[ind]+lpcs]=lnewcand; lpcs++; if (pcs[ind]+lpcs>=MAX_CAND) {int newpcs=DeleteByHalf(cands[ind], pcs[ind]); memcpy(&cands[ind][newpcs], &cands[ind][pcs[ind]], sizeof(TTempAtom*)*lpcs); pcs[ind]=newpcs;} } delete lcand->R; lcand->R=0; } else{delete lcand->R; lcand->R=0;} pcs[ind]+=lpcs; } ind++; } //select the candidate with maximal s int maxs=0; double vmax=cands[ind-1][0]->s; for (int i=1; i<pcs[ind-1]; i++) if (vmax<cands[ind-1][i]->s) maxs=i, vmax=cands[ind-1][i]->s; TTempAtom* lcand=cands[ind-1][maxs]; //get results //read frequencies of found partials int P=0; int* ps=new int[maxp]; double* fs=new double[maxp*2], *rsrs=&fs[maxp]; //these are used for evaluating f0 and B while (lcand){if (lcand->pind && lcand->f) {ps[P]=lcand->pind; fs[P]=lcand->f; rsrs[P]=lcand->rsr; P++;} lcand=lcand->Prev;} //read R of candidate with maximal s as R0 TPolygon* R0=cands[ind-1][maxs]->R; if (Fr==1) result=GetResultsSingleFr(f0, B, R, R0, P, ps, fs, rsrs, x[0], N, psb, numsam, settings, results); else result=GetResultsMultiFr(f0, B, R, R0, P, ps, fs, rsrs, Fr, x, N, offst, psb, numsam, settings, results, forceinputlocalfr); delete[] ps; delete[] fs; } cleanup: for (int i=0; i<=numsam; i++) for (int j=0; j<pcs[i]; j++) delete cands[i][j]; DeAlloc2(cands); delete[] psb; return result; }//NoteMatchStiff3 /* function NoteMatchStiff3: wrapper function of the above that does peak picking and HA grouping (or constant-pitch HS finding) in a single call. In: x[Fr][N/2+1]: spectrogram N, offst: atom scale and hop size R: initial F-G polygon constraint, optional startfr, validfrrange: centre and half width, in frames, of the interval used for peak picking if Fr>1 settings: note match settings computes: pointer to a function that computes HA score, must be ds0 or ds1 lastvfp[lastp]: amplitude of the previous harmonic atom forceinputlocalfr: specifies if partial settings->pin0 is taken for granted ("pinned") Out: results: note match results f0, B: fundamental frequency and stiffness coefficient R: F-G polygon of harmonic atom Returns the total energy of HA or constant-pitch HS. */ double NoteMatchStiff3(TPolygon* R, double &f0, double& B, int Fr, cdouble** x, int N, int offst, NMSettings* settings, NMResults* results, int lastp, double* lastvfp, double (*computes)(double a, void* params), bool forceinputlocalfr, int startfr, int validfrrange) { //find spectral peaks double *fps=(double*)malloc8(sizeof(double)*N*2*Fr), *vps=&fps[N/2*Fr], *rsr=&fps[N*Fr]; int pc; if (Fr>1) pc=QuickPeaks(fps, vps, Fr, N, x, startfr, validfrrange, settings->M, settings->c, settings->iH2, 0.0005, -1, -1, settings->hB*2, rsr); else pc=QuickPeaks(fps, vps, N, x[0], settings->M, settings->c, settings->iH2, 0.0005, -1, -1, settings->hB*2, rsr); //if no peaks are present, return 0, indicating empty hp if (pc==0){free8(fps); return 0;} double result=NoteMatchStiff3(R, f0, B, pc, fps, vps, rsr, Fr, x, N, offst, settings, results, lastp, lastvfp, computes, forceinputlocalfr?startfr:-1); free8(fps); return result; }//NoteMatchStiff3 /* function NoteMatchStiff3: finds harmonic atom from spectrum given the pitch as a (partial index, frequency) pair. This is used internally by NoteMatch3FB(). In: x[N/2+1]: spectrum fps[pc], vps[pc]: primitive (rough) peak frequencies and amplitudes R: initial F-G polygon constraint, optional pin0: partial index in the pitch specifier pair peak0: index to frequency in fps[] in the pitch specifier pair settings: note match settings Out: vfp[]: partial amplitudes R: F-G polygon of harmonic atom Returns the total energy of HA. */ double NoteMatchStiff3(TPolygon* R, int peak0, int pin0, cdouble* x, int pc, double* fps, double* vps, int N, NMSettings* settings, double* vfp, int** pitchind, int newpc) { double maxB=settings->maxB, minf0=settings->minf0, maxf0=settings->maxf0, delm=settings->delm, delp=settings->delp, *c=settings->c, iH2=settings->iH2, hB=settings->hB; int maxp=settings->maxp, M=settings->M; // pcount=settings->pcount; double f0=fps[peak0]; //determine numsam int numsam=N*pin0/2.1/f0; if (numsam>maxp) numsam=maxp; if (pin0>=numsam) return 0; //pcs[p] contains the number of candidates for partial index p TTempAtom*** Allocate2(TTempAtom*, numsam+1, MAX_CAND, cands); int *pcs=new int[numsam+1]; memset(pcs, 0, sizeof(int)*(numsam+1)); if (R->N==0) cands[0][0]=new TTempAtom(1, (minf0+maxf0)*0.5, (maxf0-minf0)*0.5, maxB); //initialization: trivial candidate with maxB else cands[0][0]=new TTempAtom(R, delp, delp, minf0); //initialization: trivial candidate by expanding R cands[1][0]=new TTempAtom(cands[0][0], pin0, fps[peak0], delm, vps[peak0], vps[peak0]); cands[1][0]->tag[0]=peak0; delete cands[0][0]->R; cands[0][0]->R=0; int pm=0, pM=0, ind=2, p=0; pcs[0]=pcs[1]=1; while (ind<=numsam) { p++; if (p==pin0) continue; for (int i=0; i<pcs[ind-1]; i++) { TTempAtom* lcand=cands[ind-1][i]; //the the ith candidate of last partial int lpcs=0; { double f1, f2; ExFmStiff(f1, f2, p, lcand->R->N, lcand->R->X, lcand->R->Y); //calculate the frequency range to search for peak f1-=delm, f2+=delm; //allow a frequency error for the new partial if (f1<0) f1=0; if (f1>N/2.1) f1=N/2.1; if (f2>N/2.1) f2=N/2.1; while (pm>0 && fps[pm]>=f1) pm--; while (pm<pc-1 && fps[pm]<f1) pm++; while (pM<pc-1 && fps[pM]<=f2) pM++; while (pM>0 && fps[pM]>f2) pM--; //an index range for peaks for (int j=pm; j<=pM; j++) { if (fps[j]>f1 && fps[j]<f2 && (p>pin0 || pitchind[j][p]<0)) { //this peak frequency falls in the frequency range cands[ind][pcs[ind]+lpcs]=new TTempAtom(lcand, p, fps[j], delm, vps[j], lcand->s+vps[j]); cands[ind][pcs[ind]+lpcs]->tag[0]=j; lpcs++; if (pcs[ind]+lpcs>=MAX_CAND){int newpcs=DeleteByHalf(cands[ind], pcs[ind]); memcpy(&cands[ind][newpcs], &cands[ind][pcs[ind]], sizeof(TTempAtom)*lpcs); pcs[ind]=newpcs;} } } if (lpcs==0) //there is no peak found for the partial i, the last level { cands[ind][pcs[ind]+lpcs]=lcand; lpcs++; //new TTempAtom(lcand, false); cands[ind-1][i]=0; if (pcs[ind]+lpcs>=MAX_CAND){int newpcs=DeleteByHalf(cands[ind], pcs[ind]); memcpy(&cands[ind][newpcs], &cands[ind][pcs[ind]], sizeof(TTempAtom*)*lpcs); pcs[ind]=newpcs;} } else { delete lcand->R; lcand->R=0; } } pcs[ind]+=lpcs; } ind++; } //select the candidate with maximal s int maxs=0; double vmax=cands[ind-1][0]->s; for (int i=1; i<pcs[ind-1]; i++) if (vmax<cands[ind-1][i]->s) maxs=i, vmax=cands[ind-1][i]->s; TTempAtom* lcand=cands[ind-1][maxs]; //get fp, vfp, pfp, ptype memset(vfp, 0, sizeof(double)*maxp); int P=0; int *ps=new int[maxp], *peaks=new int[maxp]; double* fs=new double[maxp]; //these are used for evaluating f0 and B while (lcand){if (lcand->pind) {ps[P]=lcand->pind; fs[P]=lcand->f; peaks[P]=lcand->tag[0]; P++;} lcand=lcand->Prev;} R->N=0; if (P>0) { for (int i=P-1; i>=0; i--) { int lp=ps[i]; double lf=fs[i]; vfp[lp-1]=vps[peaks[i]]; if (R->N==0) InitializeR(R, lp, lf, delm, maxB); else if (vfp[lp-1]>1) CutR(R, lp, lf, delm, true); if (pitchind[peaks[i]][lp]>=0) {R->N=0; goto cleanup;} else pitchind[peaks[i]][lp]=newpc; } //estimate f0 and B double tmpa, cF, cG; // double norm[1024]; for (int i=0; i<1024; i++) norm[i]=1; areaandcentroid(tmpa, cF, cG, R->N, R->X, R->Y); testnn(cF); f0=sqrt(cF); //B=cG/cF; for (int i=0; i<numsam; i++) { if (vfp[i]==0) //no peak is found for this partial in lcand { int m=i+1; double tmp=cF+(m*m-1)*cG; testnn(tmp); double lf=m*sqrt(tmp); if (lf<N/2.1) { cdouble r=IPWindowC(lf, x, N, M, c, iH2, lf-hB, lf+hB); vfp[i]=-sqrt(r.x*r.x+r.y*r.y); // vfp[i]=-IPWindowC(lf, xr, xi, N, M, c, iH2, lf-hB, lf+hB, 0); } } } } cleanup: delete[] ps; delete[] fs; delete[] peaks; for (int i=0; i<=numsam; i++) for (int j=0; j<pcs[i]; j++) delete cands[i][j]; DeAlloc2(cands); delete[] pcs; double result=0; if (f0>0) for (int i=0; i<numsam; i++) result+=vfp[i]*vfp[i]; return result; }//NoteMatchStiff3*/ /* function NoteMatchStiff3FB: does one dynamic programming step in forward-background pitch tracking of harmonic sinusoids. This is used internally by FindNoteFB(). In: R[pitchcount]: initial F-G polygons associated with pitch candidates at last frame vfp[pitchcount][maxp]: amplitude vectors associated with pitch candidates at last frame sc[pitchcount]: accumulated scores associated with pitch candidates at last frame fps[pc], vps[pc]: primitive (rough) peak frequencies and amplitudes at this frame maxpitch: maximal number of pitch candidates x[wid/2+1]: spectrum settings: note match settings Out: pitchcount: number of pitch candidiate at this frame newpitches[pitchcount]: pitch candidates at this frame, whose lower word is peak index, higher word is partial index prev[pitchcount]: indices to predecessors of the pitch candidates at this frame R[pitchcount]: F-G polygons associated with pitch candidates at this frame vfp[pitchcount][maxp]: amplitude vectors associated with pitch candidates at this frame sc[pitchcount]: accumulated scores associated with pitch candidates at this frame No return value. */ void NoteMatchStiff3FB(int& pitchcount, TPolygon**& R, double**& vfp, double*& sc, int* newpitches, int* prev, int pc, double* fps, double* vps, cdouble* x, int wid, int maxpitch, NMSettings* settings) { int maxpin0=6; //this specifies the maximal value that a pitch candidate may have as partial index double minf0=settings->minf0, delm=settings->delm, delp=settings->delp; int maxp=settings->maxp; //pc is the number of peaks at this frame, maxp is the maximal number of partials in the model //pitchind is initialized to a sequence of -1 int** Allocate2(int, pc, maxp+1, pitchind); memset(pitchind[0], 0xff, sizeof(int)*pc*(maxp+1)); //extend F-G polygons to allow pitch variation for (int i=0; i<pitchcount; i++) ExtendR(R[i], delp, delp, minf0); double *f1=new double[pitchcount], *f2=new double[pitchcount]; int pm=0, newpc=0; TPolygon** newR=new TPolygon*[maxpitch]; memset(newR, 0, sizeof(TPolygon*)*maxpitch); double* newsc=new double[maxpitch]; memset(newsc, 0, sizeof(double)*maxpitch); double** Allocate2(double, maxpitch, maxp, newvfp); for (int pin0=1; pin0<=maxpin0; pin0++) //pin0: the partial index of a candidate pitch { //find out the range [pm, pM) of indices into fps[], so that for a * in this range (pin0, fps[*]) may fall //in the feasible pitch range succeeding one of the candidate pitches of the previous frame for (int i=0; i<pitchcount; i++) {ExFmStiff(f1[i], f2[i], pin0, R[i]->N, R[i]->X, R[i]->Y); f1[i]-=delm, f2[i]+=delm;} double f1a=f1[0], f2a=f2[0]; for (int i=1; i<pitchcount; i++) {if (f1a>f1[i]) f1a=f1[i]; if (f2a<f2[i]) f2a=f2[i];} while (pm<pc-1 && fps[pm]<f1a) pm++; int pM=pm; while (pM<pc-1 && fps[pM]<f2a) pM++; for (int p=pm; p<pM; p++) //loop through all peaks in this range { if (pitchind[p][pin0]>=0) continue; //skip this peak if it is already ... int max; double maxs; TPolygon* lnewR=0; for (int i=0; i<pitchcount; i++) //loop through candidate pitches of the previous frame { if (fps[p]>f1[i] && fps[p]<f2[i]) //so that this peak is a feasible successor of the i-th candidate pitch of the previous frame { if (pitchind[p][pin0]<0) //if this peak has not been registered as a candidate pitch with pin0 being the partial index { lnewR=new TPolygon(maxp*2+4, R[i]); //create a F-G polygon for (this peak, pin0) pair if (newpc==maxpitch) //maximal number of candidate pitches is reached { //delete the candidate with the lowest score without checking if this lowest score is below the score //of the current pitch candidate (which is not yet computed). of course there is a risk that the new //score is even lower so that $maxpitch buffered scores may not be the highest $maxpitch scores, but //the (maxpitch-1) highest of the $maxpitch buffered scores should be the highest (maxpitch01) scores. int minsc=0; for (int j=0; j<maxpitch; j++) if (newsc[j]<newsc[minsc]) minsc=j; delete newR[minsc]; if (minsc!=newpc-1) {newR[minsc]=newR[newpc-1]; memcpy(newvfp[minsc], newvfp[newpc-1], sizeof(double)*maxp); newsc[minsc]=newsc[newpc-1]; prev[minsc]=prev[newpc-1]; newpitches[minsc]=newpitches[newpc-1];} } else newpc++; //try to find harmonic atom with this candidate pitch if (NoteMatchStiff3(lnewR, p, pin0, x, pc, fps, vps, wid, settings, newvfp[newpc-1], pitchind, newpc-1)>0) {//if successful, buffer its F-G polygon and save its score newR[newpc-1]=lnewR; max=i; maxs=sc[i]+conta(maxp, newvfp[newpc-1], vfp[i]); } else {//if not, discard this candidate pitch delete lnewR; lnewR=0; newpc--; } } else //if this peak has already been registered as a candidate pitch with pin0 being the partial index { //compute it score as successor to the i-th candidate of the previous frame double ls=sc[i]+conta(maxp, newvfp[newpc-1], vfp[i]); //if the score is higher than mark the i-th candidate of previous frame as its predecessor if (ls>maxs) maxs=ls, max=i; } } } if (lnewR) //i.e. a HA is found for this pitch candidate { ((__int16*)&newpitches[newpc-1])[0]=p; ((__int16*)&newpitches[newpc-1])[1]=pin0; newsc[newpc-1]=maxs; //take note of its score prev[newpc-1]=max; //take note of its predecessor } } } DeAlloc2(pitchind); delete[] f1; delete[] f2; for (int i=0; i<pitchcount; i++) delete R[i]; delete[] R; R=newR; for (int i=0; i<newpc; i++) for (int j=0; j<maxp; j++) if (newvfp[i][j]<0) newvfp[i][j]=-newvfp[i][j]; DeAlloc2(vfp); vfp=newvfp; delete[] sc; sc=newsc; pitchcount=newpc; }//NoteMatchStiff3FB /* function PeakShapeC: residue-sinusoid-ratio for a given (hypothesis) sinusoid frequency In: x[Fr][N/2+1]: spectrogram M, c[], iH2: cosine-family window specifiers f: reference frequency, in bins B: spectral truncation width Returns the residue-sinusoid-ratio. */ double PeakShapeC(double f, int Fr, int N, cdouble** x, int B, int M, double* c, double iH2) { cdouble* w=new cdouble[B]; int fst=floor(f+1-B/2.0); if (fst<0) fst=0; if (fst+B>N/2) fst=N/2-B; Window(w, f, N, M, c, fst, fst+B-1); cdouble xx=0, ww=Inner(B, w, w); double xw2=0; for (int fr=0; fr<Fr; fr++) xw2+=~Inner(B, &x[fr][fst], w), xx+=Inner(B, &x[fr][fst], &x[fr][fst]); delete[] w; if (xx.x==0) return 1; return 1-xw2/(xx.x*ww.x); }//PeakShapeC //version using cmplx<float> as spectrogram data type double PeakShapeC(double f, int Fr, int N, cfloat** x, int B, int M, double* c, double iH2) { cdouble* w=new cdouble[B]; int fst=floor(f+1-B/2.0); if (fst<0) fst=0; if (fst+B>N/2) fst=N/2-B; Window(w, f, N, M, c, fst, fst+B-1); cdouble xx=0, ww=Inner(B, w, w); double xw2=0; for (int fr=0; fr<Fr; fr++) xw2+=~Inner(B, &x[fr][fst], w), xx+=Inner(B, &x[fr][fst], &x[fr][fst]); delete[] w; if (xx.x==0) return 1; return 1-xw2/(xx.x*ww.x); }//PeakShapeC /* function QuickPeaks: finds rough peaks in the spectrum (peak picking) In: x[N/2+1]: spectrum M, c[], iH2: cosine-family window function specifiers mina: minimal amplitude to spot a spectral peak [binst, binen): frequency range, in bins, to look for peaks B: spectral truncation width Out; f[return value], a[return value]: frequencies (in bins) and amplitudes of found peaks rsr[return value]: residue-sinusoid-ratio of found peaks, optional Returns the number of peaks found. f[] and a[] must be allocated enough space before calling. */ int QuickPeaks(double* f, double* a, int N, cdouble* x, int M, double* c, double iH2, double mina, int binst, int binen, int B, double* rsr) { double hB=B*0.5; if (binst<2) binst=2; if (binst<hB) binst=hB; if (binen<0) binen=N/2-1; if (binen<0 || binen+hB>N/2-1) binen=N/2-1-hB; double a0=~x[binst-1], a1=~x[binst], a2; double minA=mina*mina*2/iH2; int p=0, n=binst; cdouble* w=new cdouble[B]; while (n<binen) { a2=~x[n+1]; if (a1>0 && a1>=a0 && a1>=a2) { if (a1>minA) { double A0=sqrt(a0), A1=sqrt(a1), A2=sqrt(a2); f[p]=n+(A0-A2)/2/(A0+A2-2*A1); int fst=floor(f[p]+1-hB); Window(w, f[p], N, M, c, fst, fst+B-1); double xw2=~Inner(B, &x[fst], w); a[p]=sqrt(xw2)*iH2; if (rsr) { cdouble xx=Inner(B, &x[fst], &x[fst]); if (xx.x==0) rsr[p]=1; else rsr[p]=1-xw2/xx.x*iH2; } p++; } } a0=a1; a1=a2; n++; } delete[] w; return p; }//QuickPeaks /* function QuickPeaks: finds rough peaks in the spectrogram (peak picking) for constant-frequency sinusoids In: x[Fr][N/2+1]: spectrogram fr0, r0: centre and half width of interval (in frames) to use for peak picking M, c[], iH2: cosine-family window function specifiers mina: minimal amplitude to spot a spectral peak [binst, binen): frequency range, in bins, to look for peaks B: spectral truncation width Out; f[return value], a[return value]: frequencies (in bins) and summary amplitudes of found peaks rsr[return value]: residue-sinusoid-ratio of found peaks, optional Returns the number of peaks found. f[] and a[] must be allocated enough space before calling. */ int QuickPeaks(double* f, double* a, int Fr, int N, cdouble** x, int fr0, int r0, int M, double* c, double iH2, double mina, int binst, int binen, int B, double* rsr) { double hB=B*0.5; int hN=N/2; if (binst<hB) binst=hB; if (binen<0 || binen>hN-hB) binen=hN-hB; double minA=mina*mina*2/iH2; double *a0s=new double[hN*3*r0], *a1s=&a0s[hN*r0], *a2s=&a0s[hN*2*r0]; int *idx=new int[hN*r0], *frc=new int[hN*r0]; int** Allocate2(int, (hN*r0), (r0*2+1), frs); int pc=0; int lr=0; while (lr<=r0) { int fr[2]; //indices to frames to process for this lr if (lr==0) fr[0]=fr0, fr[1]=-1; else { fr[0]=fr0-lr, fr[1]=fr0+lr; if (fr[1]>=Fr) fr[1]=-1; if (fr[0]<0) {fr[0]=fr[1]; fr[1]=-1;} } for (int i=0; i<2; i++) { if (fr[i]>=0) { cdouble* xfr=x[fr[i]]; for (int k=binst; k<binen; k++) { double a0=~xfr[k-1], a1=~xfr[k], a2=~xfr[k+1]; if (a1>=a0 && a1>=a2) { if (a1>minA) { double A1=sqrt(a1), A2=sqrt(a2), A0=sqrt(a0); double lf=k+(A0-A2)/2/(A0+A2-2*A1); if (lr==0) //starting frame { f[pc]=lf; a0s[pc]=a0, a1s[pc]=a1, a2s[pc]=a2; idx[pc]=pc; frs[pc][0]=fr[i]; frc[pc]=1; pc++; } else { int closep, indexp=InsertInc(lf, f, pc, false); //find the closest peak ever found in previous (i.e. closer to fr0) frames if (indexp==-1) indexp=pc, closep=pc-1; else if (indexp-1>=0 && fabs(lf-f[indexp-1])<fabs(lf-f[indexp])) closep=indexp-1; else closep=indexp; if (fabs(lf-f[closep])<0.2) //i.e. lf is very close to previous peak at fs[closep] { int idxp=idx[closep]; a0s[idxp]+=a0, a1s[idxp]+=a1, a2s[idxp]+=a2; frs[idxp][frc[idxp]]=fr[i]; frc[idxp]++; double A0=sqrt(a0s[idxp]), A1=sqrt(a1s[idxp]), A2=sqrt(a2s[idxp]); f[closep]=k+(A0-A2)/2/(A0+A2-2*A1); } else { memmove(&f[indexp+1], &f[indexp], sizeof(double)*(pc-indexp)); f[indexp]=lf; memmove(&idx[indexp+1], &idx[indexp], sizeof(int)*(pc-indexp)); idx[indexp]=pc; a0s[pc]=a0, a1s[pc]=a1, a2s[pc]=a2, frs[pc][0]=fr[i], frc[pc]=1; pc++; } } } } } } } lr++; } cdouble* w=new cdouble[B]; int* frused=new int[Fr]; for (int p=0; p<pc; p++) { int fst=floor(f[p]+1-hB); memset(frused, 0, sizeof(int)*Fr); int idxp=idx[p]; Window(w, f[p], N, M, c, fst, fst+B-1); double xw2=0, xw2r=0, xxr=0; for (int ifr=0; ifr<frc[idxp]; ifr++) { int fr=frs[idxp][ifr]; frused[fr]=1; xw2r+=~Inner(B, &x[fr][fst], w); xxr+=Inner(B, &x[fr][fst], &x[fr][fst]).x; } xw2=xw2r; for (int fr=0; fr<Fr; fr++) if (!frused[fr]) xw2+=~Inner(B, &x[fr][fst], w); a[p]=sqrt(xw2/Fr)*iH2; if (xxr==0) rsr[p]=1; else rsr[p]=1-xw2r/xxr*iH2; } delete[] w; delete[] frused; delete[] a0s; delete[] idx; delete[] frc; DeAlloc2(frs); return pc; }//QuickPeaks /* function ReEstHS1: reestimates a harmonic sinusoid by one multiplicative reestimation using phasor multiplier In: partials[M][Fr]: HS partials [frst, fren): frame (measurement point) range on which to do reestimation Data16[]: waveform data Out: partials[M][Fr]: updated HS partials No return value. */ void ReEstHS1(int M, int Fr, int frst, int fren, atom** partials, __int16* Data16) { double* fs=new double[Fr*3], *as=&fs[Fr], *phs=&fs[Fr*2]; int wid=partials[0][0].s, offst=partials[0][1].t-partials[0][0].t; int ldatastart=partials[0][0].t-wid/2; __int16* ldata16=&Data16[ldatastart]; for (int m=0; m<M; m++) { for (int fr=0; fr<Fr; fr++) {fs[fr]=partials[m][fr].f; if (fs[fr]<=0){delete[] fs; return;} as[fr]=partials[m][fr].a, phs[fr]=partials[m][fr].p;} MultiplicativeUpdateF(fs, as, phs, ldata16, Fr, frst, fren, wid, offst); for (int fr=0; fr<Fr; fr++) partials[m][fr].f=fs[fr], partials[m][fr].a=as[fr], partials[m][fr].p=phs[fr]; } delete[] fs; }//ReEstHS1 /* function ReEstHS1: wrapper function. In: HS: a harmonic sinusoid Data16: its waveform data Out: HS: updated harmonic sinusoid No return value. */ void ReEstHS1(THS* HS, __int16* Data16) { ReEstHS1(HS->M, HS->Fr, 0, HS->Fr, HS->Partials, Data16); }//ReEstHS1 /* function SortCandid: inserts a candid object into a listed of candid objects sorted first by f, then (for identical f's) by df. In: cand: the candid object to insert to the list cands[newN]: the sorted list of candid objects Out: cands[newN+1]: the sorted list after the insertion Returns the index of $cand in the new list. */ int SortCandid(candid cand, candid* cands, int newN) { int lnN=newN-1; while (lnN>=0 && cands[lnN].f>cand.f) lnN--; while (lnN>=0 && cands[lnN].f==cand.f && cands[lnN].df>cand.df) lnN--; lnN++; //now insert cantmp as cands[lnN] memmove(&cands[lnN+1], &cands[lnN], sizeof(candid)*(newN-lnN)); cands[lnN]=cand; return lnN; }//SortCandid /* function SynthesisHS: synthesizes a harmonic sinusoid without aligning the phases In: partials[M][Fr]: HS partials terminatetag: external termination flag. Function SynthesisHS() polls *terminatetag and exits with 0 when it is set. Out: [dst, den): time interval synthesized xrec[den-dst]: resynthesized harmonic sinusoid Returns pointer to xrec on normal finish, or 0 on external termination by setting $terminatetag. In the first case xrec is created anew with malloc8() and must be freed by caller using free8(). */ double* SynthesisHS(int M, int Fr, atom** partials, int& dst, int& den, bool* terminatetag) { int wid=partials[0][0].s, hwid=wid/2; double *as=(double*)malloc8(sizeof(double)*Fr*13); double *fs=&as[Fr], *f3=&as[Fr*2], *f2=&as[Fr*3], *f1=&as[Fr*4], *f0=&as[Fr*5], *a3=&as[Fr*6], *a2=&as[Fr*7], *a1=&as[Fr*8], *a0=&as[Fr*9], *xs=&as[Fr*11]; int* ixs=(int*)&as[Fr*12]; dst=partials[0][0].t-hwid, den=partials[0][Fr-1].t+hwid; double* xrec=(double*)malloc8(sizeof(double)*(den-dst)); memset(xrec, 0, sizeof(double)*(den-dst)); for (int m=0; m<M; m++) { atom* part=partials[m]; bool fzero=false; for (int fr=0; fr<Fr; fr++) { if (part[fr].f<=0){fzero=true; break;} ixs[fr]=part[fr].t; xs[fr]=part[fr].t; as[fr]=part[fr].a*2; fs[fr]=part[fr].f; if (part[fr].type==atMuted) as[fr]=0; } if (fzero) break; if (terminatetag && *terminatetag) {free8(xrec); free8(as); return 0;} CubicSpline(Fr-1, f3, f2, f1, f0, xs, fs, 1, 1); CubicSpline(Fr-1, a3, a2, a1, a0, xs, as, 1, 1); double ph=0, ph0=0; for (int fr=0; fr<Fr-1; fr++) { part[fr].p=ph; ALIGN8(Sinusoid(&xrec[ixs[fr]-dst], 0, ixs[fr+1]-ixs[fr], a3[fr], a2[fr], a1[fr], a0[fr], f3[fr], f2[fr], f1[fr], f0[fr], ph, true);) if (terminatetag && *terminatetag) {free8(xrec); free8(as); return 0;} } part[Fr-1].p=ph; ALIGN8(Sinusoid(&xrec[ixs[Fr-2]-dst], ixs[Fr-1]-ixs[Fr-2], den-ixs[Fr-2], a3[Fr-2], a2[Fr-2], a1[Fr-2], a0[Fr-2], f3[Fr-2], f2[Fr-2], f1[Fr-2], f0[Fr-2], ph, true); Sinusoid(&xrec[ixs[0]-dst], dst-ixs[0], 0, a3[0], a2[0], a1[0], a0[0], f3[0], f2[0], f1[0], f0[0], ph0, true);) } free8(as); return xrec; }//SynthesisHS /* function SynthesisHS: synthesizes a perfectly harmonic sinusoid without aligning the phases. Frequencies of partials above the fundamental are not used in this synthesis process. In: partials[M][Fr]: HS partials terminatetag: external termination flag. This function polls *terminatetag and exits with 0 when it is set. Out: [dst, den) time interval synthesized xrec[den-dst]: resynthesized harmonic sinusoid Returns pointer to xrec on normal finish, or 0 on external termination by setting $terminatetag. In the first case xrec is created anew with malloc8() and must be freed by caller using free8(). */ double* SynthesisHS2(int M, int Fr, atom** partials, int& dst, int& den, bool* terminatetag) { int wid=partials[0][0].s, hwid=wid/2; double *as=(double*)malloc8(sizeof(double)*Fr*7); //*as=new double[Fr*8], double *xs=&as[Fr], *f3=&as[Fr*2], *f2=&as[Fr*3], *f1=&as[Fr*4], *f0=&as[Fr*5]; int *ixs=(int*)&as[Fr*6]; double** a0=new double*[M*4], **a1=&a0[M], **a2=&a0[M*2], **a3=&a0[M*3]; a0[0]=(double*)malloc8(sizeof(double)*M*Fr*4); //a0[0]=new double[M*Fr*4]; for (int m=0; m<M; m++) a0[m]=&a0[0][m*Fr*4], a1[m]=&a0[m][Fr], a2[m]=&a0[m][Fr*2], a3[m]=&a0[m][Fr*3]; dst=partials[0][0].t-hwid, den=partials[0][Fr-1].t+hwid; double* xrec=(double*)malloc8(sizeof(double)*(den-dst)); memset(xrec, 0, sizeof(double)*(den-dst)); atom* part=partials[0]; for (int fr=0; fr<Fr; fr++) { xs[fr]=ixs[fr]=part[fr].t; as[fr]=part[fr].f; } CubicSpline(Fr-1, f3, f2, f1, f0, xs, as, 1, 1); for (int m=0; m<M; m++) { part=partials[m]; for (int fr=0; fr<Fr; fr++) { if (part[fr].f<=0){M=m; break;} as[fr]=part[fr].a*2; } if (terminatetag && *terminatetag) {free8(xrec); free8(as); free8(a0[0]); delete[] a0; return 0;} if (m>=M) break; CubicSpline(Fr-1, a3[m], a2[m], a1[m], a0[m], xs, as, 1, 1); } double *ph=(double*)malloc8(sizeof(double)*M*2), *ph0=&ph[M]; memset(ph, 0, sizeof(double)*M*2); for (int fr=0; fr<Fr-1; fr++) { // double *la0=new double[M*4], *la1=&la0[M], *la2=&la0[M*2], *la3=&la0[M*3]; for (int m=0; m<M; m++) la0[m]=a0[m][fr], la1[m]=a1[m][fr], la2[m]=a2[m][fr], la3[m]=a3[m][fr], part[fr].p=ph[m]; Sinusoids(M, &xrec[ixs[fr]-dst], 0, ixs[fr+1]-ixs[fr], la3, la2, la1, la0, f3[fr], f2[fr], f1[fr], f0[fr], ph, true); delete[] la0; for (int m=0; m<M; m++) ALIGN8(Sinusoid(&xrec[ixs[fr]-dst], 0, ixs[fr+1]-ixs[fr], a3[m][fr], a2[m][fr], a1[m][fr], a0[m][fr], (m+1)*f3[fr], (m+1)*f2[fr], (m+1)*f1[fr], (m+1)*f0[fr], ph[m], true);) } for (int m=0; m<M; m++) { part[Fr-1].p=ph[m]; ALIGN8(Sinusoid(&xrec[ixs[Fr-2]-dst], ixs[Fr-1]-ixs[Fr-2], den-ixs[Fr-2], a3[m][Fr-2], a2[m][Fr-2], a1[m][Fr-2], a0[m][Fr-2], (m+1)*f3[Fr-2], (m+1)*f2[Fr-2], (m+1)*f1[Fr-2], (m+1)*f0[Fr-2], ph[m], true); Sinusoid(&xrec[ixs[0]-dst], dst-ixs[0], 0, a3[m][0], a2[m][0], a1[m][0], a0[m][0], (m+1)*f3[0], (m+1)*f2[0], (m+1)*f1[0], (m+1)*f0[0], ph0[m], true);) if (terminatetag && *terminatetag) {free8(xrec); free8(as); free8(a0[0]); delete[] a0; free8(ph); return 0;} } free8(as); free8(ph); free8(a0[0]); delete[] a0; return xrec; }//SynthesisHS2 /* function SynthesisHSp: synthesizes a harmonic sinusoid with phase alignment In: partials[pm][pfr]: HS partials startamp[pm][st_count]: onset amplifiers, optional st_start, st_offst: start of and interval between onset amplifying points, optional Out: [dst, den): time interval synthesized xrec[den-dst]: resynthesized harmonic sinusoid Returns pointer to xrec. xrec is created anew with malloc8() and must be freed by caller with free8(). */ double* SynthesisHSp(int pm, int pfr, atom** partials, int& dst, int& den, double** startamp, int st_start, int st_offst, int st_count) { int wid=partials[0][0].s, hwid=wid/2, offst=partials[0][1].t-partials[0][0].t; double *a1=new double[pfr*13]; double *f1=&a1[pfr], *fa=&a1[pfr*2], *fb=&a1[pfr*3], *fc=&a1[pfr*4], *fd=&a1[pfr*5], *aa=&a1[pfr*6], *ab=&a1[pfr*7], *ac=&a1[pfr*8], *ad=&a1[pfr*9], *p1=&a1[pfr*10], *xs=&a1[pfr*11]; int* ixs=(int*)&a1[pfr*12]; dst=partials[0][0].t-hwid, den=partials[0][pfr-1].t+hwid; double *xrec=(double*)malloc8(sizeof(double)*(den-dst)*2), *xrecm=&xrec[den-dst]; memset(xrec, 0, sizeof(double)*(den-dst)); for (int p=0; p<pm; p++) { atom* part=partials[p]; bool fzero=false; for (int fr=0; fr<pfr; fr++) { if (part[fr].f<=0) { fzero=true; break; } ixs[fr]=part[fr].t; xs[fr]=part[fr].t; a1[fr]=part[fr].a*2; f1[fr]=part[fr].f; p1[fr]=part[fr].p; if (part[fr].type==atMuted) a1[fr]=0; } if (fzero) break; CubicSpline(pfr-1, fa, fb, fc, fd, xs, f1, 1, 1); CubicSpline(pfr-1, aa, ab, ac, ad, xs, a1, 1, 1); for (int fr=0; fr<pfr-1; fr++) Sinusoid(&xrecm[ixs[fr]-dst], 0, offst, aa[fr], ab[fr], ac[fr], ad[fr], fa[fr], fb[fr], fc[fr], fd[fr], p1[fr], p1[fr+1], false); // Sinusoid(&xrecm[ixs[0]-dst], -hwid, 0, aa[0], ab[0], ac[0], ad[0], fa[0], fb[0], fc[0], fd[0], p1[0], p1[1], false); // Sinusoid(&xrecm[ixs[pfr-2]-dst], offst, offst+hwid, aa[pfr-2], ab[pfr-2], ac[pfr-2], ad[pfr-2], fa[pfr-2], fb[pfr-2], fc[pfr-2], fd[pfr-2], p1[pfr-2], p1[pfr-1], false); Sinusoid(&xrecm[ixs[0]-dst], -hwid, 0, 0, 0, 0, ad[0], fa[0], fb[0], fc[0], fd[0], p1[0], p1[1], false); Sinusoid(&xrecm[ixs[pfr-2]-dst], offst, offst+hwid, 0, 0, 0, ad[pfr-2], fa[pfr-2], fb[pfr-2], fc[pfr-2], fd[pfr-2], p1[pfr-2], p1[pfr-1], false); if (st_count) { double* amp=startamp[p]; for (int c=0; c<st_count; c++) { double a1=amp[c], a2=(c+1<st_count)?amp[c+1]:1, da=(a2-a1)/st_offst; int lst=ixs[0]-dst+st_start+c*st_offst; double *lxrecm=&xrecm[lst]; if (lst>0) for (int i=0; i<st_offst; i++) lxrecm[i]*=(a1+da*i); else for (int i=-lst; i<st_offst; i++) lxrecm[i]*=(a1+da*i); } for (int i=0; i<ixs[0]-dst+st_start; i++) xrecm[i]*=amp[0]; } else { /* for (int i=0; i<=hwid; i++) { double tmp=0.5+0.5*cos(M_PI*i/hwid); xrecm[ixs[0]-dst-i]*=tmp; xrecm[ixs[pfr-2]-dst+offst+i]*=tmp; } */ } for (int n=0; n<den-dst; n++) xrec[n]+=xrecm[n]; } delete[] a1; return xrec; }//SynthesisHSp /* function SynthesisHSp: wrapper function. In: HS: a harmonic sinusoid. Out: [dst, den): time interval synthesized xrec[den-dst]: resynthesized harmonic sinusoid Returns pointer to xrec, which is created anew with malloc8() and must be freed by caller using free8(). */ double* SynthesisHSp(THS* HS, int& dst, int& den) { return SynthesisHSp(HS->M, HS->Fr, HS->Partials, dst, den, HS->startamp, HS->st_start, HS->st_offst, HS->st_count); }//SynthesisHSp