A Portion of Buff

Everybody else had one, so...

April 2005 - Posts

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.

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.
More Posts