static void barkSpec_tilde_bang(t_barkSpec *x) { int i, j, window, windowHalf, bangSample; t_atom *listOut; t_sample *signal_R, *signal_I; t_float *windowFuncPtr; double currentTime, timef = 0.0; window = x->window; windowHalf = window*0.5; // create local memory listOut = (t_atom *)t_getbytes_(x->numFilters*sizeof(t_atom)); signal_R = (t_sample *)t_getbytes_(window*sizeof(t_sample)); signal_I = (t_sample *)t_getbytes_((windowHalf+1)*sizeof(t_sample)); //currentTime = clock_gettimesince(x->lastDspTime); clock_getftime(&timef); currentTime = timef - x->lastDspTime; bangSample = (int)(((currentTime/1000.0)*x->sr)+0.5); // round if (bangSample < 0) bangSample = 0; else if ( bangSample >= x->n ) bangSample = x->n - 1; // construct analysis window using bangSample as the end of the window for(i=0, j=bangSample; i<window; i++, j++) signal_R[i] = x->signal_R[j]; // set window function windowFuncPtr = x->hann; //default case to get rid of compile warning switch(x->windowFunction) { case 0: break; case 1: windowFuncPtr = x->blackman; break; case 2: windowFuncPtr = x->cosine; break; case 3: windowFuncPtr = x->hamming; break; case 4: windowFuncPtr = x->hann; break; default: break; }; // if windowFunction == 0, skip the windowing (rectangular) if(x->windowFunction>0) for(i=0; i<window; i++, windowFuncPtr++) signal_R[i] *= *windowFuncPtr; mayer_realfft(window, signal_R); tIDLib_realfftUnpack(window, windowHalf, signal_R, signal_I); tIDLib_power(windowHalf+1, signal_R, signal_I); // power spectrum sometimes generates lower scores than magnitude. make it optional. if(!x->powerSpectrum) tIDLib_mag(windowHalf+1, signal_R); tIDLib_filterbankMultiply(signal_R, x->normalize, x->filterAvg, x->x_filterbank, x->numFilters); for(i=0; i<x->numFilters; i++) SETFLOAT(listOut+i, signal_R[i]); outlet_list(x->x_featureList, 0, x->numFilters, listOut); // free local memory t_freebytes_(listOut, x->numFilters*sizeof(t_atom)); t_freebytes_(signal_R, window*sizeof(t_sample)); t_freebytes_(signal_I, (windowHalf+1)*sizeof(t_sample)); }
static void bark_analyze(t_bark *x, t_floatarg startTime, t_floatarg endTime) { int i, j, window, windowHalf, hop, nFrames, frame, sampRange, startSamp, endSamp; t_float totalGrowth, totalVel, *windowFuncPtr; t_garray *a; if(!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) pd_error(x, "%s: no such array", x->x_arrayname->s_name); else if(!garray_getfloatwords(a, &x->x_array_points, &x->x_vec)) pd_error(x, "%s: bad template for bark", x->x_arrayname->s_name); else { window = x->window; windowHalf = window*0.5; hop = x->hop; if(endTime) { startSamp = floor(startTime*x->sr); endSamp = floor(endTime*x->sr); if(startSamp>=0 && endSamp<x->x_array_points) sampRange = endSamp-startSamp+1; else { error("invalid time range"); return; } } else { sampRange = x->x_array_points; startSamp = 0; endSamp = x->x_array_points-1; } nFrames = floor((sampRange-window)/hop); // init mask to zero for(i=0; i<x->numFilters; i++) x->mask[i] = 0.0; for(frame=0; frame<nFrames; frame++) { // fill buffer with <window> samples for(i=0, j=frame*hop+startSamp; i<window; i++, j++) x->signalBuf[i] = x->x_vec[j].w_float; totalGrowth = 0.0; totalVel = 0.0; // set window function windowFuncPtr = x->hann; //default case to get rid of compile warning switch(x->windowFunction) { case 0: break; case 1: windowFuncPtr = x->blackman; break; case 2: windowFuncPtr = x->cosine; break; case 3: windowFuncPtr = x->hamming; break; case 4: windowFuncPtr = x->hann; break; default: break; }; // if windowFunction == 0, skip the windowing (rectangular) if(x->windowFunction>0) for(i=0; i<window; i++, windowFuncPtr++) x->analysisBuf[i] = x->signalBuf[i] * *windowFuncPtr; else for(i=0; i<window; i++, windowFuncPtr++) x->analysisBuf[i] = x->signalBuf[i]; mayer_realfft(window, x->analysisBuf); // calculate the power spectrum in place. we'll overwrite the first N/2+1 points in x->analysisBuf with the magnitude spectrum, as this is all that's used below in filterbank_multiply() x->analysisBuf[0] = x->analysisBuf[0] * x->analysisBuf[0]; // DC x->analysisBuf[windowHalf] = x->analysisBuf[windowHalf] * x->analysisBuf[windowHalf]; // Nyquist for(i=(window-1), j=1; i>windowHalf; i--, j++) x->analysisBuf[j] = (x->analysisBuf[j]*x->analysisBuf[j]) + (x->analysisBuf[i]*x->analysisBuf[i]); // optional use of power/magnitude spectrum if(!x->powerSpectrum) for(i=0; i<=windowHalf; i++) x->analysisBuf[i] = sqrt(x->analysisBuf[i]); tIDLib_filterbankMultiply(x->analysisBuf, x->normalize, x->filterAvg, x->x_filterbank, x->numFilters); // optional loudness weighting if(x->useWeights) for(i=0; i<x->numFilters; i++) x->analysisBuf[i] *= x->loudWeights[i]; for(i=0; i<x->numFilters; i++) totalVel += x->analysisBuf[i]; // init growth list to zero for(i=0; i<x->numFilters; i++) x->growth[i] = 0.0; for(i=0; i<x->numFilters; i++) { // from p.3 of Puckette/Apel/Zicarelli, 1998 // salt divisor with + 1.0e-15 in case previous power was zero if(x->analysisBuf[i] > x->mask[i]) x->growth[i] = x->analysisBuf[i]/(x->mask[i] + 1.0e-15) - 1.0; if(i>=x->loBin && i<=x->hiBin && x->growth[i]>0) totalGrowth += x->growth[i]; SETFLOAT(x->growthList+i, x->growth[i]); } if(frame*hop+startSamp >= x->debounceActive) x->debounceActive = -1; if(totalVel >= x->minvel && totalGrowth > x->hiThresh && !x->haveHit && x->debounceActive < 0) { if(x->debug) post("peak: %f", totalGrowth); x->haveHit = 1; x->debounceActive = frame*hop+startSamp + x->debounceSamp; } else if(x->haveHit && x->loThresh>0 && totalGrowth < x->loThresh) // if loThresh is an actual value (not -1), then wait until growth drops below that value before reporting attack { if(x->debug) post("drop: %f", totalGrowth); x->haveHit = 0; // don't output data if spew will do it anyway below if(!x->spew) { outlet_list(x->x_outputList, 0, x->numFilters, x->growthList); outlet_float(x->x_growthOut, totalGrowth); } // add half a window of samples as a fudge factor. note that since this NRT and we can look into the future, all attack reports will be roughly a half window EARLY. in RT, everything is a half window LATE because the point of reference is the END of the window. here, it's the BEGINNING of the window. outlet_float(x->x_timeOut, (frame*hop+startSamp + windowHalf)/x->sr); } else if(x->haveHit && x->loThresh<0 && totalGrowth < x->prevTotalGrowth) // if loThresh == -1, report attack as soon as growth shows any decay at all { if(x->debug) post("drop: %f", totalGrowth); x->haveHit = 0; // don't output data if spew will do it anyway below if(!x->spew) { outlet_list(x->x_outputList, 0, x->numFilters, x->growthList); outlet_float(x->x_growthOut, totalGrowth); } // add half a window of samples as a fudge factor. note that since this NRT and we can look into the future, all attack reports will be roughly a half window EARLY. in RT, everything is a half window LATE because the point of reference is the END of the window. here, it's the BEGINNING of the window. outlet_float(x->x_timeOut, (frame*hop+startSamp + windowHalf)/x->sr); } if(x->spew) { outlet_list(x->x_outputList, 0, x->numFilters, x->growthList); outlet_float(x->x_growthOut, totalGrowth); } // update mask for(i=0; i<x->numFilters; i++) { if(x->analysisBuf[i] > x->mask[i]) { x->mask[i] = x->analysisBuf[i]; x->numPeriods[i] = 0; } else if(++x->numPeriods[i] >= x->maskPeriods) x->mask[i] *= x->maskDecay; } x->prevTotalGrowth = totalGrowth; } } post("analyzed %i frames", nFrames); }