Chris@35
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@35
|
2
|
Chris@35
|
3 /*
|
Chris@35
|
4 NNLS-Chroma / Chordino
|
Chris@35
|
5
|
Chris@35
|
6 Audio feature extraction plugins for chromagram and chord
|
Chris@35
|
7 estimation.
|
Chris@35
|
8
|
Chris@35
|
9 Centre for Digital Music, Queen Mary University of London.
|
Chris@35
|
10 This file copyright 2008-2010 Matthias Mauch and QMUL.
|
Chris@35
|
11
|
Chris@35
|
12 This program is free software; you can redistribute it and/or
|
Chris@35
|
13 modify it under the terms of the GNU General Public License as
|
Chris@35
|
14 published by the Free Software Foundation; either version 2 of the
|
Chris@35
|
15 License, or (at your option) any later version. See the file
|
Chris@35
|
16 COPYING included with this distribution for more information.
|
Chris@35
|
17 */
|
Chris@35
|
18
|
Chris@27
|
19 #include "chromamethods.h"
|
Chris@27
|
20
|
Chris@27
|
21 #include <cmath>
|
Chris@27
|
22 #include <list>
|
Chris@27
|
23 #include <iostream>
|
Chris@27
|
24 #include <fstream>
|
Chris@27
|
25 #include <sstream>
|
Chris@27
|
26 #include <cassert>
|
Chris@27
|
27 #include <cstdlib>
|
Chris@27
|
28 #include <cstdio>
|
Chris@27
|
29 #include <boost/tokenizer.hpp>
|
Chris@27
|
30 #include <boost/iostreams/device/file.hpp>
|
Chris@27
|
31 #include <boost/iostreams/stream.hpp>
|
Chris@27
|
32 #include <boost/lexical_cast.hpp>
|
Chris@27
|
33
|
Chris@27
|
34 using namespace std;
|
Chris@27
|
35 using namespace boost;
|
Chris@27
|
36
|
Chris@27
|
37
|
Chris@27
|
38 /** Special Convolution
|
mail@115
|
39 Special convolution is as long as the convolvee, i.e. the first argument.
|
mail@115
|
40 In the "valid" core part of the convolution it contains the usual convolution
|
mail@115
|
41 values, but the parts at the beginning (ending) that would normally be
|
mail@115
|
42 calculated using zero padding simply have the same values as the first
|
mail@115
|
43 (last) valid convolution bin.
|
Chris@27
|
44 **/
|
Chris@27
|
45
|
Chris@27
|
46 vector<float> SpecialConvolution(vector<float> convolvee, vector<float> kernel)
|
Chris@27
|
47 {
|
Chris@27
|
48 float s;
|
Chris@27
|
49 int m, n;
|
Chris@27
|
50 int lenConvolvee = convolvee.size();
|
Chris@27
|
51 int lenKernel = kernel.size();
|
Chris@27
|
52
|
mail@77
|
53 vector<float> Z(nNote,0);
|
Chris@27
|
54 assert(lenKernel % 2 != 0); // no exception handling !!!
|
Chris@27
|
55
|
Chris@27
|
56 for (n = lenKernel - 1; n < lenConvolvee; n++) {
|
Chris@27
|
57 s=0.0;
|
Chris@27
|
58 for (m = 0; m < lenKernel; m++) {
|
Chris@27
|
59 // cerr << "m = " << m << ", n = " << n << ", n-m = " << (n-m) << '\n';
|
Chris@27
|
60 s += convolvee[n-m] * kernel[m];
|
Chris@27
|
61 // if (debug_on) cerr << "--> s = " << s << '\n';
|
Chris@27
|
62 }
|
Chris@27
|
63 // cerr << n - lenKernel/2 << endl;
|
Chris@27
|
64 Z[n -lenKernel/2] = s;
|
Chris@27
|
65 }
|
Chris@27
|
66
|
Chris@27
|
67 // fill upper and lower pads
|
Chris@27
|
68 for (n = 0; n < lenKernel/2; n++) Z[n] = Z[lenKernel/2];
|
Chris@27
|
69 for (n = lenConvolvee; n < lenConvolvee +lenKernel/2; n++) Z[n - lenKernel/2] =
|
Chris@27
|
70 Z[lenConvolvee - lenKernel/2 - 1];
|
Chris@27
|
71 return Z;
|
Chris@27
|
72 }
|
Chris@27
|
73
|
Chris@27
|
74 float cospuls(float x, float centre, float width)
|
Chris@27
|
75 {
|
Chris@27
|
76 float recipwidth = 1.0/width;
|
Chris@27
|
77 if (abs(x - centre) <= 0.5 * width) {
|
Chris@27
|
78 return cos((x-centre)*2*M_PI*recipwidth)*.5+.5;
|
Chris@27
|
79 }
|
Chris@27
|
80 return 0.0;
|
Chris@27
|
81 }
|
Chris@27
|
82
|
Chris@27
|
83 float pitchCospuls(float x, float centre, int binsperoctave)
|
Chris@27
|
84 {
|
Chris@27
|
85 float warpedf = -binsperoctave * (log2(centre) - log2(x));
|
Chris@27
|
86 float out = cospuls(warpedf, 0.0, 2.0);
|
Chris@27
|
87 // now scale to correct for note density
|
Chris@27
|
88 float c = log(2.0)/binsperoctave;
|
Chris@27
|
89 if (x > 0) {
|
Chris@27
|
90 out = out / (c * x);
|
Chris@27
|
91 } else {
|
Chris@27
|
92 out = 0;
|
Chris@27
|
93 }
|
Chris@27
|
94 return out;
|
Chris@27
|
95 }
|
Chris@27
|
96
|
mail@115
|
97 /**
|
mail@115
|
98 * Calculates a matrix that can be used to linearly map from the magnitude spectrum to a pitch-scale spectrum.
|
mail@115
|
99 * @return this always returns true, which is a bit stupid, really. The main purpose of the function is to change the values in the "matrix" pointed to by *outmatrix
|
mail@115
|
100 */
|
Chris@27
|
101 bool logFreqMatrix(int fs, int blocksize, float *outmatrix) {
|
mail@115
|
102 // TODO: rewrite so that everyone understands what is done here.
|
mail@115
|
103 // TODO: make this more general, such that it works with all minoctave, maxoctave and whatever nBPS (or check if it already does)
|
Chris@27
|
104
|
mail@80
|
105 int binspersemitone = nBPS;
|
Chris@27
|
106 int minoctave = 0; // this must be 0
|
Chris@27
|
107 int maxoctave = 7; // this must be 7
|
Chris@27
|
108 int oversampling = 80;
|
Chris@27
|
109
|
Chris@27
|
110 // linear frequency vector
|
Chris@27
|
111 vector<float> fft_f;
|
Chris@27
|
112 for (int i = 0; i < blocksize/2; ++i) {
|
Chris@27
|
113 fft_f.push_back(i * (fs * 1.0 / blocksize));
|
Chris@27
|
114 }
|
Chris@27
|
115 float fft_width = fs * 2.0 / blocksize;
|
Chris@27
|
116
|
Chris@27
|
117 // linear oversampled frequency vector
|
Chris@27
|
118 vector<float> oversampled_f;
|
Chris@91
|
119 for (int i = 0; i < oversampling * blocksize/2; ++i) {
|
Chris@27
|
120 oversampled_f.push_back(i * ((fs * 1.0 / blocksize) / oversampling));
|
Chris@27
|
121 }
|
Chris@27
|
122
|
Chris@27
|
123 // pitch-spaced frequency vector
|
Chris@27
|
124 int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone!
|
Chris@27
|
125 int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone!
|
Chris@27
|
126 vector<float> cq_f;
|
Chris@27
|
127 float oob = 1.0/binspersemitone; // one over binspersemitone
|
mail@80
|
128 for (int i = minMIDI; i < maxMIDI; ++i) {
|
mail@80
|
129 for (int k = 0; k < binspersemitone; ++k) {
|
Chris@27
|
130 cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69)));
|
Chris@27
|
131 }
|
Chris@27
|
132 }
|
mail@80
|
133 // cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-oob-69)));
|
Chris@27
|
134 cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69)));
|
Chris@27
|
135
|
Chris@27
|
136 int nFFT = fft_f.size();
|
Chris@27
|
137
|
Chris@27
|
138 vector<float> fft_activation;
|
Chris@27
|
139 for (int iOS = 0; iOS < 2 * oversampling; ++iOS) {
|
Chris@27
|
140 float cosp = cospuls(oversampled_f[iOS],fft_f[1],fft_width);
|
Chris@27
|
141 fft_activation.push_back(cosp);
|
Chris@27
|
142 // cerr << cosp << endl;
|
Chris@27
|
143 }
|
Chris@135
|
144
|
Chris@146
|
145 for (int i = 0; i < nFFT * (int)cq_f.size(); ++i) {
|
Chris@135
|
146 outmatrix[i] = 0.f;
|
Chris@135
|
147 }
|
Chris@27
|
148
|
Chris@27
|
149 float cq_activation;
|
Chris@27
|
150 for (int iFFT = 1; iFFT < nFFT; ++iFFT) {
|
Chris@27
|
151 // find frequency stretch where the oversampled vector can be non-zero (i.e. in a window of width fft_width around the current frequency)
|
Chris@27
|
152 int curr_start = oversampling * iFFT - oversampling;
|
Chris@27
|
153 int curr_end = oversampling * iFFT + oversampling; // don't know if I should add "+1" here
|
Chris@27
|
154 // cerr << oversampled_f[curr_start] << " " << fft_f[iFFT] << " " << oversampled_f[curr_end] << endl;
|
matthiasm@122
|
155 for (int iCQ = 0; iCQ < (int)cq_f.size(); ++iCQ) {
|
Chris@27
|
156 if (cq_f[iCQ] * pow(2.0, 0.084) + fft_width > fft_f[iFFT] && cq_f[iCQ] * pow(2.0, -0.084 * 2) - fft_width < fft_f[iFFT]) { // within a generous neighbourhood
|
Chris@27
|
157 for (int iOS = curr_start; iOS < curr_end; ++iOS) {
|
Chris@27
|
158 cq_activation = pitchCospuls(oversampled_f[iOS],cq_f[iCQ],binspersemitone*12);
|
Chris@27
|
159 // cerr << oversampled_f[iOS] << " " << cq_f[iCQ] << " " << cq_activation << endl;
|
Chris@27
|
160 outmatrix[iFFT + nFFT * iCQ] += cq_activation * fft_activation[iOS-curr_start];
|
Chris@27
|
161 }
|
mail@115
|
162 }
|
Chris@27
|
163 }
|
Chris@27
|
164 }
|
Chris@27
|
165 return true;
|
Chris@27
|
166 }
|
Chris@27
|
167
|
mail@41
|
168 void dictionaryMatrix(float* dm, float s_param) {
|
mail@115
|
169 // TODO: make this more general, such that it works with all minoctave, maxoctave and even more than one note per semitone
|
mail@80
|
170 int binspersemitone = nBPS;
|
Chris@27
|
171 int minoctave = 0; // this must be 0
|
Chris@27
|
172 int maxoctave = 7; // this must be 7
|
Chris@27
|
173
|
Chris@27
|
174 // pitch-spaced frequency vector
|
Chris@27
|
175 int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone!
|
Chris@27
|
176 int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone!
|
Chris@27
|
177 vector<float> cq_f;
|
Chris@27
|
178 float oob = 1.0/binspersemitone; // one over binspersemitone
|
mail@80
|
179 for (int i = minMIDI; i < maxMIDI; ++i) {
|
mail@80
|
180 for (int k = 0; k < binspersemitone; ++k) {
|
Chris@27
|
181 cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69)));
|
Chris@27
|
182 }
|
Chris@27
|
183 }
|
Chris@27
|
184 cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69)));
|
Chris@27
|
185
|
Chris@146
|
186 // float curr_f;
|
Chris@27
|
187 float floatbin;
|
Chris@27
|
188 float curr_amp;
|
Chris@27
|
189 // now for every combination calculate the matrix element
|
Chris@91
|
190 for (int iOut = 0; iOut < 12 * (maxoctave - minoctave); ++iOut) {
|
Chris@27
|
191 // cerr << iOut << endl;
|
Chris@91
|
192 for (int iHarm = 1; iHarm <= 20; ++iHarm) {
|
Chris@146
|
193 // curr_f = 440 * pow(2,(minMIDI-69+iOut)*1.0/12) * iHarm;
|
Chris@27
|
194 // if (curr_f > cq_f[nNote-1]) break;
|
Chris@27
|
195 floatbin = ((iOut + 1) * binspersemitone + 1) + binspersemitone * 12 * log2(iHarm);
|
Chris@27
|
196 // cerr << floatbin << endl;
|
Chris@27
|
197 curr_amp = pow(s_param,float(iHarm-1));
|
Chris@27
|
198 // cerr << "curramp" << curr_amp << endl;
|
Chris@91
|
199 for (int iNote = 0; iNote < nNote; ++iNote) {
|
Chris@27
|
200 if (abs(iNote+1.0-floatbin)<2) {
|
mail@77
|
201 dm[iNote + nNote * iOut] += cospuls(iNote+1.0, floatbin, binspersemitone + 0.0) * curr_amp;
|
Chris@27
|
202 // dm[iNote + nNote * iOut] += 1 * curr_amp;
|
Chris@27
|
203 }
|
Chris@27
|
204 }
|
Chris@27
|
205 }
|
Chris@27
|
206 }
|
Chris@27
|
207 }
|
Chris@27
|
208
|
Chris@30
|
209 static
|
Chris@30
|
210 std::vector<std::string>
|
Chris@30
|
211 getPluginPath()
|
Chris@30
|
212 {
|
Chris@30
|
213 //!!! This is duplicated from PluginHostAdapter::getPluginPath,
|
Chris@30
|
214 //!!! which is not available to us in the plugin (only to the
|
Chris@30
|
215 //!!! host)
|
Chris@30
|
216
|
Chris@30
|
217 std::vector<std::string> path;
|
Chris@30
|
218 std::string envPath;
|
Chris@30
|
219
|
Chris@30
|
220 char *cpath = getenv("VAMP_PATH");
|
Chris@30
|
221 if (cpath) envPath = cpath;
|
Chris@30
|
222
|
Chris@30
|
223 #ifdef _WIN32
|
Chris@30
|
224 #define PATH_SEPARATOR ';'
|
Chris@30
|
225 #define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins"
|
Chris@30
|
226 #else
|
Chris@30
|
227 #define PATH_SEPARATOR ':'
|
Chris@30
|
228 #ifdef __APPLE__
|
Chris@30
|
229 #define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp"
|
Chris@30
|
230 #else
|
Chris@30
|
231 #define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp"
|
Chris@30
|
232 #endif
|
Chris@30
|
233 #endif
|
Chris@30
|
234
|
Chris@30
|
235 if (envPath == "") {
|
Chris@30
|
236 envPath = DEFAULT_VAMP_PATH;
|
Chris@30
|
237 char *chome = getenv("HOME");
|
Chris@30
|
238 if (chome) {
|
Chris@30
|
239 std::string home(chome);
|
Chris@30
|
240 std::string::size_type f;
|
Chris@30
|
241 while ((f = envPath.find("$HOME")) != std::string::npos &&
|
Chris@30
|
242 f < envPath.length()) {
|
Chris@30
|
243 envPath.replace(f, 5, home);
|
Chris@30
|
244 }
|
Chris@30
|
245 }
|
Chris@30
|
246 #ifdef _WIN32
|
Chris@30
|
247 char *cpfiles = getenv("ProgramFiles");
|
Chris@30
|
248 if (!cpfiles) cpfiles = (char *)"C:\\Program Files";
|
Chris@30
|
249 std::string pfiles(cpfiles);
|
Chris@30
|
250 std::string::size_type f;
|
Chris@30
|
251 while ((f = envPath.find("%ProgramFiles%")) != std::string::npos &&
|
Chris@30
|
252 f < envPath.length()) {
|
Chris@30
|
253 envPath.replace(f, 14, pfiles);
|
Chris@30
|
254 }
|
Chris@30
|
255 #endif
|
Chris@30
|
256 }
|
Chris@30
|
257
|
Chris@30
|
258 std::string::size_type index = 0, newindex = 0;
|
Chris@30
|
259
|
Chris@30
|
260 while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
|
Chris@30
|
261 path.push_back(envPath.substr(index, newindex - index));
|
Chris@30
|
262 index = newindex + 1;
|
Chris@30
|
263 }
|
Chris@30
|
264
|
Chris@30
|
265 path.push_back(envPath.substr(index));
|
Chris@30
|
266
|
Chris@30
|
267 return path;
|
Chris@30
|
268 }
|
Chris@27
|
269
|
mail@90
|
270
|
mail@90
|
271
|
mail@90
|
272 static vector<string> staticChordnames() {
|
mail@90
|
273 vector<string> chordnames;
|
mail@90
|
274 chordnames.push_back("");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@90
|
275 chordnames.push_back("");// =0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@90
|
276 chordnames.push_back("m");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0
|
mail@90
|
277 chordnames.push_back("m");//=0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0
|
matthiasm@120
|
278 chordnames.push_back("m7b5");//=0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0
|
matthiasm@120
|
279 chordnames.push_back("m7b5");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0
|
mail@90
|
280 chordnames.push_back("6");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0
|
mail@90
|
281 chordnames.push_back("7");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0
|
mail@90
|
282 chordnames.push_back("maj7");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1
|
mail@90
|
283 chordnames.push_back("m7");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0
|
mail@90
|
284 chordnames.push_back("m6");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,1,0,0
|
mail@90
|
285 chordnames.push_back("");//=0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@90
|
286 chordnames.push_back("");//=0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@123
|
287 chordnames.push_back("dim");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0
|
mail@90
|
288 chordnames.push_back("aug");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0
|
mail@90
|
289 chordnames.push_back("");//=0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@90
|
290 chordnames.push_back("");//=0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@90
|
291 chordnames.push_back("7");//=0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0
|
mail@112
|
292 // from here: Harte syntax
|
mail@112
|
293 chordnames.push_back("");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@112
|
294 chordnames.push_back("");// =0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@112
|
295 chordnames.push_back(":min");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0
|
mail@112
|
296 chordnames.push_back(":min");//=0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0
|
mail@112
|
297 chordnames.push_back(":hdim7");//=0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0
|
mail@112
|
298 chordnames.push_back(":hdim7");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0
|
mail@112
|
299 chordnames.push_back(":maj6");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0
|
mail@112
|
300 chordnames.push_back(":7");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0
|
mail@112
|
301 chordnames.push_back(":maj7");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1
|
mail@112
|
302 chordnames.push_back(":min7");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0
|
mail@112
|
303 chordnames.push_back(":min6");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,1,0,0
|
mail@112
|
304 chordnames.push_back("");//=0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@112
|
305 chordnames.push_back("");//=0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@123
|
306 chordnames.push_back(":dim");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0
|
mail@112
|
307 chordnames.push_back(":aug");//=1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0
|
mail@112
|
308 chordnames.push_back("");//=0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@112
|
309 chordnames.push_back("");//=0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0
|
mail@112
|
310 chordnames.push_back(":7");//=0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0
|
mail@90
|
311 return chordnames;
|
mail@90
|
312 }
|
mail@90
|
313
|
mail@90
|
314 static vector<float> staticChordvalues() {
|
mail@90
|
315 vector<float> chordvalues;
|
mail@90
|
316 float chordvaluearray[] = {1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,
|
mail@90
|
317 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,
|
mail@90
|
318 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,
|
mail@90
|
319 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,
|
mail@90
|
320 0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,
|
mail@90
|
321 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,
|
mail@90
|
322 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0,
|
mail@90
|
323 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0,
|
mail@90
|
324 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,
|
mail@90
|
325 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,
|
mail@90
|
326 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,1,0,0,
|
mail@90
|
327 0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,
|
mail@90
|
328 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,
|
mail@90
|
329 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,
|
mail@90
|
330 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
|
mail@90
|
331 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,
|
mail@90
|
332 0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,
|
mail@90
|
333 0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0};
|
mail@90
|
334 for (int iChord = 0; iChord < 18; ++iChord) {
|
mail@90
|
335 for (int iNote = 0; iNote < 24; iNote++) {
|
mail@90
|
336 chordvalues.push_back(chordvaluearray[24*iChord+iNote]);
|
mail@90
|
337 }
|
mail@90
|
338 }
|
mail@90
|
339 return chordvalues;
|
mail@90
|
340 }
|
mail@90
|
341
|
mail@115
|
342 vector<string> chordDictionary(vector<float> *mchorddict, vector<vector<int> > *m_chordnotes, float boostN, float harte_syntax) {
|
matthiasm@86
|
343
|
Chris@27
|
344 typedef tokenizer<char_separator<char> > Tok;
|
Chris@27
|
345 char_separator<char> sep(",; ","=");
|
Chris@30
|
346
|
Chris@30
|
347 string chordDictBase("chord.dict");
|
Chris@30
|
348 string chordDictFilename;
|
Chris@30
|
349
|
Chris@30
|
350 vector<string> ppath = getPluginPath();
|
mail@90
|
351
|
matthiasm@120
|
352 const char* notenames[24] = {
|
matthiasm@120
|
353 "A (bass)","Bb (bass)","B (bass)","C (bass)","C# (bass)","D (bass)","Eb (bass)","E (bass)","F (bass)","F# (bass)","G (bass)","Ab (bass)",
|
matthiasm@120
|
354 "A","Bb","B","C","C#","D","Eb","E","F","F#","G","Ab"};
|
matthiasm@120
|
355
|
matthiasm@144
|
356 const char* bassnames[13][12] ={
|
matthiasm@120
|
357 {"A","","B","C","C#","D","","E","","F#","G","G#"},
|
matthiasm@120
|
358 {"Bb","","C","Db","D","Eb","","F","","G","Ab","A"},
|
matthiasm@120
|
359 {"B","","C#","D","D#","E","","F#","","G#","A","A#"},
|
matthiasm@120
|
360 {"C","","D","Eb","E","F","","G","","A","Bb","B"},
|
matthiasm@120
|
361 {"C#","","D#","E","E#","F#","","G#","","A#","B","B#"},
|
matthiasm@120
|
362 {"D","","E","F","F#","G","","A","","B","C","C#"},
|
matthiasm@120
|
363 {"Eb","","F","Gb","G","Ab","","Bb","","C","Db","D"},
|
matthiasm@120
|
364 {"E","","F#","G","G#","A","","B","","C#","D","D#"},
|
matthiasm@120
|
365 {"F","","G","Ab","A","Bb","","C","","D","Eb","E"},
|
matthiasm@120
|
366 {"F#","","G#","A","A#","B","","C#","","D#","E","E#"},
|
matthiasm@120
|
367 {"G","","A","Bb","B","C","","D","","E","F","F#"},
|
matthiasm@144
|
368 {"Ab","","Bb","Cb","C","Db","","Eb","","F","Gb","G"},
|
matthiasm@144
|
369 {"1","","2","b3","3","4","","5","","6","b7","7"}
|
matthiasm@120
|
370 };
|
matthiasm@120
|
371
|
mail@90
|
372 bool hasExternalDictinoary = true;
|
matthiasm@122
|
373 int ppathsize = static_cast<int>(ppath.size());
|
matthiasm@122
|
374 for (int i = 0; i < ppathsize; ++i) {
|
mail@61
|
375 chordDictFilename = ppath[i] + "/" + chordDictBase;
|
Chris@163
|
376 // cerr << "Looking for chord.dict in " << chordDictFilename << "..." ;
|
mail@61
|
377 fstream fin;
|
mail@61
|
378 fin.open(chordDictFilename.c_str(),ios::in);
|
mail@61
|
379 if( fin.is_open() )
|
mail@61
|
380 {
|
mail@61
|
381 fin.close();
|
Chris@163
|
382 // cerr << " success." << endl;
|
mail@61
|
383 break;
|
mail@61
|
384 } else {
|
Chris@163
|
385 if (i+1 < ppathsize) {
|
Chris@163
|
386 // cerr << " (not found yet) ..." << endl;
|
Chris@163
|
387 } else {
|
Chris@163
|
388 // cerr << "* WARNING: failed to find chord dictionary, using default chord dictionary." << endl;
|
mail@90
|
389 hasExternalDictinoary = false;
|
mail@90
|
390 }
|
mail@61
|
391 }
|
Chris@30
|
392 }
|
Chris@30
|
393
|
Chris@30
|
394 iostreams::stream<iostreams::file_source> chordDictFile(chordDictFilename);
|
Chris@27
|
395 string line;
|
mail@89
|
396 // int iElement = 0;
|
Chris@27
|
397 int nChord = 0;
|
Chris@27
|
398
|
mail@112
|
399 vector<float> tempChordDict = staticChordvalues();
|
Chris@91
|
400 vector<string> tempChordNames = staticChordnames();
|
mail@115
|
401 if (harte_syntax == 1.0) {
|
mail@112
|
402 tempChordNames.erase(tempChordNames.begin(),tempChordNames.begin()+tempChordNames.size()/2);
|
mail@112
|
403 } else {
|
mail@112
|
404 tempChordNames.erase(tempChordNames.begin()+tempChordNames.size()/2,tempChordNames.begin()+tempChordNames.size());
|
mail@112
|
405 }
|
mail@112
|
406
|
Chris@27
|
407 vector<string> loadedChordNames;
|
Chris@27
|
408 vector<float> loadedChordDict;
|
mail@90
|
409 if (hasExternalDictinoary && chordDictFile.is_open()) {
|
mail@90
|
410 tempChordDict.clear();
|
mail@90
|
411 tempChordNames.clear();
|
matthiasm@86
|
412 while (std::getline(chordDictFile, line)) { // loop over lines in chord.dict file
|
Chris@27
|
413 // first, get the chord definition
|
Chris@27
|
414 string chordType;
|
Chris@27
|
415 vector<float> tempPCVector;
|
Chris@27
|
416 // cerr << line << endl;
|
Chris@27
|
417 if (!line.empty() && line.substr(0,1) != "#") {
|
Chris@27
|
418 Tok tok(line, sep);
|
Chris@27
|
419 for(Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) { // loop over line elements
|
Chris@27
|
420 string tempString = *tok_iter;
|
Chris@27
|
421 // cerr << tempString << endl;
|
mail@90
|
422 if (tok_iter == tok.begin()) { // either the chord name or a colon
|
Chris@27
|
423 if (tempString == "=") {
|
Chris@27
|
424 chordType = "";
|
Chris@27
|
425 } else {
|
Chris@27
|
426 chordType = tempString;
|
mail@90
|
427 tok_iter++;
|
Chris@27
|
428 }
|
Chris@27
|
429 } else {
|
mail@90
|
430 tempChordDict.push_back(lexical_cast<float>(*tok_iter));
|
Chris@27
|
431 }
|
mail@90
|
432 }
|
mail@90
|
433 tempChordNames.push_back(chordType);
|
mail@90
|
434 }
|
mail@90
|
435 }
|
matthias@140
|
436 cerr << "-----------------> " << tempChordNames.size() << endl;
|
mail@90
|
437 }
|
mail@90
|
438
|
mail@90
|
439
|
Chris@91
|
440 for (int iType = 0; iType < (int)tempChordNames.size(); ++iType) {
|
mail@90
|
441 // now make all 12 chords of every type
|
Chris@91
|
442 for (int iSemitone = 0; iSemitone < 12; iSemitone++) {
|
mail@90
|
443 vector<int> tempchordnotes;
|
mail@90
|
444 // add bass slash notation
|
mail@90
|
445 string slashNotation = "";
|
Chris@91
|
446 for (int kSemitone = 1; kSemitone < 12; kSemitone++) {
|
mail@90
|
447 if (tempChordDict[24*iType+(kSemitone) % 12] > 0.99) {
|
mail@115
|
448 if (harte_syntax == 0.0) {
|
mail@112
|
449 slashNotation = bassnames[iSemitone][kSemitone];
|
matthiasm@144
|
450 } else {
|
matthiasm@144
|
451 slashNotation = bassnames[12][kSemitone];
|
matthiasm@144
|
452 }
|
Chris@27
|
453 }
|
Chris@27
|
454 }
|
mail@90
|
455 if (slashNotation=="") tempchordnotes.push_back(MIDI_basenote + (iSemitone+12) % 12);
|
Chris@91
|
456 for (int kSemitone = 0; kSemitone < 12; kSemitone++) { // bass pitch classes
|
mail@90
|
457 // cerr << ((kSemitone - iSemitone + 12) % 12) << endl;
|
mail@90
|
458 float bassValue = 0;
|
mail@90
|
459 if (tempChordDict[24*iType+(kSemitone - iSemitone + 12) % 12]==1) {
|
mail@90
|
460 bassValue = 1;
|
mail@90
|
461 tempchordnotes.push_back(MIDI_basenote + (kSemitone+12) % 12);
|
mail@90
|
462 } else {
|
mail@90
|
463 if (tempChordDict[24*iType+((kSemitone - iSemitone + 12) % 12) + 12] == 1) bassValue = 0.5;
|
mail@90
|
464 }
|
mail@90
|
465 loadedChordDict.push_back(bassValue);
|
mail@90
|
466 }
|
Chris@91
|
467 for (int kSemitone = 0; kSemitone < 12; kSemitone++) { // chord pitch classes
|
mail@90
|
468 loadedChordDict.push_back(tempChordDict[24*iType+((kSemitone - iSemitone + 12) % 12) + 12]);
|
mail@90
|
469 if (tempChordDict[24*iType+((kSemitone - iSemitone + 12) % 12) + 12] > 0) tempchordnotes.push_back(MIDI_basenote + (kSemitone+12+6) % 12 - 6 + 24);
|
mail@90
|
470 }
|
mail@90
|
471 ostringstream os;
|
mail@90
|
472 if (slashNotation.empty()) {
|
mail@90
|
473 os << notenames[12+iSemitone] << tempChordNames[iType];
|
mail@90
|
474 } else {
|
mail@90
|
475 os << notenames[12+iSemitone] << tempChordNames[iType] << "/" << slashNotation;
|
mail@90
|
476 }
|
mail@90
|
477 // cerr << os.str() << endl;
|
mail@90
|
478 loadedChordNames.push_back(os.str());
|
mail@90
|
479
|
mail@90
|
480 m_chordnotes->push_back(tempchordnotes);
|
mail@90
|
481 // for (int iNote = 0; iNote < tempchordnotes.size(); ++iNote) {
|
mail@90
|
482 // cerr << tempchordnotes[iNote] << " ";
|
mail@90
|
483 // }
|
mail@90
|
484 // cerr << endl;
|
Chris@27
|
485 }
|
mail@90
|
486 }
|
mail@90
|
487
|
mail@90
|
488
|
mail@90
|
489 // N type
|
mail@90
|
490 loadedChordNames.push_back("N");
|
matthiasm@122
|
491 for (int kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(0.5);
|
matthiasm@122
|
492 for (int kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(1.0);
|
mail@90
|
493 vector<int> tempchordvector;
|
mail@90
|
494 m_chordnotes->push_back(tempchordvector);
|
mail@90
|
495 float exponent = 2.0;
|
mail@90
|
496 // float m_boostN = 1.1;
|
mail@90
|
497 // cerr << " N BOOST : " << boostN << endl << endl;
|
Chris@91
|
498 for (int iChord = 0; iChord < (int)loadedChordDict.size()/24; iChord++) {
|
mail@90
|
499 float sum = 0;
|
mail@90
|
500 float stand = 0;
|
mail@90
|
501 for (int iST = 0; iST < 24; ++iST) {
|
mail@90
|
502 sum += loadedChordDict[24 * iChord + iST];
|
mail@90
|
503 }
|
mail@90
|
504 for (int iST = 0; iST < 24; ++iST) {
|
mail@90
|
505 // loadedChordDict[24 * iChord + iST] -= sum/24;
|
mail@90
|
506 stand += pow(abs(loadedChordDict[24 * iChord + iST]),exponent)/24;
|
mail@90
|
507 }
|
Chris@91
|
508 if (iChord < (int)loadedChordDict.size()/24 - 1) {
|
Chris@91
|
509 stand = powf(stand,1.0f/exponent);
|
mail@90
|
510 } else {
|
matthiasm@95
|
511 stand = powf(stand,1.0f/exponent) / (1+boostN);
|
mail@90
|
512 }
|
mail@90
|
513 for (int iST = 0; iST < 24; ++iST) {
|
mail@90
|
514 loadedChordDict[24 * iChord + iST] /= stand;
|
Chris@27
|
515 }
|
matthiasm@44
|
516
|
mail@90
|
517 }
|
mail@90
|
518
|
mail@90
|
519
|
mail@90
|
520
|
mail@90
|
521 nChord = 0;
|
Chris@91
|
522 for (int i = 0; i < (int)loadedChordNames.size(); i++) {
|
mail@90
|
523 nChord++;
|
mail@90
|
524 }
|
mail@90
|
525 chordDictFile.close();
|
mail@90
|
526
|
mail@90
|
527
|
mail@90
|
528 // mchorddict = new float[nChord*24];
|
mail@90
|
529 for (int i = 0; i < nChord*24; i++) {
|
mail@90
|
530 mchorddict->push_back(loadedChordDict[i]);
|
mail@90
|
531 }
|
Chris@27
|
532
|
Chris@27
|
533 // cerr << "before leaving" << chordnames[1] << endl;
|
Chris@27
|
534 return loadedChordNames;
|
Chris@27
|
535 }
|
mail@90
|
536
|
Chris@91
|
537
|