How do you set a knob's value so that it does not "snap back"?
a2aaron opened this issue · 1 comments
I have a VST parameter, in this case, volume, that I have interacting with a knob::State
. I would like for the knob to be able to set the parameter, and for when the parameter is set by a VST host, that the knob change to reflect it. (ex: If I have an automation curve in Ableton that changes my volume parameter, I would like for the volume knob to rotate with it, and if I rotate the knob in GUI, I would like to have the parameter change to reflect that). There are a few problems I have encountered:
I have manage to almost achieve this goal. Specifically, I can
- Turn the knob in the GUI and have the parameter change to reflect it.
- Change the parameter via the VST host and have the knob rotate. I do this by altering
knob.normal_param.value
to the value I want.
However, when I click on the knob, the knob ends up "snapping" back to the previous spot it was since interacting with it in the GUI. (For example, if you lower the volume via the host, then click the knob in the GUI, the knob will "snap back" to its prior value). I think this is due to the fact that knob::State
has an internal field continuous_normal
, which looks like it tracks what the "real" position of the knob was. Hence, when the knob gets interacted with, this overwrites the previous value of knob.normal_param.value
, causing the snapping behavior. It seems like this makes it impossible to actually keep a knob set via the normal_param.value
field, since it will just snap upon interaction.
Thus, my question: How do I set the knob so that it does not do this snapping behavior?
Also, I've included some of the relevant code below:
// Messages for iced
pub enum Message {
MasterVolume(Normal),
ForceRedraw,
}
// My GUI struct
pub struct UIFrontEnd {
// My knob in the GUI
master_vol_knob: knob::State,
// The actual parameters that the VST host sets. This is shared with the
// host thread. All that is relevant is that it contains a field called
// volume that is an AtomicFloat.
params: std::sync::Arc<RawParameters>,
}
impl Application for UIFrontEnd {
type Message = Message;
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
match message {
Message::MasterVolume(normal) => {
// This is meant to update the parameter when the GUI knob is
// changed. However, `normal` ends up being the value of whatever
// the knob was since the last time it was interacted with in the
// GUI, which causes the volume to "snap back" to the wrong value
self.params.volume.set(normal.as_f32());
}
// I end up sending this message via a Subscription every few
// miliseconds. This is used to make the GUI knob update when
// the VST host sets the parameter.
Message::ForceRedraw => {
self.master_vol_knob.normal_param.value = Normal::new(self.params.volume.get());
}
}
Command::none()
}
fn view(&mut self) -> iced::Element<'_, Self::Message> {
/// The knob widget displayed on the GUI. It sends Message::MasterVolume messages
/// when interacted with.
let master_vol_widget = Knob::new(&mut self.master_vol_knob, Message::MasterVolume);
let content: Element<_> = Column::new().push(master_vol_widget).into();
Container::new(content).into()
}
// This subscription is used to send ForceRedraw messages, which is just used
// so that iced redraws the GUI.
fn subscription(
&self,
_window_subs: &mut WindowSubs<Self::Message>,
) -> Subscription<Self::Message> {
iced_futures::time::every(Duration::from_millis(33)).map(|_| Message::ForceRedraw)
}
}
TL;DR: How do you set a knob to a different value so that when the user interacts with it, the knob does not go back to its previous value?
Maybe fix found: I implemented a function that sets both continuous_normal
and normal_param.value
. This seems to fix the snapping behavior.
I think I can just add a similar function to the other widgets, since all of those seem to have the same sort of bug.