annotate chromamethods.cpp @ 30:608b0c8ad3f8 matthiasm-plugin

* Use proper Vamp path to look up chord.dict
author Chris Cannam
date Thu, 21 Oct 2010 21:48:04 +0100
parents 690bd9148467
children cf8898a0174c
rev   line source
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@30 211 static
Chris@30 212 std::vector<std::string>
Chris@30 213 getPluginPath()
Chris@30 214 {
Chris@30 215 //!!! This is duplicated from PluginHostAdapter::getPluginPath,
Chris@30 216 //!!! which is not available to us in the plugin (only to the
Chris@30 217 //!!! host)
Chris@30 218
Chris@30 219 std::vector<std::string> path;
Chris@30 220 std::string envPath;
Chris@30 221
Chris@30 222 char *cpath = getenv("VAMP_PATH");
Chris@30 223 if (cpath) envPath = cpath;
Chris@30 224
Chris@30 225 #ifdef _WIN32
Chris@30 226 #define PATH_SEPARATOR ';'
Chris@30 227 #define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins"
Chris@30 228 #else
Chris@30 229 #define PATH_SEPARATOR ':'
Chris@30 230 #ifdef __APPLE__
Chris@30 231 #define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp"
Chris@30 232 #else
Chris@30 233 #define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp"
Chris@30 234 #endif
Chris@30 235 #endif
Chris@30 236
Chris@30 237 if (envPath == "") {
Chris@30 238 envPath = DEFAULT_VAMP_PATH;
Chris@30 239 char *chome = getenv("HOME");
Chris@30 240 if (chome) {
Chris@30 241 std::string home(chome);
Chris@30 242 std::string::size_type f;
Chris@30 243 while ((f = envPath.find("$HOME")) != std::string::npos &&
Chris@30 244 f < envPath.length()) {
Chris@30 245 envPath.replace(f, 5, home);
Chris@30 246 }
Chris@30 247 }
Chris@30 248 #ifdef _WIN32
Chris@30 249 char *cpfiles = getenv("ProgramFiles");
Chris@30 250 if (!cpfiles) cpfiles = (char *)"C:\\Program Files";
Chris@30 251 std::string pfiles(cpfiles);
Chris@30 252 std::string::size_type f;
Chris@30 253 while ((f = envPath.find("%ProgramFiles%")) != std::string::npos &&
Chris@30 254 f < envPath.length()) {
Chris@30 255 envPath.replace(f, 14, pfiles);
Chris@30 256 }
Chris@30 257 #endif
Chris@30 258 }
Chris@30 259
Chris@30 260 std::string::size_type index = 0, newindex = 0;
Chris@30 261
Chris@30 262 while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
Chris@30 263 path.push_back(envPath.substr(index, newindex - index));
Chris@30 264 index = newindex + 1;
Chris@30 265 }
Chris@30 266
Chris@30 267 path.push_back(envPath.substr(index));
Chris@30 268
Chris@30 269 return path;
Chris@30 270 }
Chris@27 271
Chris@27 272 vector<string> chordDictionary(vector<float> *mchorddict) {
Chris@30 273
Chris@27 274 typedef tokenizer<char_separator<char> > Tok;
Chris@27 275 char_separator<char> sep(",; ","=");
Chris@30 276
Chris@30 277 string chordDictBase("chord.dict");
Chris@30 278 string chordDictFilename;
Chris@30 279
Chris@30 280 vector<string> ppath = getPluginPath();
Chris@30 281 for (int i = 0; i < ppath.size(); ++i) {
Chris@30 282 chordDictFilename = ppath[i] + "/" + chordDictBase;
Chris@30 283 cerr << "Looking for chord.dict in " << chordDictFilename << "..." << endl;
Chris@30 284 if (iostreams::stream<iostreams::file_source>(chordDictFilename.c_str())
Chris@30 285 .is_open()) {
Chris@30 286 cerr << "(Success)" << endl;
Chris@30 287 break;
Chris@30 288 }
Chris@30 289 }
Chris@30 290
Chris@30 291 iostreams::stream<iostreams::file_source> chordDictFile(chordDictFilename);
Chris@27 292 string line;
Chris@27 293 int iElement = 0;
Chris@27 294 int nChord = 0;
Chris@27 295
Chris@27 296 vector<string> loadedChordNames;
Chris@27 297 vector<float> loadedChordDict;
Chris@27 298 if (chordDictFile.is_open()) {
Chris@27 299 while (std::getline(chordDictFile, line)) { // loop over lines in chord.dict file
Chris@27 300 // first, get the chord definition
Chris@27 301 string chordType;
Chris@27 302 vector<float> tempPCVector;
Chris@27 303 // cerr << line << endl;
Chris@27 304 if (!line.empty() && line.substr(0,1) != "#") {
Chris@27 305 Tok tok(line, sep);
Chris@27 306 for(Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) { // loop over line elements
Chris@27 307 string tempString = *tok_iter;
Chris@27 308 // cerr << tempString << endl;
Chris@27 309 if (tok_iter == tok.begin()) { // either the chord name or a colon
Chris@27 310 if (tempString == "=") {
Chris@27 311 chordType = "";
Chris@27 312 } else {
Chris@27 313 chordType = tempString;
Chris@27 314 tok_iter++; // is this cheating ? :)
Chris@27 315 }
Chris@27 316 } else {
Chris@27 317 tempPCVector.push_back(lexical_cast<float>(*tok_iter));
Chris@27 318 }
Chris@27 319 }
Chris@27 320
Chris@27 321 // now make all 12 chords of every type
Chris@27 322 for (unsigned iSemitone = 0; iSemitone < 12; iSemitone++) {
Chris@27 323 // add bass slash notation
Chris@27 324 string slashNotation = "";
Chris@27 325 for (unsigned kSemitone = 1; kSemitone < 12; kSemitone++) {
Chris@27 326 if (tempPCVector[(kSemitone) % 12] > 0.99) {
Chris@27 327 slashNotation = bassnames[iSemitone][kSemitone];
Chris@27 328 }
Chris@27 329 }
Chris@27 330 for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) { // bass pitch classes
Chris@27 331 // cerr << ((kSemitone - iSemitone + 12) % 12) << endl;
Chris@27 332 float bassValue = 0;
Chris@27 333 if (tempPCVector[(kSemitone - iSemitone + 12) % 12]==1) {
Chris@27 334 bassValue = 1;
Chris@27 335 } else {
Chris@27 336 if (tempPCVector[((kSemitone - iSemitone + 12) % 12) + 12] == 1) bassValue = 0.5;
Chris@27 337 }
Chris@27 338 loadedChordDict.push_back(bassValue);
Chris@27 339 }
Chris@27 340 for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) { // chord pitch classes
Chris@27 341 loadedChordDict.push_back(tempPCVector[((kSemitone - iSemitone + 12) % 12) + 12]);
Chris@27 342 }
Chris@27 343 ostringstream os;
Chris@27 344 if (slashNotation.empty()) {
Chris@27 345 os << notenames[12+iSemitone] << chordType;
Chris@27 346 } else {
Chris@27 347 os << notenames[12+iSemitone] << chordType << "/" << slashNotation;
Chris@27 348 }
Chris@27 349 // cerr << os.str() << endl;
Chris@27 350 loadedChordNames.push_back(os.str());
Chris@27 351 }
Chris@27 352 }
Chris@27 353 }
Chris@27 354 // N type
Chris@27 355 loadedChordNames.push_back("N");
Chris@27 356 for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(0.5);
Chris@27 357 for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(1.0);
Chris@27 358
Chris@27 359 // normalise
Chris@27 360 float sum = 0;
Chris@27 361 for (int i = 0; i < loadedChordDict.size(); i++) {
Chris@27 362 sum += pow(loadedChordDict[i],2);
Chris@27 363 if (i % 24 == 23) {
Chris@27 364 float invertedsum = 1.0/sqrt(sum);
Chris@27 365 for (int k = 0; k < 24; k++) {
Chris@27 366 loadedChordDict[i-k] *= invertedsum;
Chris@27 367 }
Chris@27 368 sum = 0;
Chris@27 369 }
Chris@27 370
Chris@27 371 }
Chris@27 372
Chris@27 373
Chris@27 374 nChord = 0;
Chris@27 375 for (int i = 0; i < loadedChordNames.size(); i++) {
Chris@27 376 nChord++;
Chris@27 377 }
Chris@27 378 chordDictFile.close();
Chris@27 379
Chris@27 380
Chris@27 381 // mchorddict = new float[nChord*24];
Chris@27 382 for (int i = 0; i < nChord*24; i++) {
Chris@27 383 mchorddict->push_back(loadedChordDict[i]);
Chris@27 384 }
Chris@27 385
Chris@27 386 } else {// use default from chorddict.cpp
Chris@27 387 // mchorddict = new float[nChorddict];
Chris@27 388 for (int i = 0; i < nChorddict; i++) {
Chris@27 389 mchorddict->push_back(chorddict[i]);
Chris@27 390 }
Chris@27 391
Chris@27 392 nChord = nChorddict/24;
Chris@27 393 // mchordnames = new string[nChorddict/24];
Chris@27 394 char buffer1 [50];
Chris@27 395 for (int i = 0; i < nChorddict/24; i++) {
Chris@27 396 if (i < nChorddict/24 - 1) {
Chris@27 397 sprintf(buffer1, "%s%s", notenames[i % 12 + 12], chordtypes[i]);
Chris@27 398 } else {
Chris@27 399 sprintf(buffer1, "N");
Chris@27 400 }
Chris@27 401 ostringstream os;
Chris@27 402 os << buffer1;
Chris@27 403 loadedChordNames.push_back(os.str());
Chris@27 404
Chris@27 405 }
Chris@27 406
Chris@27 407 }
Chris@27 408 // cerr << "before leaving" << chordnames[1] << endl;
Chris@27 409 return loadedChordNames;
Chris@27 410 }