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