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@208
|
44
|
cannam@208
|
45 #include "PluginClient.h"
|
cannam@208
|
46
|
cannam@208
|
47 #include <cstdint>
|
cannam@208
|
48 #include <iostream>
|
cannam@208
|
49
|
cannam@208
|
50 namespace piper_vamp {
|
cannam@208
|
51 namespace client {
|
cannam@208
|
52
|
cannam@210
|
53 /**
|
cannam@210
|
54 * PiperVampPlugin presents a Piper feature extractor in the form of a
|
cannam@210
|
55 * Vamp plugin.
|
cannam@210
|
56 */
|
cannam@208
|
57 class PiperVampPlugin : public Vamp::Plugin
|
cannam@208
|
58 {
|
cannam@208
|
59 enum State {
|
cannam@208
|
60 /**
|
cannam@208
|
61 * The plugin's corresponding Piper feature extractor has been
|
cannam@208
|
62 * loaded but no subsequent state change has happened. This is
|
cannam@208
|
63 * the initial state of PiperVampPlugin on construction, since
|
cannam@208
|
64 * it is associated with a pre-loaded handle.
|
cannam@208
|
65 */
|
cannam@208
|
66 Loaded,
|
cannam@208
|
67
|
cannam@208
|
68 /**
|
cannam@208
|
69 * The plugin has been configured, and the step and block size
|
cannam@208
|
70 * received from the host in its last call to initialise()
|
cannam@208
|
71 * match those that were returned in the configuration
|
cannam@208
|
72 * response (i.e. the server's desired step and block
|
cannam@208
|
73 * size). Our m_config record reflects these correct
|
cannam@208
|
74 * values. The plugin is ready to process.
|
cannam@208
|
75 */
|
cannam@208
|
76 Configured,
|
cannam@208
|
77
|
cannam@208
|
78 /**
|
cannam@208
|
79 * The plugin has been configured, but the step and block size
|
cannam@208
|
80 * received from the host in its last call to initialise()
|
cannam@208
|
81 * differ from those returned by the server in the
|
cannam@208
|
82 * configuration response. Our initialise() call therefore
|
cannam@208
|
83 * returned false, and the plugin cannot be used until the
|
cannam@208
|
84 * host calls initialise() again with the "correct" step and
|
cannam@208
|
85 * block size. Our m_config record reflects these correct
|
cannam@208
|
86 * values, so the host can retrieve them through
|
cannam@208
|
87 * getPreferredStepSize and getPreferredBlockSize.
|
cannam@208
|
88 */
|
cannam@208
|
89 Misconfigured,
|
cannam@208
|
90
|
cannam@208
|
91 /**
|
cannam@208
|
92 * The finish() function has been called and the plugin
|
cannam@208
|
93 * unloaded. No further plugin activity is possible.
|
cannam@208
|
94 */
|
cannam@208
|
95 Finished,
|
cannam@208
|
96
|
cannam@208
|
97 /**
|
cannam@208
|
98 * A call has failed unrecoverably. No further plugin activity
|
cannam@208
|
99 * is possible.
|
cannam@208
|
100 */
|
cannam@208
|
101 Failed
|
cannam@208
|
102 };
|
cannam@208
|
103
|
cannam@208
|
104 public:
|
cannam@208
|
105 PiperVampPlugin(PluginClient *client,
|
cannam@208
|
106 std::string pluginKey,
|
cannam@208
|
107 float inputSampleRate,
|
cannam@208
|
108 int adapterFlags,
|
cannam@208
|
109 PluginStaticData psd,
|
cannam@208
|
110 PluginConfiguration defaultConfig) :
|
cannam@208
|
111 Plugin(inputSampleRate),
|
cannam@208
|
112 m_client(client),
|
cannam@208
|
113 m_key(pluginKey),
|
cannam@208
|
114 m_adapterFlags(adapterFlags),
|
cannam@208
|
115 m_state(Loaded),
|
cannam@208
|
116 m_psd(psd),
|
cannam@208
|
117 m_defaultConfig(defaultConfig),
|
cannam@208
|
118 m_config(defaultConfig)
|
cannam@208
|
119 { }
|
cannam@208
|
120
|
cannam@208
|
121 virtual ~PiperVampPlugin() {
|
cannam@208
|
122 if (m_state != Finished && m_state != Failed) {
|
cannam@208
|
123 try {
|
cannam@208
|
124 (void)m_client->finish(this);
|
cannam@208
|
125 } catch (const std::exception &e) {
|
cannam@208
|
126 // Finish can throw, but our destructor must not
|
cannam@208
|
127 std::cerr << "WARNING: PiperVampPlugin::~PiperVampPlugin: caught exception from finish(): " << e.what() << std::endl;
|
cannam@208
|
128 }
|
cannam@208
|
129 }
|
cannam@208
|
130 }
|
cannam@208
|
131
|
cannam@208
|
132 virtual std::string getIdentifier() const {
|
cannam@208
|
133 return m_psd.basic.identifier;
|
cannam@208
|
134 }
|
cannam@208
|
135
|
cannam@208
|
136 virtual std::string getName() const {
|
cannam@208
|
137 return m_psd.basic.name;
|
cannam@208
|
138 }
|
cannam@208
|
139
|
cannam@208
|
140 virtual std::string getDescription() const {
|
cannam@208
|
141 return m_psd.basic.description;
|
cannam@208
|
142 }
|
cannam@208
|
143
|
cannam@208
|
144 virtual std::string getMaker() const {
|
cannam@208
|
145 return m_psd.maker;
|
cannam@208
|
146 }
|
cannam@208
|
147
|
cannam@208
|
148 virtual std::string getCopyright() const {
|
cannam@208
|
149 return m_psd.copyright;
|
cannam@208
|
150 }
|
cannam@208
|
151
|
cannam@208
|
152 virtual int getPluginVersion() const {
|
cannam@208
|
153 return m_psd.pluginVersion;
|
cannam@208
|
154 }
|
cannam@208
|
155
|
cannam@208
|
156 virtual ParameterList getParameterDescriptors() const {
|
cannam@208
|
157 return m_psd.parameters;
|
cannam@208
|
158 }
|
cannam@208
|
159
|
cannam@208
|
160 virtual float getParameter(std::string name) const {
|
cannam@208
|
161 if (m_config.parameterValues.find(name) != m_config.parameterValues.end()) {
|
cannam@208
|
162 return m_config.parameterValues.at(name);
|
cannam@208
|
163 } else {
|
cannam@208
|
164 return 0.f;
|
cannam@208
|
165 }
|
cannam@208
|
166 }
|
cannam@208
|
167
|
cannam@208
|
168 virtual void setParameter(std::string name, float value) {
|
cannam@208
|
169 if (m_state == Failed) {
|
cannam@208
|
170 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
171 }
|
cannam@208
|
172 if (m_state != Loaded) {
|
cannam@208
|
173 m_state = Failed;
|
cannam@208
|
174 throw std::logic_error("Can't set parameter after plugin initialised");
|
cannam@208
|
175 }
|
cannam@208
|
176 m_config.parameterValues[name] = value;
|
cannam@208
|
177 }
|
cannam@208
|
178
|
cannam@208
|
179 virtual ProgramList getPrograms() const {
|
cannam@208
|
180 return m_psd.programs;
|
cannam@208
|
181 }
|
cannam@208
|
182
|
cannam@208
|
183 virtual std::string getCurrentProgram() const {
|
cannam@208
|
184 return m_config.currentProgram;
|
cannam@208
|
185 }
|
cannam@208
|
186
|
cannam@208
|
187 virtual void selectProgram(std::string program) {
|
cannam@208
|
188 if (m_state == Failed) {
|
cannam@208
|
189 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
190 }
|
cannam@208
|
191 if (m_state != Loaded) {
|
cannam@208
|
192 m_state = Failed;
|
cannam@208
|
193 throw std::logic_error("Can't select program after plugin initialised");
|
cannam@208
|
194 }
|
cannam@208
|
195 m_config.currentProgram = program;
|
cannam@208
|
196 }
|
cannam@208
|
197
|
cannam@208
|
198 virtual bool initialise(size_t inputChannels,
|
cannam@208
|
199 size_t stepSize,
|
cannam@208
|
200 size_t blockSize) {
|
cannam@208
|
201
|
cannam@208
|
202 if (m_state == Failed) {
|
cannam@208
|
203 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
204 }
|
cannam@208
|
205
|
cannam@208
|
206 if (m_state == Misconfigured) {
|
cannam@208
|
207 if (int(stepSize) == m_config.framing.stepSize &&
|
cannam@208
|
208 int(blockSize) == m_config.framing.blockSize) {
|
cannam@208
|
209 m_state = Configured;
|
cannam@208
|
210 return true;
|
cannam@208
|
211 } else {
|
cannam@208
|
212 return false;
|
cannam@208
|
213 }
|
cannam@208
|
214 }
|
cannam@208
|
215
|
cannam@208
|
216 if (m_state != Loaded) {
|
cannam@208
|
217 m_state = Failed;
|
cannam@208
|
218 throw std::logic_error("Plugin has already been initialised");
|
cannam@208
|
219 }
|
cannam@208
|
220
|
cannam@208
|
221 m_config.channelCount = int(inputChannels);
|
cannam@208
|
222 m_config.framing.stepSize = int(stepSize);
|
cannam@208
|
223 m_config.framing.blockSize = int(blockSize);
|
cannam@208
|
224
|
cannam@208
|
225 try {
|
cannam@208
|
226 auto response = m_client->configure(this, m_config);
|
cannam@208
|
227 m_outputs = response.outputs;
|
cannam@208
|
228
|
cannam@208
|
229 // Update with the new preferred step and block size now
|
cannam@208
|
230 // that the plugin has taken into account its parameter
|
cannam@208
|
231 // settings. If the values passed in to initialise()
|
cannam@208
|
232 // weren't suitable, then this ensures that a subsequent
|
cannam@208
|
233 // call to getPreferredStepSize/BlockSize on this plugin
|
cannam@208
|
234 // object will at least get acceptable values from now on
|
cannam@208
|
235 m_config.framing = response.framing;
|
cannam@208
|
236
|
cannam@208
|
237 // And if they didn't match up with the passed-in ones,
|
cannam@208
|
238 // lodge ourselves in Misconfigured state and report
|
cannam@208
|
239 // failure so as to provoke the host to call initialise()
|
cannam@208
|
240 // again before any processing.
|
cannam@208
|
241 if (m_config.framing.stepSize != int(stepSize) ||
|
cannam@208
|
242 m_config.framing.blockSize != int(blockSize)) {
|
cannam@208
|
243 m_state = Misconfigured;
|
cannam@208
|
244 return false;
|
cannam@208
|
245 }
|
cannam@208
|
246
|
cannam@208
|
247 } catch (const std::exception &e) {
|
cannam@208
|
248 m_state = Failed;
|
cannam@208
|
249 throw;
|
cannam@208
|
250 }
|
cannam@208
|
251
|
cannam@208
|
252 if (!m_outputs.empty()) {
|
cannam@208
|
253 m_state = Configured;
|
cannam@208
|
254 return true;
|
cannam@208
|
255 } else {
|
cannam@208
|
256 return false;
|
cannam@208
|
257 }
|
cannam@208
|
258 }
|
cannam@208
|
259
|
cannam@208
|
260 virtual void reset() {
|
cannam@208
|
261
|
cannam@208
|
262 if (m_state == Failed) {
|
cannam@208
|
263 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
264 }
|
cannam@208
|
265 if (m_state == Loaded || m_state == Misconfigured) {
|
cannam@208
|
266 // reset is a no-op if the plugin hasn't been initialised yet
|
cannam@208
|
267 return;
|
cannam@208
|
268 }
|
cannam@208
|
269
|
cannam@208
|
270 try {
|
cannam@208
|
271 m_client->reset(this, m_config);
|
cannam@208
|
272 } catch (const std::exception &e) {
|
cannam@208
|
273 m_state = Failed;
|
cannam@208
|
274 throw;
|
cannam@208
|
275 }
|
cannam@208
|
276
|
cannam@208
|
277 m_state = Configured;
|
cannam@208
|
278 }
|
cannam@208
|
279
|
cannam@208
|
280 virtual InputDomain getInputDomain() const {
|
cannam@208
|
281 return m_psd.inputDomain;
|
cannam@208
|
282 }
|
cannam@208
|
283
|
cannam@208
|
284 virtual size_t getPreferredBlockSize() const {
|
cannam@208
|
285 // Return this from m_config instead of m_defaultConfig, so
|
cannam@208
|
286 // that it gets updated in the event of an initialise() call
|
cannam@208
|
287 // that fails for the wrong value
|
cannam@208
|
288 return m_config.framing.blockSize;
|
cannam@208
|
289 }
|
cannam@208
|
290
|
cannam@208
|
291 virtual size_t getPreferredStepSize() const {
|
cannam@208
|
292 // Return this from m_config instead of m_defaultConfig, so
|
cannam@208
|
293 // that it gets updated in the event of an initialise() call
|
cannam@208
|
294 // that fails for the wrong value
|
cannam@208
|
295 return m_config.framing.stepSize;
|
cannam@208
|
296 }
|
cannam@208
|
297
|
cannam@208
|
298 virtual size_t getMinChannelCount() const {
|
cannam@208
|
299 return m_psd.minChannelCount;
|
cannam@208
|
300 }
|
cannam@208
|
301
|
cannam@208
|
302 virtual size_t getMaxChannelCount() const {
|
cannam@208
|
303 return m_psd.maxChannelCount;
|
cannam@208
|
304 }
|
cannam@208
|
305
|
cannam@208
|
306 virtual OutputList getOutputDescriptors() const {
|
cannam@208
|
307
|
cannam@208
|
308 if (m_state == Failed) {
|
cannam@208
|
309 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
310 }
|
cannam@208
|
311 if (m_state == Configured) {
|
cannam@208
|
312 return m_outputs;
|
cannam@208
|
313 }
|
cannam@208
|
314
|
cannam@208
|
315 //!!! todo: figure out for which hosts (and adapters?) it may
|
cannam@208
|
316 //!!! be a problem that the output descriptors are incomplete
|
cannam@208
|
317 //!!! here. Any such hosts/adapters are broken, but I bet they
|
cannam@208
|
318 //!!! exist
|
cannam@208
|
319
|
cannam@208
|
320 OutputList staticOutputs;
|
cannam@208
|
321 for (const auto &o: m_psd.basicOutputInfo) {
|
cannam@208
|
322 OutputDescriptor od;
|
cannam@208
|
323 od.identifier = o.identifier;
|
cannam@208
|
324 od.name = o.name;
|
cannam@208
|
325 od.description = o.description;
|
cannam@208
|
326 staticOutputs.push_back(od);
|
cannam@208
|
327 }
|
cannam@208
|
328 return staticOutputs;
|
cannam@208
|
329 }
|
cannam@208
|
330
|
cannam@208
|
331 virtual FeatureSet process(const float *const *inputBuffers,
|
cannam@208
|
332 Vamp::RealTime timestamp) {
|
cannam@208
|
333
|
cannam@208
|
334 if (m_state == Failed) {
|
cannam@208
|
335 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
336 }
|
cannam@208
|
337 if (m_state == Loaded || m_state == Misconfigured) {
|
cannam@208
|
338 m_state = Failed;
|
cannam@208
|
339 throw std::logic_error("Plugin has not been initialised");
|
cannam@208
|
340 }
|
cannam@208
|
341 if (m_state == Finished) {
|
cannam@208
|
342 m_state = Failed;
|
cannam@208
|
343 throw std::logic_error("Plugin has already been disposed of");
|
cannam@208
|
344 }
|
cannam@208
|
345
|
cannam@208
|
346 std::vector<std::vector<float> > vecbuf;
|
cannam@208
|
347 for (int c = 0; c < m_config.channelCount; ++c) {
|
cannam@208
|
348 vecbuf.push_back(std::vector<float>
|
cannam@208
|
349 (inputBuffers[c],
|
cannam@208
|
350 inputBuffers[c] + m_config.framing.blockSize));
|
cannam@208
|
351 }
|
cannam@208
|
352
|
cannam@208
|
353 try {
|
cannam@208
|
354 return m_client->process(this, vecbuf, timestamp);
|
cannam@208
|
355 } catch (const std::exception &e) {
|
cannam@208
|
356 m_state = Failed;
|
cannam@208
|
357 throw;
|
cannam@208
|
358 }
|
cannam@208
|
359 }
|
cannam@208
|
360
|
cannam@208
|
361 virtual FeatureSet getRemainingFeatures() {
|
cannam@208
|
362
|
cannam@208
|
363 if (m_state == Failed) {
|
cannam@208
|
364 throw std::logic_error("Plugin is in failed state");
|
cannam@208
|
365 }
|
cannam@208
|
366 if (m_state == Loaded || m_state == Misconfigured) {
|
cannam@208
|
367 m_state = Failed;
|
cannam@208
|
368 throw std::logic_error("Plugin has not been configured");
|
cannam@208
|
369 }
|
cannam@208
|
370 if (m_state == Finished) {
|
cannam@208
|
371 m_state = Failed;
|
cannam@208
|
372 throw std::logic_error("Plugin has already been disposed of");
|
cannam@208
|
373 }
|
cannam@208
|
374
|
cannam@208
|
375 m_state = Finished;
|
cannam@208
|
376
|
cannam@208
|
377 try {
|
cannam@208
|
378 return m_client->finish(this);
|
cannam@208
|
379 } catch (const std::exception &e) {
|
cannam@208
|
380 m_state = Failed;
|
cannam@208
|
381 throw;
|
cannam@208
|
382 }
|
cannam@208
|
383 }
|
cannam@208
|
384
|
cannam@208
|
385 // Not Plugin methods, but needed by the PluginClient to support reloads:
|
cannam@208
|
386
|
cannam@208
|
387 virtual float getInputSampleRate() const {
|
cannam@208
|
388 return m_inputSampleRate;
|
cannam@208
|
389 }
|
cannam@208
|
390
|
cannam@208
|
391 virtual std::string getPluginKey() const {
|
cannam@208
|
392 return m_key;
|
cannam@208
|
393 }
|
cannam@208
|
394
|
cannam@208
|
395 virtual int getAdapterFlags() const {
|
cannam@208
|
396 return m_adapterFlags;
|
cannam@208
|
397 }
|
cannam@208
|
398
|
cannam@208
|
399 private:
|
cannam@208
|
400 PluginClient *m_client;
|
cannam@208
|
401 std::string m_key;
|
cannam@208
|
402 int m_adapterFlags;
|
cannam@208
|
403 State m_state;
|
cannam@208
|
404 PluginStaticData m_psd;
|
cannam@208
|
405 OutputList m_outputs;
|
cannam@208
|
406 PluginConfiguration m_defaultConfig;
|
cannam@208
|
407 PluginConfiguration m_config;
|
cannam@208
|
408 };
|
cannam@208
|
409
|
cannam@208
|
410 }
|
cannam@208
|
411 }
|
cannam@208
|
412
|
cannam@208
|
413 #endif
|