Mercurial > hg > piper-cpp
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 |