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