aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2016-09-17 13:23:59 -0400
committerDavid Robillard <d@drobilla.net>2016-09-17 13:23:59 -0400
commitbb9372f2df47956b74cf9b7f08b7ec0c96f88b23 (patch)
treec821a4c1001cc211d0aea260f965e485e899c72c
parent25cdc0a3a8a7a947b66ea95e56afb306f5d0bbe6 (diff)
downloadjalv-bb9372f2df47956b74cf9b7f08b7ec0c96f88b23.tar.gz
jalv-bb9372f2df47956b74cf9b7f08b7ec0c96f88b23.tar.bz2
jalv-bb9372f2df47956b74cf9b7f08b7ec0c96f88b23.zip
Unify control port and parameter implementations
-rw-r--r--src/control.c40
-rw-r--r--src/jalv.c172
-rw-r--r--src/jalv_gtk.c282
-rw-r--r--src/jalv_internal.h22
-rw-r--r--src/jalv_qt.cpp11
5 files changed, 307 insertions, 220 deletions
diff --git a/src/control.c b/src/control.c
index 3a0a0a0..ed26ff1 100644
--- a/src/control.c
+++ b/src/control.c
@@ -38,11 +38,17 @@ new_port_control(Jalv* jalv, uint32_t index)
ControlID* id = (ControlID*)calloc(1, sizeof(ControlID));
id->jalv = jalv;
id->type = PORT;
+ id->node = lilv_node_duplicate(lilv_port_get_node(plug, lport));
+ id->symbol = lilv_node_duplicate(lilv_port_get_symbol(plug, lport));
+ id->label = lilv_port_get_name(plug, lport);
id->index = index;
+ id->group = lilv_port_get(plug, lport, jalv->nodes.pg_group);
id->min = lilv_port_get(plug, lport, nodes->lv2_minimum);
id->max = lilv_port_get(plug, lport, nodes->lv2_maximum);
id->def = lilv_port_get(plug, lport, nodes->lv2_default);
id->value_type = jalv->forge.Float;
+ id->is_writable = lilv_port_is_a(plug, lport, nodes->lv2_InputPort);
+ id->is_readable = lilv_port_is_a(plug, lport, nodes->lv2_OutputPort);
id->is_toggle = lilv_port_has_property(plug, lport, nodes->lv2_toggled);
id->is_integer = lilv_port_has_property(plug, lport, nodes->lv2_integer);
id->is_enumeration = lilv_port_has_property(plug, lport, nodes->lv2_enumeration);
@@ -63,6 +69,32 @@ new_port_control(Jalv* jalv, uint32_t index)
}
}
+ /* Get scale points */
+ LilvScalePoints* sp = lilv_port_get_scale_points(plug, lport);
+ if (sp) {
+ id->points = (ScalePoint*)malloc(
+ lilv_scale_points_size(sp) * sizeof(ScalePoint));
+ size_t np = 0;
+ LILV_FOREACH(scale_points, s, sp) {
+ const LilvScalePoint* p = lilv_scale_points_get(sp, s);
+ if (lilv_node_is_float(lilv_scale_point_get_value(p)) ||
+ lilv_node_is_int(lilv_scale_point_get_value(p))) {
+ id->points[np].value = lilv_node_as_float(
+ lilv_scale_point_get_value(p));
+ id->points[np].label = strdup(
+ lilv_node_as_string(lilv_scale_point_get_label(p)));
+ ++np;
+ }
+ /* TODO: Non-float scale points? */
+ }
+
+ qsort(id->points, np, sizeof(ScalePoint),
+ (int (*)(const void*, const void*))scale_point_cmp);
+ id->n_points = np;
+
+ lilv_scale_points_free(sp);
+ }
+
return id;
}
@@ -82,6 +114,9 @@ new_property_control(Jalv* jalv, const LilvNode* property)
ControlID* id = (ControlID*)calloc(1, sizeof(ControlID));
id->jalv = jalv;
id->type = PROPERTY;
+ id->node = lilv_node_duplicate(property);
+ id->symbol = lilv_world_get_symbol(jalv->world, property);
+ id->label = lilv_world_get(jalv->world, property, jalv->nodes.rdfs_label, NULL);
id->property = jalv->map.map(jalv, lilv_node_as_uri(property));
id->min = lilv_world_get(jalv->world, property, jalv->nodes.lv2_minimum, NULL);
@@ -104,6 +139,11 @@ new_property_control(Jalv* jalv, const LilvNode* property)
id->is_integer = (id->value_type == jalv->forge.Int ||
id->value_type == jalv->forge.Long);
+ const size_t sym_len = strlen(lilv_node_as_string(id->symbol));
+ if (sym_len > jalv->longest_sym) {
+ jalv->longest_sym = sym_len;
+ }
+
if (!id->value_type) {
fprintf(stderr, "Unknown value type for property <%s>\n",
lilv_node_as_string(property));
diff --git a/src/jalv.c b/src/jalv.c
index 92b5e17..316af17 100644
--- a/src/jalv.c
+++ b/src/jalv.c
@@ -232,6 +232,10 @@ create_port(Jalv* jalv,
if (lilv_port_is_a(jalv->plugin, port->lilv_port, jalv->nodes.lv2_ControlPort)) {
port->type = TYPE_CONTROL;
port->control = isnan(default_value) ? 0.0f : default_value;
+ if (jalv->opts.show_hidden ||
+ !lilv_port_has_property(jalv->plugin, port->lilv_port, jalv->nodes.pprops_notOnGUI)) {
+ add_control(&jalv->controls, new_port_control(jalv, port->index));
+ }
} else if (lilv_port_is_a(jalv->plugin, port->lilv_port,
jalv->nodes.lv2_AudioPort)) {
port->type = TYPE_AUDIO;
@@ -345,6 +349,18 @@ jalv_port_by_symbol(Jalv* jalv, const char* sym)
return NULL;
}
+ControlID*
+jalv_control_by_symbol(Jalv* jalv, const char* sym)
+{
+ for (size_t i = 0; i < jalv->controls.n_controls; ++i) {
+ if (!strcmp(lilv_node_as_string(jalv->controls.controls[i]->symbol),
+ sym)) {
+ return jalv->controls.controls[i];
+ }
+ }
+ return NULL;
+}
+
static void
print_control_value(Jalv* jalv, const struct Port* port, float value)
{
@@ -377,7 +393,6 @@ activate_port(Jalv* jalv,
/* Connect the port based on its type */
switch (port->type) {
case TYPE_CONTROL:
- print_control_value(jalv, port, port->control);
lilv_instance_connect_port(jalv->instance, port_index, &port->control);
break;
case TYPE_AUDIO:
@@ -428,6 +443,96 @@ activate_port(Jalv* jalv,
#endif
}
+void
+jalv_create_controls(Jalv* jalv, bool writable)
+{
+ const LilvPlugin* plugin = jalv->plugin;
+ LilvWorld* world = jalv->world;
+ LilvNode* patch_writable = lilv_new_uri(world, LV2_PATCH__writable);
+ LilvNode* patch_readable = lilv_new_uri(world, LV2_PATCH__readable);
+
+ LilvNodes* properties = lilv_world_find_nodes(
+ world,
+ lilv_plugin_get_uri(plugin),
+ writable ? patch_writable : patch_readable,
+ NULL);
+ LILV_FOREACH(nodes, p, properties) {
+ const LilvNode* property = lilv_nodes_get(properties, p);
+ ControlID* record = NULL;
+
+ if (!writable && lilv_world_ask(world,
+ lilv_plugin_get_uri(plugin),
+ patch_writable,
+ property)) {
+ // Find existing writable control
+ for (size_t i = 0; i < jalv->controls.n_controls; ++i) {
+ if (lilv_node_equals(jalv->controls.controls[i]->node, property)) {
+ record = jalv->controls.controls[i];
+ record->is_readable = true;
+ break;
+ }
+ }
+
+ if (record) {
+ continue;
+ }
+ }
+
+ record = new_property_control(jalv, property);
+ if (writable) {
+ record->is_writable = true;
+ } else {
+ record->is_readable = true;
+ }
+
+ if (record->value_type) {
+ add_control(&jalv->controls, record);
+ } else {
+ fprintf(stderr, "Parameter <%s> has unknown value type, ignored\n",
+ lilv_node_as_string(record->node));
+ free(record);
+ }
+ }
+ lilv_nodes_free(properties);
+
+ lilv_node_free(patch_readable);
+ lilv_node_free(patch_writable);
+}
+
+void
+jalv_set_control(const ControlID* control,
+ uint32_t size,
+ LV2_URID type,
+ const void* body)
+{
+ Jalv* jalv = control->jalv;
+ if (control->type == PORT && type == jalv->forge.Float) {
+ struct Port* port = &control->jalv->ports[control->index];
+ port->control = *(float*)body;
+ } else if (control->type == PROPERTY) {
+ // Copy forge since it is used by process thread
+ LV2_Atom_Forge forge = jalv->forge;
+ LV2_Atom_Forge_Frame frame;
+ uint8_t buf[1024];
+ lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf));
+
+ lv2_atom_forge_object(&forge, &frame, 0, jalv->urids.patch_Set);
+ lv2_atom_forge_key(&forge, jalv->urids.patch_property);
+ lv2_atom_forge_urid(&forge, control->property);
+ lv2_atom_forge_key(&forge, jalv->urids.patch_value);
+ lv2_atom_forge_atom(&forge, size, type);
+ lv2_atom_forge_write(&forge, body, size);
+
+ const LV2_Atom* atom = lv2_atom_forge_deref(&forge, frame.ref);
+ jalv_ui_write(jalv,
+ jalv->control_in,
+ lv2_atom_total_size(atom),
+ jalv->urids.atom_eventTransfer,
+ atom);
+ }
+}
+
+
/** Jack buffer size callback. */
static int
jack_buffer_size_cb(jack_nframes_t nframes, void* data)
@@ -817,17 +922,6 @@ jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent)
lilv_free(binary_path);
lilv_free(bundle_path);
-
- /* Set initial control values on UI */
- if (jalv->ui_instance) {
- for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- if (jalv->ports[i].type == TYPE_CONTROL) {
- suil_instance_port_event(jalv->ui_instance, i,
- sizeof(float), 0,
- &jalv->ports[i].control);
- }
- }
- }
}
bool
@@ -904,6 +998,36 @@ jalv_ui_port_index(SuilController controller, const char* symbol)
return port ? port->index : LV2UI_INVALID_PORT_INDEX;
}
+void
+jalv_init_ui(Jalv* jalv)
+{
+ // Set initial control port values
+ for (uint32_t i = 0; i < jalv->num_ports; ++i) {
+ if (jalv->ports[i].type == TYPE_CONTROL) {
+ jalv_ui_port_event(jalv, i,
+ sizeof(float), 0,
+ &jalv->ports[i].control);
+ }
+ }
+
+ if (jalv->control_in != (uint32_t)-1) {
+ // Send patch:Get message for initial parameters/etc
+ LV2_Atom_Forge forge = jalv->forge;
+ LV2_Atom_Forge_Frame frame;
+ uint8_t buf[1024];
+ lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf));
+ lv2_atom_forge_object(&forge, &frame, 0, jalv->urids.patch_Get);
+
+ const LV2_Atom* atom = lv2_atom_forge_deref(&forge, frame.ref);
+ jalv_ui_write(jalv,
+ jalv->control_in,
+ lv2_atom_total_size(atom),
+ jalv->urids.atom_eventTransfer,
+ atom);
+ lv2_atom_forge_pop(&forge, &frame);
+ }
+}
+
bool
jalv_update(Jalv* jalv)
{
@@ -966,13 +1090,15 @@ jalv_apply_control_arg(Jalv* jalv, const char* s)
return false;
}
- struct Port* port = jalv_port_by_symbol(jalv, sym);
- if (!port) {
- fprintf(stderr, "warning: Ignoring value for unknown port `%s'\n", sym);
+ ControlID* control = jalv_control_by_symbol(jalv, sym);
+ if (!control) {
+ fprintf(stderr, "warning: Ignoring value for unknown control `%s'\n", sym);
return false;
}
- port->control = val;
+ jalv_set_control(control, sizeof(float), jalv->urids.atom_Float, &val);
+ printf("%-*s = %f\n", jalv->longest_sym, sym, val);
+
return true;
}
@@ -992,6 +1118,7 @@ main(int argc, char** argv)
jalv.midi_buf_size = 1024; /* Should be set by jack_buffer_size_cb */
jalv.play_state = JALV_PAUSED;
jalv.bpm = 120.0f;
+ jalv.control_in = (uint32_t)-1;
if (jalv_init(&argc, &argv, &jalv.opts)) {
return EXIT_FAILURE;
@@ -1248,8 +1375,10 @@ main(int argc, char** argv)
fprintf(stderr, "UI: None\n");
}
- /* Create port structures (jalv.ports) */
+ /* Create port and control structures */
jalv_create_ports(&jalv);
+ jalv_create_controls(&jalv, true);
+ jalv_create_controls(&jalv, false);
/* Determine the name of the JACK client */
char* jack_name = NULL;
@@ -1410,6 +1539,15 @@ main(int argc, char** argv)
activate_port(&jalv, i);
}
+ /* Print initial control values */
+ for (size_t i = 0; i < jalv.controls.n_controls; ++i) {
+ ControlID* control = jalv.controls.controls[i];
+ if (control->type == PORT) {// && control->value_type == jalv->forge.Float) {
+ struct Port* port = &jalv.ports[control->index];
+ print_control_value(&jalv, port, port->control);
+ }
+ }
+
/* Activate plugin */
lilv_instance_activate(jalv.instance);
diff --git a/src/jalv_gtk.c b/src/jalv_gtk.c
index 3d7e2ab..55dafb0 100644
--- a/src/jalv_gtk.c
+++ b/src/jalv_gtk.c
@@ -487,30 +487,8 @@ set_control(const ControlID* control,
LV2_URID type,
const void* body)
{
- Jalv* jalv = control->jalv;
- if (control->type == PORT && type == jalv->forge.Float) {
- struct Port* port = &control->jalv->ports[control->index];
- port->control = *(float*)body;
- } else if (!updating && control->type == PROPERTY) {
- // Copy forge since it is used by process thread
- LV2_Atom_Forge forge = jalv->forge;
- LV2_Atom_Forge_Frame frame;
- uint8_t buf[1024];
- lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf));
-
- lv2_atom_forge_object(&forge, &frame, 0, jalv->urids.patch_Set);
- lv2_atom_forge_key(&forge, jalv->urids.patch_property);
- lv2_atom_forge_urid(&forge, control->property);
- lv2_atom_forge_key(&forge, jalv->urids.patch_value);
- lv2_atom_forge_atom(&forge, size, type);
- lv2_atom_forge_write(&forge, body, size);
-
- const LV2_Atom* atom = lv2_atom_forge_deref(&forge, frame.ref);
- jalv_ui_write(jalv,
- jalv->control_in,
- lv2_atom_total_size(atom),
- jalv->urids.atom_eventTransfer,
- atom);
+ if (!updating) {
+ jalv_set_control(control, size, type, body);
}
}
@@ -670,7 +648,11 @@ jalv_ui_port_event(Jalv* jalv,
uint32_t protocol,
const void* buffer)
{
- if (protocol == 0 && (Controller*)jalv->ports[port_index].widget) {
+ if (jalv->ui_instance) {
+ suil_instance_port_event(jalv->ui_instance, port_index,
+ buffer_size, protocol, buffer);
+ return;
+ } else if (protocol == 0 && (Controller*)jalv->ports[port_index].widget) {
control_changed(jalv,
(Controller*)jalv->ports[port_index].widget,
buffer_size,
@@ -805,7 +787,7 @@ new_controller(GtkSpinButton* spin, GtkWidget* control)
}
static Controller*
-make_combo(ControlID* record, float value, bool writable)
+make_combo(ControlID* record, float value)
{
GtkListStore* list_store = gtk_list_store_new(
2, G_TYPE_FLOAT, G_TYPE_STRING);
@@ -827,13 +809,13 @@ make_combo(ControlID* record, float value, bool writable)
gtk_combo_box_set_active(GTK_COMBO_BOX(combo), active);
g_object_unref(list_store);
- gtk_widget_set_sensitive(GTK_WIDGET(list_store), writable);
+ gtk_widget_set_sensitive(GTK_WIDGET(list_store), record->is_writable);
GtkCellRenderer* cell = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", 1, NULL);
- if (writable) {
+ if (record->is_writable) {
g_signal_connect(G_OBJECT(combo), "changed",
G_CALLBACK(combo_changed), record);
}
@@ -842,7 +824,7 @@ make_combo(ControlID* record, float value, bool writable)
}
static Controller*
-make_log_slider(ControlID* record, float value, bool writable)
+make_log_slider(ControlID* record, float value)
{
const float min = get_float(record->min, 0.0f);
const float max = get_float(record->max, 1.0f);
@@ -852,14 +834,14 @@ make_log_slider(ControlID* record, float value, bool writable)
GtkWidget* scale = new_hscale(lmin, lmax, 0.001);
GtkWidget* spin = gtk_spin_button_new_with_range(min, max, 0.000001);
- gtk_widget_set_sensitive(scale, writable);
- gtk_widget_set_sensitive(spin, writable);
+ gtk_widget_set_sensitive(scale, record->is_writable);
+ gtk_widget_set_sensitive(spin, record->is_writable);
gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
gtk_range_set_value(GTK_RANGE(scale), ldft);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value);
- if (writable) {
+ if (record->is_writable) {
g_signal_connect(G_OBJECT(scale), "value-changed",
G_CALLBACK(log_scale_changed), record);
g_signal_connect(G_OBJECT(spin), "value-changed",
@@ -870,7 +852,7 @@ make_log_slider(ControlID* record, float value, bool writable)
}
static Controller*
-make_slider(ControlID* record, float value, bool writable)
+make_slider(ControlID* record, float value)
{
const float min = get_float(record->min, 0.0f);
const float max = get_float(record->max, 1.0f);
@@ -878,8 +860,8 @@ make_slider(ControlID* record, float value, bool writable)
GtkWidget* scale = new_hscale(min, max, step);
GtkWidget* spin = gtk_spin_button_new_with_range(min, max, step);
- gtk_widget_set_sensitive(scale, writable);
- gtk_widget_set_sensitive(spin, writable);
+ gtk_widget_set_sensitive(scale, record->is_writable);
+ gtk_widget_set_sensitive(spin, record->is_writable);
if (record->is_integer) {
gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0);
@@ -901,7 +883,7 @@ make_slider(ControlID* record, float value, bool writable)
}
}
- if (writable) {
+ if (record->is_writable) {
g_signal_connect(G_OBJECT(scale), "value-changed",
G_CALLBACK(scale_changed), record);
g_signal_connect(G_OBJECT(spin), "value-changed",
@@ -912,17 +894,17 @@ make_slider(ControlID* record, float value, bool writable)
}
static Controller*
-make_toggle(ControlID* record, float value, bool writable)
+make_toggle(ControlID* record, float value)
{
GtkWidget* check = gtk_check_button_new();
- gtk_widget_set_sensitive(check, writable);
+ gtk_widget_set_sensitive(check, record->is_writable);
if (value) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
}
- if (writable) {
+ if (record->is_writable) {
g_signal_connect(G_OBJECT(check), "toggled",
G_CALLBACK(toggle_changed), record);
}
@@ -931,13 +913,12 @@ make_toggle(ControlID* record, float value, bool writable)
}
static Controller*
-make_entry(ControlID* control, bool writable)
+make_entry(ControlID* control)
{
GtkWidget* entry = gtk_entry_new();
- gtk_widget_set_sensitive(entry, writable);
-
- if (writable) {
+ gtk_widget_set_sensitive(entry, control->is_writable);
+ if (control->is_writable) {
g_signal_connect(G_OBJECT(entry), "activate",
G_CALLBACK(string_changed), control);
}
@@ -946,14 +927,14 @@ make_entry(ControlID* control, bool writable)
}
static Controller*
-make_file_chooser(ControlID* record, bool writable)
+make_file_chooser(ControlID* record)
{
GtkWidget* button = gtk_file_chooser_button_new(
"Open File", GTK_FILE_CHOOSER_ACTION_OPEN);
- gtk_widget_set_sensitive(button, writable);
+ gtk_widget_set_sensitive(button, record->is_writable);
- if (writable) {
+ if (record->is_writable) {
g_signal_connect(G_OBJECT(button), "file-set",
G_CALLBACK(file_changed), record);
}
@@ -962,18 +943,18 @@ make_file_chooser(ControlID* record, bool writable)
}
static Controller*
-make_controller(ControlID* control, float value, bool writable)
+make_controller(ControlID* control, float value)
{
Controller* controller = NULL;
if (control->is_toggle) {
- controller = make_toggle(control, value, writable);
+ controller = make_toggle(control, value);
} else if (control->is_enumeration) {
- controller = make_combo(control, value, writable);
+ controller = make_combo(control, value);
} else if (control->is_logarithmic) {
- controller = make_log_slider(control, value, writable);
+ controller = make_log_slider(control, value);
} else {
- controller = make_slider(control, value, writable);
+ controller = make_slider(control, value);
}
return controller;
@@ -1015,121 +996,43 @@ add_control_row(GtkWidget* table,
}
static int
-port_group_cmp(const void* p1, const void* p2, void* data)
+control_group_cmp(const void* p1, const void* p2, void* data)
{
- Jalv* jalv = (Jalv*)data;
- const LilvPort* port1 = *(const LilvPort**)p1;
- const LilvPort* port2 = *(const LilvPort**)p2;
-
- LilvNode* group1 = lilv_port_get(
- jalv->plugin, port1, jalv->nodes.pg_group);
- LilvNode* group2 = lilv_port_get(
- jalv->plugin, port2, jalv->nodes.pg_group);
+ const ControlID* control1 = *(const ControlID**)p1;
+ const ControlID* control2 = *(const ControlID**)p2;
- const int cmp = (group1 && group2)
- ? strcmp(lilv_node_as_string(group1), lilv_node_as_string(group2))
- : ((intptr_t)group1 - (intptr_t)group2);
-
- lilv_node_free(group2);
- lilv_node_free(group1);
+ const int cmp = (control1->group && control2->group)
+ ? strcmp(lilv_node_as_string(control1->group),
+ lilv_node_as_string(control2->group))
+ : ((intptr_t)control1->group - (intptr_t)control2->group);
return cmp;
}
-static void
-build_property_widgets(Jalv* jalv,
- GtkWidget* window,
- GtkWidget* port_table,
- int* n_rows,
- bool writable)
-{
- const LilvPlugin* plugin = jalv->plugin;
- LilvWorld* world = jalv->world;
- LilvNode* patch_writable = lilv_new_uri(world, LV2_PATCH__writable);
- LilvNode* patch_readable = lilv_new_uri(world, LV2_PATCH__readable);
-
- LilvNodes* properties = lilv_world_find_nodes(
- world,
- lilv_plugin_get_uri(plugin),
- writable ? patch_writable : patch_readable,
- NULL);
- LILV_FOREACH(nodes, p, properties) {
- const LilvNode* property = lilv_nodes_get(properties, p);
-
- if (!writable && lilv_world_ask(world,
- lilv_plugin_get_uri(plugin),
- patch_writable,
- property)) {
- continue; // Writable wridget has already been created, skip
- }
-
- LilvNode* label = lilv_nodes_get_first(
- lilv_world_find_nodes(
- world, property, jalv->nodes.rdfs_label, NULL));
-
- Controller* controller = NULL;
- ControlID* record = new_property_control(jalv, property);
- if (!record->value_type) {
- fprintf(stderr, "Unknown property range, no control shown\n");
- } else if (record->value_type == jalv->forge.String) {
- controller = make_entry(record, writable);
- } else if (record->value_type == jalv->forge.Path) {
- controller = make_file_chooser(record, writable);
- } else {
- const float val = get_float(record->def, 0.0f);
- controller = make_controller(record, val, writable);
- }
-
- record->widget = controller;
- if (controller) {
- add_control(&jalv->controls, record);
- add_control_row(
- port_table, (*n_rows)++,
- label ? lilv_node_as_string(label) : lilv_node_as_uri(property),
- controller);
- }
- }
- lilv_nodes_free(properties);
-
- lilv_node_free(patch_readable);
- lilv_node_free(patch_writable);
-}
-
static GtkWidget*
build_control_widget(Jalv* jalv, GtkWidget* window)
{
- const LilvPlugin* plugin = jalv->plugin;
- LilvWorld* world = jalv->world;
-
- LilvNode* pprop_notOnGUI = lilv_new_uri(world, LV2_PORT_PROPS__notOnGUI);
- GtkWidget* port_table = gtk_table_new(jalv->num_ports, 3, false);
+ GtkWidget* port_table = gtk_table_new(jalv->num_ports, 3, false);
- /* Make an array of control port pointers and sort it by group */
- GArray* control_ports = g_array_new(FALSE, TRUE, sizeof(LilvPort*));
- for (unsigned i = 0; i < jalv->num_ports; ++i) {
- if (jalv->ports[i].type == TYPE_CONTROL) {
- g_array_append_vals(control_ports, &jalv->ports[i].lilv_port, 1);
- }
+ /* Make an array of controls sorted by group */
+ GArray* controls = g_array_new(FALSE, TRUE, sizeof(ControlID*));
+ for (unsigned i = 0; i < jalv->controls.n_controls; ++i) {
+ g_array_append_vals(controls, &jalv->controls.controls[i], 1);
}
- g_array_sort_with_data(control_ports, port_group_cmp, jalv);
+ g_array_sort_with_data(controls, control_group_cmp, jalv);
- /* Iterate over control ports ordered by group */
+ /* Add controls in group order */
LilvNode* last_group = NULL;
int n_rows = 0;
- for (unsigned i = 0; i < control_ports->len; ++i) {
- const LilvPort* port = g_array_index(control_ports, LilvPort*, i);
- if (!jalv->opts.show_hidden &&
- lilv_port_has_property(plugin, port, pprop_notOnGUI)) {
- continue;
- }
+ for (size_t i = 0; i < controls->len; ++i) {
+ ControlID* record = g_array_index(controls, ControlID*, i);
+ Controller* controller = NULL;
+ LilvNode* group = record->group;
- uint32_t index = lilv_port_get_index(plugin, port);
- LilvNode* name = lilv_port_get_name(plugin, port);
- LilvNode* group = lilv_port_get(plugin, port, jalv->nodes.pg_group);
+ /* Check group and add new heading if necessary */
if (group && !lilv_node_equals(group, last_group)) {
- /* Group has changed, add a heading row here */
LilvNode* group_name = lilv_world_get(
- world, group, jalv->nodes.lv2_name, NULL);
+ jalv->world, group, jalv->nodes.lv2_name, NULL);
GtkWidget* group_label = new_label(
lilv_node_as_string(group_name), true, 0.0f, 1.0f);
gtk_table_attach(GTK_TABLE(port_table), group_label,
@@ -1139,63 +1042,40 @@ build_control_widget(Jalv* jalv, GtkWidget* window)
}
last_group = group;
- ControlID* control_id = new_port_control(jalv, index);
-
- /* Get scale points */
- LilvScalePoints* sp = lilv_port_get_scale_points(plugin, port);
- if (sp) {
- control_id->points = (ScalePoint*)malloc(
- lilv_scale_points_size(sp) * sizeof(ScalePoint));
- size_t np = 0;
- LILV_FOREACH(scale_points, s, sp) {
- const LilvScalePoint* p = lilv_scale_points_get(sp, s);
- if (lilv_node_is_float(lilv_scale_point_get_value(p)) ||
- lilv_node_is_int(lilv_scale_point_get_value(p))) {
- control_id->points[np].value = lilv_node_as_float(
- lilv_scale_point_get_value(p));
- control_id->points[np].label = g_strdup(
- lilv_node_as_string(lilv_scale_point_get_label(p)));
- ++np;
- }
- /* TODO: Non-float scale points? */
- }
-
- qsort(control_id->points, np, sizeof(ScalePoint),
- (int (*)(const void*, const void*))scale_point_cmp);
- control_id->n_points = np;
-
- lilv_scale_points_free(sp);
+ /* Make control widget */
+ if (record->value_type == jalv->forge.String) {
+ controller = make_entry(record);
+ } else if (record->value_type == jalv->forge.Path) {
+ controller = make_file_chooser(record);
+ } else {
+ const float val = get_float(record->def, 0.0f);
+ controller = make_controller(record, val);
}
- /* Make controller */
- struct Port* jport = &jalv->ports[index];
- Controller* controller = make_controller(
- control_id, jport->control, jport->flow == FLOW_INPUT);
-
- control_id->widget = controller;
- jport->widget = controller;
-
- /* Set tooltip text from comment, if available */
- LilvNode* comment = lilv_port_get(plugin, port, jalv->nodes.rdfs_comment);
- if (comment) {
- gtk_widget_set_tooltip_text(controller->control,
- lilv_node_as_string(comment));
+ record->widget = controller;
+ if (record->type == PORT) {
+ jalv->ports[record->index].widget = controller;
}
- lilv_node_free(comment);
-
- add_control(&jalv->controls, control_id);
- add_control_row(
- port_table, n_rows++, lilv_node_as_string(name), controller);
+ if (controller) {
+ /* Add row to table for this controller */
+ add_control_row(
+ port_table, n_rows++,
+ (record->label
+ ? lilv_node_as_string(record->label)
+ : lilv_node_as_uri(record->node)),
+ controller);
- lilv_node_free(name);
+ /* Set tooltip text from comment, if available */
+ LilvNode* comment = lilv_world_get(
+ jalv->world, record->node, jalv->nodes.rdfs_comment, NULL);
+ if (comment) {
+ gtk_widget_set_tooltip_text(controller->control,
+ lilv_node_as_string(comment));
+ }
+ lilv_node_free(comment);
+ }
}
- /* Add controllers for properties (event-based controls) */
- build_property_widgets(jalv, window, port_table, &n_rows, true);
- build_property_widgets(jalv, window, port_table, &n_rows, false);
-
- lilv_node_free(pprop_notOnGUI);
-
if (n_rows > 0) {
gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
GtkWidget* alignment = gtk_alignment_new(0.5, 0.0, 1.0, 0.0);
@@ -1329,6 +1209,8 @@ jalv_open_ui(Jalv* jalv)
box_size.height + controls_size.height);
}
+ jalv_init_ui(jalv);
+
g_timeout_add(1000 / jalv->ui_update_hz, (GSourceFunc)jalv_update, jalv);
gtk_window_present(GTK_WINDOW(window));
diff --git a/src/jalv_internal.h b/src/jalv_internal.h
index b34df86..c46a2ac 100644
--- a/src/jalv_internal.h
+++ b/src/jalv_internal.h
@@ -101,12 +101,17 @@ int scale_point_cmp(const ScalePoint* a, const ScalePoint* b);
typedef struct {
Jalv* jalv;
ControlType type;
+ LilvNode* node;
+ LilvNode* symbol; ///< Symbol
+ LilvNode* label; ///< Human readable label
LV2_URID property; ///< Iff type == PROPERTY
uint32_t index; ///< Iff type == PORT
+ LilvNode* group; ///< Port/control group, or NULL
void* widget; ///< Control Widget
size_t n_points; ///< Number of scale points
ScalePoint* points; ///< Scale points
LV2_URID value_type; ///< Type of control value
+ LV2_Atom value; ///< Current value
LilvNode* min; ///< Minimum value
LilvNode* max; ///< Maximum value
LilvNode* def; ///< Default value
@@ -114,6 +119,8 @@ typedef struct {
bool is_integer; ///< Integer values only
bool is_enumeration; ///< Point values only
bool is_logarithmic; ///< Logarithmic scale
+ bool is_writable; ///< Writable (input)
+ bool is_readable; ///< Readable (output)
} ControlID;
ControlID*
@@ -316,6 +323,18 @@ jalv_create_ports(Jalv* jalv);
struct Port*
jalv_port_by_symbol(Jalv* jalv, const char* sym);
+void
+jalv_create_controls(Jalv* jalv, bool writable);
+
+ControlID*
+jalv_control_by_symbol(Jalv* jalv, const char* sym);
+
+void
+jalv_set_control(const ControlID* control,
+ uint32_t size,
+ LV2_URID type,
+ const void* body);
+
const char*
jalv_native_ui_type(Jalv* jalv);
@@ -325,6 +344,9 @@ jalv_discover_ui(Jalv* jalv);
int
jalv_open_ui(Jalv* jalv);
+void
+jalv_init_ui(Jalv* jalv);
+
int
jalv_close_ui(Jalv* jalv);
diff --git a/src/jalv_qt.cpp b/src/jalv_qt.cpp
index b7ed4b1..d19378c 100644
--- a/src/jalv_qt.cpp
+++ b/src/jalv_qt.cpp
@@ -361,9 +361,14 @@ jalv_ui_port_event(Jalv* jalv,
uint32_t protocol,
const void* buffer)
{
- Control* control = (Control*)jalv->ports[port_index].widget;
- if (control) {
- control->setValue(*(const float*)buffer);
+ if (jalv->ui_instance) {
+ suil_instance_port_event(jalv->ui_instance, port_index,
+ buffer_size, protocol, buffer);
+ } else {
+ Control* control = (Control*)jalv->ports[port_index].widget;
+ if (control) {
+ control->setValue(*(const float*)buffer);
+ }
}
}