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