# HG changeset patch # User Wen X # Date 1286271957 -3600 # Node ID 6422640a802f01f9a5774896d67315b6490cd1f0 # Parent 9b9f21935f24c5fea30e500344e8aebb2fbd818a first upload diff -r 9b9f21935f24 -r 6422640a802f Matrix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Matrix.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,2245 @@ +//--------------------------------------------------------------------------- +#include +#include +#include "Matrix.h" +//--------------------------------------------------------------------------- +/* + function BalanceSim: applies a similarity transformation to matrix a so that a is "balanced". This is + used by various eigenvalue evaluation routines. + + In: matrix A[n][n] + Out: balanced matrix a + + No return value. +*/ +void BalanceSim(int n, double** A) +{ + if (n<2) return; + const int radix=2; + double sqrdx; + sqrdx=radix*radix; + bool finish=false; + while (!finish) + { + finish=true; + for (int i=0; iar) + { + ac/=radix; + sc/=sqrdx; + } + } + if ((sc+sr)/ac<0.95*s) + { + finish=false; + ar=1.0/ac; + for (int j=0; jAdd(Z, 2);} + int sizeN=sizeof(double)*N; + for (int m=0; mAdd(Z, 2);} + int sizeN=sizeof(cdouble)*N; + for (int m=0; mAdd(z, 1);} + memcpy(z, a, sizeof(double)*N); + return z; +}//Copy +cdouble* Copy(int N, cdouble* z, cdouble* a, MList* List) +{ + if (!z){z=new cdouble[N]; if (List) List->Add(z, 1);} + memcpy(z, a, sizeof(cdouble)*N); + return z; +}//Copy +//version without specifying pre-allocated z +double* Copy(int N, double* a, MList* List){return Copy(N, 0, a, List);} +cdouble* Copy(int N, cdouble* a, MList* List){return Copy(N, 0, a, List);} + +//--------------------------------------------------------------------------- +/* + function det: computes determinant by Gaussian elimination method with column pivoting + + In: matrix A[N][N] + + Returns det(A). On return content of matrix A is unchanged if mode=0. +*/ +double det(int N, double** A, int mode) +{ + int c, p, ip, *rp=new int[N]; for (int i=0; ifabs(A[rp[p]][i])) p=ip; ip++;} + if (A[rp[p]][i]==0) {result=0; goto ret;} + if (p!=i) {c=rp[i]; rp[i]=rp[p]; rp[p]=c; result=-result;} + for (int j=i+1; jmp) mp=mm, p=ip; ip++;} + if (mp==0) {result=0; goto ret;} + if (p!=i) {c=rp[i]; rp[i]=rp[p]; rp[p]=c;} + for (int j=i+1; j2) {l=m0-(m1-m0)*(m1-m0)/(m-2*m1+m0); delete[] y; return 0;} + k++; m0=m1; m1=m; + } + delete[] y; return 1; +}//EigPowerA + +//--------------------------------------------------------------------------- +/* + function EigPowerI: Inverse power method for solving the eigenvalue given an approximate non-zero + eigenvector. + + In: matrix A[N][N], approximate eigenvector x[N]. + Out: eigenvalue l, eigenvector x[N]. + + Returns 0 is successful. Content of matrix A is unchangd on return. Initial x[N] must not be zero. +*/ +int EigPowerI(int N, double& l, double* x, double** A, double ep, int maxiter) +{ + int sizeN=sizeof(double)*N; + double* y=new double[N]; MultiplyXy(N, N, y, A, x); + double q=Inner(N, x, y)/Inner(N, x, x), dt; + double** aa=new double*[N]; aa[0]=new double[N*N]; + for (int i=0; i=N) {delete[] rp; return 1;} + if (p!=i){c=rp[i]; rp[i]=rp[p]; rp[p]=c;} + for (int j=i+1; j=0; i--) + { + x[i]=b[rp[i]]; for (int j=i+1; jfabs(A[rp[p]][i])/s[rp[p]]) p=ip; ip++;} + if (A[rp[p]][i]==0) {delete[] s; delete[] rp; return 1;} + if (p!=i) {c=rp[i]; rp[i]=rp[p]; rp[p]=c;} + for (int j=i+1; j=0; i--) + { + x[i]=b[rp[i]]; for (int j=i+1; j=0; n--) + { + double xn=a[n]; + for (int m=n+1; m=0; n--) + { + for (int m=n+1; mA. + + In: matrix A[N][N] + Out: matrix A[N][N] + + Returns the determinant of the inverse matrix, 0 on failure. +*/ +double GICP(int N, double** A) +{ + int c, p, ip, *rp=new int[N]; for (int i=0; ifabs(A[rp[p]][i])) p=ip; ip++;} + if (A[rp[p]][i]==0) {delete[] rp; return 0;} + if (p!=i) {c=rp[i]; rp[i]=rp[p]; rp[p]=c; result=-result;} + result/=A[rp[i]][i]; + for (int j=i+1; j~A[rp[p]][i]) p=ip; ip++;} + if (A[rp[p]][i]==0) {delete[] rp; return 0;} + if (p!=i) {c=rp[i]; rp[i]=rp[p]; rp[p]=c; result=-result;} + result=result/(A[rp[i]][i]); + for (int j=i+1; jX. + + In: matrix A[N][N] + Out: matrix X[N][N] + + Returns the determinant of the inverse matrix, 0 on failure. +*/ +double GICP(int N, double** X, double** A) +{ + Copy(N, X, A); + return GICP(N, X); +}//GICP + +//--------------------------------------------------------------------------- +/* + function GILT: inv(lower trangular of A)->lower trangular of A + + In: matrix A[N][N] + Out: matrix A[N][N] + + Returns the determinant of the lower trangular of A +*/ +double GILT(int N, double** A) +{ + double result=1; + A[0][0]=1/A[0][0]; + for (int i=1; iupper trangular of A + + In: matrix A[N][N] + Out: matrix A[N][N] + + Returns the determinant of the upper trangular of A +*/ +double GIUT(int N, double** A) +{ + double result=1; + A[0][0]=1/A[0][0]; + for (int i=1; iA. + + In: matrix A[N][N] + Out: matrix A[N][N] + + Returns the determinant of the inverse matrix, 0 on failure. +*/ +double GISCP(int N, double** A) +{ + int c, p, ip, *rp=new int[N]; for (int i=0; ifabs(A[rp[p]][i]/s[rp[p]])) p=ip; ip++;} + if (A[rp[p]][i]==0) {delete[] s; delete[] rp; return 0;} + if (p!=i) {c=rp[i]; rp[i]=rp[p]; rp[p]=c; result=-result;} + result/=A[rp[i]][i]; + for (int j=i+1; jX. + + In: matrix A[N][N] + Out: matrix X[N][N] + + Returns the determinant of the inverse matrix, 0 on failure. +*/ +double GISCP(int N, double** X, double** A) +{ + Copy(N, X, A); + return GISCP(N, X); +}//GISCP + +//--------------------------------------------------------------------------- +/* + function GSI: Gaussian-Seidel iterative algorithm for solving linear system Ax=b. Breaks down if any + Aii=0, like the Jocobi method JI(...). + + Gaussian-Seidel iteration is x(k)=(D-L)^(-1)(Ux(k-1)+b), where D is diagonal, L is lower triangular, + U is upper triangular and A=L+D+U. + + In: matrix A[N][N], vector b[N], initial vector x0[N] + Out: vector x0[N] + + Returns 0 is successful. Contents of matrix A and vector b remain unchanged on return. +*/ +int GSI(int N, double* x0, double** A, double* b, double ep, int maxiter) +{ + double e, *x=new double[N]; + int k=0, sizeN=sizeof(double)*N; + while (k=maxiter) return 1; + return 0; +}//GSI + +//--------------------------------------------------------------------------- +/* + function Hessenb: reducing a square matrix A to upper Hessenberg form + + In: matrix A[N][N] + Out: matrix A[N][N], in upper Hessenberg form + + No return value. +*/ +void Hessenb(int N, double** A) +{ + double x, y; + for (int m=1; m fabs(x)) + { + x=A[j][m-1]; + i=j; + } + } + if (i!=m) + { + for (int j=m-1; j=1; i--) + { + int l=i-1; + double h=0, scale=0; + if (l>0) + { + for (int k=0; k<=l; k++) scale+=fabs(A[i][k]); + if (scale==0.0) sd[i]=A[i][l]; + else + { + for (int k=0; k<=l; k++) + { + A[i][k]/=scale; + h+=A[i][k]*A[i][k]; + } + double f=A[i][l]; + double g=(f>=0?-sqrt(h): sqrt(h)); + sd[i]=scale*g; + h-=f*g; + A[i][l]=f-g; + f=0; + for (int j=0; j<=l; j++) + { + A[j][i]=A[i][j]/h; + g=0; + for (int k=0; k<=j; k++) g+=A[j][k]*A[i][k]; + for (int k=j+1; k<=l; k++) g+=A[k][j]*A[i][k]; + sd[j]=g/h; + f+=sd[j]*A[i][j]; + } + double hh=f/(h+h); + for (int j=0; j<=l; j++) + { + f=A[i][j]; + sd[j]=g=sd[j]-hh*f; + for (int k=0; k<=j; k++) A[j][k]-=(f*sd[k]+g*A[i][k]); + } + } + } + else + sd[i]=A[i][l]; + d[i]=h; + } + + d[0]=sd[0]=0; + + for (int i=0; i=maxiter) return 1; + else return 0; +}//JI + +//--------------------------------------------------------------------------- +/* + function LDL: LDL' decomposition A=LDL', where L is lower triangular and D is diagonal identical l and + a allowed. + + The symmetric matrix A is positive definite iff A can be factorized as LDL', where L is lower + triangular with ones on its diagonal and D is diagonal with positive diagonal entries. + + If a symmetric matrix A can be reduced by Gaussian elimination without row interchanges, then it can + be factored into LDL', where L is lower triangular with ones on its diagonal and D is diagonal with + non-zero diagonal entries. + + In: matrix A[N][N] + Out: lower triangular matrix L[N][N], vector d[N] containing diagonal elements of D + + Returns 0 if successful. Content of matrix A is unchanged on return. +*/ +int LDL(int N, double** L, double* d, double** A) +{ + double* v=new double[N]; + + if (A[0][0]==0) {delete[] v; return 1;} + d[0]=A[0][0]; for (int j=1; j=N. Use of this function requires + the submatrix A[N][N] be invertible. + + In: matrix A[M][N], vector y[M], M>=N. + Out: vector x[N]. + + No return value. Contents of matrix A and vector y are unchanged on return. +*/ +void LSLinear2(int M, int N, double* x, double** A, double* y) +{ + double** A1=Copy(N, N, 0, A); + LU(N, x, A1, y); + if (M>N) + { + double** B=&A[N]; + double* Del=MultiplyXy(M-N, N, B, x); + MultiAdd(M-N, Del, Del, &y[N], -1); + double** A2=MultiplyXtX(N, N, A); + MultiplyXtX(N, M-N, A1, B); + MultiAdd(N, N, A2, A2, A1, 1); + double* b2=MultiplyXty(N, M-N, B, Del); + double* dx=new double[N]; + GESCP(N, dx, A2, b2); + MultiAdd(N, x, x, dx, -1); + delete[] dx; + delete[] Del; + delete[] b2; + DeAlloc2(A2); + } + DeAlloc2(A1); +}//LSLinear2 + +//--------------------------------------------------------------------------- +/* + function LU: LU decomposition A=LU, where L is lower triangular with diagonal entries 1 and U is upper + triangular. + + LU is possible if A can be reduced by Gaussian elimination without row interchanges. + + In: matrix A[N][N] + Out: matrices L[N][N] and U[N][N], subject to input values of L and U: + if L euqals NULL, L is not returned + if U equals NULL or A, U is returned in A, s.t. A is modified + if L equals A, L is returned in A, s.t. A is modified + if L equals U, L and U are returned in the same matrix + when L and U are returned in the same matrix, diagonal of L (all 1) is not returned + + Returns 0 if successful. +*/ +int LU(int N, double** L, double** U, double** A) +{ + double* diagl=new double[N]; + for (int i=0; i=0; i--) + { + x[i]/=A[i][i]; + for (int j=0; jb[i-j, j] + the main diagonal is b[0][0]~b[0][N-1] + the 1st upper subdiagonal is b[-1][1]~b[-1][N-1] + the 2nd upper subdiagonal is b[-2][2]~b[-2][N-1] + the 1st lower subdiagonal is b[1][0]~b[1][N-2] + the 2nd lower subdiagonal is b[2][0]~b[2][N-3] + + Out: L[N][N] and U[N][N], main diagonal of L being all 1 (probably), stored in a compact format in + b[-2:2][N]. + + Returns 0 if successful. +*/ +int LU_PD(int N, double** b) +{ + if (b[0][0]==0) return 1; + b[1][0]/=b[0][0], b[2][0]/=b[0][0]; + + //i=1, not to double b[*][i-2], b[-2][i] + b[0][1]-=b[1][0]*b[-1][1]; + if (b[0][1]==0) return 2; + b[-1][2]-=b[1][0]*b[-2][2]; + b[1][1]-=b[2][0]*b[-1][1]; + b[1][1]/=b[0][1]; + b[2][1]/=b[0][1]; + + for (int i=2; i=0; i--) + c[i]=(c[i]-b[-1][i+1]*c[i+1]-b[-2][i+2]*c[i+2])/b[0][i]; + } + return result; +}//LU_PD + +//--------------------------------------------------------------------------- +/* + function LUCP: LU decomposition A=LU with column pivoting + + In: matrix A[N][N] + Out: matrix A[N][N] now holding L and U by L_U[i][j]=A[ind[i]][j], where L_U + hosts L and U according to mode: + mode=0: L diag=abs(U diag), U diag as return + mode=1: L diag=1, U diag as return + mode=2: U diag=1, L diag as return + + Returns the determinant of A. +*/ +double LUCP(double **A, int N, int *ind, int &parity, int mode) +{ + double det=1; + parity=1; + + for (int i=0; ivmax) vmax=tmp; + if (vmax==0) { parity=0; goto deletenorm; } //det=0 at this point + norm[i]=1/vmax; + } + + int maxind; + for (int j=0; j=j + double tmp=A[i][j]; for (int k=0; k=vmax) maxind=i, vmax=tmp2; + } + if (vmax==0) { parity=0; goto deletenorm; } //pivot being zero + if (j!=maxind) + { + //do column pivoting: switching rows + for (int k=0; kAdd(z, 1);} \ + for (int m=0; mAdd(z, 1); \ + for (int m=0; m=maxiter) return 1; + double g=(d[l+1]-d[l])/(2*sd[l]); + double r=sqrt(g*g+1); + g=d[m]-d[l]+sd[l]/(g+(g>=0?r:-r)); + double s=1, c=1, p=0; + int i; + for (i=m-1; i>=l; i--) + { + double f=s*sd[i], b=c*sd[i]; + sd[i+1]=(r=sqrt(f*f+g*g)); + if (r==0) + { + d[i+1]-=p; + sd[m]=0; + break; + } + s=f/r, c=g/r; + g=d[i+1]-p; + r=(d[i]-g)*s+2.0*c*b; + p=s*r; + d[i+1]=g+p; + g=c*r-b; + for (int k=0; k=l) continue; + d[l]-=p; + sd[l]=g; + sd[m]=0.0; + } + } + while (m!=l); + } + return 0; +}//QL + +//--------------------------------------------------------------------------- +/* + function QR: nr version of QR method for solving upper Hessenberg system A. This is compatible with + Hessenb method. + + In: matrix A[N][N] + Out: vector ev[N] of eigenvalues + + Returns 0 on success. Content of matrix A is destroyed on return. +*/ +int QR(int N, double **A, cdouble* ev) +{ + int n=N, m, l, k, j, iter, i, mmin, maxiter=30; + double **a=A, z, y, x, w, v, u, t=0, s, r, q, p, a1=0; + for (i=0; i0?i-1:0; j=0) + { + iter=0; + do + { + for (l=n; l>0; l--) + { + s=fabs(a[l-1][l-1])+fabs(a[l][l]); + if (s==0) s=a1; + if (fabs(a[l][l-1])+s==s) {a[l][l-1]=0; break;} + } + x=a[n][n]; + if (l==n) {ev[n].x=x+t; ev[n--].y=0;} + else + { + y=a[n-1][n-1], w=a[n][n-1]*a[n-1][n]; + if (l==(n-1)) + { + p=0.5*(y-x); + q=p*p+w; + z=sqrt(fabs(q)); + x+=t; + if (q>=0) + { + z=p+(p>=0?z:-z); + ev[n-1].x=ev[n].x=x+z; + if (z) ev[n].x=x-w/z; + ev[n-1].y=ev[n].y=0; + } + else + { + ev[n-1].x=ev[n].x=x+p; + ev[n].y=z; ev[n-1].y=-z; + } + n-=2; + } + else + { + if (iter>=maxiter) return 1; + if (iter%10==9) + { + t+=x; + for (i=0; i<=n; i++) a[i][i]-=x; + s=fabs(a[n][n-1])+fabs(a[n-1][n-2]); + y=x=0.75*s; + w=-0.4375*s*s; + } + iter++; + for (m=n-2; m>=l; m--) + { + z=a[m][m]; + r=x-z; s=y-z; + p=(r*s-w)/a[m+1][m]+a[m][m+1]; q=a[m+1][m+1]-z-r-s; r=a[m+2][m+1]; + s=fabs(p)+fabs(q)+fabs(r); + p/=s; q/=s; r/=s; + if (m==l) break; + u=fabs(a[m][m-1])*(fabs(q)+fabs(r)); + v=fabs(p)*(fabs(a[m-1][m-1])+fabs(z)+fabs(a[m+1][m+1])); + if (u+v==v) break; + } + for (i=m+2; i<=n; i++) + { + a[i][i-2]=0; + if (i!=m+2) a[i][i-3]=0; + } + for (k=m; k<=n-1; k++) + { + if (k!=m) + { + p=a[k][k-1]; + q=a[k+1][k-1]; + r=0; + if (k!=n-1) r=a[k+2][k-1]; + x=fabs(p)+fabs(q)+fabs(r); + if (x!=0) p/=x, q/=x, r/=x; + } + if (p>=0) s=sqrt(p*p+q*q+r*r); + else s=-sqrt(p*p+q*q+r*r); + if (s!=0) + { + if (k==m) + { + if (l!=m) a[k][k-1]=-a[k][k-1]; + } + else a[k][k-1]=-s*x; + p+=s; + x=p/s; y=q/s; z=r/s; q/=p; r/=p; + for (j=k; j<=n; j++) + { + p=a[k][j]+q*a[k+1][j]; + if (k!=n-1) + { + p+=r*a[k+2][j]; + a[k+2][j]-=p*z; + } + a[k+1][j]-=p*y; a[k][j]-=p*x; + } + mmin=nl+1); + } + return 0; +}//QR + +/* + function QR_GS: QR decomposition A=QR using Gram-Schmidt method + + In: matrix A[M][N], M>=N + Out: Q[M][N], R[N][N] + + No return value. +*/ +void QR_GS(int M, int N, double** A, double** Q, double** R) +{ + double *u=new double[M]; + for (int n=0; n=N + Out: Q[M][M], R[M][N] + + No return value. +*/ +void QR_householder(int M, int N, double** A, double** Q, double** R) +{ + double *u=new double[M*3], *ur=&u[M], *qu=&u[M*2]; + for (int m=0; m0) alf=-alf; + for (int m=n; mAdd(z, 2);} + for (int m=0; m1) memset(A[1], 0, sizeof(double)*N*(N-1)); + for (int i=1; i=maxiter) return 1; + return 0; +}//SorI + +//--------------------------------------------------------------------------- +//Submatrix routines + +/* + function SetSubMatrix: copy matrix x[Y][X] into matrix z at (Y1, X1). + + In: matrix x[Y][X], matrix z with dimensions no less than [Y+Y1][X+X1] + Out: matrix z, updated. + + No return value. +*/ +void SetSubMatrix(double** z, double** x, int Y1, int Y, int X1, int X) +{ + for (int y=0; yAdd(z, 2);} + for (int y=0; yAdd(z, 1);} + memcpy(z, &x[X1], sizeof(cdouble)*X); + return z; +}//SubVector +//wrapper function +cdouble* SubVector(cdouble* x, int X1, int X, MList* List) +{ + return SubVector(0, x, X1, X, List); +}//SubVector + +//--------------------------------------------------------------------------- +/* + function transpose: matrix transpose: A'->A + + In: matrix a[N][N] + Out: matrix a[N][N] after transpose + + No return value. +*/ +void transpose(int N, double** a) +{ + double tmp; + for (int i=1; iZ + + In: matrix a[M][N] + Out: matrix z[N][M] + + Returns pointer to z. z is created anew if z=0 is specifid on start. +*/ +double** transpose(int N, int M, double** ta, double** a, MList* List) +{ + if (!ta) {Allocate2(double, N, M, ta); if (List) List->Add(ta, 2);} + for (int n=0; nAdd(P, 2);} + int sizeN=sizeof(double)*N; + for (int i=0; iAdd(P, 2); + return P; +}//Unitary +//wrapper functions +double** Unitary(int N, double* x, double* y, MList* List){return Unitary(N, 0, x, y, List);} +cdouble** Unitary(int N, cdouble* x, cdouble* y, MList* List){return Unitary(N, 0, x, y, List);} + + + diff -r 9b9f21935f24 -r 6422640a802f Matrix.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Matrix.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,407 @@ +#ifndef MatrixH +#define MatrixH + +/* + Matrix.cpp - matrix operations. + + Matrices are accessed by double pointers as MATRIX[Y][X], where Y is the row index. +*/ + +#include "xcomplex.h" +#include "arrayalloc.h" + +//--Similar balance---------------------------------------------------------- +void BalanceSim(int n, double** A); + +//--Choleski factorization--------------------------------------------------- +int Choleski(int N, double** L, double** A); + +//--matrix copy-------------------------------------------------------------- +double** Copy(int M, int N, double** Z, double** A, MList* List=0); + double** Copy(int M, int N, double** A, MList* List=0); + double** Copy(int N, double** Z, double ** A, MList* List=0); + double** Copy(int N, double ** A, MList* List=0); +cdouble** Copy(int M, int N, cdouble** Z, cdouble** A, MList* List=0); + cdouble** Copy(int M, int N, cdouble** A, MList* List=0); + cdouble** Copy(int N, cdouble** Z, cdouble** A, MList* List=0); + cdouble** Copy(int N, cdouble** A, MList* List=0); +double* Copy(int N, double* z, double* a, MList* List=0); + double* Copy(int N, double* a, MList* List=0); +cdouble* Copy(int N, cdouble* z, cdouble* a, MList* List=0); + cdouble* Copy(int N, cdouble* a, MList* List=0); + +//--matrix determinant calculation------------------------------------------- +double det(int N, double** A, int mode=0); +cdouble det(int N, cdouble** A, int mode=0); + +//--power methods for solving eigenproblems---------------------------------- +int EigPower(int N, double& l, double* x, double** A, double ep=1e-6, int maxiter=50); +int EigPowerA(int N, double& l, double* x, double** A, double ep=1e-6, int maxiter=50); +int EigPowerI(int N, double& l, double* x, double** A, double ep=1e-6, int maxiter=50); +int EigPowerS(int N, double& l, double* x, double** A, double ep=1e-6, int maxiter=50); +int EigPowerWielandt(int N, double& m, double* u, double l, double* v, double** A, double ep=1e-06, int maxiter=50); +int EigenValues(int N, double** A, cdouble* ev); +int EigSym(int N, double** A, double* d, double** Q); + +//--Gaussian elimination for solving linear systems-------------------------- +int GEB(int N, double* x, double** A, double* b); +int GESCP(int N, double* x, double** A, double*b); +void GExL(int N, double* x, double** L, double* a); +void GExLAdd(int N, double* x, double** L, double* a); +void GExL1(int N, double* x, double** L, double a); +void GExL1Add(int N, double* x, double** L, double a); + +/* + template GECP: Gaussian elimination with maximal column pivoting for solving linear system Ax=b + + In: matrix A[N][N], vector b[N] + Out: vector x[N] + + Returns 0 if successful. Contents of matrix A and vector b are destroyed on return. +*/ +templateint GECP(int N, T* x, Ta** A, T *b, Ta* logdet=0) +{ + if (logdet) *logdet=1E-302; int c, p, ip, *rp=new int[N]; for (int i=0; ifabs(A[rp[p]][i])) p=ip; ip++;} + if (A[rp[p]][i]==0) {delete[] rp; return 1;} + if (p!=i) {c=rp[i]; rp[i]=rp[p]; rp[p]=c;} + for (int j=i+1; j=0; i--) + { + x[i]=b[rp[i]]; for (int j=i+1; j + + In: vectors x[N], w[N] and y[N] + + Returns inner product of xw and y. +*/ +templatecdouble Inner(int N, Tx* x, Tw* w, cdouble* y) +{ + cdouble result=0; + for (int i=0; icdouble Inner(int N, Tx* x, Tw* w, double* y) +{ + cdouble result=0; + for (int i=0; i=N. + + In: matrix A[M][N], vector y[M] + Out: vector x[N] + + Returns the log determinant of AtA. Contents of matrix A and vector y are unchanged on return. +*/ +template T LSLinear(int M, int N, T* x, T** A, T* y) +{ + MList* mlist=new MList; + T** AtA=MultiplyXtX(N, M, A, mlist); + T* Aty=MultiplyXty(N, M, A, y, mlist); + T result; GECP(N, x, AtA, Aty, &result); + delete mlist; + return result; +}//LSLinear + +//--2-stage Least-square solution of overdetermined linear system------------ +void LSLinear2(int M, int N, double* x, double** A, double* y); + +//--LU factorization of a non-singular matrix-------------------------------- +int LU_Direct(int mode, int N, double* diag, double** a); +int LU(int N, double** l, double** u, double** a); void LU_DiagL(int N, double** l, double* diagl, double** u, double** a); +int LU_PD(int N, double** b); +int LU_PD(int N, double** b, double* c); +double LUCP(double **a, int N, int *ind, int& parity, int mode=1); + +//--LU factorization method for solving a linear system---------------------- +void LU(int N, double* x, double** A, double* y, int* ind=0); + +//--find maximum of vector--------------------------------------------------- +int maxind(double* data, int from, int to); + +//--matrix linear combination------------------------------------------------ +/* + template MultiAdd: matrix linear combination Z=X+aY + + In: matrices X[M][N], Y[M][N], scalar a + Out: matrix Z[M][N] + + Returns pointer to Z. Z is created anew if Z=0 is specified on start. +*/ +template Tz** MultiAdd(int M, int N, Tz** Z, Tx** X, Ty** Y, Ta a, MList* List=0) +{ + if (!Z){Allocate2(Tz, M, N, Z); if (List) List->Add(Z, 2);} + for (int i=0; i Tz* MultiAdd(int N, Tz* z, Tx* x, Ty* y, Ta a, MList* List=0) +{ + if (!z){z=new Tz[N]; if (List) List->Add(z, 1);} + for (int i=0; i Tx* MultiAdd(int N, Tx* x, Ty* y, Ta a, MList* List=0) +{ + return MultiAdd(N, (Tx*)0, x, y, a, List); +}//MultiAdd + +//--matrix multiplication by constant---------------------------------------- +/* + template Multiply: matrix-constant multiplication Z=aX + + In: matrix X[M][N], scalar a + Out: matrix Z[M][N] + + Returns pointer to Z. Z is created anew if Z=0 is specified on start. +*/ +template Tz** Multiply(int M, int N, Tz** Z, Tx** X, Ta a, MList* List=0) +{ + if (!Z){Allocate2(Tz, M, N, Z); if (List) List->Add(Z, 2);} + for (int i=0; i Tz* Multiply(int N, Tz* z, Tx* x, Ta a, MList* List=0) +{ + if (!z){z=new Tz[N]; if (List) List->Add(z, 1);} + for (int i=0; iTx* Multiply(int N, Tx* x, Ta a, MList* List=0) +{ + return Multiply(N, (Tx*)0, x, a, List); +}//Multiply + +//--matrix multiplication operations----------------------------------------- +/* + macro Multiply_full: matrix-matrix multiplication z=xy and multiplication-accumulation z=z+xy. + + Each expansion of the macro defines three function templates; two are named $MULTIPLY and do matrix + multiplication only; one is named $MULTIADD and accumulates the multiplicated result on top of a + specified matrix. One of the two $MULTIPLY functions allows using a pre-allocated matrix to accept + the matrix product, while the other directly allocates a new matrix and returns the pointer. + + Functions are named after their exact functions. For example, MultiplyXYc multiplies matrix X with + the complex conjugate of matrix Y, where postfix "c" attched to Y stands for conjugate. Likewise, + the postfix "t" stands for transpose, and "h" stnads for Hermitian (conjugate transpose). + + Three dimension arguments are needed by each function template. The first and last of the three are + the number of rows and columns, respectively, of the product (output) matrix. The middle one is the + "other", common dimension of both multiplier matrices. +*/ +#define Multiply_full(MULTIPLY, MULTIADD, xx, yy) \ + templateTx** MULTIPLY(int N, int M, int K, Tx** z, Tx** x, Ty** y, MList* List=0){ \ + if (!z) {Allocate2(Tx, N, K, z); if (List) List->Add(z, 2);} \ + for (int n=0; nTx** MULTIPLY(int N, int M, int K, Tx** x, Ty** y, MList* List=0){ \ + Tx** Allocate2(Tx, N, K, z); if (List) List->Add(z, 2); \ + for (int n=0; nvoid MULTIADD(int N, int M, int K, Tx** z, Tx** x, Ty** y){ \ + for (int n=0; nT** MULTIPLY(int N, T** z, T** x, T** y, MList* List=0){ \ + if (!z){Allocate2(T, N, N, z); if (List) List->Add(z, 2);} \ + if (z!=x) MULTIPLY(N, N, N, z, x, y); else{ \ + T* tmp=new T[N]; int sizeN=sizeof(T)*N; for (int i=0; iT** MULTIPLY(int N, T** x, T** y, MList* List=0){return MULTIPLY(N, N, N, x, y, List);}\ + templatevoid MULTIADD(int N, T** z, T** x, T** y){MULTIADD(N, N, N, z, x, y);} +Multiply_square(MultiplyXY, MultiAddXY) + +/* + macro Multiply_xx: matrix self multiplication z=xx and self-multiplication-accumulation z=z+xx. + + Each expansion of the macro defines three function templates; two are named $MULTIPLY and do matrix + multiplication only; one is named $MULTIADD and accumulates the multiplicated result on top of a + specified matrix. One of the two $MULTIPLY functions allows using a pre-allocated matrix to accept + the matrix product, while the other directly allocates a new matrix and returns the pointer. +*/ +#define Multiply_xx(MULTIPLY, MULTIADD, xx1, xx2, zzt) \ + templateT** MULTIPLY(int M, int N, T** z, T** x, MList* List=0){ \ + if (!z){Allocate2(T, M, M, z); if (List) List->Add(z, 2);} \ + for (int m=0; mT** MULTIPLY(int M, int N, T ** x, MList* List=0){ \ + T** Allocate2(T, M, M, z); if (List) List->Add(z, 2); \ + for (int m=0; mvoid MULTIADD(int M, int N, T** z, T** x){ \ + for (int m=0; m +#include +#include "QuickSpec.h" + + +//--------------------------------------------------------------------------- +/* + method TQuickSpectrogram::TQuickSpectrogram: + + In: AParent: pointer argument for calling G, if G is specified + AnId: integer argument for calling G, if G is specified + G: pointer to a function that supplies buffers used for FFT, 0 by default + Ausex: set if complete complex spectrogram is to be buffered and accessible + Auseph: set if phase spectrogram is to be buffered and accessible +*/ +__fastcall TQuickSpectrogram::TQuickSpectrogram(void* AParent, int AnId, bool Ausex, bool Auseph, GetBuf G) +{ + memset(this, 0, sizeof(TQuickSpectrogram)); + Parent=AParent; + Id=AnId; + usex=Ausex; + useph=Auseph; + GetFFTBuffers=G; + BufferSize=QSpec_BufferSize; + fwt=wtRectangle; + Wid=1024; + Offst=512; +}//TQuickSpectrogram + +//TQuickSpectrogram::~TQuickSpectrogram +__fastcall TQuickSpectrogram::~TQuickSpectrogram() +{ + FreeBuffers(); + free8(fw); + free8(fwin); + free(fhbi); +}//~TQuickSpectrogram + +//--------------------------------------------------------------------------- +/* + method TQuickSpectrogram::A: accesses amplitude spectrogram by frame + + In: fr: frame index, 0-based + + Returns pointer to amplitude spectrum of the fr'th frame, NULL if N/A +*/ +QSPEC_FORMAT* __fastcall TQuickSpectrogram::A(int fr) +{ + if (Capacity==0) SetFrCapacity((DataLength-Wid)/Offst+2); + if (fr<0 || fr>=Capacity) return NULL; + if (Frame[fr]<0 || !Valid[fr]) CalculateSpectrum(fr); + return fA[Frame[fr]]; +}//A + +//--------------------------------------------------------------------------- +/* + method TQuickSpectrogram::AddBuffer: increases internal buffer by BufferSize frames. Allocated buffers + beyond Capacity frames will not be indexed or used by TQuickSpectrogram. +*/ +void TQuickSpectrogram::AddBuffer() +{ + int base=1, Dim=Wid/2+1; + if (usex) base+=2; + if (useph) base+=1; + QSPEC_FORMAT* newbuffer=(QSPEC_FORMAT*)malloc(sizeof(QSPEC_FORMAT)*Dim*BufferSize*base); + int fr0=BufferCount*BufferSize; + for (int i=0; i*)&newbuffer[(BufferSize+i*2)*Dim], base+=2; + if (useph) fPh[fr]=&newbuffer[(BufferSize*base+i)*Dim]; + } + else break; + } + BufferCount++; +}//AddBuffer + +/* + method TQuickSpectrogram::AddBuffer: increase internal buffer by a multiple of BufferSize so that + it will be enough to host another AddFrCount frames. +*/ +void TQuickSpectrogram::AddBuffer(int AddFrCount) +{ + while (FrCount+AddFrCount>BufferSize*BufferCount) AddBuffer(); +}//AddBuffer + +//--------------------------------------------------------------------------- +/* + function IntToDouble: copy content of integer array to double array + + In: in: pointer to integer array + BytesPerSample: number of bytes each integer takes + Count: size of integer array, in integers + Out: vector out[Count]. + + No return value. +*/ +void IntToDouble(double* out, void* in, int BytesPerSample, int Count) +{ + if (BytesPerSample==1){unsigned char* in8=(unsigned char*)in; for (int k=0; k* lX=usex?fSpec[realfr]:NULL; + //choose the buffer actually used for FFT - use lX if it is specified as complex double array + //because it saves unnecessary data copying operations + cdouble *lx=(usex && sizeof(QSPEC_FORMAT)==sizeof(double))?(cdouble*)lX:x; + + //Actual FFT + if (fr*Offst+Wid<=DataLength) + ::CalculateSpectrum(&((char*)Data)[fr*Offst*BytesPerSample], BytesPerSample, win, fA[realfr], lph, Wid, w, lx, hbi); + else + ::CalculateSpectrum(&((char*)Data)[fr*Offst*BytesPerSample], BytesPerSample, win, fA[realfr], lph, Wid, DataLength-fr*Offst, w, x, hbi); + + //optional data copy from x to lX + if (usex && lx==x) for (int i=0; i=Capacity) fr2=Capacity-1; + for (int fr=fr1; fr<=fr2; fr++) if (Frame[fr]>=0) Valid[fr]=false, result++; + } + return result; +}//Invalidate + +//--------------------------------------------------------------------------- +/* + method TQuickSpectrogram::Ph: accesses phase spectrogram by frame + + In: fr: frame index, 0-based + + Returns pointer to phase spectrum of the fr'th frame, NULL if N/A +*/ +QSPEC_FORMAT* __fastcall TQuickSpectrogram::Ph(int fr) +{ + if (Capacity==0) SetFrCapacity((DataLength-Wid)/Offst+2); + if (fr<0 || fr>=Capacity) return NULL; + if (Frame[fr]<0 || !Valid[fr]) CalculateSpectrum(fr); + return fPh[Frame[fr]]; +}//Ph + +//--------------------------------------------------------------------------- +/* + method TQuickSpectrogram::SetFrCapacity: sets the capacity, i.e. the maximal number of frames handled + by this TQuickSpectrogram. + + In: AnFrCapacity: the new Capacity, in frames + + This method should not be called to set Capacity to a smaller value. +*/ +void TQuickSpectrogram::SetFrCapacity(int AnFrCapacity) +{ + //adjusting the size of index and validity arrays + Frame=(int*)realloc(Frame, sizeof(int)*AnFrCapacity); + Valid=(int*)realloc(Valid, sizeof(int)*AnFrCapacity); + + // + fA=(QSPEC_FORMAT**)realloc(fA, sizeof(QSPEC_FORMAT*)*AnFrCapacity); + if (usex) fSpec=(cmplx**)realloc(fSpec, sizeof(cmplx*)*AnFrCapacity); + if (useph) fPh=(QSPEC_FORMAT**)realloc(fPh, sizeof(QSPEC_FORMAT*)*AnFrCapacity); + if (AnFrCapacity>Capacity) + { + memset(&Frame[Capacity], 0xFF, sizeof(int)*(AnFrCapacity-Capacity)); + memset(&Valid[Capacity], 0x00, sizeof(int)*(AnFrCapacity-Capacity)); + + if (Capacity*)(&thisbuffer[(BufferSize+lfr*2)*Dim]), base+=2; + if (useph) fPh[fr]=&thisbuffer[(BufferSize*base+lfr)*Dim]; + } + else break; + } + } + } + Capacity=AnFrCapacity; +}//SetFrCapacity + +//--------------------------------------------------------------------------- +/* + method TQuickSpectrogram::Ph: accesses complex spectrogram by frame + + In: fr: frame index, 0-based + + Returns pointer to complex spectrum of the fr'th frame, NULL if N/A +*/ +cmplx* __fastcall TQuickSpectrogram::Spec(int fr) +{ + if (Capacity==0) SetFrCapacity((DataLength-Wid)/Offst+2); + if (fr<0 || fr>=Capacity) return NULL; + if (Frame[fr]<0 || !Valid[fr]) CalculateSpectrum(fr); + return fSpec[Frame[fr]]; +}//Spec + + diff -r 9b9f21935f24 -r 6422640a802f QuickSpec.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QuickSpec.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,158 @@ +#ifndef QuickSpecH +#define QuickSpecH + +/* + QuickSpec.cpp - TQuickSpectrogram class + + TQuickSpectrogram implements a compute-on-request spectrogram class. Every time one frame of the + spectrogram is read from this class, it checks if the single-frame spectrogram has been computed + already, computes it if not, and return the pointer to the spectrum. + + Further reading: "A buffering technique for real-time spectrogram displaying.pdf" +*/ + + +#include +#include +#include "xcomplex.h" +#include "windowfunctions.h" +#include "fft.h" +//--------------------------------------------------------------------------- + +#define QSPEC_FORMAT float //default spectral data format +#define QSpec_BufferSize 1024 //default initial buffer size, in frames, of TQuickSpectrogram + + +/* + __int24 is a 24bit signed integer type and __pint24 is its pointer type. Although __int24* will also + return a pointer to an __int24 structure, operations based on __int24* may have unspecified results, + depending on structure alignments imposed by compiler. It is therefore necessary to have an explicit + pointer type __pint24 to enforce 24-bit data alighment. + + Using __int24: + When storage format is not a concern, __int24 can be used the same way as __int16 or __int32. However, + a default 32-bit compiler may fail to implement compact 24-bit alignment to entries of an __int24[] + array. If 24-bit alignment is desired, then always create the array dynamically with new[], which + returns a __pint24 type pointer. Assigning a __pint24 type pointer to __int24* type variable should + be avoided. +*/ + +#ifndef INT24 +#define INT24 +struct __int24; +struct __pint24 +{ + char* p; + __fastcall __pint24(){} + __fastcall __pint24(const void* ap){p=(char*)ap;} + __pint24& __fastcall operator=(const void* ap){p=(char*)ap; return *this;} + __int24& __fastcall operator*(){return *(__int24*)p;} + __int24& __fastcall operator[](int index){return *(__int24*)&p[3*index];} + __pint24 __fastcall operator++(int){__pint24 result=*this; p+=3; return result;} + __pint24& __fastcall operator++(){p+=3; return *this;} + __pint24 __fastcall operator--(int){__pint24 result=*this; p-=3; return result;} + __pint24& __fastcall operator--(){p-=3; return *this;} + __pint24& __fastcall operator+=(int a){p+=3*a; return *this;} + __pint24& __fastcall operator-=(int a){p-=3*a; return *this;} + __fastcall operator void*() const{return p;} +}; +struct __int24 +{ + __int16 loword; + __int8 hibyte; + __fastcall __int24(){} + __fastcall __int24(const __int32 a){loword=*(__int16*)&a; hibyte=((__int16*)&a)[1];} + __fastcall __int24(const double f){__int32 a=f; loword=*(__int16*)&a; hibyte=((__int16*)&a)[1];} + __int24& __fastcall operator=(const __int32 a){loword=*(__int16*)&a; hibyte=((__int16*)&a)[1]; return *this;} + __int24& __fastcall operator=(const double f){__int32 a=f; loword=*(__int16*)&a; hibyte=((__int16*)&a)[1]; return *this;} + __fastcall operator __int32() const{__int32 result; *(__int16*)&result=loword; ((__int16*)&result)[1]=hibyte; return result;} + __pint24 operator &(){return (__pint24)this;} + void* operator new[](size_t count){void* result=malloc(3*count); return result;} + void operator delete[](void* p){free(p);} +}; +#endif + +/* + TQuickSpectrogram is a spectrogram class the handles the computation and storage of a spectrogram. + + Using TQuickSpectrogram: + + TQuickSpectrogram provides read-only access to the spectrogram of a given waveform. The spectrogram + contains a sequence of windowed Fourier transforms, computed from uniformly placed frames. The 0th + frame starts at 0 (inclusive) and finishes at Wid (exclusive). Each spectrum has Wid/2+1 entries. + + Follow these steps: + 1. create a QuickSpectrogram, specifying usex and useph, and optionally, external buffer provide G + and its arguments Id and Parent; + 2. set Data, DataLength and BytesPerSample; + 3. set frame size and hop size Wid and Offst; + 4. if G is not specified, set window type (optional extra parameter) for computing spectra; + 5. access the spectrogram via A(fr), Spec(fr) (optional) and Ph(fr) (optional). + Steps 2~4 do not have to follow the given order. + + Call Invalidate() to notify the object of changes of waveform content. + Call FreeBuffers() to return the object to the initial state before step 2. +*/ + +typedef void (*GetBuf)(int Id, cdouble* &w, cdouble* &x, double* &win, int* &hbi, void* Parent); +class TQuickSpectrogram +{ + int BufferCount; //number of buffers in use + int BufferSize; //number of frames each additional buffer holds + int FrCount; //number of allocated frames + + //internal storage of spectrogram + QSPEC_FORMAT** fPh; //phase spectrogram, optional + QSPEC_FORMAT** fA; //amplitude spectrogram, compulsory + cmplx** fSpec; //complete complex spectrogram, optional + + //internal buffers (optional) for FFT + WindowType fwt; //type of window + int fWid; //size of window + int* fhbi; //half-size bit-inversed integer table + double fwdp; //additional parameter specifying window type + double* fwin; //internal window + cdouble* fw; //FFT twiddle factors + cdouble* fx; //FFT data buffer + + //x and ph create-time switch + bool usex; //set at create time if complex spectrogram is required + bool useph; //set at create time if phase spectrogram is required + + //internal methods + void AddBuffer(); + void AddBuffer(int AddFrCount); + void __fastcall CalculateSpectrum(int fr); + void SetFrCapacity(int AnFrCapacity); + +public: + //if specified, Parent is responsible to supply FFT buffers through GetFFTBuffers (optional) + int Id; //an identifier given at create time, used as argument for calling GetFFTBuffers() + void* Parent; //a pointer used as argument for calling GetFFTBuffers() + GetBuf GetFFTBuffers; //if specified, this supplies FFT buffers + + //index and validity arrays associated with internal storage + int Capacity; //size of $Frame[] and &Valid[], usually set to the total number of frames of the data + int* Frame; //indices to individual frames in internal storage + int* Valid; //validity tags to individual frames in internal storage + + WindowType WinType; //window type for computing spectrogram + double WinParam; //additional parameter specifying certain window types (Gaussian, Kaiser, etc.) + + void* Data; //pointer to waveform audio + int DataLength; //length of waveform audio, in samples + int BytesPerSample; //bytes per sample of waveform audio + + int Offst; //frame offset + int Wid; //frame size, the same as window size + + __fastcall TQuickSpectrogram(void* AParent, int AnId, bool Ausex, bool Auseph, GetBuf G); + __fastcall ~TQuickSpectrogram(); + + QSPEC_FORMAT* __fastcall A(int fr); //accesses amplitude spectrogram at frame fr + void FreeBuffers(); //discards all computed frames and free memory + int Invalidate(int From, int To); //discards computed frames + QSPEC_FORMAT* __fastcall Ph(int fr); //accesses phase spectrogram at frame fr + cmplx* __fastcall Spec(int fr); //accesses complex spectrogram at frame fr +}; +#endif diff -r 9b9f21935f24 -r 6422640a802f SinEst.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SinEst.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,3970 @@ +//--------------------------------------------------------------------------- + +#include +#include "SinEst.h" +#include "fft.h" +#include "opt.h" +#include "SinSyn.h" +#include "splines.h" +#include "WindowFunctions.h" + +//--------------------------------------------------------------------------- +/* + function dsincd_unn: derivative of unnormalized discrete sinc function + + In: x, scale N + + Returns the derivative of sincd_unn(x, N) +*/ +double dsincd_unn(double x, int N) +{ + double r=0; + double omg=M_PI*x; + double domg=omg/N; + if (fabs(x)>1e-6) + { + r=M_PI*(cos(omg)-sin(omg)*cos(domg)/sin(domg)/N)/sin(domg); + } + else + { + if (domg!=0) + { + double sindomg=sin(domg); + r=-omg*omg*omg*(1-1.0/(1.0*N*N))/3*M_PI/N/sindomg/sindomg; + } + else + r=0; + } + return r; +}//dsincd_unn + +/* + function ddsincd_unn: 2nd-order derivative of unnormalized discrete sinc function + + In: x, scale (equivalently, window size) N + + Returns the 2nd-order derivative of sincd_unn(x, N) +*/ +double ddsincd_unn(double x, int N) +{ + double r=0; + double omg=M_PI*x; + double domg=omg/N; + double PI2=M_PI*M_PI; + double NN=1.0/N/N-1; + if (domg==0) + { + r=PI2*N*NN/3; + } + else + { + if (fabs(x)>1e-5) + { + r=sin(domg)*cos(omg)-sin(omg)*cos(domg)/N; + } + else + { + r=omg*omg*omg/N*NN/3; + } + double ss=sin(omg)*NN; + r=-2.0/N*cos(domg)*r/sin(domg)/sin(domg)+ss; + r=r*PI2/sin(domg); + } + return r; +}//ddsincd_unn + +//--------------------------------------------------------------------------- +/* + function Window: calculates the cosine-family-windowed spectrum of a complex sinusoid on [0:N-1] at + frequency f bins with zero central phase. + + In: f: frequency, in bins + N: window size + M, c[]: cosine-family window decomposition coefficients + Out: x[0...K2-K1] containing the spectrum at bins K1, ..., K2. + + Returns pointer to x. x is created anew if x=0 is specified on start. +*/ +cdouble* Window(cdouble* x, double f, int N, int M, double* c, int K1, int K2) +{ + if (K1<0) K1=0; + if (K2>N/2-1) K2=N/2-1; + + if (!x) x=new cdouble[K2-K1+1]; + memset(x, 0, sizeof(cdouble)*(K2-K1+1)); + + for (int l=K1-M; l<=K2+M; l++) + { + double ang=(f-l)*M_PI; + double omg=ang/N; + long double si, co, sinn=sin(ang); + si=sin(omg), co=cos(omg); + double sa=(ang==0)?N:(sinn/si); + double saco=sa*co; + + int k1=l-M, k2=l+M; + if (k1K2) k2=K2; + + for (int k=k1; k<=k2; k++) + { + int m=k-l, kt=k-K1; + if (m<0) m=-m; + if (k%2) + { + x[kt].x-=c[m]*saco; + x[kt].y+=c[m]*sinn; + } + else + { + x[kt].x+=c[m]*saco; + x[kt].y-=c[m]*sinn; + } + } + } + return x; +}//Window + +/* + function dWindow: calculates the cosine-family-windowed spectrum and its derivative of a complex + sinusoid on [0:N-1] at frequency f bins with zero central phase. + + In: f: frequency, in bins + N: window size + M, c[]: cosine-family window decomposition coefficients + Out: x[0...K2-K1] containing the spectrum at bins K1, ..., K2, + dx[0...K2-K1] containing the derivative spectrum at bins K1, ..., K2 + + No return value. +*/ +void dWindow(cdouble* dx, cdouble* x, double f, int N, int M, double* c, int K1, int K2) +{ + if (K1<0) K1=0; + if (K2>N/2-1) K2=N/2-1; + memset(x, 0, sizeof(cdouble)*(K2-K1+1)); + memset(dx, 0, sizeof(cdouble)*(K2-K1+1)); + + for (int l=K1-M; l<=K2+M; l++) + { + double ang=(f-l), Omg=ang*M_PI, omg=Omg/N; + long double si, co, sinn=sin(Omg), cosn=cos(Omg); + si=sin(omg), co=cos(omg); + double sa=(ang==0)?N:(sinn/si), dsa=dsincd_unn(ang, N); + double saco=sa*co, dsaco=dsa*co, sinnpi_n=sinn*M_PI/N, cosnpi=cosn*M_PI; + + int k1=l-M, k2=l+M; + if (k1K2) k2=K2; + + for (int k=k1; k<=k2; k++) + { + int m=k-l, kt=k-K1; + if (m<0) m=-m; + if (k%2) + { + x[kt].x-=c[m]*saco; + x[kt].y+=c[m]*sinn; + dx[kt].x-=c[m]*(-sinnpi_n+dsaco); + dx[kt].y+=c[m]*cosnpi; + } + else + { + x[kt].x+=c[m]*saco; + x[kt].y-=c[m]*sinn; + dx[kt].x+=c[m]*(-sinnpi_n+dsaco); + dx[kt].y-=c[m]*cosnpi; + } + } + } +}//dWindow + +/* + function ddWindow: calculates the cosine-family-windowed spectrum and its 1st and 2nd derivatives of + a complex sinusoid on [0:N-1] at frequency f bins with zero central phase. + + In: f: frequency, in bins + N: window size + M, c[]: cosine-family window decomposition coefficients + Out: x[0...K2-K1] containing the spectrum at bins K1, ..., K2, + dx[0...K2-K1] containing the derivative spectrum at bins K1, ..., K2 + ddx[0...K2-K1] containing the 2nd-order derivative spectrum at bins K1, ..., K2 + + No return value. +*/ +void ddWindow(cdouble* ddx, cdouble* dx, cdouble* x, double f, int N, int M, double* c, int K1, int K2) +{ + if (K1<0) K1=0; + if (K2>N/2-1) K2=N/2-1; + memset(x, 0, sizeof(cdouble)*(K2-K1+1)); + memset(dx, 0, sizeof(cdouble)*(K2-K1+1)); + memset(ddx, 0, sizeof(cdouble)*(K2-K1+1)); + + for (int l=K1-M; l<=K2+M; l++) + { + double ang=(f-l), Omg=ang*M_PI, omg=Omg/N; + long double si, co, sinn=sin(Omg), cosn=cos(Omg); + si=sin(omg), co=cos(omg); + double sa=(ang==0)?N:(sinn/si), dsa=dsincd_unn(ang, N), ddsa=ddsincd_unn(ang, N); + double saco=sa*co, dsaco=dsa*co, sinnpi_n=sinn*M_PI/N, sinnpipi=sinn*M_PI*M_PI, cosnpi=cosn*M_PI, + cosnpipi_n=cosnpi*M_PI/N, sipi_n=si*M_PI/N; + + int k1=l-M, k2=l+M; + if (k1K2) k2=K2; + + for (int k=k1; k<=k2; k++) + { + int m=k-l, kt=k-K1; + if (m<0) m=-m; + if (k%2) + { + x[kt].x-=c[m]*saco; + x[kt].y+=c[m]*sinn; + dx[kt].x-=c[m]*(-sinnpi_n+dsaco); + dx[kt].y+=c[m]*cosnpi; + ddx[kt].x-=c[m]*(-cosnpipi_n+ddsa*co-dsa*sipi_n); + ddx[kt].y-=c[m]*sinnpipi; + } + else + { + x[kt].x+=c[m]*saco; + x[kt].y-=c[m]*sinn; + dx[kt].x+=c[m]*(-sinnpi_n+dsaco); + dx[kt].y-=c[m]*cosnpi; + ddx[kt].x+=c[m]*(-cosnpipi_n+ddsa*co-dsa*sipi_n); + ddx[kt].y+=c[m]*sinnpipi; + } + } + } +}//ddWindow + +//--------------------------------------------------------------------------- +/* + function IPWindow: computes the truncated inner product of a windowed spectrum with that of a sinusoid + at reference frequency f. + + In: x[0:N-1]: input spectrum + f: reference frequency, in bins + M, c[], iH2: cosine-family window specification parameters + K1, K2: spectrum truncation bounds, in bins, inclusive + returnamplitude: specifies return value, true for amplitude, false for angle + + Returns the amplitude or phase of the inner product, as specified by $returnamplitude. The return + value is interpreted as the actual amplitude/phase of a sinusoid being estimated at f. +*/ +double IPWindow(double f, cdouble* x, int N, int M, double* c, double iH2, int K1, int K2, bool returnamplitude) +{ + cdouble r=IPWindowC(f, x, N, M, c, iH2, K1, K2); + double result; + if (returnamplitude) result=sqrt(r.x*r.x+r.y*r.y); + else result=arg(r); + return result; +}//IPWindow +//wrapper function +double IPWindow(double f, void* params) +{ + struct l_ip {int N; int k1; int k2; int M; double* c; double iH2; cdouble* x; double dipwindow; double ipwindow;} *p=(l_ip *)params; + return IPWindow(f, p->x, p->N, p->M, p->c, p->iH2, p->k1, p->k2, true); +}//IPWindow + +/* + function ddIPWindow: computes the norm of the truncated inner product of a windowed spectrum with + that of a sinusoid at reference frequency f, as well as its 1st and 2nd derivatives. + + In: x[0:N-1]: input spectrum + f: reference frequency, in bins + M, c[], iH2: cosine-family window specification parameters + K1, K2: spectrum truncation bounds, in bins, inclusive + Out: ipwindow and dipwindow: the truncated inner product norm and its derivative + + Returns the 2nd derivative of the norm of the truncated inner product. +*/ +double ddIPWindow(double f, cdouble* x, int N, int M, double* c, double iH2, int K1, int K2, double& dipwindow, double& ipwindow) +{ + if (K1<0) K1=0; if (K2>=N/2) K2=N/2-1; + int K=K2-K1+1; + cdouble *w=new cdouble[K*3], *dw=&w[K], *ddw=&w[K*2], *lx=&x[K1]; + ddWindow(ddw, dw, w, f, N, M, c, K1, K2); + cdouble r=Inner(K, lx, w), dr=Inner(K, lx, dw), ddr=Inner(K, lx, ddw); + delete[] w; + + double R2=~r, + R=sqrt(R2), + dR2=2*(r.x*dr.x+r.y*dr.y), + dR=dR2/(2*R), + ddR2=2*(r.x*ddr.x+r.y*ddr.y+~dr), + ddR=(R*ddR2-dR2*dR)/(2*R2); + ipwindow=R*iH2; + dipwindow=dR*iH2; + return ddR*iH2; +}//ddIPWindow +//wrapper function +double ddIPWindow(double f, void* params) +{ + struct l_ip {int N; int k1; int k2; int M; double* c; double iH2; cdouble* x; double dipwindow; double ipwindow;} *p=(l_ip *)params; + return ddIPWindow(f, p->x, p->N, p->M, p->c, p->iH2, p->k1, p->k2, p->dipwindow, p->ipwindow); +}//ddIPWindow + +//--------------------------------------------------------------------------- +/* + function IPWindowC: computes the truncated inner product of a windowed spectrum with that of a + sinusoid at reference frequency f. + + In: x[0:N-1]: input spectrum + f: reference frequency, in bins + M, c[], iH2: cosine-family window specification parameters + K1, K2: spectrum truncation bounds, in bins, inclusive + + Returns the inner product. The return value is interpreted as the actual amplitude-phase factor of a + sinusoid being estimated at f. +*/ +cdouble IPWindowC(double f, cdouble* x, int N, int M, double* c, double iH2, int K1, int K2) +{ + if (K1<0) K1=0; if (K2>=N/2) K2=N/2-1; + int K=K2-K1+1; + cdouble *w=new cdouble[K]; + cdouble *lx=&x[K1], result=0; + Window(w, f, N, M, c, K1, K2); + for (int k=0; k=N/2) K2=N/2-1; + int K=K2-K1+1; + cdouble *w=new cdouble[K]; + Window(w, f, N, M, c, K1, K2); + for (int l=0; lFr, p->x, p->N, p->M, p->c, p->iH2, p->k1, p->k2, p->lmd); +}//sIPWindow + +/* + function dsIPWindow: computes the total energy of truncated inner products between multiple windowed + spectra and that of a sinusoid at a reference frequency f, as well as its derivative. This does not + consider phase synchronization between the spectra, supposedly measured at a sequence of known + instants. + + In: x[L][N]: input spectra + f: reference frequency, in bins + M, c[], iH2: cosine-family window specification parameters + K1, K2: spectrum truncation bounds, in bins, inclusive + Out: sip, the energy of the vector of inner products. + + Returns the derivative of the energy of the vector of inner products. +*/ +double dsIPWindow(double f, int L, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, double& sip) +{ + if (K1<0) K1=0; if (K2>=N/2) K2=N/2-1; + int K=K2-K1+1; + cdouble *w=new cdouble[K*2], *dw=&w[K]; + dWindow(dw, w, f, N, M, c, K1, K2); + double dsip; sip=0; + for (int l=0; lFr, p->x, p->N, p->M, p->c, p->iH2, p->k1, p->k2, p->sip); +}//dsIPWindow + +/* + function dsdIPWindow_unn: computes the energy of unnormalized truncated inner products between a given + windowed spectrum and that of a sinusoid at a reference frequency f, as well as its 1st and 2nd + derivatives. "Unnormalized" indicates that the inner product cannot be taken as the actual amplitude- + phase factor of a sinusoid, but deviate from that by an unspecified factor. + + In: x[N]: input spectrum + f: reference frequency, in bins + M, c[], iH2: cosine-family window specification parameters + K1, K2: spectrum truncation bounds, in bins, inclusive + Out: sipwindow and dsipwindow, the energy and its derivative of the unnormalized inner product. + + Returns the 2nd derivative of the inner product. +*/ +double ddsIPWindow_unn(double f, cdouble* x, int N, int M, double* c, int K1, int K2, double& dsipwindow, double& sipwindow, cdouble* w_unn) +{ + if (K1<0) K1=0; if (K2>=N/2) K2=N/2-1; + int K=K2-K1+1; + + cdouble *w=new cdouble[K*3], *dw=&w[K], *ddw=&w[K*2]; + + ddWindow(ddw, dw, w, f, N, M, c, K1, K2); + + double rr=0, ri=0, drr=0, dri=0, ddrr=0, ddri=0; + cdouble *lx=&x[K1]; + for (int k=0; kx=rr, w_unn->y=ri; + return ddR2; +}//ddsIPWindow_unn + +/* + function ddsIPWindow: computes the total energy of truncated inner products between multiple windowed + spectra and that of a sinusoid at a reference frequency f, as well as its 1st and 2nd derivatives. + This does not consider phase synchronization between the spectra, supposedly measured at a sequence + of known instants. + + In: x[L][N]: input spectra + f: reference frequency, in bins + M, c[], iH2: cosine-family window specification parameters + K1, K2: spectrum truncation bounds, in bins, inclusive + Out: sip and dsip, the energy of the vector of inner products and its derivative. + + Returns the 2nd derivative of the energy of the vector of inner products. +*/ +double ddsIPWindow(double f, int L, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, double& dsip, double& sip) +{ + if (K1<0) K1=0; if (K2>=N/2) K2=N/2-1; + int K=K2-K1+1; + cdouble *w=new cdouble[K*3], *dw=&w[K], *ddw=&w[K*2]; + ddWindow(ddw, dw, w, f, N, M, c, K1, K2); + double ddsip=0; dsip=sip=0; + for (int l=0; lFr, p->x, p->N, p->M, p->c, p->iH2, p->k1, p->k2, p->dsip, p->sip); +}//ddsIPWindow + +//--------------------------------------------------------------------------- +/* + function sIPWindowC: computes the total energy of truncated inner products between multiple frames of + a spectrogram and multiple frames of a spectrogram of a sinusoid at a reference frequency f. + + In: x[L][N]: the spectrogram + offst_rel: frame offset, relative to frame size + f: reference frequency, in bins + M, c[], iH2: cosine-family window specification parameters + K1, K2: spectrum truncation bounds, in bins, inclusive + Out: lmd[L]: the actual individual inner products representing actual ampltiude-phase factors (optional) + + Returns the energy of the vector of inner products. +*/ +double sIPWindowC(double f, int L, double offst_rel, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, cdouble* lmd) +{ + if (K1<0) K1=0; if (K2>=N/2) K2=N/2-1; + int K=K2-K1+1; + cdouble *w=new cdouble[K]; + double Cr=0; + cdouble Cc=0; + Window(w, f, N, M, c, K1, K2); + for (int l=0; lL, p->offst_rel, p->x, p->N, p->M, p->c, p->iH2, p->k1, p->k2); +}//sIPWindowC + +/* + function dsIPWindowC: computes the total energy of truncated inner products between multiple frames of + a spectrogram and multiple frames of a spectrogram of a sinusoid at a reference frequency f, together + with its derivative. + + In: x[L][N]: the spectrogram + offst_rel: frame offset, relative to frame size + f: reference frequency, in bins + M, c[], iH2: cosine-family window specification parameters + K1, K2: spectrum truncation bounds, in bins, inclusive + Out: sip: energy of the vector of the inner products + + Returns the 1st derivative of the energy of the vector of inner products. +*/ +double dsIPWindowC(double f, int L, double offst_rel, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, double& sip) +{ + if (K1<0) K1=0; if (K2>=N/2) K2=N/2-1; + int K=K2-K1+1; + + cdouble *w=new cdouble[K*2], *dw=&w[K]; + dWindow(dw, w, f, N, M, c, K1, K2); + double Cr=0, dCr=0; + cdouble Cc=0, dCc=0; + for (int l=0; lL, p->offst_rel, p->x, p->N, p->M, p->c, p->iH2, p->k1, p->k2, p->sip); +}//dsIPWindowC + +/* + function ddsIPWindowC: computes the total energy of truncated inner products between multiple frames + of a spectrogram and multiple frames of a spectrogram of a sinusoid at a reference frequency f, + together with its 1st and 2nd derivatives. + + In: x[L][N]: the spectrogram + offst_rel: frame offset, relative to frame size + f: reference frequency, in bins + M, c[], iH2: cosine-family window specification parameters + K1, K2: spectrum truncation bounds, in bins, inclusive + Out: sipwindow, dsipwindow: energy of the vector of the inner products and its derivative + + Returns the 2nd derivative of the energy of the vector of inner products. +*/ +double ddsIPWindowC(double f, int L, double offst_rel, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, double& dsipwindow, double& sipwindow) +{ + if (K1<0) K1=0; if (K2>=N/2) K2=N/2-1; + int K=K2-K1+1; + + cdouble *w=new cdouble[K*3], *dw=&w[K], *ddw=&w[K*2]; + ddWindow(ddw, dw, w, f, N, M, c, K1, K2); + double Cr=0, dCr=0, ddCr=0; + cdouble Cc=0, dCc=0, ddCc=0; + for (int l=0; lL, p->offst_rel, p->x, p->N, p->M, p->c, p->iH2, p->k1, p->k2, p->dipwindow, p->ipwindow); +}//ddsIPWindowC + +//-------------------------------------------------------------------------- +/* + Least-square-error sinusoid detection function + + version1: picking the highest peak and take measurement of a single sinusoid + version2: given a rough peak location and take measurement of a single sinusoid + + Complex spectrum x is calculated using N data points windowed by a window function that is specified + by the parameter set (M, c, iH2). c[0:M] is provided according to Table 3 in the transfer report, on + pp.11. iH2 is simply 1/H2, where H2 can be calculated using formula (2.17) on pp.12. + + f & epf are given/returned in bins. + + Further reading: "Least-square-error estimation of sinusoids.pdf" +*/ + +/* + function LSESinusoid: LSE estimation of the predominant stationary sinusoid. + + In: x[N]: windowed spectrum + B: spectral truncation half width, in bins. + M, c[], iH2: cosine-family window specification parameters + epf: frequency error tolerance, in bins + Out: a and pp: amplitude and phase estimates + + Returns the frequency estimate, in bins. +*/ +double LSESinusoid(cdouble* x, int N, double B, int M, double* c, double iH2, double& a, double& pp, double epf) +{ + struct l_hx {int N; int k1; int k2; int M; double* c; double iH2; cdouble* x; double dhxpeak; double hxpeak;} p={N, 0, 0, M, c, iH2, x, 0, 0}; //(l_hx *)¶ms; + int dfshift=int(&((l_hx*)0)->dhxpeak); + + int inp; + double minp=0; + for (int i=0; i=p.N/2) p.k2=p.N/2-1; + tmp=IPWindow(lf, &p); + if (minp=p.N/2) p.k2=p.N/2-1; + double tmp=Newton(f, ddIPWindow, &p, dfshift, epf); + if (tmp==-1) + { + Search1Dmax(f, &p, IPWindow, inp-1, inp+1, &a, epf); + } + else + a=p.hxpeak; + pp=IPWindow(f, x, N, M, c, iH2, p.k1, p.k2, false); + return f; +}//LSESinusoid + +/*function LSESinusoid: LSE estimation of stationary sinusoid near a given initial frequency. + + In: x[N]: windowed spectrum + f: initial frequency, in bins + B: spectral truncation half width, in bins. + M, c[], iH2: cosine-family window specification parameters + epf: frequency error tolerance, in bins + Out: f, a and pp: frequency, amplitude and phase estimates + + No return value. +*/ +void LSESinusoid(double& f, cdouble* x, int N, double B, int M, double* c, double iH2, double& a, double& pp, double epf) +{ + struct l_hx {int N; int k1; int k2; int M; double* c; double iH2; cdouble* x; double dhxpeak; double hxpeak;} p={N, 0, 0, M, c, iH2, x, 0, 0}; + int dfshift=int(&((l_hx*)0)->dhxpeak); + + double inp=f; + p.k1=ceil(inp-B); if (p.k1<0) p.k1=0; + p.k2=floor(inp+B); if (p.k2>=p.N/2) p.k2=p.N/2-1; + double tmp=Newton(f, ddIPWindow, &p, dfshift, epf); + if (tmp==-1) + { + Search1Dmax(f, &p, IPWindow, inp-1, inp+1, &a, epf); + } + else + a=p.hxpeak; + pp=IPWindow(f, x, N, M, c, iH2, p.k1, p.k2, false); +}//LSESinusoid + +/* + function LSESinusoid: LSE estimation of stationary sinusoid predominant within [f1, f2]. + + In: x[N]: windowed spectrum + [f1, f2]: frequency range + B: spectral truncation half width, in bins. + M, c[], iH2: cosine-family window specification parameters + epf: frequency error tolerance, in bins + Out: a and pp: amplitude and phase estimates + + Returns the frequency estimate, in bins. +*/ +double LSESinusoid(int f1, int f2, cdouble* x, int N, double B, int M, double* c, double iH2, double& a, double& pp, double epf) +{ + struct l_hx {int N; int k1; int k2; int M; double* c; double iH2; cdouble* x; double dhxpeak; double hxpeak;} p={N, 0, 0, M, c, iH2, x, 0, 0}; + int dfshift=int(&((l_hx*)0)->dhxpeak); + + int inp; + double minp=0; + for (int i=f1; i=p.N/2) p.k2=p.N/2-1; + tmp=IPWindow(lf, &p); + if (minp=p.N/2) p.k2=p.N/2-1; + double tmp=Newton(f, ddIPWindow, &p, dfshift, epf); + if (tmp==-1) + { + Search1Dmax(f, &p, IPWindow, inp-1, inp+1, &a, epf); + } + else + a=p.hxpeak; + pp=IPWindow(f, x, N, M, c, iH2, p.k1, p.k2, false); + return f; +}//LSESinusoid + +/* + function LSESinusoid: LSE estimation of stationary sinusoid near a given initial frequency within [f1, + f2]. + + In: x[N]: windowed spectrum + f: initial frequency, in bins + [f1, f2]: frequency range + B: spectral truncation half width, in bins. + M, c[], iH2: cosine-family window specification parameters + epf: frequency error tolerance, in bins + Out: f, a and pp: frequency, amplitude and phase estimates + + Returns 1 if managed to find a sinusoid, 0 if not, upon which $a and $pp are estimated at the initial + f. +*/ +int LSESinusoid(double& f, double f1, double f2, cdouble* x, int N, double B, int M, double* c, double iH2, double& a, double& pp, double epf) +{ + struct l_hx {int N; int k1; int k2; int M; double* c; double iH2; cdouble* x; double dhxpeak; double hxpeak;} p={N, 0, 0, M, c, iH2, x, 0, 0};//(l_hx *)¶ms; + int dfshift=int(&((l_hx*)0)->dhxpeak); + + int result=0; + double inp=f; + p.k1=ceil(inp-B); if (p.k1<0) p.k1=0; + p.k2=floor(inp+B); if (p.k2>=p.N/2) p.k2=p.N/2-1; + double tmp=Newton(f, ddIPWindow, &p, dfshift, epf, 100, 1e-256, f1, f2); + if (tmp!=-1 && f>f1 && f=f2) + { + f=inp; + cdouble r=IPWindowC(f, x, N, M, c, iH2, p.k1, p.k2); + a=abs(r); + pp=arg(r); + } + else + { + result=1; + pp=IPWindow(f, x, N, M, c, iH2, p.k1, p.k2, false); + } + } + return result; +}//LSESinusoid + +/* + function LSESinusoidMP: LSE estimation of a stationary sinusoid from multi-frames spectrogram without + considering phase-frequency consistency across frames. + + In: x[Fr][N]: spectrogram + f: initial frequency, in bins + [f1, f2]: frequency range + B: spectral truncation half width, in bins. + M, c[], iH2: cosine-family window specification parameters + epf: frequency error tolerance, in bins + Out: f, a[Fr] and ph[Fr]: frequency, amplitudes and phase angles estimates + + Returns an error bound of the frequency estimate. +*/ +double LSESinusoidMP(double& f, double f1, double f2, cdouble** x, int Fr, int N, double B, int M, double* c, double iH2, double* a, double* ph, double epf) +{ + struct l_ip1 {int N; int k1; int k2; int M; double* c; double iH2; int L; cdouble** x; double dsip; double sip; cdouble* lmd;} p={N, 0, 0, M, c,iH2, Fr, x, 0, 0, 0}; + int dfshift=int(&((l_ip1*)0)->dsip), fshift=int(&((l_ip1*)0)->sip); + + double inp=f; + p.k1=ceil(inp-B); if (p.k1<0) p.k1=0; + p.k2=floor(inp+B); if (p.k2>=p.N/2) p.k2=p.N/2-1; + double errf=Newton1dmax(f, f1, f2, ddsIPWindow, &p, dfshift, fshift, dsIPWindow, dfshift, epf); + if (errf<0) errf=Search1Dmax(f, &p, sIPWindow, f1, f2, a, epf); + if (a || ph) + { + for (int fr=0; frsdip), fshift=int(&((l_ip*)0)->sip); + + double inp=f; + p.k1=ceil(inp-B); if (p.k1<0) p.k1=0; + p.k2=floor(inp+B); if (p.k2>=p.N/2) p.k2=p.N/2-1; + double errf=Newton1dmax(f, f1, f2, ddsIPWindowC, &p, dfshift, fshift, dsIPWindowC, dfshift, epf); + if (errf<0) errf=Search1Dmax(f, &p, sIPWindowC, f1, f2, a, epf); + if (a || ph) + { + cdouble* lmd=new cdouble[Fr]; + sIPWindowC(f, Fr, Offst*1.0/N, x, N, M, c, iH2, p.k1, p.k2, lmd); + for (int fr=0; fr=Wid/2) K2=Wid/2-1; int K=K2-K1+1; + MList* List=new MList; + cdouble** Allocate2L(cdouble, I, K, wt, List); + for (int i=0; i=Wid/2) K2=Wid/2-1; int K=K2-K1+1; + MList* List=new MList; + cdouble** Allocate2L(cdouble, I, K, wt, List); + for (int i=0; i=Wid/2) K2=Wid/2-1; int K=K2-K1+1; + MList* List=new MList; + cdouble** Allocate2L(cdouble, I, K, wt, List); + for (int i=0; i0 && f[i]-f[i-1]>Bw) || i==I-1) + { + if (i==I-1) i++; + //process frequencies from ist to i-1 + if (i-1==ist) //one sinusoid + { + double fb=f[ist]; int K1=floor(fb-B+0.5), K2=floor(fb+B+0.5); + lmd[ist]=IPWindowC(fb, x, Wid, M, c, iH2, K1, K2); + } + else + { + MList* List=new MList; + int N=i-ist, K1=floor(f[ist]-B+0.5), K2=floor(f[i-1]+B+0.5), K=K2-K1+1; + cdouble** Allocate2L(cdouble, N, K, wt, List); + for (int n=0; n0 && f[i]-f[i-1]>Bw) || i==I-1) + { + if (i==I-1) i++; + + //process frequencies from ist to i-1 + if (i-1==ist) //one sinusoid + { + double fb=f[ist]; + cdouble* w=Window(0, fb, Wid, M, c, 0, hWid-1); + for (int k=0; k0 && f[i]-f[i-1]>Bw) || i==I-1) + { + if (i==I-1) i++; + + //process frequencies from ist to i-1 + if (i-1==ist) //one sinusoid + { + double fb=f[ist]; + cdouble* w=Window(0, fb, Wid, M, c, 0, hWid-1); + for (int k=0; kx=wr, w->y=wi; + double result=wr*wr+wi*wi; + return result; +}//WindowDuo + +/* + function ddWindowDuo: calcualtes the square norm of the inner product between windowed spectra of two + sinusoids at frequencies f1 and f2, df=f1-f2, with its 1st and 2nd derivatives + + In: df: frequency difference, in bins + N: DFT size + M, d[]: cosine-family window specification parameters (see "further reading" for d[]). + Out: w[0], the inner product, optional + window, dwindow: square norm and its derivative, of the inner product + + Returns 2nd derivative of the square norm of the inner product. +*/ +double ddWindowDuo(double df, int N, double* d, int M, double& dwindow, double& window, cdouble* w) +{ + double wr=0, wi=0, dwr=0, dwi=0, ddwr=0, ddwi=0, PI_N=M_PI/N, PIPI_N=PI_N*M_PI, PIPI=M_PI*M_PI; + for (int m=-2*M; m<=2*M; m++) + { + double ang=df+m, Omg=ang*M_PI, omg=Omg/N; + double si=sin(omg), co=cos(omg), sinn=sin(Omg), cosn=cos(Omg); + double sa=(ang==0)?N:(sinn/si), dsa=dsincd_unn(ang, N), ddsa=ddsincd_unn(ang, N); + double dm; if (m<0) dm=d[-m]; else dm=d[m]; + wr+=dm*sa*co, wi+=-dm*sinn; + dwr+=dm*(dsa*co-PI_N*sinn), dwi+=-dm*M_PI*cosn; + ddwr+=dm*(ddsa*co-PI_N*dsa*si-PIPI_N*cosn), ddwi+=dm*PIPI*sinn; + } + wr*=N, wi*=N, dwr*=N, dwi*=N, ddwr*=N, ddwi*=N; + window=wr*wr+wi*wi; + dwindow=2*(wr*dwr+wi*dwi); + if (w) w->x=wr, w->y=wi; + double ddwindow=2*(wr*ddwr+dwr*dwr+wi*ddwi+dwi*dwi); + return ddwindow; +}//ddWindowDuo + +/* + function sIPWindowDuo: calculates the square norm of the orthogonal projection of a windowed spectrum + onto the linear span of the windowed spectra of two sinusoids at reference frequencies f1 and f2. + + In: x[N]: spectrum + f1, f2: reference frequencies. + M, c[], d[], iH2: cosine-family window specification parameters. + K1, K2: spectrum truncation range, i.e. bins outside [K1, K2] are ignored. + Out: lmd1, lmd2: projection coefficients, interpreted as actual amplitude-phase factors + + Returns the square norm of the orthogonal projection. +*/ +double sIPWindowDuo(double f1, double f2, cdouble* x, int N, double* c, double* d, int M, double iH2, int K1, int K2, cdouble& lmd1, cdouble& lmd2) +{ + int K=K2-K1+1; + cdouble xw1=0, *lx=&x[K1], *w1=new cdouble[K*2], *r1=&w1[K]; + Window(w1, f1, N, M, c, K1, K2); + double w1w1=0; + for (int k=0; kf1, f2, p->x, p->N, p->c, p->d, p->M, p->iH2, p->k1, p->k2, r1, r2); +}//sIPWindowDuo + +/* + function ddsIPWindowDuo: calculates the square norm, and its 1st and 2nd derivatives against f2,, of + the orthogonal projection of a windowed spectrum onto the linear span of the windowed spectra of two + sinusoids at reference frequencies f1 and f2. + + In: x[N]: spectrum + f1, f2: reference frequencies. + M, c[], d[], iH2: cosine-family window specification parameters. + K1, K2: spectrum truncation range, i.e. bins outside [K1, K2] are ignored. + + Out: lmd1, lmd2: projection coefficients, interpreted as actual amplitude-phase factors + ddsip[3]: the 2nd, 1st and 0th derivatives (against f2) of the square norm. + + No return value. +*/ +void ddsIPWindowDuo(double* ddsip2, double f1, double f2, cdouble* x, int N, double* c, double* d, int M, double iH2, int K1, int K2, cdouble& lmd1, cdouble& lmd2) +{ + int K=K2-K1+1; + cdouble xw1=0, *lx=&x[K1], *w1=new cdouble[K*2], *r1=&w1[K]; + Window(w1, f1, N, M, c, K1, K2); + double w1w1=0; + for (int k=0; kf1, f2, p->x, p->N, p->c, p->d, p->M, p->iH2, p->k1, p->k2, r1, r2); + p->dipwindow=ddsip2[1], p->ipwindow=ddsip2[2]; + return ddsip2[0]; +}//ddsIPWindowDuo + +/* + function LSEDuo: least-square estimation of two sinusoids of which one has a fixed frequency + + In: x[N]: the windowed spectrum + f1: the fixed frequency + f2: initial value of the flexible frequency + fmin, fmax: search range for f2, the flexible frequency + B: spectral truncation half width + M, c[], d[], iH2: + epf: frequency error tolerance + Out: f2: frequency estimate + lmd1, lmd2: amplitude-phase factor estimates + Returns 1 if managed to find a good f2, 0 if not, upon which the initial f2 is used for estimating + + amplitudes and phase angles. +*/ +int LSEDuo(double& f2, double fmin, double fmax, double f1, cdouble* x, int N, double B, double* c, double* d, int M, double iH2, cdouble& r1, cdouble &r2, double epf) +{ + int result=0; + double inp=f2; + int k1=ceil(inp-B); if (k1<0) k1=0; + int k2=floor(inp+B); if (k2>=N/2) k2=N/2-1; + struct l_hx {int N; int k1; int k2; double* c; double* d; int M; double iH2; cdouble* x; double f1; double dipwindow; double ipwindow;} p={N, k1, k2, c, d, M, iH2, x, f1, 0, 0}; + int dfshift=int(&((l_hx*)0)->dipwindow);// fshift=int(&((l_hx*)0)->ipwindow); + + double tmp=Newton(f2, ddsIPWindowDuo, &p, dfshift, epf, 100, 1e-256, fmin, fmax); + if (tmp!=-1 && f2>fmin && f2=fmax) f2=inp; + else result=1; + } + sIPWindowDuo(f1, f2, x, N, c, d, M, iH2, k1, k2, r1, r2); + return result; +}//LSEDuo + +//--------------------------------------------------------------------------- +/* + Time-frequency reassignment sinusoid estimation routines. + + Further reading: A. R?bel, ¡°Estimating partial frequency and frequency slope using reassignment + operators,¡± in Proc. ICMC¡¯02. G?teborg. 2002. +*/ + +/* + function CDFTW: single-frequency windowed DTFT, centre-aligned + + In: data[Wid]: waveform data x + win[Wid+1]: window function + k: frequency, in bins, where bin=1/Wid + Out: X: DTFT of xw at frequency k bins + + No return value. +*/ +void CDFTW(cdouble& X, double k, int Wid, cdouble* data, double* win) +{ + X=0; + int hWid=Wid/2; + for (int i=0; i6) logaslope=6.0/Wid; + else if (logaslope*Wid<-6) logaslope=-6.0/Wid; + + f=f+fslope*(t-localt); //obtain frequency estimate at t + + cdouble x=0; + if (win==0) + { + for (int n=0; n0.5){} + } + + cdouble *y=new cdouble[Count]; + double *lf=new double[Count*4], *la=&lf[Count], *lp=&lf[Count*2], *lda=&lf[Count*3]; + + __int16* ref=new __int16[Count]; + for (int i=0; i1) + { + afr[fr]=a, pfr[fr]=fai, ffr[fr]=f; + } + else + { + double rr=a*cos(fai)+b*cos(thet); + double ii=a*sin(fai)+b*sin(thet); + ffr[fr]=(a*f*(a+b*cos(delt))+b*g*(b+a*cos(delt))+(da*b-a*db)*sin(delt)/(2*M_PI))/(a*a+b*b+2*a*b*cos(delt)); + afr[fr]=sqrt(rr*rr+ii*ii); + pfr[fr]=atan2(ii, rr); + } + if (LogA) afr[fr]=log(afr[fr]); + } + CubicSpline(Fr-1, fa, fb, fc, fd, xs, ffr, 1, 1); + CubicSpline(Fr-1, aa, ab, ac, ad, xs, afr, 1, 1); + for (int fr=0; fr0.5){} + } + delete[] y; delete[] lf; delete[] ref; +}//AdditiveUpdate + +/* + function AdditiveAnalyzer: sinusoid analyzer with one additive update + + In: x[Count]: waveform data + Wid, Offst: frame size and hop size + BasicAnalyzer: pointer to a sinusoid analyzer + ref[Count]: reference frequencies, in bins, used by BasicAnalyzer + BasicAnalyzer: pointer to a sinusoid analyzer + LogA: indicates if amplitudes are interpolated at cubic spline or exponential cubic spline + Out: fs[Count], as[Count], phs[Count]: sinusoid parameter estimates + das[Count]: estimate of amplitude derivative + + No return value. +*/ +void AdditiveAnalyzer(double* fs, double* as, double* phs, double* das, cdouble* x, int Count, int Wid, int Offst, __int16* ref, TBasicAnalyzer BasicAnalyzer, int reserved, bool LogA) +{ + BasicAnalyzer(fs, as, phs, das, x, Count, Wid, Offst, ref, reserved, LogA); + AdditiveUpdate(fs, as, phs, das, x, Count, Wid, Offst, BasicAnalyzer, reserved, LogA); +}//AdditiveAnalyzer + +/* + function MultiplicativeUpdate: multiplicative reestimation of time-varying sinusoid + + In: x[Count]: waveform data + Wid, Offst: frame size and hop + fs[Count], as[Count], phs[Count]: initial estimate of sinusoid parameters + das[Count]: initial estimate of amplitude derivative + BasicAnalyzer: pointer to a sinusoid analyzer + LogA: indicates if amplitudes are interpolated at cubic spline or exponential cubic spline + Out: fs[Count], as[Count], phs[Count], das[Count]: estimates after additive update + + No return value. +*/ +void MultiplicativeUpdate(double* fs, double* as, double* phs, double* das, cdouble* x, int Count, int Wid, int Offst, TBasicAnalyzer BasicAnalyzer, int reserved, bool LogA) +{ + int HWid=Wid/2; + cdouble *y=new cdouble[Count]; + double *lf=new double[Count*8], *la=&lf[Count], *lp=&lf[Count*2], *lda=&lf[Count*3], + *lf2=&lf[Count*4], *la2=&lf2[Count], *lp2=&lf2[Count*2], *lda2=&lf2[Count*3]; + __int16 *lref=new __int16[Count]; + + for (int i=0; i0.5){} + } + + delete[] y; delete[] lf; delete[] lref; +}//MultiplicativeUpdate + +/* + function MultiplicativeAnalyzer: sinusoid analyzer with one multiplicative update + + In: x[Count]: waveform data + Wid, Offst: frame size and hop size + BasicAnalyzer: pointer to a sinusoid analyzer + ref[Count]: reference frequencies, in bins, used by BasicAnalyzer + BasicAnalyzer: pointer to a sinusoid analyzer + LogA: indicates if amplitudes are interpolated at cubic spline or exponential cubic spline + Out: fs[Count], as[Count], phs[Count]: sinusoid parameter estimates + das[Count]: estimate of amplitude derivative + + No return value. +*/ +void MultiplicativeAnalyzer(double* fs, double* as, double* phs, double* das, cdouble* x, int Count, int Wid, int Offst, __int16* ref, TBasicAnalyzer BasicAnalyzer, int reserved, bool LogA) +{ + BasicAnalyzer(fs, as, phs, das, x, Count, Wid, Offst, ref, reserved, LogA); + MultiplicativeUpdate(fs, as, phs, das, x, Count, Wid, Offst, BasicAnalyzer, reserved); +}//MultiplicativeAnalyzer + +/* + This is an earlier version of the multiplicative method without using a user-provided BasicAnalyzer. + This updates the sinusoid estimates at the selected consecutive FRAMES of x. Only frequency modulation + is included in the multiplier. The first frame (0) is centred at x[Wid/2]. fs, as, and phs are based + on frames rather than samples. Updates include frame frst, but not frame fren. +*/ +void MultiplicativeUpdateF(double* fs, double* as, double* phs, __int16* x, int Fr, int frst, int fren, int Wid, int Offst) +{ + int HWid=Wid/2; + + double *fa=new double[Fr*12], *fb=&fa[Fr], *fc=&fa[Fr*2], *fd=&fa[Fr*3], + *xs=&fa[Fr*8]; + for (int fr=0; frAmp[mpf]) mpf=k; + if (mpf>pf-4 && mpfabuf[fr]) fbuf[fr]=lf/lWid, abuf[fr]=la*2, pbuf[fr]=lp; + } + else + { + CFFTCW(xs, win, NULL, NULL, log2(Wid), w, xc); + double lf=fbuf[fr]*Wid, la, lp; + LSESinusoid(lf, lf-3, lf+3, xc, Wid, 3, M, c, iH2, la, lp, 1e-3); + if (la*2>abuf[fr]) + fbuf[fr]=lf/Wid, abuf[fr]=la*2, pbuf[fr]=lp; + } + } +}//ReEstFreq + +/* + function ReEstFreq_2: sinusoid reestimation by demodulating frequency. This is that same as ReEstFreq(...) + except that it calls Sinusoid(...) to synthesize the phase track used for demodulation and that it + does not allow variable window sizes for estimating demodulated sinusoid. + + In: x[Wid+Offst*(FrCount-1)]: waveform data + FrCount, Wid, Offst: frame count, frame size and hop size + fbuf[FrCount], ns[FrCount]: initial frequency estiamtes and their timing + win[Wid]: window function for LSE sinusoid estimation + M, c[], iH2: cosine-family window specification parameters, must be consistent with M, c, iH2 + w[Wid/2], xs[Wid], xc[Wid], f3[FrCount-1], f2[FrCount-1], f1[FrCount-1], f0[FrCount-1]: buffers + Out: fbuf[FrCount], abuf[FrCount], pbuf[FrCount]: reestimated frequencies, amplitudes and phase angles + + No return value. +*/ +void ReEstFreq_2(int FrCount, int Wid, int Offst, double* x, double* fbuf, double* abuf, double* pbuf, double* win, int M, double* c, double iH2, cdouble* w, cdouble* xc, cdouble* xs, double* f3, double* f2, double* f1, double* f0, double* ns) +{ + int hWid=Wid/2; + //reestimate using frequency track + CubicSpline(FrCount-1, f3, f2, f1, f0, ns, fbuf, 1, 1); + double *refcos=(double*)malloc8(sizeof(double)*Wid), *refsin=&refcos[hWid], ph=0, centralph; + + memset(f0, 0, sizeof(double)*FrCount); + + int N=Wid+Offst*(FrCount-1); + double* cosine=new double[N], *sine=new double[N]; + Sinusoid(&cosine[hWid], &sine[hWid], -hWid, 0, f3[0], f2[0], f1[0], f0[0], ph); + for (int fr=0; frabuf[fr]) + fbuf[fr]=lf/Wid, abuf[fr]=la*2, pbuf[fr]=lp+centralph; + } +}//ReEstFreq_2 + +/* + function ReEstFreqAmp: sinusoid reestimation by demodulating frequency and amplitude. + + In: x[Wid+Offst*(FrCount-1)]: waveform data + FrCount, Wid, Offst: frame count, frame size and hop size + fbuf[FrCount], abuf[FrCount], ns[FrCount]: initial frequency and amplitude estiamtes and their + timing + win[Wid]: window function for estimating demodulated sinusoid + M, c[], iH2: cosine-family window specification parameters, must be consistent with win[] + Wids[FrCount]: specifies frame sizes for estimating individual frames of demodulated sinusoid, + optional + w[Wid/2], ps[Wid], xs[Wid], xc[Wid]: buffers + fa[FrCount-1], fb[FrCount-1], fc[FrCount-1], fd[FrCount-1]: buffers + aa[FrCount-1], ab[FrCount-1], ac[FrCount-1], ad[FrCount-1]: buffers + Out: fbuf[FrCount], abuf[FrCount], pbuf[FrCount]: reestimated frequencies, amplitudes and phase angles + + No return value. +*/ +void ReEstFreqAmp(int FrCount, int Wid, int Offst, double* x, double* fbuf, double* abuf, double* pbuf, double* win, int M, double* c, double iH2, cdouble* w, cdouble* xc, cdouble* xs, double* ps, double* as, double* fa, double* fb, double* fc, double* fd, double* aa, double* ab, double* ac, double* ad, double* ns, int* Wids) +{ + int hWid=Wid/2; + //reestimate using amplitude and frequency track + CubicSpline(FrCount-1, fa, fb, fc, fd, ns, fbuf, 0, 1); + CubicSpline(FrCount-1, aa, ab, ac, ad, ns, abuf, 0, 1); + for (int fr=0; fr=hWid)) tmp=1; + else if (as[hWid]>100*as[j]) tmp=100; + else tmp=as[hWid]/as[j]; + tmp=tmp*ldata[j]; + xs[j].x=tmp*cos(-ps[j]); + xs[j].y=tmp*sin(-ps[j]); + } + + if (Wids) + { + int lWid=Wids[fr], lhWid=Wids[fr]/2, lM; + SetTwiddleFactors(lWid, w); + double *lwin=NewWindow(wtHann, lWid), lc[4], liH2; + windowspec(wtHann, lWid, &lM, lc, &liH2); + CFFTCW(&xs[hWid-lhWid], lwin, NULL, NULL, log2(lWid), w, xc); + delete[] lwin; + double lf=fbuf[fr]*lWid, la, lp; + LSESinusoid(lf, lf-3, lf+3, xc, lWid, 3, lM, lc, liH2, la, lp, 1e-3); + if (la*2>abuf[fr]) fbuf[fr]=lf/lWid, abuf[fr]=la*2, pbuf[fr]=lp; + } + else + { + CFFTCW(xs, win, NULL, NULL, log2(Wid), w, xc); + double lf=fbuf[fr]*Wid, la, lp; + LSESinusoid(lf, lf-3, lf+3, xc, Wid, 3, M, c, iH2, la, lp, 1e-3); + if (la*2>abuf[fr]) fbuf[fr]=lf/Wid, abuf[fr]=la*2, pbuf[fr]=lp; + } + } +}//ReEstFreqAmp + +/* + function Reestimate2: iterative demodulation method for sinusoid parameter reestimation. + + In: x[(FrCount-1)*Offst+Wid]: waveform data + FrCount, Wid, Offst: frame count, frame size and hop size + win[Wid]: window function + M, c[], iH2: cosine-family window specification parameters, must be consistent with win[] + Wids[FrCount]: specifies frame sizes for estimating individual frames of demodulated sinusoid, + optional + maxiter: maximal number of iterates + ae[FrCount], fe[FrCount], pe[FrCount]: initial amplitude, frequency and phase estimates + Out: aret[FrCount], fret[FrCount], pret[FrCount]: reestimated amplitudes, frequencies and phase angles + + Returns the number of unused iterates left of the total of maxiter. +*/ +int Reestimate2(int FrCount, int Wid, int Offst, double* win, int M, double* c, double iH2, double* x, double* ae, double* fe, double* pe, double* aret, double* fret, double *pret, int maxiter, int* Wids) +{ + AllocateFFTBuffer(Wid, fft, w, xc); + double convep=1e-4, dif=0, lastdif=0; //convep is the hard-coded threshold that stops the iteration + int iter=1, hWid=Wid/2; + + double *ns=new double[FrCount*12], *as=new double[Wid*5]; + double *fbuf=&ns[FrCount], *abuf=&ns[FrCount*2], + *aa=&ns[FrCount*3], *ab=&ns[FrCount*4], *ac=&ns[FrCount*5], *ad=&ns[FrCount*6], + *fa=&ns[FrCount*7], *fb=&ns[FrCount*8], *fc=&ns[FrCount*9], *fd=&ns[FrCount*10], + *pbuf=&ns[FrCount*11]; + double *ps=&as[Wid]; + cdouble *xs=(cdouble*)&as[Wid*3]; + + memcpy(fbuf, fe, sizeof(double)*FrCount); + memcpy(abuf, ae, sizeof(double)*FrCount); + memcpy(pbuf, pe, sizeof(double)*FrCount); + for (int i=0; i1) lastdif=dif; + dif=0; + if (iter==1) + { + for (int fr=0; frfabs(ae[fr])) + dif+=fabs(fe[fr]-fbuf[fr])*Wid+fabs((ae[fr]-abuf[fr])/abuf[fr]); + else + dif+=fabs(fe[fr]-fbuf[fr])*Wid+fabs((ae[fr]-abuf[fr])/ae[fr]); + } + } + else + { + for (int fr=0; frfabs(aret[fr])) + dif+=fabs(fret[fr]-fbuf[fr])*Wid+fabs((aret[fr]-abuf[fr])/abuf[fr]); + else + dif+=fabs(fret[fr]-fbuf[fr])*Wid+fabs((aret[fr]-abuf[fr])/aret[fr]); + } + } + memcpy(fret, fbuf, sizeof(double)*FrCount); + memcpy(aret, abuf, sizeof(double)*FrCount); + dif/=FrCount; + if (fabs(dif)1 && fabs(dif-lastdif)hWid) m1=hWid; + for (int n=m0; n<=m1; n++) if (fft[n]>fft[m]) m=n; + cdouble Sw=0, S1w=0, S2w=0; + for (int n=0; n=0; kk--) thisX[kk]=-lastX[kk+1]+cdouble(0, omg)*lastX[kk]; + } +}//Xkw + +/* + function Xkw: computes windowed spectrum of x and its derivatives up to order K at angular frequency + omg, from x and its derivatives using window w. + + In: x[K+1][Wid]: waveform data and its derivatives up to order K. + w[Wid]: window function + omg: angular frequency + Out: X[K+1]: windowed spectrum and its derivatives up to order K + + No return value. This function is for testing only. +*/ +void Xkw(cdouble* X, int K, int Wid, double** x, double* w, double omg) +{ + int hWid=Wid/2; + memset(X, 0, sizeof(cdouble)*(K+1)); + for (int i=0; i=0) + { + uind[up]=p; + ind[p]=up; + up++; + } + p++; + } + if (up!=Kr) throw(""); + } + + cdouble* Skw=new cdouble[M]; + Xkw(Skw, Kc, Wid, s, win, omg); + + double* x=new double[Wid]; + cdouble** Allocate2(cdouble, M, Kc, Smkw); + for (int m=1; m=0) //the real part being unknown + { + A[2*k][lind]=Smkw[m+1][k].x; + A[2*k+1][lind]=Smkw[m+1][k].y; + } + if ((lind=ind[2*m+1])>=0) //the imag part being unknown + { + A[2*k+1][lind]=Smkw[m+1][k].x; + A[2*k][lind]=-Smkw[m+1][k].y; + } + } + } + + bool dropeq=(2*Kc-1==Kr); + if (dropeq) + { + Allocate2(double, Kr+2, Kr, AA); + bb=AA[Kr], pqpq=AA[Kr+1]; + memcpy(AA[0], A[0], sizeof(double)*Kr*(Kr-1)); + memcpy(AA[Kr-1], A[Kr], sizeof(double)*Kr); + memcpy(bb, b, sizeof(double)*(Kr-1)); + bb[Kr-1]=((double*)(&Skw[1]))[Kr]; + } + + double det; + GECP(Kr, pq, A, b, &det); + if (dropeq) + { + double det2; + GECP(Kr, pqpq, AA, bb, &det2); + if (fabs(det2)>fabs(det)) memcpy(pq, pqpq, sizeof(double)*Kr); + DeAlloc2(AA); + } + memset(&r[1], 0, sizeof(double)*M1*2); + for (int k=0; k=0){uind[up]=p; ind[p]=up; up++;} p++;} if (up!=Kr) throw("");} + + //allocate buffer for linear system A(pq)=b + cdouble* Skw=new cdouble[Kc+1]; + double* x=new double[Wid]; + cdouble** Allocate2(cdouble, M, Kc, Smkw); + + Alloc2(2*Kc+2, 2*Kc, A); + double *b=A[2*Kc], *pq=A[2*Kc+1]; + + Xkw(Skw, Kc, Wid, s, win, omg); + for (int m=1; m=0) + { + A[2*k][lind]=Smkw[m+1][k].x; + A[2*k+1][lind]=Smkw[m+1][k].y; + } + if ((lind=ind[2*m+1])>=0) + { + A[2*k+1][lind]=Smkw[m+1][k].x; + A[2*k][lind]=-Smkw[m+1][k].y; + } + } + } + + if (2*Kc==Kr) GECP(Kr, pq, A, b); + else LSLinear2(2*Kc, Kr, pq, A, b); + + memset(&r[1], 0, sizeof(double)*M1*2); + for (int k=0; k=0){uind[up]=p; ind[p]=up; up++;} p++;}} + + //allocate buffer for linear system A(pq)=b + cdouble* Skw=new cdouble[Kc+1], Skw00; + double* x=new double[Wid]; + cdouble** Allocate2(cdouble, M, Kc, Smkw); + + Alloc2(2*Fr*Kc, 2*Fr*Kc, A); + double *pq=new double[2*Fr*Kc], *b=new double[2*Fr*Kc]; + + for (int fr=0; fr=0) + { + A[2*fr*Kc+2*k][lind]=Smkw[m+1][k].x; + A[2*fr*Kc+2*k+1][lind]=Smkw[m+1][k].y; + } + if ((lind=ind[2*m+1])>=0) + { + A[2*fr*Kc+2*k+1][lind]=Smkw[m+1][k].x; + A[2*fr*Kc+2*k][lind]=-Smkw[m+1][k].y; + } + } + } + } + if (2*Fr*Kc==Kr) GECP(Kr, pq, A, b); + else LSLinear2(2*Fr*Kc, Kr, pq, A, b); + + memset(&r[1], 0, sizeof(double)*M1*2); + for (int k=0; kM_PI) + { + if (ph_1-ph0>0) ph_1-=M_PI*2; + else ph_1+=M_PI*2; + } + if (fabs(ph1-ph0)>M_PI) + { + if (ph1-ph0>0) ph1-=M_PI*2; + else ph1+=M_PI*2; + } + double b2=(ph1+ph_1)*0.5-ph0, b1=(ph1-ph_1)*0.5, b0=ph0; + ph=b0+b1*(df+b2*df); + //now we have the QI estimates + double uff=2*a2, vf=b1+2*b2*df, vff=2*b2; + double dfdp=Wid/(2*M_PI*res); + double upp=uff*dfdp*dfdp, vp=vf*dfdp, vpp=vff*dfdp*dfdp; + double p=-upp*0.5/(upp*upp+vpp*vpp); + double alf=-2*p*vp, beta=p*vpp/upp; + //*direct method + double beta_p=beta/p; + double feses=f-alf*beta/p /(2*M_PI)*Wid, + yeses=y-alf*alf*0.25/p+0.25*log(1+beta_p*beta_p), + pheses=ph+alf*alf*beta*0.25/p-0.5*atan(beta_p); //*/ + /*adapted method + double zt[]={0, 0.995354, 0.169257, 1.393056, 0.442406, -0.717980, -0.251620, 0.177511, 0.158120, -0.503299}; + double delt=res/Wid; double delt0=df*delt; + beta=zt[3]*beta+zt[4]*delt0*alf; + alf=(zt[1]+zt[2]*delt*delt)*alf; + double beta_p=beta/p; + double feses=f+zt[5]*alf*beta/p /(2*M_PI)*Wid, + yeses=y+zt[6]*alf*alf/p+zt[7]*log(1+beta_p*beta_p), + pheses=ph+zt[8]*alf*alf*beta/p+zt[9]*atan(beta_p); //*/ + f=feses/Wid, a=exp(yeses), ph=pheses, fslope=2*beta/2/M_PI, aesp=alf; +}//TFAS05 + +/* + function TFAS05_enh: the Abe-Smith method 2005 enhanced by LSE amplitude and phase estimation + + In: data[Wid]: waveform data + w[Wid]: window function + res: resolution of frequency for QIFFT + Out: f, a, ph: frequency, amplitude and phase angle estimates + aesp, fslope: estimates of log amplitude and frequency derivatives + + No return value. +*/ +void TFAS05_enh(double& f, double& t, double& a, double& ph, double& aesp, double& fslope, int Wid, double* data, double* w, double res) +{ + TFAS05(f, t, a, ph, aesp, fslope, Wid, data, w, res); + double xr=0, xi=0, p, win2=0; + for (int n=0; n<=Wid; n++) + { + double ni=n-Wid/2, tmp=data[n]*w[n]*w[n];//*exp(-aesp*(n-Wid/2)); if (IsInfinite(tmp)) continue; + p=-2*M_PI*(f+0.5*fslope*ni)*ni; + xr+=tmp*cos(p); + xi+=tmp*sin(p); + win2+=w[n]*w[n]; + } + a=sqrt(xr*xr+xi*xi)/win2; + ph=(xr==0 && xi==0)?0:atan2(xi, xr); +}//TFAS05_enh +//version without returning aesp and fslope +void TFAS05_enh(double& f, double& t, double& a, double& ph, int Wid, double* data, double* w, double res) +{ + double aesp, fslope; + TFAS05_enh(f, t, a, ph, aesp, fslope, Wid, data, w, res); +}//TFAS05_enh + +//--------------------------------------------------------------------------- +/* + function DerivativeLSv_AmpPh: estimate the constant-term in the local derivative method. This is used + by the local derivative algorithm, whose implementation is found in the header file as templates. + + In: sv0: inner product , where s is the sinusoid being estimated. + integr_h[M][Wid]: M vectors containing samples of the integral of basis functions h[M]. + v0[M]: a test function + lmd[M]: coefficients to h[M] + + Returns coefficient of integr_h[0]=1. +*/ +cdouble DerivativeLSv_AmpPh(int Wid, int M, double** integr_h, cdouble* lmd, cdouble* v0, cdouble sv0) +{ + cdouble e0=0; + for (int n=0; n=fbin || I%2==1){v[I-1][c]=u[I-1][c]*rot; dv[I-1][c]=du[I-1][c]*rot+jomg*v[I-1][c];} + else{v[I-1][c]=u[I][c]*rot; dv[I-1][c]=du[I][c]*rot+jomg*v[I-1][c];} + } +}//setv + +/* + function setvhalf: computes I half-size test functions v[I] by modulation u[I] to frequency f. + + In: u[I][hWid*2], du[I][Wid*2]: base-band test functions and their derivatives + f: carrier frequency + Out: v[I][hWid], dv[hWid]: half-size test functions and their derivatives + + No return value. +*/void setvhalf(int I, int hWid, cdouble** v, cdouble** dv, double f, cdouble** u, cdouble** du) +{ + double fbin=floor(f*hWid)/hWid; + double omg=fbin*2*M_PI; + cdouble jomg=cdouble(0, omg); + for (int c=0; c + cdouble*** Allocate3L(cdouble, L_1, I, N, srv, mlist); + cdouble** Allocate2L(cdouble, I, M, shv1, mlist); + cdouble** Allocate2L(cdouble, I, M, shv2, mlist); + +#ifdef ERROR_CHECK + cdouble dsv1[128], dsv2[128]; +#endif + for (int l=0; l over the lth frame + cdouble* ls=&s[l*T]; for (int i=0; i over the lth frame + cdouble *ls1=&s[l*T], *ls2=&s[l*T+T]; + for (int i=0; i=- + if (ds) + { + cdouble* lds=&ds[l*T]; + for (int i=0; i + //cdouble* ls=&s[l*T]; + //cdouble lsv2=Inner(2*T, ls, dv[i]); + dsv1[l*I+i]=lsv-sv[l][i]; //i.e. =-+dsv1[lI+i] + } + + //error check: srv[l]*pq= + for (int i=0; i over the lth frame + cdouble* ls=&s[0]; for (int i=0; i over the lth frame + for (int i=0; i=- + if (ds) + { + cdouble* lds=&ds[0]; + for (int i=0; i + //cdouble* ls=&s[l*T]; + //cdouble lsv2=Inner(2*T, ls, dv[i]); + dsv1[L_1*I+i]=lsv-sv[L_1][i]; //i.e. =-+dsv1[lI+i] + } + + //error check: srv[l]*pq= + for (int i=0; i over the lth frame + cdouble* ls=&s[(L-1)*T]; for (int i=0; i over the lth frame + for (int i=0; i=- + if (ds) + { + cdouble* lds=&ds[(L-1)*T]; + for (int i=0; i + //cdouble* ls=&s[l*T]; + //cdouble lsv2=Inner(2*T, ls, dv[i]); + dsv1[L_1*I+i]=lsv-sv[L_1][i]; //i.e. =-+dsv1[lI+i] + } + + //error check: srv[l]*pq= + for (int i=0; i + cdouble*** Allocate3L(cdouble, L_1, I, Np, srav, mlist); + cdouble*** srbv; + if (Np==Nq && B==A) srbv=srav; else {Allocate3L(cdouble, L_1, I, Nq, srbv, mlist);} //same model for amplitude and phase + cdouble** Allocate2L(cdouble, I, M, shv1, mlist); + cdouble** Allocate2L(cdouble, I, M, shv2, mlist); + + for (int l=0; l over the lth frame + cdouble* ls=&s[l*T]; for (int i=0; i over the lth frame + cdouble *ls1=&s[l*T], *ls2=&s[l*T+T]; + for (int i=0; i over the lth frame + cdouble* ls=&s[0]; for (int i=0; i over the lth frame + for (int i=0; i over the lth frame + cdouble* ls=&s[(L-1)*T]; for (int i=0; i over the lth frame + for (int i=0; iaita= + double** Allocate2L(double, L_1*I*2, Np+Nq, AM, mlist); + for (int l=0; lAdd(pq, 1); + double* b=new double[2*L_1*I]; for (int i=0; i equals -, dsv2[I] tests that *pq= + +*/ +void testdsv(cdouble* dsv1, cdouble* dsv2, int Np, double* p, int Nq, double* q, int TT, cdouble* dsl, int I, cdouble** vl, cdouble* svl, cdouble** sravl, cdouble** srbvl) +{ + for (int i=0; i + //cdouble* ls=&s[l*T]; + dsv1[i]=lsv-svl[i]; //i.e. =-+dsv1[lI+i] + //sv[l][i]=lsv; + } + //error check: srv[l]*pq= + for (int i=0; i +*/ +double testsv(int L, double* f, int T, cdouble* s, int I, cdouble** u, cdouble** du, int endmode) +{ + cdouble** Allocate2(cdouble, I, T*2, v); + cdouble** Allocate2(cdouble, I, T*2, dv); + double ene=0; + for (int l=0; l over the lth frame + cdouble* ls=&s[l*T]; + for (int i=0; i + cdouble*** Allocate3L(cdouble, L_1, I, Np, srav, mlist); + cdouble*** srbv; + if (Np==Nq && B==DA) srbv=srav; else {Allocate3L(cdouble, L_1, I, Nq, srbv, mlist);} //same model for amplitude and phase + cdouble** Allocate2L(cdouble, I, M, shv1, mlist); + cdouble** Allocate2L(cdouble, I, M, shv2, mlist); + +#ifdef ERROR_CHECK + cdouble dsv1in[128], dsv2in[128]; +#endif + + for (int l=0; l over the lth frame + cdouble* ls=&s[l*T]; for (int i=0; i over the lth frame + cdouble *ls1=&s[l*T], *ls2=&s[l*T+T]; + for (int i=0; i=- and srv[l]*pq= + if (ds) testdsv(&dsv1in[l*I], &dsv2in[l*I], Np, p, Nq, q, T*2, &ds[l*T], I, v, sv[l], srav[l], srbv[l]); +#endif + } + L_1=L-1; + if (endmode==1 || endmode==3) + { + //v from u given f[l] + setvhalf(I, T, v, dv, (f[0]+f[1])/2, u, du); + //compute - over the lth frame + cdouble* ls=&s[0]; for (int i=0; i over the lth frame + for (int i=0; i=- and srv[l]*pq= + if (ds) testdsv(&dsv1in[L_1*I], &dsv2in[L_1*I], Np, p, Nq, q, T, &ds[0], I, v, sv[L_1], srav[L_1], srbv[L_1]); +#endif + L_1++; + } + if (endmode==2 || endmode==3) + { + //v from u given f[l] + setvhalf(I, T, v, dv, (f[L-1]+f[L])/2, u, du); + //compute - over the lth frame + cdouble* ls=&s[(L-1)*T]; for (int i=0; i over the lth frame + for (int i=0; i=- and srv[l]*pq= + if (ds) testdsv(&dsv1in[L_1*I], &dsv2in[L_1*I], Np, p, Nq, q, T, &ds[(L-1)*T], I, v, sv[L_1], srav[L_1], srbv[L_1]); +#endif + L_1++; + } + + //real implementation of aita= + double** Allocate2L(double, L_1*I*2, Np+Nq, AM, mlist); + for (int l=0; lAdd(pq, 1); + double* b=new double[2*L_1*I]; for (int i=0; iAdd(b, 1); +#ifdef ERROR_CHECK + //tests that AM is invariant to a constant shift of p + double errAM=0, errAM2=0, err1, err2; + for (int l=0; lAdd(edsin, 1); + errdsin=testds_pqA(Np, p, Nq, q, L, T, s, ds, M, h, dh, DA, B, edsin); + errdsvin=testsv(L, f, T, edsin, I, u, du, endmode); + } +#endif + Alloc2L(L_1*I*2, Np+Nq-1, Am, mlist); + for (int l=0; lAdd(edsout, 1); + errdsout=testds_pqA(Np, pq, Nq, &pq[Np], L, T, s, ds, M, h, dh, DA, B, edsout); + errdsvout=testsv(L, f, T, edsout, I, u, du, endmode); + } +#endif + memcpy(p, pq, sizeof(double)*Np); memcpy(q, &pq[Np], sizeof(double)*Nq); + + delete mlist; +}//DerivativePiecewise3 + +//initialization routines for the piecewise derivative method + +/* + function seth: set h[M] to a series of power functions. + + In: M, T. + Out: h[M][T], where h[m] is power function of order m. + + No return value. h is allocated anew and must be freed by caller. +*/ +void seth(int M, int T, double**& h, MList* mlist) +{ + if (M<=0){h=0; return;} + Allocate2L(double, M, T, h, mlist); + double* hm=h[0]; for (int t=0; t1){dhm=dh[1]; for (int t=0; t0) li++; + } + } + DeAlloc2(wins); +}//setu + +/* + function DerivativePiecewiseI: wrapper for DerivativePiecewise(), doing the initialization ,etc. + + In: L, T: number and length of pieces + s[LT]: waveform signal + ds[LT]: derivative of s[LT], used only when ERROR_CHECK is defined. + f[L+1]: reference frequencies at knots + M: polynomial degree of piecewise approximation + SpecifyA, ssmode: pointer to a function that fills A[L], and mode argument to call it + WinOrder: order(=vanishing moment) of window used for constructing test functions + I: number of test functions per frame. + endmode: set to 1 or 3 to apply half-size frame over [0, T], to 2 or 3 to apply over [LT-T, LT] + Out: aita[N]: independent coefficients, where N is specified by SpecifyA. + + No return vlue. +*/ +void DerivativePiecewiseI(cdouble* aita, int L, double* f, int T, cdouble* s, int M, + void (*SpecifyA)(int L, int T, int M, int &N, double*** &A, MList* mlist, int mode), int ssmode, + int WinOrder, int I, int endmode, cdouble* ds) +{ + MList* mlist=new MList; + cdouble **u, **du; + setu(I, 2*T, u, du, WinOrder, mlist); + + int N; double **h, ***A; + seth(M, T, h, mlist); + SpecifyA(L, T, M, N, A, mlist, ssmode); + + DerivativePiecewise(N, aita, L, f, T, s, A, M, h, I, u, du, endmode, ds); + delete mlist; +}//DerivativePiecewiseI + +/* + function DerivativePiecewiseII: wrapper for DerivativePiecewise2(), doing the initialization ,etc. + This models the derivative of log ampltiude and frequency as separate piecewise polynomials, the first + specified by SpecifyA, the second by SpecifyB. + + In: L, T: number and length of pieces + s[LT]: waveform signal + ds[LT]: derivative of s[LT], used only when ERROR_CHECK is defined. + f[L+1]: reference frequencies at knots + M: polynomial degree of piecewise approximation + SpecifyA, ssAmode: pointer to a function that fills A[L], and mode argument to call it + SpecifyB, ssBmode: pointer to a function that fills B[L], and mode argument to call it + WinOrder: order(=vanishing moment) of window used for constructing test functions + I: number of test functions per frame. + endmode: set to 1 or 3 to apply half-size frame over [0, T], to 2 or 3 to apply over [LT-T, LT] + Out: p[Np], q[Nq]: independent coefficients, where Np and Nq are specified by SpecifyA and SpecifyB. + + No reutrn value. +*/ +void DerivativePiecewiseII(double* p, double* q, int L, double* f, int T, cdouble* s, int M, + void (*SpecifyA)(int L, int T, int M, int &N, double*** &A, MList* mlist, int mode), int ssAmode, + void (*SpecifyB)(int L, int T, int M, int &N, double*** &B, MList* mlist, int mode), int ssBmode, + int WinOrder, int I, int endmode, cdouble* ds) +{ + MList* mlist=new MList; + cdouble **u, **du; + setu(I, 2*T, u, du, WinOrder, mlist); + + int Np, Nq; + double **h, ***A, ***B; + seth(M, T, h, mlist); + SpecifyA(L, T, M, Np, A, mlist, ssAmode); + SpecifyB(L, T, M, Nq, B, mlist, ssBmode); + + DerivativePiecewise2(Np, p, Nq, q, L, f, T, s, A, B, M, h, I, u, du, endmode, ds); + + delete mlist; +}//DerivativePiecewiseII + +/* + function DerivativePiecewiseIII: wrapper for DerivativePiecewise3(), doing the initialization ,etc. + Notice that this time the log amplitude, rather than its derivative, is modeled as a piecewise + polynomial specified by SpecifyA. + + In: L, T: number and length of pieces + s[LT]: waveform signal + ds[LT]: derivative of s[LT], used only when ERROR_CHECK is defined. + f[L+1]: reference frequencies at knots + M: polynomial degree of piecewise approximation + SpecifyA, ssAmode: pointer to a function that fills A[L], and mode argument to call it + SpecifyB, ssBmode: pointer to a function that fills B[L], and mode argument to call it + WinOrder: order(=vanishing moment) of window used for constructing test functions + I: number of test functions per frame. + endmode: set to 1 or 3 to apply half-size frame over [0, T], to 2 or 3 to apply over [LT-T, LT] + Out: p[Np], q[Nq]: independent coefficients, where Np and Nq are specified by SpecifyA and SpecifyB. + + No reutrn value. +*/ +void DerivativePiecewiseIII(double* p, double* q, int L, double* f, int T, cdouble* s, int M, + void (*SpecifyA)(int L, int T, int M, int &N, double*** &A, MList* mlist, int mode), int ssAmode, + void (*SpecifyB)(int L, int T, int M, int &N, double*** &B, MList* mlist, int mode), int ssBmode, + int WinOrder, int I, int endmode, cdouble* ds) +{ + MList* mlist=new MList; + int Np, Nq; + double **h, ***A, ***B, **dh=0; + cdouble **u, **du; + setu(I, T*2, u, du, WinOrder, mlist); + seth(M, T, h, mlist); + if (ds) setdh(M, T, dh, mlist); + SpecifyA(L, T, M, Np, A, mlist, ssAmode); + SpecifyB(L, T, M, Nq, B, mlist, ssBmode); + Alloc2L(M, M, DM, mlist); + memset(DM[0], 0, sizeof(double)*M*M); for (int m=0; mAdd(s2ph, 1); + double *phcor=new double[L+1]; mlist->Add(phcor, 1); + cdouble* lamda=new cdouble[M]; mlist->Add(lamda, 1); + double* lamdax=new double[M]; mlist->Add(lamdax, 1); + double* lamday=new double[M]; mlist->Add(lamday, 1); + { + double tmpph=0; + memset(s2ph, 0, sizeof(double)*(L+1)); + s2ph[0]=tmpph; + for (int l=0; lAdd(win, 1); + for (int l=1; lAdd(b, 1); mlist->Add(zet, 1); mlist->Add(dzet, 1); + double ihT[]={T, T/2.0*T, T/3.0*T*T, T/4.0*T*T*T}; + + Alloc2L(L, N, BB, mlist); + //prepare linear system (BB)(zet)=(b) + for (int l=0; l +#include "xcomplex.h" +#include "arrayalloc.h" +#include "matrix.h" +#ifdef I +#undef I +#endif + +//--since function derivative------------------------------------------------ +double ddsincd_unn(double x, int N); +double dsincd_unn(double x, int N); + +//--window spectrum and derivatives------------------------------------------ +cdouble* Window(cdouble* x, double f, int N, int M, double* c, int K1, int K2); +void dWindow(cdouble* dx, cdouble* x, double f, int N, int M, double* c, int K1, int K2); +void ddWindow(cdouble* ddx, cdouble* dx, cdouble* x, double f, int N, int M, double* c, int K1, int K2); + +//--spectral projection routines--------------------------------------------- +cdouble IPWindowC(double f, cdouble* x, int N, int M, double* c, double iH2, int K1, int K2); + +double IPWindow(double f, cdouble* x, int N, int M, double* c, double iH2, int K1, int K2, bool returnamplitude); +double IPWindow(double f, void* params); +double ddIPWindow(double f, void* params); +double ddIPWindow(double f, cdouble* x, int N, int M, double* c, double iH2, int K1, int K2, double& dipwindow, double& ipwindow); + +double sIPWindow(double f, int L, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, cdouble* ej2ph=0); +double sIPWindow(double f, void* params); +double dsIPWindow(double f, int L, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, double& sip); +double dsIPWindow(double f, void* params); +double ddsIPWindow(double f, int L, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, double& dsip, double& sip); +double ddsIPWindow(double f, void* params); +double ddsIPWindow_unn(double f, cdouble* x, int N, int M, double* c, int K1, int K2, double& dsipwindow, double& sipwindow, cdouble* w_unn=0); + +double sIPWindowC(double f, int L, double offst_rel, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, cdouble* ej2ph=0); +double sIPWindowC(double f, void* params); +double dsIPWindowC(double f, int L, double offst_rel, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, double& sip); +double dsIPWindowC(double f, void* params); +double ddsIPWindowC(double f, int L, double offst_rel, cdouble** x, int N, int M, double* c, double iH2, int K1, int K2, double& dsip, double& sip); +double ddsIPWindowC(double f, void* params); + +//--least-square sinusoid estimation routines-------------------------------- +double LSESinusoid(cdouble* x, int N, double B, int M, double* c, double iH2, double& a, double& pp, double epf=1e-6); +void LSESinusoid(double& f, cdouble* x, int N, double B, int M, double* c, double iH2, double& a, double& pp, double epf=1e-6); +double LSESinusoid(int f1, int f2, cdouble* x, int N, double B, int M, double* c, double iH2, double& a, double& pp, double epf); +int LSESinusoid(double& f, double f1, double f2, cdouble* x, int N, double B, int M, double* c, double iH2, double& a, double& pp, double epf); +double LSESinusoidMP(double& f, double f1, double f2, cdouble** x, int Fr, int N, double B, int M, double* c, double iH2, double* a, double* ph, double epf); + +//--multi-sinusoid spectral projection routines------------------------------ +void IPMulti(int I, double* f, cdouble* lmd, cdouble* x, int Wid, int K1, int K2, int M, double* c, double eps=0); +void IPMulti(int I, double* f, cdouble* lmd, cfloat* x, int Wid, int K1, int K2, int M, double* c, double eps=0, double* sens=0, double* r1=0); +void IPMultiSens(int I, double* f, int Wid, int K1, int K2, int M, double* c, double* sens, double eps=0); +double IPMulti(int I, double* f, cdouble* lmd, cdouble* x, int Wid, int M, double* c, double iH2, int B); +double IPMulti_Direct(int I, double* f, double* ph, double* a, cdouble* x, int Wid, int M, double* c, double iH2, int B); +double IPMulti_GS(int I, double* f, double* ph, double* a, cdouble* x, int Wid, int M, double* c, double iH2, int B, double** L=0, double** Q=0); +cdouble* IPMulti(int I, int J, double* f, double* ph, double* a, cdouble* x, int Wid, int M, double* c, cdouble** wt=0, cdouble** Q=0, double** L=0, MList* RetList=0); + +//--dual-sinusoid spectral projection routines------------------------------- +double WindowDuo(double df, int N, double* d, int M, cdouble* w); +double ddWindowDuo(double df, int N, double* d, int M, double& dwindow, double& window, cdouble* w); +double sIPWindowDuo(double f1, double f2, cdouble* x, int N, double* c, double* d, int M, double iH2, int K1, int K2, cdouble& lmd1, cdouble& lmd2); +double sIPWindowDuo(double f2, void* params); +void ddsIPWindowDuo(double* ddsip2, double f1, double f2, cdouble* x, int N, double* c, double* d, int M, double iH2, int K1, int K2, cdouble& lmd1, cdouble& lmd2); +double ddsIPWindowDuo(double f2, void* params); +int LSEDuo(double& f2, double fmin, double fmax, double f1, cdouble* x, int N, double B, double* c, double* d, int M, double iH2, cdouble& r1, cdouble &r2, double epf); + +//--time-frequency reassignment---------------------------------------------- +void TFReas(double& f, double& t, double& fslope, int Wid, cdouble* data, double* win, double* dwin, double* ddwin, double* plogaslope=0); +void TFReas(double& f, double t, double& a, double& ph, double& fslope, int Wid, cdouble* data, double* w, double* dw, double* ddw, double* win=0); + +//--additive and multiplicative reestimation routines------------------------ +typedef double (*TBasicAnalyzer)(double* fs, double* as, double* phs, double* das, cdouble* x, int Count, int Wid, int Offst, __int16* ref, int reserved, bool LogA); +void AdditiveUpdate(double* fs, double* as, double* phs, double* das, cdouble* x, int Count, int Wid, int Offst, TBasicAnalyzer BasicAnalyzer, int reserved, bool LogA=false); +void AdditiveAnalyzer(double* fs, double* as, double* phs, double* das, cdouble* x, int Count, int Wid, int Offst, __int16* ref, TBasicAnalyzer BasicAnalyzer, int reserved, bool LogA=false); +void MultiplicativeUpdate(double* fs, double* as, double* phs, double* das, cdouble* x, int Count, int Wid, int Offst, TBasicAnalyzer BasicAnalyzer, int reserved, bool LogA=false); +void MultiplicativeAnalyzer(double* fs, double* as, double* phs, double* das, cdouble* x, int Count, int Wid, int Offst, __int16* ref, TBasicAnalyzer BasicAnalyzer, int reserved, bool LogA=false); +void MultiplicativeUpdateF(double* fs, double* as, double* phs, __int16* x, int Fr, int frst, int fren, int Wid, int Offst); + +void ReEstFreq(int FrCount, int Wid, int Offst, double* x, double* fbuf, double* abuf, double* pbuf, double* win, int M, double* c, double iH2, cdouble* w, cdouble* xc, cdouble* xs, double* ps, double* fa, double* fb, double* fc, double* fd, double* ns, int* Wids=0); +void ReEstFreq_2(int FrCount, int Wid, int Offst, double* x, double* fbuf, double* abuf, double* pbuf, double* win, int M, double* c, double iH2, cdouble* w, cdouble* xc, cdouble* xs, double* f3, double* f2, double* f1, double* f0, double* ns); +void ReEstFreqAmp(int FrCount, int Wid, int Offst, double* x, double* fbuf, double* abuf, double* pbuf, double* win, int M, double* c, double iH2, cdouble* w, cdouble* xc, cdouble* xs, double* ps, double* as, double* fa, double* fb, double* fc, double* fd, double* aa, double* ab, double* ac, double* ad, double* ns, int* Wids=0); +int Reestimate2(int FrCount, int Wid, int Offst, double* win, int M, double* c, double iH2, double* x, double* ae, double* fe, double* pe, double* aret, double* fret, double *pret, int maxiter, int* Wids=0); + +//--local derivative algorithms - DAFx09------------------------------------- +void Derivative(int M, double (**h)(double t, void*), double (**dh)(double t, void*), cdouble* r, int p0s, int* p0, int q0s, int* q0, int Wid, double* s, double** win, double omg, void* harg); +void DerivativeLS(int K, int M, double (**h)(double t, void* harg), double (**dh)(double t, void* harg), cdouble* r, int p0s, int* p0, int q0s, int* q0, int Wid, double* s, double** win, double omg, void* harg, bool r0=false); +void DerivativeLS(int Fr, int K, int M, double (**h)(double t, void* harg), double (**dh)(double t, void* harg), cdouble* r, int p0s, int* p0, int q0s, int* q0, int Wid, double* s, double** win, double omg, void* harg, bool r0=false); + +//--the Abe-Smith estimator-------------------------------------------------- +void TFAS05(double& f, double& t, double& a, double& ph, double& aesp, double& fslope, int Wid, double* data, double* w, double res=1); +void TFAS05_enh(double& f, double& t, double& a, double& ph, double& aesp, double& fslope, int Wid, double* data, double* w, double res=1); +void TFAS05_enh(double& f, double& t, double& a, double& ph, int Wid, double* data, double* w, double res=1); + +//--piecewise derivative algorithms and tools-------------------------------- +void DerivativePiecewise(int N, cdouble* aita, int L, double* f, int T, cdouble* s, double*** A, int M, double** h, int I, cdouble** u, cdouble** du, int endmode=0, cdouble* ds=0); +void DerivativePiecewise2(int Np, double* p, int Nq, double* q, int L, double* f, int T, cdouble* s, double*** A, double*** B, int M, double** h, int I, cdouble** u, cdouble** du, int endmode=0, cdouble* ds=0); +void DerivativePiecewise3(int Np, double* p, int Nq, double* q, int L, double* f, int T, cdouble* s, double*** DA, double*** B, int M, double** h, int I, cdouble** u, cdouble** du, int endmode=0, cdouble* ds=0, double** dh=0); +void seth(int M, int T, double**& h, MList* mlist); +void setdh(int M, int T, double**& dh, MList* mlist); +void setdih(int M, int T, double**& dih, MList* mlist); +void setu(int I, int Wid, cdouble**& u, cdouble**& du, int WinOrder=2, MList* mlist=0); +void ssALinearSpline(int L, int T, int M, int& N, double*** &A, MList* mlist, int mode=0); +void ssACubicHermite(int L, int T, int M, int& N, double*** &A, MList* mlist, int mode=0); +void ssACubicSpline(int L, int T, int M, int& N, double*** &A, MList* mlist, int mode=0); +void ssLinearSpline(int L, int T, int M, int &N, double** &h, double*** &A, MList* mlist, int mode=0); +void ssCubicHermite(int L, int T, int M, int& N, double** &h, double*** &A, MList* mlist, int mode=0); +void ssCubicSpline(int L, int T, int M, int& N, double** &h, double*** &A, MList* mlist, int mode=0); +void DerivativePiecewiseI(cdouble* aita, int L, double* f, int T, cdouble* s, int M, void (*SpecifyA)(int L, int T, int M, int &N, double*** &A, MList* mlist, int mode), int ssmode=0, int WinOrder=2, int I=2, int endmode=0, cdouble* ds=0); +void DerivativePiecewiseII(double* p, double* q, int L, double* f, int T, cdouble* s, int M, void (*SpecifyA)(int L, int T, int M, int &N, double*** &A, MList* mlist, int mode), int ssAmode, void (*SpecifyB)(int L, int T, int M, int &N, double*** &B, MList* mlist, int mode), int ssBmode, int WinOrder=2, int I=2, int endmode=0, cdouble* ds=0); +void DerivativePiecewiseIII(double* p, double* q, int L, double* f, int T, cdouble* s, int M, void (*SpecifyA)(int L, int T, int M, int &N, double*** &A, MList* mlist, int mode), int ssAmode, void (*SpecifyB)(int L, int T, int M, int &N, double*** &B, MList* mlist, int mode), int ssBmode, int WinOrder=2, int I=2, int endmode=0, cdouble* ds=0); +double AmpPhCorrectionExpA(cdouble* s2, int N, cdouble* aita, int L, int T, cdouble* sre, int M, double** h, double** dih, double*** A, void (*SpecifyA)(int L, int T, int M, int &N, double*** &A, MList* mlist, int mode), int WinOrder); + +//--local derivative algorithms - general------------------------------------ +/* + template DerivativeLSv: local derivative algorithm for estimating time-varying sinusoids, "v" version, + i.e. using tuned test functions. + + In: s[Wid]: waveform data + v[I][Wid], dv[I][Wid]: test functions and their derivatives + h[M+1][Wid]: basis functions + p0[p0s], q0[q0s]: zero-constraints, i.e. Re(lmd[p0[*]]) and Im(lmd[q0[*]]) are constrained zero. + Out: lmd[1:M]: coefficients of h[1:M]. + + Returns inner product of s and v[0]. +*/ +templatecdouble DerivativeLSv(int Wid, Ts* s, int I, cdouble** v, cdouble** dv, int M, double **h, cdouble* lmd, int p0s, int* p0, int q0s, int* q0) +{ + int Kr=M*2-p0s-q0s; //number of real unknowns apart from p0 and q0 + if (I=0){uind[up]=p; ind[p]=up; up++;} p++;} + if (up!=Kr) throw(""); + } + + cdouble* sv1=new cdouble[I]; + for (int i=0; i=0) + { + A[2*i][lind]=shv.x; + A[2*i+1][lind]=shv.y; + } + if ((lind=ind[2*m-1])>=0) + { + A[2*i][lind]=-shv.y; + A[2*i+1][lind]=shv.x; + } + } + + double* pq=new double[Kr]; + if (2*I==Kr) GECP(Kr, pq, A, (double*)sv1); + else LSLinear(2*I, Kr, pq, A, (double*)sv1); + + memset(lmd, 0, sizeof(double)*(M+1)*2); + for (int k=0; kcdouble DerivativeLS(int Wid, Ts* s, int I, double omg, Tu** u, Tu** du, int M, double **h, cdouble* lmd, int p0s, int* p0, int q0s, int* q0) +{ + cdouble** Allocate2(cdouble, I, Wid, v); + cdouble** Allocate2(cdouble, I, Wid, dv); + cdouble jomg=cdouble(0, omg); int hWid=Wid/2; + for (int c=0; ccdouble DerivativeLS_AmpPh(int Wid, int M, double** integr_h, cdouble* lmd, double omg, Tu* u0, cdouble sv0) +{ + cdouble e0=0; double hWid=Wid/2.0; + for (int n=0; n300) expo.x=300; + else if (expo.x<-300) expo.x=-300; + e0+=exp(expo)**(cdouble(u0[n]).rotate(omg*(n-hWid))); + } + return log(sv0/e0); +}//DerivativeLS_AmpPh + +/* + template DerivativeLS_AmpPh: amplitude and phase estimation in the local derivative algorithm, "u" + version. + + In: s[Wid]: waveform data + u0[Wid], omg: base-band test function and carrier frequency used for computing v0[] + integr_h[M+1][Wid]: integrals of basis functions + + Returns coefficient to integr_h[0]=1. +*/ +templatecdouble DerivativeLS_AmpPh(int Wid, int M, double** integr_h, cdouble* lmd, double omg, Tu* u0, Ts* s) +{ + cdouble ss0=0, e0=0; double hWid=Wid/2.0; + for (int n=0; n300) expo.x=300; + else if (expo.x<-300) expo.x=-300; + e0+=~exp(expo)*abs(u0[n]); + ss0+=s[n]**exp(expo)*abs(u0[n]); + } + return log(ss0/e0); +}//DerivativeLS_AmpPh + +cdouble DerivativeLSv_AmpPh(int, int, double**, cdouble*, cdouble*, cdouble); //the "v" version is implemented as a normal function in SinEst.cpp. + +/* + template DerivativeLSv: local derivative algorithm for estimating time-varying sinusoids, "v" version. + + In: s[Wid]: waveform data + v[I][Wid], dv[I][Wid]: test functions and their derivatives + h[M+1][Wid], integr_h[M+1][Wid]: basis functions and their integrals + p0[p0s], q0[q0s]: zero-constraints, i.e. Re(lmd[p0[*]]) and Im(lmd[q0[*]]) are constrained zero. + Out: lmd[M+1]: coefficients of h[M+1], including lmd[0]. + + No return value. +*/ +template void DerivativeLSv(int Wid, Ts* s, int I, cdouble** v, cdouble** dv, int M, double **h, double **integr_h, cdouble* lmd, int p0s, int* p0, int q0s, int* q0) +{ + cdouble sv0=DerivativeLSv(Wid, s, I, v, dv, M, h, lmd, p0s, p0, q0s, q0); + lmd[0]=DerivativeLSv_AmpPh(Wid, M, integr_h, lmd, v[0], sv0); +}//DerivativeLSv_AmpPh + +/*template DerivativeLSv: local derivative algorithm for estimating time-varying sinusoids, "u" version. + + In: s[Wid]: waveform data + u[I][Wid], du[I][Wid]: base-band test functions and their derivatives + omg: angular frequency onto which u[I] and du[I] are modulated to give the test functions + h[M+1][Wid], integr_h[M+1][Wid]: basis functions and their integrals + p0[p0s], q0[q0s]: zero-constraints, i.e. Re(lmd[p0[*]]) and Im(lmd[q0[*]]) are constrained zero. + Out: lmd[M+1]: coefficients of h[M+1], including lmd[0]. + + No return value. +*/ +templatevoid DerivativeLS(int Wid, Ts* s, int I, double omg, Tu** u, Tu** du, int M, double **h, double **integr_h, cdouble* lmd, int p0s, int* p0, int q0s, int* q0) +{ + cdouble sv0=DerivativeLS(Wid, s, I, omg, u, du, M, h, lmd, p0s, p0, q0s, q0); + lmd[0]=DerivativeLS_AmpPh(Wid, M, integr_h, lmd, omg, u[0], s); //sv0); +}//DerivativeLSv + +/* + template CosineWindows: generates the Hann^(K/2) window and its L-1 derivatives as Result[L][Wid+1] + + In: K, L, Wid + Out: w[L][Wid+1]: Hann^(K/2) window function and its derivatives up to order L-1 + + Returns pointer to w. w is created anew if w=0 is specified on start. +*/ +templateT** CosineWindows(int K, int Wid, T **w, int L=0) +{ + if (L<=0) L=K; + if (!w) {Allocate2(T, L, Wid+1, w);} + memset(w[0], 0, sizeof(T)*L*(Wid+1)); + int hWid=Wid/2, dWid=Wid*2; + double *s=new double[dWid+hWid], *c=&s[hWid]; //s[n]=sin(pi*n/N), n=0, ..., 2N-1 + double *C=new double[K+2], *lK=&C[K/2+1], piC=M_PI/Wid; + //C[i]=C(K, i)(-1)^i*2^(-K+1), the combination number, i=0, ..., K/2 + //ik[i]=(K-2i)^k*(M_PI/Wid)^k, i=0, ..., K/2 + //calculate C(K,i)(-1)^i*2^(-K+1) + C[0]=1.0/(1<<(K-1)); double lC=C[0]; for (int i=1; i+i<=K; i++){lC=lC*(K-i+1)/i; C[i]=(i%2)?(-lC):lC;} + //calculate sin/cos functions + for (int n=0; n300) e=300; + if (e<-300) e=-300; + s[t]=exp(cdouble(e, ph+(t*t*(_q+t*_p)))); + ph+=dph; + } +}//SinusoidExpA + +/* +//This is not used any longer as the recursion does not seem to help saving computation with all its overheads. +void SinusoidExp(cdouble* data, int CountSt, int CountEn, double a3, double a2, double a1, double a0, + double omg3, double omg2, double omg1, double omg0, double &ea, double &ph, bool add) +{ + int i; + double dea, ddea, dddea, ddddea, + dph, ddph, dddph, ddddph, + sph, cph, sdph, cdph, sddph, cddph, sdddph, cdddph, sddddph, cddddph, + e0=ea, e1=a0, e2=0.5*a1, e3=a2/3, e4=a3/4, + p0=ph, p1=omg0, p2=0.5*omg1, p3=omg2/3, p4=omg3/4, tmp; + if (CountSt==0) + { + dea=e1+e2+e3+e4; ddea=2*e2+6*e3+14*e4; dddea=6*e3+36*e4; ddddea=24*e3; + dph=p1+p2+p3+p4; ddph=2*p2+6*p3+14*p4; dddph=6*p3+36*p4; ddddph=24*p4; + } + else + { + ea=e0+CountSt*(e1+CountSt*(e2+CountSt*(e3+CountSt*e4))); + dea=e1+e2+e3+e4+CountSt*(2*e2+3*e3+4*e4+CountSt*(3*e3+6*e4+CountSt*4*e4)); + ddea=2*e2+6*e3+14*e4+CountSt*(6*e3+24*e4+CountSt*12*e4); + dddea=6*e3+36*e4+CountSt*24*e4; ddddea=24*e4; + ph=p0+CountSt*(p1+CountSt*(p2+CountSt*(p3+CountSt*p4))); + dph=p1+p2+p3+p4+CountSt*(2*p2+3*p3+4*p4+CountSt*(3*p3+6*p4+CountSt*4*p4)); + ddph=2*p2+6*p3+14*p4+CountSt*(6*p3+24*p4+CountSt*12*p4); + dddph=6*p3+36*p4+CountSt*24*p4; ddddph=24*p4; + } + sph=sin(ph), cph=cos(ph); + sdph=sin(dph), cdph=cos(dph); + sddph=sin(ddph), cddph=cos(ddph); + sdddph=sin(dddph), cdddph=cos(dddph); + sddddph=sin(ddddph), cddddph=cos(ddddph); + if (add) + { + for (i=CountSt; i=xs[Fr-1] + add: specifies if the resynthesized sinusoid is to be added to or to replace the content of output buffer + Out: xrec[0:den-dst-1]: output buffer hosting synthesized sinusoid from dst to den. + phs[Fr]: phase angles at xs[Fr] + + Returns pointer to xrec. +*/ +double* SynthesizeSinusoid(double* xrec, int dst, int den, double* phs, int Fr, double* xs, double* fs, double* as, bool add, bool* terminatetag) +{ + double *f3=new double[Fr*8], *f2=&f3[Fr], *f1=&f3[Fr*2], *f0=&f3[Fr*3], + *a3=&f3[Fr*4], *a2=&a3[Fr], *a1=&a3[Fr*2], *a0=&a3[Fr*3]; + CubicSpline(Fr-1, f3, f2, f1, f0, xs, fs, 1, 1); + CubicSpline(Fr-1, a3, a2, a1, a0, xs, as, 1, 1); + double ph=phs[0]; + for (int fr=0; fr=xs[Fr-1] + add: specifies if the resynthesized sinusoid is to be added to or to replace the content of output + buffer + Out: xrecm[0:den-dst-1]: output buffer hosting synthesized sinusoid from dst to den. + + Returns pointer to xrecm. +*/ +double* SynthesizeSinusoidP(double* xrecm, int dst, int den, double* phs, int Fr, double* xs, double* fs, double* as, bool add) +{ + double *f3=new double[Fr*8], *f2=&f3[Fr], *f1=&f3[Fr*2], *f0=&f3[Fr*3], + *a3=&f3[Fr*4], *a2=&a3[Fr], *a1=&a3[Fr*2], *a0=&a3[Fr*3]; + CubicSpline(Fr-1, f3, f2, f1, f0, xs, fs, 1, 1); + CubicSpline(Fr-1, a3, a2, a1, a0, xs, as, 1, 1); + for (int fr=0; fr +#include +#include "WindowFunctions.h" +#include "align8.h" +//--------------------------------------------------------------------------- + +/* + function I0: Bessel function of order zero + + Returns the Bessel function of x. +*/ +double I0(double x) +{ + const double eps=1e-9; + + int n=1; + double S=1, D=1, T; + + while (D>eps*S) + { + T=x/(2*n++); + D*=T*T; + S+=D; + } + return S; +}//I0 + +/* + function FillWindow: fills a buffer $Result with a window function. + + In: wt:window type + Count: window size + ips & ups: extra window specificatin parameters + Out: newwindow[Count+1]: the window function. + + No return value. This function is designed assuming Count being even, and Count/2 is the symmetry + centre of newwindow. newwindow has physical size Count+1 for compatibility purpose. For all vanishing + windows (e.g. Hann) newwindow[0]=newwindow[Count]=0. +*/ +void FillWindow(double* Result, WindowType wt, int Count, int* ips, double* dps) +{ + switch(wt) + { + case wtRectangle: + for (int i=0; i<=Count; i++) + Result[i]=1; + break; + case wtTriangular: + for (int i=0; i<=Count; i++) + Result[i]=1-2*fabs(i-Count/2.0)/Count; + break; + case wtHamming: + for (int i=0; i<=Count; i++) + Result[i]=(0.54-0.46*cos(2*M_PI*i/Count)); + break; + case wtBlackman: + for (int i=0; i<=Count; i++) + Result[i]=0.42-0.5*cos(2*M_PI*i/Count)+0.08*cos(4*M_PI*i/Count); + break; + case wtGaussian: + { + double num=*dps*M_PI*M_PI/2/Count/Count; + for (int i=0; i<=Count; i++) + Result[i]=exp(-(i-Count/2.0)*(i-Count/2.0)*num); + break; + } + case wtKaiser: + { + double ldps=*dps*M_PI*M_PI/2/Count; + double den=I0(0.5*Count*ldps); + for (int i=0; i<=Count; i++) + Result[i]=I0(ldps*sqrt(0.25*Count*Count-(i-Count*0.5)*(i-Count*0.5)))/den; + break; + } + case wtHalfCosine: + { + for (int i=0; i<=Count; i++) + Result[i]=sin(M_PI*i/Count); + break; + } + case wtHann: + { + for (int i=0; i<=Count; i++) + Result[i]=(0.5-cos(M_PI*2*i/Count)/2); + break; + } + case wtHannSqr: + { + for (int i=0; i<=Count; i++) + Result[i]=(3-4*cos(M_PI*2*i/Count)+cos(M_PI*4*i/Count))/8; + break; + } + case wtHann3sqrt: + { + for (int i=0; i<=Count; i++) + { + double tmp=sin(M_PI*i/Count); + Result[i]=tmp*tmp*tmp; + } + break; + } + } +}//FillWindow + +/* + function NewWindow: creates a window function. + + In: wt: window type + Count: window size + ips & ups: extra window specificatin parameters + Out: newwindow[Count+1]: the window function. + + Returns pointer to newwindow. newwindow is created anew if newwindow=0 is specified on start. +*/ +double* NewWindow(WindowType wt, int Count, int* ips, double* dps, double* newwindow) +{ + double* Result=newwindow; if (!Result) Result=new double[Count+1]; + FillWindow(Result, wt, Count, ips, dps); + return Result; +}//NewWindow + +/* + function NewWindow8: 8-byte aligned version of NewWindow. + + In: wt: window type + Count: window size + ips & ups: extra window specificatin parameters + Out: newwindow[Count+1]: the window function. + + Returns pointer to newwindow. newwindow is created anew and 8-byte aligned if newwindow=0 is + specified on start. However, if newwindow is not 8-byte aligned on start, the unaligned buffer is + returned. +*/ +double* NewWindow8(WindowType wt, int Count, int* ips, double* dps, double* newwindow) +{ + double* Result=newwindow; if (!Result) Result=(double*)malloc8(sizeof(double)*(Count+1)); + FillWindow(Result, wt, Count, ips, dps); + return Result; +}//NewWindow8 + +/* + function NewdWindow: computes the derivative of a window function. + + In: wt: window type + Count: window size + ips & ups: extra window specificatin parameters + Out: newdwindow[Count+1]: the derivative window function. + + Returns pointer to newdwindow. newdwindow is created anew if newdwindow=0 is specified on start. +*/ +double* NewdWindow(WindowType wt, int Count, int* ips, double* dps, double* newdwindow) +{ + double* Result=newdwindow; if (!Result) Result=new double[Count+1]; + double piiCount=M_PI/Count; + switch(wt) + { + case wtRectangle: + memset(Result, 0, sizeof(double)*(Count+1)); + break; + case wtTriangular: + throw("Trying to differentiate triangular window."); + break; + case wtHamming: + for (int i=0; i<=Count; i++) + Result[i]=0.92*piiCount*sin(2*piiCount*i); + break; + case wtBlackman: + for (int i=0; i<=Count; i++) + Result[i]=piiCount*sin(2*piiCount*i)-0.32*piiCount*sin(4*piiCount*i); + break; + case wtGaussian: + throw("Trying to differentiate Gaussian window."); + break; + case wtKaiser: + throw("Trying to differentiate Kaiser window."); + break; + case wtHalfCosine: + { + for (int i=0; i<=Count; i++) + Result[i]=piiCount*cos(piiCount*i); + break; + } + case wtHann: + { + for (int i=0; i<=Count; i++) + Result[i]=piiCount*sin(2*piiCount*i); + break; + } + case wtHannSqr: + { + for (int i=0; i<=Count; i++) + Result[i]=piiCount*sin(2*piiCount*i)-0.5*piiCount*sin(4*piiCount*i); + break; + } + case wtHann3sqrt: + { + for (int i=0; i<=Count; i++) + { + double s=sin(M_PI*i/Count), c=cos(M_PI*i/Count); + Result[i]=3*piiCount*s*s*c; + } + } + } + return Result; +}//NewdWindow + +/* + function NewddWindow: computes the 2nd-order derivative of a window function. + + In: wt: window type + Count: window size + ips & ups: extra window specificatin parameters + Out: newddwindow[Count+1]: the 2nd-order derivative window function. + + Returns pointer to newddwindow. newddwindow is created anew if newddwindow=0 is specified on start. +*/ +double* NewddWindow(WindowType wt, int Count, int* ips, double* dps, double* newddwindow) +{ + double* Result=newddwindow; if (!Result) Result=new double[Count+1]; + double piiCount=M_PI/Count; + double piC2=piiCount*piiCount; + switch(wt) + { + case wtRectangle: + memset(Result, 0, sizeof(double)*(Count+1)); + break; + case wtTriangular: + throw("Trying to double-differentiate triangular window."); + break; + case wtHamming: + for (int i=0; i<=Count; i++) + Result[i]=1.84*piC2*cos(2*piiCount*i); + break; + case wtBlackman: + for (int i=0; i<=Count; i++) + Result[i]=2*piC2*cos(2*piiCount*i)-1.28*piC2*cos(4*piiCount*i); + break; + case wtGaussian: + throw("Trying to double-differentiate Gaussian window."); + break; + case wtKaiser: + throw("Trying to double-differentiate Kaiser window."); + break; + case wtHalfCosine: + { + for (int i=0; i<=Count; i++) + Result[i]=-piC2*sin(piiCount*i); + break; + } + case wtHann: + { + for (int i=0; i<=Count; i++) + Result[i]=2*piC2*cos(2*piiCount*i); + break; + } + case wtHannSqr: + { + for (int i=0; i<=Count; i++) + Result[i]=2*piC2*cos(2*piiCount*i)-2*piC2*cos(4*piiCount*i); + break; + } + case wtHann3sqrt: + { + for (int i=0; i<=Count; i++) + { + double s=sin(M_PI*i/Count), c=cos(M_PI*i/Count); + Result[i]=3*piC2*(2*c*c-s*s)*s; + } + break; + } + } + return Result; +}//NewddWindow + +/* + function NewdddWindow: computes the 3rd-order derivative of a window function. + In: wt: window type + Count: window size + ips & ups: extra window specificatin parameters + Out: newdddwindow[Count+1]: the 3rd-order derivative window function. + + Returns pointer to newdddwindow. newdddwindow is created anew if newdddwindow=0 is specified on start. +*/ +double* NewdddWindow(WindowType wt, int Count, int* ips, double* dps, double* newdddwindow) +{ + double* Result=newdddwindow; if (!Result) Result=new double[Count+1]; + double piiCount=M_PI/Count; + double piC2=piiCount*piiCount; + double piC3=piiCount*piC2; + switch(wt) + { + case wtRectangle: + memset(Result, 0, sizeof(double)*(Count+1)); + break; + case wtTriangular: + throw("Trying to triple-differentiate triangular window."); + break; + case wtHamming: + for (int i=0; i<=Count; i++) + Result[i]=-3.68*piC3*sin(2*piiCount*i); + break; + case wtBlackman: + for (int i=0; i<=Count; i++) + Result[i]=-4*piC3*sin(2*piiCount*i)+5.12*piC3*sin(4*piiCount*i); + break; + case wtGaussian: + throw("Trying to triple-differentiate Gaussian window."); + break; + case wtKaiser: + throw("Trying to triple-differentiate Kaiser window."); + break; + case wtHalfCosine: + { + for (int i=0; i<=Count; i++) + Result[i]=-piC3*cos(piiCount*i); + break; + } + case wtHann: + { + for (int i=0; i<=Count; i++) + Result[i]=-4*piC3*sin(2*piiCount*i); + break; + } + case wtHannSqr: + { + for (int i=0; i<=Count; i++) + Result[i]=-4*piC3*sin(2*piiCount*i)+8*piC3*sin(4*piiCount*i); + break; + } + case wtHann3sqrt: + throw("Trying to triple-differentiate Hann^1.5 window."); + break; + } + return Result; +}//NewdddWindow + +//--------------------------------------------------------------------------- +/* + function windowspec: computes a few descriptors of a cosine family window. A window function in the + cosine window family is the linear combination of a few cosine functions, therefore has a cosine + decomposition. + + In: wt: window type + N: window size + Out: c[M+1], coefficients of cosine decomposition + iH2: reciprocal of square of L2 norm, + d[2M+1]: self-convolution of c[], optional + + No return value. +*/ +void windowspec(WindowType wt, int N, int* M, double* c, double* iH2, double* d) +{ + switch(wt) + { + case wtRectangle: + M[0]=0, c[0]=1, iH2[0]=1.0/N/N; + if (d) d[0]=1; + break; + case wtHamming: + M[0]=1, c[0]=0.54, c[1]=0.23, iH2[0]=1.0/(0.3974*N*N); + if (d) d[0]=0.3974, d[1]=0.2484, d[2]=0.0529; + break; + case wtBlackman: + M[0]=2, c[0]=0.42, c[1]=0.25, c[2]=0.04, iH2[0]=1.0/(0.3046*N*N); + if (d) d[0]=0.3046, d[1]=0.23, d[2]=0.0961, d[4]=0.02, d[5]=0.0016; + break; + case wtHann: + M[0]=1, c[0]=0.5, c[1]=0.25, iH2[0]=1.0/(0.375*N*N); + if (d) d[0]=0.375, d[1]=0.25, d[2]=0.0625; + break; + default: + M[0]=1, c[0]=0.5, c[1]=0.25, iH2[0]=1.0/(0.375*N*N); + if (d) d[0]=0.375, d[1]=0.25, d[2]=0.0625; + break; + } +}//windowspec diff -r 9b9f21935f24 -r 6422640a802f WindowFunctions.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WindowFunctions.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,33 @@ +#ifndef WindowFunctionsH +#define WindowFunctionsH + +/* + WindowFunctions.cpp - implements a few common window functions. +*/ + +enum WindowType +{ + wtRectangle, + wtTriangular, + wtHamming, + wtBlackman, + wtGaussian, + wtKaiser, + wtHalfCosine, + wtHann, + wtHannSqr, + wtHann3sqrt +}; + +//--window function computation routines------------------------------------- +void FillWindow(double* newwindow, WindowType wt, int Count, int* ips=0, double* dps=0); +double* NewWindow(WindowType wt, int Count, int* ips=0, double* dps=0, double* newwindow=0); +double* NewWindow8(WindowType wt, int Count, int* ips=0, double* dps=0, double* newwindow=0); +double* NewdWindow(WindowType wt, int Count, int* ips=0, double* dps=0, double* newdwindow=0); +double* NewddWindow(WindowType wt, int Count, int* ips=0, double* dps=0, double* newddwindow=0); +double* NewdddWindow(WindowType wt, int Count, int* ips=0, double* dps=0, double* newdddwindow=0); + +//--other functions---------------------------------------------------------- +void windowspec(WindowType wt, int N, int* M, double* c, double* iH2, double* d=0); + +#endif diff -r 9b9f21935f24 -r 6422640a802f align8.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/align8.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,33 @@ +//--------------------------------------------------------------------------- + + +#include +#include +#include "align8.h" + +//--------------------------------------------------------------------------- +/* + function malloc8: 8-byte (64-bit) aligned memory allocation. + + Returns pointer to a memory block of $size starting at an address divisible by 8. +*/ +void* malloc8(unsigned size) +{ + char *buffer, *result; + buffer=(char*)malloc(size+8+sizeof(void*)); + if(!buffer) return(NULL); + char* tmp=&buffer[sizeof(void*)]; + result=&((char*)((unsigned)tmp&0xFFFFFFF8))[8]; + ((void**)result)[-1]=buffer; + return(result); +}//malloc8 + +/* + function free8: deallocation for malloc8() + + No return value. +*/ +void free8(void* buffer8) +{ + if (buffer8) free(((void**)buffer8)[-1]); +}//free8 diff -r 9b9f21935f24 -r 6422640a802f align8.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/align8.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,39 @@ +//--------------------------------------------------------------------------- + +#ifndef align8H +#define align8H + +/* + align8.cpp - 8-byte (64-bit) alignment routines. + + This file provides tools for aligning boundaries of arrays, particularly of 64-byte units, e.g. double- + precision floating points, to physical addresses divisible by 8, including aligning dynamically + allocated memory block and aligning call stack. Currently stack alignment is disabled due to lack + of compiler support for Intel assembly. + + Further reading: "Double precision floating point alignment issue.pdf" +*/ + +//--stack alignment---------------------------------------------------------- +/* + These two macros are used to envelop a function call so that the function is called with a stack + pointer that is a multiple of 8. Aligning the stack does not automatically ensure that local double + -precision floating-point variables (or other variables one wants to align to 8-byte boundaries) are + aligned to 8-byte boundaries; one has to adjust the variable declaration order, using dummy variables + if necessary, to make the alignment happen. +*/ +//macro ALIGN_STACK_8 moves the current stack pointer to a multiple of 8 +#define ALIGN_STACK_8 {int alignstack; asm {mov alignstack, esp} asm {and esp, 0xFFFFFFF8} +//macro RESTORE_STACK moves the stack pointer back to where it was before ALIGN_STACK_8 +#define RESTORE_STACK asm {mov esp, alignstack}} + +//ALIGN8 ensures F is executed with a start stack pointer divisible by 8. Currently disabled. +#define ALIGN8(F) F +//uncomment the following line to enable stack alignment +//#define ALIGN8(F) ALIGN_STACK_8 F RESTORE_STACK + +//--64-bit aligned memory allocation routines-------------------------------- +void* malloc8(unsigned size); +void free8(void* buffer8); + +#endif diff -r 9b9f21935f24 -r 6422640a802f arrayalloc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/arrayalloc.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,88 @@ +#ifndef ARRAYALLOC +#define ARRAYALLOC + +/* + arrayalloc.h - 2D, 3D and 4D array memory allocation routines. + + An x-dimensional array (x=2, 3, 4, ...) is managed as a single memory block hosting all records, plus + an index block which is a (x-1)D array itself. Therefore a 2D array is allocated as two 1D arrays, a + 3D array is allocated as three 1D arrays, etc. + + Examples: + Alloc2(4, N, x) declares an array x[4][N] of double-precision floating points. + Allocate3(int, K, L, M, x) allocates an array x[K][L][M] of integers and returns x as int***. + + This file also includes a garbage collector class MList that works with arrays allcoated in this way. +*/ + + +#include + +//2D array allocation macros for declaring an array of double +#define Alloc2(M, N, x) \ + double **x=new double*[M]; x[0]=new double[(M)*(N)]; for (int _z=1; _zAdd(x, 2); +//2D array allocation macros for all data types +#define Allocate2(INT, M, N, x) \ + x=new INT*[M]; x[0]=new INT[(M)*(N)]; for (int _z=1; _zAdd(x, 2); +//2D array deallocation macro +#define DeAlloc2(x) {if (x) {delete[] (char*)(x[0]); delete[] x; x=0;}} + +//3D array allocation macro for declaring an array of double +#define Alloc3(L, M, N, x) \ + double*** x=new double**[L]; x[0]=new double*[(L)*(M)]; x[0][0]=new double[(L)*(M)*(N)]; \ + for (int _z=0; _zAdd(x, 3); +//3D array deallocation macro +#define DeAlloc3(x) {if (x) {delete[] (char*)(x[0][0]); delete[] x[0]; delete[] x; x=0;}} + +//4D array allocation macro for declaring an array of double +#define Alloc4(L, M, N, O, x) \ + double**** x=new double***[L]; x[0]=new double**[(L)*(M)]; \ + x[0][0]=new double*[(L)*(M)*(N)]; x[0][0][0]=new double[(L)*(M)*(N)*(O)]; \ + for (int _z=0; _z +#include +#include "fft.h" + +//--------------------------------------------------------------------------- +/* + function Atan2: (0, 0)-safe atan2 + + Returns 0 is x=y=0, atan2(x,y) otherwise. +*/ +double Atan2(double y, double x) +{ + if (x==0 && y==0) return 0; + else return atan2(y, x); +}//Atan2 + +/* + function BitInv: inverse bit order of Value within an $Order-bit expression. + + In: integer Value smaller than 2^Order + + Returns an integer whose lowest Order bits are the lowest Order bits of Value in reverse order. +*/ +int BitInv(int Value, int Order) +{ + int Result; + Result=0; + for (int i=0;i>1; + } + return Result; +}//BitInv + +/* + function SetTwiddleFactors: fill w[N/2] with twiddle factors used in N-point complex FFT. + + In: N + Out: array w[N/2] containing twiddle factors + + No return value. +*/ +void SetTwiddleFactors(int N, cdouble* w) +{ + double ep=-M_PI*2/N; + for (int i=0; i1) + { + //N=2^order, 4|N, 2|hN + //keep subsequence 0, 2C, 4C, ... 2(N-1)C + //process sequence C, 3C, ..., (2N-1)C only + //RDCT4 routine + int hN=N/2, N2=N*2; + for (int i=0; i>1); + C=(C<<1); + for (int i=1; i0) + { + //N=2^order, 4|N, 2|hN + //keep subsequence 0, 2C, 4C, ... 2(N-1)C + //process sequence C, 3C, ..., (2N-1)C only + //data pass from Input + for (int i=0; i>1); + C=(C<<1); + for (int i=1; iwid) memcpy(&spec[fr][hwid], &data[fr*Wid+hwid], sizeof(double)*(Wid-wid)); + for (int i=0, j=(fr+1)*Wid-hwid, k=(fr+1)*Wid+hwid-1, l=Wid-hwid; i>=1, M<<=1) + { + cdouble *Xn=X[n], *Xp=X[n-1]; + for (int l=0; l3 + { + cdouble *aX=&X[n][l*M+M/2]; + for (int an=1, aL=1, aM=M/2; an +#include "xcomplex.h" +#include "align8.h" + +//--------------------------------------------------------------------------- +/* + macro AllocateFFTBuffer: allocates FFT buffers X and W of given length with an extra LDATA buffer the + right size for hosting such arrays as the standard-size window function or output amplitude spectrum. + + This macro allocates buffer with 64-bit alignment, which must be freed with free8() or FreeFFTBuffer + macro. +*/ +#define AllocateFFTBuffer(_WID, _LDATA, _W, _X) \ + double *_LDATA=(double*)malloc8(sizeof(double)*_WID*4); \ + cdouble *_X=(cdouble*)&_LDATA[_WID]; \ + cdouble *_W=(cdouble*)&_X[_WID]; \ + SetTwiddleFactors(_WID, _W); +#define FreeFFTBuffer(_LDATA) free8(_LDATA); + +//--compelx FFT and IFFT----------------------------------------------------- +void CFFTCbii(int Order, cdouble* W, cdouble* X); //complex FFT subroutine after bit-inversed ordering +void CFFTC(int Order, cdouble* W, cdouble* X, int* bitinv=0); //complex FFT, compact in-place +void CFFTC(cdouble* Input, double* Amp, double *Arg, int Order, cdouble* W, cdouble* X, int* bitinv=0); //complex FFT +void CFFTCW(double* Window, int Order, cdouble* W, cdouble* X, int* bitinv=0); //complex FFT with window, compact in-place +void CFFTCW(cdouble* Input, double* Window, double *Amp, double *Arg, int Order, cdouble* W, cdouble* X, int* bitinv=0); //complex FFT with window +void CIFFTC(int Order, cdouble* W, cdouble* X, int* bitinv=0); //complex IFFT, compact in-place +void CIFFTC(cdouble* Input, int Order, cdouble* W, cdouble* X, int* bitinv=0); //complex IFFT + +//--real FFT and IFFT-------------------------------------------------------- +void RFFTC_ual(double* Input, double* Amp, double* Arg, int Order, cdouble* W, cdouble* X, int* hbitinv=0); //real-to-complex FFT +void RFFTC_ual_old(double* Input, double *Amp, double *Arg, int Order, cdouble* W, double* XR, double* XI, int* bitinv=0); //depreciated +void RFFTCW(double* Input, double* Window, double *Amp, double *Arg, int Order, cdouble* W, cdouble* X, int* hbitinv=0); //real-to-complex FFT with window, double-type input +void RFFTCW(__int16* Input, double* Window, double *Amp, double *Arg, int Order, cdouble* W, cdouble* X, int* hbitinv=0); //real-to-complex FFT with window, __int16 input +void CIFFTR(cdouble* Input, int Order, cdouble* W, double* X, int* hbitinv=0); //complex-to-real IFFT + +/* + macro RFFTC: was defined as RFFTC_ual (literally "unaligned RFFTC") enveloped by ALIGN8(). However, as + ALIGN8() is currently disabled for lack of compiler support, RFFTC is equivalent to RFFTC_ual. +*/ +#define RFFTC(Input, Amp, Arg, Order, W, X, hbitinv) RFFTC_ual(Input, Amp, Arg, Order, W, X, hbitinv); + +//--MFT---------------------------------------------------------------------- +void CMFTC(double* xR, double* xI, int Order, cdouble** X, cdouble* W); //complex fast multiresolution FT + +//--DCT and IDCT------------------------------------------------------------- +void RDCT1(double* Input, double* Output, int Order, cdouble* qW, cdouble* qX, int* qbitinv=0); //DCT1 +void RDCT4(double* Input, double* Output, int Order, cdouble* hW, cdouble* hX, int* hbitinv=0); //DCT4 +void RIDCT1(double* Input, double* Output, int Order, cdouble* qW, cdouble* qX, int* qbitinv=0); //IDCT1 +void RIDCT4(double* Input, double* Output, int Order, cdouble* hW, cdouble* hX, int* hbitinv=0); //IDCT4 + +//--LCT---------------------------------------------------------------------- +void RLCT(double** spec, double* data, int Count, int Order, int wid, double* g); //local cosine transform +void RILCT(double* data, double** spec, int Fr, int Order, int wid, double* g); //inverse local cosine transform + +//--tools-------------------------------------------------------------------- +double Atan2(double, double); +int* CreateBitInvTable(int Order); //creates table of bit-inversed integers +void SetTwiddleFactors(int N, cdouble* w); //set twiddle factors + + +#endif diff -r 9b9f21935f24 -r 6422640a802f hs.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hs.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,4798 @@ +//--------------------------------------------------------------------------- +#include +#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 0N, 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[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; mPartials[m], sizeof(atom)*Fr); + Allocate2(double, M, st_count, startamp); + if (st_count) for (int m=0; mstartamp[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].tPartials[0][fren-1].t>end && fren>frst) fren--; + Fr=fren-frst; + Allocate2(atom, M, Fr, Partials); + for (int m=0; mPartials[m][frst], sizeof(atom)*Fr); + for (int fr=0; fr0){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; mPosition-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 || MWrite((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; mSeek(-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 (capN) 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; i0) + { + int t1, t2, i=0; + while (i0) + { + if (Mpart[i].t) t1=part[i].t; + if (t20) + { + 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; n0)?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 (n0) //points 0, 1, ..., n-1 satisfies the constraint, point n does not + { + ns=n; + n++; + while (n0) 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 (n0) 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 (n0, 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 //xen-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; js, tmp, newpcs); + int k=0; + for (int j=0; js>=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; js, tmp, newpcs); + int k=0; + for (int j=0; js>=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; ivi) Bmin=vi; + else if (Bmaxvi) vmin=vi; + else if (vmaxN; + 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 (ftmpslope) + { + R->N=4; + testnn(F[1]); f=sqrt(F[1]); ftmp=f*mdelf1; if (ftmpN=3; + } + else + { + R->N=4; + testnn(F[1]); f=sqrt(F[1]); ftmp=f*mdelf1; if (ftmpN=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; n1e-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 (ftmpN; + while (N>0 && F[N-1]==0) N--; + R->N=N; + int n=1; + while (nN=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* 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* 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(PSpec(fr1); + for (int i=0; i<=wid/2; i++) x[i]=spec[i]; + double ls=0; + for (int m=0; m=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; m0.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=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; m0.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; frSpec(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; mfrst)?1:(-1); + for (int h=frst+step; (h-fren)*step<0; h+=step) + { + cmplx* 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 (startsfren) 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* 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 (fraSpec(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; if0a/pow2delp-delm) + { + double s=sca[i]+scb[j]+conta(M, vfpa[i], vfpb[j]); + if (maxsfrst; 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; frX[0]), f0s[fren]=sqrt(Ren->X[0]); + + //partial tracking + int lastp=0; while (lastp0) 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; hSpec(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; iN=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=f1[lp] && lf<=f2[lp]) fdep[lp]=0; + else if (lff2[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; iN, 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]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; i0) for (int i=0; i0) 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=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; frhB, 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; frhB, 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; frpin0asanchor)) for (int fr=0; fr1 + 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=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; fr0) 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; n0 + 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; ndm + 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 (dsCanvas->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; llvmax) lvmax=e, lmax=l; + } + mnl[n]=lmax, vmnl[n]=lvmax; + if (n==0) + { + vmax=lvmax, nc=n, l1=lmax; + } + else + { + if (lvmax...; (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 (d10) //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 (d10 && 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 (d11e-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; le && lE[i]=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=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=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 //e1e2 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]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; lmaxlmd) continue; //the solution is in the wrong direction + if (lmd[i]e) {lmax=false; break;} + } + if (!lmax) + { + for (int tp=0; tpmaxlmd) 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; nfabs(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; lmaxlmd) 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; tpe) {lmax=false; break;} + } + if (!lmax) + { + for (int tp=0; tpmaxlmd) 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; l1) 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; tpe) {lmax=false; break;} + } + if (!lmax) + { + for (int tp=0; tp1) 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; it=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; i0 && 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=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; fr0 && fps[pm]>=pinf[i]) pm--; + if (pmfps[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~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 && lfs; + 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 (pm0 && fps[pm]>=f0) pm--; + if (pmfps[pm+1]-f0) pm++; + if (fps[pm]>f1 && fps[pm]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 (pm0 && fps[pM]>f0+epf0) pM--; + //add peaks as candidates + for (int j=pm; j<=pM; j++) if (fps[j]>f1 && fps[j]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; iR->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 (pm0 && 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]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; is) 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; j1. 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; i0 && 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=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; frrsr=lrsr; + } + else + { + //find the nearest peak to pinf[i] + while (pm0 && fps[pm]>=pinf[i]) pm--; + if (pmfps[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~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 && lfs; + 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 (pm0 && fps[pm]>=f0) pm--; + if (pmfps[pm+1]-f0) pm++; + if (fps[pm]>f1 && fps[pm]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 (pm0 && fps[pM]>f0+epf0) pM--; + //add peaks as candidates + for (int j=pm; j<=pM; j++) if (fps[j]>f1 && fps[j]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; iR->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 (pm0 && 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]tightf1 && fps[j]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 (tightfs; + 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 (tightfs; + 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; is) 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; j1 + 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; iR->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 (pm0 && fps[pM]>f2) pM--; //an index range for peaks + for (int j=pm; j<=pM; j++) + { + if (fps[j]>f1 && fps[j]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; is) 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; i0) for (int i=0; iminf0, 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; iN, R[i]->X, R[i]->Y); f1[i]-=delm, f2[i]+=delm;} + double f1a=f1[0], f2a=f2[0]; for (int i=1; if1[i]) f1a=f1[i]; if (f2a=0) continue; //skip this peak if it is already ... + int max; double maxs; TPolygon* lnewR=0; + + for (int i=0; if1[i] && fps[p]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; iN/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 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; frN/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 (n0 && 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 (binsthN-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=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])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) 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; fr0) for (int i=0; iM, HS->Fr, HS->Partials, dst, den, HS->startamp, HS->st_start, HS->st_offst, HS->st_count); +}//SynthesisHSp + diff -r 9b9f21935f24 -r 6422640a802f hs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hs.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,273 @@ +#ifndef hsH +#define hsH + +/* + hs.cpp - harmonic sinusoid model + + Further reading: Wen X. and M. Sandler, "Sinusoid modeling in a harmonic context," in Proc. DAFx'07, Bordeaux, 2007. +*/ + +//--------------------------------------------------------------------------- +#include +#include "arrayalloc.h" +#include "align8.h" +#include "fft.h" +#include "QuickSpec.h" +#include "TStream.h" + +#define ATOM_LOCALANCHOR 1 +#define HS_CONSTF 1 +#define MAX_CAND 64 +#define STIFF_B_MAX 0.01 + +enum atomtype //type flags of an HS atom +{ + atAnchor, //"anchor" is an atom whose validity is taken for granted, e.g. user input + atPeak, //an atom whose frequency is an actual spectral peak + atInfered, //an atom which has no spectral peak and whose frequency is inferred from other atoms + atMuted, //an atom which is being muted + atBuried //an atom which is buried in background noise +}; + +struct atom //an atom of a harmonic sinusoid +{ + double t; //time + double s; //scale + double f; //digital frequency + double a; //amplitude + double p; //phase angle + int pin; //partial index + atomtype type; //atom type + int tags; //additional info on atom type + float r1; +}; + +struct candid //candidate atom +{ + int p; //partial index + double f; //frequency + double df; //delta freqdel folk + double s; //score in partial-sum + int prev; //index of last partial f-df + int type; //0: f0, 1: local maximum, 2: not a maxixum + double ms; +}; + +struct dsparams1 //argument structure for calling ds1() +{ + double s; //score + double lastene; //energy of last frame + double currentacce; //energy of this frame, currently accumulated + int p; //partial index + int lastP; //number of efficient partials in last frame + double* lastvfp; //amplitude estimates of partials at last frame +}; + +struct NMResults //note match output structure +{ + double* fp; //frequencies, in bins + double* vfp; //amplitudes + double* pfp; //phase angles + atomtype* ptype; //atom types +}; + +struct NMSettings //note match algorithm settings +{ + double c[4]; //cosine-family window specifiers + int M; //ditto + double iH2; //ditto + double hB; //spectral truncation half width + int maxp; //maximal number of partials + double maxB; //stiffness coefficient upper bound + double epf; //frequency estimation error tolerance for LSE estimation + double epf0; //input frequency error bound for harmonic grouping + double delm; //frequency error bound for harmonic grouping + double delp; //pitch jump upper bound + double minf0; //minimal fundamental frequency + double maxf0; //maximal fundamental frequency + int pin0; //input partial index + bool forcepin0; //force the peak nearest to input frequency as an atom + bool pin0asanchor; //mark atom found near the input frequency as anchor + int pcount; //number of "pinned" (anchor) partials + int* pin; //partial indices of pinned partials + double* pinf; //frequencies of pinned partials + int* pinfr; //frame indices of pinned partials, used in multi-frame constant-pitch note match +}; + + +/* + stiffcandid is the harmonic atom class used internally by harmonic grouping and tracking routines. Literally + it means "candidate harmonic atoms on a stiff string model". The stiff string is the main harmonic model used + by me for describing frequency relations between partials of a harmonic sound. + + stiffcandid is superceded by TTempAtom class. +*/ + +class stiffcandid +{ +public: + int P; //number of partial estimates located + int* p; //partial indices of located partials, sizeof(int)*P + double* f; //frequencies of located partials, sizeof(double)*P + double s; //score in partial-sum + //{N; F, G}: the feasible polygonal area of (F, G) given the P partials, where F=F0*F0, G=F0*B + int N; + double* F; //sizeof(double)*N + double* G; //sizeof(double)*N + + stiffcandid(int Wid); //create empty with minimal info + stiffcandid(int Wid, int ap, double af, double errf); //create empty with pitch range + stiffcandid(int Wid, int ap, double af, double errf, double ds); //create with 1 atom + stiffcandid(stiffcandid* prev, int ap, double af, double errf, double ds); //create by updating with new atom + ~stiffcandid(); +}; + + +/* + THS is the data structure hosting a harmonic sinusoid representation. Its key members include the number + of partials, number of frames, and all its atoms (sinusoid parameters of all partials at measurement points). +*/ + +class THS +{ +public: + int Channel; //channel id: THS describes a harmonic sinusoid in single channel + int M; //number of partials + int Fr; //number of frames + atom** Partials; //atoms, [M][Fr] + + int* BufM[128]; //a buffer for algorithmic use + + int isconstf; //constant frequencies flag + + double** startamp;//onset amplifiers, optional + int st_start; //position of the first onset amplifying point + int st_offst; //interval of onset amplifying points + int st_count; //number of onset amplifying points + + //constructors and destructor + THS(); + THS(int aM, int aFr); + THS(THS* HS); + THS(THS* HS, double start, double end); + ~THS(); + + int StartPos(); //start position + int EndPos(); //end position + int EndPosEx(); //extended end position + int StdOffst(); //hop size + + void ClearBufM(); //free buffers registered in BufM[] + void Resize(int aM, int aFr, bool copydata=false, bool forcealloc=false); //change size (M or Fr) + + //I/O routines + int WriteHdrToStream(TStream* Stream); + int ReadHdrFromStream(TStream* Stream); + int WriteAtomToStream(TStream* Stream, atom* atm); + int ReadAtomFromStream(TStream* Stream, atom* atm); + int WriteToStream(TStream* Stream); + int ReadFromStream(TStream* Stream); +}; + + +/* + TPolygon is a polygon class. This class itself does not enforce convexness. However, when used for solving + the stiff string model, all polygons are convex, as they are the results of current a first convex polygon + by straight lines. + + For the convenience of computation, the sequence of vertices are arranged so that they come in clockwise + order starting from the leftmost (smallest x coordinate) vertex; in case two vertices are both leftmost + the upper (larger y coordinate) one is placed at the start and the lower one is placed at the end. +*/ + +class TPolygon +{ +public: + int N; //number of vertices (equals number of edges) + double* X; //x-coordinates of vertices + double* Y; //y-coordinates of vertices + TPolygon(int cap); + TPolygon(int cap, TPolygon* R); + ~TPolygon(); +}; + + +/* + TTempAtom is an atom class within the stiff-stirng harmonic atom context used internally by harmonic + grouping and tracking routines. Literally it means "a temporary atom", and truly it hosts most info + one expects of a potential atom. TTempAtom replaces stiffcandid class in harmonic sinusoid group and + tracking algorithms, due to the advanges that it carries atom information (frequency, amplitude, etc.) + as well as harmonic atom information (link to other atom, the F-G polygon), and is therefore both an + individual atom and a harmonic atom containing all atoms tracked down through the linked list Prev. +*/ + +class TTempAtom +{ +public: + int pind; //partial index + double f; //frequency + double a; //amplitude + double s; //scale + double rsr; //residue-sinusoid ratio + TTempAtom* Prev; //previous atom + TPolygon *R; //F-G polygon for harmonic group + union {double acce; int tag[2];}; + + TTempAtom(double af, double ef, double maxB); //create empty with frequency range + TTempAtom(int apin, double af, double ef, double maxB); //create empty with frequency range + TTempAtom(TPolygon* AR, double delf1, double delf2, double minf); //create empty with extended R + TTempAtom(int apind, double af, double ef, double aa, double as, double maxB); //create with one partial + TTempAtom(TTempAtom* APrev, bool DupR); //create duplicate + TTempAtom(TTempAtom* APrev, int apind, double af, double ef, double aa, double as, bool updateR=true); //duplicate and add one partial + ~TTempAtom(); +}; + +//--internal function-------------------------------------------------------- +double ds0(double, void*); //a score function, internal use only + +//--general polygon routines------------------------------------------------- +void areaandcentroid(double& A, double& cx, double& cy, int N, double* x, double* y); //compute area and centroid +void cutcvpoly(int& N, double* x, double* y, double A, double B, double C, bool protect=false); //sever polygon by line +double maximalminimum(double& x, double& y, int N, double* sx, double* sy); //maximum inscribed circle + +//--F-G polygon routines----------------------------------------------------- +void CutR(TPolygon* R, int apind, double af, double ef, bool protect=false); +void ExBStiff(double& Bmin, double& Bmax, int N, double* F, double* G); +void ExFmStiff(double& Fmin, double& Fmax, int m, int N, double* F, double* G); +void ExtendR(TPolygon* R, double delf1, double delf2, double minf); +void InitializeR(TPolygon* R, double af, double ef, double maxB); +void InitializeR(TPolygon* R, int apind, double af, double ef, double maxB); + +//--internal structure conversion routines----------------------------------- +int NMResultToAtoms(int M, atom* HP, int t, int wid, NMResults results); +int NMResultToPartials(int M, int fr, atom** Partials, int t, int wid, NMResults results); + +//--batch sinusoid estimation routines--------------------------------------- +double PeakShapeC(double f, int Fr, int N, cdouble** x, int B, int M, double* c, double iH2); +double PeakShapeC(double f, int Fr, int N, cfloat** x, int B, int M, double* c, double iH2); +int QuickPeaks(double* f, double* a, int N, cdouble* x, int M, double* c, double iH2, double mina, int binst=-1, int binen=-1, int B=5, double* rsr=0); +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=-1, int binen=-1, int B=5, double* rsr=0); + +//--harmonic atom detection (harmonic grouping) routines--------------------- +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)=ds0, int forceinputlocalfr=-1); //basic grouping without rsr +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)=ds0, int forceinputlocalfr=-1); //basic grouping with rsr +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 (*deltas)(double a, void* params)=ds0, bool forceinputlocalfr=false, int startfr=-1, int validfrrange=0); //basic grouping with rsr - wrapper +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); //grouping with given pitch, single-frame + +//--harmonic sinusoid tracking routines-------------------------------------- +int FindNote(int _t, double _f, int& M, int& Fr, atom**& partials, int frst, int fren, int wid, int offst, TQuickSpectrogram* Spec, NMSettings settings); //harmonic sinusoid tracking (forward and backward tracking) +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); //constant-pitch harmonic sinusoid tracking +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); //forward harmonic sinusoid tracking +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); //forward-backward harmonic sinusoid tracking +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); //single DP step of FindNoteFB() + +//--harmonic sinusoid synthesis routines------------------------------------- +double* SynthesisHS(int pm, int pfr, atom** partials, int& dst, int& den, bool* terminatetag=0); +double* SynthesisHS2(int M, int Fr, atom** partials, int& dst, int& den, bool* terminatetag=0); +double* SynthesisHSp(int pm, int pfr, atom** partials, int& dst, int& den, double** startamp=0, int st_start=0, int st_offst=0, int st_count=0); +double* SynthesisHSp(THS* HS, int& dst, int& den); + +//--other functions---------------------------------------------------------- +void ReEstHS1(THS* HS, __int16* Data16); //reestimation of HS + +#endif diff -r 9b9f21935f24 -r 6422640a802f hsedit.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hsedit.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,190 @@ +//--------------------------------------------------------------------------- + +#include "hsedit.h" +#include "splines.h" + +//--------------------------------------------------------------------------- + +/* + function DeFM: frequency de-modulation + + In: peakfr[npfr]: segmentation into FM cycles, peakfr[0]=0, peakfr[npfr-1]=Fr-1 + a1[Fr], f1[Fr]: sequence of amplitudes and frequencies + arec[Fr]: amplitude-based weights for frequency averaging + Out: a2[Fr], f2[Fr]: amplitude and frequency sequance after demodulation + + No return value. +*/ +void DeFM(double* a2, double* f2, double* a1, double* f1, double* arec, int npfr, int* peakfr) +{ + double *frs=new double[npfr*12], *a=&frs[npfr], *f=&frs[npfr*2], + *aa=&frs[npfr*3], *ab=&frs[npfr*4], *ac=&frs[npfr*5], *ad=&frs[npfr*6], + *fa=&frs[npfr*7], *fb=&frs[npfr*8], *fc=&frs[npfr*9], *fd=&frs[npfr*10]; + a[0]=a1[0], f[0]=f1[0], frs[0]=peakfr[0]; + for (int i=1; ifrec[fr-1] && frec[fr]>frec[fr+1])) + { + peakfr[npfr]=fr; + if (peakfr[npfr]-peakfr[npfr-1]>2) npfr++; + } + } + peakfr[npfr++]=Fr-1; + delete[] frec; +}//DFMSeg + +/* + function HSAM: harmonic sinusoid amplitude modulation + + In: SrcHS: source harmonic sinusoid + dep: modulation depth + fre: modulator frequency + ph: modulator phase + Out: HS: destination harmonic sinusoid + + No reutrn value. +*/ +void HSAM(THS* HS, THS* SrcHS, double dep, double fre, double ph) +{ + double omg=M_PI*2*fre; + for (int m=0; mM; m++) + for (int fr=0; frFr; fr++) + HS->Partials[m][fr].a=SrcHS->Partials[m][fr].a*(1+dep*cos(omg*SrcHS->Partials[m][fr].t+ph)); +}//HSAM + +/* + function HSFM: harmonic sinusoid frequency modulation + + In: SrcHS: source harmonic sinusoid + a: modulation extent, in semitones + fre: modulator frequency + ph: modulator phase + Out: HS: destination harmonic sinusoid + + No reutrn value. +*/ +void HSFM(THS* HS, THS* SrcHS, double a, double freq, double ph) +{ + double omg=M_PI*2*freq, pa=pow(2, a/12.0)-1; + for (int m=0; mM; m++) + for (int fr=0; frFr; fr++) + HS->Partials[m][fr].f=SrcHS->Partials[m][fr].f*(1+pa*cos(omg*SrcHS->Partials[m][fr].t+ph)); +}//HSFM + +/* + function HSFM_SF: harmonic sinusoid frequency modulation with source-filter model + + In: SrcHS: source harmonic sinusoid + a: modulation extent, in semitones + fre: modulator frequency + ph: modulator phase + SF: source-filter model + Out: HS: destination harmonic sinusoid + + No reutrn value. +*/ +void HSFM_SF(THS* HS, THS* SrcHS, double a, double freq, double ph, TSF* SF) +{ + double omg=M_PI*2*freq, pa=pow(2, a/12.0)-1; + for (int m=0; mM; m++) for (int fr=0; frFr; fr++) + { + double f0=SrcHS->Partials[m][fr].f; + double f1=f0*(1+pa*cos(omg*SrcHS->Partials[m][fr].t+ph)); + HS->Partials[m][fr].f=f1; + HS->Partials[m][fr].a=SrcHS->Partials[m][fr].a*exp(SF->LogAF(f1)-SF->LogAF(f0)); + } +}//HSFM_SF + +/* + function: HSPitchShift: harmonic sinusoid pitch shifting + + In: SrcHS: source harmonic sinusoid + ps12: amount of pitch shift, in semitones + Out: HS: destination harmonic sinusoid + + No return value. +*/ +void HSPitchShift(THS* HS, THS* SrcHS, double ps12) +{ + double pa=pow(2, ps12/12.0); + for (int m=0; mM; m++) for (int fr=0; frFr; fr++) HS->Partials[m][fr].f=SrcHS->Partials[m][fr].f*pa; +}//HSPitchShift + +/* + function ReFM: frequency re-modulation + + In: partials[M][Fr]: HS partials + amount: relative modulation depth after remodulation + rate: relateive modulation rate after remodulation + SF: a source-filter model, optional + Out: partials2[M][Fr]: remodulated HS partials. Must be allocated before calling. + + No return value. +*/ +void ReFM(int M, int Fr, atom** partials, atom** partials2, double amount, double rate, TSF* SF) +{ + double *arec=new double[Fr]; int *peakfr=new int[Fr], npfr; + DFMSeg(arec, npfr, peakfr, M, Fr, partials); + + double *a1=new double[Fr*8]; + double *f1=&a1[Fr], *a2=&a1[Fr*3], *f2=&a1[Fr*4], *da=&a1[Fr*5], *df=&a1[Fr*6]; + + for (int m=0; mLogAF(part2[fr].f)-SF->LogAF(part[fr].f)); + else part2[fr].a=(a2[fr]+lda*amount)*0.5; + } + } + delete[] a1; + delete[] arec; delete[] peakfr; +}//ReFM + + + + diff -r 9b9f21935f24 -r 6422640a802f hsedit.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hsedit.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,23 @@ +#ifndef hseditH +#define hseditH + + +/* + hsedit.cpp - harmonic sinusoid audio editing routines. +*/ + +#include "hs.h" +#include "hssf.h" + +//--tool procedures---------------------------------------------------------- +void DeFM(double* a2, double* f2, double* a1, double* f1, double* arec, int npfr, int* peakfr); +void DFMSeg(double* arec, int& npfr, int* peakfr, int M, int Fr, atom** partials); +void ReFM(int M, int Fr, atom** partials, atom** partials2, double amount=1, double rate=1, TSF* SF=0); + +//--HS editing sample routines----------------------------------------------- +void HSAM(THS* HS, THS* SrcHS, double dep, double fre, double ph); +void HSFM(THS* HS, THS* SrcHS, double a, double freq, double ph); +void HSFM_SF(THS* HS, THS* SrcHS, double a, double freq, double ph, TSF* SF); +void HSPitchShift(THS* HS, THS* SrcHS, double ps12); + +#endif diff -r 9b9f21935f24 -r 6422640a802f hssf.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hssf.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,1156 @@ +//--------------------------------------------------------------------------- + + +#include +#include +#include "hssf.h" +#include "arrayalloc.h" +#include "Matrix.h" +#include "vibrato.h" + +//--------------------------------------------------------------------------- +//method TSF::TSF: default constructor. +TSF::TSF() +{ + memset(this, 0, sizeof(TSF)); +}//TSF + +//method TSF::~TSF: default destructor. +TSF::~TSF() +{ + if (h) DeAlloc2(h); + if (b) DeAlloc2(b); + delete[] lp; + delete[] F0; + free(avgh); + free(avgb); +}//~TSF + +/* + method TSF::AllocateL: allocates or reallocates storage space whose size depends on L. This uses an + external L value for allocation and updates L itself. +*/ +void TSF::AllocateL(int AnL) +{ + L=AnL; + delete[] F0; F0=new double[(L+2)*6]; + F0C=&F0[L+2], F0D=&F0[(L+2)*2], logA0C=&F0[(L+2)*3], logA0=&F0[(L+2)*4], logA0D=&F0[(L+2)*5]; +}//AllocateL + +/* + method TSF::AllocateP: allocates or reallocates storage space whose size depends on P, using the + interal P. +*/ +void TSF::AllocateP() +{ + delete[] lp; + lp=new double[P+2]; +}//AllocateP + +/* + method TSF::AllocateSF: allocates or reallocates storage space for source-filter coefficients. +*/ +void TSF::AllocateSF() +{ + if (h) DeAlloc2(h); int k=1.0/F; Allocate2(double, L, (k+2), h); memset(h[0], 0, sizeof(double)*(L)*(k+2)); + if (b) DeAlloc2(b); Allocate2(double, L, M, b); memset(b[0], 0, sizeof(double)*(L)*M); + avgh=(double*)realloc(avgh, sizeof(double)*(k+2)); avgb=(double*)realloc(avgb, sizeof(double)*M); +}//AllocateSF + +/* + method TSF::Duplicate: copies the complete contents from another SF object + + In: SF: source object +*/ +void TSF::Duplicate(TSF& SF) +{ + memcpy(this, &SF, sizeof(TSF)); lp=F0=avgh=avgb=0, h=b=0; + AllocateL(L); memcpy(F0, SF.F0, sizeof(double)*(L+2)*6); + AllocateP(); memcpy(lp, SF.lp, sizeof(double)*(P+2)); + AllocateSF(); int SFL=ceil(lp[P-1])-ceil(lp[0]); + for (int l=0; l=lpp-lp0) l=lpp-lp0-1; + if (FScaleMode) f=log(1+f*Fs/700)/log(1+Fs/700); + int k=floor(f/F); + double f_plus=f/F-k; + if (k=lpp-lp0) l=lpp-lp0-1; + return b[l][m]; +}//LogAS + +/* + method TSF::ReAllocateL: reallocates storage space whose size depends on L, and transfer the contents. + This uses an external L value for allocation but does not update L itself. +*/ +void TSF::ReAllocateL(int newL) +{ + double* newF0=new double[(newL+2)*4]; F0C=&newF0[newL+2], F0D=&newF0[(newL+2)*2], logA0C=&newF0[(newL+2)*3], logA0=&newF0[(L+2)*4], logA0D=&F0[(L+2)*5]; + memcpy(logA0C, &F0[(L+2)*3], sizeof(double)*(L+2)); + memcpy(logA0D, &F0[(L+2)*5], sizeof(double)*(L+2)); + memcpy(F0D, &F0[(L+2)*2], sizeof(double)*(L+2)); + memcpy(F0C, &F0[L+2], sizeof(double)*(L+2)); + memcpy(newF0, F0, sizeof(double)*(L+2)); + delete[] F0; + F0=newF0; +}//ReAllocateL + +/* + method TSF::SaveSFToFileHandle: writes SF coefficients to file +*/ +void TSF::SaveSFToFileHandle(FILE* f) +{ + fwrite(&K, sizeof(int), 1, f); + fwrite(&FScaleMode, sizeof(int), 1, f); + fwrite(&F, sizeof(double), 1, f); + fwrite(&Fs, sizeof(double), 1, f); + for (int l=0; lSF.F0[fr]) SF.F0min=SF.F0[fr]; + A0[fr]=suma2; + } + + //Basic descriptor: M + SF.M=0.45/SF.F0max; + if (SF.M>M) SF.M=M; + + //rough estimation of rate + double* f0d=new double[Fr-1]; for (int i=0; i=1) //de-modulation + { + //Basic descriptor: F0C[] + DeFM(SF.F0C, SF.F0, SF.logA0, SF.P, SF.lp, Fr, SF.F0Overall, *cyclefrcount, cyclefrs, cyclefs); + //Basic descriptor: A0C[] + double *A0C=SF.logA0C, *logA0C=SF.logA0C, *logA0D=SF.logA0D, *logA0=SF.logA0; + DeAM(A0C, SF.logA0, SF.P, SF.lp, Fr); + for (int fr=0; frSF.F0C[fr]) SF.F0Cmin=SF.F0C[fr]; + SF.F0D[fr]=SF.F0[fr]-SF.F0C[fr]; + } + + //Basic descriptor: b, h + //Source-filter modeling + { + SF.F=SFF; SF.Fs=sps; SF.AllocateSF(); + int M=SF.M; double **h=SF.h, **b=SF.b;// *avgh=SF.avgh, *avgb=SF.avgb; + + double *logA0C=useA0?SF.logA0:SF.logA0C; + if (SFMode==0){ + S_F_FB(M, Partials, logA0C, SF.lp, SF.P, SF.K, h, SF.avgh, b, SF.avgb, SFF, SFFScale, sps);} + else if (SFMode==1){ + S_F_SV(M, Partials, logA0C, SF.lp, SF.P, SF.K, h, SF.avgh, b, SF.avgb, SFF, SFFScale, SFtheta, sps);} + + SF.FScaleMode=SFFScale; +// int K=SF.K, effL=ceil(SF.lp[SF.P-1])-ceil(SF.lp[0]); +// for (int k=0; k0) + { + double loga=log(Partials[m][fr].a); + loga-=loga0; + double ff=f/F; + int klm=floor(ff); + double f0=ff-klm; + double hlm=h[l][klm]*(1-f0)+h[l][klm+1]*f0; + b[l][m]=loga-hlm; + } + else b[l][m]=-100; + } + } + // for (int k=0; k0) a[l][m]=Partials[m][fr].a*ia0c; //log(Partials[m][fr].a); + else a[l][m]=0; + if (fmax0) loga[l][m]=log(Partials[m][fr].a); + else loga[l][m]=-100; + if (fmax0) loga[l][m]=log(Partials[m][fr].a); + else loga[l][m]=-100; + if (fmax0) memcpy(h[l], h[0], sizeof(double)*(K+2)); + double loga0=log(A0C[l]); + for (int m=0; m0) LogAS[m]/=ccount; + else LogAS[m]=0; + } + + DeAlloc2(loga); DeAlloc2(f); + return 0; +}//S_F_SV_cf + +/* + function SF_FB: computes filter coefficients with filter-bank method: single frame. See "further + reading", pp.12, P5. + + In: al[][M], fl[][M]: partial amplitudes and frequencies, al[0] and fl[0] are of current frame + F: filter response sampling interval + LMode: 1: current frame is the first frame; -1: the last frame; 0: neither first nor last frame + Out: hl[K+2]: filter coefficients integrated at current frame + + Returns K. +*/ +int SF_FB(double* hl, int M, int K, double** al, double** fl, double F, int LMode) +{ + memset(hl, 0, sizeof(double)*(K+2)); + double* norm=new double[K+2]; memset(norm, 0, sizeof(double)*(K+2)); + for (int m=0; m0) K1=ceil(fl[-1][m]/F); else K1=floor(fl[-1][m]/F); + t0=-1; k=K1; + if (fl[-1][m]>0 && fl[0][m]>0) do + { + t1=-1+(k*F-fl[-1][m])/(fl[0][m]-fl[-1][m]); + if (t1>0) t1=0; + + if (t0==-1){a0=al[-1][m], u0=0, w0k=1-fabs(fl[-1][m]/F-k); w0k_=1-w0k;} + else{a0=al[0][m]+t0*(al[0][m]-al[-1][m]), u0=1+t0, w0k=0, w0k_=1;} + if (t1==0){a1=al[0][m], u1=1, w1k=1-fabs(fl[0][m]/F-k); w1k_=1-w1k;} + else{a1=al[0][m]+t1*(al[0][m]-al[-1][m]), u1=1+t1, w1k=1, w1k_=0;} + + hl[k]+=I3(t1-t0, w0k, u0, a0, w1k, u1, a1); + hl[k-dfsgn]+=I3(t1-t0, w0k_, u0, a0, w1k_, u1, a1); + norm[k]+=I3(t1-t0, w0k, u0, 1, w1k, u1, 1); + norm[k-dfsgn]+=I3(t1-t0, w0k_, u0, 1, w1k_, u1, 1); + + t0=t1; + k+=dfsgn; + } + while (t1<0); + } + } + + if (LMode!=-1) + { + double a0, wk, wk_, a1; + if (fl[1][m]-fl[0][m]==0) + { + int k=floor(fl[0][m]/F); + a0=al[0][m], a1=al[1][m]; + wk=1-fabs(fl[0][m]/F-k), wk_=1-wk; + double dh=(2*a0+a1)/6.0; + hl[k]+=wk*dh; //I3(1, wk, 1, a0, wk, 0, a1); + hl[k+1]+=wk_*dh;//I3(1, wk_, 1, a0, wk_, 0, a1); + norm[k]+=wk*0.5;//I3(1, wk, 1, 1, wk, 0, 1); + norm[k+1]+=wk_*0.5;//I3(1, wk_, 1, 1, wk_, 0, 1); + } + else + { + double t0, t1, a0, u0, w0k, w0k_, a1, u1, w1k, w1k_; + dfsgn=Sign(fl[1][m]-fl[0][m]); + if (dfsgn>0) K1=ceil(fl[0][m]/F); else K1=floor(fl[0][m]/F); + t0=0; k=K1; + if (fl[0][m]>0 && fl[1][m]>0) do + { + try{ + t1=(k*F-fl[0][m])/(fl[1][m]-fl[0][m]);} + catch(...){} + if (t1>1) t1=1; + + if (t0==0){a0=al[0][m], u0=1, w0k=1-fabs(fl[0][m]/F-k); w0k_=1-w0k;} + else{a0=al[0][m]+t0*(al[1][m]-al[0][m]), u0=1-t0, w0k=1, w0k_=0;} + if (t1==1){a1=al[1][m], u1=0, w1k=1-fabs(fl[1][m]/F-k); w1k_=1-w1k;} + else{a1=al[0][m]+t1*(al[1][m]-al[0][m]), u1=1-t1, w1k=0, w1k_=1;} + + hl[k]+=I3(t1-t0, w0k, u0, a0, w1k, u1, a1); + hl[k-dfsgn]+=I3(t1-t0, w0k_, u0, a0, w1k_, u1, a1); + norm[k]+=I3(t1-t0, w0k, u0, 1, w1k, u1, 1); + norm[k-dfsgn]+=I3(t1-t0, w0k_, u0, 1, w1k_, u1, 1); + + t0=t1; + k+=dfsgn; + } + while (t1<1); + } + } + } + + int mink=0; while (mink<=K+1 && norm[mink]==0) mink++; + int maxk=K+1; while (maxk>=0 && norm[maxk]==0) maxk--; + int lastk=mink, nextk=-1; + for (int k=mink; k<=maxk; k++) + { + if (k==nextk) lastk=k; + else if (norm[k]!=0) hl[k]=log(hl[k]/norm[k]), lastk=k; + else + { + if (k>nextk){nextk=k+1; while (norm[nextk]==0) nextk++; hl[nextk]=log(hl[nextk]/norm[nextk]);} + hl[k]=(hl[lastk]*(nextk-k)+hl[nextk]*(k-lastk))/(nextk-lastk); + } + } + for (int k=0; kep) + { + P_Ax(q, L, M, K, d, f, F, theta); + /*debug point: watch if hAh-bh keeps decreasing + double lastdel=Del; + hAh=P_Ax(t_Ax, L, M, K, h, f, F, theta); + bh=Inner(L, K+2, b, h); + Del=hAh-bh; + if (Del>lastdel) + {lastdel=Del;} //set breakpoint here*/ + double alpha=delta/Inner(L, K+2, d, q); + MultiAdd(L, K+2, h, h, d, alpha); + if (iter%50==0 && false){P_Ax(Ax, L, M, K, h, f, F, theta); MultiAdd(L, K+2, r, b, Ax, -1);} + else MultiAdd(L, K+2, r, r, q, -alpha); + double deltanew=Inner(L, K+2, r, r); + MultiAdd(L, K+2, d, r, d, deltanew/delta); + delta=deltanew; + iter++; + } + + DeAlloc2(b); + return K; +}//SF_SV + +/* + function SF_SV_cf: CG method for estimation source-(constant)filter model by slow-variation criterion. + + In: a[L][M], f[L][M]: partial amplitudes and frequencies + F: filter response sampling interval + ep: convergence test threshold + maxiter: maximal number of iterates + Out: h[K+2]: filter coefficients + + Returns K. +*/ +int SF_SV_cf(double* h, int L, int M, int K, double** a, double** f, double F, double ep, int maxiter) +{ + double* b=new double[(K+2)*7]; + double *Ax=&b[K+2], *d=&Ax[K+2], *r=&d[K+2], *q=&r[K+2]; + + //calcualte b + P1_b_cf(b, L, M, K, a, f, F); + //calculate Ah + //double hAh= + P_Ax_cf(Ax, L, M, K, h, f, F); + + MultiAdd(K+2, r, b, Ax, -1); + Copy(K+2, d, r); + double delta=Inner(K+2, r, r); + ep=ep*delta; + + int iter=0; + while(iterep) + { + P_Ax_cf(q, L, M, K, d, f, F); + double alpha=delta/Inner(K+2, d, q); + MultiAdd(K+2, h, h, d, alpha); + if (iter%50==0 && false){P_Ax_cf(Ax, L, M, K, h, f, F); MultiAdd(K+2, r, b, Ax, -1);} + else MultiAdd(K+2, r, r, q, -alpha); + double deltanew=Inner(K+2, r, r); + MultiAdd(K+2, d, r, d, deltanew/delta); + delta=deltanew; + iter++; + } + + delete[] b; + return K; +}//SF_SV_cf + +/* + function SF_SV_cf: CG method for estimation source-(constant)filter model by slow-variation criterion. + + In: a[L][M], f[L][M]: partial amplitudes and frequencies + F: filter response sampling interval + ep: convergence test threshold + maxiter: maximal number of iterates + Out: h[K+2]: filter coefficients + b[L][M]: source coefficients + + No reutrn value. +*/ +void SF_SV_cf(double* h, double** b, int L, int M, int K, double** a, double** f, double F, double ep, int maxiter) +{ + memset(h, 0, sizeof(double)*(K+2)); + SF_SV_cf(h, L, M, K, a, f, F, ep, maxiter); + for (int l=0; l0, -1 if x<0. +*/ +int Sign(double x) +{ + if (x==0) return 0; + else if (x>0) return 1; + else return -1; +}//Sign + +/* + function SynthesizeSF: synthesizes a harmonic sinusoid representation from a TSF object. + + In: SF: a TSF object + sps: sampling rate ("samples per second") + Out: HS: teh synthesized HS. + + No return value. +*/ +void SynthesizeSF(THS* HS, TSF* SF, double sps) +{ + int t0=HS->Partials[0][0].t, s0=HS->Partials[0][0].s, L=SF->L, M=SF->M; + double *F0C=SF->F0C, *F0D=SF->F0D, *F0=SF->F0, offst=SF->offst; + double *logA0C=useA0?SF->logA0:SF->logA0C; + HS->Resize(M, L); + + atom** Partials=HS->Partials; + + for (int l=0; l0.5 || Partials[m][l].f<0) Partials[m][l].a=0; + else Partials[m][l].a=exp(logA0C[l]+SF->LogAS(m,l)+SF->LogAF(Partials[m][l].f,l)); + } + } +}//SynthesizeSF + + + + diff -r 9b9f21935f24 -r 6422640a802f hssf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hssf.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,118 @@ +#ifndef hssfH +#define hssfH + +/* + hssf.cpp - source-filter modeling for harmonic sinusoids + + Further reading: Wen X. and M. Sandler, "Source-filter modeling in sinusoid domain," in Proc. AES 126th + Convention, Munich, 2009. +*/ + + +#include +#include "hs.h" + +//--------------------------------------------------------------------------- +const double Amel=1127.0104803341574386544633680278; +const bool useA0=true; //if true, use A0D+A0C instead of A0C in S-F decomposition as pre-normalizer + + +/* + TSF is the class implementing source-filter model for harmonic sinusoids. TSF shares the basic framework + of the vibrato description class TVo, but implements a more compact source-filter representation. It does + not go into detailed vibrato analysis such as extraction modulator shape. + + An analysis/synthesis cycle converts THS to TSF and back. +*/ + +struct TSF +{ + //basic characteristics + int M; //number of partials + int L; //number of frames + int P; //number of segmentation points + double offst; //hop size + double* F0C; //[0:L-1] pitch carrier + double* F0D; //[0:L-1] pitch modulator + double* logA0C; //[0:L-1] amplitude carreir + double* logA0D; //[0:L-1] amplitude modulator + + double* lp; //[0:P-1] peak positions + + double F; //filter: band with (linear or mel) associated to each b[][] + double Fs; //sampling frequency + int FScaleMode; //linear or mel + int K; //number of filter bands + double** b; //[0:L-1][0:M-1] single-frame source, in dB + double** h; //[0:L-1][0:K+1] single-frame filter, in dB + double* avgb; //[0:M-1] average source + double* avgh; //[0:K+1] average filter + + //other properties + + double rate; //vibrato rate + double regularity;//vibrato regularity + double F0max; //maximal fundamental frequency + double F0min; //minimal fundamental frequency + double F0Cmax; //maximal fundamental carrier frequency + double F0Cmin; //minimal fundamental carrier frequency + double F0Overall; //overall average fundamental frequency + double F0Dmax; //maximal fundamental modulator frequency + double F0Dmin; //minimal fundamental modulator frequency + double* F0; //[0:L-1] fundamental frequency + double* logA0; //[0:L-1] log amplitude + + TSF(); + ~TSF(); + + //copy function + void Duplicate(TSF& SF); + + //output routines + double LogAF(double f); + double LogAF(double f, int fr); + double LogAS(int m, int fr); + + //memory handling routines + void AllocateL(int AnL); + void ReAllocateL(int newL); + void AllocateP(); + void AllocateSF(); + + //I/O routines + void SaveSFToFileHandle(FILE* f); + void SaveToFileHandle(FILE* f); + void LoadSFFromFileHandle(FILE* f); + void LoadFromFileHandle(FILE* f); + void SaveToFile(char* filename); + + //other member functions + void ShiftFilterByDB(double dB); +}; + +//--tool procedures---------------------------------------------------------- +int Sign(double); + +//--general source-filter model routines------------------------------------- +void S_F_b(TSF& SF, atom** Partials); + +//--slow-variation SF estimation routines------------------------------------ +double P2_DelFtr(double** d, int L, int K, double** x, double F); +double P3_DelSrc(double** d, int L, int M, int K, double** x, double** f, double F); +int SF_SV(double** h, int L, int M, int K, double** a, double** f, double F, double theta, double ep, int maxiter); +double S_F_SV(int M, atom** Partials, double* logA0C, double* lp, int P, int& K, double** h, double* avgh, double** b, double* avgb, double F=0.005, int FScaleMode=0, double theta=0.5, double Fs=44100); +void SF_SV_cf(double* h, double** b, int L, int M, int K, double** a, double** f, double F, double ep, int maxiter); +double S_F_SV_cf(int afres, double* LogAF, double* LogAS, int Fr, int M, atom** Partials, double* A0C, double* lp, int P, int& K, double** h, double** b, double F=0.005, int FScaleMode=0, double Fs=44100); + +//--filter-bank SF estimation routines--------------------------------------- +int SF_FB(double* hl, int M, int K, double** al, double** fl, double F, int LMode); +double S_F_FB(int M, atom** Partials, double* logA0C, double* lp, int P, int& K, double** h, double* avgh, double** b, double* avgb, double F=0.005, int FScaleMode=0, double Fs=44100); + +//--source-filter analysis and synthesis routines---------------------------- +void AnalyzeSF_1(THS& HS, TSF& SF, double sps, double offst); +void AnalyzeSF_2(THS& HS, TSF& SF, double*& cyclefrs, double*& cyclefs, double sps, int* cyclefrcount=0, int SFMode=0, double SFF=0.01, int SFFScale=0, double SFtheta=0); +void AnalyzeSF(THS& HS, TSF& SF, double*& cyclefrs, double*& cyclefs, double sps, double offst, int* cyclefrcount=0, int SFMode=0, double SFF=0.01, int SFFScale=0, double SFtheta=0); +void SynthesizeSF(THS* HS, TSF* SF, double sps); + + +#endif diff -r 9b9f21935f24 -r 6422640a802f multires.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/multires.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,849 @@ +//--------------------------------------------------------------------------- + + +#include +#include "multires.h" +#include "arrayalloc.h" +#include "procedures.h" + +//--------------------------------------------------------------------------- + +//function xlogx(x): returns x*log(x) +inline double xlogx(double x) +{ + if (x==0) return 0; + else return x*log(x); +}//xlogx + +//macro NORMAL_: a normalization step used for tiling +#define NORMAL_(A, a) A=a*(A+log(a)); +// #define NORMAL_(A, a) A=a*a*A; +// #define NORMAL_(A, a) A=sqrt(a)*A; + +/* + function DoCutSpectrogramSquare: find optimal tiling of a square. This is a recursive procedure. + + In: Specs[0][1][N], Specs[1][2][N/2], ..., Specs[log2(N)][N][1], multiresolution power spectrogram + e[Res]: total power of each level, e[i] equals the sum of Specs[i][][] + NN: maximal tile height + Out: cuts[N-1]: the tiling result + ents[Res] + + Returns the entropy of the output tiling. +*/ +double DoCutSpectrogramSquare(int* cuts, double*** Specs, double* e, int N, int NN, bool Norm, double* ents) +{ + double result; + int Res=log2(N)+1; + + if (N==1) // 1*1 only(no cuts), returns the sample function. + { + double sp00; + if (e[0]!=0) sp00=Specs[0][0][0]/e[0]; else sp00=0; + ents[0]=xlogx(sp00); + return ents[0]; + } + else if (N==2) + { + double sp00, sp01, sp10, sp11; + if (e[0]!=0) sp00=Specs[0][0][0]/e[0], sp01=Specs[0][0][1]/e[0]; else sp00=sp01=0; + if (e[1]!=0) sp10=Specs[1][0][0]/e[1], sp11=Specs[1][1][0]/e[1]; else sp10=sp11=0; + double ent0=xlogx(sp00)+xlogx(sp01); + double ent1=xlogx(sp10)+xlogx(sp11); + if (ent0left half, r->right half + if (N<=NN) + { + lcuts=&cuts[1], rcuts=&cuts[N/2]; + VSplitSpecs(N, Specs, lSpecs, rSpecs); + el=new double[Res-1], er=new double[Res-1]; + memset(el, 0, sizeof(double)*(Res-1)); memset(er, 0, sizeof(double)*(Res-1)); + if (Norm) + { + //normalization + for (int i=0, Fr=1, n=N/2; i0) {NORMAL_(entl1[i], a);} else entl1[i]=0; ent1=ent1+entl1[i]; + a=er[i]/e[i]; if (a>0) {NORMAL_(entr1[i], a);} else entr1[i]=0; ent1=ent1+entr1[i]; + } + else + entl1[i]=entr1[i]=0; + } + + DeAlloc2(lSpecs); DeAlloc2(rSpecs); + delete[] el; delete[] er; + + } + //horizontal cuts: l->lower half, r->upper half + lcuts=tmpcuts, rcuts=&tmpcuts[N/2-1]; + HSplitSpecs(N, Specs, lSpecs, rSpecs); + el=new double[Res-1], er=new double[Res-1]; + memset(el, 0, sizeof(double)*(Res-1)); memset(er, 0, sizeof(double)*(Res-1)); + if (Norm) + { + //normalization + for (int i=0, Fr=1, n=N/2; i0) {NORMAL_(entl0[i], a);} else entl0[i]=0; ent0=ent0+entl0[i]; + a=er[i]/e[i]; if (a>0) {NORMAL_(entr0[i], a);} else entr0[i]=0; ent0=ent0+entr0[i]; + } + else + entl0[i]=entr0[i]=0; + } + + DeAlloc2(lSpecs); DeAlloc2(rSpecs); + delete[] el; delete[] er; + + if (N<=NN && ent0em) e[i]=em/e[i]; + else e[i]=1; + if (e[i]>em) em=e[i]; + } e[0]=1; + for (int i=0, Fr=1, n=N; iem) e[i]=em/e[i]; + else e[i]=1; + if (e[i]>em) em=e[i]; + } e[0]=1; + for (int i=0, Fr=1, n=N; i2?(log2(N)-1):2, norm, normmix, cuts[i]); + else if (i==1) + result+=MixSpectrogramSquare(lSpec, lSpecs, N, Log2N>1?Log2N:2, norm, normmix, cuts[i]); + else + result+=MixSpectrogramSquare(lSpec, lSpecs, N, Log2N+1, norm, normmix, cuts[i]); + } + delete[] lSpec; + DeAlloc2(lSpecs); + if (createcuts) delete[] cuts; + return result; +}//MixSpectrogramBlock + +/* + function MixSpectrogramBlock: obtain the composite spectrogram of a waveform block as vectors. + + In: Specs[0][1][WID], Specs[1][2][WID/2], ..., Specs[Res-1][WID/wid][wid], multiresolution spectrogram + Out: spl[WID], Spec[WID], composite spectrogram as tiling and value vectors. Each of the vectors is + made up of $wid subvectors, each subvector pair describing a N*N block of the composite + spectrogram. + + Returns entropy. +*/ +double MixSpectrogramBlock(int* spl, double* Spec, double*** Specs, int WID, int wid, bool norm, bool normmix) +{ + int N=WID/wid, Res=log2(WID/wid)+1, *lspl; + double result=0, *lSpec, ***lSpecs=new double**[Res]; + lSpecs[0]=new double*[Res*N]; for (int i=1; i2?(log2(N)-1):2, norm, normmix); + else if (i==1) + result+=MixSpectrogramSquare(lspl, lSpec, lSpecs, N, Log2N>1?Log2N:2, norm, normmix); + else */ + result+=MixSpectrogramSquare(lspl, lSpec, lSpecs, N, Log2N+1, norm, normmix); + + } + DeAlloc2(lSpecs); + + return result; +}//MixSpectrogramBlock + + +//--------------------------------------------------------------------------- +/* + Functions names as ...Block2(...) implement the same functions as the above directly without + explicitly dividing the multiresolution spectrogram into square blocks. +*/ + +/* + function DoCutSpectrogramBlock2: find optimal tiling for a block + + In: Specs[R0][x0:x0+x-1][Y0:Y0+Y-1], [R0+1][2x0:2x0+2x-1][Y0/2:Y0/2+Y/2-1],..., + Specs[R0+?][Nx0:Nx0+Nx-1][Y0/N:Y0/N+Y/N-1], multiresolution spectrogram + Out: spl[Y-1], tiling of this block + + Returns entropy. +*/ +double DoCutSpectrogramBlock2(int* spl, double*** Specs, int Y, int R0, int x0, int Y0, int N, double& ene) +{ + double ent=0; + if (Y>N) //N=WID/wid, the actual square size + { + spl[0]=0; + double ene1, ene2; + ent+=DoCutSpectrogramBlock2(&spl[1], Specs, Y/2, R0, x0, Y0, N, ene1); + ent+=DoCutSpectrogramBlock2(&spl[Y/2], Specs, Y/2, R0, x0, Y0+Y/2, N, ene2); + ene=ene1+ene2; + } + else if (N==1) + { + double tmp=Specs[R0][x0][Y0]; + ene=tmp; + ent=xlogx(tmp); + } + else //Y==N, the square case + { + double enel, ener, enet, eneb, entl, entr, entt, entb; + int* tmpspl=new int[Y]; + entl=DoCutSpectrogramBlock2(&spl[1], Specs, Y/2, R0+1, 2*x0, Y0/2, N/2, enel); + entr=DoCutSpectrogramBlock2(&spl[Y/2], Specs, Y/2, R0+1, 2*x0+1, Y0/2, N/2, ener); + entb=DoCutSpectrogramBlock2(&tmpspl[1], Specs, Y/2, R0, x0, Y0, N/2, eneb); + entt=DoCutSpectrogramBlock2(&tmpspl[Y/2], Specs, Y/2, R0, x0, Y0+Y/2, N/2, enet); + double ene0=enet+eneb, ene1=enel+ener, ent0=entt+entb, ent1=entl+entr; + //normalize + double eneres=1-(ene0+ene1)/2, norment0, norment1; + //double a0=1/(ene0+eneres), a1=1/(ene1+eneres); + //quasi-global normalization + norment0=(ent0-ene0*log(ene0+eneres))/(ene0+eneres), norment1=(ent1-ene1*log(ene1+eneres))/(ene1+eneres); + //local normalization + //if (ene0>0) norment0=ent0/ene0-log(ene0); else norment0=0; if (ene1>0) norment1=ent1/ene1-log(ene1); else norment1=0; + if (norment1le[0]*2) le[i]=e[i]*le[0]*2/lle; + else le[i]=e[i]; + } + le[0]=e[0]; + } + else + { + memcpy(le, e, sizeof(double)*res); + } + + if (spl[0]==0) + { + int newres; + if (Y>=(1<=wid; i++, Fr*=2, Wid/=2) + { + double lene=0; + for (int fr=0; fr=wid; i++, Fr*=2, Wid/=2) + { + double lene=ene[i]; + if (lene!=0) + for (int fr=0; fr +#include +#include +#include "opt.h" +#include "matrix.h" + +//--------------------------------------------------------------------------- +//macro nsign: judges if two double-precision floating pointer numbers have different signs +#define nsign(x1, x2) (0x80&(((char*)&x1)[7]^((char*)&x2)[7])) + +//--------------------------------------------------------------------------- +/* + function GradientMax: gradient method maximizing F(x) + + In: function F(x) and its derivative dF(x) + start[dim]: initial value of x + ep: a stopping threshold + maxiter: maximal number of iterates + Out: start[dim]: the final x + + Returnx max F(final x) +*/ +double GradientMax(void* params, double (*F)(int, double*, void*), void (*dF)(double*, int, double*, void*), + int dim, double* start, double ep, int maxiter) +{ + double oldG, G=F(dim, start, params), *dG=new double[dim], *X=new double[dim]; + + int iter=0; + do + { + oldG=G; + + dF(dG, dim, start, params); + Search1Dmaxfrom(params, F, dim, start, dG, 1e-2, 1e-6, 1e6, X, &G, ep); + memcpy(start, X, sizeof(double)*dim); + + iter++; + } + while (iteroldG*(1+ep)); + return G; +}//GradientMax + +/* + function GradientOneMax: gradient method maximizing F(x), which searches $dim dimensions one after + another instead of taking the gradient descent direction. + + In: function F(x) and its derivative dF(x) + start[dim]: initial value of x + ep: a stopping threshold + maxiter: maximal number of iterates + Out: start[dim]: the final x + + Returnx max F(final x) +*/ +double GradientOneMax(void* params, double (*F)(int, double*, void*), void (*dF)(double*, int, double*, void*), + int dim, double* start, double ep, int maxiter) +{ + double oldG, G=F(dim, start, params), *dG=new double[dim], *X=new double[dim]; + + int iter=0; + do + { + oldG=G; + for (int d=0; doldG*(1+ep)); + return G; +}//GradientOneMax + +//--------------------------------------------------------------------------- +/* + function mindenormaldouble: returns the minimal denormal double-precision floating point number. +*/ +double mindenormaldouble() +{ + double result=0; + *((unsigned char*)&result)=1; + return result; +}//mindenormaldouble + +/* + function minnormaldouble: returns the minimal normal double-precision floating point number. +*/ +double minnormaldouble() +{ + double result=0; + ((unsigned char*)&result)[6]=16; + return result; +}//minnormaldouble + +/* + //function nextdouble: returns the next double value after x in the direction of dir +*/ +double nextdouble(double x, double dir) +{ + if (x==0) + { + if (dir<0) return -mindenormaldouble(); + else return mindenormaldouble(); + } + else if ((x>0)^(dir>0)) *((__int64*)&x)-=1; + else *((__int64*)&x)+=1; + return x; +}//nextdouble + +/* + function Newton: Newton method for finding the root of y(x) + + In: function y(x) and its derivative y'(x) + params: additional argument for calling y and y' + x0: inital value of x + epx, epy: stopping thresholds on x and y respectively + maxiter: maximal number of iterates + Out: x0: the root + + Returns 0 if successful. +*/ +double Newton(double& x0, double (*y)(double, void*), double (*y1)(double, void*), void* params, double epx, int maxiter, double epy) +{ + double y0=y(x0, params), x2, y2, dx, dy, x3, y3; + if (y0==0) return 0; + + int iter=0; + while (iterfabs(y0)) + { + x2=(x2+x0)/2; + y2=y(x2, params); + } + if (y2==0) + { + x0=x2; + return 0; + } + dx=fabs(x0-x2); + if (dx==0) + { + if (fabs(y0)fabs(y0)) + { + dx/=2; + x2=x0+dx; + dy2=dy(x2, params); + y2=*(double*)((char*)params+yshift); + } + //the above may fail due to precision problem, i.e. x0+dx=x0 + if (x2==x0) + { + if (fabs(y0)fabs(y0)) + return -1; + } + } + } + else if (y2==0) + { + x0=x2; + return 0; + } + else + { + double fabsdx=fabs(dx); + if (fabsdxxmax) return -1; + x0=x2; + y0=y2; + dy0=dy2; + iter++; + } + if (fabs(y0)=y(xa), y(x0)>=y(xb) (or y(x0)<=y(xa), y(x0)<=y(xb)). + On return, y(x0)>=y(xa), y(x0)>=y(xb) (or y(x0)<=y(xa), y(x0)<=y(xb)), and either y'(xa)>0, y'(xb)<0 + (or y'(xa)<0, y'(xb)>0), or xb-xa=ya && y0>=yb) + { + //look for xayb, so that dya>0, dyb<0 + while ((dya<=0 || dyb>=0) && xb-xa>=epx) + { + double x1=(x0+xa)/2; + double dy1=dy(x1, params); + double y1=*(double*)((char*)params+yshift); + double x2=(x0+xb)/2; + double dy2=dy(x2, params); + double y2=*(double*)((char*)params+yshift); + if (y0>=y1 && y0>=y2) xa=x1, ya=y1, dya=dy1, xb=x2, yb=y2, dyb=dy2; + else if (y1>=y0 && y1>=y2) {xb=x0, yb=y0, dyb=dy0; x0=x1, y0=y1, dy0=dy1;} + else {xa=x0, ya=y0, dya=dy0; x0=x2, y0=y2, dy0=dy2;} + } + return xb-xa; + } + else if (y0<=ya && y0<=yb) + { + //look for xay00 + while ((dya>=0 || dyb<=0) && xb-xa>=epx) + { + double x1=(x0+xa)/2; + double dy1=dy(x1, params); + double y1=*(double*)((char*)params+yshift); + double x2=(x0+xb)/2; + double dy2=dy(x2, params); + double y2=*(double*)((char*)params+yshift); + if (y0<=y1 && y0<=y2) xa=x1, ya=y1, dya=dy1, xb=x2, yb=y2, dyb=dy2; + else if (y1<=y0 && y1<=y2) {xb=x0, yb=y0, dyb=dy0; x0=x1, y0=y1, dy0=dy1;} + else {xa=x0, ya=y0, dya=dy0; x0=x2, y0=y2, dy0=dy2;} + } + return xb-xa; + } + else//meaning y(x0) is not bigger(smaller) than both y(xa) and y(xb) + { + if (y0<=yb) return -0.5; //ya<=y0<=yb + if (y0<=ya) return -0.6; //ya>=y0>=yb + } + return -0; +}//init_Newton_1d + +/* + function init_Newton_1d_max: finds an initial x and interval (xa, xb) from which to search for an + maximum. + + On calling xa=y(xa), y(x0)>=y(xb). If the conditions on y are met, + it find a new set of xay(xa), y(x0)>=y(xb), y'(xa)>0, y'(xb)<0, or xb-xa0; otherwise it returns a negative value showing the initial order of y(x0), y(xa) + and y(xb) as + -0.5 for ya<=y0=ya && y0>=yb) + { + //look for xayb, so that dya>0, dyb<0 + while ((dya<=0 || dyb>=0) && xb-xa>=epx) + { + double x1=(x0+xa)/2; + double dy1=dy(x1, params); + double y1=*(double*)((char*)params+yshift); + double x2=(x0+xb)/2; + double dy2=dy(x2, params); + double y2=*(double*)((char*)params+yshift); + if (y0>=y1 && y0>=y2) xa=x1, ya=y1, dya=dy1, xb=x2, yb=y2, dyb=dy2; + else if (y1>=y0 && y1>=y2) {xb=x0, yb=y0, dyb=dy0; x0=x1, y0=y1, dy0=dy1;} + else {xa=x0, ya=y0, dya=dy0; x0=x2, y0=y2, dy0=dy2;} + } + return xb-xa; + } + else//meaning y(x0) is not bigger than both y(xa) and y(xb) + { + if (y0=ya) return -0.5; //ya<=y0=yb) return -0.6; //ya>y0>=yb + if (ya0, or xb-xa0; otherwise it returns a negative value showing the initial order of y(x0), y(xa) + and y(xb) as + -0.5 for ya<=y0yb>ya + -0.8 for y0>ya>=yb + + In: function dy(x) that computes y and y' at x + params: additional argument for calling dy + yshift: the byte shift in params where dy(x) put the value of y(x) as double type + x0: inital value of x + (xa, xb): initial search range + epx: tolerable error of extremum + Out: improved initial value x0 and search range (xa, xb) + + Returns xb-xa if successful, a negative value as above if not. +*/ +double init_Newton_1d_min(double& x0, double& xa, double& xb, double (*dy)(double, void*), void* params, int yshift, double epx) +{ + double dy0, dya, dyb, y0, ya, yb; + dy0=dy(x0, params); y0=*(double*)((char*)params+yshift); + dya=dy(xa, params); ya=*(double*)((char*)params+yshift); + dyb=dy(xb, params); yb=*(double*)((char*)params+yshift); + if (y0<=ya && y0<=yb) + { + //look for xay00 + while ((dya>=0 || dyb<=0) && xb-xa>=epx) + { + double x1=(x0+xa)/2; + double dy1=dy(x1, params); + double y1=*(double*)((char*)params+yshift); + double x2=(x0+xb)/2; + double dy2=dy(x2, params); + double y2=*(double*)((char*)params+yshift); + if (y0<=y1 && y0<=y2) xa=x1, ya=y1, dya=dy1, xb=x2, yb=y2, dyb=dy2; + else if (y1<=y0 && y1<=y2) {xb=x0, yb=y0, dyb=dy0; x0=x1, y0=y1, dy0=dy1;} + else {xa=x0, ya=y0, dya=dy0; x0=x2, y0=y2, dy0=dy2;} + } + return xb-xa; + } + else//meaning y(x0) is not bigger than both y(xa) and y(xb) + { + if (y0=ya) return -0.5; //ya<=y0=yb) return -0.6; //ya>y0>=yb + if (ya=y(xa), y(x0)>=y(xb), + y'(xa)>0, y'(xb)<0 + + In: function ddy(x) that computes y, y' and y" at x + params: additional argument for calling ddy + y1shift: the byte shift in params where ddy(x) put the value of y'(x) as double type + yshift: the byte shift in params where ddy(x) put the value of y(x) as double type + x0: inital value of x + (xa, xb): initial search range + epx, epdy: stopping threshold for x and y', respectively + maxiter: maximal number of iterates + Out: x0: the maximum + + Returns the error bound for the maximum x0, or + -2 if y(*) do not satisfy input conditions + -3 if y'(*) do not satisfy input conditions +*/ +double Newton_1d_max(double& x0, double xa, double xb, double (*ddy)(double, void*), void* params, int y1shift, int yshift, double epx, int maxiter, double epdy, bool checkinput) +{ + double x1, y1, x2, y2, dx, dy1, dy2, dy3, x3, y3, ddy1, ddy2, ddy3; + double y0, ya, yb, dy0, dya, dyb, ddy0; + if (xb-xa=ya && y0>=yb) + { + if (dya<=0 || dyb>=0) return -3; + } + else return -2; + } + + if (dy0==0) return 0; + + int iter=0; + while (iter=0) //newton method not applicable + { + do + { + x1=(x0+xa)/2; + ddy1=ddy(x1, params); + dy1=*(double*)((char*)params+y1shift), y1=*(double*)((char*)params+yshift); + x2=(x0+xb)/2; + ddy2=ddy(x2, params); + dy2=*(double*)((char*)params+y1shift), y2=*(double*)((char*)params+yshift); + if (y0>=y1 && y0>=y2) xa=x1, ya=y1, dya=dy1, xb=x2, yb=y2, dyb=dy2; + else if (y1>=y0 && y1>=y2) {xb=x0, yb=y0, dyb=dy0; x0=x1, y0=y1, dy0=dy1, ddy0=ddy1;} + else {xa=x0, ya=y0, dya=dy0; x0=x2, y0=y2, dy0=dy2, ddy0=ddy2;} + } + while(ddy0>=0 && (dya<=0 || dyb>=0) && xb-xa>=epx); + if (xb-xaxb) {x2=(xb+x0)/2; dx=x2-x0;} + ddy2=ddy(x2, params); + y2=*(double*)((char*)params+yshift); + + //make sure the iteration yields a larger y + if (y2epx) + { + dx/=2; + x2=x0+dx; + ddy2=ddy(x2, params); + y2=*(double*)((char*)params+yshift); + } + if (y2=y0 + dy2=*(double*)((char*)params+y1shift); + if (fabs(dy2)=y3) {x0=x2; return fabsdx;} + else x2=x3, y2=y3, dy2=dy3, ddy2=ddy3; + } + + if (x00 + + In: function ddy(x) that computes y, y' and y" at x + params: additional argument for calling ddy + y1shift: the byte shift in params where ddy(x) put the value of y'(x) as double type + yshift: the byte shift in params where ddy(x) put the value of y(x) as double type + x0: inital value of x + (xa, xb): initial search range + epx, epdy: stopping threshold for x and y', respectively + maxiter: maximal number of iterates + Out: x0: the maximum + + Returns the error bound for the maximum x0, or + -2 if y(*) do not satisfy input conditions + -3 if y'(*) do not satisfy input conditions +*/ +double Newton_1d_min(double& x0, double xa, double xb, double (*ddy)(double, void*), void* params, int y1shift, int yshift, double epx, int maxiter, double epdy, bool checkinput) +{ + double x1, y1, x2, y2, dx, dy1, dy2, dy3, x3, y3, ddy1, ddy2, ddy3; + double y0, ya, yb, dy0, dya, dyb, ddy0; + if (xb-xa=0 || dyb<=0) return -3; + } + else return -2; + } + + if (dy0==0) return 0; + + int iter=0; + while (iter=0 || dyb<=0) && xb-xa>=epx); + } + else + { + //Newton point + //dx is the same direction as dy0 if ddy0<0 + if (fabs(dy0)xb) {x2=(xb+x0)/2; dx=x2-x0;} + ddy2=ddy(x2, params); + y2=*(double*)((char*)params+yshift); + //make sure the iteration yields a larger y + if (y2>y0) + { + while (y2>y0) + { + dx/=2; + x2=x0+dx; + ddy2=ddy(x2, params); + y2=*(double*)((char*)params+yshift); + } + if (x2==x0) return 0; //dy0<0 but all y(x+(ep)dx)<0 + } + dy2=*(double*)((char*)params+y1shift); + if (fabs(dy2)=ya, y0>=yb. If the conditions on y are not met, it + returns a negative value showing the order of y0, ya and yb, i.e. + -0.5 for ya<=y0epx) ep=Newton_1d_max(x0, xa, xb, ddy, params, y1shift, yshift, epx, maxiter, epdy, true); + return ep; +}//Newton1dmax + +//function Newton1dmin: same as Newton1dmax, in the other direction. +double Newton1dmin(double& x0, double xa, double xb, double (*ddy)(double, void*), void* params, int y1shift, int yshift, double (*dy)(double, void*), int dyshift, double epx, int maxiter, double epdy, bool checkinput) +{ + double ep=init_Newton_1d_min(x0, xa, xb, dy, params, dyshift, epx); + if (ep>epx) ep=Newton_1d_min(x0, xa, xb, ddy, params, y1shift, yshift, epx, maxiter, epdy, true); + return ep; +}//Newton1dmin + +/* + function NewtonMax: looks for the maximum of y within [x0, x1] using Newton method. + + In: function ddy(x) that computes y, y' and y" at x + params: additional argument for calling ddy + y1shift: the byte shift in params where ddy(x) put the value of y'(x) as double type + yshift: the byte shift in params where ddy(x) put the value of y(x) as double type + (x0, x1): initial search range + epx, epy: stopping threshold for x and y, respectively + maxiter: maximal number of iterates + Out: maximum x0 + + Returns an error bound on maximum x0 if successful, a negative value if not. +*/ +double NewtonMax(double& x0, double x1, double (*ddy)(double, void*), void* params, int y1shift, int yshift, double epx, int maxiter, double epy) +{ + double y0, x2, y2, dx, dy0, dy2, dy3, x3, y3; + dy0=ddy(x0, params); + y0=*(double*)((char*)params+yshift); + if (y0==0) return 0; + + int iter=0; + while (iterfabs(y0)) + { + dx/=2; + x2=x0+dx; + dy2=ddy(x2, params); + y2=*(double*)((char*)params+yshift); + } + //the above may fail due to precision problem, i.e. x0+dx=x0 + if (x2==x0) + { + if (fabs(y0)fabs(y0)) + return -1; + } + } + } + else if (y2==0) + { + x0=x2; + return 0; + } + else + { + double fabsdx=fabs(dx); + if (fabsdxx2) {double tmp=x1; x1=x2; x2=tmp;} + double y1=y(x1, params), y3, x3, ep2=epx*2; + if (y1==0) return 0; + else + { + double y2=y(x2, params); + if (y2==0) + { + x1=x2; + return 0; + } + else if nsign(y1, y2) //y1 and y2 have different signs + { + int iter=0; + while (x2-x1>ep2 && iterfabs(y2)) + { + x3=x1, y3=y1; + } + else + { + x3=x2, y3=y2; + x2=x1, y2=y1; + } + + int iter=0; + while (iterx2) {double tmp=x1; x1=x2; x2=tmp;} + double y1=y(x1, params), y3, y4, dy2, dy3, x3, x4, ep2=epx*2; + if (y1==0) return 0; + else + { + double y2=y(x2, params); + if (y2==0) + { + x1=x2; + return 0; + } + else if nsign(y1, y2) //y1 and y2 have different signs + { + int iter=0; + while (x2-x1>ep2 && iterx3 + if (x4>=x2) x1=x3, y1=y3; + else + { + y4=y(x4, params); + if (y4==0) + { + x1=x4; + return 0; + } + else + { + if nsign(y4, y3) x1=x3, y1=y3, x2=x4, y2=y4; + else x1=x4, y1=y4; + } + } + } + } + } + iter++; + } + x1=(x1+x2)/2; + return x2-x1; + } + else + return -1; + } +}//rootsearchbisecant + +//--------------------------------------------------------------------------- +/* + function Search1Dmax: 1-dimensional maximum search within interval [inf, sup] using 0.618 method + + In: function F(x), + params: additional arguments for calling F + (inf, sup): inital range + epx: stopping threshold on x + maxiter: maximal number of iterates + convcheck: specifies if convexity is checked at each interate, false recommended. + Out: x: the maximum + maxresult: the maximal value, if specified + + Returns an estimated error bound of x if successful, a negative value if not. +*/ +double Search1Dmax(double& x, void* params, double (*F)(double, void*), double inf, double sup, double* maxresult, double ep, int maxiter, bool convcheck) +{ + double v0618=(sqrt(5.0)-1)/2, iv0618=1-v0618; + double l=sup-inf; + double startsup=sup, startinf=inf, startl=l; + double x1=inf+l*(1-v0618); + double x2=inf+l*v0618; + double result; + + double v0=F(inf, params); + double v1=F(x1, params); + double v2=F(x2, params); + double v3=F(sup, params); + int iter=0; + + if (v1v2) + { + sup=x2, v3=v2; + l=sup-inf; + x2=x1, v2=v1; + x1=inf+l*(1-v0618); + v1=F(x1, params); + if (convcheck && (v1v2) {x=x1; if (maxresult) maxresult[0]=v1; result=x1-inf;} + else {x=x2; if (maxresult) maxresult[0]=v2; result=sup-x2;} + return result; + +return1: //where at some stage v(.) fails to be convex + if (v0>=v1 && v0>=v2 && v0>=v3){x=inf; if (maxresult) maxresult[0]=v0; result=(x==startinf)?(-1):(sup-x1);} + else if (v1>=v0 && v1>=v2 && v1>=v3){x=x1; if (maxresult) maxresult[0]=v1; result=x1-inf;} + else if (v2>=v0 && v2>=v1 && v2>=v3){x=x2; if (maxresult) maxresult[0]=v2; result=sup-x2;} + else {x=sup; if (maxresult) maxresult[0]=v3; result=(x==startsup)?(-1):(x2-inf);} + return result; +}//Search1Dmax + +/* + function Search1DmaxEx: 1-dimensional maximum search using Search1Dmax, but allows setting a $len + argument so that a pre-location of the maximum is performed by sampling (inf, sup) at interval $len, + so that Search1Dmax will work on an interval no longer than 2*len. + + In: function F(x), + params: additional arguments for calling F + (inf, sup): inital range + len: rough sampling interval for pre-locating maximum + epx: stopping threshold on x + maxiter: maximal number of iterates + convcheck: specifies if convexity is checked at each interate, false recommended. + Out: x: the maximum + maxresult: the maximal value, if specified + + Returns an estimated error bound of x if successful, a negative value if not. +*/ +double Search1DmaxEx(double& x, void* params, double (*F)(double, void*), double inf, double sup, double* maxresult, double ep, int maxiter, double len) +{ + int c=ceil((sup-inf)/len+1); + if (c<8) c=8; + double* vs=new double[c+1], step=(sup-inf)/c; + for (int i=0; i<=c; i++) vs[i]=F(inf+step*i, params); + int max=0; for (int i=1; i<=c; i++) if (vs[max]0 && max0 that locally maximizes F(start+x*direct) + or x|-x>0 that maximizes F(start-x*direct) at calculated points if convex check fails. +*/ +double Search1Dmaxfrom(void* params, double (*F)(int, double*, void*), int dim, double* start, double* direct, + double step, double minstep, double maxstep, double* opt, double* max, double ep, int maxiter, bool convcheck) +{ + bool record=true; + if (!opt) + { + record=false; + opt=new double[dim]; + } + + double v0618=(sqrt(5.0)-1)/2, iv0618=1-v0618; + double x3=step, x0=0, x1=-1, x2=-1, l=x3-x0; + + //prepares x0~x3, v0~v3, x0=0, v2>v3 + memcpy(opt, start, sizeof(double)*dim); + double v0=F(dim, opt, params); + MultiAdd(dim, opt, start, direct, x3); + double v1, v2, v3=F(dim, opt, params); + if (v0>=v3) + { + x1=x0+l*iv0618; + MultiAdd(dim, opt, start, direct, x1); + v1=F(dim, opt, params); + x2=x0+l*v0618; + MultiAdd(dim, opt, start, direct, x2); + v2=F(dim, opt, params); + while (l>minstep && v0>v1) + { + x3=x2, v3=v2; + x2=x1, v2=v1; + l=x3-x0; + x1=x0+l*iv0618; + MultiAdd(dim, opt, start, direct, x1); + v1=F(dim, opt, params); + } + if (l<=minstep) + { + if (max) max[0]=v0; + if (!record) delete[] opt; + return x0; + } + } + else//v0<=v3 + { + do + { + x1=x2, v1=v2; + x2=x3, v2=v3; + x3+=l*v0618; + l=x3-x0; + MultiAdd(dim, opt, start, direct, x3); + v3=F(dim, opt, params); + } while (v2=maxstep) //increasing all the way to maxstep + { + if (max) max[0]=v3; + if (!record) delete opt; + return x3; + } + else if (x1<0) + { + x1=x0+l*iv0618; + MultiAdd(dim, opt, start, direct, x1); + v1=F(dim, opt, params); + } + } + + double oldl=l; + + int iter=0; + + if (convcheck && (v1v2) + { + x3=x2, v3=v2; + l=x3-x0; + x2=x1, v2=v1; + x1=x0+l*iv0618; + MultiAdd(dim, opt, start, direct, x1); + v1=F(dim, opt, params); + if (convcheck && (v1v0) v0=v1,x0=x1; if (v2>v0) v0=v2, x0=x2; if (v3>v0) v0=v3, x0=x3; + if (max) max[0]=v0; + return -x0; +}//Search1Dmaxfrom + + diff -r 9b9f21935f24 -r 6422640a802f opt.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/opt.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,31 @@ +#ifndef optH +#define optH + +/* + opt.cpp - optimization routines +*/ + +//--tool procedures---------------------------------------------------------- +double nextdouble(double x, double dir); //returns next double-precision floating pointer number + +//--Gradient method---------------------------------------------------------- +double GradientMax(void* params, double (*F)(int, double*, void*), void (*dF)(double*, int, double*, void*), int dim, double* start, double ep=1e-6, int maxiter=100); +double GradientOneMax(void* params, double (*F)(int, double*, void*), void (*dF)(double*, int, double*, void*), int dim, double* start, double ep, int maxiter); + +//--Newton methods------------------------------------------------------------ +double Newton(double& x0, double (*y)(double, void*), double (*y1)(double, void*), void* params=0, double epx=1e-6, int maxiter=100, double epy=1e-256); +int Newton(double& x0, double (*dy)(double, void*), void* params, int yshift, double epx=1e-6, int maxiter=100, double epy=1e-256, double xmin=-1e308, double xmax=1e308); +double Newton1dmax(double& x0, double xa, double xb, double (*ddy)(double, void*), void* params, int y1shift, int yshift, double (*dy)(double, void*), int dyshift, double epx=1e-6, int maxiter=100, double epdy=1e-128, bool checkinput=true); +double Newton2D(double& x1, double& x2, double (*dy)(double&, double&, void*), double (*ddy)(double&, double&, double&, void*), void* params=0, double epx1=1e-6, double epx2=1e-6, int maxiter=100, double epdy=1e-256); + +//--root search routines----------------------------------------------------- +double rootsearchhalf(double&x1, double x2, double (*y)(double, void*), void* params=0, double epx=1e-6, int maxiter=100); +double rootsearchsecant(double& x1, double x2, double (*y)(double, void*), void* params=0, double epx=1e-6, int maxiter=100); +double rootsearchbisecant(double&x1, double x2, double (*y)(double, void*), void* params=0, double epx=1e-6, int maxiter=100); + +//--1-D maximum search routines---------------------------------------------- +double Search1Dmax(double& x, void* params, double (*F)(double, void*), double inf, double sup, double* maxresult=0, double ep=1e-6, int maxiter=100, bool convcheck=false); +double Search1DmaxEx(double& x, void* params, double (*F)(double, void*), double inf, double sup, double* maxresult=0, double ep=1e-6, int maxiter=100, double len=1); +double Search1Dmaxfrom(void* params, double (*F)(int, double*, void*), int dim, double* start, double* direct, double step=1e-2, double minstep=1e-6, double maxstep=1e6, double* opt=0, double* max=0, double ep=1e-6, int maxiter=100, bool convcheck=false); + +#endif diff -r 9b9f21935f24 -r 6422640a802f procedures.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/procedures.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,2860 @@ +//--------------------------------------------------------------------------- + +#include +#include +#include "procedures.h" +#include "matrix.h" +#include "opt.h" +#include "SinEst.h" + +//--------------------------------------------------------------------------- +//TGMM methods + +//method TGMM::TGMM: default constructor +TGMM::TGMM() +{ + p=0, m=dev=0; +}//TGMM + +//method GMM:~TGMM: default destructor +TGMM::~TGMM() +{ + ReleaseGMM(p, m, dev) +}; + +//--------------------------------------------------------------------------- +//TFSpans methods + +//method TTFSpans: default constructor +TTFSpans::TTFSpans() +{ + Count=0; + Capacity=100; + Items=new TTFSpan[Capacity]; +}//TTFSpans + +//method ~TTFSpans: default destructor +TTFSpans::~TTFSpans() +{ + delete[] Items; +}//~TTFSpans + +/* + method Add: add a new span to the list + + In: ATFSpan: the new span to add +*/ +void TTFSpans::Add(TTFSpan& ATFSpan) +{ + if (Count==Capacity) + { + int OldCapacity=Capacity; + Capacity+=50; + TTFSpan* NewItems=new TTFSpan[Capacity]; + memcpy(NewItems, Items, sizeof(TTFSpan)*OldCapacity); + delete[] Items; + Items=NewItems; + } + Items[Count]=ATFSpan; + Count++; +}//Add + +/* + method Clear: discard the current content without freeing memory. +*/ +void TTFSpans::Clear() +{ + Count=0; +}//Clear + +/* + method Delete: delete a span from current list + + In: Index: index to the span to delete +*/ +int TTFSpans::Delete(int Index) +{ + if (Index<0 || Index>=Count) + return 0; + memmove(&Items[Index], &Items[Index+1], sizeof(TTFSpan)*(Count-1-Index)); + Count--; + return 1; +}//Delete + +//--------------------------------------------------------------------------- +//SpecTrack methods + +/* + method TSpecTrack::Add: adds a SpecPeak to the track. + + In: APeak: the SpecPeak to add. +*/ +int TSpecTrack::Add(TSpecPeak& APeak) +{ + if (Count>=Capacity) + { + Peaks=(TSpecPeak*)realloc(Peaks, sizeof(TSpecPeak)*(Capacity*2)); + Capacity*=2; + } + int ind=LocatePeak(APeak); + if (ind<0) + { + InsertPeak(APeak, -ind-1); + ind=-ind-1; + } + + int t=APeak.t; + double f=APeak.f; + if (Count==1) t1=t2=t, fmin=fmax=f; + else + { + if (tt2) t2=t; + if (ffmax) fmax=f; + } + return ind; +}//Add + +/* + method TSpecTrack::TSpecTrack: creates a SpecTrack with an inital capacity. + + In: ACapacity: initial capacity, i.e. the number SpecPeak's to allocate storage space for. +*/ +TSpecTrack::TSpecTrack(int ACapacity) +{ + Count=0; + Capacity=ACapacity; + Peaks=new TSpecPeak[Capacity]; +}//TSpecTrack + +//method TSpecTrack::~TSpecTrack: default destructor. +TSpecTrack::~TSpecTrack() +{ + delete[] Peaks; +}//TSpecTrack + +/* + method InsertPeak: inserts a new SpecPeak into the track at a given index. Internal use only. + + In: APeak: the SpecPeak to insert. + index: the position in the list to place the new SpecPeak. Original SpecPeak's at and after this + position are shifted by 1 posiiton. +*/ +void TSpecTrack::InsertPeak(TSpecPeak& APeak, int index) +{ + memmove(&Peaks[index+1], &Peaks[index], sizeof(TSpecPeak)*(Count-index)); + Peaks[index]=APeak; + Count++; +}//InsertPeak + +/* + method TSpecTrack::LocatePeak: looks for a SpecPeak in the track that has the same time (t) as APeak. + + In: APeak: a SpecPeak + + Returns the index in this track of the first SpecPeak that has the same time stamp as APeak. However, + if there is no peak with that time stamp, the method returns -1 if APeaks comes before the first + SpecPeak, -2 if between 1st and 2nd SpecPeak's, -3 if between 2nd and 3rd SpecPeak's, etc. +*/ +int TSpecTrack::LocatePeak(TSpecPeak& APeak) +{ + if (APeak.tPeaks[Count-1].t) return -Count-1; + + if (APeak.t==Peaks[0].t) return 0; + else if (APeak.t==Peaks[Count-1].t) return Count-1; + + int a=0, b=Count-1, c=(a+b)/2; + while (a0) zero=-size2/2, trunc=true; + if (pre_buffer==NULL || (post_buffer==NULL && size2+zero!=0)) trunc=true; + + AllocateFFTBuffer(Wid, fft, w, x1); + int* hbitinv=CreateBitInvTable(order-1); + cdouble* x2=new cdouble[Wid]; + double* tmp=new double[HWid]; + memset(tmp, 0, sizeof(double)*HWid); + + memcpy(fft, source2, sizeof(double)*size2); + memset(&fft[size2], 0, sizeof(double)*(Wid-size2)); + RFFTC(fft, 0, 0, order, w, x2, hbitinv); + + double r1, r2, i1, i2; + int ind, ind_; + for (int i=0; i0) + { + memcpy(fft, &source1[Fr*HWid], sizeof(double)*res); + memset(&fft[res], 0, sizeof(double)*(Wid-res)); + + RFFTC(fft, 0, 0, order, w, x1, hbitinv); + + for (int j=0; jsize1) + { + memcpy(&dest[ind], tmp, sizeof(double)*(size1-ind)); + if (!trunc && post_buffer) + { + if (ind_>size1+size2+zero) + memcpy(post_buffer, &tmp[size1-ind], sizeof(double)*(size2+zero)); + else + memcpy(post_buffer, &tmp[size1-ind], sizeof(double)*(ind_-size1)); + } + } + else + memcpy(&dest[ind], tmp, sizeof(double)*HWid); + memcpy(tmp, &fft[HWid], sizeof(double)*HWid); + Fr++; + } + + ind=Fr*HWid+zero; + ind_=ind+HWid; + + if (indsize1) + { + memcpy(&dest[ind], tmp, sizeof(double)*(size1-ind)); + if (!trunc && post_buffer) + { + if (ind_>size1+size2+zero) + memcpy(post_buffer, &tmp[size1-ind], sizeof(double)*(size2+zero)); + else + memcpy(post_buffer, &tmp[size1-ind], sizeof(double)*(ind_-size1)); + } + } + else + memcpy(&dest[ind], tmp, sizeof(double)*HWid); + } + else //ind>=size1 => ind_>=size1+size2+zero + { + if (!trunc && post_buffer) + memcpy(&post_buffer[ind-size1], tmp, sizeof(double)*(size1+size2+zero-ind)); + } + + FreeFFTBuffer(fft); + delete[] x2; + delete[] tmp; + delete[] hbitinv; +}//FFTConv + +/* + function FFTConv: the simplified version using two output buffers instead of three. This is almost + equivalent to setting zero=-size2 in the three-output-buffer version (so that post_buffer no longer + exists), except that this version requires size2 (renamed HWid) be a power of 2, and pre_buffer point + to the END of the storage (so that pre_buffer=dest automatically connects the two buffers in a + continuous memory block). + + In: source1[size1]: convolvend + source2[HWid]: second convolved, HWid be a power of 2 + pre_buffer[-HWid:-1]: buffer hosting values to be overlap-added to the start of the result. + Out: dest[size1]: main output buffer, now hosting end part of the result (after HWid samples). + pre_buffer[-HWid:-1]: now updated by added the start of the result + + No return value. +*/ +void FFTConv(double* dest, double* source1, int size1, double* source2, int HWid, double* pre_buffer) +{ + int Wid=HWid*2; + int order=log2(Wid); + int Fr=size1/HWid; + int res=size1-HWid*Fr; + + AllocateFFTBuffer(Wid, fft, w, x1); + cdouble *x2=new cdouble[Wid]; + double *tmp=new double[HWid]; + int* hbitinv=CreateBitInvTable(order-1); + + memcpy(fft, source2, sizeof(double)*HWid); + memset(&fft[HWid], 0, sizeof(double)*HWid); + RFFTC(fft, 0, 0, order, w, x2, hbitinv); + + double r1, r2, i1, i2; + for (int i=0; i0) + { + if (Fr==0) memset(tmp, 0, sizeof(double)*HWid); + + memcpy(fft, &source1[Fr*HWid], sizeof(double)*res); + memset(&fft[res], 0, sizeof(double)*(Wid-res)); + + RFFTC(fft, 0, 0, order, w, x1, hbitinv); + for (int j=0; j0) zero=-size2/2, trunc=true; + if (pre_buffer==NULL || (post_buffer==NULL && size2+zero!=0)) trunc=true; + + AllocateFFTBuffer(Wid, fft, w, x1); + cdouble* x2=new cdouble[Wid]; + double* tmp=new double[HWid]; + memset(tmp, 0, sizeof(double)*HWid); + int* hbitinv=CreateBitInvTable(order-1); + + memcpy(fft, source2, sizeof(double)*size2); + memset(&fft[size2], 0, sizeof(double)*(Wid-size2)); + RFFTC(fft, 0, 0, order, w, x2, hbitinv); + + double r1, r2, i1, i2; + int ind, ind_; + for (int i=0; i0) + { + IntToDouble(fft, &source1[Fr*HWid*bps], bps, res); + memset(&fft[res], 0, sizeof(double)*(Wid-res)); + + RFFTC(fft, 0, 0, order, w, x1, hbitinv); + + for (int j=0; jsize1) + { + DoubleToInt(&dest[ind*bps], bps, tmp, size1-ind); + if (!trunc && post_buffer) + { + if (ind_>size1+size2+zero) + DoubleToInt(post_buffer, bps, &tmp[size1-ind], size2+zero); + else + DoubleToInt(post_buffer, bps, &tmp[size1-ind], ind_-size1); + } + } + else + DoubleToInt(&dest[ind*bps], bps, tmp, HWid); + memcpy(tmp, &fft[HWid], sizeof(double)*HWid); + Fr++; + } + + ind=Fr*HWid+zero; + ind_=ind+HWid; + + if (indsize1) + { + DoubleToInt(&dest[ind*bps], bps, tmp, size1-ind); + if (!trunc && post_buffer) + { + if (ind_>size1+size2+zero) + DoubleToInt(post_buffer, bps, &tmp[size1-ind], size2+zero); + else + DoubleToInt(post_buffer, bps, &tmp[size1-ind], ind_-size1); + } + } + else + DoubleToInt(&dest[ind*bps], bps, tmp, HWid); + } + else //ind>=size1 => ind_>=size1+size2+zero + { + if (!trunc && post_buffer) + DoubleToInt(&post_buffer[(ind-size1)*bps], bps, tmp, size1+size2+zero-ind); + } + + FreeFFTBuffer(fft); + delete[] x2; + delete[] tmp; + delete[] hbitinv; +}//FFTConv + +//--------------------------------------------------------------------------- +/* + function FFTFilter: FFT with cosine-window overlap-add: This FFT filter is not an actural LTI system, + but an block processing with overlap-add. In this function the blocks are overlapped by 50% and summed + up with Hann windowing. + + In: data[Count]: input data + Wid: DFT size + On, Off: cut-off frequencies of FFT filter. OnOff: band-stop. + Out: dataout[Count]: filtered data + + No return value. Identical data and dataout allowed +*/ +void FFTFilter(double* dataout, double* data, int Count, int Wid, int On, int Off) +{ + int Order=log2(Wid); + int HWid=Wid/2; + int Fr=(Count-Wid)/HWid+1; + AllocateFFTBuffer(Wid, ldata, w, x); + + double* win=new double[Wid]; + for (int i=0; i0) + for (int k=0; k=1) + memset(&x[Wid-On+1], 0, (On-1)*sizeof(cdouble)); + if (Off*2<=Wid) + memset(&x[Off], 0, (Wid-Off*2+1)*sizeof(cdouble)); + } + else //band stop: set [Off, On) to zero. + { + memset(&x[Off], 0, sizeof(cdouble)*(On-Off)); + memset(&x[Wid-On+1], 0, sizeof(double)*(On-Off)); + } + + CIFFTR(x, Order, w, ldata); + + if (i>0) for (int k=0; k0) + for (int k=0; k0) + for (int k=0; k0) + for (int k=0; k=data[Count-1]) return Count-1; + if (valuedhxpeak); + int fshift=int(&((l_hx*)0)->hxpeak); + double B=p->B; + int count=0; + + int den=ceil(en), dst=floor(st); + if (den-dst<3) den++, dst--; + if (den-dst<3) den++, dst--; + if (dst<1) dst=1; + + double step=0.5; + int num=(den-dst)/step+1; + bool allochps=false, allocvhps=false; + if (hps==NULL) allochps=true, hps=new double[num]; + if (vhps==NULL) allocvhps=true, vhps=new double[num]; + + { + double* inp=new double[num]; + for (int i=0; ik1=ceil(lf-B); if (p->k1<0) p->k1=0; + p->k2=floor(lf+B); if (p->k2>=p->N/2) p->k2=p->N/2-1; + inp[i]=F(lf, params); + } + + for (int i=1; i=inp[i-1] && inp[i]>=inp[i+1]) //inp[i]=F(dst+step*i) + { + if (inp[i]==inp[i-1] && inp[i]==inp[i+1]) continue; + double fa=dst+step*(i-1), fb=dst+step*(i+1); + double ff=dst+step*i; + p->k1=ceil(ff-B); if (p->k1<0) p->k1=0; + p->k2=floor(ff+B); if (p->k2>=p->N/2) p->k2=p->N/2-1; + + double tmp=Newton1dmax(ff, fa, fb, ddF, params, dfshift, fshift, dF, dfshift, epf); + + //although we have selected inp[i] to be a local maximum, different truncation + // of local spectrum implies it may not hold as the truncation of inp[i] is + // used for recalculating inp[i-1] and inp[i+1] in init_Newton method. In this + // case we retry the sub-maximal frequency to see if it becomes a local maximum + // when the spectrum is truncated to centre on it. + + if (tmp==-0.5 || tmp==-0.7) //y(fa)<=y(ff)k1=ceil(ff2-B); if (p->k1<0) p->k1=0; + p->k2=floor(ff2+B); if (p->k2>=p->N/2) p->k2=p->N/2-1; + tmp=Newton1dmax(ff2, ff, fb, ddF, params, dfshift, fshift, dF, dfshift, epf); + p->k1=ceil(ff-B); if (p->k1<0) p->k1=0; + p->k2=floor(ff+B); if (p->k2>=p->N/2) p->k2=p->N/2-1; */ + } + else if (tmp==-0.6 || tmp==-0.8) //y(fb)<=y(ff)hxpeak; + } + if (ff>=st && ff<=en && ff>dst+step*(i-0.99) && ff0.1) +// { + hps[count]=ff; + count++; +// } + } + } + } + delete[] inp; + } + + if (allochps) hps=(double*)realloc(hps, sizeof(double)*count); + if (allocvhps) vhps=(double*)realloc(vhps, sizeof(double)*count); + return count; +}//HxPeak2 + +//--------------------------------------------------------------------------- +/* + function InsertDec: inserts value into sorted decreasing list + + In: data[Count]: a sorted decreasing list. + value: the value to be added + Out: data[Count]: the list with $value inserted if the latter is larger than its last entry, in which + case the original last entry is discarded. + + Returns the index where $value is located in data[], or -1 if $value is smaller than or equal to + data[Count-1]. +*/ +int InsertDec(int value, int* data, int Count) +{ + if (Count<=0) return -1; + if (value<=data[Count-1]) return -1; + if (value>data[0]) + { + memmove(&data[1], &data[0], sizeof(int)*(Count-1)); + data[0]=value; + return 0; + } + + //now Count>=2 + int head=0, end=Count-1, mid; + + //D(head)>=value>D(end) + while (end-head>1) + { + mid=(head+end)/2; + if (value<=data[mid]) head=mid; + else end=mid; + } + + //D(head=end-1)>=value>D(end) + memmove(&data[end+1], &data[end], sizeof(int)*(Count-end-1)); + data[end]=value; + return end; +}//InsertDec +//the double version +int InsertDec(double value, double* data, int Count) +{ + if (Count<=0) return -1; + if (value<=data[Count-1]) return -1; + if (value>data[0]) + { + memmove(&data[1], &data[0], sizeof(double)*(Count-1)); + data[0]=value; + return 0; + } + + //now Count>=2 + int head=0, end=Count-1, mid; + + //D(head)>=value>D(end) + while (end-head>1) + { + mid=(head+end)/2; + if (value<=data[mid]) head=mid; + else end=mid; + } + + //D(head=end-1)>=value>D(end) + memmove(&data[end+1], &data[end], sizeof(double)*(Count-end-1)); + data[end]=value; + return end; +}//InsertDec + +/* + function InsertDec: inserts value and attached integer into sorted decreasing list + + In: data[Count]: a sorted decreasing list + indices[Count]: a list of integers attached to entries of data[] + value, index: the value to be added and its attached integer + Out: data[Count], indices[Count]: the lists with $value and $index inserted if $value is larger than + the last entry of data[], in which case the original last entries are discarded. + + Returns the index where $value is located in data[], or -1 if $value is smaller than or equal to + data[Count-1]. +*/ +int InsertDec(double value, int index, double* data, int* indices, int Count) +{ + if (Count<=0) return -1; + if (value<=data[Count-1]) return -1; + if (value>data[0]) + { + memmove(&data[1], data, sizeof(double)*(Count-1)); + memmove(&indices[1], indices, sizeof(int)*(Count-1)); + data[0]=value, indices[0]=index; + return 0; + } + + //now Count>=2 + int head=0, end=Count-1, mid; + + //D(head)>=value>D(end) + while (end-head>1) + { + mid=(head+end)/2; + if (value<=data[mid]) head=mid; + else end=mid; + } + + //D(head=end-1)>=value>D(end) + memmove(&data[end+1], &data[end], sizeof(double)*(Count-end-1)); + memmove(&indices[end+1], &indices[end], sizeof(int)*(Count-end-1)); + data[end]=value, indices[end]=index; + return end; +}//InsertDec + +/* + InsertInc: inserts value into sorted increasing list. + + In: data[Count]: a sorted increasing list. + Capacity: maximal size of data[] + value: the value to be added + Compare: pointer to function that compare two values + Out: data[Count]: the list with $value inserted. If the original list is full (Count=Capacity) then + either $value, or the last entry of data[], whichever is larger, is discarded. + + Returns the index where $value is located in data[], or -1 if it is not inserted, which happens if + Count=Capacity and $value is larger than or equal to the last entry in data[Capacity]. +*/ +int InsertInc(void* value, void** data, int Count, int Capacity, int (*Compare)(void*, void*)) +{ + if (Capacity<=0) return -1; + if (Count>Capacity) Count=Capacity; + +//Compare(A,B)<0 if A0 if A>B + int PosToInsert; + if (Count==0) PosToInsert=0; + else if (Compare(value, data[Count-1])>0) PosToInsert=Count; + else if (Compare(value, data[0])<0) PosToInsert=0; + else + { + //now Count>=2 + int head=0, end=Count-1, mid; + + //D(head)<=value1) + { + mid=(head+end)/2; + if (Compare(value, data[mid])>=0) head=mid; + else end=mid; + } + //D(head=end-1)<=value=Capacity) return -1; + memmove(&data[PosToInsert+1], &data[PosToInsert], sizeof(void*)*(Count-PosToInsert-1)); + data[PosToInsert]=value; + } + return PosToInsert; +}//InsertInc + +/* + function InsertInc: inserts value into sorted increasing list + + In: data[Count]: a sorted increasing list. + value: the value to be added + doinsert: specifies whether the actually insertion is to be performed + Out: data[Count]: the list with $value inserted if the latter is smaller than its last entry, in which + case the original last entry of data[] is discarded. + + Returns the index where $value is located in data[], or -1 if value is larger than or equal to + data[Count-1]. +*/ +int InsertInc(double value, double* data, int Count, bool doinsert) +{ + if (Count<=0) return -1; + if (value>=data[Count-1]) return -1; + if (value=2 + int head=0, end=Count-1, mid; + + //D(head)<=value1) + { + mid=(head+end)/2; + if (value>=data[mid]) head=mid; + else end=mid; + } + + //D(head=end-1)<=value=data[Count-1]) return -1; + if (value=2 + int head=0, end=Count-1, mid; + + //D(head)<=value1) + { + mid=(head+end)/2; + if (value>=data[mid]) head=mid; + else end=mid; + } + + //D(head=end-1)<=value=data[Count-1]) return -1; + if (value=2 + int head=0, end=Count-1, mid; + + //D(head)>=value>D(end) + while (end-head>1) + { + mid=(head+end)/2; + if (value>=data[mid]) head=mid; + else end=mid; + } + + //D(head=end-1)>=value>D(end) + memmove(&data[end+1], &data[end], sizeof(double)*(Count-end-1)); + memmove(&indices[end+1], &indices[end], sizeof(int)*(Count-end-1)); + data[end]=value, indices[end]=index; + return end; +}//InsertInc +//version where indices[] is double-precision floating point. +int InsertInc(double value, double index, double* data, double* indices, int Count) +{ + if (Count<=0) return -1; + if (value>=data[Count-1]) return -1; + if (value=2 + int head=0, end=Count-1, mid; + + //D(head)>=value>D(end) + while (end-head>1) + { + mid=(head+end)/2; + if (value>=data[mid]) head=mid; + else end=mid; + } + + //D(head=end-1)>=value>D(end) + memmove(&data[end+1], &data[end], sizeof(double)*(Count-end-1)); + memmove(&indices[end+1], &indices[end], sizeof(double)*(Count-end-1)); + data[end]=value, indices[end]=index; + return end; +}//InsertInc + +/* + function InsertIncApp: inserts value into flexible-length sorted increasing list + + In: data[Count]: a sorted increasing list. + value: the value to be added + Out: data[Count+1]: the list with $value inserted. + + Returns the index where $value is located in data[], or -1 if Count<0. data[] must have Count+1 + storage units on calling. +*/ +int InsertIncApp(double value, double* data, int Count) +{ + if (Count<0) return -1; + if (Count==0){data[0]=value; return 0;} + if (value>=data[Count-1]){data[Count]=value; return Count;} + if (value=2 + int head=0, end=Count-1, mid; + + //D(head)<=value1) + { + mid=(head+end)/2; + if (value>=data[mid]) head=mid; + else end=mid; + } + + //D(head=end-1)<=valuehwid-2) k=hwid-2; + double hr=0.5*(x[k].x-0.5*(x[k+1].x+x[k-1].x)), hi=0.5*(x[k].y-0.5*(x[k+1].y+x[k-1].y)); + double ph0=Atan2(hi, hr); + double c=cos(M_PI/hwid), s=sin(M_PI/hwid); + hr=0.5*(x[k].x-0.5*(x[k+1].x*c-x[k+1].y*s+x[k-1].x*c+x[k-1].y*s)); + hi=0.5*(x[k].y-0.5*(x[k+1].y*c+x[k+1].x*s+x[k-1].y*c-x[k-1].x*s)); + double ph1=Atan2(hi, hr); + result=(ph1-ph0)/(2*M_PI); + if (result<-0.5) result+=1; + if (result>0.5) result-=1; + result+=k*0.5/hwid; + break; + } + case 2: + break; + } + return result; +}//InstantFreq + +/* + function InstantFreq; calculates "frequency spectrum", a sequence of frequencies evaluated at DFT bins + + In: x[hwid]: spectrum with scale 2hwid + mode: must be 1. + Out: freqspec[hwid]: the frequency spectrum + + No return value. +*/ +void InstantFreq(double* freqspec, int hwid, cdouble* x, int mode) +{ + for (int i=0; icount-2) anindex=count-2; + + if (data[anindex]>data[anindex-1] && data[anindex]>data[anindex+1]) return anindex; + + if (data[anindex]0 && data[downind-1]>data[downind]) downind--; + if (data[anindex]upind-anindex) return upind; + else if (data[upind]laste) lmd+=step*lastdel, step/=2; + else lmd+=-step*sample*del; + } + while (e && iter<=maxiter && (!laste || fabs(laste-e)/e>eps)); + return sqrt(e/Count); +}//NegativeExp + +//--------------------------------------------------------------------------- +/* + function: NL: noise level, calculated on 5% of total frames with least energy + + In: data[Count]: + Wid: window width for power level estimation + + Returns noise level, in rms. +*/ +double NL(double* data, int Count, int Wid) +{ + int Fr=Count/Wid; + int Num=Fr/20+1; + double* ene=new double[Num], tmp; + for (int i=0; imax) max=*ldata; + else if (-*ldata>max) max=-*ldata; + ldata++; + } + if (max>0) + { + Maxi=Maxi/max; + for (int i=0; iAmp, Count); + RFFTC(Amp, Amp, Arg, log2(Count*Pad), w, x, 0); + + SmoothPhase(Arg, Count*Pad/2+1); + double result=Arg[Count*Pad/2]-Arg[0]; + delete[] Arg; + FreeFFTBuffer(Amp); + return result; +}//PhaseSpan + +//--------------------------------------------------------------------------- +/* + function PolyFit: least square polynomial fitting y=sum(i){a[i]*x^i} + + In: x[N], y[N]: sample points + P: order of polynomial, Pmod/2) in-=mod; + if (in<-mod/2) in+=mod; + return in; +}//Res + +//--------------------------------------------------------------------------- +/* + function Romberg: Romberg algorithm for numerical integration + + In: f: function to integrate + params: extra argument for calling f + a, b: integration boundaries + n: depth of sampling + + Returns the integral of f(*, params) over [a, b]. +*/ +double Romberg(int n, double(*f)(double, void*), double a, double b, void* params) +{ + int np=1; + double* r1=new double[n+1];. + double* r2=new double[n+1]; + double h=b-a, *swp; + r1[1]=h*(f(a, params)+f(b, params))/2; + for (int i=2; i<=n; i++) + { + double akh=a+0.5*h; r2[1]=f(akh, params); + for (int k=2; k<=np; k++) {akh+=h; r2[1]+=f(akh, params);} //akh=a+(k-0.5)h + r2[1]=0.5*(r1[1]+h*r2[1]); + double fj=4; + for (int j=2; j<=i; j++) {r2[j]=(fj*r2[j-1]-r1[j-1])/(fj-1); fj*=4;} //fj=4^(j-1) + h/=2; np*=2; + swp=r1; r1=r2; r2=swp; + } + h=r1[n]; + delete[] r1; + delete[] r2; + return h; +}//Romberg + +/* + function Romberg: Romberg algorithm for numerical integration, may return before specified depth on + convergence. + + In: f: function to integrate + params: extra argument for calling f + a, b: integration boundaries + n: depth of sampling + ep: convergence test threshold + + Returns the integral of f(*, params) over [a, b]. +*/ +double Romberg(double(*f)(double, void*), double a, double b, void* params, int n, double ep) +{ + int i, np=1; + double* r1=new double[n+1]; + double* r2=new double[n+1]; + double h=b-a, *swp; + r1[1]=h*(f(a, params)+f(b, params))/2; + bool mep=false; + for (i=2; i<=n; i++) + { + double akh=a+0.5*h; r2[1]=f(akh, params); + for (int k=2; k<=np; k++) {akh+=h; r2[1]+=f(akh, params);} //akh=a+(k-0.5)h + r2[1]=0.5*(r1[1]+h*r2[1]); + double fj=4; + for (int j=2; j<=i; j++) {r2[j]=(fj*r2[j-1]-r1[j-1])/(fj-1); fj*=4;} //fj=4^(j-1) + + if (fabs(r2[i]-r1[i-1])Count; i++) + { + int lx1, lx2, ly1, ly2; + lx1=(Spans->Items[i].T1-TOffst)/HWid-1; + lx2=(Spans->Items[i].T2-1-TOffst)/HWid; + ly1=Spans->Items[i].F1*2/Sps*HWid+0.001; + ly2=Spans->Items[i].F2*2/Sps*HWid+1; + if (lx1<0) lx1=0; + if (lx2>=Fr) lx2=Fr-1; + if (ly1<0) ly1=0; + if (ly1>HWid) ly1=HWid; + if (Pass) + for (int x=lx1; x<=lx2; x++) + for (int y=ly1; y<=ly2; y++) + lspan[x][y]=1; + else + for (int x=lx1; x<=lx2; x++) + for (int y=ly1; y<=ly2; y++) + lspan[x][y]=0; + } + for (int i=0; i0) + for (int k=0; k0) + for (int k=0; k0) + for (int k=0; kCount; i++) + { + int lx1, lx2, ly1, ly2; + lx1=(Spans->Items[i].T1-TOffst)/HWid-1; + lx2=(Spans->Items[i].T2-1-TOffst)/HWid; + ly1=Spans->Items[i].F1*2/Sps*HWid+0.001; + ly2=Spans->Items[i].F2*2/Sps*HWid+1; + if (lx1<0) lx1=0; + if (lx2>=Fr) lx2=Fr-1; + if (ly1<0) ly1=0; + if (ly1>HWid) ly1=HWid; + if (Pass) + for (int x=lx1; x<=lx2; x++) + for (int y=ly1; y<=ly2; y++) + lspan[x][y]=1; + else + for (int x=lx1; x<=lx2; x++) + for (int y=ly1; y<=ly2; y++) + lspan[x][y]=0; + } + for (int i=0; i0) + for (int k=0; k0) + for (int k=0; k0) + for (int k=0; k +#include +#include +#include "fft.h" +#include "windowfunctions.h" + +/* + macro testnn: non-negative test. This macro throws out an exception if the value of x is negative. +*/ +#ifndef testnn +#define testnn(x) {try{if (x<0) throw("testnn");}catch(...){x=0;}} +#endif + +/* + macro AllocateGMM and ReleaseGMM: allocates and frees buffers that hosts descriptors of a Gaussian- + mixture model. +*/ +#define AllocateGMM(_MIXS, _DIM, _P, _M, _DEV) \ + double* _P=new double[_MIXS]; double** _M=new double*[_MIXS]; double ** _DEV=new double*[_MIXS]; \ + _M[0]=new double[(_MIXS)*(_DIM)]; _DEV[0]=new double[(_MIXS)*(_DIM)]; \ + for (int _MIX=1; _MIX<_MIXS; _MIX++) _M[_MIX]=&_M[0][_MIX*(_DIM)], _DEV[_MIX]=&_DEV[0][_MIX*(_DIM)]; +#define ReleaseGMM(_P, _M, _DEV) \ + delete[] _P; if (_M) {delete[] _M[0]; delete[] _M;} if (_DEV) {delete[] _DEV[0]; delete[] _DEV;} + + +//--------------------------------------------------------------------------- +/* + Tick count tool (stop watch) is made up of a TickClock struct and three macros that uses this + structure as augument. + + Use of TickClock: declare the instance directly. Use StartClock() and StopClock() to start and stop + tick counting. Each time the clock is stopped the interval between last start and stop operations is + added to run-time t. Clear the recorded run-time with ResetClock(). +*/ +struct TickClock //tick counter struct +{ + double t; //accumulated run time + __int64 Ticks; //tick count recorded at start trigger + __int64 Ticks2; //tick count recorded at stop trigger +}; +#define ResetClock(AClock) {AClock.t=0;} +#define StartClock(AClock) {AClock.Ticks=GetTickCount();} +#define StopClock(AClock) {AClock.Ticks2=GetTickCount(); AClock.t+=0.001*(AClock.Ticks2-AClock.Ticks);} + +//--------------------------------------------------------------------------- + +struct fftparams //buffers used by FFT routines +{ + double* Amp; //amplitude output buffer + double* Arg; //phase angle output buffer + cdouble* w; //twiddle factor buffer + cdouble* x; //data buffer +}; + +struct TGMM //Gaussian mixture model +{ +public: + bool mcs; + int mixs; //number of mixtures + int dim; //data dimension + double* p; //mixture weights + double** m; //mixture component centres + double** dev; //diagonal mixture component correlation + double acct; + TGMM(); + ~TGMM(); +}; + +struct TTFSpan //a rectanguar area in the T-F plane +{ + int T1; //start time + int T2; //finish time + double F1; //lowest frequency + double F2; //highest frequency +}; + +struct TSpecPeak //spectral peak +{ + int t; //time in samples + int tres; //time resolution + double f; //digital frequency + double fres; //frequency resolution + double df; //derivative of f + double am; //amplitude + double ph; //phase angle +}; + +/* + TSpecTrack is a class that maintains a list of TSpecPeak objects that form a spectral track. It models + a sinusid track in sinusoid modeling. +*/ +class TSpecTrack +{ +private: + int Capacity; //number of peaks the current storage space is allocated to host +public: + int t1; //time of first peak + int t2; //time of last peak + double fmin; //minimum peak frequency + double fmax; //maximum peak frequency + + int Count; //number of peaks in the track + TSpecPeak* Peaks; //peaks in the track, ordered by time + + TSpecTrack(int ACapacity=50); + ~TSpecTrack(); + + void InsertPeak(TSpecPeak& APeak, int index); + int LocatePeak(TSpecPeak& APeak); + int Add(TSpecPeak& APeak); +}; + +/* + TTFSpans is a class that maintains a list of TFSpans. This is used to mark selected areas in the time- + frequency plane for further processing. +*/ +class TTFSpans +{ +public: + int Count; //number of spans + int Capacity; //number of spans the current storage space is allocated to host + TTFSpan* Items; //the hosted spans + + TTFSpans(); + ~TTFSpans(); + void Add(TTFSpan& ATFSpan); + void Clear(); + int Delete(int Index); +}; + +double ACPower(double* data, int Count, void*); +void Add(double* dest, double* source, int Count); +void Add(double* out, double* addend, double* adder, int count); +void ApplyWindow(double* OutBuffer, double* Buffer, double* Weights, int Size); +double Avg(double*, int, void*); +void AvgFilter(double* dataout, double* data, int Count, int HWid); +int BitInv(int value, int order); +void CalculateSpectrogram(double* data, int Count, int start, int end, int Wid, int Offst, double* Window=0, double** Spec=0, double** Ph=0, double amp=1, bool half=true); +void Conv(double* out, int N1, double* in1, int N2, double* in2); +void DCT( double* output, double* input, int N); +void IDCT( double* output, double* input, int N); +double DCAmplitude(double*, int, void*); +double DCPower(double*, int, void*); +double ddIPHann(double, void*); +void DeDC(double* data, int Count, int HWid); +void DeDC_static(double* data, int Count); +void DoubleToInt(void* out, int BytesPerSample, double* in, int Count); +void DoubleToIntAdd(void* out, int BytesPerSample, double* in, int Count); +double DPower(double* data, int Count, void*); +double Energy(double* data, int Count); +double ExpOnsetFilter(double* dataout, double* data, int Count, double Tr, double Ta); +double ExpOnsetFilter_balanced(double* dataout, double* data, int Count, double Tr, double Ta, int bal=5); +double ExtractLinearComponent(double* dataout, double* data, int Count); +void FFTConv(double* dest, double* source1, int size1, double* source2, int size2, int zero=0, double* pre_buffer=NULL, double* post_buffer=NULL); +void FFTConv(unsigned char* dest, unsigned char* source1, int bps, int size1, double* source2, int size2, int zero=0, unsigned char* pre_buffer=NULL, unsigned char* post_buffer=NULL); +void FFTConv(double* dest, double* source1, int size1, double* source2, int size2, double* pre_buffer=NULL); +void FFTFilter(double* dataout, double* data, int Count, int Wid, int On, int Off); +void FFTFilterOLA(double* dataout, double* data, int Count, int Wid, int On, int Off, double* pre_buffer=NULL); +void FFTFilterOLA(unsigned char* dataout, unsigned char* data, int BytesPerSample, int Count, int Wid, int On, int Off, unsigned char* pre_buffer=NULL); +void FFTFilterOLA(double* dataout, double* data, int Count, double* amp, double* ph, int Wid, double* pre_buffer=NULL); +double FFTMask(double* dataout, double* data, int Count, int Wid, double DigiOn, double DigiOff, double maskcoef=1); +int FindInc(double value, double* data, int Count); +double Gaussian(int Dim, double* Vector, double* Mean, double* Dev, bool StartFrom1); +void HalfDCT(double* data, int Count, double* CC, int order); +double Hamming(double f, double T); +double Hann(double x, double N); +double HannSq(double x, double N); +int HxPeak2(double*& hps, double*& vhps, double (*F)(double, void*), double (*dF)(double, void*), double(*ddF)(double, void*), void* params, double st, double en, double epf=1e-6); +double I0(double x); +int InsertDec(double value, double* data, int Count); +int InsertDec(int value, int* data, int Count); +int InsertDec(double value, int index, double* data, int* indices, int Count); +int InsertInc(void* value, void** data, int Count, int (*Compare)(void*, void*)); +int InsertInc(double value, double* data, int Count, bool doinsert=true); +int InsertInc(double value, int index, double* data, int* indices, int Count); +int InsertInc(double value, double index, double* data, double* indices, int Count); +int InsertInc(double value, int* data, int Count, bool doinsert=true); +int InsertIncApp(double value, double* data, int Count); +double InstantFreq(int k, int hwid, cdouble* x, int mode=1); +void InstantFreq(double* freqspec, int hwid, cdouble* x, int mode=1); +void IntToDouble(double* out, void* in, int BytesPerSample, int Count); +double IPHannC(double f, cdouble* x, int N, int K1, int K2); +double IPHannC(double f, void* params); +double IPHannCP(double f, void* params); +void lse(double* x, double* y, int Count, double& a, double& b); +void memdoubleadd(double* dest, double* source, int count); +void MFCC(int FrameWidth, int NumBands, int Ceps_Order, double* Data, double* Bands, double* C, double* Amps, cdouble* W, cdouble* X); +double* MFCCPrepareBands(int NumberOfBands, int SamplesPerSec, int NumberOfBins); +void Multi(double* data, int count, double mul); +void Multi(double* out, double* in, int count, double mul); +void MultiAdd(double* out, double* in, double* adder, int count, double mul); +int NearestPeak(double* data, int count, int anindex); +double NegativeExp(double* x, double* y, int Count, double& lmd, int sample=1, double step=0.05, double eps=1e-6, int maxiter=50); +double NL(double* data, int Count, int Wid); +double Normalize(double* data, int Count, double Maxi=1); +double Normalize2(double* data, int Count, double Norm=1); +double nextdouble(double x, double dir); +double PhaseSpan(double* data, int Count, void*); +void PolyFit(int P, double* a, int N, double* x, double* y); +void Pow(double* data, int Count, double ex); +//int prevcand(candid** cands, int p, int n, int ap); +int Rectify(double* data, int Count, double th=0); +double Res(double in, double mod); +double Romberg(int n, double(*f)(double, void*), double a, double b, void* params=0); +double Romberg(double(*f)(double, void*), double a, double b, void* params=0, int maxiter=50, double ep=1e-6); +double sinca(double x); +double sincd_unn(double x, int N); +double SmoothPhase(double* Arg, int Count, int mpi=2); +//int SortCandid(candid cand, candid* cands, int newN); +double StiffB(double f0, double fm, int m); +double StiffFm(double f0, int m, double B); +double StiffF0(double fm, int m, double B); +double StiffM(double f0, double fm, double B); +void TFFilter(double* data, double* dataout, int Count, int Wid, TTFSpans* Spans, bool Pass, WindowType wt, double windp, int Sps, int TOffst=0); +void TFFilter(void* data, void* dataout, int BytesPerSample, int Count, int Wid, TTFSpans* Spans, bool Pass, WindowType wt, double windp, int Sps, int TOffst); +void TFMask(double* data, double* dataout, int Count, int Wid, TTFSpans* Spans, double masklevel, WindowType wt, double windp, int Sps, int TOffst); +void TFMask(void* data, void* dataout, int BytesPerSec, int Count, int Wid, TTFSpans* Spans, double masklevel, WindowType wt, double windp, int Sps, int TOffst); +//double dIPHannC(double f, void* params); +//void ddsIPWindowStiff(double& df0f0, double& df0B, double& dBB, double& df0, double& dB, double& y, double f0, double B, cdouble* x, int N, int M, double* c, double iH2, double hB, int maxp); +//void dsIPWindowStiff(double& df0, double& dB, double& y, double f0, double B, cdouble* x, int N, int M, double* c, double iH2, double hB, int maxp); +//double IPWindowStiff(double f0, double B, cdouble* x, int N, int M, double* c, double iH2, double* ffp, double* vfp, double* pfp, double hB, int maxp); +//double sIPWindowStiff(double f0, double B, cdouble* x, int N, int M, double* c, double iH2, double* ffp, double* vfp, double* pfp, double hB, int maxp); +//double sIPWindowStiff(double f0, double B, cdouble* x, int N, int M, double* c, double iH2, double hB, int maxp); +//double IPWindowMulti(double* f, int ss, cdouble* x, int N, int M, double* c, double iH2, int K1, int K2); + +#endif diff -r 9b9f21935f24 -r 6422640a802f splines.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/splines.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,247 @@ +//--------------------------------------------------------------------------- + + +#include "splines.h" + +//--------------------------------------------------------------------------- +/* + function tridiagonal: solves linear system A[N][N]x[N]=d[N] where A is tridiagonal + + In: tridiagonal matrix A[N][N] gives as three vectors - lower subdiagonal + a[1:N-1], main diagonal b[0:N-1], upper subdiagonal c[0:N-2] + vector d[N] + Out: vector d[N] now containing x[N] + + No return value. +*/ +void tridiagonal(int N, double* a, double* b, double* c, double* d) +{ + for (int k=1; k=0; k--) + d[k]=(d[k]-c[k]*d[k+1])/b[k]; +}//tridiagonal + +/* + function CubicSpline: computing piece-wise trinomial coefficients of a cubic spline + + In: x[N+1], y[N+1]: knots + bordermode: boundary mode of cubic spline + bordermode=0: natural: y''(x0)=y''(x_N)=0 + bordermode=1: quadratic runout: y''(x0)=y''(x1), y''(x_N)=y''(x_(N-1)) + bordermode=2: cubic runout: y''(x0)=2y''(x1)-y''(x2), the same at the end point + mode: spline format + mode=0: spline defined on the l'th piece as y(x)=a[l]x^3+b[l]x^2+c[l]x+d[l] + mode=1: spline defined on the l'th piece as y(x)=a[l]t^3+b[l]t^2+c[l]t+d[l], t=x-x[l] + Out: a[N],b[N],c[N],d[N]: piecewise trinomial coefficients + data[]: cubic spline computed for [xstart:xinterval:xend], if specified + No return value. On start d must be allocated no less than N+1 storage units. +*/ +void CubicSpline(int N, double* a, double* b, double* c, double* d, double* x, double* y, int bordermode, int mode, double* data, double xinterval, double xstart, double xend) +{ + double *h=new double[N]; + for (int i=0; ix[n+1]) n++; + if (mode==1) xx=xx-x[n]; + data[i]=d[n]+xx*(c[n]+xx*(b[n]+xx*a[n])); + } + } +}//CubicSpline + +/* + function CubicSpline: computing piece-wise trinomial coefficients of a cubic spline with uniformly placed knots + + In: y[N+1]: spline values at knots (0, h, 2h, ..., Nh) + bordermode: boundary mode of cubic spline + bordermode=0: natural: y''(x0)=y''(x_N)=0 + bordermode=1: quadratic runout: y''(x0)=y''(x1), y''(x_N)=y''(x_(N-1)) + bordermode=2: cubic runout: y''(x0)=2y''(x1)-y''(x2), the same at the end point + mode: spline format + mode=0: spline defined on the l'th piece as y(x)=a[l]x^3+b[l]x^2+c[l]x+d[l] + mode=1: spline defined on the l'th piece as y(x)=a[l]t^3+b[l]t^2+c[l]t+d[l], t=x-x[l] + Out: a[N],b[N],c[N],d[N]: piecewise trinomial coefficients + data[]: cubic spline computed for [xstart:xinterval:xend], if specified + No return value. On start d must be allocated no less than N+1 storage units. +*/ +void CubicSpline(int N, double* a, double* b, double* c, double* d, double h, double* y, int bordermode, int mode, double* data, double xinterval, double xstart, double xend) +{ + for (int i=0; i(n+1)*h) n++; + if (mode==1) xx=xx-n*h; + data[i]=d[n]+xx*(c[n]+xx*(b[n]+xx*a[n])); + } + } +}//CubicSpline + +/* + function Smooth_Interpolate: smoothly interpolate/extrapolate P-piece spline from P-1 values. The + interpolation scheme is chosen according to P: + P-1=1: constant + P-1=2: linear function + P-1=3: quadratic function + P-1>=4: cubic spline + + In: xind[P-1], x[P-1]: knot points + Out: y[N]: smoothly interpolated signal, y(xind[p])=x[p], p=0, ..., P-2. + + No return value. +*/ +void Smooth_Interpolate(double* y, int N, int P, double* x, double* xind) +{ + if (P==2) + { + for (int n=0; n +#include +#include "vibrato.h" +#include "arrayalloc.h" +#include "fft.h" +#include "hssf.h" +#include "Matrix.h" +#include "splines.h" +#include "xcomplex.h" +//--------------------------------------------------------------------------- +//method TVo::TVo: default constructor +TVo::TVo() +{ + memset(this, 0, sizeof(TVo)); + afres=8192; +}//TVo + +//method TVo::~TVo: default destructor +TVo::~TVo() +{ + free(peakfr); + delete[] lp; + delete[] F0; + if (fxr) DeAlloc2(fxr); + if (fres) DeAlloc2(fres); + if (LogASp) DeAlloc2(LogASp); +}//~TVo + +/* + method TVo::AllocateL: allocates or reallocates storage space whose size depends on L. This uses an + external L value for allocation and updates L itself. +*/ +void TVo::AllocateL(int AnL) +{ + L=AnL; + delete[] F0; + F0=new double[(L+2)*4]; + F0C=&F0[L+2], F0D=&F0[(L+2)*2], A0C=&F0[(L+2)*3]; +}//AllocateL + +/* + method TVo::AllocateP: allocates or reallocates storage space whose size depends on P, using the + interal P. +*/ +void TVo::AllocateP() +{ + peakfr=(int*)realloc(peakfr, sizeof(int)*P); + delete[] lp; lp=new double[(P+2)*3]; + Dp=&lp[P+2]; + Kp=(int*)&lp[(P+2)*2]; + if (LogASp) DeAlloc2(LogASp); Allocate2(double, P, M, LogASp); +}//AllocateP + +/* + method TVo::AllocatePK: allocates or reallocates storage space whose size depends on both P and K. +*/ +void TVo::AllocatePK() +{ + if (fxr) DeAlloc2(fxr); + Allocate2(double, P, 2*K+1, fxr); + memset(fxr[0], 0, sizeof(double)*P*(2*K+1)); + if (fres) DeAlloc2(fres); + Allocate2(double, P, K, fres); +}//AllocatePK + +/* + method TVo::GetOldP: returns the number of "equivalent" cycles of the whole duration. Although P-1 + cycles are delineated by lp[P], the parts before lp[0] and after lp[P-1] are also a part of the + harmonic sinusoid being modeled and therefore should be taken into account when necessary. +*/ +double TVo::GetOldP() +{ + return P-1+lp[0]/(lp[1]-lp[0])+(L-lp[P-1])/(lp[P-1]-lp[P-2]); +}//GetOldP + +/* + methodTVo::LoadFromFileHandle: reads TVo object from a file stream. +*/ +void TVo::LoadFromFileHandle(FILE* file) +{ + fread(&M, sizeof(int), 1, file); + fread(&L, sizeof(int), 1, file); + fread(&P, sizeof(int), 1, file); + fread(&K, sizeof(int), 1, file); + fread(&h, sizeof(double), 1, file); + fread(&afres, sizeof(int), 1, file); + AllocateL(L); AllocateP(); AllocatePK(); + fread(F0C, sizeof(double), L, file); + fread(A0C, sizeof(double), L, file); + fread(LogAF, sizeof(double), 4096, file); + fread(peakfr, sizeof(int), P, file); + fread(lp, sizeof(double), P, file); + fread(Dp, sizeof(double), P, file); + fread(Kp, sizeof(int), P, file); + fread(fxr[0], sizeof(double), P*(2*K+1), file); + fread(LogASp[0], sizeof(double), P*M, file); +}//LoadFromFileHandle + +/* + method TVo::ReAllocateL: reallocates storage space whose size depends on L, and transfer the contents. + This uses an external L value for allocation but does not update L itself. +*/ +void TVo::ReAllocateL(int newL) +{ + double* newF0=new double[(newL+2)*4]; + F0C=&newF0[newL+2], F0D=&newF0[(newL+2)*2], A0C=&newF0[(newL+2)*3]; + memcpy(A0C, &F0[(L+2)*3], sizeof(double)*(L+2)); + memcpy(F0D, &F0[(L+2)*2], sizeof(double)*(L+2)); + memcpy(F0C, &F0[L+2], sizeof(double)*(L+2)); + memcpy(newF0, F0, sizeof(double)*(L+2)); + delete[] F0; + F0=newF0; +}//ReAllocateL + +/* + method TVo::SaveToFile: saves a TVo object to a file. + + In: filename: path to the destination file. +*/ +void TVo::SaveToFile(char* filename) +{ + FILE* file=fopen(filename, "wb"); + if (file) + { + SaveToFileHandle(file); + fclose(file); + } + else + { + throw(strcat((char*)"Cannot open file ", filename)); + } +}//SaveToFile + +/* + method TVo::SaveToFileHandle: saves a TVo object to an open file stream. +*/ +void TVo::SaveToFileHandle(FILE* file) +{ + fwrite(&M, sizeof(int), 1, file); + fwrite(&L, sizeof(int), 1, file); + fwrite(&P, sizeof(int), 1, file); + fwrite(&K, sizeof(int), 1, file); + fwrite(&h, sizeof(double), 1, file); + fwrite(&afres, sizeof(int), 1, file); + fwrite(F0C, sizeof(double), L, file); + fwrite(A0C, sizeof(double), L, file); + fwrite(LogAF, sizeof(double), 4096, file); + fwrite(peakfr, sizeof(int), P, file); + fwrite(lp, sizeof(double), P, file); + fwrite(Dp, sizeof(double), P, file); + fwrite(Kp, sizeof(int), P, file); + fwrite(fxr[0], sizeof(double), P*(2*K+1), file); + fwrite(LogASp[0], sizeof(double), P*M, file); +}//SaveToFileHandle + +//--------------------------------------------------------------------------- +//functions + + +/* + function AnalyzeV: calculates all basic and non-basic descriptors of a vibrato from a harmonic + sinusoid + + In: HS: harmonic sinusoid to compute vibrato representation from + sps: sampling rate + h: hop size + SFMode: specifies source-filter estimation criterion, 0=FB (filter bank), 1=SV (slow variation) + SFF: filter response sampling interval + SFScale: set if filter sampling uses mel scale + SFtheta: balancing factor of amplitude and frequency variations, needed for SV approach + Out: V: a TVo object hosting vibrato descriptors (vibrato representation) of HS + cyclefrcount: number of cycles between cycle boundaries, equals V.P-1. + cyclefrs[cyclefrcount], cyclefs[cyclefrcount]: time (in frames) and F0 centroid of cycles. These are + knots from which V.F0C[] is interpolated. + peaky[V.P]: shape score of cycle peaks + + No return value. +*/ +void AnalyzeV(THS& HS, TVo& V, double*& peaky, double*& cyclefrs, double*& cyclefs, double sps, double h, int* cyclefrcount, + int SFMode, double SFF, int SFFScale, double SFtheta) +{ + int M=HS.M, Fr=HS.Fr; + if (M<=0 || Fr<=0) return; + atom** Partials=HS.Partials; + + //basic descriptor: h + V.h=h; + + //demodulating pitch + //Basic descriptor: L + V.AllocateL(Fr); + + double* AA=new double[Fr]; + //find the pitch track + V.F0max=0, V.F0min=1; + for (int fr=0; frV.F0[fr]) V.F0min=V.F0[fr]; + AA[fr]=suma2; + } + + //Basic descriptor: M + V.M=0.45/V.F0max; + if (V.M>M) V.M=M; + + //rough estimation of rate + double* f0d=new double[Fr-1]; for (int i=0; i=1) //de-modulation + { + //Basic descriptor: F0C[] + DeFM(V.F0C, V.F0, AA, V.P, V.peakfr, Fr, V.F0Overall, *cyclefrcount, cyclefrs, cyclefs, true); + //Basic descriptor: A0C[] + DeAM(V.A0C, AA, V.P, V.peakfr, Fr); + } + + V.F0Cmax=0; V.F0Dmax=-1; V.F0Cmin=1; V.F0Dmin=1; + for (int fr=0; frV.F0C[fr]) V.F0Cmin=V.F0C[fr]; + V.F0D[fr]=V.F0[fr]-V.F0C[fr]; + } + + V.D=0; int cyclestart=(V.P>=2)?V.peakfr[0]:0, cycleend=(V.P>=2)?V.peakfr[V.P-1]:Fr; + for (int fr=cyclestart; frlf0d) V.F0Dmin=lf0d; + V.D+=lf0d*lf0d; + } + V.D=sqrt(V.D/(cycleend-cyclestart)); + delete[] AA; + + //Calculate rate and regularity + //find the section used for calculating regularity and rate + { + int frst=0, fren=Fr-1; + while (frst0) frst++; + while (fren>frst && V.F0D[fren]*V.F0D[fren-1]>0) fren--; + + RateAndReg(V.rate, V.regularity, V.F0D, frst, fren, 8, sps, h); + } + + //Basic descriptor: P + //Basic descriptor: peakfr[] + periodinframe=sps/(V.rate*h); + FindPeaks(V.peakfr, V.P, V.F0D, Fr, periodinframe, peaky); + + //Basic descriptor: lp[] + V.AllocateP(); + for (int p=0; pperiodinframe/2) V.K=periodinframe/2; + + //single-cycle analyses + //Basic descriptor: Dp[] + V.AllocatePK(); + for (int p=1; p0) V.LogASp[p][m]/=ccount; + else V.LogASp[p][m]=0; + } + } + memset(V.FXR, 0, sizeof(double)*2*V.K); memset(V.FRes, 0, sizeof(double)*V.K); + double sumdp=0; for (int p=1; p0)?(kb/kka):(kb*0.1); + while (iterepx) + { + iter++; + th2pi+=dth2pi; + kb=kka=y=0; + for (int k=1; kmy) my=y, mth2pi=th2pi; + dth2pi=(kka>0)?(kb/kka):(kb*0.1); + } + if (iter>=maxiter || th2pi!=mth2pi) + { + th2pi=mth2pi; + } + for (int k=1; kf[pfr-1] && f[pfr]>f[pfr+1]) || (f[pfr]f[pfr] && f[pfr+1]>f[pfr+2]) || (f[pfr+1]F0[fr-1] && F0[fr]>F0[fr+1]) + { + peakfr[npfr++]=fr; + } + } + + bool localpeaky=(peaky==(double*)0xFFFFFFFF); if (!localpeaky) delete[] peaky; + + peaky=new double[npfr]; + for (int pfr=0; pfrperiodinframe/2) + { + double ef0=0; int frcount=0; for (int fr=peakfr[pfr]-periodinframe/2; frmpfr) impfr=pfr, mpfr=mp; + pfr++; + } + } + else + { + while (pfr0) {impfr=pfr; break;} + pfr++; + } + } + if (impfr>=0) peakfr[pen++]=peakfr[impfr]; + } + pst--; pfr=pst; + while (pfr>=0) + { + int impfr=-1; + while (pfr>=0 && peakfr[pfr]>peakfr[pst+1]-periodinframe*0.5) pfr--; + if (pfr>=0 && peakfr[pfr]>=peakfr[pst+1]-periodinframe*1.5) + { + double mpfr=peaky[pfr]+copeak(F0, peakfr, pfr, pst+1, periodinframe); + impfr=pfr; pfr--; + while (pfr>=0 && peakfr[pfr]>=peakfr[pst+1]-periodinframe*1.5) + { + double mp=peaky[pfr]+copeak(F0, peakfr, pfr, pst+1, periodinframe); + if (mp>mpfr) impfr=pfr, mpfr=mp; + pfr--; + } + } + else + { + while (pfr>=0) + { + if (peaky[pfr]>0) {impfr=pfr; break;} + pfr--; + } + } + if (impfr>=0) peakfr[pst--]=peakfr[impfr]; + } + pst++; + npfr=pen-pst; + memmove(peakfr, &peakfr[pst], sizeof(int)*npfr); //*/ + if (localpeaky) delete[] peaky; +}//FindPeaks + +/* + function FS: Fourier series decomposition + + In: data[frst:fren]: signal to decompose + period: period of Fourier series decomposition + shift: amount of original shift of Fourier components (from frst to frst-shift) + K: number of Fourier components requested + Out: K: number of Fourier components returned + FXR[K], FXI[K]: real and imaginary parts of Fourier series coefficients + FRES[K]: decomposition residues + + No return value. +*/ +void FS(int& K, double* FXR, double* FXI, double* FRES, double* data, int frst, int fren, double period, double shift) +{ + if (K>period/2) K=period/2; + int len=fren-frst+1; + double omg0=2*M_PI/period, ilen=1.0/len; + + //for (int fr=frst; fr<=fren; fr++) data[fr]=cos(2*M_PI*fr/period+1); + + double* res=new double[len]; memcpy(res, &data[frst], sizeof(double)*len); + double* rec=new double[len]; + double resene=0; for (int i=0; i0) xr/=eer; +// if (eei>0) xi/=eei; + FXR[k]=xr, FXI[k]=xi; + double lresene=0; + for (int fr=frst; fr<=fren; fr++) + { + double ph=omg*(fr-shift); +// rec[fr-frst]=xr*cos(ph)+xi*sin(ph); + if (k==0) rec[fr-frst]=xr*cos(ph)+xi*sin(ph); + else rec[fr-frst]=2*(xr*cos(ph)+xi*sin(ph)); + res[fr-frst]-=rec[fr-frst]; + lresene+=res[fr-frst]*res[fr-frst]; + } + resene=lresene; + FRES[k]=resene/ene; + } + delete[] rec; + delete[] res; +}//FS + +/* + function FS_QR: Fourier series decomposition with QR orthogonalization. Since the Fourier series is + applied on finite-length discrate signal, the Fourier components are no longer orthogonal to each + other. A decreasing residue can be guaranteed by applying QR (or any other) orthogonalization process + to the Fourier components before decomposition. + + In: data[0:Fr]: signal to decompose + period: period of Fourier series decomposition + shift: amount of original shift of Fourier components (from 0 to -shift) + K: number of Fourier components requested + Out: K: number of Fourier components returned + FXR[2K-1]: Fourier series coefficients, in the order of 0r, 1i, 1r, 2i, 2r, .... + FRES[K]: decomposition residues, optional + + No return value. +*/ +void FS_QR(int& K, double* FXR, double* data, int Fr, double period, double shift, double* FRES) +{ + int len=Fr+1; + if (K>period/2) K=period/2; + if (K>Fr/2) K=Fr/2; + if (K>len/2) K=len/2; + if (K<=0) return; + memset(FXR, 0, sizeof(double)*(2*K-1)); + + double omg0=2*M_PI/period, ene, resene; + + if (FRES) + { + ene=0; for (int i=0; iF0C=0; newV->A0C=0; newV->peakfr=0; newV->Dp=0; newV->lp=0, newV->Kp=0; + newV->fxr=0; newV->LogASp=0; newV->F0=0; newV->F0D=0; newV->fres=0; + + int inewP=ceil(newP+Pst); + newV->P=inewP; + + newV->peakfr=new int[inewP]; + newV->AllocateP(); newV->AllocatePK(); + + newV->lp[0]=V.lp[0]/rate; + newV->peakfr[0]=floor(newV->lp[0]+0.5); + for (int p=1; p=V.P) ip=V.P-1, rp=0; + if (fabs(rp)<1e-16) + { + newV->lp[p]=newV->lp[p-1]+(V.lp[ip]-V.lp[ip-1])/rate; + newV->Dp[p]=V.Dp[ip]; + newV->Kp[p]=V.Kp[ip]; + memcpy(newV->fxr[p], V.fxr[ip], sizeof(double)*2*newV->Kp[p]); + memcpy(newV->LogASp[p], V.LogASp[ip], sizeof(double)*V.M); + } + else + { + double fv1=1.0/(V.lp[ip]-V.lp[ip-1]), fv2=1.0/(V.lp[ip+1]-V.lp[ip]); + double fv=rate*(fv1*(1-rp)+fv2*rp); + newV->lp[p]=newV->lp[p-1]+1.0/fv; + newV->Dp[p]=V.Dp[ip]*(1-rp)+V.Dp[ip+1]*rp; + int minK=V.Kp[ip]; if (minK>V.Kp[ip+1]) minK=V.Kp[ip+1]; + for (int k=0; k<2*minK-1; k++) newV->fxr[p][k]=V.fxr[ip][k]*(1-rp)+V.fxr[ip+1][k]*rp; + for (int m=0; mLogASp[p][m]=V.LogASp[ip][m]*(1-rp)+V.LogASp[ip+1][m]*rp; + newV->Kp[p]=minK; + } + newV->peakfr[p]=floor(newV->lp[p]+0.5); + memset(newV->fres[0], 0, sizeof(double)*newV->P*V.K); + } + int newL=newV->lp[inewP-1]+(newP+Pst-(inewP-1))*(V.lp[V.P-1]-V.lp[V.P-2])/rate+0.5; + newV->AllocateL(newL); + + //F0C and A0C + if (V.L!=newL) + { + for (int l=0; lF0C[l]=V.F0C[il]*(1-rl)+V.F0C[il+1]*rl; + newV->A0C[l]=V.A0C[il]*(1-rl)+V.A0C[il+1]*rl; + } + } + else + { + memcpy(newV->F0C, V.F0C, sizeof(double)*V.L); + memcpy(newV->A0C, V.A0C, sizeof(double)*V.L); +// memcpy(newV->F0, V.F0, sizeof(double)*V.L); +// memcpy(newV->F0D, V.F0D, sizeof(double)*V.L); + } + return newV; +}//InterpoalteV + +//vibrato rate boundaries, in Hz +#define MAXMOD 15.0 +#define MINMOD 3.0 + +/* + function RateAndReg: evaluates modulation rate and regularity + + In: data[frst:fren]: modulated signal, time measured in frames + padrate: padding rate used for inverse FFT + sps: sampling frequency + offst: hop size + Out: rate, regularity: rate and regularity + + No return value. +*/ +void RateAndReg(double& rate, double& regularity, double* data, int frst, int fren, int padrate, double sps, double offst) +{ + rate=0; regularity=0; + //find the section used for calculating regularity and rate + int frlen=fren-frst+1, frlenp=1<<(int(log2(fren))+2), frlenpp=frlenp*padrate; + double *lf=new double[frlenpp*4]; + cdouble *w=(cdouble*)&lf[frlenpp]; + cdouble *x=(cdouble*)&lf[frlenpp*2]; + SetTwiddleFactors(frlenp, w); + + memset(lf, 0, sizeof(double)*frlenp); + memcpy(lf, &data[frst], sizeof(double)*frlen); +// for (int i=0; iV.Kp[p]) K=V.Kp[p]; + double D=0, *fxr=new double[2*K-1]; + memset(fxr, 0, sizeof(double)*(2*K-1)); + for (int p=1; p0.5 || Partials[m][l].f<0) Partials[m][l].f=-1, Partials[m][l].a=0; + else Partials[m][l].a=V.A0C[l]*exp(V.LogAS[m]+V.LogAF[int(Partials[m][l].f*V.afres)]); + } + } + + delete[] fxr; +}//RegularizeV + +/* + function QIE: computes extremum using quadratic interpolation + + In: y[-1:1]: three points to interpolate from. It is assumed y[0] is larger (or smaller) than both + y[-1] and y[1]. + + Returns the extremum of y. +*/ +double QIE(double* y) +{ + double a=0.5*(y[1]+y[-1])-y[0], b=0.5*(y[1]-y[-1]); + return y[0]-0.25*b*b/a; +}//QIE + +/* + function QIE: computes extremum using quadratic interpolation + + In: y[-1:1]: three points to interpolate from. It is assumed y[0] is larger (or smaller) than both + y[-1] and y[1]. + Out: x: the argument value that yields extremum of y(x) + + Returns the extremum of y. +*/ +double QIE(double* y, double& x) +{ + double a=0.5*(y[1]+y[-1])-y[0], b=0.5*(y[1]-y[-1]); + x=-0.5*b/a; + return y[0]-0.25*b*b/a; +}//QIE + +/* + function S_F: original source-filter estimation used in AES124. + + In: afres: filter response resolution + Partials[M][Fr]: HS partials + A0C[Fr]: partial-independent amplitude carrier + lp[P]: cycle boundaries + F0Overall: average fundamental frequency + Out: LogAF[afres/2]: filter response + LogAS[M]: source amplitudes + + Returns 0. +*/ +double S_F(int afres, double* LogAF, double* LogAS, int Fr, int M, atom** Partials, double* A0C, double* lp, int P, double F0Overall) +{ + //SOURCE-FILTER estimation routines + //derivative of log A(f), f is sampled at 1/8192 (5hz), averaged over (-15hz, 15hz) + double *DAF=new double[afres], *NAF=&DAF[afres/2]; + memset(DAF, 0, sizeof(double)*afres); + double avgwidth1, avgwidth2; + for (int fr=lp[1]; fr0 && Partials[m][fr-1].f>0 && Partials[m][fr+1].f>0) + { + double f_1=Partials[m][fr-1].f, f0=Partials[m][fr].f, f1=Partials[m][fr+1].f, a_1=Partials[m][fr-1].a/A0C[fr-1], a1=Partials[m][fr+1].a/A0C[fr+1]; + if ((f0-f_1)*(f0-f1)<0) + { + double dlogaf_df=(log(a1)-log(a_1))/(f1-f_1); //calculate derivative of log + + //this derivative contributes within a frequency interval of |f1-f_1| + if (f10) DAF[i]=DAF[i]/NAF[i]; //else NAF[i]=0; + int i=0, nbands=0, bandsstart[100], bandsendinc[100]; double mininaf=3; + while (i=afres/2) break; + bandsstart[nbands]=i; + while (i0) i++; if (i>=afres/2) {bandsendinc[nbands++]=i-1; break;} + if (NAF[i]<=0) while (NAF[i]=bandsstart[0]){LogAF[i]=LogAF[i+1]-0.5*(DAF[i]+DAF[i+1])/afres; i--;} + i=F0Overall*afres+1; + LogAF[i]=0.5*(1-theta)*(DAF[i]+DAF[i-1]*(1-theta)+DAF[i]*theta)/afres; i++; + while (i<=bandsendinc[0]){LogAF[i]=LogAF[i-1]+0.5*(DAF[i]+DAF[i-1])/afres; i++;} + + int band=1; + while (bandbandsstart[band-1] && (fbandsendinc[band-1])) continue; + int intf=floor(f); + double logaflast=LogAF[intf]+(DAF[intf]+0.5*(DAF[intf+1]-DAF[intf])*(f-intf))*(f-intf)/afres; + f=Partials[band][fr].f*afres; + if (bandsendinc[band]>bandsstart[band] && (fbandsendinc[band])) continue; + intf=floor(f); + double logafthis=LogAF[intf]+(DAF[intf]+0.5*(DAF[intf+1]-DAF[intf])*(f-intf))*(f-intf)/afres; + cshift=cshift+log(Partials[band][fr].a*(band+1)/(Partials[band-1][fr].a*band))+logaflast-logafthis; + ccount++; + } + cshift/=ccount; + //apply the shift + for (int i=bandsstart[band]; i<=bandsendinc[band]; i++) LogAF[i]+=cshift; + //make up the blank band + int lastend=bandsendinc[band-1], thisstart=bandsstart[band]; + for (int i=lastend+1; i0) LogAS[m]/=ccount; + else LogAS[m]=0; + } + return 0; +}//S_F + +/* + function S_F_SV: slow-variation source-filter estimation used in AES126, adapted from TSF to TVo. + + In: afres: filter response resolution + Partials[M][Fr]: HS partials + A0C[Fr]: partial-independent amplitude carrier + lp[P]: cycle boundaries + F: filter response sampling interval + FScaleMode: set if filter sampling uses mel scale + theta: balancing factor of amplitude and frequency variations, needed for SV approach + Out: LogAF[afres/2]: filter response + LogAS[M]: source amplitudes + + Returns 0. +*/ +double S_F_SV(int afres, double* LogAF, double* LogAS, int Fr, int M, atom** Partials, double* A0C, double* lp, int P, double F, int FScaleMode, double theta, double Fs) +{ + int lp0=lp[1], lpp=lp[P-1]; + int L=lpp-lp0; + Alloc2(L, M, loga); Alloc2(L, M, f); + double fmax=0; + for (int l=0; l0) loga[l][m]=log(Partials[m][fr].a); + else loga[l][m]=-100; + if (fmax0) LogAS[m]/=ccount; + else LogAS[m]=0; + } + + DeAlloc2(loga); DeAlloc2(f); DeAlloc2(h); + return 0; +}//S_F_SV + +/* + function SynthesizeV: synthesizes a harmonic sinusoid from vibrato descriptors + + In: V: a TVo object hosting vibrato descriptors + sps: sampling rate + UseK: maximal number of Fourier series components to use to construct frequency modulator + Out: HS: a harmonic sinusoid + + No return value. +*/ +void SynthesizeV(THS* HS, TVo* V, double sps, int UseK) +{ + int HSt0=HS->Partials[0][0].t, HSs0=HS->Partials[0][0].s, L=V->L, M=V->M, + P=V->P, *Kp=V->Kp, K=V->K, afres=V->afres; + double **fxr=V->fxr, *lp=V->lp, **LogASp=V->LogASp, *LogAF=V->LogAF, *Dp=V->Dp, + *F0C=V->F0C, *F0D=V->F0D, *F0=V->F0, *A0C=V->A0C, h=V->h; + HS->Resize(M, L); + double *gamp=new double[4*P], *gamm=&gamp[P]; + + Alloc2(P, K*2, newfxr); + int* newKp=new int[P]; + for (int p=1; p=2 && newKp[p]>UseK) newKp[p]=UseK; + CorrectFS(newKp[p], newfxr[p]); + } + + double f0, f1; + for (int p=1; pPartials; + for (int p=0; p0.5 || Partials[m][l].f<0) Partials[m][l].a=0; + else if (ilp==1) Partials[m][l].a=A0C[l]*exp(LogASp[1][m]*(1-0.5*theta)+LogASp[2][m]*0.5*theta+LogAF[int(Partials[m][l].f*afres)]); + else if (ilp==P-1) Partials[m][l].a=A0C[l]*exp(LogASp[ilp][m]*0.5*(1+theta)+LogASp[ilp-1][m]*0.5*(1-theta)+LogAF[int(Partials[m][l].f*afres)]); + else +// Partials[m][l].a=A0C[l]*exp(LogASp[ilp][m]*0.5+LogASp[ilp-1][m]*0.5*(1-theta)+LogASp[ilp+1][m]*0.5*theta+LogAF[int(Partials[m][l].f*afres)]); + Partials[m][l].a=A0C[l]*exp(LogASp[p][m]+LogAF[int(Partials[m][l].f*afres)]); + } + } + } + delete[] gamp; + DeAlloc2(newfxr); + delete[] newKp; +// VibratoDemoForm->Label13->Caption=L; +}//SynthesizeV + + + + + + diff -r 9b9f21935f24 -r 6422640a802f vibrato.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vibrato.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,99 @@ +#ifndef vibratoH +#define vibratoH + + +/* + vibrato.cpp - vibrato analysis and synthesis using harmonic sinusoids + + Further reading: Wen X. and M. Sandler, "Analysis and synthesis of audio vibrato using harmonic sinusoids," + in Proc. AES 124th Convention, Amsterdam, 2008. +*/ + +#include +#include "hs.h" +//--------------------------------------------------------------------------- + +/* + TVo is the data structure hosting descriptors of a vibrato representation of a harmonic sinusoid. Its + basic framework is shared by the TSF object which hosts a more elaborate source-filter model than TVo, + but does not look into vibrato-specific features such as modulator shape. + + An analysis/sythesis cycle converts THS to TVo and back. +*/ +struct TVo +{ + //basic characteristics + int M; //number of partials + int L; //number of frames + int P; //number of F0 peaks + double h; //hop size + double* F0C; //[0:L-1] pitch carrier + double* A0C; //[0:L-1] amplitude carreir + int afres; //filter model bins + double LogAF[4096]; //[0:afres-1] filter model + + int* peakfr; //[0:P-1] peak positions, in frames + double* lp; //[0:P-1] peak positions, refined to floating-point values in frames + double* Dp; //[1:P-1] single-cycle rms of F0D + int* Kp; //[1:P-1] order of single-cycle modulator shape descriptor + double** fxr; //[1:P-1][0:2K] single-cycle modulator shape coefficients - cosine, sine, cosine, ... + double** LogASp; //[1:P-1][0:M-1] single-cycle source model + int K; + + //other properties + double rate; //vibrato rate + double regularity;//vibrato regularity + double F0max; //maximal fundamental frequency + double F0min; //minimal fundamental frequency + double F0Cmax; //maximal fundamental carrier frequency + double F0Cmin; //minimal fundamental carrier frequency + double F0Overall; //overall average fundamental frequency + double F0Dmax; //maximal fundamental modulator frequency + double F0Dmin; //minimal fundamental modulator frequency + double* F0; //[0:L-1] pitch + double* F0D; //[0:L-1] pitch modulator + double D; //rms of F0D, + double LogAS[100];//[0:M-1] source model + double FXR[100]; //average cycle modulator shape coefficients + double FRes[50]; //average modulator residues + double** fres; //[1:P-1][0:K-1] single-cycle modulator residues + + TVo(); + ~TVo(); + void AllocateL(int AnL); + void ReAllocateL(int newL); + void AllocateP(); + void AllocatePK(); + void SaveToFile(char* filename); + void SaveToFileHandle(FILE* file); + void LoadFromFileHandle(FILE* file); + double GetOldP(); +}; + +//--tool procedures---------------------------------------------------------- +double QIE(double* y); +double QIE(double* y, double& x); + +//--demodulation routines---------------------------------------------------- +void DeFM(double* f2, double* f1, double* AA, int npfr, int* peakfr, int Fr, double& fct, int& fcount, double* &frs, double* &f, bool furthersmoothing=false); +void DeFM(double* f2, double* f1, double* AA, int npfr, double* lp, int Fr, double& fct, int& fcount, double* &frs, double* &f, bool furthersmoothing=false); +void DeAM(double* A2, double* A1, int npfr, int* peakfr, int Fr); +void DeAM(double* A2, double* A1, int npfr, double* lp, int Fr); + +//--source-filter analysis routine------------------------------------------- +double S_F(int afres, double* LogAF, double* LogAS, int Fr, int M, atom** Partials, double* A0C, double* lp, int P, double F0Overall); +double S_F_SV(int afres, double* LogAF, double* LogAS, int Fr, int M, atom** Partials, double* A0C, double* lp, int P, double F=0.005, int FScaleMode=0, double theta=0.5, double sps=44100); + +//--vibrato analysis and synthesis routines---------------------------------- +void AnalyzeV(THS& HS, TVo& V, double*& peaky, double*& cyclefrs, double*& cyclefs, double sps, double h, int* cyclefrcount=0, + int SFMode=0, double SFF=0.01, int SFFScale=0, double SFtheta=0); +void RegularizeV(THS& HS, TVo& V, double sps, double h); +void SynthesizeV(THS* HS, TVo* V, double sps, int UseK=0); +TVo* InterpolateV(double newP, double rate, TVo& V); + +//--other functions---------------------------------------------------------- +void FindPeaks(int* peakfr, int& npfr, double* F0, int Fr, double periodinframe, double*& peaky); +void FS_QR(int& K, double* FXR, double* data, int Fr, double period, double shift, double* FRES); +void RateAndReg(double& rate, double& regularity, double* data, int frst, int fren, int padrate, double sps, double offst); + +#endif diff -r 9b9f21935f24 -r 6422640a802f wavelet.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wavelet.cpp Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,1018 @@ +//--------------------------------------------------------------------------- + +#include +#include +#include "wavelet.h" +#include "matrix.h" + +//--------------------------------------------------------------------------- +/* + function csqrt: real implementation of complex square root z=sqrt(x) + + In: xr and xi: real and imaginary parts of x + Out: zr and zi: real and imaginary parts of z=sqrt(x) + + No return value. +*/ +void csqrt(double& zr, double& zi, double xr, double xi) +{ + if (xi==0) + if (xr>=0) zr=sqrt(xr), zi=0; + else zi=sqrt(-xr), zr=0; + else + { + double xm=sqrt(xr*xr+xi*xi); + double ri=sqrt((xm-xr)/2); + zr=xi/2/ri; + zi=ri; + } +}//csqrt + +/* + function Daubechies: calculates the Daubechies filter of a given order p + + In: filter order p + Out: h[2p]: the 2p FIR coefficients + + No reutrn value. The calculated filters are minimum phase, which means the energy concentrates at the + beginning. This is usually used for reconstruction. On the contrary, for wavelet analysis the filter + is mirrored. +*/ +void Daubechies(int p, double* h) +{ +//initialize h(z) + double r01=pow(2, -p-p+1.5); + + h[0]=1; + for (int i=1; i<=p; i++) + { + h[i]=h[i-1]*(p+1-i)/i; + } + + //construct polynomial p + double *P=new double[p], *rp=new double[p], *ip=new double[p]; + + P[p-1]=1; + double r02=1; + for (int i=p-1; i>0; i--) + { + double rate=(i+1-1.0)/(p-2.0+i+1); + P[i-1]=P[i]*rate; + r02/=rate; + } + Roots(p-1, P, rp, ip); + for (int i=0; i1) akr=bkr-dlr, aki=bki-dli; + double ak1=-2*akr, ak2=akr*akr+aki*aki; + h[p+2+i]=ak2*h[p+i]; + h[p+1+i]=ak2*h[p-1+i]+ak1*h[p+i]; + for (int j=p+i; j>1; j--) h[j]=h[j]+ak1*h[j-1]+ak2*h[j-2]; + h[1]=h[1]+ak1*h[0]; + r02/=ak2; + i++; + } + else //real root of P + { + double ak, bk=-(2*rp[i]-1), delk=4*rp[i]*(rp[i]-1); + if (bk>0) ak=bk-sqrt(delk); + else ak=bk+sqrt(delk); + r02/=ak; + h[p+1+i]=-ak*h[p+i]; + for (int j=p+i; j>0; j--) h[j]=h[j]-ak*h[j-1]; + } + } + delete[] P; delete[] rp; delete[] ip; + r01=r01*sqrt(r02); + for (int i=0; i-M; j--) + { + if (i-j-Mh; j--) + { + if (i-j-Mg; j--) + { + if (i-j=C) tmpa[i2]+=tmpla[ind-C]*h[j]; + else if (ind<0) tmpa[i2]+=tmpla[ind+C]*h[j]; + else tmpa[i2]+=tmpla[ind]*h[j]; + } + for (int j=sg; j<=eg; j++) + { + int ind=i-j; + if (ind>=C) tmpd[i2]+=tmpla[i-j-C]*g[j]; + else if (ind<0) tmpd[i2]+=tmpla[i-j+C]*g[j]; + else tmpd[i2]+=tmpla[i-j]*g[j]; + } + } + for (int i=0; i*2+11) + { + memset(tmp, 0, sizeof(double)*C*2); + //2k<1) + { + memset(tmp, 0, sizeof(double)*C*2); + //2k<=C*2) + { + tmp[ind-C*2]+=out[(2*i)<=C*2) + { + tmp[ind-C*2]+=out[(2*i+1)<1) + { + memset(tmp, 0, sizeof(double)*C*2); + //2k<=C*2) tmp[ind-C*2]+=out[(2*i)<=C*2) tmp[ind-C*2]+=out[(2*i+1)<=k>=0) +int Cmb(int n, int k) +{ + if (k>n/2) k=n-k; + int c=1; + for (int i=1; i<=k; i++) c=c*(n+1-i)/i; + return c; +} + +/* + function splinewl: computes spline biorthogonal wavelet filters. This version of splinewl calcualtes + the positive-time half of h and hm coefficients only. + + p1 and p2 must have the same parity. If p1 is even, p1 coefficients will be returned in h1; if p1 is + odd, p1-1 coefficients will be returned in h1. + + Actual length of h is p1+1, of hm is p1+2p2-1. only a half of each is kept. + p even: h[0:p1/2] <- [p1/2:p1], hm[0:p1/2+p2-1] <- [p1/2+p2-1:p1+2p2-2] + p odd: h[0:(p1-1)/2] <- [(p1+1)/2:p1], hm[0:(p1-3)/2+p2] <- [(p1-1)/2+p2:p1+2p2-2] + i.e. h[0:hp1] <- [p1-hp1:p1], hm[0:hp1+p2-1] <- [p1-hp1-1+p2:p1+2p2-2] + + In: p1, p2: specify vanishing moments of h and hm + Out: h[] and hm[] as specified above. + + No return value. +*/ +void splinewl(int p1, int p2, double* h, double* hm) +{ + int hp1=p1/2, hp2=p2/2; + int q=(p1+p2)/2; + h[hp1]=sqrt(2.0)*pow(2, -p1); +// h1[hp1]=1; + for (int i=1, j=hp1-1; i<=hp1; i++, j--) + { + h[j]=h[j+1]*(p1+1-i)/i; + } + + double *_hh1=new double[p2+1], *_hh2=new double[2*q]; + double *hh1=&_hh1[p2-hp2], *hh2=&_hh2[q]; + + hh1[hp2]=sqrt(2.0)*pow(2, -p2); + for (int i=1, j=hp2-1; i<=hp2; i++, j--) + { + hh1[j]=hh1[j+1]*(p2+1-i)/i; + } + if (p2%2) //p2 is odd + { + for (int i=0; i<=hp2; i++) hh1[-i-1]=hh1[i]; + } + else //p2 even + { + for (int i=1; i<=hp2; i++) hh1[-i]=hh1[i]; + } + + memset(_hh2, 0, sizeof(double)*2*q); + for (int n=1-q; n<=q-1; n++) + { + int k=abs(n); + int CC1=Cmb(q-1+k, k), CC2=Cmb(2*k, k-n); //CC=1.0*C(q-1+k, k)*C(2*k, k-n) + for (; k<=q-1; k++) + { + hh2[n]=hh2[n]+1.0*CC1*CC2*pow(0.25, k); + CC1=CC1*(q+k)/(k+1); + CC2=CC2*(2*k+1)*(2*k+2)/((k+1-n)*(k+1+n)); + } + hh2[n]*=pow(-1, n); + } + + //hh1[hp2-p2:hp2], hh2[1-q:q-1] + //h2=conv(hh1, hh2), but the positive half only + memset(hm, 0, sizeof(double)*(hp1+p2)); + for (int i=hp2-p2; i<=hp2; i++) + for (int j=1-q; j<=q-1; j++) + { + if (i+j>=0 && i+j=0 && i+j2*M + Out: spec[0][fr][WID], spec[1][2*fr][WID/2], ..., spec[ORDER-order-1][FR][wid] + + No return value. +*/ +void wavpacqmf(double*** spec, double* data, int Count, int WID, int wid, int M, double* h, double* g) +{ + int fr=Count/WID, ORDER=log2(WID), order=log2(wid); + double* _data1=new double[Count*2]; + double *data1=_data1, *data2=&_data1[Count]; + //the qmf always filters data1 and puts the results to data2 + memcpy(data1, data, sizeof(double)*Count); + int l=0, C=fr*WID, FR=1; + double *ldata, *ldataa, *ldatad; + while (l>1); + FR=(FR<<1); + if (l>=order) + { + for (int f=0; fM*2 + h[M], g[M], quadratic mirror filter pair + Out: data[Fr*Wid] + + No return value. +*/ +void iwavpacqmf(double* data, double** spec, int Fr, int Wid, int M, double* h, double* g) +{ + int Count=Fr*Wid, Order=log2(Wid); + double* _data1=new double[Count]; + double *data1, *data2, *ldata, *ldataa, *ldatad; + if (Order%2) data1=_data1, data2=data; + else data1=data, data2=_data1; + //data pass to buffer + for (int i=0, t=0; i0) + { + memset(data2, 0, sizeof(double)*Count); + for (int k=0; k>1); + } + delete[] _data1; +}//iwavpacqmf + +/* + function wavpac: calculate pseudo local cosine transforms using wavelet packet, + + In: data[Count], Count=fr*WID, waveform data + WID: largest scale, equals 2^ORDER + wid: smallest scale, euqals 2^order + h[hs:he-1], g[gs:ge-1]: filter pair + Out: spec[0][fr][WID], spec[1][2*fr][WID/2], ..., spec[ORDER-order-1][FR][wid] + + No return value. +*/ +void wavpac(double*** spec, double* data, int Count, int WID, int wid, double* h, int hs, int he, double* g, int gs, int ge) +{ + int fr=Count/WID, ORDER=log2(WID), order=log2(wid); + double* _data1=new double[Count*2]; + double *data1=_data1, *data2=&_data1[Count]; + //the qmf always filters data1 and puts the results to data2 + memcpy(data1, data, sizeof(double)*Count); + int l=0, C=fr*WID, FR=1; + double *ldata, *ldataa, *ldatad; + while (l=C) + { + ldataa[i2]+=ldata[ind-C]*h[j]; + } + else if (ind<0) + { + ldataa[i2]+=ldata[ind+C]*h[j]; + } + else + { + ldataa[i2]+=ldata[ind]*h[j]; + } + } + for (int j=gs; j<=ge; j++) + { + int ind=i-j; + if (ind>=C) + { + ldatad[i2]+=ldata[ind-C]*g[j]; + } + else if (ind<0) + { + ldatad[i2]+=ldata[ind+C]*g[j]; + } + else + { + ldatad[i2]+=ldata[ind]*g[j]; + } + } + } + } + double *tmp=data2; data2=data1; data1=tmp; + l++; + C=(C>>1); + FR=(FR<<1); + if (l>=order) + { + for (int f=0; f0) + { + memset(data2, 0, sizeof(double)*Count); + for (int k=0; k=C*2) + { + ldata[ind-C*2]+=ldataa[i]*h[j]; + } + else if (ind<0) + { + ldata[ind+C*2]+=ldataa[i]*h[j]; + } + else + { + ldata[ind]+=ldataa[i]*h[j]; + } + } + for (int j=gs; j<=ge; j++) + { + int ind=i*2+j; + if (ind>=C*2) + { + ldata[ind-C*2]+=ldatad[i]*g[j]; + } + else if (ind<0) + { + ldata[ind+C*2]+=ldatad[i]*g[j]; + } + else + { + ldata[ind]+=ldatad[i]*g[j]; + } + } + } + } + + double *tmp=data2; data2=data1; data1=tmp; + l--; + C=(C<<1); + K=(K>>1); + } + delete[] _data1; +}//iwavpac diff -r 9b9f21935f24 -r 6422640a802f wavelet.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wavelet.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,28 @@ +#ifndef waveletH +#define waveletH + +/* + wavelet.cpp - wavelet routines +*/ + +//--wavelet filter routines-------------------------------------------------- +void Daubechies(int p, double* h); //computes Daubechies filter +void splinewl(int p1, int p2, double* h1, double* h2); //compute spline biorthogonal wavelet filter +int splinewl(int p1, int p2, double* h, double* hm, double* g, double* gm, int normmode=0, int* points=0);//compute spline biorthogonal wavelet filter + +//--periodic DWT and IDWT---------------------------------------------------- +int dwtpqmf(double* in, int Count, int N, double* h, double* g, int M, double* out); +int dwtp(double* in, int Count, int N, double* h, double* g, int M, double* out); +int dwtp(double* in, int Count, int N, double* h, int Mh, double* g, int Mg, double* out); +int dwtp(double* in, int Count, int N, double* h, int sh, int eh, double* g, int sg, int eg, double* out); +void idwtp(double* in, int Count, int N, double* h, double* g, int M, double* out); +void idwtp(double* in, int Count, int N, double* h, int Mh, double* g, int Mg, double* out); +void idwtp(double* in, int Count, int N, double* h, int sh, int eh, double* g, int sg, int eg, double* out); + +//--pseudo local cosine with wavelet packet---------------------------------- +void wavpacqmf(double*** spec, double* data, int Count, int WID, int wid, int M, double* h, double* g); +void wavpac(double*** spec, double* data, int Count, int WID, int wid, double* h, int hs, int he, double* g, int gs, int ge); +void iwavpacqmf(double* data, double** spec, int Fr, int Wid, int M, double* h, double* g); +void iwavpac(double* data, double** spec, int Fr, int Wid, double* h, int hs, int he, double* g, int gs, int ge); + +#endif diff -r 9b9f21935f24 -r 6422640a802f xcomplex.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xcomplex.h Tue Oct 05 10:45:57 2010 +0100 @@ -0,0 +1,118 @@ +#ifndef XCOMPLEX +#define XCOMPLEX + + +/* + xcomplex.h - Xue's complex number class. + + This classes is modelled after standard c++ complex class, with a few additional methods. Unused + standard member and non-member functions/operators may not have been implemented. +*/ + +#include + +//* +template class cmplx +{ +public: +//standard members + T x; + T y; + cmplx(){} + cmplx(const T& c, const T& d) {x=c; y=d;} + cmplx(const T& c) {x=c; y=0;} + template cmplx(const cmplx& c){x=c.x, y=c.y;} + + T real(){return x;} + T imag(){return y;} + + cmplx& operator=(const T& c){x=c; y=0; return *this;} + template cmplx& operator+=(const X& c){x+=c; y=0; return *this;} + template cmplx& operator-=(const X& c){x-=c; y=0; return *this;} + template cmplx& operator*=(const X& c){x*=c; y*=c; return *this;} + template cmplx& operator/=(const X& c){x/=c; y/=c; return *this;} + template cmplx& operator=(const cmplx& c){x=c.x; y=c.y; return* this;} + + template cmplx& operator+=(const cmplx& c){x+=c.x; y+=c.y; return *this;} + template cmplx& operator-=(const cmplx& c){x-=c.x; y-=c.y; return *this;} + template cmplx& operator*=(const cmplx& c){T tmpx=x*c.x-y*c.y; y=x*c.y+y*c.x; x=tmpx; return *this;} + template cmplx& operator/=(const cmplx& c){if (c.y==0){x/=c.x; y/=c.x;} else if (c.x==0){T tmpx=y/c.y; y=-x/c.y; x=tmpx;} else {T xm=c.x*c.x+c.y*c.y; T tmpx=(c.x*x+c.y*y)/xm; y=(y*c.x-x*c.y)/xm; x=tmpx;} return *this;} + +//non-standard members + //operator~: square of absolute value + T operator~() {return x*x+y*y;} + //operator*: complex conjugate + cmplx operator*(){cmplx result; result.x=x; result.y=-y; return result;} + //operator^: multiplicaiton with the complex conjugate of the argument + cmplx operator^(const cmplx &b){cmplx result; result.x=x*b.x+y*b.y; result.y=y*b.x-x*b.y; return result;} + //cinv: complex reciprocal + cmplx cinv(){cmplx result; if (y==0) result.x=1/x, result.y=0; else if (x==0) result.y=-1/y, result.x=0; else{T xm=x*x+y*y; result.x=x/xm; result.y=-y/xm;} return result;} + //rotate: rotate by an angle + cmplx& rotate(const T ph) {double s=sin(ph), c=cos(ph), tmpx; tmpx=x*c-y*s; y=x*s+y*c; x=tmpx; return *this;} +}; //*/ +//standard Non-member Operators +template cmplx operator+(const cmplx& a, const cmplx& b){cmplx result=a; result+=b; return result;} +template cmplx operator+(const cmplx& a, Tb& b){cmplx result=a; result+=b; return result;} +template cmplx operator+(T a, const cmplx& b){cmplx result=a; result+=b; return result;} +template cmplx operator-(const cmplx& a, const cmplx& b){cmplx result=a; result-=b; return result;} +template cmplx operator-(const cmplx& a, Tb& b){cmplx result=a; result-=b; return result;} +template cmplx operator-(T a, const cmplx& b){cmplx result=a; result-=b; return result;} +template cmplx operator*(const cmplx& a, const cmplx& b){cmplx result=a; result*=b; return result;} +template cmplx operator*(const cmplx& a, Tb& b){cmplx result=a; result*=b; return result;} +template cmplx operator*(T& a, const cmplx& b){cmplx result=b; result*=a; return result;} +template cmplx operator/(const cmplx& a, const cmplx& b){cmplx result=a; result/=b; return result;} +template cmplx operator/(const cmplx& a, Tb& b){cmplx result=a; result/=b; return result;} +template cmplx operator/(T a, const cmplx& b){cmplx result=a; result/=b; return result;} +template cmplx operator+(const cmplx& a){return a;} +template cmplx operator-(const cmplx& a){cmplx result; result.x=-a.x; result.y=-a.y; return result;} +template bool operator==(const cmplx& a, const cmplx& b){return (a.x==b.x && a.y==b.y);} +template bool operator==(const cmplx& a, Tb b){return (a.x==b && a.y==0);} +template bool operator==(Ta a, const cmplx& b){return (a==b.x && 0==b.y);} +template bool operator!=(const cmplx& a, const cmplx& b){return (a.x!=b.x || a.y!=b.y);} +template bool operator!=(const cmplx& a, Tb b){return (a.x!=b || a.y!=0);} +template bool operator!=(Ta a, const cmplx& b){return (a!=b.x || 0!=b.y);} +/* +template basic_istream& operator>>(istream&, complex&); +template basic_ostream& operator<<(ostream&, const complex&); +*/ +//Values +template T real(const cmplx& a){return a.x;} +template T imag(const cmplx& a){return a.y;} +template T abs(const cmplx& a){return sqrt(a.x*a.x+a.y*a.y);} +template T fabs(const cmplx& a){return sqrt(a.x*a.x+a.y*a.y);} +template T arg(const cmplx& a){return (a.x==0 && a.y==0)?0:atan2(a.y, a.x);} +template T norm(const cmplx& a){return a.x*a.x+a.y*a.y;} +template cmplx conj(const cmplx& a){cmplx result; result.x=a.x; result.y=-a.y; return result;} +template cmplx polar(const T& r, const T& theta){cmplx result; result.x=r*cos(theta); result.y=r*sin(theta); return result;} +//Transcendentals +/* +template cmplx cos (const cmplx&); +template cmplx cosh (const cmplx&); +*/ +template cmplx exp(const cmplx& a){return polar(exp(a.x), a.y);} +template cmplx log(const cmplx& a){cmplx result; result.x=0.5*log(norm(a)); result.y=arg(a); return result;} +/* +template cmplx log10 (const cmplx&); +template cmplx pow (const cmplx&, int); +*/ +template cmplx pow(const cmplx& a, T& e){cmplx result; T rad=abs(a); T angle=arg(a); return polar(rad, angle*e);} +/* +template cmplx pow (const cmplx&, const cmplx&); +template cmplx pow (const T&, const cmplx&); +template cmplx sin (const cmplx&); +template cmplx sinh (const cmplx&); +*/ +template cmplx sqrt(const cmplx& a){cmplx result; if (a.y==0) {if (a.x>=0){result.x=sqrt(a.x); result.y=0;} else{result.y=sqrt(-a.x); result.x=0;}} else {result.y=sqrt((sqrt(a.x*a.x+a.y*a.y)-a.x)*0.5); result.x=0.5*a.y/result.y;} return result;} +/* +template cmplx tan (const cmplx&); +template cmplx tanh (const cmplx&); +*/ + +//non-standard non-member functions +//template operator^: multiplying one complex number with the complex conjugate of another +template cmplx operator^(const cmplx& a, const cmplx& b){cmplx result=a; result^=b; return result;} + +typedef cmplx cdouble; +typedef cmplx cfloat; + +#endif