cannam@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@0
|
2 /*
|
cannam@0
|
3 Vamp feature extraction plugin using the OnsetsDS onset detector.
|
cannam@0
|
4 This file copyright (c) 2008 Chris Cannam.
|
cannam@0
|
5
|
cannam@0
|
6 OnsetsDS - real time musical onset detection library.
|
cannam@0
|
7 Copyright (c) 2007 Dan Stowell. All rights reserved.
|
cannam@0
|
8
|
cannam@0
|
9 This program is free software; you can redistribute it and/or modify
|
cannam@0
|
10 it under the terms of the GNU General Public License as published by
|
cannam@0
|
11 the Free Software Foundation; either version 2 of the License, or
|
cannam@0
|
12 (at your option) any later version.
|
cannam@0
|
13
|
cannam@0
|
14 This program is distributed in the hope that it will be useful,
|
cannam@0
|
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
cannam@0
|
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
cannam@0
|
17 GNU General Public License for more details.
|
cannam@0
|
18
|
cannam@0
|
19 You should have received a copy of the GNU General Public License
|
cannam@0
|
20 along with this program; if not, write to the Free Software
|
cannam@0
|
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
cannam@0
|
22 */
|
cannam@0
|
23
|
cannam@0
|
24 #include "onsetsdsplugin.h"
|
cannam@0
|
25 #include <vamp-sdk/PluginAdapter.h>
|
cannam@0
|
26
|
cannam@0
|
27 using std::string;
|
cannam@0
|
28 using std::vector;
|
cannam@0
|
29 using std::cerr;
|
cannam@0
|
30 using std::endl;
|
cannam@0
|
31
|
cannam@0
|
32
|
cannam@0
|
33 OnsetsDSPlugin::OnsetsDSPlugin(float inputSampleRate) :
|
cannam@0
|
34 Vamp::Plugin(inputSampleRate),
|
cannam@0
|
35 m_ods(0),
|
cannam@0
|
36 m_odsdata(0),
|
cannam@0
|
37 m_dfType(ODS_ODF_RCOMPLEX),
|
cannam@0
|
38 m_medspan(11),
|
cannam@0
|
39 m_stepSize(512),
|
cannam@0
|
40 m_fftSize(1024)
|
cannam@0
|
41 {
|
cannam@0
|
42 }
|
cannam@0
|
43
|
cannam@0
|
44 OnsetsDSPlugin::~OnsetsDSPlugin()
|
cannam@0
|
45 {
|
cannam@0
|
46 delete[] m_odsdata;
|
cannam@0
|
47 delete m_ods;
|
cannam@0
|
48 }
|
cannam@0
|
49
|
cannam@0
|
50 string
|
cannam@0
|
51 OnsetsDSPlugin::getIdentifier() const
|
cannam@0
|
52 {
|
cannam@0
|
53 return "onsetsds";
|
cannam@0
|
54 }
|
cannam@0
|
55
|
cannam@0
|
56 string
|
cannam@0
|
57 OnsetsDSPlugin::getName() const
|
cannam@0
|
58 {
|
cannam@0
|
59 return "OnsetsDS Onset Detector";
|
cannam@0
|
60 }
|
cannam@0
|
61
|
cannam@0
|
62 string
|
cannam@0
|
63 OnsetsDSPlugin::getDescription() const
|
cannam@0
|
64 {
|
cannam@0
|
65 return "Detect note onsets";
|
cannam@0
|
66 }
|
cannam@0
|
67
|
cannam@0
|
68 string
|
cannam@0
|
69 OnsetsDSPlugin::getMaker() const
|
cannam@0
|
70 {
|
cannam@0
|
71 return "Dan Stowell";
|
cannam@0
|
72 }
|
cannam@0
|
73
|
cannam@0
|
74 int
|
cannam@0
|
75 OnsetsDSPlugin::getPluginVersion() const
|
cannam@0
|
76 {
|
cannam@0
|
77 return 1;
|
cannam@0
|
78 }
|
cannam@0
|
79
|
cannam@0
|
80 string
|
cannam@0
|
81 OnsetsDSPlugin::getCopyright() const
|
cannam@0
|
82 {
|
cannam@0
|
83 return "Copyright (c) 2007-2008 Dan Stowell";
|
cannam@0
|
84 }
|
cannam@0
|
85
|
cannam@0
|
86 OnsetsDSPlugin::ParameterList
|
cannam@0
|
87 OnsetsDSPlugin::getParameterDescriptors() const
|
cannam@0
|
88 {
|
cannam@0
|
89 ParameterList list;
|
cannam@0
|
90
|
cannam@0
|
91 ParameterDescriptor desc;
|
cannam@0
|
92 desc.identifier = "dftype";
|
cannam@0
|
93 desc.name = "Onset detection function";
|
cannam@0
|
94 desc.description = "Method used to calculate the onset detection function";
|
cannam@0
|
95 desc.minValue = 0;
|
cannam@0
|
96 desc.maxValue = 6;
|
cannam@0
|
97 desc.defaultValue = 3;
|
cannam@0
|
98 desc.isQuantized = true;
|
cannam@0
|
99 desc.quantizeStep = 1;
|
cannam@0
|
100 desc.valueNames.push_back("Power");
|
cannam@0
|
101 desc.valueNames.push_back("Sum of magnitudes");
|
cannam@0
|
102 desc.valueNames.push_back("Complex-domain deviation");
|
cannam@0
|
103 desc.valueNames.push_back("Rectified complex-domain deviation");
|
cannam@0
|
104 desc.valueNames.push_back("Phase deviation");
|
cannam@0
|
105 desc.valueNames.push_back("Weighted phase deviation");
|
cannam@0
|
106 desc.valueNames.push_back("Modified Kullback-Liebler deviation");
|
cannam@0
|
107 list.push_back(desc);
|
cannam@0
|
108
|
cannam@0
|
109 desc.identifier = "medspan";
|
cannam@0
|
110 desc.name = "Median frame span";
|
cannam@0
|
111 desc.description = "Number of past frames used in median calculation";
|
cannam@0
|
112 desc.minValue = 5;
|
cannam@0
|
113 desc.maxValue = 21;
|
cannam@0
|
114 desc.defaultValue = 11;
|
cannam@0
|
115 desc.isQuantized = true;
|
cannam@0
|
116 desc.quantizeStep = 2;
|
cannam@0
|
117 desc.valueNames.clear();
|
cannam@0
|
118 list.push_back(desc);
|
cannam@0
|
119
|
cannam@0
|
120 return list;
|
cannam@0
|
121 }
|
cannam@0
|
122
|
cannam@0
|
123 float
|
cannam@0
|
124 OnsetsDSPlugin::getParameter(std::string name) const
|
cannam@0
|
125 {
|
cannam@0
|
126 if (name == "dftype") {
|
cannam@0
|
127 switch (m_dfType) {
|
cannam@0
|
128 case ODS_ODF_POWER: return 0;
|
cannam@0
|
129 case ODS_ODF_MAGSUM: return 1;
|
cannam@0
|
130 case ODS_ODF_COMPLEX: return 2;
|
cannam@0
|
131 case ODS_ODF_RCOMPLEX: return 3;
|
cannam@0
|
132 case ODS_ODF_PHASE: return 4;
|
cannam@0
|
133 case ODS_ODF_WPHASE: return 5;
|
cannam@0
|
134 case ODS_ODF_MKL: return 6;
|
cannam@0
|
135 }
|
cannam@0
|
136 } else if (name == "medspan") {
|
cannam@0
|
137 return m_medspan;
|
cannam@0
|
138 }
|
cannam@0
|
139 return 0.0;
|
cannam@0
|
140 }
|
cannam@0
|
141
|
cannam@0
|
142 void
|
cannam@0
|
143 OnsetsDSPlugin::setParameter(std::string name, float value)
|
cannam@0
|
144 {
|
cannam@0
|
145 if (name == "dftype") {
|
cannam@0
|
146 onsetsds_odf_types dfType = m_dfType;
|
cannam@0
|
147 switch (lrintf(value)) {
|
cannam@0
|
148 case 0: dfType = ODS_ODF_POWER; break;
|
cannam@0
|
149 case 1: dfType = ODS_ODF_MAGSUM; break;
|
cannam@0
|
150 case 2: dfType = ODS_ODF_COMPLEX; break;
|
cannam@0
|
151 case 3: dfType = ODS_ODF_RCOMPLEX; break;
|
cannam@0
|
152 case 4: dfType = ODS_ODF_PHASE; break;
|
cannam@0
|
153 case 5: dfType = ODS_ODF_WPHASE; break;
|
cannam@0
|
154 case 6: dfType = ODS_ODF_MKL; break;
|
cannam@0
|
155 }
|
cannam@0
|
156 if (dfType == m_dfType) return;
|
cannam@0
|
157 m_dfType = dfType;
|
cannam@0
|
158 } else if (name == "medspan") {
|
cannam@0
|
159 m_medspan = lrintf(value);
|
cannam@0
|
160 }
|
cannam@0
|
161 }
|
cannam@0
|
162
|
cannam@0
|
163 bool
|
cannam@0
|
164 OnsetsDSPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
cannam@0
|
165 {
|
cannam@0
|
166 if (channels < getMinChannelCount() ||
|
cannam@0
|
167 channels > getMaxChannelCount()) {
|
cannam@0
|
168 std::cerr << "OnsetsDSPlugin::initialise: Unsupported channel count: "
|
cannam@0
|
169 << channels << std::endl;
|
cannam@0
|
170 return false;
|
cannam@0
|
171 }
|
cannam@0
|
172
|
cannam@0
|
173 if (stepSize != getPreferredStepSize()) {
|
cannam@0
|
174 std::cerr << "WARNING: OnsetsDSPlugin::initialise: Using unusual step size: "
|
cannam@0
|
175 << stepSize << " (wanted " << (getPreferredStepSize()) << ")" << std::endl;
|
cannam@0
|
176 }
|
cannam@0
|
177
|
cannam@0
|
178 if (blockSize != getPreferredBlockSize()) {
|
cannam@0
|
179 std::cerr << "WARNING: OnsetsDSPlugin::initialise: Using unusual block size: "
|
cannam@0
|
180 << blockSize << " (wanted " << (getPreferredBlockSize()) << ")" << std::endl;
|
cannam@0
|
181 }
|
cannam@0
|
182
|
cannam@0
|
183 m_stepSize = stepSize;
|
cannam@0
|
184 m_fftSize = blockSize;
|
cannam@0
|
185
|
cannam@0
|
186 delete[] m_odsdata;
|
cannam@0
|
187 delete m_ods;
|
cannam@0
|
188
|
cannam@0
|
189 m_odsdata = new float[onsetsds_memneeded(m_dfType, m_fftSize, m_medspan)];
|
cannam@0
|
190 m_ods = new OnsetsDS;
|
cannam@0
|
191 memset(m_ods, 0, sizeof(OnsetsDS));
|
cannam@0
|
192 onsetsds_init(m_ods, m_odsdata, ODS_FFT_FFTW3_R2C, m_dfType, m_fftSize,
|
cannam@0
|
193 m_medspan, m_inputSampleRate);
|
cannam@0
|
194
|
cannam@0
|
195 return true;
|
cannam@0
|
196 }
|
cannam@0
|
197
|
cannam@0
|
198 void
|
cannam@0
|
199 OnsetsDSPlugin::reset()
|
cannam@0
|
200 {
|
cannam@0
|
201 if (!m_ods) {
|
cannam@0
|
202 std::cerr << "ERROR: OnsetsDSPlugin::reset: Plugin has not been initialised" << std::endl;
|
cannam@0
|
203 return;
|
cannam@0
|
204 }
|
cannam@0
|
205 onsetsds_init(m_ods, m_odsdata, ODS_FFT_FFTW3_R2C, m_dfType, m_fftSize,
|
cannam@0
|
206 m_medspan, m_inputSampleRate);
|
cannam@0
|
207 }
|
cannam@0
|
208
|
cannam@0
|
209 size_t
|
cannam@0
|
210 OnsetsDSPlugin::getPreferredStepSize() const
|
cannam@0
|
211 {
|
cannam@0
|
212 return 512;
|
cannam@0
|
213 }
|
cannam@0
|
214
|
cannam@0
|
215 size_t
|
cannam@0
|
216 OnsetsDSPlugin::getPreferredBlockSize() const
|
cannam@0
|
217 {
|
cannam@0
|
218 return 1024;
|
cannam@0
|
219 }
|
cannam@0
|
220
|
cannam@0
|
221 OnsetsDSPlugin::OutputList
|
cannam@0
|
222 OnsetsDSPlugin::getOutputDescriptors() const
|
cannam@0
|
223 {
|
cannam@0
|
224 OutputList list;
|
cannam@0
|
225
|
cannam@0
|
226 OutputDescriptor onsets;
|
cannam@0
|
227 onsets.identifier = "onsets";
|
cannam@0
|
228 onsets.name = "Note Onsets";
|
cannam@0
|
229 onsets.description = "Note onset positions";
|
cannam@0
|
230 onsets.unit = "";
|
cannam@0
|
231 onsets.hasFixedBinCount = true;
|
cannam@0
|
232 onsets.binCount = 0;
|
cannam@0
|
233 onsets.sampleType = OutputDescriptor::VariableSampleRate;
|
cannam@0
|
234 onsets.sampleRate = (m_inputSampleRate / m_stepSize);
|
cannam@0
|
235
|
cannam@0
|
236 list.push_back(onsets);
|
cannam@0
|
237
|
cannam@0
|
238 return list;
|
cannam@0
|
239 }
|
cannam@0
|
240
|
cannam@0
|
241 OnsetsDSPlugin::FeatureSet
|
cannam@0
|
242 OnsetsDSPlugin::process(const float *const *inputBuffers,
|
cannam@0
|
243 Vamp::RealTime timestamp)
|
cannam@0
|
244 {
|
cannam@0
|
245 if (!m_ods) {
|
cannam@0
|
246 cerr << "ERROR: OnsetsDSPlugin::process: Plugin has not been initialised"
|
cannam@0
|
247 << endl;
|
cannam@0
|
248 return FeatureSet();
|
cannam@0
|
249 }
|
cannam@0
|
250
|
cannam@0
|
251 // We can const_cast because we happen to know onsetsds_process
|
cannam@0
|
252 // does not modify this buffer
|
cannam@0
|
253 bool result = onsetsds_process(m_ods, const_cast<float *>(inputBuffers[0]));
|
cannam@0
|
254
|
cannam@0
|
255 FeatureSet returnFeatures;
|
cannam@0
|
256
|
cannam@0
|
257 if (result) {
|
cannam@0
|
258 Feature feature;
|
cannam@0
|
259 feature.hasTimestamp = true;
|
cannam@0
|
260 feature.timestamp = timestamp;
|
cannam@0
|
261 returnFeatures[0].push_back(feature); // onsets are output 0
|
cannam@0
|
262 }
|
cannam@0
|
263
|
cannam@0
|
264 return returnFeatures;
|
cannam@0
|
265 }
|
cannam@0
|
266
|
cannam@0
|
267 OnsetsDSPlugin::FeatureSet
|
cannam@0
|
268 OnsetsDSPlugin::getRemainingFeatures()
|
cannam@0
|
269 {
|
cannam@0
|
270 return FeatureSet();
|
cannam@0
|
271 }
|
cannam@0
|
272
|
cannam@0
|
273
|
cannam@0
|
274 static Vamp::PluginAdapter<OnsetsDSPlugin> adapter;
|
cannam@0
|
275
|
cannam@0
|
276 const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int version,
|
cannam@0
|
277 unsigned int index)
|
cannam@0
|
278 {
|
cannam@0
|
279 if (version < 1) return 0;
|
cannam@0
|
280
|
cannam@0
|
281 switch (index) {
|
cannam@0
|
282 case 0: return adapter.getDescriptor();
|
cannam@0
|
283 default: return 0;
|
cannam@0
|
284 }
|
cannam@0
|
285 }
|
cannam@0
|
286
|