view chromagramm/ChordDetect.cpp @ 2:fa2af670b5c5 tip

SoundFileLoader might have moved
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Fri, 06 Jan 2012 00:23:26 +0000
parents bcb0d40158f4
children
line wrap: on
line source
/*
 *  ChordDetect.cpp
 *  ChordDetect
 *
 *  Created by Adam Stark on 28/04/2008.
 *  Copyright 2008 __MyCompanyName__. All rights reserved.
 *
 */

#include "ChordDetect.h"
#include <iostream>
#include <math.h>
using namespace std;

ChordDetect :: ChordDetect()
{
	
	bias = 1.06;
	
	makeprofiles();
		
}

//--------------------------------------------------------------------------------------
// destructor
ChordDetect :: ~ChordDetect()
{
										
}


//--------------------------------------------------------------------------------------
// main function to be called with 8192 sample frame at 11025 Hz
void ChordDetect :: C_Detect(float c[],float c_low[])
{
	for (int i = 0;i < 12;i++)
	{
		chroma[i] = c[i];
		chroma_low[i] = c_low[i];
	}
	
	calculateweightings();
	
	classifychromagram();
		
	//cout << root << " " << quality << " " << intervals << endl;
}

//--------------------------------------------------------------------------------------
// analyse the chromagram and assign it a root note, chord type and any features
void ChordDetect :: classifychromagram()
{
	int i;
	int j;
	int fifth;
	int chordindex;
	
	// remove some of the 5th note energy from chromagram
	for (i = 0;i < 12;i++)
	{
		fifth = (i+7) % 12;
		chroma[fifth] = chroma[fifth] - (0.1*chroma[i]);
		
		if (chroma[fifth] < 0)
		{
			chroma[fifth] = 0;
		}
		
	}
	
	
	// major chords
	for (j=0;j < 12;j++)
	{
		chord[j] = calcchordvalue(chroma,profiles[j],bias,3);
	}
	
	// minor chords
	for (j=12;j < 24;j++)
	{
		chord[j] = calcchordvalue(chroma,profiles[j],bias,3);
	}
	
	// diminished 5th chords
	for (j=24;j < 36;j++)
	{
		chord[j] = calcchordvalue(chroma,profiles[j],bias,3);
	}
	
	// augmented 5th chords
	for (j=36;j < 48;j++)
	{
		chord[j] = calcchordvalue(chroma,profiles[j],bias,3);
		
		chord[j] = chord[j] / weight_aug[j-36];
	}
	
	// sus2 chords
	for (j=48;j < 60;j++)
	{
		chord[j] = calcchordvalue(chroma,profiles[j],1,3);
		
		chord[j] = chord[j] / weight_sus[j-48];
	}
	
	// sus4 chords
	for (j=60;j < 72;j++)
	{
		chord[j] = calcchordvalue(chroma,profiles[j],1,3);
		
		chord[j] = chord[j] / weight_sus[j-60];
	}
	
	// major 7th chords
	for (j=72;j < 84;j++)
	{
		chord[j] = calcchordvalue(chroma,profiles[j],1,4);
	}
	
	// minor 7th chords
	for (j=84;j < 96;j++)
	{
		chord[j] = calcchordvalue(chroma,profiles[j],bias,4);
	}

	// dominant 7th chords
	for (j=96;j < 108;j++)
	{
		chord[j] = calcchordvalue(chroma,profiles[j],bias,4);
	}
	
	chordindex = minindex(chord,108);
	
	// major
	if (chordindex < 12)
	{
		root = chordindex;
		quality = 1;
		intervals = 0;
	}
	
	// minor
	if ((chordindex >= 12) && (chordindex < 24))
	{
		root = chordindex-12;
		quality = 0;
		intervals = 0;
	}
	
	// diminished 5th
	if ((chordindex >= 24) && (chordindex < 36))
	{
		root = chordindex-24;
		quality = 4;
		intervals = 0;
	}
	
	// augmented 5th
	if ((chordindex >= 36) && (chordindex < 48))
	{
		root = chordindex-36;
		quality = 6;
		intervals = 0;
	}
	
	// sus2
	if ((chordindex >= 48) && (chordindex < 60))
	{
		root = chordindex-48;
		quality = 2;
		intervals = 2;
	}
	
	// sus4
	if ((chordindex >= 60) && (chordindex < 72))
	{
		root = chordindex-60;
		quality = 2;
		intervals = 4;
	}
	
	// major 7th
	if ((chordindex >= 72) && (chordindex < 84))
	{
		root = chordindex-72;
		quality = 1;
		intervals = 7;
	}
	
	// minor 7th
	if ((chordindex >= 84) && (chordindex < 96))
	{
		root = chordindex-84;
		quality = 0;
		intervals = 7;
	}
	
	// dominant 7th
	if ((chordindex >= 96) && (chordindex < 108))
	{
		root = chordindex-96;
		quality = 2;
		intervals = 7;
	}
}

//--------------------------------------------------------------------------------------
// calculate weightings to help distinguish between sus2/sus4 and aug chords
void ChordDetect :: calculateweightings()
{
	int i;
	float maxval = 0;
	int fifth;
	int augfifth;
	
	maxval = max(chroma_low,12); 
		
	// normalise chroma low
	for (i = 0;i < 12;i++)
	{
		chroma_low[i] = chroma_low[i] / maxval;
	}
	
	// make weight for sus chords
	for (i = 0;i < 12;i++)
	{
		fifth = i+7;
		augfifth = i+8;
		
		if (fifth >= 12)
		{
			fifth = fifth-12;
		}
		
		if (augfifth >= 12)
		{
			augfifth = augfifth-12;
		}
		
		weight_sus[i] = chroma_low[i] + (chroma_low[fifth]/2);
		weight_aug[i] = chroma_low[i] + (chroma_low[augfifth]/2);
	}
	
	maxval = max(weight_sus,12);
	// normalise weight_sus
	for (i = 0;i < 12;i++)
	{
		weight_sus[i] = weight_sus[i] / maxval;
	}
	
	maxval = max(weight_aug,12);
	// normalise weight_aug
	for (i = 0;i < 12;i++)
	{
		weight_aug[i] = weight_aug[i] / maxval;
	}
	
	
}


//--------------------------------------------------------------------------------------
// return delta value indicating how similar the chroma is to the chord template - lower value = more similar
float ChordDetect :: calcchordvalue(float c[],float T[],float biasval, float N)
{
	float sum = 0;
	float delta;

	for (int i=0;i < 12;i++)
	{
		sum = sum + ((1-T[i])*(pow(c[i],2)));
	}

	delta = sqrt(sum) / ((12 - N)*biasval);
	
	return delta;
}


//--------------------------------------------------------------------------------------
// returns max value of an array
float ChordDetect :: max(float array[],int length)
{
	float max = 0;
	
	for (int i=0;i < length;i++)
	{
		if (array[i] > max)
		{
			max = array[i];
		}
	}
	
	return max;
}

//--------------------------------------------------------------------------------------
// returns index of minimum value of array
int ChordDetect :: minindex(float array[],int length)
{
	float min = 10000;
	int minindex = 0;
	int i;
	
	for (i = 0;i < length;i++)
	{
		if (array[i] < min)
		{
			min = array[i];
			minindex = i;
		}
	}
	
	return minindex;
}

//--------------------------------------------------------------------------------------
// calculates bit mask chord profiles
void ChordDetect :: makeprofiles()
{
	int i;
	int t;
	int j = 0;
	int root;
	int third;
	int fifth;
	int seventh;
	
	float v1 = 1;
	float v2 = 1;
	float v3 = 1;
	
	// set profiles matrix to all zeros
	for (j = 0;j < 108;j++)
	{
		for (t = 0;t < 12;t++)
		{
			profiles[j][t] = 0;
		}
	}
	
	// reset j to zero to begin creating profiles
	j = 0;
	
	// major chords
	for (i = 0;i < 12;i++)
	{
		root = i % 12;
		third = (i+4) % 12;
		fifth = (i+7) % 12;
		
		profiles[j][root] = v1;
		profiles[j][third] = v2;
		profiles[j][fifth] = v3;
		
		j++;				
	}

	// minor chords
	for (i = 0;i < 12;i++)
	{
		root = i % 12;
		third = (i+3) % 12;
		fifth = (i+7) % 12;
		
		profiles[j][root] = v1;
		profiles[j][third] = v2;
		profiles[j][fifth] = v3;
		
		j++;				
	}

	// diminished chords
	for (i = 0;i < 12;i++)
	{
		root = i % 12;
		third = (i+3) % 12;
		fifth = (i+6) % 12;
		
		profiles[j][root] = v1;
		profiles[j][third] = v2;
		profiles[j][fifth] = v3;
		
		j++;				
	}	
	
	// augmented chords
	for (i = 0;i < 12;i++)
	{
		root = i % 12;
		third = (i+4) % 12;
		fifth = (i+8) % 12;
		
		profiles[j][root] = v1;
		profiles[j][third] = v2;
		profiles[j][fifth] = v3;
		
		j++;				
	}	
	
	// sus2 chords
	for (i = 0;i < 12;i++)
	{
		root = i % 12;
		third = (i+2) % 12;
		fifth = (i+7) % 12;
		
		profiles[j][root] = v1;
		profiles[j][third] = v2;
		profiles[j][fifth] = v3;
		
		j++;				
	}
	
	// sus4 chords
	for (i = 0;i < 12;i++)
	{
		root = i % 12;
		third = (i+5) % 12;
		fifth = (i+7) % 12;
		
		profiles[j][root] = v1;
		profiles[j][third] = v2;
		profiles[j][fifth] = v3;
		
		j++;				
	}		
	
	// major 7th chords
	for (i = 0;i < 12;i++)
	{
		root = i % 12;
		third = (i+4) % 12;
		fifth = (i+7) % 12;
		seventh = (i+11) % 12;
		
		profiles[j][root] = v1;
		profiles[j][third] = v2;
		profiles[j][fifth] = v3;
		profiles[j][seventh] = v3;
		
		j++;				
	}	
	
	// minor 7th chords
	for (i = 0;i < 12;i++)
	{
		root = i % 12;
		third = (i+3) % 12;
		fifth = (i+7) % 12;
		seventh = (i+10) % 12;
		
		profiles[j][root] = v1;
		profiles[j][third] = v2;
		profiles[j][fifth] = v3;
		profiles[j][seventh] = v3;
		
		j++;				
	}
	
	// dominant 7th chords
	for (i = 0;i < 12;i++)
	{
		root = i % 12;
		third = (i+4) % 12;
		fifth = (i+7) % 12;
		seventh = (i+10) % 12;
		
		profiles[j][root] = v1;
		profiles[j][third] = v2;
		profiles[j][fifth] = v3;
		profiles[j][seventh] = v3;
		
		j++;				
	}
}