AudioApp::getNextAudioBlock() vs PluginProcessor::processBlock()... Where do I find startSample data?


#1

Hello, everyone. I’m hoping someone can help me out, because I seem to be missing something, despite several hours of research and experimentation.

I’ve created a sine wave synth as a VST Plugin, following the example set forth in the tutorial found at the link I’ve posted below:

https://docs.juce.com/master/tutorial_synth_using_midi_input.html

I am getting a lot of crackling in my synth when I actually trigger it with my midi keyboard within (my host application) Reaper. I think it is because I have set the (int) startSample argument within synth.renderNextBlock() to zero, which I did because I could not find a way to get the startSample from the buffer, as is possible from within a standalone AudioApp during the getNextAudioBlock() call.

   synth.renderNextBlock (*bufferToFill.buffer, incomingMidi,
                           bufferToFill.startSample, bufferToFill.numSamples); // [5]

So my question is this: without the AudioSourceChannelInfo providing bufferToFill.startSample, how do I get this same information from the straight AudioBuffer that is provided by PluginProcessor::processBlock()?

Do I need to count the samples that pass from the original NoteOn event and use the last count to set the startSample argument? That was my most recent guess, but I have yet to attempt an implementation. Or should the startSample be derived from some other host-provided data that I’m unaware of?

Thanks in advance for any and all assistance!


#2

In AudioProcessors, which plugin projects use, the buffer given to processBlock should be filled from index 0 with the full buffer length (Gotten from AudioBuffer::getNumSamples).


#3

Right, so then I am correct to set the startSample to zero and the length to numSamples? So that means the crackling is caused by some other problem… sigh


#4

It’s very difficult to say what could be wrong without seeing your actual code. Can you show your processBlock implementation?


#5

Yes, let me clean it up first and then I’ll post it here.


#6

@Xenakios

 void AudioSynthDemoAsPluginAudioProcessor::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
	ScopedNoDenormals noDenormals;
	auto numSamples = buffer.getNumSamples();
	buffer.clear();
	synth.renderNextBlock(buffer, midiMessages, 0, numSamples);
}

#7

It looks as it should be.

Could it be a problem with channel counts? Are both the plugin and synth stereo?


#8

@Xenakios You might also want to see…

	void renderNextBlock(AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
{
	if (angleDelta != 0.0)
	{
		if (tailOff > 0.0)
		{
			while (--numSamples >= 0)
			{
				auto currentSample = (float)(std::sin(currentAngle) * level * tailOff);

				for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
					outputBuffer.addSample(i, startSample, currentSample);

				currentAngle += angleDelta;
				++startSample;

				tailOff *= 0.99;

				if (tailOff <= 0.005)
				{
					clearCurrentNote();

					angleDelta = 0.0;
					break;
				}
			}
		}
		else
		{
			while (--numSamples >= 0)
			{
				auto currentSample = (float)(std::sin(currentAngle) * level);

				for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
					outputBuffer.addSample(i, startSample, currentSample);

				currentAngle += angleDelta;
				++startSample;
			}
		}
	}
}

#9

@Xenakios The channel config is {0,2} in the plugin setup. Host is {2,2} I would think. The synth itself I believe produces one channel, which the SynthesizerVoice class copies to the other channel, if I remember correctly.


#10

edit : nevermind.


#11

It occurs to me that my rig might be to blame rather than the code. I’m using a laptop with built-in sound at the moment. I’ll try this again with an ASIO device attached and see if I still get crackling. Thanks for your responses, in any case. Much appreciated!


#12

Are you testing a Debug or Release build? Debug builds are quite slow and very prompt to crackling.


#13

Good point! However, the crackles persist for the synth despite a Release build. On the other hand, I’ve noticed I don’t have the same problem when I load a wave file sample (via SamplerSound) and trigger that via the same Synthesiser object. Something about generating the sine wave seems to be at issue. At any rate, my goal is to build a sampler not a sine synth, so I’m going to set this matter aside for now. Thanks for the tip, though!


#14

I think I see what kind of problem you could be having with generating the sine wave. Please have a read at:
Sine wavetable and #1 most common programming mistake that we see on the forum.


#15

That is very good to know about and watch out for, and sounds like something I would do unwittingly, so I greatly appreciate the link! However, in this case, I have simply copied the tutorial sine synth code as-is. The only difference is that it now gets called from AudioProcessor::processBlock(AudioBuffer, etc.) instead of AudioApp::getNextAudioBlock(HostChannelInfo, etc.) so unless there is some auto-magical process taking place in the AudioApp version of the loop that I’m unaware of, it should be the same. Also, it looks to me like a single channel source that is merely duplicated to the other channel to provide stereo, which I would think obviates this particular problem described in the link about separate states for the channel data. But if I’m wrong about that, I would love to learn what exactly is going on in getNextAudioBlock() that is fundamentally different from processBlock() and how I need to handle them differently.