annotate VamPipePluginLibrary.cpp @ 105:4845fbb1a516

Instead of using separate values and b64values entries in JSON serialisations, allow numeric arrays to be replaced by b64 variants wherever they appear (discriminating by type). Also rename values to featureValues in feature throughout, as values turns out to be a hazardous name in a JS context. Finally use Array instead of Text for array encoding (seems clearer).
author Chris Cannam <c.cannam@qmul.ac.uk>
date Tue, 27 Sep 2016 15:04:59 +0100
parents d91781a91861
children a9dbf9f45896
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@99 47 Json
c@99 48 convertRequestJson(string input, string &err)
c@69 49 {
c@69 50 Json j = Json::parse(input, err);
c@69 51 if (err != "") {
c@99 52 err = "invalid json: " + err;
c@99 53 return {};
c@69 54 }
c@69 55 if (!j.is_object()) {
c@99 56 err = "object expected at top level";
c@99 57 } else if (!j["type"].is_string()) {
c@99 58 err = "string expected for type field";
c@99 59 } else if (!j["content"].is_null() && !j["content"].is_object()) {
c@99 60 err = "object expected for content field";
c@69 61 }
c@69 62 return j;
c@69 63 }
c@69 64
c@88 65 VamPipePluginLibrary::VamPipePluginLibrary(vector<VamPipeAdapterInterface *> pp) :
c@81 66 m_useBase64(false)
c@69 67 {
c@88 68 for (VamPipeAdapterInterface *p: pp) {
c@69 69 string key = p->getStaticData().pluginKey;
c@69 70 m_adapters[key] = p;
c@69 71 }
c@69 72 }
c@69 73
c@94 74 Vamp::HostExt::ListResponse
c@69 75 VamPipePluginLibrary::listPluginData() const
c@69 76 {
c@94 77 Vamp::HostExt::ListResponse resp;
c@69 78 for (auto a: m_adapters) {
c@101 79 resp.plugins.push_back(a.second->getStaticData());
c@69 80 }
c@94 81 return resp;
c@69 82 }
c@69 83
c@69 84 Vamp::HostExt::LoadResponse
c@99 85 VamPipePluginLibrary::loadPlugin(Vamp::HostExt::LoadRequest req, string &err) const
c@69 86 {
c@69 87 string key = req.pluginKey;
c@69 88 if (m_adapters.find(key) != m_adapters.end()) {
c@99 89 auto resp = m_adapters.at(key)->loadPlugin(req);
c@99 90 if (!resp.plugin) {
c@99 91 // This should not actually happen -- the load call here
c@99 92 // is just an object construction, not a dynamic load. But
c@99 93 // report it if it does...
c@99 94 err = "failed to construct plugin with key " + key;
c@99 95 }
c@99 96 return resp;
c@69 97 } else {
c@99 98 err = "no adapter for plugin key " + key;
c@99 99 return {};
c@69 100 }
c@69 101 }
c@69 102
c@69 103 Vamp::HostExt::ConfigurationResponse
c@99 104 VamPipePluginLibrary::configurePlugin(Vamp::HostExt::ConfigurationRequest req,
c@99 105 string &err) const
c@69 106 {
c@69 107 for (Vamp::HostExt::PluginConfiguration::ParameterMap::const_iterator i =
c@69 108 req.configuration.parameterValues.begin();
c@69 109 i != req.configuration.parameterValues.end(); ++i) {
c@69 110 req.plugin->setParameter(i->first, i->second);
c@69 111 }
c@69 112
c@69 113 if (req.configuration.currentProgram != "") {
c@69 114 req.plugin->selectProgram(req.configuration.currentProgram);
c@69 115 }
c@69 116
c@69 117 Vamp::HostExt::ConfigurationResponse response;
c@69 118
c@96 119 response.plugin = req.plugin;
c@96 120
c@69 121 if (req.plugin->initialise(req.configuration.channelCount,
c@69 122 req.configuration.stepSize,
c@69 123 req.configuration.blockSize)) {
c@69 124 response.outputs = req.plugin->getOutputDescriptors();
c@99 125 } else {
c@99 126 err = "configuration failed (wrong channel count, step size, block size?)";
c@69 127 }
c@69 128
c@69 129 return response;
c@69 130 }
c@69 131
c@69 132 string
c@83 133 VamPipePluginLibrary::processRawImpl(int pluginHandle,
c@83 134 const float *const *inputBuffers,
c@83 135 int sec,
c@83 136 int nsec)
c@82 137 {
c@99 138 Vamp::Plugin *plugin = m_mapper.handleToPlugin(pluginHandle);
c@99 139 if (!plugin) {
c@99 140 return VampJson::fromError("unknown plugin handle", RRType::Process)
c@98 141 .dump();
c@82 142 }
c@99 143
c@99 144 if (!m_mapper.isConfigured(pluginHandle)) {
c@99 145 return VampJson::fromError("plugin has not been configured", RRType::Process)
c@99 146 .dump();
c@99 147 }
c@99 148
c@99 149 Vamp::RealTime timestamp(sec, nsec);
c@99 150
c@99 151 Vamp::HostExt::ProcessResponse resp;
c@99 152 resp.plugin = plugin;
c@99 153 resp.features = plugin->process(inputBuffers, timestamp);
c@99 154
c@99 155 m_useBase64 = true;
c@99 156
c@99 157 return VampJson::fromVampResponse_Process
c@99 158 (resp, m_mapper,
c@99 159 VampJson::BufferSerialisation::Base64)
c@99 160 .dump();
c@82 161 }
c@82 162
c@82 163 string
c@69 164 VamPipePluginLibrary::requestJsonImpl(string req)
c@69 165 {
c@99 166 string err;
c@99 167
c@99 168 Json j = convertRequestJson(req, err);
c@99 169 if (err != "") {
c@99 170 return VampJson::fromError(err, RRType::NotValid).dump();
c@99 171 }
c@99 172
c@99 173 RRType type = VampJson::getRequestResponseType(j, err);
c@99 174 if (err != "") {
c@99 175 return VampJson::fromError(err, RRType::NotValid).dump();
c@70 176 }
c@69 177
c@98 178 VampJson::BufferSerialisation serialisation =
c@98 179 (m_useBase64 ?
c@98 180 VampJson::BufferSerialisation::Base64 :
c@105 181 VampJson::BufferSerialisation::Array);
c@69 182
c@98 183 Json rj;
c@98 184
c@99 185 switch (type) {
c@69 186
c@99 187 case RRType::List:
c@99 188 rj = VampJson::fromVampResponse_List(listPluginData());
c@99 189 break;
c@69 190
c@99 191 case RRType::Load:
c@99 192 {
c@99 193 auto req = VampJson::toVampRequest_Load(j, err);
c@99 194 if (err != "") {
c@99 195 rj = VampJson::fromError(err, type);
c@99 196 } else {
c@99 197 auto resp = loadPlugin(req, err);
c@99 198 if (err != "") {
c@99 199 rj = VampJson::fromError(err, type);
c@99 200 } else {
c@98 201 m_mapper.addPlugin(resp.plugin);
c@99 202 rj = VampJson::fromVampResponse_Load(resp, m_mapper);
c@98 203 }
c@92 204 }
c@99 205 break;
c@99 206 }
c@92 207
c@99 208 case RRType::Configure:
c@99 209 {
c@99 210 auto req = VampJson::toVampRequest_Configure(j, m_mapper, err);
c@99 211 if (err != "") {
c@99 212 rj = VampJson::fromError(err, type);
c@99 213 } else {
c@98 214 auto h = m_mapper.pluginToHandle(req.plugin);
c@99 215 if (h == m_mapper.INVALID_HANDLE) {
c@99 216 rj = VampJson::fromError("unknown or invalid plugin handle", type);
c@99 217 } else if (m_mapper.isConfigured(h)) {
c@99 218 rj = VampJson::fromError("plugin has already been configured", type);
c@99 219 } else {
c@99 220 auto resp = configurePlugin(req, err);
c@99 221 if (err != "") {
c@99 222 rj = VampJson::fromError(err, type);
c@99 223 } else {
c@99 224 m_mapper.markConfigured(h,
c@99 225 req.configuration.channelCount,
c@99 226 req.configuration.blockSize);
c@99 227 rj = VampJson::fromVampResponse_Configure(resp, m_mapper);
c@99 228 }
c@98 229 }
c@99 230 }
c@99 231 break;
c@99 232 }
c@69 233
c@99 234 case RRType::Process:
c@99 235 {
c@99 236 VampJson::BufferSerialisation serialisation;
c@99 237
c@99 238 auto req = VampJson::toVampRequest_Process(j, m_mapper,
c@99 239 serialisation, err);
c@99 240 if (err != "") {
c@99 241 rj = VampJson::fromError(err, type);
c@99 242 } else {
c@99 243 auto h = m_mapper.pluginToHandle(req.plugin);
c@99 244 int channels = int(req.inputBuffers.size());
c@99 245 if (h == m_mapper.INVALID_HANDLE) {
c@99 246 rj = VampJson::fromError("unknown or invalid plugin handle", type);
c@99 247 } else if (!m_mapper.isConfigured(h)) {
c@99 248 rj = VampJson::fromError("plugin has not been configured", type);
c@99 249 } else if (channels != m_mapper.getChannelCount(h)) {
c@99 250 rj = VampJson::fromError("wrong number of channels supplied", type);
c@99 251 } else {
c@99 252
c@99 253 if (serialisation == VampJson::BufferSerialisation::Base64) {
c@99 254 m_useBase64 = true;
c@99 255 }
c@99 256
c@99 257 size_t blockSize = m_mapper.getBlockSize(h);
c@99 258
c@99 259 const float **fbuffers = new const float *[channels];
c@99 260 for (int i = 0; i < channels; ++i) {
c@99 261 if (req.inputBuffers[i].size() != blockSize) {
c@99 262 delete[] fbuffers;
c@99 263 fbuffers = 0;
c@99 264 rj = VampJson::fromError("wrong block size supplied", type);
c@99 265 break;
c@99 266 }
c@99 267 fbuffers[i] = req.inputBuffers[i].data();
c@99 268 }
c@99 269
c@99 270 if (fbuffers) {
c@99 271 Vamp::HostExt::ProcessResponse resp;
c@99 272 resp.plugin = req.plugin;
c@99 273 resp.features = req.plugin->process(fbuffers, req.timestamp);
c@99 274 delete[] fbuffers;
c@99 275 rj = VampJson::fromVampResponse_Process
c@99 276 (resp, m_mapper, serialisation);
c@99 277 }
c@98 278 }
c@99 279 }
c@99 280 break;
c@99 281 }
c@98 282
c@99 283 case RRType::Finish:
c@99 284 {
c@99 285 auto req = VampJson::toVampRequest_Finish(j, m_mapper, err);
c@99 286 if (err != "") {
c@99 287 rj = VampJson::fromError(err, type);
c@99 288 } else {
c@99 289 auto h = m_mapper.pluginToHandle(req.plugin);
c@99 290 if (h == m_mapper.INVALID_HANDLE) {
c@99 291 rj = VampJson::fromError("unknown or invalid plugin handle", type);
c@99 292 } else if (!m_mapper.isConfigured(h)) {
c@99 293 rj = VampJson::fromError("plugin has not been configured", type);
c@99 294 } else {
c@99 295
c@99 296 Vamp::HostExt::ProcessResponse resp;
c@99 297 resp.plugin = req.plugin;
c@99 298 resp.features = req.plugin->getRemainingFeatures();
c@99 299
c@99 300 rj = VampJson::fromVampResponse_Finish
c@99 301 (resp, m_mapper, serialisation);
c@99 302
c@99 303 m_mapper.removePlugin(h);
c@99 304 delete req.plugin;
c@99 305 }
c@98 306 }
c@99 307 break;
c@99 308 }
c@98 309
c@99 310 case RRType::NotValid:
c@99 311 rj = VampJson::fromError("invalid request", type);
c@99 312 break;
c@69 313 }
c@98 314
c@98 315 return rj.dump();
c@69 316 }
c@69 317
c@69 318 }
c@69 319