forked from AnthonyChiavelli/ChordFinder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ChordFinder.cpp
129 lines (119 loc) · 4.03 KB
/
ChordFinder.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "ChordFinder.h"
#include <iostream>
#include <boost/regex.hpp>
using namespace std;
set<string> *findChords (string notes) {
//Get note tokens
set<string> noteList = tokenize(notes);
set<string> *chordNames = new set<string>;
//Iterate over notes, assuming each is tonic
for (set<string>::iterator tonic = noteList.begin(); tonic != noteList.end(); ++tonic) {
//Pattern of intervals between tonic and other notes
set<int> pattern;
//Iterate over other notes, finding interval to current tonic
for (set<string>::iterator note = noteList.begin(); note != noteList.end(); ++note) {
int interval = ((chrIndexOf(*note) - chrIndexOf(*tonic)) + 12 ) % 12;
pattern.insert(interval);
}
//Match to chord pattern library
for (map<set<int>, string>::iterator pairs = patternMap.begin(); pairs != patternMap.end(); ++pairs) {
if (patternsEqual(pairs->first, pattern)) {
chordNames->insert(*tonic + string(" ") + string(pairs->second));
}
}
}
return chordNames;
}
static set<string> tokenize (string notes) {
set<string> *validNotes = new set<string>;
//Matches chromatic scale notes
static boost::regex validNotesRE("[A-Ga-g][#b]{0,2}");
//End of sequence iterator for later comparison
boost::regex_token_iterator<string::iterator> eos;
boost::regex_token_iterator<string::iterator> iter (notes.begin(), notes.end(), validNotesRE, 0);
//Add all matches to a set
while (iter != eos) {
string note = *iter++;
//If lowercase, make uppercase
if ((note)[0] > 'Z')
(note)[0] -= ('a' - 'A');
validNotes->insert(note);
}
return *validNotes;
}
static int chrIndexOf(string note) {
//Find the index of the note without any accidentals
int index = ((note[0] - 'A') * 2) - ((note[0] >= 'C') + (note[0] >= 'F'));
//No accidentals
if (note.length() == 1)
return index;
for (int i = 1; i < note.length(); i++) {
//Add or subtract for each accidental
if (note[i] == '#') index++;
else if (note[i] == 'b') index--;
}
return index;
}
int initPatternData() {
//Triads
set<int> major = {0, 4, 7};
patternMap[major] = "major";
set<int> minor = {0, 3, 7};
patternMap[minor] = "minor";
set<int> augmented = {0, 4, 8};
patternMap[augmented] = "augmented";
set<int> diminished = {0, 3, 6};
patternMap[diminished] = "diminshed";
//Triads with suspensions
set<int> add9 = {0, 2, 4, 7};
patternMap[add9] = "add 9";
set<int> sus2 = {0, 2, 7};
patternMap[sus2] = "sus 2";
set<int> sus4 = {0, 5, 7};
patternMap[sus4] = "sus 4";
//Sixth chords
set<int> major6 = {0, 4, 7, 9};
patternMap[major6] = "major 6";
set<int> minor6 = {0, 3, 7, 9};
patternMap[minor6] = "minor 6";
//Seventh chords
set<int> major7 = {0, 4, 7, 11};
patternMap[major7] = "major 7";
set<int> minor7 = {0, 3, 7, 10};
patternMap[minor7] = "minor 7";
set<int> dominant7 = {0, 4, 7, 10};
patternMap[dominant7] = "dominant 7";
set<int> diminished7 = {0, 3, 6, 9};
patternMap[diminished7] = "diminished 7";
set<int> halfdiminished7 = {0, 3, 6, 10};
patternMap[halfdiminished7] = "half-diminished 7";
set<int> minorMajor7 = {0, 3, 7, 11};
patternMap[minorMajor7] = "minor major 7";
set<int> augmented7= {0, 4, 8, 10};
patternMap[augmented7] = "augmented 7";
set<int> augmentedMajor7 = {0, 4, 8, 11};
patternMap[augmentedMajor7] = "augmented major 7";
return 0;
}
static bool patternsEqual(const set<int> &a, const set<int> &b) {
if (a.size() != b.size())
return false;
return equal(a.begin(), a.end(), b.begin());
}
int main(int argc, char *argv[]) {
if (argc < 2) {
cout << "Usage: ChordFinder note1 [note2] [note3] ..." << endl;
return 1;
}
string chordString;
//Flatten command line args into a single string
for (int i = 1; i < argc; i++)
chordString += string(argv[i]) + " ";
//Initialize chord data
initPatternData();
//Find and print
set<string> *notes = findChords(chordString);
for (set<string>::iterator iter = notes->begin(); iter != notes->end(); ++iter)
cout << *iter << endl;
return 0;
}