Mercurial > hg > piper-vamp-js
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/PiperPluginLibrary.cpp Tue Nov 08 12:02:57 2016 +0000 @@ -0,0 +1,349 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Piper Vamp JSON Adapter + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2015-2016 QMUL. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#include "PiperPluginLibrary.h" +#include "PiperAdapter.h" + +#include "vamp-json/VampJson.h" + +using namespace std; +using namespace json11; +using namespace piper_vamp; + +namespace piper_vamp_js { //!!! not good + +//!!! too many explicit namespaces here + +//!!! dup with piper-convert +Json +convertRequestJson(string input, string &err) +{ + Json j = Json::parse(input, err); + if (err != "") { + err = "invalid json: " + err; + return {}; + } + if (!j.is_object()) { + err = "object expected at top level"; + } + return j; +} + +PiperPluginLibrary::PiperPluginLibrary(vector<PiperAdapterInterface *> pp) : + m_useBase64(false) +{ + for (PiperAdapterInterface *p: pp) { + string key = p->getStaticData().pluginKey; + m_adapters[key] = p; + } +} + +ListResponse +PiperPluginLibrary::listPluginData(ListRequest req) const +{ + bool filtered = !req.from.empty(); + ListResponse resp; + for (auto a: m_adapters) { + if (filtered) { + auto n = a.second->getLibraryName(); + bool found = false; + for (const auto &f: req.from) { + if (f == n) { + found = true; + break; + } + } + if (!found) { + continue; + } + } + resp.available.push_back(a.second->getStaticData()); + } + return resp; +} + +LoadResponse +PiperPluginLibrary::loadPlugin(LoadRequest req, string &err) const +{ + string key = req.pluginKey; + if (m_adapters.find(key) != m_adapters.end()) { + auto resp = m_adapters.at(key)->loadPlugin(req); + if (!resp.plugin) { + // This should not actually happen -- the load call here + // is just an object construction, not a dynamic load. But + // report it if it does... + err = "failed to construct plugin with key " + key; + } + return resp; + } else { + err = "no adapter for plugin key " + key; + return {}; + } +} + +ConfigurationResponse +PiperPluginLibrary::configurePlugin(ConfigurationRequest req, + string &err) const +{ + for (PluginConfiguration::ParameterMap::const_iterator i = + req.configuration.parameterValues.begin(); + i != req.configuration.parameterValues.end(); ++i) { + req.plugin->setParameter(i->first, i->second); + } + + if (req.configuration.currentProgram != "") { + req.plugin->selectProgram(req.configuration.currentProgram); + } + + ConfigurationResponse response; + + response.plugin = req.plugin; + + if (req.plugin->initialise(req.configuration.channelCount, + req.configuration.stepSize, + req.configuration.blockSize)) { + response.outputs = req.plugin->getOutputDescriptors(); + } else { + err = "configuration failed (wrong channel count, step size, block size?)"; + } + + return response; +} + +string +PiperPluginLibrary::processRawImpl(int handle, + const float *const *inputBuffers, + int sec, + int nsec) +{ + Vamp::Plugin *plugin = m_mapper.handleToPlugin(handle); + if (!plugin) { + return VampJson::fromError("unknown plugin handle", + RRType::Process, Json()) + .dump(); + } + + if (!m_mapper.isConfigured(handle)) { + return VampJson::fromError("plugin has not been configured", + RRType::Process, Json()) + .dump(); + } + + Vamp::RealTime timestamp(sec, nsec); + + ProcessResponse resp; + resp.plugin = plugin; + resp.features = plugin->process(inputBuffers, timestamp); + + m_useBase64 = true; + + return VampJson::fromRpcResponse_Process + (resp, m_mapper, + VampJson::BufferSerialisation::Base64, + Json()) + .dump(); +} + +string +PiperPluginLibrary::requestJsonImpl(string req) +{ + string err; + + Json j = convertRequestJson(req, err); + + // we don't care what this is, only that it is retained in the response: + auto id = j["id"]; + + Json rj; + if (err != "") { + return VampJson::fromError(err, RRType::NotValid, id).dump(); + } + + RRType type = VampJson::getRequestResponseType(j, err); + if (err != "") { + return VampJson::fromError(err, RRType::NotValid, id).dump(); + } + + VampJson::BufferSerialisation serialisation = + (m_useBase64 ? + VampJson::BufferSerialisation::Base64 : + VampJson::BufferSerialisation::Array); + + switch (type) { + + case RRType::List: + { + auto req = VampJson::toRpcRequest_List(j, err); + if (err != "") { + rj = VampJson::fromError(err, type, id); + } else { + rj = VampJson::fromRpcResponse_List(listPluginData(req), id); + } + break; + } + + case RRType::Load: + { + auto req = VampJson::toRpcRequest_Load(j, err); + if (err != "") { + rj = VampJson::fromError(err, type, id); + } else { + auto resp = loadPlugin(req, err); + if (err != "") { + rj = VampJson::fromError(err, type, id); + } else { + m_mapper.addPlugin(resp.plugin); + rj = VampJson::fromRpcResponse_Load(resp, m_mapper, id); + } + } + break; + } + + case RRType::Configure: + { + auto req = VampJson::toRpcRequest_Configure(j, m_mapper, err); + if (err != "") { + rj = VampJson::fromError(err, type, id); + } else { + auto h = m_mapper.pluginToHandle(req.plugin); + if (h == m_mapper.INVALID_HANDLE) { + rj = VampJson::fromError("unknown or invalid plugin handle", type, id); + } else if (m_mapper.isConfigured(h)) { + rj = VampJson::fromError("plugin has already been configured", type, id); + } else { + auto resp = configurePlugin(req, err); + if (err != "") { + rj = VampJson::fromError(err, type, id); + } else { + m_mapper.markConfigured(h, + req.configuration.channelCount, + req.configuration.blockSize); + rj = VampJson::fromRpcResponse_Configure(resp, m_mapper, id); + } + } + } + break; + } + + case RRType::Process: + { + VampJson::BufferSerialisation serialisation; + + auto req = VampJson::toRpcRequest_Process(j, m_mapper, + serialisation, err); + if (err != "") { + rj = VampJson::fromError(err, type, id); + } else { + auto h = m_mapper.pluginToHandle(req.plugin); + int channels = int(req.inputBuffers.size()); + if (h == m_mapper.INVALID_HANDLE) { + rj = VampJson::fromError("unknown or invalid plugin handle", type, id); + } else if (!m_mapper.isConfigured(h)) { + rj = VampJson::fromError("plugin has not been configured", type, id); + } else if (channels != m_mapper.getChannelCount(h)) { + rj = VampJson::fromError("wrong number of channels supplied", type, id); + } else { + + if (serialisation == VampJson::BufferSerialisation::Base64) { + m_useBase64 = true; + } + + size_t blockSize = m_mapper.getBlockSize(h); + + const float **fbuffers = new const float *[channels]; + for (int i = 0; i < channels; ++i) { + if (req.inputBuffers[i].size() != blockSize) { + delete[] fbuffers; + fbuffers = 0; + rj = VampJson::fromError("wrong block size supplied", type, id); + break; + } + fbuffers[i] = req.inputBuffers[i].data(); + } + + if (fbuffers) { + ProcessResponse resp; + resp.plugin = req.plugin; + resp.features = req.plugin->process(fbuffers, req.timestamp); + delete[] fbuffers; + rj = VampJson::fromRpcResponse_Process + (resp, m_mapper, serialisation, id); + } + } + } + break; + } + + case RRType::Finish: + { + auto req = VampJson::toRpcRequest_Finish(j, m_mapper, err); + if (err != "") { + rj = VampJson::fromError(err, type, id); + } else { + auto h = m_mapper.pluginToHandle(req.plugin); + if (h == m_mapper.INVALID_HANDLE) { + rj = VampJson::fromError("unknown or invalid plugin handle", type, id); + } else { + + FinishResponse resp; + resp.plugin = req.plugin; + + // Finish can be called (to unload the plugin) even if + // the plugin has never been configured or used. But + // we want to make sure we call getRemainingFeatures + // only if we have actually configured the plugin. + if (m_mapper.isConfigured(h)) { + resp.features = req.plugin->getRemainingFeatures(); + } + + rj = VampJson::fromRpcResponse_Finish + (resp, m_mapper, serialisation, id); + + m_mapper.removePlugin(h); + delete req.plugin; + } + } + break; + } + + case RRType::NotValid: + rj = VampJson::fromError("invalid request", type, id); + break; + } + + return rj.dump(); +} + +} +