annotate src/PiperPluginLibrary.cpp @ 117:2e8aacb7f883

Move some things around (have not yet updated builds)
author Chris Cannam <c.cannam@qmul.ac.uk>
date Tue, 08 Nov 2016 12:02:57 +0000
parents
children 4b593b643918
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,
c@117 117 string &err) const
c@117 118 {
c@117 119 for (PluginConfiguration::ParameterMap::const_iterator i =
c@117 120 req.configuration.parameterValues.begin();
c@117 121 i != req.configuration.parameterValues.end(); ++i) {
c@117 122 req.plugin->setParameter(i->first, i->second);
c@117 123 }
c@117 124
c@117 125 if (req.configuration.currentProgram != "") {
c@117 126 req.plugin->selectProgram(req.configuration.currentProgram);
c@117 127 }
c@117 128
c@117 129 ConfigurationResponse response;
c@117 130
c@117 131 response.plugin = req.plugin;
c@117 132
c@117 133 if (req.plugin->initialise(req.configuration.channelCount,
c@117 134 req.configuration.stepSize,
c@117 135 req.configuration.blockSize)) {
c@117 136 response.outputs = req.plugin->getOutputDescriptors();
c@117 137 } else {
c@117 138 err = "configuration failed (wrong channel count, step size, block size?)";
c@117 139 }
c@117 140
c@117 141 return response;
c@117 142 }
c@117 143
c@117 144 string
c@117 145 PiperPluginLibrary::processRawImpl(int handle,
c@117 146 const float *const *inputBuffers,
c@117 147 int sec,
c@117 148 int nsec)
c@117 149 {
c@117 150 Vamp::Plugin *plugin = m_mapper.handleToPlugin(handle);
c@117 151 if (!plugin) {
c@117 152 return VampJson::fromError("unknown plugin handle",
c@117 153 RRType::Process, Json())
c@117 154 .dump();
c@117 155 }
c@117 156
c@117 157 if (!m_mapper.isConfigured(handle)) {
c@117 158 return VampJson::fromError("plugin has not been configured",
c@117 159 RRType::Process, Json())
c@117 160 .dump();
c@117 161 }
c@117 162
c@117 163 Vamp::RealTime timestamp(sec, nsec);
c@117 164
c@117 165 ProcessResponse resp;
c@117 166 resp.plugin = plugin;
c@117 167 resp.features = plugin->process(inputBuffers, timestamp);
c@117 168
c@117 169 m_useBase64 = true;
c@117 170
c@117 171 return VampJson::fromRpcResponse_Process
c@117 172 (resp, m_mapper,
c@117 173 VampJson::BufferSerialisation::Base64,
c@117 174 Json())
c@117 175 .dump();
c@117 176 }
c@117 177
c@117 178 string
c@117 179 PiperPluginLibrary::requestJsonImpl(string req)
c@117 180 {
c@117 181 string err;
c@117 182
c@117 183 Json j = convertRequestJson(req, err);
c@117 184
c@117 185 // we don't care what this is, only that it is retained in the response:
c@117 186 auto id = j["id"];
c@117 187
c@117 188 Json rj;
c@117 189 if (err != "") {
c@117 190 return VampJson::fromError(err, RRType::NotValid, id).dump();
c@117 191 }
c@117 192
c@117 193 RRType type = VampJson::getRequestResponseType(j, err);
c@117 194 if (err != "") {
c@117 195 return VampJson::fromError(err, RRType::NotValid, id).dump();
c@117 196 }
c@117 197
c@117 198 VampJson::BufferSerialisation serialisation =
c@117 199 (m_useBase64 ?
c@117 200 VampJson::BufferSerialisation::Base64 :
c@117 201 VampJson::BufferSerialisation::Array);
c@117 202
c@117 203 switch (type) {
c@117 204
c@117 205 case RRType::List:
c@117 206 {
c@117 207 auto req = VampJson::toRpcRequest_List(j, err);
c@117 208 if (err != "") {
c@117 209 rj = VampJson::fromError(err, type, id);
c@117 210 } else {
c@117 211 rj = VampJson::fromRpcResponse_List(listPluginData(req), id);
c@117 212 }
c@117 213 break;
c@117 214 }
c@117 215
c@117 216 case RRType::Load:
c@117 217 {
c@117 218 auto req = VampJson::toRpcRequest_Load(j, err);
c@117 219 if (err != "") {
c@117 220 rj = VampJson::fromError(err, type, id);
c@117 221 } else {
c@117 222 auto resp = loadPlugin(req, err);
c@117 223 if (err != "") {
c@117 224 rj = VampJson::fromError(err, type, id);
c@117 225 } else {
c@117 226 m_mapper.addPlugin(resp.plugin);
c@117 227 rj = VampJson::fromRpcResponse_Load(resp, m_mapper, id);
c@117 228 }
c@117 229 }
c@117 230 break;
c@117 231 }
c@117 232
c@117 233 case RRType::Configure:
c@117 234 {
c@117 235 auto req = VampJson::toRpcRequest_Configure(j, m_mapper, err);
c@117 236 if (err != "") {
c@117 237 rj = VampJson::fromError(err, type, id);
c@117 238 } else {
c@117 239 auto h = m_mapper.pluginToHandle(req.plugin);
c@117 240 if (h == m_mapper.INVALID_HANDLE) {
c@117 241 rj = VampJson::fromError("unknown or invalid plugin handle", type, id);
c@117 242 } else if (m_mapper.isConfigured(h)) {
c@117 243 rj = VampJson::fromError("plugin has already been configured", type, id);
c@117 244 } else {
c@117 245 auto resp = configurePlugin(req, err);
c@117 246 if (err != "") {
c@117 247 rj = VampJson::fromError(err, type, id);
c@117 248 } else {
c@117 249 m_mapper.markConfigured(h,
c@117 250 req.configuration.channelCount,
c@117 251 req.configuration.blockSize);
c@117 252 rj = VampJson::fromRpcResponse_Configure(resp, m_mapper, id);
c@117 253 }
c@117 254 }
c@117 255 }
c@117 256 break;
c@117 257 }
c@117 258
c@117 259 case RRType::Process:
c@117 260 {
c@117 261 VampJson::BufferSerialisation serialisation;
c@117 262
c@117 263 auto req = VampJson::toRpcRequest_Process(j, m_mapper,
c@117 264 serialisation, err);
c@117 265 if (err != "") {
c@117 266 rj = VampJson::fromError(err, type, id);
c@117 267 } else {
c@117 268 auto h = m_mapper.pluginToHandle(req.plugin);
c@117 269 int channels = int(req.inputBuffers.size());
c@117 270 if (h == m_mapper.INVALID_HANDLE) {
c@117 271 rj = VampJson::fromError("unknown or invalid plugin handle", type, id);
c@117 272 } else if (!m_mapper.isConfigured(h)) {
c@117 273 rj = VampJson::fromError("plugin has not been configured", type, id);
c@117 274 } else if (channels != m_mapper.getChannelCount(h)) {
c@117 275 rj = VampJson::fromError("wrong number of channels supplied", type, id);
c@117 276 } else {
c@117 277
c@117 278 if (serialisation == VampJson::BufferSerialisation::Base64) {
c@117 279 m_useBase64 = true;
c@117 280 }
c@117 281
c@117 282 size_t blockSize = m_mapper.getBlockSize(h);
c@117 283
c@117 284 const float **fbuffers = new const float *[channels];
c@117 285 for (int i = 0; i < channels; ++i) {
c@117 286 if (req.inputBuffers[i].size() != blockSize) {
c@117 287 delete[] fbuffers;
c@117 288 fbuffers = 0;
c@117 289 rj = VampJson::fromError("wrong block size supplied", type, id);
c@117 290 break;
c@117 291 }
c@117 292 fbuffers[i] = req.inputBuffers[i].data();
c@117 293 }
c@117 294
c@117 295 if (fbuffers) {
c@117 296 ProcessResponse resp;
c@117 297 resp.plugin = req.plugin;
c@117 298 resp.features = req.plugin->process(fbuffers, req.timestamp);
c@117 299 delete[] fbuffers;
c@117 300 rj = VampJson::fromRpcResponse_Process
c@117 301 (resp, m_mapper, serialisation, id);
c@117 302 }
c@117 303 }
c@117 304 }
c@117 305 break;
c@117 306 }
c@117 307
c@117 308 case RRType::Finish:
c@117 309 {
c@117 310 auto req = VampJson::toRpcRequest_Finish(j, m_mapper, err);
c@117 311 if (err != "") {
c@117 312 rj = VampJson::fromError(err, type, id);
c@117 313 } else {
c@117 314 auto h = m_mapper.pluginToHandle(req.plugin);
c@117 315 if (h == m_mapper.INVALID_HANDLE) {
c@117 316 rj = VampJson::fromError("unknown or invalid plugin handle", type, id);
c@117 317 } else {
c@117 318
c@117 319 FinishResponse resp;
c@117 320 resp.plugin = req.plugin;
c@117 321
c@117 322 // Finish can be called (to unload the plugin) even if
c@117 323 // the plugin has never been configured or used. But
c@117 324 // we want to make sure we call getRemainingFeatures
c@117 325 // only if we have actually configured the plugin.
c@117 326 if (m_mapper.isConfigured(h)) {
c@117 327 resp.features = req.plugin->getRemainingFeatures();
c@117 328 }
c@117 329
c@117 330 rj = VampJson::fromRpcResponse_Finish
c@117 331 (resp, m_mapper, serialisation, id);
c@117 332
c@117 333 m_mapper.removePlugin(h);
c@117 334 delete req.plugin;
c@117 335 }
c@117 336 }
c@117 337 break;
c@117 338 }
c@117 339
c@117 340 case RRType::NotValid:
c@117 341 rj = VampJson::fromError("invalid request", type, id);
c@117 342 break;
c@117 343 }
c@117 344
c@117 345 return rj.dump();
c@117 346 }
c@117 347
c@117 348 }
c@117 349