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 void *
__sp_worker_to_app_request(sp_app_t * app,size_t minimum,size_t * maximum)22 __sp_worker_to_app_request(sp_app_t *app, size_t minimum, size_t *maximum)
23 {
24 	if(app->driver->to_app_request)
25 		return app->driver->to_app_request(minimum, maximum, app->data);
26 
27 	sp_app_log_error(app, "%s: buffer request failed\n", __func__);
28 	return NULL;
29 }
30 #define _sp_worker_to_app_request(APP, MINIMUM) \
31 	ASSUME_ALIGNED(__sp_worker_to_app_request((APP), (MINIMUM), NULL))
32 #define _sp_worker_to_app_request_max(APP, MINIMUM, MAXIMUM) \
33 	ASSUME_ALIGNED(__sp_worker_to_app_request((APP), (MINIMUM), (MAXIMUM)))
34 
35 static inline void
_sp_worker_to_app_advance(sp_app_t * app,size_t size)36 _sp_worker_to_app_advance(sp_app_t *app, size_t size)
37 {
38 	if(app->driver->to_app_advance)
39 		app->driver->to_app_advance(size, app->data);
40 	else
41 		sp_app_log_error(app, "%s: buffer advance failed\n", __func__);
42 }
43 
44 bool
sp_app_from_worker(sp_app_t * app,uint32_t len,const void * data)45 sp_app_from_worker(sp_app_t *app, uint32_t len, const void *data)
46 {
47 	if(!advance_work[app->block_state])
48 		return false; // we are blocking
49 
50 	const job_t *job = ASSUME_ALIGNED(data);
51 
52 	switch(job->reply)
53 	{
54 		case JOB_TYPE_REPLY_MODULE_SUPPORTED:
55 		{
56 #if 0
57 			//signal to UI
58 			size_t size = sizeof(transmit_module_supported_t)
59 				+ lv2_atom_pad_size(strlen(job->uri) + 1);
60 			transmit_module_supported_t *trans = _sp_app_to_ui_request(app, size);
61 			if(trans)
62 			{
63 				_sp_transmit_module_supported_fill(&app->regs, &app->forge, trans, size,
64 					job->status, job->uri);
65 				_sp_app_to_ui_advance(app, size);
66 			}
67 #endif
68 
69 			break;
70 		}
71 		case JOB_TYPE_REPLY_MODULE_ADD:
72 		{
73 			mod_t *mod = job->mod;
74 
75 			if(app->num_mods >= MAX_MODS)
76 				break; //TODO delete mod
77 
78 			// inject module into module graph
79 			app->mods[app->num_mods] = app->mods[app->num_mods-1]; // system sink
80 			app->mods[app->num_mods-1] = mod;
81 			app->num_mods += 1;
82 
83 			_sp_app_order(app);
84 
85 			//signal to NK
86 			LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
87 			if(answer)
88 			{
89 				LV2_Atom_Forge_Ref ref = synthpod_patcher_add(&app->regs, &app->forge,
90 					0, 0, app->regs.synthpod.module_list.urid, //TODO subject
91 					sizeof(uint32_t), app->forge.URID, &mod->urn);
92 				if(ref)
93 				{
94 					_sp_app_to_ui_advance_atom(app, answer);
95 				}
96 				else
97 				{
98 					_sp_app_to_ui_overflow(app);
99 				}
100 			}
101 			else
102 			{
103 				_sp_app_to_ui_overflow(app);
104 			}
105 
106 			break;
107 		}
108 		case JOB_TYPE_REPLY_MODULE_DEL:
109 		{
110 			const LV2_URID urn = job->urn;
111 
112 			// signal to NK
113 			size_t maximum;
114 			LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
115 			if(answer)
116 			{
117 				LV2_Atom_Forge_Ref ref = synthpod_patcher_remove(&app->regs, &app->forge,
118 					0, 0, app->regs.synthpod.module_list.urid, //TODO subject
119 				 	sizeof(uint32_t), app->forge.URID, &urn);
120 				if(ref)
121 				{
122 					_sp_app_to_ui_advance_atom(app, answer);
123 				}
124 				else
125 				{
126 					_sp_app_to_ui_overflow(app);
127 				}
128 			}
129 			else
130 			{
131 				_sp_app_to_ui_overflow(app);
132 			}
133 
134 			break;
135 		}
136 		case JOB_TYPE_REPLY_MODULE_REINSTANTIATE:
137 		{
138 			mod_t *mod = job->mod;
139 
140 			assert(app->block_state == BLOCKING_STATE_WAIT);
141 			app->block_state = BLOCKING_STATE_RUN; // release block
142 			mod->bypassed = false;
143 
144 			if(app->silence_state == SILENCING_STATE_WAIT)
145 			{
146 				app->silence_state = SILENCING_STATE_RUN;
147 
148 				// ramping
149 				for(unsigned p1=0; p1<mod->num_ports; p1++)
150 				{
151 					port_t *port = &mod->ports[p1];
152 
153 					// desilence sinks
154 					for(unsigned m=0; m<app->num_mods; m++)
155 					{
156 						for(unsigned p2=0; p2<app->mods[m]->num_ports; p2++)
157 						{
158 							_sp_app_port_desilence(app, port, &app->mods[m]->ports[p2]);
159 						}
160 					}
161 				}
162 			}
163 
164 			//FIXME signal to ui
165 
166 			break;
167 		}
168 		case JOB_TYPE_REPLY_PRESET_LOAD:
169 		{
170 			//printf("app: preset loaded\n");
171 			mod_t *mod = job->mod;
172 
173 			assert(app->block_state == BLOCKING_STATE_WAIT);
174 			app->block_state = BLOCKING_STATE_RUN; // release block
175 			mod->bypassed = false;
176 
177 			if(app->silence_state == SILENCING_STATE_WAIT)
178 			{
179 				app->silence_state = SILENCING_STATE_RUN;
180 
181 				// ramping
182 				for(unsigned p1=0; p1<mod->num_ports; p1++)
183 				{
184 					port_t *port = &mod->ports[p1];
185 
186 					// desilence sinks
187 					for(unsigned m=0; m<app->num_mods; m++)
188 					{
189 						for(unsigned p2=0; p2<app->mods[m]->num_ports; p2++)
190 						{
191 							_sp_app_port_desilence(app, port, &app->mods[m]->ports[p2]);
192 						}
193 					}
194 				}
195 			}
196 
197 #if 0
198 			//signal to UI
199 			size_t size = sizeof(transmit_module_preset_load_t);
200 			transmit_module_preset_load_t *trans = _sp_app_to_ui_request(app, size);
201 			if(trans)
202 			{
203 				_sp_transmit_module_preset_load_fill(&app->regs, &app->forge, trans, size,
204 					mod->uid, NULL);
205 				_sp_app_to_ui_advance(app, size);
206 			}
207 #endif
208 
209 			break;
210 		}
211 		case JOB_TYPE_REPLY_PRESET_SAVE:
212 		{
213 			//printf("app: preset saved\n");
214 
215 			assert(app->block_state == BLOCKING_STATE_WAIT);
216 			app->block_state = BLOCKING_STATE_RUN; // release block
217 
218 			// signal to NK
219 			size_t maximum;
220 			LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
221 			if(answer)
222 			{
223 				LV2_Atom_Forge_Ref ref = synthpod_patcher_copy(&app->regs, &app->forge,
224 					job->mod->urn, 0, job->urn);
225 				if(ref)
226 				{
227 					_sp_app_to_ui_advance_atom(app, answer);
228 				}
229 				else
230 				{
231 					_sp_app_to_ui_overflow(app);
232 				}
233 			}
234 			else
235 			{
236 				_sp_app_to_ui_overflow(app);
237 			}
238 
239 			break;
240 		}
241 		case JOB_TYPE_REPLY_BUNDLE_LOAD:
242 		{
243 			//printf("app: bundle loaded\n");
244 
245 			assert(app->block_state == BLOCKING_STATE_WAIT);
246 			app->block_state = BLOCKING_STATE_RUN; // releae block
247 			assert(app->load_bundle == true);
248 			app->load_bundle = false; // for sp_app_bypassed
249 
250 #if 0
251 			// signal to UI
252 			size_t size = sizeof(transmit_bundle_load_t)
253 				+ lv2_atom_pad_size(strlen(job->uri) + 1);
254 			transmit_bundle_load_t *trans = _sp_app_to_ui_request(app, size);
255 			if(trans)
256 			{
257 				_sp_transmit_bundle_load_fill(&app->regs, &app->forge, trans, size,
258 					job->status, job->uri);
259 				_sp_app_to_ui_advance(app, size);
260 			}
261 #endif
262 
263 			break;
264 		}
265 		case JOB_TYPE_REPLY_BUNDLE_SAVE:
266 		{
267 			//printf("app: bundle saved\n");
268 
269 			assert(app->block_state == BLOCKING_STATE_WAIT);
270 			app->block_state = BLOCKING_STATE_RUN; // release block
271 			assert(app->load_bundle == false);
272 
273 #if 0
274 			// signal to UI
275 			size_t size = sizeof(transmit_bundle_save_t)
276 				+ lv2_atom_pad_size(strlen(job->uri) + 1);
277 			transmit_bundle_save_t *trans = _sp_app_to_ui_request(app, size);
278 			if(trans)
279 			{
280 				_sp_transmit_bundle_save_fill(&app->regs, &app->forge, trans, size,
281 					job->status, job->uri);
282 				_sp_app_to_ui_advance(app, size);
283 			}
284 #endif
285 
286 			break;
287 		}
288 		case JOB_TYPE_REPLY_DRAIN:
289 		{
290 			assert(app->block_state == BLOCKING_STATE_DRAIN);
291 			app->block_state = BLOCKING_STATE_BLOCK;
292 
293 			break;
294 		}
295 	}
296 
297 	return advance_work[app->block_state];
298 }
299 
300 void
sp_worker_from_app(sp_app_t * app,uint32_t len,const void * data)301 sp_worker_from_app(sp_app_t *app, uint32_t len, const void *data)
302 {
303 	const job_t *job = ASSUME_ALIGNED(data);
304 
305 	switch(job->request)
306 	{
307 		case JOB_TYPE_REQUEST_MODULE_SUPPORTED:
308 		{
309 #if 0
310 			const int32_t status= _sp_app_mod_is_supported(app, job->uri) ? 1 : 0;
311 
312 			// signal to app
313 			size_t job_size = sizeof(job_t) + strlen(job->uri) + 1;
314 			job_t *job1 = _sp_worker_to_app_request(app, job_size);
315 			if(job1)
316 			{
317 				job1->reply = JOB_TYPE_REPLY_MODULE_SUPPORTED;
318 				job1->status = status;
319 				memcpy(job1->uri, job->uri, strlen(job->uri) + 1);
320 				_sp_worker_to_app_advance(app, job_size);
321 			}
322 #endif
323 			break;
324 		}
325 		case JOB_TYPE_REQUEST_MODULE_ADD:
326 		{
327 			const char *uri = app->driver->unmap->unmap(app->driver->unmap->handle, job->urn);
328 			mod_t *mod = uri ? _sp_app_mod_add(app, uri, 0) : NULL;
329 			if(!mod)
330 				break; //TODO report
331 
332 			// signal to app
333 			job_t *job1 = _sp_worker_to_app_request(app, sizeof(job_t));
334 			if(job1)
335 			{
336 				job1->reply = JOB_TYPE_REPLY_MODULE_ADD;
337 				job1->mod = mod;
338 				_sp_worker_to_app_advance(app, sizeof(job_t));
339 			}
340 			else
341 			{
342 				sp_app_log_error(app, "%s: buffer request failed\n", __func__);
343 			}
344 
345 			break;
346 		}
347 		case JOB_TYPE_REQUEST_MODULE_DEL:
348 		{
349 			const LV2_URID urn = job->mod->urn;
350 			int status = _sp_app_mod_del(app, job->mod);
351 
352 			// signal to app
353 			job_t *job1 = _sp_worker_to_app_request(app, sizeof(job_t));
354 			if(job1)
355 			{
356 				job1->reply = JOB_TYPE_REPLY_MODULE_DEL;
357 				job1->urn = urn;
358 				_sp_worker_to_app_advance(app, sizeof(job_t));
359 			}
360 			else
361 			{
362 				sp_app_log_error(app, "%s: buffer request failed\n", __func__);
363 			}
364 
365 			break;
366 		}
367 		case JOB_TYPE_REQUEST_MODULE_REINSTANTIATE:
368 		{
369 			mod_t *mod = job->mod;
370 			if(!mod)
371 				break; //TODO report
372 
373 			_sp_app_mod_reinstantiate(app, mod);
374 
375 			// signal to app
376 			job_t *job1 = _sp_worker_to_app_request(app, sizeof(job_t));
377 			if(job1)
378 			{
379 				job1->reply = JOB_TYPE_REPLY_MODULE_REINSTANTIATE;
380 				job1->mod = job->mod;
381 				_sp_worker_to_app_advance(app, sizeof(job_t));
382 			}
383 			else
384 			{
385 				sp_app_log_error(app, "%s: buffer request failed\n", __func__);
386 			}
387 
388 			break;
389 		}
390 		case JOB_TYPE_REQUEST_PRESET_LOAD:
391 		{
392 			const char *uri = app->driver->unmap->unmap(app->driver->unmap->handle, job->urn);
393 			int status = _sp_app_state_preset_load(app, job->mod, uri, true); (void)status; //FIXME check this
394 
395 			// signal to app
396 			job_t *job1 = _sp_worker_to_app_request(app, sizeof(job_t));
397 			if(job1)
398 			{
399 				job1->reply = JOB_TYPE_REPLY_PRESET_LOAD;
400 				job1->mod = job->mod;
401 				_sp_worker_to_app_advance(app, sizeof(job_t));
402 			}
403 			else
404 			{
405 				sp_app_log_error(app, "%s: buffer request failed\n", __func__);
406 			}
407 
408 			break;
409 		}
410 		case JOB_TYPE_REQUEST_PRESET_SAVE:
411 		{
412 			const char *uri = app->driver->unmap->unmap(app->driver->unmap->handle, job->urn);
413 			int status = _sp_app_state_preset_save(app, job->mod, uri);
414 			(void)status; //FIXME check this
415 
416 			// signal to app
417 			job_t *job1 = _sp_worker_to_app_request(app, sizeof(job_t));
418 			if(job1)
419 			{
420 				job1->reply = JOB_TYPE_REPLY_PRESET_SAVE;
421 				job1->mod = job->mod;
422 				job1->urn = job->urn;
423 				_sp_worker_to_app_advance(app, sizeof(job_t));
424 			}
425 			else
426 			{
427 				sp_app_log_error(app, "%s: buffer request failed\n", __func__);
428 			}
429 
430 			break;
431 		}
432 		case JOB_TYPE_REQUEST_BUNDLE_LOAD:
433 		{
434 			sp_app_bundle_load(app, job->urn, true);
435 
436 			break;
437 		}
438 		case JOB_TYPE_REQUEST_BUNDLE_SAVE:
439 		{
440 			sp_app_bundle_save(app, job->urn, true);
441 
442 			break;
443 		}
444 		case JOB_TYPE_REQUEST_DRAIN:
445 		{
446 			// signal to app
447 			job_t *job1 = _sp_worker_to_app_request(app, sizeof(job_t));
448 			if(job1)
449 			{
450 				job1->reply = JOB_TYPE_REPLY_DRAIN;
451 				job1->status = 0;
452 				_sp_worker_to_app_advance(app, sizeof(job_t));
453 			}
454 			else
455 			{
456 				sp_app_log_error(app, "%s: buffer request failed\n", __func__);
457 			}
458 
459 			break;
460 		}
461 	}
462 }
463 
464 void
sp_app_bundle_load(sp_app_t * app,LV2_URID urn,bool via_app)465 sp_app_bundle_load(sp_app_t *app, LV2_URID urn, bool via_app)
466 {
467 	if(!via_app) //FIXME not rt-safe
468 	{
469 		// manually switch to blocking state and wait for initial bundle to be loaded
470 		app->block_state = BLOCKING_STATE_WAIT; // wait for job
471 		app->load_bundle = true; // for sp_app_bypassed
472 		// TODO keep in sync with synthpod_app_ui
473 	}
474 
475 	const char *uri = app->driver->unmap->unmap(app->driver->unmap->handle, urn);
476 	int status = _sp_app_state_bundle_load(app, uri);
477 	sp_app_log_note(app, "%s: <%s>\n", __func__, uri);
478 
479 	// signal to app
480 	job_t *job1 = _sp_worker_to_app_request(app, sizeof(job_t));
481 	if(job1)
482 	{
483 		job1->reply = JOB_TYPE_REPLY_BUNDLE_LOAD;
484 		job1->status = status;
485 		job1->urn = urn;
486 		_sp_worker_to_app_advance(app, sizeof(job_t));
487 	}
488 	else
489 	{
490 		sp_app_log_error(app, "%s: buffer request failed\n", __func__);
491 	}
492 }
493 
494 void
sp_app_bundle_save(sp_app_t * app,LV2_URID urn,bool via_app)495 sp_app_bundle_save(sp_app_t *app, LV2_URID urn, bool via_app)
496 {
497 	if(!via_app) // FIXME not rt-safe
498 	{
499 		// manually switch to blocking state and wait for bundle to be saved
500 		app->block_state = BLOCKING_STATE_WAIT; // wait for job
501 	}
502 
503 	const char *uri = app->driver->unmap->unmap(app->driver->unmap->handle, urn);
504 	const int status = _sp_app_state_bundle_save(app, uri);
505 	sp_app_log_note(app, "%s: <%s>\n", __func__, uri);
506 
507 	// signal to app
508 	job_t *job1 = _sp_worker_to_app_request(app, sizeof(job_t));
509 	if(job1)
510 	{
511 		job1->reply = JOB_TYPE_REPLY_BUNDLE_SAVE;
512 		job1->status = status;
513 		job1->urn = urn;
514 		_sp_worker_to_app_advance(app, sizeof(job_t));
515 	}
516 	else
517 	{
518 		sp_app_log_error(app, "%s: buffer request failed\n", __func__);
519 	}
520 }
521 
522 void
sp_app_apply(sp_app_t * app,LV2_Atom_Object * obj,char * bundle_path)523 sp_app_apply(sp_app_t *app, LV2_Atom_Object *obj, char *bundle_path)
524 {
525 	//FIXME not rt-safe
526 	// manually switch to blocking state and wait for bundle to be saved
527 	app->block_state = BLOCKING_STATE_WAIT; // wait for job
528 
529 	sp_app_restore(app, sp_app_state_retrieve, obj,
530 		LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE,
531 		sp_app_state_features(app, bundle_path));
532 	sp_app_log_note(app, "%s: <%s>\n", __func__, bundle_path);
533 
534 	const int status = 0; //FIXME
535 	const LV2_URID urn = 0; //FIXME
536 
537 	// signal to app
538 	job_t *job1 = _sp_worker_to_app_request(app, sizeof(job_t));
539 	if(job1)
540 	{
541 		job1->reply = JOB_TYPE_REPLY_BUNDLE_SAVE;
542 		job1->status = status;
543 		job1->urn = urn;
544 		_sp_worker_to_app_advance(app, sizeof(job_t));
545 	}
546 	else
547 	{
548 		sp_app_log_error(app, "%s: buffer request failed\n", __func__);
549 	}
550 }
551