bbfd76eaae7e3771cc6ca68fdb9806b180184f31
[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, this);
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 ports)
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                 int port = conf.ports[i];
194                 if( !(port & ports) ) continue;
195                 if( (port & PORTS_CONTROL) ) {
196                         lilv_instance_connect_port(inst, i, &conf.ctls[i]);
197                         continue;
198                 }
199                 if( (port & PORTS_AUDIO) ) {
200                         if( (port & PORTS_INPUT) ) {
201                                 lilv_instance_connect_port(inst, i, in_buffers[ich]);
202                                 iport[ich++] = i;
203                         }
204                         else if( (port & PORTS_OUTPUT) ) {
205                                 lilv_instance_connect_port(inst, i, out_buffers[och]);
206                                 oport[och++] = i;
207                         }
208                         continue;
209                 }
210                 if( (port & PORTS_ATOM) ) {
211                         if( (port & PORTS_INPUT) )
212                                 lilv_instance_connect_port(inst, i, &seq_in);
213                         else if( (port & PORTS_OUTPUT) )
214                                 lilv_instance_connect_port(inst, i, seq_out);
215                         continue;
216                 }
217         }
218
219         seq_in[0].atom.size = sizeof(LV2_Atom_Sequence_Body);
220         seq_in[0].atom.type = uri_table.map(LV2_ATOM__Sequence);
221         seq_in[1].atom.size = 0;
222         seq_in[1].atom.type = 0;
223         seq_out->atom.size  = LV2_SEQ_SIZE;
224         seq_out->atom.type  = uri_table.map(LV2_ATOM__Chunk);
225 }
226
227 void PluginLV2::del_buffer()
228 {
229         if( shmid >= 0 )
230                 shm_buffer(-1);
231
232         delete [] in_buffers;  in_buffers = 0;
233         delete [] out_buffers;  out_buffers = 0;
234 }
235
236 void PluginLV2::new_buffer(int64_t sz)
237 {
238         uint8_t *bp = 0;
239         if( use_shm ) {  // currently, always uses shm
240                 shmid = shmget(IPC_PRIVATE, sz, IPC_CREAT | 0777);
241                 if( shmid >= 0 ) {
242                         bp = (unsigned char*)shmat(shmid, NULL, 0);
243                         if( bp == (void *) -1 ) { perror("shmat"); bp = 0; }
244                         shmctl(shmid, IPC_RMID, 0); // delete when last ref gone
245                 }
246                 else {
247                         perror("PluginLV2::allocate_buffer: shmget failed\n");
248                         BC_Trace::dump_shm_stats(stdout);
249                 }
250         }
251
252         shm_bfr = (shm_bfr_t *) bp;
253         if( shm_bfr ) shm_bfr->sz = sz;
254 }
255
256 shm_bfr_t *PluginLV2::shm_buffer(int shmid)
257 {
258         if( this->shmid != shmid ) {
259                 if( this->shmid >= 0 ) {
260                         shmdt(shm_bfr);  this->shmid = -1;
261                         shm_bfr = 0;
262                 }
263                 if( shmid >= 0 ) {
264                         shm_bfr = (shm_bfr_t *)shmat(shmid, NULL, 0);
265                         if( shm_bfr == (void *)-1 ) { perror("shmat");  shm_bfr = 0; }
266                         this->shmid = shm_bfr ? shmid : -1;
267                 }
268         }
269         return shm_bfr;
270 }
271
272 void PluginLV2::init_buffer(int samples)
273 {
274         int64_t sz = sizeof(shm_bfr_t) +
275                 sizeof(iport[0])*nb_inputs + sizeof(oport[0])*nb_outputs +
276                 sizeof(*in_buffers[0]) *samples * nb_inputs +
277                 sizeof(*out_buffers[0])*samples * nb_outputs;
278
279         if( shm_bfr ) {
280                 if( shm_bfr->sz < sz ||
281                     shm_bfr->nb_inputs != nb_inputs ||
282                     shm_bfr->nb_outputs != nb_outputs )
283                         del_buffer();
284         }
285
286         if( !shm_bfr )
287                 new_buffer(sz);
288
289         shm_bfr->samples = samples;
290         shm_bfr->done = 0;
291         shm_bfr->nb_inputs = nb_inputs;
292         shm_bfr->nb_outputs = nb_outputs;
293
294         map_buffer();
295 }
296
297 // shm_bfr layout:
298 // struct shm_bfr {
299 //   int64_t sz;
300 //   int samples, done;
301 //   int nb_inputs, nb_outputs;
302 //   int iport[nb_inputs], 
303 //   float in_buffers[samples][nb_inputs];
304 //   int oport[nb_outputs];
305 //   float out_buffers[samples][nb_outputs];
306 // };
307
308 void PluginLV2::map_buffer()
309 {
310         uint8_t *bp = (uint8_t *)(shm_bfr + 1);
311
312         nb_inputs = shm_bfr->nb_inputs;
313         iport = (int *)bp;
314         bp += sizeof(iport[0])*nb_inputs;
315         in_buffers = new float*[nb_inputs];
316         int samples = shm_bfr->samples;
317         for(int i=0; i<nb_inputs; ++i ) {
318                 in_buffers[i] = (float *)bp;
319                 bp += sizeof(*in_buffers[0])*samples;
320         }
321
322         nb_outputs = shm_bfr->nb_outputs;
323         oport = (int *)bp;
324         bp += sizeof(oport[0])*nb_outputs;
325         out_buffers = new float*[nb_outputs];
326         for( int i=0; i<nb_outputs; ++i ) {
327                 out_buffers[i] = (float *)bp;
328                 bp += sizeof(*out_buffers[0])*samples;
329         }
330 }
331
332
333 #include "file.h"
334 #include "pluginlv2ui.h"
335
336 PluginLV2ChildUI::PluginLV2ChildUI()
337  : ForkChild()
338 {
339 }
340
341 PluginLV2ChildUI::~PluginLV2ChildUI()
342 {
343 }
344
345 void PluginLV2ChildUI::run()
346 {
347         ArrayList<char *> av;
348         av.set_array_delete();
349         char arg[BCTEXTLEN];
350         const char *exec_path = File::get_cinlib_path();
351         snprintf(arg, sizeof(arg), "%s/%s", exec_path, "lv2ui");
352         av.append(cstrdup(arg));
353         sprintf(arg, "%d", child_fd);
354         av.append(cstrdup(arg));
355         sprintf(arg, "%d", parent_fd);
356         av.append(cstrdup(arg));
357         sprintf(arg, "%d", ppid);
358         av.append(cstrdup(arg));
359         av.append(0);
360         execv(av[0], &av.values[0]);
361         fprintf(stderr, "execv failed: %s\n %m\n", av.values[0]);
362         av.remove_all_objects();
363         _exit(1);
364 }
365
366
367 PluginLV2UI::PluginLV2UI()
368  : PluginLV2()
369 {
370         lilv_ui = 0;
371         lilv_type = 0;
372         uri_map = 0;
373
374         done = -1;
375         running = 0;
376         updates = 0;
377         hidden = 1;
378         title[0] = 0;
379         child = 0;
380
381 // only gtk-2
382         gtk_type = "http://lv2plug.in/ns/extensions/ui#GtkUI";
383         top_level = 0;
384 }
385
386 PluginLV2UI::~PluginLV2UI ()
387 {
388         reset_gui();
389 }
390
391 #endif