1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /***************************************************************************
3 * lv2_test_host.cc
4 *
5 * Wed Feb 11 23:11:21 CET 2015
6 * Copyright 2015 Bent Bisballe Nyeng
7 * deva@aasimon.org
8 ****************************************************************************/
9
10 /*
11 * This file is part of DrumGizmo.
12 *
13 * DrumGizmo is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * DrumGizmo is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public License
24 * along with DrumGizmo; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 */
27 #include "lv2_test_host.h"
28
29 #include <serd/serd.h>
30
31 #include <string.h>
32 #include <stdlib.h>
33
34 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
35 #include <lv2/lv2plug.in/ns/ext/state/state.h>
36 #include <lv2/lv2plug.in/ns/ext/midi/midi.h>
37
38 ///////////////////////////////
39 // Base64 encoder:
40 //
41 #include <openssl/bio.h>
42 #include <openssl/err.h>
43 #include <openssl/evp.h>
44 #include <string>
45
46 class Base64 {
47 public:
Base64()48 Base64()
49 {
50 mbio = (void*)BIO_new(BIO_s_mem());
51 BIO *b64bio = BIO_new(BIO_f_base64());
52 bio = (void*)BIO_push(b64bio, (BIO*)mbio);
53 }
54
~Base64()55 ~Base64()
56 {
57 BIO_free_all((BIO*)bio);
58 }
59
write(std::string in)60 std::string write(std::string in)
61 {
62 return this->write(in.data(), in.length());
63 }
64
write(const char * in,size_t size)65 std::string write(const char *in, size_t size)
66 {
67 std::string out;
68
69 BIO_write((BIO*)bio, in, size);
70
71 size_t osize = BIO_ctrl_pending((BIO*)mbio);
72 char *outbuf = (char*)malloc(osize);
73
74 int len = BIO_read((BIO*)mbio, outbuf, osize);
75 if(len < 1)
76 {
77 return "";
78 }
79
80 out.append(outbuf, len);
81
82 free(outbuf);
83
84 return out;
85 }
86
flush()87 std::string flush()
88 {
89 std::string out;
90
91 (void)BIO_flush((BIO*)bio);
92
93 size_t size = BIO_ctrl_pending((BIO*)mbio);
94 char *outbuf = (char*)malloc(size);
95
96 int len = BIO_read((BIO*)mbio, outbuf, size);
97 if(len < 1)
98 {
99 return "";
100 }
101
102 out.append(outbuf, len);
103
104 free(outbuf);
105
106 return out;
107 }
108
109 private:
110 void *bio;
111 void *mbio;
112 };
113 //
114 // Base64 encoder
115 ///////////////////////////////
116
117
118 // TODO: Use map<int, std::string> instead
119 static char** uris = nullptr;
120 static size_t n_uris = 0;
121
map_uri(LV2_URID_Map_Handle handle,const char * uri)122 static LV2_URID map_uri(LV2_URID_Map_Handle handle, const char* uri)
123 {
124 for(size_t i = 0; i < n_uris; ++i)
125 {
126 if(!strcmp(uris[i], uri))
127 {
128 return i + 1;
129 }
130 }
131
132 uris = (char**)realloc(uris, ++n_uris * sizeof(char*));
133 uris[n_uris - 1] = strdup(uri);
134
135 return n_uris;
136 }
137
unmap_uri(LV2_URID_Map_Handle handle,LV2_URID urid)138 static const char* unmap_uri(LV2_URID_Map_Handle handle, LV2_URID urid)
139 {
140 if((urid > 0) && (urid <= n_uris))
141 {
142 return uris[urid - 1];
143 }
144 return nullptr;
145 }
146
147 LV2_URID_Map map = { nullptr, map_uri };
148 LV2_Feature map_feature = { LV2_URID_MAP_URI, &map };
149 LV2_URID_Unmap unmap = { nullptr, unmap_uri };
150 LV2_Feature unmap_feature = { LV2_URID_UNMAP_URI, &unmap };
151 const LV2_Feature* features[] = { &map_feature, &unmap_feature, nullptr };
152
Sequence(void * buffer,size_t buffer_size)153 LV2TestHost::Sequence::Sequence(void *buffer, size_t buffer_size)
154 {
155 this->buffer = buffer;
156 this->buffer_size = buffer_size;
157
158 seq = (LV2_Atom_Sequence *)buffer;
159
160 seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
161 seq->atom.type = map.map(map.handle, LV2_ATOM__Sequence);
162 seq->body.unit = 0;
163 seq->body.pad = 0;
164 }
165
166 // Keep this to support atom extension from lv2 < 1.10
_lv2_atom_sequence_clear(LV2_Atom_Sequence * seq)167 static inline void _lv2_atom_sequence_clear(LV2_Atom_Sequence* seq)
168 {
169 seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
170 }
171
clear()172 void LV2TestHost::Sequence::clear()
173 {
174 _lv2_atom_sequence_clear(seq);
175 }
176
177 // Keep this to support atom extension from lv2 < 1.10
178 static inline LV2_Atom_Event*
_lv2_atom_sequence_append_event(LV2_Atom_Sequence * seq,uint32_t capacity,const LV2_Atom_Event * event)179 _lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq,
180 uint32_t capacity,
181 const LV2_Atom_Event* event)
182 {
183 const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size;
184
185 if(capacity - seq->atom.size < total_size)
186 {
187 return nullptr;
188 }
189
190 LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size);
191 memcpy(e, event, total_size);
192
193 seq->atom.size += lv2_atom_pad_size(total_size);
194
195 return e;
196 }
197
addMidiNote(uint64_t pos,uint8_t key,int8_t velocity)198 void LV2TestHost::Sequence::addMidiNote(uint64_t pos,
199 uint8_t key, int8_t velocity)
200 {
201 typedef struct {
202 LV2_Atom_Event event;
203 uint8_t msg[3];
204 } MIDINoteEvent;
205
206 uint8_t note_on = 0x90;
207
208 MIDINoteEvent ev;
209 ev.event.time.frames = pos;// sample position
210 ev.event.body.type = map.map(map.handle, LV2_MIDI__MidiEvent);
211 ev.event.body.size = sizeof(ev.msg);
212
213 ev.msg[0] = note_on;
214 ev.msg[1] = key;
215 ev.msg[2] = velocity;
216
217 LV2_Atom_Event *e =
218 _lv2_atom_sequence_append_event(seq, this->buffer_size, &ev.event);
219 (void)e;
220 }
221
data()222 void *LV2TestHost::Sequence::data()
223 {
224 return buffer;
225 }
226
LV2TestHost(const char * lv2_path)227 LV2TestHost::LV2TestHost(const char *lv2_path)
228 {
229 if(lv2_path)
230 {
231 setenv("LV2_PATH", lv2_path, 1);
232 }
233
234 world = lilv_world_new();
235 if(world == nullptr)
236 {
237 return;
238 }
239
240 lilv_world_load_all(world);
241 }
242
~LV2TestHost()243 LV2TestHost::~LV2TestHost()
244 {
245 if(world)
246 {
247 lilv_world_free(world);
248 }
249 }
250
open(const char * plugin_uri)251 int LV2TestHost::open(const char *plugin_uri)
252 {
253 if(world == nullptr)
254 {
255 return 1;
256 }
257
258 plugins = lilv_world_get_all_plugins(world);
259 if(plugins == nullptr)
260 {
261 return 2;
262 }
263
264 uri = lilv_new_uri(world, plugin_uri);
265 if(uri == nullptr)
266 {
267 return 3;
268 }
269
270 plugin = lilv_plugins_get_by_uri(plugins, uri);
271 if(plugin == nullptr)
272 {
273 return 4;
274 }
275
276 return 0;
277 }
278
verify()279 int LV2TestHost::verify()
280 {
281 bool verify = lilv_plugin_verify(plugin);
282 if(!verify)
283 {
284 return 1;
285 }
286
287 return 0;
288 }
289
close()290 int LV2TestHost::close()
291 {
292 // plugin is a const pointer; nothing to close here.
293 return 0;
294 }
295
296 /* // Get metadata
297
298 static void dumpNodes(LilvNodes *nodes)
299 {
300 LilvIter* iter = lilv_nodes_begin(nodes);
301 while(iter) {
302 const LilvNode* node = lilv_nodes_get(nodes, iter);
303 printf(" - '%s'\n", lilv_node_as_uri(node));
304 iter = lilv_nodes_next(nodes, iter);
305 }
306 }
307
308 void getMetadata()
309 {
310 LilvNode* name = lilv_plugin_get_name(plugin);
311 if(name) printf("Name: %s\n", lilv_node_as_uri(name));
312
313 // ---> line 731 in lilv.h
314 bool has_latency = lilv_plugin_has_latency(plugin);
315 printf("Has latency: %d\n", has_latency);
316
317 if(has_latency) {
318 uint32_t latency_port_index = lilv_plugin_get_latency_port_index(plugin);
319 const LilvPort* port =
320 lilv_plugin_get_port_by_index(plugin, latency_port_index);
321 // Do something to actually get latency from port....
322 }
323
324 LilvNode* author = lilv_plugin_get_author_name(plugin);
325 if(author) printf("Author: %s\n", lilv_node_as_uri(author));
326
327 LilvNode* email = lilv_plugin_get_author_email(plugin);
328 if(email) printf("Email: %s\n", lilv_node_as_uri(email));
329
330 LilvNode* homepage = lilv_plugin_get_author_homepage(plugin);
331 if(homepage) printf("Homepage: %s\n", lilv_node_as_uri(homepage));
332
333 LilvNodes* supported = lilv_plugin_get_supported_features(plugin);
334 LilvNodes* required = lilv_plugin_get_required_features(plugin);
335 LilvNodes* optional = lilv_plugin_get_optional_features(plugin);
336
337 printf("Supported:\n");
338 dumpNodes(supported);
339
340 printf("Required:\n");
341 dumpNodes(required);
342
343 printf("Optional:\n");
344 dumpNodes(optional);
345
346 lilv_nodes_free(supported);
347 lilv_nodes_free(required);
348 lilv_nodes_free(optional);
349 }
350 */
351 /*
352 int LV2TestHost::getPorts()
353 {
354 // Iterate ports:
355 const LilvPort* port;
356 uint32_t portidx = 0;
357 while( (port = lilv_plugin_get_port_by_index(plugin, portidx)) != 0) {
358 printf("Port: %d\n", portidx);
359
360 LilvNode* port_name = lilv_port_get_name(plugin, port);
361 if(port_name) printf(" Name: %s\n", lilv_node_as_uri(port_name));
362
363 portidx++;
364 }
365 }
366 */
createInstance(size_t samplerate)367 int LV2TestHost::createInstance(size_t samplerate)
368 {
369 instance = lilv_plugin_instantiate(plugin, samplerate, features);
370 if(instance == nullptr)
371 {
372 return 1;
373 }
374
375 return 0;
376 }
377
destroyInstance()378 int LV2TestHost::destroyInstance()
379 {
380 if(instance)
381 {
382 lilv_instance_free(instance);
383 }
384
385 return 0;
386 }
387
activate()388 int LV2TestHost::activate()
389 {
390 lilv_instance_activate(instance);
391 return 0;
392 }
393
deactivate()394 int LV2TestHost::deactivate()
395 {
396 lilv_instance_deactivate(instance);
397 return 0;
398 }
399
loadConfig(const char * config,size_t size)400 int LV2TestHost::loadConfig(const char *config, size_t size)
401 {
402 Base64 b64;
403 std::string b64_config = b64.write(config, size);
404 b64_config += b64.flush();
405
406 //printf("Base 64 config: [%s]\n", b64_config.c_str());
407
408 const char ttl_config_fmt[] =
409 "<http://drumgizmo.org/lv2/atom#config>\n"
410 " a pset:Preset ;\n"
411 " lv2:appliesTo <http://drumgizmo.org/lv2> ;\n"
412 " state:state [\n"
413 " <http://drumgizmo.org/lv2/atom#config> \"\"\"%s\"\"\"^^xsd:base64Binary\n"
414 " ] .\n";
415
416 char ttl_config[sizeof(ttl_config_fmt) * 2 + b64_config.size()];
417 sprintf(ttl_config, ttl_config_fmt, b64_config.c_str());
418
419 //printf("ttl config: [%s]\n", ttl_config);
420
421 {
422 LilvState* restore_state =
423 lilv_state_new_from_string(world, &map, ttl_config);
424
425 lilv_state_restore(restore_state, instance, nullptr, nullptr,
426 LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE,
427 features);
428 }
429
430 return 0;
431 }
432
connectPort(int port,void * portdata)433 int LV2TestHost::connectPort(int port, void *portdata)
434 {
435 // if(lilv_port_is_a(p, port, lv2_ControlPort)) ...
436
437 lilv_instance_connect_port(instance, port, portdata);
438
439 return 0;
440 }
441
run(int num_samples)442 int LV2TestHost::run(int num_samples)
443 {
444 lilv_instance_run(instance, num_samples);
445 return 0;
446 }
447