comparison vamp-client/PiperVampPlugin.h @ 214:0906984b9496

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