Help with simple vst. Channel issue?


#1

Hi, this is my first post. I’m pretty new to JUCE, but I at least thought I was more comfortable with programming and c++ to have this much trouble. I am learning to write VST plugins.

I am writing a simple delay effect, in order to learn, stepping up the complexity a bit at a time. I managed to get a delay-effect working (without anything user-changable, and pretty much all parameters hard-coded), once i finally figured out that i was sending data from both channels to the same place.

Now i have started from scratch, using my own class for processing the delay-effect instead. The problem is that I get the same noise or distortion as when i was sending both channels through the same processing unit, but I really don’t think I am doing that still. I suspect I am doing something obviously wrong (since it is still really simple), so any pointers is very appreciated.

This is the code that is added to the processBlock. First the delay and then a volume-control.

for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
    float* channelData = buffer.getWritePointer (channel);
	delayBlock [channel].process(channelData, buffer.getNumSamples());

	for(int sample = 0; sample < buffer.getNumSamples(); ++sample)
	{
		channelData[sample] = channelData[sample] * outGainFactor;
	}
}

This is the class i wrote for the delay processing.:

DelayModule::DelayModule()
{
    for (int i = 0; i < bufferSize; ++i)
    {
	delayBuffer [i] = 0;
    }
    readHead = 100;
    writeHead = 0;
}

void DelayModule::process (float* channelData, int numSamples)
{
    for (int sample = 0; sample < numSamples; ++sample) //write samples to buffer
    {
	delayBuffer [writeHead] = channelData[sample];
	writeHead = (++writeHead) % bufferSize;
    }

    for (int sample = 0; sample < numSamples; ++sample) //write processed audio back to channel
    {
	channelData[sample] = channelData [sample] + delayBuffer[readHead];
	readHead = (++readHead) % bufferSize;
    }
}

Thanks!


#2

What is in the buffer?
float* channelData = buffer.getWritePointer (channel) will give the first bytes in the buffer every time. Maybe you should advance the channelData ptr numSamples for every processBlock call? Or perhaps that’s been taken care of outside your code excerpts.

Anyway the best thing you could do is, I think, to test volume and reverb separately. Another trick that’s sometimes useful is to set the audio buffer size in your audio device to a ridiculously high value, This could make it easier to discriminate between faulty buffer handling and just bad algorithms…


#3

a) I hope your buffersize is big enough, remember to get 1 sec delay you need “samplerate” samples, e.g. 48000
b) your delayBuffer variable is member of DelayModule, so it exists for each channel separately, right? (if not: oops, youre mixing blocks from each channel after each other, leaving a mess)
c) in the first run you already crash your writehead into the readhead: write from 0 to 512 (e.g.) and then reading from 100 to 612, so the last 100 samples read are leftovers from previous round

Now some hints for performance:
don’t iterate just for simple things like multiplication, instead use AudioBuffer::applyGain() like:

buffer.applyGain (channel, 0, buffer.getNumSamples(), outGainFactor);

or in your case even outside the loop:

buffer.applyGain (outputGain);

And what I do is saving the outputGain from last round, so I don’t get any jumps in the gain:

if (outputGain != lastOutputGain) {
    for (int channel = 0; channel < totalNumInputChannels; ++channel)
        buffer.applyGainRamp (channel, 0,  buffer.getNumSamples(), lastOutputGain, outputGain);
    lastOutputGain = outputGain;
}
else {
    buffer.applyGain (outputGain);
}

Make it your personal challenge to use as many vector operations as you can…
Here you find them all: FloatVectorOperations. The background is, if you loop each sample takes one cpu instruction (if the optimizer is not helping you), but the vector operations use a processor extension called SIMD, so they are much faster.

And for a delay, check out the AbstractFifo, it’s awesome. No more index problems, no modulo per each sample (also a lot cpu waste…)
Simply exchange the “copySomeData” with buffer.copyFrom and done, ok I’ll copy from my project:

void addToFifo (const AudioSampleBuffer& samples, int numSamples=-1)
{
    const int addSamples = numSamples < 0 ? samples.getNumSamples() : numSamples;
    jassert (getFreeSpace() > addSamples);

    int start1, size1, start2, size2;
    prepareToWrite (addSamples, start1, size1, start2, size2);
    if (size1 > 0)
        for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
            buffer.copyFrom (channel, start1, samples.getReadPointer (channel), size1);
    if (size2 > 0)
        for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
            buffer.copyFrom (channel, start2, samples.getReadPointer (channel, size1), size2);
    finishedWrite (size1 + size2);
}

void readFromFifo (AudioSampleBuffer& samples, int numSamples=-1)
{
    const int readSamples = numSamples > 0 ? numSamples : samples.getNumSamples();
    jassert (getNumReady() >= readSamples);

    int start1, size1, start2, size2;
    prepareToRead (readSamples, start1, size1, start2, size2);
    if (size1 > 0)
        for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
            samples.copyFrom (channel, 0, buffer.getReadPointer (channel, start1), size1);
    if (size2 > 0)
        for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
            samples.copyFrom (channel, size1, buffer.getReadPointer (channel, start2), size2);
    finishedRead (size1 + size2);
}

#4

Thanks a lot for the help! The channelData pointer is taken care of outside of the posted code. I have taken into account the samplerate for the length of the delay, and the delayBuffer is a member of DelayModule, so that isn’t the problem either.

Running the writeHead past the readHead was. Thanks! It works as intended now.

I have been trying to deliberately ignore optimization so far, since i can barely get it to function, but that should be very useful/helpful in a while. I appreciate it!