Chris@27
|
1 #include "chromamethods.h"
|
Chris@27
|
2
|
Chris@27
|
3 #include <cmath>
|
Chris@27
|
4 #include <list>
|
Chris@27
|
5 #include <iostream>
|
Chris@27
|
6 #include <fstream>
|
Chris@27
|
7 #include <sstream>
|
Chris@27
|
8 #include <cassert>
|
Chris@27
|
9 #include <cstdlib>
|
Chris@27
|
10 #include <cstdio>
|
Chris@27
|
11 #include <boost/tokenizer.hpp>
|
Chris@27
|
12 #include <boost/iostreams/device/file.hpp>
|
Chris@27
|
13 #include <boost/iostreams/stream.hpp>
|
Chris@27
|
14 #include <boost/lexical_cast.hpp>
|
Chris@27
|
15
|
Chris@27
|
16 #include "chorddict.cpp"
|
Chris@27
|
17
|
Chris@27
|
18 using namespace std;
|
Chris@27
|
19 using namespace boost;
|
Chris@27
|
20
|
Chris@27
|
21
|
Chris@27
|
22 /** Special Convolution
|
Chris@27
|
23 special convolution is as long as the convolvee, i.e. the first argument. in the valid core part of the
|
Chris@27
|
24 convolution it contains the usual convolution values, but the pads at the beginning (ending) have the same values
|
Chris@27
|
25 as the first (last) valid convolution bin.
|
Chris@27
|
26 **/
|
Chris@27
|
27
|
Chris@27
|
28 vector<float> SpecialConvolution(vector<float> convolvee, vector<float> kernel)
|
Chris@27
|
29 {
|
Chris@27
|
30 float s;
|
Chris@27
|
31 int m, n;
|
Chris@27
|
32 int lenConvolvee = convolvee.size();
|
Chris@27
|
33 int lenKernel = kernel.size();
|
Chris@27
|
34
|
Chris@27
|
35 vector<float> Z(256,0);
|
Chris@27
|
36 assert(lenKernel % 2 != 0); // no exception handling !!!
|
Chris@27
|
37
|
Chris@27
|
38 for (n = lenKernel - 1; n < lenConvolvee; n++) {
|
Chris@27
|
39 s=0.0;
|
Chris@27
|
40 for (m = 0; m < lenKernel; m++) {
|
Chris@27
|
41 // cerr << "m = " << m << ", n = " << n << ", n-m = " << (n-m) << '\n';
|
Chris@27
|
42 s += convolvee[n-m] * kernel[m];
|
Chris@27
|
43 // if (debug_on) cerr << "--> s = " << s << '\n';
|
Chris@27
|
44 }
|
Chris@27
|
45 // cerr << n - lenKernel/2 << endl;
|
Chris@27
|
46 Z[n -lenKernel/2] = s;
|
Chris@27
|
47 }
|
Chris@27
|
48
|
Chris@27
|
49 // fill upper and lower pads
|
Chris@27
|
50 for (n = 0; n < lenKernel/2; n++) Z[n] = Z[lenKernel/2];
|
Chris@27
|
51 for (n = lenConvolvee; n < lenConvolvee +lenKernel/2; n++) Z[n - lenKernel/2] =
|
Chris@27
|
52 Z[lenConvolvee - lenKernel/2 - 1];
|
Chris@27
|
53 return Z;
|
Chris@27
|
54 }
|
Chris@27
|
55
|
Chris@27
|
56 // vector<float> FftBin2Frequency(vector<float> binnumbers, int fs, int blocksize)
|
Chris@27
|
57 // {
|
Chris@27
|
58 // vector<float> freq(binnumbers.size, 0.0);
|
Chris@27
|
59 // for (unsigned i = 0; i < binnumbers.size; ++i) {
|
Chris@27
|
60 // freq[i] = (binnumbers[i]-1.0) * fs * 1.0 / blocksize;
|
Chris@27
|
61 // }
|
Chris@27
|
62 // return freq;
|
Chris@27
|
63 // }
|
Chris@27
|
64
|
Chris@27
|
65 float cospuls(float x, float centre, float width)
|
Chris@27
|
66 {
|
Chris@27
|
67 float recipwidth = 1.0/width;
|
Chris@27
|
68 if (abs(x - centre) <= 0.5 * width) {
|
Chris@27
|
69 return cos((x-centre)*2*M_PI*recipwidth)*.5+.5;
|
Chris@27
|
70 }
|
Chris@27
|
71 return 0.0;
|
Chris@27
|
72 }
|
Chris@27
|
73
|
Chris@27
|
74 float pitchCospuls(float x, float centre, int binsperoctave)
|
Chris@27
|
75 {
|
Chris@27
|
76 float warpedf = -binsperoctave * (log2(centre) - log2(x));
|
Chris@27
|
77 float out = cospuls(warpedf, 0.0, 2.0);
|
Chris@27
|
78 // now scale to correct for note density
|
Chris@27
|
79 float c = log(2.0)/binsperoctave;
|
Chris@27
|
80 if (x > 0) {
|
Chris@27
|
81 out = out / (c * x);
|
Chris@27
|
82 } else {
|
Chris@27
|
83 out = 0;
|
Chris@27
|
84 }
|
Chris@27
|
85 return out;
|
Chris@27
|
86 }
|
Chris@27
|
87
|
Chris@27
|
88 bool logFreqMatrix(int fs, int blocksize, float *outmatrix) {
|
Chris@27
|
89
|
Chris@27
|
90 int binspersemitone = 3; // this must be 3
|
Chris@27
|
91 int minoctave = 0; // this must be 0
|
Chris@27
|
92 int maxoctave = 7; // this must be 7
|
Chris@27
|
93 int oversampling = 80;
|
Chris@27
|
94
|
Chris@27
|
95 // linear frequency vector
|
Chris@27
|
96 vector<float> fft_f;
|
Chris@27
|
97 for (int i = 0; i < blocksize/2; ++i) {
|
Chris@27
|
98 fft_f.push_back(i * (fs * 1.0 / blocksize));
|
Chris@27
|
99 }
|
Chris@27
|
100 float fft_width = fs * 2.0 / blocksize;
|
Chris@27
|
101
|
Chris@27
|
102 // linear oversampled frequency vector
|
Chris@27
|
103 vector<float> oversampled_f;
|
Chris@27
|
104 for (unsigned int i = 0; i < oversampling * blocksize/2; ++i) {
|
Chris@27
|
105 oversampled_f.push_back(i * ((fs * 1.0 / blocksize) / oversampling));
|
Chris@27
|
106 }
|
Chris@27
|
107
|
Chris@27
|
108 // pitch-spaced frequency vector
|
Chris@27
|
109 int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone!
|
Chris@27
|
110 int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone!
|
Chris@27
|
111 vector<float> cq_f;
|
Chris@27
|
112 float oob = 1.0/binspersemitone; // one over binspersemitone
|
Chris@27
|
113 cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-69))); // 0.083333 is approx 1/12
|
Chris@27
|
114 cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI+oob-69)));
|
Chris@27
|
115 for (int i = minMIDI + 1; i < maxMIDI; ++i) {
|
Chris@27
|
116 for (int k = -1; k < 2; ++k) {
|
Chris@27
|
117 cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69)));
|
Chris@27
|
118 }
|
Chris@27
|
119 }
|
Chris@27
|
120 cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-oob-69)));
|
Chris@27
|
121 cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69)));
|
Chris@27
|
122
|
Chris@27
|
123 int nFFT = fft_f.size();
|
Chris@27
|
124
|
Chris@27
|
125 vector<float> fft_activation;
|
Chris@27
|
126 for (int iOS = 0; iOS < 2 * oversampling; ++iOS) {
|
Chris@27
|
127 float cosp = cospuls(oversampled_f[iOS],fft_f[1],fft_width);
|
Chris@27
|
128 fft_activation.push_back(cosp);
|
Chris@27
|
129 // cerr << cosp << endl;
|
Chris@27
|
130 }
|
Chris@27
|
131
|
Chris@27
|
132 float cq_activation;
|
Chris@27
|
133 for (int iFFT = 1; iFFT < nFFT; ++iFFT) {
|
Chris@27
|
134 // 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
|
135 int curr_start = oversampling * iFFT - oversampling;
|
Chris@27
|
136 int curr_end = oversampling * iFFT + oversampling; // don't know if I should add "+1" here
|
Chris@27
|
137 // cerr << oversampled_f[curr_start] << " " << fft_f[iFFT] << " " << oversampled_f[curr_end] << endl;
|
Chris@27
|
138 for (unsigned iCQ = 0; iCQ < cq_f.size(); ++iCQ) {
|
Chris@27
|
139 outmatrix[iFFT + nFFT * iCQ] = 0;
|
Chris@27
|
140 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
|
141 for (int iOS = curr_start; iOS < curr_end; ++iOS) {
|
Chris@27
|
142 cq_activation = pitchCospuls(oversampled_f[iOS],cq_f[iCQ],binspersemitone*12);
|
Chris@27
|
143 // cerr << oversampled_f[iOS] << " " << cq_f[iCQ] << " " << cq_activation << endl;
|
Chris@27
|
144 outmatrix[iFFT + nFFT * iCQ] += cq_activation * fft_activation[iOS-curr_start];
|
Chris@27
|
145 }
|
Chris@27
|
146 // if (iCQ == 1 || iCQ == 2) {
|
Chris@27
|
147 // cerr << " " << outmatrix[iFFT + nFFT * iCQ] << endl;
|
Chris@27
|
148 // }
|
Chris@27
|
149 }
|
Chris@27
|
150 }
|
Chris@27
|
151 }
|
Chris@27
|
152 return true;
|
Chris@27
|
153 }
|
Chris@27
|
154
|
Chris@27
|
155 void dictionaryMatrix(float* dm) {
|
Chris@27
|
156 int binspersemitone = 3; // this must be 3
|
Chris@27
|
157 int minoctave = 0; // this must be 0
|
Chris@27
|
158 int maxoctave = 7; // this must be 7
|
Chris@27
|
159 float s_param = 0.7;
|
Chris@27
|
160
|
Chris@27
|
161 // pitch-spaced frequency vector
|
Chris@27
|
162 int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone!
|
Chris@27
|
163 int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone!
|
Chris@27
|
164 vector<float> cq_f;
|
Chris@27
|
165 float oob = 1.0/binspersemitone; // one over binspersemitone
|
Chris@27
|
166 cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-69))); // 0.083333 is approx 1/12
|
Chris@27
|
167 cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI+oob-69)));
|
Chris@27
|
168 for (int i = minMIDI + 1; i < maxMIDI; ++i) {
|
Chris@27
|
169 for (int k = -1; k < 2; ++k) {
|
Chris@27
|
170 cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69)));
|
Chris@27
|
171 }
|
Chris@27
|
172 }
|
Chris@27
|
173 cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-oob-69)));
|
Chris@27
|
174 cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69)));
|
Chris@27
|
175
|
Chris@27
|
176 float curr_f;
|
Chris@27
|
177 float floatbin;
|
Chris@27
|
178 float curr_amp;
|
Chris@27
|
179 // now for every combination calculate the matrix element
|
Chris@27
|
180 for (unsigned iOut = 0; iOut < 12 * (maxoctave - minoctave); ++iOut) {
|
Chris@27
|
181 // cerr << iOut << endl;
|
Chris@27
|
182 for (unsigned iHarm = 1; iHarm <= 20; ++iHarm) {
|
Chris@27
|
183 curr_f = 440 * pow(2,(minMIDI-69+iOut)*1.0/12) * iHarm;
|
Chris@27
|
184 // if (curr_f > cq_f[nNote-1]) break;
|
Chris@27
|
185 floatbin = ((iOut + 1) * binspersemitone + 1) + binspersemitone * 12 * log2(iHarm);
|
Chris@27
|
186 // cerr << floatbin << endl;
|
Chris@27
|
187 curr_amp = pow(s_param,float(iHarm-1));
|
Chris@27
|
188 // cerr << "curramp" << curr_amp << endl;
|
Chris@27
|
189 for (unsigned iNote = 0; iNote < nNote; ++iNote) {
|
Chris@27
|
190 if (abs(iNote+1.0-floatbin)<2) {
|
Chris@27
|
191 dm[iNote + 256 * iOut] += cospuls(iNote+1.0, floatbin, binspersemitone + 0.0) * curr_amp;
|
Chris@27
|
192 // dm[iNote + nNote * iOut] += 1 * curr_amp;
|
Chris@27
|
193 }
|
Chris@27
|
194 }
|
Chris@27
|
195 }
|
Chris@27
|
196 }
|
Chris@27
|
197
|
Chris@27
|
198
|
Chris@27
|
199 }
|
Chris@27
|
200
|
Chris@27
|
201 string get_env_var( std::string const & key ) {
|
Chris@27
|
202 char * val;
|
Chris@27
|
203 val = getenv( key.c_str() );
|
Chris@27
|
204 string retval;
|
Chris@27
|
205 if (val != NULL) {
|
Chris@27
|
206 retval = val;
|
Chris@27
|
207 }
|
Chris@27
|
208 return retval;
|
Chris@27
|
209 }
|
Chris@27
|
210
|
Chris@27
|
211
|
Chris@27
|
212 vector<string> chordDictionary(vector<float> *mchorddict) {
|
Chris@27
|
213 // ifstream chordDictFile;
|
Chris@27
|
214 string chordDictFilename(get_env_var("VAMP_PATH")+"/chord.dict");
|
Chris@27
|
215 // string instring[] = ",1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0\nm,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0\n6,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0\n7,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0\nmaj7,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1\nmin7,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0\n,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0\n,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0\ndim,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0\naug,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0\n";
|
Chris@27
|
216 typedef tokenizer<char_separator<char> > Tok;
|
Chris@27
|
217 // char_separator<char> sep; // default constructed
|
Chris@27
|
218 char_separator<char> sep(",; ","=");
|
Chris@27
|
219 iostreams::stream<iostreams::file_source> chordDictFile(chordDictFilename.c_str());
|
Chris@27
|
220 string line;
|
Chris@27
|
221 int iElement = 0;
|
Chris@27
|
222 int nChord = 0;
|
Chris@27
|
223
|
Chris@27
|
224 vector<string> loadedChordNames;
|
Chris@27
|
225 vector<float> loadedChordDict;
|
Chris@27
|
226 if (chordDictFile.is_open()) {
|
Chris@27
|
227 while (std::getline(chordDictFile, line)) { // loop over lines in chord.dict file
|
Chris@27
|
228 // first, get the chord definition
|
Chris@27
|
229 string chordType;
|
Chris@27
|
230 vector<float> tempPCVector;
|
Chris@27
|
231 // cerr << line << endl;
|
Chris@27
|
232 if (!line.empty() && line.substr(0,1) != "#") {
|
Chris@27
|
233 Tok tok(line, sep);
|
Chris@27
|
234 for(Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) { // loop over line elements
|
Chris@27
|
235 string tempString = *tok_iter;
|
Chris@27
|
236 // cerr << tempString << endl;
|
Chris@27
|
237 if (tok_iter == tok.begin()) { // either the chord name or a colon
|
Chris@27
|
238 if (tempString == "=") {
|
Chris@27
|
239 chordType = "";
|
Chris@27
|
240 } else {
|
Chris@27
|
241 chordType = tempString;
|
Chris@27
|
242 tok_iter++; // is this cheating ? :)
|
Chris@27
|
243 }
|
Chris@27
|
244 } else {
|
Chris@27
|
245 tempPCVector.push_back(lexical_cast<float>(*tok_iter));
|
Chris@27
|
246 }
|
Chris@27
|
247 }
|
Chris@27
|
248
|
Chris@27
|
249 // now make all 12 chords of every type
|
Chris@27
|
250 for (unsigned iSemitone = 0; iSemitone < 12; iSemitone++) {
|
Chris@27
|
251 // add bass slash notation
|
Chris@27
|
252 string slashNotation = "";
|
Chris@27
|
253 for (unsigned kSemitone = 1; kSemitone < 12; kSemitone++) {
|
Chris@27
|
254 if (tempPCVector[(kSemitone) % 12] > 0.99) {
|
Chris@27
|
255 slashNotation = bassnames[iSemitone][kSemitone];
|
Chris@27
|
256 }
|
Chris@27
|
257 }
|
Chris@27
|
258 for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) { // bass pitch classes
|
Chris@27
|
259 // cerr << ((kSemitone - iSemitone + 12) % 12) << endl;
|
Chris@27
|
260 float bassValue = 0;
|
Chris@27
|
261 if (tempPCVector[(kSemitone - iSemitone + 12) % 12]==1) {
|
Chris@27
|
262 bassValue = 1;
|
Chris@27
|
263 } else {
|
Chris@27
|
264 if (tempPCVector[((kSemitone - iSemitone + 12) % 12) + 12] == 1) bassValue = 0.5;
|
Chris@27
|
265 }
|
Chris@27
|
266 loadedChordDict.push_back(bassValue);
|
Chris@27
|
267 }
|
Chris@27
|
268 for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) { // chord pitch classes
|
Chris@27
|
269 loadedChordDict.push_back(tempPCVector[((kSemitone - iSemitone + 12) % 12) + 12]);
|
Chris@27
|
270 }
|
Chris@27
|
271 ostringstream os;
|
Chris@27
|
272 if (slashNotation.empty()) {
|
Chris@27
|
273 os << notenames[12+iSemitone] << chordType;
|
Chris@27
|
274 } else {
|
Chris@27
|
275 os << notenames[12+iSemitone] << chordType << "/" << slashNotation;
|
Chris@27
|
276 }
|
Chris@27
|
277 // cerr << os.str() << endl;
|
Chris@27
|
278 loadedChordNames.push_back(os.str());
|
Chris@27
|
279 }
|
Chris@27
|
280 }
|
Chris@27
|
281 }
|
Chris@27
|
282 // N type
|
Chris@27
|
283 loadedChordNames.push_back("N");
|
Chris@27
|
284 for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(0.5);
|
Chris@27
|
285 for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(1.0);
|
Chris@27
|
286
|
Chris@27
|
287 // normalise
|
Chris@27
|
288 float sum = 0;
|
Chris@27
|
289 for (int i = 0; i < loadedChordDict.size(); i++) {
|
Chris@27
|
290 sum += pow(loadedChordDict[i],2);
|
Chris@27
|
291 if (i % 24 == 23) {
|
Chris@27
|
292 float invertedsum = 1.0/sqrt(sum);
|
Chris@27
|
293 for (int k = 0; k < 24; k++) {
|
Chris@27
|
294 loadedChordDict[i-k] *= invertedsum;
|
Chris@27
|
295 }
|
Chris@27
|
296 sum = 0;
|
Chris@27
|
297 }
|
Chris@27
|
298
|
Chris@27
|
299 }
|
Chris@27
|
300
|
Chris@27
|
301
|
Chris@27
|
302 nChord = 0;
|
Chris@27
|
303 for (int i = 0; i < loadedChordNames.size(); i++) {
|
Chris@27
|
304 nChord++;
|
Chris@27
|
305 }
|
Chris@27
|
306 chordDictFile.close();
|
Chris@27
|
307
|
Chris@27
|
308
|
Chris@27
|
309 // mchorddict = new float[nChord*24];
|
Chris@27
|
310 for (int i = 0; i < nChord*24; i++) {
|
Chris@27
|
311 mchorddict->push_back(loadedChordDict[i]);
|
Chris@27
|
312 }
|
Chris@27
|
313
|
Chris@27
|
314 } else {// use default from chorddict.cpp
|
Chris@27
|
315 // mchorddict = new float[nChorddict];
|
Chris@27
|
316 for (int i = 0; i < nChorddict; i++) {
|
Chris@27
|
317 mchorddict->push_back(chorddict[i]);
|
Chris@27
|
318 }
|
Chris@27
|
319
|
Chris@27
|
320 nChord = nChorddict/24;
|
Chris@27
|
321 // mchordnames = new string[nChorddict/24];
|
Chris@27
|
322 char buffer1 [50];
|
Chris@27
|
323 for (int i = 0; i < nChorddict/24; i++) {
|
Chris@27
|
324 if (i < nChorddict/24 - 1) {
|
Chris@27
|
325 sprintf(buffer1, "%s%s", notenames[i % 12 + 12], chordtypes[i]);
|
Chris@27
|
326 } else {
|
Chris@27
|
327 sprintf(buffer1, "N");
|
Chris@27
|
328 }
|
Chris@27
|
329 ostringstream os;
|
Chris@27
|
330 os << buffer1;
|
Chris@27
|
331 loadedChordNames.push_back(os.str());
|
Chris@27
|
332
|
Chris@27
|
333 }
|
Chris@27
|
334
|
Chris@27
|
335 }
|
Chris@27
|
336 // cerr << "before leaving" << chordnames[1] << endl;
|
Chris@27
|
337 return loadedChordNames;
|
Chris@27
|
338 }
|