andrew@0: /* andrew@0: * ChordDetect.cpp andrew@0: * ChordDetect andrew@0: * andrew@0: * Created by Adam Stark on 28/04/2008. andrew@0: * Copyright 2008 __MyCompanyName__. All rights reserved. andrew@0: * andrew@0: */ andrew@0: andrew@0: #include "ChordDetect.h" andrew@0: #include andrew@0: #include andrew@0: using namespace std; andrew@0: andrew@0: ChordDetect :: ChordDetect() andrew@0: { andrew@0: andrew@0: bias = 1.06; andrew@0: andrew@0: makeprofiles(); andrew@0: andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // destructor andrew@0: ChordDetect :: ~ChordDetect() 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 ChordDetect :: C_Detect(float c[],float c_low[]) andrew@0: { andrew@0: for (int i = 0;i < 12;i++) andrew@0: { andrew@0: chroma[i] = c[i]; andrew@0: chroma_low[i] = c_low[i]; andrew@0: } andrew@0: andrew@0: calculateweightings(); andrew@0: andrew@0: classifychromagram(); andrew@0: andrew@0: //cout << root << " " << quality << " " << intervals << endl; andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // analyse the chromagram and assign it a root note, chord type and any features andrew@0: void ChordDetect :: classifychromagram() andrew@0: { andrew@0: int i; andrew@0: int j; andrew@0: int fifth; andrew@0: int chordindex; andrew@0: andrew@0: // remove some of the 5th note energy from chromagram andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: fifth = (i+7) % 12; andrew@0: chroma[fifth] = chroma[fifth] - (0.1*chroma[i]); andrew@0: andrew@0: if (chroma[fifth] < 0) andrew@0: { andrew@0: chroma[fifth] = 0; andrew@0: } andrew@0: andrew@0: } andrew@0: andrew@0: andrew@0: // major chords andrew@0: for (j=0;j < 12;j++) andrew@0: { andrew@0: chord[j] = calcchordvalue(chroma,profiles[j],bias,3); andrew@0: } andrew@0: andrew@0: // minor chords andrew@0: for (j=12;j < 24;j++) andrew@0: { andrew@0: chord[j] = calcchordvalue(chroma,profiles[j],bias,3); andrew@0: } andrew@0: andrew@0: // diminished 5th chords andrew@0: for (j=24;j < 36;j++) andrew@0: { andrew@0: chord[j] = calcchordvalue(chroma,profiles[j],bias,3); andrew@0: } andrew@0: andrew@0: // augmented 5th chords andrew@0: for (j=36;j < 48;j++) andrew@0: { andrew@0: chord[j] = calcchordvalue(chroma,profiles[j],bias,3); andrew@0: andrew@0: chord[j] = chord[j] / weight_aug[j-36]; andrew@0: } andrew@0: andrew@0: // sus2 chords andrew@0: for (j=48;j < 60;j++) andrew@0: { andrew@0: chord[j] = calcchordvalue(chroma,profiles[j],1,3); andrew@0: andrew@0: chord[j] = chord[j] / weight_sus[j-48]; andrew@0: } andrew@0: andrew@0: // sus4 chords andrew@0: for (j=60;j < 72;j++) andrew@0: { andrew@0: chord[j] = calcchordvalue(chroma,profiles[j],1,3); andrew@0: andrew@0: chord[j] = chord[j] / weight_sus[j-60]; andrew@0: } andrew@0: andrew@0: // major 7th chords andrew@0: for (j=72;j < 84;j++) andrew@0: { andrew@0: chord[j] = calcchordvalue(chroma,profiles[j],1,4); andrew@0: } andrew@0: andrew@0: // minor 7th chords andrew@0: for (j=84;j < 96;j++) andrew@0: { andrew@0: chord[j] = calcchordvalue(chroma,profiles[j],bias,4); andrew@0: } andrew@0: andrew@0: // dominant 7th chords andrew@0: for (j=96;j < 108;j++) andrew@0: { andrew@0: chord[j] = calcchordvalue(chroma,profiles[j],bias,4); andrew@0: } andrew@0: andrew@0: chordindex = minindex(chord,108); andrew@0: andrew@0: // major andrew@0: if (chordindex < 12) andrew@0: { andrew@0: root = chordindex; andrew@0: quality = 1; andrew@0: intervals = 0; andrew@0: } andrew@0: andrew@0: // minor andrew@0: if ((chordindex >= 12) && (chordindex < 24)) andrew@0: { andrew@0: root = chordindex-12; andrew@0: quality = 0; andrew@0: intervals = 0; andrew@0: } andrew@0: andrew@0: // diminished 5th andrew@0: if ((chordindex >= 24) && (chordindex < 36)) andrew@0: { andrew@0: root = chordindex-24; andrew@0: quality = 4; andrew@0: intervals = 0; andrew@0: } andrew@0: andrew@0: // augmented 5th andrew@0: if ((chordindex >= 36) && (chordindex < 48)) andrew@0: { andrew@0: root = chordindex-36; andrew@0: quality = 6; andrew@0: intervals = 0; andrew@0: } andrew@0: andrew@0: // sus2 andrew@0: if ((chordindex >= 48) && (chordindex < 60)) andrew@0: { andrew@0: root = chordindex-48; andrew@0: quality = 2; andrew@0: intervals = 2; andrew@0: } andrew@0: andrew@0: // sus4 andrew@0: if ((chordindex >= 60) && (chordindex < 72)) andrew@0: { andrew@0: root = chordindex-60; andrew@0: quality = 2; andrew@0: intervals = 4; andrew@0: } andrew@0: andrew@0: // major 7th andrew@0: if ((chordindex >= 72) && (chordindex < 84)) andrew@0: { andrew@0: root = chordindex-72; andrew@0: quality = 1; andrew@0: intervals = 7; andrew@0: } andrew@0: andrew@0: // minor 7th andrew@0: if ((chordindex >= 84) && (chordindex < 96)) andrew@0: { andrew@0: root = chordindex-84; andrew@0: quality = 0; andrew@0: intervals = 7; andrew@0: } andrew@0: andrew@0: // dominant 7th andrew@0: if ((chordindex >= 96) && (chordindex < 108)) andrew@0: { andrew@0: root = chordindex-96; andrew@0: quality = 2; andrew@0: intervals = 7; andrew@0: } andrew@0: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // calculate weightings to help distinguish between sus2/sus4 and aug chords andrew@0: void ChordDetect :: calculateweightings() andrew@0: { andrew@0: int i; andrew@0: float maxval = 0; andrew@0: int fifth; andrew@0: int augfifth; andrew@0: andrew@0: maxval = max(chroma_low,12); andrew@0: andrew@0: // normalise chroma low andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: chroma_low[i] = chroma_low[i] / maxval; andrew@0: } andrew@0: andrew@0: // make weight for sus chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: fifth = i+7; andrew@0: augfifth = i+8; andrew@0: andrew@0: if (fifth >= 12) andrew@0: { andrew@0: fifth = fifth-12; andrew@0: } andrew@0: andrew@0: if (augfifth >= 12) andrew@0: { andrew@0: augfifth = augfifth-12; andrew@0: } andrew@0: andrew@0: weight_sus[i] = chroma_low[i] + (chroma_low[fifth]/2); andrew@0: weight_aug[i] = chroma_low[i] + (chroma_low[augfifth]/2); andrew@0: } andrew@0: andrew@0: maxval = max(weight_sus,12); andrew@0: // normalise weight_sus andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: weight_sus[i] = weight_sus[i] / maxval; andrew@0: } andrew@0: andrew@0: maxval = max(weight_aug,12); andrew@0: // normalise weight_aug andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: weight_aug[i] = weight_aug[i] / maxval; andrew@0: } andrew@0: andrew@0: andrew@0: } andrew@0: andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // return delta value indicating how similar the chroma is to the chord template - lower value = more similar andrew@0: float ChordDetect :: calcchordvalue(float c[],float T[],float biasval, float N) andrew@0: { andrew@0: float sum = 0; andrew@0: float delta; andrew@0: andrew@0: for (int i=0;i < 12;i++) andrew@0: { andrew@0: sum = sum + ((1-T[i])*(pow(c[i],2))); andrew@0: } andrew@0: andrew@0: delta = sqrt(sum) / ((12 - N)*biasval); andrew@0: andrew@0: return delta; andrew@0: } andrew@0: andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // returns max value of an array andrew@0: float ChordDetect :: 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 ChordDetect :: 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: } andrew@0: andrew@0: //-------------------------------------------------------------------------------------- andrew@0: // calculates bit mask chord profiles andrew@0: void ChordDetect :: makeprofiles() andrew@0: { andrew@0: int i; andrew@0: int t; andrew@0: int j = 0; andrew@0: int root; andrew@0: int third; andrew@0: int fifth; andrew@0: int seventh; andrew@0: andrew@0: float v1 = 1; andrew@0: float v2 = 1; andrew@0: float v3 = 1; andrew@0: andrew@0: // set profiles matrix to all zeros andrew@0: for (j = 0;j < 108;j++) andrew@0: { andrew@0: for (t = 0;t < 12;t++) andrew@0: { andrew@0: profiles[j][t] = 0; andrew@0: } andrew@0: } andrew@0: andrew@0: // reset j to zero to begin creating profiles andrew@0: j = 0; andrew@0: andrew@0: // major chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: root = i % 12; andrew@0: third = (i+4) % 12; andrew@0: fifth = (i+7) % 12; andrew@0: andrew@0: profiles[j][root] = v1; andrew@0: profiles[j][third] = v2; andrew@0: profiles[j][fifth] = v3; andrew@0: andrew@0: j++; andrew@0: } andrew@0: andrew@0: // minor chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: root = i % 12; andrew@0: third = (i+3) % 12; andrew@0: fifth = (i+7) % 12; andrew@0: andrew@0: profiles[j][root] = v1; andrew@0: profiles[j][third] = v2; andrew@0: profiles[j][fifth] = v3; andrew@0: andrew@0: j++; andrew@0: } andrew@0: andrew@0: // diminished chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: root = i % 12; andrew@0: third = (i+3) % 12; andrew@0: fifth = (i+6) % 12; andrew@0: andrew@0: profiles[j][root] = v1; andrew@0: profiles[j][third] = v2; andrew@0: profiles[j][fifth] = v3; andrew@0: andrew@0: j++; andrew@0: } andrew@0: andrew@0: // augmented chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: root = i % 12; andrew@0: third = (i+4) % 12; andrew@0: fifth = (i+8) % 12; andrew@0: andrew@0: profiles[j][root] = v1; andrew@0: profiles[j][third] = v2; andrew@0: profiles[j][fifth] = v3; andrew@0: andrew@0: j++; andrew@0: } andrew@0: andrew@0: // sus2 chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: root = i % 12; andrew@0: third = (i+2) % 12; andrew@0: fifth = (i+7) % 12; andrew@0: andrew@0: profiles[j][root] = v1; andrew@0: profiles[j][third] = v2; andrew@0: profiles[j][fifth] = v3; andrew@0: andrew@0: j++; andrew@0: } andrew@0: andrew@0: // sus4 chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: root = i % 12; andrew@0: third = (i+5) % 12; andrew@0: fifth = (i+7) % 12; andrew@0: andrew@0: profiles[j][root] = v1; andrew@0: profiles[j][third] = v2; andrew@0: profiles[j][fifth] = v3; andrew@0: andrew@0: j++; andrew@0: } andrew@0: andrew@0: // major 7th chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: root = i % 12; andrew@0: third = (i+4) % 12; andrew@0: fifth = (i+7) % 12; andrew@0: seventh = (i+11) % 12; andrew@0: andrew@0: profiles[j][root] = v1; andrew@0: profiles[j][third] = v2; andrew@0: profiles[j][fifth] = v3; andrew@0: profiles[j][seventh] = v3; andrew@0: andrew@0: j++; andrew@0: } andrew@0: andrew@0: // minor 7th chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: root = i % 12; andrew@0: third = (i+3) % 12; andrew@0: fifth = (i+7) % 12; andrew@0: seventh = (i+10) % 12; andrew@0: andrew@0: profiles[j][root] = v1; andrew@0: profiles[j][third] = v2; andrew@0: profiles[j][fifth] = v3; andrew@0: profiles[j][seventh] = v3; andrew@0: andrew@0: j++; andrew@0: } andrew@0: andrew@0: // dominant 7th chords andrew@0: for (i = 0;i < 12;i++) andrew@0: { andrew@0: root = i % 12; andrew@0: third = (i+4) % 12; andrew@0: fifth = (i+7) % 12; andrew@0: seventh = (i+10) % 12; andrew@0: andrew@0: profiles[j][root] = v1; andrew@0: profiles[j][third] = v2; andrew@0: profiles[j][fifth] = v3; andrew@0: profiles[j][seventh] = v3; andrew@0: andrew@0: j++; andrew@0: } andrew@0: }