cannam@208
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@208
|
2 /*
|
cannam@208
|
3 Piper C++
|
cannam@208
|
4
|
cannam@208
|
5 An API for audio analysis and feature extraction plugins.
|
cannam@208
|
6
|
cannam@208
|
7 Centre for Digital Music, Queen Mary, University of London.
|
cannam@208
|
8 Copyright 2006-2017 Chris Cannam and QMUL.
|
cannam@208
|
9
|
cannam@208
|
10 Permission is hereby granted, free of charge, to any person
|
cannam@208
|
11 obtaining a copy of this software and associated documentation
|
cannam@208
|
12 files (the "Software"), to deal in the Software without
|
cannam@208
|
13 restriction, including without limitation the rights to use, copy,
|
cannam@208
|
14 modify, merge, publish, distribute, sublicense, and/or sell copies
|
cannam@208
|
15 of the Software, and to permit persons to whom the Software is
|
cannam@208
|
16 furnished to do so, subject to the following conditions:
|
cannam@208
|
17
|
cannam@208
|
18 The above copyright notice and this permission notice shall be
|
cannam@208
|
19 included in all copies or substantial portions of the Software.
|
cannam@208
|
20
|
cannam@208
|
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@208
|
22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@208
|
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
cannam@208
|
24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
cannam@208
|
25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@208
|
26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@208
|
27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@208
|
28
|
cannam@208
|
29 Except as contained in this notice, the names of the Centre for
|
cannam@208
|
30 Digital Music; Queen Mary, University of London; and Chris Cannam
|
cannam@208
|
31 shall not be used in advertising or otherwise to promote the sale,
|
cannam@208
|
32 use or other dealings in this Software without prior written
|
cannam@208
|
33 authorization.
|
cannam@208
|
34 */
|
cannam@208
|
35
|
cannam@208
|
36 #ifndef PIPER_VAMP_PLUGIN_H
|
cannam@208
|
37 #define PIPER_VAMP_PLUGIN_H
|
cannam@208
|
38
|
cannam@208
|
39 #include <vamp-hostsdk/Plugin.h>
|
cannam@208
|
40 #include <vamp-hostsdk/PluginLoader.h>
|
cannam@208
|
41
|
cannam@208
|
42 #include "vamp-support/PluginStaticData.h"
|
cannam@208
|
43 #include "vamp-support/PluginConfiguration.h"
|
cannam@287
|
44 #include "vamp-support/PluginProgramParameters.h"
|
cannam@208
|
45
|
cannam@208
|
46 #include "PluginClient.h"
|
cannam@208
|
47
|
cannam@208
|
48 #include <cstdint>
|
cannam@208
|
49 #include <iostream>
|
cannam@208
|
50
|
cannam@208
|
51 namespace piper_vamp {
|
cannam@208
|
52 namespace client {
|
cannam@208
|
53
|
cannam@210
|
54 /**
|
cannam@210
|
55 * PiperVampPlugin presents a Piper feature extractor in the form of a
|
cannam@210
|
56 * Vamp plugin.
|
cannam@210
|
57 */
|
cannam@208
|
58 class PiperVampPlugin : public Vamp::Plugin
|
cannam@208
|
59 {
|
cannam@208
|
60 enum State {
|
cannam@208
|
61 /**
|
cannam@208
|
62 * The plugin's corresponding Piper feature extractor has been
|
cannam@208
|
63 * loaded but no subsequent state change has happened. This is
|
cannam@208
|
64 * the initial state of PiperVampPlugin on construction, since
|
cannam@208
|
65 * it is associated with a pre-loaded handle.
|
cannam@208
|
66 */
|
cannam@208
|
67 Loaded,
|
cannam@208
|
68
|
cannam@208
|
69 /**
|
cannam@208
|
70 * The plugin has been configured, and the step and block size
|
cannam@208
|
71 * received from the host in its last call to initialise()
|
cannam@208
|
72 * match those that were returned in the configuration
|
cannam@208
|
73 * response (i.e. the server's desired step and block
|
cannam@208
|
74 * size). Our m_config record reflects these correct
|
cannam@208
|
75 * values. The plugin is ready to process.
|
cannam@208
|
76 */
|
cannam@208
|
77 Configured,
|
cannam@208
|
78
|
cannam@208
|
79 /**
|
cannam@208
|
80 * The plugin has been configured, but the step and block size
|
cannam@208
|
81 * received from the host in its last call to initialise()
|
cannam@208
|
82 * differ from those returned by the server in the
|
cannam@208
|
83 * configuration response. Our initialise() call therefore
|
cannam@208
|
84 * returned false, and the plugin cannot be used until the
|
cannam@208
|
85 * host calls initialise() again with the "correct" step and
|
cannam@208
|
86 * block size. Our m_config record reflects these correct
|
cannam@208
|
87 * values, so the host can retrieve them through
|
cannam@208
|
88 * getPreferredStepSize and getPreferredBlockSize.
|
cannam@208
|
89 */
|
cannam@208
|
90 Misconfigured,
|
cannam@208
|
91
|
cannam@208
|
92 /**
|
cannam@208
|
93 * The finish() function has been called and the plugin
|
cannam@208
|
94 * unloaded. No further plugin activity is possible.
|
cannam@208
|
95 */
|
cannam@208
|
96 Finished,
|
cannam@208
|
97
|
cannam@208
|
98 /**
|
cannam@208
|
99 * A call has failed unrecoverably. No further plugin activity
|
cannam@208
|
100 * is possible.
|
cannam@208
|
101 */
|
cannam@208
|
102 Failed
|
cannam@208
|
103 };
|
cannam@208
|
104
|
cannam@208
|
105 public:
|
cannam@208
|
106 PiperVampPlugin(PluginClient *client,
|
cannam@208
|
107 std::string pluginKey,
|
cannam@208
|
108 float inputSampleRate,
|
cannam@208
|
109 int adapterFlags,
|
cannam@208
|
110 PluginStaticData psd,
|
cannam@287
|
111 PluginConfiguration defaultConfig,
|
cannam@287
|
112 PluginProgramParameters programParameters) :
|
cannam@208
|
113 Plugin(inputSampleRate),
|
cannam@208
|
114 m_client(client),
|
cannam@208
|
115 m_key(pluginKey),
|
cannam@208
|
116 m_adapterFlags(adapterFlags),
|
cannam@208
|
117 m_state(Loaded),
|
cannam@208
|
118 m_psd(psd),
|
cannam@208
|
119 m_defaultConfig(defaultConfig),
|
cannam@287
|
120 m_config(defaultConfig),
|
cannam@287
|
121 m_programParameters(programParameters)
|
cannam@208
|
122 { }
|
cannam@289
|
123
|
cannam@208
|
124 virtual ~PiperVampPlugin() {
|
cannam@208
|
125 if (m_state != Finished && m_state != Failed) {
|
cannam@208
|
126 try {
|
cannam@208
|
127 (void)m_client->finish(this);
|
cannam@208
|
128 } catch (const std::exception &e) {
|
cannam@208
|
129 // Finish can throw, but our destructor must not
|
cannam@208
|
130 std::cerr << "WARNING: PiperVampPlugin::~PiperVampPlugin: caught exception from finish(): " << e.what() << std::endl;
|
cannam@208
|
131 }
|
cannam@208
|
132 }
|
cannam@208
|
133 }
|
cannam@208
|
134
|
cannam@280
|
135 std::string getIdentifier() const override {
|
cannam@208
|
136 return m_psd.basic.identifier;
|
cannam@208
|
137 }
|
cannam@208
|
138
|
cannam@280
|
139 std::string getName() const override {
|
cannam@208
|
140 return m_psd.basic.name;
|
cannam@208
|
141 }
|
cannam@208
|
142
|
cannam@280
|
143 std::string getDescription() const override {
|
cannam@208
|
144 return m_psd.basic.description;
|
cannam@208
|
145 }
|
cannam@208
|
146
|
cannam@280
|
147 std::string getMaker() const override {
|
cannam@208
|
148 return m_psd.maker;
|
cannam@208
|
149 }
|
cannam@208
|
150
|
cannam@280
|
151 std::string getCopyright() const override {
|
cannam@208
|
152 return m_psd.copyright;
|
cannam@208
|
153 }
|
cannam@208
|
154
|
cannam@280
|
155 int getPluginVersion() const override {
|
cannam@208
|
156 return m_psd.pluginVersion;
|
cannam@208
|
157 }
|
cannam@208
|
158
|
cannam@280
|
159 ParameterList getParameterDescriptors() const override {
|
cannam@208
|
160 return m_psd.parameters;
|
cannam@208
|
161 }
|
cannam@208
|
162
|
cannam@280
|
163 float getParameter(std::string name) const override {
|
cannam@208
|
164 if (m_config.parameterValues.find(name) != m_config.parameterValues.end()) {
|
cannam@208
|
165 return m_config.parameterValues.at(name);
|
cannam@208
|
166 } else {
|
cannam@208
|
167 return 0.f;
|
cannam@208
|
168 }
|
cannam@208
|
169 }
|
cannam@208
|
170
|
cannam@280
|
171 void setParameter(std::string name, float value) override {
|
cannam@208
|
172 if (m_state == Failed) {
|
cannam@208
|
173 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
174 }
|
cannam@208
|
175 if (m_state != Loaded) {
|
cannam@208
|
176 m_state = Failed;
|
cannam@208
|
177 throw std::logic_error("Can't set parameter after plugin initialised");
|
cannam@208
|
178 }
|
cannam@208
|
179 m_config.parameterValues[name] = value;
|
cannam@208
|
180 }
|
cannam@208
|
181
|
cannam@280
|
182 ProgramList getPrograms() const override {
|
cannam@208
|
183 return m_psd.programs;
|
cannam@208
|
184 }
|
cannam@208
|
185
|
cannam@280
|
186 std::string getCurrentProgram() const override {
|
cannam@208
|
187 return m_config.currentProgram;
|
cannam@208
|
188 }
|
cannam@208
|
189
|
cannam@280
|
190 void selectProgram(std::string program) override {
|
cannam@208
|
191 if (m_state == Failed) {
|
cannam@208
|
192 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
193 }
|
cannam@208
|
194 if (m_state != Loaded) {
|
cannam@208
|
195 m_state = Failed;
|
cannam@208
|
196 throw std::logic_error("Can't select program after plugin initialised");
|
cannam@208
|
197 }
|
cannam@208
|
198 m_config.currentProgram = program;
|
cannam@287
|
199
|
cannam@287
|
200 const auto &pp = m_programParameters.programParameters;
|
cannam@287
|
201 if (pp.find(program) != pp.end()) {
|
cannam@287
|
202 for (auto param: pp.at(program)) {
|
cannam@287
|
203 m_config.parameterValues[param.first] = param.second;
|
cannam@287
|
204 }
|
cannam@287
|
205 }
|
cannam@208
|
206 }
|
cannam@208
|
207
|
cannam@280
|
208 bool initialise(size_t inputChannels,
|
cannam@287
|
209 size_t stepSize,
|
cannam@287
|
210 size_t blockSize) override {
|
cannam@208
|
211
|
cannam@208
|
212 if (m_state == Failed) {
|
cannam@208
|
213 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
214 }
|
cannam@208
|
215
|
cannam@208
|
216 if (m_state == Misconfigured) {
|
cannam@208
|
217 if (int(stepSize) == m_config.framing.stepSize &&
|
cannam@208
|
218 int(blockSize) == m_config.framing.blockSize) {
|
cannam@208
|
219 m_state = Configured;
|
cannam@208
|
220 return true;
|
cannam@208
|
221 } else {
|
cannam@208
|
222 return false;
|
cannam@208
|
223 }
|
cannam@208
|
224 }
|
cannam@208
|
225
|
cannam@208
|
226 if (m_state != Loaded) {
|
cannam@208
|
227 m_state = Failed;
|
cannam@208
|
228 throw std::logic_error("Plugin has already been initialised");
|
cannam@208
|
229 }
|
cannam@208
|
230
|
cannam@208
|
231 m_config.channelCount = int(inputChannels);
|
cannam@208
|
232 m_config.framing.stepSize = int(stepSize);
|
cannam@208
|
233 m_config.framing.blockSize = int(blockSize);
|
cannam@208
|
234
|
cannam@208
|
235 try {
|
cannam@208
|
236 auto response = m_client->configure(this, m_config);
|
cannam@208
|
237 m_outputs = response.outputs;
|
cannam@208
|
238
|
cannam@208
|
239 // Update with the new preferred step and block size now
|
cannam@208
|
240 // that the plugin has taken into account its parameter
|
cannam@208
|
241 // settings. If the values passed in to initialise()
|
cannam@208
|
242 // weren't suitable, then this ensures that a subsequent
|
cannam@208
|
243 // call to getPreferredStepSize/BlockSize on this plugin
|
cannam@208
|
244 // object will at least get acceptable values from now on
|
cannam@208
|
245 m_config.framing = response.framing;
|
cannam@208
|
246
|
cannam@208
|
247 // And if they didn't match up with the passed-in ones,
|
cannam@208
|
248 // lodge ourselves in Misconfigured state and report
|
cannam@208
|
249 // failure so as to provoke the host to call initialise()
|
cannam@208
|
250 // again before any processing.
|
cannam@208
|
251 if (m_config.framing.stepSize != int(stepSize) ||
|
cannam@208
|
252 m_config.framing.blockSize != int(blockSize)) {
|
cannam@208
|
253 m_state = Misconfigured;
|
cannam@208
|
254 return false;
|
cannam@208
|
255 }
|
cannam@208
|
256
|
cannam@264
|
257 } catch (const std::exception &) {
|
cannam@208
|
258 m_state = Failed;
|
cannam@208
|
259 throw;
|
cannam@208
|
260 }
|
cannam@208
|
261
|
cannam@208
|
262 if (!m_outputs.empty()) {
|
cannam@208
|
263 m_state = Configured;
|
cannam@208
|
264 return true;
|
cannam@208
|
265 } else {
|
cannam@208
|
266 return false;
|
cannam@208
|
267 }
|
cannam@208
|
268 }
|
cannam@208
|
269
|
cannam@280
|
270 void reset() override {
|
cannam@208
|
271
|
cannam@208
|
272 if (m_state == Failed) {
|
cannam@208
|
273 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
274 }
|
cannam@208
|
275 if (m_state == Loaded || m_state == Misconfigured) {
|
cannam@208
|
276 // reset is a no-op if the plugin hasn't been initialised yet
|
cannam@208
|
277 return;
|
cannam@208
|
278 }
|
cannam@208
|
279
|
cannam@208
|
280 try {
|
cannam@208
|
281 m_client->reset(this, m_config);
|
cannam@264
|
282 } catch (const std::exception &) {
|
cannam@208
|
283 m_state = Failed;
|
cannam@208
|
284 throw;
|
cannam@208
|
285 }
|
cannam@208
|
286
|
cannam@208
|
287 m_state = Configured;
|
cannam@208
|
288 }
|
cannam@208
|
289
|
cannam@280
|
290 InputDomain getInputDomain() const override {
|
cannam@208
|
291 return m_psd.inputDomain;
|
cannam@208
|
292 }
|
cannam@208
|
293
|
cannam@280
|
294 size_t getPreferredBlockSize() const override {
|
cannam@208
|
295 // Return this from m_config instead of m_defaultConfig, so
|
cannam@208
|
296 // that it gets updated in the event of an initialise() call
|
cannam@208
|
297 // that fails for the wrong value
|
cannam@208
|
298 return m_config.framing.blockSize;
|
cannam@208
|
299 }
|
cannam@208
|
300
|
cannam@280
|
301 size_t getPreferredStepSize() const override {
|
cannam@208
|
302 // Return this from m_config instead of m_defaultConfig, so
|
cannam@208
|
303 // that it gets updated in the event of an initialise() call
|
cannam@208
|
304 // that fails for the wrong value
|
cannam@208
|
305 return m_config.framing.stepSize;
|
cannam@208
|
306 }
|
cannam@208
|
307
|
cannam@280
|
308 size_t getMinChannelCount() const override {
|
cannam@208
|
309 return m_psd.minChannelCount;
|
cannam@208
|
310 }
|
cannam@208
|
311
|
cannam@280
|
312 size_t getMaxChannelCount() const override {
|
cannam@208
|
313 return m_psd.maxChannelCount;
|
cannam@208
|
314 }
|
cannam@208
|
315
|
cannam@280
|
316 OutputList getOutputDescriptors() const override {
|
cannam@208
|
317
|
cannam@208
|
318 if (m_state == Failed) {
|
cannam@208
|
319 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
320 }
|
cannam@208
|
321 if (m_state == Configured) {
|
cannam@208
|
322 return m_outputs;
|
cannam@208
|
323 }
|
cannam@208
|
324
|
cannam@208
|
325 //!!! todo: figure out for which hosts (and adapters?) it may
|
cannam@208
|
326 //!!! be a problem that the output descriptors are incomplete
|
cannam@208
|
327 //!!! here. Any such hosts/adapters are broken, but I bet they
|
cannam@208
|
328 //!!! exist
|
cannam@208
|
329
|
cannam@208
|
330 OutputList staticOutputs;
|
cannam@208
|
331 for (const auto &o: m_psd.basicOutputInfo) {
|
cannam@208
|
332 OutputDescriptor od;
|
cannam@208
|
333 od.identifier = o.identifier;
|
cannam@208
|
334 od.name = o.name;
|
cannam@208
|
335 od.description = o.description;
|
cannam@208
|
336 staticOutputs.push_back(od);
|
cannam@208
|
337 }
|
cannam@208
|
338 return staticOutputs;
|
cannam@208
|
339 }
|
cannam@208
|
340
|
cannam@280
|
341 FeatureSet process(const float *const *inputBuffers,
|
cannam@280
|
342 Vamp::RealTime timestamp) override {
|
cannam@208
|
343
|
cannam@208
|
344 if (m_state == Failed) {
|
cannam@208
|
345 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
346 }
|
cannam@208
|
347 if (m_state == Loaded || m_state == Misconfigured) {
|
cannam@208
|
348 m_state = Failed;
|
cannam@208
|
349 throw std::logic_error("Plugin has not been initialised");
|
cannam@208
|
350 }
|
cannam@208
|
351 if (m_state == Finished) {
|
cannam@208
|
352 m_state = Failed;
|
cannam@208
|
353 throw std::logic_error("Plugin has already been disposed of");
|
cannam@208
|
354 }
|
cannam@208
|
355
|
cannam@208
|
356 std::vector<std::vector<float> > vecbuf;
|
cannam@273
|
357
|
cannam@273
|
358 int bufferSize;
|
cannam@273
|
359 if (m_psd.inputDomain == FrequencyDomain) {
|
cannam@273
|
360 bufferSize = 2 * (m_config.framing.blockSize / 2) + 2;
|
cannam@273
|
361 } else {
|
cannam@273
|
362 bufferSize = m_config.framing.blockSize;
|
cannam@273
|
363 }
|
cannam@273
|
364
|
cannam@208
|
365 for (int c = 0; c < m_config.channelCount; ++c) {
|
cannam@208
|
366 vecbuf.push_back(std::vector<float>
|
cannam@273
|
367 (inputBuffers[c], inputBuffers[c] + bufferSize));
|
cannam@208
|
368 }
|
cannam@208
|
369
|
cannam@208
|
370 try {
|
cannam@208
|
371 return m_client->process(this, vecbuf, timestamp);
|
cannam@264
|
372 } catch (const std::exception &) {
|
cannam@208
|
373 m_state = Failed;
|
cannam@208
|
374 throw;
|
cannam@208
|
375 }
|
cannam@208
|
376 }
|
cannam@208
|
377
|
cannam@280
|
378 FeatureSet getRemainingFeatures() override {
|
cannam@208
|
379
|
cannam@208
|
380 if (m_state == Failed) {
|
cannam@208
|
381 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
382 }
|
cannam@208
|
383 if (m_state == Loaded || m_state == Misconfigured) {
|
cannam@208
|
384 m_state = Failed;
|
cannam@208
|
385 throw std::logic_error("Plugin has not been configured");
|
cannam@208
|
386 }
|
cannam@208
|
387 if (m_state == Finished) {
|
cannam@208
|
388 m_state = Failed;
|
cannam@208
|
389 throw std::logic_error("Plugin has already been disposed of");
|
cannam@208
|
390 }
|
cannam@208
|
391
|
cannam@208
|
392 m_state = Finished;
|
cannam@208
|
393
|
cannam@208
|
394 try {
|
cannam@208
|
395 return m_client->finish(this);
|
cannam@264
|
396 } catch (const std::exception &) {
|
cannam@208
|
397 m_state = Failed;
|
cannam@208
|
398 throw;
|
cannam@208
|
399 }
|
cannam@208
|
400 }
|
cannam@208
|
401
|
cannam@208
|
402 // Not Plugin methods, but needed by the PluginClient to support reloads:
|
cannam@208
|
403
|
cannam@208
|
404 virtual float getInputSampleRate() const {
|
cannam@208
|
405 return m_inputSampleRate;
|
cannam@208
|
406 }
|
cannam@208
|
407
|
cannam@208
|
408 virtual std::string getPluginKey() const {
|
cannam@208
|
409 return m_key;
|
cannam@208
|
410 }
|
cannam@208
|
411
|
cannam@208
|
412 virtual int getAdapterFlags() const {
|
cannam@208
|
413 return m_adapterFlags;
|
cannam@208
|
414 }
|
cannam@208
|
415
|
cannam@208
|
416 private:
|
cannam@208
|
417 PluginClient *m_client;
|
cannam@208
|
418 std::string m_key;
|
cannam@208
|
419 int m_adapterFlags;
|
cannam@208
|
420 State m_state;
|
cannam@208
|
421 PluginStaticData m_psd;
|
cannam@208
|
422 OutputList m_outputs;
|
cannam@208
|
423 PluginConfiguration m_defaultConfig;
|
cannam@208
|
424 PluginConfiguration m_config;
|
cannam@287
|
425 PluginProgramParameters m_programParameters;
|
cannam@208
|
426 };
|
cannam@208
|
427
|
cannam@208
|
428 }
|
cannam@208
|
429 }
|
cannam@208
|
430
|
cannam@208
|
431 #endif
|