return !ppid || !kill(ppid, 0) ? 1 : 0;
}
-int ForkChild::child_iteration()
-{
- int ret = read_child(100);
- if( ret <= 0 ) return ret;
- return handle_child();
-}
-
void ForkParent::start_child()
{
lock("ForkParent::new_child");
// Return -1 if the parent is dead
// Return 0 if timeout
// Return 1 if success
-int ForkBase::read_timeout(int ms, int fd, void *data, int bytes)
+int ForkBase::read_timeout(int64_t usec, int fd, void *data, int bytes)
{
fd_set rfds;
struct timeval timeout_struct;
uint8_t *bp = (uint8_t *)data;
while( bytes_read < bytes ) {
- timeout_struct.tv_sec = ms / 1000;
- timeout_struct.tv_usec = (ms % 1000) * 1000;
+ timeout_struct.tv_sec = usec / 1000000;
+ timeout_struct.tv_usec = usec % 1000000;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
int result = select(fd+1, &rfds, 0, 0, &timeout_struct);
return !pid || !kill(pid, 0) ? 1 : 0;
}
-int ForkBase::read_parent(int ms)
+int ForkBase::read_parent(int64_t usec)
{
token_bfr_t bfr;
- int ret = read_timeout(ms, parent_fd, &bfr, sizeof(bfr));
+ int ret = read_timeout(usec, parent_fd, &bfr, sizeof(bfr));
if( ret > 0 ) {
parent_token = bfr.token;
parent_bytes = bfr.bytes;
parent_data = new uint8_t[parent_allocated = parent_bytes];
}
if( parent_bytes ) {
- ret = read_timeout(1000, parent_fd, parent_data, parent_bytes);
+ ret = read_timeout(1000000, parent_fd, parent_data, parent_bytes);
if( !ret ) {
printf("read_parent timeout: %d\n", parent_fd);
ret = -1;
return ret;
}
-int ForkBase::read_child(int ms)
+int ForkBase::read_child(int64_t usec)
{
token_bfr_t bfr;
- int ret = read_timeout(ms, child_fd, &bfr, sizeof(bfr));
+ int ret = read_timeout(usec, child_fd, &bfr, sizeof(bfr));
if( ret > 0 ) {
child_token = bfr.token;
child_bytes = bfr.bytes;
child_data = new uint8_t[child_allocated = child_bytes];
}
if( child_bytes ) {
- ret = read_timeout(1000, child_fd, child_data, child_bytes);
+ ret = read_timeout(1000000, child_fd, child_data, child_bytes);
if( !ret ) {
printf("read_child timeout: %d\n", child_fd);
ret = -1;
ForkChild::ForkChild()
{
- done = 0;
+ parent_done = 0;
}
ForkChild::~ForkChild()
{
}
-int ForkChild::handle_child()
-{
- printf("ForkChild::handle_child %d\n", __LINE__);
- return 0;
-}
-
ForkParent::ForkParent()
: Thread(1, 0, 0)
{
- done = -1;
+ parent_done = -1;
}
ForkParent::~ForkParent()
void ForkParent::start()
{
- done = 0;
+ parent_done = 0;
Thread::start();
}
void ForkParent::run()
{
- while( !done && parent_iteration() >= 0 );
- done = 1;
+ while( !parent_done && parent_iteration() >= 0 );
+ parent_done = 1;
}
virtual int is_running() = 0;
void send_bfr(int fd, const void *bfr, int len);
- int read_timeout(int ms, int fd, void *data, int bytes);
- int read_parent(int ms);
+ int read_timeout(int64_t usec, int fd, void *data, int bytes);
+
+ int read_parent(int64_t usec);
int send_parent(int64_t value, const void *data, int bytes);
- int read_child(int ms);
+ int read_child(int64_t usec);
int send_child(int64_t value, const void *data, int bytes);
- int done, ppid, pid;
+ int parent_done;
+ int ppid, pid;
ForkChild *child;
int child_fd;
public:
ForkChild();
virtual ~ForkChild();
- virtual int handle_child();
- int child_iteration();
+ virtual int child_iteration(int64_t usec) = 0;
int is_running();
virtual void run() {}
};
#include "pluginlv2client.h"
#include "pluginlv2ui.h"
-int PluginLV2UI::run(int ac, char **av)
-{
- int sample_rate = 48000;
- if( ac > 2 ) sample_rate = atoi(av[2]);
- if( init_ui(av[1], sample_rate) ) {
- fprintf(stderr," init_ui failed\n");
- return 1;
- }
- start_gui();
- return run_ui();
-}
-
-int PluginLV2ChildUI::run(int ac, char **av)
-{
- signal(SIGINT, SIG_IGN);
- ForkBase::child_fd = atoi(av[1]);
- ForkBase::parent_fd = atoi(av[2]);
- ForkBase::ppid = atoi(av[3]);
- return run_ui(this);
-}
-
-
int main(int ac, char **av)
{
// to grab this task in the debugger
signals.initialize("/tmp/lv2ui_%d.dmp");
BC_Signals::set_catch_segv(1);
}
- gtk_set_locale();
- gtk_init(&ac, &av);
- return ac < 3 ?
- PluginLV2UI().run(ac, av) :
- PluginLV2ChildUI().run(ac, av);
+ return PluginLV2ChildUI().run(ac, av);
+}
+
+int PluginLV2ChildUI::run(int ac, char **av)
+{
+ this->ac = ac;
+ this->av = av;
+
+ if( ac > 3 ) {
+ signal(SIGINT, SIG_IGN);
+ ForkBase::child_fd = atoi(av[1]);
+ ForkBase::parent_fd = atoi(av[2]);
+ ForkBase::ppid = atoi(av[3]);
+ }
+ else {
+ int sample_rate = 48000;
+ if( ac > 2 ) sample_rate = atoi(av[2]);
+ if( init_ui(av[1], sample_rate) ) {
+ fprintf(stderr," init_ui failed\n");
+ return 1;
+ }
+ start_gui();
+ }
+ return run_ui();
}
max_bufsz = 0;
ui_features = 0;
+ samplerate = 44100;
+ refreshrate = 30.;
+ block_length = 4096;
+ midi_buf_size = 8192;
+
world = 0;
lilv = 0;
lilv_uis = 0;
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;
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;
}
}
}
+
+ 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));
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 &&
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;
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;
}
}
+// 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()
_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
#define PORTS_OUTPUT 0x10
#define PORTS_UPDATE 0x20
+class PluginLV2Options : public ArrayList<LV2_Options_Option>
+{
+public:
+ PluginLV2Options() {}
+ ~PluginLV2Options() {}
+ void add(LV2_URID key, unsigned sz, LV2_URID typ, void *vp) {
+ LV2_Options_Option *ap = &append();
+ ap->context = LV2_OPTIONS_INSTANCE; ap->subject = 0;
+ ap->key = key; ap->size = sz; ap->type = typ; ap->value = vp;
+ }
+};
+
+class PluginLV2Work
+{
+public:
+ PluginLV2Work();
+ ~PluginLV2Work();
+ void load(const void *vp, unsigned size);
+
+ PluginLV2Work *next;
+ unsigned alloc, used;
+ char *data;
+};
+
class PluginLV2
{
public:
void reset_lv2();
int load_lv2(const char *path,char *title=0);
int init_lv2(PluginLV2ClientConfig &conf, int sample_rate);
+ virtual int is_forked() { return 0; }
static LV2_URID uri_table_map(LV2_URID_Map_Handle handle, const char *uri);
static const char *uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid);
+ static uint32_t uri_to_id(LV2_URI_Map_Callback_Data callback_data,
+ const char *map, const char *uri);
void connect_ports(PluginLV2ClientConfig &conf, int ports);
void del_buffer();
void new_buffer(int64_t sz);
const LilvPlugin *lilv;
LilvUIs *lilv_uis;
+ LV2_URI_Map_Feature uri_map;
PluginLV2UriTable uri_table;
LV2_URID_Map map;
LV2_Feature map_feature;
LV2_URID_Unmap unmap;
LV2_Feature unmap_feature;
+
+ PluginLV2Options options;
Lv2Features features;
LV2_Atom_Sequence seq_in[2];
LV2_Atom_Sequence *seq_out;
+ float samplerate, refreshrate;
+ int block_length, midi_buf_size;
LilvInstance *inst;
SuilInstance *sinst;
LilvNode *powerOf2BlockLength;
LilvNode *fixedBlockLength;
LilvNode *boundedBlockLength;
+
+ LV2_URID atom_int;
+ LV2_URID atom_float;
+
+ LV2_URID param_sampleRate;
+ LV2_URID bufsz_minBlockLength;
+ LV2_URID bufsz_maxBlockLength;
+ LV2_URID bufsz_sequenceSize;
+ LV2_URID ui_updateRate;
+
+ pthread_t worker_thread;
+ LV2_Worker_Interface *worker_iface;
+ static void *worker_func(void *vp);
+ void *worker_func();
+ void worker_start();
+ void worker_stop();
+ LV2_Worker_Status worker_schedule(uint32_t inp_size, const void *inp_data);
+ static LV2_Worker_Status lv2_worker_schedule(LV2_Worker_Schedule_Handle vp,
+ uint32_t inp_size, const void *inp_data);
+ LV2_Worker_Status worker_respond(uint32_t out_size, const void *out_data);
+ static LV2_Worker_Status lv2_worker_respond(LV2_Worker_Respond_Handle vp,
+ uint32_t out_size, const void *out_data);
+ PluginLV2Work *get_work();
+ void work_stop(PluginLV2Work *&work);
+ void worker_responses();
+
+ LV2_Worker_Schedule schedule;
+ PluginLV2Work *work_avail, *work_input;
+ PluginLV2Work *work_output, **work_tail;
+ pthread_mutex_t worker_lock;
+ pthread_cond_t worker_ready;
+ int worker_done;
};
typedef struct { int sample_rate; char path[1]; } open_bfr_t;
}
+PluginLV2BlackList::PluginLV2BlackList(const char *path)
+{
+ set_array_delete();
+ char lv2_blacklist_path[BCTEXTLEN];
+ sprintf(lv2_blacklist_path, "%s/%s", File::get_cindat_path(), path);
+ FILE *bfp = fopen(lv2_blacklist_path, "r");
+ if( !bfp ) return;
+ while( fgets(lv2_blacklist_path, sizeof(lv2_blacklist_path), bfp) ) {
+ if( lv2_blacklist_path[0] == '#' ) continue;
+ int len = strlen(lv2_blacklist_path);
+ if( len > 0 && lv2_blacklist_path[len-1] == '\n' )
+ lv2_blacklist_path[len-1] = 0;
+ if( !lv2_blacklist_path[0] ) continue;
+ append(cstrdup(lv2_blacklist_path));
+ }
+ fclose(bfp);
+}
+
+PluginLV2BlackList::~PluginLV2BlackList()
+{
+ remove_all_objects();
+}
+
+int PluginLV2BlackList::is_badboy(const char *uri)
+{
+ for( int i=size(); --i>=0; )
+ if( !strcmp(uri, get(i)) ) return 1;
+ return 0;
+}
+
PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name)
{
return new PluginServer(mwindow, name, PLUGIN_TYPE_LV2);
int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp)
{
printf("init lv2 index:\n");
+ PluginLV2BlackList blacklist("lv2_blacklist.txt");
+
LilvWorld *world = lilv_world_new();
lilv_world_load_all(world);
const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
LILV_FOREACH(plugins, i, all_plugins) {
const LilvPlugin *lilv = lilv_plugins_get(all_plugins, i);
const char *uri = lilv_node_as_uri(lilv_plugin_get_uri(lilv));
+ if( blacklist.is_badboy(uri) ) continue;
+printf("LOAD: %s\n", uri);
PluginServer server(mwindow, uri, PLUGIN_TYPE_LV2);
int result = server.open_plugin(1, preferences, 0, 0);
if( !result ) {
return 0;
}
-
-// stub in parent
-int PluginLV2ChildUI::handle_child() { return 0; }
-void PluginLV2UI::reset_gui() {}
-
ForkChild *PluginLV2ParentUI::new_fork()
{
return new PluginLV2ChildUI();
}
+// stub in parent
+int PluginLV2ChildUI::child_iteration(int64_t usec) { return -1; }
+int PluginLV2ChildUI::send_host(int64_t token, const void *data, int bytes) { return -1; }
#else
#include "mwindow.h"
static PluginLV2UIs plugin_lv2;
};
+class PluginLV2BlackList : public ArrayList<const char *>
+{
+public:
+ PluginLV2BlackList(const char *path);
+ ~PluginLV2BlackList();
+
+ int is_badboy(const char *uri);
+};
+
class PluginLV2Client : public PluginAClient, public PluginLV2
{
public:
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
#include <lv2/lv2plug.in/ns/ext/log/log.h>
#include <lv2/lv2plug.in/ns/ext/options/options.h>
+#include <lv2/lv2plug.in/ns/ext/parameters/parameters.h>
#include <lv2/lv2plug.in/ns/ext/buf-size/buf-size.h>
#include <lv2/lv2plug.in/ns/ext/uri-map/uri-map.h>
#include <lv2/lv2plug.in/ns/ext/data-access/data-access.h>
~PluginLV2UriTable();
LV2_URID map(const char *uri);
const char *unmap(LV2_URID urid);
+ operator LV2_URID_Map_Handle() { return (LV2_URID_Map_Handle)this; }
};
#endif
add_subwindow(slider = new PluginLV2ClientSlider(this, x1, y+10));
y += pot->get_h() + 10;
- client->init_lv2();
client->load_configuration();
client->config.update();
#include <ctype.h>
#include <string.h>
-int PluginLV2UI::init_ui(const char *path, int sample_rate)
-{
- if( load_lv2(path, title) ) return 1;
- if( init_lv2(config, sample_rate) ) return 1;
-
- lilv_uis = lilv_plugin_get_uis(lilv);
- if( !lilv_uis ) {
- printf("lv2: lilv_plugin_get_uis(%s) failed\n", path);
- return 1;
- }
-
- if( gtk_type ) {
- LilvNode *gui_type = lilv_new_uri(world, gtk_type);
- LILV_FOREACH(uis, i, lilv_uis) {
- const LilvUI *ui = lilv_uis_get(lilv_uis, i);
- if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
- lilv_ui = ui;
- break;
- }
- }
- lilv_node_free(gui_type);
- }
- if( !lilv_ui )
- lilv_ui = lilv_uis_get(lilv_uis, lilv_uis_begin(lilv_uis));
- if( !lilv_ui ) {
- printf("lv2_gui: init_ui failed: %s\n", title);
- return 1;
- }
-
- lilv_instance_activate(inst);
- return 0;
-}
-
void PluginLV2UI::update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
{
if( idx >= config.nb_ports ) return;
PluginLV2Client_Opt *opt = config[i];
if( opt->idx == idx ) {
opt->set_value(*(const float*)bfr);
- updates = UPDATE_HOST;
+ updates |= UPDATE_HOST;
break;
}
}
}
-#if 0
-void PluginLV2UI::touch(void *obj, uint32_t pidx, bool grabbed)
-{
- PluginLV2UI* the = (PluginLV2GUI*)obj;
- int idx = pidx;
- if( idx >= the->config.nb_ports ) return;
-printf("%s %s(%u)\n", (grabbed? _("press") : _("release")),
- the->config.names[idx], idx);
-}
-#endif
-
uint32_t PluginLV2UI::uri_to_id(LV2_URID_Map_Handle handle, const char *map, const char *uri)
{
return ((PluginLV2UriTable *)handle)->map(uri);
PluginLV2UI::write_from_ui,
PluginLV2UI::port_index,
0, 0);
-// suil_host_set_touch_func(ui_host,
-// PluginLV2GUI::touch);
+// suil_host_set_touch_func(ui_host, cb_touch);
}
features.remove(); // remove terminating zero
features.append(new Lv2Feature(LV2_DATA_ACCESS_URI, ext_data));
features.append(new Lv2Feature(LV2_UI__parent, parent));
features.append(new Lv2Feature(LV2_UI__idleInterface, 0));
+ features.append(new Lv2Feature(LV2_EXTERNAL_UI_URI, &extui_host));
+ features.append(new Lv2Feature(LV2_EXTERNAL_UI_URI__KX__Host, &extui_host));
features.append(0); // add new terminating zero
const char* bundle_uri = lilv_node_as_uri(lilv_ui_get_bundle_uri(lilv_ui));
char* bundle_path = lilv_file_uri_parse(bundle_uri, NULL);
const char* binary_uri = lilv_node_as_uri(lilv_ui_get_binary_uri(lilv_ui));
char* binary_path = lilv_file_uri_parse(binary_uri, NULL);
- sinst = suil_instance_new(ui_host, this, gtk_type,
+ sinst = suil_instance_new(ui_host, this, ui_type,
lilv_node_as_uri(lilv_plugin_get_uri(lilv)),
lilv_node_as_uri(lilv_ui_get_uri(lilv_ui)),
lilv_node_as_uri(lilv_type),
return !fs_matches && !nrs_matches;
}
-int PluginLV2UI::send_host(int64_t token, const void *data, int bytes)
-{
- return !child ? 0 : child->send_parent(token, data, bytes);
-}
-
int PluginLV2UI::update_lv2_input(float *vals, int force)
{
int ret = 0;
}
}
-static void lilv_destroy(GtkWidget* widget, gpointer data)
+void PluginLV2UI::run_lilv(int samples)
{
- PluginLV2UI *the = (PluginLV2UI*)data;
- the->top_level = 0;
-}
+ float ctls[config.nb_ports];
+ for( int i=0; i<config.nb_ports; ++i ) ctls[i] = config.ctls[i];
-void PluginLV2UI::start_gui()
-{
- if( !hidden ) return;
- top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- g_signal_connect(top_level, "destroy", G_CALLBACK(lilv_destroy), this);
- gtk_window_set_title(GTK_WINDOW(top_level), title);
+ lilv_instance_run(inst, samples);
- GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
- gtk_window_set_role(GTK_WINDOW(top_level), "plugin_ui");
- gtk_container_add(GTK_CONTAINER(top_level), vbox);
+ if( worker_iface ) worker_responses();
- GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
- gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
- gtk_widget_show(alignment);
- lv2ui_instantiate(alignment);
- GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(sinst);
- gtk_container_add(GTK_CONTAINER(alignment), widget);
- gtk_window_set_resizable(GTK_WINDOW(top_level), lv2ui_resizable());
- gtk_widget_show_all(vbox);
- gtk_widget_grab_focus(widget);
+ for( int i=0; i<config.nb_ports; ++i ) {
+ if( !(config.ports[i] & PORTS_OUTPUT) ) continue;
+ if( !(config.ports[i] & PORTS_CONTROL) ) continue;
+ if( config.ctls[i] == ctls[i] ) continue;
+ config.ports[i] |= PORTS_UPDATE;
+ updates |= UPDATE_PORTS;
+ }
+}
+
+void PluginLV2ChildUI::start_gui()
+{
+ gui->start_gui();
update_lv2_input(config.ctls, 1);
- connect_ports(config, PORTS_CONTROL);
+ connect_ports(config, PORTS_CONTROL | PORTS_ATOM);
updates = 0;
run_lilv(0);
- gtk_window_present(GTK_WINDOW(top_level));
send_host(LV2_SHOW, 0, 0);
hidden = 0;
}
send_host(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
}
-void PluginLV2UI::reset_gui()
+
+static void lv2ui_extui_host_ui_closed(void *p)
+{
+ PluginLV2UI *ui = (PluginLV2UI *)p;
+ ui->hidden = -1;
+}
+
+int PluginLV2ChildUI::init_ui(const char *path, int sample_rate)
+{
+ char *title = PluginLV2UI::title;
+ if( load_lv2(path, title) ) return 1;
+ if( init_lv2(config, sample_rate) ) return 1;
+
+ lilv_uis = lilv_plugin_get_uis(lilv);
+ if( !lilv_uis ) {
+ printf("lv2: lilv_plugin_get_uis(%s) failed\n", path);
+ return 1;
+ }
+
+ if( !gui && wgt_type ) {
+ LilvNode *gui_type = lilv_new_uri(world, wgt_type);
+ LILV_FOREACH(uis, i, lilv_uis) {
+ const LilvUI *ui = lilv_uis_get(lilv_uis, i);
+ if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
+ extui_host.ui_closed = lv2ui_extui_host_ui_closed;
+ gui = new PluginLV2ChildWgtUI(this);
+ lilv_ui = ui;
+ ui_type = wgt_type;
+ break;
+ }
+ }
+ lilv_node_free(gui_type);
+ }
+ if( !gui && gtk_type ) {
+ LilvNode *gui_type = lilv_new_uri(world, gtk_type);
+ LILV_FOREACH(uis, i, lilv_uis) {
+ const LilvUI *ui = lilv_uis_get(lilv_uis, i);
+ if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
+ gui = new PluginLV2ChildGtkUI(this);
+ lilv_ui = ui;
+ ui_type = gtk_type;
+ break;
+ }
+ }
+ lilv_node_free(gui_type);
+ ui_type = gtk_type;
+ }
+
+ if( !gui ) {
+ printf("lv2_gui: init_ui failed: %s\n", title);
+ return 1;
+ }
+
+ lilv_instance_activate(inst);
+ return 0;
+}
+
+void PluginLV2ChildUI::reset_gui()
{
- if( hidden ) return;
+ gui->reset_gui();
if( sinst ) { suil_instance_free(sinst); sinst = 0; }
if( ui_host ) { suil_host_free(ui_host); ui_host = 0; }
- if( top_level ) { gtk_widget_destroy(top_level); top_level = 0; }
while( features.size() > ui_features ) features.remove_object();
features.append(0);
- hidden = 1;
send_host(LV2_HIDE, 0, 0);
+ hidden = 1;
}
-
-// child main
-int PluginLV2UI::run_ui(PluginLV2ChildUI *child)
+// main loop
+int PluginLV2ChildUI::run_ui()
{
- this->child = child;
+ double last_time = 0, fps = 30;
+ int64_t frame_usecs = 1e6 / fps;
running = 1;
done = 0;
while( !done ) {
- if( gtk_events_pending() ) {
- gtk_main_iteration();
- continue;
- }
- if( !top_level && !hidden )
+ if( hidden < 0 ) {
+ gui->top_level = 0;
reset_gui();
+ done = -1;
+ break;
+ }
if( updates ) {
if( (updates & UPDATE_PORTS) )
update_lv2_output();
update_host();
updates = 0;
}
- if( !child ) usleep(10000);
- else if( child->child_iteration() < 0 )
+ struct timeval tv; gettimeofday(&tv, 0);
+ double t = tv.tv_sec + tv.tv_usec / 1e6;
+ double dt = t - last_time; last_time = t;
+ int64_t usec = frame_usecs - dt*1e6;
+ if( usec < 0 ) usec = 0;
+ if( child_iteration(usec) < 0 )
done = 1;
}
running = 0;
return 0;
}
-void PluginLV2UI::run_lilv(int samples)
-{
- float ctls[config.nb_ports];
- for( int i=0; i<config.nb_ports; ++i ) ctls[i] = config.ctls[i];
-
- lilv_instance_run(inst, samples);
-
- for( int i=0; i<config.nb_ports; ++i ) {
- if( !(config.ports[i] & PORTS_OUTPUT) ) continue;
- if( !(config.ports[i] & PORTS_CONTROL) ) continue;
- if( config.ctls[i] == ctls[i] ) continue;
- config.ports[i] |= PORTS_UPDATE;
- updates |= UPDATE_PORTS;
- }
-}
-
void PluginLV2ChildUI::run_buffer(int shmid)
{
if( !shm_buffer(shmid) ) return;
shm_bfr->done = 1;
}
-int PluginLV2ChildUI::handle_child()
-{
- switch( child_token ) {
- case LV2_OPEN: {
- open_bfr_t *open_bfr = (open_bfr_t *)child_data;
- if( init_ui(open_bfr->path, open_bfr->sample_rate) ) exit(1);
- break; }
- case LV2_LOAD: {
- float *vals = (float *)child_data;
- update_lv2_input(vals, 1);
- break; }
- case LV2_UPDATE: {
- float *vals = (float *)child_data;
- update_lv2_input(vals, 0);
- break; }
- case LV2_SHOW: {
- start_gui();
- break; }
- case LV2_HIDE: {
- reset_gui();
- break; }
- case LV2_SHMID: {
- int shmid = *(int *)child_data;
- run_buffer(shmid);
- send_parent(LV2_SHMID, 0, 0);
- break; }
- case EXIT_CODE:
- return -1;
+int PluginLV2ChildUI::child_iteration(int64_t usec)
+{
+ int ret = 0;
+ if( is_forked() )
+ ret = read_child(usec);
+ else
+ usleep(usec);
+ if( ret > 0 ) {
+ switch( child_token ) {
+ case LV2_OPEN: {
+ open_bfr_t *open_bfr = (open_bfr_t *)child_data;
+ if( init_ui(open_bfr->path, open_bfr->sample_rate) ) {
+ printf("lv2ui: unable to init: %s\n", open_bfr->path);
+ exit(1);
+ }
+ break; }
+ case LV2_LOAD: {
+ float *vals = (float *)child_data;
+ update_lv2_input(vals, 1);
+ break; }
+ case LV2_UPDATE: {
+ float *vals = (float *)child_data;
+ update_lv2_input(vals, 0);
+ break; }
+ case LV2_SHOW: {
+ start_gui();
+ break; }
+ case LV2_HIDE: {
+ reset_gui();
+ break; }
+ case LV2_SHMID: {
+ int shmid = *(int *)child_data;
+ run_buffer(shmid);
+ send_parent(LV2_SHMID, 0, 0);
+ break; }
+ case EXIT_CODE:
+ return -1;
+ }
+ }
+ if( ret >= 0 && gui )
+ gui->handle_child();
+ return ret;
+}
+
+int PluginLV2ChildUI::send_host(int64_t token, const void *data, int bytes)
+{
+ return is_forked() ? send_parent(token, data, bytes) : 0;
+}
+
+PluginLV2GUI::PluginLV2GUI(PluginLV2ChildUI *child_ui)
+{
+ this->child_ui = child_ui;
+ top_level = 0;
+}
+
+PluginLV2GUI::~PluginLV2GUI()
+{
+}
+
+PluginLV2ChildGtkUI::PluginLV2ChildGtkUI(PluginLV2ChildUI *child_ui)
+ : PluginLV2GUI(child_ui)
+{
+ gtk_set_locale();
+ gtk_init(&child_ui->ac, &child_ui->av);
+}
+
+PluginLV2ChildGtkUI::~PluginLV2ChildGtkUI()
+{
+}
+
+void PluginLV2ChildGtkUI::reset_gui()
+{
+ GtkWidget *top_level = (GtkWidget *)this->top_level;
+ if( top_level ) { gtk_widget_destroy(top_level); this->top_level = 0; }
+}
+
+static void lilv_gtk_destroy(GtkWidget* widget, gpointer data)
+{
+ PluginLV2ChildGtkUI *gui = (PluginLV2ChildGtkUI*)data;
+ gui->child_ui->hidden = -1;
+}
+
+void PluginLV2ChildGtkUI::start_gui()
+{
+ this->top_level = (void *)gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ GtkWidget *top_level = (GtkWidget *)this->top_level;
+ g_signal_connect(top_level, "destroy", G_CALLBACK(lilv_gtk_destroy), this);
+ char *title = child_ui->PluginLV2UI::title;
+ gtk_window_set_title(GTK_WINDOW(top_level), title);
+
+ GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
+ gtk_window_set_role(GTK_WINDOW(top_level), "plugin_ui");
+ gtk_container_add(GTK_CONTAINER(top_level), vbox);
+
+ GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
+ gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
+ gtk_widget_show(alignment);
+ child_ui->lv2ui_instantiate(alignment);
+ GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(child_ui->sinst);
+ gtk_container_add(GTK_CONTAINER(alignment), widget);
+ gtk_window_set_resizable(GTK_WINDOW(top_level), child_ui->lv2ui_resizable());
+ gtk_widget_show_all(vbox);
+ gtk_widget_grab_focus(widget);
+ gtk_window_present(GTK_WINDOW(top_level));
+}
+
+int PluginLV2ChildGtkUI::handle_child()
+{
+ if( gtk_events_pending() ) {
+ gtk_main_iteration();
}
return 1;
}
+PluginLV2ChildWgtUI::PluginLV2ChildWgtUI(PluginLV2ChildUI *child_ui)
+ : PluginLV2GUI(child_ui)
+{
+}
+
+PluginLV2ChildWgtUI::~PluginLV2ChildWgtUI()
+{
+}
+
+void PluginLV2ChildWgtUI::reset_gui()
+{
+ lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
+ if( top_level ) { LV2_EXTERNAL_UI_HIDE(top_level); this->top_level = 0; }
+}
+
+void PluginLV2ChildWgtUI::start_gui()
+{
+ child_ui->lv2ui_instantiate(0);
+ this->top_level = (void *)suil_instance_get_widget(child_ui->sinst);
+ lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
+ if( top_level ) LV2_EXTERNAL_UI_SHOW(top_level);
+}
+
+int PluginLV2ChildWgtUI::handle_child()
+{
+ lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
+ if( top_level )
+ LV2_EXTERNAL_UI_RUN(top_level);
+ return 1;
+}
+
#include "pluginlv2gui.inc"
#include "pluginlv2ui.inc"
+//=== lv2_external_ui.h
+#define LV2_EXTERNAL_UI_URI "http://lv2plug.in/ns/extensions/ui#external"
+#define LV2_EXTERNAL_UI_URI__KX__Host "http://kxstudio.sf.net/ns/lv2ext/external-ui#Host"
+
+typedef struct _lv2_external_ui lv2_external_ui;
+struct _lv2_external_ui {
+ void (*run)(lv2_external_ui *ui);
+ void (*show)(lv2_external_ui *ui);
+ void (*hide)(lv2_external_ui *ui);
+ void *self;
+};
+
+typedef struct _lv2_external_ui_host lv2_external_ui_host;
+struct _lv2_external_ui_host {
+ void (*ui_closed)(void* controller);
+ const char *plugin_human_id;
+};
+
+#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr)
+#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr)
+#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr)
+//===
+
typedef struct _GtkWidget GtkWidget;
#define UPDATE_HOST 1
const LilvNode *lilv_type;
LV2_Extension_Data_Feature *ext_data;
- PluginLV2UriTable uri_table;
- LV2_URI_Map_Feature *uri_map;
- LV2_URID_Map map;
- LV2_URID_Unmap unmap;
+ lv2_external_ui_host extui_host;
+ const char *wgt_type;
+ const char *gtk_type;
+ const char *ui_type;
char title[BCSTRLEN];
PluginLV2ClientConfig config;
- int updates, hidden;
int done, running;
- const char *gtk_type;
- GtkWidget *top_level;
- PluginLV2ChildUI *child;
+ int hidden, updates;
- void reset_gui();
- int init_ui(const char *path, int sample_rate);
void start();
void stop();
- int send_host(int64_t token, const void *data, int bytes);
int update_lv2_input(float *vals, int force);
void update_lv2_output();
+ virtual int send_host(int64_t token, const void *data, int bytes) = 0;
+ virtual int child_iteration(int64_t usec) { return -1; }
void run_lilv(int samples);
void update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr);
static const char *uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid);
void lv2ui_instantiate(void *parent);
bool lv2ui_resizable();
- void start_gui();
- int run_ui(PluginLV2ChildUI *child=0);
void update_host();
- int run(int ac, char **av);
+};
+
+class PluginLV2GUI
+{
+public:
+ PluginLV2GUI(PluginLV2ChildUI *child_ui);
+ virtual ~PluginLV2GUI();
+ virtual void reset_gui() = 0;
+ virtual void start_gui() = 0;
+ virtual int handle_child() = 0;
+
+ PluginLV2ChildUI *child_ui;
+ void *top_level;
+};
+
+class PluginLV2ChildGtkUI : public PluginLV2GUI
+{
+public:
+ PluginLV2ChildGtkUI(PluginLV2ChildUI *child_ui);
+ ~PluginLV2ChildGtkUI();
+
+ void reset_gui();
+ void start_gui();
+ int handle_child();
+};
+
+class PluginLV2ChildWgtUI : public PluginLV2GUI
+{
+public:
+ PluginLV2ChildWgtUI(PluginLV2ChildUI *child_ui);
+ ~PluginLV2ChildWgtUI();
+
+ void reset_gui();
+ void start_gui();
+ int handle_child();
};
class PluginLV2ChildUI : public ForkChild, public PluginLV2UI
public:
PluginLV2ChildUI();
~PluginLV2ChildUI();
- void run();
- void run_buffer(int shmid);
+ int init_ui(const char *path, int sample_rate);
+ void reset_gui();
+ void start_gui();
int handle_child();
+
+ int is_forked() { return ppid; }
+ int send_host(int64_t token, const void *data, int bytes);
+ int child_iteration(int64_t usec);
+ void run();
int run(int ac, char **av);
+ int run_ui();
+ void run_buffer(int shmid);
+
+ int ac;
+ char **av;
+ PluginLV2GUI *gui;
};
#endif