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 <synthpod_app_private.h>
19 #include <synthpod_patcher.h>
20
21 static inline bool
_mod_needs_ramping(mod_t * mod,ramp_state_t state,bool silencing)22 _mod_needs_ramping(mod_t *mod, ramp_state_t state, bool silencing)
23 {
24 sp_app_t *app = mod->app;
25
26 // ramping
27 int needs_ramping = 0;
28 for(unsigned p1=0; p1<mod->num_ports; p1++)
29 {
30 port_t *port = &mod->ports[p1];
31
32 // silence sources
33 /* TODO is this needed?
34 for(int s=0; s<port->num_sources; s++)
35 {
36 _sp_app_port_silence_request(app,
37 port->sources[s].port, port, state);
38 }
39 */
40
41 // silence sinks
42 for(unsigned m=0; m<app->num_mods; m++)
43 {
44 for(unsigned p2=0; p2<app->mods[m]->num_ports; p2++)
45 {
46 if(silencing)
47 {
48 needs_ramping += _sp_app_port_silence_request(app,
49 port, &app->mods[m]->ports[p2], state);
50 }
51 else
52 {
53 needs_ramping += _sp_app_port_desilence(app,
54 port, &app->mods[m]->ports[p2]);
55 }
56 }
57 }
58 }
59
60 return needs_ramping > 0;
61 }
62
63 //FIXME move into another file
64 __realtime static mod_t *
_mod_find_by_urn(sp_app_t * app,LV2_URID urn)65 _mod_find_by_urn(sp_app_t *app, LV2_URID urn)
66 {
67 for(unsigned m = 0; m < app->num_mods; m++)
68 {
69 mod_t *mod = app->mods[m];
70
71 if(mod->urn == urn)
72 return mod;
73 }
74
75 return NULL;
76 }
77
78 //FIXME move into another file
79 __realtime static port_t *
_port_find_by_symbol(sp_app_t * app,LV2_URID urn,const char * symbol)80 _port_find_by_symbol(sp_app_t *app, LV2_URID urn, const char *symbol)
81 {
82 mod_t *mod = _mod_find_by_urn(app, urn);
83 if(mod)
84 {
85 for(unsigned p = 0; p < mod->num_ports; p++)
86 {
87 port_t *port = &mod->ports[p];
88
89 if(!strcmp(port->symbol, symbol))
90 return port;
91 }
92 }
93
94 return NULL;
95 }
96
97 __realtime void
_sp_app_ui_set_modlist(sp_app_t * app,LV2_URID subj,int32_t seqn)98 _sp_app_ui_set_modlist(sp_app_t *app, LV2_URID subj, int32_t seqn)
99 {
100 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
101 if(answer)
102 {
103 LV2_Atom_Forge_Frame frame [2];
104 LV2_Atom_Forge_Ref ref = synthpod_patcher_set_object(
105 &app->regs, &app->forge, &frame[0], subj, seqn, app->regs.synthpod.module_list.urid);
106 if(ref)
107 ref = lv2_atom_forge_tuple(&app->forge, &frame[1]);
108 for(unsigned m = 0; m < app->num_mods; m++)
109 {
110 mod_t *mod = app->mods[m];
111
112 if(ref)
113 ref = lv2_atom_forge_urid(&app->forge, mod->urn);
114 }
115 if(ref)
116 {
117 synthpod_patcher_pop(&app->forge, frame, 2);
118 _sp_app_to_ui_advance_atom(app, answer);
119 }
120 else
121 {
122 _sp_app_to_ui_overflow(app);
123 }
124 }
125 else
126 {
127 _sp_app_to_ui_overflow(app);
128 }
129 }
130
131 __realtime LV2_Atom_Forge_Ref
_sp_app_forge_midi_automation(sp_app_t * app,LV2_Atom_Forge_Frame * frame,mod_t * mod,port_t * port,const auto_t * automation)132 _sp_app_forge_midi_automation(sp_app_t *app, LV2_Atom_Forge_Frame *frame,
133 mod_t *mod, port_t *port, const auto_t *automation)
134 {
135 const midi_auto_t *mauto = &automation->midi;
136 LV2_Atom_Forge_Ref ref;
137
138 ref = lv2_atom_forge_object(&app->forge, frame, 0, app->regs.midi.Controller.urid);
139 if(ref)
140 {
141 if(ref)
142 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_module.urid);
143 if(ref)
144 ref = lv2_atom_forge_urid(&app->forge, mod->urn);
145
146 if(automation->property)
147 {
148 if(ref)
149 ref = lv2_atom_forge_key(&app->forge, app->regs.patch.property.urid);
150 if(ref)
151 ref = lv2_atom_forge_urid(&app->forge, automation->property);
152 if(ref)
153 ref = lv2_atom_forge_key(&app->forge, app->regs.rdfs.range.urid);
154 if(ref)
155 ref = lv2_atom_forge_urid(&app->forge, automation->range);
156 }
157 else
158 {
159 if(ref)
160 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_symbol.urid);
161 if(ref)
162 ref = lv2_atom_forge_string(&app->forge, port->symbol, strlen(port->symbol));
163 }
164
165 if(ref)
166 ref = lv2_atom_forge_key(&app->forge, app->regs.midi.channel.urid);
167 if(ref)
168 ref = lv2_atom_forge_int(&app->forge, mauto->channel);
169
170 if(ref)
171 ref = lv2_atom_forge_key(&app->forge, app->regs.midi.controller_number.urid);
172 if(ref)
173 ref = lv2_atom_forge_int(&app->forge, mauto->controller);
174
175 if(ref)
176 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.source_min.urid);
177 if(ref)
178 ref = lv2_atom_forge_double(&app->forge, automation->a);
179
180 if(ref)
181 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.source_max.urid);
182 if(ref)
183 ref = lv2_atom_forge_double(&app->forge, automation->b);
184
185 if(ref)
186 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_min.urid);
187 if(ref)
188 ref = lv2_atom_forge_double(&app->forge, automation->c);
189
190 if(ref)
191 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_max.urid);
192 if(ref)
193 ref = lv2_atom_forge_double(&app->forge, automation->d);
194
195 if(ref)
196 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.source_enabled.urid);
197 if(ref)
198 ref = lv2_atom_forge_bool(&app->forge, automation->src_enabled);
199
200 if(ref)
201 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_enabled.urid);
202 if(ref)
203 ref = lv2_atom_forge_bool(&app->forge, automation->snk_enabled);
204 }
205 if(ref)
206 lv2_atom_forge_pop(&app->forge, frame);
207
208 return ref;
209 }
210
211 __realtime LV2_Atom_Forge_Ref
_sp_app_forge_osc_automation(sp_app_t * app,LV2_Atom_Forge_Frame * frame,mod_t * mod,port_t * port,const auto_t * automation)212 _sp_app_forge_osc_automation(sp_app_t *app, LV2_Atom_Forge_Frame *frame,
213 mod_t *mod, port_t *port, const auto_t *automation)
214 {
215 const osc_auto_t *oauto = &automation->osc;
216 LV2_Atom_Forge_Ref ref;
217
218 ref = lv2_atom_forge_object(&app->forge, frame, 0, app->regs.osc.message.urid);
219 if(ref)
220 {
221 if(ref)
222 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_module.urid);
223 if(ref)
224 ref = lv2_atom_forge_urid(&app->forge, mod->urn);
225
226 if(automation->property)
227 {
228 if(ref)
229 ref = lv2_atom_forge_key(&app->forge, app->regs.patch.property.urid);
230 if(ref)
231 ref = lv2_atom_forge_urid(&app->forge, automation->property);
232 if(ref)
233 ref = lv2_atom_forge_key(&app->forge, app->regs.rdfs.range.urid);
234 if(ref)
235 ref = lv2_atom_forge_urid(&app->forge, automation->range);
236 }
237 else
238 {
239 if(ref)
240 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_symbol.urid);
241 if(ref)
242 ref = lv2_atom_forge_string(&app->forge, port->symbol, strlen(port->symbol));
243 }
244
245 if(ref)
246 ref = lv2_atom_forge_key(&app->forge, app->regs.osc.path.urid);
247 if(ref)
248 ref = lv2_atom_forge_string(&app->forge, oauto->path, strlen(oauto->path));
249
250 if(ref)
251 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.source_min.urid);
252 if(ref)
253 ref = lv2_atom_forge_double(&app->forge, automation->a);
254
255 if(ref)
256 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.source_max.urid);
257 if(ref)
258 ref = lv2_atom_forge_double(&app->forge, automation->b);
259
260 if(ref)
261 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_min.urid);
262 if(ref)
263 ref = lv2_atom_forge_double(&app->forge, automation->c);
264
265 if(ref)
266 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_max.urid);
267 if(ref)
268 ref = lv2_atom_forge_double(&app->forge, automation->d);
269
270 if(ref)
271 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.source_enabled.urid);
272 if(ref)
273 ref = lv2_atom_forge_bool(&app->forge, automation->src_enabled);
274
275 if(ref)
276 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_enabled.urid);
277 if(ref)
278 ref = lv2_atom_forge_bool(&app->forge, automation->snk_enabled);
279 }
280 if(ref)
281 lv2_atom_forge_pop(&app->forge, frame);
282
283 return ref;
284 }
285
286 __realtime static bool
_sp_app_from_ui_patch_get(sp_app_t * app,const LV2_Atom * atom)287 _sp_app_from_ui_patch_get(sp_app_t *app, const LV2_Atom *atom)
288 {
289 const LV2_Atom_Object *obj = ASSUME_ALIGNED(atom);
290
291 const LV2_Atom_URID *subject = NULL;
292 const LV2_Atom_Int *seqn = NULL;
293 const LV2_Atom_URID *property = NULL;
294
295 lv2_atom_object_get(obj,
296 app->regs.patch.subject.urid, &subject,
297 app->regs.patch.sequence_number.urid, &seqn,
298 app->regs.patch.property.urid, &property,
299 0);
300
301 const LV2_URID subj = subject && (subject->atom.type == app->forge.URID)
302 ? subject->body : 0;
303 const int32_t sn = seqn && (seqn->atom.type == app->forge.Int)
304 ? seqn->body : 0;
305 const LV2_URID prop = property && (property->atom.type == app->forge.URID)
306 ? property->body : 0;
307
308 //printf("got patch:Get for <%s>\n", app->driver->unmap->unmap(app->driver->unmap->handle, subj));
309
310 if(!subj && prop) //FIXME
311 {
312 //printf("\tpatch:property <%s>\n", app->driver->unmap->unmap(app->driver->unmap->handle, prop));
313
314 if(prop == app->regs.synthpod.module_list.urid)
315 {
316 _sp_app_ui_set_modlist(app, subj, sn);
317 }
318 else if(prop == app->regs.synthpod.connection_list.urid)
319 {
320 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
321 if(answer)
322 {
323 LV2_Atom_Forge_Frame frame [3];
324 LV2_Atom_Forge_Ref ref = synthpod_patcher_set_object(
325 &app->regs, &app->forge, &frame[0], subj, sn, prop);
326 if(ref)
327 ref = lv2_atom_forge_tuple(&app->forge, &frame[1]);
328 for(unsigned m = 0; m < app->num_mods; m++)
329 {
330 mod_t *mod = app->mods[m];
331
332 for(unsigned p = 0; p < mod->num_ports; p++)
333 {
334 port_t *port = &mod->ports[p];
335
336 connectable_t *conn = _sp_app_port_connectable(port);
337 if(conn)
338 {
339 for(int s = 0; s < conn->num_sources; s++)
340 {
341 source_t *source = &conn->sources[s];
342
343 if(ref)
344 ref = lv2_atom_forge_object(&app->forge, &frame[2], 0, 0);
345 {
346 if(ref)
347 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.source_module.urid);
348 if(ref)
349 ref = lv2_atom_forge_urid(&app->forge, source->port->mod->urn);
350
351 if(ref)
352 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.source_symbol.urid);
353 if(ref)
354 ref = lv2_atom_forge_string(&app->forge, source->port->symbol, strlen(source->port->symbol));
355
356 if(ref)
357 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_module.urid);
358 if(ref)
359 ref = lv2_atom_forge_urid(&app->forge, port->mod->urn);
360
361 if(ref)
362 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_symbol.urid);
363 if(ref)
364 ref = lv2_atom_forge_string(&app->forge, port->symbol, strlen(port->symbol));
365
366 if(ref)
367 ref = lv2_atom_forge_key(&app->forge, app->regs.param.gain.urid);
368 if(ref)
369 ref = lv2_atom_forge_float(&app->forge, source->gain);
370 }
371 if(ref)
372 lv2_atom_forge_pop(&app->forge, &frame[2]);
373 }
374 }
375 }
376 }
377 if(ref)
378 {
379 synthpod_patcher_pop(&app->forge, frame, 2);
380 _sp_app_to_ui_advance_atom(app, answer);
381 }
382 else
383 {
384 _sp_app_to_ui_overflow(app);
385 }
386 }
387 else
388 {
389 _sp_app_to_ui_overflow(app);
390 }
391 }
392 else if(prop == app->regs.synthpod.node_list.urid)
393 {
394 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
395 if(answer)
396 {
397 LV2_Atom_Forge_Frame frame [3];
398 LV2_Atom_Forge_Ref ref = synthpod_patcher_set_object(
399 &app->regs, &app->forge, &frame[0], subj, sn, prop);
400 if(ref)
401 ref = lv2_atom_forge_tuple(&app->forge, &frame[1]);
402 for(unsigned m1 = 0; m1 < app->num_mods; m1++)
403 {
404 mod_t *snk_mod = app->mods[m1];
405
406 for(unsigned m2=0; m2<app->num_mods; m2++)
407 {
408 mod_t *src_mod = app->mods[m2];
409 bool mods_are_connected = false;
410 float x = 0.f;
411 float y = 0.f;
412
413 for(unsigned p=0; p<snk_mod->num_ports; p++)
414 {
415 port_t *port = &snk_mod->ports[p];
416
417 connectable_t *conn = _sp_app_port_connectable(port);
418 if(conn)
419 {
420 for(int j=0; j<conn->num_sources; j++)
421 {
422 source_t *source = &conn->sources[j];
423 port_t *src_port = source->port;
424
425 if(src_port->mod == src_mod)
426 {
427 mods_are_connected = true;
428 x = source->pos.x;
429 y = source->pos.y;
430 break;
431 }
432 }
433 }
434
435 if(mods_are_connected)
436 break;
437 }
438
439 if(mods_are_connected)
440 {
441 if(ref)
442 ref = lv2_atom_forge_object(&app->forge, &frame[2], 0, 0);
443 {
444 if(ref)
445 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.source_module.urid);
446 if(ref)
447 ref = lv2_atom_forge_urid(&app->forge, src_mod->urn);
448
449 if(ref)
450 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.sink_module.urid);
451 if(ref)
452 ref = lv2_atom_forge_urid(&app->forge, snk_mod->urn);
453
454 if(ref)
455 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.node_position_x.urid);
456 if(ref)
457 ref = lv2_atom_forge_float(&app->forge, x);
458
459 if(ref)
460 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.node_position_y.urid);
461 if(ref)
462 ref = lv2_atom_forge_float(&app->forge, y);
463 }
464 if(ref)
465 lv2_atom_forge_pop(&app->forge, &frame[2]);
466 }
467 }
468 }
469 if(ref)
470 {
471 synthpod_patcher_pop(&app->forge, frame, 2);
472 _sp_app_to_ui_advance_atom(app, answer);
473 }
474 else
475 {
476 _sp_app_to_ui_overflow(app);
477 }
478 }
479 else
480 {
481 _sp_app_to_ui_overflow(app);
482 }
483 }
484 else if(prop == app->regs.pset.preset.urid)
485 {
486 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
487 if(answer)
488 {
489 const LV2_URID bundle_urid = app->driver->map->map(app->driver->map->handle, app->bundle_path); //FIXME store bundle path as URID
490
491 LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
492 &app->regs, &app->forge, subj, sn, prop,
493 sizeof(uint32_t), app->forge.URID, &bundle_urid);
494 if(ref)
495 {
496 _sp_app_to_ui_advance_atom(app, answer);
497 }
498 else
499 {
500 _sp_app_to_ui_overflow(app);
501 }
502 }
503 else
504 {
505 _sp_app_to_ui_overflow(app);
506 }
507 }
508 else if(prop == app->regs.synthpod.automation_list.urid)
509 {
510 //printf("patch:Get for spod:automationList\n");
511
512 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
513 if(answer)
514 {
515 LV2_Atom_Forge_Frame frame [3];
516 LV2_Atom_Forge_Ref ref = synthpod_patcher_set_object(
517 &app->regs, &app->forge, &frame[0], subj, sn, prop);
518 if(ref)
519 ref = lv2_atom_forge_tuple(&app->forge, &frame[1]);
520
521 for(unsigned m = 0; m < app->num_mods; m++)
522 {
523 mod_t *mod = app->mods[m];
524
525 for(unsigned i = 0; i < MAX_AUTOMATIONS; i++)
526 {
527 auto_t *automation = &mod->automations[i];
528 port_t *port = &mod->ports[automation->index];
529
530 if(automation->type == AUTO_TYPE_MIDI)
531 {
532 if(ref)
533 ref = _sp_app_forge_midi_automation(app, &frame[2], mod, port, automation);
534 }
535 else if(automation->type == AUTO_TYPE_OSC)
536 {
537 if(ref)
538 ref = _sp_app_forge_osc_automation(app, &frame[2], mod, port, automation);
539 }
540 }
541 }
542
543 if(ref)
544 {
545 synthpod_patcher_pop(&app->forge, frame, 2);
546 _sp_app_to_ui_advance_atom(app, answer);
547 }
548 else
549 {
550 _sp_app_to_ui_overflow(app);
551 }
552 }
553 else
554 {
555 _sp_app_to_ui_overflow(app);
556 }
557 }
558 else if(prop == app->regs.synthpod.graph_position_x.urid)
559 {
560 //printf("patch:Get for spod:graphPositionX\n");
561 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
562 if(answer)
563 {
564 LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
565 &app->regs, &app->forge, subj, sn, prop,
566 sizeof(float), app->forge.Float, &app->pos.x);
567 if(ref)
568 {
569 _sp_app_to_ui_advance_atom(app, answer);
570 }
571 else
572 {
573 _sp_app_to_ui_overflow(app);
574 }
575 }
576 else
577 {
578 _sp_app_to_ui_overflow(app);
579 }
580 }
581 else if(prop == app->regs.synthpod.graph_position_y.urid)
582 {
583 //printf("patch:Get for spod:graphPositionY\n");
584 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
585 if(answer)
586 {
587 LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
588 &app->regs, &app->forge, subj, sn, prop,
589 sizeof(float), app->forge.Float, &app->pos.y);
590 if(ref)
591 {
592 _sp_app_to_ui_advance_atom(app, answer);
593 }
594 else
595 {
596 _sp_app_to_ui_overflow(app);
597 }
598 }
599 else
600 {
601 _sp_app_to_ui_overflow(app);
602 }
603 }
604 else if(prop == app->regs.synthpod.column_enabled.urid)
605 {
606 //printf("patch:Get for spod:columnEnabled\n");
607 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
608 if(answer)
609 {
610 LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
611 &app->regs, &app->forge, subj, sn, prop,
612 sizeof(int32_t), app->forge.Bool, &app->column_enabled);
613 if(ref)
614 {
615 _sp_app_to_ui_advance_atom(app, answer);
616 }
617 else
618 {
619 _sp_app_to_ui_overflow(app);
620 }
621 }
622 else
623 {
624 _sp_app_to_ui_overflow(app);
625 }
626 }
627 else if(prop == app->regs.synthpod.row_enabled.urid)
628 {
629 //printf("patch:Get for spod:rowEnabled\n");
630 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
631 if(answer)
632 {
633 LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
634 &app->regs, &app->forge, subj, sn, prop,
635 sizeof(int32_t), app->forge.Bool, &app->row_enabled);
636 if(ref)
637 {
638 _sp_app_to_ui_advance_atom(app, answer);
639 }
640 else
641 {
642 _sp_app_to_ui_overflow(app);
643 }
644 }
645 else
646 {
647 _sp_app_to_ui_overflow(app);
648 }
649 }
650 else if(prop == app->regs.synthpod.cpus_available.urid)
651 {
652 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
653 if(answer)
654 {
655 const int32_t cpus_available = app->dsp_master.num_slaves + 1;
656
657 LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
658 &app->regs, &app->forge, subj, sn, prop,
659 sizeof(int32_t), app->forge.Int, &cpus_available);
660 if(ref)
661 {
662 _sp_app_to_ui_advance_atom(app, answer);
663 }
664 else
665 {
666 _sp_app_to_ui_overflow(app);
667 }
668 }
669 else
670 {
671 _sp_app_to_ui_overflow(app);
672 }
673 }
674 else if(prop == app->regs.synthpod.cpus_used.urid)
675 {
676 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
677 if(answer)
678 {
679 const int32_t cpus_used = (app->dsp_master.concurrent > app->dsp_master.num_slaves + 1)
680 ? app->dsp_master.num_slaves + 1
681 : app->dsp_master.concurrent;
682
683 LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
684 &app->regs, &app->forge, subj, sn, prop,
685 sizeof(int32_t), app->forge.Int, &cpus_used);
686 if(ref)
687 {
688 _sp_app_to_ui_advance_atom(app, answer);
689 }
690 else
691 {
692 _sp_app_to_ui_overflow(app);
693 }
694 }
695 else
696 {
697 _sp_app_to_ui_overflow(app);
698 }
699 }
700 else if(prop == app->regs.synthpod.period_size.urid)
701 {
702 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
703 if(answer)
704 {
705 const int32_t period_size = app->driver->max_block_size;
706
707 LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
708 &app->regs, &app->forge, subj, sn, prop,
709 sizeof(int32_t), app->forge.Int, &period_size);
710 if(ref)
711 {
712 _sp_app_to_ui_advance_atom(app, answer);
713 }
714 else
715 {
716 _sp_app_to_ui_overflow(app);
717 }
718 }
719 else
720 {
721 _sp_app_to_ui_overflow(app);
722 }
723 }
724 else if(prop == app->regs.synthpod.num_periods.urid)
725 {
726 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
727 if(answer)
728 {
729 const int32_t num_periods = app->driver->num_periods;
730
731 LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
732 &app->regs, &app->forge, subj, sn, prop,
733 sizeof(int32_t), app->forge.Int, &num_periods);
734 if(ref)
735 {
736 _sp_app_to_ui_advance_atom(app, answer);
737 }
738 else
739 {
740 _sp_app_to_ui_overflow(app);
741 }
742 }
743 else
744 {
745 _sp_app_to_ui_overflow(app);
746 }
747 }
748 //TODO handle more properties
749 }
750 else if(subj)
751 {
752 for(unsigned m = 0; m < app->num_mods; m++)
753 {
754 mod_t *mod = app->mods[m];
755
756 if(mod->urn == subj)
757 {
758 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
759 if(answer)
760 {
761 LV2_Atom_Forge_Frame frame [2];
762 LV2_Atom_Forge_Ref ref = synthpod_patcher_put_object(
763 &app->regs, &app->forge, &frame[0], subj, sn);
764 if(ref)
765 ref = lv2_atom_forge_object(&app->forge, &frame[1], 0, 0);
766 {
767 if(ref)
768 ref = lv2_atom_forge_key(&app->forge, app->regs.core.plugin.urid);
769 if(ref)
770 ref = lv2_atom_forge_urid(&app->forge, mod->plug_urid);
771
772 if(ref)
773 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.module_position_x.urid);
774 if(ref)
775 ref = lv2_atom_forge_float(&app->forge, mod->pos.x);
776
777 if(ref)
778 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.module_position_y.urid);
779 if(ref)
780 ref = lv2_atom_forge_float(&app->forge, mod->pos.y);
781
782 if(strlen(mod->alias))
783 {
784 if(ref)
785 ref = lv2_atom_forge_key(&app->forge, app->regs.synthpod.module_alias.urid);
786 if(ref)
787 ref = lv2_atom_forge_string(&app->forge, mod->alias, strlen(mod->alias));
788 }
789
790 if(mod->ui)
791 {
792 if(ref)
793 ref = lv2_atom_forge_key(&app->forge, app->regs.ui.ui.urid);
794 if(ref)
795 ref = lv2_atom_forge_urid(&app->forge, mod->ui);
796 }
797 }
798 if(ref)
799 {
800 synthpod_patcher_pop(&app->forge, frame, 2);
801 _sp_app_to_ui_advance_atom(app, answer);
802 }
803 else
804 {
805 _sp_app_to_ui_overflow(app);
806 }
807 }
808 else
809 {
810 _sp_app_to_ui_overflow(app);
811 }
812
813 break; // match
814 }
815 }
816 }
817
818 return advance_ui[app->block_state];
819 }
820
821 __realtime static bool
_sp_app_from_ui_patch_set(sp_app_t * app,const LV2_Atom * atom)822 _sp_app_from_ui_patch_set(sp_app_t *app, const LV2_Atom *atom)
823 {
824 const LV2_Atom_Object *obj = ASSUME_ALIGNED(atom);
825
826 const LV2_Atom_URID *subject = NULL;
827 const LV2_Atom_Int *seqn = NULL;
828 const LV2_Atom_URID *property = NULL;
829 const LV2_Atom *value = NULL;
830
831 lv2_atom_object_get(obj,
832 app->regs.patch.subject.urid, &subject,
833 app->regs.patch.sequence_number.urid, &seqn,
834 app->regs.patch.property.urid, &property,
835 app->regs.patch.value.urid, &value,
836 0);
837
838 const LV2_URID subj = subject && (subject->atom.type == app->forge.URID)
839 ? subject->body : 0;
840 const int32_t sn = seqn && (seqn->atom.type == app->forge.Int)
841 ? seqn->body : 0;
842 const LV2_URID prop = property && (property->atom.type == app->forge.URID)
843 ? property->body : 0;
844
845 if(subj && prop && value) // is for a module
846 {
847 //printf("got patch:Set: %s\n", app->driver->unmap->unmap(app->driver->unmap->handle, prop));
848
849 mod_t *mod = _mod_find_by_urn(app, subj);
850 if(mod)
851 {
852 if( (prop == app->regs.synthpod.module_position_x.urid)
853 && (value->type == app->forge.Float) )
854 {
855 mod->pos.x = ((const LV2_Atom_Float *)value)->body;
856 _sp_app_order(app);
857 }
858 else if( (prop == app->regs.synthpod.module_position_y.urid)
859 && (value->type == app->forge.Float) )
860 {
861 mod->pos.y = ((const LV2_Atom_Float *)value)->body;
862 _sp_app_order(app);
863 }
864 else if( (prop == app->regs.synthpod.module_alias.urid)
865 && (value->type == app->forge.String) )
866 {
867 strncpy(mod->alias, LV2_ATOM_BODY_CONST(value), ALIAS_MAX - 1);
868 }
869 else if( (prop == app->regs.ui.ui.urid)
870 && (value->type == app->forge.URID) )
871 {
872 mod->ui = ((const LV2_Atom_URID *)value)->body;
873 }
874 else if( (prop == app->regs.pset.preset.urid)
875 && (value->type == app->forge.URID) )
876 {
877 if(app->block_state == BLOCKING_STATE_RUN)
878 {
879 const bool needs_ramping = _mod_needs_ramping(mod, RAMP_STATE_DOWN_DRAIN, true);
880 app->silence_state = !needs_ramping
881 ? SILENCING_STATE_RUN
882 : SILENCING_STATE_BLOCK;
883
884 // send request to worker thread
885 size_t size = sizeof(job_t);
886 job_t *job = _sp_app_to_worker_request(app, size);
887 if(job)
888 {
889 app->block_state = BLOCKING_STATE_DRAIN; // wait for drain
890
891 job->request = JOB_TYPE_REQUEST_DRAIN;
892 job->status = 0;
893 _sp_app_to_worker_advance(app, size);
894 }
895 else
896 {
897 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
898 }
899 }
900 else if(app->block_state == BLOCKING_STATE_BLOCK)
901 {
902 if(app->silence_state == SILENCING_STATE_BLOCK)
903 return false; // not fully silenced yet, wait
904
905 // send request to worker thread
906 const LV2_URID pset_urn = ((const LV2_Atom_URID *)value)->body;
907 size_t size = sizeof(job_t);
908 job_t *job = _sp_app_to_worker_request(app, size);
909 if(job)
910 {
911 app->block_state = BLOCKING_STATE_WAIT; // wait for job
912 mod->bypassed = mod->needs_bypassing;
913
914 job->request = JOB_TYPE_REQUEST_PRESET_LOAD;
915 job->mod = mod;
916 job->urn = pset_urn;
917 _sp_app_to_worker_advance(app, size);
918
919 return true; // advance
920 }
921 else
922 {
923 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
924 }
925 }
926 }
927 else if( (prop == app->regs.idisp.surface.urid)
928 && (value->type == app->forge.Bool) )
929 {
930 mod->idisp.subscribed = ((const LV2_Atom_Bool *)value)->body;
931
932 if(mod->idisp.iface && mod->idisp.subscribed)
933 {
934 _sp_app_mod_queue_draw(mod); // trigger update
935 }
936 }
937 else if( (prop == app->regs.synthpod.module_reinstantiate.urid)
938 && (value->type == app->forge.Bool) )
939 {
940 if(app->block_state == BLOCKING_STATE_RUN)
941 {
942 const bool needs_ramping = _mod_needs_ramping(mod, RAMP_STATE_DOWN_DRAIN, true);
943 app->silence_state = !needs_ramping
944 ? SILENCING_STATE_RUN
945 : SILENCING_STATE_BLOCK;
946
947 // send request to worker thread
948 size_t size = sizeof(job_t);
949 job_t *job = _sp_app_to_worker_request(app, size);
950 if(job)
951 {
952 app->block_state = BLOCKING_STATE_DRAIN; // wait for drain
953
954 job->request = JOB_TYPE_REQUEST_DRAIN;
955 job->status = 0;
956 _sp_app_to_worker_advance(app, size);
957 }
958 else
959 {
960 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
961 }
962 }
963 else if(app->block_state == BLOCKING_STATE_BLOCK)
964 {
965 if(app->silence_state == SILENCING_STATE_BLOCK)
966 return false; // not fully silenced yet, wait
967
968 // send request to worker thread
969 const LV2_URID pset_urn = ((const LV2_Atom_URID *)value)->body;
970 size_t size = sizeof(job_t);
971 job_t *job = _sp_app_to_worker_request(app, size);
972 if(job)
973 {
974 app->block_state = BLOCKING_STATE_WAIT; // wait for job
975 mod->bypassed = mod->needs_bypassing;
976
977 job->request = JOB_TYPE_REQUEST_MODULE_REINSTANTIATE;
978 job->mod = mod;
979 _sp_app_to_worker_advance(app, size);
980
981 return true; // advance
982 }
983 else
984 {
985 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
986 }
987 }
988 }
989 }
990
991 //TODO handle more properties
992 }
993 else if(prop && value)// is for host
994 {
995 if( (prop == app->regs.synthpod.graph_position_x.urid)
996 && (value->type == app->forge.Float) )
997 {
998 app->pos.x = ((const LV2_Atom_Float *)value)->body;
999 }
1000 else if( (prop == app->regs.synthpod.graph_position_y.urid)
1001 && (value->type == app->forge.Float) )
1002 {
1003 app->pos.y = ((const LV2_Atom_Float *)value)->body;
1004 }
1005 else if( (prop == app->regs.synthpod.column_enabled.urid)
1006 && (value->type == app->forge.Bool) )
1007 {
1008 app->column_enabled = ((const LV2_Atom_Bool *)value)->body;
1009 }
1010 else if( (prop == app->regs.synthpod.row_enabled.urid)
1011 && (value->type == app->forge.Bool) )
1012 {
1013 app->row_enabled = ((const LV2_Atom_Bool *)value)->body;
1014 }
1015 }
1016
1017 return advance_ui[app->block_state];
1018 }
1019
1020 __realtime static bool
_sp_app_from_ui_patch_copy(sp_app_t * app,const LV2_Atom * atom)1021 _sp_app_from_ui_patch_copy(sp_app_t *app, const LV2_Atom *atom)
1022 {
1023 const LV2_Atom_Object *obj = ASSUME_ALIGNED(atom);
1024
1025 const LV2_Atom_URID *subject = NULL;
1026 const LV2_Atom_Int *seqn = NULL;
1027 const LV2_Atom_URID *destination = NULL;
1028
1029 lv2_atom_object_get(obj,
1030 app->regs.patch.subject.urid, &subject,
1031 app->regs.patch.sequence_number.urid, &seqn,
1032 app->regs.patch.destination.urid, &destination,
1033 0);
1034
1035 const LV2_URID subj = subject && (subject->atom.type == app->forge.URID)
1036 ? subject->body : 0;
1037 const int32_t sn = seqn && (seqn->atom.type == app->forge.Int)
1038 ? seqn->body : 0;
1039 const LV2_URID dest = destination && (destination->atom.type == app->forge.URID)
1040 ? destination->body : 0;
1041
1042 if(!subj && dest) // save bundle to dest
1043 {
1044 if(app->block_state == BLOCKING_STATE_RUN)
1045 {
1046 // send request to worker thread
1047 size_t size = sizeof(job_t);
1048 job_t *job = _sp_app_to_worker_request(app, size);
1049 if(job)
1050 {
1051 app->block_state = BLOCKING_STATE_DRAIN; // wait for drain
1052
1053 job->request = JOB_TYPE_REQUEST_DRAIN;
1054 job->status = 0;
1055 _sp_app_to_worker_advance(app, size);
1056 }
1057 else
1058 {
1059 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
1060 }
1061 }
1062 else if(app->block_state == BLOCKING_STATE_BLOCK)
1063 {
1064 // send request to worker thread
1065 size_t size = sizeof(job_t);
1066 job_t *job = _sp_app_to_worker_request(app, size);
1067 if(job)
1068 {
1069 app->block_state = BLOCKING_STATE_WAIT; // wait for job
1070
1071 job->request = JOB_TYPE_REQUEST_BUNDLE_SAVE;
1072 job->status = -1; // TODO for what for?
1073 job->urn = dest;
1074 _sp_app_to_worker_advance(app, size);
1075
1076 return true; // advance
1077 }
1078 else
1079 {
1080 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
1081 }
1082 }
1083 }
1084 else if(subj && !dest) // copy bundle from subj
1085 {
1086 if(app->block_state == BLOCKING_STATE_RUN)
1087 {
1088 //FIXME ramp down system outputs
1089
1090 // send request to worker thread
1091 size_t size = sizeof(job_t);
1092 job_t *job = _sp_app_to_worker_request(app, size);
1093 if(job)
1094 {
1095 app->block_state = BLOCKING_STATE_DRAIN; // wait for drain
1096
1097 job->request = JOB_TYPE_REQUEST_DRAIN;
1098 job->status = 0;
1099 _sp_app_to_worker_advance(app, size);
1100 }
1101 else
1102 {
1103 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
1104 }
1105 }
1106 else if(app->block_state == BLOCKING_STATE_BLOCK)
1107 {
1108 //FIXME ramp up system outputs
1109
1110 // send request to worker thread
1111 job_t *job = _sp_app_to_worker_request(app, sizeof(job_t));
1112 if(job)
1113 {
1114 app->block_state = BLOCKING_STATE_WAIT; // wait for job
1115 app->load_bundle = true; // for sp_app_bypassed
1116
1117 job->request = JOB_TYPE_REQUEST_BUNDLE_LOAD;
1118 job->status = -1; // TODO for what for?
1119 job->urn = subj;
1120 _sp_app_to_worker_advance(app, sizeof(job_t));
1121
1122 return true; // advance
1123 }
1124 else
1125 {
1126 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
1127 }
1128 }
1129 }
1130 else if(subj && dest) // copy preset to dest
1131 {
1132 mod_t *mod = _mod_find_by_urn(app, subj);
1133
1134 if(app->block_state == BLOCKING_STATE_RUN)
1135 {
1136 // send request to worker thread
1137 job_t *job = _sp_app_to_worker_request(app, sizeof(job_t));
1138 if(job)
1139 {
1140 app->block_state = BLOCKING_STATE_DRAIN; // wait for drain
1141
1142 job->request = JOB_TYPE_REQUEST_DRAIN;
1143 job->status = 0;
1144 _sp_app_to_worker_advance(app, sizeof(job_t));
1145 }
1146 else
1147 {
1148 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
1149 }
1150 }
1151 else if(app->block_state == BLOCKING_STATE_BLOCK)
1152 {
1153 // send request to worker thread
1154 job_t *job = _sp_app_to_worker_request(app, sizeof(job_t));
1155 if(job)
1156 {
1157 app->block_state = BLOCKING_STATE_WAIT; // wait for job
1158
1159 job->request = JOB_TYPE_REQUEST_PRESET_SAVE;
1160 job->mod = mod;
1161 job->urn = dest;
1162 _sp_app_to_worker_advance(app, sizeof(job_t));
1163
1164 return true; // advance
1165 }
1166 else
1167 {
1168 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
1169 }
1170 }
1171 }
1172
1173 return advance_ui[app->block_state];
1174 }
1175
1176 __realtime void
_connection_list_add(sp_app_t * app,const LV2_Atom_Object * obj)1177 _connection_list_add(sp_app_t *app, const LV2_Atom_Object *obj)
1178 {
1179 //printf("got patch:add for connectionList:\n");
1180
1181 const LV2_Atom_URID *src_module = NULL;
1182 const LV2_Atom *src_symbol = NULL;
1183 const LV2_Atom_URID *snk_module = NULL;
1184 const LV2_Atom *snk_symbol = NULL;
1185 const LV2_Atom_Float *link_gain = NULL;
1186
1187 lv2_atom_object_get(obj,
1188 app->regs.synthpod.source_module.urid, &src_module,
1189 app->regs.synthpod.source_symbol.urid, &src_symbol,
1190 app->regs.synthpod.sink_module.urid, &snk_module,
1191 app->regs.synthpod.sink_symbol.urid, &snk_symbol,
1192 app->regs.param.gain.urid, &link_gain,
1193 0);
1194
1195 const LV2_URID src_urn = src_module
1196 ? src_module->body : 0;
1197 const char *src_sym = src_symbol
1198 ? LV2_ATOM_BODY_CONST(src_symbol) : NULL;
1199 const LV2_URID snk_urn = snk_module
1200 ? snk_module->body : 0;
1201 const char *snk_sym = snk_symbol
1202 ? LV2_ATOM_BODY_CONST(snk_symbol) : NULL;
1203 const float gain = link_gain
1204 ? link_gain->body : 1.f;
1205
1206 if(src_urn && src_sym && snk_urn && snk_sym)
1207 {
1208 port_t *src_port = _port_find_by_symbol(app, src_urn, src_sym);
1209 port_t *snk_port = _port_find_by_symbol(app, snk_urn, snk_sym);
1210
1211 if(src_port && snk_port)
1212 {
1213 const int32_t state = _sp_app_port_connect(app, src_port, snk_port, gain);
1214 (void)state;
1215
1216 // signal to UI
1217 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
1218 if(answer)
1219 {
1220 LV2_Atom_Forge_Ref ref = synthpod_patcher_add_atom(&app->regs, &app->forge,
1221 0, 0, app->regs.synthpod.connection_list.urid, &obj->atom); //TODO subject
1222 if(ref)
1223 {
1224 _sp_app_to_ui_advance_atom(app, answer);
1225 }
1226 else
1227 {
1228 _sp_app_to_ui_overflow(app);
1229 }
1230 }
1231 else
1232 {
1233 _sp_app_to_ui_overflow(app);
1234 }
1235 }
1236 }
1237 }
1238
1239 __realtime static void
_connection_list_rem(sp_app_t * app,const LV2_Atom_Object * obj)1240 _connection_list_rem(sp_app_t *app, const LV2_Atom_Object *obj)
1241 {
1242 //printf("got patch:remove for connectionList:\n");
1243
1244 const LV2_Atom_URID *src_module = NULL;
1245 const LV2_Atom *src_symbol = NULL;
1246 const LV2_Atom_URID *snk_module = NULL;
1247 const LV2_Atom *snk_symbol = NULL;
1248
1249 lv2_atom_object_get(obj,
1250 app->regs.synthpod.source_module.urid, &src_module,
1251 app->regs.synthpod.source_symbol.urid, &src_symbol,
1252 app->regs.synthpod.sink_module.urid, &snk_module,
1253 app->regs.synthpod.sink_symbol.urid, &snk_symbol,
1254 0);
1255
1256 const LV2_URID src_urn = src_module
1257 ? src_module->body : 0;
1258 const char *src_sym = src_symbol
1259 ? LV2_ATOM_BODY_CONST(src_symbol) : NULL;
1260 const LV2_URID snk_urn = snk_module
1261 ? snk_module->body : 0;
1262 const char *snk_sym = snk_symbol
1263 ? LV2_ATOM_BODY_CONST(snk_symbol) : NULL;
1264
1265 if(src_urn && src_sym && snk_urn && snk_sym)
1266 {
1267 port_t *src_port = _port_find_by_symbol(app, src_urn, src_sym);
1268 port_t *snk_port = _port_find_by_symbol(app, snk_urn, snk_sym);
1269
1270 if(src_port && snk_port)
1271 {
1272 const int32_t state = _sp_app_port_disconnect_request(app, src_port, snk_port, RAMP_STATE_DOWN);
1273 (void)state;
1274
1275 // signal to UI
1276 LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
1277 if(answer)
1278 {
1279 LV2_Atom_Forge_Ref ref = synthpod_patcher_remove_atom(&app->regs, &app->forge,
1280 0, 0, app->regs.synthpod.connection_list.urid, &obj->atom); //TODO subject
1281 if(ref)
1282 {
1283 _sp_app_to_ui_advance_atom(app, answer);
1284 }
1285 else
1286 {
1287 _sp_app_to_ui_overflow(app);
1288 }
1289 }
1290 else
1291 {
1292 _sp_app_to_ui_overflow(app);
1293 }
1294 }
1295 }
1296 }
1297
1298 __realtime void
_node_list_add(sp_app_t * app,const LV2_Atom_Object * obj)1299 _node_list_add(sp_app_t *app, const LV2_Atom_Object *obj)
1300 {
1301 //printf("got patch:add for nodeList:\n");
1302
1303 const LV2_Atom_URID *src_module = NULL;
1304 const LV2_Atom_URID *snk_module = NULL;
1305 const LV2_Atom_Float *pos_x = NULL;
1306 const LV2_Atom_Float *pos_y = NULL;
1307
1308 lv2_atom_object_get(obj,
1309 app->regs.synthpod.source_module.urid, &src_module,
1310 app->regs.synthpod.sink_module.urid, &snk_module,
1311 app->regs.synthpod.node_position_x.urid, &pos_x,
1312 app->regs.synthpod.node_position_y.urid, &pos_y,
1313 0);
1314
1315 const LV2_URID src_urn = src_module
1316 ? src_module->body : 0;
1317 const LV2_URID snk_urn = snk_module
1318 ? snk_module->body : 0;
1319 const float x = pos_x
1320 ? pos_x->body : 0.f;
1321 const float y = pos_y
1322 ? pos_y->body : 0.f;
1323
1324 if(src_urn && snk_urn)
1325 {
1326 mod_t *src_mod = _mod_find_by_urn(app, src_urn);
1327 mod_t *snk_mod = _mod_find_by_urn(app, snk_urn);
1328
1329 if(src_mod && snk_mod)
1330 {
1331 for(unsigned p=0; p<snk_mod->num_ports; p++)
1332 {
1333 port_t *port = &snk_mod->ports[p];
1334
1335 connectable_t *conn = _sp_app_port_connectable(port);
1336 if(conn)
1337 {
1338 for(int j=0; j<conn->num_sources; j++)
1339 {
1340 source_t *source = &conn->sources[j];
1341 port_t *source_port = source->port;
1342
1343 if(source_port->mod == src_mod)
1344 {
1345 source->pos.x = x;
1346 source->pos.y = y;
1347 }
1348 }
1349 }
1350 }
1351 }
1352 }
1353 }
1354
1355 //FIXME _subscription_list_clear, e.g. with patch:wildcard
1356
1357 __realtime static void
_subscription_list_add(sp_app_t * app,const LV2_Atom_Object * obj)1358 _subscription_list_add(sp_app_t *app, const LV2_Atom_Object *obj)
1359 {
1360 //printf("got patch:add for subscriptionList:\n");
1361
1362 const LV2_Atom_URID *src_module = NULL;
1363 const LV2_Atom *src_symbol = NULL;
1364
1365 lv2_atom_object_get(obj,
1366 app->regs.synthpod.sink_module.urid, &src_module,
1367 app->regs.synthpod.sink_symbol.urid, &src_symbol,
1368 0);
1369
1370 const LV2_URID src_urn = src_module
1371 ? src_module->body : 0;
1372 const char *src_sym = src_symbol
1373 ? LV2_ATOM_BODY_CONST(src_symbol) : NULL;
1374
1375 if(src_urn && src_sym)
1376 {
1377 port_t *src_port = _port_find_by_symbol(app, src_urn, src_sym);
1378
1379 if(src_port)
1380 {
1381 src_port->subscriptions += 1;
1382
1383 if(src_port->type == PORT_TYPE_CONTROL)
1384 {
1385 const float *buf_ptr = PORT_BASE_ALIGNED(src_port);
1386 src_port->control.last = *buf_ptr - 0.1; // will force notification
1387 }
1388 }
1389 }
1390 }
1391
1392 __realtime static void
_subscription_list_rem(sp_app_t * app,const LV2_Atom_Object * obj)1393 _subscription_list_rem(sp_app_t *app, const LV2_Atom_Object *obj)
1394 {
1395 //printf("got patch:remove for subscriptionList:\n");
1396
1397 const LV2_Atom_URID *src_module = NULL;
1398 const LV2_Atom *src_symbol = NULL;
1399
1400 lv2_atom_object_get(obj,
1401 app->regs.synthpod.sink_module.urid, &src_module,
1402 app->regs.synthpod.sink_symbol.urid, &src_symbol,
1403 0);
1404
1405 const LV2_URID src_urn = src_module
1406 ? src_module->body : 0;
1407 const char *src_sym = src_symbol
1408 ? LV2_ATOM_BODY_CONST(src_symbol) : NULL;
1409
1410 if(src_urn && src_sym)
1411 {
1412 port_t *src_port = _port_find_by_symbol(app, src_urn, src_sym);
1413
1414 if(src_port)
1415 {
1416 if(src_port->subscriptions > 0)
1417 src_port->subscriptions -= 1;
1418 }
1419 }
1420 }
1421
1422 __realtime static void
_notification_list_add(sp_app_t * app,const LV2_Atom_Object * obj)1423 _notification_list_add(sp_app_t *app, const LV2_Atom_Object *obj)
1424 {
1425 //printf("got patch:add for notificationList:\n");
1426
1427 const LV2_URID src_proto = obj->body.otype;
1428 const LV2_Atom_URID *src_module = NULL;
1429 const LV2_Atom *src_symbol = NULL;
1430 const LV2_Atom *src_value = NULL;
1431
1432 lv2_atom_object_get(obj,
1433 app->regs.synthpod.sink_module.urid, &src_module,
1434 app->regs.synthpod.sink_symbol.urid, &src_symbol,
1435 app->regs.rdf.value.urid, &src_value,
1436 0);
1437
1438 const LV2_URID src_urn = src_module
1439 ? src_module->body : 0;
1440 const char *src_sym = src_symbol
1441 ? LV2_ATOM_BODY_CONST(src_symbol) : NULL;
1442
1443 if(src_urn && src_sym && src_value)
1444 {
1445 port_t *src_port = _port_find_by_symbol(app, src_urn, src_sym);
1446
1447 if(src_port)
1448 {
1449 if( (src_proto == app->regs.port.float_protocol.urid)
1450 && (src_value->type == app->forge.Float) )
1451 {
1452 const float val = ((const LV2_Atom_Float *)src_value)->body;
1453 float *buf_ptr = PORT_BASE_ALIGNED(src_port);
1454
1455 if(src_port->type == PORT_TYPE_CONTROL)
1456 {
1457 *buf_ptr = val;
1458 src_port->control.last = *buf_ptr; // we don't want any notification
1459 src_port->control.auto_dirty = true;
1460 _sp_app_port_control_stash(src_port);
1461 }
1462 else if(src_port->type == PORT_TYPE_CV)
1463 {
1464 for(unsigned i = 0; i < app->driver->max_block_size; i++)
1465 {
1466 buf_ptr[i] = val;
1467 //FIXME omit notification ?
1468 }
1469 }
1470 }
1471 else if( (src_proto == app->regs.port.event_transfer.urid)
1472 && (src_port->type == PORT_TYPE_ATOM) )
1473 {
1474 //printf("got atom:eventTransfer\n");
1475
1476 // messages from UI are ALWAYS appended to default port buffer, no matter
1477 // how many sources the port may have
1478 const uint32_t capacity = PORT_SIZE(src_port);
1479 LV2_Atom_Sequence *seq = PORT_BASE_ALIGNED(src_port);
1480
1481 const LV2_Atom_Event *dummy = (const void *)src_value - offsetof(LV2_Atom_Event, body);
1482 LV2_Atom_Event *ev = lv2_atom_sequence_append_event(seq, capacity, dummy);
1483 if(ev)
1484 ev->time.frames = 0;
1485 else
1486 sp_app_log_trace(app, "%s: failed to append\n", __func__);
1487
1488 //FIXME handle output automation
1489 }
1490 else if( (src_proto == app->regs.port.atom_transfer.urid)
1491 && (src_port->type == PORT_TYPE_ATOM) )
1492 {
1493 //printf("got atom:atomTransfer\n");
1494 LV2_Atom *atom = PORT_BASE_ALIGNED(src_port);
1495 //FIXME memcpy(atom, src_value, lv2_atom_total-size(src_value));
1496 }
1497 }
1498 }
1499 }
1500
1501 __realtime static void
_automation_list_rem_internal(port_t * port,LV2_URID prop)1502 _automation_list_rem_internal(port_t *port, LV2_URID prop)
1503 {
1504 mod_t *mod = port->mod;
1505
1506 for(unsigned i = 0; i < MAX_AUTOMATIONS; i++)
1507 {
1508 auto_t *automation = &mod->automations[i];
1509
1510 if(automation->type == AUTO_TYPE_NONE)
1511 continue; // ignore
1512
1513 if(!prop && (automation->index == port->index))
1514 automation->type = AUTO_TYPE_NONE; // invalidate
1515 else if(prop && (automation->property == prop) )
1516 automation->type = AUTO_TYPE_NONE; // invalidate
1517 }
1518 }
1519
1520 __realtime static port_t *
_automation_port_find(mod_t * mod,const char * src_sym,LV2_URID src_prop)1521 _automation_port_find(mod_t *mod, const char *src_sym, LV2_URID src_prop)
1522 {
1523 for(unsigned p = 0; p < mod->num_ports; p++)
1524 {
1525 port_t *port = &mod->ports[p];
1526
1527 if(src_sym)
1528 {
1529 if( (port->type == PORT_TYPE_CONTROL) && !strcmp(port->symbol, src_sym) )
1530 return port;
1531 }
1532 else if(src_prop)
1533 {
1534 if( (port->type == PORT_TYPE_ATOM) && port->atom.patchable)
1535 return port;
1536 }
1537 }
1538
1539 return NULL;
1540 }
1541
1542 __realtime static void
_automation_list_rem(sp_app_t * app,const LV2_Atom_Object * obj)1543 _automation_list_rem(sp_app_t *app, const LV2_Atom_Object *obj)
1544 {
1545 //printf("got patch:remove for automationList:\n");
1546
1547 const LV2_Atom_URID *src_module = NULL;
1548 const LV2_Atom *src_symbol = NULL;
1549 const LV2_Atom_URID *src_property = NULL;
1550
1551 lv2_atom_object_get(obj,
1552 app->regs.synthpod.sink_module.urid, &src_module,
1553 app->regs.synthpod.sink_symbol.urid, &src_symbol,
1554 app->regs.patch.property.urid, &src_property,
1555 0);
1556
1557 const LV2_URID src_urn = src_module
1558 ? src_module->body : 0;
1559 const char *src_sym = src_symbol
1560 ? LV2_ATOM_BODY_CONST(src_symbol) : NULL;
1561 const LV2_URID src_prop = src_property
1562 ? src_property->body : 0;
1563
1564 mod_t *mod = _mod_find_by_urn(app, src_urn);
1565 if(mod)
1566 {
1567 port_t *port = _automation_port_find(mod, src_sym, src_prop);
1568 if(port)
1569 {
1570 _automation_list_rem_internal(port, src_prop);
1571 }
1572 }
1573 }
1574
1575 __realtime void
_automation_refresh_mul_add(auto_t * automation)1576 _automation_refresh_mul_add(auto_t *automation)
1577 {
1578 const double div = automation->b - automation->a;
1579 automation->mul = div
1580 ? (automation->d - automation->c) / div
1581 : 0.0;
1582 automation->add = div
1583 ? (automation->c*automation->b - automation->a*automation->d) / div
1584 : 0.0;
1585 }
1586
1587 __realtime void
_automation_list_add(sp_app_t * app,const LV2_Atom_Object * obj)1588 _automation_list_add(sp_app_t *app, const LV2_Atom_Object *obj)
1589 {
1590 //printf("got patch:add for automationList:\n");
1591
1592 const LV2_Atom_URID *src_module = NULL;
1593 const LV2_Atom *src_symbol = NULL;
1594 const LV2_Atom_URID *src_property = NULL;
1595 const LV2_Atom_URID *src_range = NULL;
1596 const LV2_Atom_Int *src_channel = NULL;
1597 const LV2_Atom_Int *src_controller = NULL;
1598 const LV2_Atom_String *src_path = NULL;
1599 const LV2_Atom_Double *src_min = NULL;
1600 const LV2_Atom_Double *src_max = NULL;
1601 const LV2_Atom_Double *snk_min = NULL;
1602 const LV2_Atom_Double *snk_max = NULL;
1603 const LV2_Atom_Bool *src_enabled = NULL;
1604 const LV2_Atom_Bool *snk_enabled = NULL;
1605 const LV2_Atom_Bool *is_learning = NULL;
1606
1607 lv2_atom_object_get(obj,
1608 app->regs.synthpod.sink_module.urid, &src_module,
1609 app->regs.synthpod.sink_symbol.urid, &src_symbol,
1610 app->regs.patch.property.urid, &src_property,
1611 app->regs.rdfs.range.urid, &src_range,
1612 app->regs.midi.channel.urid, &src_channel,
1613 app->regs.midi.controller_number.urid, &src_controller,
1614 app->regs.osc.path.urid, &src_path,
1615 app->regs.synthpod.source_min.urid, &src_min,
1616 app->regs.synthpod.source_max.urid, &src_max,
1617 app->regs.synthpod.sink_min.urid, &snk_min,
1618 app->regs.synthpod.sink_max.urid, &snk_max,
1619 app->regs.synthpod.source_enabled.urid, &src_enabled,
1620 app->regs.synthpod.sink_enabled.urid, &snk_enabled,
1621 app->regs.synthpod.learning.urid, &is_learning,
1622 0);
1623
1624 const LV2_URID src_urn = src_module
1625 ? src_module->body : 0;
1626 const char *src_sym = src_symbol
1627 ? LV2_ATOM_BODY_CONST(src_symbol) : NULL;
1628 const LV2_URID src_prop = src_property
1629 ? src_property->body : 0;
1630 const LV2_URID src_ran = src_range
1631 ? src_range->body : 0;
1632
1633 mod_t *mod = _mod_find_by_urn(app, src_urn);
1634 if(mod)
1635 {
1636 port_t *port = _automation_port_find(mod, src_sym, src_prop);
1637 if(port)
1638 {
1639 _automation_list_rem_internal(port, src_prop); // remove any previously registered automation
1640
1641 for(unsigned i = 0; i < MAX_AUTOMATIONS; i++)
1642 {
1643 auto_t *automation = &mod->automations[i];
1644
1645 if(automation->type != AUTO_TYPE_NONE)
1646 continue; // search empty slot
1647
1648 // fill slot
1649 automation->index = port->index;
1650 automation->property = src_prop;
1651 automation->range = src_ran;
1652
1653 automation->a = src_min ? src_min->body : 0.0;
1654 automation->b = src_max ? src_max->body : 0.0;
1655 automation->c = snk_min ? snk_min->body : 0.0;
1656 automation->d = snk_max ? snk_max->body : 0.0;
1657 automation->src_enabled = src_enabled ? src_enabled->body : false;
1658 automation->snk_enabled = snk_enabled ? snk_enabled->body : false;
1659 automation->learning = is_learning ? is_learning->body : false;
1660
1661 _automation_refresh_mul_add(automation);
1662
1663 if(obj->body.otype == app->regs.midi.Controller.urid)
1664 {
1665 automation->type = AUTO_TYPE_MIDI;
1666 automation->midi.channel = src_channel ? src_channel->body : -1;
1667 automation->midi.controller = src_controller ? src_controller->body : -1;
1668 }
1669 else if(obj->body.otype == app->regs.osc.message.urid)
1670 {
1671 automation->type = AUTO_TYPE_OSC;
1672 if(src_path)
1673 strncpy(automation->osc.path, LV2_ATOM_BODY_CONST(src_path), 256);
1674 else
1675 automation->osc.path[0] = '\0';
1676 }
1677
1678 break;
1679 }
1680 }
1681 }
1682 }
1683
1684 __realtime static void
_mod_list_add(sp_app_t * app,const LV2_Atom_URID * urid)1685 _mod_list_add(sp_app_t *app, const LV2_Atom_URID *urid)
1686 {
1687 //printf("got patch:add for moduleList: %s\n", uri);
1688
1689 // send request to worker thread
1690 const size_t size = sizeof(job_t);
1691 job_t *job = _sp_app_to_worker_request(app, size);
1692 if(job)
1693 {
1694 job->request = JOB_TYPE_REQUEST_MODULE_ADD;
1695 job->status = 0;
1696 job->urn = urid->body;
1697 _sp_app_to_worker_advance(app, size);
1698 }
1699 else
1700 {
1701 sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
1702 }
1703 }
1704
1705 __realtime static void
_mod_list_rem(sp_app_t * app,const LV2_Atom_URID * urn)1706 _mod_list_rem(sp_app_t *app, const LV2_Atom_URID *urn)
1707 {
1708 const char *uri = app->driver->unmap->unmap(app->driver->unmap->handle, urn->body);
1709 //printf("got patch:remove for moduleList: %s\n", uri);
1710
1711 // search mod according to its URN
1712 mod_t *mod = _mod_find_by_urn(app, urn->body);
1713 if(!mod) // mod not found
1714 return;
1715
1716 int needs_ramping = 0;
1717 for(unsigned p1=0; p1<mod->num_ports; p1++)
1718 {
1719 port_t *port = &mod->ports[p1];
1720
1721 connectable_t *conn = _sp_app_port_connectable(port);
1722 if(conn)
1723 {
1724 // disconnect sources
1725 for(int s=0; s<conn->num_sources; s++)
1726 {
1727 _sp_app_port_disconnect_request(app,
1728 conn->sources[s].port, port, RAMP_STATE_DOWN);
1729 }
1730 }
1731
1732 // disconnect sinks
1733 for(unsigned m=0; m<app->num_mods; m++)
1734 {
1735 for(unsigned p2=0; p2<app->mods[m]->num_ports; p2++)
1736 {
1737 needs_ramping += _sp_app_port_disconnect_request(app,
1738 port, &app->mods[m]->ports[p2], RAMP_STATE_DOWN_DEL);
1739 }
1740 }
1741 }
1742 if(needs_ramping == 0)
1743 _sp_app_mod_eject(app, mod);
1744 }
1745
1746 __realtime static bool
_sp_app_from_ui_patch_patch(sp_app_t * app,const LV2_Atom * atom)1747 _sp_app_from_ui_patch_patch(sp_app_t *app, const LV2_Atom *atom)
1748 {
1749 const LV2_Atom_Object *obj = ASSUME_ALIGNED(atom);
1750
1751 const LV2_Atom_URID *subject = NULL;
1752 const LV2_Atom_Int *seqn = NULL;
1753 const LV2_Atom_Object *add = NULL;
1754 const LV2_Atom_Object *rem = NULL;
1755
1756 lv2_atom_object_get(obj,
1757 app->regs.patch.subject.urid, &subject,
1758 app->regs.patch.sequence_number.urid, &seqn,
1759 app->regs.patch.add.urid, &add,
1760 app->regs.patch.remove.urid, &rem,
1761 0);
1762
1763 const LV2_URID subj = subject && (subject->atom.type == app->forge.URID)
1764 ? subject->body : 0; //FIXME check for
1765 const int32_t sn = seqn && (seqn->atom.type == app->forge.Int)
1766 ? seqn->body : 0;
1767
1768 //printf("got patch:Patch: %s\n", app->driver->unmap->unmap(app->driver->unmap->handle, subj));
1769
1770 if( add && (add->atom.type == app->forge.Object)
1771 && rem && (rem->atom.type == app->forge.Object) )
1772 {
1773 LV2_ATOM_OBJECT_FOREACH(rem, prop)
1774 {
1775 //printf("got patch:remove: %s\n", app->driver->unmap->unmap(app->driver->unmap->handle, prop->key));
1776
1777 if( (prop->key == app->regs.synthpod.connection_list.urid)
1778 && (prop->value.type == app->forge.Object) )
1779 {
1780 _connection_list_rem(app, (const LV2_Atom_Object *)&prop->value);
1781 }
1782 else if( (prop->key == app->regs.synthpod.node_list.urid)
1783 && (prop->value.type == app->forge.Object) )
1784 {
1785 //FIXME never reached
1786 }
1787 else if( (prop->key == app->regs.synthpod.subscription_list.urid)
1788 && (prop->value.type == app->forge.Object) )
1789 {
1790 _subscription_list_rem(app, (const LV2_Atom_Object *)&prop->value);
1791 }
1792 else if( (prop->key == app->regs.synthpod.notification_list.urid)
1793 && (prop->value.type == app->forge.Object) )
1794 {
1795 //FIXME never reached
1796 }
1797 else if( (prop->key == app->regs.synthpod.module_list.urid)
1798 && (prop->value.type == app->forge.URID) )
1799 {
1800 _mod_list_rem(app, (const LV2_Atom_URID *)&prop->value);
1801 }
1802 else if( (prop->key == app->regs.synthpod.automation_list.urid)
1803 && (prop->value.type == app->forge.Object) )
1804 {
1805 _automation_list_rem(app, (const LV2_Atom_Object *)&prop->value);
1806 }
1807 }
1808
1809 LV2_ATOM_OBJECT_FOREACH(add, prop)
1810 {
1811 //printf("got patch:add: %s\n", app->driver->unmap->unmap(app->driver->unmap->handle, prop->key));
1812
1813 if( (prop->key == app->regs.synthpod.connection_list.urid)
1814 && (prop->value.type == app->forge.Object) )
1815 {
1816 _connection_list_add(app, (const LV2_Atom_Object *)&prop->value);
1817 }
1818 else if( (prop->key == app->regs.synthpod.node_list.urid)
1819 && (prop->value.type == app->forge.Object) )
1820 {
1821 _node_list_add(app, (const LV2_Atom_Object *)&prop->value);
1822 }
1823 else if( (prop->key == app->regs.synthpod.subscription_list.urid)
1824 && (prop->value.type == app->forge.Object) )
1825 {
1826 _subscription_list_add(app, (const LV2_Atom_Object *)&prop->value);
1827 }
1828 else if( (prop->key == app->regs.synthpod.notification_list.urid)
1829 && (prop->value.type == app->forge.Object) )
1830 {
1831 _notification_list_add(app, (const LV2_Atom_Object *)&prop->value);
1832 }
1833 else if( (prop->key == app->regs.synthpod.module_list.urid)
1834 && (prop->value.type == app->forge.URID) )
1835 {
1836 _mod_list_add(app, (const LV2_Atom_URID *)&prop->value);
1837 }
1838 else if( (prop->key == app->regs.synthpod.automation_list.urid)
1839 && (prop->value.type == app->forge.Object) )
1840 {
1841 _automation_list_add(app, (const LV2_Atom_Object *)&prop->value);
1842 }
1843 }
1844 }
1845
1846 return advance_ui[app->block_state];
1847 }
1848
1849 bool
sp_app_from_ui(sp_app_t * app,const LV2_Atom * atom)1850 sp_app_from_ui(sp_app_t *app, const LV2_Atom *atom)
1851 {
1852 if(!advance_ui[app->block_state])
1853 return false; // we are draining or waiting
1854
1855 const LV2_Atom_Object *obj = ASSUME_ALIGNED(atom);
1856 //printf("%s\n", app->driver->unmap->unmap(app->driver->unmap->handle, obj->body.otype));
1857
1858 if(lv2_atom_forge_is_object_type(&app->forge, obj->atom.type))
1859 {
1860 if(obj->body.otype == app->regs.patch.get.urid)
1861 return _sp_app_from_ui_patch_get(app, &obj->atom);
1862 else if(obj->body.otype == app->regs.patch.set.urid)
1863 return _sp_app_from_ui_patch_set(app, &obj->atom);
1864 else if(obj->body.otype == app->regs.patch.copy.urid)
1865 return _sp_app_from_ui_patch_copy(app, &obj->atom);
1866 else if(obj->body.otype == app->regs.patch.patch.urid)
1867 return _sp_app_from_ui_patch_patch(app, &obj->atom);
1868 else
1869 sp_app_log_trace(app, "%s: unknown object type\n", __func__);
1870 }
1871 else
1872 sp_app_log_trace(app, "%s: not an atom object\n", __func__);
1873
1874 return advance_ui[app->block_state];
1875 }
1876