Solved

# Musical chord recognition

Posted on 2003-11-27
465 Views
I am working a program that takes note names on the command line and prints the name of the chord.
I have defined the data types for the chords and know the basic algorythm.
I just need it written but I don't have the time. No this is not homework.

gNotenames will hold the names of the notes that are going to be passed
gChordTable defines the chords

the Notes are
C C# D D# E F F# G G# A A# B C C# D D# E F F# G G# A A# B C C# D D# E F F# G G# A A# B C
Notice how they wrap

EXAMPLE on the command line is passed
G D B

the function should
1) Use G as the root of the chord
2) Recognize that D is a perfect_5th from G. (see my enums)
This would be done by counting UP from G G G# A A# B C C# D  = 7

3) Recogsize that B is a major_3rd from G
4) Search through gChordTable and find that the chord is a G Major

I will add code myself to deal with flats and  will  deal with
chords with implied notes such as major_3rd,  major_6th,  nineth and know that the chord is Maj6_9
even though the perfect_5th is  missing.

You have the algorythm so please write it.
You can assume all data passed in will be valid. I will add the error checking.

Here is what I have so far

#define NOTE_STR(note)      #note

char* gNotenames[] = {
NOTE_STR(C),
NOTE_STR(C#),
NOTE_STR(D),
NOTE_STR(D#),
NOTE_STR(E),
NOTE_STR(F),
NOTE_STR(F#),
NOTE_STR(G),
NOTE_STR(G#),
NOTE_STR(A),
NOTE_STR(A#),
NOTE_STR(B)
};

enum {
Unison = 0,
flat_2nd = 1,
major_second = 2,
minor_3rd = 3,
flat_3rd = 3,
major_3rd = 4,
perfect_4th = 5,
flat_5th = 6,
dim_5th = 6,
aug_4th = 6,
perfect_5th = 7,
minor_6th = 8,
sharp_5th = 8,
augmented_5th = 8,
major_6th = 9,
dim_7th = 9,                  /* (double flat 7) */
minor_7th = 10,

flat_7th = 10,
major_7th = 11,
octave = 12,
flat_9th = 13,
nineth = 14,
sharp_9th = 15,
minor_10th = 15,             /* (just minor 3rd one octave higher) */
major_10th = 16,             /* (just major 3rd one octave higher) */
eleventh = 17,
augmented_11th = 18,
perfect_12th = 19,            /* (octave above perfect 5th) */
flat_13th = 20,
thirteenth = 21,
flat_fifteenth = 23,
fifteenth = 24
};

typedef enum {
Major = 0,
Major6,
Maj7,
Maj9,
Maj6_9,
Sus4,
Sus2,
Minor,
Minor6,
Minor7,
Minor9,
Minor11,
Minor7b5,
Dominant7,
Dominant7b5,
Dominant7Sharp5,
Dominant9,
Dominant7b9,
Dominant7Sharp9,
Dominant11,
Dominant13,
Augmented,
Dim,
LastChord
} ChordType;

#define kMaxNotesPerChord 10
typedef struct {
char*            chordName;
ChordType      chord;
short            numNotes;
Steps            steps[kMaxNotesPerChord];
} Chord;

#define CHORD(chord)      #chord, ##chord
Chord gChordTable[] = {
CHORD(Major),                  3,      { Unison, major_3rd, perfect_5th },
CHORD(Major6),                  4,       { Unison, major_3rd, perfect_5th, major_6th },
CHORD(Maj7),                  4,      { Unison, major_3rd, perfect_5th, major_7th },
CHORD(Maj9),                  5,      { Unison, major_3rd, perfect_5th, major_7th,  nineth },
CHORD(Maj6_9),                  5,      { Unison, major_3rd, perfect_5th, major_6th,  nineth },
CHORD(Sus4),                  3,      { Unison, perfect_4th, perfect_5th },
CHORD(Sus2),                  3,      { Unison, major_second, perfect_5th },

CHORD(Minor),                  3,      { Unison, minor_3rd, perfect_5th },
CHORD(Minor6),                  4,      { Unison, minor_3rd, perfect_5th, major_6th},
CHORD(Minor7),                  4,      { Unison, minor_3rd, perfect_5th, minor_7th },
CHORD(Minor9),                  5,      { Unison, minor_3rd, perfect_5th, minor_7th, nineth },
CHORD(Minor11),                  6,      { Unison, minor_3rd, perfect_5th, minor_7th, nineth, eleventh },
CHORD(Minor7b5),            4,      { Unison, minor_3rd, flat_5th, minor_7th },

CHORD(Dominant7),            4,      { Unison, major_3rd, perfect_5th, flat_7th },
CHORD(Dominant7b5),            4,      { Unison, major_3rd, flat_5th, flat_7th },
CHORD(Dominant7Sharp5), 4,      { Unison, major_3rd, sharp_5th, flat_7th },
CHORD(Dominant9),            5,      { Unison, major_3rd, perfect_5th, flat_7th,  nineth},
CHORD(Dominant7b9),            5,      { Unison, major_3rd, perfect_5th, flat_7th,  flat_9th},
CHORD(Dominant7Sharp9),      5,      { Unison, major_3rd, perfect_5th, flat_7th,  sharp_9th},
CHORD(Dominant11),            6,      { Unison, major_3rd, perfect_5th, flat_7th,  nineth, eleventh},
CHORD(Dominant13),            7,      { Unison, major_3rd, perfect_5th, flat_7th,  nineth, eleventh, thirteenth},
CHORD(Augmented),            3,      { Unison, major_3rd, augmented_5th },
CHORD(Dim),                        4,      { Unison, minor_3rd, flat_5th, dim_7th },
};
#define kNumChords sizeof(gChordTable) / sizeof(Chord)

0
Question by:guitardude101
• 3
• 2

LVL 1

Author Comment

ID: 9833764
the typedef for Steps is

typedef short Steps;
0

LVL 16

Accepted Solution

ID: 9839050
This seems to fit the bill:

#include <stdio.h>
#include <string.h>

#define NOTE_STR(note)     #note

char* gNotenames[] = {
NOTE_STR(C),
NOTE_STR(C#),
NOTE_STR(D),
NOTE_STR(D#),
NOTE_STR(E),
NOTE_STR(F),
NOTE_STR(F#),
NOTE_STR(G),
NOTE_STR(G#),
NOTE_STR(A),
NOTE_STR(A#),
NOTE_STR(B)
};

enum {
Unison = 0,
flat_2nd = 1,
major_second = 2,
minor_3rd = 3,
flat_3rd = 3,
major_3rd = 4,
perfect_4th = 5,
flat_5th = 6,
dim_5th = 6,
aug_4th = 6,
perfect_5th = 7,
minor_6th = 8,
sharp_5th = 8,
augmented_5th = 8,
major_6th = 9,
dim_7th = 9,               /* (double flat 7) */
minor_7th = 10,

flat_7th = 10,
major_7th = 11,
octave = 12,
flat_9th = 13,
nineth = 14,
sharp_9th = 15,
minor_10th = 15,           /* (just minor 3rd one octave higher) */
major_10th = 16,           /* (just major 3rd one octave higher) */
eleventh = 17,
augmented_11th = 18,
perfect_12th = 19,          /* (octave above perfect 5th) */
flat_13th = 20,
thirteenth = 21,
flat_fifteenth = 23,
fifteenth = 24
};

typedef enum {
Major = 0,
Major6,
Maj7,
Maj9,
Maj6_9,
Sus4,
Sus2,
Minor,
Minor6,
Minor7,
Minor9,
Minor11,
Minor7b5,
Dominant7,
Dominant7b5,
Dominant7Sharp5,
Dominant9,
Dominant7b9,
Dominant7Sharp9,
Dominant11,
Dominant13,
Augmented,
Dim,
LastChord
} ChordType;

typedef short Steps;

#define kMaxNotesPerChord 10
typedef struct {
char*          chordName;
ChordType     chord;
short          numNotes;
Steps          steps[kMaxNotesPerChord];
} Chord;

#define CHORD(chord)     #chord, ##chord
Chord gChordTable[] = {
CHORD(Major),               3,     { Unison, major_3rd, perfect_5th },
CHORD(Major6),               4,      { Unison, major_3rd, perfect_5th, major_6th },
CHORD(Maj7),               4,     { Unison, major_3rd, perfect_5th, major_7th },
CHORD(Maj9),               5,     { Unison, major_3rd, perfect_5th, major_7th,  nineth },
CHORD(Maj6_9),               5,     { Unison, major_3rd, perfect_5th, major_6th,  nineth },
CHORD(Sus4),               3,     { Unison, perfect_4th, perfect_5th },
CHORD(Sus2),               3,     { Unison, major_second, perfect_5th },

CHORD(Minor),               3,     { Unison, minor_3rd, perfect_5th },
CHORD(Minor6),               4,     { Unison, minor_3rd, perfect_5th, major_6th},
CHORD(Minor7),               4,     { Unison, minor_3rd, perfect_5th, minor_7th },
CHORD(Minor9),               5,     { Unison, minor_3rd, perfect_5th, minor_7th, nineth },
CHORD(Minor11),               6,     { Unison, minor_3rd, perfect_5th, minor_7th, nineth, eleventh },
CHORD(Minor7b5),          4,     { Unison, minor_3rd, flat_5th, minor_7th },

CHORD(Dominant7),          4,     { Unison, major_3rd, perfect_5th, flat_7th },
CHORD(Dominant7b5),          4,     { Unison, major_3rd, flat_5th, flat_7th },
CHORD(Dominant7Sharp5), 4,     { Unison, major_3rd, sharp_5th, flat_7th },
CHORD(Dominant9),          5,     { Unison, major_3rd, perfect_5th, flat_7th,  nineth},
CHORD(Dominant7b9),          5,     { Unison, major_3rd, perfect_5th, flat_7th,  flat_9th},
CHORD(Dominant7Sharp9),     5,     { Unison, major_3rd, perfect_5th, flat_7th,  sharp_9th},
CHORD(Dominant11),          6,     { Unison, major_3rd, perfect_5th, flat_7th,  nineth, eleventh},
CHORD(Dominant13),          7,     { Unison, major_3rd, perfect_5th, flat_7th,  nineth, eleventh, thirteenth},
CHORD(Augmented),          3,     { Unison, major_3rd, augmented_5th },
CHORD(Dim),                    4,     { Unison, minor_3rd, flat_5th, dim_7th },
};
#define kNumChords sizeof(gChordTable) / sizeof(Chord)

void getChord(char [],int,char *[]);
int  getNoteIx(char *);

void main(int argc,char *argv[])
{      char thecord[30];

getChord(thecord,argc-1,argv+1);
printf("%s\n",thecord);
}

void getChord(char thecord[],int numnotes,char *notes[])
{      int i,j,k,rootix,nix;
int relnote[kMaxNotesPerChord],ncheck[kMaxNotesPerChord];

relnote[0]=0;            /* relnote will contain the "distance" from the root */
rootix=getNoteIx(notes[0]);
for(i=1; i<numnotes; ++i)
{      nix=getNoteIx(notes[i]);                  /* get note index */
if(nix>rootix)relnote[i]=nix-rootix;/* calculate "distance" to root */
else relnote[i]=(12-rootix)+nix;
}
for(i=0; i<kNumChords; ++i)
{      if(gChordTable[i].numNotes==numnotes)      /* if chord has same number of notes */
{      for(j=0; j<kMaxNotesPerChord; ++j)ncheck[j]=0;      /* clear check array */

/* for each note in the chord
check if it is somewhere in the definition
if it is, set the check mark
*/
for(j=1; j<numnotes; ++j)
{      for(k=1; k<numnotes && gChordTable[i].steps[k]!=relnote[j]; ++k);
if(k<numnotes)ncheck[k]=1;
}
for(j=1; j<numnotes && ncheck[j]==1; ++j);      /* all notes checked? */
if(j>=numnotes)      /* if they are, then the chord is found */
{      sprintf(thecord,"%s %s",notes[0],gChordTable[i].chordName);
return;
}
}
}
strcpy(thecord,"Unknown");
return;
}

int getNoteIx(char *note)
{      int i;

for(i=0; i<12 && strcmp(note,gNotenames[i])!=0; ++i);
return(i);
}

0

LVL 16

Expert Comment

ID: 9867379
Did that program do what you wanted?

If so, it is now time to grade the answer and close the question.

If not, an indication of what more you need would be helpful.
0

LVL 1

Author Comment

ID: 9869143
Sorry I took long to test it. Like I said I am very busy. Great answer!
0

LVL 1

Author Comment

ID: 9870355
FYI for others that may use this code I did change the function slightly to be able to deal with 9ths, 11ths ect,
The problem is the 9th in the above code will be evaluated to a 2nd instead. Very simple change.

void getChord(char thecord[],int numnotes,char *notes[])
{
int index, noteindex, relcount, rootix, nix;
int relnote[kMaxNotesPerChord], ncheck[kMaxNotesPerChord];
relnote[0]=0;                                                      /* relnote will contain the "distance" from the root */
rootix=getNoteIx(notes[0]);
for(index=1; index<numnotes; index++)
{
nix=getNoteIx(notes[index]);                        /* get note index */
if(nix>rootix)
relnote[index]=nix-rootix;                        /* calculate "distance" to root */
else
relnote[index]=(octave - rootix)+nix;
}
for(index=0; index<kNumChords; index++)
{
if(gChordTable[index].numNotes==numnotes)      /* if chord has same number of notes */
{
for(noteindex = 0; noteindex < kMaxNotesPerChord; noteindex++)
ncheck[noteindex]=0;                        /* clear check array */
/* for each note in the chord
check if it is somewhere in the definition
if it is, set the check mark
*/
for(noteindex = 1; noteindex<numnotes; noteindex++)
{
int match = 0, matchindex;
relcount = 0;
do
{
match = gChordTable[index].steps[relcount] == relnote[noteindex];
if ((match == 0) && (gChordTable[index].steps[relcount] > octave))
match = gChordTable[index].steps[relcount] == relnote[noteindex] + octave;
if (match)
matchindex = relcount;
else
relcount++;
} while (relcount < numnotes && !match);

if(match)
ncheck[matchindex]=1;
}
for(noteindex = 1; noteindex < numnotes && ncheck[noteindex]==1; noteindex++) /* all notes checked? */
;
if(noteindex >= numnotes) /* if they are, then the chord is found */
{
sprintf(thecord,"%s %s", notes[0], gChordTable[index].chordName);
return;
}
}
}
strcpy(thecord,"Unknown");
return;
}
0

## Featured Post

### Suggested Solutions

logging Access violation 6 18
memory mapped I/O query 6 136
Way to improve it 16 59
Problem in finding output of a program 11 93
Have you thought about creating an iPhone application (app), but didn't even know where to get started? Here's how: ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Important pre-programming comments: I’ve never tri…
Preface I don't like visual development tools that are supposed to write a program for me. Even if it is Xcode and I can use Interface Builder. Yes, it is a perfect tool and has helped me a lot, mainly, in the beginning, when my programs were small…
The goal of this video is to provide viewers with basic examples to understand and use nested-loops in the C programming language.
The goal of this video is to provide viewers with basic examples to understand how to create, access, and change arrays in the C programming language.