annotate VamPipePluginLibrary.cpp @ 26:98be88612dd1

Merge from branch outputid-string-in-featureset
author Chris Cannam
date Mon, 19 Sep 2016 14:48:43 +0100
parents 4a5de1bb2c6a
children 65fa1b372648
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@0 4 VamPipe
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@0 35 #include "VamPipePluginLibrary.h"
Chris@0 36 #include "VamPipeAdapter.h"
Chris@0 37 #include "json/VampJson.h"
Chris@0 38
Chris@0 39 using namespace std;
Chris@0 40 using namespace json11;
Chris@0 41
Chris@0 42 namespace vampipe {
Chris@0 43
Chris@0 44 //!!! too many explicit namespaces here
Chris@0 45
Chris@0 46 //!!! dup with vampipe-convert
Chris@0 47 static Json
Chris@0 48 convertRequestJson(string input)
Chris@0 49 {
Chris@0 50 string err;
Chris@0 51 Json j = Json::parse(input, err);
Chris@0 52 if (err != "") {
Chris@0 53 throw VampJson::Failure("invalid json: " + err);
Chris@0 54 }
Chris@0 55 if (!j.is_object()) {
Chris@0 56 throw VampJson::Failure("object expected at top level");
Chris@0 57 }
Chris@0 58 if (!j["type"].is_string()) {
Chris@0 59 throw VampJson::Failure("string expected for type field");
Chris@0 60 }
Chris@2 61 if (!j["content"].is_null() && !j["content"].is_object()) {
Chris@0 62 throw VampJson::Failure("object expected for content field");
Chris@0 63 }
Chris@0 64 return j;
Chris@0 65 }
Chris@0 66
Chris@19 67 VamPipePluginLibrary::VamPipePluginLibrary(vector<VamPipeAdapterInterface *> pp) :
Chris@12 68 m_useBase64(false)
Chris@0 69 {
Chris@19 70 for (VamPipeAdapterInterface *p: pp) {
Chris@0 71 string key = p->getStaticData().pluginKey;
Chris@0 72 m_adapters[key] = p;
Chris@0 73 }
Chris@0 74 }
Chris@0 75
Chris@0 76 RequestOrResponse
Chris@12 77 VamPipePluginLibrary::readRequest(string req)
Chris@0 78 {
Chris@0 79 RequestOrResponse rr;
Chris@0 80 rr.direction = RequestOrResponse::Request;
Chris@0 81
Chris@0 82 Json j = convertRequestJson(req);
Chris@0 83
Chris@0 84 //!!! reduce, reduce
Chris@0 85 rr.type = VampJson::getRequestResponseType(j);
Chris@12 86 VampJson::BufferSerialisation serialisation = VampJson::BufferSerialisation::Text;
Chris@0 87
Chris@0 88 switch (rr.type) {
Chris@0 89
Chris@0 90 case RRType::List:
Chris@0 91 VampJson::toVampRequest_List(j); // type check only
Chris@0 92 break;
Chris@0 93 case RRType::Load:
Chris@0 94 rr.loadRequest = VampJson::toVampRequest_Load(j);
Chris@0 95 break;
Chris@0 96 case RRType::Configure:
Chris@0 97 rr.configurationRequest = VampJson::toVampRequest_Configure(j, m_mapper);
Chris@0 98 break;
Chris@0 99 case RRType::Process:
Chris@12 100 rr.processRequest = VampJson::toVampRequest_Process(j, m_mapper, serialisation);
Chris@0 101 break;
Chris@0 102 case RRType::Finish:
Chris@24 103 rr.finishRequest = VampJson::toVampRequest_Finish(j, m_mapper);
Chris@0 104 break;
Chris@0 105 case RRType::NotValid:
Chris@0 106 break;
Chris@0 107 }
Chris@0 108
Chris@12 109 if (serialisation == VampJson::BufferSerialisation::Base64) {
Chris@12 110 m_useBase64 = true;
Chris@12 111 }
Chris@12 112
Chris@0 113 return rr;
Chris@0 114 }
Chris@0 115
Chris@0 116 string
Chris@0 117 VamPipePluginLibrary::writeResponse(const RequestOrResponse &rr) const
Chris@0 118 {
Chris@0 119 Json j;
Chris@0 120
Chris@12 121 VampJson::BufferSerialisation serialisation =
Chris@12 122 (m_useBase64 ?
Chris@12 123 VampJson::BufferSerialisation::Base64 :
Chris@12 124 VampJson::BufferSerialisation::Text);
Chris@12 125
Chris@23 126 if (!rr.success) {
Chris@0 127
Chris@23 128 j = VampJson::fromError(rr.errorText, rr.type);
Chris@23 129
Chris@23 130 } else {
Chris@23 131
Chris@23 132 switch (rr.type) {
Chris@23 133
Chris@23 134 case RRType::List:
Chris@23 135 j = VampJson::fromVampResponse_List("", rr.listResponse);
Chris@23 136 break;
Chris@23 137 case RRType::Load:
Chris@23 138 j = VampJson::fromVampResponse_Load(rr.loadResponse, m_mapper);
Chris@23 139 break;
Chris@23 140 case RRType::Configure:
Chris@24 141 j = VampJson::fromVampResponse_Configure(rr.configurationResponse,
Chris@24 142 m_mapper);
Chris@23 143 break;
Chris@23 144 case RRType::Process:
Chris@23 145 j = VampJson::fromVampResponse_Process
Chris@23 146 (rr.processResponse, m_mapper, serialisation);
Chris@23 147 break;
Chris@23 148 case RRType::Finish:
Chris@23 149 j = VampJson::fromVampResponse_Finish
Chris@23 150 (rr.finishResponse, m_mapper, serialisation);
Chris@23 151 break;
Chris@23 152 case RRType::NotValid:
Chris@23 153 break;
Chris@23 154 }
Chris@0 155 }
Chris@0 156
Chris@0 157 return j.dump();
Chris@0 158 }
Chris@0 159
Chris@25 160 Vamp::HostExt::ListResponse
Chris@0 161 VamPipePluginLibrary::listPluginData() const
Chris@0 162 {
Chris@25 163 Vamp::HostExt::ListResponse resp;
Chris@0 164 for (auto a: m_adapters) {
Chris@25 165 resp.pluginData.push_back(a.second->getStaticData());
Chris@0 166 }
Chris@25 167 return resp;
Chris@0 168 }
Chris@0 169
Chris@0 170 Vamp::HostExt::LoadResponse
Chris@0 171 VamPipePluginLibrary::loadPlugin(Vamp::HostExt::LoadRequest req) const
Chris@0 172 {
Chris@0 173 string key = req.pluginKey;
Chris@0 174 if (m_adapters.find(key) != m_adapters.end()) {
Chris@0 175 return m_adapters.at(key)->loadPlugin(req);
Chris@0 176 } else {
Chris@0 177 throw runtime_error("no adapter for plugin key " + key);
Chris@0 178 }
Chris@0 179 }
Chris@0 180
Chris@0 181 Vamp::HostExt::ConfigurationResponse
Chris@0 182 VamPipePluginLibrary::configurePlugin(Vamp::HostExt::ConfigurationRequest req) const
Chris@0 183 {
Chris@0 184 for (Vamp::HostExt::PluginConfiguration::ParameterMap::const_iterator i =
Chris@0 185 req.configuration.parameterValues.begin();
Chris@0 186 i != req.configuration.parameterValues.end(); ++i) {
Chris@0 187 req.plugin->setParameter(i->first, i->second);
Chris@0 188 }
Chris@0 189
Chris@0 190 if (req.configuration.currentProgram != "") {
Chris@0 191 req.plugin->selectProgram(req.configuration.currentProgram);
Chris@0 192 }
Chris@0 193
Chris@0 194 Vamp::HostExt::ConfigurationResponse response;
Chris@0 195
Chris@0 196 if (req.plugin->initialise(req.configuration.channelCount,
Chris@0 197 req.configuration.stepSize,
Chris@0 198 req.configuration.blockSize)) {
Chris@0 199 response.outputs = req.plugin->getOutputDescriptors();
Chris@0 200 }
Chris@0 201
Chris@0 202 return response;
Chris@0 203 }
Chris@0 204
Chris@0 205 string
Chris@14 206 VamPipePluginLibrary::processRawImpl(int pluginHandle,
Chris@14 207 const float *const *inputBuffers,
Chris@14 208 int sec,
Chris@14 209 int nsec)
Chris@13 210 {
Chris@13 211 RequestOrResponse response;
Chris@13 212 response.direction = RequestOrResponse::Response;
Chris@13 213 response.type = RRType::Process;
Chris@13 214
Chris@13 215 try {
Chris@13 216 if (!m_mapper.isConfigured(pluginHandle)) {
Chris@13 217 throw runtime_error("plugin has not been configured");
Chris@13 218 }
Chris@13 219
Chris@13 220 Vamp::Plugin *plugin = m_mapper.handleToPlugin(pluginHandle);
Chris@13 221 Vamp::RealTime timestamp(sec, nsec);
Chris@23 222
Chris@23 223 response.processResponse.plugin = plugin;
Chris@13 224 response.processResponse.features = plugin->process(inputBuffers, timestamp);
Chris@13 225 response.success = true;
Chris@14 226
Chris@14 227 m_useBase64 = true;
Chris@13 228
Chris@13 229 return writeResponse(response);
Chris@13 230
Chris@13 231 } catch (const std::exception &e) {
Chris@13 232 return VampJson::fromException(e, RRType::Process).dump();
Chris@13 233 }
Chris@13 234 }
Chris@13 235
Chris@13 236 string
Chris@0 237 VamPipePluginLibrary::requestJsonImpl(string req)
Chris@0 238 {
Chris@1 239 RequestOrResponse request;
Chris@1 240
Chris@1 241 try {
Chris@1 242 request = readRequest(req);
Chris@1 243 } catch (const std::exception &e) {
Chris@1 244 return VampJson::fromException(e, RRType::NotValid).dump();
Chris@1 245 }
Chris@0 246
Chris@0 247 RequestOrResponse response;
Chris@0 248 response.direction = RequestOrResponse::Response;
Chris@0 249 response.type = request.type;
Chris@0 250
Chris@1 251 try {
Chris@1 252 switch (request.type) {
Chris@0 253
Chris@1 254 case RRType::List:
Chris@1 255 response.listResponse = listPluginData();
Chris@1 256 response.success = true;
Chris@1 257 break;
Chris@0 258
Chris@1 259 case RRType::Load:
Chris@1 260 response.loadResponse = loadPlugin(request.loadRequest);
Chris@1 261 if (response.loadResponse.plugin) {
Chris@1 262 m_mapper.addPlugin(response.loadResponse.plugin);
Chris@1 263 response.success = true;
Chris@1 264 }
Chris@1 265 break;
Chris@0 266
Chris@1 267 case RRType::Configure:
Chris@1 268 {
Chris@1 269 auto &creq = request.configurationRequest;
Chris@1 270 auto h = m_mapper.pluginToHandle(creq.plugin);
Chris@1 271 if (m_mapper.isConfigured(h)) {
Chris@1 272 throw runtime_error("plugin has already been configured");
Chris@1 273 }
Chris@1 274
Chris@1 275 response.configurationResponse = configurePlugin(creq);
Chris@1 276
Chris@1 277 if (!response.configurationResponse.outputs.empty()) {
Chris@1 278 m_mapper.markConfigured
Chris@1 279 (h, creq.configuration.channelCount, creq.configuration.blockSize);
Chris@1 280 response.success = true;
Chris@1 281 }
Chris@1 282 break;
Chris@0 283 }
Chris@0 284
Chris@1 285 case RRType::Process:
Chris@1 286 {
Chris@1 287 auto &preq = request.processRequest;
Chris@1 288 auto h = m_mapper.pluginToHandle(preq.plugin);
Chris@1 289 if (!m_mapper.isConfigured(h)) {
Chris@1 290 throw runtime_error("plugin has not been configured");
Chris@1 291 }
Chris@1 292
Chris@1 293 int channels = int(preq.inputBuffers.size());
Chris@1 294 if (channels != m_mapper.getChannelCount(h)) {
Chris@1 295 throw runtime_error("wrong number of channels supplied to process");
Chris@1 296 }
Chris@13 297
Chris@1 298 const float **fbuffers = new const float *[channels];
Chris@1 299 for (int i = 0; i < channels; ++i) {
Chris@1 300 if (int(preq.inputBuffers[i].size()) != m_mapper.getBlockSize(h)) {
Chris@1 301 delete[] fbuffers;
Chris@1 302 throw runtime_error("wrong block size supplied to process");
Chris@1 303 }
Chris@1 304 fbuffers[i] = preq.inputBuffers[i].data();
Chris@1 305 }
Chris@1 306
Chris@23 307 response.processResponse.plugin = preq.plugin;
Chris@1 308 response.processResponse.features =
Chris@12 309 preq.plugin->process(fbuffers, preq.timestamp);
Chris@0 310 response.success = true;
Chris@0 311
Chris@1 312 delete[] fbuffers;
Chris@1 313 break;
Chris@0 314 }
Chris@0 315
Chris@1 316 case RRType::Finish:
Chris@1 317 {
Chris@24 318 response.finishResponse.plugin = request.finishRequest.plugin;
Chris@1 319 response.finishResponse.features =
Chris@24 320 request.finishRequest.plugin->getRemainingFeatures();
Chris@1 321
Chris@23 322 // We do not delete the plugin here -- we need it in the
Chris@23 323 // mapper when converting the features. It gets deleted
Chris@23 324 // below, after the writeResponse() call.
Chris@23 325
Chris@1 326 response.success = true;
Chris@1 327 break;
Chris@0 328 }
Chris@0 329
Chris@1 330 case RRType::NotValid:
Chris@1 331 break;
Chris@1 332 }
Chris@1 333
Chris@23 334 string rstr = writeResponse(response);
Chris@23 335
Chris@23 336 if (request.type == RRType::Finish) {
Chris@24 337 auto h = m_mapper.pluginToHandle(request.finishRequest.plugin);
Chris@23 338 m_mapper.removePlugin(h);
Chris@24 339 delete request.finishRequest.plugin;
Chris@23 340 }
Chris@23 341
Chris@23 342 return rstr;
Chris@0 343
Chris@1 344 } catch (const std::exception &e) {
Chris@1 345 return VampJson::fromException(e, request.type).dump();
Chris@0 346 }
Chris@0 347 }
Chris@0 348
Chris@0 349 }
Chris@0 350