Mercurial > hg > nnls-chroma
changeset 27:690bd9148467 matthiasm-plugin
* Split out some common code into chromamethods.cpp from NNLSChroma.cpp
(the latter is destined to become the chroma plugin only, eventually)
author | Chris Cannam |
---|---|
date | Thu, 21 Oct 2010 16:34:58 +0100 |
parents | 906d3705536d |
children | 52b6dbd61553 e2b8b2a1cd9b |
files | Makefile.cc-linux NNLSChroma.cpp chromamethods.cpp chromamethods.h |
diffstat | 4 files changed, 392 insertions(+), 419 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile.cc-linux Thu Oct 21 14:50:24 2010 +0100 +++ b/Makefile.cc-linux Thu Oct 21 16:34:58 2010 +0100 @@ -19,60 +19,24 @@ # Edit this to list one .o file for each .cpp file in your plugin project # -PLUGIN_CODE_OBJECTS = NNLSChroma.o plugins.o nnls.o +PLUGIN_CODE_OBJECTS = chromamethods.o NNLSChroma.o plugins.o nnls.o # Edit this to the location of the Vamp plugin SDK, relative to your # project directory # VAMP_SDK_DIR = /work/vamp-plugin-sdk -#LAPACK_DIR = /work/qm-dsp/include QMDSP_DIR = /work/qm-dsp -FFT_DIR = /work/qm-dsp/dsp/transforms NNLS_DIR = ../tsnnls/tsnnls -## Uncomment these for an OS/X native build using command-line tools: -#CXXFLAGS = -I$(VAMP_SDK_DIR) -I$(LAPACK_DIR) -I$(FFT_DIR) -I$(NNLS_DIR) -Wall -fPIC -g -#PLUGIN_EXT = .dylib -#PLUGIN = $(PLUGIN_LIBRARY_NAME)$(PLUGIN_EXT) -#LDFLAGS = -dynamiclib -install_name $(PLUGIN) $(VAMP_SDK_DIR)/libvamp-sdk.a $(QMDSP_DIR)/libqm-dsp.a ../tsnnls/tsnnls/.libs/libtsnnls.a -exported_symbols_list vamp-plugin.list -framework Accelerate - - -## Uncomment these for an OS/X universal binary using command-line tools: - -# CXXFLAGS = -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc -I$(VAMP_SDK_DIR) -Wall -fPIC -# PLUGIN_EXT = .dylib -# PLUGIN = $(PLUGIN_LIBRARY_NAME)$(PLUGIN_EXT) -# LDFLAGS = -dynamiclib -install_name $(PLUGIN) $(VAMP_SDK_DIR)/libvamp-sdk.a -exported_symbols_list vamp-plugin.list - - -## Uncomment these for Linux using the standard tools: - -CFLAGS = -I$(VAMP_SDK_DIR) -I$(LAPACK_DIR) -I$(FFT_DIR) -I$(NNLS_DIR) -Wall -fPIC -CXXFLAGS = -I$(VAMP_SDK_DIR) -I$(LAPACK_DIR) -I$(FFT_DIR) -I$(NNLS_DIR) -Wall -fPIC +CFLAGS = -I$(VAMP_SDK_DIR) -I$(LAPACK_DIR) -I$(NNLS_DIR) -Wall -fPIC +CXXFLAGS = -I$(VAMP_SDK_DIR) -I$(LAPACK_DIR) -I$(NNLS_DIR) -Wall -fPIC PLUGIN_EXT = .so PLUGIN = $(PLUGIN_LIBRARY_NAME)$(PLUGIN_EXT) #LDFLAGS = -shared -Wl,-soname=$(PLUGIN) $(VAMP_SDK_DIR)/libvamp-sdk.a -Wl,--version-script=vamp-plugin.map $(QMDSP_DIR)/libqm-dsp.a ../tsnnls/tsnnls/.libs/libtsnnls.a -llapack-3 LDFLAGS = -shared -Wl,-soname=$(PLUGIN) $(VAMP_SDK_DIR)/libvamp-sdk.a -Wl,--version-script=vamp-plugin.map $(QMDSP_DIR)/libqm-dsp.a -## Uncomment these for a cross-compile from Linux to Windows using MinGW: - -# CXX = i586-mingw32msvc-g++ -# CXXFLAGS = -I$(VAMP_SDK_DIR) -Wall -# PLUGIN_EXT = .dll -# PLUGIN = $(PLUGIN_LIBRARY_NAME)$(PLUGIN_EXT) -# LDFLAGS = --static-libgcc -Wl,-soname=$(PLUGIN) -shared $(VAMP_SDK_DIR)/libvamp-sdk.a - - -## Uncomment these for OpenSolaris using SunStudio compiler and GNU make: - -# CXX = CC -# CXXFLAGS = -G -I$(VAMP_SDK_DIR) +w -KPIC -# PLUGIN_EXT = .so -# PLUGIN = $(PLUGIN_LIBRARY_NAME)$(PLUGIN_EXT) -# LDFLAGS = -G -h$(PLUGIN) $(VAMP_SDK_DIR)/libvamp-sdk.a -Qoption ld -Mvamp-plugin.map - ## All of the above
--- a/NNLSChroma.cpp Thu Oct 21 14:50:24 2010 +0100 +++ b/NNLSChroma.cpp Thu Oct 21 16:34:58 2010 +0100 @@ -1,391 +1,18 @@ /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ #include "NNLSChroma.h" + +#include "chromamethods.h" + +#include <cstdlib> +#include <fstream> #include <cmath> -// #include <omp.h> -#include <list> -#include <iostream> -#include <fstream> -#include <sstream> -#include <cassert> -#include <cstdlib> -#include <cstdio> -#include <boost/tokenizer.hpp> -#include <boost/iostreams/device/file.hpp> -#include <boost/iostreams/stream.hpp> -#include <boost/lexical_cast.hpp> -#include "nnls.h" -#include "chorddict.cpp" -// #include <omp.h> -// #define N 1000 -// #define CHUNKSIZE 100 - - -using namespace std; -using namespace boost; - -const float sinvalue = 0.866025404; -const float cosvalue = -0.5; -const float hammingwind[19] = {0.0082, 0.0110, 0.0191, 0.0316, 0.0470, 0.0633, 0.0786, 0.0911, 0.0992, 0.1020, 0.0992, 0.0911, 0.0786, 0.0633, 0.0470, 0.0316, 0.0191, 0.0110, 0.0082}; -const float basswindow[] = {0.001769, 0.015848, 0.043608, 0.084265, 0.136670, 0.199341, 0.270509, 0.348162, 0.430105, 0.514023, 0.597545, 0.678311, 0.754038, 0.822586, 0.882019, 0.930656, 0.967124, 0.990393, 0.999803, 0.995091, 0.976388, 0.944223, 0.899505, 0.843498, 0.777785, 0.704222, 0.624888, 0.542025, 0.457975, 0.375112, 0.295778, 0.222215, 0.156502, 0.100495, 0.055777, 0.023612, 0.004909, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000}; -const float treblewindow[] = {0.000350, 0.003144, 0.008717, 0.017037, 0.028058, 0.041719, 0.057942, 0.076638, 0.097701, 0.121014, 0.146447, 0.173856, 0.203090, 0.233984, 0.266366, 0.300054, 0.334860, 0.370590, 0.407044, 0.444018, 0.481304, 0.518696, 0.555982, 0.592956, 0.629410, 0.665140, 0.699946, 0.733634, 0.766016, 0.796910, 0.826144, 0.853553, 0.878986, 0.902299, 0.923362, 0.942058, 0.958281, 0.971942, 0.982963, 0.991283, 0.996856, 0.999650, 0.999650, 0.996856, 0.991283, 0.982963, 0.971942, 0.958281, 0.942058, 0.923362, 0.902299, 0.878986, 0.853553, 0.826144, 0.796910, 0.766016, 0.733634, 0.699946, 0.665140, 0.629410, 0.592956, 0.555982, 0.518696, 0.481304, 0.444018, 0.407044, 0.370590, 0.334860, 0.300054, 0.266366, 0.233984, 0.203090, 0.173856, 0.146447, 0.121014, 0.097701, 0.076638, 0.057942, 0.041719, 0.028058, 0.017037, 0.008717, 0.003144, 0.000350}; -const char* notenames[24] = {"A (bass)","Bb (bass)","B (bass)","C (bass)","C# (bass)","D (bass)","Eb (bass)","E (bass)","F (bass)","F# (bass)","G (bass)","Ab (bass)", - "A","Bb","B","C","C#","D","Eb","E","F","F#","G","Ab"}; - -const char* bassnames[12][12] ={ - {"A","","B","C","C#","D","","E","","F#","G","G#"}, - {"Bb","","C","Db","D","Eb","","F","","G","Ab","A"}, - {"B","","C#","D","D#","E","","F#","","G#","A","A#"}, - {"C","","D","Eb","E","F","","G","","A","Bb","B"}, - {"C#","","D#","E","E#","F#","","G#","","A#","B","B#"}, - {"D","","E","F","F#","G","","A","","B","C","C#"}, - {"Eb","","F","Gb","G","Ab","","Bb","","C","Db","D"}, - {"E","","F#","G","G#","A","","B","","C#","D","D#"}, - {"F","","G","Ab","A","Bb","","C","","D","Eb","E"}, - {"F#","","G#","A","A#","B","","C#","","D#","E","E#"}, - {"G","","A","Bb","B","C","","D","","E","F","F#"}, - {"Ab","","Bb","Cb","C","Db","","Eb","","F","Gb","G"} -}; - - -// const char* bassnames[12][12] ={ -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// {"1","","2","b3","3","4","","5","","6","b7","7"}, -// }; - -const vector<float> hw(hammingwind, hammingwind+19); -const int nNote = 256; - -/** Special Convolution - special convolution is as long as the convolvee, i.e. the first argument. in the valid core part of the - convolution it contains the usual convolution values, but the pads at the beginning (ending) have the same values - as the first (last) valid convolution bin. -**/ +#include <algorithm> const bool debug_on = false; -vector<float> SpecialConvolution(vector<float> convolvee, vector<float> kernel) -{ - float s; - int m, n; - int lenConvolvee = convolvee.size(); - int lenKernel = kernel.size(); - - vector<float> Z(256,0); - assert(lenKernel % 2 != 0); // no exception handling !!! - - for (n = lenKernel - 1; n < lenConvolvee; n++) { - s=0.0; - for (m = 0; m < lenKernel; m++) { - // cerr << "m = " << m << ", n = " << n << ", n-m = " << (n-m) << '\n'; - s += convolvee[n-m] * kernel[m]; - // if (debug_on) cerr << "--> s = " << s << '\n'; - } - // cerr << n - lenKernel/2 << endl; - Z[n -lenKernel/2] = s; - } - - // fill upper and lower pads - for (n = 0; n < lenKernel/2; n++) Z[n] = Z[lenKernel/2]; - for (n = lenConvolvee; n < lenConvolvee +lenKernel/2; n++) Z[n - lenKernel/2] = - Z[lenConvolvee - lenKernel/2 - 1]; - return Z; -} - -// vector<float> FftBin2Frequency(vector<float> binnumbers, int fs, int blocksize) -// { -// vector<float> freq(binnumbers.size, 0.0); -// for (unsigned i = 0; i < binnumbers.size; ++i) { -// freq[i] = (binnumbers[i]-1.0) * fs * 1.0 / blocksize; -// } -// return freq; -// } - -float cospuls(float x, float centre, float width) -{ - float recipwidth = 1.0/width; - if (abs(x - centre) <= 0.5 * width) { - return cos((x-centre)*2*M_PI*recipwidth)*.5+.5; - } - return 0.0; -} - -float pitchCospuls(float x, float centre, int binsperoctave) -{ - float warpedf = -binsperoctave * (log2(centre) - log2(x)); - float out = cospuls(warpedf, 0.0, 2.0); - // now scale to correct for note density - float c = log(2.0)/binsperoctave; - if (x > 0) { - out = out / (c * x); - } else { - out = 0; - } - return out; -} - -bool logFreqMatrix(int fs, int blocksize, float *outmatrix) { - - int binspersemitone = 3; // this must be 3 - int minoctave = 0; // this must be 0 - int maxoctave = 7; // this must be 7 - int oversampling = 80; - - // linear frequency vector - vector<float> fft_f; - for (int i = 0; i < blocksize/2; ++i) { - fft_f.push_back(i * (fs * 1.0 / blocksize)); - } - float fft_width = fs * 2.0 / blocksize; - - // linear oversampled frequency vector - vector<float> oversampled_f; - for (unsigned int i = 0; i < oversampling * blocksize/2; ++i) { - oversampled_f.push_back(i * ((fs * 1.0 / blocksize) / oversampling)); - } - - // pitch-spaced frequency vector - int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone! - int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone! - vector<float> cq_f; - float oob = 1.0/binspersemitone; // one over binspersemitone - cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-69))); // 0.083333 is approx 1/12 - cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI+oob-69))); - for (int i = minMIDI + 1; i < maxMIDI; ++i) { - for (int k = -1; k < 2; ++k) { - cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69))); - } - } - cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-oob-69))); - cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69))); - - int nFFT = fft_f.size(); - - vector<float> fft_activation; - for (int iOS = 0; iOS < 2 * oversampling; ++iOS) { - float cosp = cospuls(oversampled_f[iOS],fft_f[1],fft_width); - fft_activation.push_back(cosp); - // cerr << cosp << endl; - } - - float cq_activation; - for (int iFFT = 1; iFFT < nFFT; ++iFFT) { - // find frequency stretch where the oversampled vector can be non-zero (i.e. in a window of width fft_width around the current frequency) - int curr_start = oversampling * iFFT - oversampling; - int curr_end = oversampling * iFFT + oversampling; // don't know if I should add "+1" here - // cerr << oversampled_f[curr_start] << " " << fft_f[iFFT] << " " << oversampled_f[curr_end] << endl; - for (unsigned iCQ = 0; iCQ < cq_f.size(); ++iCQ) { - outmatrix[iFFT + nFFT * iCQ] = 0; - 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 - for (int iOS = curr_start; iOS < curr_end; ++iOS) { - cq_activation = pitchCospuls(oversampled_f[iOS],cq_f[iCQ],binspersemitone*12); - // cerr << oversampled_f[iOS] << " " << cq_f[iCQ] << " " << cq_activation << endl; - outmatrix[iFFT + nFFT * iCQ] += cq_activation * fft_activation[iOS-curr_start]; - } - // if (iCQ == 1 || iCQ == 2) { - // cerr << " " << outmatrix[iFFT + nFFT * iCQ] << endl; - // } - } - } - } - return true; -} - -void dictionaryMatrix(float* dm) { - int binspersemitone = 3; // this must be 3 - int minoctave = 0; // this must be 0 - int maxoctave = 7; // this must be 7 - float s_param = 0.7; - - // pitch-spaced frequency vector - int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone! - int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone! - vector<float> cq_f; - float oob = 1.0/binspersemitone; // one over binspersemitone - cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-69))); // 0.083333 is approx 1/12 - cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI+oob-69))); - for (int i = minMIDI + 1; i < maxMIDI; ++i) { - for (int k = -1; k < 2; ++k) { - cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69))); - } - } - cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-oob-69))); - cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69))); - - float curr_f; - float floatbin; - float curr_amp; - // now for every combination calculate the matrix element - for (unsigned iOut = 0; iOut < 12 * (maxoctave - minoctave); ++iOut) { - // cerr << iOut << endl; - for (unsigned iHarm = 1; iHarm <= 20; ++iHarm) { - curr_f = 440 * pow(2,(minMIDI-69+iOut)*1.0/12) * iHarm; - // if (curr_f > cq_f[nNote-1]) break; - floatbin = ((iOut + 1) * binspersemitone + 1) + binspersemitone * 12 * log2(iHarm); - // cerr << floatbin << endl; - curr_amp = pow(s_param,float(iHarm-1)); - // cerr << "curramp" << curr_amp << endl; - for (unsigned iNote = 0; iNote < nNote; ++iNote) { - if (abs(iNote+1.0-floatbin)<2) { - dm[iNote + 256 * iOut] += cospuls(iNote+1.0, floatbin, binspersemitone + 0.0) * curr_amp; - // dm[iNote + nNote * iOut] += 1 * curr_amp; - } - } - } - } - - -} - -string get_env_var( std::string const & key ) { - char * val; - val = getenv( key.c_str() ); - string retval; - if (val != NULL) { - retval = val; - } - return retval; -} - - -vector<string> chordDictionary(vector<float> *mchorddict) { - // ifstream chordDictFile; - string chordDictFilename(get_env_var("VAMP_PATH")+"/chord.dict"); - // 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"; - typedef tokenizer<char_separator<char> > Tok; - // char_separator<char> sep; // default constructed - char_separator<char> sep(",; ","="); - iostreams::stream<iostreams::file_source> chordDictFile(chordDictFilename.c_str()); - string line; - int iElement = 0; - int nChord = 0; - - vector<string> loadedChordNames; - vector<float> loadedChordDict; - if (chordDictFile.is_open()) { - while (std::getline(chordDictFile, line)) { // loop over lines in chord.dict file - // first, get the chord definition - string chordType; - vector<float> tempPCVector; - // cerr << line << endl; - if (!line.empty() && line.substr(0,1) != "#") { - Tok tok(line, sep); - for(Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) { // loop over line elements - string tempString = *tok_iter; - // cerr << tempString << endl; - if (tok_iter == tok.begin()) { // either the chord name or a colon - if (tempString == "=") { - chordType = ""; - } else { - chordType = tempString; - tok_iter++; // is this cheating ? :) - } - } else { - tempPCVector.push_back(lexical_cast<float>(*tok_iter)); - } - } - - // now make all 12 chords of every type - for (unsigned iSemitone = 0; iSemitone < 12; iSemitone++) { - // add bass slash notation - string slashNotation = ""; - for (unsigned kSemitone = 1; kSemitone < 12; kSemitone++) { - if (tempPCVector[(kSemitone) % 12] > 0.99) { - slashNotation = bassnames[iSemitone][kSemitone]; - } - } - for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) { // bass pitch classes - // cerr << ((kSemitone - iSemitone + 12) % 12) << endl; - float bassValue = 0; - if (tempPCVector[(kSemitone - iSemitone + 12) % 12]==1) { - bassValue = 1; - } else { - if (tempPCVector[((kSemitone - iSemitone + 12) % 12) + 12] == 1) bassValue = 0.5; - } - loadedChordDict.push_back(bassValue); - } - for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) { // chord pitch classes - loadedChordDict.push_back(tempPCVector[((kSemitone - iSemitone + 12) % 12) + 12]); - } - ostringstream os; - if (slashNotation.empty()) { - os << notenames[12+iSemitone] << chordType; - } else { - os << notenames[12+iSemitone] << chordType << "/" << slashNotation; - } - // cerr << os.str() << endl; - loadedChordNames.push_back(os.str()); - } - } - } - // N type - loadedChordNames.push_back("N"); - for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(0.5); - for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(1.0); - - // normalise - float sum = 0; - for (int i = 0; i < loadedChordDict.size(); i++) { - sum += pow(loadedChordDict[i],2); - if (i % 24 == 23) { - float invertedsum = 1.0/sqrt(sum); - for (int k = 0; k < 24; k++) { - loadedChordDict[i-k] *= invertedsum; - } - sum = 0; - } - - } - - - nChord = 0; - for (int i = 0; i < loadedChordNames.size(); i++) { - nChord++; - } - chordDictFile.close(); - - - // mchorddict = new float[nChord*24]; - for (int i = 0; i < nChord*24; i++) { - mchorddict->push_back(loadedChordDict[i]); - } - - } else {// use default from chorddict.cpp - // mchorddict = new float[nChorddict]; - for (int i = 0; i < nChorddict; i++) { - mchorddict->push_back(chorddict[i]); - } - - nChord = nChorddict/24; - // mchordnames = new string[nChorddict/24]; - char buffer1 [50]; - for (int i = 0; i < nChorddict/24; i++) { - if (i < nChorddict/24 - 1) { - sprintf(buffer1, "%s%s", notenames[i % 12 + 12], chordtypes[i]); - } else { - sprintf(buffer1, "N"); - } - ostringstream os; - os << buffer1; - loadedChordNames.push_back(os.str()); - - } - - } - // cerr << "before leaving" << chordnames[1] << endl; - return loadedChordNames; -} +const vector<float> hw(hammingwind, hammingwind+19); NNLSChroma::NNLSChroma(float inputSampleRate) : Plugin(inputSampleRate),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chromamethods.cpp Thu Oct 21 16:34:58 2010 +0100 @@ -0,0 +1,338 @@ +#include "chromamethods.h" + +#include <cmath> +#include <list> +#include <iostream> +#include <fstream> +#include <sstream> +#include <cassert> +#include <cstdlib> +#include <cstdio> +#include <boost/tokenizer.hpp> +#include <boost/iostreams/device/file.hpp> +#include <boost/iostreams/stream.hpp> +#include <boost/lexical_cast.hpp> + +#include "chorddict.cpp" + +using namespace std; +using namespace boost; + + +/** Special Convolution + special convolution is as long as the convolvee, i.e. the first argument. in the valid core part of the + convolution it contains the usual convolution values, but the pads at the beginning (ending) have the same values + as the first (last) valid convolution bin. +**/ + +vector<float> SpecialConvolution(vector<float> convolvee, vector<float> kernel) +{ + float s; + int m, n; + int lenConvolvee = convolvee.size(); + int lenKernel = kernel.size(); + + vector<float> Z(256,0); + assert(lenKernel % 2 != 0); // no exception handling !!! + + for (n = lenKernel - 1; n < lenConvolvee; n++) { + s=0.0; + for (m = 0; m < lenKernel; m++) { + // cerr << "m = " << m << ", n = " << n << ", n-m = " << (n-m) << '\n'; + s += convolvee[n-m] * kernel[m]; + // if (debug_on) cerr << "--> s = " << s << '\n'; + } + // cerr << n - lenKernel/2 << endl; + Z[n -lenKernel/2] = s; + } + + // fill upper and lower pads + for (n = 0; n < lenKernel/2; n++) Z[n] = Z[lenKernel/2]; + for (n = lenConvolvee; n < lenConvolvee +lenKernel/2; n++) Z[n - lenKernel/2] = + Z[lenConvolvee - lenKernel/2 - 1]; + return Z; +} + +// vector<float> FftBin2Frequency(vector<float> binnumbers, int fs, int blocksize) +// { +// vector<float> freq(binnumbers.size, 0.0); +// for (unsigned i = 0; i < binnumbers.size; ++i) { +// freq[i] = (binnumbers[i]-1.0) * fs * 1.0 / blocksize; +// } +// return freq; +// } + +float cospuls(float x, float centre, float width) +{ + float recipwidth = 1.0/width; + if (abs(x - centre) <= 0.5 * width) { + return cos((x-centre)*2*M_PI*recipwidth)*.5+.5; + } + return 0.0; +} + +float pitchCospuls(float x, float centre, int binsperoctave) +{ + float warpedf = -binsperoctave * (log2(centre) - log2(x)); + float out = cospuls(warpedf, 0.0, 2.0); + // now scale to correct for note density + float c = log(2.0)/binsperoctave; + if (x > 0) { + out = out / (c * x); + } else { + out = 0; + } + return out; +} + +bool logFreqMatrix(int fs, int blocksize, float *outmatrix) { + + int binspersemitone = 3; // this must be 3 + int minoctave = 0; // this must be 0 + int maxoctave = 7; // this must be 7 + int oversampling = 80; + + // linear frequency vector + vector<float> fft_f; + for (int i = 0; i < blocksize/2; ++i) { + fft_f.push_back(i * (fs * 1.0 / blocksize)); + } + float fft_width = fs * 2.0 / blocksize; + + // linear oversampled frequency vector + vector<float> oversampled_f; + for (unsigned int i = 0; i < oversampling * blocksize/2; ++i) { + oversampled_f.push_back(i * ((fs * 1.0 / blocksize) / oversampling)); + } + + // pitch-spaced frequency vector + int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone! + int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone! + vector<float> cq_f; + float oob = 1.0/binspersemitone; // one over binspersemitone + cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-69))); // 0.083333 is approx 1/12 + cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI+oob-69))); + for (int i = minMIDI + 1; i < maxMIDI; ++i) { + for (int k = -1; k < 2; ++k) { + cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69))); + } + } + cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-oob-69))); + cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69))); + + int nFFT = fft_f.size(); + + vector<float> fft_activation; + for (int iOS = 0; iOS < 2 * oversampling; ++iOS) { + float cosp = cospuls(oversampled_f[iOS],fft_f[1],fft_width); + fft_activation.push_back(cosp); + // cerr << cosp << endl; + } + + float cq_activation; + for (int iFFT = 1; iFFT < nFFT; ++iFFT) { + // find frequency stretch where the oversampled vector can be non-zero (i.e. in a window of width fft_width around the current frequency) + int curr_start = oversampling * iFFT - oversampling; + int curr_end = oversampling * iFFT + oversampling; // don't know if I should add "+1" here + // cerr << oversampled_f[curr_start] << " " << fft_f[iFFT] << " " << oversampled_f[curr_end] << endl; + for (unsigned iCQ = 0; iCQ < cq_f.size(); ++iCQ) { + outmatrix[iFFT + nFFT * iCQ] = 0; + 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 + for (int iOS = curr_start; iOS < curr_end; ++iOS) { + cq_activation = pitchCospuls(oversampled_f[iOS],cq_f[iCQ],binspersemitone*12); + // cerr << oversampled_f[iOS] << " " << cq_f[iCQ] << " " << cq_activation << endl; + outmatrix[iFFT + nFFT * iCQ] += cq_activation * fft_activation[iOS-curr_start]; + } + // if (iCQ == 1 || iCQ == 2) { + // cerr << " " << outmatrix[iFFT + nFFT * iCQ] << endl; + // } + } + } + } + return true; +} + +void dictionaryMatrix(float* dm) { + int binspersemitone = 3; // this must be 3 + int minoctave = 0; // this must be 0 + int maxoctave = 7; // this must be 7 + float s_param = 0.7; + + // pitch-spaced frequency vector + int minMIDI = 21 + minoctave * 12 - 1; // this includes one additional semitone! + int maxMIDI = 21 + maxoctave * 12; // this includes one additional semitone! + vector<float> cq_f; + float oob = 1.0/binspersemitone; // one over binspersemitone + cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-69))); // 0.083333 is approx 1/12 + cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI+oob-69))); + for (int i = minMIDI + 1; i < maxMIDI; ++i) { + for (int k = -1; k < 2; ++k) { + cq_f.push_back(440 * pow(2.0,0.083333333333 * (i+oob*k-69))); + } + } + cq_f.push_back(440 * pow(2.0,0.083333 * (minMIDI-oob-69))); + cq_f.push_back(440 * pow(2.0,0.083333 * (maxMIDI-69))); + + float curr_f; + float floatbin; + float curr_amp; + // now for every combination calculate the matrix element + for (unsigned iOut = 0; iOut < 12 * (maxoctave - minoctave); ++iOut) { + // cerr << iOut << endl; + for (unsigned iHarm = 1; iHarm <= 20; ++iHarm) { + curr_f = 440 * pow(2,(minMIDI-69+iOut)*1.0/12) * iHarm; + // if (curr_f > cq_f[nNote-1]) break; + floatbin = ((iOut + 1) * binspersemitone + 1) + binspersemitone * 12 * log2(iHarm); + // cerr << floatbin << endl; + curr_amp = pow(s_param,float(iHarm-1)); + // cerr << "curramp" << curr_amp << endl; + for (unsigned iNote = 0; iNote < nNote; ++iNote) { + if (abs(iNote+1.0-floatbin)<2) { + dm[iNote + 256 * iOut] += cospuls(iNote+1.0, floatbin, binspersemitone + 0.0) * curr_amp; + // dm[iNote + nNote * iOut] += 1 * curr_amp; + } + } + } + } + + +} + +string get_env_var( std::string const & key ) { + char * val; + val = getenv( key.c_str() ); + string retval; + if (val != NULL) { + retval = val; + } + return retval; +} + + +vector<string> chordDictionary(vector<float> *mchorddict) { + // ifstream chordDictFile; + string chordDictFilename(get_env_var("VAMP_PATH")+"/chord.dict"); + // 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"; + typedef tokenizer<char_separator<char> > Tok; + // char_separator<char> sep; // default constructed + char_separator<char> sep(",; ","="); + iostreams::stream<iostreams::file_source> chordDictFile(chordDictFilename.c_str()); + string line; + int iElement = 0; + int nChord = 0; + + vector<string> loadedChordNames; + vector<float> loadedChordDict; + if (chordDictFile.is_open()) { + while (std::getline(chordDictFile, line)) { // loop over lines in chord.dict file + // first, get the chord definition + string chordType; + vector<float> tempPCVector; + // cerr << line << endl; + if (!line.empty() && line.substr(0,1) != "#") { + Tok tok(line, sep); + for(Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) { // loop over line elements + string tempString = *tok_iter; + // cerr << tempString << endl; + if (tok_iter == tok.begin()) { // either the chord name or a colon + if (tempString == "=") { + chordType = ""; + } else { + chordType = tempString; + tok_iter++; // is this cheating ? :) + } + } else { + tempPCVector.push_back(lexical_cast<float>(*tok_iter)); + } + } + + // now make all 12 chords of every type + for (unsigned iSemitone = 0; iSemitone < 12; iSemitone++) { + // add bass slash notation + string slashNotation = ""; + for (unsigned kSemitone = 1; kSemitone < 12; kSemitone++) { + if (tempPCVector[(kSemitone) % 12] > 0.99) { + slashNotation = bassnames[iSemitone][kSemitone]; + } + } + for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) { // bass pitch classes + // cerr << ((kSemitone - iSemitone + 12) % 12) << endl; + float bassValue = 0; + if (tempPCVector[(kSemitone - iSemitone + 12) % 12]==1) { + bassValue = 1; + } else { + if (tempPCVector[((kSemitone - iSemitone + 12) % 12) + 12] == 1) bassValue = 0.5; + } + loadedChordDict.push_back(bassValue); + } + for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) { // chord pitch classes + loadedChordDict.push_back(tempPCVector[((kSemitone - iSemitone + 12) % 12) + 12]); + } + ostringstream os; + if (slashNotation.empty()) { + os << notenames[12+iSemitone] << chordType; + } else { + os << notenames[12+iSemitone] << chordType << "/" << slashNotation; + } + // cerr << os.str() << endl; + loadedChordNames.push_back(os.str()); + } + } + } + // N type + loadedChordNames.push_back("N"); + for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(0.5); + for (unsigned kSemitone = 0; kSemitone < 12; kSemitone++) loadedChordDict.push_back(1.0); + + // normalise + float sum = 0; + for (int i = 0; i < loadedChordDict.size(); i++) { + sum += pow(loadedChordDict[i],2); + if (i % 24 == 23) { + float invertedsum = 1.0/sqrt(sum); + for (int k = 0; k < 24; k++) { + loadedChordDict[i-k] *= invertedsum; + } + sum = 0; + } + + } + + + nChord = 0; + for (int i = 0; i < loadedChordNames.size(); i++) { + nChord++; + } + chordDictFile.close(); + + + // mchorddict = new float[nChord*24]; + for (int i = 0; i < nChord*24; i++) { + mchorddict->push_back(loadedChordDict[i]); + } + + } else {// use default from chorddict.cpp + // mchorddict = new float[nChorddict]; + for (int i = 0; i < nChorddict; i++) { + mchorddict->push_back(chorddict[i]); + } + + nChord = nChorddict/24; + // mchordnames = new string[nChorddict/24]; + char buffer1 [50]; + for (int i = 0; i < nChorddict/24; i++) { + if (i < nChorddict/24 - 1) { + sprintf(buffer1, "%s%s", notenames[i % 12 + 12], chordtypes[i]); + } else { + sprintf(buffer1, "N"); + } + ostringstream os; + os << buffer1; + loadedChordNames.push_back(os.str()); + + } + + } + // cerr << "before leaving" << chordnames[1] << endl; + return loadedChordNames; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chromamethods.h Thu Oct 21 16:34:58 2010 +0100 @@ -0,0 +1,44 @@ + +#ifndef _CHROMA_METHODS_H_ +#define _CHROMA_METHODS_H_ + +#include <vector> +#include <string> + +const int nNote = 256; + +extern std::vector<float> SpecialConvolution(std::vector<float> convolvee, std::vector<float> kernel); +extern void dictionaryMatrix(float* dm); +extern std::vector<std::string> chordDictionary(std::vector<float> *mchorddict); +extern bool logFreqMatrix(int fs, int blocksize, float *outmatrix); + +static const char* notenames[24] = { + "A (bass)","Bb (bass)","B (bass)","C (bass)","C# (bass)","D (bass)","Eb (bass)","E (bass)","F (bass)","F# (bass)","G (bass)","Ab (bass)", + "A","Bb","B","C","C#","D","Eb","E","F","F#","G","Ab"}; + +static const char* bassnames[12][12] ={ + {"A","","B","C","C#","D","","E","","F#","G","G#"}, + {"Bb","","C","Db","D","Eb","","F","","G","Ab","A"}, + {"B","","C#","D","D#","E","","F#","","G#","A","A#"}, + {"C","","D","Eb","E","F","","G","","A","Bb","B"}, + {"C#","","D#","E","E#","F#","","G#","","A#","B","B#"}, + {"D","","E","F","F#","G","","A","","B","C","C#"}, + {"Eb","","F","Gb","G","Ab","","Bb","","C","Db","D"}, + {"E","","F#","G","G#","A","","B","","C#","D","D#"}, + {"F","","G","Ab","A","Bb","","C","","D","Eb","E"}, + {"F#","","G#","A","A#","B","","C#","","D#","E","E#"}, + {"G","","A","Bb","B","C","","D","","E","F","F#"}, + {"Ab","","Bb","Cb","C","Db","","Eb","","F","Gb","G"} +}; + +static const float hammingwind[19] = {0.0082, 0.0110, 0.0191, 0.0316, 0.0470, 0.0633, 0.0786, 0.0911, 0.0992, 0.1020, 0.0992, 0.0911, 0.0786, 0.0633, 0.0470, 0.0316, 0.0191, 0.0110, 0.0082}; +static const float basswindow[] = {0.001769, 0.015848, 0.043608, 0.084265, 0.136670, 0.199341, 0.270509, 0.348162, 0.430105, 0.514023, 0.597545, 0.678311, 0.754038, 0.822586, 0.882019, 0.930656, 0.967124, 0.990393, 0.999803, 0.995091, 0.976388, 0.944223, 0.899505, 0.843498, 0.777785, 0.704222, 0.624888, 0.542025, 0.457975, 0.375112, 0.295778, 0.222215, 0.156502, 0.100495, 0.055777, 0.023612, 0.004909, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000}; +static const float treblewindow[] = {0.000350, 0.003144, 0.008717, 0.017037, 0.028058, 0.041719, 0.057942, 0.076638, 0.097701, 0.121014, 0.146447, 0.173856, 0.203090, 0.233984, 0.266366, 0.300054, 0.334860, 0.370590, 0.407044, 0.444018, 0.481304, 0.518696, 0.555982, 0.592956, 0.629410, 0.665140, 0.699946, 0.733634, 0.766016, 0.796910, 0.826144, 0.853553, 0.878986, 0.902299, 0.923362, 0.942058, 0.958281, 0.971942, 0.982963, 0.991283, 0.996856, 0.999650, 0.999650, 0.996856, 0.991283, 0.982963, 0.971942, 0.958281, 0.942058, 0.923362, 0.902299, 0.878986, 0.853553, 0.826144, 0.796910, 0.766016, 0.733634, 0.699946, 0.665140, 0.629410, 0.592956, 0.555982, 0.518696, 0.481304, 0.444018, 0.407044, 0.370590, 0.334860, 0.300054, 0.266366, 0.233984, 0.203090, 0.173856, 0.146447, 0.121014, 0.097701, 0.076638, 0.057942, 0.041719, 0.028058, 0.017037, 0.008717, 0.003144, 0.000350}; + +static const float sinvalue = 0.866025404; +static const float cosvalue = -0.5; + +#include "nnls.h" + +#endif +