annotate chromamethods.cpp @ 184:82d5d11b68d7 tip

Update library URI so it's not document-local
author Chris Cannam
date Wed, 22 Apr 2020 14:21:25 +0100
parents 59b26f52550d
children
rev   line source
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