A Portion of Buff

Everybody else had one, so...

  • Noise 0.3 released

    I've put a new cut of Noise, the premier solution for C# VST developers (ahem), up on GForge.  The changelog:

    * Finished implementing v1.0 of the SDK _apart from_ setChunk()/getChunk() because I don't know how best to handle them yet.  It would
    be nice to do something a bit smarter than a byte stream.  Suggestions welcome.

    * Refactored ManagedBridge a little bit so it directly returns values instead of using out params.

    * Added very basic error handling to HostPlugin - if your plugin fails to initialise properly, it will report nicely back to the host.

    * Added an example of a compressor.  Doesn't work properly yet, but it _sort of_ sounds compressor-like.

    * You can now provide your own unique ID to the host (see CompressorPlugin for an example).

    * Made some of the process classes mono, which makes them a bit easier to understand.

    Read more...

  • The subdued garbage collector

    I want Noise to support any .Net language, not just C# (although why anyone would use another language...).  At the moment, the plugin interface exposes unsafe methods - Process and ProcessReplacing - which take float* parameters and are therefore unusable by a pussy language like VB.Net.  Pointers are used because it allows us to write directly to the host's memory instead of allocating our own buffers and copying them back to the host.

    If we are prepared to take a performance hit, we can make this interface safe.  Not only is there the cost of copying unmanaged memory to a managed buffer on the way in, and then back again on the way out, we need to allocate some managed memory in the first place.  Allocations lead to collections, collections lead to hate, hate leads to suffering.

    However, we can take advantage of the fact that the maximum number of samples being passed to the plugin rarely changes (usually not until the host's latency setting has been changed, which is hardly ever under normal use), although the host is free to send fewer samples if it wishes.  The host tells us the maximum block size by calling the SetBlockSize() method, which is a good place to allocate our buffers:


    public override void SetBlockSize(int blockSize)
    {
        leftBufferIn = new float[blockSize];
        rightBufferIn = new float[blockSize];
        leftBufferOut = new float[blockSize];
        rightBufferOut = new float[blockSize];
    }


    and in the Process or ProcessReplacing methods, we can just fill these buffers using the Marshal class:

    public override void ProcessReplacing(float* inLeft, float* inRight, float* outLeft, float* outRight, int sampleFrames)
    {
        Marshal.Copy(new IntPtr(inLeft), leftBufferIn, 0, sampleFrames);
        Marshal.Copy(new IntPtr(inRight), rightBufferIn, 0, sampleFrames);
        Marshal.Copy(new IntPtr(outLeft), leftBufferOut, 0, sampleFrames);
        Marshal.Copy(new IntPtr(outRight), rightBufferOut, 0, sampleFrames);
    //...
    }


    It doesn't matter if sampleFrames is less than blockSize, we just copy that many samples to our buffers.  This enables us to call out to a safe method which deals with float arrays, and performance is constant because no new allocations occur.  Hurrah.

    Read more...

  • Noise 0.2 source released

    Now installs to the GAC (this was the only way I could let hosts load a plugin from an arbitrary location.  Sometimes assembly-resolution sucks).  Added a reverb plugin to the package, and generally fixed up the solution.  To get up and running:

    1) Download and extract noise-source-0.2.zip somewhere.
    2) Download the VST Plugin SDK from http://www.steinberg.net/Steinberg/Developers.asp
    3) Copy everything from vstsdk2.3\source\common into Noise\HostPlugin\vst
    4) Build the solution
    5) Open project properties for Noise.ManagedVst, go to Configuration Properties-->Debugging
    6) Change Debug Mode to "Program"
    7) Change "Start Application" to point to VstHost.exe (under Noise\ by default)
    8) Hit [ctrl +] F5
    9) Load up your favourite synth plugin, then chain HostPlugin.dll after it (deployed in Noise\build).
    10) Enjoy


    Read more...

  • Keeping it real-time (3)

    These late nights are going to catch up with me, but until then...

    I've kicked off Noise, an open source project for this managed VST plugin thing I'm working on:

    (Last year I was working on an abortive software synthesizer called "Noise!", so it seemed appropriate to recycle the name).

    It's obviously quite rough at the moment, but please, if you use any VST-compatible audio software (Cubase, SoundForge, FruityLoops etc), download it, give it a try, and let me know if you had any problems (positive feedback is nice too).  If you don't have a VST host, there's a free one included, so you can still play around if you're interested. 

    So far I've got delay, lo/hi-pass filter, gain and reverb plug-ins - the latter being the most fun to do and the most difficult to get my head around.  One thing that struck me tonight was just how incredibly fast computers are.  I know I should be accustomed to that, being a developer, but when you're writing code which applies 12 different filters to 44,100 stereo samples per second, in parallel to a software synthesizer which is generating those 44,100 stereo samples per second with its own oscillators, filters and envelopes, on top of a host application routing all these samples from the synth, through your plugin (via layers of C, Managed C++ and C#) and out to a sound card, and it only takes 3% of available CPU time, it kind of brings it home.

    Here's a stereo comb filter:


    public class CombFilter
    {
        float feedback;

        float damp1;
        float damp2;

        float lastLeftValue;
        float lastRightValue;

        int leftIndex;
        int rightIndex;

        float[] leftBuffer;
        float[] rightBuffer;

        public CombFilter(int leftTuning, int rightTuning, float damp, float feedback)
        {
            this.feedback = feedback;
            this.damp1 = damp;
            this.damp2 = (1 - damp);
            this.leftBuffer = new float[leftTuning];
            this.rightBuffer = new float[rightTuning];
        }

        public float ProcessLeft(float input)
        {
            lastLeftValue = (leftBuffer[leftIndex] * damp2) + (lastLeftValue * damp1);

            leftBuffer[leftIndex] = input + (lastLeftValue * feedback);

            if (++leftIndex >= leftBuffer.Length)
            {
                leftIndex = 0;
            }

            return leftBuffer[leftIndex];
        }

        public float ProcessRight(float input)
        {
            lastRightValue = (rightBuffer[rightIndex] * damp2) + (lastRightValue * damp1);

            rightBuffer[rightIndex] = input + (lastRightValue * feedback);

            if (++rightIndex >= rightBuffer.Length)
            {
                rightIndex = 0;
            }

            return rightBuffer[rightIndex];
        }
    }

    Read more...

  • 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;
            }
        }
    }

    Read more...

  • Keeping it real-time

    Since we last spoke, I have realised that a managed VST plug-in is a far from practical application for hosting the CLR.  Which means no more COM haxx0ring.  Shame.

    On the upside, I have discovered the twin joys of Managed C++ and porting DSP code from C to C#.  MC++, after several failed attempts, now shuttles calls from the VST host through to C#.  The CLR is of course loaded automatically whenever the managed DLL is touched, so hosting it explicitly is pointless. 

    It is splendid fun.  At the moment, much of the real-time processing is done with unsafe code (the VST host passes in pointers to the input and output buffers), but it’s nicer than working in C.  Here’s a simple delay:

    public class Delay
    {
          float samplingRate;
          float[] delayLine;
          int delayLineIndex;
          int bufferLength;
          private float delayTime;
          private float feedback;
          private float dryLevel;
          private float wetLevel; 
          private const int MAX_DELAY_TIME = 1000; 

         
    public Delay(float delayTime, float feedBack, float dryLevel, float wetLevel, float samplingRate)
          {
                this.delayTime = delayTime;
                this.feedback = feedBack;
                this.dryLevel = dryLevel;
                this.wetLevel = wetLevel;
                this.samplingRate = samplingRate; 

               
    int maxBufferLength = CalculateBufferLength(MAX_DELAY_TIME); 
                delayLine = new float[maxBufferLength]; 
                bufferLength = CalculateBufferLength(delayTime); 
                this.delayLineIndex = 0;
          }

          unsafe public void Process(float* input, float* output, int sampleFrames)
          {
                for (int i = 0; i < sampleFrames; i++)
                {
                      output[i] += (dryLevel * input[i]) + (wetLevel * delayLine[delayLineIndex]); 
                      delayLine[delayLineIndex] = input[i] + (feedback * delayLine[delayLineIndex]);

                      if (++delayLineIndex >= bufferLength)
                      {
                            delayLineIndex = 0;
                      }
                }
          }

          public float DelayTime
          {
                get { return delayTime; }
                set
                {
                      this.delayTime = value;
                      this.bufferLength = CalculateBufferLength(value);    
                }
          }

          public float Feedback
          {
                get { return feedback; }
                set { feedback = value; }
          } 

         
    public float DryLevel
          {
                get { return dryLevel; }
                set { dryLevel = value; }
          } 

         
    public float WetLevel
          {
                get { return wetLevel; }
                set { wetLevel = value; }
          } 

         
    private int CalculateBufferLength(float delayTime)
          {
                return (int)(delayTime * samplingRate / 1000);
          }
    }

    Performance hasn’t been an issue so far, but I haven’t tried to do anything particularly taxing.  The real test will come when I start creating garbage, although I think Gen 0 collections will be tolerable.

    Read more...

  • Practical applications for CLR hosting

    Well then.  It's been a long time since I last got cozy with the unmanaged side of the CLR, but all the interop I have been doing lately has made me cocky.  This post is a write-up of my attempts to develop a VST plugin in C#.

    A what?

    VST stands for
    Virtual Studio Technology.  It's a standard developed by Steinberg for producing audio software components.  The idea is that a VST host application (Cubase, Logic Audio, ACID etc) acts as a software mixing desk, routing audio in and out of these plugins.  A plugin can be an effect, such as reverb or chorus, which changes the sound fed into them in some way; or an instrument which produces sounds of its own.

    Steinberg provides an SDK which developers can use to make plugins but it only provides a C++ API.  This is understandable as plugins need to be fast and work in realtime, and any long pauses could cause glitches in an audio stream or throw timing off.  In addition, C++ is the de facto standard for writing audio software.  On the other hand, as we have seen with Managed DirectX, it is possible to write performance-sensitive software in managed code as long as an effort is made to reduce the impact of garbage-collection and other overhead.

    VST plugins are loaded into the host application as native DLLs, so I needed some way to call out into managed code from that point.  There are a few options, but I took the route of hosting the CLR and calling into managed code with COM interop: partly because it sounds cool, and partly because it was the first thing that worked.

    I’m not going to explain how to use the VST SDK – there are plenty of tutorials out there (this is an excellent one).  This post is about hosting the CLR and calling into managed code, and assumes you can stumble your way through C++ COM code.

    Plugins inherit from a base class provided in the SDK called AudioEffectX. The constructor would seem to be an appropriate place to start up the CLR, because it is called before any performance-critical processing takes place.  The code to do this is well-documented (here, for starters), but looks something like this:

    ICorRuntimeHost *pRuntimeHost = NULL;

    CorBindToRuntimeEx(

                NULL,

                NULL,      

                0,

                CLSID_CorRuntimeHost,

                IID_ICorRuntimeHost,

                (LPVOID*) &pRuntimeHost);

    pRuntimeHost->Start();

     
    On its own, this won’t get us very far.  We need some managed code to run!  Getting a reference to the default AppDomain is easy enough:
     

    IUnknown   *punk = NULL;

    pRuntimeHost->GetDefaultDomain(&punk); 

    _AppDomain *pDefaultDomain = NULL;

    punk->QueryInterface(__uuidof(_AppDomain), (PVOID*) &pDefaultDomain);
     

    The _AppDomain interface is, essentially, identical to the managed AppDomain class which means we use it to load assemblies, create objects, create new AppDomains and all that good stuff.  My managed code is in an assembly called “ManagedVst”, and I want to create an object of type “PluginAdapter” in the “ManagedVst” namespace:

    _ObjectHandle *pObjHandle = NULL;

    pDefaultDomain->CreateInstance(_bstr_t("ManagedVst"), _bstr_t("ManagedVst.PluginAdapter"), &pObjHandle);

    Now we need to do some tedious unwrapping of all the COM layers around this object.  Eventually we end up with a pointer to an interface matching that of the managed class (I defined this interface explicitly, but you can get the framework to create one for you).
     

    VARIANT v;

    VariantInit(&v);

    pObjHandle->Unwrap(&v);

    IPluginAdapter *pPluginAdapter;

    v.pdispVal->QueryInterface(__uuidof(IPluginAdapter), (void**) &pPluginAdapter);

     
    (Now you see why I want to write this plugin in C#?)  So, finally, we have a pathway to a custom managed object which we can use and abuse at will.  Next time, I’ll show how to marshal calls between the VST host and the managed plugin.

    Read more...

  • Eat Static

    I answered a question in a C# forum the other day from someone looking to do runtime subclassing, similar to NMock.  The question went something like this:

    Read more...

  • How big are your privates?

    There has recently been, and will continue to be, increased noise about Team System and its assorted components.  The most widely discussed seems to be the unit testing piece, which is encouraging on one level because it means people are interested in unit testing.  I have yet to see anyone ask the question "why?", however, as we already have several perfectly good testing frameworks (one of which VSTS blatently clones), with perfectly good (better than VSTS, even) integration with Visual Studio.

    Read more...