annotate src/PiperPluginLibrary.cpp @ 176:eaf46e7647a0 tip master

Update for latest Emscripten - Pointer_stringify has apparently been deprecated for a while, and was removed in v1.38.27
author Chris Cannam <cannam@all-day-breakfast.com>
date Wed, 27 Feb 2019 11:29:53 +0000
parents c43d2e93153f
children
rev   line source
c@117 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@117 2
c@117 3 /*
c@117 4 Piper Vamp JSON Adapter
c@117 5
c@117 6 Centre for Digital Music, Queen Mary, University of London.
c@117 7 Copyright 2015-2016 QMUL.
c@117 8
c@117 9 Permission is hereby granted, free of charge, to any person
c@117 10 obtaining a copy of this software and associated documentation
c@117 11 files (the "Software"), to deal in the Software without
c@117 12 restriction, including without limitation the rights to use, copy,
c@117 13 modify, merge, publish, distribute, sublicense, and/or sell copies
c@117 14 of the Software, and to permit persons to whom the Software is
c@117 15 furnished to do so, subject to the following conditions:
c@117 16
c@117 17 The above copyright notice and this permission notice shall be
c@117 18 included in all copies or substantial portions of the Software.
c@117 19
c@117 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
c@117 21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
c@117 22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
c@117 23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
c@117 24 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
c@117 25 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
c@117 26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
c@117 27
c@117 28 Except as contained in this notice, the names of the Centre for
c@117 29 Digital Music; Queen Mary, University of London; and Chris Cannam
c@117 30 shall not be used in advertising or otherwise to promote the sale,
c@117 31 use or other dealings in this Software without prior written
c@117 32 authorization.
c@117 33 */
c@117 34
c@117 35 #include "PiperPluginLibrary.h"
c@117 36 #include "PiperAdapter.h"
c@117 37
c@117 38 #include "vamp-json/VampJson.h"
c@117 39
c@117 40 using namespace std;
c@117 41 using namespace json11;
c@117 42 using namespace piper_vamp;
c@117 43
c@117 44 namespace piper_vamp_js { //!!! not good
c@117 45
c@117 46 //!!! too many explicit namespaces here
c@117 47
c@117 48 //!!! dup with piper-convert
c@117 49 Json
c@117 50 convertRequestJson(string input, string &err)
c@117 51 {
c@117 52 Json j = Json::parse(input, err);
c@117 53 if (err != "") {
c@117 54 err = "invalid json: " + err;
c@117 55 return {};
c@117 56 }
c@117 57 if (!j.is_object()) {
c@117 58 err = "object expected at top level";
c@117 59 }
c@117 60 return j;
c@117 61 }
c@117 62
c@117 63 PiperPluginLibrary::PiperPluginLibrary(vector<PiperAdapterInterface *> pp) :
c@117 64 m_useBase64(false)
c@117 65 {
c@117 66 for (PiperAdapterInterface *p: pp) {
c@117 67 string key = p->getStaticData().pluginKey;
c@117 68 m_adapters[key] = p;
c@117 69 }
c@117 70 }
c@117 71
c@117 72 ListResponse
c@117 73 PiperPluginLibrary::listPluginData(ListRequest req) const
c@117 74 {
c@117 75 bool filtered = !req.from.empty();
c@117 76 ListResponse resp;
c@117 77 for (auto a: m_adapters) {
c@117 78 if (filtered) {
c@117 79 auto n = a.second->getLibraryName();
c@117 80 bool found = false;
c@117 81 for (const auto &f: req.from) {
c@117 82 if (f == n) {
c@117 83 found = true;
c@117 84 break;
c@117 85 }
c@117 86 }
c@117 87 if (!found) {
c@117 88 continue;
c@117 89 }
c@117 90 }
c@117 91 resp.available.push_back(a.second->getStaticData());
c@117 92 }
c@117 93 return resp;
c@117 94 }
c@117 95
c@117 96 LoadResponse
c@117 97 PiperPluginLibrary::loadPlugin(LoadRequest req, string &err) const
c@117 98 {
c@117 99 string key = req.pluginKey;
c@117 100 if (m_adapters.find(key) != m_adapters.end()) {
c@117 101 auto resp = m_adapters.at(key)->loadPlugin(req);
c@117 102 if (!resp.plugin) {
c@117 103 // This should not actually happen -- the load call here
c@117 104 // is just an object construction, not a dynamic load. But
c@117 105 // report it if it does...
c@117 106 err = "failed to construct plugin with key " + key;
c@117 107 }
c@117 108 return resp;
c@117 109 } else {
c@117 110 err = "no adapter for plugin key " + key;
c@117 111 return {};
c@117 112 }
c@117 113 }
c@117 114
c@117 115 ConfigurationResponse
c@117 116 PiperPluginLibrary::configurePlugin(ConfigurationRequest req,
cannam@152 117 const PluginStaticData &psd,
cannam@138 118 string &err) const
c@117 119 {
c@117 120 for (PluginConfiguration::ParameterMap::const_iterator i =
c@117 121 req.configuration.parameterValues.begin();
c@117 122 i != req.configuration.parameterValues.end(); ++i) {
c@117 123 req.plugin->setParameter(i->first, i->second);
c@117 124 }
c@117 125
c@117 126 if (req.configuration.currentProgram != "") {
c@117 127 req.plugin->selectProgram(req.configuration.currentProgram);
c@117 128 }
c@117 129
c@117 130 ConfigurationResponse response;
c@117 131 response.plugin = req.plugin;
cannam@152 132 response.staticOutputInfo = psd.staticOutputInfo;
c@117 133
cannam@138 134 Framing pluginPreferredFraming;
cannam@138 135 pluginPreferredFraming.stepSize = req.plugin->getPreferredStepSize();
cannam@138 136 pluginPreferredFraming.blockSize = req.plugin->getPreferredBlockSize();
cannam@138 137
c@117 138 if (req.plugin->initialise(req.configuration.channelCount,
cannam@138 139 req.configuration.framing.stepSize,
cannam@138 140 req.configuration.framing.blockSize)) {
cannam@138 141
c@117 142 response.outputs = req.plugin->getOutputDescriptors();
cannam@138 143
cannam@138 144 // If the Vamp plugin initialise() call succeeds, then by
cannam@138 145 // definition it is accepting the step and block size we
cannam@138 146 // passed to it
cannam@138 147 response.framing = req.configuration.framing;
cannam@138 148
c@117 149 } else {
cannam@138 150
cannam@138 151 // If initialise() fails, one reason could be that it didn't
cannam@138 152 // like the passed-in framing (step and block size). We need
cannam@138 153 // to check whether the passed-in framing differs from the
cannam@138 154 // plugin's preferences; if so, then we form a working
cannam@138 155 // supposition that initialise() failed because of this. Vamp
cannam@138 156 // contains nothing to allow us to test this, except to try
cannam@138 157 // initialise() again with different values. So we try again
cannam@138 158 // with the values the plugin told us it would prefer and, if
cannam@138 159 // that succeeds, return them in a successful response.
cannam@138 160 //
cannam@138 161 // See also LoaderRequests in piper-cpp/vamp-support.
cannam@138 162 //
cannam@138 163 if (req.plugin->initialise(req.configuration.channelCount,
cannam@138 164 pluginPreferredFraming.stepSize,
cannam@138 165 pluginPreferredFraming.blockSize)) {
cannam@138 166
cannam@138 167 response.outputs = req.plugin->getOutputDescriptors();
cannam@138 168 response.framing = pluginPreferredFraming;
cannam@138 169
cannam@138 170 } else {
cannam@138 171 err = "configuration failed (wrong channel count, step size, block size?)";
cannam@138 172 }
c@117 173 }
cannam@152 174
c@117 175 return response;
c@117 176 }
c@117 177
c@117 178 string
c@117 179 PiperPluginLibrary::processRawImpl(int handle,
c@117 180 const float *const *inputBuffers,
c@117 181 int sec,
c@117 182 int nsec)
c@117 183 {
c@117 184 Vamp::Plugin *plugin = m_mapper.handleToPlugin(handle);
c@117 185 if (!plugin) {
c@117 186 return VampJson::fromError("unknown plugin handle",
c@117 187 RRType::Process, Json())
c@117 188 .dump();
c@117 189 }
c@117 190
c@117 191 if (!m_mapper.isConfigured(handle)) {
c@117 192 return VampJson::fromError("plugin has not been configured",
c@117 193 RRType::Process, Json())
c@117 194 .dump();
c@117 195 }
c@117 196
c@117 197 Vamp::RealTime timestamp(sec, nsec);
c@117 198
c@117 199 ProcessResponse resp;
c@117 200 resp.plugin = plugin;
c@117 201 resp.features = plugin->process(inputBuffers, timestamp);
c@117 202
c@117 203 m_useBase64 = true;
c@117 204
c@117 205 return VampJson::fromRpcResponse_Process
c@117 206 (resp, m_mapper,
c@117 207 VampJson::BufferSerialisation::Base64,
c@117 208 Json())
c@117 209 .dump();
c@117 210 }
c@117 211
c@117 212 string
c@117 213 PiperPluginLibrary::requestJsonImpl(string req)
c@117 214 {
c@117 215 string err;
c@117 216
c@117 217 Json j = convertRequestJson(req, err);
c@117 218
c@117 219 // we don't care what this is, only that it is retained in the response:
c@117 220 auto id = j["id"];
c@117 221
c@117 222 Json rj;
c@117 223 if (err != "") {
c@117 224 return VampJson::fromError(err, RRType::NotValid, id).dump();
c@117 225 }
c@117 226
c@117 227 RRType type = VampJson::getRequestResponseType(j, err);
c@117 228 if (err != "") {
c@117 229 return VampJson::fromError(err, RRType::NotValid, id).dump();
c@117 230 }
c@117 231
c@117 232 VampJson::BufferSerialisation serialisation =
c@117 233 (m_useBase64 ?
c@117 234 VampJson::BufferSerialisation::Base64 :
c@117 235 VampJson::BufferSerialisation::Array);
c@117 236
c@117 237 switch (type) {
c@117 238
c@117 239 case RRType::List:
c@117 240 {
c@117 241 auto req = VampJson::toRpcRequest_List(j, err);
c@117 242 if (err != "") {
c@117 243 rj = VampJson::fromError(err, type, id);
c@117 244 } else {
c@117 245 rj = VampJson::fromRpcResponse_List(listPluginData(req), id);
c@117 246 }
c@117 247 break;
c@117 248 }
c@117 249
c@117 250 case RRType::Load:
c@117 251 {
c@117 252 auto req = VampJson::toRpcRequest_Load(j, err);
c@117 253 if (err != "") {
c@117 254 rj = VampJson::fromError(err, type, id);
c@117 255 } else {
c@117 256 auto resp = loadPlugin(req, err);
c@117 257 if (err != "") {
c@117 258 rj = VampJson::fromError(err, type, id);
c@117 259 } else {
c@117 260 m_mapper.addPlugin(resp.plugin);
cannam@152 261 m_pluginStaticData[m_mapper.pluginToHandle(resp.plugin)] =
cannam@152 262 resp.staticData;
c@117 263 rj = VampJson::fromRpcResponse_Load(resp, m_mapper, id);
c@117 264 }
c@117 265 }
c@117 266 break;
c@117 267 }
c@117 268
c@117 269 case RRType::Configure:
c@117 270 {
c@117 271 auto req = VampJson::toRpcRequest_Configure(j, m_mapper, err);
c@117 272 if (err != "") {
c@117 273 rj = VampJson::fromError(err, type, id);
c@117 274 } else {
c@117 275 auto h = m_mapper.pluginToHandle(req.plugin);
c@117 276 if (h == m_mapper.INVALID_HANDLE) {
cannam@152 277 rj = VampJson::fromError
cannam@152 278 ("unknown or invalid plugin handle", type, id);
c@117 279 } else if (m_mapper.isConfigured(h)) {
cannam@152 280 rj = VampJson::fromError
cannam@152 281 ("plugin has already been configured", type, id);
c@117 282 } else {
cannam@152 283 PluginStaticData psd(m_pluginStaticData[h]);
cannam@152 284 auto resp = configurePlugin(req, psd, err);
c@117 285 if (err != "") {
c@117 286 rj = VampJson::fromError(err, type, id);
c@117 287 } else {
c@117 288 m_mapper.markConfigured(h,
c@117 289 req.configuration.channelCount,
cannam@138 290 req.configuration.framing.blockSize);
c@117 291 rj = VampJson::fromRpcResponse_Configure(resp, m_mapper, id);
c@117 292 }
c@117 293 }
c@117 294 }
c@117 295 break;
c@117 296 }
c@117 297
c@117 298 case RRType::Process:
c@117 299 {
c@117 300 VampJson::BufferSerialisation serialisation;
c@117 301
c@117 302 auto req = VampJson::toRpcRequest_Process(j, m_mapper,
c@117 303 serialisation, err);
c@117 304 if (err != "") {
c@117 305 rj = VampJson::fromError(err, type, id);
c@117 306 } else {
c@117 307 auto h = m_mapper.pluginToHandle(req.plugin);
c@117 308 int channels = int(req.inputBuffers.size());
c@117 309 if (h == m_mapper.INVALID_HANDLE) {
cannam@152 310 rj = VampJson::fromError
cannam@152 311 ("unknown or invalid plugin handle", type, id);
c@117 312 } else if (!m_mapper.isConfigured(h)) {
cannam@152 313 rj = VampJson::fromError
cannam@152 314 ("plugin has not been configured", type, id);
c@117 315 } else if (channels != m_mapper.getChannelCount(h)) {
cannam@152 316 rj = VampJson::fromError
cannam@152 317 ("wrong number of channels supplied", type, id);
c@117 318 } else {
c@117 319
c@117 320 if (serialisation == VampJson::BufferSerialisation::Base64) {
c@117 321 m_useBase64 = true;
c@117 322 }
c@117 323
c@117 324 size_t blockSize = m_mapper.getBlockSize(h);
c@117 325
c@117 326 const float **fbuffers = new const float *[channels];
c@117 327 for (int i = 0; i < channels; ++i) {
c@117 328 if (req.inputBuffers[i].size() != blockSize) {
c@117 329 delete[] fbuffers;
c@117 330 fbuffers = 0;
cannam@152 331 rj = VampJson::fromError
cannam@152 332 ("wrong block size supplied", type, id);
c@117 333 break;
c@117 334 }
c@117 335 fbuffers[i] = req.inputBuffers[i].data();
c@117 336 }
c@117 337
c@117 338 if (fbuffers) {
c@117 339 ProcessResponse resp;
c@117 340 resp.plugin = req.plugin;
c@117 341 resp.features = req.plugin->process(fbuffers, req.timestamp);
c@117 342 delete[] fbuffers;
c@117 343 rj = VampJson::fromRpcResponse_Process
c@117 344 (resp, m_mapper, serialisation, id);
c@117 345 }
c@117 346 }
c@117 347 }
c@117 348 break;
c@117 349 }
c@117 350
c@117 351 case RRType::Finish:
c@117 352 {
c@117 353 auto req = VampJson::toRpcRequest_Finish(j, m_mapper, err);
c@117 354 if (err != "") {
c@117 355 rj = VampJson::fromError(err, type, id);
c@117 356 } else {
c@117 357 auto h = m_mapper.pluginToHandle(req.plugin);
c@117 358 if (h == m_mapper.INVALID_HANDLE) {
cannam@152 359 rj = VampJson::fromError
cannam@152 360 ("unknown or invalid plugin handle", type, id);
c@117 361 } else {
c@117 362
c@117 363 FinishResponse resp;
c@117 364 resp.plugin = req.plugin;
c@117 365
c@117 366 // Finish can be called (to unload the plugin) even if
c@117 367 // the plugin has never been configured or used. But
c@117 368 // we want to make sure we call getRemainingFeatures
c@117 369 // only if we have actually configured the plugin.
c@117 370 if (m_mapper.isConfigured(h)) {
c@117 371 resp.features = req.plugin->getRemainingFeatures();
c@117 372 }
c@117 373
c@117 374 rj = VampJson::fromRpcResponse_Finish
c@117 375 (resp, m_mapper, serialisation, id);
cannam@152 376
cannam@152 377 m_pluginStaticData.erase(h);
c@117 378 m_mapper.removePlugin(h);
c@117 379 delete req.plugin;
c@117 380 }
c@117 381 }
c@117 382 break;
c@117 383 }
c@117 384
c@117 385 case RRType::NotValid:
c@117 386 rj = VampJson::fromError("invalid request", type, id);
c@117 387 break;
c@117 388 }
c@117 389
c@117 390 return rj.dump();
c@117 391 }
c@117 392
c@117 393 }
c@117 394