/* * copy a chunk containing only frequencies between flo and fhi into stream. */ void BandPassFilterFFT::band_pass(uint8_t *stream, double flo, double fhi) { double band_data[2*samples]; band_window(band_data, ffind(fhi)+1, ffind(flo)); //writef(band_data, "test.dat"); fft_inverse(band_data, stream); }
/* * graphical equaliser using the function eq to boost/cut the stream. */ void BandPassFilterFFT::EQ(uint8_t *stream, double(*eq)(double, void*), void *args) { double band_data[2*samples]; /* * EQ the data */ for(uint32_t i=0; i<=freqs; i++) // Go over each frequency. { double Eq = eq(f[i], args); REAL(band_data,POSITIVE(i,samples)) = Eq * REAL(fcache,POSITIVE(i,samples)); IMAG(band_data,POSITIVE(i,samples)) = Eq * IMAG(fcache,POSITIVE(i,samples)); REAL(band_data,NEGATIVE(i,samples)) = Eq * REAL(fcache,NEGATIVE(i,samples)); IMAG(band_data,NEGATIVE(i,samples)) = Eq * IMAG(fcache,NEGATIVE(i,samples)); } fft_inverse(band_data, stream); }
void AutoTalent::init(unsigned long sr) { unsigned long ti; fs = sr; aref = 440; if (fs >=88200) { cbsize = 4096; } else { cbsize = 2048; } corrsize = cbsize / 2 + 1; pmax = 1/(float)70; // max and min periods (ms) pmin = 1/(float)700; // eventually may want to bring these out as sliders pperiod = pmax; nmax = (unsigned long)(fs * pmax); if (nmax > corrsize) { nmax = corrsize; } nmin = (unsigned long)(fs * pmin); cbi = (float*) calloc(cbsize, sizeof(float)); cbo = (float*) calloc(cbsize, sizeof(float)); cbonorm = (float*) calloc(cbsize, sizeof(float)); cbiwr = 0; cbord = 0; // Standard raised cosine window, max height at N/2 hannwindow = (float*) calloc(cbsize, sizeof(float)); for (ti=0; ti<cbsize; ti++) { hannwindow[ti] = -0.5*cos(2*PI*ti/(cbsize - 1)) + 0.5; } // Generate a window with a single raised cosine from N/4 to 3N/4 cbwindow = (float*) calloc(cbsize, sizeof(float)); for (ti=0; ti<(cbsize / 2); ti++) { cbwindow[ti+cbsize/4] = -0.5*cos(4*PI*ti/(cbsize - 1)) + 0.5; } noverlap = 4; fmembvars = fft_con(cbsize); ffttime = (float*) calloc(cbsize, sizeof(float)); fftfreqre = (float*) calloc(corrsize, sizeof(float)); fftfreqim = (float*) calloc(corrsize, sizeof(float)); // ---- Calculate autocorrelation of window ---- acwinv = (float*) calloc(cbsize, sizeof(float)); for (ti=0; ti<cbsize; ti++) { ffttime[ti] = cbwindow[ti]; } fft_forward(fmembvars, cbwindow, fftfreqre, fftfreqim); for (ti=0; ti<corrsize; ti++) { fftfreqre[ti] = (fftfreqre[ti])*(fftfreqre[ti]) + (fftfreqim[ti])*(fftfreqim[ti]); fftfreqim[ti] = 0; } fft_inverse(fmembvars, fftfreqre, fftfreqim, ffttime); for (ti=1; ti<cbsize; ti++) { acwinv[ti] = ffttime[ti]/ffttime[0]; if (acwinv[ti] > 0.000001) { acwinv[ti] = (float)1/acwinv[ti]; } else { acwinv[ti] = 0; } } acwinv[0] = 1; // ---- END Calculate autocorrelation of window ---- lrshift = 0; ptarget = 0; sptarget = 0; wasvoiced = 0; persistamt = 0; glidepersist = 100; // 100 ms glide persist vthresh = 0.8; // The voiced confidence (unbiased peak) threshold level // Pitch shifter initialization phprdd = 0.01; // Default period phprd = phprdd; phinc = (float)1/(phprd * fs); phincfact = 1; phasein = 0; phaseout = 0; frag = (float*) calloc(cbsize, sizeof(float)); fragsize = 0; }
/** This is the main loop where we'll process our samples */ void AutoTalent::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames) { // Mutex is already locked for us. double* in1 = inputs[0]; double* out1 = outputs[0]; double* out2 = outputs[1]; // copy struct variables to local /* float fMix = fMix; float fShift = fShift; float fTune = fTune; float fA = fA; float fBb = fBb; float fB = fB; float fC = fC; float fDb = fDb; float fD = fD; float fEb = fEb; float fE = fE; float fF = fF; float fGb = fGb; float fG = fG; float fAb = fAb; float fGlide = fGlide; float fAmount = fAmount; */ float fPersist = glidepersist; aref = (float)440*pow(2,fTune/12); unsigned long N = cbsize; unsigned long Nf = corrsize; //unsigned long fs = fs; /* float pmax = pmax; float pmin = pmin; unsigned long nmax = nmax; unsigned long nmin = nmin; float pperiod = pperiod; float pitch = pitch; float conf = conf; float aref = aref; */ // long int ti; long int ti2; long int ti3; float tf; float tf2; float tf3; //double samplesPerBeat = GetSamplesPerBeat(); //double samplePos = (double) GetSamplePos(); for (int s = 0; s < nFrames; ++s, ++in1, ++out1, ++out2) { // load data into circular buffer tf = (float) *in1; cbi[cbiwr] = tf; cbiwr++; if (cbiwr >= N) { cbiwr = 0; } // ******************** // * Low-rate section * // ******************** // Every N/noverlap samples, run pitch estimation / correction code if ((cbiwr)%(N/noverlap) == 0) { // ---- Obtain autocovariance ---- // Window and fill FFT buffer ti2 = (long) cbiwr; for (ti=0; ti<(long)N; ti++) { ffttime[ti] = (float)(cbi[(ti2-ti)%N]*cbwindow[ti]); } // Calculate FFT fft_forward(fmembvars, ffttime, fftfreqre, fftfreqim); // Remove DC fftfreqre[0] = 0; fftfreqim[0] = 0; // Take magnitude squared for (ti=1; ti< (long) Nf; ti++) { fftfreqre[ti] = (fftfreqre[ti])*(fftfreqre[ti]) + (fftfreqim[ti])*(fftfreqim[ti]); fftfreqim[ti] = 0; } // Calculate IFFT fft_inverse(fmembvars, fftfreqre, fftfreqim, ffttime); // Normalize for (ti=1; ti<(long)N; ti++) { ffttime[ti] = ffttime[ti] / ffttime[0]; } ffttime[0] = 1; // ---- END Obtain autocovariance ---- // ---- Calculate pitch and confidence ---- // Calculate pitch period // Pitch period is determined by the location of the max (biased) // peak within a given range // Confidence is determined by the corresponding unbiased height tf2 = 0; pperiod = pmin; for (ti=nmin; ti<(long)nmax; ti++) { ti2 = ti-1; ti3 = ti+1; if (ti2<0) { ti2 = 0; } if (ti3>(long)Nf) { ti3 = Nf; } tf = ffttime[ti]; if (tf>ffttime[ti2] && tf>=ffttime[ti3] && tf>tf2) { tf2 = tf; conf = tf*acwinv[ti]; pperiod = (float)ti/fs; } } // Convert to semitones pitch = (float) -12*log10((float)aref*pperiod)*L2SC; pitch = pitch; pperiod = pperiod; conf = conf; // ---- END Calculate pitch and confidence ---- // ---- Determine pitch target ---- // If voiced if (conf>=vthresh) { // TODO: Scale sliders // Determine pitch target tf = -1; tf2 = 0; tf3 = 0; for (ti=0; ti<12; ti++) { switch (ti) { case 0: tf2 = fNotes[9]; break; case 1: tf2 = fNotes[10]; break; case 2: tf2 = fNotes[11]; break; case 3: tf2 = fNotes[0]; break; case 4: tf2 = fNotes[1]; break; case 5: tf2 = fNotes[2]; break; case 6: tf2 = fNotes[3]; break; case 7: tf2 = fNotes[4]; break; case 8: tf2 = fNotes[5]; break; case 9: tf2 = fNotes[6]; break; case 10: tf2 = fNotes[7]; break; case 11: tf2 = fNotes[8]; break; } /* if (ti==ptarget) { */ /* tf2 = tf2 + 0.01; // add a little hysteresis */ /* } */ tf2 = tf2 - (float)fabs( (pitch-(float)ti)/6 - 2*floorf(((pitch-(float)ti)/12 + 0.5)) ); // like a likelihood function if (tf2>=tf) { // that we're maximizing tf3 = (float)ti; // to find the target pitch tf = tf2; } } ptarget = tf3; // Glide persist if (wasvoiced == 0) { wasvoiced = 1; tf = persistamt; sptarget = (1-tf)*ptarget + tf*sptarget; persistamt = 1; } // Glide on circular scale tf3 = (float)ptarget - sptarget; tf3 = tf3 - (float)12*floorf(tf3/12 + 0.5); if (fGlide>0) { tf2 = (float)1-pow((float)1/24, (float)N * 1000/ (noverlap*fs*fGlide)); } else { tf2 = 1; } sptarget = sptarget + tf3*tf2; } // If not voiced else { wasvoiced = 0; // Keep track of persist amount if (fPersist>0) { tf = pow((float)1/2, (float)N * 1000/ (noverlap*fs*fPersist)); } else { tf = 0; } persistamt = persistamt * tf; // Persist amount decays exponentially } // END If voiced // ---- END Determine pitch target ---- // ---- Determine correction to feed to the pitch shifter ---- tf = sptarget - pitch; // Correction amount tf = tf - (float)12*floorf(tf/12 + 0.5); // Never do more than +- 6 semitones of correction if (conf<vthresh) { tf = 0; } lrshift = fShift + fAmount*tf; // Add in pitch shift slider // ---- Compute variables for pitch shifter that depend on pitch --- phincfact = (float)pow(2, lrshift/12); if (conf>=vthresh) { // Keep old period when unvoiced phinc = (float)1/(pperiod*fs); phprd = pperiod*2; } } // ************************ // * END Low-Rate Section * // ************************ // ***************** // * Pitch Shifter * // ***************** // TODO: Pre-filter with some kind of filter (maybe cheby2 or just svf) // TODO: Use cubic spline interpolation // IMPROVE QUALITY OF PITCH SHIFTER! // what is the glitch at "lAaAack"? probably pitch shifter // Better snippet management // Pre-filter // Cubic spline interp // Pitch shifter (overlap-add, pitch synchronous) // Note: pitch estimate is naturally N/2 samples old phasein = phasein + phinc; phaseout = phaseout + phinc*phincfact; // If it happens that there are no snippets placed at the output, grab a new snippet! /* if (cbonorm[((long int)cbord + (long int)(N/2*(1 - (float)1 / phincfact)))%N] < 0.2) { */ /* fprintf(stderr, "help!"); */ /* phasein = 1; */ /* phaseout = 1; */ /* } */ // When input phase resets, take a snippet from N/2 samples in the past if (phasein >= 1) { phasein = phasein - 1; ti2 = cbiwr - (long int)N/2; for (ti=-((long int)N)/2; ti<(long int)N/2; ti++) { frag[ti%N] = cbi[(ti + ti2)%N]; } } // When output phase resets, put a snippet N/2 samples in the future if (phaseout >= 1) { fragsize = fragsize*2; if (fragsize >= N) { fragsize = N; } phaseout = phaseout - 1; ti2 = cbord + N/2; ti3 = (long int)(((float)fragsize) / phincfact); for (ti=-ti3/2; ti<(ti3/2); ti++) { tf = hannwindow[(long int)N/2 + ti*(long int)N/ti3]; cbo[(ti + ti2)%N] = cbo[(ti + ti2)%N] + frag[((int)(phincfact*ti))%N]*tf; cbonorm[(ti + ti2)%N] = cbonorm[(ti + ti2)%N] + tf; } fragsize = 0; } fragsize++; // Get output signal from buffer tf = cbonorm[cbord]; // Normalize if (tf>0.5) { tf = (float)1/tf; } else { tf = 1; } tf = tf*cbo[cbord]; // read buffer tf = cbo[cbord]; cbo[cbord] = 0; // erase for next cycle cbonorm[cbord] = 0; cbord++; // increment read pointer if (cbord >= N) { cbord = 0; } // ********************* // * END Pitch Shifter * // ********************* // Write audio to output of plugin // Mix (blend between original (delayed) =0 and shifted/corrected =1) *out1 = *out2 = (double) fMix*tf + (1-fMix)*cbi[(cbiwr - N + 1)%N]; } }
//*************************// // Perform Routine PD// //*************************// t_int *autotune_perform(t_int *w) { t_autotune *x = (t_autotune *)(w[1]); // object is first arg t_float *in = (t_float *)(w[2]); t_float *out = (t_float *)(w[3]); unsigned long SampleCount = (unsigned long)(w[4]); // copy struct variables to local /*float fA = x->fA; float fBb = x->fBb; float fB = x->fB; float fC = x->fC; float fDb = x->fDb; float fD = x->fD; float fEb = x->fEb; float fE = x->fE; float fF = x->fF; float fGb = x->fGb; float fG = x->fG; float fAb = x->fAb;*/ //float fGlide = x->fGlide; //float fPersist = x->glidepersist; int iNotes[12]; int iPitch2Note[12]; int iNote2Pitch[12]; int numNotes; float fAmount = x->fAmount; float fSmooth = x->fSmooth * 0.8; float fTune = x->fTune; iNotes[0] = (int) x->fA; iNotes[1] = (int) x->fBb; iNotes[2] = (int) x->fB; iNotes[3] = (int) x->fC; iNotes[4] = (int) x->fDb; iNotes[5] = (int) x->fD; iNotes[6] = (int) x->fEb; iNotes[7] = (int) x->fE; iNotes[8] = (int) x->fF; iNotes[9] = (int) x->fGb; iNotes[10] = (int) x->fG; iNotes[11] = (int) x->fAb; float fFixed = x->fFixed; float fPull = x->fPull; float fShift = x->fShift; int iScwarp = x->fScwarp; float fLfoamp = x->fLfoamp; float fLforate = x->fLforate; float fLfoshape = x->fLfoshape; float fLfosymm = x->fLfosymm; int iLfoquant = x->fLfoquant; int iFcorr = x->fFcorr; float fFwarp = x->fFwarp; float fMix = x->fMix; //x->aref = (float)440*pow(2,fTune/12); unsigned long int lSampleIndex; unsigned long N = x->cbsize; unsigned long Nf = x->corrsize; unsigned long fs = x->fs; float pmax = x->pmax; float pmin = x->pmin; unsigned long nmax = x->nmax; unsigned long nmin = x->nmin; //float pperiod = x->pperiod; //float pitch = x->pitch; // volatile long int ti; volatile long int ti2; volatile long int ti3; volatile long int ti4; volatile float tf; volatile float tf2; volatile float tf3; // Variables for cubic spline interpolator volatile float indd; volatile int ind0; volatile int ind1; volatile int ind2; volatile int ind3; volatile float vald; volatile float val0; volatile float val1; volatile float val2; volatile float val3; volatile int lowersnap; volatile int uppersnap; volatile float lfoval; volatile float pperiod; volatile float inpitch; volatile float conf; volatile float outpitch; volatile float aref; volatile float fa; volatile float fb; volatile float fc; volatile float fk; volatile float flamb; volatile float frlamb; volatile float falph; volatile float foma; volatile float f1resp; volatile float f0resp; volatile float flpa; volatile int ford; // Some logic for the semitone->scale and scale->semitone conversion // If no notes are selected as being in the scale, instead snap to all notes ti2 = 0; for (ti=0; ti<12; ti++) { if (iNotes[ti]>=0) { iPitch2Note[ti] = ti2; iNote2Pitch[ti2] = ti; ti2 = ti2 + 1; } else { iPitch2Note[ti] = -1; } } numNotes = ti2; while (ti2<12) { iNote2Pitch[ti2] = -1; ti2 = ti2 + 1; } if (numNotes==0) { for (ti=0; ti<12; ti++) { iNotes[ti] = 1; iPitch2Note[ti] = ti; iNote2Pitch[ti] = ti; } numNotes = 12; } iScwarp = (iScwarp + numNotes*5)%numNotes; ford = x->ford; falph = x->falph; foma = (float)1 - falph; flpa = x->flpa; flamb = x->flamb; tf = pow((float)2,fFwarp/2)*(1+flamb)/(1-flamb); frlamb = (tf - 1)/(tf + 1); x->aref = (float)fTune; N = x->cbsize; Nf = x->corrsize; fs = x->fs; pmax = x->pmax; pmin = x->pmin; nmax = x->nmax; nmin = x->nmin; aref = x->aref; pperiod = x->pmax; inpitch = x->inpitch; conf = x->conf; outpitch = x->outpitch; //******************// // MAIN DSP LOOP // //******************// for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) { // load data into circular buffer tf = (float) *(in++); ti4 = x->cbiwr; //fprintf(stderr,"ti4=%d N=%d\n", ti4, N); x->cbi[ti4] = tf; /*x->cbiwr++; if (x->cbiwr >= N) { x->cbiwr = 0; }*/ if (iFcorr>=1) { // Somewhat experimental formant corrector // formants are removed using an adaptive pre-filter and // re-introduced after pitch manipulation using post-filter // tf is signal input fa = tf - x->fhp; // highpass pre-emphasis filter x->fhp = tf; fb = fa; for (ti=0; ti<(long)ford; ti++) { x->fsig[ti] = fa*fa*foma + x->fsig[ti]*falph; fc = (fb-x->fc[ti])*flamb + x->fb[ti]; x->fc[ti] = fc; x->fb[ti] = fb; fk = fa*fc*foma + x->fk[ti]*falph; x->fk[ti] = fk; tf = fk/(x->fsig[ti] + 0.000001); tf = tf*foma + x->fsmooth[ti]*falph; x->fsmooth[ti] = tf; x->fbuff[ti][ti4] = tf; fb = fc - tf*fa; fa = fa - tf*fc; } x->cbf[ti4] = fa; // Now hopefully the formants are reduced // More formant correction code at the end of the DSP loop } else { x->cbf[ti4] = tf; } //fprintf(stderr,"x->cbf[ti4]=%f\n", x->cbf[ti4]); // Input write pointer logic x->cbiwr++; if (x->cbiwr >= N) { x->cbiwr = 0; } // ********************// // * Low-rate section *// // ********************// //fprintf(stderr,"overlap=%d outpitch=%f inpitch=%f\n", (x->cbiwr)%(N/x->noverlap), outpitch, inpitch); //fprintf(stderr,"outpitch=%f inpitch=%f\n", outpitch, inpitch); // Every N/noverlap samples, run pitch estimation / correction code if ((x->cbiwr)%(N/x->noverlap) == 0) { //fprintf(stderr,"ti4=%d N=%d\n", ti4, N); // ---- Obtain autocovariance ---- // // Window and fill FFT buffer ti2 = (long) x->cbiwr; for (ti=0; ti<(long)N; ti++) { x->ffttime[ti] = (float)(x->cbi[(ti2-ti)%N]*x->cbwindow[ti]); } // Calculate FFT fft_forward(x->fx, x->ffttime, x->fftfreqre, x->fftfreqim); // Remove DC x->fftfreqre[0] = 0; x->fftfreqim[0] = 0; // Take magnitude squared for (ti=1; ti< (long) Nf; ti++) { x->fftfreqre[ti] = (x->fftfreqre[ti])*(x->fftfreqre[ti]) + (x->fftfreqim[ti])*(x->fftfreqim[ti]); x->fftfreqim[ti] = 0; } // Calculate IFFT fft_inverse(x->fx, x->fftfreqre, x->fftfreqim, x->ffttime); // Normalize for (ti=1; ti<(long)N; ti++) { x->ffttime[ti] = x->ffttime[ti] / x->ffttime[0]; } x->ffttime[0] = 1; // ---- END Obtain autocovariance ---- // ---- Calculate pitch and confidence ---- // Calculate pitch period // Pitch period is determined by the location of the max (biased) // peak within a given range // Confidence is determined by the corresponding unbiased height tf2 = 0; pperiod = pmin; for (ti=nmin; ti<(long)nmax; ti++) { ti2 = ti-1; ti3 = ti+1; if (ti2<0) { ti2 = 0; } if (ti3>(long)Nf) { ti3 = Nf; } tf = x->ffttime[ti]; if (tf>x->ffttime[ti2] && tf>=x->ffttime[ti3] && tf>tf2) { tf2 = tf; ti4 = ti; //conf = tf*x->acwinv[ti]; //pperiod = (float)ti/fs; } } if (tf2>0) { conf = tf2*x->acwinv[ti4]; if (ti4>0 && ti4<(long)Nf) { // Find the center of mass in the vicinity of the detected peak tf = x->ffttime[ti4-1]*(ti4-1); tf = tf + x->ffttime[ti4]*(ti4); tf = tf + x->ffttime[ti4+1]*(ti4+1); tf = tf/(x->ffttime[ti4-1] + x->ffttime[ti4] + x->ffttime[ti4+1]); pperiod = tf/fs; } else { pperiod = (float)ti4/fs; } } // Convert to semitones tf = (float) -12*log10((float)aref*pperiod)*L2SC; //fprintf(stderr,"tf=%f aref=%f pperiod=%f\n", tf, aref, pperiod); //post("pperiod=%f conf=%f\n", pperiod, conf); float pp_test = x->pperiod/(x->pperiod - pperiod); if (pp_test < 0.5 || pp_test > 2) pp_test = 1; else pp_test = 0; if (conf>=x->vthresh && tf == tf) { // second check is for NANs inpitch = tf; x->inpitch = tf; // update pitch only if voiced x->pperiod = pperiod; } x->conf = conf; x->fPitch = inpitch; x->fConf = conf; //x->pitch = pitch; //x->pperiod = pperiod; //x->conf = conf; // ---- END Calculate pitch and confidence ---- /* // ---- Determine pitch target ---- // If voiced if (conf>=x->vthresh) { // TODO: Scale sliders // Determine pitch target tf = -1; tf2 = 0; tf3 = 0; for (ti=0; ti<12; ti++) { switch (ti) { case 0: tf2 = fA; break; case 1: tf2 = fBb; break; case 2: tf2 = fB; break; case 3: tf2 = fC; break; case 4: tf2 = fDb; break; case 5: tf2 = fD; break; case 6: tf2 = fEb; break; case 7: tf2 = fE; break; case 8: tf2 = fF; break; case 9: tf2 = fGb; break; case 10: tf2 = fG; break; case 11: tf2 = fAb; break; } // if (ti==x->ptarget) { // tf2 = tf2 + 0.01; // add a little hysteresis // } tf2 = tf2 - (float)fabs( (pitch-(float)ti)/6 - 2*floorf(((pitch-(float)ti)/12 + 0.5)) ); // like a likelihood function if (tf2>=tf) { // that we're maximizing tf3 = (float)ti; // to find the target pitch tf = tf2; } } x->ptarget = tf3; // Glide persist if (x->wasvoiced == 0) { x->wasvoiced = 1; tf = x->persistamt; x->sptarget = (1-tf)*x->ptarget + tf*x->sptarget; x->persistamt = 1; } // Glide on circular scale tf3 = (float)x->ptarget - x->sptarget; tf3 = tf3 - (float)12*floorf(tf3/12 + 0.5); if (fGlide>0) { tf2 = (float)1-pow((float)1/24, (float)N * 1000/ (x->noverlap*fs*fGlide)); } else { tf2 = 1; } x->sptarget = x->sptarget + tf3*tf2; } // If not voiced else { x->wasvoiced = 0; // Keep track of persist amount if (fPersist>0) { tf = pow((float)1/2, (float)N * 1000/ (x->noverlap*fs*fPersist)); } else { tf = 0; } x->persistamt = x->persistamt * tf; // Persist amount decays exponentially } // END If voiced // ---- END Determine pitch target ---- // ---- Determine correction to feed to the pitch shifter ---- tf = x->sptarget - pitch; // Correction amount tf = tf - (float)12*floorf(tf/12 + 0.5); // Never do more than +- 6 semitones of correction if (conf<x->vthresh) { tf = 0; } x->lrshift = fShift + fAmount*tf; // Add in pitch shift slider // ---- Compute variables for pitch shifter that depend on pitch --- x->phincfact = (float)pow(2, x->lrshift/12); if (conf>=x->vthresh) { // Keep old period when unvoiced x->inphinc = (float)1/(pperiod*fs); x->phprd = pperiod*2; } } // ************************ // * END Low-Rate Section * // ************************ */ //fprintf(stderr,"%f %f %f %f", inpitch, outpitch, pperiod, ti4); // ---- Modify pitch in all kinds of ways! ---- outpitch = inpitch; //fprintf(stderr,"outpitch=%f\n", outpitch); // Pull to fixed pitch // when fPull is 1 (legacy behavior which picks absolute pitch in respect to A intonation) if (fPull <= 1) { outpitch = (1-fPull)*outpitch + fPull*fFixed; } else { // Special pull case when fPull is 2 /*if (fFixed < 0) while (fFixed < 0) fFixed += 12; else if (fFixed > 12) while (fFixed > 12) fFixed -= 12;*/ float inpitch_norm = inpitch; if (inpitch_norm < 6) while (inpitch_norm < 6) inpitch_norm += 12; else if (inpitch_norm > 6) while (inpitch_norm > 6) inpitch_norm -= 12; /*float a = fFixed - inpitch_norm; float b = fFixed - 12 - inpitch_norm; float c = fFixed + 12 - inpitch_norm; float result = a; if (abs(b) < abs(result)) result = b; if (abs(c) < abs(result)) result = c; outpitch = inpitch + result;*/ float a = inpitch - inpitch_norm; float b = inpitch - 12 - inpitch_norm; float c = inpitch + 12 - inpitch_norm; //post("a=%f b=%f c=%f in_norm=%f\n", a, b, c, inpitch_norm); float result = a; if (abs(b) < abs(result)) result = b; if (abs(c) < abs(result)) result = c; outpitch = result + fFixed; //fprintf(stderr,"outpitch=%f inpitch=%f in_norm=%f\n", outpitch, inpitch, inpitch_norm); } // -- Convert from semitones to scale notes -- ti = (int)(outpitch/12 + 32) - 32; // octave tf = outpitch - ti*12; // semitone in octave ti2 = (int)tf; ti3 = ti2 + 1; // a little bit of pitch correction logic, since it's a convenient place for it if (iNotes[ti2%12]<0 || iNotes[ti3%12]<0) { // if between 2 notes that are more than a semitone apart lowersnap = 1; uppersnap = 1; } else { lowersnap = 0; uppersnap = 0; if (iNotes[ti2%12]==1) { // if specified by user lowersnap = 1; } if (iNotes[ti3%12]==1) { // if specified by user uppersnap = 1; } } // (back to the semitone->scale conversion) // finding next lower pitch in scale while (iNotes[(ti2+12)%12]<0) { ti2 = ti2 - 1; } // finding next higher pitch in scale while (iNotes[ti3%12]<0) { ti3 = ti3 + 1; } tf = (tf-ti2)/(ti3-ti2) + iPitch2Note[(ti2+12)%12]; if (ti2<0) { tf = tf - numNotes; } outpitch = tf + numNotes*ti; // -- Done converting to scale notes -- // The actual pitch correction ti = (int)(outpitch+128) - 128; tf = outpitch - ti - 0.5; ti2 = ti3-ti2; if (ti2>2) { // if more than 2 semitones apart, put a 2-semitone-like transition halfway between tf2 = (float)ti2/2; } else { tf2 = (float)1; } if (fSmooth<0.001) { tf2 = tf*tf2/0.001; } else { tf2 = tf*tf2/fSmooth; } if (tf2<-0.5) tf2 = -0.5; if (tf2>0.5) tf2 = 0.5; tf2 = 0.5*sin(PI*tf2) + 0.5; // jumping between notes using horizontally-scaled sine segment tf2 = tf2 + ti; if ( (tf<0.5 && lowersnap) || (tf>=0.5 && uppersnap) ) { outpitch = fAmount*tf2 + ((float)1-fAmount)*outpitch; } // Add in pitch shift outpitch = outpitch + fShift; // LFO logic tf = fLforate*N/(x->noverlap*fs); if (tf>1) tf=1; x->lfophase = x->lfophase + tf; if (x->lfophase>1) x->lfophase = x->lfophase-1; lfoval = x->lfophase; tf = (fLfosymm + 1)/2; if (tf<=0 || tf>=1) { if (tf<=0) lfoval = 1-lfoval; } else { if (lfoval<=tf) { lfoval = lfoval/tf; } else { lfoval = 1 - (lfoval-tf)/(1-tf); } } if (fLfoshape>=0) { // linear combination of cos and line lfoval = (0.5 - 0.5*cos(lfoval*PI))*fLfoshape + lfoval*(1-fLfoshape); lfoval = fLfoamp*(lfoval*2 - 1); } else { // smoosh the sine horizontally until it's squarish tf = 1 + fLfoshape; if (tf<0.001) { lfoval = (lfoval - 0.5)*2/0.001; } else { lfoval = (lfoval - 0.5)*2/tf; } if (lfoval>1) lfoval = 1; if (lfoval<-1) lfoval = -1; lfoval = fLfoamp*sin(lfoval*PI*0.5); } // add in quantized LFO if (iLfoquant>=1) { outpitch = outpitch + (int)(numNotes*lfoval + numNotes + 0.5) - numNotes; } // Convert back from scale notes to semitones outpitch = outpitch + iScwarp; // output scale rotate implemented here ti = (int)(outpitch/numNotes + 32) - 32; tf = outpitch - ti*numNotes; ti2 = (int)tf; ti3 = ti2 + 1; outpitch = iNote2Pitch[ti3%numNotes] - iNote2Pitch[ti2]; if (ti3>=numNotes) { outpitch = outpitch + 12; } outpitch = outpitch*(tf - ti2) + iNote2Pitch[ti2]; outpitch = outpitch + 12*ti; outpitch = outpitch - (iNote2Pitch[iScwarp] - iNote2Pitch[0]); //more scale rotation here // add in unquantized LFO if (iLfoquant<=0) { outpitch = outpitch + lfoval*2; } if (outpitch<-36) outpitch = -48; if (outpitch>24) outpitch = 24; x->outpitch = outpitch; // ---- END Modify pitch in all kinds of ways! ---- // Compute variables for pitch shifter that depend on pitch x->inphinc = aref*pow(2,inpitch/12)/fs; x->outphinc = aref*pow(2,outpitch/12)/fs; x->phincfact = x->outphinc/x->inphinc; } // ************************ // * END Low-Rate Section * // ************************ // ***************** // * Pitch Shifter * // ***************** // Pitch shifter (kind of like a pitch-synchronous version of Fairbanks' technique) // Note: pitch estimate is naturally N/2 samples old x->phasein = x->phasein + x->inphinc; x->phaseout = x->phaseout + x->inphinc*x->phincfact; // If it happens that there are no snippets placed at the output, grab a new snippet! /* if (x->cbonorm[((long int)x->cbord + (long int)(N/2*(1 - (float)1 / x->phincfact)))%N] < 0.2) { */ /* post( "help!"); */ /* x->phasein = 1; */ /* x->phaseout = 1; */ /* } */ // When input phase resets, take a snippet from N/2 samples in the past if (x->phasein >= 1) { x->phasein = x->phasein - 1; ti2 = x->cbiwr - (long int)N/2; for (ti=-((long int)N)/2; ti<(long int)N/2; ti++) { x->frag[ti%N] = x->cbi[(ti + ti2)%N]; } } // When output phase resets, put a snippet N/2 samples in the future if (x->phaseout >= 1) { x->fragsize = x->fragsize*2; if (x->fragsize >= N) { x->fragsize = N; } x->phaseout = x->phaseout - 1; ti2 = x->cbord + N/2; ti3 = (long int)(((float)x->fragsize) / x->phincfact); if (ti3>=(long int)N/2) { ti3 = N/2 - 1; } for (ti=-ti3/2; ti<(ti3/2); ti++) { tf = x->hannwindow[(long int)N/2 + ti*(long int)N/ti3]; // 3rd degree polynomial interpolator - based on eqns from Hal Chamberlin's book indd = x->phincfact*ti; ind1 = (int)indd; ind2 = ind1+1; ind3 = ind1+2; ind0 = ind1-1; val0 = x->frag[(ind0+N)%N]; val1 = x->frag[(ind1+N)%N]; val2 = x->frag[(ind2+N)%N]; val3 = x->frag[(ind3+N)%N]; vald = 0; vald = vald - (float)0.166666666667 * val0 * (indd - ind1) * (indd - ind2) * (indd - ind3); vald = vald + (float)0.5 * val1 * (indd - ind0) * (indd - ind2) * (indd - ind3); vald = vald - (float)0.5 * val2 * (indd - ind0) * (indd - ind1) * (indd - ind3); vald = vald + (float)0.166666666667 * val3 * (indd - ind0) * (indd - ind1) * (indd - ind2); x->cbo[(ti + ti2 + N)%N] = x->cbo[(ti + ti2 + N)%N] + vald*tf; } x->fragsize = 0; } x->fragsize++; // Get output signal from buffer tf = x->cbo[x->cbord]; /*// Normalize if (tf>0.5) { tf = (float)1/tf; } else { tf = 1; }*/ //tf = tf*x->cbo[x->cbord]; // read buffer tf = x->cbo[x->cbord]; x->cbo[x->cbord] = 0; // erase for next cycle //x->cbonorm[x->cbord] = 0; x->cbord++; // increment read pointer if (x->cbord >= N) { x->cbord = 0; } // ********************* // * END Pitch Shifter * // ********************* ti4 = (x->cbiwr + 2)%N; if (iFcorr>=1) { // The second part of the formant corrector // This is a post-filter that re-applies the formants, designed // to result in the exact original signal when no pitch // manipulation is performed. // tf is signal input // gotta run it 3 times because of a pesky delay free loop // first time: compute 0-response tf2 = tf; fa = 0; fb = fa; for (ti=0; ti<ford; ti++) { fc = (fb-x->frc[ti])*frlamb + x->frb[ti]; tf = x->fbuff[ti][ti4]; fb = fc - tf*fa; x->ftvec[ti] = tf*fc; fa = fa - x->ftvec[ti]; } tf = -fa; for (ti=ford-1; ti>=0; ti--) { tf = tf + x->ftvec[ti]; } f0resp = tf; // second time: compute 1-response fa = 1; fb = fa; for (ti=0; ti<ford; ti++) { fc = (fb-x->frc[ti])*frlamb + x->frb[ti]; tf = x->fbuff[ti][ti4]; fb = fc - tf*fa; x->ftvec[ti] = tf*fc; fa = fa - x->ftvec[ti]; } tf = -fa; for (ti=ford-1; ti>=0; ti--) { tf = tf + x->ftvec[ti]; } f1resp = tf; // now solve equations for output, based on 0-response and 1-response tf = (float)2*tf2; tf2 = tf; tf = ((float)1 - f1resp + f0resp); if (tf!=0) { tf2 = (tf2 + f0resp) / tf; } else { tf2 = 0; } // third time: update delay registers fa = tf2; fb = fa; for (ti=0; ti<ford; ti++) { fc = (fb-x->frc[ti])*frlamb + x->frb[ti]; x->frc[ti] = fc; x->frb[ti] = fb; tf = x->fbuff[ti][ti4]; fb = fc - tf*fa; fa = fa - tf*fc; } tf = tf2; tf = tf + flpa*x->flp; // lowpass post-emphasis filter x->flp = tf; // Bring up the gain slowly when formant correction goes from disabled // to enabled, while things stabilize. if (x->fmute>0.5) { tf = tf*(x->fmute - 0.5)*2; } else { tf = 0; } tf2 = x->fmutealph; x->fmute = (1-tf2) + tf2*x->fmute; // now tf is signal output // ...and we're done messing with formants } else { x->fmute = 0; } // Write audio to output of plugin // Mix (blend between original (delayed) =0 and shifted/corrected =1) *(out++) = fMix*tf + (1-fMix)*x->cbi[ti4]; //*(pfOutput++) = (float) fMix*tf + (1-fMix)*x->cbi[(x->cbiwr - N + 1)%N]; } return (w + 5); // always add one more than the 2nd argument in dsp_add() }
void autotune_init(t_autotune *x,unsigned long sr) { unsigned long ti; x->fs = sr; x->aref = 440; x->fTune = x->aref; if (x->cbsize == 0) { if (x->fs >=88200) { x->cbsize = 4096; } else { x->cbsize = 2048; } } x->corrsize = x->cbsize / 2 + 1; x->pmax = 1/(float)70; // max and min periods (ms) x->pmin = 1/(float)2400; // eventually may want to bring these out as sliders x->pperiod = x->pmax; x->nmax = (unsigned long)(x->fs * x->pmax); if (x->nmax > x->corrsize) { x->nmax = x->corrsize; } x->nmin = (unsigned long)(x->fs * x->pmin); x->cbi = (float*) calloc(x->cbsize, sizeof(float)); x->cbf = (float*) calloc(x->cbsize, sizeof(float)); x->cbo = (float*) calloc(x->cbsize, sizeof(float)); //x->cbonorm = (float*) calloc(x->cbsize, sizeof(float)); x->cbiwr = 0; x->cbord = 0; x->lfophase = 0; // Initialize formant corrector x->ford = 7; // should be sufficient to capture formants x->falph = pow(0.001, (float) 80 / (x->fs)); x->flamb = -(0.8517*sqrt(atan(0.06583*x->fs))-0.1916); // or about -0.88 @ 44.1kHz x->fk = calloc(x->ford, sizeof(float)); x->fb = calloc(x->ford, sizeof(float)); x->fc = calloc(x->ford, sizeof(float)); x->frb = calloc(x->ford, sizeof(float)); x->frc = calloc(x->ford, sizeof(float)); x->fsig = calloc(x->ford, sizeof(float)); x->fsmooth = calloc(x->ford, sizeof(float)); x->fhp = 0; x->flp = 0; x->flpa = pow(0.001, (float) 10 / (x->fs)); x->fbuff = (float**) malloc((x->ford)*sizeof(float*)); for (ti=0; ti<x->ford; ti++) { x->fbuff[ti] = calloc(x->cbsize, sizeof(float)); } x->ftvec = calloc(x->ford, sizeof(float)); x->fmute = 1; x->fmutealph = pow(0.001, (float)1 / (x->fs)); // Standard raised cosine window, max height at N/2 x->hannwindow = (float*) calloc(x->cbsize, sizeof(float)); for (ti=0; ti<x->cbsize; ti++) { x->hannwindow[ti] = -0.5*cos(2*PI*ti/(x->cbsize - 1)) + 0.5; } // Generate a window with a single raised cosine from N/4 to 3N/4 x->cbwindow = (float*) calloc(x->cbsize, sizeof(float)); for (ti=0; ti<(x->cbsize / 2); ti++) { x->cbwindow[ti+x->cbsize/4] = -0.5*cos(4*PI*ti/(x->cbsize - 1)) + 0.5; } if (x->noverlap == 0) x->noverlap = 4; //fprintf(stderr,"%d %d\n", x->cbsize, x->noverlap); x->fx = fft_con(x->cbsize); x->ffttime = (float*) calloc(x->cbsize, sizeof(float)); x->fftfreqre = (float*) calloc(x->corrsize, sizeof(float)); x->fftfreqim = (float*) calloc(x->corrsize, sizeof(float)); // ---- Calculate autocorrelation of window ---- x->acwinv = (float*) calloc(x->cbsize, sizeof(float)); for (ti=0; ti<x->cbsize; ti++) { x->ffttime[ti] = x->cbwindow[ti]; } fft_forward(x->fx, x->cbwindow, x->fftfreqre, x->fftfreqim); for (ti=0; ti<x->corrsize; ti++) { x->fftfreqre[ti] = (x->fftfreqre[ti])*(x->fftfreqre[ti]) + (x->fftfreqim[ti])*(x->fftfreqim[ti]); x->fftfreqim[ti] = 0; } fft_inverse(x->fx, x->fftfreqre, x->fftfreqim, x->ffttime); for (ti=1; ti<x->cbsize; ti++) { x->acwinv[ti] = x->ffttime[ti]/x->ffttime[0]; if (x->acwinv[ti] > 0.000001) { x->acwinv[ti] = (float)1/x->acwinv[ti]; } else { x->acwinv[ti] = 0; } } x->acwinv[0] = 1; // ---- END Calculate autocorrelation of window ---- x->lrshift = 0; x->ptarget = 0; x->sptarget = 0; //x->sptarget = 0; //x->wasvoiced = 0; //x->persistamt = 0; //x->glidepersist = 100; // 100 ms glide persist x->vthresh = 0.7; // The voiced confidence (unbiased peak) threshold level // Pitch shifter initialization x->phprdd = 0.01; // Default period //x->phprd = x->phprdd; x->inphinc = (float)1/(x->phprdd * x->fs); x->phincfact = 1; x->phasein = 0; x->phaseout = 0; x->frag = (float*) calloc(x->cbsize, sizeof(float)); x->fragsize = 0; }
// Called every time we get a new chunk of audio void runAutotalent(Autotalent * Instance, unsigned long SampleCount) { // some kind of buffer, need to find out the type, looks like floats float* pfInput; float* pfOutput; float fAmount; float fSmooth; int iNotes[12]; int iPitch2Note[12]; int iNote2Pitch[12]; int numNotes; float fTune; float fFixed; float fPull; float fShift; int iScwarp; float fLfoamp; float fLforate; float fLfoshape; float fLfosymm; int iLfoquant; int iFcorr; float fFwarp; float fMix; Autotalent* psAutotalent; unsigned long lSampleIndex; long int N; long int Nf; long int fs; float pmin; float pmax; unsigned long nmin; unsigned long nmax; long int ti; long int ti2; long int ti3; long int ti4; float tf; float tf2; // Variables for cubic spline interpolator float indd; int ind0; int ind1; int ind2; int ind3; float vald; float val0; float val1; float val2; float val3; int lowersnap; int uppersnap; float lfoval; float pperiod; float inpitch; float conf; float outpitch; float aref; float fa; float fb; float fc; float fk; float flamb; float frlamb; float falph; float foma; float f1resp; float f0resp; float flpa; int ford; psAutotalent = (Autotalent *)Instance; pfInput = psAutotalent->m_pfInputBuffer1; pfOutput = psAutotalent->m_pfOutputBuffer1; fAmount = (float) *(psAutotalent->m_pfAmount); fSmooth = (float) *(psAutotalent->m_pfSmooth) * 0.8; // Scales max to a more reasonable value fTune = (float) *(psAutotalent->m_pfTune); iNotes[0] = psAutotalent->m_pfKey[AT_A]; iNotes[1] = psAutotalent->m_pfKey[AT_Bb]; iNotes[2] = psAutotalent->m_pfKey[AT_B]; iNotes[3] = psAutotalent->m_pfKey[AT_C]; iNotes[4] = psAutotalent->m_pfKey[AT_Db]; iNotes[5] = psAutotalent->m_pfKey[AT_D]; iNotes[6] = psAutotalent->m_pfKey[AT_Eb]; iNotes[7] = psAutotalent->m_pfKey[AT_E]; iNotes[8] = psAutotalent->m_pfKey[AT_F]; iNotes[9] = psAutotalent->m_pfKey[AT_Gb]; iNotes[10] = psAutotalent->m_pfKey[AT_G]; iNotes[11] = psAutotalent->m_pfKey[AT_Ab]; fFixed = (float) *(psAutotalent->m_pfFixed); fPull = (float) *(psAutotalent->m_pfPull); fShift = (float) *(psAutotalent->m_pfShift); iScwarp = (int) *(psAutotalent->m_pfScwarp); fLfoamp = (float) *(psAutotalent->m_pfLfoamp); fLforate = (float) *(psAutotalent->m_pfLforate); fLfoshape = (float) *(psAutotalent->m_pfLfoshape); fLfosymm = (float) *(psAutotalent->m_pfLfosymm); iLfoquant = (int) *(psAutotalent->m_pfLfoquant); iFcorr = (int) *(psAutotalent->m_pfFcorr); fFwarp = (float) *(psAutotalent->m_pfFwarp); fMix = (float) *(psAutotalent->m_pfMix); // Some logic for the semitone->scale and scale->semitone conversion // If no notes are selected as being in the scale, instead snap to all notes ti2 = 0; for (ti=0; ti<12; ti++) { if (iNotes[ti]>=0) { iPitch2Note[ti] = ti2; iNote2Pitch[ti2] = ti; ti2 = ti2 + 1; } else { iPitch2Note[ti] = -1; } } numNotes = ti2; while (ti2<12) { iNote2Pitch[ti2] = -1; ti2 = ti2 + 1; } if (numNotes==0) { for (ti=0; ti<12; ti++) { iNotes[ti] = 1; iPitch2Note[ti] = ti; iNote2Pitch[ti] = ti; } numNotes = 12; } iScwarp = (iScwarp + numNotes*5)%numNotes; ford = psAutotalent->ford; falph = psAutotalent->falph; foma = (float)1 - falph; flpa = psAutotalent->flpa; flamb = psAutotalent->flamb; tf = pow((float)2,fFwarp/2)*(1+flamb)/(1-flamb); frlamb = (tf - 1)/(tf + 1); psAutotalent->aref = (float)fTune; N = psAutotalent->cbsize; Nf = psAutotalent->corrsize; fs = psAutotalent->fs; pmax = psAutotalent->pmax; pmin = psAutotalent->pmin; nmax = psAutotalent->nmax; nmin = psAutotalent->nmin; aref = psAutotalent->aref; pperiod = psAutotalent->pmax; inpitch = psAutotalent->inpitch; conf = psAutotalent->conf; outpitch = psAutotalent->outpitch; /******************* * MAIN DSP LOOP * *******************/ for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) { // load data into circular buffer tf = (float) *(pfInput++); ti4 = psAutotalent->cbiwr; psAutotalent->cbi[ti4] = tf; if (iFcorr>=1) { // Somewhat experimental formant corrector // formants are removed using an adaptive pre-filter and // re-introduced after pitch manipulation using post-filter // tf is signal input fa = tf - psAutotalent->fhp; // highpass pre-emphasis filter psAutotalent->fhp = tf; fb = fa; for (ti=0; ti<ford; ti++) { psAutotalent->fsig[ti] = fa*fa*foma + psAutotalent->fsig[ti]*falph; fc = (fb-psAutotalent->fc[ti])*flamb + psAutotalent->fb[ti]; psAutotalent->fc[ti] = fc; psAutotalent->fb[ti] = fb; fk = fa*fc*foma + psAutotalent->fk[ti]*falph; psAutotalent->fk[ti] = fk; tf = fk/(psAutotalent->fsig[ti] + 0.000001); tf = tf*foma + psAutotalent->fsmooth[ti]*falph; psAutotalent->fsmooth[ti] = tf; psAutotalent->fbuff[ti][ti4] = tf; fb = fc - tf*fa; fa = fa - tf*fc; } psAutotalent->cbf[ti4] = fa; // Now hopefully the formants are reduced // More formant correction code at the end of the DSP loop } else { psAutotalent->cbf[ti4] = tf; } // Input write pointer logic psAutotalent->cbiwr++; if (psAutotalent->cbiwr >= N) { psAutotalent->cbiwr = 0; } // ******************** // * Low-rate section * // ******************** // Every N/noverlap samples, run pitch estimation / manipulation code if ((psAutotalent->cbiwr)%(N/psAutotalent->noverlap) == 0) { // ---- Obtain autocovariance ---- // Window and fill FFT buffer ti2 = psAutotalent->cbiwr; for (ti=0; ti<N; ti++) { psAutotalent->ffttime[ti] = (float)(psAutotalent->cbi[(ti2-ti+N)%N]*psAutotalent->cbwindow[ti]); } // Calculate FFT fft_forward(psAutotalent->fmembvars, psAutotalent->ffttime, psAutotalent->fftfreqre, psAutotalent->fftfreqim); // Remove DC psAutotalent->fftfreqre[0] = 0; psAutotalent->fftfreqim[0] = 0; // Take magnitude squared for (ti=1; ti<Nf; ti++) { psAutotalent->fftfreqre[ti] = (psAutotalent->fftfreqre[ti])*(psAutotalent->fftfreqre[ti]) + (psAutotalent->fftfreqim[ti])*(psAutotalent->fftfreqim[ti]); psAutotalent->fftfreqim[ti] = 0; } // Calculate IFFT fft_inverse(psAutotalent->fmembvars, psAutotalent->fftfreqre, psAutotalent->fftfreqim, psAutotalent->ffttime); // Normalize tf = (float)1/psAutotalent->ffttime[0]; for (ti=1; ti<N; ti++) { psAutotalent->ffttime[ti] = psAutotalent->ffttime[ti] * tf; } psAutotalent->ffttime[0] = 1; // ---- END Obtain autocovariance ---- // ---- Calculate pitch and confidence ---- // Calculate pitch period // Pitch period is determined by the location of the max (biased) // peak within a given range // Confidence is determined by the corresponding unbiased height tf2 = 0; pperiod = pmin; for (ti=nmin; ti<nmax; ti++) { ti2 = ti-1; ti3 = ti+1; if (ti2<0) { ti2 = 0; } if (ti3>Nf) { ti3 = Nf; } tf = psAutotalent->ffttime[ti]; if (tf>psAutotalent->ffttime[ti2] && tf>=psAutotalent->ffttime[ti3] && tf>tf2) { tf2 = tf; ti4 = ti; } } if (tf2>0) { conf = tf2*psAutotalent->acwinv[ti4]; if (ti4>0 && ti4<Nf) { // Find the center of mass in the vicinity of the detected peak tf = psAutotalent->ffttime[ti4-1]*(ti4-1); tf = tf + psAutotalent->ffttime[ti4]*(ti4); tf = tf + psAutotalent->ffttime[ti4+1]*(ti4+1); tf = tf/(psAutotalent->ffttime[ti4-1] + psAutotalent->ffttime[ti4] + psAutotalent->ffttime[ti4+1]); pperiod = tf/fs; } else { pperiod = (float)ti4/fs; } } // Convert to semitones tf = (float) -12*log10((float)aref*pperiod)*L2SC; if (conf>=psAutotalent->vthresh) { inpitch = tf; psAutotalent->inpitch = tf; // update pitch only if voiced } psAutotalent->conf = conf; *(psAutotalent->m_pfPitch) = inpitch; *(psAutotalent->m_pfConf) = conf; // ---- END Calculate pitch and confidence ---- // ---- Modify pitch in all kinds of ways! ---- outpitch = inpitch; // Pull to fixed pitch outpitch = (1-fPull)*outpitch + fPull*fFixed; // -- Convert from semitones to scale notes -- ti = (int)(outpitch/12 + 32) - 32; // octave tf = outpitch - ti*12; // semitone in octave ti2 = (int)tf; ti3 = ti2 + 1; // a little bit of pitch correction logic, since it's a convenient place for it if (iNotes[ti2%12]<0 || iNotes[ti3%12]<0) { // if between 2 notes that are more than a semitone apart lowersnap = 1; uppersnap = 1; } else { lowersnap = 0; uppersnap = 0; if (iNotes[ti2%12]==1) { // if specified by user lowersnap = 1; } if (iNotes[ti3%12]==1) { // if specified by user uppersnap = 1; } } // (back to the semitone->scale conversion) // finding next lower pitch in scale while (iNotes[(ti2+12)%12]<0) { ti2 = ti2 - 1; } // finding next higher pitch in scale while (iNotes[ti3%12]<0) { ti3 = ti3 + 1; } tf = (tf-ti2)/(ti3-ti2) + iPitch2Note[(ti2+12)%12]; if (ti2<0) { tf = tf - numNotes; } outpitch = tf + numNotes*ti; // -- Done converting to scale notes -- // The actual pitch correction ti = (int)(outpitch+128) - 128; tf = outpitch - ti - 0.5; ti2 = ti3-ti2; if (ti2>2) { // if more than 2 semitones apart, put a 2-semitone-like transition halfway between tf2 = (float)ti2/2; } else { tf2 = (float)1; } if (fSmooth<0.001) { tf2 = tf*tf2/0.001; } else { tf2 = tf*tf2/fSmooth; } if (tf2<-0.5) tf2 = -0.5; if (tf2>0.5) tf2 = 0.5; tf2 = 0.5*sin(PI*tf2) + 0.5; // jumping between notes using horizontally-scaled sine segment tf2 = tf2 + ti; if ( (tf<0.5 && lowersnap) || (tf>=0.5 && uppersnap) ) { outpitch = fAmount*tf2 + ((float)1-fAmount)*outpitch; } // Add in pitch shift outpitch = outpitch + fShift; // LFO logic tf = fLforate*N/(psAutotalent->noverlap*fs); if (tf>1) tf=1; psAutotalent->lfophase = psAutotalent->lfophase + tf; if (psAutotalent->lfophase>1) psAutotalent->lfophase = psAutotalent->lfophase-1; lfoval = psAutotalent->lfophase; tf = (fLfosymm + 1)/2; if (tf<=0 || tf>=1) { if (tf<=0) { lfoval = 1-lfoval; } } else { if (lfoval<=tf) { lfoval = lfoval/tf; } else { lfoval = 1 - (lfoval-tf)/(1-tf); } } if (fLfoshape>=0) { // linear combination of cos and line lfoval = (0.5 - 0.5*cos(lfoval*PI))*fLfoshape + lfoval*(1-fLfoshape); lfoval = fLfoamp*(lfoval*2 - 1); } else { // smoosh the sine horizontally until it's squarish tf = 1 + fLfoshape; if (tf<0.001) { lfoval = (lfoval - 0.5)*2/0.001; } else { lfoval = (lfoval - 0.5)*2/tf; } if (lfoval>1) lfoval = 1; if (lfoval<-1) lfoval = -1; lfoval = fLfoamp*sin(lfoval*PI*0.5); } // add in quantized LFO if (iLfoquant>=1) { outpitch = outpitch + (int)(numNotes*lfoval + numNotes + 0.5) - numNotes; } // Convert back from scale notes to semitones outpitch = outpitch + iScwarp; // output scale rotate implemented here ti = (int)(outpitch/numNotes + 32) - 32; tf = outpitch - ti*numNotes; ti2 = (int)tf; ti3 = ti2 + 1; outpitch = iNote2Pitch[ti3%numNotes] - iNote2Pitch[ti2]; if (ti3>=numNotes) { outpitch = outpitch + 12; } outpitch = outpitch*(tf - ti2) + iNote2Pitch[ti2]; outpitch = outpitch + 12*ti; outpitch = outpitch - (iNote2Pitch[iScwarp] - iNote2Pitch[0]); //more scale rotation here // add in unquantized LFO if (iLfoquant<=0) { outpitch = outpitch + lfoval*2; } if (outpitch<-36) outpitch = -48; if (outpitch>24) outpitch = 24; psAutotalent->outpitch = outpitch; // ---- END Modify pitch in all kinds of ways! ---- // Compute variables for pitch shifter that depend on pitch psAutotalent->inphinc = aref*pow(2,inpitch/12)/fs; psAutotalent->outphinc = aref*pow(2,outpitch/12)/fs; psAutotalent->phincfact = psAutotalent->outphinc/psAutotalent->inphinc; } // ************************ // * END Low-Rate Section * // ************************ // ***************** // * Pitch Shifter * // ***************** // Pitch shifter (kind of like a pitch-synchronous version of Fairbanks' technique) // Note: pitch estimate is naturally N/2 samples old psAutotalent->phasein = psAutotalent->phasein + psAutotalent->inphinc; psAutotalent->phaseout = psAutotalent->phaseout + psAutotalent->outphinc; // When input phase resets, take a snippet from N/2 samples in the past if (psAutotalent->phasein >= 1) { psAutotalent->phasein = psAutotalent->phasein - 1; ti2 = psAutotalent->cbiwr - N/2; for (ti=-N/2; ti<N/2; ti++) { psAutotalent->frag[(ti+N)%N] = psAutotalent->cbf[(ti + ti2 + N)%N]; } } // When output phase resets, put a snippet N/2 samples in the future if (psAutotalent->phaseout >= 1) { psAutotalent->fragsize = psAutotalent->fragsize*2; if (psAutotalent->fragsize > N) { psAutotalent->fragsize = N; } psAutotalent->phaseout = psAutotalent->phaseout - 1; ti2 = psAutotalent->cbord + N/2; ti3 = (long int)(((float)psAutotalent->fragsize) / psAutotalent->phincfact); if (ti3>=N/2) { ti3 = N/2 - 1; } for (ti=-ti3/2; ti<(ti3/2); ti++) { tf = psAutotalent->hannwindow[(long int)N/2 + ti*(long int)N/ti3]; // 3rd degree polynomial interpolator - based on eqns from Hal Chamberlin's book indd = psAutotalent->phincfact*ti; ind1 = (int)indd; ind2 = ind1+1; ind3 = ind1+2; ind0 = ind1-1; val0 = psAutotalent->frag[(ind0+N)%N]; val1 = psAutotalent->frag[(ind1+N)%N]; val2 = psAutotalent->frag[(ind2+N)%N]; val3 = psAutotalent->frag[(ind3+N)%N]; vald = 0; vald = vald - (float)0.166666666667 * val0 * (indd - ind1) * (indd - ind2) * (indd - ind3); vald = vald + (float)0.5 * val1 * (indd - ind0) * (indd - ind2) * (indd - ind3); vald = vald - (float)0.5 * val2 * (indd - ind0) * (indd - ind1) * (indd - ind3); vald = vald + (float)0.166666666667 * val3 * (indd - ind0) * (indd - ind1) * (indd - ind2); psAutotalent->cbo[(ti + ti2 + N)%N] = psAutotalent->cbo[(ti + ti2 + N)%N] + vald*tf; } psAutotalent->fragsize = 0; } psAutotalent->fragsize++; // Get output signal from buffer tf = psAutotalent->cbo[psAutotalent->cbord]; // read buffer psAutotalent->cbo[psAutotalent->cbord] = 0; // erase for next cycle psAutotalent->cbord++; // increment read pointer if (psAutotalent->cbord >= N) { psAutotalent->cbord = 0; } // ********************* // * END Pitch Shifter * // ********************* ti4 = (psAutotalent->cbiwr + 2)%N; if (iFcorr>=1) { // The second part of the formant corrector // This is a post-filter that re-applies the formants, designed // to result in the exact original signal when no pitch // manipulation is performed. // tf is signal input // gotta run it 3 times because of a pesky delay free loop // first time: compute 0-response tf2 = tf; fa = 0; fb = fa; for (ti=0; ti<ford; ti++) { fc = (fb-psAutotalent->frc[ti])*frlamb + psAutotalent->frb[ti]; tf = psAutotalent->fbuff[ti][ti4]; fb = fc - tf*fa; psAutotalent->ftvec[ti] = tf*fc; fa = fa - psAutotalent->ftvec[ti]; } tf = -fa; for (ti=ford-1; ti>=0; ti--) { tf = tf + psAutotalent->ftvec[ti]; } f0resp = tf; // second time: compute 1-response fa = 1; fb = fa; for (ti=0; ti<ford; ti++) { fc = (fb-psAutotalent->frc[ti])*frlamb + psAutotalent->frb[ti]; tf = psAutotalent->fbuff[ti][ti4]; fb = fc - tf*fa; psAutotalent->ftvec[ti] = tf*fc; fa = fa - psAutotalent->ftvec[ti]; } tf = -fa; for (ti=ford-1; ti>=0; ti--) { tf = tf + psAutotalent->ftvec[ti]; } f1resp = tf; // now solve equations for output, based on 0-response and 1-response tf = (float)2*tf2; tf2 = tf; tf = ((float)1 - f1resp + f0resp); if (tf!=0) { tf2 = (tf2 + f0resp) / tf; } else { tf2 = 0; } // third time: update delay registers fa = tf2; fb = fa; for (ti=0; ti<ford; ti++) { fc = (fb-psAutotalent->frc[ti])*frlamb + psAutotalent->frb[ti]; psAutotalent->frc[ti] = fc; psAutotalent->frb[ti] = fb; tf = psAutotalent->fbuff[ti][ti4]; fb = fc - tf*fa; fa = fa - tf*fc; } tf = tf2; tf = tf + flpa*psAutotalent->flp; // lowpass post-emphasis filter psAutotalent->flp = tf; // Bring up the gain slowly when formant correction goes from disabled // to enabled, while things stabilize. if (psAutotalent->fmute>0.5) { tf = tf*(psAutotalent->fmute - 0.5)*2; } else { tf = 0; } tf2 = psAutotalent->fmutealph; psAutotalent->fmute = (1-tf2) + tf2*psAutotalent->fmute; // now tf is signal output // ...and we're done messing with formants } else { psAutotalent->fmute = 0; } // Write audio to output of plugin // Mix (blend between original (delayed) =0 and processed =1) *(pfOutput++) = fMix*tf + (1-fMix)*psAutotalent->cbi[ti4]; } // Tell the host the algorithm latency *(psAutotalent->m_pfLatency) = (N-1); }
Autotalent * instantiateAutotalent(unsigned long SampleRate) { unsigned long ti; Autotalent* membvars = malloc(sizeof(Autotalent)); membvars->aref = 440; membvars->fs = SampleRate; if (SampleRate>=88200) { membvars->cbsize = 4096; } else { membvars->cbsize = 2048; } membvars->corrsize = membvars->cbsize / 2 + 1; membvars->pmax = 1/(float)70; // max and min periods (ms) membvars->pmin = 1/(float)700; // eventually may want to bring these out as sliders membvars->nmax = (unsigned long)(SampleRate * membvars->pmax); if (membvars->nmax > membvars->corrsize) { membvars->nmax = membvars->corrsize; } membvars->nmin = (unsigned long)(SampleRate * membvars->pmin); membvars->cbi = calloc(membvars->cbsize, sizeof(float)); membvars->cbf = calloc(membvars->cbsize, sizeof(float)); membvars->cbo = calloc(membvars->cbsize, sizeof(float)); membvars->cbiwr = 0; membvars->cbord = 0; membvars->lfophase = 0; // Initialize formant corrector membvars->ford = 7; // should be sufficient to capture formants membvars->falph = pow(0.001, (float) 80 / (SampleRate)); membvars->flamb = -(0.8517*sqrt(atan(0.06583*SampleRate))-0.1916); // or about -0.88 @ 44.1kHz membvars->fk = calloc(membvars->ford, sizeof(float)); membvars->fb = calloc(membvars->ford, sizeof(float)); membvars->fc = calloc(membvars->ford, sizeof(float)); membvars->frb = calloc(membvars->ford, sizeof(float)); membvars->frc = calloc(membvars->ford, sizeof(float)); membvars->fsig = calloc(membvars->ford, sizeof(float)); membvars->fsmooth = calloc(membvars->ford, sizeof(float)); membvars->fhp = 0; membvars->flp = 0; membvars->flpa = pow(0.001, (float) 10 / (SampleRate)); membvars->fbuff = (float**) malloc((membvars->ford)*sizeof(float*)); for (ti=0; ti<membvars->ford; ti++) { membvars->fbuff[ti] = calloc(membvars->cbsize, sizeof(float)); } membvars->ftvec = calloc(membvars->ford, sizeof(float)); membvars->fmute = 1; membvars->fmutealph = pow(0.001, (float)1 / (SampleRate)); // Standard raised cosine window, max height at N/2 membvars->hannwindow = calloc(membvars->cbsize, sizeof(float)); for (ti=0; ti<membvars->cbsize; ti++) { membvars->hannwindow[ti] = -0.5*cos(2*PI*ti/membvars->cbsize) + 0.5; } // Generate a window with a single raised cosine from N/4 to 3N/4 membvars->cbwindow = calloc(membvars->cbsize, sizeof(float)); for (ti=0; ti<(membvars->cbsize / 2); ti++) { membvars->cbwindow[ti+membvars->cbsize/4] = -0.5*cos(4*PI*ti/(membvars->cbsize - 1)) + 0.5; } membvars->noverlap = 4; membvars->fmembvars = fft_con(membvars->cbsize); membvars->ffttime = calloc(membvars->cbsize, sizeof(float)); membvars->fftfreqre = calloc(membvars->corrsize, sizeof(float)); membvars->fftfreqim = calloc(membvars->corrsize, sizeof(float)); // ---- Calculate autocorrelation of window ---- membvars->acwinv = calloc(membvars->cbsize, sizeof(float)); for (ti=0; ti<membvars->cbsize; ti++) { membvars->ffttime[ti] = membvars->cbwindow[ti]; } fft_forward(membvars->fmembvars, membvars->cbwindow, membvars->fftfreqre, membvars->fftfreqim); for (ti=0; ti<membvars->corrsize; ti++) { membvars->fftfreqre[ti] = (membvars->fftfreqre[ti])*(membvars->fftfreqre[ti]) + (membvars->fftfreqim[ti])*(membvars->fftfreqim[ti]); membvars->fftfreqim[ti] = 0; } fft_inverse(membvars->fmembvars, membvars->fftfreqre, membvars->fftfreqim, membvars->ffttime); for (ti=1; ti<membvars->cbsize; ti++) { membvars->acwinv[ti] = membvars->ffttime[ti]/membvars->ffttime[0]; if (membvars->acwinv[ti] > 0.000001) { membvars->acwinv[ti] = (float)1/membvars->acwinv[ti]; } else { membvars->acwinv[ti] = 0; } } membvars->acwinv[0] = 1; // ---- END Calculate autocorrelation of window ---- membvars->lrshift = 0; membvars->ptarget = 0; membvars->sptarget = 0; membvars->vthresh = 0.7; // The voiced confidence (unbiased peak) threshold level // Pitch shifter initialization membvars->phprdd = 0.01; // Default period membvars->inphinc = (float)1/(membvars->phprdd * SampleRate); membvars->phincfact = 1; membvars->phasein = 0; membvars->phaseout = 0; membvars->frag = calloc(membvars->cbsize, sizeof(float)); membvars->fragsize = 0; return membvars; }