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