diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/control.c | 129 | ||||
-rw-r--r-- | src/jalv.c | 26 | ||||
-rw-r--r-- | src/jalv_gtk.c | 417 | ||||
-rw-r--r-- | src/jalv_internal.h | 91 | ||||
-rw-r--r-- | src/state.c | 1 |
5 files changed, 438 insertions, 226 deletions
diff --git a/src/control.c b/src/control.c new file mode 100644 index 0000000..1921785 --- /dev/null +++ b/src/control.c @@ -0,0 +1,129 @@ +/* + Copyright 2007-2015 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "jalv_internal.h" + +int +scale_point_cmp(const ScalePoint* a, const ScalePoint* b) +{ + if (a->value < b->value) { + return -1; + } else if (a->value == b->value) { + return 0; + } + return 1; +} + +ControlID* +new_port_control(Jalv* jalv, uint32_t index) +{ + struct Port* port = &jalv->ports[index]; + const LilvPort* lport = port->lilv_port; + const LilvPlugin* plug = jalv->plugin; + const JalvNodes* nodes = &jalv->nodes; + + ControlID* id = (ControlID*)calloc(1, sizeof(ControlID)); + id->jalv = jalv; + id->type = PORT; + id->index = index; + 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); + id->is_logarithmic = lilv_port_has_property(plug, lport, nodes->pprops_logarithmic); + + lilv_port_get_range(plug, lport, &id->def, &id->min, &id->max); + if (lilv_port_has_property(plug, lport, jalv->nodes.lv2_sampleRate)) { + /* Adjust range for lv2:sampleRate controls */ + if (lilv_node_is_float(id->min)) { + const float min = lilv_node_as_float(id->min) * jalv->sample_rate; + lilv_node_free(id->min); + id->min = lilv_new_float(jalv->world, min); + } + if (lilv_node_is_float(id->max)) { + const float max = lilv_node_as_float(id->max) * jalv->sample_rate; + lilv_node_free(id->max); + id->max = lilv_new_float(jalv->world, max); + } + } + + return id; +} + +static bool +has_range(Jalv* jalv, const LilvNode* subject, const char* range_uri) +{ + LilvNode* range = lilv_new_uri(jalv->world, range_uri); + const bool result = lilv_world_ask( + jalv->world, subject, jalv->nodes.rdfs_range, range); + lilv_node_free(range); + return result; +} + +ControlID* +new_property_control(Jalv* jalv, const LilvNode* property) +{ + ControlID* id = (ControlID*)calloc(1, sizeof(ControlID)); + id->jalv = jalv; + id->type = PROPERTY; + id->property = jalv->map.map(jalv, lilv_node_as_uri(property)); + + id->min = lilv_world_get(jalv->world, property, jalv->nodes.lv2_minimum, NULL); + id->max = lilv_world_get(jalv->world, property, jalv->nodes.lv2_maximum, NULL); + id->def = lilv_world_get(jalv->world, property, jalv->nodes.lv2_default, NULL); + + const char* const types[] = { + LV2_ATOM__Int, LV2_ATOM__Long, LV2_ATOM__Float, LV2_ATOM__Double, + LV2_ATOM__Bool, LV2_ATOM__String, LV2_ATOM__Path, NULL + }; + + for (const char*const* t = types; *t; ++t) { + if (has_range(jalv, property, *t)) { + id->value_type = jalv->map.map(jalv, *t); + break; + } + } + + id->is_toggle = (id->value_type == jalv->forge.Bool); + id->is_integer = (id->value_type == jalv->forge.Int || + id->value_type == jalv->forge.Long); + + if (!id->value_type) { + fprintf(stderr, "Unknown value type for property <%s>\n", + lilv_node_as_string(property)); + } + + return id; +} + +void +add_control(Controls* controls, ControlID* control) +{ + controls->controls = realloc( + controls->controls, (controls->n_controls + 1) * sizeof(ControlID*)); + controls->controls[controls->n_controls++] = control; +} + +ControlID* +get_property_control(const Controls* controls, LV2_URID property) +{ + for (size_t i = 0; i < controls->n_controls; ++i) { + if (controls->controls[i]->property == property) { + return controls->controls[i]; + } + } + + return NULL; +} @@ -1,5 +1,5 @@ /* - Copyright 2007-2014 David Robillard <http://drobilla.net> + Copyright 2007-2015 David Robillard <http://drobilla.net> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -530,6 +530,19 @@ jack_process_cb(jack_nframes_t nframes, void* data) lv2_pos->type, lv2_pos->size, LV2_ATOM_BODY(lv2_pos)); } + if (jalv->state_changed) { + /* Plugin state has changed, request an update */ + const LV2_Atom_Object get = { + { jalv->urids.atom_Object, sizeof(LV2_Atom_Object) }, + { 0, jalv->urids.patch_Get } }; + + lv2_evbuf_write( + &iter, 0, 0, + get.atom.type, get.atom.size, LV2_ATOM_BODY(&get)); + + jalv->state_changed = false; + } + if (port->jack_port) { /* Write Jack MIDI input */ void* buf = jack_port_get_buffer(port->jack_port, nframes); @@ -836,7 +849,9 @@ jalv_ui_write(SuilController controller, char* str = sratom_to_turtle( jalv->sratom, &jalv->unmap, "jalv:", NULL, NULL, atom->type, atom->size, LV2_ATOM_BODY_CONST(atom)); + jalv_ansi_start(stdout, 36); printf("\n## UI => Plugin (%u bytes) ##\n%s\n", atom->size, str); + jalv_ansi_reset(stdout); free(str); } @@ -889,7 +904,9 @@ jalv_update(Jalv* jalv) char* str = sratom_to_turtle( jalv->ui_sratom, &jalv->unmap, "jalv:", NULL, NULL, atom->type, atom->size, LV2_ATOM_BODY(atom)); + jalv_ansi_start(stdout, 35); printf("\n## Plugin => UI (%u bytes) ##\n%s\n", atom->size, str); + jalv_ansi_reset(stdout); free(str); } @@ -975,6 +992,9 @@ main(int argc, char** argv) jalv.urids.atom_Float = symap_map(jalv.symap, LV2_ATOM__Float); jalv.urids.atom_Int = symap_map(jalv.symap, LV2_ATOM__Int); + jalv.urids.atom_Object = symap_map(jalv.symap, LV2_ATOM__Object); + jalv.urids.atom_Path = symap_map(jalv.symap, LV2_ATOM__Path); + jalv.urids.atom_String = symap_map(jalv.symap, LV2_ATOM__String); jalv.urids.atom_eventTransfer = symap_map(jalv.symap, LV2_ATOM__eventTransfer); jalv.urids.bufsz_maxBlockLength = symap_map(jalv.symap, LV2_BUF_SIZE__maxBlockLength); jalv.urids.bufsz_minBlockLength = symap_map(jalv.symap, LV2_BUF_SIZE__minBlockLength); @@ -982,6 +1002,8 @@ main(int argc, char** argv) jalv.urids.log_Trace = symap_map(jalv.symap, LV2_LOG__Trace); jalv.urids.midi_MidiEvent = symap_map(jalv.symap, LV2_MIDI__MidiEvent); jalv.urids.param_sampleRate = symap_map(jalv.symap, LV2_PARAMETERS__sampleRate); + jalv.urids.patch_Get = symap_map(jalv.symap, LV2_PATCH__Get); + jalv.urids.patch_Put = symap_map(jalv.symap, LV2_PATCH__Put); jalv.urids.patch_Set = symap_map(jalv.symap, LV2_PATCH__Set); jalv.urids.patch_property = symap_map(jalv.symap, LV2_PATCH__property); jalv.urids.patch_value = symap_map(jalv.symap, LV2_PATCH__value); @@ -1159,7 +1181,7 @@ main(int argc, char** argv) fprintf(stderr, "UI: %s\n", lilv_node_as_uri(lilv_ui_get_uri(jalv.ui))); } else { - fprintf(stderr, "No appropriate UI found\n"); + fprintf(stderr, "UI: None\n"); } /* Create port structures (jalv.ports) */ diff --git a/src/jalv_gtk.c b/src/jalv_gtk.c index ada2c78..a3f6320 100644 --- a/src/jalv_gtk.c +++ b/src/jalv_gtk.c @@ -31,111 +31,6 @@ typedef struct { GtkWidget* control; } Controller; -/** Type of plugin control. */ -typedef enum { - PORT, ///< Control port - PROPERTY ///< Property (set via atom message) -} ControlType; - -/** Plugin control. */ -typedef struct { - Jalv* jalv; - ControlType type; - LilvNode* property; ///< Iff type == PROPERTY - uint32_t index; ///< Iff type == PORT - Controller* widget; ///< Control Widget - GHashTable* points; ///< Scale points - LV2_URID value_type; ///< Type of control value - LilvNode* min; ///< Minimum value - LilvNode* max; ///< Maximum value - LilvNode* def; ///< Default value - bool is_toggle; ///< Boolean (0 and 1 only) - bool is_integer; ///< Integer values only - bool is_enumeration; ///< Point values only - bool is_logarithmic; ///< Logarithmic scale -} ControlID; - -static ControlID* -new_port_control(Jalv* jalv, uint32_t index) -{ - struct Port* port = &jalv->ports[index]; - const LilvPort* lport = port->lilv_port; - const LilvPlugin* plug = jalv->plugin; - const JalvNodes* nodes = &jalv->nodes; - - ControlID* id = (ControlID*)calloc(1, sizeof(ControlID)); - id->jalv = jalv; - id->type = PORT; - id->index = index; - 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); - id->is_logarithmic = lilv_port_has_property(plug, lport, nodes->pprops_logarithmic); - - lilv_port_get_range(plug, lport, &id->def, &id->min, &id->max); - if (lilv_port_has_property(plug, lport, jalv->nodes.lv2_sampleRate)) { - /* Adjust range for lv2:sampleRate controls */ - if (lilv_node_is_float(id->min)) { - const float min = lilv_node_as_float(id->min) * jalv->sample_rate; - lilv_node_free(id->min); - id->min = lilv_new_float(jalv->world, min); - } - if (lilv_node_is_float(id->max)) { - const float max = lilv_node_as_float(id->max) * jalv->sample_rate; - lilv_node_free(id->max); - id->max = lilv_new_float(jalv->world, max); - } - } - - return id; -} - -static bool -has_range(Jalv* jalv, const LilvNode* subject, const char* range_uri) -{ - LilvNode* range = lilv_new_uri(jalv->world, range_uri); - const bool result = lilv_world_ask( - jalv->world, subject, jalv->nodes.rdfs_range, range); - lilv_node_free(range); - return result; -} - -static ControlID* -new_property_control(Jalv* jalv, const LilvNode* property) -{ - ControlID* id = (ControlID*)calloc(1, sizeof(ControlID)); - id->jalv = jalv; - id->type = PROPERTY; - id->property = lilv_node_duplicate(property); - - id->min = lilv_world_get(jalv->world, property, jalv->nodes.lv2_minimum, NULL); - id->max = lilv_world_get(jalv->world, property, jalv->nodes.lv2_maximum, NULL); - id->def = lilv_world_get(jalv->world, property, jalv->nodes.lv2_default, NULL); - - const char* const types[] = { - LV2_ATOM__Int, LV2_ATOM__Long, LV2_ATOM__Float, LV2_ATOM__Double, - LV2_ATOM__Bool, LV2_ATOM__String, LV2_ATOM__Path, NULL - }; - - for (const char*const* t = types; *t; ++t) { - if (has_range(jalv, property, *t)) { - id->value_type = jalv->map.map(jalv, *t); - break; - } - } - - id->is_toggle = (id->value_type == jalv->forge.Bool); - id->is_integer = (id->value_type == jalv->forge.Int || - id->value_type == jalv->forge.Long); - - if (!id->value_type) { - fprintf(stderr, "Unknown value type for property <%s>\n", - lilv_node_as_string(property)); - } - - return id; -} - static GtkWidget* new_box(gboolean horizontal, gint spacing) { @@ -574,49 +469,6 @@ on_delete_preset_activate(GtkWidget* widget, void* ptr) gtk_widget_destroy(dialog); } -void -jalv_ui_port_event(Jalv* jalv, - uint32_t port_index, - uint32_t buffer_size, - uint32_t protocol, - const void* buffer) -{ - Controller* controller = (Controller*)jalv->ports[port_index].widget; - if (!controller) { - return; - } - - if (controller->spin) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(controller->spin), - *(const float*)buffer); - } - - GtkWidget* widget = controller->control; - if (GTK_IS_COMBO_BOX(widget)) { - GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); - GValue value = { 0, { { 0 } } }; - GtkTreeIter i; - bool valid = gtk_tree_model_get_iter_first(model, &i); - while (valid) { - gtk_tree_model_get_value(model, &i, 0, &value); - const double v = g_value_get_double(&value); - g_value_unset(&value); - if (fabs(v - *(const float*)buffer) < FLT_EPSILON) { - gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &i); - return; - } - valid = gtk_tree_model_iter_next(model, &i); - } - } else if (GTK_IS_TOGGLE_BUTTON(widget)) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), - *(const float*)buffer > 0.0f); - } else if (GTK_IS_RANGE(widget)) { - gtk_range_set_value(GTK_RANGE(widget), *(const float*)buffer); - } else { - fprintf(stderr, "Unknown widget type for port %d\n", port_index); - } -} - static void set_control(const ControlID* control, uint32_t size, @@ -636,8 +488,7 @@ set_control(const ControlID* control, 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, jalv->map.map(jalv, lilv_node_as_uri(control->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); @@ -669,45 +520,182 @@ set_float_control(const ControlID* control, float value) const int32_t ival = value; set_control(control, sizeof(ival), control->jalv->forge.Bool, &ival); } + + Controller* controller = (Controller*)control->widget; + if (controller && controller->spin && + gtk_spin_button_get_value(controller->spin) != value) { + gtk_spin_button_set_value(controller->spin, value); + } +} + +static double +get_atom_double(Jalv* jalv, uint32_t size, LV2_URID type, const void* body) +{ + if (type == jalv->forge.Int || type == jalv->forge.Bool) { + return *(const int32_t*)body; + } else if (type == jalv->forge.Long) { + return *(const int64_t*)body; + } else if (type == jalv->forge.Float) { + return *(const float*)body; + } else if (type == jalv->forge.Double) { + return *(const double*)body; + } + return NAN; +} + +static void +control_changed(Jalv* jalv, + Controller* controller, + uint32_t size, + LV2_URID type, + const void* body) +{ + GtkWidget* widget = controller->control; + const double fvalue = get_atom_double(jalv, size, type, body); + + if (!isnan(fvalue)) { + // Numeric control + if (controller->spin) { + gtk_spin_button_set_value(GTK_SPIN_BUTTON(controller->spin), + fvalue); + } + + if (GTK_IS_COMBO_BOX(widget)) { + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); + GValue value = { 0, { { 0 } } }; + GtkTreeIter i; + bool valid = gtk_tree_model_get_iter_first(model, &i); + while (valid) { + gtk_tree_model_get_value(model, &i, 0, &value); + const double v = g_value_get_double(&value); + g_value_unset(&value); + if (fabs(v - fvalue) < FLT_EPSILON) { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &i); + return; + } + valid = gtk_tree_model_iter_next(model, &i); + } + } else if (GTK_IS_TOGGLE_BUTTON(widget)) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), + fvalue > 0.0f); + } else if (GTK_IS_RANGE(widget)) { + gtk_range_set_value(GTK_RANGE(widget), fvalue); + } else { + fprintf(stderr, "Unknown widget type for value\n"); + } + } else if (GTK_IS_ENTRY(widget) && type == jalv->urids.atom_String) { + gtk_entry_set_text(GTK_ENTRY(widget), (const char*)body); + } else if (GTK_IS_FILE_CHOOSER(widget) && type == jalv->urids.atom_Path) { + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), (const char*)body); + } else { + fprintf(stderr, "Unknown widget type for value\n"); + } +} + +static int +patch_set_get(Jalv* jalv, + const LV2_Atom_Object* obj, + const LV2_Atom_URID** property, + const LV2_Atom** value) +{ + lv2_atom_object_get(obj, + jalv->urids.patch_property, (const LV2_Atom*)property, + jalv->urids.patch_value, value, + 0); + if (!*property) { + fprintf(stderr, "patch:Set message with no property\n"); + return 1; + } else if ((*property)->atom.type != jalv->forge.URID) { + fprintf(stderr, "patch:Set property is not a URID\n"); + return 1; + } + + return 0; +} + +static void +property_changed(Jalv* jalv, LV2_URID key, const LV2_Atom* value) +{ + ControlID* control = get_property_control(&jalv->controls, key); + if (control) { + control_changed(jalv, + (Controller*)control->widget, + value->size, + value->type, + value + 1); + } +} + +void +jalv_ui_port_event(Jalv* jalv, + uint32_t port_index, + uint32_t buffer_size, + uint32_t protocol, + const void* buffer) +{ + if (protocol == 0) { + control_changed(jalv, + jalv->ports[port_index].widget, + buffer_size, + jalv->forge.Float, + buffer); + } else if (protocol != jalv->urids.atom_eventTransfer) { + fprintf(stderr, "Unknown port event protocol\n"); + return; + } + + const LV2_Atom* atom = (const LV2_Atom*)buffer; + if (lv2_atom_forge_is_object_type(&jalv->forge, atom->type)) { + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)buffer; + if (obj->body.otype == jalv->urids.patch_Set) { + const LV2_Atom_URID* property = NULL; + const LV2_Atom* value = NULL; + if (!patch_set_get(jalv, obj, &property, &value)) { + property_changed(jalv, property->body, value); + } + } else if (obj->body.otype == jalv->urids.patch_Put) { + LV2_ATOM_OBJECT_FOREACH(obj, prop) { + property_changed(jalv, prop->key, &prop->value); + } + } else { + printf("Unknown object type?\n"); + } + } } static gboolean scale_changed(GtkRange* range, gpointer data) { - const ControlID* control = (const ControlID*)data; - const double value = gtk_range_get_value(range); - set_float_control(control, value); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(control->widget->spin), value); + set_float_control((const ControlID*)data, gtk_range_get_value(range)); return FALSE; } static gboolean spin_changed(GtkSpinButton* spin, gpointer data) { - const ControlID* control = (const ControlID*)data; - const double value = gtk_spin_button_get_value(spin); + const ControlID* control = (const ControlID*)data; + Controller* controller = (Controller*)control->widget; + const double value = gtk_spin_button_get_value(spin); set_float_control(control, value); - gtk_range_set_value(GTK_RANGE(control->widget->control), value); + gtk_range_set_value(GTK_RANGE(controller->control), value); return FALSE; } static gboolean log_scale_changed(GtkRange* range, gpointer data) { - const ControlID* control = (const ControlID*)data; - const double value = expf(gtk_range_get_value(range)); - set_float_control(control, value); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(control->widget->control), value); + set_float_control((const ControlID*)data, expf(gtk_range_get_value(range))); return FALSE; } static gboolean log_spin_changed(GtkSpinButton* spin, gpointer data) { - const ControlID* control = (const ControlID*)data; - const double value = gtk_spin_button_get_value(spin); + const ControlID* control = (const ControlID*)data; + Controller* controller = (Controller*)control->widget; + const double value = gtk_spin_button_get_value(spin); set_float_control(control, value); - gtk_range_set_value(GTK_RANGE(control->widget->control), logf(value)); + gtk_range_set_value(GTK_RANGE(controller->control), logf(value)); return FALSE; } @@ -732,9 +720,8 @@ combo_changed(GtkComboBox* box, gpointer data) static gboolean toggle_changed(GtkToggleButton* button, gpointer data) { - const ControlID* control = (const ControlID*)data; - const float value = gtk_toggle_button_get_active(button) ? 1.0f : 0.0f; - set_float_control(control, value); + set_float_control((const ControlID*)data, + gtk_toggle_button_get_active(button) ? 1.0f : 0.0f); return FALSE; } @@ -744,35 +731,19 @@ string_changed(GtkEntry* widget, gpointer data) ControlID* control = (ControlID*)data; const char* string = gtk_entry_get_text(widget); - set_control(control, strlen(string), control->jalv->forge.String, string); + set_control(control, strlen(string) + 1, control->jalv->forge.String, string); } static void file_changed(GtkFileChooserButton* widget, gpointer data) { - ControlID* record = (ControlID*)data; - Jalv* jalv = record->jalv; + ControlID* control = (ControlID*)data; + Jalv* jalv = control->jalv; const char* filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(widget)); - set_control(record, strlen(filename), jalv->forge.Path, filename); -} - -static gint -dcmp(gconstpointer a, gconstpointer b) -{ - double y = *(const double*)a; - double z = *(const double*)b; - return y < z ? -1 : z < y ? 1 : 0; -} - -static gint -drcmp(gconstpointer a, gconstpointer b) -{ - double y = *(const double*)a; - double z = *(const double*)b; - return y < z ? 1 : z < y ? -1 : 0; + set_control(control, strlen(filename), jalv->forge.Path, filename); } static Controller* @@ -787,22 +758,21 @@ new_controller(GtkSpinButton* spin, GtkWidget* control) static Controller* make_combo(ControlID* record, float value) { - GList* list = g_hash_table_get_keys(record->points); GtkListStore* list_store = gtk_list_store_new( - 2, G_TYPE_DOUBLE, G_TYPE_STRING); - gint active = -1, count = 0; - for (GList* cur = g_list_sort(list, dcmp); cur; cur = cur->next, ++count) { - GtkTreeIter iter; + 2, G_TYPE_FLOAT, G_TYPE_STRING); + int active = -1; + for (size_t i = 0; i < record->n_points; ++i) { + const ScalePoint* point = &record->points[i]; + GtkTreeIter iter; gtk_list_store_append(list_store, &iter); gtk_list_store_set(list_store, &iter, - 0, *(double*)cur->data, - 1, g_hash_table_lookup(record->points, cur->data), + 0, point->value, + 1, point->label, -1); - if (fabs(value - *(double*)cur->data) < FLT_EPSILON) { - active = count; + if (fabs(value - point->value) < FLT_EPSILON) { + active = i; } } - g_list_free(list); GtkWidget* combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store)); gtk_combo_box_set_active(GTK_COMBO_BOX(combo), active); @@ -818,14 +788,6 @@ make_combo(ControlID* record, float value) return new_controller(NULL, combo); } -static void -add_mark(gdouble key, const gchar* value, void* scale) -{ - gchar* str = g_markup_printf_escaped("<span font_size=\"small\">%s</span>", - value); - gtk_scale_add_mark(GTK_SCALE(scale), key, GTK_POS_TOP, str); -} - static Controller* make_log_slider(ControlID* record, float value) { @@ -859,18 +821,22 @@ make_slider(ControlID* record, float value) GtkWidget* spin = gtk_spin_button_new_with_range(min, max, step); if (record->is_integer) { - gtk_scale_set_digits(GTK_SCALE(scale), 0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0); + } else { + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 7); } gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE); gtk_range_set_value(GTK_RANGE(scale), value); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value); if (record->points) { - GList* list = g_hash_table_get_keys(record->points); - for (GList* cur = g_list_sort(list, drcmp); cur; cur = cur->next) { - add_mark(*(gdouble*)cur->data, - g_hash_table_lookup(record->points, cur->data), - scale); + for (size_t i = 0; i < record->n_points; ++i) { + const ScalePoint* point = &record->points[i]; + + gchar* str = g_markup_printf_escaped( + "<span font_size=\"small\">%s</span>", point->label); + gtk_scale_add_mark( + GTK_SCALE(scale), point->value, GTK_POS_TOP, str); } } @@ -907,7 +873,7 @@ static Controller* make_file_chooser(ControlID* record) { GtkWidget* button = gtk_file_chooser_button_new( - lilv_node_as_uri(record->property), GTK_FILE_CHOOSER_ACTION_OPEN); + "Open File", GTK_FILE_CHOOSER_ACTION_OPEN); g_signal_connect(G_OBJECT(button), "file-set", G_CALLBACK(file_changed), record); return new_controller(NULL, button); @@ -1038,18 +1004,25 @@ build_control_widget(Jalv* jalv, GtkWidget* window) /* Get scale points */ LilvScalePoints* sp = lilv_port_get_scale_points(plugin, port); if (sp) { - control_id->points = g_hash_table_new(g_double_hash, g_double_equal); - int idx = 0; - gdouble* values = (gdouble*)malloc( - lilv_scale_points_size(sp) * sizeof(gdouble)); + 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); - values[idx] = lilv_node_as_float(lilv_scale_point_get_value(p)); - char* label = g_strdup( - lilv_node_as_string(lilv_scale_point_get_label(p))); - g_hash_table_insert(control_id->points, values + idx, label); - ++idx; + if (lilv_node_is_float(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); } @@ -1067,6 +1040,7 @@ build_control_widget(Jalv* jalv, GtkWidget* window) } lilv_node_free(comment); + add_control(&jalv->controls, control_id); add_control_row( port_table, n_rows++, lilv_node_as_string(name), controller); @@ -1102,6 +1076,7 @@ build_control_widget(Jalv* jalv, GtkWidget* window) 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), diff --git a/src/jalv_internal.h b/src/jalv_internal.h index dcd396a..bea236c 100644 --- a/src/jalv_internal.h +++ b/src/jalv_internal.h @@ -1,5 +1,5 @@ /* - Copyright 2007-2014 David Robillard <http://drobilla.net> + Copyright 2007-2015 David Robillard <http://drobilla.net> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -17,8 +17,12 @@ #ifndef JALV_INTERNAL_H #define JALV_INTERNAL_H +#include <stdio.h> #include <stdlib.h> #include <string.h> +#ifdef HAVE_ISATTY +# include <unistd.h> +#endif #include <jack/jack.h> #include <jack/ringbuffer.h> @@ -48,6 +52,8 @@ extern "C" { #endif +typedef struct Jalv Jalv; + enum PortFlow { FLOW_UNKNOWN, FLOW_INPUT, @@ -74,6 +80,58 @@ struct Port { bool old_api; ///< True for event, false for atom }; +/* Controls */ + +/** Type of plugin control. */ +typedef enum { + PORT, ///< Control port + PROPERTY ///< Property (set via atom message) +} ControlType; + +typedef struct { + float value; + char* label; +} ScalePoint; + +/** Order scale points by value. */ +int scale_point_cmp(const ScalePoint* a, const ScalePoint* b); + +/** Plugin control. */ +typedef struct { + Jalv* jalv; + ControlType type; + LV2_URID property; ///< Iff type == PROPERTY + uint32_t index; ///< Iff type == PORT + void* widget; ///< Control Widget + size_t n_points; ///< Number of scale points + ScalePoint* points; ///< Scale points + LV2_URID value_type; ///< Type of control value + LilvNode* min; ///< Minimum value + LilvNode* max; ///< Maximum value + LilvNode* def; ///< Default value + bool is_toggle; ///< Boolean (0 and 1 only) + bool is_integer; ///< Integer values only + bool is_enumeration; ///< Point values only + bool is_logarithmic; ///< Logarithmic scale +} ControlID; + +ControlID* +new_port_control(Jalv* jalv, uint32_t index); + +ControlID* +new_property_control(Jalv* jalv, const LilvNode* property); + +typedef struct { + size_t n_controls; + ControlID** controls; +} Controls; + +void +add_control(Controls* controls, ControlID* control); + +ControlID* +get_property_control(const Controls* controls, LV2_URID property); + /** Control change event, sent through ring buffers for UI updates. */ @@ -105,6 +163,9 @@ typedef struct { typedef struct { LV2_URID atom_Float; LV2_URID atom_Int; + LV2_URID atom_Object; + LV2_URID atom_Path; + LV2_URID atom_String; LV2_URID atom_eventTransfer; LV2_URID bufsz_maxBlockLength; LV2_URID bufsz_minBlockLength; @@ -112,6 +173,8 @@ typedef struct { LV2_URID log_Trace; LV2_URID midi_MidiEvent; LV2_URID param_sampleRate; + LV2_URID patch_Get; + LV2_URID patch_Put; LV2_URID patch_Set; LV2_URID patch_property; LV2_URID patch_value; @@ -177,7 +240,7 @@ typedef struct { const LV2_Worker_Interface* iface; ///< Plugin worker interface } JalvWorker; -typedef struct { +struct Jalv { JalvOptions opts; ///< Command-line options JalvURIDs urids; ///< URIDs JalvNodes nodes; ///< Nodes @@ -210,6 +273,7 @@ typedef struct { SuilInstance* ui_instance; ///< Plugin UI instance (shared library) void* window; ///< Window (if applicable) struct Port* ports; ///< Port array of size num_ports + Controls controls; ///< Available plugin controls uint32_t block_length; ///< Jack buffer size (block length) size_t midi_buf_size; ///< Size of MIDI port buffers uint32_t control_in; ///< Index of control input port @@ -226,7 +290,8 @@ typedef struct { bool buf_size_set; ///< True iff buffer size callback fired bool exit; ///< True iff execution is finished bool has_ui; ///< True iff a control UI is present -} Jalv; + bool state_changed; ///< Plugin state has changed +}; int jalv_init(int* argc, char*** argv, JalvOptions* opts); @@ -355,6 +420,26 @@ jalv_vprintf(LV2_Log_Handle handle, const char* fmt, va_list ap); +static inline void +jalv_ansi_start(FILE* stream, int color) +{ +#ifdef HAVE_ISATTY + if (isatty(fileno(stream))) { + fprintf(stream, "\033[0;%dm\n", color); + } +#endif +} + +static inline void +jalv_ansi_reset(FILE* stream) +{ +#ifdef HAVE_ISATTY + if (isatty(fileno(stream))) { + fprintf(stream, "\033[0m\n"); + } +#endif +} + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/state.c b/src/state.c index d0c04e4..8401252 100644 --- a/src/state.c +++ b/src/state.c @@ -190,6 +190,7 @@ jalv_apply_state(Jalv* jalv, LilvState* state) lilv_state_restore( state, jalv->instance, set_port_value, jalv, 0, NULL); + jalv->state_changed = true; if (must_pause) { jalv->play_state = JALV_RUNNING; } |