c@69: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ c@69: c@69: /* c@69: VamPipe c@69: c@69: Centre for Digital Music, Queen Mary, University of London. c@69: Copyright 2015-2016 QMUL. c@69: c@69: Permission is hereby granted, free of charge, to any person c@69: obtaining a copy of this software and associated documentation c@69: files (the "Software"), to deal in the Software without c@69: restriction, including without limitation the rights to use, copy, c@69: modify, merge, publish, distribute, sublicense, and/or sell copies c@69: of the Software, and to permit persons to whom the Software is c@69: furnished to do so, subject to the following conditions: c@69: c@69: The above copyright notice and this permission notice shall be c@69: included in all copies or substantial portions of the Software. c@69: c@69: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, c@69: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF c@69: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND c@69: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR c@69: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF c@69: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION c@69: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. c@69: c@69: Except as contained in this notice, the names of the Centre for c@69: Digital Music; Queen Mary, University of London; and Chris Cannam c@69: shall not be used in advertising or otherwise to promote the sale, c@69: use or other dealings in this Software without prior written c@69: authorization. c@69: */ c@69: c@69: #include "VamPipePluginLibrary.h" c@69: #include "VamPipeAdapter.h" c@69: #include "json/VampJson.h" c@69: c@69: using namespace std; c@69: using namespace json11; c@69: c@69: namespace vampipe { c@69: c@69: //!!! too many explicit namespaces here c@69: c@69: //!!! dup with vampipe-convert c@99: Json c@99: convertRequestJson(string input, string &err) c@69: { c@69: Json j = Json::parse(input, err); c@69: if (err != "") { c@99: err = "invalid json: " + err; c@99: return {}; c@69: } c@69: if (!j.is_object()) { c@99: err = "object expected at top level"; c@99: } else if (!j["type"].is_string()) { c@99: err = "string expected for type field"; c@99: } else if (!j["content"].is_null() && !j["content"].is_object()) { c@99: err = "object expected for content field"; c@69: } c@69: return j; c@69: } c@69: c@88: VamPipePluginLibrary::VamPipePluginLibrary(vector pp) : c@81: m_useBase64(false) c@69: { c@88: for (VamPipeAdapterInterface *p: pp) { c@69: string key = p->getStaticData().pluginKey; c@69: m_adapters[key] = p; c@69: } c@69: } c@69: c@94: Vamp::HostExt::ListResponse c@69: VamPipePluginLibrary::listPluginData() const c@69: { c@94: Vamp::HostExt::ListResponse resp; c@69: for (auto a: m_adapters) { c@101: resp.plugins.push_back(a.second->getStaticData()); c@69: } c@94: return resp; c@69: } c@69: c@69: Vamp::HostExt::LoadResponse c@99: VamPipePluginLibrary::loadPlugin(Vamp::HostExt::LoadRequest req, string &err) const c@69: { c@69: string key = req.pluginKey; c@69: if (m_adapters.find(key) != m_adapters.end()) { c@99: auto resp = m_adapters.at(key)->loadPlugin(req); c@99: if (!resp.plugin) { c@99: // This should not actually happen -- the load call here c@99: // is just an object construction, not a dynamic load. But c@99: // report it if it does... c@99: err = "failed to construct plugin with key " + key; c@99: } c@99: return resp; c@69: } else { c@99: err = "no adapter for plugin key " + key; c@99: return {}; c@69: } c@69: } c@69: c@69: Vamp::HostExt::ConfigurationResponse c@99: VamPipePluginLibrary::configurePlugin(Vamp::HostExt::ConfigurationRequest req, c@99: string &err) const c@69: { c@69: for (Vamp::HostExt::PluginConfiguration::ParameterMap::const_iterator i = c@69: req.configuration.parameterValues.begin(); c@69: i != req.configuration.parameterValues.end(); ++i) { c@69: req.plugin->setParameter(i->first, i->second); c@69: } c@69: c@69: if (req.configuration.currentProgram != "") { c@69: req.plugin->selectProgram(req.configuration.currentProgram); c@69: } c@69: c@69: Vamp::HostExt::ConfigurationResponse response; c@69: c@96: response.plugin = req.plugin; c@96: c@69: if (req.plugin->initialise(req.configuration.channelCount, c@69: req.configuration.stepSize, c@69: req.configuration.blockSize)) { c@69: response.outputs = req.plugin->getOutputDescriptors(); c@99: } else { c@99: err = "configuration failed (wrong channel count, step size, block size?)"; c@69: } c@69: c@69: return response; c@69: } c@69: c@69: string c@83: VamPipePluginLibrary::processRawImpl(int pluginHandle, c@83: const float *const *inputBuffers, c@83: int sec, c@83: int nsec) c@82: { c@99: Vamp::Plugin *plugin = m_mapper.handleToPlugin(pluginHandle); c@99: if (!plugin) { c@99: return VampJson::fromError("unknown plugin handle", RRType::Process) c@98: .dump(); c@82: } c@99: c@99: if (!m_mapper.isConfigured(pluginHandle)) { c@99: return VampJson::fromError("plugin has not been configured", RRType::Process) c@99: .dump(); c@99: } c@99: c@99: Vamp::RealTime timestamp(sec, nsec); c@99: c@99: Vamp::HostExt::ProcessResponse resp; c@99: resp.plugin = plugin; c@99: resp.features = plugin->process(inputBuffers, timestamp); c@99: c@99: m_useBase64 = true; c@99: c@99: return VampJson::fromVampResponse_Process c@99: (resp, m_mapper, c@99: VampJson::BufferSerialisation::Base64) c@99: .dump(); c@82: } c@82: c@82: string c@69: VamPipePluginLibrary::requestJsonImpl(string req) c@69: { c@99: string err; c@99: c@99: Json j = convertRequestJson(req, err); c@99: if (err != "") { c@99: return VampJson::fromError(err, RRType::NotValid).dump(); c@99: } c@99: c@99: RRType type = VampJson::getRequestResponseType(j, err); c@99: if (err != "") { c@99: return VampJson::fromError(err, RRType::NotValid).dump(); c@70: } c@69: c@98: VampJson::BufferSerialisation serialisation = c@98: (m_useBase64 ? c@98: VampJson::BufferSerialisation::Base64 : c@98: VampJson::BufferSerialisation::Text); c@69: c@98: Json rj; c@98: c@99: switch (type) { c@69: c@99: case RRType::List: c@99: rj = VampJson::fromVampResponse_List(listPluginData()); c@99: break; c@69: c@99: case RRType::Load: c@99: { c@99: auto req = VampJson::toVampRequest_Load(j, err); c@99: if (err != "") { c@99: rj = VampJson::fromError(err, type); c@99: } else { c@99: auto resp = loadPlugin(req, err); c@99: if (err != "") { c@99: rj = VampJson::fromError(err, type); c@99: } else { c@98: m_mapper.addPlugin(resp.plugin); c@99: rj = VampJson::fromVampResponse_Load(resp, m_mapper); c@98: } c@92: } c@99: break; c@99: } c@92: c@99: case RRType::Configure: c@99: { c@99: auto req = VampJson::toVampRequest_Configure(j, m_mapper, err); c@99: if (err != "") { c@99: rj = VampJson::fromError(err, type); c@99: } else { c@98: auto h = m_mapper.pluginToHandle(req.plugin); c@99: if (h == m_mapper.INVALID_HANDLE) { c@99: rj = VampJson::fromError("unknown or invalid plugin handle", type); c@99: } else if (m_mapper.isConfigured(h)) { c@99: rj = VampJson::fromError("plugin has already been configured", type); c@99: } else { c@99: auto resp = configurePlugin(req, err); c@99: if (err != "") { c@99: rj = VampJson::fromError(err, type); c@99: } else { c@99: m_mapper.markConfigured(h, c@99: req.configuration.channelCount, c@99: req.configuration.blockSize); c@99: rj = VampJson::fromVampResponse_Configure(resp, m_mapper); c@99: } c@98: } c@99: } c@99: break; c@99: } c@69: c@99: case RRType::Process: c@99: { c@99: VampJson::BufferSerialisation serialisation; c@99: c@99: auto req = VampJson::toVampRequest_Process(j, m_mapper, c@99: serialisation, err); c@99: if (err != "") { c@99: rj = VampJson::fromError(err, type); c@99: } else { c@99: auto h = m_mapper.pluginToHandle(req.plugin); c@99: int channels = int(req.inputBuffers.size()); c@99: if (h == m_mapper.INVALID_HANDLE) { c@99: rj = VampJson::fromError("unknown or invalid plugin handle", type); c@99: } else if (!m_mapper.isConfigured(h)) { c@99: rj = VampJson::fromError("plugin has not been configured", type); c@99: } else if (channels != m_mapper.getChannelCount(h)) { c@99: rj = VampJson::fromError("wrong number of channels supplied", type); c@99: } else { c@99: c@99: if (serialisation == VampJson::BufferSerialisation::Base64) { c@99: m_useBase64 = true; c@99: } c@99: c@99: size_t blockSize = m_mapper.getBlockSize(h); c@99: c@99: const float **fbuffers = new const float *[channels]; c@99: for (int i = 0; i < channels; ++i) { c@99: if (req.inputBuffers[i].size() != blockSize) { c@99: delete[] fbuffers; c@99: fbuffers = 0; c@99: rj = VampJson::fromError("wrong block size supplied", type); c@99: break; c@99: } c@99: fbuffers[i] = req.inputBuffers[i].data(); c@99: } c@99: c@99: if (fbuffers) { c@99: Vamp::HostExt::ProcessResponse resp; c@99: resp.plugin = req.plugin; c@99: resp.features = req.plugin->process(fbuffers, req.timestamp); c@99: delete[] fbuffers; c@99: rj = VampJson::fromVampResponse_Process c@99: (resp, m_mapper, serialisation); c@99: } c@98: } c@99: } c@99: break; c@99: } c@98: c@99: case RRType::Finish: c@99: { c@99: auto req = VampJson::toVampRequest_Finish(j, m_mapper, err); c@99: if (err != "") { c@99: rj = VampJson::fromError(err, type); c@99: } else { c@99: auto h = m_mapper.pluginToHandle(req.plugin); c@99: if (h == m_mapper.INVALID_HANDLE) { c@99: rj = VampJson::fromError("unknown or invalid plugin handle", type); c@99: } else if (!m_mapper.isConfigured(h)) { c@99: rj = VampJson::fromError("plugin has not been configured", type); c@99: } else { c@99: c@99: Vamp::HostExt::ProcessResponse resp; c@99: resp.plugin = req.plugin; c@99: resp.features = req.plugin->getRemainingFeatures(); c@99: c@99: rj = VampJson::fromVampResponse_Finish c@99: (resp, m_mapper, serialisation); c@99: c@99: m_mapper.removePlugin(h); c@99: delete req.plugin; c@99: } c@98: } c@99: break; c@99: } c@98: c@99: case RRType::NotValid: c@99: rj = VampJson::fromError("invalid request", type); c@99: break; c@69: } c@98: c@98: return rj.dump(); c@69: } c@69: c@69: } c@69: