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