static void vftom_perform(t_vftom *x, t_symbol *s, int argc, t_atom *argv) { int i; t_atom *ap,*app; ap = (t_atom *)getbytes(sizeof(t_atom)*argc); app=ap; for (i = 0; i < argc; i++) { SETFLOAT(app, ftom(atom_getfloat(argv++))); app++; } outlet_list(x->x_obj.ob_outlet,gensym("list"),argc,ap); freebytes(ap,argc); }
// This is the actual Fiddle~ code void pitch_getit(t_pitch *x) { t_int i, j, k; t_peak *pk1; // peaks found t_peakout *pk2; // peaks to output t_histopeak *hp1; t_float power_spec = 0.0f, total_power = 0.0f, total_loudness = 0.0f, total_db = 0.0f; t_float *fp1, *fp2; t_float *spec = x->BufFFT, *powSpec = x->BufPower, threshold, mult; t_int n = x->FFTSize/2; t_int npitch, newphase, oldphase, npeak = 0; t_int logn = pitch_ilog2(n); t_float maxbin = BINPEROCT * (logn-2); t_float hzperbin = x->x_Fs/x->FFTSize; t_float coeff = x->FFTSize/(t_float)x->BufSize; t_float *histogram = x->histBuf + BINGUARD; t_int npeaktot = (x->x_npeakout > x->x_npeakanal ? x->x_npeakout : x->x_npeakanal); t_pitchhist *phist; // Circular buffer for History oldphase = x->x_histphase; newphase = x->x_histphase + 1; if (newphase == HISTORY) newphase = 0; x->x_histphase = newphase; // Get spectrum power for (i=0; i<n; i++) power_spec += powSpec[i]; total_power = 4.0f * power_spec; // Compensate for fiddle~ power estimation (difference of 6 dB) if (total_power > POWERTHRES) { total_db = (FIDDLEDB_REF-DBFUDGE) + LOGTODB*flog(total_power/n); // dB power estimation of fiddle~ total_loudness = fsqrt(fsqrt(power_spec)); // Use the actual real estimation rather than fiddle~'s if (total_db < 0) total_db = 0.0f; } else { total_db = total_loudness = 0.0f; } // Store new db in history vector x->x_dbs[newphase] = total_db; // Not enough power to find anything if (total_db < x->x_amplo) goto nopow; // search for peaks pk1 = x->x_peaklist; for (i=MINBIN, fp1=spec+2*MINBIN, fp2=powSpec+MINBIN; (i<n-3) && (npeak<npeaktot); i++, fp1+=2, fp2++) { t_float height = fp2[0], h1 = fp2[-1], h2 = fp2[1]; // Bin power and adjacents t_float totalfreq, pfreq, f1, f2, m, var, stdev; if (height<h1 || height<h2 || h1*coeff<POWERTHRES*total_power || h2*coeff<POWERTHRES*total_power) continue; // Go to next // Use an informal phase vocoder to estimate the frequency pfreq = ((fp1[-4] - fp1[4]) * (2.0f * fp1[0] - fp1[4] - fp1[-4]) + (fp1[-3] - fp1[5]) * (2.0f * fp1[1] - fp1[5] - fp1[-3])) / (2.0f * height); // Do this for the two adjacent bins too f1 = ((fp1[-6] - fp1[2]) * (2.0f * fp1[-2] - fp1[2] - fp1[-6]) + (fp1[-5] - fp1[3]) * (2.0f * fp1[-1] - fp1[3] - fp1[-5])) / (2.0f * h1) - 1; f2 = ((fp1[-2] - fp1[6]) * (2.0f * fp1[2] - fp1[6] - fp1[-2]) + (fp1[-1] - fp1[7]) * (2.0f * fp1[3] - fp1[7] - fp1[-1])) / (2.0f * h2) + 1; // get sample mean and variance of the three m = 0.333333f * (pfreq + f1 + f2); var = 0.5f * ((pfreq-m)*(pfreq-m) + (f1-m)*(f1-m) + (f2-m)*(f2-m)); totalfreq = i + m; // BAD HACK TO BE CHANGE IN NEXT VERSION !!!! if (coeff > 1) { switch ((t_int)coeff) { case 2: mult = 0.005; break; case 4: mult = 0.125; break; case 8: mult = 0.2; break; case 16: mult = 0.25; // weird values found by trying to get npeak around 6-7 break; default: mult = 0.25; } threshold = KNOCKTHRESH * height * mult; } else { threshold = KNOCKTHRESH * height; } if ((var * total_power) > threshold || (var < 1e-30)) continue; stdev = fsqrt(var); if (totalfreq < 4) totalfreq = 4; // Store the peak info in the list of peaks pk1->p_width = stdev; pk1->p_pow = height; pk1->p_loudness = fsqrt(fsqrt(height)); pk1->p_fp = fp1; pk1->p_freq = totalfreq; npeak++; pk1++; } // end for // prepare the raw peaks for output for (i=0, pk1=x->x_peaklist, pk2=x->peakBuf; i<npeak; i++, pk1++, pk2++) { t_float loudness = pk1->p_loudness; if (i>=x->x_npeakout) break; pk2->po_freq = hzperbin * pk1->p_freq; pk2->po_amp = (2.f/(t_float)n) * loudness * loudness * coeff; } // in case npeak < x->x_npeakout for (; i<x->x_npeakout; i++, pk2++) pk2->po_amp = pk2->po_freq = 0; // now, make a sort of "likelihood" spectrum. Proceeding in 48ths of an octave, // from 2 to n/2 (in bins), the likelihood of each pitch range is contributed // to by every peak in peaklist that's an integer multiple of it in frequency if (npeak > x->x_npeakanal) npeak = x->x_npeakanal; // max # peaks to analyze // Initialize histogram buffer to 0 for (i=0, fp1=histogram; i<maxbin; i++) *fp1++ = 0.0f; for (i=0, pk1=x->x_peaklist; i<npeak; i++, pk1++) { t_float pit = BPEROOVERLOG2 * flog(pk1->p_freq) - 96.0f; t_float binbandwidth = FACTORTOBINS * pk1->p_width/pk1->p_freq; t_float putbandwidth = (binbandwidth < 2 ? 2 : binbandwidth); t_float weightbandwidth = (binbandwidth < 1.0f ? 1.0f : binbandwidth); t_float weightamp = 4.0f * pk1->p_loudness / total_loudness; for (j=0, fp2=pitch_partialonset; j<NPARTIALONSET; j++, fp2++) { t_float bin = pit - *fp2; if (bin<maxbin) { t_float para, pphase, score = BINAMPCOEFF * weightamp / ((j+x->x_npartial) * weightbandwidth); t_int firstbin = bin + 0.5f - 0.5f * putbandwidth; t_int lastbin = bin + 0.5f + 0.5f * putbandwidth; t_int ibw = lastbin - firstbin; if (firstbin < -BINGUARD) break; para = 1.0f / (putbandwidth * putbandwidth); for (k=0, fp1=histogram+firstbin, pphase=firstbin-bin; k<=ibw; k++, fp1++, pphase+=1.0f) *fp1 += score * (1.0f - para * pphase * pphase); } } // end for } // end for //post("npeaks = %d, %f",npeak,mult); // For debugging weird hack!!! // Next we find up to NPITCH strongest peaks in the histogram. // If a peak is related to a stronger one via an interval in // the pitch_partialonset array, we suppress it. for (npitch=0; npitch<x->x_npitch; npitch++) { t_int index; t_float best; if (npitch) { for (best=0, index=-1, j=1; j<maxbin-1; j++) { if ((histogram[j]>best) && (histogram[j]>histogram[j-1]) && (histogram[j]>histogram[j+1])) { for (k=0; k<npitch; k++) if (x->x_histvec[k].h_index == j) goto peaknogood; for (k=0; k<NPARTIALONSET; k++) { if ((j-pitch_intpartialonset[k]) < 0) break; if (histogram[j-pitch_intpartialonset[k]] > histogram[j]) goto peaknogood; } for (k=0; k<NPARTIALONSET; k++) { if (j+ pitch_intpartialonset[k] >= maxbin) break; if (histogram[j+pitch_intpartialonset[k]] > histogram[j]) goto peaknogood; } index = j; best = histogram[j]; } peaknogood: ; } } else { best = 0; index = -1; for (j=0; j<maxbin; j++) if (histogram[j] > best) { index = j; best = histogram[j]; } } if (index < 0) break; x->x_histvec[npitch].h_value = best; x->x_histvec[npitch].h_index = index; } // for each histogram peak, we now search back through the // FFT peaks. A peak is a pitch if either there are several // harmonics that match it, or else if (a) the fundamental is // present, and (b) the sum of the powers of the contributing peaks // is at least 1/100 of the total power. // // A peak is a contributor if its frequency is within 25 cents of // a partial from 1 to 16. // // Finally, we have to be at least 5 bins in frequency, which // corresponds to 2-1/5 periods fitting in the analysis window. for (i=0; i<npitch; i++) { t_float cumpow=0, cumstrength=0, freqnum=0, freqden=0; t_int npartials=0, nbelow8=0; // guessed-at frequency in bins t_float putfreq = fexp((1.0f / BPEROOVERLOG2) * (x->x_histvec[i].h_index + 96.0f)); for (j=0; j<npeak; j++) { t_float fpnum = x->x_peaklist[j].p_freq/putfreq; t_int pnum = fpnum + 0.5f; t_float fipnum = pnum; t_float deviation; if ((pnum>16) || (pnum<1)) continue; deviation = 1.0f - fpnum/fipnum; if ((deviation > -PARTIALDEVIANCE) && (deviation < PARTIALDEVIANCE)) { // we figure this is a partial since it's within 1/4 of // a halftone of a multiple of the putative frequency. t_float stdev, weight; npartials++; if (pnum<8) nbelow8++; cumpow += x->x_peaklist[j].p_pow; cumstrength += fsqrt(fsqrt(x->x_peaklist[j].p_pow)); stdev = (x->x_peaklist[j].p_width > MINBW ? x->x_peaklist[j].p_width : MINBW); weight = 1.0f / ((stdev*fipnum) * (stdev*fipnum)); freqden += weight; freqnum += weight * x->x_peaklist[j].p_freq/fipnum; } // end if } // end for if (((nbelow8<4) || (npartials<DEFNPARTIAL)) && (cumpow < (0.01f * total_power))) { x->x_histvec[i].h_value = 0; } else { t_float pitchpow = (cumstrength * cumstrength * cumstrength * cumstrength); t_float freqinbins = freqnum/freqden; // check for minimum output frequency if (freqinbins < MINFREQINBINS) { x->x_histvec[i].h_value = 0; } else { // we passed all tests... save the values we got x->x_histvec[i].h_pitch = ftom(hzperbin * freqnum/freqden); x->x_histvec[i].h_loud = (FIDDLEDB_REF-DBFUDGE) + LOGTODB*flog(pitchpow*coeff/n); } } // end else } // end for // Now try to find continuous pitch tracks that match the new pitches. // First mark each peak unmatched. for (i=0, hp1=x->x_histvec; i<npitch; i++, hp1++) hp1->h_used = 0; // For each old pitch, try to match a new one to it. for (i=0, phist=x->x_hist; i<x->x_npitch; i++, phist++) { t_float thispitch = phist->h_pitches[oldphase]; phist->h_pitch = 0; // no output, thanks... phist->h_wherefrom = 0; if (thispitch == 0.0f) continue; for (j=0, hp1=x->x_histvec; j<npitch; j++, hp1++) if ((hp1->h_value > 0) && (hp1->h_pitch > thispitch - GLISS) && (hp1->h_pitch < thispitch + GLISS)) { phist->h_wherefrom = hp1; hp1->h_used = 1; } } for (i=0, hp1=x->x_histvec; i<npitch; i++, hp1++) if ((hp1->h_value > 0) && !hp1->h_used) { for (j=0, phist=x->x_hist; j<x->x_npitch; j++, phist++) if (!phist->h_wherefrom) { phist->h_wherefrom = hp1; phist->h_age = 0; phist->h_noted = 0; hp1->h_used = 1; goto happy; } break; happy: ; } // end if // Copy the pitch info into the history vector for (i=0, phist=x->x_hist; i<x->x_npitch; i++, phist++) { if (phist->h_wherefrom) { phist->h_amps[newphase] = phist->h_wherefrom->h_loud; phist->h_pitches[newphase] = phist->h_wherefrom->h_pitch; (phist->h_age)++; } else { phist->h_age = 0; phist->h_amps[newphase] = phist->h_pitches[newphase] = 0; } } // end for // Look for envelope attacks x->x_attackvalue = 0; if (x->x_peaked) { if (total_db > x->x_amphi) { t_int binlook = newphase - x->x_attackbins; if (binlook < 0) binlook += HISTORY; if (total_db > x->x_dbs[binlook] + x->x_attackthresh) { x->x_attackvalue = 1; x->x_peaked = 0; } } } else { t_int binlook = newphase - x->x_attackbins; if (binlook < 0) binlook += HISTORY; if ((x->x_dbs[binlook] > x->x_amphi) && (x->x_dbs[binlook] > total_db)) x->x_peaked = 1; } // For each current frequency track, test for a new note using a // stability criterion. Later perhaps we should also do as in // pitch~ and check for unstable notes a posteriori when // there's a new attack with no note found since the last onset; // but what's an attack &/or onset when we're polyphonic? for (i=0, phist=x->x_hist; i<x->x_npitch; i++, phist++) { // if we've found a pitch but we've now strayed from it, turn it off if (phist->h_noted) { if (phist->h_pitches[newphase] > phist->h_noted + x->x_vibdepth || phist->h_pitches[newphase] < phist->h_noted - x->x_vibdepth) phist->h_noted = 0; } else { if (phist->h_wherefrom && phist->h_age >= x->x_vibbins) { t_float centroid = 0; t_int not = 0; for (j=0, k=newphase; j<x->x_vibbins; j++) { centroid += phist->h_pitches[k]; k--; if (k<0) k = HISTORY-1; } centroid /= x->x_vibbins; for (j=0, k=newphase; j<x->x_vibbins; j++) { // calculate deviation from norm t_float dev = centroid - phist->h_pitches[k]; k--; if (k<0) k = HISTORY-1; if ((dev > x->x_vibdepth) || (-dev > x->x_vibdepth)) not = 1; } if (!not) { phist->h_pitch = phist->h_noted = centroid; } } // end if } // end else } return; nopow: for (i=0; i<x->x_npitch; i++) { x->x_hist[i].h_pitch = x->x_hist[i].h_noted = x->x_hist[i].h_pitches[newphase] = x->x_hist[i].h_amps[newphase] = x->x_hist[i].h_age = 0; } x->x_peaked = 1; x->x_dbage = 0; return; }