Chris@158
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@158
|
2
|
Chris@158
|
3 /*
|
Chris@158
|
4 NNLS-Chroma / Chordino
|
Chris@158
|
5
|
Chris@158
|
6 Audio feature extraction plugins for chromagram and chord
|
Chris@158
|
7 estimation.
|
Chris@158
|
8
|
Chris@158
|
9 Centre for Digital Music, Queen Mary University of London.
|
Chris@158
|
10 This file copyright 2014 QMUL.
|
Chris@158
|
11
|
Chris@158
|
12 This program is free software; you can redistribute it and/or
|
Chris@158
|
13 modify it under the terms of the GNU General Public License as
|
Chris@158
|
14 published by the Free Software Foundation; either version 2 of the
|
Chris@158
|
15 License, or (at your option) any later version. See the file
|
Chris@158
|
16 COPYING included with this distribution for more information.
|
Chris@158
|
17 */
|
Chris@158
|
18
|
Chris@158
|
19 /*
|
Chris@158
|
20 Extract chords from an audio file, read using libsndfile. Works by
|
Chris@158
|
21 constructing the plugin as a C++ class directly, and using plugin
|
Chris@158
|
22 adapters from the Vamp Host SDK to provide input.
|
Chris@158
|
23
|
Chris@158
|
24 You can compile this with e.g. the following (Linux example):
|
Chris@158
|
25
|
Chris@158
|
26 $ 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
|
Chris@158
|
27
|
Chris@158
|
28 But the same idea should work on any platform, so long as the Boost
|
Chris@158
|
29 Tokenizer headers and the Vamp Host SDK library are available and
|
Chris@158
|
30 the _VAMP_PLUGIN_IN_HOST_NAMESPACE preprocessor symbol is defined
|
Chris@158
|
31 throughout.
|
Chris@158
|
32 */
|
Chris@158
|
33
|
Chris@158
|
34 #define _VAMP_PLUGIN_IN_HOST_NAMESPACE 1
|
Chris@158
|
35
|
Chris@158
|
36 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
|
Chris@158
|
37 #include <vamp-hostsdk/PluginBufferingAdapter.h>
|
Chris@158
|
38
|
Chris@158
|
39 #include "Chordino.h"
|
Chris@158
|
40
|
Chris@158
|
41 #include <sndfile.h>
|
Chris@158
|
42
|
Chris@158
|
43 #include <iostream>
|
Chris@158
|
44 #include <string>
|
Chris@158
|
45
|
Chris@158
|
46 using namespace std;
|
Chris@158
|
47 using namespace Vamp;
|
Chris@158
|
48 using namespace Vamp::HostExt;
|
Chris@158
|
49
|
Chris@158
|
50 int main(int argc, char **argv)
|
Chris@158
|
51 {
|
Chris@158
|
52 const char *myname = argv[0];
|
Chris@158
|
53
|
Chris@158
|
54 if (argc != 2) {
|
Chris@158
|
55 cerr << "usage: " << myname << " file.wav" << endl;
|
Chris@158
|
56 return 2;
|
Chris@158
|
57 }
|
Chris@158
|
58
|
Chris@158
|
59 const char *infile = argv[1];
|
Chris@158
|
60
|
Chris@158
|
61 SF_INFO sfinfo;
|
Chris@158
|
62 SNDFILE *sndfile = sf_open(infile, SFM_READ, &sfinfo);
|
Chris@158
|
63
|
Chris@158
|
64 if (!sndfile) {
|
Chris@158
|
65 cerr << myname << ": Failed to open input file " << infile
|
Chris@158
|
66 << ": " << sf_strerror(sndfile) << endl;
|
Chris@158
|
67 return 1;
|
Chris@158
|
68 }
|
Chris@158
|
69
|
Chris@158
|
70 Chordino *chordino = new Chordino(sfinfo.samplerate);
|
Chris@158
|
71 PluginInputDomainAdapter *ia = new PluginInputDomainAdapter(chordino);
|
Chris@158
|
72 ia->setProcessTimestampMethod(PluginInputDomainAdapter::ShiftData);
|
Chris@158
|
73 PluginBufferingAdapter *adapter = new PluginBufferingAdapter(ia);
|
Chris@158
|
74
|
Chris@158
|
75 int blocksize = adapter->getPreferredBlockSize();
|
Chris@158
|
76
|
Chris@158
|
77 // Plugin requires 1 channel (we will mix down)
|
Chris@158
|
78 if (!adapter->initialise(1, blocksize, blocksize)) {
|
Chris@158
|
79 cerr << myname << ": Failed to initialise Chordino adapter!" << endl;
|
Chris@158
|
80 return 1;
|
Chris@158
|
81 }
|
Chris@158
|
82
|
Chris@158
|
83 float *filebuf = new float[sfinfo.channels * blocksize];
|
Chris@158
|
84 float *mixbuf = new float[blocksize];
|
Chris@158
|
85
|
Chris@158
|
86 Plugin::FeatureList chordFeatures;
|
Chris@158
|
87 Plugin::FeatureSet fs;
|
Chris@158
|
88
|
Chris@158
|
89 int chordFeatureNo = -1;
|
Chris@158
|
90 Plugin::OutputList outputs = adapter->getOutputDescriptors();
|
Chris@158
|
91 for (int i = 0; i < int(outputs.size()); ++i) {
|
Chris@158
|
92 if (outputs[i].identifier == "simplechord") {
|
Chris@158
|
93 chordFeatureNo = i;
|
Chris@158
|
94 }
|
Chris@158
|
95 }
|
Chris@158
|
96 if (chordFeatureNo < 0) {
|
Chris@158
|
97 cerr << myname << ": Failed to identify chords output!" << endl;
|
Chris@158
|
98 return 1;
|
Chris@158
|
99 }
|
Chris@158
|
100
|
Chris@158
|
101 int frame = 0;
|
Chris@158
|
102 while (frame < sfinfo.frames) {
|
Chris@158
|
103
|
Chris@158
|
104 int count = -1;
|
Chris@158
|
105 if ((count = sf_readf_float(sndfile, filebuf, blocksize)) <= 0) break;
|
Chris@158
|
106
|
Chris@158
|
107 // mix down
|
Chris@158
|
108 for (int i = 0; i < blocksize; ++i) {
|
Chris@158
|
109 mixbuf[i] = 0.f;
|
Chris@158
|
110 if (i < count) {
|
Chris@158
|
111 for (int c = 0; c < sfinfo.channels; ++c) {
|
Chris@158
|
112 mixbuf[i] += filebuf[i * sfinfo.channels + c] / sfinfo.channels;
|
Chris@158
|
113 }
|
Chris@158
|
114 }
|
Chris@158
|
115 }
|
Chris@158
|
116
|
Chris@158
|
117 RealTime timestamp = RealTime::frame2RealTime(frame, sfinfo.samplerate);
|
Chris@158
|
118
|
Chris@158
|
119 // feed to plugin: can just take address of buffer, as only one channel
|
Chris@158
|
120 fs = adapter->process(&mixbuf, timestamp);
|
Chris@158
|
121
|
Chris@158
|
122 chordFeatures.insert(chordFeatures.end(),
|
Chris@158
|
123 fs[chordFeatureNo].begin(),
|
Chris@158
|
124 fs[chordFeatureNo].end());
|
Chris@158
|
125
|
Chris@158
|
126 frame += count;
|
Chris@158
|
127 }
|
Chris@158
|
128
|
Chris@158
|
129 sf_close(sndfile);
|
Chris@158
|
130
|
Chris@158
|
131 // features at end of processing (actually Chordino does all its work here)
|
Chris@158
|
132 fs = adapter->getRemainingFeatures();
|
Chris@158
|
133
|
Chris@158
|
134 // chord output is output index 0
|
Chris@158
|
135 chordFeatures.insert(chordFeatures.end(),
|
Chris@158
|
136 fs[chordFeatureNo].begin(),
|
Chris@158
|
137 fs[chordFeatureNo].end());
|
Chris@158
|
138
|
Chris@158
|
139 for (int i = 0; i < (int)chordFeatures.size(); ++i) {
|
Chris@160
|
140 cout << chordFeatures[i].timestamp.toString() << ": "
|
Chris@158
|
141 << chordFeatures[i].label << endl;
|
Chris@158
|
142 }
|
Chris@158
|
143
|
Chris@162
|
144 delete[] filebuf;
|
Chris@162
|
145 delete[] mixbuf;
|
Chris@162
|
146
|
Chris@158
|
147 delete adapter;
|
Chris@158
|
148 }
|
Chris@158
|
149
|