lv2 phase 2 fixups
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginlv2.C
index bbfd76eaae7e3771cc6ca68fdb9806b180184f31..4ec32648ae3441d44048cee56fc570852b736473 100644 (file)
@@ -20,6 +20,11 @@ PluginLV2::PluginLV2()
        max_bufsz = 0;
        ui_features = 0;
 
+       samplerate = 44100;
+       refreshrate = 30.;
+       block_length = 4096;
+       midi_buf_size = 8192;
+
        world = 0;
        lilv = 0;
        lilv_uis = 0;
@@ -39,16 +44,29 @@ PluginLV2::PluginLV2()
        fixedBlockLength = 0;
        boundedBlockLength = 0;
        seq_out = 0;
+
+       worker_thread = 0;
+       memset(&schedule, 0, sizeof(schedule));
+       schedule.handle = (LV2_Worker_Schedule_Handle)this;
+       schedule.schedule_work = lv2_worker_schedule;
+       worker_iface = 0;  worker_done = -1;
+       pthread_mutex_init(&worker_lock, 0);
+       pthread_cond_init(&worker_ready, 0);
+       work_avail = 0;   work_input = 0;
+       work_output = 0;  work_tail = &work_output;
 }
 
 PluginLV2::~PluginLV2()
 {
        reset_lv2();
        if( world )     lilv_world_free(world);
+       pthread_mutex_destroy(&worker_lock);
+       pthread_cond_destroy(&worker_ready);
 }
 
 void PluginLV2::reset_lv2()
 {
+       worker_stop();
        if( inst ) lilv_instance_deactivate(inst);
        lilv_instance_free(inst);             inst = 0;
        lilv_uis_free(lilv_uis);              lilv_uis = 0;
@@ -94,7 +112,7 @@ int PluginLV2::load_lv2(const char *path, char *title)
        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);
+               printf("lv2: lilv_plugins_get_by_uri (%s) failed\n", path);
                return 1;
        }
 
@@ -147,6 +165,10 @@ int PluginLV2::init_lv2(PluginLV2ClientConfig &conf, int sample_rate)
                }
        }
 
+
+       uri_map.callback_data = (LV2_URI_Map_Callback_Data)this;
+       uri_map.uri_to_id = uri_to_id;
+       features.append(new Lv2Feature(NS_EXT "uri-map", &uri_map));
        map.handle = (void*)&uri_table;
        map.map = uri_table_map;
        features.append(new Lv2Feature(LV2_URID_MAP_URI, &map));
@@ -156,15 +178,41 @@ int PluginLV2::init_lv2(PluginLV2ClientConfig &conf, int sample_rate)
        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(new Lv2Feature(LV2_WORKER__schedule, &schedule));
+
+       if( sample_rate < 64 ) sample_rate = 44100;
+
+       atom_int   = uri_table.map(LV2_ATOM__Int);
+       atom_float = uri_table.map(LV2_ATOM__Float);
+       param_sampleRate = uri_table.map(LV2_PARAMETERS__sampleRate);
+       bufsz_minBlockLength =  uri_table.map(LV2_BUF_SIZE__minBlockLength);
+       bufsz_maxBlockLength = uri_table.map(LV2_BUF_SIZE__maxBlockLength);
+       bufsz_sequenceSize =  uri_table.map(LV2_BUF_SIZE__sequenceSize);
+       ui_updateRate = uri_table.map(LV2_UI__updateRate);
+
+       samplerate = sample_rate;
+       options.add(param_sampleRate, sizeof(float), atom_float, &samplerate);
+       options.add(bufsz_minBlockLength, sizeof(int), atom_int, &block_length);
+       options.add(bufsz_maxBlockLength, sizeof(int), atom_int, &block_length);
+       options.add(bufsz_sequenceSize, sizeof(int), atom_int, &midi_buf_size);
+       options.add(ui_updateRate, sizeof(float),  atom_float, &refreshrate);
+       options.add(0, 0, 0, 0);
+
+       features.append(new Lv2Feature(LV2_OPTIONS__options,  &options[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;
        }
 
+       const LV2_Descriptor *lilv_desc = inst->lv2_descriptor;
+       worker_iface = !lilv_desc->extension_data ? 0 :
+               (LV2_Worker_Interface*)lilv_desc->extension_data(LV2_WORKER__interface);
+       if( worker_iface )
+               worker_start();
+
        lilv_instance_activate(inst);
 // not sure what to do with these
        max_bufsz = nb_inputs &&
@@ -184,6 +232,13 @@ const char *PluginLV2::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid
        return ((PluginLV2UriTable *)handle)->unmap(urid);
 }
 
+uint32_t PluginLV2::uri_to_id(LV2_URI_Map_Callback_Data callback_data,
+       const char *map, const char *uri)
+{
+       PluginLV2 *the = (PluginLV2 *)callback_data;
+       return the->map.map(the->uri_table, uri);
+}
+
 void PluginLV2::connect_ports(PluginLV2ClientConfig &conf, int ports)
 {
        int ich = 0, och = 0;
@@ -210,7 +265,7 @@ void PluginLV2::connect_ports(PluginLV2ClientConfig &conf, int ports)
                if( (port & PORTS_ATOM) ) {
                        if( (port & PORTS_INPUT) )
                                lilv_instance_connect_port(inst, i, &seq_in);
-                       else if( (port & PORTS_OUTPUT) )
+                       else
                                lilv_instance_connect_port(inst, i, seq_out);
                        continue;
                }
@@ -330,16 +385,157 @@ void PluginLV2::map_buffer()
 }
 
 
+// LV2 Worker
+
+PluginLV2Work::PluginLV2Work()
+{
+       next = 0;
+       alloc = used = 0;
+       data = 0;
+}
+
+PluginLV2Work::~PluginLV2Work()
+{
+       delete [] data;
+}
+
+void PluginLV2Work::load(const void *vp, unsigned size)
+{
+       if( alloc < size ) {
+               delete [] data;
+               data = new char[alloc=size];
+       }
+       memcpy(data, vp, used=size);
+}
+
+PluginLV2Work *PluginLV2::get_work()
+{
+// must hold worker_lock
+       if( !work_avail ) return new PluginLV2Work();
+       PluginLV2Work *wp = work_avail;
+       work_avail = wp->next;  wp->next = 0;
+       return wp;
+}
+
+void *PluginLV2::worker_func()
+{
+       pthread_mutex_lock(&worker_lock);
+       for(;;) {
+               while( !worker_done && !work_input )
+                       pthread_cond_wait(&worker_ready, &worker_lock);
+               if( worker_done ) break;
+               PluginLV2Work *wp = work_input;  work_input = wp->next;
+
+               pthread_mutex_unlock(&worker_lock);
+               worker_iface->work(inst, lv2_worker_respond, this, wp->used, wp->data);
+               pthread_mutex_lock(&worker_lock);
+               wp->next = work_avail;  work_avail = wp;
+       }
+       pthread_mutex_unlock(&worker_lock);
+       return NULL;
+}
+void *PluginLV2::worker_func(void* vp)
+{
+       PluginLV2 *the = (PluginLV2 *)vp;
+       return the->worker_func();
+}
+
+void PluginLV2::worker_start()
+{
+       pthread_create(&worker_thread, 0, worker_func, this);
+}
+
+void PluginLV2::worker_stop()
+{
+       if( !worker_done ) {
+               worker_done = 1;
+               pthread_mutex_lock(&worker_lock);
+               pthread_cond_signal(&worker_ready);
+               pthread_mutex_unlock(&worker_lock);
+               pthread_join(worker_thread, 0);
+               worker_thread = 0;
+       }
+       work_stop(work_avail);
+       work_stop(work_input);
+       work_stop(work_output);
+       work_tail = &work_output;
+}
+
+void PluginLV2::work_stop(PluginLV2Work *&work)
+{
+       while( work ) {
+               PluginLV2Work *wp = work;
+               work = wp->next;
+               delete wp;
+       }
+}
+
+LV2_Worker_Status PluginLV2::worker_schedule(uint32_t inp_size, const void *inp_data)
+{
+       if( is_forked() ) {
+               if( !pthread_mutex_trylock(&worker_lock) ) {
+                       PluginLV2Work *wp = get_work();
+                       wp->load(inp_data, inp_size);
+                       wp->next = work_input;  work_input = wp;
+                       pthread_cond_signal(&worker_ready);
+                       pthread_mutex_unlock(&worker_lock);
+               }
+       }
+       else if( worker_iface )
+               worker_iface->work(inst, lv2_worker_respond, this, inp_size, inp_data);
+       return LV2_WORKER_SUCCESS;
+}
+LV2_Worker_Status PluginLV2::lv2_worker_schedule(LV2_Worker_Schedule_Handle vp,
+                    uint32_t inp_size, const void *inp_data)
+{
+       PluginLV2 *the = (PluginLV2 *)vp;
+       return the->worker_schedule(inp_size, inp_data);
+}
+
+LV2_Worker_Status PluginLV2::worker_respond(uint32_t out_size, const void *out_data)
+{
+       pthread_mutex_lock(&worker_lock);
+       PluginLV2Work *wp = get_work();
+       wp->load(out_data, out_size);
+       *work_tail = wp;  work_tail = &wp->next;
+       pthread_mutex_unlock(&worker_lock);
+       return LV2_WORKER_SUCCESS;
+}
+LV2_Worker_Status PluginLV2::lv2_worker_respond(LV2_Worker_Respond_Handle vp,
+               uint32_t out_size, const void *out_data)
+{
+       PluginLV2 *the = (PluginLV2 *)vp;
+       return the->worker_respond(out_size, out_data);
+}
+
+void PluginLV2::worker_responses()
+{
+       pthread_mutex_lock(&worker_lock);
+       while( work_output ) {
+               PluginLV2Work *rp = work_output;
+               if( !(work_output=rp->next) ) work_tail = &work_output;
+               pthread_mutex_unlock(&worker_lock);
+               worker_iface->work_response(inst, rp->used, rp->data);
+               pthread_mutex_lock(&worker_lock);
+               rp->next = work_avail;  work_avail = rp;
+       }
+       pthread_mutex_unlock(&worker_lock);
+}
+
 #include "file.h"
 #include "pluginlv2ui.h"
 
 PluginLV2ChildUI::PluginLV2ChildUI()
- : ForkChild()
 {
+       ac = 0;
+       av = 0;
+       gui = 0;
+       hidden = 1;
 }
 
 PluginLV2ChildUI::~PluginLV2ChildUI()
 {
+       delete gui;
 }
 
 void PluginLV2ChildUI::run()
@@ -363,29 +559,28 @@ void PluginLV2ChildUI::run()
        _exit(1);
 }
 
+#define LV2_EXTERNAL_UI_URI__KX__Widget "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget"
 
 PluginLV2UI::PluginLV2UI()
- : PluginLV2()
 {
        lilv_ui = 0;
        lilv_type = 0;
-       uri_map = 0;
 
        done = -1;
        running = 0;
        updates = 0;
        hidden = 1;
        title[0] = 0;
-       child = 0;
 
-// only gtk-2
-       gtk_type = "http://lv2plug.in/ns/extensions/ui#GtkUI";
-       top_level = 0;
+       memset(&uri_map, 0, sizeof(uri_map));
+       memset(&extui_host, 0, sizeof(extui_host));
+       wgt_type = LV2_EXTERNAL_UI_URI__KX__Widget;
+       gtk_type = LV2_UI__GtkUI;
+       ui_type = 0;
 }
 
 PluginLV2UI::~PluginLV2UI ()
 {
-       reset_gui();
 }
 
 #endif