lv2 rework, sams ffmpeg icons, elision patch
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginlv2.C
diff --git a/cinelerra-5.1/cinelerra/pluginlv2.C b/cinelerra-5.1/cinelerra/pluginlv2.C
new file mode 100644 (file)
index 0000000..698be88
--- /dev/null
@@ -0,0 +1,393 @@
+#ifdef HAVE_LV2
+
+#include "bctrace.h"
+#include "bcwindowbase.h"
+#include "pluginlv2.h"
+#include "samples.h"
+
+#include <sys/shm.h>
+#include <sys/mman.h>
+
+PluginLV2::PluginLV2()
+{
+       shm_bfr = 0;
+       use_shm = 1;
+       shmid = -1;
+       in_buffers = 0;   iport = 0;
+       out_buffers = 0;  oport = 0;
+       nb_inputs = 0;
+       nb_outputs = 0;
+       max_bufsz = 0;
+       ui_features = 0;
+
+       world = 0;
+       lilv = 0;
+       lilv_uis = 0;
+       inst = 0;
+       sinst = 0;
+       ui_host = 0;
+
+       lv2_InputPort = 0;
+       lv2_OutputPort = 0;
+       lv2_AudioPort = 0;
+       lv2_ControlPort = 0;
+       lv2_CVPort = 0;
+       lv2_Optional = 0;
+       atom_AtomPort = 0;
+       atom_Sequence = 0;
+       powerOf2BlockLength = 0;
+       fixedBlockLength = 0;
+       boundedBlockLength = 0;
+       seq_out = 0;
+}
+
+PluginLV2::~PluginLV2()
+{
+       reset_lv2();
+       if( world )     lilv_world_free(world);
+}
+
+void PluginLV2::reset_lv2()
+{
+       if( inst ) lilv_instance_deactivate(inst);
+       lilv_instance_free(inst);             inst = 0;
+       lilv_uis_free(lilv_uis);              lilv_uis = 0;
+
+        lilv_node_free(lv2_InputPort);        lv2_InputPort = 0;
+        lilv_node_free(lv2_OutputPort);       lv2_OutputPort = 0;
+       lilv_node_free(lv2_AudioPort);        lv2_AudioPort = 0;
+       lilv_node_free(lv2_ControlPort);      lv2_ControlPort = 0;
+        lilv_node_free(lv2_CVPort);           lv2_CVPort = 0;
+
+       lilv_node_free(lv2_Optional);         lv2_Optional = 0;
+       lilv_node_free(atom_AtomPort);        atom_AtomPort = 0;
+       lilv_node_free(atom_Sequence);        atom_Sequence = 0;
+       lilv_node_free(boundedBlockLength);   boundedBlockLength = 0;
+       lilv_node_free(fixedBlockLength);     fixedBlockLength = 0;
+       lilv_node_free(powerOf2BlockLength);  powerOf2BlockLength = 0;
+
+       delete [] (char *)seq_out;            seq_out = 0;
+       uri_table.remove_all_objects();
+       features.remove_all_objects();        ui_features = 0;
+       del_buffer();
+}
+
+
+int PluginLV2::load_lv2(const char *path, char *title)
+{
+       if( !world ) {
+               world = lilv_world_new();
+               if( !world ) {
+                       printf("lv2: lilv_world_new failed\n");
+                       return 1;
+               }
+               lilv_world_load_all(world);
+       }
+
+       LilvNode *uri = lilv_new_uri(world, path);
+       if( !uri ) {
+               printf("lv2: lilv_new_uri(%s) failed\n", path);
+               return 1;
+       }
+
+       const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
+       lilv = lilv_plugins_get_by_uri(all_plugins, uri);
+       lilv_node_free(uri);
+       if( !lilv ) {
+               printf("lv2: lilv_plugins_get_by_uriPlugin(%s) failed\n", path);
+               return 1;
+       }
+
+       if( title ) {
+               LilvNode *name = lilv_plugin_get_name(lilv);
+               const char *nm = lilv_node_as_string(name);
+               sprintf(title, "L2_%s", nm);
+               lilv_node_free(name);
+       }
+       return 0;
+}
+
+int PluginLV2::init_lv2(PluginLV2ClientConfig &conf, int sample_rate)
+{
+       reset_lv2();
+
+       lv2_AudioPort       = lilv_new_uri(world, LV2_CORE__AudioPort);
+       lv2_ControlPort     = lilv_new_uri(world, LV2_CORE__ControlPort);
+       lv2_CVPort          = lilv_new_uri(world, LV2_CORE__CVPort);
+       lv2_InputPort       = lilv_new_uri(world, LV2_CORE__InputPort);
+       lv2_OutputPort      = lilv_new_uri(world, LV2_CORE__OutputPort);
+       lv2_Optional        = lilv_new_uri(world, LV2_CORE__connectionOptional);
+       atom_AtomPort       = lilv_new_uri(world, LV2_ATOM__AtomPort);
+       atom_Sequence       = lilv_new_uri(world, LV2_ATOM__Sequence);
+       powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength);
+       fixedBlockLength    = lilv_new_uri(world, LV2_BUF_SIZE__fixedBlockLength);
+       boundedBlockLength  = lilv_new_uri(world, LV2_BUF_SIZE__boundedBlockLength);
+       seq_out = (LV2_Atom_Sequence *) new char[sizeof(LV2_Atom_Sequence) + LV2_SEQ_SIZE];
+
+       conf.init_lv2(lilv);
+       nb_inputs = nb_outputs = 0;
+
+       for( int i=0; i<conf.nb_ports; ++i ) {
+               const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
+               if( !lp ) continue;
+               int is_input = lilv_port_is_a(lilv, lp, lv2_InputPort);
+               if( !is_input && !lilv_port_is_a(lilv, lp, lv2_OutputPort) &&
+                   !lilv_port_has_property(lilv, lp, lv2_Optional) ) {
+                       printf("lv2: not input, not output, and not optional: %s\n", conf.names[i]);
+                       continue;
+               }
+               if( is_input && lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
+                       conf.append(new PluginLV2Client_Opt(&conf, i));
+                       continue;
+               }
+               if( lilv_port_is_a(lilv, lp, lv2_AudioPort) ||
+                   lilv_port_is_a(lilv, lp, lv2_CVPort ) ) {
+                       if( is_input ) ++nb_inputs; else ++nb_outputs;
+                       continue;
+               }
+       }
+
+       map.handle = (void*)&uri_table;
+       map.map = uri_table_map;
+       features.append(new Lv2Feature(LV2_URID_MAP_URI, &map));
+       unmap.handle = (void*)&uri_table;
+       unmap.unmap  = uri_table_unmap;
+       features.append(new Lv2Feature(LV2_URID_UNMAP_URI, &unmap));
+       features.append(new Lv2Feature(LV2_BUF_SIZE__powerOf2BlockLength, 0));
+       features.append(new Lv2Feature(LV2_BUF_SIZE__fixedBlockLength,    0));
+       features.append(new Lv2Feature(LV2_BUF_SIZE__boundedBlockLength,  0));
+       features.append(0);
+
+       if( sample_rate < 64 ) sample_rate = 64;
+       inst = lilv_plugin_instantiate(lilv, sample_rate, features);
+       if( !inst ) {
+               printf("lv2: lilv_plugin_instantiate failed\n");
+               return 1;
+       }
+
+       lilv_instance_activate(inst);
+// not sure what to do with these
+       max_bufsz = nb_inputs &&
+               (lilv_plugin_has_feature(lilv, powerOf2BlockLength) ||
+                lilv_plugin_has_feature(lilv, fixedBlockLength) ||
+                lilv_plugin_has_feature(lilv, boundedBlockLength)) ? 4096 : 0;
+       return 0;
+}
+
+LV2_URID PluginLV2::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
+{
+       return ((PluginLV2UriTable *)handle)->map(uri);
+}
+
+const char *PluginLV2::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
+{
+       return ((PluginLV2UriTable *)handle)->unmap(urid);
+}
+
+void PluginLV2::connect_ports(PluginLV2ClientConfig &conf, int typ)
+{
+       int ich = 0, och = 0;
+       for( int i=0; i<conf.nb_ports; ++i ) {
+               const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
+               if( !lp ) continue;
+               if( (typ & TYP_CONTROL) &&
+                   lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
+                       lilv_instance_connect_port(inst, i, &conf.ctls[i]);
+                       continue;
+               }
+               if( (typ & TYP_AUDIO) &&
+                   ( lilv_port_is_a(lilv, lp, lv2_AudioPort) ||
+                     lilv_port_is_a(lilv, lp, lv2_CVPort) ) ) {
+                       if( lilv_port_is_a(lilv, lp, lv2_InputPort) ) {
+                               lilv_instance_connect_port(inst, iport[ich]=i, in_buffers[ich]);
+                               ++ich;
+                       }
+                       else if( lilv_port_is_a(lilv, lp, lv2_OutputPort)) {
+                               lilv_instance_connect_port(inst, oport[och]=i, out_buffers[och]);
+                               ++och;
+                       }
+                       continue;
+               }
+               if( (typ & TYP_ATOM) &&
+                   lilv_port_is_a(lilv, lp, atom_AtomPort) ) {
+                       if( lilv_port_is_a(lilv, lp, lv2_InputPort) )
+                               lilv_instance_connect_port(inst, i, &seq_in);
+                       else
+                               lilv_instance_connect_port(inst, i, seq_out);
+                       continue;
+               }
+       }
+
+       seq_in[0].atom.size = sizeof(LV2_Atom_Sequence_Body);
+       seq_in[0].atom.type = uri_table.map(LV2_ATOM__Sequence);
+       seq_in[1].atom.size = 0;
+       seq_in[1].atom.type = 0;
+       seq_out->atom.size  = LV2_SEQ_SIZE;
+       seq_out->atom.type  = uri_table.map(LV2_ATOM__Chunk);
+}
+
+void PluginLV2::del_buffer()
+{
+       if( shmid >= 0 )
+               shm_buffer(-1);
+
+       delete [] in_buffers;  in_buffers = 0;
+       delete [] out_buffers;  out_buffers = 0;
+}
+
+void PluginLV2::new_buffer(int64_t sz)
+{
+       uint8_t *bp = 0;
+       if( use_shm ) {  // currently, always uses shm
+               shmid = shmget(IPC_PRIVATE, sz, IPC_CREAT | 0777);
+               if( shmid >= 0 ) {
+                       bp = (unsigned char*)shmat(shmid, NULL, 0);
+                       if( bp == (void *) -1 ) { perror("shmat"); bp = 0; }
+                       shmctl(shmid, IPC_RMID, 0); // delete when last ref gone
+               }
+               else {
+                       perror("PluginLV2::allocate_buffer: shmget failed\n");
+                       BC_Trace::dump_shm_stats(stdout);
+               }
+       }
+
+       shm_bfr = (shm_bfr_t *) bp;
+       if( shm_bfr ) shm_bfr->sz = sz;
+}
+
+shm_bfr_t *PluginLV2::shm_buffer(int shmid)
+{
+       if( this->shmid != shmid ) {
+               if( this->shmid >= 0 ) {
+                       shmdt(shm_bfr);  this->shmid = -1;
+                       shm_bfr = 0;
+               }
+               if( shmid >= 0 ) {
+                       shm_bfr = (shm_bfr_t *)shmat(shmid, NULL, 0);
+                       if( shm_bfr == (void *)-1 ) { perror("shmat");  shm_bfr = 0; }
+                       this->shmid = shm_bfr ? shmid : -1;
+               }
+       }
+       return shm_bfr;
+}
+
+void PluginLV2::init_buffer(int samples)
+{
+       int64_t sz = sizeof(shm_bfr_t) +
+               sizeof(iport[0])*nb_inputs + sizeof(oport[0])*nb_outputs +
+               sizeof(*in_buffers[0]) *samples * nb_inputs +
+               sizeof(*out_buffers[0])*samples * nb_outputs;
+
+       if( shm_bfr ) {
+               if( shm_bfr->sz < sz ||
+                   shm_bfr->nb_inputs != nb_inputs ||
+                   shm_bfr->nb_outputs != nb_outputs )
+                       del_buffer();
+       }
+
+       if( !shm_bfr )
+               new_buffer(sz);
+
+       shm_bfr->samples = samples;
+       shm_bfr->done = 0;
+       shm_bfr->nb_inputs = nb_inputs;
+       shm_bfr->nb_outputs = nb_outputs;
+
+       map_buffer();
+}
+
+// shm_bfr layout:
+// struct shm_bfr {
+//   int64_t sz;
+//   int samples, done;
+//   int nb_inputs, nb_outputs;
+//   int iport[nb_inputs], 
+//   float in_buffers[samples][nb_inputs];
+//   int oport[nb_outputs];
+//   float out_buffers[samples][nb_outputs];
+// };
+
+void PluginLV2::map_buffer()
+{
+       uint8_t *bp = (uint8_t *)(shm_bfr + 1);
+
+       nb_inputs = shm_bfr->nb_inputs;
+       iport = (int *)bp;
+       bp += sizeof(iport[0])*nb_inputs;
+       in_buffers = new float*[nb_inputs];
+       int samples = shm_bfr->samples;
+       for(int i=0; i<nb_inputs; ++i ) {
+               in_buffers[i] = (float *)bp;
+               bp += sizeof(*in_buffers[0])*samples;
+       }
+
+       nb_outputs = shm_bfr->nb_outputs;
+       oport = (int *)bp;
+       bp += sizeof(oport[0])*nb_outputs;
+       out_buffers = new float*[nb_outputs];
+       for( int i=0; i<nb_outputs; ++i ) {
+               out_buffers[i] = (float *)bp;
+               bp += sizeof(*out_buffers[0])*samples;
+       }
+}
+
+
+#include "file.h"
+#include "pluginlv2ui.h"
+
+PluginLV2ChildUI::PluginLV2ChildUI()
+ : ForkChild()
+{
+}
+
+PluginLV2ChildUI::~PluginLV2ChildUI()
+{
+}
+
+void PluginLV2ChildUI::run()
+{
+       ArrayList<char *> av;
+       av.set_array_delete();
+       char arg[BCTEXTLEN];
+       const char *exec_path = File::get_cinlib_path();
+       snprintf(arg, sizeof(arg), "%s/%s", exec_path, "lv2ui");
+       av.append(cstrdup(arg));
+       sprintf(arg, "%d", child_fd);
+       av.append(cstrdup(arg));
+       sprintf(arg, "%d", parent_fd);
+       av.append(cstrdup(arg));
+       sprintf(arg, "%d", ppid);
+       av.append(cstrdup(arg));
+       av.append(0);
+       execv(av[0], &av.values[0]);
+       fprintf(stderr, "execv failed: %s\n %m\n", av.values[0]);
+       av.remove_all_objects();
+       _exit(1);
+}
+
+
+PluginLV2UI::PluginLV2UI()
+ : PluginLV2()
+{
+       lilv_ui = 0;
+       lilv_type = 0;
+       uri_map = 0;
+
+       done = 0;
+       running = 0;
+       redraw = 0;
+       host_updates = updates = 0;
+       host_hidden = hidden = 1;
+       title[0] = 0;
+
+// only gtk-2
+       gtk_type = "http://lv2plug.in/ns/extensions/ui#GtkUI";
+       top_level = 0;
+}
+
+PluginLV2UI::~PluginLV2UI ()
+{
+       reset_gui();
+}
+
+#endif