The best Plugin Structre for my Plugin and all about developing VSTMatrix plugin

Topics: Audio, Editor UI, Newbie, Plugin Development, Plugin Parameters, Plugin Programs, VST.NET Framework
Apr 11, 2014 at 1:03 AM
Hi guy, I took as a topic of my final thesis in University "Describe VST technology and create own VST plugin effect". It should be a kind of matrix which enable to assign different kinds of DSP to their modulators. The basic version will have Distortion, Volume, Delay and White noise generator as DSP and LFO and ADSR envelope for driving them. I found this project and really wanna accomplish the Plugin with it. Please could some more-skilled guys advice me the best Project structure ? Such as which classes I should use how structure them etc. I have been playing with VST.NET for a while so I understand how things work inside...... The plugin is supposed to have own GUI (Made with Winforms) and support Programs. I started from the VstNetAudioPlugin Template for Visual studio.

Thanks a lot guys I will keep this thread for all my coding questions and hopefully at the end, the Output will be the functional Plugin, then it can serve as a nice starting point for other guys in the same situation which I have right now.
Coordinator
Apr 11, 2014 at 6:22 AM
If you create a class for each term you talk about in your question (Matrix, Modulator, Delay, Distortion etc) and treat all the DSP objects as a "mini-plugin" that is internally connected (or not - depending on the matrix) then you'd be on the right track. Start small and simple and expand from there. Refactor your code when you see code duplication or the design does not fit anymore.

Two (other) main concerns that need to be addressed (and that I can think of right now ;-) are the Parameters for each component and how to pass the audio around to each of the components. The VS AudioPlugin template has an example of a Delay DSP component managing its own parameters. For passing around the audio samples I would create a base class with a (audio)buffer based 'process' method - that turns out to be most effective/efficient.

As on to how to build a Distortion etc. internally - I cannot help you there. I am a total DSP-noob ;-)

Hope it helps,
Marc
Apr 11, 2014 at 5:41 PM
Edited Apr 11, 2014 at 5:53 PM
My experience of developping plugins with the Vst SDK (C++) has been different. I found it was more appropriate to write procedural code instead of using object oriented concepts. Vst parameters are global so encapsulating them in objects is sometimes redundant. I wouldn't worry about a fancy structure for such a basic DSP project because you won't have to write a lot of code to achieve your goals.

I would create methods signature with all the required parameters, for a Volume effect I would do:
static void ProcessVolume(input, output, volume)
{
     output = input * volume;
}
For a modulator, I would use a object/structure that keeps it's current cycle position / value and add a process method operating on that structure:
static void ProcessLFO(modulatorItem)
{
     if (modulatorItem.waveform == Sinus)
         modulatorItem.value = Sin(modulatorItem.cyclePos++) 
}
For a matrix effect chaining, I would flatten the chaining to a single dimension list:
input -> effect 1 -> effect 2 -> volume -> output

For running the chain I would use a simple case structure:
foreach (modulatorItem)
   if (modulatorItem.modulator == adsr)
      proccessAdsr(modulatorItem); 
   else if (modulatorItem.modulator == lfo)
      proccessLFO(modulatorItem); 

foreach (effectItem)
   if (effectItem.effect == effect1)
      processEffect1(input, output, modulatorItem1.value, modulatorItem2.value);
   else if (effectItem.effect == effect2)
      processEffect2(input, output, modulatorItem1.value, null);
   else if (effectItem.effect == volume)
      processVolume(input, output, modulatorItem3.value);
Apr 13, 2014 at 7:55 PM
Thanks Guys, very valuable, I am going to try it and I will let you know
Apr 13, 2014 at 10:05 PM
Edited Apr 13, 2014 at 10:15 PM
Why I get this error during build??? (Build of tepmplate VSTaudioplugin)
Error   1   The type 'System.Configuration.Configuration' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. C:\Users\asleis\Documents\Visual Studio 2012\Projects\3TMatrixPlugin\3TMatrixPlugin\PluginCommandStub.cs    15  18  3TMatrixPlugin
Image

I can get over it by adding a reference to System.configuration assembly but I am not sure why I have to do it... Do you have any clue?
Apr 14, 2014 at 4:43 AM
Short answer is you have to add dependencies needed by PluginCommandStub to the project. In the delay sample plugin is there a dependency on configuration in the reference folder?
Apr 16, 2014 at 1:14 PM
Hi guys,

Good news, I already got working distortion effect and volume and white noise generator so I am on the right track.
Short question about the buffer provided from host.
it is float buffer, I tried to catch and display samples values and it seems to be really low

Is it the range of values -1 to +1 ?


btw noise generator works only when both channels have the same sample value (MONO)
When I generate different random values for each channel(STEREO) it causes crackling sound and
CUbase get lagged and return to me buffer with values above 1 ...

Also if I dont Play the cubase the Buffer is still filled by samples until bypass the plugin. Is it intentional ? I thought that this buffer should provide me with samples only when some sound is coming on his Input (when playback is on)
Coordinator
Apr 16, 2014 at 1:31 PM
Excellent news indeed.

"Is it the range of values -1 to +1 ?"
Yes.

"... it causes crackling sound ..."
Usually caused by missing audio samples/buffers (processing too slow) or sharp value changes in the samples (clicks).
Don't know why it would feed your plugin with values outside the [-1,1] range...?
Are you sure you don't have an error in your code? It sounds really strange to me/never heard it before...
Perhaps try outputting only one channel and leave the other 0 or pass-through..? See what happens.

"Also if I dont Play the cubase the Buffer is still filled by samples until bypass the plugin. Is it intentional ?"
Yes. You still want the sound that is coming in (say a mic) to be processed through all the effects, even if the transport is not running.

Hope it helps,
Marc
Apr 16, 2014 at 4:45 PM
About crackling sound...

Check the answer I gave here:
https://vstnet.codeplex.com/discussions/454740

You might have the same problem.
Apr 18, 2014 at 2:19 AM
Edited Apr 18, 2014 at 3:00 AM
Crackling is solved..anyway thanks for help
Marc Values above one was my fault ... The Trace express long values only with their shorter version and mantisa so just my bad understanding of representation of samples

next issue is ....

I have in DSP folder my class Distortion( similiar to Delay class from Template) there I put the enum
public enum distMakeUpOnOff
        {
            On,
            Off

        }
and a proprety to drive this process
public distMakeUpOnOff makeUpOnOff;
I added a checkbox into my PluginEditorVIew and generated the handler
private void checkBox1_CheckedChanged(object sender, System.EventArgs e)
        {
            // here should be changed the "public distMakeUpOnOff makeUpOnOff;" in a instance of my Distortion class created in my AudioProcessor
          
        }
How can I make it? Do I need use a parameter for that or there is some other way?

I wanted to pass into PluginEditorVIew contructor an instance of _Plugin so that I would be able to get something like
_Plugin.AudioProcessor.DIstoriton.makeUpOnOff 
then easily change the status... But I cant find where the constuctor of PluginEditorVIew is called to pass from there Plugin instance...
Or is there any easier way?
My project structure is very similiar to the Template so you can use it as a model, just how to pass any property from Delay class to PluginEditorVIew

EDIT: I found a workaround. when the plugin is created the PluginEditor calls
_view.SafeInstance.InitializeParameters(paramList,);
and PluginEditor keeps a reference on Plugin so I extended this method like below and passed the Plugin refference to the PluginEditorVIew .
 internal bool InitializeParameters(List<VstParameterManager> parameters, Plugin plugin)
It works but I am not very familiar with Threads so maybe I broke something else by doing this :) Look forward to your notes
Apr 18, 2014 at 5:58 AM
I've only created Hosts wit Vst.Net but I've taken a quick look into the Delay sample plugin to see how parameters are handled.

This is where they are referenced in Vst.Net framework:
ParameterFactory = new PluginParameterFactory();
ParameterFactory.ParameterInfos.AddRange(audioProcessor.Delay.ParameterInfos);
I'd venture you only need to keep a reference to the ParameterInfos Observable Collection in your UI:
public VstParameterInfoCollection ParameterInfos

If it were me I would keep a single static reference of this collection and access it from everywhere it's needed.
Since it is an observable collection, I believe you can add your parameters to this collection later on.

But be sure you lock the collection when using it to be thread safe. A Host callback thread could modify it at the same time your UI thread tries updating it.
I doubt there's internal locking in the collection or in Vst.Net but I don't have time to look that far.

There is an example of locking a notes collection in the MidiNoteMapper plugin sample:
lock (((ICollection)NoteOnEvents).SyncRoot)
More about threads, unless Vst.Net does special invoking which I doubt again, you would be executing in the thread context of the Vst Host which can't touch your UI thread without invoking. Here's an example of creating a parameter and handling the host callback with Invoke:

First create a VstParameterInfo for your parameter. Then add it to your ParameterInfos observable collection.

Subscribe to the observable Parameter collection in your UI Form to be notified by callback:
_delayTimeMgr.PropertyChanged += new PropertyChangedEventHandler(_delayTimeMgr_PropertyChanged);

private void _delayTimeMgr_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
            if (e.PropertyName == "CurrentValue")
            {
                VstParameterManager paramMgr = (VstParameterManager)sender;

                // Here we are in the Host thread so use Invoke to update your UI, 'this' refers to your Form instance
                // Without invoke you would have a cross thread exception because UI controls (CheckBox) can't be touched directly by the Host thread
                this.Invoke((MethodInvoker) delegate 
                {
                     delayTimeCheckBox.Checked = (paramMgr.CurrentValue > 0);
                });

                // If you are in your audio thread (the host thread) no invoke is necessary
                _bufferLength = (int)(paramMgr.CurrentValue * _sampleRate / 1000);
            }
 }
Seems like you are on the right track and solving problems fast, good luck with your project!
Coordinator
Apr 18, 2014 at 6:48 AM
You could make the on-off switch a VstParameter. That way the host can save that state automatically (even without you having to implement persistence) and the value can be automated in the host - something a lot of users would appreciate.

But as Yuri also hinted on: there are a number of threads involved and VST.NET does NOTHING with threads or locking. This was done on purpose because I did not wanted to introduce locks at places that would hurt a specific plugin design. In practice you notice very little of those host threads. The only one is the main/UI thread and having to use Invoke to 'post' calls to your form or controls.

The use of the VstParameterManager is optional - but I do think it solves a couple of 'issues' with parameters especially when implementing Programs. So assuming you use VstParameterManager you could simply set the value on the CurrentParameter.Value. Do not forget to attach an event handler to the VstParameterManager to handle reflecting automation changes back onto the UI.

Hope it helps,
Marc.
Apr 18, 2014 at 6:24 PM
Edited Apr 18, 2014 at 6:26 PM
Ok guys thanks, I created VstParameterInfo With
paramInfo.MinInteger = 0;
paramInfo.MaxInteger = 1;
paramInfo.StepInteger = 1;


assign to the VstParameterManager DistortionMakeUpOnOff and add into obbservable collection.

in PluginEditorView bind to the DistortionMakeUpOnOffCheckbox control and it works.

It is nice way to for this case but I have a concern about how I will Implement my customizable Matrix it can not be done this way I guess...something like in Reason THor device as you can see below (at very bottom the green part)
Image



Ok Next issue is that every time when I close and open my Plugin The GUi components are in the default state, How to force them to keep states from previous time..
Apr 18, 2014 at 8:36 PM
Edited Apr 18, 2014 at 8:38 PM
What do you mean by it can't be done like Thor? What would be the limitation? Exposing the parameters, the custom control drawing, the audio routing etc. ?
For parameters Thor matrix would have Row1SourceModulatorType, Row1SourceAmount, Row1DestinationParam, Row1DestAmount, Row1Scale, repeat that for Row2-Row-7, make named constant parameters for Source and Destination, 0=Lfo1, 1=Envelope1...
How to force them to keep states from previous time..
Implement programs with VstParameterManager like Marc suggested. When the host reload your plugin it will send a program change and set the parameters. Like he said you have to subscribe to Parameter change events and update your gui when you receive them.
Coordinator
Apr 19, 2014 at 8:18 AM
Most host DAWs use the plugin parameters if no Persistence (chunks) is implemented by the plugin. So if you represent the plugin's state with parameters you should get persistence for free. If you save the 'song' in the DAW your plugin settings should also be saved and applied when the song loads again.
Apr 20, 2014 at 6:43 PM
Yuryk, srry I am not sure where and how to do this... My plugin still starts with default values for GUi (without the parameters values for example a label starts with value Label4)
Apr 20, 2014 at 6:50 PM
Use the code I posted above to add PropertyChangedEventHandler on your parameters, update your GUI in this handler.
When the plugin is loaded by the host, it should set parameters, this will call your handler with the parameter value.
Apr 20, 2014 at 11:50 PM
Edited Apr 21, 2014 at 12:05 AM
I do not understand what is Host thread and Audio thread, where is a boundary between them? Sry I am confused here...
I understand it this way....

There are two directions of information flow

Host (VSTparameterMangers instances) -> My plugin
and My Plugin -> Host


fist of them I understand this way
  1. I create an instance of VstParameterManager for example DistThreshold
  2. It is added to the plugin into this collection VstParameterInfoCollection parameterInfos = _plugin.PluginPrograms.ParameterInfos;
  3. It is happening inside my Distortion DSP class which is instanciated from AudioEditor class to perform DSP so I have in Distortion class this
public Distortion(Plugin plugin)
        {
            _plugin = plugin;
            InitializeParameters();
 
            DistMakeUpOnOff.PropertyChanged += new PropertyChangedEventHandler(DistMakeUpOnOff_PropertyChanged);
        }

private void DistMakeUpOnOff_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "CurrentValue")
            {
                
                //switch of value
                VstParameterManager paramMgr = (VstParameterManager)sender;
                if (paramMgr.CurrentValue == 0)
                {
                    this.makeUpOnOff = distMakeUpOnOff.Off;
                }
                else 
                {
                    this.makeUpOnOff = distMakeUpOnOff.On;
                
                }
so when anything change my VstParameterManager value I can catch it here and handle it like I do it is the direction from Host to My plugin
In the PlugiEditorVIew class I have this
private void BindParameterToKnob(VstParameterManager paramMgr, Label label, Knob knob, Label shortLabel)
        {
            // NOTE: This code works best with integer parameter values.
            label.Text = paramMgr.ParameterInfo.Name;
            shortLabel.Text = paramMgr.ParameterInfo.ShortLabel;

            // use databinding for VstParameter/Manager changed notifications.
            knob.DataBindings.Add("Value", paramMgr, "ActiveParameter.Value");
            
            knob.ValueChanged += new System.EventHandler(Knob_ValueChanged);
            knob.Tag = paramMgr;
        }
Here I am starting to be confused.... I thing that databinding connect the Value of knob GUi control to my VstParameter instance DistThreshold.Value but I cant find this property inside the class VstParameterManager...

I thought that this DataBinding is enough to update my Parameter value when the GUI knob is changed so why I have to write knob.ValueChanged += new System.EventHandler(Knob_ValueChanged); ??? But I do

so I have next piece of code in here
private void Knob_ValueChanged(object sender, System.EventArgs e)
        {
            var knob = (Knob)sender;
            var paramMgr = (VstParameterManager)knob.Tag;

            paramMgr.ActiveParameter.Value = knob.Value;

            switch (knob.Name)
            {
                case "DistortionThresholdKnob":
                    DistortioThresholdValueLabel.Text = paramMgr.ActiveParameter.Value.ToString();
                    break;
}
so right now I have implemented the reaction on the GUI Knob action which is relflected into my VstParameter DistThreshold and also catched inside my DIstortion Class where it affects DSP processing...

so far Am I right?


But still I miss the part of code where the knob takes the information which value should have when the Plugin Window is created... It is somewhere because knobs keeps their state... Sorry it is quite long post but I really wanna understand how it works....

btw is this evnet fired everytime when the window of plugin is reloaded??
_delayTimeMgr.PropertyChanged += new PropertyChangedEventHandler(_delayTimeMgr_PropertyChanged);
Apr 21, 2014 at 1:03 AM
Edited Apr 21, 2014 at 2:50 AM
Ok I got it partly.
I added into my PluginEditorVIew class this
private void BindParameterToCheckBox(VstParameterManager paramMgr, Label label, CheckBox checkBox)
        {
                //this code obviously does notwork when the Window is opened why?
               label.Text = paramMgr.CurrentValue.ToString();
            
            checkBox.CheckedChanged += new System.EventHandler(CheckBox_CheckedChanged);
            checkBox.Tag = paramMgr;


            paramMgr.PropertyChanged += new PropertyChangedEventHandler(_checkbox_PropertyChanged);
        }
and my handler for the PopertyChanged event below... U can also see what an exeption.. I can open the window first time change the checkbox state and it works(except the LIme color, But it load the correct value ) then when I close the plugin WIndow and open it again and click the checkbox the exception arises.

Image

And I still do not get the fact why I have to do this Invoke for checkbox and I do not need to do it for Knobs...
Apr 21, 2014 at 1:54 AM
Edited Apr 21, 2014 at 2:27 AM
I don't see the image in your post but I can probably guess what is happening.

First you should avoid using binding if you don't own all the threads involved.
Binding is inherently not thread safe and you lose control of execution when you use it.
Just call the SetValue function or Text/Checked properties yourself so you can use invoke when appropriate.

In function _checkbox_PropertyChanged you need to wrap all access to GUI controls in an invoke method call.
// Would be better to name function paramMgr_PropertyChanged
void _checkbox_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    this.Invoke((MethodInvoker) delegate 
    {
        label.Text = paramMgr.CurrentValue.ToString();
        knob.SetValue(paramMgr.CurrentValue);
        checkBox.Checked = (paramMgr.CurrentValue > 0);
    });
}
You got to do invoke when a function call originating outside from your UI thread wants to access a control on your UI thread.
If the call originates from the Form like knob.ValueChanged then don't use it because the caller is the UI thread.
Same if it's from the constructor or other events of your plugin.

If the call originates from outside your plugin like when the host change a parameter and Vst.Net calls PropertyChanged handler use invoke.
If it's your plugin that initiated the PropertyChange than invoke is not necessary. There is an InvokeRequired property in .Net to check if the caller needs invoking.
In most scenario if the code is not touching a UI component invoke is never required.
Apr 21, 2014 at 2:06 AM
Edited Apr 21, 2014 at 2:17 AM
I do not understand what is Host thread and Audio thread, where is a boundary between them? Sry I am confused here...
  • Host and audio thread is the same. Any serious host has an audio thread that will call your plugins. Functions called by the host like ProcessReplacing or SetParameter are said to be running in the context of the audio thread. Functions called by an event sent from your Form like control.ValueChanged are said to be running in the UI thread.
btw is this evnet fired everytime when the window of plugin is reloaded??
_delayTimeMgr.PropertyChanged += new PropertyChangedEventHandler(_delayTimeMgr_PropertyChanged);
  • It is called when someone change the value of the delayTime property.
    Wheter the value was changed by the host, the Vst.Net framework or your plugin UI should be irrelevant.
    Especially since you don't control the behavior of the host and the Vst.Net framework.
    For your plugin, if Vst.Net doesn't provide a way to set the default value of this parameter than you could set it yourself by changing _delsyTime.Value.
    For your host if you have saved a project and re-open it, it should set some parameters to initialize the plugin back to it's previous state but it's your job to handle these parameter change.
And I still do not get the fact why I have to do this Invoke for checkbox and I do not need to do it for Knobs...
  • You really should do it for both if by knob you imply a visual control on your form. I can't tell without looking at all the code why it works for knob. Maybe binding is interfering here by doing some invoke calls but you can't see the framework code that your knob use for binding hence my suggestion to remove it.
Apr 21, 2014 at 3:34 AM
Edited Apr 21, 2014 at 3:44 AM
THx for answer, I uploaded the image again.. I commented the Databinding row for Knobs and it does not keep the state of those knobs after closing and opening the plugin window..... so for knobs it works only because of that row... I added your invoke method for knobs and I again got the same exception and crash of host.... I think that maybe you misunderstood me ... Subscribe to the event which is fired on a change of a VstParameterManager property does not solve the problem with reloading the states for UI controls... Because when you just close the WIndow of your plugin and Open it again no property of any pluginParameter is being changed- THis event does not come on the scene until you turn a knob or click a checkbox or whatever ( or host change a param value by automation .....)

I think it works like when you close The GUI window it just destroys all GUI objects(I mean that the window of a plugin is not only hidden but destroyed) and when you open the GUI for the same plugin instance it just creates all GUI components with default values... I set the breakpoint into InitializeComponent() method of PluginEditorVIew class and it is called every single time you re opening the plugin Window so that it assign to the UI controls again the default values (except Knobs which use the Databinding mechanism...)

So I have two sugestions ... Add into PluginEditorVIew'S constructor the additional method which will walk through the VSTParameterManager collection and will assign all current values...

Or Find an event which is fired on the Window opening(and do the same).... Otherwise without the databinding there is no mechanism to reload your current states... I thought about using Programs... Then could be possible to create a program OnCLose event of the Plugin Window and then load on Open event but there is a problem when you have more then one instance of the Plugin in a DAW because they somehow share the programs list and if you close one plugin instance and open different one it would load the Program saved by the first instance (here I guess...) any ideas?

Marc Why do you use the Databinding in the VstAudioTemplate, Does it have any special purpose ?
Youryk if you are intrested in my code I am not stingy to share it :) ANd again I really appriciate you help me guys, otherwise I would be really desperete :)
Apr 21, 2014 at 10:04 AM
Edited Apr 21, 2014 at 10:04 AM
For some unknown reason I still can't see the image, only a broken link in my mail client. Can you post the exception full stack in text form?
Should be enough to understand what is happening.
Apr 21, 2014 at 10:49 AM
Edited Apr 21, 2014 at 10:52 AM
I commented the Databinding row for Knobs and it does not keep the state of those knobs after closing and opening the plugin window.....
  • Are you setting the value of the knobs somewhere? Databading is not magic, it calls a SetValue function or set a Value property of the knob, I suggested you set this value manually.
I added your invoke method for knobs and I again got the same exception and crash of host....
  • The point is that this is the correct way to set the parameter IMO and the exception you got is probably related to a more mundane programming error. Maybe controls are null at this time and you need to implement lazy initialization. Or maybe you're trying to do invoke on a disposed control, theres a way to check that too.
Subscribe to the event which is fired on a change of a VstParameterManager property does not solve the problem with reloading the states for UI controls... Because when you just close the WIndow of your plugin and Open it again no property of any pluginParameter is being changed.
  • It shouldn't, if you just destroy the window without unloading the plugin, the host won't help you besides calling EditorOpen.
THis event does not come on the scene until you turn a knob or click a checkbox or whatever ( or host change a param value by automation .....)
  • The host must have an initialization mechanism to recall your parameters from last session when you close the program and re-open it. This is either done with SetChunk or SetParameter and will vary depending on the host. To be compliant you should implement both.
I think it works like when you close The GUI window it just destroys all GUI objects(I mean that the window of a plugin is not only hidden but destroyed) and when you open the GUI for the same plugin instance it just creates all GUI components with default values... I set the breakpoint into InitializeComponent() method of PluginEditorVIew class and it is called every single time you re opening the plugin Window so that it assign to the UI controls again the default values (except Knobs which use the Databinding mechanism...)
  • Again you should make a difference between re-openng your window which is a different scenario than the host recalling parameters from last session.
So I have two Add into PluginEditorVIew'S constructor the additional method which will walk through the VSTParameterManager collection and will assign all current values...
  • If the host has initialized the parameter by that point it should work. But if the host initialize the parameters after opening your window then it would'nt work. I would cover both scenarios. Many plugins fail due to programming errors when the host initialize parameters before opening the window so many host may call SetParameter after opening the GUI to workaround this issue.
Otherwise without the databinding there is no mechanism to reload your current states...
  • Again databinding is just an unoptimised way of setting a value of a control, there's no reason why you couldn't write the same code that databinding use unless the control is read-only. In practice you would use a more basic technique. The thought of databinding in the context of audio programming makes me cringe. I have seen all sort of thread related and performance bug crop up in data driven business apps that are super hard to track down. Fortunately there is almost always an easier alternative to databinding. You could use databinding on every control of your form label and checkbox included but when you'll need to debug no one is gonna be able to precisely tell what is happening in your program.
I thought about using Programs... Then could be possible to create a program OnCLose event of the Plugin Window and then load on Open event but there is a problem when you have more then one instance of the Plugin in a DAW because they somehow share the programs list and if you close one plugin instance and open different one it would load the Program saved by the first instance (here I guess...) any ideas?
  • Programs are not used to recall parameters, they are used to recall program number. It's a different job than SetChunk and SetParameter.
Youryk if you are intrested in my code I am not stingy to share it
  • I'm still wondering what you do in your ParameterChange callback, when and where is the exception occuring and what is the content of the exception.
Apr 21, 2014 at 11:24 PM
Edited Apr 21, 2014 at 11:33 PM
Hi gays next day of programming is up and I ve solved something and got stuck with something new :)

I solved the problem with keeping state of my UI controls by adding this into PluginEditorView class
private void BindParameterToKnob(VstParameterManager paramMgr, Label label, Knob knob, Label shortLabel, Label ValueLabel)
        {
            // NOTE: This code works best with integer parameter values.
            label.Text = paramMgr.ParameterInfo.Name;
            shortLabel.Text = paramMgr.ParameterInfo.ShortLabel;
            knob.Minimum = paramMgr.ParameterInfo.MinInteger;
            knob.Maximum = paramMgr.ParameterInfo.MaxInteger;
            knob.Value = Convert.ToInt32(paramMgr.CurrentValue);
            ValueLabel.Text = paramMgr.CurrentValue.ToString();


            // use databinding for VstParameter/Manager changed notifications.
            //knob.DataBindings.Add("Value", paramMgr, "ActiveParameter.Value");
            knob.Tag = paramMgr;
            paramMgr.PropertyChanged += new PropertyChangedEventHandler(_knobVstParameterManager_PropertyChanged);
            knob.ValueChanged += new System.EventHandler(Knob_ValueChanged);

        }
BUT
At the very bottom I Register two events,
first
paramMgr.PropertyChanged

to get know when current value of Parameter is changed (fx by host automation) to be able react on it in My GUI, reaction is implemented like below
 private void _knobVstParameterManager_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "CurrentValue")
            {
                VstParameterManager paramMgr = (VstParameterManager)sender;


                this.Invoke((MethodInvoker)delegate
               {

                   switch (paramMgr.ParameterInfo.Label)
                   {
                       case "Threshold":
                           DistortioThresholdValueLabel.Text = paramMgr.CurrentValue.ToString();
                           DistortionThresholdKnob.Value = Convert.ToInt32(paramMgr.CurrentValue / 100);
                           break;

                   }

               } );         
            
        }}
and second
knob.ValueChanged += new System.EventHandler(Knob_ValueChanged);

to know when a user tweak the Knob in the GUI to update appropriate VstParameterManager like below
 private void Knob_ValueChanged(object sender, System.EventArgs e)
        {
            var knob = (Knob)sender;
            var paramMgr = (VstParameterManager)knob.Tag;

            paramMgr.ActiveParameter.Value = knob.Value;

            switch (knob.Name)
            {
                case "DistortionThresholdKnob":
                    DistortioThresholdValueLabel.Text = paramMgr.ActiveParameter.Value.ToString();
                    break;

            }
Here I get infinite loop because when the user tweak the knob It calls ValueCHanged where the Value of knob is reflected into Current value of VstParameterManager then the _knobVstParameterManager_PropertyChanged is called because the current Value of parameter has been changed, so I assign this change back to the GUI Knob what causes again calling the ValueCHanged etc.....

In my understanding I need to have both of them to pass values

GUI -> VstParameterManager by ValueCHanged

and

VstParameterManager -> GUI by _knobVstParameterManager_PropertyChanged


How could I solve this ? I do not use the Databinding anymore on your advice.....

next question is if VstParameterManager values

paramMgr.CurrentValue and paramMgr.ActiveParameter.Value are always equal (the same)
Coordinator
Apr 22, 2014 at 6:45 AM
You could simply maintain a class member flag (bool) variable 'inEvent'. Check it for true in each event handler and exit if so. Otherwise set it to true and execute normal handler code. Make sure you reset the flag when your done (use finally block so exceptions do not bypass this mechanism). Theoretically you should use locking on the flag because automation and UI probably run on different threads. Use the Interlocked.Exchange for simple one-line syntax.
Apr 22, 2014 at 7:57 AM
Edited Apr 22, 2014 at 8:18 AM
Great news! I like the code you've produced so far.

I finally see the image you linked to and it is what I suspected, just before calling invoke check that the control is initialized and not disposed.
if (this.IsHandleCreated && !this.IsDisposed)
{
    // this.invoke here
}
Here I get infinite loop...
  • This is a common problem with UI validation and is easily solved. It's also a good example why I suggested to avoid binding. Imagine if you had two controls modifying each other in their event handlers with databindings. There would be no straightforward way to stop the infinite loop because you don't control who initiated the event.
When you can control who initiated the event, you can use a control variable to determine if the event will be executed.
Personally I use this pattern everywhere in my GUI (Mark's advice to use finally and locking makes it more solid for sure) :
// Member variable, controls if event is executed to bail out of infinite loops
private bool executeEvent = false;

public Constructro()
{
   // Initialize controls without raising their event handlers, executeEvent is false
   control1.Value = 0;
   control2.Value = 0;
   executeEvent = true;
}

private control1_valueChanged(object sender, EventArgs e)
{
   // Do not execute event
   if (!executeEvent)
      return;

   // Control1 valueChanged event will change Control2 value and
   // Control2 valueChanged event will change Control1 value but
   // there won't be an infinite loop because of executeEvent variable
   executeEvent = false;
   control2.Value = control1.Value;
   executeEvent = true;
}

private control2_valueChanged(object sender, EventArgs e)
{
   if (!executeEvent)
      return;

   executeEvent = false;

   control1.Value = control2.Value;

   executeEvent = true;
}
Are paramMgr.CurrentValue and paramMgr.ActiveParameter.Value always the same?
  • I don't know. It's an implementation detail of Vst.Net but based on the name of the variables I would use CurrentValue when reading Value of a parameter and use Value when setting value of a parameter. I have a doubt about VstParameterManager usage, not sure if you should use one for each parameter or one for all parameters. Maybe you need to use VstParameterInfo for each individual parameter and one VstParameterManager for all of them, you know better than me.
This is nitpicking but here are three little tips to boost your productivity and give a better structure to your program:
1 Avoid using var if the type is known, it hampers readability of the code. It's often an indication that the programmer is not familiar with strong typing. When I see it in code reviews it attracts my attention because there are often other unrelated thing to optimize there.
var knob = (Knob)sender;
var paramMgr = (VstParameterManager)knob.Tag;

// Could be replaced by
Knob knob = (Knob)sender;
VstParameterManager paramMgr = (VstParameterManager)knob.Tag;
2 Avoid using the Tag or Name property as a generic placeholder:
knob.Tag = paramMgr;
VstParameterManager paramMgr = (VstParameterManager)knob.Tag;
3 The alternative to using Tag and long switch statements is to use a more generic approach to programming. You'll probably be adding more parameters to your plugin so it would be nice to minimize the number of changes you have to make each time you add a new one. A hash table like the Dictionary collection is good for this.

Check this example:
private void Dictionary<Knob, VstParameterManager> parameters = new Dictionary<Knob, VstParameterManager>();
private void Dictionary<Knob, Label> parametersLabel = new Dictionary<Knob, Label>();

public Constructor()
{
   parameter.add(knob1, new VstParameterManager());
   parameter.add(knob2, new VstParameterManager());

   parametersLabel.add(knob1, label1);
   parametersLabel.add(knob2, label2);

   knob1.ValueChanged += new ValueChangedEvent(knob_valueChanged);
   knob2.ValueChanged += new ValueChangedEvent(knob_valueChanged);
}

 // When you add a new parameter, you only need to add new items to the Dictionaries instead of modifying the switch statement in this function
private void knob_valueChanged(object sender, System.EventArgs e)
{
   // Not sure if casting sender in the bracket is required
   KeyValuePair<Knob, VstParameterManager> param = parameters[(Knob)sender];
   Knob knob = param.Key;
   VstParameterManager paramMgr = param.Value;
   Label label = parametersLabel[knob].Value;

   // With generic programming you eliminate maintenance of switch statements
   paramMgr.ActiveParameter.Value = knob.Value;
   label.Text = Convert.ToString(paramMgr.ActiveParameter.Value, CultureInfo.InvariantCulture);
}
Apr 22, 2014 at 10:27 PM
Edited Apr 22, 2014 at 10:37 PM
Great advice guys, I am falling in love more and more with this VST craft :) Tommorow I have a consultation with my Thesis leader so hopefully I will convince him to not kick my ass :)

Right Now I wonder how input buffers are process I have this method inside my Audio Processor,
public override void Process(VstAudioBuffer[] inChannels, VstAudioBuffer[] outChannels)
        {
            // by resetting the time info each cycle, accessing the TimeInfo property will fetch new info.
            _timeInfo = null;

            if (!Bypass)
            {
                // TODO: Implement your audio (effect) processing here.

                           
                            
                DistortionEffect.ProcessStereo(inChannels, outChannels);
                HpFilter.ProcessStereo(inChannels, outChannels);

             
                VstAudioBuffer input1 = inChannels[0];
               VstAudioBuffer input2 = inChannels[1];
               VstAudioBuffer output1 = outChannels[0];
               VstAudioBuffer output2 = outChannels[1];
               float sample = 0;
               float[] buffer = new float[input1.SampleCount];

               for (int i = 0; i < input1.SampleCount; i++)
               {



                   LpFilter.CutoffMod = (FilterLFOGenerator.nextSample()); 
                   
                   sample = input1[i];

                   sample = LpFilter.ProcessSample(sample);
                   //sample = Chor.Sample(sample);
                   output1[i] = sample;




                   sample = input2[i];
                   sample = LpFilter.ProcessSample(sample);
                   //sample = Chor.Sample(sample);
                   output2[i] = sample;




               }
So first is called my distortion class where both channels are process so it is like InputBuffers -> DIstortion -> OutPutBuffers . Now the outputBuffers are full of distorted samples and it comes into HpFIlter Here I wonder What samples are going into for filtering

a) The inputBuffers are again with same sample values as before distortion or not ?

b) OutputBuffers have the output samples from distortion so the filtering just rewrite them by originalBuffersValue -> HPfiltering -> OutputBuffer


Other way how to ask > Is my implementation kind off parallel processing or serial?
I wonder why both processing I can hear in sound, because IMO when the outputBuffers are rewritten in each row , And InputBuffers are for every row just like they was on row one the logic is that I get at the end of processing just OUtbuffer with only last row of processing aplied ... Please bring here some light :)


btw below my working version of the Plugin All knobs already works... I decided do something little bitless complex than at the beginning because my deadline is In 14 days... Then I will add more functionalities.. I will also motivate my leader to invent some interesting Thesis topics for new students, with my work they will not have to stuck with beginnings and woul be able to spend time by creating much more complex stuff... so far thanks for support again

Image
Apr 23, 2014 at 12:06 AM
It's up to you how to do the audio routing depending on the kind of DSP. If the effect acts only on one sample at a time like a volume control you can do:
Volume.ProcessStereo(outChannels, outChannels);

If the effects need look ahead, use an accumulator or mixes multiple sources you might have to create a temporary buffer.
VstAudioBuffer[] tempBuffer = inChannels.Clone() // dunno if clone exists
Filter.ProcessStereo(tempBuffer, outChannels);
Apr 23, 2014 at 12:43 AM
Edited Apr 23, 2014 at 12:58 AM
public override void Process(VstAudioBuffer[] inChannels, VstAudioBuffer[] outChannels)
        {
            
                              
                InGain.ProcessStereo(inChannels, outChannels);        
              
                DistortionEffect.ProcessStereo(inChannels, outChannels);                         
                     
                HpFilter.ProcessStereo(inChannels, outChannels);
You get me wrong probabely. let me ask again. each of called effects has own method in which goes through the buffers, process single samples and put new samples inside the Output buffers like bellow( no matter what the processSample() does)
 public void ProcessStereo(VstAudioBuffer[] inChannels, VstAudioBuffer[] outChannels)
        {

            VstAudioBuffer input1 = inChannels[0];
            VstAudioBuffer input2 = inChannels[1];
            VstAudioBuffer output1 = outChannels[0];
            VstAudioBuffer output2 = outChannels[1];
            float sample = 0;

            for (int i = 0; i < input1.SampleCount; i++)
            {

                
                        //process first channel
                        sample = input1[i];
                        sample = ProcessSample(sample);
                        output1[i] = sample;

                        //process second channel
                        sample = input2[i];
                        sample = ProcessSample(sample);
                        output2[i] = sample;
                     
                }

            }
So first InGain effect, this one goes through all buffers, convert all samples from InputBuffers and put them into OutputBuffers.... then program goes back to the main public override void Process function.. Right now It goes to the DistortionEffect but again it Takes initial InputBuffers Values because they has not been changed by InGain... DistortionEffect runs again the processStereo where again it puts the processed samples into OutputBuffers BUT HERE are already samples processed by InGain effect so I guess they are overwritten... If it works like that, Here I should lost the InGain effect because "InGained" samples were overwritten..... But it seems to be not like that because I can hear by ears both effects.... DO you understand? I would say that I have to after running InGain.ProcessStereo(inChannels, outChannels); copy from OutCHannels Buffers processed samples into inChannels but apparently I do not need to do this and that is why I am confused....


Btw In Java I used to be able during Debugging to see all values in the collection(here I wanna check the VstBuffers)... But I cant.....Is it not possible in C# or it could be intentionally forbidden by code design?
Apr 23, 2014 at 1:10 AM
Edited Apr 23, 2014 at 1:16 AM
You can't see the values because VstAudioBuffer uses pointer arithmetic to treat the buffer as a single address instead of exposing every individual values. You can poke at any index if you want to get a glimpse but in DSP there's no point at looking at the value of individual samples.

To understand what happens exactly in your DSP routines, it is necessary to know the algorhitms in use. Maybe you are using convolution which essentially transforms the samples instead of overriding it. Gain is a convolution, you multiply the signal, you don't overwrite it. If the other effect in the chain performs an addition or multiplication on the sample instead of an affectation you will in essence mix (or perform a convolution) the two effects together. Try clearing output buffer between the two effects, you will lose the first one.

If this is not the behavior desired you should use temporary output buffers and mix them after processing.
Apr 23, 2014 at 1:33 AM
regarding the two specified effects gain and distortion, the usual way of processing them would be:
input->distortion->gain

I suppose you are doing:
input->gain->output
input->distortion->output

if the second effect is in the form:
output[index] += sample
you are mixing the two effects together.

if it is:
output[index] *= sample
it's a convolution, if value of sample is small like in a distortion effects it will sound like mixing
Apr 23, 2014 at 1:45 AM
Edited Apr 23, 2014 at 1:47 AM
I changed this
public override void Process(VstAudioBuffer[] inChannels, VstAudioBuffer[] outChannels)
        {
            VstAudioBuffer[] _inChannels = inChannels;
            VstAudioBuffer[] _outChannels = outChannels;
                              
                InGain.ProcessStereo(_inChannels , _outChannels);        
              
                DistortionEffect.ProcessStereo(_inChannels , _outChannels);                         
                     
                HpFilter.ProcessStereo(_inChannels , _outChannels);
By this
public override void Process(VstAudioBuffer[] inChannels, VstAudioBuffer[] outChannels)
        {
            VstAudioBuffer[] _inChannels = inChannels;
            VstAudioBuffer[] _outChannels = outChannels;
                              
                InGain.ProcessStereo(_inChannels , _outChannels);        
              
                DistortionEffect.ProcessStereo(_outChannels  , _outChannels);                         
                     
                HpFilter.ProcessStereo(_outChannels , _outChannels);
And it works much better, seems to be a serial processing instead of the odd processing before... Probabely I heart all effects because of algoritms.... But in the case I used some more complex effect It would be not a serial chain as I intend....Is my way of thinking about this correct? Again thx
Apr 23, 2014 at 2:00 AM
Edited Apr 23, 2014 at 2:15 AM
YuryK wrote:
regarding the two specified effects gain and distortion, the usual way of processing them would be:
input->distortion->gain

I suppose you are doing:
input->gain->output
input->distortion->output

if the second effect is in the form:
output[index] += sample
you are mixing the two effects together.

if it is:
output[index] *= sample
it's a convolution, if value of sample is small like in a distortion effects it will sound like mixing
Here I do not understand fully.. Gain is not a MakeUp part of the distortion effect it is simple Input Gain at the beginning of the Plugin DSP... So my intention is
input->gain->distortion

by code change above I got it IMO, or not ? :-D

but previous state is not clear to me because I thought that the AudioProcessor gets whole InputBuffers and dont send it back to the host unitl everything is done inside the public override void Process ............ Is this true?

so when you take this
input->gain->output
input->distortion->output
In my understanding the AudioProcessor .Process function is done at the moment when second row output is filled up and this output will send back to the Host .. the First output is never used as a input somewhere so it shouldnt be heart at all... Obviously I am wrong but where :-D


for fullness here are my algorithms

Gain
float ProcessSample(float sample)
        {
            float _gain = this.InGain.CurrentValue / 100;
            float _sample = sample;

            _sample = _sample + _sample * _gain;

            return _sample;


        }
and Distortion as a simple clipper
float ProcessSample(float sample)
        {
            float _Threshold = this.DistThreshold.CurrentValue / 100;
            float _sample = sample;

            if (sample >= 0)
            {
                _sample = Math.Min(sample, _Threshold);

                _sample /= _Threshold;

                return _sample;
            }
            else
            {
                _sample = Math.Max(sample, -_Threshold);
                _sample /= _Threshold;
                return _sample;
            }
In gain algorithm I do _sample = _sample + _sample * _gain; because my Knob has -100 to 100 and starts on 0 what would cause in the case _sample = _sample * _gain; canceling the Sound
Apr 23, 2014 at 12:14 PM
Strictly speaking the effect known as gain would be able to increase the volume beyond clipping.
However most of the time what is labeled gain in plugins is really an attenuation which is implemented in DSP like this:
sample *= [0, 1]

which is equivalent to:
sample = sample * [0, 1]

It seems you have an addition in there which could clip the signal. You should normalize value of gain to be in range 0 to 1 then multiply the samples by that.
First output is never used as a input somewhere so it shouldnt be heart at all... Obviously I am wrong but where :-D
  • What you say is right but you overlook the fact that both effects share the same output. The output is not replaced by the second effect, it is modified by the second effect.
Here I do not understand fully.. Gain is not a MakeUp part of the distortion effect it is simple Input Gain at the beginning of the Plugin DSP... So my intention is
input->gain->distortion
  • The reason you often see attenuation at the end of an effect chain in DSP is because computers have limited precision when using small floating points number. By performing effects on lower strength signal you increase distortion caused by rounding effects. Some algorhitms even go as far as oversampling the signal from float to double to increase the precision in order to minimize distortion.
Apr 28, 2014 at 2:23 PM
Next question is about this piece of code in the root plugin Class of the VSTaudioPluginTemplate
protected override IVstPluginAudioProcessor CreateAudioProcessor(IVstPluginAudioProcessor instance)
        {
            if (instance == null)
            {
                return new AudioProcessor(this);
            }

            // TODO: implement a thread-safe wrapper.
            return base.CreateAudioProcessor(instance);
        }
What means TODO here? I really need to implement something or it is enough to let it like this?

I understand it like if my AudioProcessor class is not instantiated (and I wanna do audio processing) It creates a new instance. otherwise it return the previously created instance saved in the base class... If I do not wanna support audio processing I just change the body of this method to simply return null ,,, Am I right?
Coordinator
Apr 28, 2014 at 4:55 PM
If you do not want to implement an AudioProcessor simply remove the override completely.

That TODO is because the AudioProcessor could potentially be accessed by multiple Threads. I did not want to make any assumptions on how a plugin would synchronize concurrent access to its state, so I tried to give you the opportunity to have a hook to intercept the request. The first call is made on the thread that initiated the creation of the object (AudioProcessor in this case) and the instance parameter is null. The return value is stored along with the Thread Id that created the instance. If another thread also tries to access the (AudioProcessor) reference this is detected (it a different Thread Id) and the method is called again. This time the instance parameter contains the object you returned on the first call and you now have the opportunity to wrap the instance in a synchronization wrapper - securing concurrent access to the state of the object. The other option is to write the AudioProcessor class you implement as thread-safe to begin with.

Hope it helps,
Marc
Apr 30, 2014 at 8:28 PM
Hi Guys, I am wondering how to support MIDI events inside my plugin To be able to create the ADSR envelope. I added a class MIDIproccesor which implement the IVSTMidiProcessor interface but what to do else? Could you help me understand basic concept? Do I need to think about which note I receive or for ADSR functionality it is irrelevant ? What is the best way to connect my parameters logic with MIDI logic? thx
Apr 30, 2014 at 9:54 PM
I had great success using ADSR enveloppe with this tutorial:
http://www.earlevel.com/main/2013/06/01/envelope-generators/

I was doing a poly-synth at the time so I needed to fire a new enveloppe for each note press.
The enveloppe was controlling the note attenuator (Amp) parameter.
I controlled the gate time using the NoteOn/NoteOff midi event:
// at some point, by MIDI perhaps, the envelope is gated "on"
env->gate(true);
…
// and some time later, it's gated "off"
env->gate(false);
Some effects have fixed time enveloppe triggered by sound.
When they detect sound input (often with an audio gate) they open the enveloppe gate and they close it after a fixed amount of time.
The fixed amount of time is often a parameter of the plugin.
Reverbs effects could do this to control the early resonance for instance, notice their Attack, Decay, and Time parameters.
Apr 30, 2014 at 10:14 PM
Niiiiice I got Automation running!!! I registred tothe pplugin.Opened event at the end of the AudioProcessor constructor. (Is it good place for it ?)

Next question is.
My params are defined in int like below, so this one has range 1 - 100 but Cubase seems to set values to it between 0 and 1... It causes that when I automated my knob which has same range like my param it just stay on the value 1... For my DSP calculation I aways receive the param.CurrentValue and dived it by 100 (for this case) but it is getting more complicated right now with automation and Cubase ... What do you use for this purposes?

def of my Param Threeshold for clipper
//Distortion Threshold
            paramInfo = new VstParameterInfo();
            paramInfo.Category = paramCategory;
            paramInfo.CanBeAutomated = true;
            paramInfo.Name = "DThres";
            paramInfo.Label = "Threshold";
            paramInfo.ShortLabel = "%";
            //make sure the Value is one to avoid divide by zero
            paramInfo.MinInteger = 1;
            paramInfo.MaxInteger = 100;
            paramInfo.LargeStepInteger = 10;
            paramInfo.StepInteger = 5;
            paramInfo.StepFloat = 0.1f;
            paramInfo.DefaultValue = 70.0f;
            DistThreshold = new VstParameterManager(paramInfo);
            VstParameterNormalizationInfo.AttachTo(paramInfo);

            parameterInfos.Add(paramInfo);
Apr 30, 2014 at 10:56 PM
Edited Apr 30, 2014 at 11:21 PM
YuryK wrote:
I had great success using ADSR enveloppe with this tutorial:
http://www.earlevel.com/main/2013/06/01/envelope-generators/

I was doing a poly-synth at the time so I needed to fire a new enveloppe for each note press.
The enveloppe was controlling the note attenuator (Amp) parameter.
I controlled the gate time using the NoteOn/NoteOff midi event:
// at some point, by MIDI perhaps, the envelope is gated "on"
env->gate(true);
…
// and some time later, it's gated "off"
env->gate(false);
Some effects have fixed time enveloppe triggered by sound.
When they detect sound input (often with an audio gate) they open the enveloppe gate and they close it after a fixed amount of time.
The fixed amount of time is often a parameter of the plugin.
Reverbs effects could do this to control the early resonance for instance, notice their Attack, Decay, and Time parameters.
Ok I added this class into my project and do not know how to extract the MIDI event from VSTEvents collection...
 class MidiProcessor: IVstMidiProcessor
    {

        private Plugin _plugin;

        public MidiProcessor(Plugin plugin)
            
        {

            _plugin = plugin;
        
        
        }
                        
        public int ChannelCount
        {
            get { return 2; }
        }

       
        public void Process(VstEventCollection events)
        {
            //wanna do here  something like 
            //if e== noteOn  set the parammeter Gate in _plugin.AudioProcessor.ADSR.Gate.TRUE
              VstMidiEvent e = events[1];
            

            
        }
    }
I added also into my root plugin class this¨
 protected override IVstMidiProcessor CreateMidiProcessor(IVstMidiProcessor instance)
        {
            if (instance == null)
            {
                return new MidiProcessor(this);
            }

            // TODO: implement a thread-safe wrapper.
            return base.CreateMidiProcessor(instance);

        }

        public MidiProcessor MidiProcessor
        {
            get { return GetInstance<MidiProcessor>(); }
        }
should not it be enough to reach a break point in MidiProcesor Process method when the note in the cubase is played? Because I cant reach it ...
May 1, 2014 at 2:34 AM
My params are defined in int like below, so this one has range 1 - 100 but Cubase seems to set values to it between 0 and 1...
May 1, 2014 at 2:45 AM
Ok I added this class into my project and do not know how to extract the MIDI event from VSTEvents collection...
  • Vst midi event contains an array of 4 bytes which are the standard midi event structure. You should refer to the midi specs to know which values represent which events. For channel events like note on and note off, the first byte will be the command on/off + the channel number. Usually you'll find those value in hexadecimal which are written with the "0x" prefix in c# but you can also convert them in decimal like I did here:
    https://vstnet.codeplex.com/discussions/79792
May 1, 2014 at 3:29 AM
Edited May 1, 2014 at 3:30 AM
Ok great sources I will read it carefully. but I cant start to extract MIDI events until I get my program into public void Process(VstEventCollection events) method of my MIDIProcessor: IVstMidiProcessor class... I put there a breakpoint and it is never reached... I debugged the inicialization of the plugin when is opened and It got into the

protected override IVstMidiProcessor CreateMidiProcessor(IVstMidiProcessor instance)

and there fired return new MidiProcessor(this);

so I think that my My MidiProcessor should be prepared to receive some MIDI data into Procces method... But nothing is coming :-(
May 1, 2014 at 4:27 AM
This might be because there's no clear definition of what type of plugins the host should send vst midi events to. Many hosts will require that you open the plugin as an instrument instead of an effect in their program. Some might require that you declare your plugin as an instrument instead of an effect. Most will require that you return a canReceiveMidiEvent response to a can do request. If you don't find a suitable host, an option would be to send a note-on from the sample host in vst.net, my last link has the snippet to send that event.
Coordinator
May 1, 2014 at 6:02 AM
The VST.NET framework will emit the correct capabilities to the host depending on the interfaces you have implemented. From what I can tell by your story you have done everything right. What is your host? Some hosts (Cubase) are very particular on what the will treat as a midi plugin. I have found that also implementing a dummy audio processor will sometimes help to trick the host. See the Midi VS project template for a possible implementation.

Hope it helps,
Marc
May 1, 2014 at 6:58 AM
Edited May 1, 2014 at 7:12 AM
obiwanjacobi wrote:
The VST.NET framework will emit the correct capabilities to the host depending on the interfaces you have implemented. From what I can tell by your story you have done everything right. What is your host? Some hosts (Cubase) are very particular on what the will treat as a midi plugin. I have found that also implementing a dummy audio processor will sometimes help to trick the host. See the Midi VS project template for a possible implementation.

Hope it helps,
Marc
Yes I am testing the plugin against CUbase 5
CUbase has two separate rack one for audio proccesing insert effects and one for midi insert effects...
SO you mean that the VST plugin can be either MIDI effect or audio processing effect or VSTi and that the audioprocessing effect cant receive Midi events ?

Because Cubase has different sets of effect for each of the racks and no effect is shown in both of them... But there is for instance EnvelopeShaper audioprocessing effect which I guess has to receive MIDI events to be able to work....

Will a plugin implemented with the template VSTnetmidiPlugin be shown in the Cubase inside the MIDI inserts or Audioprocessing inserts?

I will look at the Midi template tommorow right now I need some sleep 20 hours of work done today . GN :)v and thx
Coordinator
May 1, 2014 at 10:02 AM
SO you mean that the VST plugin can be either MIDI effect or audio processing effect or VSTi and that the audioprocessing effect cant receive Midi events ?
Could be.
Will a plugin implemented with the template VSTnetmidiPlugin be shown in the Cubase inside the MIDI inserts or Audioprocessing inserts?
That is very easy to test...
May 1, 2014 at 2:28 PM
Bad news guys, I tested VSTmidiPlugin in CUbase and it is loaded as Instrument so it is not able to be loaded into effect rack... Good news is that it receiving MIDI properely.

But probabely there is no way to receive MIDI by your FX effect... This sucks because I was suposed to add ADSR envelope inside my effect, I also wanted to add White noise generator but now I have no mechanism to trigger it... I could use as a trigger the volume of input signal but CUbase passes through my Plugin samles all the time and what is the worst is that there could be for example Input Microphone FX which would cause the noise generator be heard despite the fact that playback is not running...
May 3, 2014 at 5:18 AM
Edited May 3, 2014 at 5:20 AM
Guys I am playing with my clipper unit.. I wanna amplify samples before clipping. I put there 1000*sample and it is still a little.... How much usualy Clippers or distortion effects Gain a signal before waveshapping?
 public float ProcessSample(float sample)
        {
            // this comes from my knob  range 0-100 so I divide by 100 
            float _Threshold = this.DistThreshold.CurrentValue / 100;
            //here I gain
            float _sample = sample*1000;
            
           

            if (sample >= 0)
            {
                _sample = Math.Min(sample, _Threshold);

                _sample /= _Threshold;


                return _sample;
            }
            else
            {
                _sample = Math.Max(sample, -_Threshold);
                _sample /= _Threshold;
               
                return _sample;
            }

           

        }
still I have to go really low with my threeshold to hear any effect, I know that best is ask o nKVR forum but I do not have an account there at the moment and I am quite in rush with my plugin..
Jun 12, 2014 at 10:34 PM
Thanks guys I finished my plugin on time and got quite good grades for my thesis. I wrote it like a tutorial for other students so hopefuly some of them will follow my work and it will bring new developers to play around with VST.NET. I wanna really thank you for support, helpfullnes and especialy for whole VST.NET
Jun 13, 2014 at 7:43 PM
Good News, I am way off topic here but may I ask a question only to satisfy my curiosity.
Looking at your username through this whole thread I was wondering what nationality you were and if there was a meaning to Attentat31?