Credit Andrew - improve in-tree documentation
[goodguy/cinelerra.git] / cinelerra / pluginlv2.C
1 #ifdef HAVE_LV2
2
3 #include "bctrace.h"
4 #include "bcwindowbase.h"
5 #include "pluginlv2.h"
6 #include "samples.h"
7
8 #include <sys/shm.h>
9 #include <sys/mman.h>
10
11 PluginLV2::PluginLV2()
12 {
13         shm_bfr = 0;
14         use_shm = 1;
15         shmid = -1;
16         in_buffers = 0;   iport = 0;
17         out_buffers = 0;  oport = 0;
18         nb_inputs = 0;
19         nb_outputs = 0;
20         max_bufsz = 0;
21         ui_features = 0;
22
23         samplerate = 44100;
24         refreshrate = 30.;
25         block_length = 4096;
26         midi_buf_size = 8192;
27
28         world = 0;
29         lilv = 0;
30         lilv_uis = 0;
31         inst = 0;
32         sinst = 0;
33         ui_host = 0;
34
35         lv2_InputPort = 0;
36         lv2_OutputPort = 0;
37         lv2_AudioPort = 0;
38         lv2_ControlPort = 0;
39         lv2_CVPort = 0;
40         lv2_Optional = 0;
41         atom_AtomPort = 0;
42         atom_Sequence = 0;
43         powerOf2BlockLength = 0;
44         fixedBlockLength = 0;
45         boundedBlockLength = 0;
46         seq_out = 0;
47
48         worker_thread = 0;
49         memset(&schedule, 0, sizeof(schedule));
50         schedule.handle = (LV2_Worker_Schedule_Handle)this;
51         schedule.schedule_work = lv2_worker_schedule;
52         worker_iface = 0;  worker_done = -1;
53         pthread_mutex_init(&worker_lock, 0);
54         pthread_cond_init(&worker_ready, 0);
55         work_avail = 0;   work_input = 0;
56         work_output = 0;  work_tail = &work_output;
57 }
58
59 PluginLV2::~PluginLV2()
60 {
61         reset_lv2();
62         if( world )     lilv_world_free(world);
63         pthread_mutex_destroy(&worker_lock);
64         pthread_cond_destroy(&worker_ready);
65 }
66
67 void PluginLV2::reset_lv2()
68 {
69         worker_stop();
70         if( inst ) lilv_instance_deactivate(inst);
71         lilv_instance_free(inst);             inst = 0;
72         lilv_uis_free(lilv_uis);              lilv_uis = 0;
73
74         lilv_node_free(lv2_InputPort);        lv2_InputPort = 0;
75         lilv_node_free(lv2_OutputPort);       lv2_OutputPort = 0;
76         lilv_node_free(lv2_AudioPort);        lv2_AudioPort = 0;
77         lilv_node_free(lv2_ControlPort);      lv2_ControlPort = 0;
78         lilv_node_free(lv2_CVPort);           lv2_CVPort = 0;
79
80         lilv_node_free(lv2_Optional);         lv2_Optional = 0;
81         lilv_node_free(atom_AtomPort);        atom_AtomPort = 0;
82         lilv_node_free(atom_Sequence);        atom_Sequence = 0;
83         lilv_node_free(boundedBlockLength);   boundedBlockLength = 0;
84         lilv_node_free(fixedBlockLength);     fixedBlockLength = 0;
85         lilv_node_free(powerOf2BlockLength);  powerOf2BlockLength = 0;
86
87         delete [] (char *)seq_out;            seq_out = 0;
88         uri_table.remove_all_objects();
89         features.remove_all_objects();        ui_features = 0;
90         del_buffer();
91 }
92
93
94 int PluginLV2::load_lv2(const char *path, char *title)
95 {
96         if( !world ) {
97                 world = lilv_world_new();
98                 if( !world ) {
99                         printf("lv2: lilv_world_new failed\n");
100                         return 1;
101                 }
102                 lilv_world_load_all(world);
103         }
104
105         LilvNode *uri = lilv_new_uri(world, path);
106         if( !uri ) {
107                 printf("lv2: lilv_new_uri(%s) failed\n", path);
108                 return 1;
109         }
110
111         const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
112         lilv = lilv_plugins_get_by_uri(all_plugins, uri);
113         lilv_node_free(uri);
114         if( !lilv ) {
115                 printf("lv2: lilv_plugins_get_by_uri (%s) failed\n", path);
116                 return 1;
117         }
118
119         if( title ) {
120                 LilvNode *name = lilv_plugin_get_name(lilv);
121                 const char *nm = lilv_node_as_string(name);
122                 sprintf(title, "L2_%s", nm);
123                 lilv_node_free(name);
124         }
125         return 0;
126 }
127
128 int PluginLV2::init_lv2(PluginLV2ClientConfig &conf, int sample_rate, int bfrsz)
129 {
130         reset_lv2();
131         double bps = 2. * sample_rate / bfrsz;
132         if( bps > refreshrate ) refreshrate = bps;
133
134         lv2_AudioPort       = lilv_new_uri(world, LV2_CORE__AudioPort);
135         lv2_ControlPort     = lilv_new_uri(world, LV2_CORE__ControlPort);
136         lv2_CVPort          = lilv_new_uri(world, LV2_CORE__CVPort);
137         lv2_InputPort       = lilv_new_uri(world, LV2_CORE__InputPort);
138         lv2_OutputPort      = lilv_new_uri(world, LV2_CORE__OutputPort);
139         lv2_Optional        = lilv_new_uri(world, LV2_CORE__connectionOptional);
140         atom_AtomPort       = lilv_new_uri(world, LV2_ATOM__AtomPort);
141         atom_Sequence       = lilv_new_uri(world, LV2_ATOM__Sequence);
142         powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength);
143         fixedBlockLength    = lilv_new_uri(world, LV2_BUF_SIZE__fixedBlockLength);
144         boundedBlockLength  = lilv_new_uri(world, LV2_BUF_SIZE__boundedBlockLength);
145         seq_out = (LV2_Atom_Sequence *) new char[sizeof(LV2_Atom_Sequence) + LV2_SEQ_SIZE];
146
147         conf.init_lv2(lilv, this);
148         nb_inputs = nb_outputs = 0;
149
150         for( int i=0; i<conf.nb_ports; ++i ) {
151                 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
152                 if( !lp ) continue;
153                 int is_input = lilv_port_is_a(lilv, lp, lv2_InputPort);
154                 if( !is_input && !lilv_port_is_a(lilv, lp, lv2_OutputPort) &&
155                     !lilv_port_has_property(lilv, lp, lv2_Optional) ) {
156                         printf("lv2: not input, not output, and not optional: %s\n", conf.names[i]);
157                         continue;
158                 }
159                 if( is_input && lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
160                         conf.append(new PluginLV2Client_Opt(&conf, i));
161                         continue;
162                 }
163                 if( lilv_port_is_a(lilv, lp, lv2_AudioPort) ||
164                     lilv_port_is_a(lilv, lp, lv2_CVPort ) ) {
165                         if( is_input ) ++nb_inputs; else ++nb_outputs;
166                         continue;
167                 }
168         }
169
170
171         uri_map.callback_data = (LV2_URI_Map_Callback_Data)this;
172         uri_map.uri_to_id = uri_to_id;
173         features.append(new Lv2Feature(NS_EXT "uri-map", &uri_map));
174         map.handle = (void*)&uri_table;
175         map.map = uri_table_map;
176         features.append(new Lv2Feature(LV2_URID_MAP_URI, &map));
177         unmap.handle = (void*)&uri_table;
178         unmap.unmap  = uri_table_unmap;
179         features.append(new Lv2Feature(LV2_URID_UNMAP_URI, &unmap));
180         features.append(new Lv2Feature(LV2_BUF_SIZE__powerOf2BlockLength, 0));
181         features.append(new Lv2Feature(LV2_BUF_SIZE__fixedBlockLength,    0));
182         features.append(new Lv2Feature(LV2_BUF_SIZE__boundedBlockLength,  0));
183         features.append(new Lv2Feature(LV2_WORKER__schedule, &schedule));
184
185         if( sample_rate < 64 ) sample_rate = samplerate;
186
187         atom_int   = uri_table.map(LV2_ATOM__Int);
188         atom_float = uri_table.map(LV2_ATOM__Float);
189         param_sampleRate = uri_table.map(LV2_PARAMETERS__sampleRate);
190         bufsz_minBlockLength =  uri_table.map(LV2_BUF_SIZE__minBlockLength);
191         bufsz_maxBlockLength = uri_table.map(LV2_BUF_SIZE__maxBlockLength);
192         bufsz_sequenceSize =  uri_table.map(LV2_BUF_SIZE__sequenceSize);
193         ui_updateRate = uri_table.map(LV2_UI__updateRate);
194
195         samplerate = sample_rate;
196         block_length = bfrsz;
197         options.add(param_sampleRate, sizeof(float), atom_float, &samplerate);
198         options.add(bufsz_minBlockLength, sizeof(int), atom_int, &block_length);
199         options.add(bufsz_maxBlockLength, sizeof(int), atom_int, &block_length);
200         options.add(bufsz_sequenceSize, sizeof(int), atom_int, &midi_buf_size);
201         options.add(ui_updateRate, sizeof(float),  atom_float, &refreshrate);
202         options.add(0, 0, 0, 0);
203
204         features.append(new Lv2Feature(LV2_OPTIONS__options,  &options[0]));
205         features.append(0);
206
207         inst = lilv_plugin_instantiate(lilv, sample_rate, features);
208         if( !inst ) {
209                 printf("lv2: lilv_plugin_instantiate failed\n");
210                 return 1;
211         }
212
213         const LV2_Descriptor *lilv_desc = inst->lv2_descriptor;
214         worker_iface = !lilv_desc->extension_data ? 0 :
215                 (LV2_Worker_Interface*)lilv_desc->extension_data(LV2_WORKER__interface);
216         if( worker_iface )
217                 worker_start();
218
219         lilv_instance_activate(inst);
220 // not sure what to do with these
221         max_bufsz = nb_inputs &&
222                 (lilv_plugin_has_feature(lilv, powerOf2BlockLength) ||
223                  lilv_plugin_has_feature(lilv, fixedBlockLength) ||
224                  lilv_plugin_has_feature(lilv, boundedBlockLength)) ? 4096 : 0;
225         return 0;
226 }
227
228 LV2_URID PluginLV2::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
229 {
230         return ((PluginLV2UriTable *)handle)->map(uri);
231 }
232
233 const char *PluginLV2::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
234 {
235         return ((PluginLV2UriTable *)handle)->unmap(urid);
236 }
237
238 uint32_t PluginLV2::uri_to_id(LV2_URI_Map_Callback_Data callback_data,
239         const char *map, const char *uri)
240 {
241         PluginLV2 *the = (PluginLV2 *)callback_data;
242         return the->map.map(the->uri_table, uri);
243 }
244
245 void PluginLV2::connect_ports(PluginLV2ClientConfig &conf, int ports)
246 {
247         int ich = 0, och = 0;
248         for( int i=0; i<conf.nb_ports; ++i ) {
249                 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
250                 if( !lp ) continue;
251                 int port = conf.ports[i];
252                 if( !(port & ports) ) continue;
253                 if( (port & PORTS_CONTROL) ) {
254                         lilv_instance_connect_port(inst, i, &conf.ctls[i]);
255                         continue;
256                 }
257                 if( (port & PORTS_AUDIO) ) {
258                         if( (port & PORTS_INPUT) ) {
259                                 lilv_instance_connect_port(inst, i, in_buffers[ich]);
260                                 iport[ich++] = i;
261                         }
262                         else if( (port & PORTS_OUTPUT) ) {
263                                 lilv_instance_connect_port(inst, i, out_buffers[och]);
264                                 oport[och++] = i;
265                         }
266                         continue;
267                 }
268                 if( (port & PORTS_ATOM) ) {
269                         if( (port & PORTS_INPUT) )
270                                 lilv_instance_connect_port(inst, i, &seq_in);
271                         else
272                                 lilv_instance_connect_port(inst, i, seq_out);
273                         continue;
274                 }
275         }
276
277         seq_in[0].atom.size = sizeof(LV2_Atom_Sequence_Body);
278         seq_in[0].atom.type = uri_table.map(LV2_ATOM__Sequence);
279         seq_in[1].atom.size = 0;
280         seq_in[1].atom.type = 0;
281         seq_out->atom.size  = LV2_SEQ_SIZE;
282         seq_out->atom.type  = uri_table.map(LV2_ATOM__Chunk);
283 }
284
285 void PluginLV2::del_buffer()
286 {
287         if( shmid >= 0 )
288                 shm_buffer(-1);
289
290         delete [] in_buffers;  in_buffers = 0;
291         delete [] out_buffers;  out_buffers = 0;
292 }
293
294 void PluginLV2::new_buffer(int64_t sz)
295 {
296         uint8_t *bp = 0;
297         if( use_shm ) {  // currently, always uses shm
298                 shmid = shmget(IPC_PRIVATE, sz, IPC_CREAT | 0777);
299                 if( shmid >= 0 ) {
300                         bp = (unsigned char*)shmat(shmid, NULL, 0);
301                         if( bp == (void *) -1 ) { perror("shmat"); bp = 0; }
302                         shmctl(shmid, IPC_RMID, 0); // delete when last ref gone
303                 }
304                 else {
305                         perror("PluginLV2::allocate_buffer: shmget failed\n");
306                         BC_Trace::dump_shm_stats(stdout);
307                 }
308         }
309
310         shm_bfr = (shm_bfr_t *) bp;
311         if( shm_bfr ) shm_bfr->sz = sz;
312 }
313
314 shm_bfr_t *PluginLV2::shm_buffer(int shmid)
315 {
316         if( this->shmid != shmid ) {
317                 if( this->shmid >= 0 ) {
318                         shmdt(shm_bfr);  this->shmid = -1;
319                         shm_bfr = 0;
320                 }
321                 if( shmid >= 0 ) {
322                         shm_bfr = (shm_bfr_t *)shmat(shmid, NULL, 0);
323                         if( shm_bfr == (void *)-1 ) { perror("shmat");  shm_bfr = 0; }
324                         this->shmid = shm_bfr ? shmid : -1;
325                 }
326         }
327         return shm_bfr;
328 }
329
330 void PluginLV2::init_buffer(int samples)
331 {
332         int64_t sz = sizeof(shm_bfr_t) +
333                 sizeof(iport[0])*nb_inputs + sizeof(oport[0])*nb_outputs +
334                 sizeof(*in_buffers[0]) *samples * nb_inputs +
335                 sizeof(*out_buffers[0])*samples * nb_outputs;
336
337         if( shm_bfr ) {
338                 if( shm_bfr->sz < sz ||
339                     shm_bfr->nb_inputs != nb_inputs ||
340                     shm_bfr->nb_outputs != nb_outputs )
341                         del_buffer();
342         }
343
344         if( !shm_bfr )
345                 new_buffer(sz);
346
347         shm_bfr->samples = samples;
348         shm_bfr->done = 0;
349         shm_bfr->nb_inputs = nb_inputs;
350         shm_bfr->nb_outputs = nb_outputs;
351
352         map_buffer();
353 }
354
355 // shm_bfr layout:
356 // struct shm_bfr {
357 //   int64_t sz;
358 //   int samples, done;
359 //   int nb_inputs, nb_outputs;
360 //   int iport[nb_inputs], 
361 //   float in_buffers[samples][nb_inputs];
362 //   int oport[nb_outputs];
363 //   float out_buffers[samples][nb_outputs];
364 // };
365
366 void PluginLV2::map_buffer()
367 {
368         uint8_t *bp = (uint8_t *)(shm_bfr + 1);
369
370         nb_inputs = shm_bfr->nb_inputs;
371         iport = (int *)bp;
372         bp += sizeof(iport[0])*nb_inputs;
373         in_buffers = new float*[nb_inputs];
374         int samples = shm_bfr->samples;
375         for(int i=0; i<nb_inputs; ++i ) {
376                 in_buffers[i] = (float *)bp;
377                 bp += sizeof(*in_buffers[0])*samples;
378         }
379
380         nb_outputs = shm_bfr->nb_outputs;
381         oport = (int *)bp;
382         bp += sizeof(oport[0])*nb_outputs;
383         out_buffers = new float*[nb_outputs];
384         for( int i=0; i<nb_outputs; ++i ) {
385                 out_buffers[i] = (float *)bp;
386                 bp += sizeof(*out_buffers[0])*samples;
387         }
388 }
389
390
391 // LV2 Worker
392
393 PluginLV2Work::PluginLV2Work()
394 {
395         next = 0;
396         alloc = used = 0;
397         data = 0;
398 }
399
400 PluginLV2Work::~PluginLV2Work()
401 {
402         delete [] data;
403 }
404
405 void PluginLV2Work::load(const void *vp, unsigned size)
406 {
407         if( alloc < size ) {
408                 delete [] data;
409                 data = new char[alloc=size];
410         }
411         memcpy(data, vp, used=size);
412 }
413
414 PluginLV2Work *PluginLV2::get_work()
415 {
416 // must hold worker_lock
417         if( !work_avail ) return new PluginLV2Work();
418         PluginLV2Work *wp = work_avail;
419         work_avail = wp->next;  wp->next = 0;
420         return wp;
421 }
422
423 void *PluginLV2::worker_func()
424 {
425         pthread_mutex_lock(&worker_lock);
426         for(;;) {
427                 while( !worker_done && !work_input )
428                         pthread_cond_wait(&worker_ready, &worker_lock);
429                 if( worker_done ) break;
430                 PluginLV2Work *wp = work_input;  work_input = wp->next;
431
432                 pthread_mutex_unlock(&worker_lock);
433                 worker_iface->work(inst, lv2_worker_respond, this, wp->used, wp->data);
434                 pthread_mutex_lock(&worker_lock);
435                 wp->next = work_avail;  work_avail = wp;
436         }
437         pthread_mutex_unlock(&worker_lock);
438         return NULL;
439 }
440 void *PluginLV2::worker_func(void* vp)
441 {
442         PluginLV2 *the = (PluginLV2 *)vp;
443         return the->worker_func();
444 }
445
446 void PluginLV2::worker_start()
447 {
448         pthread_create(&worker_thread, 0, worker_func, this);
449 }
450
451 void PluginLV2::worker_stop()
452 {
453         if( !worker_done ) {
454                 worker_done = 1;
455                 pthread_mutex_lock(&worker_lock);
456                 pthread_cond_signal(&worker_ready);
457                 pthread_mutex_unlock(&worker_lock);
458                 pthread_join(worker_thread, 0);
459                 worker_thread = 0;
460         }
461         work_stop(work_avail);
462         work_stop(work_input);
463         work_stop(work_output);
464         work_tail = &work_output;
465 }
466
467 void PluginLV2::work_stop(PluginLV2Work *&work)
468 {
469         while( work ) {
470                 PluginLV2Work *wp = work;
471                 work = wp->next;
472                 delete wp;
473         }
474 }
475
476 LV2_Worker_Status PluginLV2::worker_schedule(uint32_t inp_size, const void *inp_data)
477 {
478         if( is_forked() ) {
479                 if( !pthread_mutex_trylock(&worker_lock) ) {
480                         PluginLV2Work *wp = get_work();
481                         wp->load(inp_data, inp_size);
482                         wp->next = work_input;  work_input = wp;
483                         pthread_cond_signal(&worker_ready);
484                         pthread_mutex_unlock(&worker_lock);
485                 }
486         }
487         else if( worker_iface )
488                 worker_iface->work(inst, lv2_worker_respond, this, inp_size, inp_data);
489         return LV2_WORKER_SUCCESS;
490 }
491 LV2_Worker_Status PluginLV2::lv2_worker_schedule(LV2_Worker_Schedule_Handle vp,
492                     uint32_t inp_size, const void *inp_data)
493 {
494         PluginLV2 *the = (PluginLV2 *)vp;
495         return the->worker_schedule(inp_size, inp_data);
496 }
497
498 LV2_Worker_Status PluginLV2::worker_respond(uint32_t out_size, const void *out_data)
499 {
500         pthread_mutex_lock(&worker_lock);
501         PluginLV2Work *wp = get_work();
502         wp->load(out_data, out_size);
503         *work_tail = wp;  work_tail = &wp->next;
504         pthread_mutex_unlock(&worker_lock);
505         return LV2_WORKER_SUCCESS;
506 }
507 LV2_Worker_Status PluginLV2::lv2_worker_respond(LV2_Worker_Respond_Handle vp,
508                 uint32_t out_size, const void *out_data)
509 {
510         PluginLV2 *the = (PluginLV2 *)vp;
511         return the->worker_respond(out_size, out_data);
512 }
513
514 void PluginLV2::worker_responses()
515 {
516         pthread_mutex_lock(&worker_lock);
517         while( work_output ) {
518                 PluginLV2Work *rp = work_output;
519                 if( !(work_output=rp->next) ) work_tail = &work_output;
520                 pthread_mutex_unlock(&worker_lock);
521                 worker_iface->work_response(inst, rp->used, rp->data);
522                 pthread_mutex_lock(&worker_lock);
523                 rp->next = work_avail;  work_avail = rp;
524         }
525         pthread_mutex_unlock(&worker_lock);
526 }
527
528 #include "file.h"
529 #include "pluginlv2ui.h"
530
531 PluginLV2ChildUI::PluginLV2ChildUI()
532 {
533         ac = 0;
534         av = 0;
535         gui = 0;
536         hidden = 1;
537 }
538
539 PluginLV2ChildUI::~PluginLV2ChildUI()
540 {
541         delete gui;
542 }
543
544 void PluginLV2ChildUI::run()
545 {
546         ArrayList<char *> av;
547         av.set_array_delete();
548         char arg[BCTEXTLEN];
549         const char *exec_path = File::get_cinlib_path();
550         snprintf(arg, sizeof(arg), "%s/%s", exec_path, "lv2ui");
551         av.append(cstrdup(arg));
552         sprintf(arg, "%d", child_fd);
553         av.append(cstrdup(arg));
554         sprintf(arg, "%d", parent_fd);
555         av.append(cstrdup(arg));
556         sprintf(arg, "%d", ppid);
557         av.append(cstrdup(arg));
558         av.append(0);
559         execv(av[0], &av.values[0]);
560         fprintf(stderr, "execv failed: %s\n %m\n", av.values[0]);
561         av.remove_all_objects();
562         _exit(1);
563 }
564
565 #define LV2_EXTERNAL_UI_URI__KX__Widget "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget"
566
567 PluginLV2UI::PluginLV2UI()
568 {
569         lilv_ui = 0;
570         lilv_type = 0;
571
572         done = -1;
573         running = 0;
574         updates = 0;
575         hidden = 1;
576         title[0] = 0;
577
578         memset(&uri_map, 0, sizeof(uri_map));
579         memset(&extui_host, 0, sizeof(extui_host));
580         wgt_type = LV2_EXTERNAL_UI_URI__KX__Widget;
581         gtk_type = LV2_UI__GtkUI;
582         ui_type = 0;
583 }
584
585 PluginLV2UI::~PluginLV2UI ()
586 {
587 }
588
589 #endif