annotate src/PiperPluginLibrary.cpp @ 48:1ed825597b6f

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