Chris@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@0: VamPipe Chris@0: Chris@0: Centre for Digital Music, Queen Mary, University of London. Chris@0: Copyright 2015-2016 QMUL. Chris@0: Chris@0: Permission is hereby granted, free of charge, to any person Chris@0: obtaining a copy of this software and associated documentation Chris@0: files (the "Software"), to deal in the Software without Chris@0: restriction, including without limitation the rights to use, copy, Chris@0: modify, merge, publish, distribute, sublicense, and/or sell copies Chris@0: of the Software, and to permit persons to whom the Software is Chris@0: furnished to do so, subject to the following conditions: Chris@0: Chris@0: The above copyright notice and this permission notice shall be Chris@0: included in all copies or substantial portions of the Software. Chris@0: Chris@0: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Chris@0: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF Chris@0: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND Chris@0: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR Chris@0: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF Chris@0: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION Chris@0: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Chris@0: Chris@0: Except as contained in this notice, the names of the Centre for Chris@0: Digital Music; Queen Mary, University of London; and Chris Cannam Chris@0: shall not be used in advertising or otherwise to promote the sale, Chris@0: use or other dealings in this Software without prior written Chris@0: authorization. Chris@0: */ Chris@0: Chris@0: #include "VamPipePluginLibrary.h" Chris@0: #include "VamPipeAdapter.h" Chris@0: #include "json/VampJson.h" Chris@0: Chris@0: using namespace std; Chris@0: using namespace json11; Chris@0: Chris@0: namespace vampipe { Chris@0: Chris@0: //!!! too many explicit namespaces here Chris@0: Chris@0: //!!! dup with vampipe-convert Chris@0: static Json Chris@0: convertRequestJson(string input) Chris@0: { Chris@0: string err; Chris@0: Json j = Json::parse(input, err); Chris@0: if (err != "") { Chris@0: throw VampJson::Failure("invalid json: " + err); Chris@0: } Chris@0: if (!j.is_object()) { Chris@0: throw VampJson::Failure("object expected at top level"); Chris@0: } Chris@0: if (!j["type"].is_string()) { Chris@0: throw VampJson::Failure("string expected for type field"); Chris@0: } Chris@2: if (!j["content"].is_null() && !j["content"].is_object()) { Chris@0: throw VampJson::Failure("object expected for content field"); Chris@0: } Chris@0: return j; Chris@0: } Chris@0: Chris@0: VamPipePluginLibrary::VamPipePluginLibrary(vector pp) Chris@0: { Chris@0: for (VamPipeAdapterBase *p: pp) { Chris@0: string key = p->getStaticData().pluginKey; Chris@0: m_adapters[key] = p; Chris@0: } Chris@0: } Chris@0: Chris@0: RequestOrResponse Chris@0: VamPipePluginLibrary::readRequest(string req) const Chris@0: { Chris@0: RequestOrResponse rr; Chris@0: rr.direction = RequestOrResponse::Request; Chris@0: Chris@0: Json j = convertRequestJson(req); Chris@0: Chris@0: //!!! reduce, reduce Chris@0: rr.type = VampJson::getRequestResponseType(j); Chris@0: Chris@0: switch (rr.type) { Chris@0: Chris@0: case RRType::List: Chris@0: VampJson::toVampRequest_List(j); // type check only Chris@0: break; Chris@0: case RRType::Load: Chris@0: rr.loadRequest = VampJson::toVampRequest_Load(j); Chris@0: break; Chris@0: case RRType::Configure: Chris@0: rr.configurationRequest = VampJson::toVampRequest_Configure(j, m_mapper); Chris@0: break; Chris@0: case RRType::Process: Chris@0: rr.processRequest = VampJson::toVampRequest_Process(j, m_mapper); Chris@0: break; Chris@0: case RRType::Finish: Chris@0: rr.finishPlugin = VampJson::toVampRequest_Finish(j, m_mapper); Chris@0: break; Chris@0: case RRType::NotValid: Chris@0: break; Chris@0: } Chris@0: Chris@0: return rr; Chris@0: } Chris@0: Chris@0: string Chris@0: VamPipePluginLibrary::writeResponse(const RequestOrResponse &rr) const Chris@0: { Chris@0: Json j; Chris@0: Chris@0: switch (rr.type) { Chris@0: Chris@0: case RRType::List: Chris@0: j = VampJson::fromVampResponse_List("", rr.listResponse); Chris@0: break; Chris@0: case RRType::Load: Chris@0: j = VampJson::fromVampResponse_Load(rr.loadResponse, m_mapper); Chris@0: break; Chris@0: case RRType::Configure: Chris@0: j = VampJson::fromVampResponse_Configure(rr.configurationResponse); Chris@0: break; Chris@0: case RRType::Process: Chris@0: j = VampJson::fromVampResponse_Process(rr.processResponse); Chris@0: break; Chris@0: case RRType::Finish: Chris@0: j = VampJson::fromVampResponse_Finish(rr.finishResponse); Chris@0: break; Chris@0: case RRType::NotValid: Chris@0: break; Chris@0: } Chris@0: Chris@0: return j.dump(); Chris@0: } Chris@0: Chris@0: vector Chris@0: VamPipePluginLibrary::listPluginData() const Chris@0: { Chris@0: vector data; Chris@0: for (auto a: m_adapters) { Chris@0: data.push_back(a.second->getStaticData()); Chris@0: } Chris@0: return data; Chris@0: } Chris@0: Chris@0: Vamp::HostExt::LoadResponse Chris@0: VamPipePluginLibrary::loadPlugin(Vamp::HostExt::LoadRequest req) const Chris@0: { Chris@0: string key = req.pluginKey; Chris@0: if (m_adapters.find(key) != m_adapters.end()) { Chris@0: return m_adapters.at(key)->loadPlugin(req); Chris@0: } else { Chris@0: throw runtime_error("no adapter for plugin key " + key); Chris@0: } Chris@0: } Chris@0: Chris@0: Vamp::HostExt::ConfigurationResponse Chris@0: VamPipePluginLibrary::configurePlugin(Vamp::HostExt::ConfigurationRequest req) const Chris@0: { Chris@0: for (Vamp::HostExt::PluginConfiguration::ParameterMap::const_iterator i = Chris@0: req.configuration.parameterValues.begin(); Chris@0: i != req.configuration.parameterValues.end(); ++i) { Chris@0: req.plugin->setParameter(i->first, i->second); Chris@0: } Chris@0: Chris@0: if (req.configuration.currentProgram != "") { Chris@0: req.plugin->selectProgram(req.configuration.currentProgram); Chris@0: } Chris@0: Chris@0: Vamp::HostExt::ConfigurationResponse response; Chris@0: Chris@0: if (req.plugin->initialise(req.configuration.channelCount, Chris@0: req.configuration.stepSize, Chris@0: req.configuration.blockSize)) { Chris@0: response.outputs = req.plugin->getOutputDescriptors(); Chris@0: } Chris@0: Chris@0: return response; Chris@0: } Chris@0: Chris@0: string Chris@0: VamPipePluginLibrary::requestJsonImpl(string req) Chris@0: { Chris@1: RequestOrResponse request; Chris@1: Chris@1: try { Chris@1: request = readRequest(req); Chris@1: } catch (const std::exception &e) { Chris@1: return VampJson::fromException(e, RRType::NotValid).dump(); Chris@1: } Chris@0: Chris@0: RequestOrResponse response; Chris@0: response.direction = RequestOrResponse::Response; Chris@0: response.type = request.type; Chris@0: Chris@1: try { Chris@1: switch (request.type) { Chris@0: Chris@1: case RRType::List: Chris@1: response.listResponse = listPluginData(); Chris@1: response.success = true; Chris@1: break; Chris@0: Chris@1: case RRType::Load: Chris@1: response.loadResponse = loadPlugin(request.loadRequest); Chris@1: if (response.loadResponse.plugin) { Chris@1: m_mapper.addPlugin(response.loadResponse.plugin); Chris@1: response.success = true; Chris@1: } Chris@1: break; Chris@0: Chris@1: case RRType::Configure: Chris@1: { Chris@1: auto &creq = request.configurationRequest; Chris@1: auto h = m_mapper.pluginToHandle(creq.plugin); Chris@1: if (m_mapper.isConfigured(h)) { Chris@1: //!!! again, can't return through C abi Chris@1: throw runtime_error("plugin has already been configured"); Chris@1: } Chris@1: Chris@1: response.configurationResponse = configurePlugin(creq); Chris@1: Chris@1: if (!response.configurationResponse.outputs.empty()) { Chris@1: m_mapper.markConfigured Chris@1: (h, creq.configuration.channelCount, creq.configuration.blockSize); Chris@1: response.success = true; Chris@1: } Chris@1: break; Chris@0: } Chris@0: Chris@1: case RRType::Process: Chris@1: { Chris@1: auto &preq = request.processRequest; Chris@1: auto h = m_mapper.pluginToHandle(preq.plugin); Chris@1: if (!m_mapper.isConfigured(h)) { Chris@1: throw runtime_error("plugin has not been configured"); Chris@1: } Chris@1: Chris@1: int channels = int(preq.inputBuffers.size()); Chris@1: if (channels != m_mapper.getChannelCount(h)) { Chris@1: throw runtime_error("wrong number of channels supplied to process"); Chris@1: } Chris@1: Chris@1: const float **fbuffers = new const float *[channels]; Chris@1: for (int i = 0; i < channels; ++i) { Chris@1: if (int(preq.inputBuffers[i].size()) != m_mapper.getBlockSize(h)) { Chris@1: delete[] fbuffers; Chris@1: throw runtime_error("wrong block size supplied to process"); Chris@1: } Chris@1: fbuffers[i] = preq.inputBuffers[i].data(); Chris@1: } Chris@1: Chris@1: response.processResponse.features = Chris@1: preq.plugin->process(fbuffers, preq.timestamp); Chris@0: response.success = true; Chris@0: Chris@1: delete[] fbuffers; Chris@1: break; Chris@0: } Chris@0: Chris@1: case RRType::Finish: Chris@1: { Chris@1: auto h = m_mapper.pluginToHandle(request.finishPlugin); Chris@1: Chris@1: response.finishResponse.features = Chris@1: request.finishPlugin->getRemainingFeatures(); Chris@1: Chris@1: m_mapper.removePlugin(h); Chris@1: delete request.finishPlugin; Chris@1: response.success = true; Chris@1: break; Chris@0: } Chris@0: Chris@1: case RRType::NotValid: Chris@1: break; Chris@1: } Chris@1: Chris@1: return writeResponse(response); Chris@0: Chris@1: } catch (const std::exception &e) { Chris@1: return VampJson::fromException(e, request.type).dump(); Chris@0: } Chris@0: } Chris@0: Chris@0: } Chris@0: