Solved

Musical chord recognition

Posted on 2003-11-27
5
465 Views
Last Modified: 2010-04-15
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
Comment
Question by:guitardude101
  • 3
  • 2
5 Comments
 
LVL 1

Author Comment

by:guitardude101
ID: 9833764
the typedef for Steps is

typedef short Steps;
0
 
LVL 16

Accepted Solution

by:
imladris earned 150 total points
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

by:imladris
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

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

Author Comment

by:guitardude101
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.
Still an A answer.

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

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
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…
Video by: Grant
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.

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now