Chris@35: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@35: Chris@35: /* Chris@35: NNLS-Chroma / Chordino Chris@35: Chris@35: Audio feature extraction plugins for chromagram and chord Chris@35: estimation. Chris@35: Chris@35: Centre for Digital Music, Queen Mary University of London. Chris@35: This file copyright 2008-2010 Matthias Mauch and QMUL. Chris@35: Chris@35: This program is free software; you can redistribute it and/or Chris@35: modify it under the terms of the GNU General Public License as Chris@35: published by the Free Software Foundation; either version 2 of the Chris@35: License, or (at your option) any later version. See the file Chris@35: COPYING included with this distribution for more information. Chris@35: */ Chris@35: Chris@27: #include "chromamethods.h" Chris@27: Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: #include Chris@27: Chris@27: using namespace std; Chris@27: using namespace boost; Chris@27: Chris@27: Chris@27: /** Special Convolution mail@115: Special convolution is as long as the convolvee, i.e. the first argument. mail@115: In the "valid" core part of the convolution it contains the usual convolution mail@115: values, but the parts at the beginning (ending) that would normally be mail@115: calculated using zero padding simply have the same values as the first mail@115: (last) valid convolution bin. Chris@27: **/ Chris@27: Chris@27: vector SpecialConvolution(vector convolvee, vector kernel) Chris@27: { Chris@27: float s; Chris@27: int m, n; Chris@27: int lenConvolvee = convolvee.size(); Chris@27: int lenKernel = kernel.size(); Chris@27: mail@77: vector Z(nNote,0); Chris@27: assert(lenKernel % 2 != 0); // no exception handling !!! Chris@27: Chris@27: for (n = lenKernel - 1; n < lenConvolvee; n++) { Chris@27: s=0.0; Chris@27: for (m = 0; m < lenKernel; m++) { Chris@27: // cerr << "m = " << m << ", n = " << n << ", n-m = " << (n-m) << '\n'; Chris@27: s += convolvee[n-m] * kernel[m]; Chris@27: // if (debug_on) cerr << "--> s = " << s << '\n'; Chris@27: } Chris@27: // cerr << n - lenKernel/2 << endl; Chris@27: Z[n -lenKernel/2] = s; Chris@27: } Chris@27: Chris@27: // fill upper and lower pads Chris@27: for (n = 0; n < lenKernel/2; n++) Z[n] = Z[lenKernel/2]; Chris@27: for (n = lenConvolvee; n < lenConvolvee +lenKernel/2; n++) Z[n - lenKernel/2] = Chris@27: Z[lenConvolvee - lenKernel/2 - 1]; Chris@27: return Z; Chris@27: } Chris@27: Chris@27: float cospuls(float x, float centre, float width) Chris@27: { Chris@27: float recipwidth = 1.0/width; Chris@27: if (abs(x - centre) <= 0.5 * width) { Chris@27: return cos((x-centre)*2*M_PI*recipwidth)*.5+.5; Chris@27: } Chris@27: return 0.0; Chris@27: } Chris@27: Chris@27: float pitchCospuls(float x, float centre, int binsperoctave) Chris@27: { Chris@27: float warpedf = -binsperoctave * (log2(centre) - log2(x)); Chris@27: float out = cospuls(warpedf, 0.0, 2.0); Chris@27: // now scale to correct for note density Chris@27: float c = log(2.0)/binsperoctave; Chris@27: if (x > 0) { Chris@27: out = out / (c * x); Chris@27: } else { Chris@27: out = 0; Chris@27: } Chris@27: return out; Chris@27: } Chris@27: mail@115: /** mail@115: * Calculates a matrix that can be used to linearly map from the magnitude spectrum to a pitch-scale spectrum. mail@115: * @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: */ Chris@27: bool logFreqMatrix(int fs, int blocksize, float *outmatrix) { mail@115: // TODO: rewrite so that everyone understands what is done here. mail@115: // TODO: make this more general, such that it works with all minoctave, maxoctave and whatever nBPS (or check if it already does) Chris@27: mail@80: int binspersemitone = nBPS; Chris@27: int minoctave = 0; // this must be 0 Chris@27: int maxoctave = 7; // this must be 7 Chris@27: int oversampling = 80; Chris@27: Chris@27: // linear frequency vector Chris@27: vector fft_f; Chris@27: for (int i = 0; i < blocksize/2; ++i) { Chris@27: fft_f.push_back(i * (fs * 1.0 / blocksize)); Chris@27: } Chris@27: float fft_width = fs * 2.0 / blocksize; Chris@27: Chris@27: // linear oversampled frequency vector Chris@27: vector oversampled_f; Chris@91: for (int i = 0; i < oversampling * blocksize/2; ++i) { Chris@27: oversampled_f.push_back(i * ((fs * 1.0 / blocksize) / oversampling)); Chris@27: } Chris@27: Chris@27: // pitch-spaced frequency vector Chris@27: int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone! Chris@27: int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone! Chris@27: vector cq_f; Chris@27: float oob = 1.0/binspersemitone; // one over binspersemitone mail@80: for (int i = minMIDI; i < maxMIDI; ++i) { mail@80: for (int k = 0; k < binspersemitone; ++k) { Chris@27: cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69))); Chris@27: } Chris@27: } mail@80: // cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-oob-69))); Chris@27: cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69))); Chris@27: Chris@27: int nFFT = fft_f.size(); Chris@27: Chris@27: vector fft_activation; Chris@27: for (int iOS = 0; iOS < 2 * oversampling; ++iOS) { Chris@27: float cosp = cospuls(oversampled_f[iOS],fft_f[1],fft_width); Chris@27: fft_activation.push_back(cosp); Chris@27: // cerr << cosp << endl; Chris@27: } Chris@135: Chris@146: for (int i = 0; i < nFFT * (int)cq_f.size(); ++i) { Chris@135: outmatrix[i] = 0.f; Chris@135: } Chris@27: Chris@27: float cq_activation; Chris@27: for (int iFFT = 1; iFFT < nFFT; ++iFFT) { Chris@27: // 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: int curr_start = oversampling * iFFT - oversampling; Chris@27: int curr_end = oversampling * iFFT + oversampling; // don't know if I should add "+1" here Chris@27: // cerr << oversampled_f[curr_start] << " " << fft_f[iFFT] << " " << oversampled_f[curr_end] << endl; matthiasm@122: for (int iCQ = 0; iCQ < (int)cq_f.size(); ++iCQ) { Chris@27: 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: for (int iOS = curr_start; iOS < curr_end; ++iOS) { Chris@27: cq_activation = pitchCospuls(oversampled_f[iOS],cq_f[iCQ],binspersemitone*12); Chris@27: // cerr << oversampled_f[iOS] << " " << cq_f[iCQ] << " " << cq_activation << endl; Chris@27: outmatrix[iFFT + nFFT * iCQ] += cq_activation * fft_activation[iOS-curr_start]; Chris@27: } mail@115: } Chris@27: } Chris@27: } Chris@27: return true; Chris@27: } Chris@27: mail@41: void dictionaryMatrix(float* dm, float s_param) { mail@115: // TODO: make this more general, such that it works with all minoctave, maxoctave and even more than one note per semitone mail@80: int binspersemitone = nBPS; Chris@27: int minoctave = 0; // this must be 0 Chris@27: int maxoctave = 7; // this must be 7 Chris@27: Chris@27: // pitch-spaced frequency vector Chris@27: int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone! Chris@27: int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone! Chris@27: vector cq_f; Chris@27: float oob = 1.0/binspersemitone; // one over binspersemitone mail@80: for (int i = minMIDI; i < maxMIDI; ++i) { mail@80: for (int k = 0; k < binspersemitone; ++k) { Chris@27: cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69))); Chris@27: } Chris@27: } Chris@27: cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69))); Chris@27: Chris@146: // float curr_f; Chris@27: float floatbin; Chris@27: float curr_amp; Chris@27: // now for every combination calculate the matrix element Chris@91: for (int iOut = 0; iOut < 12 * (maxoctave - minoctave); ++iOut) { Chris@27: // cerr << iOut << endl; Chris@91: for (int iHarm = 1; iHarm <= 20; ++iHarm) { Chris@146: // curr_f = 440 * pow(2,(minMIDI-69+iOut)*1.0/12) * iHarm; Chris@27: // if (curr_f > cq_f[nNote-1]) break; Chris@27: floatbin = ((iOut + 1) * binspersemitone + 1) + binspersemitone * 12 * log2(iHarm); Chris@27: // cerr << floatbin << endl; Chris@27: curr_amp = pow(s_param,float(iHarm-1)); Chris@27: // cerr << "curramp" << curr_amp << endl; Chris@91: for (int iNote = 0; iNote < nNote; ++iNote) { Chris@27: if (abs(iNote+1.0-floatbin)<2) { mail@77: dm[iNote + nNote * iOut] += cospuls(iNote+1.0, floatbin, binspersemitone + 0.0) * curr_amp; Chris@27: // dm[iNote + nNote * iOut] += 1 * curr_amp; Chris@27: } Chris@27: } Chris@27: } Chris@27: } Chris@27: } Chris@27: Chris@30: static Chris@30: std::vector Chris@30: getPluginPath() Chris@30: { Chris@30: //!!! This is duplicated from PluginHostAdapter::getPluginPath, Chris@30: //!!! which is not available to us in the plugin (only to the Chris@30: //!!! host) Chris@30: Chris@30: std::vector path; Chris@30: std::string envPath; Chris@30: Chris@30: char *cpath = getenv("VAMP_PATH"); Chris@30: if (cpath) envPath = cpath; Chris@30: Chris@30: #ifdef _WIN32 Chris@30: #define PATH_SEPARATOR ';' Chris@30: #define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins" Chris@30: #else Chris@30: #define PATH_SEPARATOR ':' Chris@30: #ifdef __APPLE__ Chris@30: #define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp" Chris@30: #else Chris@30: #define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp" Chris@30: #endif Chris@30: #endif Chris@30: Chris@30: if (envPath == "") { Chris@30: envPath = DEFAULT_VAMP_PATH; Chris@30: char *chome = getenv("HOME"); Chris@30: if (chome) { Chris@30: std::string home(chome); Chris@30: std::string::size_type f; Chris@30: while ((f = envPath.find("$HOME")) != std::string::npos && Chris@30: f < envPath.length()) { Chris@30: envPath.replace(f, 5, home); Chris@30: } Chris@30: } Chris@30: #ifdef _WIN32 Chris@30: char *cpfiles = getenv("ProgramFiles"); Chris@30: if (!cpfiles) cpfiles = (char *)"C:\\Program Files"; Chris@30: std::string pfiles(cpfiles); Chris@30: std::string::size_type f; Chris@30: while ((f = envPath.find("%ProgramFiles%")) != std::string::npos && Chris@30: f < envPath.length()) { Chris@30: envPath.replace(f, 14, pfiles); Chris@30: } Chris@30: #endif Chris@30: } Chris@30: Chris@30: std::string::size_type index = 0, newindex = 0; Chris@30: Chris@30: while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) { Chris@30: path.push_back(envPath.substr(index, newindex - index)); Chris@30: index = newindex + 1; Chris@30: } Chris@30: Chris@30: path.push_back(envPath.substr(index)); Chris@30: Chris@30: return path; Chris@30: } Chris@27: mail@90: mail@90: mail@90: static vector staticChordnames() { mail@90: vector chordnames; mail@90: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: // from here: Harte syntax mail@112: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: return chordnames; mail@90: } mail@90: mail@90: static vector staticChordvalues() { mail@90: vector chordvalues; mail@90: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: for (int iChord = 0; iChord < 18; ++iChord) { mail@90: for (int iNote = 0; iNote < 24; iNote++) { mail@90: chordvalues.push_back(chordvaluearray[24*iChord+iNote]); mail@90: } mail@90: } mail@90: return chordvalues; mail@90: } mail@90: mail@115: vector chordDictionary(vector *mchorddict, vector > *m_chordnotes, float boostN, float harte_syntax) { matthiasm@86: Chris@27: typedef tokenizer > Tok; Chris@27: char_separator sep(",; ","="); Chris@30: Chris@30: string chordDictBase("chord.dict"); Chris@30: string chordDictFilename; Chris@30: Chris@30: vector ppath = getPluginPath(); mail@90: matthiasm@120: const char* notenames[24] = { matthiasm@120: "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: "A","Bb","B","C","C#","D","Eb","E","F","F#","G","Ab"}; matthiasm@120: matthiasm@144: const char* bassnames[13][12] ={ matthiasm@120: {"A","","B","C","C#","D","","E","","F#","G","G#"}, matthiasm@120: {"Bb","","C","Db","D","Eb","","F","","G","Ab","A"}, matthiasm@120: {"B","","C#","D","D#","E","","F#","","G#","A","A#"}, matthiasm@120: {"C","","D","Eb","E","F","","G","","A","Bb","B"}, matthiasm@120: {"C#","","D#","E","E#","F#","","G#","","A#","B","B#"}, matthiasm@120: {"D","","E","F","F#","G","","A","","B","C","C#"}, matthiasm@120: {"Eb","","F","Gb","G","Ab","","Bb","","C","Db","D"}, matthiasm@120: {"E","","F#","G","G#","A","","B","","C#","D","D#"}, matthiasm@120: {"F","","G","Ab","A","Bb","","C","","D","Eb","E"}, matthiasm@120: {"F#","","G#","A","A#","B","","C#","","D#","E","E#"}, matthiasm@120: {"G","","A","Bb","B","C","","D","","E","F","F#"}, matthiasm@144: {"Ab","","Bb","Cb","C","Db","","Eb","","F","Gb","G"}, matthiasm@144: {"1","","2","b3","3","4","","5","","6","b7","7"} matthiasm@120: }; matthiasm@120: mail@90: bool hasExternalDictinoary = true; matthiasm@122: int ppathsize = static_cast(ppath.size()); matthiasm@122: for (int i = 0; i < ppathsize; ++i) { mail@61: chordDictFilename = ppath[i] + "/" + chordDictBase; Chris@163: // cerr << "Looking for chord.dict in " << chordDictFilename << "..." ; mail@61: fstream fin; mail@61: fin.open(chordDictFilename.c_str(),ios::in); mail@61: if( fin.is_open() ) mail@61: { mail@61: fin.close(); Chris@163: // cerr << " success." << endl; mail@61: break; mail@61: } else { Chris@163: if (i+1 < ppathsize) { Chris@163: // cerr << " (not found yet) ..." << endl; Chris@163: } else { Chris@163: // cerr << "* WARNING: failed to find chord dictionary, using default chord dictionary." << endl; mail@90: hasExternalDictinoary = false; mail@90: } mail@61: } Chris@30: } Chris@30: Chris@30: iostreams::stream chordDictFile(chordDictFilename); Chris@27: string line; mail@89: // int iElement = 0; Chris@27: int nChord = 0; Chris@27: mail@112: vector tempChordDict = staticChordvalues(); Chris@91: vector tempChordNames = staticChordnames(); mail@115: if (harte_syntax == 1.0) { mail@112: tempChordNames.erase(tempChordNames.begin(),tempChordNames.begin()+tempChordNames.size()/2); mail@112: } else { mail@112: tempChordNames.erase(tempChordNames.begin()+tempChordNames.size()/2,tempChordNames.begin()+tempChordNames.size()); mail@112: } mail@112: Chris@27: vector loadedChordNames; Chris@27: vector loadedChordDict; mail@90: if (hasExternalDictinoary && chordDictFile.is_open()) { mail@90: tempChordDict.clear(); mail@90: tempChordNames.clear(); matthiasm@86: while (std::getline(chordDictFile, line)) { // loop over lines in chord.dict file Chris@27: // first, get the chord definition Chris@27: string chordType; Chris@27: vector tempPCVector; Chris@27: // cerr << line << endl; Chris@27: if (!line.empty() && line.substr(0,1) != "#") { Chris@27: Tok tok(line, sep); Chris@27: for(Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) { // loop over line elements Chris@27: string tempString = *tok_iter; Chris@27: // cerr << tempString << endl; mail@90: if (tok_iter == tok.begin()) { // either the chord name or a colon Chris@27: if (tempString == "=") { Chris@27: chordType = ""; Chris@27: } else { Chris@27: chordType = tempString; mail@90: tok_iter++; Chris@27: } Chris@27: } else { mail@90: tempChordDict.push_back(lexical_cast(*tok_iter)); Chris@27: } mail@90: } mail@90: tempChordNames.push_back(chordType); mail@90: } mail@90: } matthias@140: cerr << "-----------------> " << tempChordNames.size() << endl; mail@90: } mail@90: mail@90: Chris@91: for (int iType = 0; iType < (int)tempChordNames.size(); ++iType) { mail@90: // now make all 12 chords of every type Chris@91: for (int iSemitone = 0; iSemitone < 12; iSemitone++) { mail@90: vector tempchordnotes; mail@90: // add bass slash notation mail@90: string slashNotation = ""; Chris@91: for (int kSemitone = 1; kSemitone < 12; kSemitone++) { mail@90: if (tempChordDict[24*iType+(kSemitone) % 12] > 0.99) { mail@115: if (harte_syntax == 0.0) { mail@112: slashNotation = bassnames[iSemitone][kSemitone]; matthiasm@144: } else { matthiasm@144: slashNotation = bassnames[12][kSemitone]; matthiasm@144: } Chris@27: } Chris@27: } mail@90: if (slashNotation=="") tempchordnotes.push_back(MIDI_basenote + (iSemitone+12) % 12); Chris@91: for (int kSemitone = 0; kSemitone < 12; kSemitone++) { // bass pitch classes mail@90: // cerr << ((kSemitone - iSemitone + 12) % 12) << endl; mail@90: float bassValue = 0; mail@90: if (tempChordDict[24*iType+(kSemitone - iSemitone + 12) % 12]==1) { mail@90: bassValue = 1; mail@90: tempchordnotes.push_back(MIDI_basenote + (kSemitone+12) % 12); mail@90: } else { mail@90: if (tempChordDict[24*iType+((kSemitone - iSemitone + 12) % 12) + 12] == 1) bassValue = 0.5; mail@90: } mail@90: loadedChordDict.push_back(bassValue); mail@90: } Chris@91: for (int kSemitone = 0; kSemitone < 12; kSemitone++) { // chord pitch classes mail@90: loadedChordDict.push_back(tempChordDict[24*iType+((kSemitone - iSemitone + 12) % 12) + 12]); mail@90: if (tempChordDict[24*iType+((kSemitone - iSemitone + 12) % 12) + 12] > 0) tempchordnotes.push_back(MIDI_basenote + (kSemitone+12+6) % 12 - 6 + 24); mail@90: } mail@90: ostringstream os; mail@90: if (slashNotation.empty()) { mail@90: os << notenames[12+iSemitone] << tempChordNames[iType]; mail@90: } else { mail@90: os << notenames[12+iSemitone] << tempChordNames[iType] << "/" << slashNotation; mail@90: } mail@90: // cerr << os.str() << endl; mail@90: loadedChordNames.push_back(os.str()); mail@90: mail@90: m_chordnotes->push_back(tempchordnotes); mail@90: // for (int iNote = 0; iNote < tempchordnotes.size(); ++iNote) { mail@90: // cerr << tempchordnotes[iNote] << " "; mail@90: // } mail@90: // cerr << endl; Chris@27: } mail@90: } mail@90: mail@90: mail@90: // N type mail@90: loadedChordNames.push_back("N"); matthiasm@122: for (int kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(0.5); matthiasm@122: for (int kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(1.0); mail@90: vector tempchordvector; mail@90: m_chordnotes->push_back(tempchordvector); mail@90: float exponent = 2.0; mail@90: // float m_boostN = 1.1; mail@90: // cerr << " N BOOST : " << boostN << endl << endl; Chris@91: for (int iChord = 0; iChord < (int)loadedChordDict.size()/24; iChord++) { mail@90: float sum = 0; mail@90: float stand = 0; mail@90: for (int iST = 0; iST < 24; ++iST) { mail@90: sum += loadedChordDict[24 * iChord + iST]; mail@90: } mail@90: for (int iST = 0; iST < 24; ++iST) { mail@90: // loadedChordDict[24 * iChord + iST] -= sum/24; mail@90: stand += pow(abs(loadedChordDict[24 * iChord + iST]),exponent)/24; mail@90: } Chris@91: if (iChord < (int)loadedChordDict.size()/24 - 1) { Chris@91: stand = powf(stand,1.0f/exponent); mail@90: } else { matthiasm@95: stand = powf(stand,1.0f/exponent) / (1+boostN); mail@90: } mail@90: for (int iST = 0; iST < 24; ++iST) { mail@90: loadedChordDict[24 * iChord + iST] /= stand; Chris@27: } matthiasm@44: mail@90: } mail@90: mail@90: mail@90: mail@90: nChord = 0; Chris@91: for (int i = 0; i < (int)loadedChordNames.size(); i++) { mail@90: nChord++; mail@90: } mail@90: chordDictFile.close(); mail@90: mail@90: mail@90: // mchorddict = new float[nChord*24]; mail@90: for (int i = 0; i < nChord*24; i++) { mail@90: mchorddict->push_back(loadedChordDict[i]); mail@90: } Chris@27: Chris@27: // cerr << "before leaving" << chordnames[1] << endl; Chris@27: return loadedChordNames; Chris@27: } mail@90: Chris@91: