lv2 rework, sams ffmpeg icons, elision patch
[goodguy/history.git] / cinelerra-5.1 / 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         world = 0;
24         lilv = 0;
25         lilv_uis = 0;
26         inst = 0;
27         sinst = 0;
28         ui_host = 0;
29
30         lv2_InputPort = 0;
31         lv2_OutputPort = 0;
32         lv2_AudioPort = 0;
33         lv2_ControlPort = 0;
34         lv2_CVPort = 0;
35         lv2_Optional = 0;
36         atom_AtomPort = 0;
37         atom_Sequence = 0;
38         powerOf2BlockLength = 0;
39         fixedBlockLength = 0;
40         boundedBlockLength = 0;
41         seq_out = 0;
42 }
43
44 PluginLV2::~PluginLV2()
45 {
46         reset_lv2();
47         if( world )     lilv_world_free(world);
48 }
49
50 void PluginLV2::reset_lv2()
51 {
52         if( inst ) lilv_instance_deactivate(inst);
53         lilv_instance_free(inst);             inst = 0;
54         lilv_uis_free(lilv_uis);              lilv_uis = 0;
55
56         lilv_node_free(lv2_InputPort);        lv2_InputPort = 0;
57         lilv_node_free(lv2_OutputPort);       lv2_OutputPort = 0;
58         lilv_node_free(lv2_AudioPort);        lv2_AudioPort = 0;
59         lilv_node_free(lv2_ControlPort);      lv2_ControlPort = 0;
60         lilv_node_free(lv2_CVPort);           lv2_CVPort = 0;
61
62         lilv_node_free(lv2_Optional);         lv2_Optional = 0;
63         lilv_node_free(atom_AtomPort);        atom_AtomPort = 0;
64         lilv_node_free(atom_Sequence);        atom_Sequence = 0;
65         lilv_node_free(boundedBlockLength);   boundedBlockLength = 0;
66         lilv_node_free(fixedBlockLength);     fixedBlockLength = 0;
67         lilv_node_free(powerOf2BlockLength);  powerOf2BlockLength = 0;
68
69         delete [] (char *)seq_out;            seq_out = 0;
70         uri_table.remove_all_objects();
71         features.remove_all_objects();        ui_features = 0;
72         del_buffer();
73 }
74
75
76 int PluginLV2::load_lv2(const char *path, char *title)
77 {
78         if( !world ) {
79                 world = lilv_world_new();
80                 if( !world ) {
81                         printf("lv2: lilv_world_new failed\n");
82                         return 1;
83                 }
84                 lilv_world_load_all(world);
85         }
86
87         LilvNode *uri = lilv_new_uri(world, path);
88         if( !uri ) {
89                 printf("lv2: lilv_new_uri(%s) failed\n", path);
90                 return 1;
91         }
92
93         const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
94         lilv = lilv_plugins_get_by_uri(all_plugins, uri);
95         lilv_node_free(uri);
96         if( !lilv ) {
97                 printf("lv2: lilv_plugins_get_by_uriPlugin(%s) failed\n", path);
98                 return 1;
99         }
100
101         if( title ) {
102                 LilvNode *name = lilv_plugin_get_name(lilv);
103                 const char *nm = lilv_node_as_string(name);
104                 sprintf(title, "L2_%s", nm);
105                 lilv_node_free(name);
106         }
107         return 0;
108 }
109
110 int PluginLV2::init_lv2(PluginLV2ClientConfig &conf, int sample_rate)
111 {
112         reset_lv2();
113
114         lv2_AudioPort       = lilv_new_uri(world, LV2_CORE__AudioPort);
115         lv2_ControlPort     = lilv_new_uri(world, LV2_CORE__ControlPort);
116         lv2_CVPort          = lilv_new_uri(world, LV2_CORE__CVPort);
117         lv2_InputPort       = lilv_new_uri(world, LV2_CORE__InputPort);
118         lv2_OutputPort      = lilv_new_uri(world, LV2_CORE__OutputPort);
119         lv2_Optional        = lilv_new_uri(world, LV2_CORE__connectionOptional);
120         atom_AtomPort       = lilv_new_uri(world, LV2_ATOM__AtomPort);
121         atom_Sequence       = lilv_new_uri(world, LV2_ATOM__Sequence);
122         powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength);
123         fixedBlockLength    = lilv_new_uri(world, LV2_BUF_SIZE__fixedBlockLength);
124         boundedBlockLength  = lilv_new_uri(world, LV2_BUF_SIZE__boundedBlockLength);
125         seq_out = (LV2_Atom_Sequence *) new char[sizeof(LV2_Atom_Sequence) + LV2_SEQ_SIZE];
126
127         conf.init_lv2(lilv);
128         nb_inputs = nb_outputs = 0;
129
130         for( int i=0; i<conf.nb_ports; ++i ) {
131                 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
132                 if( !lp ) continue;
133                 int is_input = lilv_port_is_a(lilv, lp, lv2_InputPort);
134                 if( !is_input && !lilv_port_is_a(lilv, lp, lv2_OutputPort) &&
135                     !lilv_port_has_property(lilv, lp, lv2_Optional) ) {
136                         printf("lv2: not input, not output, and not optional: %s\n", conf.names[i]);
137                         continue;
138                 }
139                 if( is_input && lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
140                         conf.append(new PluginLV2Client_Opt(&conf, i));
141                         continue;
142                 }
143                 if( lilv_port_is_a(lilv, lp, lv2_AudioPort) ||
144                     lilv_port_is_a(lilv, lp, lv2_CVPort ) ) {
145                         if( is_input ) ++nb_inputs; else ++nb_outputs;
146                         continue;
147                 }
148         }
149
150         map.handle = (void*)&uri_table;
151         map.map = uri_table_map;
152         features.append(new Lv2Feature(LV2_URID_MAP_URI, &map));
153         unmap.handle = (void*)&uri_table;
154         unmap.unmap  = uri_table_unmap;
155         features.append(new Lv2Feature(LV2_URID_UNMAP_URI, &unmap));
156         features.append(new Lv2Feature(LV2_BUF_SIZE__powerOf2BlockLength, 0));
157         features.append(new Lv2Feature(LV2_BUF_SIZE__fixedBlockLength,    0));
158         features.append(new Lv2Feature(LV2_BUF_SIZE__boundedBlockLength,  0));
159         features.append(0);
160
161         if( sample_rate < 64 ) sample_rate = 64;
162         inst = lilv_plugin_instantiate(lilv, sample_rate, features);
163         if( !inst ) {
164                 printf("lv2: lilv_plugin_instantiate failed\n");
165                 return 1;
166         }
167
168         lilv_instance_activate(inst);
169 // not sure what to do with these
170         max_bufsz = nb_inputs &&
171                 (lilv_plugin_has_feature(lilv, powerOf2BlockLength) ||
172                  lilv_plugin_has_feature(lilv, fixedBlockLength) ||
173                  lilv_plugin_has_feature(lilv, boundedBlockLength)) ? 4096 : 0;
174         return 0;
175 }
176
177 LV2_URID PluginLV2::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
178 {
179         return ((PluginLV2UriTable *)handle)->map(uri);
180 }
181
182 const char *PluginLV2::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
183 {
184         return ((PluginLV2UriTable *)handle)->unmap(urid);
185 }
186
187 void PluginLV2::connect_ports(PluginLV2ClientConfig &conf, int typ)
188 {
189         int ich = 0, och = 0;
190         for( int i=0; i<conf.nb_ports; ++i ) {
191                 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
192                 if( !lp ) continue;
193                 if( (typ & TYP_CONTROL) &&
194                     lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
195                         lilv_instance_connect_port(inst, i, &conf.ctls[i]);
196                         continue;
197                 }
198                 if( (typ & TYP_AUDIO) &&
199                     ( lilv_port_is_a(lilv, lp, lv2_AudioPort) ||
200                       lilv_port_is_a(lilv, lp, lv2_CVPort) ) ) {
201                         if( lilv_port_is_a(lilv, lp, lv2_InputPort) ) {
202                                 lilv_instance_connect_port(inst, iport[ich]=i, in_buffers[ich]);
203                                 ++ich;
204                         }
205                         else if( lilv_port_is_a(lilv, lp, lv2_OutputPort)) {
206                                 lilv_instance_connect_port(inst, oport[och]=i, out_buffers[och]);
207                                 ++och;
208                         }
209                         continue;
210                 }
211                 if( (typ & TYP_ATOM) &&
212                     lilv_port_is_a(lilv, lp, atom_AtomPort) ) {
213                         if( lilv_port_is_a(lilv, lp, lv2_InputPort) )
214                                 lilv_instance_connect_port(inst, i, &seq_in);
215                         else
216                                 lilv_instance_connect_port(inst, i, seq_out);
217                         continue;
218                 }
219         }
220
221         seq_in[0].atom.size = sizeof(LV2_Atom_Sequence_Body);
222         seq_in[0].atom.type = uri_table.map(LV2_ATOM__Sequence);
223         seq_in[1].atom.size = 0;
224         seq_in[1].atom.type = 0;
225         seq_out->atom.size  = LV2_SEQ_SIZE;
226         seq_out->atom.type  = uri_table.map(LV2_ATOM__Chunk);
227 }
228
229 void PluginLV2::del_buffer()
230 {
231         if( shmid >= 0 )
232                 shm_buffer(-1);
233
234         delete [] in_buffers;  in_buffers = 0;
235         delete [] out_buffers;  out_buffers = 0;
236 }
237
238 void PluginLV2::new_buffer(int64_t sz)
239 {
240         uint8_t *bp = 0;
241         if( use_shm ) {  // currently, always uses shm
242                 shmid = shmget(IPC_PRIVATE, sz, IPC_CREAT | 0777);
243                 if( shmid >= 0 ) {
244                         bp = (unsigned char*)shmat(shmid, NULL, 0);
245                         if( bp == (void *) -1 ) { perror("shmat"); bp = 0; }
246                         shmctl(shmid, IPC_RMID, 0); // delete when last ref gone
247                 }
248                 else {
249                         perror("PluginLV2::allocate_buffer: shmget failed\n");
250                         BC_Trace::dump_shm_stats(stdout);
251                 }
252         }
253
254         shm_bfr = (shm_bfr_t *) bp;
255         if( shm_bfr ) shm_bfr->sz = sz;
256 }
257
258 shm_bfr_t *PluginLV2::shm_buffer(int shmid)
259 {
260         if( this->shmid != shmid ) {
261                 if( this->shmid >= 0 ) {
262                         shmdt(shm_bfr);  this->shmid = -1;
263                         shm_bfr = 0;
264                 }
265                 if( shmid >= 0 ) {
266                         shm_bfr = (shm_bfr_t *)shmat(shmid, NULL, 0);
267                         if( shm_bfr == (void *)-1 ) { perror("shmat");  shm_bfr = 0; }
268                         this->shmid = shm_bfr ? shmid : -1;
269                 }
270         }
271         return shm_bfr;
272 }
273
274 void PluginLV2::init_buffer(int samples)
275 {
276         int64_t sz = sizeof(shm_bfr_t) +
277                 sizeof(iport[0])*nb_inputs + sizeof(oport[0])*nb_outputs +
278                 sizeof(*in_buffers[0]) *samples * nb_inputs +
279                 sizeof(*out_buffers[0])*samples * nb_outputs;
280
281         if( shm_bfr ) {
282                 if( shm_bfr->sz < sz ||
283                     shm_bfr->nb_inputs != nb_inputs ||
284                     shm_bfr->nb_outputs != nb_outputs )
285                         del_buffer();
286         }
287
288         if( !shm_bfr )
289                 new_buffer(sz);
290
291         shm_bfr->samples = samples;
292         shm_bfr->done = 0;
293         shm_bfr->nb_inputs = nb_inputs;
294         shm_bfr->nb_outputs = nb_outputs;
295
296         map_buffer();
297 }
298
299 // shm_bfr layout:
300 // struct shm_bfr {
301 //   int64_t sz;
302 //   int samples, done;
303 //   int nb_inputs, nb_outputs;
304 //   int iport[nb_inputs], 
305 //   float in_buffers[samples][nb_inputs];
306 //   int oport[nb_outputs];
307 //   float out_buffers[samples][nb_outputs];
308 // };
309
310 void PluginLV2::map_buffer()
311 {
312         uint8_t *bp = (uint8_t *)(shm_bfr + 1);
313
314         nb_inputs = shm_bfr->nb_inputs;
315         iport = (int *)bp;
316         bp += sizeof(iport[0])*nb_inputs;
317         in_buffers = new float*[nb_inputs];
318         int samples = shm_bfr->samples;
319         for(int i=0; i<nb_inputs; ++i ) {
320                 in_buffers[i] = (float *)bp;
321                 bp += sizeof(*in_buffers[0])*samples;
322         }
323
324         nb_outputs = shm_bfr->nb_outputs;
325         oport = (int *)bp;
326         bp += sizeof(oport[0])*nb_outputs;
327         out_buffers = new float*[nb_outputs];
328         for( int i=0; i<nb_outputs; ++i ) {
329                 out_buffers[i] = (float *)bp;
330                 bp += sizeof(*out_buffers[0])*samples;
331         }
332 }
333
334
335 #include "file.h"
336 #include "pluginlv2ui.h"
337
338 PluginLV2ChildUI::PluginLV2ChildUI()
339  : ForkChild()
340 {
341 }
342
343 PluginLV2ChildUI::~PluginLV2ChildUI()
344 {
345 }
346
347 void PluginLV2ChildUI::run()
348 {
349         ArrayList<char *> av;
350         av.set_array_delete();
351         char arg[BCTEXTLEN];
352         const char *exec_path = File::get_cinlib_path();
353         snprintf(arg, sizeof(arg), "%s/%s", exec_path, "lv2ui");
354         av.append(cstrdup(arg));
355         sprintf(arg, "%d", child_fd);
356         av.append(cstrdup(arg));
357         sprintf(arg, "%d", parent_fd);
358         av.append(cstrdup(arg));
359         sprintf(arg, "%d", ppid);
360         av.append(cstrdup(arg));
361         av.append(0);
362         execv(av[0], &av.values[0]);
363         fprintf(stderr, "execv failed: %s\n %m\n", av.values[0]);
364         av.remove_all_objects();
365         _exit(1);
366 }
367
368
369 PluginLV2UI::PluginLV2UI()
370  : PluginLV2()
371 {
372         lilv_ui = 0;
373         lilv_type = 0;
374         uri_map = 0;
375
376         done = 0;
377         running = 0;
378         redraw = 0;
379         host_updates = updates = 0;
380         host_hidden = hidden = 1;
381         title[0] = 0;
382
383 // only gtk-2
384         gtk_type = "http://lv2plug.in/ns/extensions/ui#GtkUI";
385         top_level = 0;
386 }
387
388 PluginLV2UI::~PluginLV2UI ()
389 {
390         reset_gui();
391 }
392
393 #endif