xue@11: /* xue@11: Harmonic sinusoidal modelling and tools xue@11: xue@11: C++ code package for harmonic sinusoidal modelling and relevant signal processing. xue@11: Centre for Digital Music, Queen Mary, University of London. xue@11: This file copyright 2011 Wen Xue. xue@11: xue@11: This program is free software; you can redistribute it and/or xue@11: modify it under the terms of the GNU General Public License as xue@11: published by the Free Software Foundation; either version 2 of the xue@11: License, or (at your option) any later version. xue@11: */ xue@1: //--------------------------------------------------------------------------- xue@1: xue@1: #include Chris@2: #include xue@1: #include "vibrato.h" xue@1: #include "arrayalloc.h" xue@1: #include "fft.h" xue@1: #include "hssf.h" Chris@2: #include "matrix.h" xue@1: #include "splines.h" xue@1: #include "xcomplex.h" Chris@5: Chris@5: /** \file vibrato.h */ Chris@5: xue@1: //--------------------------------------------------------------------------- xue@1: //method TVo::TVo: default constructor xue@1: TVo::TVo() xue@1: { xue@1: memset(this, 0, sizeof(TVo)); xue@1: afres=8192; xue@1: }//TVo xue@1: xue@1: //method TVo::~TVo: default destructor xue@1: TVo::~TVo() xue@1: { xue@1: free(peakfr); xue@1: delete[] lp; xue@1: delete[] F0; xue@1: if (fxr) DeAlloc2(fxr); xue@1: if (fres) DeAlloc2(fres); xue@1: if (LogASp) DeAlloc2(LogASp); xue@1: }//~TVo xue@1: Chris@5: /** xue@1: method TVo::AllocateL: allocates or reallocates storage space whose size depends on L. This uses an xue@1: external L value for allocation and updates L itself. xue@1: */ xue@1: void TVo::AllocateL(int AnL) xue@1: { xue@1: L=AnL; xue@1: delete[] F0; xue@1: F0=new double[(L+2)*4]; xue@1: F0C=&F0[L+2], F0D=&F0[(L+2)*2], A0C=&F0[(L+2)*3]; xue@1: }//AllocateL xue@1: Chris@5: /** xue@1: method TVo::AllocateP: allocates or reallocates storage space whose size depends on P, using the xue@1: interal P. xue@1: */ xue@1: void TVo::AllocateP() xue@1: { xue@1: peakfr=(int*)realloc(peakfr, sizeof(int)*P); xue@1: delete[] lp; lp=new double[(P+2)*3]; xue@1: Dp=&lp[P+2]; xue@1: Kp=(int*)&lp[(P+2)*2]; xue@1: if (LogASp) DeAlloc2(LogASp); Allocate2(double, P, M, LogASp); xue@1: }//AllocateP xue@1: Chris@5: /** xue@1: method TVo::AllocatePK: allocates or reallocates storage space whose size depends on both P and K. xue@1: */ xue@1: void TVo::AllocatePK() xue@1: { xue@1: if (fxr) DeAlloc2(fxr); xue@1: Allocate2(double, P, 2*K+1, fxr); xue@1: memset(fxr[0], 0, sizeof(double)*P*(2*K+1)); xue@1: if (fres) DeAlloc2(fres); xue@1: Allocate2(double, P, K, fres); xue@1: }//AllocatePK xue@1: Chris@5: /** xue@1: method TVo::GetOldP: returns the number of "equivalent" cycles of the whole duration. Although P-1 xue@1: cycles are delineated by lp[P], the parts before lp[0] and after lp[P-1] are also a part of the xue@1: harmonic sinusoid being modeled and therefore should be taken into account when necessary. xue@1: */ xue@1: double TVo::GetOldP() xue@1: { xue@1: return P-1+lp[0]/(lp[1]-lp[0])+(L-lp[P-1])/(lp[P-1]-lp[P-2]); xue@1: }//GetOldP xue@1: Chris@5: /** xue@1: methodTVo::LoadFromFileHandle: reads TVo object from a file stream. xue@1: */ xue@1: void TVo::LoadFromFileHandle(FILE* file) xue@1: { xue@1: fread(&M, sizeof(int), 1, file); xue@1: fread(&L, sizeof(int), 1, file); xue@1: fread(&P, sizeof(int), 1, file); xue@1: fread(&K, sizeof(int), 1, file); xue@1: fread(&h, sizeof(double), 1, file); xue@1: fread(&afres, sizeof(int), 1, file); xue@1: AllocateL(L); AllocateP(); AllocatePK(); xue@1: fread(F0C, sizeof(double), L, file); xue@1: fread(A0C, sizeof(double), L, file); xue@1: fread(LogAF, sizeof(double), 4096, file); xue@1: fread(peakfr, sizeof(int), P, file); xue@1: fread(lp, sizeof(double), P, file); xue@1: fread(Dp, sizeof(double), P, file); xue@1: fread(Kp, sizeof(int), P, file); xue@1: fread(fxr[0], sizeof(double), P*(2*K+1), file); xue@1: fread(LogASp[0], sizeof(double), P*M, file); xue@1: }//LoadFromFileHandle xue@1: Chris@5: /** xue@1: method TVo::ReAllocateL: reallocates storage space whose size depends on L, and transfer the contents. xue@1: This uses an external L value for allocation but does not update L itself. xue@1: */ xue@1: void TVo::ReAllocateL(int newL) xue@1: { xue@1: double* newF0=new double[(newL+2)*4]; xue@1: F0C=&newF0[newL+2], F0D=&newF0[(newL+2)*2], A0C=&newF0[(newL+2)*3]; xue@1: memcpy(A0C, &F0[(L+2)*3], sizeof(double)*(L+2)); xue@1: memcpy(F0D, &F0[(L+2)*2], sizeof(double)*(L+2)); xue@1: memcpy(F0C, &F0[L+2], sizeof(double)*(L+2)); xue@1: memcpy(newF0, F0, sizeof(double)*(L+2)); xue@1: delete[] F0; xue@1: F0=newF0; xue@1: }//ReAllocateL xue@1: Chris@5: /** xue@1: method TVo::SaveToFile: saves a TVo object to a file. xue@1: xue@1: In: filename: path to the destination file. xue@1: */ xue@1: void TVo::SaveToFile(char* filename) xue@1: { xue@1: FILE* file=fopen(filename, "wb"); xue@1: if (file) xue@1: { xue@1: SaveToFileHandle(file); xue@1: fclose(file); xue@1: } xue@1: else xue@1: { xue@1: throw(strcat((char*)"Cannot open file ", filename)); xue@1: } xue@1: }//SaveToFile xue@1: Chris@5: /** xue@1: method TVo::SaveToFileHandle: saves a TVo object to an open file stream. xue@1: */ xue@1: void TVo::SaveToFileHandle(FILE* file) xue@1: { xue@1: fwrite(&M, sizeof(int), 1, file); xue@1: fwrite(&L, sizeof(int), 1, file); xue@1: fwrite(&P, sizeof(int), 1, file); xue@1: fwrite(&K, sizeof(int), 1, file); xue@1: fwrite(&h, sizeof(double), 1, file); xue@1: fwrite(&afres, sizeof(int), 1, file); xue@1: fwrite(F0C, sizeof(double), L, file); xue@1: fwrite(A0C, sizeof(double), L, file); xue@1: fwrite(LogAF, sizeof(double), 4096, file); xue@1: fwrite(peakfr, sizeof(int), P, file); xue@1: fwrite(lp, sizeof(double), P, file); xue@1: fwrite(Dp, sizeof(double), P, file); xue@1: fwrite(Kp, sizeof(int), P, file); xue@1: fwrite(fxr[0], sizeof(double), P*(2*K+1), file); xue@1: fwrite(LogASp[0], sizeof(double), P*M, file); xue@1: }//SaveToFileHandle xue@1: xue@1: //--------------------------------------------------------------------------- xue@1: //functions xue@1: xue@1: Chris@5: /** xue@1: function AnalyzeV: calculates all basic and non-basic descriptors of a vibrato from a harmonic xue@1: sinusoid xue@1: xue@1: In: HS: harmonic sinusoid to compute vibrato representation from xue@1: sps: sampling rate xue@1: h: hop size xue@1: SFMode: specifies source-filter estimation criterion, 0=FB (filter bank), 1=SV (slow variation) xue@1: SFF: filter response sampling interval xue@1: SFScale: set if filter sampling uses mel scale xue@1: SFtheta: balancing factor of amplitude and frequency variations, needed for SV approach xue@1: Out: V: a TVo object hosting vibrato descriptors (vibrato representation) of HS xue@1: cyclefrcount: number of cycles between cycle boundaries, equals V.P-1. xue@1: cyclefrs[cyclefrcount], cyclefs[cyclefrcount]: time (in frames) and F0 centroid of cycles. These are xue@1: knots from which V.F0C[] is interpolated. xue@1: peaky[V.P]: shape score of cycle peaks xue@1: xue@1: No return value. xue@1: */ xue@1: void AnalyzeV(THS& HS, TVo& V, double*& peaky, double*& cyclefrs, double*& cyclefs, double sps, double h, int* cyclefrcount, xue@1: int SFMode, double SFF, int SFFScale, double SFtheta) xue@1: { xue@1: int M=HS.M, Fr=HS.Fr; xue@1: if (M<=0 || Fr<=0) return; xue@1: atom** Partials=HS.Partials; xue@1: xue@1: //basic descriptor: h xue@1: V.h=h; xue@1: xue@1: //demodulating pitch xue@1: //Basic descriptor: L xue@1: V.AllocateL(Fr); xue@1: xue@1: double* AA=new double[Fr]; xue@1: //find the pitch track xue@1: V.F0max=0, V.F0min=1; xue@1: for (int fr=0; frV.F0[fr]) V.F0min=V.F0[fr]; xue@1: AA[fr]=suma2; xue@1: } xue@1: xue@1: //Basic descriptor: M xue@1: V.M=0.45/V.F0max; xue@1: if (V.M>M) V.M=M; xue@1: xue@1: //rough estimation of rate xue@1: double* f0d=new double[Fr-1]; for (int i=0; i=1) //de-modulation xue@1: { xue@1: //Basic descriptor: F0C[] xue@1: DeFM(V.F0C, V.F0, AA, V.P, V.peakfr, Fr, V.F0Overall, *cyclefrcount, cyclefrs, cyclefs, true); xue@1: //Basic descriptor: A0C[] xue@1: DeAM(V.A0C, AA, V.P, V.peakfr, Fr); xue@1: } xue@1: xue@1: V.F0Cmax=0; V.F0Dmax=-1; V.F0Cmin=1; V.F0Dmin=1; xue@1: for (int fr=0; frV.F0C[fr]) V.F0Cmin=V.F0C[fr]; xue@1: V.F0D[fr]=V.F0[fr]-V.F0C[fr]; xue@1: } xue@1: xue@1: V.D=0; int cyclestart=(V.P>=2)?V.peakfr[0]:0, cycleend=(V.P>=2)?V.peakfr[V.P-1]:Fr; xue@1: for (int fr=cyclestart; frlf0d) V.F0Dmin=lf0d; xue@1: V.D+=lf0d*lf0d; xue@1: } xue@1: V.D=sqrt(V.D/(cycleend-cyclestart)); xue@1: delete[] AA; xue@1: xue@1: //Calculate rate and regularity xue@1: //find the section used for calculating regularity and rate xue@1: { xue@1: int frst=0, fren=Fr-1; xue@1: while (frst0) frst++; xue@1: while (fren>frst && V.F0D[fren]*V.F0D[fren-1]>0) fren--; xue@1: xue@1: RateAndReg(V.rate, V.regularity, V.F0D, frst, fren, 8, sps, h); xue@1: } xue@1: xue@1: //Basic descriptor: P xue@1: //Basic descriptor: peakfr[] xue@1: periodinframe=sps/(V.rate*h); xue@1: FindPeaks(V.peakfr, V.P, V.F0D, Fr, periodinframe, peaky); xue@1: xue@1: //Basic descriptor: lp[] xue@1: V.AllocateP(); xue@1: for (int p=0; pperiodinframe/2) V.K=periodinframe/2; xue@1: xue@1: //single-cycle analyses xue@1: //Basic descriptor: Dp[] xue@1: V.AllocatePK(); xue@1: for (int p=1; p0) V.LogASp[p][m]/=ccount; xue@1: else V.LogASp[p][m]=0; xue@1: } xue@1: } xue@1: memset(V.FXR, 0, sizeof(double)*2*V.K); memset(V.FRes, 0, sizeof(double)*V.K); xue@1: double sumdp=0; for (int p=1; p0)?(kb/kka):(kb*0.1); xue@1: while (iterepx) xue@1: { xue@1: iter++; xue@1: th2pi+=dth2pi; xue@1: kb=kka=y=0; xue@1: for (int k=1; kmy) my=y, mth2pi=th2pi; xue@1: dth2pi=(kka>0)?(kb/kka):(kb*0.1); xue@1: } xue@1: if (iter>=maxiter || th2pi!=mth2pi) xue@1: { xue@1: th2pi=mth2pi; xue@1: } xue@1: 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]) xue@1: { xue@1: peakfr[npfr++]=fr; xue@1: } xue@1: } xue@1: xue@1: bool localpeaky=(peaky==(double*)0xFFFFFFFF); if (!localpeaky) delete[] peaky; xue@1: xue@1: peaky=new double[npfr]; xue@1: for (int pfr=0; pfrperiodinframe/2) xue@1: { xue@1: double ef0=0; int frcount=0; for (int fr=peakfr[pfr]-periodinframe/2; frmpfr) impfr=pfr, mpfr=mp; xue@1: pfr++; xue@1: } xue@1: } xue@1: else xue@1: { xue@1: while (pfr0) {impfr=pfr; break;} xue@1: pfr++; xue@1: } xue@1: } xue@1: if (impfr>=0) peakfr[pen++]=peakfr[impfr]; xue@1: } xue@1: pst--; pfr=pst; xue@1: while (pfr>=0) xue@1: { xue@1: int impfr=-1; xue@1: while (pfr>=0 && peakfr[pfr]>peakfr[pst+1]-periodinframe*0.5) pfr--; xue@1: if (pfr>=0 && peakfr[pfr]>=peakfr[pst+1]-periodinframe*1.5) xue@1: { xue@1: double mpfr=peaky[pfr]+copeak(F0, peakfr, pfr, pst+1, periodinframe); xue@1: impfr=pfr; pfr--; xue@1: while (pfr>=0 && peakfr[pfr]>=peakfr[pst+1]-periodinframe*1.5) xue@1: { xue@1: double mp=peaky[pfr]+copeak(F0, peakfr, pfr, pst+1, periodinframe); xue@1: if (mp>mpfr) impfr=pfr, mpfr=mp; xue@1: pfr--; xue@1: } xue@1: } xue@1: else xue@1: { xue@1: while (pfr>=0) xue@1: { xue@1: if (peaky[pfr]>0) {impfr=pfr; break;} xue@1: pfr--; xue@1: } xue@1: } xue@1: if (impfr>=0) peakfr[pst--]=peakfr[impfr]; xue@1: } xue@1: pst++; xue@1: npfr=pen-pst; xue@1: memmove(peakfr, &peakfr[pst], sizeof(int)*npfr); //*/ xue@1: if (localpeaky) delete[] peaky; xue@1: }//FindPeaks xue@1: Chris@5: /** xue@1: function FS: Fourier series decomposition xue@1: xue@1: In: data[frst:fren]: signal to decompose xue@1: period: period of Fourier series decomposition xue@1: shift: amount of original shift of Fourier components (from frst to frst-shift) xue@1: K: number of Fourier components requested xue@1: Out: K: number of Fourier components returned xue@1: FXR[K], FXI[K]: real and imaginary parts of Fourier series coefficients xue@1: FRES[K]: decomposition residues xue@1: xue@1: No return value. xue@1: */ xue@1: void FS(int& K, double* FXR, double* FXI, double* FRES, double* data, int frst, int fren, double period, double shift) xue@1: { xue@1: if (K>period/2) K=period/2; xue@1: int len=fren-frst+1; xue@1: double omg0=2*M_PI/period, ilen=1.0/len; xue@1: xue@1: //for (int fr=frst; fr<=fren; fr++) data[fr]=cos(2*M_PI*fr/period+1); xue@1: xue@1: double* res=new double[len]; memcpy(res, &data[frst], sizeof(double)*len); xue@1: double* rec=new double[len]; xue@1: double resene=0; for (int i=0; i0) xr/=eer; xue@1: // if (eei>0) xi/=eei; xue@1: FXR[k]=xr, FXI[k]=xi; xue@1: double lresene=0; xue@1: for (int fr=frst; fr<=fren; fr++) xue@1: { xue@1: double ph=omg*(fr-shift); xue@1: // rec[fr-frst]=xr*cos(ph)+xi*sin(ph); xue@1: if (k==0) rec[fr-frst]=xr*cos(ph)+xi*sin(ph); xue@1: else rec[fr-frst]=2*(xr*cos(ph)+xi*sin(ph)); xue@1: res[fr-frst]-=rec[fr-frst]; xue@1: lresene+=res[fr-frst]*res[fr-frst]; xue@1: } xue@1: resene=lresene; xue@1: FRES[k]=resene/ene; xue@1: } xue@1: delete[] rec; xue@1: delete[] res; xue@1: }//FS xue@1: Chris@5: /** xue@1: function FS_QR: Fourier series decomposition with QR orthogonalization. Since the Fourier series is xue@1: applied on finite-length discrate signal, the Fourier components are no longer orthogonal to each xue@1: other. A decreasing residue can be guaranteed by applying QR (or any other) orthogonalization process xue@1: to the Fourier components before decomposition. xue@1: xue@1: In: data[0:Fr]: signal to decompose xue@1: period: period of Fourier series decomposition xue@1: shift: amount of original shift of Fourier components (from 0 to -shift) xue@1: K: number of Fourier components requested xue@1: Out: K: number of Fourier components returned xue@1: FXR[2K-1]: Fourier series coefficients, in the order of 0r, 1i, 1r, 2i, 2r, .... xue@1: FRES[K]: decomposition residues, optional xue@1: xue@1: No return value. xue@1: */ xue@1: void FS_QR(int& K, double* FXR, double* data, int Fr, double period, double shift, double* FRES) xue@1: { xue@1: int len=Fr+1; xue@1: if (K>period/2) K=period/2; xue@1: if (K>Fr/2) K=Fr/2; xue@1: if (K>len/2) K=len/2; xue@1: if (K<=0) return; xue@1: memset(FXR, 0, sizeof(double)*(2*K-1)); xue@1: xue@1: double omg0=2*M_PI/period, ene, resene; xue@1: xue@1: if (FRES) xue@1: { xue@1: ene=0; for (int i=0; iF0C=0; newV->A0C=0; newV->peakfr=0; newV->Dp=0; newV->lp=0, newV->Kp=0; xue@1: newV->fxr=0; newV->LogASp=0; newV->F0=0; newV->F0D=0; newV->fres=0; xue@1: xue@1: int inewP=ceil(newP+Pst); xue@1: newV->P=inewP; xue@1: xue@11: newV->peakfr=(int*)malloc(sizeof(int)*inewP); xue@1: newV->AllocateP(); newV->AllocatePK(); xue@1: xue@1: newV->lp[0]=V.lp[0]/rate; xue@1: newV->peakfr[0]=floor(newV->lp[0]+0.5); xue@1: for (int p=1; p=V.P) ip=V.P-1, rp=0; xue@1: if (fabs(rp)<1e-16) xue@1: { xue@1: newV->lp[p]=newV->lp[p-1]+(V.lp[ip]-V.lp[ip-1])/rate; xue@1: newV->Dp[p]=V.Dp[ip]; xue@1: newV->Kp[p]=V.Kp[ip]; xue@1: memcpy(newV->fxr[p], V.fxr[ip], sizeof(double)*2*newV->Kp[p]); xue@1: memcpy(newV->LogASp[p], V.LogASp[ip], sizeof(double)*V.M); xue@1: } xue@1: else xue@1: { xue@1: double fv1=1.0/(V.lp[ip]-V.lp[ip-1]), fv2=1.0/(V.lp[ip+1]-V.lp[ip]); xue@1: double fv=rate*(fv1*(1-rp)+fv2*rp); xue@1: newV->lp[p]=newV->lp[p-1]+1.0/fv; xue@1: newV->Dp[p]=V.Dp[ip]*(1-rp)+V.Dp[ip+1]*rp; xue@1: int minK=V.Kp[ip]; if (minK>V.Kp[ip+1]) minK=V.Kp[ip+1]; xue@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; xue@1: for (int m=0; mLogASp[p][m]=V.LogASp[ip][m]*(1-rp)+V.LogASp[ip+1][m]*rp; xue@1: newV->Kp[p]=minK; xue@1: } xue@1: newV->peakfr[p]=floor(newV->lp[p]+0.5); xue@1: memset(newV->fres[0], 0, sizeof(double)*newV->P*V.K); xue@1: } xue@1: int newL=newV->lp[inewP-1]+(newP+Pst-(inewP-1))*(V.lp[V.P-1]-V.lp[V.P-2])/rate+0.5; xue@1: newV->AllocateL(newL); xue@1: xue@1: //F0C and A0C xue@1: if (V.L!=newL) xue@1: { xue@1: for (int l=0; lF0C[l]=V.F0C[il]*(1-rl)+V.F0C[il+1]*rl; xue@1: newV->A0C[l]=V.A0C[il]*(1-rl)+V.A0C[il+1]*rl; xue@1: } xue@1: } xue@1: else xue@1: { xue@1: memcpy(newV->F0C, V.F0C, sizeof(double)*V.L); xue@1: memcpy(newV->A0C, V.A0C, sizeof(double)*V.L); xue@1: // memcpy(newV->F0, V.F0, sizeof(double)*V.L); xue@1: // memcpy(newV->F0D, V.F0D, sizeof(double)*V.L); xue@1: } xue@1: return newV; xue@1: }//InterpoalteV xue@1: xue@1: //vibrato rate boundaries, in Hz xue@1: #define MAXMOD 15.0 xue@1: #define MINMOD 3.0 xue@1: Chris@5: /** xue@1: function RateAndReg: evaluates modulation rate and regularity xue@1: xue@1: In: data[frst:fren]: modulated signal, time measured in frames xue@1: padrate: padding rate used for inverse FFT xue@1: sps: sampling frequency xue@1: offst: hop size xue@1: Out: rate, regularity: rate and regularity xue@1: xue@1: No return value. xue@1: */ xue@1: void RateAndReg(double& rate, double& regularity, double* data, int frst, int fren, int padrate, double sps, double offst) xue@1: { xue@1: rate=0; regularity=0; xue@1: //find the section used for calculating regularity and rate xue@11: int frlen=fren-frst+1, frlenp=1<<(int(Log2(fren))+2), frlenpp=frlenp*padrate; xue@1: double *lf=new double[frlenpp*4]; xue@1: cdouble *w=(cdouble*)&lf[frlenpp]; xue@1: cdouble *x=(cdouble*)&lf[frlenpp*2]; xue@1: SetTwiddleFactors(frlenp, w); xue@1: xue@1: memset(lf, 0, sizeof(double)*frlenp); xue@1: memcpy(lf, &data[frst], sizeof(double)*frlen); xue@1: // for (int i=0; iV.Kp[p]) K=V.Kp[p]; xue@1: double D=0, *fxr=new double[2*K-1]; xue@1: memset(fxr, 0, sizeof(double)*(2*K-1)); xue@1: for (int p=1; p0.5 || Partials[m][l].f<0) Partials[m][l].f=-1, Partials[m][l].a=0; xue@1: else Partials[m][l].a=V.A0C[l]*exp(V.LogAS[m]+V.LogAF[int(Partials[m][l].f*V.afres)]); xue@1: } xue@1: } xue@1: xue@1: delete[] fxr; xue@1: }//RegularizeV xue@1: Chris@5: /** xue@1: function QIE: computes extremum using quadratic interpolation xue@1: xue@1: In: y[-1:1]: three points to interpolate from. It is assumed y[0] is larger (or smaller) than both xue@1: y[-1] and y[1]. xue@1: xue@1: Returns the extremum of y. xue@1: */ xue@1: double QIE(double* y) xue@1: { xue@1: double a=0.5*(y[1]+y[-1])-y[0], b=0.5*(y[1]-y[-1]); xue@1: return y[0]-0.25*b*b/a; xue@1: }//QIE xue@1: Chris@5: /** xue@1: function QIE: computes extremum using quadratic interpolation xue@1: xue@1: In: y[-1:1]: three points to interpolate from. It is assumed y[0] is larger (or smaller) than both xue@1: y[-1] and y[1]. xue@1: Out: x: the argument value that yields extremum of y(x) xue@1: xue@1: Returns the extremum of y. xue@1: */ xue@1: double QIE(double* y, double& x) xue@1: { xue@1: double a=0.5*(y[1]+y[-1])-y[0], b=0.5*(y[1]-y[-1]); xue@1: x=-0.5*b/a; xue@1: return y[0]-0.25*b*b/a; xue@1: }//QIE xue@1: Chris@5: /** xue@1: function S_F: original source-filter estimation used in AES124. xue@1: xue@1: In: afres: filter response resolution xue@1: Partials[M][Fr]: HS partials xue@1: A0C[Fr]: partial-independent amplitude carrier xue@1: lp[P]: cycle boundaries xue@1: F0Overall: average fundamental frequency xue@1: Out: LogAF[afres/2]: filter response xue@1: LogAS[M]: source amplitudes xue@1: xue@1: Returns 0. xue@1: */ xue@1: double S_F(int afres, double* LogAF, double* LogAS, int Fr, int M, atom** Partials, double* A0C, double* lp, int P, double F0Overall) xue@1: { xue@1: //SOURCE-FILTER estimation routines xue@1: //derivative of log A(f), f is sampled at 1/8192 (5hz), averaged over (-15hz, 15hz) xue@1: double *DAF=new double[afres], *NAF=&DAF[afres/2]; xue@1: memset(DAF, 0, sizeof(double)*afres); xue@1: double avgwidth1, avgwidth2; xue@1: for (int fr=lp[1]; fr0 && Partials[m][fr-1].f>0 && Partials[m][fr+1].f>0) xue@1: { xue@1: 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]; xue@1: if ((f0-f_1)*(f0-f1)<0) xue@1: { xue@1: double dlogaf_df=(log(a1)-log(a_1))/(f1-f_1); //calculate derivative of log xue@1: xue@1: //this derivative contributes within a frequency interval of |f1-f_1| xue@1: if (f10) DAF[i]=DAF[i]/NAF[i]; //else NAF[i]=0; xue@1: int i=0, nbands=0, bandsstart[100], bandsendinc[100]; double mininaf=3; xue@1: while (i=afres/2) break; xue@1: bandsstart[nbands]=i; xue@1: while (i0) i++; if (i>=afres/2) {bandsendinc[nbands++]=i-1; break;} xue@1: if (NAF[i]<=0) while (NAF[i]=bandsstart[0]){LogAF[i]=LogAF[i+1]-0.5*(DAF[i]+DAF[i+1])/afres; i--;} xue@1: i=F0Overall*afres+1; xue@1: LogAF[i]=0.5*(1-theta)*(DAF[i]+DAF[i-1]*(1-theta)+DAF[i]*theta)/afres; i++; xue@1: while (i<=bandsendinc[0]){LogAF[i]=LogAF[i-1]+0.5*(DAF[i]+DAF[i-1])/afres; i++;} xue@1: xue@1: int band=1; xue@1: while (bandbandsstart[band-1] && (fbandsendinc[band-1])) continue; xue@1: int intf=floor(f); xue@1: double logaflast=LogAF[intf]+(DAF[intf]+0.5*(DAF[intf+1]-DAF[intf])*(f-intf))*(f-intf)/afres; xue@1: f=Partials[band][fr].f*afres; xue@1: if (bandsendinc[band]>bandsstart[band] && (fbandsendinc[band])) continue; xue@1: intf=floor(f); xue@1: double logafthis=LogAF[intf]+(DAF[intf]+0.5*(DAF[intf+1]-DAF[intf])*(f-intf))*(f-intf)/afres; xue@1: cshift=cshift+log(Partials[band][fr].a*(band+1)/(Partials[band-1][fr].a*band))+logaflast-logafthis; xue@1: ccount++; xue@1: } xue@11: if (ccount!=0) cshift/=ccount; xue@11: else cshift=0; xue@1: //apply the shift xue@1: for (int i=bandsstart[band]; i<=bandsendinc[band]; i++) LogAF[i]+=cshift; xue@1: //make up the blank band xue@1: int lastend=bandsendinc[band-1], thisstart=bandsstart[band]; xue@1: for (int i=lastend+1; i0) LogAS[m]/=ccount; xue@1: else LogAS[m]=0; xue@1: } xue@1: return 0; xue@1: }//S_F xue@1: Chris@5: /** xue@1: function S_F_SV: slow-variation source-filter estimation used in AES126, adapted from TSF to TVo. xue@1: xue@1: In: afres: filter response resolution xue@1: Partials[M][Fr]: HS partials xue@1: A0C[Fr]: partial-independent amplitude carrier xue@1: lp[P]: cycle boundaries xue@1: F: filter response sampling interval xue@1: FScaleMode: set if filter sampling uses mel scale xue@1: theta: balancing factor of amplitude and frequency variations, needed for SV approach xue@1: Out: LogAF[afres/2]: filter response xue@1: LogAS[M]: source amplitudes xue@1: xue@1: Returns 0. xue@1: */ xue@1: 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) xue@1: { xue@1: int lp0=lp[1], lpp=lp[P-1]; xue@1: int L=lpp-lp0; xue@1: Alloc2(L, M, loga); Alloc2(L, M, f); xue@1: double fmax=0; xue@1: for (int l=0; l0) loga[l][m]=log(Partials[m][fr].a); xue@1: else loga[l][m]=-100; xue@1: if (fmax0) LogAS[m]/=ccount; xue@1: else LogAS[m]=0; xue@1: } xue@1: xue@1: DeAlloc2(loga); DeAlloc2(f); DeAlloc2(h); xue@1: return 0; xue@1: }//S_F_SV xue@1: Chris@5: /** xue@1: function SynthesizeV: synthesizes a harmonic sinusoid from vibrato descriptors xue@1: xue@1: In: V: a TVo object hosting vibrato descriptors xue@1: sps: sampling rate xue@1: UseK: maximal number of Fourier series components to use to construct frequency modulator xue@1: Out: HS: a harmonic sinusoid xue@1: xue@1: No return value. xue@1: */ xue@1: void SynthesizeV(THS* HS, TVo* V, double sps, int UseK) xue@1: { xue@1: int HSt0=HS->Partials[0][0].t, HSs0=HS->Partials[0][0].s, L=V->L, M=V->M, xue@1: P=V->P, *Kp=V->Kp, K=V->K, afres=V->afres; xue@1: double **fxr=V->fxr, *lp=V->lp, **LogASp=V->LogASp, *LogAF=V->LogAF, *Dp=V->Dp, xue@1: *F0C=V->F0C, *F0D=V->F0D, *F0=V->F0, *A0C=V->A0C, h=V->h; xue@1: HS->Resize(M, L); xue@1: double *gamp=new double[4*P], *gamm=&gamp[P]; xue@1: xue@1: Alloc2(P, K*2, newfxr); xue@1: int* newKp=new int[P]; xue@1: for (int p=1; p=2 && newKp[p]>UseK) newKp[p]=UseK; xue@1: CorrectFS(newKp[p], newfxr[p]); xue@1: } xue@1: xue@1: double f0, f1; xue@1: for (int p=1; pPartials; xue@1: for (int p=0; p0.5 || Partials[m][l].f<0) Partials[m][l].a=0; xue@1: 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)]); xue@1: 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)]); xue@1: else xue@1: // 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)]); xue@1: Partials[m][l].a=A0C[l]*exp(LogASp[p][m]+LogAF[int(Partials[m][l].f*afres)]); xue@1: } xue@1: } xue@1: } xue@1: delete[] gamp; xue@1: DeAlloc2(newfxr); xue@1: delete[] newKp; xue@1: // VibratoDemoForm->Label13->Caption=L; xue@1: }//SynthesizeV xue@1: xue@1: xue@1: xue@1: xue@1: xue@1: