Suggestion AudioProcessorParameter as Slider-data source


#1

Now we have the AudioProcessorParameter-class, it would be great if there is a simple way to connect it to a slider. 

The slider could use all the nice things, like text-to-parameter functions/beginParameterGesture/setParameterNotifyingHost and would auto-update itself when the parameter changes. This could massively speed-up plugin-gui-development.   

Also there should be some protection, to prevent accidently overriding the old-style-parameter methods when using the new style AudioProcessorParameter.

 

 

 

 


#2

Hi chkn, thank you for your ideas on AudioProcessorParameter. We are currently working on adding many features to the AudioProcessorParameter class including easier integration with editor components. We will let you know when we have something,


#3

I'm interested in this too (even though I've already rolled my own classes pre AudioProcessorParameter).

Fabian - can I ask how this is being designed? I'm hoping that a normalised (0..1) range is being used to control sliders and automation rather than relying on skew factors in sliders. I think that's the only way that an automated control in a host can have the same feel as a control in the plugin gui. There's a bunch of other things I'm interested in, but it'd probably be more efficient if you guys outlined some of your key design decisions rather than we all give you a laundry list...


#4

We are currently discussing different approaches, but I think having an option for AudioProcessorParameter (or another provided subclass thereof) to take care of any normalised range conversions is a good idea. Sorry that we can't be more specific currently.

Please feel free to tell us all your comments/ideas for AudioProcessorParameter and especially your specific use cases. This sort of feedback will help us when deciding between the different ideas that we are currently having.


#5

First of all there should be a some kind of possibility to get informed when a parameter changed. 

With the current implementation, the derived class needs to store the value, but i think that is something the origin AudioProcesserParameter should do, and then call something like parameterChanged()-Callback (but this will change the current behavior and may broke the whole internal design of plugins, not sure if its a good idea) 

Actually i have  1000 ideas for the whole AudioProcessor-class. Maybe its just better to create something new, along the old AudioProcessorClass which harmonizes all the different hosts behavior (AudioPlugin-Class), to have to a clear interface for the plugin-writer.

For example, some host call reset(), some other uses prepareToPlay()/releaseResources to reset the audio-state

Some hosts call setStateInformation before preparteToPlay (even if sampleRate is not set), other hosts after

Some hosts use more input channels than output-channels for sidechaining even if no  sidechainig is used (sonar)

​Should i store/recreate all parameter-values also in the setState/getState procedure... etc...

Why is the callback-Lock only used in the Wrappers, but not in the AudioProcessorClass itself...

Threading Issues, Sending Data-beween GUI-Processor.... a ValueTree which can be safely accessed from Processor/GUI 

I think there has to be some "Middleware" between the Wrapper and the Plugin-Interface which harmonizes all the different behavior. I already have something, but its very specific to my plugins.

 

 

 


#6

OK, I'll add to the laundry list then - and I'll do it by dumping the interface documentation from my current implementation (I'm happy to share code if it helps understand what I'm trying to do).

Things worth highlighting:

  • I've derived my own AudioProcessor class to minimise boilerplate
  • I need certain changes in the AudioProcessor (e.g. sample rate change) to trigger modification of parameter setting limits in the GUI. This is useful for things like a biquad frequency control where I'd like the lowest & highest allowable values to be dependent on sample rate.
  • Inc/Dec buttons are tricky for non-linear mappings, you can't just use slider interval as the interval is linear. Instead you need to add the GUI interval to the current value and convert back from GUI value to normalised value.
  • I provide myself with lots of constructor methods so I can easily set up parameters. I also provide methods and a structured approach for naming parameters and GUI controls so they can automatically link up when the GUI is constructed.
  • I've created a mediator class to handle communication between the GUI and the AudioProcessor so that I can easily change my GUI update mechanism later (e.g. from timer refresh to async fifo or maybe some funky lambda stuff)
  • I precompute things like dB conversions when automation values change so that I don't have to do them repeatedly in processBlock()

PluginParameter:

A class for managing a plugin parameter used by an ObliqueAudioProcessor.

Provides an array of values for a single parameter. The size of the array is set according to the number of preset programs.

Parameters have three types of values: automation, GUI & native. The GUI and native values are linearly related, but the mapping of automation to GUI/native may be a non-linear function.

The automation value is a double normalised to the 0..1 range and is used for communication with the host. This is the root value of the parameter that all others are derived from - thus it is always updated by the set routines for the other value types. Any GUI widgets should have their motion mapped linearly with the automation value, but with the value displayed being the GUI value which may or may not be linearly mapped (see ParameterSlider).

The native value is the value used by the DSP code. To save on processing, it is pre-calculated and set whenever the automation value is set (as by definition this will happen less often than reads of this value). If the native parameter type is dB, then the linear value of the variable is also calculated and stored (this saves processing time for DSP routines). Note that dB(Volts) is used.

The GUI value is the value displayed in the GUI or input from the GUI. Setting the GUI value triggers setting of the automation value (which in turn triggers setting of the native value). The GUI value is not stored, it is calculated on demand. The number of decimal places defined for the GUI value is enforced by rounding to the nearest value whenever conversions are made internally in this class.

If isEnumeration is set to true, then a string array may be input using setEnumeration() and the elements of this array will be returned when toStringWithUnits() is called. In this case, minGUIValue should be set to 0.0, maxGUIValue should correspond to the number of elements in the array, and intervalForGUI should be set to 1.0.

The parameter has a label, units and default value - these are all consistent for the whole array of values.

The parameter name is formed from a prefix specified in the constructor as well as a suffix specified in the ConstructorArgs. This name will be used to automatically link the PluginParameter to associated ParameterSliders. The parameter name will also be used in any saved preset program files. The parameter name must be safe for use as an XML attribute name ([reference](http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name)).

This class is not necessarily thread safe - however, most operations are atomic.

PluginParameters:

A class for managing a collection of plugin parameters used in an ObliqueAudioProcessor.

Parameters can be accessed via index or via name (which is slightly slower). Methods are provided for managing preset programs, including serialising to XML and de-serialising from XML.

MappingFunction:

This class is used for mapping an automation value to either a GUI or a native value. Methods are provided for conversions in either direction. The non abstract methods are declared final so that they may not be overridden. Note that the obLib unit tests only test the non abstract methods for the LinearMappingFunction class (as a proxy for MappingFunction).

A note on setting arbitrary limits for automation (these apply to getting parameter values):
- The initial implementation has code in ObliqueAudioProcessor::prepareToPlay() change these limits when sample rate changes
- At the same time, the GUI can listen for sample rate changes and take the necessary action to update itself appropriately
- If we changed to an asynchronous fifo signalling method, then prepareToPlay() routine would need to also trigger a resending of the newly limited get values (remember that MappingFunction doesn't link back to PluginParameter and hence can't do this work by itself)
- Maybe it would be better if changes to the MappingFunction propagated to the PluginParameter and changes from the PluginParameter propagated to the GUI
 - This means the MappingFunction would need to hold a pointer to its PluginParameter
 - In that case we would need to avoid recursive includes

ObliqueAudioProcessor:

A variation of juce::AudioProcessor which integrates with ob::PluginParameters.

Notes on usage:
===============
1.    The constructor of the derived class must initialise all PluginParameters.
<snip>

ObliqueAudioProcessorMonitor:

A class which monitors status changes in an ObliqueAudioProcessor and sends changes to listeners.

Typically you would instantiate one object of this class in a parent GUI component and then have child components register as a monitor (similar to a Listener, but synchronous).

ParameterSlider:

This slider is customised for use with ob::GUI and ob::PluginParameters. It provides an optionally visible label component and a juce::Slider. The slider operates internally with a normalised 0..1 range so that GUI movement corresponds directly with automation movement. The GUI value can be mapped in any fashion - this would typically be linear, but could be logarithmic for frequency. Hence the "feel" of any control is the same whether it is varied in the plugin GUI or by host automation. This mapping is provided by the linked 
ob::PluginParameter.

If the slider style is set to IncDecButtons, then this class implements custom inc/dec buttons that work correctly for non-linear mapping functions. The standard inc/dec implementation relies on the interval property (which of course isn't consistent along the slider's range for non-linear mappings). The custom buttons work by calculating what the final desired GUI value is and then setting the corresponding automation value.

If the slider is associated with a parameter that can be altered by sample rate changes (e.g. biquad filter cut-off frequency) then you will need to add it as sample rate monitor to an ObliqueAudioProcessorMonitor object. In that case, sample rate changes in the ObliqueAudioProcessor will automatically trigger a refresh of the slider limits.

Multiple ParameterSliders may control the same PluginParameter. To do this, simply create a new ParameterSlider with the same name as the PluginParameter you wish to link to (note that this will lead to multiple ParameterSliders with the same name).

ParameterSliderMediator:

Acts as an intermediary between a collection of ParameterSliders and a PluginParameters collection. It takes care of notifying the host of ParameterSlider value changes (the mediator calls setParameterNotifyingHost() for automatable parameters or setParameter() for non-automatable parameters) and of updating ParameterSliders when parameters change (the mediator calls updateValue()). The GUI needs to call updateGUI() to pick up
parameter changes and reflect them (typically in a timerCallback).

Different types of GUI panels (e.g. BiquadControl) should implement their own mediator which inherits from this. The parent GUI components should then declare a member variable of that type (e.g. BiquadControlMediator).

ParameterSliders are added to the mediator's management set via the abstract method nominateParameterSliders(). This method needs to be provided by all derived classes, but it is automatically called by initialise(). It is vital for the initialise() method to be called after construction of an
object of this class.

Each ParameterSlider is then mapped to its linked PluginParameter by calling PluginParameter::isLinkedSlider(). A PluginParameter may be linked to multiple ParameterSliders, but a ParameterSlider can only be linked to one PluginParameter.

 


#8

A worthy 5c :)