Mercurial > hg > nnls-chroma
diff chordextract.cpp @ 158:55c2dacd0d34
Example program
author | Chris Cannam |
---|---|
date | Fri, 05 Dec 2014 10:45:27 +0000 |
parents | |
children | 9d706d314e08 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chordextract.cpp Fri Dec 05 10:45:27 2014 +0000 @@ -0,0 +1,146 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + NNLS-Chroma / Chordino + + Audio feature extraction plugins for chromagram and chord + estimation. + + Centre for Digital Music, Queen Mary University of London. + This file copyright 2014 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +/* + Extract chords from an audio file, read using libsndfile. Works by + constructing the plugin as a C++ class directly, and using plugin + adapters from the Vamp Host SDK to provide input. + + You can compile this with e.g. the following (Linux example): + + $ g++ -D_VAMP_PLUGIN_IN_HOST_NAMESPACE -O2 -ffast-math chordextract.cpp Chordino.cpp NNLSBase.cpp chromamethods.cpp viterbi.cpp nnls.c -o chordextract -lsndfile -lvamp-hostsdk -ldl + + But the same idea should work on any platform, so long as the Boost + Tokenizer headers and the Vamp Host SDK library are available and + the _VAMP_PLUGIN_IN_HOST_NAMESPACE preprocessor symbol is defined + throughout. +*/ + +#define _VAMP_PLUGIN_IN_HOST_NAMESPACE 1 + +#include <vamp-hostsdk/PluginInputDomainAdapter.h> +#include <vamp-hostsdk/PluginBufferingAdapter.h> + +#include "Chordino.h" + +#include <sndfile.h> + +#include <iostream> +#include <string> + +using namespace std; +using namespace Vamp; +using namespace Vamp::HostExt; + +int main(int argc, char **argv) +{ + const char *myname = argv[0]; + + if (argc != 2) { + cerr << "usage: " << myname << " file.wav" << endl; + return 2; + } + + const char *infile = argv[1]; + + SF_INFO sfinfo; + SNDFILE *sndfile = sf_open(infile, SFM_READ, &sfinfo); + + if (!sndfile) { + cerr << myname << ": Failed to open input file " << infile + << ": " << sf_strerror(sndfile) << endl; + return 1; + } + + Chordino *chordino = new Chordino(sfinfo.samplerate); + PluginInputDomainAdapter *ia = new PluginInputDomainAdapter(chordino); + ia->setProcessTimestampMethod(PluginInputDomainAdapter::ShiftData); + PluginBufferingAdapter *adapter = new PluginBufferingAdapter(ia); + + int blocksize = adapter->getPreferredBlockSize(); + + // Plugin requires 1 channel (we will mix down) + if (!adapter->initialise(1, blocksize, blocksize)) { + cerr << myname << ": Failed to initialise Chordino adapter!" << endl; + return 1; + } + + float *filebuf = new float[sfinfo.channels * blocksize]; + float *mixbuf = new float[blocksize]; + + Plugin::FeatureList chordFeatures; + Plugin::FeatureSet fs; + + int chordFeatureNo = -1; + Plugin::OutputList outputs = adapter->getOutputDescriptors(); + for (int i = 0; i < int(outputs.size()); ++i) { + if (outputs[i].identifier == "simplechord") { + chordFeatureNo = i; + } + } + if (chordFeatureNo < 0) { + cerr << myname << ": Failed to identify chords output!" << endl; + return 1; + } + + int frame = 0; + while (frame < sfinfo.frames) { + + int count = -1; + if ((count = sf_readf_float(sndfile, filebuf, blocksize)) <= 0) break; + + // mix down + for (int i = 0; i < blocksize; ++i) { + mixbuf[i] = 0.f; + if (i < count) { + for (int c = 0; c < sfinfo.channels; ++c) { + mixbuf[i] += filebuf[i * sfinfo.channels + c] / sfinfo.channels; + } + } + } + + RealTime timestamp = RealTime::frame2RealTime(frame, sfinfo.samplerate); + + // feed to plugin: can just take address of buffer, as only one channel + fs = adapter->process(&mixbuf, timestamp); + + chordFeatures.insert(chordFeatures.end(), + fs[chordFeatureNo].begin(), + fs[chordFeatureNo].end()); + + frame += count; + } + + sf_close(sndfile); + + // features at end of processing (actually Chordino does all its work here) + fs = adapter->getRemainingFeatures(); + + // chord output is output index 0 + chordFeatures.insert(chordFeatures.end(), + fs[chordFeatureNo].begin(), + fs[chordFeatureNo].end()); + + for (int i = 0; i < (int)chordFeatures.size(); ++i) { + cerr << chordFeatures[i].timestamp.toString() << ": " + << chordFeatures[i].label << endl; + } + + delete adapter; +} +