mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-08 15:54:57 +01:00
More generic RT-safe implementation of LV2 properties.
This commit is contained in:
parent
324ab35abc
commit
5de6c21ec1
7 changed files with 422 additions and 198 deletions
|
|
@ -41,9 +41,6 @@
|
||||||
#include "ardour/plugin.h"
|
#include "ardour/plugin.h"
|
||||||
#include "ardour/plugin_insert.h"
|
#include "ardour/plugin_insert.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
#ifdef LV2_SUPPORT
|
|
||||||
#include "ardour/lv2_plugin.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ardour_ui.h"
|
#include "ardour_ui.h"
|
||||||
#include "prompter.h"
|
#include "prompter.h"
|
||||||
|
|
@ -68,9 +65,6 @@ GenericPluginUI::GenericPluginUI (boost::shared_ptr<PluginInsert> pi, bool scrol
|
||||||
, scroller_view(hAdjustment, vAdjustment)
|
, scroller_view(hAdjustment, vAdjustment)
|
||||||
, automation_menu (0)
|
, automation_menu (0)
|
||||||
, is_scrollable(scrollable)
|
, is_scrollable(scrollable)
|
||||||
#ifdef LV2_SUPPORT
|
|
||||||
, _fcb(0)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
set_name ("PluginEditor");
|
set_name ("PluginEditor");
|
||||||
set_border_width (10);
|
set_border_width (10);
|
||||||
|
|
@ -143,9 +137,6 @@ GenericPluginUI::~GenericPluginUI ()
|
||||||
if (output_controls.size() > 0) {
|
if (output_controls.size() > 0) {
|
||||||
screen_update_connection.disconnect();
|
screen_update_connection.disconnect();
|
||||||
}
|
}
|
||||||
#ifdef LV2_SUPPORT
|
|
||||||
free(_fcb);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some functions for calculating the 'similarity' of two plugin
|
// Some functions for calculating the 'similarity' of two plugin
|
||||||
|
|
@ -314,33 +305,42 @@ GenericPluginUI::build ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LV2_SUPPORT
|
// Add property controls (currently file chooser button for paths only)
|
||||||
boost::shared_ptr<ARDOUR::LV2Plugin> lv2p = boost::dynamic_pointer_cast<LV2Plugin> (plugin);
|
typedef std::vector<Plugin::ParameterDescriptor> Descs;
|
||||||
if (lv2p) {
|
Descs descs;
|
||||||
_fcb = (Gtk::FileChooserButton**) malloc(lv2p->patch_count() * sizeof(Gtk::FileChooserButton*));
|
plugin->get_supported_properties(descs);
|
||||||
for (uint32_t p = 0; p < lv2p->patch_count(); ++p) {
|
for (Descs::const_iterator d = descs.begin(); d != descs.end(); ++d) {
|
||||||
_fcb[p] = manage (new Gtk::FileChooserButton (Gtk::FILE_CHOOSER_ACTION_OPEN));
|
if (d->datatype == Variant::PATH) {
|
||||||
_fcb[p]->signal_file_set().connect (sigc::bind(sigc::mem_fun (*this, &GenericPluginUI::patch_set_file), p));
|
// Create/add label
|
||||||
lv2p->PatchChanged.connect (*this, invalidator (*this), boost::bind (&GenericPluginUI::patch_changed, this, _1), gui_context());
|
Gtk::Label* label = manage(new Label(d->label));
|
||||||
// when user cancels file selection the FileChooserButton will display "None"
|
button_table.attach(*label,
|
||||||
// TODO hack away around this..
|
0, button_cols, button_row, button_row + 1,
|
||||||
if (lv2p->patch_val(p)) {
|
FILL|EXPAND, FILL);
|
||||||
_fcb[p]->set_filename(lv2p->patch_val(p));
|
|
||||||
}
|
|
||||||
if (lv2p->patch_key(p)) {
|
|
||||||
_fcb[p]->set_title(lv2p->patch_key(p));
|
|
||||||
Gtk::Label* fcl = manage (new Label (lv2p->patch_key(p)));
|
|
||||||
button_table.attach (*fcl, 0, button_cols, button_row, button_row + 1, FILL|EXPAND, FILL);
|
|
||||||
++button_row;
|
|
||||||
} else {
|
|
||||||
_fcb[p]->set_title(_("LV2 Patch"));
|
|
||||||
}
|
|
||||||
|
|
||||||
button_table.attach (*_fcb[p], 0, button_cols, button_row, button_row + 1, FILL|EXPAND, FILL);
|
|
||||||
++button_row;
|
++button_row;
|
||||||
|
|
||||||
|
// Create/add controller
|
||||||
|
Gtk::FileChooserButton* widget = manage(
|
||||||
|
new Gtk::FileChooserButton(Gtk::FILE_CHOOSER_ACTION_OPEN));
|
||||||
|
widget->set_title(d->label);
|
||||||
|
_property_controls.insert(std::make_pair(d->key, widget));
|
||||||
|
button_table.attach(*widget,
|
||||||
|
0, button_cols, button_row, button_row + 1,
|
||||||
|
FILL|EXPAND, FILL);
|
||||||
|
++button_row;
|
||||||
|
|
||||||
|
// Connect signals
|
||||||
|
widget->signal_file_set().connect(
|
||||||
|
sigc::bind(sigc::mem_fun(*this, &GenericPluginUI::set_property), *d, widget));
|
||||||
|
plugin->PropertyChanged.connect(*this, invalidator(*this),
|
||||||
|
boost::bind(&GenericPluginUI::property_changed, this, _1, _2),
|
||||||
|
gui_context());
|
||||||
|
} else {
|
||||||
|
// TODO: widgets for other datatypes, use ControlUI?
|
||||||
|
std::cerr << "warning: unsupported property " << d->key
|
||||||
|
<< " type " << d->datatype << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
plugin->announce_property_values();
|
||||||
|
|
||||||
// Iterate over the list of controls to find which adjacent controls
|
// Iterate over the list of controls to find which adjacent controls
|
||||||
// are similar enough to be grouped together.
|
// are similar enough to be grouped together.
|
||||||
|
|
@ -971,20 +971,20 @@ GenericPluginUI::output_update ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LV2_SUPPORT
|
|
||||||
|
|
||||||
void
|
void
|
||||||
GenericPluginUI::patch_set_file (uint32_t p)
|
GenericPluginUI::set_property (const Plugin::ParameterDescriptor& desc,
|
||||||
|
Gtk::FileChooserButton* widget)
|
||||||
{
|
{
|
||||||
boost::shared_ptr<ARDOUR::LV2Plugin> lv2p = boost::dynamic_pointer_cast<LV2Plugin> (plugin);
|
plugin->set_property(desc.key, Variant(Variant::PATH, widget->get_filename()));
|
||||||
lv2p->patch_set(p, _fcb[p]->get_filename ().c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GenericPluginUI::patch_changed (uint32_t p)
|
GenericPluginUI::property_changed (uint32_t key, const Variant& value)
|
||||||
{
|
{
|
||||||
boost::shared_ptr<ARDOUR::LV2Plugin> lv2p = boost::dynamic_pointer_cast<LV2Plugin> (plugin);
|
PropertyControls::iterator c = _property_controls.find(key);
|
||||||
_fcb[p]->set_filename(lv2p->patch_val(p));
|
if (c != _property_controls.end()) {
|
||||||
|
c->second->set_filename(value.get_path());
|
||||||
|
} else {
|
||||||
|
std::cerr << "warning: property change for property with no control" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
|
|
||||||
#include "ardour/types.h"
|
#include "ardour/types.h"
|
||||||
#include "ardour/plugin.h"
|
#include "ardour/plugin.h"
|
||||||
|
#include "ardour/variant.h"
|
||||||
|
|
||||||
#include "automation_controller.h"
|
#include "automation_controller.h"
|
||||||
#include "ardour_button.h"
|
#include "ardour_button.h"
|
||||||
|
|
@ -279,11 +280,12 @@ class GenericPluginUI : public PlugUIBase, public Gtk::VBox
|
||||||
bool integer_printer (char* buf, Gtk::Adjustment &, ControlUI *);
|
bool integer_printer (char* buf, Gtk::Adjustment &, ControlUI *);
|
||||||
bool midinote_printer(char* buf, Gtk::Adjustment &, ControlUI *);
|
bool midinote_printer(char* buf, Gtk::Adjustment &, ControlUI *);
|
||||||
|
|
||||||
#ifdef LV2_SUPPORT
|
void set_property (const ARDOUR::Plugin::ParameterDescriptor& desc,
|
||||||
void patch_set_file (uint32_t patch_idx);
|
Gtk::FileChooserButton* widget);
|
||||||
void patch_changed (uint32_t patch_idx);
|
void property_changed (uint32_t key, const ARDOUR::Variant& value);
|
||||||
Gtk::FileChooserButton **_fcb;
|
|
||||||
#endif
|
typedef std::map<uint32_t, Gtk::FileChooserButton*> PropertyControls;
|
||||||
|
PropertyControls _property_controls;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PluginUIWindow : public ArdourWindow
|
class PluginUIWindow : public ArdourWindow
|
||||||
|
|
|
||||||
|
|
@ -146,16 +146,13 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
|
||||||
|
|
||||||
Worker* worker() { return _worker; }
|
Worker* worker() { return _worker; }
|
||||||
|
|
||||||
uint32_t patch_count() const { return _patch_count; }
|
|
||||||
const char * patch_uri(const uint32_t p) const { if (p < _patch_count) return _patch_value_uri[p]; else return NULL; }
|
|
||||||
const char * patch_key(const uint32_t p) const { if (p < _patch_count) return _patch_value_key[p]; else return NULL; }
|
|
||||||
const char * patch_val(const uint32_t p) const { if (p < _patch_count) return _patch_value_cur[p]; else return NULL; }
|
|
||||||
bool patch_set(const uint32_t p, const char * val);
|
|
||||||
PBD::Signal1<void,const uint32_t> PatchChanged;
|
|
||||||
|
|
||||||
int work(uint32_t size, const void* data);
|
int work(uint32_t size, const void* data);
|
||||||
int work_response(uint32_t size, const void* data);
|
int work_response(uint32_t size, const void* data);
|
||||||
|
|
||||||
|
void set_property(uint32_t key, const Variant& value);
|
||||||
|
void get_supported_properties(std::vector<ParameterDescriptor>& descs);
|
||||||
|
void announce_property_values();
|
||||||
|
|
||||||
static URIMap _uri_map;
|
static URIMap _uri_map;
|
||||||
|
|
||||||
struct URIDs {
|
struct URIDs {
|
||||||
|
|
@ -165,6 +162,7 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
|
||||||
uint32_t atom_eventTransfer;
|
uint32_t atom_eventTransfer;
|
||||||
uint32_t atom_URID;
|
uint32_t atom_URID;
|
||||||
uint32_t atom_Blank;
|
uint32_t atom_Blank;
|
||||||
|
uint32_t atom_Object;
|
||||||
uint32_t log_Error;
|
uint32_t log_Error;
|
||||||
uint32_t log_Note;
|
uint32_t log_Note;
|
||||||
uint32_t log_Warning;
|
uint32_t log_Warning;
|
||||||
|
|
@ -177,6 +175,7 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
|
||||||
uint32_t time_beatsPerMinute;
|
uint32_t time_beatsPerMinute;
|
||||||
uint32_t time_frame;
|
uint32_t time_frame;
|
||||||
uint32_t time_speed;
|
uint32_t time_speed;
|
||||||
|
uint32_t patch_Get;
|
||||||
uint32_t patch_Set;
|
uint32_t patch_Set;
|
||||||
uint32_t patch_property;
|
uint32_t patch_property;
|
||||||
uint32_t patch_value;
|
uint32_t patch_value;
|
||||||
|
|
@ -202,13 +201,8 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
|
||||||
framepos_t _next_cycle_start; ///< Expected start frame of next run cycle
|
framepos_t _next_cycle_start; ///< Expected start frame of next run cycle
|
||||||
double _next_cycle_speed; ///< Expected start frame of next run cycle
|
double _next_cycle_speed; ///< Expected start frame of next run cycle
|
||||||
PBD::ID _insert_id;
|
PBD::ID _insert_id;
|
||||||
|
uint32_t _patch_port_in_index;
|
||||||
uint32_t _patch_count;
|
uint32_t _patch_port_out_index;
|
||||||
char ** _patch_value_uri;
|
|
||||||
char ** _patch_value_key;
|
|
||||||
char (*_patch_value_cur)[PATH_MAX]; ///< current value
|
|
||||||
char (*_patch_value_set)[PATH_MAX]; ///< new value to set
|
|
||||||
Glib::Threads::Mutex _patch_set_lock;
|
|
||||||
|
|
||||||
friend const void* lv2plugin_get_port_value(const char* port_symbol,
|
friend const void* lv2plugin_get_port_value(const char* port_symbol,
|
||||||
void* user_data,
|
void* user_data,
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,11 @@
|
||||||
#include "ardour/chan_mapping.h"
|
#include "ardour/chan_mapping.h"
|
||||||
#include "ardour/cycles.h"
|
#include "ardour/cycles.h"
|
||||||
#include "ardour/latent.h"
|
#include "ardour/latent.h"
|
||||||
#include "ardour/plugin_insert.h"
|
|
||||||
#include "ardour/libardour_visibility.h"
|
#include "ardour/libardour_visibility.h"
|
||||||
#include "ardour/types.h"
|
|
||||||
#include "ardour/midi_state_tracker.h"
|
#include "ardour/midi_state_tracker.h"
|
||||||
|
#include "ardour/plugin_insert.h"
|
||||||
|
#include "ardour/types.h"
|
||||||
|
#include "ardour/variant.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
@ -128,6 +129,8 @@ class LIBARDOUR_API Plugin : public PBD::StatefulDestructible, public Latent
|
||||||
bool max_unbound;
|
bool max_unbound;
|
||||||
bool enumeration;
|
bool enumeration;
|
||||||
bool midinote; ///< only used if integer_step is also true
|
bool midinote; ///< only used if integer_step is also true
|
||||||
|
uint32_t key; ///< for properties
|
||||||
|
Variant::Type datatype; ///< for properties
|
||||||
};
|
};
|
||||||
|
|
||||||
XMLNode& get_state ();
|
XMLNode& get_state ();
|
||||||
|
|
@ -267,6 +270,31 @@ class LIBARDOUR_API Plugin : public PBD::StatefulDestructible, public Latent
|
||||||
void set_cycles (uint32_t c) { _cycles = c; }
|
void set_cycles (uint32_t c) { _cycles = c; }
|
||||||
cycles_t cycles() const { return _cycles; }
|
cycles_t cycles() const { return _cycles; }
|
||||||
|
|
||||||
|
/** Get a descrption of all properties supported by this plugin.
|
||||||
|
*
|
||||||
|
* Properties are distinct from parameters in that they are potentially
|
||||||
|
* dynamic, referred to by key, and do not correspond 1:1 with ports.
|
||||||
|
*
|
||||||
|
* For LV2 plugins, properties are implemented by sending/receiving set/get
|
||||||
|
* messages to/from the plugin via event ports.
|
||||||
|
*/
|
||||||
|
virtual void get_supported_properties(std::vector<ParameterDescriptor>& descs) {}
|
||||||
|
|
||||||
|
/** Set a property from the UI.
|
||||||
|
*
|
||||||
|
* This is not UI-specific, but may only be used by one thread. If the
|
||||||
|
* Ardour UI is present, that is the UI thread, but otherwise, any thread
|
||||||
|
* except the audio thread may call this function as long as it is not
|
||||||
|
* called concurrently.
|
||||||
|
*/
|
||||||
|
virtual void set_property(uint32_t key, const Variant& value) {}
|
||||||
|
|
||||||
|
/** Emit PropertyChanged for all current property values. */
|
||||||
|
virtual void announce_property_values() {}
|
||||||
|
|
||||||
|
/** Emitted when a property is changed in the plugin. */
|
||||||
|
PBD::Signal2<void, uint32_t, Variant> PropertyChanged;
|
||||||
|
|
||||||
PBD::Signal1<void,uint32_t> StartTouch;
|
PBD::Signal1<void,uint32_t> StartTouch;
|
||||||
PBD::Signal1<void,uint32_t> EndTouch;
|
PBD::Signal1<void,uint32_t> EndTouch;
|
||||||
|
|
||||||
|
|
|
||||||
102
libs/ardour/ardour/variant.h
Normal file
102
libs/ardour/ardour/variant.h
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 Paul Davis
|
||||||
|
Author: David Robillard
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ardour_variant_h__
|
||||||
|
#define __ardour_variant_h__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "ardour/libardour_visibility.h"
|
||||||
|
#include "pbd/compose.h"
|
||||||
|
|
||||||
|
namespace ARDOUR {
|
||||||
|
|
||||||
|
/** A value with dynamic type (tagged union). */
|
||||||
|
class LIBARDOUR_API Variant
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Type {
|
||||||
|
BOOL, ///< Boolean
|
||||||
|
DOUBLE, ///< C double (64-bit IEEE-754)
|
||||||
|
FLOAT, ///< C float (32-bit IEEE-754)
|
||||||
|
INT, ///< Signed 32-bit int
|
||||||
|
LONG, ///< Signed 64-bit int
|
||||||
|
PATH, ///< File path string
|
||||||
|
STRING, ///< Raw string (no semantics)
|
||||||
|
URI ///< URI string
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit Variant(bool value) : _type(BOOL) { _bool = value; }
|
||||||
|
explicit Variant(double value) : _type(DOUBLE) { _double = value; }
|
||||||
|
explicit Variant(float value) : _type(FLOAT) { _float = value; }
|
||||||
|
explicit Variant(int value) : _type(INT) { _int = value; }
|
||||||
|
explicit Variant(long value) : _type(LONG) { _long = value; }
|
||||||
|
|
||||||
|
Variant(Type type, const std::string& value)
|
||||||
|
: _type(type)
|
||||||
|
, _string(value)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool get_bool() const { ensure_type(BOOL); return _bool; }
|
||||||
|
double get_double() const { ensure_type(DOUBLE); return _double; }
|
||||||
|
float get_float() const { ensure_type(FLOAT); return _float; }
|
||||||
|
int get_int() const { ensure_type(INT); return _int; }
|
||||||
|
long get_long() const { ensure_type(LONG); return _long; }
|
||||||
|
|
||||||
|
const std::string& get_path() const { ensure_type(PATH); return _string; }
|
||||||
|
const std::string& get_string() const { ensure_type(STRING); return _string; }
|
||||||
|
const std::string& get_uri() const { ensure_type(URI); return _string; }
|
||||||
|
|
||||||
|
Type type() const { return _type; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const char* type_name(const Type type) {
|
||||||
|
static const char* names[] = {
|
||||||
|
"bool", "double", "float", "int", "long", "path", "string", "uri"
|
||||||
|
};
|
||||||
|
|
||||||
|
return names[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensure_type(const Type type) const {
|
||||||
|
if (_type != type) {
|
||||||
|
throw std::domain_error(
|
||||||
|
string_compose("get_%1 called on %2 variant",
|
||||||
|
type_name(type), type_name(_type)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type _type; ///< Type tag
|
||||||
|
std::string _string; ///< For all string types (PATH, STRING, URI)
|
||||||
|
|
||||||
|
// Union of all primitive numeric types
|
||||||
|
union {
|
||||||
|
bool _bool;
|
||||||
|
double _double;
|
||||||
|
float _float;
|
||||||
|
int32_t _int;
|
||||||
|
int64_t _long;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ARDOUR
|
||||||
|
|
||||||
|
#endif // __ardour_variant_h__
|
||||||
|
|
@ -78,6 +78,15 @@
|
||||||
#include <suil/suil.h>
|
#include <suil/suil.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Compatibility for lv2-1.0.0
|
||||||
|
#ifndef LV2_ATOM_CONTENTS_CONST
|
||||||
|
#define LV2_ATOM_CONTENTS_CONST(type, atom) \
|
||||||
|
((const void*)((const uint8_t*)(atom) + sizeof(type)))
|
||||||
|
#endif
|
||||||
|
#ifndef LV2_ATOM_BODY_CONST
|
||||||
|
#define LV2_ATOM_BODY_CONST(atom) LV2_ATOM_CONTENTS_CONST(LV2_Atom, atom)
|
||||||
|
#endif
|
||||||
|
|
||||||
/** The number of MIDI buffers that will fit in a UI/worker comm buffer.
|
/** The number of MIDI buffers that will fit in a UI/worker comm buffer.
|
||||||
This needs to be roughly the number of cycles the UI will get around to
|
This needs to be roughly the number of cycles the UI will get around to
|
||||||
actually processing the traffic. Lower values are flakier but save memory.
|
actually processing the traffic. Lower values are flakier but save memory.
|
||||||
|
|
@ -97,6 +106,7 @@ LV2Plugin::URIDs LV2Plugin::urids = {
|
||||||
_uri_map.uri_to_id(LV2_ATOM__eventTransfer),
|
_uri_map.uri_to_id(LV2_ATOM__eventTransfer),
|
||||||
_uri_map.uri_to_id(LV2_ATOM__URID),
|
_uri_map.uri_to_id(LV2_ATOM__URID),
|
||||||
_uri_map.uri_to_id(LV2_ATOM__Blank),
|
_uri_map.uri_to_id(LV2_ATOM__Blank),
|
||||||
|
_uri_map.uri_to_id(LV2_ATOM__Object),
|
||||||
_uri_map.uri_to_id(LV2_LOG__Error),
|
_uri_map.uri_to_id(LV2_LOG__Error),
|
||||||
_uri_map.uri_to_id(LV2_LOG__Note),
|
_uri_map.uri_to_id(LV2_LOG__Note),
|
||||||
_uri_map.uri_to_id(LV2_LOG__Warning),
|
_uri_map.uri_to_id(LV2_LOG__Warning),
|
||||||
|
|
@ -109,6 +119,7 @@ LV2Plugin::URIDs LV2Plugin::urids = {
|
||||||
_uri_map.uri_to_id(LV2_TIME__beatsPerMinute),
|
_uri_map.uri_to_id(LV2_TIME__beatsPerMinute),
|
||||||
_uri_map.uri_to_id(LV2_TIME__frame),
|
_uri_map.uri_to_id(LV2_TIME__frame),
|
||||||
_uri_map.uri_to_id(LV2_TIME__speed),
|
_uri_map.uri_to_id(LV2_TIME__speed),
|
||||||
|
_uri_map.uri_to_id(LV2_PATCH__Get),
|
||||||
_uri_map.uri_to_id(LV2_PATCH__Set),
|
_uri_map.uri_to_id(LV2_PATCH__Set),
|
||||||
_uri_map.uri_to_id(LV2_PATCH__property),
|
_uri_map.uri_to_id(LV2_PATCH__property),
|
||||||
_uri_map.uri_to_id(LV2_PATCH__value)
|
_uri_map.uri_to_id(LV2_PATCH__value)
|
||||||
|
|
@ -145,6 +156,8 @@ public:
|
||||||
LilvNode* lv2_toggled;
|
LilvNode* lv2_toggled;
|
||||||
LilvNode* midi_MidiEvent;
|
LilvNode* midi_MidiEvent;
|
||||||
LilvNode* rdfs_comment;
|
LilvNode* rdfs_comment;
|
||||||
|
LilvNode* rdfs_label;
|
||||||
|
LilvNode* rdfs_range;
|
||||||
LilvNode* rsz_minimumSize;
|
LilvNode* rsz_minimumSize;
|
||||||
LilvNode* time_Position;
|
LilvNode* time_Position;
|
||||||
LilvNode* ui_GtkUI;
|
LilvNode* ui_GtkUI;
|
||||||
|
|
@ -250,6 +263,7 @@ struct LV2Plugin::Impl {
|
||||||
const LV2_Worker_Interface* work_iface;
|
const LV2_Worker_Interface* work_iface;
|
||||||
LilvState* state;
|
LilvState* state;
|
||||||
LV2_Atom_Forge forge;
|
LV2_Atom_Forge forge;
|
||||||
|
LV2_Atom_Forge ui_forge;
|
||||||
};
|
};
|
||||||
|
|
||||||
LV2Plugin::LV2Plugin (AudioEngine& engine,
|
LV2Plugin::LV2Plugin (AudioEngine& engine,
|
||||||
|
|
@ -262,11 +276,8 @@ LV2Plugin::LV2Plugin (AudioEngine& engine,
|
||||||
, _features(NULL)
|
, _features(NULL)
|
||||||
, _worker(NULL)
|
, _worker(NULL)
|
||||||
, _insert_id("0")
|
, _insert_id("0")
|
||||||
, _patch_count(0)
|
, _patch_port_in_index((uint32_t)-1)
|
||||||
, _patch_value_uri(NULL)
|
, _patch_port_out_index((uint32_t)-1)
|
||||||
, _patch_value_key(NULL)
|
|
||||||
, _patch_value_cur(NULL)
|
|
||||||
, _patch_value_set(NULL)
|
|
||||||
{
|
{
|
||||||
init(c_plugin, rate);
|
init(c_plugin, rate);
|
||||||
}
|
}
|
||||||
|
|
@ -278,11 +289,8 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other)
|
||||||
, _features(NULL)
|
, _features(NULL)
|
||||||
, _worker(NULL)
|
, _worker(NULL)
|
||||||
, _insert_id(other._insert_id)
|
, _insert_id(other._insert_id)
|
||||||
, _patch_count(0)
|
, _patch_port_in_index((uint32_t)-1)
|
||||||
, _patch_value_uri(NULL)
|
, _patch_port_out_index((uint32_t)-1)
|
||||||
, _patch_value_key(NULL)
|
|
||||||
, _patch_value_cur(NULL)
|
|
||||||
, _patch_value_set(NULL)
|
|
||||||
{
|
{
|
||||||
init(other._impl->plugin, other._sample_rate);
|
init(other._impl->plugin, other._sample_rate);
|
||||||
|
|
||||||
|
|
@ -353,6 +361,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
lv2_atom_forge_init(&_impl->forge, _uri_map.urid_map());
|
lv2_atom_forge_init(&_impl->forge, _uri_map.urid_map());
|
||||||
|
lv2_atom_forge_init(&_impl->ui_forge, _uri_map.urid_map());
|
||||||
|
|
||||||
#ifdef HAVE_LV2_1_2_0
|
#ifdef HAVE_LV2_1_2_0
|
||||||
LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int);
|
LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int);
|
||||||
|
|
@ -476,6 +485,11 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
|
||||||
}
|
}
|
||||||
if (lilv_nodes_contains(atom_supports, _world.patch_Message)) {
|
if (lilv_nodes_contains(atom_supports, _world.patch_Message)) {
|
||||||
flags |= PORT_PATCHMSG;
|
flags |= PORT_PATCHMSG;
|
||||||
|
if (flags & PORT_INPUT) {
|
||||||
|
_patch_port_in_index = i;
|
||||||
|
} else {
|
||||||
|
_patch_port_out_index = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LilvNodes* min_size_v = lilv_port_get_value(_impl->plugin, port, _world.rsz_minimumSize);
|
LilvNodes* min_size_v = lilv_port_get_value(_impl->plugin, port, _world.rsz_minimumSize);
|
||||||
|
|
@ -553,32 +567,6 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
|
||||||
|
|
||||||
delete[] params;
|
delete[] params;
|
||||||
|
|
||||||
/* scan supported patch:writable for this plugin.
|
|
||||||
* Note: the first Atom-port (in every direction) that supports patch:Message will be used
|
|
||||||
*/
|
|
||||||
LilvNode* rdfs_label = lilv_new_uri(_world.world, LILV_NS_RDFS "label");
|
|
||||||
LilvNode* rdfs_range = lilv_new_uri(_world.world, LILV_NS_RDFS "range");
|
|
||||||
LilvNodes* properties = lilv_world_find_nodes (_world.world, lilv_plugin_get_uri(plugin), _world.patch_writable, NULL);
|
|
||||||
LILV_FOREACH(nodes, p, properties) {
|
|
||||||
const LilvNode* property = lilv_nodes_get(properties, p);
|
|
||||||
LilvNode* label = lilv_nodes_get_first (lilv_world_find_nodes (_world.world, property, rdfs_label, NULL));
|
|
||||||
LilvNode* range = lilv_nodes_get_first (lilv_world_find_nodes (_world.world, property, rdfs_range, NULL));
|
|
||||||
if (!range || _uri_map.uri_to_id(lilv_node_as_uri(range)) != LV2Plugin::urids.atom_Path) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_patch_value_uri = (char**) realloc (_patch_value_uri, (_patch_count + 1) * sizeof(char**));
|
|
||||||
_patch_value_key = (char**) realloc (_patch_value_key, (_patch_count + 1) * sizeof(char**));
|
|
||||||
_patch_value_uri[_patch_count] = strdup(lilv_node_as_uri(property));
|
|
||||||
_patch_value_key[_patch_count] = strdup(lilv_node_as_string(label ? label : property));
|
|
||||||
++_patch_count;
|
|
||||||
}
|
|
||||||
lilv_node_free(rdfs_label);
|
|
||||||
lilv_node_free(rdfs_range);
|
|
||||||
lilv_nodes_free(properties);
|
|
||||||
_patch_value_cur = (char(*)[PATH_MAX]) calloc(_patch_count, sizeof(char[PATH_MAX]));
|
|
||||||
_patch_value_set = (char(*)[PATH_MAX]) calloc(_patch_count, sizeof(char[PATH_MAX]));
|
|
||||||
|
|
||||||
LilvUIs* uis = lilv_plugin_get_uis(plugin);
|
LilvUIs* uis = lilv_plugin_get_uis(plugin);
|
||||||
if (lilv_uis_size(uis) > 0) {
|
if (lilv_uis_size(uis) > 0) {
|
||||||
#ifdef HAVE_SUIL
|
#ifdef HAVE_SUIL
|
||||||
|
|
@ -644,13 +632,6 @@ LV2Plugin::~LV2Plugin ()
|
||||||
free(_make_path_feature.data);
|
free(_make_path_feature.data);
|
||||||
free(_work_schedule_feature.data);
|
free(_work_schedule_feature.data);
|
||||||
|
|
||||||
for (uint32_t pidx = 0; pidx < _patch_count; ++pidx) {
|
|
||||||
free(_patch_value_uri[pidx]);
|
|
||||||
free(_patch_value_key[pidx]);
|
|
||||||
}
|
|
||||||
free(_patch_value_cur);
|
|
||||||
free(_patch_value_set);
|
|
||||||
|
|
||||||
delete _to_ui;
|
delete _to_ui;
|
||||||
delete _from_ui;
|
delete _from_ui;
|
||||||
delete _worker;
|
delete _worker;
|
||||||
|
|
@ -1239,6 +1220,166 @@ LV2Plugin::write_to_ui(uint32_t index,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forge_variant(LV2_Atom_Forge* forge, const Variant& value)
|
||||||
|
{
|
||||||
|
switch (value.type()) {
|
||||||
|
case Variant::BOOL:
|
||||||
|
lv2_atom_forge_bool(forge, value.get_bool());
|
||||||
|
break;
|
||||||
|
case Variant::DOUBLE:
|
||||||
|
lv2_atom_forge_double(forge, value.get_double());
|
||||||
|
break;
|
||||||
|
case Variant::FLOAT:
|
||||||
|
lv2_atom_forge_float(forge, value.get_float());
|
||||||
|
break;
|
||||||
|
case Variant::INT:
|
||||||
|
lv2_atom_forge_int(forge, value.get_int());
|
||||||
|
break;
|
||||||
|
case Variant::LONG:
|
||||||
|
lv2_atom_forge_long(forge, value.get_long());
|
||||||
|
break;
|
||||||
|
case Variant::PATH:
|
||||||
|
lv2_atom_forge_path(
|
||||||
|
forge, value.get_path().c_str(), value.get_path().size());
|
||||||
|
break;
|
||||||
|
case Variant::STRING:
|
||||||
|
lv2_atom_forge_string(
|
||||||
|
forge, value.get_string().c_str(), value.get_string().size());
|
||||||
|
break;
|
||||||
|
case Variant::URI:
|
||||||
|
lv2_atom_forge_uri(
|
||||||
|
forge, value.get_uri().c_str(), value.get_uri().size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a variant type from a URI, return false iff no match found. */
|
||||||
|
static bool
|
||||||
|
uri_to_variant_type(const std::string& uri, Variant::Type& type)
|
||||||
|
{
|
||||||
|
if (uri == LV2_ATOM__Bool) {
|
||||||
|
type = Variant::BOOL;
|
||||||
|
} else if (uri == LV2_ATOM__Double) {
|
||||||
|
type = Variant::DOUBLE;
|
||||||
|
} else if (uri == LV2_ATOM__Float) {
|
||||||
|
type = Variant::FLOAT;
|
||||||
|
} else if (uri == LV2_ATOM__Int) {
|
||||||
|
type = Variant::INT;
|
||||||
|
} else if (uri == LV2_ATOM__Long) {
|
||||||
|
type = Variant::LONG;
|
||||||
|
} else if (uri == LV2_ATOM__Path) {
|
||||||
|
type = Variant::PATH;
|
||||||
|
} else if (uri == LV2_ATOM__String) {
|
||||||
|
type = Variant::STRING;
|
||||||
|
} else if (uri == LV2_ATOM__URI) {
|
||||||
|
type = Variant::URI;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LV2Plugin::set_property(uint32_t key, const Variant& value)
|
||||||
|
{
|
||||||
|
if (_patch_port_in_index == (uint32_t)-1) {
|
||||||
|
error << "LV2: set_property called with unset patch_port_in_index" << endmsg;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up forge to write to temporary buffer on the stack
|
||||||
|
LV2_Atom_Forge* forge = &_impl->ui_forge;
|
||||||
|
LV2_Atom_Forge_Frame frame;
|
||||||
|
uint8_t buf[PATH_MAX]; // Ought to be enough for anyone...
|
||||||
|
|
||||||
|
lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
|
||||||
|
|
||||||
|
// Serialize patch:Set message to set property
|
||||||
|
#ifdef HAVE_LV2_1_10_0
|
||||||
|
lv2_atom_forge_object(forge, &frame, 1, LV2Plugin::urids.patch_Set);
|
||||||
|
lv2_atom_forge_key(forge, LV2Plugin::urids.patch_property);
|
||||||
|
lv2_atom_forge_urid(forge, key);
|
||||||
|
lv2_atom_forge_key(forge, LV2Plugin::urids.patch_value);
|
||||||
|
#else
|
||||||
|
lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.patch_Set);
|
||||||
|
lv2_atom_forge_property_head(forge, LV2Plugin::urids.patch_property, 0);
|
||||||
|
lv2_atom_forge_urid(forge, key);
|
||||||
|
lv2_atom_forge_property_head(forge, LV2Plugin::urids.patch_value, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
forge_variant(forge, value);
|
||||||
|
|
||||||
|
// Write message to UI=>Plugin ring
|
||||||
|
const LV2_Atom* const atom = (const LV2_Atom*)buf;
|
||||||
|
write_from_ui(_patch_port_in_index,
|
||||||
|
LV2Plugin::urids.atom_eventTransfer,
|
||||||
|
lv2_atom_total_size(atom),
|
||||||
|
(const uint8_t*)atom);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LV2Plugin::get_supported_properties(std::vector<ParameterDescriptor>& descs)
|
||||||
|
{
|
||||||
|
LilvWorld* lworld = _world.world;
|
||||||
|
const LilvNode* subject = lilv_plugin_get_uri(_impl->plugin);
|
||||||
|
LilvNodes* properties = lilv_world_find_nodes(
|
||||||
|
lworld, subject, _world.patch_writable, NULL);
|
||||||
|
LILV_FOREACH(nodes, p, properties) {
|
||||||
|
// Get label and range
|
||||||
|
const LilvNode* prop = lilv_nodes_get(properties, p);
|
||||||
|
LilvNode* label = lilv_world_get(lworld, prop, _world.rdfs_label, NULL);
|
||||||
|
LilvNode* range = lilv_world_get(lworld, prop, _world.rdfs_range, NULL);
|
||||||
|
|
||||||
|
// Convert range to variant type (TODO: support for multiple range types)
|
||||||
|
Variant::Type datatype;
|
||||||
|
if (!uri_to_variant_type(lilv_node_as_uri(range), datatype)) {
|
||||||
|
error << string_compose(_("LV2: unknown variant datatype \"%1\""),
|
||||||
|
lilv_node_as_uri(range));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add description to result
|
||||||
|
ParameterDescriptor desc;
|
||||||
|
desc.key = _uri_map.uri_to_id(lilv_node_as_uri(prop));
|
||||||
|
desc.label = lilv_node_as_string(label);
|
||||||
|
desc.datatype = datatype;
|
||||||
|
desc.toggled = datatype == Variant::BOOL;
|
||||||
|
desc.integer_step = datatype == Variant::INT || datatype == Variant::LONG;
|
||||||
|
descs.push_back(desc);
|
||||||
|
|
||||||
|
lilv_node_free(label);
|
||||||
|
lilv_node_free(range);
|
||||||
|
}
|
||||||
|
lilv_nodes_free(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LV2Plugin::announce_property_values()
|
||||||
|
{
|
||||||
|
if (_patch_port_in_index == (uint32_t)-1) {
|
||||||
|
error << "LV2: set_property called with unset patch_port_in_index" << endmsg;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up forge to write to temporary buffer on the stack
|
||||||
|
LV2_Atom_Forge* forge = &_impl->ui_forge;
|
||||||
|
LV2_Atom_Forge_Frame frame;
|
||||||
|
uint8_t buf[PATH_MAX]; // Ought to be enough for anyone...
|
||||||
|
|
||||||
|
lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
|
||||||
|
|
||||||
|
// Serialize patch:Get message with no subject (implicitly plugin instance)
|
||||||
|
lv2_atom_forge_object(forge, &frame, 1, LV2Plugin::urids.patch_Get);
|
||||||
|
|
||||||
|
// Write message to UI=>Plugin ring
|
||||||
|
const LV2_Atom* const atom = (const LV2_Atom*)buf;
|
||||||
|
write_from_ui(_patch_port_in_index,
|
||||||
|
LV2Plugin::urids.atom_eventTransfer,
|
||||||
|
lv2_atom_total_size(atom),
|
||||||
|
(const uint8_t*)atom);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LV2Plugin::enable_ui_emission()
|
LV2Plugin::enable_ui_emission()
|
||||||
{
|
{
|
||||||
|
|
@ -1587,6 +1728,24 @@ write_position(LV2_Atom_Forge* forge,
|
||||||
uint8_t pos_buf[256];
|
uint8_t pos_buf[256];
|
||||||
lv2_atom_forge_set_buffer(forge, pos_buf, sizeof(pos_buf));
|
lv2_atom_forge_set_buffer(forge, pos_buf, sizeof(pos_buf));
|
||||||
LV2_Atom_Forge_Frame frame;
|
LV2_Atom_Forge_Frame frame;
|
||||||
|
#ifdef HAVE_LV2_1_10_0
|
||||||
|
lv2_atom_forge_object(forge, &frame, 1, LV2Plugin::urids.time_Position);
|
||||||
|
lv2_atom_forge_key(forge, LV2Plugin::urids.time_frame);
|
||||||
|
lv2_atom_forge_long(forge, position);
|
||||||
|
lv2_atom_forge_key(forge, LV2Plugin::urids.time_speed);
|
||||||
|
lv2_atom_forge_float(forge, speed);
|
||||||
|
lv2_atom_forge_key(forge, LV2Plugin::urids.time_barBeat);
|
||||||
|
lv2_atom_forge_float(forge, bbt.beats - 1 +
|
||||||
|
(bbt.ticks / Timecode::BBT_Time::ticks_per_beat));
|
||||||
|
lv2_atom_forge_key(forge, LV2Plugin::urids.time_bar);
|
||||||
|
lv2_atom_forge_long(forge, bbt.bars - 1);
|
||||||
|
lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatUnit);
|
||||||
|
lv2_atom_forge_int(forge, t.meter().note_divisor());
|
||||||
|
lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatsPerBar);
|
||||||
|
lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
|
||||||
|
lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatsPerMinute);
|
||||||
|
lv2_atom_forge_float(forge, t.tempo().beats_per_minute());
|
||||||
|
#else
|
||||||
lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.time_Position);
|
lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.time_Position);
|
||||||
lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_frame, 0);
|
lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_frame, 0);
|
||||||
lv2_atom_forge_long(forge, position);
|
lv2_atom_forge_long(forge, position);
|
||||||
|
|
@ -1603,6 +1762,7 @@ write_position(LV2_Atom_Forge* forge,
|
||||||
lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
|
lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
|
||||||
lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatsPerMinute, 0);
|
lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatsPerMinute, 0);
|
||||||
lv2_atom_forge_float(forge, t.tempo().beats_per_minute());
|
lv2_atom_forge_float(forge, t.tempo().beats_per_minute());
|
||||||
|
#endif
|
||||||
|
|
||||||
LV2_Evbuf_Iterator end = lv2_evbuf_end(buf);
|
LV2_Evbuf_Iterator end = lv2_evbuf_end(buf);
|
||||||
const LV2_Atom* const atom = (const LV2_Atom*)pos_buf;
|
const LV2_Atom* const atom = (const LV2_Atom*)pos_buf;
|
||||||
|
|
@ -1610,48 +1770,6 @@ write_position(LV2_Atom_Forge* forge,
|
||||||
(const uint8_t*)(atom + 1));
|
(const uint8_t*)(atom + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
write_patch_change(
|
|
||||||
LV2_Atom_Forge* forge,
|
|
||||||
LV2_Evbuf* buf,
|
|
||||||
const char* uri,
|
|
||||||
const char* filename
|
|
||||||
)
|
|
||||||
{
|
|
||||||
LV2_Atom_Forge_Frame frame;
|
|
||||||
uint8_t patch_buf[PATH_MAX];
|
|
||||||
lv2_atom_forge_set_buffer(forge, patch_buf, sizeof(patch_buf));
|
|
||||||
|
|
||||||
#if 0 // new LV2
|
|
||||||
lv2_atom_forge_object(forge, &frame, 0, LV2Plugin::urids.patch_Set);
|
|
||||||
lv2_atom_forge_key(forge, LV2Plugin::urids.patch_property);
|
|
||||||
lv2_atom_forge_urid(forge, uri_map.uri_to_id(uri));
|
|
||||||
lv2_atom_forge_key(forge, LV2Plugin::urids.patch_value);
|
|
||||||
lv2_atom_forge_path(forge, filename, strlen(filename));
|
|
||||||
#else
|
|
||||||
lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.patch_Set);
|
|
||||||
lv2_atom_forge_property_head(forge, LV2Plugin::urids.patch_property, 0);
|
|
||||||
lv2_atom_forge_urid(forge, LV2Plugin::_uri_map.uri_to_id(uri));
|
|
||||||
lv2_atom_forge_property_head(forge, LV2Plugin::urids.patch_value, 0);
|
|
||||||
lv2_atom_forge_path(forge, filename, strlen(filename));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LV2_Evbuf_Iterator end = lv2_evbuf_end(buf);
|
|
||||||
const LV2_Atom* const atom = (const LV2_Atom*)patch_buf;
|
|
||||||
return lv2_evbuf_write(&end, 0, 0, atom->type, atom->size,
|
|
||||||
(const uint8_t*)(atom + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
LV2Plugin::patch_set (const uint32_t p, const char * val) {
|
|
||||||
if (p >= _patch_count) return false;
|
|
||||||
_patch_set_lock.lock();
|
|
||||||
strncpy(_patch_value_set[p], val, PATH_MAX);
|
|
||||||
_patch_value_set[p][PATH_MAX - 1] = 0;
|
|
||||||
_patch_set_lock.unlock();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
LV2Plugin::connect_and_run(BufferSet& bufs,
|
LV2Plugin::connect_and_run(BufferSet& bufs,
|
||||||
ChanMapping in_map, ChanMapping out_map,
|
ChanMapping in_map, ChanMapping out_map,
|
||||||
|
|
@ -1784,23 +1902,6 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
|
||||||
(flags & PORT_INPUT), 0, (flags & PORT_EVENT));
|
(flags & PORT_INPUT), 0, (flags & PORT_EVENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* queue patch messages */
|
|
||||||
if (flags & PORT_PATCHMSG) {
|
|
||||||
if (_patch_set_lock.trylock()) {
|
|
||||||
for (uint32_t pidx = 0; pidx < _patch_count; ++ pidx) {
|
|
||||||
if (strlen(_patch_value_set[pidx]) > 0
|
|
||||||
&& 0 != strcmp(_patch_value_cur[pidx], _patch_value_set[pidx])
|
|
||||||
)
|
|
||||||
{
|
|
||||||
write_patch_change(&_impl->forge, _ev_buffers[port_index],
|
|
||||||
_patch_value_uri[pidx], _patch_value_set[pidx]);
|
|
||||||
strncpy(_patch_value_cur[pidx], _patch_value_set[pidx], PATH_MAX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_patch_set_lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]);
|
buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]);
|
||||||
} else {
|
} else {
|
||||||
continue; // Control port, leave buffer alone
|
continue; // Control port, leave buffer alone
|
||||||
|
|
@ -1875,7 +1976,8 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
|
||||||
|
|
||||||
|
|
||||||
// Write messages to UI
|
// Write messages to UI
|
||||||
if ((_to_ui || _patch_count > 0) && (flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
|
if ((_to_ui || _patch_port_out_index != (uint32_t)-1) &&
|
||||||
|
(flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
|
||||||
LV2_Evbuf* buf = _ev_buffers[port_index];
|
LV2_Evbuf* buf = _ev_buffers[port_index];
|
||||||
for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(buf);
|
for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(buf);
|
||||||
lv2_evbuf_is_valid(i);
|
lv2_evbuf_is_valid(i);
|
||||||
|
|
@ -1884,42 +1986,32 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
|
||||||
uint8_t* data;
|
uint8_t* data;
|
||||||
lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data);
|
lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data);
|
||||||
|
|
||||||
// intercept patch change messages
|
// Intercept patch change messages to emit PropertyChanged signal
|
||||||
/* TODO this should eventually be done in the UI-thread
|
if ((flags & PORT_PATCHMSG)) {
|
||||||
* using a ringbuffer and no locks.
|
|
||||||
* The current UI ringbuffer is unsuitable. It only exists
|
|
||||||
* if a UI enabled and misses initial patch-set or changes.
|
|
||||||
*/
|
|
||||||
if (_patch_count > 0 && (flags & PORT_OUTPUT) && (flags & PORT_PATCHMSG)) {
|
|
||||||
// TODO reduce if() nesting below:
|
|
||||||
LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom));
|
LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom));
|
||||||
if (atom->type == LV2Plugin::urids.atom_Blank) {
|
if (atom->type == LV2Plugin::urids.atom_Blank ||
|
||||||
|
atom->type == LV2Plugin::urids.atom_Object) {
|
||||||
LV2_Atom_Object* obj = (LV2_Atom_Object*)atom;
|
LV2_Atom_Object* obj = (LV2_Atom_Object*)atom;
|
||||||
if (obj->body.otype == LV2Plugin::urids.patch_Set) {
|
if (obj->body.otype == LV2Plugin::urids.patch_Set) {
|
||||||
const LV2_Atom* property = NULL;
|
const LV2_Atom* property = NULL;
|
||||||
lv2_atom_object_get (obj, LV2Plugin::urids.patch_property, &property, 0);
|
const LV2_Atom* value = NULL;
|
||||||
if (property->type == LV2Plugin::urids.atom_URID) {
|
lv2_atom_object_get(obj,
|
||||||
for (uint32_t pidx = 0; pidx < _patch_count; ++ pidx) {
|
LV2Plugin::urids.patch_property, &property,
|
||||||
if (((const LV2_Atom_URID*)property)->body != _uri_map.uri_to_id(_patch_value_uri[pidx])) {
|
LV2Plugin::urids.patch_value, &value,
|
||||||
continue;
|
0);
|
||||||
}
|
|
||||||
/* Get value. */
|
if (!property || !value ||
|
||||||
const LV2_Atom* file_path = NULL;
|
property->type != LV2Plugin::urids.atom_URID ||
|
||||||
lv2_atom_object_get(obj, LV2Plugin::urids.patch_value, &file_path, 0);
|
value->type != LV2Plugin::urids.atom_Path) {
|
||||||
if (!file_path || file_path->type != LV2Plugin::urids.atom_Path) {
|
std::cerr << "warning: patch:Set for unknown property" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
// LV2_ATOM_BODY() casts away qualifiers, so do it explicitly:
|
|
||||||
const char* uri = (const char*)((uint8_t const*)(file_path) + sizeof(LV2_Atom));
|
|
||||||
_patch_set_lock.lock();
|
|
||||||
strncpy(_patch_value_cur[pidx], uri, PATH_MAX);
|
|
||||||
strncpy(_patch_value_set[pidx], uri, PATH_MAX);
|
|
||||||
_patch_value_cur[pidx][PATH_MAX - 1] = 0;
|
|
||||||
_patch_value_set[pidx][PATH_MAX - 1] = 0;
|
|
||||||
_patch_set_lock.unlock();
|
|
||||||
PatchChanged(pidx); // emit signal
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint32_t prop_id = ((const LV2_Atom_URID*)property)->body;
|
||||||
|
const char* path = (const char*)LV2_ATOM_BODY_CONST(value);
|
||||||
|
|
||||||
|
// Emit PropertyChanged signal for UI
|
||||||
|
PropertyChanged(prop_id, Variant(Variant::PATH, path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2133,6 +2225,8 @@ LV2World::LV2World()
|
||||||
lv2_freewheeling = lilv_new_uri(world, LV2_CORE__freeWheeling);
|
lv2_freewheeling = lilv_new_uri(world, LV2_CORE__freeWheeling);
|
||||||
midi_MidiEvent = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
|
midi_MidiEvent = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
|
||||||
rdfs_comment = lilv_new_uri(world, LILV_NS_RDFS "comment");
|
rdfs_comment = lilv_new_uri(world, LILV_NS_RDFS "comment");
|
||||||
|
rdfs_label = lilv_new_uri(world, LILV_NS_RDFS "label");
|
||||||
|
rdfs_range = lilv_new_uri(world, LILV_NS_RDFS "range");
|
||||||
rsz_minimumSize = lilv_new_uri(world, LV2_RESIZE_PORT__minimumSize);
|
rsz_minimumSize = lilv_new_uri(world, LV2_RESIZE_PORT__minimumSize);
|
||||||
time_Position = lilv_new_uri(world, LV2_TIME__Position);
|
time_Position = lilv_new_uri(world, LV2_TIME__Position);
|
||||||
ui_GtkUI = lilv_new_uri(world, LV2_UI__GtkUI);
|
ui_GtkUI = lilv_new_uri(world, LV2_UI__GtkUI);
|
||||||
|
|
@ -2156,6 +2250,8 @@ LV2World::~LV2World()
|
||||||
lilv_node_free(time_Position);
|
lilv_node_free(time_Position);
|
||||||
lilv_node_free(rsz_minimumSize);
|
lilv_node_free(rsz_minimumSize);
|
||||||
lilv_node_free(rdfs_comment);
|
lilv_node_free(rdfs_comment);
|
||||||
|
lilv_node_free(rdfs_label);
|
||||||
|
lilv_node_free(rdfs_range);
|
||||||
lilv_node_free(midi_MidiEvent);
|
lilv_node_free(midi_MidiEvent);
|
||||||
lilv_node_free(lv2_enumeration);
|
lilv_node_free(lv2_enumeration);
|
||||||
lilv_node_free(lv2_freewheeling);
|
lilv_node_free(lv2_freewheeling);
|
||||||
|
|
|
||||||
|
|
@ -267,6 +267,8 @@ def configure(conf):
|
||||||
atleast_version='1.0.0', mandatory=True)
|
atleast_version='1.0.0', mandatory=True)
|
||||||
autowaf.check_pkg(conf, 'lv2', uselib_store='LV2_1_2_0',
|
autowaf.check_pkg(conf, 'lv2', uselib_store='LV2_1_2_0',
|
||||||
atleast_version='1.2.0', mandatory=False)
|
atleast_version='1.2.0', mandatory=False)
|
||||||
|
autowaf.check_pkg(conf, 'lv2', uselib_store='LV2_1_10_0',
|
||||||
|
atleast_version='1.10.0', mandatory=False)
|
||||||
autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD',
|
autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD',
|
||||||
atleast_version='0.14.0', mandatory=True)
|
atleast_version='0.14.0', mandatory=True)
|
||||||
autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD',
|
autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue