Processing

Previous | Next | Home

    In my experience, Process() and ProcessReplacing() are the methods least understood by Delphi programmers who want to make VST plugins. Why is this? I don't really know, but my guess is it has something to do with pointers. These two methods work with the audio data provided by the host. This data is sent to the plugin by a specific kind of parameter, which is used regularly in C/C++, but not very often in ObjectPascal. I'll explain this, but first an explanation of the difference between these two methods.

Difference
The Process method gets audio data, does something to it and then adds the result to the original data. This is done like this :
    resultsample := dosomethingwith(currentsample) + currentsample;
ProcessReplacing on the other hand doesn't care about the original sample data once it's done its work.
    resultsample := dosomethingwith(currentsample);

That's the difference between Process and ProcessReplacing. It's that simple. ProcessReplacing only gets called when you called canProcessReplacing(TRUE) in your constructor. In general, it's very easy to make a ProcessReplacing method, so there's really no reason not to.

Declaration
Now for the declaration of the two methods:
    procedure process(inputs, outputs: PPSingle; sampleframes: Longint);
    procedure processReplacing(inputs, outputs: PPSingle; sampleframes: Longint);
As you can see, they have the same parameters. Like I said, it's easy to create a processReplacing method if you already have a Process method.

Sample Values
Samples can have any value between -1.0 and +1.0. A value of 0.0 means no sound. -1.0 and +1.0 mean full sound.

Parameters
Inputs : This is a pointer to the buffers with the original samples.
Outputs : This is a pointer to the buffers where you will store the result of your processing.
SampleFrames : This is the easiest parameter. It tells you how many samples are in the Inputs and Outputs buffers.

Like I said, a lot of people have difficulty with the Inputs and Outputs parameters. Actually, it's quite simple. Inputs is a pointer to the input buffers. It points to the area in memory where you can find the pointer to the first buffer. Look at it as an array of pointers. I'll use a little drawing to make it clear.
    Inputs --> inputbuffer1 --> samples for input1
                     inputbuffer2 --> samples for input2
So provided you defined two inputs in your constructor, Inputs points to two other pointers. These last pointers point to the actual sample data.

Maybe the easiest way to look at it is like this : Inputs is an array of pointers. It has as many elements as your plugin has inputs.
    type
        PDataBuffer = ^TDataBuffer;
        TDataBuffer = array[0..NumSamples-1] of Single;
        TInputArray = array[0..NumInputs-1] of PDataBuffer;

Examples
Enough for the theory, how do you get at these buffers? It's quite simple. Look at this example (there are two inputs and two outputs in this example):
    procedure Process(inputs, outputs: PPSingle; sampleframes: longint)
    var
        input1, input2, output1, output2: PSingle;
        temp : Single;
        i : integer;
    begin
        // here we get the actual sample buffers
        input1 := inputs^;
        inc(inputs);
        input2 := inputs^;
        output1 := outputs^;
        inc(outputs);
        output2 := outputs^;
            
        // sampleframes tells you how many samples there are in the buffers

        for i := 0 to sampleframes-1 do
        begin
            temp := dosomething(input1^);                // get the current sample on the first input and process it
            output1^ := temp + output1^;                 // put the processed sample on the first output
            temp := dosomething(input2^);               // get the current sample on the second input and process it
            output2^ := temp + output2^;                 // put the processed sample on the second output 

            inc(input1);      // move on to the next sample
            inc(input2);
            inc(output1);
            inc(output2);
        end;
    end;

And this is immediately an example of a complete Process method. You can also take a look at the Process method in uExampleEffect.pas.