My approaches to develop a decent wavetable engine

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

Hi to all!
I write this post in order to share some concepts I learnt for optimizing my sampling engine and to get more ideas about things I still don't know.

My engine is developed by enhancing the Simple Sampler example from Christian Budde VST Framework.

Everything starts in the main ProcessReplacing call.

Firstly I zeroed the output buffers with FillChar function.
Then I start two nested loops

while VoiceIndex < Voices.Count do

while SampleIndex<SampleFrames do

The first is a counter for the Voices that are going to be processed, while the second is the usual OutputBuffer processor.

Then I start the Voice.Process function that returns a two values array that is going to be summed on the Output array. In this way all the current Voices will be summed and the loop will finish.

At the end of this big loop I run various little routines like Voice Killing, if the voice is not playing anymore. (and others will be added when my sampler will grown in complexity).

The Voice object has this so called Process function.
This function has a big CASE statement. A BRANCH!!!
Uhm yes, because I didn't know how to avoid that :-D
But this shouldn't be a problem since the program flow will keep the same path in the whole voice life.

I did this because I needed to pass between interpolation methods and Lowpass filtering depending of the note that I will playing. In relation to the sample rate of the original file. Now if the sample is going to be played at a slow speed the interpolation has a greater importance. When the sample is going to be played ad an higher speed the interpolation lessen its importance and at extreme levels, when it is going to be played at 8x or 16x of its speed the Lowpass is needed to avoid aliasing.

The CASE statements has different paths for the different cases, beginning with a cubic interpolation, linear interpolation+littlelowpass, linearinterpolation+mediumlowpass, nointerpolation+strong lowpass.

Since I'm not a math guy I used the simple lowpassing technique of: (sample1+sample2)/2 for halving the samplerate of the signal. I don't know if it is a valid method but is works and it should be lightweight CPU wise.

Since the process function needs a truncation mode I used a single Asm64 instruction to do this, that it's faster than the delphi trunc function.

Code: Select all

function FastTrunc(FloatNumber: Single) : integer;

  asm
    CVTSS2SI EAX,xmm0
  end;
There's a comparable instruction in asm 32bit but I don't remember the name now.

There's an ADSR filter that I implemented by calculating the values in a table. So at processing time the ADSR influence is just a multiplication for the value in the table. I could go for linear interpolating the values to save memory, but I'm still checking it.

As usual the Sampler has a mapping feature, that will choose the correct sample when playing the note.

I'm very esigent about CPU consuming and since Delphi is not helping me to achieve the spectacular results I'm looking to find tricks to enhance performance.

One could be to filter all the voices in the same filtering case at once. I don't think that it will work with interpolation but I'm quite confident that it can work for lowpass filtering. I just need to review the engine structure.

I also unrolled some loops in the main ProcessReplacing.

Post

paoling wrote: Since I'm not a math guy I used the simple lowpassing technique of: (sample1+sample2)/2 for halving the samplerate of the signal. I don't know if it is a valid method but is works and it should be lightweight CPU wise.
That's called a moving average filter btw. I've read somewhere that you could add feedback to it to improve it a bit.

Just my 2cents.

Regards
Andrew

Post

Thank you so much, in programming and maths it's sometimes useful just to know how to name something that you realize by intuition. Now I'll have some other interesting things to read :-)

Post

Hey! Ok... this thread is kind of old but very actual for me....
I am also coding a simple sampler for Android, it has also an "host" part that feeds in the events, but the main idea is exactely as yours (with some less features).
I think that the tricky part would be the "Voice - Render" function, which is the function called mostly often. Some little tricks I have applied (naive but... better then nothing ) :
- take out from the buffer loop all the loop-invariant instructions (this helped a lot since I was also casting a lot)
-put the most often case first in your switch statement. This will save some time.
-compile with optimization flags
-try to reverse the buffer scan from the end to the begin (for i = BUFFER_SIZE; i >= 0; i++) instead of cycling forward

Another technique I am trying (but did not succeed yet) is avoiding the buffer reset at the begin of the "Buffer Render" function, duplicating all the writes to the buffer like the following:

before:

for (i..... i < BUFFER_SIZE) {
outbuffer = 0;
}

while (voices != NULL){
renderVoice();

for (i = 0; i ... ; i < BUFFER_LENGTH, i++)
outbuffer = voice_buffer + outputbuffer

}
******************************************************

optimized:

//NO NEED TO RESET THE BUFFER
bool first_voice = TRUE;
while (voices != NULL){
renderVoice();

for (i = 0; i ... ; i < BUFFER_LENGTH, i++){
if (first_voice) outbuffer = voice_buffer;
else outbuffer = oubuffer + voice_buffer;
first_voice = FALSE;
}
}

However... I am not yet done with this optimization so there might be something wrong in it.
By using the mentioned methods I managed to obtain more or less X2 voices without clipping.

Any further reccomandation?

Post

for (i = 0; i ... ; i < BUFFER_LENGTH, i++){
if (first_voice) outbuffer = voice_buffer;
else outbuffer = oubuffer + voice_buffer;
first_voice = FALSE;
}
}


first_voice is true only for the first loop iteration?

Post

yes, basically the output buffer is never cleaned up explitely... but via first_voice check....not sure it's a real optimization though (the mixbuffer clean up should cost more or less like a single voice)...

Post Reply

Return to “DSP and Plugin Development”