VST Stereo Chorus for guitar crashes on mono input (OS X, Cubase 8)


#1

I’ve made a simple Colorizer GUI App (I talked about it in a previous post) and now I’m making the VST and AU version.
I use OS X and Cubase 8 Element as host.
When I apply the pugin to a stereo instrument or audio track, it sound good (no noise instead of the app version that is fuzzy), but in I use a mono audio track, the plugin crashes.

Here is the code inside procesBlock() function:

void GuitarColorizerAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
if (getTotalNumInputChannels() == 1) buffer.copyFrom(1, 0, buffer, 0, 0, buffer.getNumSamples());

for (int i = 0; i < buffer.getNumSamples(); i++)
{
    float leftMod = (leftOsc.nextSample() + 1.01) * modParam * 100;
    float rightMod = (leftOsc.nextSample() + 1.01) * modParam * 100;
    
    leftDelayTime = (delayParam * 200) + leftMod + .002;
    rightDelayTime = (delayParam * 220) + rightMod + .0015;
    
    float l_xn = buffer.getReadPointer(0)[i];
    float r_xn = buffer.getReadPointer(1)[i];
    
    float l_combined;
    float r_combined;
    float l_yn;
    float r_yn;
    
    l_yn = leftBuffer.getSample(leftDelayTime);
    r_yn = rightBuffer.getSample(rightDelayTime);
    
    l_combined = l_xn + r_yn * feedbackParam;
    r_combined = r_xn + l_yn * feedbackParam;
    
    leftBuffer.addSample(l_combined);
    rightBuffer.addSample(r_combined);
    
    buffer.getWritePointer(0)[i]  = (l_xn * (1 - mixParam) + l_yn * mixParam) * level;
    buffer.getWritePointer(1)[i]  = (r_xn * (1 - mixParam) + r_yn * mixParam) * level;
}

}

That’s all based on a Stereo Chorus plugin that I found in gitHub

the “getTotalInputChannels()” returns 1 anyway…

Any idea?

PS I’ve tried also with addFrom() buffer…

the error is here:

jassert (isPositiveAndBelow (destChannel, numChannels));

in the AudioSampleBuffer class


#2

A mono instance will only have one channel in buffer, so buffer.getReadPointer(1) will return a nullptr (and trigger said assertion) resulting in the crash you see.


#3

This is the first thing that is wrong:

if (getTotalNumInputChannels() == 1) buffer.copyFrom(1, 0, buffer, 0, 0, buffer.getNumSamples());

The audio buffer you receive, buffer, has a single channel of audio, so attempting to copy data into a non-existent channel is going to cause trouble!


#4

The most important lesson to learn here is not how to fix the mistake in this specific piece of code, but the fact that when you hit an assertion, it’s whole purpose is to help you see what you’ve done wrong!


#5

Thanks, I understood.
I’ve correct the code to have a single output and double (not stereo) output, but the sound is bad…
and if I use a stereo input, i obviously ear only one channel…

Here is my new code:

void GuitarColorizerAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
//if (getTotalNumInputChannels() == 1) buffer.addFrom(1, 0, buffer, 0, 0, buffer.getNumSamples());

for (int i = 0; i < buffer.getNumSamples(); i++)
{        
    float leftMod = (leftOsc.nextSample() + 1.01) * modParam * 100;
    float rightMod = (leftOsc.nextSample() + 1.01) * modParam * 100;
    
    leftDelayTime = (delayParam * 200) + leftMod + .002;
    rightDelayTime = (delayParam * 220) + rightMod + .0015;
    
    float l_xn = buffer.getReadPointer(0)[i];
    //float r_xn = buffer.getReadPointer(1)[i];
    
    float l_combined;
    //float r_combined;
    float l_yn;
    float r_yn;
    
    l_yn = leftBuffer.getSample(leftDelayTime);
    r_yn = rightBuffer.getSample(rightDelayTime);
    
    l_combined = l_xn + r_yn * feedbackParam;
    //r_combined = r_xn + l_yn * feedbackParam;
    
    leftBuffer.addSample(l_combined);
    //rightBuffer.addSample(r_combined);
    
    buffer.getWritePointer(0)[i]  = (l_xn * (1 - mixParam) + l_yn * mixParam) * level;
    //buffer.getWritePointer(1)[i]  = (r_xn * (1 - mixParam) + r_yn * mixParam) * level;
}

}

But I’m thinking… is there a method to use one setReadPointer to two separated setWritePointers?


#6

sure there is. Instead of calling getReadPointer twice inside the loop do at the beginning:

void GuitarColorizerAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    const float* l_read = buffer.getReadPointer (0);
    const float* r_read = buffer.getReadPointer (getTotalNumInputChannels() < 2 ? 0 : 1);
    for (int i = 0; i < buffer.getNumSamples(); i++)
    {
        // etc

        l_xn = l_read [i];
        r_xn = r_read [i];

        // etc

…but try to avoid all these variables inside the loop, even though modern optimisers might save your bacon :wink:

Good luck

EDIT: forgot the const for the read pointers, just added them now… sorry


#7

Thanks Daniel, I’ll try now your solution!


#8

Ok, I’ve tried o create the second pointer with the if clause but if I toggle the comments, it crashes in an assert with (destChannels, numChannels);
I have to re-comment the previous comments line?
I don’t understand why i can’t have a mono input and a stereo output…
Should I change in the Processor constructor from .input ::stereo() to .input ::mono() ?
Could be is this a solution?


#9

Instead of accessing the buffers directly, you store the adress of the left and the right sample array:

const float* l_read = buffer.getReadPointer (0);
const float* r_read = buffer.getReadPointer (getTotalNumInputChannels() < 2 ? 0 : 1);

And the conditional in the right read pointer says in case of mono to read also from the left channel. No need to copy, except if your algorithm is destructive, then you read the modified samples on the second channel…

I do the copy and paste it into your code, maybe that makes it clearer. Note I didn’t try the rest of your code, it’s just about how to acquire the read adresses…

void GuitarColorizerAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    const float* l_read = buffer.getReadPointer (0);
    const float* r_read = buffer.getReadPointer (getTotalNumInputChannels() < 2 ? 0 : 1);

    for (int i = 0; i < buffer.getNumSamples(); i++)
    {        
        float leftMod = (leftOsc.nextSample() + 1.01) * modParam * 100;
        float rightMod = (leftOsc.nextSample() + 1.01) * modParam * 100;

        leftDelayTime = (delayParam * 200) + leftMod + .002;
        rightDelayTime = (delayParam * 220) + rightMod + .0015;

        l_xn = l_read [i];
        r_xn = r_read [i];

        float l_combined;
        //float r_combined;
        float l_yn;
        float r_yn;

        l_yn = leftBuffer.getSample(leftDelayTime);
        r_yn = rightBuffer.getSample(rightDelayTime);

        l_combined = l_xn + r_yn * feedbackParam;
        //r_combined = r_xn + l_yn * feedbackParam;

        leftBuffer.addSample(l_combined);
        //rightBuffer.addSample(r_combined);

        buffer.getWritePointer(0)[i]  = (l_xn * (1 - mixParam) + l_yn * mixParam) * level;
        //buffer.getWritePointer(1)[i]  = (r_xn * (1 - mixParam) + r_yn * mixParam) * level;
    }
}

#10

I’ve not tested yet but it seems to be a mono chorus (the output buffer is only one).
I need to maintain a stereo output even with a mono input…
I’ve tried to setup Introducer with {1, 2} but it crashes anyway.