annotate VamPipePluginLibrary.cpp @ 69:967b0619b090

Start on plugin-library-to-json-request-response-handler adapter
author Chris Cannam <c.cannam@qmul.ac.uk>
date Mon, 22 Aug 2016 17:17:28 +0100
parents
children 7cfe736fd974
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@69 61 if (!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@69 67 VamPipePluginLibrary::VamPipePluginLibrary(vector<VamPipeAdapterBase *> pp)
c@69 68 {
c@69 69 for (VamPipeAdapterBase *p: pp) {
c@69 70 string key = p->getStaticData().pluginKey;
c@69 71 m_adapters[key] = p;
c@69 72 }
c@69 73 }
c@69 74
c@69 75 RequestOrResponse
c@69 76 VamPipePluginLibrary::readRequest(string req) const
c@69 77 {
c@69 78 RequestOrResponse rr;
c@69 79 rr.direction = RequestOrResponse::Request;
c@69 80
c@69 81 //!!! todo: handle exceptions (can't pass through C abi)
c@69 82 Json j = convertRequestJson(req);
c@69 83
c@69 84 //!!! reduce, reduce
c@69 85 rr.type = VampJson::getRequestResponseType(j);
c@69 86
c@69 87 switch (rr.type) {
c@69 88
c@69 89 case RRType::List:
c@69 90 VampJson::toVampRequest_List(j); // type check only
c@69 91 break;
c@69 92 case RRType::Load:
c@69 93 rr.loadRequest = VampJson::toVampRequest_Load(j);
c@69 94 break;
c@69 95 case RRType::Configure:
c@69 96 rr.configurationRequest = VampJson::toVampRequest_Configure(j, m_mapper);
c@69 97 break;
c@69 98 case RRType::Process:
c@69 99 rr.processRequest = VampJson::toVampRequest_Process(j, m_mapper);
c@69 100 break;
c@69 101 case RRType::Finish:
c@69 102 rr.finishPlugin = VampJson::toVampRequest_Finish(j, m_mapper);
c@69 103 break;
c@69 104 case RRType::NotValid:
c@69 105 break;
c@69 106 }
c@69 107
c@69 108 return rr;
c@69 109 }
c@69 110
c@69 111 string
c@69 112 VamPipePluginLibrary::writeResponse(const RequestOrResponse &rr) const
c@69 113 {
c@69 114 Json j;
c@69 115
c@69 116 switch (rr.type) {
c@69 117
c@69 118 case RRType::List:
c@69 119 j = VampJson::fromVampResponse_List("", rr.listResponse);
c@69 120 break;
c@69 121 case RRType::Load:
c@69 122 j = VampJson::fromVampResponse_Load(rr.loadResponse, m_mapper);
c@69 123 break;
c@69 124 case RRType::Configure:
c@69 125 j = VampJson::fromVampResponse_Configure(rr.configurationResponse);
c@69 126 break;
c@69 127 case RRType::Process:
c@69 128 j = VampJson::fromVampResponse_Process(rr.processResponse);
c@69 129 break;
c@69 130 case RRType::Finish:
c@69 131 j = VampJson::fromVampResponse_Finish(rr.finishResponse);
c@69 132 break;
c@69 133 case RRType::NotValid:
c@69 134 break;
c@69 135 }
c@69 136
c@69 137 return j.dump();
c@69 138 }
c@69 139
c@69 140 vector<Vamp::HostExt::PluginStaticData>
c@69 141 VamPipePluginLibrary::listPluginData() const
c@69 142 {
c@69 143 vector<Vamp::HostExt::PluginStaticData> data;
c@69 144 for (auto a: m_adapters) {
c@69 145 data.push_back(a.second->getStaticData());
c@69 146 }
c@69 147 return data;
c@69 148 }
c@69 149
c@69 150 Vamp::HostExt::LoadResponse
c@69 151 VamPipePluginLibrary::loadPlugin(Vamp::HostExt::LoadRequest req) const
c@69 152 {
c@69 153 string key = req.pluginKey;
c@69 154 if (m_adapters.find(key) != m_adapters.end()) {
c@69 155 return m_adapters.at(key)->loadPlugin(req);
c@69 156 } else {
c@69 157 throw runtime_error("no adapter for plugin key " + key);
c@69 158 }
c@69 159 }
c@69 160
c@69 161 Vamp::HostExt::ConfigurationResponse
c@69 162 VamPipePluginLibrary::configurePlugin(Vamp::HostExt::ConfigurationRequest req) const
c@69 163 {
c@69 164 for (Vamp::HostExt::PluginConfiguration::ParameterMap::const_iterator i =
c@69 165 req.configuration.parameterValues.begin();
c@69 166 i != req.configuration.parameterValues.end(); ++i) {
c@69 167 req.plugin->setParameter(i->first, i->second);
c@69 168 }
c@69 169
c@69 170 if (req.configuration.currentProgram != "") {
c@69 171 req.plugin->selectProgram(req.configuration.currentProgram);
c@69 172 }
c@69 173
c@69 174 Vamp::HostExt::ConfigurationResponse response;
c@69 175
c@69 176 if (req.plugin->initialise(req.configuration.channelCount,
c@69 177 req.configuration.stepSize,
c@69 178 req.configuration.blockSize)) {
c@69 179 response.outputs = req.plugin->getOutputDescriptors();
c@69 180 }
c@69 181
c@69 182 return response;
c@69 183 }
c@69 184
c@69 185 string
c@69 186 VamPipePluginLibrary::requestJsonImpl(string req)
c@69 187 {
c@69 188 RequestOrResponse request = readRequest(req);
c@69 189
c@69 190 RequestOrResponse response;
c@69 191 response.direction = RequestOrResponse::Response;
c@69 192 response.type = request.type;
c@69 193
c@69 194 switch (request.type) {
c@69 195
c@69 196 case RRType::List:
c@69 197 response.listResponse = listPluginData();
c@69 198 response.success = true;
c@69 199 break;
c@69 200
c@69 201 case RRType::Load:
c@69 202 response.loadResponse = loadPlugin(request.loadRequest);
c@69 203 if (response.loadResponse.plugin) {
c@69 204 m_mapper.addPlugin(response.loadResponse.plugin);
c@69 205 response.success = true;
c@69 206 }
c@69 207 break;
c@69 208
c@69 209 case RRType::Configure:
c@69 210 {
c@69 211 auto &creq = request.configurationRequest;
c@69 212 auto h = m_mapper.pluginToHandle(creq.plugin);
c@69 213 if (m_mapper.isConfigured(h)) {
c@69 214 //!!! again, can't return through C abi
c@69 215 throw runtime_error("plugin has already been configured");
c@69 216 }
c@69 217
c@69 218 response.configurationResponse = configurePlugin(creq);
c@69 219
c@69 220 if (!response.configurationResponse.outputs.empty()) {
c@69 221 m_mapper.markConfigured
c@69 222 (h, creq.configuration.channelCount, creq.configuration.blockSize);
c@69 223 response.success = true;
c@69 224 }
c@69 225 break;
c@69 226 }
c@69 227
c@69 228 case RRType::Process:
c@69 229 {
c@69 230 auto &preq = request.processRequest;
c@69 231 auto h = m_mapper.pluginToHandle(preq.plugin);
c@69 232 if (!m_mapper.isConfigured(h)) {
c@69 233 throw runtime_error("plugin has not been configured");
c@69 234 }
c@69 235
c@69 236 int channels = int(preq.inputBuffers.size());
c@69 237 if (channels != m_mapper.getChannelCount(h)) {
c@69 238 throw runtime_error("wrong number of channels supplied to process");
c@69 239 }
c@69 240
c@69 241 const float **fbuffers = new const float *[channels];
c@69 242 for (int i = 0; i < channels; ++i) {
c@69 243 if (int(preq.inputBuffers[i].size()) != m_mapper.getBlockSize(h)) {
c@69 244 delete[] fbuffers;
c@69 245 throw runtime_error("wrong block size supplied to process");
c@69 246 }
c@69 247 fbuffers[i] = preq.inputBuffers[i].data();
c@69 248 }
c@69 249
c@69 250 response.processResponse.features =
c@69 251 preq.plugin->process(fbuffers, preq.timestamp);
c@69 252 response.success = true;
c@69 253
c@69 254 delete[] fbuffers;
c@69 255 break;
c@69 256 }
c@69 257
c@69 258 case RRType::Finish:
c@69 259 {
c@69 260 auto h = m_mapper.pluginToHandle(request.finishPlugin);
c@69 261
c@69 262 response.finishResponse.features =
c@69 263 request.finishPlugin->getRemainingFeatures();
c@69 264
c@69 265 m_mapper.removePlugin(h);
c@69 266 delete request.finishPlugin;
c@69 267 response.success = true;
c@69 268 break;
c@69 269 }
c@69 270
c@69 271 case RRType::NotValid:
c@69 272 break;
c@69 273 }
c@69 274
c@69 275 return writeResponse(response);
c@69 276 }
c@69 277
c@69 278 }
c@69 279