1 /*
2  * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
3  *
4  * This is free software: you can redistribute it and/or modify
5  * it under the terms of the Artistic License 2.0 as published by
6  * The Perl Foundation.
7  *
8  * This source is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * Artistic License 2.0 for more details.
12  *
13  * You should have received a copy of the Artistic License 2.0
14  * along the source as a COPYING file. If not, obtain it from
15  * http://www.perlfoundation.org/artistic_license_2_0.
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <assert.h>
21 
22 #include <synthpod_lv2.h>
23 #include <synthpod_app.h>
24 
25 #include <lv2/lv2plug.in/ns/ext/atom/atom.h>
26 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
27 #include <lv2/lv2plug.in/ns/ext/midi/midi.h>
28 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
29 #include <lv2/lv2plug.in/ns/ext/worker/worker.h>
30 #include <lv2/lv2plug.in/ns/ext/state/state.h>
31 #include <lv2/lv2plug.in/ns/ext/log/log.h>
32 #include <lv2/lv2plug.in/ns/ext/buf-size/buf-size.h>
33 #include <lv2/lv2plug.in/ns/ext/options/options.h>
34 
35 #include <osc.lv2/osc.h>
36 #include <varchunk.h>
37 
38 #define CHUNK_SIZE 0x100000
39 #define MAX_MSGS 10 //FIXME limit to how many events?
40 
41 typedef enum _job_type_t job_type_t;
42 typedef struct _job_t job_t;
43 typedef struct _plughandle_t plughandle_t;
44 
45 enum _job_type_t {
46 	JOB_TYPE_NONE = 0,
47 	JOB_TYPE_STASH
48 };
49 
50 struct _job_t {
51 	job_type_t type;
52 	union {
53 		struct {
54 			char *bundle_path;
55 			LV2_Atom_Object *obj;
56 		} stash;
57 	};
58 };
59 
60 struct _plughandle_t {
61 	sp_app_t *app;
62 	sp_app_driver_t driver;
63 
64 	LV2_Worker_Schedule *schedule;
65 	LV2_Options_Option *opts;
66 
67 	struct {
68 		struct {
69 			LV2_URID max_block_length;
70 			LV2_URID min_block_length;
71 			LV2_URID sequence_size;
72 		} bufsz;
73 		struct {
74 			LV2_URID event;
75 		} synthpod;
76 	} uri;
77 
78 	struct {
79 		LV2_Atom_Forge event_out;
80 #if 0
81 		LV2_Atom_Forge com_in;
82 #endif
83 		LV2_Atom_Forge notify;
84 	} forge;
85 
86 	struct {
87 		LV2_Atom_Forge_Ref event_out;
88 #if 0
89 		LV2_Atom_Forge_Ref com_in;
90 #endif
91 		LV2_Atom_Forge_Ref notify;
92 	} ref;
93 
94 	struct {
95 		const LV2_Atom_Sequence *event_in;
96 		LV2_Atom_Sequence *event_out;
97 
98 		const float *audio_in[2];
99 		float *audio_out[2];
100 
101 		const float *input[4];
102 		float *output[4];
103 
104 		const LV2_Atom_Sequence *control;
105 		LV2_Atom_Sequence *notify;
106 	} port;
107 
108 	struct {
109 		LV2_Atom_Sequence *event_in;
110 		float *audio_in[2];
111 		float *input[4];
112 #if 0
113 		LV2_Atom_Sequence *com_in;
114 #endif
115 	} source;
116 
117 	struct {
118 		const LV2_Atom_Sequence *event_out;
119 		const float *audio_out[2];
120 		const float *output[4];
121 		const LV2_Atom_Sequence *com_out;
122 	} sink;
123 
124 	uint8_t buf [CHUNK_SIZE] _ATOM_ALIGNED;
125 
126 	bool advance_worker;
127 	bool advance_ui;
128 	bool trigger_worker;
129 	varchunk_t *app_to_worker;
130 	varchunk_t *app_from_worker;
131 	varchunk_t *app_from_ui;
132 	varchunk_t *app_from_app;
133 
134 	xpress_t xpress;
135 	xpress_map_t xmap;
136 };
137 
138 static void
_apply_job(plughandle_t * handle,const job_t * job)139 _apply_job(plughandle_t *handle, const job_t *job)
140 {
141 	if(  job->stash.obj
142 		&& job->stash.bundle_path
143 		&& strlen(job->stash.bundle_path) )
144 	{
145 		sp_app_apply(handle->app, job->stash.obj, job->stash.bundle_path);
146 	}
147 
148 	free(job->stash.bundle_path);
149 	free(job->stash.obj);
150 }
151 
152 static LV2_State_Status
_state_save(LV2_Handle instance,LV2_State_Store_Function store,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)153 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
154 	LV2_State_Handle state, uint32_t flags,
155 	const LV2_Feature *const *features)
156 {
157 	plughandle_t *handle = instance;
158 	sp_app_t *app = handle->app;
159 
160 	return sp_app_save(app, store, state, flags, features);
161 }
162 
163 static LV2_State_Status
_state_restore(LV2_Handle instance,LV2_State_Retrieve_Function retrieve,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)164 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
165 	LV2_State_Handle state, uint32_t flags,
166 	const LV2_Feature *const *features)
167 {
168 	plughandle_t *handle = instance;
169 	sp_app_t *app = handle->app;
170 
171 	LV2_Worker_Schedule *schedule = NULL;
172 	LV2_State_Map_Path *map_path = NULL;
173 
174 	for(int i=0; features[i]; i++)
175 	{
176 		if(!strcmp(features[i]->URI, LV2_WORKER__schedule))
177 			schedule = features[i]->data;
178 		else if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
179 			map_path = features[i]->data;
180 	}
181 
182 	if(!map_path)
183 	{
184 		return LV2_STATE_ERR_UNKNOWN;
185 	}
186 
187 	char *bundle_path = map_path->absolute_path(map_path->handle, "");
188 	if(!bundle_path)
189 	{
190 		return LV2_STATE_ERR_UNKNOWN;
191 	}
192 
193 	LV2_Atom_Object *obj = sp_app_stash(app, retrieve, state, flags, features);
194 	if(!obj)
195 	{
196 		free(bundle_path);
197 		return LV2_STATE_ERR_UNKNOWN;
198 	}
199 
200 	const job_t job = {
201 		.type = JOB_TYPE_STASH,
202 		.stash = {
203 			.bundle_path = bundle_path,
204 			.obj = obj
205 		}
206 	};
207 
208 	if(schedule)
209 	{
210 		const LV2_Worker_Status stat = schedule->schedule_work(schedule->handle,
211 			sizeof(job_t), &job);
212 
213 		if(stat == LV2_WORKER_SUCCESS)
214 		{
215 			return LV2_STATE_SUCCESS;
216 		}
217 	}
218 	else
219 	{
220 		_apply_job(handle, &job);
221 		return LV2_STATE_SUCCESS;
222 	}
223 
224 
225 	return LV2_STATE_ERR_UNKNOWN;
226 }
227 
228 static const LV2_State_Interface state_iface = {
229 	.save = _state_save,
230 	.restore = _state_restore
231 };
232 
233 // non-rt worker-thread
234 static LV2_Worker_Status
_work(LV2_Handle instance,LV2_Worker_Respond_Function respond,LV2_Worker_Respond_Handle target,uint32_t _size,const void * _body)235 _work(LV2_Handle instance,
236 	LV2_Worker_Respond_Function respond,
237 	LV2_Worker_Respond_Handle target,
238 	uint32_t _size,
239 	const void *_body)
240 {
241 	plughandle_t *handle = instance;
242 
243 	const job_t *job = _body;
244 
245 	switch(job->type)
246 	{
247 		case JOB_TYPE_STASH:
248 		{
249 			_apply_job(handle, job);
250 		} break;
251 		case JOB_TYPE_NONE:
252 		default:
253 		{
254 			size_t size;
255 			const void *body;
256 			while((body = varchunk_read_request(handle->app_to_worker, &size)))
257 			{
258 				sp_worker_from_app(handle->app, size, body);
259 				varchunk_read_advance(handle->app_to_worker);
260 			}
261 		} break;
262 	}
263 
264 
265 	return LV2_WORKER_SUCCESS;
266 }
267 
268 __realtime static LV2_Worker_Status
_work_response(LV2_Handle instance,uint32_t size,const void * body)269 _work_response(LV2_Handle instance, uint32_t size, const void *body)
270 {
271 	//plughandle_t *handle = instance;
272 
273 	return LV2_WORKER_SUCCESS;
274 }
275 
276 __realtime static LV2_Worker_Status
_end_run(LV2_Handle instance)277 _end_run(LV2_Handle instance)
278 {
279 	//plughandle_t *handle = instance;
280 
281 	return LV2_WORKER_SUCCESS;
282 }
283 
284 static const LV2_Worker_Interface work_iface = {
285 	.work = _work,
286 	.work_response = _work_response,
287 	.end_run = _end_run
288 };
289 
290 __realtime static void *
_to_ui_request(size_t minimum,size_t * maximum,void * data)291 _to_ui_request(size_t minimum, size_t *maximum, void *data)
292 {
293 	plughandle_t *handle = data;
294 
295 	if(minimum <= CHUNK_SIZE)
296 	{
297 		*maximum = CHUNK_SIZE;
298 		return handle->buf;
299 	}
300 
301 	return NULL;
302 }
303 __realtime static void
_to_ui_advance(size_t written,void * data)304 _to_ui_advance(size_t written, void *data)
305 {
306 	plughandle_t *handle = data;
307 	LV2_Atom_Forge *forge = &handle->forge.notify;
308 	LV2_Atom_Forge_Ref *ref = &handle->ref.notify;
309 
310 	//printf("_to_ui_advance: %zu\n", written);
311 
312 	if(forge->offset + written > forge->size)
313 		return; // buffer overflow
314 
315 	if(*ref)
316 		*ref = lv2_atom_forge_frame_time(forge, 0);
317 	if(*ref)
318 		*ref = lv2_atom_forge_write(forge, handle->buf, written);
319 }
320 
321 __realtime static void *
_to_worker_request(size_t minimum,size_t * maximum,void * data)322 _to_worker_request(size_t minimum, size_t *maximum, void *data)
323 {
324 	plughandle_t *handle = data;
325 
326 	return varchunk_write_request_max(handle->app_to_worker, minimum, maximum);
327 }
328 __realtime static void
_to_worker_advance(size_t written,void * data)329 _to_worker_advance(size_t written, void *data)
330 {
331 	plughandle_t *handle = data;
332 
333 	varchunk_write_advance(handle->app_to_worker, written);
334 	handle->trigger_worker = true;
335 }
336 
337 __non_realtime static void *
_to_app_request(size_t minimum,size_t * maximum,void * data)338 _to_app_request(size_t minimum, size_t *maximum, void *data)
339 {
340 	plughandle_t *handle = data;
341 
342 	void *ptr;
343 	do
344 	{
345 		ptr = varchunk_write_request_max(handle->app_from_worker, minimum, maximum);
346 	}
347 	while(!ptr); // wait until there is enough space
348 
349 	return ptr;
350 }
351 __non_realtime static void
_to_app_advance(size_t written,void * data)352 _to_app_advance(size_t written, void *data)
353 {
354 	plughandle_t *handle = data;
355 
356 	varchunk_write_advance(handle->app_from_worker, written);
357 }
358 
359 __realtime static uint32_t
_voice_map_new_uuid(void * data,uint32_t flags)360 _voice_map_new_uuid(void *data, uint32_t flags __attribute__((unused)))
361 {
362 	xpress_t *xpress = data;
363 
364 	return xpress_map(xpress);
365 }
366 
367 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)368 instantiate(const LV2_Descriptor* descriptor, double rate,
369 	const char *bundle_path, const LV2_Feature *const *features)
370 {
371 	plughandle_t *handle = calloc(1, sizeof(plughandle_t));
372 	if(!handle)
373 		return NULL;
374 	mlock(handle, sizeof(plughandle_t));
375 
376 	handle->driver.sample_rate = rate;
377 	handle->driver.seq_size = SEQ_SIZE;
378 	handle->driver.system_port_add = NULL;
379 	handle->driver.system_port_del = NULL;
380 	handle->driver.osc_sched = NULL;
381 	handle->driver.features = 0;
382 	handle->driver.num_slaves = 0;
383 	handle->driver.bad_plugins = false; //FIXME
384 
385 	const LilvWorld *world = NULL;
386 	xpress_map_t *voice_map = NULL;
387 
388 	for(int i=0; features[i]; i++)
389 	{
390 		if(!strcmp(features[i]->URI, LV2_URID__map))
391 			handle->driver.map = (LV2_URID_Map *)features[i]->data;
392 		else if(!strcmp(features[i]->URI, LV2_URID__unmap))
393 			handle->driver.unmap = (LV2_URID_Unmap *)features[i]->data;
394 		else if(!strcmp(features[i]->URI, XPRESS__voiceMap))
395 			voice_map = features[i]->data;
396 		else if(!strcmp(features[i]->URI, LV2_LOG__log))
397 			handle->driver.log = (LV2_Log_Log *)features[i]->data;
398 		else if(!strcmp(features[i]->URI, LV2_WORKER__schedule))
399 			handle->schedule = (LV2_Worker_Schedule *)features[i]->data;
400 		else if(!strcmp(features[i]->URI, LV2_OPTIONS__options))
401 			handle->opts = (LV2_Options_Option *)features[i]->data;
402 		else if(!strcmp(features[i]->URI, SYNTHPOD_PREFIX"world"))
403 			world = (const LilvWorld *)features[i]->data;
404 		else if(!strcmp(features[i]->URI, LV2_OSC__schedule))
405 			handle->driver.osc_sched = features[i]->data;
406 		else if(!strcmp(features[i]->URI, LV2_BUF_SIZE__fixedBlockLength))
407 			handle->driver.features |= SP_APP_FEATURE_FIXED_BLOCK_LENGTH;
408 		else if(!strcmp(features[i]->URI, LV2_BUF_SIZE__powerOf2BlockLength))
409 			handle->driver.features |= SP_APP_FEATURE_POWER_OF_2_BLOCK_LENGTH;
410 	}
411 
412 	if(!handle->driver.map)
413 	{
414 		fprintf(stderr,
415 			"%s: Host does not support urid:map\n", descriptor->URI);
416 		free(handle);
417 		return NULL;
418 	}
419 
420 	if(!handle->schedule)
421 	{
422 		fprintf(stderr,
423 			"%s: Host does not support worker:schedule\n",
424 			descriptor->URI);
425 		free(handle);
426 		return NULL;
427 	}
428 
429 	if(!handle->opts)
430 	{
431 		fprintf(stderr,
432 			"%s: Host does not support options:option\n",
433 			descriptor->URI);
434 		free(handle);
435 		return NULL;
436 	}
437 
438 	xpress_init(&handle->xpress, 0, handle->driver.map, voice_map,
439 		XPRESS_EVENT_NONE, NULL, NULL, NULL);
440 	handle->xmap.new_uuid = _voice_map_new_uuid;
441 	handle->xmap.handle = &handle->xpress;
442 	handle->driver.xmap = &handle->xmap;
443 
444 	// map URIs
445 	handle->uri.bufsz.max_block_length = handle->driver.map->map(handle->driver.map->handle,
446 		LV2_BUF_SIZE__maxBlockLength);
447 	handle->uri.bufsz.min_block_length = handle->driver.map->map(handle->driver.map->handle,
448 		LV2_BUF_SIZE__minBlockLength);
449 	handle->uri.bufsz.sequence_size = handle->driver.map->map(handle->driver.map->handle,
450 		LV2_BUF_SIZE__sequenceSize);
451 
452 	handle->uri.synthpod.event = handle->driver.map->map(handle->driver.map->handle,
453 		SYNTHPOD_EVENT_URI);
454 
455 	lv2_atom_forge_init(&handle->forge.event_out, handle->driver.map);
456 #if 0
457 	lv2_atom_forge_init(&handle->forge.com_in, handle->driver.map);
458 #endif
459 	lv2_atom_forge_init(&handle->forge.notify, handle->driver.map);
460 
461 	for(LV2_Options_Option *opt = handle->opts;
462 		(opt->key != 0) && (opt->value != NULL);
463 		opt++)
464 	{
465 		if( (opt->key == handle->uri.bufsz.max_block_length)
466 				&& (opt->type == handle->forge.notify.Int) )
467 			handle->driver.max_block_size = *(int32_t *)opt->value;
468 		else if( (opt->key == handle->uri.bufsz.min_block_length)
469 				&& (opt->type == handle->forge.notify.Int) )
470 			handle->driver.min_block_size = *(int32_t *)opt->value;
471 		else if( (opt->key == handle->uri.bufsz.sequence_size)
472 				&& (opt->type == handle->forge.notify.Int) )
473 			handle->driver.seq_size = *(int32_t *)opt->value;
474 		//TODO handle more options
475 	}
476 
477 	handle->advance_worker = true; //TODO reset in activate ?
478 	handle->advance_ui = true; //TODO reset in activate ?
479 	handle->app_to_worker = varchunk_new(CHUNK_SIZE, true);
480 	handle->app_from_worker = varchunk_new(CHUNK_SIZE, true);
481 	handle->app_from_ui = varchunk_new(CHUNK_SIZE, false);
482 	handle->app_from_app = varchunk_new(CHUNK_SIZE, false);
483 
484 	handle->driver.to_ui_request = _to_ui_request;
485 	handle->driver.to_ui_advance = _to_ui_advance;
486 	handle->driver.to_worker_request = _to_worker_request;
487 	handle->driver.to_worker_advance = _to_worker_advance;
488 	handle->driver.to_app_request = _to_app_request;
489 	handle->driver.to_app_advance = _to_app_advance;
490 
491 	handle->app = sp_app_new(world, &handle->driver, handle);
492 	if(!handle->app)
493 	{
494 		fprintf(stderr,
495 			"%s: creation of app failed\n",
496 			descriptor->URI);
497 		free(handle);
498 		return NULL;
499 	}
500 	sp_app_set_bundle_path(handle->app, bundle_path);
501 
502 	return handle;
503 }
504 
505 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)506 connect_port(LV2_Handle instance, uint32_t port, void *data)
507 {
508 	plughandle_t *handle = instance;
509 
510 	switch(port)
511 	{
512 		case 0:
513 			handle->port.event_in = (const LV2_Atom_Sequence *)data;
514 			break;
515 		case 1:
516 			handle->port.event_out = (LV2_Atom_Sequence *)data;
517 			break;
518 
519 		case 2:
520 			handle->port.audio_in[0] = (const float *)data;
521 			break;
522 		case 3:
523 			handle->port.audio_in[1] = (const float *)data;
524 			break;
525 
526 		case 4:
527 			handle->port.audio_out[0] = (float *)data;
528 			break;
529 		case 5:
530 			handle->port.audio_out[1] = (float *)data;
531 			break;
532 
533 		case 6:
534 			handle->port.input[0] = (const float *)data;
535 			break;
536 		case 7:
537 			handle->port.input[1] = (const float *)data;
538 			break;
539 		case 8:
540 			handle->port.input[2] = (const float *)data;
541 			break;
542 		case 9:
543 			handle->port.input[3] = (const float *)data;
544 			break;
545 
546 		case 10:
547 			handle->port.output[0] = (float *)data;
548 			break;
549 		case 11:
550 			handle->port.output[1] = (float *)data;
551 			break;
552 		case 12:
553 			handle->port.output[2] = (float *)data;
554 			break;
555 		case 13:
556 			handle->port.output[3] = (float *)data;
557 			break;
558 
559 		case 14:
560 			handle->port.control = (const LV2_Atom_Sequence *)data;
561 			break;
562 		case 15:
563 			handle->port.notify = (LV2_Atom_Sequence *)data;
564 			break;
565 
566 		default:
567 			break;
568 	}
569 }
570 
571 static void
activate(LV2_Handle instance)572 activate(LV2_Handle instance)
573 {
574 	plughandle_t *handle = instance;
575 
576 	sp_app_activate(handle->app);
577 }
578 
579 static inline void
_process_pre(plughandle_t * handle,uint32_t nsamples,bool bypassed)580 _process_pre(plughandle_t *handle, uint32_t nsamples, bool bypassed)
581 {
582 	sp_app_t *app = handle->app;
583 
584 	// drain worker buffer
585 	{
586 		size_t size;
587 		const void *body;
588 		unsigned n = 0;
589 		while((body = varchunk_read_request(handle->app_from_worker, &size))
590 			&& (n++ < MAX_MSGS) )
591 		{
592 			const bool advance = sp_app_from_worker(handle->app, size, body);
593 			if(!advance)
594 			{
595 				//fprintf(stderr, "plugin worker is blocked\n");
596 				break;
597 			}
598 			varchunk_read_advance(handle->app_from_worker);
599 		}
600 	}
601 
602 	// run app pre
603 	if(!bypassed)
604 		sp_app_run_pre(app, nsamples);
605 
606 	// drain events from UI ringbuffer
607 	{
608 		const LV2_Atom *atom;
609 		size_t size;
610 		unsigned n = 0;
611 		while((atom = varchunk_read_request(handle->app_from_ui, &size))
612 			&& (n++ < MAX_MSGS) )
613 		{
614 			handle->advance_ui = sp_app_from_ui(app, atom);
615 			if(!handle->advance_ui)
616 			{
617 				//fprintf(stderr, "plugin ui indirect is blocked\n");
618 				break;
619 			}
620 			varchunk_read_advance(handle->app_from_ui);
621 		}
622 	}
623 
624 	// drain events from feedback ringbuffer
625 	{
626 		const LV2_Atom *atom;
627 		size_t size;
628 		unsigned n = 0;
629 		while((atom = varchunk_read_request(handle->app_from_app, &size))
630 			&& (n++ < MAX_MSGS) )
631 		{
632 			handle->advance_ui = sp_app_from_ui(app, atom);
633 			if(!handle->advance_ui)
634 			{
635 				//fprintf(stderr, "plugin feedback is blocked\n");
636 				break;
637 			}
638 			varchunk_read_advance(handle->app_from_app);
639 		}
640 	}
641 
642 	//FIXME drain event from separate feedback ringbuffer
643 
644 	// handle events from UI
645 	LV2_ATOM_SEQUENCE_FOREACH(handle->port.control, ev)
646 	{
647 		const LV2_Atom *atom = &ev->body;
648 		const LV2_Atom_Object *obj = (const LV2_Atom_Object *)atom;
649 
650 		if(atom->type == handle->forge.notify.Object)
651 		{
652 			// copy com events to com buffer
653 			if(sp_app_com_event(handle->app, obj->body.otype))
654 			{
655 				uint32_t size = obj->atom.size + sizeof(LV2_Atom);
656 #if 0
657 				if(handle->ref.com_in)
658 					handle->ref.com_in = lv2_atom_forge_frame_time(&handle->forge.com_in, ev->time.frames);
659 				if(handle->ref.com_in)
660 					handle->ref.com_in = lv2_atom_forge_raw(&handle->forge.com_in, obj, size);
661 				if(handle->ref.com_in)
662 					lv2_atom_forge_pad(&handle->forge.com_in, size);
663 #endif
664 			}
665 
666 			// try do process events directly
667 			handle->advance_ui = sp_app_from_ui(app, atom);
668 			if(!handle->advance_ui) // queue event in ringbuffer instead
669 			{
670 				//fprintf(stderr, "plugin ui direct is blocked\n");
671 
672 				void *ptr;
673 				size_t size = lv2_atom_total_size(atom);
674 				if((ptr = varchunk_write_request(handle->app_from_ui, size)))
675 				{
676 					memcpy(ptr, atom, size);
677 					varchunk_write_advance(handle->app_from_ui, size);
678 				}
679 				else
680 				{
681 					//fprintf(stderr, "app_from_ui ringbuffer full\n");
682 					//FIXME
683 				}
684 			}
685 		}
686 	}
687 
688 	// run app post
689 	if(!bypassed)
690 		sp_app_run_post(app, nsamples);
691 
692 	// write com events to feedback buffer
693 	if(handle->sink.com_out)
694 	{
695 		LV2_ATOM_SEQUENCE_FOREACH(handle->sink.com_out, ev)
696 		{
697 			const LV2_Atom *atom = &ev->body;
698 
699 			void *ptr;
700 			size_t size = lv2_atom_total_size(atom);
701 			if((ptr = varchunk_write_request(handle->app_from_app, size)))
702 			{
703 				memcpy(ptr, atom, size);
704 				varchunk_write_advance(handle->app_from_app, size);
705 			}
706 			else
707 			{
708 				//FIXME
709 			}
710 		}
711 	}
712 }
713 
714 static inline void
_process_post(plughandle_t * handle)715 _process_post(plughandle_t *handle)
716 {
717 	if(handle->trigger_worker)
718 	{
719 		//fprintf(stderr, "work triggered\n");
720 
721 		const job_t job = {
722 			.type = JOB_TYPE_NONE
723 		};
724 		handle->schedule->schedule_work(handle->schedule->handle,
725 			sizeof(job_t), &job); //FIXME check
726 
727 		handle->trigger_worker = false;
728 	}
729 }
730 
731 __realtime static void
run(LV2_Handle instance,uint32_t nsamples)732 run(LV2_Handle instance, uint32_t nsamples)
733 {
734 	plughandle_t *handle = instance;
735 	sp_app_t *app = handle->app;
736 
737 	const size_t sample_buf_size = sizeof(float) * nsamples;
738 
739 	// get input buffers
740 	handle->source.event_in = NULL;
741 	handle->source.audio_in[0] = NULL;
742 	handle->source.audio_in[1] = NULL;
743 	handle->source.input[0] = NULL;
744 	handle->source.input[1] = NULL;
745 	handle->source.input[2] = NULL;
746 	handle->source.input[3] = NULL;
747 
748 	const sp_app_system_source_t *sources = sp_app_get_system_sources(app);
749 	int audio_ptr = 0;
750 	int control_ptr = 0;
751 	for(const sp_app_system_source_t *source=sources;
752 		source->type != SYSTEM_PORT_NONE;
753 		source++)
754 	{
755 		switch(source->type)
756 		{
757 			case SYSTEM_PORT_MIDI:
758 				handle->source.event_in = source->buf;
759 				break;
760 			case SYSTEM_PORT_AUDIO:
761 				handle->source.audio_in[audio_ptr++] = source->buf;
762 				break;
763 			case SYSTEM_PORT_CONTROL:
764 				handle->source.input[control_ptr++] = source->buf;
765 				break;
766 			case SYSTEM_PORT_COM:
767 #if 0
768 				handle->source.com_in = source->buf;
769 #endif
770 				break;
771 
772 			case SYSTEM_PORT_CV:
773 			case SYSTEM_PORT_OSC:
774 			case SYSTEM_PORT_NONE:
775 				break;
776 		}
777 	}
778 
779 	//TODO use __builtin_assume_aligned
780 
781 	// fill input buffers
782 	if(handle->source.event_in)
783 		memcpy(handle->source.event_in, handle->port.event_in, SEQ_SIZE);
784 	if(handle->source.audio_in[0])
785 		memcpy(handle->source.audio_in[0], handle->port.audio_in[0], sample_buf_size);
786 	if(handle->source.audio_in[1])
787 		memcpy(handle->source.audio_in[1], handle->port.audio_in[1], sample_buf_size);
788 	if(handle->source.input[0])
789 		*handle->source.input[0] = *handle->port.input[0];
790 	if(handle->source.input[1])
791 		*handle->source.input[1] = *handle->port.input[1];
792 	if(handle->source.input[2])
793 		*handle->source.input[2] = *handle->port.input[2];
794 	if(handle->source.input[3])
795 		*handle->source.input[3] = *handle->port.input[3];
796 
797 	// get output buffers
798 	const sp_app_system_sink_t *sinks = sp_app_get_system_sinks(app);
799 
800 	// fill output buffers
801 	handle->sink.event_out = NULL;
802 	handle->sink.audio_out[0] = NULL;
803 	handle->sink.audio_out[1] = NULL;
804 	handle->sink.output[0] = NULL;
805 	handle->sink.output[1] = NULL;
806 	handle->sink.output[2] = NULL;
807 	handle->sink.output[3] = NULL;
808 
809 	audio_ptr = 0;
810 	control_ptr = 0;
811 	for(const sp_app_system_sink_t *sink=sinks;
812 		sink->type != SYSTEM_PORT_NONE;
813 		sink++)
814 	{
815 		switch(sink->type)
816 		{
817 			case SYSTEM_PORT_MIDI:
818 				handle->sink.event_out = sink->buf;
819 				break;
820 			case SYSTEM_PORT_AUDIO:
821 				handle->sink.audio_out[audio_ptr++] = sink->buf;
822 				break;
823 			case SYSTEM_PORT_CONTROL:
824 				handle->sink.output[control_ptr++] = sink->buf;
825 				break;
826 			case SYSTEM_PORT_COM:
827 				handle->sink.com_out = sink->buf;
828 				break;
829 
830 			case SYSTEM_PORT_CV:
831 			case SYSTEM_PORT_OSC:
832 			case SYSTEM_PORT_NONE:
833 				break;
834 		}
835 	}
836 
837 	struct {
838 		LV2_Atom_Forge_Frame event_out;
839 #if 0
840 		LV2_Atom_Forge_Frame com_in;
841 #endif
842 		LV2_Atom_Forge_Frame notify;
843 	} frame;
844 
845 	// prepare forge(s) & sequence(s)
846 	lv2_atom_forge_set_buffer(&handle->forge.event_out,
847 		(uint8_t *)handle->port.event_out, handle->port.event_out->atom.size);
848 	handle->ref.event_out = lv2_atom_forge_sequence_head(&handle->forge.event_out, &frame.event_out, 0);
849 
850 #if 0
851 	lv2_atom_forge_set_buffer(&handle->forge.com_in,
852 		(uint8_t *)handle->source.com_in, SEQ_SIZE);
853 	handle->ref.com_in = lv2_atom_forge_sequence_head(&handle->forge.com_in, &frame.com_in, 0);
854 #endif
855 
856 	lv2_atom_forge_set_buffer(&handle->forge.notify,
857 		(uint8_t *)handle->port.notify, handle->port.notify->atom.size);
858 	handle->ref.notify = lv2_atom_forge_sequence_head(&handle->forge.notify, &frame.notify, 0);
859 
860 	if(sp_app_bypassed(app))
861 	{
862 		//fprintf(stderr, "plugin app is bypassed\n");
863 
864 		memset(handle->port.audio_out[0], 0x0, nsamples*sizeof(float));
865 		memset(handle->port.audio_out[1], 0x0, nsamples*sizeof(float));
866 
867 		*handle->port.output[0] = 0.f;
868 		*handle->port.output[1] = 0.f;
869 		*handle->port.output[2] = 0.f;
870 		*handle->port.output[3] = 0.f;
871 
872 		_process_pre(handle, nsamples, true);
873 		_process_post(handle);
874 
875 		// end sequence(s)
876 		if(handle->ref.event_out)
877 			lv2_atom_forge_pop(&handle->forge.event_out, &frame.event_out);
878 		else
879 			lv2_atom_sequence_clear(handle->port.event_out);
880 
881 #if 0
882 		if(handle->ref.com_in)
883 			lv2_atom_forge_pop(&handle->forge.com_in, &frame.com_in);
884 		else
885 			lv2_atom_sequence_clear(handle->source.com_in);
886 #endif
887 
888 		if(handle->ref.notify)
889 			lv2_atom_forge_pop(&handle->forge.notify, &frame.notify);
890 		else
891 			lv2_atom_sequence_clear(handle->port.notify);
892 
893 		return;
894 	}
895 
896 	_process_pre(handle, nsamples, false);
897 	_process_post(handle);
898 
899 	// end sequence(s)
900 	if(handle->ref.event_out)
901 		lv2_atom_forge_pop(&handle->forge.event_out, &frame.event_out);
902 	else
903 		lv2_atom_sequence_clear(handle->port.event_out);
904 
905 #if 0
906 	if(handle->ref.com_in)
907 		lv2_atom_forge_pop(&handle->forge.com_in, &frame.com_in);
908 	else
909 		lv2_atom_sequence_clear(handle->source.com_in);
910 #endif
911 
912 	if(handle->ref.notify)
913 		lv2_atom_forge_pop(&handle->forge.notify, &frame.notify);
914 	else
915 		lv2_atom_sequence_clear(handle->port.notify);
916 
917 	if(handle->sink.event_out)
918 		memcpy(handle->port.event_out, handle->sink.event_out, SEQ_SIZE);
919 	else
920 		memset(handle->port.event_out, 0x0, SEQ_SIZE);
921 
922 	if(handle->sink.audio_out[0])
923 		memcpy(handle->port.audio_out[0], handle->sink.audio_out[0], sample_buf_size);
924 	else
925 		memset(handle->port.audio_out[0], 0x0, sample_buf_size);
926 
927 	if(handle->sink.audio_out[1])
928 		memcpy(handle->port.audio_out[1], handle->sink.audio_out[1], sample_buf_size);
929 	else
930 		memset(handle->port.audio_out[1], 0x0, sample_buf_size);
931 
932 	*handle->port.output[0] = handle->sink.output[0] ? *handle->sink.output[0] : 0.f;
933 	*handle->port.output[1] = handle->sink.output[1] ? *handle->sink.output[1] : 0.f;
934 	*handle->port.output[2] = handle->sink.output[2] ? *handle->sink.output[2] : 0.f;
935 	*handle->port.output[3] = handle->sink.output[3] ? *handle->sink.output[3] : 0.f;
936 }
937 
938 static void
deactivate(LV2_Handle instance)939 deactivate(LV2_Handle instance)
940 {
941 	plughandle_t *handle = instance;
942 
943 	sp_app_deactivate(handle->app);
944 }
945 
946 static void
cleanup(LV2_Handle instance)947 cleanup(LV2_Handle instance)
948 {
949 	plughandle_t *handle = instance;
950 
951 	sp_app_free(handle->app);
952 
953 	varchunk_free(handle->app_to_worker);
954 	varchunk_free(handle->app_from_worker);
955 	varchunk_free(handle->app_from_ui);
956 	varchunk_free(handle->app_from_app);
957 	xpress_deinit(&handle->xpress);
958 
959 	munlock(handle, sizeof(plughandle_t));
960 	free(handle);
961 }
962 
963 __realtime static uint32_t
_opts_get(LV2_Handle instance,LV2_Options_Option * options)964 _opts_get(LV2_Handle instance, LV2_Options_Option *options)
965 {
966 	// we have no options
967 
968 	return LV2_OPTIONS_ERR_BAD_KEY;
969 }
970 
971 __realtime static uint32_t
_opts_set(LV2_Handle instance,const LV2_Options_Option * options)972 _opts_set(LV2_Handle instance, const LV2_Options_Option *options)
973 {
974 	plughandle_t *handle = instance;
975 
976 	// route options to all plugins
977 	return sp_app_options_set(handle->app, options);
978 }
979 
980 static const LV2_Options_Interface opts_iface = {
981 	.get = _opts_get,
982 	.set = _opts_set
983 };
984 
985 static const void*
extension_data(const char * uri)986 extension_data(const char* uri)
987 {
988 	if(!strcmp(uri, LV2_WORKER__interface))
989 		return &work_iface;
990 	else if(!strcmp(uri, LV2_STATE__interface))
991 		return &state_iface;
992 	else if(!strcmp(uri, LV2_OPTIONS__interface))
993 		return &opts_iface;
994 
995 	return NULL;
996 }
997 
998 const LV2_Descriptor synthpod_stereo = {
999 	.URI						= SYNTHPOD_STEREO_URI,
1000 	.instantiate		= instantiate,
1001 	.connect_port		= connect_port,
1002 	.activate				= activate,
1003 	.run						= run,
1004 	.deactivate			= deactivate,
1005 	.cleanup				= cleanup,
1006 	.extension_data	= extension_data
1007 };
1008