• C

Musical chord recognition

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)

LVL 1
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Author Commented:
the typedef for Steps is

typedef short Steps;
0
Commented:
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

Experts Exchange Solution brought to you by

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Commented:
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
Author Commented:
Sorry I took long to test it. Like I said I am very busy. Great answer!
0
Author Commented:
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.