comparison 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
comparison
equal deleted inserted replaced
116:286c8e57abd0 117:2e8aacb7f883
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Piper Vamp JSON Adapter
5
6 Centre for Digital Music, Queen Mary, University of London.
7 Copyright 2015-2016 QMUL.
8
9 Permission is hereby granted, free of charge, to any person
10 obtaining a copy of this software and associated documentation
11 files (the "Software"), to deal in the Software without
12 restriction, including without limitation the rights to use, copy,
13 modify, merge, publish, distribute, sublicense, and/or sell copies
14 of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions:
16
17 The above copyright notice and this permission notice shall be
18 included in all copies or substantial portions of the Software.
19
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
24 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 Except as contained in this notice, the names of the Centre for
29 Digital Music; Queen Mary, University of London; and Chris Cannam
30 shall not be used in advertising or otherwise to promote the sale,
31 use or other dealings in this Software without prior written
32 authorization.
33 */
34
35 #include "PiperPluginLibrary.h"
36 #include "PiperAdapter.h"
37
38 #include "vamp-json/VampJson.h"
39
40 using namespace std;
41 using namespace json11;
42 using namespace piper_vamp;
43
44 namespace piper_vamp_js { //!!! not good
45
46 //!!! too many explicit namespaces here
47
48 //!!! dup with piper-convert
49 Json
50 convertRequestJson(string input, string &err)
51 {
52 Json j = Json::parse(input, err);
53 if (err != "") {
54 err = "invalid json: " + err;
55 return {};
56 }
57 if (!j.is_object()) {
58 err = "object expected at top level";
59 }
60 return j;
61 }
62
63 PiperPluginLibrary::PiperPluginLibrary(vector<PiperAdapterInterface *> pp) :
64 m_useBase64(false)
65 {
66 for (PiperAdapterInterface *p: pp) {
67 string key = p->getStaticData().pluginKey;
68 m_adapters[key] = p;
69 }
70 }
71
72 ListResponse
73 PiperPluginLibrary::listPluginData(ListRequest req) const
74 {
75 bool filtered = !req.from.empty();
76 ListResponse resp;
77 for (auto a: m_adapters) {
78 if (filtered) {
79 auto n = a.second->getLibraryName();
80 bool found = false;
81 for (const auto &f: req.from) {
82 if (f == n) {
83 found = true;
84 break;
85 }
86 }
87 if (!found) {
88 continue;
89 }
90 }
91 resp.available.push_back(a.second->getStaticData());
92 }
93 return resp;
94 }
95
96 LoadResponse
97 PiperPluginLibrary::loadPlugin(LoadRequest req, string &err) const
98 {
99 string key = req.pluginKey;
100 if (m_adapters.find(key) != m_adapters.end()) {
101 auto resp = m_adapters.at(key)->loadPlugin(req);
102 if (!resp.plugin) {
103 // This should not actually happen -- the load call here
104 // is just an object construction, not a dynamic load. But
105 // report it if it does...
106 err = "failed to construct plugin with key " + key;
107 }
108 return resp;
109 } else {
110 err = "no adapter for plugin key " + key;
111 return {};
112 }
113 }
114
115 ConfigurationResponse
116 PiperPluginLibrary::configurePlugin(ConfigurationRequest req,
117 string &err) const
118 {
119 for (PluginConfiguration::ParameterMap::const_iterator i =
120 req.configuration.parameterValues.begin();
121 i != req.configuration.parameterValues.end(); ++i) {
122 req.plugin->setParameter(i->first, i->second);
123 }
124
125 if (req.configuration.currentProgram != "") {
126 req.plugin->selectProgram(req.configuration.currentProgram);
127 }
128
129 ConfigurationResponse response;
130
131 response.plugin = req.plugin;
132
133 if (req.plugin->initialise(req.configuration.channelCount,
134 req.configuration.stepSize,
135 req.configuration.blockSize)) {
136 response.outputs = req.plugin->getOutputDescriptors();
137 } else {
138 err = "configuration failed (wrong channel count, step size, block size?)";
139 }
140
141 return response;
142 }
143
144 string
145 PiperPluginLibrary::processRawImpl(int handle,
146 const float *const *inputBuffers,
147 int sec,
148 int nsec)
149 {
150 Vamp::Plugin *plugin = m_mapper.handleToPlugin(handle);
151 if (!plugin) {
152 return VampJson::fromError("unknown plugin handle",
153 RRType::Process, Json())
154 .dump();
155 }
156
157 if (!m_mapper.isConfigured(handle)) {
158 return VampJson::fromError("plugin has not been configured",
159 RRType::Process, Json())
160 .dump();
161 }
162
163 Vamp::RealTime timestamp(sec, nsec);
164
165 ProcessResponse resp;
166 resp.plugin = plugin;
167 resp.features = plugin->process(inputBuffers, timestamp);
168
169 m_useBase64 = true;
170
171 return VampJson::fromRpcResponse_Process
172 (resp, m_mapper,
173 VampJson::BufferSerialisation::Base64,
174 Json())
175 .dump();
176 }
177
178 string
179 PiperPluginLibrary::requestJsonImpl(string req)
180 {
181 string err;
182
183 Json j = convertRequestJson(req, err);
184
185 // we don't care what this is, only that it is retained in the response:
186 auto id = j["id"];
187
188 Json rj;
189 if (err != "") {
190 return VampJson::fromError(err, RRType::NotValid, id).dump();
191 }
192
193 RRType type = VampJson::getRequestResponseType(j, err);
194 if (err != "") {
195 return VampJson::fromError(err, RRType::NotValid, id).dump();
196 }
197
198 VampJson::BufferSerialisation serialisation =
199 (m_useBase64 ?
200 VampJson::BufferSerialisation::Base64 :
201 VampJson::BufferSerialisation::Array);
202
203 switch (type) {
204
205 case RRType::List:
206 {
207 auto req = VampJson::toRpcRequest_List(j, err);
208 if (err != "") {
209 rj = VampJson::fromError(err, type, id);
210 } else {
211 rj = VampJson::fromRpcResponse_List(listPluginData(req), id);
212 }
213 break;
214 }
215
216 case RRType::Load:
217 {
218 auto req = VampJson::toRpcRequest_Load(j, err);
219 if (err != "") {
220 rj = VampJson::fromError(err, type, id);
221 } else {
222 auto resp = loadPlugin(req, err);
223 if (err != "") {
224 rj = VampJson::fromError(err, type, id);
225 } else {
226 m_mapper.addPlugin(resp.plugin);
227 rj = VampJson::fromRpcResponse_Load(resp, m_mapper, id);
228 }
229 }
230 break;
231 }
232
233 case RRType::Configure:
234 {
235 auto req = VampJson::toRpcRequest_Configure(j, m_mapper, err);
236 if (err != "") {
237 rj = VampJson::fromError(err, type, id);
238 } else {
239 auto h = m_mapper.pluginToHandle(req.plugin);
240 if (h == m_mapper.INVALID_HANDLE) {
241 rj = VampJson::fromError("unknown or invalid plugin handle", type, id);
242 } else if (m_mapper.isConfigured(h)) {
243 rj = VampJson::fromError("plugin has already been configured", type, id);
244 } else {
245 auto resp = configurePlugin(req, err);
246 if (err != "") {
247 rj = VampJson::fromError(err, type, id);
248 } else {
249 m_mapper.markConfigured(h,
250 req.configuration.channelCount,
251 req.configuration.blockSize);
252 rj = VampJson::fromRpcResponse_Configure(resp, m_mapper, id);
253 }
254 }
255 }
256 break;
257 }
258
259 case RRType::Process:
260 {
261 VampJson::BufferSerialisation serialisation;
262
263 auto req = VampJson::toRpcRequest_Process(j, m_mapper,
264 serialisation, err);
265 if (err != "") {
266 rj = VampJson::fromError(err, type, id);
267 } else {
268 auto h = m_mapper.pluginToHandle(req.plugin);
269 int channels = int(req.inputBuffers.size());
270 if (h == m_mapper.INVALID_HANDLE) {
271 rj = VampJson::fromError("unknown or invalid plugin handle", type, id);
272 } else if (!m_mapper.isConfigured(h)) {
273 rj = VampJson::fromError("plugin has not been configured", type, id);
274 } else if (channels != m_mapper.getChannelCount(h)) {
275 rj = VampJson::fromError("wrong number of channels supplied", type, id);
276 } else {
277
278 if (serialisation == VampJson::BufferSerialisation::Base64) {
279 m_useBase64 = true;
280 }
281
282 size_t blockSize = m_mapper.getBlockSize(h);
283
284 const float **fbuffers = new const float *[channels];
285 for (int i = 0; i < channels; ++i) {
286 if (req.inputBuffers[i].size() != blockSize) {
287 delete[] fbuffers;
288 fbuffers = 0;
289 rj = VampJson::fromError("wrong block size supplied", type, id);
290 break;
291 }
292 fbuffers[i] = req.inputBuffers[i].data();
293 }
294
295 if (fbuffers) {
296 ProcessResponse resp;
297 resp.plugin = req.plugin;
298 resp.features = req.plugin->process(fbuffers, req.timestamp);
299 delete[] fbuffers;
300 rj = VampJson::fromRpcResponse_Process
301 (resp, m_mapper, serialisation, id);
302 }
303 }
304 }
305 break;
306 }
307
308 case RRType::Finish:
309 {
310 auto req = VampJson::toRpcRequest_Finish(j, m_mapper, err);
311 if (err != "") {
312 rj = VampJson::fromError(err, type, id);
313 } else {
314 auto h = m_mapper.pluginToHandle(req.plugin);
315 if (h == m_mapper.INVALID_HANDLE) {
316 rj = VampJson::fromError("unknown or invalid plugin handle", type, id);
317 } else {
318
319 FinishResponse resp;
320 resp.plugin = req.plugin;
321
322 // Finish can be called (to unload the plugin) even if
323 // the plugin has never been configured or used. But
324 // we want to make sure we call getRemainingFeatures
325 // only if we have actually configured the plugin.
326 if (m_mapper.isConfigured(h)) {
327 resp.features = req.plugin->getRemainingFeatures();
328 }
329
330 rj = VampJson::fromRpcResponse_Finish
331 (resp, m_mapper, serialisation, id);
332
333 m_mapper.removePlugin(h);
334 delete req.plugin;
335 }
336 }
337 break;
338 }
339
340 case RRType::NotValid:
341 rj = VampJson::fromError("invalid request", type, id);
342 break;
343 }
344
345 return rj.dump();
346 }
347
348 }
349