andrew@0: andrew@0: andrew@0: #include "Chromagram.h" andrew@0: #include andrew@0: #include andrew@0: using namespace std; andrew@0: andrew@0: Chromagram :: Chromagram() andrew@0: { andrew@0: pi = 3.14159265; andrew@0: bsize = 8192; andrew@0: andrew@0: // array init andrew@0: buffer = new float[8192]; // make audio buffer andrew@0: wbuffer = new float[8192]; // to hold windowed audio buffer andrew@0: win = new float[8192]; andrew@0: d_frame = new float[128]; andrew@0: hop = new float[1024]; andrew@0: mag = new float[(bsize/2)+1]; andrew@0: andrew@0: maximumChromaValue = 0; andrew@0: andrew@0: float base = 130.81278265; andrew@0: andrew@0: for (int i = 0;i < 12;i++) andrew@0: { andrew@0: note[i] = base*pow(2,(((float) i)/12)); andrew@0: } andrew@0: andrew@0: harmonics = 2; andrew@0: octaves = 2; andrew@0: search = 2; // p is number of bins either side of target to search for other peaks andrew@0: andrew@0: out = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * bsize); // complex array to hold fft andrew@0: p = fftwf_plan_dft_r2c_1d(bsize,wbuffer,out,FFTW_FORWARD*-1); // plan for executing fft of wbuffer andrew@0: andrew@0: chromaready = 0; andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // destructor andrew@0: Chromagram :: ~Chromagram() andrew@0: { andrew@0: // destroy fft plan andrew@0: fftwf_destroy_plan(p); andrew@0: andrew@0: // deallocate memory andrew@0: win = NULL; andrew@0: delete [] win; andrew@0: buffer = NULL; andrew@0: delete [] buffer; andrew@0: wbuffer = NULL; andrew@0: delete [] wbuffer; andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // Initialises all framesize dependent variables. Should be called to properly initialise variables and anytime the frame size changes. andrew@0: void Chromagram :: initialise(int framesize,int hsize) andrew@0: { andrew@0: fsize = framesize; andrew@0: hopsize = hsize; andrew@0: andrew@0: d_frame = new float[fsize/4]; andrew@0: hop = new float[hopsize]; andrew@0: andrew@0: andrew@0: calcrate = (hopsize*4)/fsize; andrew@0: andrew@0: count = 0; andrew@0: andrew@0: //fsize = framesize; // set frame size andrew@0: andrew@0: ratio = ((float) 11025) / ((float) 8192); andrew@0: hamming(bsize); // define a hanning window offline andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // Processes a single frame andrew@0: void Chromagram :: processframe(float frame[]) andrew@0: { andrew@0: int i; andrew@0: int j; andrew@0: int c; andrew@0: andrew@0: chromaready = 0; // default case that chroma isn't ready andrew@0: andrew@0: // downsample frame andrew@0: downsampleframe(frame); andrew@0: andrew@0: if (count < calcrate) andrew@0: { andrew@0: c = count*(fsize/4); andrew@0: andrew@0: j = 0; andrew@0: for (i = c;i < c+(fsize/4);i++) andrew@0: { andrew@0: hop[i] = d_frame[j]; andrew@0: j++; andrew@0: } andrew@0: count = count + 1; andrew@0: } andrew@0: andrew@0: if (count == calcrate) andrew@0: { andrew@0: for (i=0;i < 8192-hopsize;i++) andrew@0: { andrew@0: buffer[i] = buffer[i+hopsize]; andrew@0: } andrew@0: andrew@0: j = 0; andrew@0: for (i=8192-hopsize;i < bsize;i++) andrew@0: { andrew@0: buffer[i] = hop[j]; andrew@0: j++; andrew@0: } andrew@0: andrew@0: chromacalc(buffer); andrew@0: andrew@0: count = 0; andrew@0: } andrew@0: andrew@0: andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // Processes a single frame andrew@0: void Chromagram :: downsampleframe(float frame[]) andrew@0: { andrew@0: float filt_frame[fsize]; andrew@0: andrew@0: float b0,b1,b2,a1,a2; andrew@0: float x_1,x_2,y_1,y_2; andrew@0: andrew@0: b0 = 0.2929; andrew@0: b1 = 0.5858; andrew@0: b2 = 0.2929; andrew@0: a1 = -0.0000; andrew@0: a2 = 0.1716; andrew@0: andrew@0: x_1 = 0; andrew@0: x_2 = 0; andrew@0: y_1 = 0; andrew@0: y_2 = 0; andrew@0: andrew@0: for (int i=0;i < fsize;i++) andrew@0: { andrew@0: filt_frame[i] = frame[i]*b0 + x_1*b1 + x_2*b2 - y_1*a1 - y_2*a2; andrew@0: andrew@0: x_2 = x_1; andrew@0: x_1 = frame[i]; andrew@0: y_2 = y_1; andrew@0: y_1 = filt_frame[i]; andrew@0: } andrew@0: andrew@0: for (int i=0;i < (fsize/4);i++) andrew@0: { andrew@0: d_frame[i] = filt_frame[i*4]; andrew@0: } andrew@0: andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // main function to be called with 8192 sample frame at 11025 Hz andrew@0: void Chromagram :: chromacalc(float frame[]) andrew@0: { andrew@0: int i; andrew@0: andrew@0: // apply hanning window to buffer andrew@0: for (i = 0; i < bsize;i++) andrew@0: { andrew@0: wbuffer[i] = frame[i] * win[i]; andrew@0: } andrew@0: andrew@0: getmagspectrum(); andrew@0: andrew@0: generatechromagram(); andrew@0: andrew@0: chromaready = 1; andrew@0: andrew@0: // for (i = 0;i < 12;i++) andrew@0: // { andrew@0: // cout << chroma[i] << " "; andrew@0: // } andrew@0: // andrew@0: // cout << endl; andrew@0: andrew@0: //cout << "root: " << root << " type: " << type << " features: " << features << endl; andrew@0: andrew@0: //cout << root << " " << quality << " " << intervals << endl; andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // creates a chromagram from the magnitude spectrum of the input signal andrew@0: void Chromagram :: generatechromagram() andrew@0: { andrew@0: int i; andrew@0: float sum; andrew@0: float notesum = 0; andrew@0: float noteval; andrew@0: int index; andrew@0: float maxval = 0; andrew@0: int searchlength; andrew@0: andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: sum = 0; andrew@0: andrew@0: for (int oct = 1;oct <= octaves;oct++) andrew@0: { andrew@0: noteval = (note[i]/ratio)*((float) oct); andrew@0: notesum = 0; andrew@0: andrew@0: for (int h = 1;h <= harmonics;h++) andrew@0: { andrew@0: index = round(noteval*((float) h)); andrew@0: andrew@0: searchlength = search*h; andrew@0: andrew@0: maxval = 0; andrew@0: for (int n = (index-searchlength);n <= index+searchlength;n++) andrew@0: { andrew@0: if (mag[n] > maxval) andrew@0: { andrew@0: maxval = mag[n]; andrew@0: } andrew@0: } andrew@0: andrew@0: notesum = notesum+(maxval*(1/((float) h))); andrew@0: } andrew@0: sum = sum + notesum; andrew@0: andrew@0: if (oct == 1) andrew@0: { andrew@0: chroma_low[i] = notesum; andrew@0: } andrew@0: } andrew@0: andrew@0: chroma[i] = sum; andrew@0: rawChroma[i] = sum; andrew@0: andrew@0: if (sum > maximumChromaValue) andrew@0: maximumChromaValue = sum; andrew@0: andrew@0: } andrew@0: andrew@0: andrew@0: // normalise chromagram andrew@0: maxval = max(chroma,12); andrew@0: andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: chroma[i] = chroma[i] / maxval; andrew@0: } andrew@0: andrew@0: } andrew@0: andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // main function to be called with 8192 sample frame at 11025 Hz andrew@0: void Chromagram :: hamming(int N) andrew@0: { andrew@0: int n; andrew@0: andrew@0: // apply hanning window to buffer andrew@0: for (n = 0; n < N;n++) andrew@0: { andrew@0: win[n] = 0.54 - 0.46*cos(2*pi*(((float) n)/((float) N))); andrew@0: } andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // finds mag spectrum of input andrew@0: void Chromagram :: getmagspectrum() andrew@0: { andrew@0: int i = 0; andrew@0: andrew@0: // execute fft plan, i.e. compute fft of buffer andrew@0: fftwf_execute(p); andrew@0: andrew@0: // compute first (N/2)+1 mag values andrew@0: for (i = 0;i < (bsize/2)+1;i++) andrew@0: { andrew@0: mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2)); andrew@0: mag[i] = sqrt(mag[i]); andrew@0: } andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // returns max value of an array andrew@0: float Chromagram :: max(float array[],int length) andrew@0: { andrew@0: float max = 0; andrew@0: andrew@0: for (int i=0;i < length;i++) andrew@0: { andrew@0: if (array[i] > max) andrew@0: { andrew@0: max = array[i]; andrew@0: } andrew@0: } andrew@0: andrew@0: return max; andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // returns index of minimum value of array andrew@0: int Chromagram :: minindex(float array[],int length) andrew@0: { andrew@0: float min = 10000; andrew@0: int minindex = 0; andrew@0: int i; andrew@0: andrew@0: for (i = 0;i < length;i++) andrew@0: { andrew@0: if (array[i] < min) andrew@0: { andrew@0: min = array[i]; andrew@0: minindex = i; andrew@0: } andrew@0: } andrew@0: andrew@0: return minindex; andrew@0: }