A Portion of Buff

Everybody else had one, so...

Keeping it real-time (2)

I had a request for more (more!!!), so I'll explain how some more of the managed plugin system works.  At the root of it, we have the VST SDK-derived class.  This is a standard C++ class which inherits from AudioEffectX (defined as part of the SDK).  Any VST host will expect an instance of this type, and will call certain methods to both interrogate your plugin about its capabilities and pass it the magical float** inputs and outputs.  (I remember reading somewhere about three star C programmers, and how they were a breed unto themselves.  We only have two stars here, but as .Net programmers, I think we should allow ourselves a smug grin).

As Managed C++ classes cannot inherit from unmanaged classes, our root plugin must be unmanaged.  However, unmanaged classes can have references to managed classes, via the gcroot<> template (there may be other ways - I have not discovered them yet).  A VST host will typically want to know how many inputs and outputs a plugin can handle, whether it can be a parallel ("send") or serial ("insert") effect, whether it has its own GUI for editing parameters, etc.  My goal is to delegate as much as possible to C#, so most of these calls must be marshalled directly to managed classes.  The good news is that MC++ makes this marshalling really fricken easy.  Anyone who's done much P/Invoke will know how fiddly all those MarshalAs attributes can be to get right.  Well, most of the time, with MC++, It Just Works.  You have to make a few decisions about which side owns which piece of memory, but I'm 90% convinced that doing interop from the unmanaged side is just easier.  Although maybe that's just the pointers talking.  An example:


void VstToManaged::process (float **inputs, float **outputs, long sampleFrames)
{
    m_pPluginBridge->Process(inputs, outputs, sampleFrames);
}


That takes us from an unmanaged, VST 0wn3d method to MC++.  No mucking about with DllImport, just include the header file.

Did I just say that? 


Anyway, taking the process() method further, I decided that most C# weenies wouldn't know what to do with two levels of indirection, so I dereferenced the left and right channels and created an interface thusly:


public interface IVstPlugin
{
    unsafe void Process(float* inLeft, float* inRight, float* outLeft, float* outRight, int sampleFrames);
}

There's more on this interface than that, but I'll keep it simple for now.  The code from an MC++ point of view?

void ManagedBridge::Process(float **inputs, float **outputs, long sampleFrames)
{
    float *in1  =  inputs[0];
    float *in2  =  inputs[1];
    float *out1 = outputs[0];
    float *out2 = outputs[1];

    plugin->Process(in1, in2, out1, out2, sampleFrames);
}

Interop schminterop.  And, as I'm feeling philanthropic, here's a mono Biquad filter in C#.  Feed it a stream of samples and see what comes out:

using System;
namespace ManagedVst.Processes
{
    public enum FilterType : byte
    {
        LPF,
        HPF,
    }

    //Ported and adapted from Tom St Denis' C version (http://www.musicdsp.org/showone.php?id=64)
    public struct BiQuad
    {
        //Coefficients.  Don't ask me what they do.
        double A0, A1, A2, A3, A4;

        //X1 is the input from the previous step, X2 is the input before that
        //Y1 is the output from the previous step, Y2 is the output before that
        double X1, X2, Y1, Y2;

        public void Init(FilterType filterType, double sampleRate, double frequency, double bandwidth)
        {
            //A frequency of 0 can lead to the filter producing
            //very small (denormal) numbers and NaNs.  This is bad because
            //most processors are very slow at calculating very small floating point numbers, so we should
            //clamp such values to something larger.  This might be slightly superstitious, but including
            //the following check banished 100% CPU usage spikes.  Alternatives welcome (double.Epsilon didn't
            //
appear to work).
            if(frequency == 0d)
            {
                frequency = 0.00000000000001d;
            }

            const double LogE2 = 0.69314718055994530942;

            double omega = 2 * Math.PI * frequency / sampleRate;
            double sinOmega = Math.Sin(omega);
            double cosOmega = Math.Cos(omega);
            double alpha = sinOmega * Math.Sinh(LogE2 / 2 * bandwidth * omega / sinOmega);
                               
            double a0 = 1 + alpha;
            double a1 = -2 * cosOmega;
            double a2 = 1 - alpha;

            double b0 = 0;
            double b1 = 0;
            double b2 = 0;

            if (filterType == FilterType.LPF)
            {
                b0 = (1 - cosOmega) / 2;
                b1 = 1 - cosOmega;
                b2 = (1 - cosOmega) / 2;
            }
            else //filterType == FilterType.HPF
            {
                b0 = (1 + cosOmega) / 2;
                b1 = -(1 + cosOmega);
                b2 = (1 + cosOmega) / 2;
            }

            A0 = b0 / a0;
            A1 = b1 / a0;
            A2 = b2 / a0;
            A3 = a1 / a0;
            A4 = a2 / a0;
        }

        public float Step(float input)
        {
            double output = A0 * input + A1 * X1 + A2 * X2 - A3 * Y1 - A4 * Y2;

            X2 = X1;
            X1 = input;

            Y2 = Y1;
            Y1 = output;

            return (float)output;
        }
    }
}

Comments

Lee Henson said:

Good work, hombre. The icing on the cake would be some kind of c# VSTGUI......

Is it possible for you to post zip files of these projects for download?
# March 15, 2005 10:22 PM

Jim Arnold said:

A project should be available very soon (I have basic Gui support too).

Jim
# March 16, 2005 6:11 PM

ChristianJames said:

When your purpose is to get high grades, you will have to use some professional <a href="www.qualityessay.com/essay-editing-service.html">essay editing service</a>.

# October 12, 2010 5:08 AM

custom essay said:

Every man has got hard moments. Nevertheless, no one wants waste his cash and buy essay but there's no other way to relieve the writing confusion.

# December 8, 2010 12:40 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)