Changeset add42b1 in jalv


Ignore:
Timestamp:
11/07/15 17:44:25 (2 years ago)
Author:
David Robillard <d@…>
Branches:
master, osx
Children:
5a7c238
Parents:
5537b61
Message:

Support numeric and string plugin properties

Also:

  • Print plugin/UI communication log in colour if supported
  • Update UI when internal plugin state is changed during preset load

git-svn-id: http://svn.drobilla.net/lad/trunk/jalv@5812 a436a847-0d15-0410-975c-d299462d15a1

Files:
1 added
6 edited

Legend:

Unmodified
Added
Removed
  • NEWS

    r238c509 radd42b1  
    33  * Improve preset support 
    44  * Support numeric and string plugin properties (event-based control) 
     5  * Update UI when internal plugin state is changed during preset load 
    56  * Add generic Qt control UI from Amadeus Folego 
    67  * Set Jack port order metadata 
     
    89  * Add command prompt to console version for changing controls 
    910  * Add option to enable plugin trace log messages 
     11  * Colorize communication dump if output is a console 
    1012  * Exit on Jack shutdown (Patch from Robin Gareus) 
    1113  * Report Jack latency (Patch from Robin Gareus) 
     
    1517  * Add Qt5 version 
    1618 
    17  -- David Robillard <d@drobilla.net>  Sat, 10 Oct 2015 14:11:00 -0400 
     19 -- David Robillard <d@drobilla.net>  Sat, 07 Nov 2015 20:25:59 -0500 
    1820 
    1921jalv (1.4.6) stable; 
  • src/jalv.c

    rfecfdd1 radd42b1  
    11/* 
    2   Copyright 2007-2014 David Robillard <http://drobilla.net> 
     2  Copyright 2007-2015 David Robillard <http://drobilla.net> 
    33 
    44  Permission to use, copy, modify, and/or distribute this software for any 
     
    529529                    &iter, 0, 0, 
    530530                    lv2_pos->type, lv2_pos->size, LV2_ATOM_BODY(lv2_pos)); 
     531            } 
     532 
     533            if (jalv->state_changed) { 
     534                /* Plugin state has changed, request an update */ 
     535                const LV2_Atom_Object get = { 
     536                    { jalv->urids.atom_Object, sizeof(LV2_Atom_Object) }, 
     537                    { 0, jalv->urids.patch_Get } }; 
     538 
     539                lv2_evbuf_write( 
     540                    &iter, 0, 0, 
     541                    get.atom.type, get.atom.size, LV2_ATOM_BODY(&get)); 
     542 
     543                jalv->state_changed = false; 
    531544            } 
    532545 
     
    837850            jalv->sratom, &jalv->unmap, "jalv:", NULL, NULL, 
    838851            atom->type, atom->size, LV2_ATOM_BODY_CONST(atom)); 
     852        jalv_ansi_start(stdout, 36); 
    839853        printf("\n## UI => Plugin (%u bytes) ##\n%s\n", atom->size, str); 
     854        jalv_ansi_reset(stdout); 
    840855        free(str); 
    841856    } 
     
    890905                jalv->ui_sratom, &jalv->unmap, "jalv:", NULL, NULL, 
    891906                atom->type, atom->size, LV2_ATOM_BODY(atom)); 
     907            jalv_ansi_start(stdout, 35); 
    892908            printf("\n## Plugin => UI (%u bytes) ##\n%s\n", atom->size, str); 
     909            jalv_ansi_reset(stdout); 
    893910            free(str); 
    894911        } 
     
    976993    jalv.urids.atom_Float           = symap_map(jalv.symap, LV2_ATOM__Float); 
    977994    jalv.urids.atom_Int             = symap_map(jalv.symap, LV2_ATOM__Int); 
     995    jalv.urids.atom_Object          = symap_map(jalv.symap, LV2_ATOM__Object); 
     996    jalv.urids.atom_Path            = symap_map(jalv.symap, LV2_ATOM__Path); 
     997    jalv.urids.atom_String          = symap_map(jalv.symap, LV2_ATOM__String); 
    978998    jalv.urids.atom_eventTransfer   = symap_map(jalv.symap, LV2_ATOM__eventTransfer); 
    979999    jalv.urids.bufsz_maxBlockLength = symap_map(jalv.symap, LV2_BUF_SIZE__maxBlockLength); 
     
    9831003    jalv.urids.midi_MidiEvent       = symap_map(jalv.symap, LV2_MIDI__MidiEvent); 
    9841004    jalv.urids.param_sampleRate     = symap_map(jalv.symap, LV2_PARAMETERS__sampleRate); 
     1005    jalv.urids.patch_Get            = symap_map(jalv.symap, LV2_PATCH__Get); 
     1006    jalv.urids.patch_Put            = symap_map(jalv.symap, LV2_PATCH__Put); 
    9851007    jalv.urids.patch_Set            = symap_map(jalv.symap, LV2_PATCH__Set); 
    9861008    jalv.urids.patch_property       = symap_map(jalv.symap, LV2_PATCH__property); 
     
    11601182                lilv_node_as_uri(lilv_ui_get_uri(jalv.ui))); 
    11611183    } else { 
    1162         fprintf(stderr, "No appropriate UI found\n"); 
     1184        fprintf(stderr, "UI:           None\n"); 
    11631185    } 
    11641186 
  • src/jalv_gtk.c

    r238c509 radd42b1  
    3131    GtkWidget*     control; 
    3232} Controller; 
    33  
    34 /** Type of plugin control. */ 
    35 typedef enum { 
    36     PORT,     ///< Control port 
    37     PROPERTY  ///< Property (set via atom message) 
    38 } ControlType; 
    39  
    40 /** Plugin control. */ 
    41 typedef struct { 
    42     Jalv*       jalv; 
    43     ControlType type; 
    44     LilvNode*   property;        ///< Iff type == PROPERTY 
    45     uint32_t    index;           ///< Iff type == PORT 
    46     Controller* widget;          ///< Control Widget 
    47     GHashTable* points;          ///< Scale points 
    48     LV2_URID    value_type;      ///< Type of control value 
    49     LilvNode*   min;             ///< Minimum value 
    50     LilvNode*   max;             ///< Maximum value 
    51     LilvNode*   def;             ///< Default value 
    52     bool        is_toggle;       ///< Boolean (0 and 1 only) 
    53     bool        is_integer;      ///< Integer values only 
    54     bool        is_enumeration;  ///< Point values only 
    55     bool        is_logarithmic;  ///< Logarithmic scale 
    56 } ControlID; 
    57  
    58 static ControlID* 
    59 new_port_control(Jalv* jalv, uint32_t index) 
    60 { 
    61     struct Port*      port  = &jalv->ports[index]; 
    62     const LilvPort*   lport = port->lilv_port; 
    63     const LilvPlugin* plug  = jalv->plugin; 
    64     const JalvNodes*  nodes = &jalv->nodes; 
    65  
    66     ControlID* id = (ControlID*)calloc(1, sizeof(ControlID)); 
    67     id->jalv           = jalv; 
    68     id->type           = PORT; 
    69     id->index          = index; 
    70     id->is_toggle      = lilv_port_has_property(plug, lport, nodes->lv2_toggled); 
    71     id->is_integer     = lilv_port_has_property(plug, lport, nodes->lv2_integer); 
    72     id->is_enumeration = lilv_port_has_property(plug, lport, nodes->lv2_enumeration); 
    73     id->is_logarithmic = lilv_port_has_property(plug, lport, nodes->pprops_logarithmic); 
    74  
    75     lilv_port_get_range(plug, lport, &id->def, &id->min, &id->max); 
    76     if (lilv_port_has_property(plug, lport, jalv->nodes.lv2_sampleRate)) { 
    77         /* Adjust range for lv2:sampleRate controls */ 
    78         if (lilv_node_is_float(id->min)) { 
    79             const float min = lilv_node_as_float(id->min) * jalv->sample_rate; 
    80             lilv_node_free(id->min); 
    81             id->min = lilv_new_float(jalv->world, min); 
    82         } 
    83         if (lilv_node_is_float(id->max)) { 
    84             const float max = lilv_node_as_float(id->max) * jalv->sample_rate; 
    85             lilv_node_free(id->max); 
    86             id->max = lilv_new_float(jalv->world, max); 
    87         } 
    88     } 
    89  
    90     return id; 
    91 } 
    92  
    93 static bool 
    94 has_range(Jalv* jalv, const LilvNode* subject, const char* range_uri) 
    95 { 
    96     LilvNode*  range  = lilv_new_uri(jalv->world, range_uri); 
    97     const bool result = lilv_world_ask( 
    98         jalv->world, subject, jalv->nodes.rdfs_range, range); 
    99     lilv_node_free(range); 
    100     return result; 
    101 } 
    102  
    103 static ControlID* 
    104 new_property_control(Jalv* jalv, const LilvNode* property) 
    105 { 
    106     ControlID* id = (ControlID*)calloc(1, sizeof(ControlID)); 
    107     id->jalv     = jalv; 
    108     id->type     = PROPERTY; 
    109     id->property = lilv_node_duplicate(property); 
    110  
    111     id->min = lilv_world_get(jalv->world, property, jalv->nodes.lv2_minimum, NULL); 
    112     id->max = lilv_world_get(jalv->world, property, jalv->nodes.lv2_maximum, NULL); 
    113     id->def = lilv_world_get(jalv->world, property, jalv->nodes.lv2_default, NULL); 
    114  
    115     const char* const types[] = { 
    116         LV2_ATOM__Int, LV2_ATOM__Long, LV2_ATOM__Float, LV2_ATOM__Double, 
    117         LV2_ATOM__Bool, LV2_ATOM__String, LV2_ATOM__Path, NULL 
    118     }; 
    119  
    120     for (const char*const* t = types; *t; ++t) { 
    121         if (has_range(jalv, property, *t)) { 
    122             id->value_type = jalv->map.map(jalv, *t); 
    123             break; 
    124         } 
    125     } 
    126  
    127     id->is_toggle  = (id->value_type == jalv->forge.Bool); 
    128     id->is_integer = (id->value_type == jalv->forge.Int || 
    129                       id->value_type == jalv->forge.Long); 
    130  
    131     if (!id->value_type) { 
    132         fprintf(stderr, "Unknown value type for property <%s>\n", 
    133                 lilv_node_as_string(property)); 
    134     } 
    135  
    136     return id; 
    137 } 
    13833 
    13934static GtkWidget* 
     
    575470} 
    576471 
    577 void 
    578 jalv_ui_port_event(Jalv*       jalv, 
    579                    uint32_t    port_index, 
    580                    uint32_t    buffer_size, 
    581                    uint32_t    protocol, 
    582                    const void* buffer) 
    583 { 
    584     Controller* controller = (Controller*)jalv->ports[port_index].widget; 
    585     if (!controller) { 
    586         return; 
    587     } 
    588  
    589     if (controller->spin) { 
    590         gtk_spin_button_set_value(GTK_SPIN_BUTTON(controller->spin), 
    591                                   *(const float*)buffer); 
    592     } 
    593  
    594     GtkWidget* widget = controller->control; 
    595     if (GTK_IS_COMBO_BOX(widget)) { 
    596         GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); 
    597         GValue        value = { 0, { { 0 } } }; 
    598         GtkTreeIter   i; 
    599         bool          valid = gtk_tree_model_get_iter_first(model, &i); 
    600         while (valid) { 
    601             gtk_tree_model_get_value(model, &i, 0, &value); 
    602             const double v = g_value_get_double(&value); 
    603             g_value_unset(&value); 
    604             if (fabs(v - *(const float*)buffer) < FLT_EPSILON) { 
    605                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &i); 
    606                 return; 
    607             } 
    608             valid = gtk_tree_model_iter_next(model, &i); 
    609         } 
    610     } else if (GTK_IS_TOGGLE_BUTTON(widget)) { 
    611         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 
    612                                      *(const float*)buffer > 0.0f); 
    613     } else if (GTK_IS_RANGE(widget)) { 
    614         gtk_range_set_value(GTK_RANGE(widget), *(const float*)buffer); 
    615     } else { 
    616         fprintf(stderr, "Unknown widget type for port %d\n", port_index); 
    617     } 
    618 } 
    619  
    620472static void 
    621473set_control(const ControlID* control, 
     
    637489        lv2_atom_forge_object(&forge, &frame, 0, jalv->urids.patch_Set); 
    638490        lv2_atom_forge_key(&forge, jalv->urids.patch_property); 
    639         lv2_atom_forge_urid( 
    640             &forge, jalv->map.map(jalv, lilv_node_as_uri(control->property))); 
     491        lv2_atom_forge_urid(&forge, control->property); 
    641492        lv2_atom_forge_key(&forge, jalv->urids.patch_value); 
    642493        lv2_atom_forge_atom(&forge, size, type); 
     
    670521        set_control(control, sizeof(ival), control->jalv->forge.Bool, &ival); 
    671522    } 
     523 
     524    Controller* controller = (Controller*)control->widget; 
     525    if (controller && controller->spin && 
     526        gtk_spin_button_get_value(controller->spin) != value) { 
     527        gtk_spin_button_set_value(controller->spin, value); 
     528    } 
     529} 
     530 
     531static double 
     532get_atom_double(Jalv* jalv, uint32_t size, LV2_URID type, const void* body) 
     533{ 
     534    if (type == jalv->forge.Int || type == jalv->forge.Bool) { 
     535        return *(const int32_t*)body; 
     536    } else if (type == jalv->forge.Long) { 
     537        return *(const int64_t*)body; 
     538    } else if (type == jalv->forge.Float) { 
     539        return *(const float*)body; 
     540    } else if (type == jalv->forge.Double) { 
     541        return *(const double*)body; 
     542    } 
     543    return NAN; 
     544} 
     545 
     546static void 
     547control_changed(Jalv*       jalv, 
     548                Controller* controller, 
     549                uint32_t    size, 
     550                LV2_URID    type, 
     551                const void* body) 
     552{ 
     553    GtkWidget*   widget = controller->control; 
     554    const double fvalue = get_atom_double(jalv, size, type, body); 
     555 
     556    if (!isnan(fvalue)) { 
     557        // Numeric control 
     558        if (controller->spin) { 
     559            gtk_spin_button_set_value(GTK_SPIN_BUTTON(controller->spin), 
     560                                      fvalue); 
     561        } 
     562 
     563        if (GTK_IS_COMBO_BOX(widget)) { 
     564            GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); 
     565            GValue        value = { 0, { { 0 } } }; 
     566            GtkTreeIter   i; 
     567            bool          valid = gtk_tree_model_get_iter_first(model, &i); 
     568            while (valid) { 
     569                gtk_tree_model_get_value(model, &i, 0, &value); 
     570                const double v = g_value_get_double(&value); 
     571                g_value_unset(&value); 
     572                if (fabs(v - fvalue) < FLT_EPSILON) { 
     573                    gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &i); 
     574                    return; 
     575                } 
     576                valid = gtk_tree_model_iter_next(model, &i); 
     577            } 
     578        } else if (GTK_IS_TOGGLE_BUTTON(widget)) { 
     579            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 
     580                                         fvalue > 0.0f); 
     581        } else if (GTK_IS_RANGE(widget)) { 
     582            gtk_range_set_value(GTK_RANGE(widget), fvalue); 
     583        } else { 
     584            fprintf(stderr, "Unknown widget type for value\n"); 
     585        } 
     586    } else if (GTK_IS_ENTRY(widget) && type == jalv->urids.atom_String) { 
     587        gtk_entry_set_text(GTK_ENTRY(widget), (const char*)body); 
     588    } else if (GTK_IS_FILE_CHOOSER(widget) && type == jalv->urids.atom_Path) { 
     589        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), (const char*)body); 
     590    } else { 
     591        fprintf(stderr, "Unknown widget type for value\n"); 
     592    } 
     593} 
     594 
     595static int 
     596patch_set_get(Jalv*                  jalv, 
     597              const LV2_Atom_Object* obj, 
     598              const LV2_Atom_URID**  property, 
     599              const LV2_Atom**       value) 
     600{ 
     601    lv2_atom_object_get(obj, 
     602                        jalv->urids.patch_property, (const LV2_Atom*)property, 
     603                        jalv->urids.patch_value,    value, 
     604                        0); 
     605    if (!*property) { 
     606        fprintf(stderr, "patch:Set message with no property\n"); 
     607        return 1; 
     608    } else if ((*property)->atom.type != jalv->forge.URID) { 
     609        fprintf(stderr, "patch:Set property is not a URID\n"); 
     610        return 1; 
     611    } 
     612 
     613    return 0; 
     614} 
     615 
     616static void 
     617property_changed(Jalv* jalv, LV2_URID key, const LV2_Atom* value) 
     618{ 
     619    ControlID* control = get_property_control(&jalv->controls, key); 
     620    if (control) { 
     621        control_changed(jalv, 
     622                        (Controller*)control->widget, 
     623                        value->size, 
     624                        value->type, 
     625                        value + 1); 
     626    } 
     627} 
     628 
     629void 
     630jalv_ui_port_event(Jalv*       jalv, 
     631                   uint32_t    port_index, 
     632                   uint32_t    buffer_size, 
     633                   uint32_t    protocol, 
     634                   const void* buffer) 
     635{ 
     636    if (protocol == 0) { 
     637        control_changed(jalv, 
     638                        jalv->ports[port_index].widget, 
     639                        buffer_size, 
     640                        jalv->forge.Float, 
     641                        buffer); 
     642    } else if (protocol != jalv->urids.atom_eventTransfer) { 
     643        fprintf(stderr, "Unknown port event protocol\n"); 
     644        return; 
     645    } 
     646 
     647    const LV2_Atom* atom = (const LV2_Atom*)buffer; 
     648    if (lv2_atom_forge_is_object_type(&jalv->forge, atom->type)) { 
     649        const LV2_Atom_Object* obj = (const LV2_Atom_Object*)buffer; 
     650        if (obj->body.otype == jalv->urids.patch_Set) { 
     651            const LV2_Atom_URID* property = NULL; 
     652            const LV2_Atom*      value    = NULL; 
     653            if (!patch_set_get(jalv, obj, &property, &value)) { 
     654                property_changed(jalv, property->body, value); 
     655            } 
     656        } else if (obj->body.otype == jalv->urids.patch_Put) { 
     657            LV2_ATOM_OBJECT_FOREACH(obj, prop) { 
     658                property_changed(jalv, prop->key, &prop->value); 
     659            } 
     660        } else { 
     661            printf("Unknown object type?\n"); 
     662        } 
     663    } 
    672664} 
    673665 
     
    675667scale_changed(GtkRange* range, gpointer data) 
    676668{ 
    677     const ControlID* control = (const ControlID*)data; 
    678     const double     value   = gtk_range_get_value(range); 
    679     set_float_control(control, value); 
    680     gtk_spin_button_set_value(GTK_SPIN_BUTTON(control->widget->spin), value); 
     669    set_float_control((const ControlID*)data, gtk_range_get_value(range)); 
    681670    return FALSE; 
    682671} 
     
    685674spin_changed(GtkSpinButton* spin, gpointer data) 
    686675{ 
    687     const ControlID* control = (const ControlID*)data; 
    688     const double     value   = gtk_spin_button_get_value(spin); 
     676    const ControlID* control    = (const ControlID*)data; 
     677    Controller*      controller = (Controller*)control->widget; 
     678    const double     value      = gtk_spin_button_get_value(spin); 
    689679    set_float_control(control, value); 
    690     gtk_range_set_value(GTK_RANGE(control->widget->control), value); 
     680    gtk_range_set_value(GTK_RANGE(controller->control), value); 
    691681    return FALSE; 
    692682} 
     
    695685log_scale_changed(GtkRange* range, gpointer data) 
    696686{ 
    697     const ControlID* control = (const ControlID*)data; 
    698     const double     value   = expf(gtk_range_get_value(range)); 
    699     set_float_control(control, value); 
    700     gtk_spin_button_set_value(GTK_SPIN_BUTTON(control->widget->control), value); 
     687    set_float_control((const ControlID*)data, expf(gtk_range_get_value(range))); 
    701688    return FALSE; 
    702689} 
     
    705692log_spin_changed(GtkSpinButton* spin, gpointer data) 
    706693{ 
    707     const ControlID* control = (const ControlID*)data; 
    708     const double     value   = gtk_spin_button_get_value(spin); 
     694    const ControlID* control    = (const ControlID*)data; 
     695    Controller*      controller = (Controller*)control->widget; 
     696    const double     value      = gtk_spin_button_get_value(spin); 
    709697    set_float_control(control, value); 
    710     gtk_range_set_value(GTK_RANGE(control->widget->control), logf(value)); 
     698    gtk_range_set_value(GTK_RANGE(controller->control), logf(value)); 
    711699    return FALSE; 
    712700} 
     
    733721toggle_changed(GtkToggleButton* button, gpointer data) 
    734722{ 
    735     const ControlID* control = (const ControlID*)data; 
    736     const float      value   = gtk_toggle_button_get_active(button) ? 1.0f : 0.0f; 
    737     set_float_control(control, value); 
     723    set_float_control((const ControlID*)data, 
     724                      gtk_toggle_button_get_active(button) ? 1.0f : 0.0f); 
    738725    return FALSE; 
    739726} 
     
    745732    const char* string  = gtk_entry_get_text(widget); 
    746733 
    747     set_control(control, strlen(string), control->jalv->forge.String, string); 
     734    set_control(control, strlen(string) + 1, control->jalv->forge.String, string); 
    748735} 
    749736 
     
    752739             gpointer              data) 
    753740{ 
    754     ControlID*  record   = (ControlID*)data; 
    755     Jalv*       jalv     = record->jalv; 
     741    ControlID*  control   = (ControlID*)data; 
     742    Jalv*       jalv     = control->jalv; 
    756743    const char* filename = gtk_file_chooser_get_filename( 
    757744        GTK_FILE_CHOOSER(widget)); 
    758745 
    759     set_control(record, strlen(filename), jalv->forge.Path, filename); 
    760 } 
    761  
    762 static gint 
    763 dcmp(gconstpointer a, gconstpointer b) 
    764 { 
    765     double y = *(const double*)a; 
    766     double z = *(const double*)b; 
    767     return y < z ? -1 : z < y ? 1 : 0; 
    768 } 
    769  
    770 static gint 
    771 drcmp(gconstpointer a, gconstpointer b) 
    772 { 
    773     double y = *(const double*)a; 
    774     double z = *(const double*)b; 
    775     return y < z ? 1 : z < y ? -1 : 0; 
     746    set_control(control, strlen(filename), jalv->forge.Path, filename); 
    776747} 
    777748 
     
    788759make_combo(ControlID* record, float value) 
    789760{ 
    790     GList*        list       = g_hash_table_get_keys(record->points); 
    791761    GtkListStore* list_store = gtk_list_store_new( 
    792         2, G_TYPE_DOUBLE, G_TYPE_STRING); 
    793     gint active = -1, count = 0; 
    794     for (GList* cur = g_list_sort(list, dcmp); cur; cur = cur->next, ++count) { 
    795         GtkTreeIter iter; 
     762        2, G_TYPE_FLOAT, G_TYPE_STRING); 
     763    int active = -1; 
     764    for (size_t i = 0; i < record->n_points; ++i) { 
     765        const ScalePoint* point = &record->points[i]; 
     766        GtkTreeIter       iter; 
    796767        gtk_list_store_append(list_store, &iter); 
    797768        gtk_list_store_set(list_store, &iter, 
    798                            0, *(double*)cur->data, 
    799                            1, g_hash_table_lookup(record->points, cur->data), 
     769                           0, point->value, 
     770                           1, point->label, 
    800771                           -1); 
    801         if (fabs(value - *(double*)cur->data) < FLT_EPSILON) { 
    802             active = count; 
    803         } 
    804     } 
    805     g_list_free(list); 
     772        if (fabs(value - point->value) < FLT_EPSILON) { 
     773            active = i; 
     774        } 
     775    } 
    806776 
    807777    GtkWidget* combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store)); 
     
    817787 
    818788    return new_controller(NULL, combo); 
    819 } 
    820  
    821 static void 
    822 add_mark(gdouble key, const gchar* value, void* scale) 
    823 { 
    824     gchar* str = g_markup_printf_escaped("<span font_size=\"small\">%s</span>", 
    825                                          value); 
    826     gtk_scale_add_mark(GTK_SCALE(scale), key, GTK_POS_TOP, str); 
    827789} 
    828790 
     
    860822 
    861823    if (record->is_integer) { 
    862         gtk_scale_set_digits(GTK_SCALE(scale), 0); 
     824        gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0); 
     825    } else { 
     826        gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 7); 
    863827    } 
    864828 
     
    867831    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value); 
    868832    if (record->points) { 
    869         GList* list = g_hash_table_get_keys(record->points); 
    870         for (GList* cur = g_list_sort(list, drcmp); cur; cur = cur->next) { 
    871             add_mark(*(gdouble*)cur->data, 
    872                      g_hash_table_lookup(record->points, cur->data), 
    873                      scale); 
     833        for (size_t i = 0; i < record->n_points; ++i) { 
     834            const ScalePoint* point = &record->points[i]; 
     835 
     836            gchar* str = g_markup_printf_escaped( 
     837                "<span font_size=\"small\">%s</span>", point->label); 
     838            gtk_scale_add_mark( 
     839                GTK_SCALE(scale), point->value, GTK_POS_TOP, str); 
    874840        } 
    875841    } 
     
    908874{ 
    909875    GtkWidget* button = gtk_file_chooser_button_new( 
    910         lilv_node_as_uri(record->property), GTK_FILE_CHOOSER_ACTION_OPEN); 
     876        "Open File", GTK_FILE_CHOOSER_ACTION_OPEN); 
    911877    g_signal_connect(G_OBJECT(button), "file-set", 
    912878                     G_CALLBACK(file_changed), record); 
     
    10391005        LilvScalePoints* sp = lilv_port_get_scale_points(plugin, port); 
    10401006        if (sp) { 
    1041             control_id->points = g_hash_table_new(g_double_hash, g_double_equal); 
    1042             int      idx    = 0; 
    1043             gdouble* values = (gdouble*)malloc( 
    1044                 lilv_scale_points_size(sp) * sizeof(gdouble)); 
     1007            control_id->points = (ScalePoint*)malloc( 
     1008                lilv_scale_points_size(sp) * sizeof(ScalePoint)); 
     1009            size_t np = 0; 
    10451010            LILV_FOREACH(scale_points, s, sp) { 
    10461011                const LilvScalePoint* p = lilv_scale_points_get(sp, s); 
    1047                 values[idx] = lilv_node_as_float(lilv_scale_point_get_value(p)); 
    1048                 char* label = g_strdup( 
    1049                     lilv_node_as_string(lilv_scale_point_get_label(p))); 
    1050                 g_hash_table_insert(control_id->points, values + idx, label); 
    1051                 ++idx; 
     1012                if (lilv_node_is_float(lilv_scale_point_get_value(p))) { 
     1013                    control_id->points[np].value = lilv_node_as_float( 
     1014                        lilv_scale_point_get_value(p)); 
     1015                    control_id->points[np].label = g_strdup( 
     1016                        lilv_node_as_string(lilv_scale_point_get_label(p))); 
     1017                    ++np; 
     1018                } 
     1019                /* TODO: Non-float scale points? */ 
    10521020            } 
     1021 
     1022            qsort(control_id->points, np, sizeof(ScalePoint), 
     1023                  (int (*)(const void*, const void*))scale_point_cmp); 
     1024            control_id->n_points = np; 
     1025 
    10531026            lilv_scale_points_free(sp); 
    10541027        } 
     
    10681041        lilv_node_free(comment); 
    10691042 
     1043        add_control(&jalv->controls, control_id); 
    10701044        add_control_row( 
    10711045            port_table, n_rows++, lilv_node_as_string(name), controller); 
     
    11031077        record->widget = controller; 
    11041078        if (controller) { 
     1079            add_control(&jalv->controls, record); 
    11051080            add_control_row( 
    11061081                port_table, n_rows++, 
  • src/jalv_internal.h

    r22ea533 radd42b1  
    11/* 
    2   Copyright 2007-2014 David Robillard <http://drobilla.net> 
     2  Copyright 2007-2015 David Robillard <http://drobilla.net> 
    33 
    44  Permission to use, copy, modify, and/or distribute this software for any 
     
    1818#define JALV_INTERNAL_H 
    1919 
     20#include <stdio.h> 
    2021#include <stdlib.h> 
    2122#include <string.h> 
     23#ifdef HAVE_ISATTY 
     24#    include <unistd.h> 
     25#endif 
    2226 
    2327#include <jack/jack.h> 
     
    4852extern "C" { 
    4953#endif 
     54 
     55typedef struct Jalv Jalv; 
    5056 
    5157enum PortFlow { 
     
    7480    bool            old_api;    ///< True for event, false for atom 
    7581}; 
     82 
     83/* Controls */ 
     84 
     85/** Type of plugin control. */ 
     86typedef enum { 
     87    PORT,     ///< Control port 
     88    PROPERTY  ///< Property (set via atom message) 
     89} ControlType; 
     90 
     91typedef struct { 
     92    float value; 
     93    char* label; 
     94} ScalePoint; 
     95 
     96/** Order scale points by value. */ 
     97int scale_point_cmp(const ScalePoint* a, const ScalePoint* b); 
     98 
     99/** Plugin control. */ 
     100typedef struct { 
     101    Jalv*       jalv; 
     102    ControlType type; 
     103    LV2_URID    property;        ///< Iff type == PROPERTY 
     104    uint32_t    index;           ///< Iff type == PORT 
     105    void*       widget;          ///< Control Widget 
     106    size_t      n_points;        ///< Number of scale points 
     107    ScalePoint* points;          ///< Scale points 
     108    LV2_URID    value_type;      ///< Type of control value 
     109    LilvNode*   min;             ///< Minimum value 
     110    LilvNode*   max;             ///< Maximum value 
     111    LilvNode*   def;             ///< Default value 
     112    bool        is_toggle;       ///< Boolean (0 and 1 only) 
     113    bool        is_integer;      ///< Integer values only 
     114    bool        is_enumeration;  ///< Point values only 
     115    bool        is_logarithmic;  ///< Logarithmic scale 
     116} ControlID; 
     117 
     118ControlID* 
     119new_port_control(Jalv* jalv, uint32_t index); 
     120 
     121ControlID* 
     122new_property_control(Jalv* jalv, const LilvNode* property); 
     123 
     124typedef struct { 
     125    size_t      n_controls; 
     126    ControlID** controls; 
     127} Controls; 
     128 
     129void 
     130add_control(Controls* controls, ControlID* control); 
     131 
     132ControlID* 
     133get_property_control(const Controls* controls, LV2_URID property); 
    76134 
    77135/** 
     
    106164    LV2_URID atom_Float; 
    107165    LV2_URID atom_Int; 
     166    LV2_URID atom_Object; 
     167    LV2_URID atom_Path; 
     168    LV2_URID atom_String; 
    108169    LV2_URID atom_eventTransfer; 
    109170    LV2_URID bufsz_maxBlockLength; 
     
    113174    LV2_URID midi_MidiEvent; 
    114175    LV2_URID param_sampleRate; 
     176    LV2_URID patch_Get; 
     177    LV2_URID patch_Put; 
    115178    LV2_URID patch_Set; 
    116179    LV2_URID patch_property; 
     
    178241} JalvWorker; 
    179242 
    180 typedef struct { 
     243struct Jalv { 
    181244    JalvOptions        opts;           ///< Command-line options 
    182245    JalvURIDs          urids;          ///< URIDs 
     
    211274    void*              window;         ///< Window (if applicable) 
    212275    struct Port*       ports;          ///< Port array of size num_ports 
     276    Controls           controls;       ///< Available plugin controls 
    213277    uint32_t           block_length;   ///< Jack buffer size (block length) 
    214278    size_t             midi_buf_size;  ///< Size of MIDI port buffers 
     
    227291    bool               exit;           ///< True iff execution is finished 
    228292    bool               has_ui;         ///< True iff a control UI is present 
    229 } Jalv; 
     293    bool               state_changed;  ///< Plugin state has changed 
     294}; 
    230295 
    231296int 
     
    356421             va_list        ap); 
    357422 
     423static inline void 
     424jalv_ansi_start(FILE* stream, int color) 
     425{ 
     426#ifdef HAVE_ISATTY 
     427    if (isatty(fileno(stream))) { 
     428        fprintf(stream, "\033[0;%dm\n", color); 
     429    } 
     430#endif 
     431} 
     432 
     433static inline void 
     434jalv_ansi_reset(FILE* stream) 
     435{ 
     436#ifdef HAVE_ISATTY 
     437    if (isatty(fileno(stream))) { 
     438        fprintf(stream, "\033[0m\n"); 
     439    } 
     440#endif 
     441} 
     442 
    358443#ifdef __cplusplus 
    359444}  // extern "C" 
  • src/state.c

    r5537b61 radd42b1  
    191191            state, jalv->instance, set_port_value, jalv, 0, NULL); 
    192192 
     193        jalv->state_changed = true; 
    193194        if (must_pause) { 
    194195            jalv->play_state = JALV_RUNNING; 
  • wscript

    rc9610bf radd42b1  
    9595               mandatory=False) 
    9696 
     97    defines = ['_POSIX_SOURCE'] 
     98 
     99    conf.check(function_name='isatty', 
     100               header_name='unistd.h', 
     101               defines=defines, 
     102               define_name='HAVE_ISATTY', 
     103               mandatory=False) 
     104 
     105    conf.check(function_name='fileno', 
     106               header_name='stdio.h', 
     107               defines=defines, 
     108               define_name='HAVE_FILENO', 
     109               mandatory=False) 
     110 
     111    if conf.is_defined('HAVE_ISATTY') and conf.is_defined('HAVE_FILENO'): 
     112        autowaf.define(conf, 'JALV_WITH_COLOR', 1) 
     113        conf.env.append_unique('CFLAGS', ['-D_POSIX_SOURCE']) 
     114 
    97115    if not Options.options.no_jack_session: 
    98116        autowaf.define(conf, 'JALV_JACK_SESSION', 1) 
     
    109127    autowaf.display_msg(conf, "Qt 4.0 support", bool(conf.env.HAVE_QT4)) 
    110128    autowaf.display_msg(conf, "Qt 5.0 support", bool(conf.env.HAVE_QT5)) 
     129    autowaf.display_msg(conf, "Color output", bool(conf.env.JALV_WITH_COLOR)) 
    111130    print('') 
    112131 
     
    114133    libs = 'LILV SUIL JACK SERD SORD SRATOM LV2' 
    115134 
    116     source = 'src/jalv.c src/symap.c src/state.c src/lv2_evbuf.c src/worker.c src/log.c' 
     135    source = 'src/jalv.c src/symap.c src/state.c src/lv2_evbuf.c src/worker.c src/log.c src/control.c' 
    117136 
    118137    # Non-GUI version 
Note: See TracChangeset for help on using the changeset viewer.