Mercurial > hg > piper-vamp-js
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 |