void FFTTrigger_Ctor(FFTTrigger *unit) { World *world = unit->mWorld; /* uint32 bufnum = (uint32)IN0(0); Print("FFTTrigger_Ctor: bufnum is %i\n", bufnum); if (bufnum >= world->mNumSndBufs) bufnum = 0; SndBuf *buf = world->mSndBufs + bufnum; */ uint32 bufnum = (uint32)IN0(0); //Print("FFTTrigger_Ctor: bufnum is %i\n", bufnum); SndBuf *buf; if (bufnum >= world->mNumSndBufs) { int localBufNum = bufnum - world->mNumSndBufs; Graph *parent = unit->mParent; if(localBufNum <= parent->localMaxBufNum) { buf = parent->mLocalSndBufs + localBufNum; } else { bufnum = 0; buf = world->mSndBufs + bufnum; } } else { buf = world->mSndBufs + bufnum; } LOCK_SNDBUF(buf); unit->m_fftsndbuf = buf; unit->m_fftbufnum = bufnum; unit->m_fullbufsize = buf->samples; int numSamples = unit->mWorld->mFullRate.mBufLength; float dataHopSize = IN0(1); int initPolar = unit->m_polar = (int)IN0(2); unit->m_numPeriods = unit->m_periodsRemain = (int)(((float)unit->m_fullbufsize * dataHopSize) / numSamples) - 1; buf->coord = (IN0(2) == 1.f) ? coord_Polar : coord_Complex; OUT0(0) = IN0(0); SETCALC(FFTTrigger_next); }
//calculation function once FFT data ready void Loudness_dofft(Loudness *unit, uint32 ibufnum) { World *world = unit->mWorld; //if (ibufnum >= world->mNumSndBufs) ibufnum = 0; SndBuf *buf; // = world->mSndBufs + ibufnum; //int numbins = buf->samples - 2 >> 1; //support LocalBuf if (ibufnum >= world->mNumSndBufs) { int localBufNum = ibufnum - world->mNumSndBufs; Graph *parent = unit->mParent; if(localBufNum <= parent->localBufNum) { buf = parent->mLocalSndBufs + localBufNum; } else { buf = world->mSndBufs; } } else { buf = world->mSndBufs + ibufnum; } LOCK_SNDBUF(buf); float * data= buf->data; float loudsum=0.0; float smask= ZIN0(1); float tmask= ZIN0(2); for (int k=0; k<unit->m_numbands; ++k){ int bandstart=eqlbandbins[k]; //int bandend=eqlbandbins[k+1]; int bandsize= eqlbandsizes[k]; int bandend= bandstart+bandsize; float bsum=0.0; float real, imag, power; int index; float lastpower=0.0; for (int h=bandstart; h<bandend;++h) { index = 2*h; real= data[index]; imag= data[index+1]; power = (real*real) + (imag*imag); //would involve spectral masking here power = sc_max(lastpower*smask,power); //sideways spectral masking with leaky integration lastpower= power; //psychophysical sensation; within critical band, sum using a p metric, (sum m^p)^(1/p) //compresses the gain //power of three combination //bsum= bsum+(power*power*power); //won't sum up power very well //if(power>bsum) bsum=power; bsum= bsum+power; } //store recips of bandsizes? //why average? surely just take max or sum is better! //bsum= bsum/bandsize; //into dB, avoid log of 0 //float db= 10*log10((bsum*10000000)+0.001); //float db= 10*log10((bsum*32382)+0.001); //empricially derived 32382*2.348 float db= 10*log10((bsum*76032.936f)+0.001f); //correct multipler until you get loudness output of 1! //correcting for power of three combination //bsum=bsum+0.001; //4.8810017610244 = log10(76032.936) //float db= 10*((0.33334*log10(bsum)) + 4.8810017610244); //correct multipler until you get loudness output of 1! //printf("bsum %f db %f \n",bsum,db); //convert via contour if(db<contours[k][0]) db=0; else if (db>contours[k][10]) db=phons[10]; else { float prop=0.0; int j; for (j=1; j<11; ++j) { if(db<contours[k][j]) { prop= (db-contours[k][j-1])/(contours[k][j]-contours[k][j-1]); break; } if(j==10) prop=1.0; } db= (1.f-prop)*phons[j-1]+ prop*phons[j]; //printf("prop %f db %f j %d\n",prop,db,j); } //spectralmasking, 6dB drop per frame? //try also with just take db unit->m_ERBbands[k] = sc_max(db, (unit->m_ERBbands[k]) - tmask); //printf("db %f erbband %f \n",db, unit->m_ERBbands[k]); //must sum as intensities, not dbs once corrected, pow used to be other way around //loudsum+= ((pow(10, 0.1*unit->m_ERBbands[k])-0.001)*0.0000308813538386); // loudsum+= ((pow(10, 0.1*unit->m_ERBbands[k])-0.001)); //multiplier not needed since invert below; can trust no overflow? } //total excitation, correct back to dB scale in phons //float phontotal= 10*log10((loudsum*32382)+0.001); float phontotal= 10*log10((loudsum)+0.001); //didn't use divisor above, so no need to restore here //unit->m_phontotal= phontotal; //now to sones: /* from Praat: Excitation.c Sones = 2 ** ((Phones - 40) / 10) */ unit->m_sones= pow (2.f, (phontotal - 40) / 10); //printf("phontotal %f sones %f \n",phontotal, unit->m_sones); //about 5 times per second //if((unit->m_triggerid) && ((unit->m_frame%2==0))) SendTrigger(&unit->mParent->mNode, unit->m_triggerid, bestkey); }
void MedianSeparation_next( MedianSeparation *unit, int inNumSamples ) { int i,j; //calculate medians around pos-midpoint int medsize = unit->mediansize_; int mid = unit->midpoint_; float * mags = unit->magnitudes_; float * phases = unit->phases_; float * array = unit->collection_; int numbands = unit->fftbins_; int top = numbands-1; int pos = unit->magnitudeposition_; int midindex = (pos+medsize-mid)%medsize; float * vertical = unit->verticalmedians_; float * horizontal = unit->horizontalmedians_; float * magsnow = mags + (midindex*numbands); //cover outputs before early return in macro OUT0(0) = -1.f; OUT0(1) = -1.f; //0 normal, //1 vertical sort //2 horizontal sort //3 final output switch (unit->amortisationstep_) { case 0: { //printf("case 0 %f \n",ZIN0(0)); PV_GET_BUF //avoid output being overwritten by macro to pass input FFT buffer OUT0(0) = -1.f; OUT0(1) = -1.f; SCPolarBuf *polar = ToPolarApx(buf); //update data stored SCPolar * data = polar->bin; int base = unit->magnitudeposition_ * numbands; mags[base] = polar->dc; mags[base+numbands-1] = polar->nyq; //no need to set phases for these indices, since will always be 0.0 and initialised in constructor for (i=0; i<numbands-2; ++i) mags[base+i+1] = data[i].mag; base = unit->phaseposition_ * numbands; for (i=0; i<numbands-2; ++i) phases[base+i+1] = data[i].phase; uint32 bufnum1 = (uint32) IN0(1); //ZIN0(1); uint32 bufnum2 = (uint32) IN0(2); //ZIN0(2); //store for recall in step 3 unit->bufnum1_ = bufnum1; unit->bufnum2_ = bufnum2; //printf("store for recall %d %d what? %d uh? %d %f %f \n",bufnum1,bufnum2,(uint32) IN0(3), (uint32) ZIN0(3), IN0(1),IN0(2)); //will have returned early otherwise unit->amortisationstep_ =1; break; } case 1: { //printf("case 1\n"); float medianormean = ZIN0(7); //to avoid if inside loop, code copied twice with difference at point of array calculation (median or mean) if(medianormean<0.5f) { //printf("median calc vertical\n"); //vertical; easier to do in indexing for (i=0; i<numbands; ++i) { int lower = i-mid; if(lower<0) lower =0; int higher = i+mid; if(higher>top) higher = top; int number = higher-lower+1; float * pnow = magsnow + lower; //collect into array for (j=0; j<number; ++j) array[j] = pnow[j]; qsort(array,number,sizeof(float),cmp); //result is midpoint of magnitudes vertical[i] = array[number/2]; } } else { float sum; for (i=0; i<numbands; ++i) { int lower = i-mid; if(lower<0) lower =0; int higher = i+mid; if(higher>top) higher = top; int number = higher-lower+1; float * pnow = magsnow + lower; // // //collect into array // for (j=0; j<number; ++j) // array[j] = pnow[j]; // sum = 0.0f; //no need for intermediate array for (j=0; j<number; ++j) sum += pnow[j]; vertical[i] = sum/number; } } unit->amortisationstep_ = 2; break; } case 2: { //printf("case 2\n"); //horizontal, requires cross steps within 2D array float medianormean = ZIN0(7); if(medianormean<0.5f) { //printf("median calc horizontal\n"); for (i=0; i<numbands; ++i) { //no checks required for the medsize, but have to get correct indices //collect into array for (j=0; j<medsize; ++j) array[j] = mags[(j*numbands) + i]; qsort(array,medsize,sizeof(float),cmp); //result is midpoint of magnitudes horizontal[i] = array[mid]; } } else { //mean float sum; float recip = 1.0f/medsize; for (i=0; i<numbands; ++i) { //no checks required for the medsize, but have to get correct indices sum = 0.0f; //sum up directly, no need for array for (j=0; j<medsize; ++j) sum+= mags[(j*numbands) + i]; horizontal[i] = sum*recip; } } unit->amortisationstep_ = 3; break; } case 3: { //printf("case 3\n"); uint32 bufnum1 = unit->bufnum1_; //(uint32) ZIN0(1); uint32 bufnum2 = unit->bufnum2_; //(uint32) ZIN0(2); //printf("now recall %d %d \n",bufnum1,bufnum2); World *world = unit->mWorld; SndBuf *buf1, *buf2; if (bufnum1 >= world->mNumSndBufs) { int localBufNum = bufnum1 - world->mNumSndBufs; Graph *parent = unit->mParent; if(localBufNum <= parent->localBufNum) { buf1 = parent->mLocalSndBufs + localBufNum; } else { buf1 = world->mSndBufs; } } else { buf1 = world->mSndBufs + bufnum1; } LOCK_SNDBUF(buf1); if (bufnum2 >= world->mNumSndBufs) { int localBufNum = bufnum2 - world->mNumSndBufs; Graph *parent = unit->mParent; if(localBufNum <= parent->localBufNum) { buf2 = parent->mLocalSndBufs + localBufNum; } else { buf2 = world->mSndBufs; } } else { buf2 = world->mSndBufs + bufnum2; } LOCK_SNDBUF(buf2); int numbins1 = (buf1->samples >> 1) + 1; int numbins2 = (buf2->samples >> 1) + 1; //printf("check %d %d numbands %d further check %d %d \n",numbins1,numbins2,numbands,buf1->samples,buf2->samples); if((numbins1 == numbands) && (numbins2 ==numbands)) { int hardorsoft = ZIN0(5); //unit->hardorsoft_ = ZIN0(5); //unit->p_ = ZIN0(6); //to magnitude and phase representation SCPolarBuf *polar1 = ToPolarApx(buf1); SCPolarBuf *polar2 = ToPolarApx(buf2); SCPolar * data1 = polar1->bin; SCPolar * data2 = polar2->bin; //writing into the two output arrays of the buffers //magsnow already pointing to write place in magnitudes //magsnow = mags + (midindex*numbands); //+mid+2, adding so no danger, just add 1 int phaseindex = (unit->phaseposition_+1)%(mid+1); //midpoint is next one (about to be overwritten after increment below) float * phasesnow = phases + (phaseindex*numbands); //dc, nyquist, phase to zero //buf1,polar 1 is harmonic, 2 is percussive //0 larger of horizontal and vertical is winner, or 1 more subtle blend if(hardorsoft==0) { //hard //printf("hard calc \n"); if(horizontal[0]>vertical[0]) { polar1->dc = magsnow[0]; polar2->dc = 0.f; } else { polar2->dc = magsnow[0]; polar1->dc = 0.f; } if(horizontal[top]>vertical[top]) { polar1->nyq = magsnow[top]; polar2->nyq = 0.f; } else { polar2->nyq = magsnow[top]; polar1->nyq = 0.f; } int count = 0; //setting for (i=0; i<numbands-2; ++i) { int indexnow = i+1; //if(i==20) {data1[i].mag=1024; data2[i].mag=1024;} //else {data1[i].mag = 0.0f; data2[i].mag = 0.f;} if(horizontal[indexnow]>vertical[indexnow]) { ++count; data1[i].mag = magsnow[indexnow]; data1[i].phase = phasesnow[indexnow]; data2[i].mag = 0.0f; data2[i].phase = 0.0f; //phasesnow[i+1]; } else { data2[i].mag = magsnow[indexnow]; data2[i].phase = phasesnow[indexnow]; data1[i].mag = 0.0f; data1[i].phase = 0.0f; //phasesnow[i+1]; } // if(i<10) { // // printf("mag %d %f %f horiz %f vert %f further %f %f \n ",i, data1[i].mag,data2[i].mag,horizontal[indexnow],vertical[indexnow],polar1->bin[i].mag,polar2->bin[i].mag); // // } // } //printf("count %d \n",count); } else { //printf("I hope not!\n"); //soft, Wiener filtering float pfactor = ZIN0(6); float maskp, maskh, hp, pp, combine; //dc hp = powf(horizontal[0],pfactor); pp = powf(vertical[0],pfactor); combine = hp+pp; maskh = 0.f; maskp = 0.f; //watch for zeroes if(combine>0.00000000001f) { maskh = hp/combine; maskp = pp/combine; } polar1->dc = magsnow[0] * maskh; polar2->dc = magsnow[0] * maskp; //nyquist hp = powf(horizontal[top],pfactor); pp = powf(vertical[top],pfactor); combine = hp+pp; maskh = 0.f; maskp = 0.f; //watch for zeroes if(combine>0.00000000001f) { maskh = hp/combine; maskp = pp/combine; } polar1->nyq = magsnow[top] * maskh; polar2->nyq = magsnow[top] * maskp; //setting for (i=0; i<numbands-2; ++i) { int indexnow = i+1; hp = powf(horizontal[indexnow],pfactor); pp = powf(vertical[indexnow],pfactor); combine = hp+pp; //if(i<5) { // printf("mag %d %f %f %f %f %f \n",i ,horizontal[indexnow],vertical[indexnow],hp,pp,combine); //} maskh = 0.f; maskp = 0.f; //watch for zeroes if(combine>0.00000000001f) { maskh = hp/combine; maskp = pp/combine; } data1[i].mag = magsnow[indexnow] * maskh; data1[i].phase = phasesnow[indexnow]; data2[i].mag = magsnow[indexnow] * maskp; data2[i].phase = phasesnow[indexnow]; } } //printf("now output %d %d \n",bufnum1,bufnum2); //update output OUT0(0) = bufnum1; //2.f; //-1.f; //bufnum1; //-1; OUT0(1) = bufnum2; //-1; } unit->magnitudeposition_ = (unit->magnitudeposition_+1)%medsize; unit->phaseposition_ = (unit->phaseposition_+1)%(mid+1); unit->amortisationstep_ = 0; break; } default: //printf("default\n"); break; } //printf("actual output %f %f \n",OUT0(0),OUT0(1)); // for(j=0; j<inNumSamples; ++j) { // // output[j]= input[j]*0.1; //((float)j/inNumSamples); // } }
//calculation function once FFT data ready void KeyTrack_calculatekey(KeyTrack *unit, uint32 ibufnum) { World *world = unit->mWorld; SndBuf *buf; if (ibufnum >= world->mNumSndBufs) { int localBufNum = ibufnum - world->mNumSndBufs; Graph *parent = unit->mParent; if(localBufNum <= parent->localBufNum) { buf = parent->mLocalSndBufs + localBufNum; } else { buf = world->mSndBufs; if(unit->mWorld->mVerbosity > -1){ Print("KeyTrack error: Buffer number overrun: %i\n", ibufnum); } } } else { buf = world->mSndBufs + ibufnum; } LOCK_SNDBUF(buf); int numbins = buf->samples - 2 >> 1; //assumed in this representation SCComplexBuf *p = ToComplexApx(buf); const float * data= buf->data; //memcpy(unit->m_FFTBuf, data, NOVER2); //to hold powers float * fftbuf= unit->m_FFTBuf; //get powers for bins //don't need to calculate past half Nyquist, because no indices involved of harmonics above 10000 Hz or so (see index data at top of file) for (int i=0; i<NOVER2; i+=2) { //i>>1 is i/2 fftbuf[i>>1] = ((data[i] * data[i]) + (data[i+1] * data[i+1])); } float * chroma= unit->m_chroma; float sum; int indexbase, index; //experimental; added leaky integration on each note; also, only add to sum if harmonic, ie not a transient float * weights = unit->m_weights; int * bins = unit->m_bins; float chromaleak= ZIN0(2); //zero for new round (should add leaky integrator here! for (int i=0;i<12;++i) chroma[i] *= chromaleak; for (int i=0;i<60;++i) { int chromaindex = (i+9)%12; //starts at A1 up to G#6 sum=0.0; indexbase= 12*i; //6 partials, 2 of each //transient sum, setting up last values too float phasesum=0.0; for(int j=0;j<12;++j) { //12 if 144 data points index=indexbase+j; //experimental transient detection code, not reliable //int binindex= unit->m_bins[index]-1; //SCPolar binnow= p->bin[binindex].ToPolarApx(); //float phaseadvance= (binindex+1)*(TWOPI*0.5); //k * (512/44100) * (44100/1024) //convert bin number to frequency //float power= binnow.mag * binnow.mag; //(p->bin[binindex].real)*(p->bin[binindex].real) + (p->bin[binindex].imag)*(p->bin[binindex].imag); //(p->bin[binindex].mag); //power *= power; //int phaseindex= indexbase+j; //float phasenow= binnow.phase; //0.0; //(p->bin[binindex].phase); //float prevphase = fmod(unit->m_prevphase[index]+phaseadvance,TWOPI); //float a,b,tmp; //a=phasenow; b=prevphase; //b=phasenow; a=prevphase; //if(b<a) {b= b+TWOPI;} //float phasechange = sc_min(b-a,a+TWOPI-b); //more complicated, need mod 2pi and to know lower and upper //phasesum+= phasechange; //unit->m_prevphase[index]= phasenow; //((p->bin[index-1].mag) * (p->bin[index-1].mag)) //printf("comparison %f %f \n",fftbuf[g_bins2[index]], power); //sum+= (unit->m_weights[index])* power; sum+= (weights[index])* (fftbuf[bins[index]]); } //transient test here too? //if(phasesum>(5*PI)){sum=0.0;} //if((i>5) && (i<15)) //printf("test phasesum %f \n", phasesum); //unit->m_leaknote[i] = (0.8*unit->m_leaknote[i]) + sum; chroma[chromaindex]+= sum; //unit->m_leaknote[i]; //sum; } float* key = unit->m_key; //major for (int i=0;i<12;++i) { sum=0.0; for (int j=0;j<7;++j) { indexbase=g_major[j]; index=(i+indexbase)%12; //sum+=(chroma[index]*g_kkmajor[indexbase]); sum+=(chroma[index]*g_diatonicmajor[indexbase]); } key[i]=sum; //10*log10(sum+1); } //minor for (int i=0;i<12;++i) { sum=0.0; for (int j=0;j<7;++j) { indexbase=g_minor[j]; index=(i+indexbase)%12; //sum+=(chroma[index]*g_kkminor[indexbase]); sum+=(chroma[index]*g_diatonicminor[indexbase]); } key[12+i]=sum; } float keyleak= ZIN0(1); //fade parameter to 0.01 for histogram in seconds, convert to FFT frames //keyleak in seconds, convert to drop time in FFT hop frames (FRAMEPERIOD) keyleak= sc_max(0.001f,keyleak/unit->m_frameperiod); //FRAMEPERIOD; //now number of frames, actual leak param is decay exponent to reach 0.01 in x seconds, ie 0.01 = leakparam ** (x/ffthopsize) //0.01 is -40dB keyleak= pow(0.01f,(1.f/keyleak)); float * histogram= unit->m_histogram; int bestkey=0; float bestscore=0.0; for (int i=0;i<24;++i) { histogram[i]= (keyleak*histogram[i])+key[i]; if(histogram[i]>bestscore) { bestscore=histogram[i]; bestkey=i; } //printf("%f ",histogram[i]); } //should find secondbest and only swap if win by a margin //printf(" best %d \n\n",bestkey); //what is winning currently? find max in histogram unit->m_currentKey=bestkey; //about 5 times per second //if((unit->m_triggerid) && ((unit->m_frame%2==0))) SendTrigger(&unit->mParent->mNode, unit->m_triggerid, bestkey); }