From bb9372f2df47956b74cf9b7f08b7ec0c96f88b23 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 17 Sep 2016 13:23:59 -0400 Subject: Unify control port and parameter implementations --- src/control.c | 40 ++++++++ src/jalv.c | 172 ++++++++++++++++++++++++++++---- src/jalv_gtk.c | 282 +++++++++++++++------------------------------------- src/jalv_internal.h | 22 ++++ src/jalv_qt.cpp | 11 +- 5 files changed, 307 insertions(+), 220 deletions(-) (limited to 'src') 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); + } } } -- cgit v1.2.1