What is KVR Audio? | Submit News | Advertise | Developer Account

Options (Affects News & Product results only):

OS:
Format:
Include:
Quick Search KVR

"Quick Search" KVR Audio's Product Database, News Items, Developer Listings, Forum Topics and videos here. For advanced Product Database searching please use the full product search. For the forum you can use the phpBB forum search.

To utilize the power of Google you can use the integrated Google Site Search.

Products 0

Developers 0

News 0

Forum 0

Videos 0

Search  

VST pitfalls with parameter synchronization

DSP, Plug-in and Host development discussion.

Moderator: Moderators (Main)

KVRist
 
79 posts since 18 Mar, 2012

Postby bitwise; Mon Sep 02, 2013 3:50 am

Jakob / Cableguys wrote:
We'd still have the problem of parameter values toggling back and forth. See my example in the very first post: the parameter might be moved by the user from 0.1 to 0.2 to 0.3, and will temporarily jump back to 0.2 when the host calls <setParameter> and echoes the parameter change, and then a little later again to 0.3.


This wouldn't happen if you let setParameter... set the parameter and the GUI "only" notify the host of the changes.
User avatar
KVRian
 
743 posts since 11 Aug, 2005, from Hamburg, Germany
   

Postby Jakob / Cableguys; Mon Sep 02, 2013 4:03 am

bitwise wrote:
Jakob / Cableguys wrote:
We'd still have the problem of parameter values toggling back and forth. See my example in the very first post: the parameter might be moved by the user from 0.1 to 0.2 to 0.3, and will temporarily jump back to 0.2 when the host calls <setParameter> and echoes the parameter change, and then a little later again to 0.3.

This wouldn't happen if you let setParameter... set the parameter and the GUI "only" notify the host of the changes.


Indeed, this would be fine for all hosts which send the parameter change back to the plugin. But hosts don't have to, so we can't rely on this.. if a host doesn't send the parameter change to the plugin we'd never get aware of the parameter change.
KVRist
 
79 posts since 18 Mar, 2012

Postby bitwise; Mon Sep 02, 2013 4:20 am

In this case, you could use a timer that sets the parameter when the host doesn't do it. You would only need this timer for a short time. As soon as the vst "learns" that the host doesn't set the parameter, the gui starts setting the value immediately.

Perhaps the hosts you refer to, expect the parameter to be flagged as automatable.
User avatar
KVRian
 
743 posts since 11 Aug, 2005, from Hamburg, Germany
   

Postby Jakob / Cableguys; Mon Sep 02, 2013 4:38 am

bitwise wrote:In this case, you could use a timer that sets the parameter when the host doesn't do it. You would only need this timer for a short time. As soon as the vst "learns" that the host doesn't set the parameter, the gui starts setting the value immediately.

Perhaps the hosts you refer to, expect the parameter to be flagged as automatable.

Good approach, definitely worth a try!
KVRist
 
497 posts since 23 Nov, 2010

Postby sonigen; Mon Sep 02, 2013 4:46 am

Jakob / Cableguys wrote:We'd still have the problem of parameter values toggling back and forth. See my example in the very first post: the parameter might be moved by the user from 0.1 to 0.2 to 0.3, and will temporarily jump back to 0.2 when the host calls <setParameter> and echoes the parameter change, and then a little later again to 0.3.


When the GUI sets a parameter record a timestamp, current system time for example, and ignore any incoming changes for that parameter from the host unless they occur maybe 1 second after that. Essentially block host automation while the use has his hands on the control.

You could override SetParameter & SetParameterAutomated and have all the logic in there. You just need an extra array with the timestamp for each parameter.

In fact you probably dont need an array, just a single timestamp and parameter index, and just ignore the last changed parameter. The user cant move the mouse that quickly that you'd be likely to have more that one being wiggled in relevant amount of time.

Should work i think.
Chris Jones
www.sonigen.com
KVRAF
 
2926 posts since 28 May, 2001, from New York, NY
 

Postby Big Tick; Mon Sep 02, 2013 2:00 pm

First, about the host echoing the parameter changes - this is really rude from the host. I would simply ignore any parameter changes coming from the host while you are automating the same parameter on the UI. Normally if you call beginEdit() and endEdit(), the host should not send any parameter changes for this parameter.

Second, about setParameter() and threads. setParameter() can be called from:
- The UI thread when tweaking values and calling setParameterAutomated.
- The Audio thread when the host plays back automation.

if setParameter() is not atomic, then processReplacing() can be dealing with incomplete (or, in the example of filter coefficients, invalid) data.

You can make setParameter() atomic using a mutex, however this leads to potential audio dropouts, if the audio thread is waiting on the mutex while the UI thread is sending automation data.

The other option is to use a lock-free queue system. This ensures the audio thread is never waiting for the UI, at the expense of potential latency in processing UI events.

You will need 2 lock-free ring buffers: one for processing changes from the UI on the audio thread, and one for notifying the UI when the host sends automation on the audio thread. You will need to override setParameterAutomated(), so that it doesn't call setParameter(), but instead pushes the parameter change on the queue.
KVRAF
 
4081 posts since 11 Feb, 2006, from Helsinki, Finland
 

Postby mystran; Mon Sep 02, 2013 8:51 pm

You don't need either mutexes or queues if you make setParameter .. set a variable (single write to an aligned address is atomic anyway).
Image <- plugins | forum
KVRAF
 
6285 posts since 10 Oct, 2005, from Toronto, Canada
 

Postby AdmiralQuality; Mon Sep 02, 2013 11:40 pm

mystran wrote:You don't need either mutexes or queues if you make setParameter .. set a variable (single write to an aligned address is atomic anyway).


Exactly.
User avatar
KVRian
 
743 posts since 11 Aug, 2005, from Hamburg, Germany
   

Postby Jakob / Cableguys; Tue Sep 03, 2013 2:40 am

AdmiralQuality wrote:
mystran wrote:You don't need either mutexes or queues if you make setParameter .. set a variable (single write to an aligned address is atomic anyway).


Exactly.

It's not about making the write to a parameter (if that's what you mean?) atomic, but of making sure that the parameter is not changed during process. And about avoiding to get "old" values sent back from the host which are already outdated.
User avatar
KVRian
 
743 posts since 11 Aug, 2005, from Hamburg, Germany
   

Postby Jakob / Cableguys; Tue Sep 03, 2013 2:47 am

Big Tick wrote:First, about the host echoing the parameter changes - this is really rude from the host. I would simply ignore any parameter changes coming from the host while you are automating the same parameter on the UI. Normally if you call beginEdit() and endEdit(), the host should not send any parameter changes for this parameter.

Good idea, I will check if <beginEdit> and <endEdit> will keep the host from sending updates!

Big Tick wrote:..

The other option is to use a lock-free queue system. This ensures the audio thread is never waiting for the UI, at the expense of potential latency in processing UI events.

You will need 2 lock-free ring buffers: one for processing changes from the UI on the audio thread, and one for notifying the UI when the host sends automation on the audio thread. You will need to override setParameterAutomated(), so that it doesn't call setParameter(), but instead pushes the parameter change on the queue.

Yes, this (or something similar) is what I thought to do in the first place, but it seemed so overkill.
KVRAF
 
6285 posts since 10 Oct, 2005, from Toronto, Canada
 

Postby AdmiralQuality; Tue Sep 03, 2013 2:49 am

Jakob / Cableguys wrote:
AdmiralQuality wrote:
mystran wrote:You don't need either mutexes or queues if you make setParameter .. set a variable (single write to an aligned address is atomic anyway).


Exactly.

It's not about making the write to a parameter (if that's what you mean?) atomic, but of making sure that the parameter is not changed during process. And about avoiding to get "old" values sent back from the host which are already outdated.


And that's why you copy the parameter at the start of your process function. Then it can't change on you.

And if you have a big coefficient calculation to do, do it there as well (only when the parameter has changed from what it last was, of course).

You can use a single, atomic variable to select from two different versions of whatever larger structure your process needs. One your process is currently using, the other is free to be constructed by setParameter. When setParameter is finished building whatever needed to be built based on the parameter(s) that changed, it sets an atomic flag to tell the process to switch over.
KVRian
 
1211 posts since 14 Dec, 2003, from Melbourne, Australia
 

Postby very angry mobster; Tue Sep 03, 2013 2:24 pm

AdmiralQuality wrote:
Jakob / Cableguys wrote:
AdmiralQuality wrote:
mystran wrote:You don't need either mutexes or queues if you make setParameter .. set a variable (single write to an aligned address is atomic anyway).


Exactly.

It's not about making the write to a parameter (if that's what you mean?) atomic, but of making sure that the parameter is not changed during process. And about avoiding to get "old" values sent back from the host which are already outdated.


And that's why you copy the parameter at the start of your process function. Then it can't change on you.


+1. I use the same approach as AdmiralQuality. I don't use locks for parameters that map to a float or integer value. I consider them atomic. In most cases my audio engine code doesn't care if these atomic parameters change mid process. The parameter is copied when it can't change mid process without something blowing up. Often the copying doesn't need to be explicit and happens as part of how my audio engine works.

For example, filter cut-off maps to a single float value (ranging from 15hz to 18khz). The filter cut-off parameter can change anytime and the audio engine is notified of changes as soon as they occur. Internally the audio engine is running at two rates. Audio rate and control rate. The filter cut-off change is ignored until the next 'control rate' update when the filter co-efficients are updated together. In this way the filter cut-off can change any time and the filter co-efficents are guaranteed to be updated together.


Shannon
KVRian
 
1211 posts since 14 Dec, 2003, from Melbourne, Australia
 

Postby very angry mobster; Tue Sep 03, 2013 3:31 pm

Slightly OT: I've recently been working on my plugin parameter code. It's helped to think about plugin parameters as using the Model-Veiw-Controller pattern.

A plugin will have an object that stores the canonical state of all parameters, the 'Model'.

A plugin has a number of 'Plugin Parameter' objects that function as 'Controllers'. Each 'Plugin Parameter' object represents one parameter. (Filter Cutoff, LFO Rate, etc.) As expected with the MVC pattern, 'Plugin Parameter' objects are the only things that read and write data from the the 'Model'.

A plugin has a number of 'Views'. The GUI and the audio engine are two obvious examples. I also have a "MIDI automation" view (parameters that have been linked to MIDI CC's for MIDI parameter automation).

The 'views' only have access to the 'controller' and function independently and are unaware of each other.

My code originally bundled the 'Controller' and 'Model' code together into one class. Ie. the plugin parameter object also stored the state of the parameter. This works with simple plugins but becomes a hindrance with some other plugins.

One of the difficulties of using the MVC pattern is finding ways to keep all the 'views' in sync with the 'model'. My code uses a ad hoc collection of techniques.

The GUI will update all parameters when the GUI is opened. Some parameters are updated on a timer. Some parameter changes will send a 'windows message' to the GUI to indicate a change.

I break with the MVC pattern to update the audio engine. My plugin parameter objects update the audio engine directly. (In a way the audio engine is a second model that mirrors the first.') I've chosen to do this because I want parameter changes to be audible ASAP. It also saves the audio engine from constantly checking for parameter changes.


Shannnon
KVRAF
 
2926 posts since 28 May, 2001, from New York, NY
 

Postby Big Tick; Wed Sep 04, 2013 9:38 am

very angry mobster wrote:+1. I use the same approach as AdmiralQuality. I don't use locks for parameters that map to a float or integer value. I consider them atomic. In most cases my audio engine code doesn't care if these atomic parameters change mid process. The parameter is copied when it can't change mid process without something blowing up. Often the copying doesn't need to be explicit and happens as part of how my audio engine works.


So you're doing something like this ?

Code: Select all
processReplacing(...) {
   float newCutoff = getParameter(CUTOFF);
   if (newCutoff != this->prevCutoff) {
      this->prevCutoff = newCutoff;
      recomputeFilterCoeffs();
   }
   ...
}
KVRAF
 
4081 posts since 11 Feb, 2006, from Helsinki, Finland
 

Postby mystran; Wed Sep 04, 2013 11:54 am

Big Tick wrote:
very angry mobster wrote:+1. I use the same approach as AdmiralQuality. I don't use locks for parameters that map to a float or integer value. I consider them atomic. In most cases my audio engine code doesn't care if these atomic parameters change mid process. The parameter is copied when it can't change mid process without something blowing up. Often the copying doesn't need to be explicit and happens as part of how my audio engine works.


So you're doing something like this ?

Code: Select all
processReplacing(...) {
   float newCutoff = getParameter(CUTOFF);
   if (newCutoff != this->prevCutoff) {
      this->prevCutoff = newCutoff;
      recomputeFilterCoeffs();
   }
   ...
}


That's essentially what I do, though with a bit of indirection.

Basically I have an "rtUpdate()" method that does something like the above for all parameters (storing just a copy of the whole array basically), and records which ones have changed (and also whether any of them changed). So "multi-dependent" stuff (like recompute filter if either cutoff or Q changed) are still possible.

Code: Select all
// eg something like this..
if(params.rtUpdate())
{
   // at least something changed if rtUpdate() returned true
   if(params.changed(P_FCUTOFF) || params.changed(P_FRESO))
   {
      // do a recompute using the internal values
      // that were cloned by rtUpdate above
      filter.recompute(
         params.getInternal(P_FCUTOFF),
         params.getInternal(P_FRESO));
   }
}
// .. rest of process()


You might want to tag the "externally accessible" parameter array (ie what getParameter() and setParameter() use) as volatile just to be sure (though in practice it's unlikely to matter much).
Image <- plugins | forum
PreviousNext

Moderator: Moderators (Main)

Return to DSP and Plug-in Development