Prevent click when changing latency with setLatencySamples()

Whenever I decrease plugin latency with setLatencySamples() I hear a click. I have to change the latency whenever my plugin’s pre-open parameter is changed. I’d like to remove those clicks to make the plugin feel professional.

I digged deeper and made some tests.

void MyAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
    int numOfSamples = buffer.getNumSamples();
    AudioBuffer<float> mainOutputBuffer = getBusBuffer(buffer, false, 0);
    float **mainOutputData = mainOutputBuffer.getArrayOfWritePointers();
    float *mainOutput0 = mainOutputData[0];
    float *mainOutput1 = mainOutputData[1];

    for (int i = 0; i < numOfSamples; i++)
    {
        mainOutput0[i] = 0;
        mainOutput1[i] = 0;
    }
}

Above code just writes zeroes to out buffer. But when I call setLatencySamples() to make latency lower (doesn’t happen when latency is increased), some samples of the input buffer are leaked to output and you can hear a short click. The latency click sound has the same tone and frequency as the input audio so I know it’s containing input samples.

Is there a way to mute all output when changing latency?

It depends on the host how it handles changing latencies from plugins during playback. Some won’t even handle it at all during playback. What host are you testing with?

You may need to resort to a solution where you report a static latency for the host that is the maximum you will need to have and then do internal buffering in your plugin.

I hope you will see my reply as constructive feedback:

  • Why do you not use the buffer parameter directly in your sample code?
  • Do not allocate on the audio thread (in processBlock())
  • The AudioSampleBuffer class has methods to access its write pointers
  • Please conform to C++ coding standards, like writing floats like 0.0f and increments as ++i
  • You can clear a buffer with a simple buffer method, without looping.

Xena,
I’m using Reaper. Static latency is a neat idea. I’d like to support zero-latency mode. One solution would be to only switch between zero latency and max latency. This would avoid most clicks and still support zero-latency.

peter,

  • My plugin supports mono/stereo modes (code sample was hugely stripped). It felt nicer to let JUCE handle buses instead of guessing myself what channel number is which buffer.
  • Didn’t notice getBusBuffer uses malloc! Thanks for this tip!
  • I guess I should use them and handle bus logic myself.
  • I come from web world so these might slip. I’ll try to behave.
  • Yea, I have seen. Normally my plugin doesn’t just clear everything. I did the sample code with as little changes to original as possible. If I did something wrong with my buffer code, someone would notice. Like you noticed that I was using malloc! That’s why this isn’t the optimal clear-buffers-plugin.

And in fact, it doesn’t. The returned buffer references the memory of the original buffer.
Hence the note in the docs to AudioProcessor::getBusBuffer():

This can be called in processBlock to get a buffer containing a sub-group of the master AudioBuffer which contains all the plugin channels.

So on that end you did nothing wrong…

That’s how it looks in the source:

1 Like