13
0

LV2: fix port/nth-parameter confusion

various LV2 callbacks from plugin DSP/GUI use raw port_index,
not nth_parameter. This lead to incorrectly queued updates
(_values_last_sent_to_ui) and since 7dac8994f6 to
potential crashes (invalid _controllables[idx]).
This commit is contained in:
Robin Gareus 2024-08-25 15:27:04 +02:00
parent eac3283b49
commit 190cd657b9
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
2 changed files with 28 additions and 40 deletions

View File

@ -70,7 +70,6 @@ LV2PluginUI::write_from_ui(void* controller,
std::shared_ptr<AutomationControl> ac = me->_controllables[port_index]; std::shared_ptr<AutomationControl> ac = me->_controllables[port_index];
if (ac) { if (ac) {
me->_updates.insert (port_index); me->_updates.insert (port_index);
ac->set_value(*(const float*)buffer, Controllable::NoGroup); ac->set_value(*(const float*)buffer, Controllable::NoGroup);
@ -239,10 +238,8 @@ LV2PluginUI::queue_port_update()
{ {
const uint32_t num_ports = _lv2->num_ports(); const uint32_t num_ports = _lv2->num_ports();
for (uint32_t i = 0; i < num_ports; ++i) { for (uint32_t i = 0; i < num_ports; ++i) {
bool ok; if (_lv2->parameter_is_control (i) && _lv2->parameter_is_input(i)) {
uint32_t port = _lv2->nth_parameter(i, ok); _updates.insert (i);
if (ok && _lv2->parameter_is_input (i)) {
_updates.insert (port);
} }
} }
} }
@ -274,9 +271,7 @@ LV2PluginUI::output_update()
/* output ports (values set by DSP) need propagating to GUI */ /* output ports (values set by DSP) need propagating to GUI */
uint32_t nports = _output_ports.size(); for (auto const& index: _output_ports) {
for (uint32_t i = 0; i < nports; ++i) {
uint32_t index = _output_ports[i];
float val = _pib->control_output (index)->get_parameter (); float val = _pib->control_output (index)->get_parameter ();
if (val != _values_last_sent_to_ui[index]) { if (val != _values_last_sent_to_ui[index]) {
@ -288,14 +283,14 @@ LV2PluginUI::output_update()
} }
/* Input ports marked for update because the control value changed /* Input ports marked for update because the control value changed
since the last redisplay. * since the last redisplay.
*/ */
for (Updates::iterator i = _updates.begin(); i != _updates.end(); ++i) { for (auto const& i : _updates) {
float val = _controllables[*i]->get_value (); float val = _controllables[i]->get_value ();
/* push current value to the GUI */ /* push current value to the GUI */
suil_instance_port_event ((SuilInstance*)_inst, (*i), 4, 0, &val); suil_instance_port_event ((SuilInstance*)_inst, i, 4, 0, &val);
_values_last_sent_to_ui[(*i)] = val; _values_last_sent_to_ui[i] = val;
} }
_updates.clear (); _updates.clear ();
@ -436,15 +431,6 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
#define GET_WIDGET(inst) suil_instance_get_widget((SuilInstance*)inst); #define GET_WIDGET(inst) suil_instance_get_widget((SuilInstance*)inst);
const uint32_t num_ports = _lv2->num_ports();
for (uint32_t i = 0; i < num_ports; ++i) {
if (_lv2->parameter_is_output(i)
&& _lv2->parameter_is_control(i)
&& is_update_wanted(i)) {
_output_ports.push_back(i);
}
}
_external_ui_ptr = NULL; _external_ui_ptr = NULL;
if (!is_external_ui) { if (!is_external_ui) {
GtkWidget* c_widget = (GtkWidget*)GET_WIDGET(_inst); GtkWidget* c_widget = (GtkWidget*)GET_WIDGET(_inst);
@ -465,28 +451,30 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
_external_ui_ptr = (struct lv2_external_ui*)GET_WIDGET(_inst); _external_ui_ptr = (struct lv2_external_ui*)GET_WIDGET(_inst);
} }
const uint32_t num_ports = _lv2->num_ports();
_values_last_sent_to_ui = new float[num_ports]; _values_last_sent_to_ui = new float[num_ports];
_controllables.resize(num_ports); _controllables.resize(num_ports);
for (uint32_t i = 0; i < num_ports; ++i) { for (uint32_t i = 0; i < num_ports; ++i) {
bool ok; if (!_lv2->parameter_is_control (i)) {
uint32_t port = _lv2->nth_parameter(i, ok); continue;
if (ok) { }
/* Cache initial value of the parameter, regardless of
whether it is input or output
*/
_values_last_sent_to_ui[port] = _lv2->get_parameter(port); /* Cache initial value of the parameter, regardless of whether it is input or output */
_controllables[port] = std::dynamic_pointer_cast<ARDOUR::AutomationControl> (
_pib->control(Evoral::Parameter(PluginAutomation, 0, port)));
if (_lv2->parameter_is_control(port) && _lv2->parameter_is_input(port)) { _values_last_sent_to_ui[i] = _lv2->get_parameter(i);
if (_controllables[port]) { _controllables[i] = std::dynamic_pointer_cast<ARDOUR::AutomationControl> (_pib->control(Evoral::Parameter(PluginAutomation, 0, i)));
_controllables[port]->Changed.connect (control_connections, invalidator (*this), boost::bind (&LV2PluginUI::control_changed, this, port), gui_context());
/* queue for first update ("push") to GUI */ if (_lv2->parameter_is_input(i)) {
_updates.insert (port); assert (_controllables[i]);
} _controllables[i]->Changed.connect (control_connections, invalidator (*this), boost::bind (&LV2PluginUI::control_changed, this, i), gui_context());
} /* queue for first update ("push") to GUI */
_updates.insert (i);
}
if (_lv2->parameter_is_output(i) && is_update_wanted(i)) {
_output_ports.push_back (i);
} }
} }

View File

@ -80,7 +80,7 @@ private:
std::shared_ptr<ARDOUR::PlugInsertBase> _pib; std::shared_ptr<ARDOUR::PlugInsertBase> _pib;
std::shared_ptr<ARDOUR::LV2Plugin> _lv2; std::shared_ptr<ARDOUR::LV2Plugin> _lv2;
std::vector<int> _output_ports; std::vector<uint32_t> _output_ports;
sigc::connection _screen_update_connection; sigc::connection _screen_update_connection;
sigc::connection _message_update_connection; sigc::connection _message_update_connection;
Gtk::Widget* _gui_widget; Gtk::Widget* _gui_widget;