annotate vamp-client/PluginStub.h @ 186:52322dde68ea

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