in

ASP.NET Weblogs

A Portion of Buff

Everybody else had one, so...

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.

Comments

 

Chris Martin said:

More. More!!!
March 15, 2005 2:09 AM
 

Keeping it real-time said:

Pingback from  Keeping it real-time

November 26, 2007 2:59 AM

Leave a Comment

(required)  
(optional)
(required)  
Add