annotate VamPipePluginLibrary.cpp @ 96:514ea085c63a

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