Plugin consumes loads of CPU after adding DSP modules

dsp_module
#1

I’m working on a simple plugin to add random noise to a signal but I’ve ran into a problem while adding filters to the signal. Adding this plugin to a project instantly makes it use 70% cpu even if the plugin isn’t getting any sound in input.

I’m generating a noise signal and after that I’m doing some processing to before storing it into a buffer and applying 2 filters to it like this:

void PluginTest1AudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();
	//Temporary buffer for DSP processing.
	juce::AudioBuffer<float> tmpBuffer = *new juce::AudioBuffer<float>(buffer.getNumChannels() ,buffer.getNumSamples());
	//resault of randomization
	double randRes = rand.nextDouble();
	// counter to check if recalculation is nessacary.
	double factorCount = 0;

    // In case we have more outputs than inputs, this code clears any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    // This is here to avoid people getting screaming feedback
    // when they first compile a plugin, but obviously you don't need to keep
    // this code if your algorithm always overwrites all the output channels.
    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    // Make sure to reset the state if your inner loop is processing
    // the samples and the outer loop is handling the channels.
    // Alternatively, you can process the samples with the channels
    // interleaved by keeping the same state.
    for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
		//get channel data
        auto* channelData = buffer.getWritePointer (channel);

		// loop through each sample
		for (int sample = 0; sample < buffer.getNumSamples(); sample++) 
		{
			// validate if recalculation is needed
			if (factorCount == (int)*treeState.getRawParameterValue(RRF_ID))
			{
				// get a value for the noise;
				randRes = rand.nextDouble();
				// set counter back to 0;
				factorCount = 0;
			}
			// apply 0 gating
			if (buffer.getSample(channel, sample) == 0.0f && zeroGate)
				//the change in amplitude is 0.
				staticChange = 0;
			else
				// add the noise as static
				staticChange = ((randRes * 2.0f) - 1.0f) * *treeState.getRawParameterValue(AMT_ID);

			//check if dynamicFollow mode is activated
			if (dynamicFollow)
				// multiply static change by the sample value * DFF
				dynamicChange = staticChange + *treeState.getRawParameterValue(DFF_ID) * (staticChange * buffer.getSample(channel, sample) - staticChange);
			else
				// dynamicchange is equal to the static change value.
				dynamicChange = staticChange;

			// Add noise sample to the audio buffer
			tmpBuffer.addSample(channel, sample, dynamicChange);

			//TMP Disabled
			//// apply the change value to the sample
			//channelData[sample] = buffer.getSample(channel, sample) + dynamicChange;

			// increment factor counter
			factorCount++;
		}
    }
	//Do DSP thing

	//instantiate an audio block
	dsp::AudioBlock<float> block(tmpBuffer);
	
	//process lowpass filter
	updateLowPass();
	lowPass.process(dsp::ProcessContextReplacing<float>(block));

	//process highpass filter
	updateHighPass();
	highPass.process(dsp::ProcessContextReplacing<float>(block));

	//Add input to the tmpBuffer.
	for (int channel = 0; channel < totalNumInputChannels; ++channel)
	{
		for (int sample = 0; sample < buffer.getNumSamples(); sample++)
		{
			buffer.setSample(channel,sample , buffer.getSample(channel, sample) + tmpBuffer.getSample(channel, sample));
		}
	}
	//Set output buffer equal to tmpBuffer.
}

void PluginTest1AudioProcessor::updateLowPass() 
{
	*lowPass.state = *dsp::IIR::Coefficients<float>::makeLowPass(44100,*treeState.getRawParameterValue(LPC_ID), *treeState.getRawParameterValue(LPR_ID));
}

void PluginTest1AudioProcessor::updateHighPass()
{
	*highPass.state = *dsp::IIR::Coefficients<float>::makeHighPass(44100, *treeState.getRawParameterValue(HPC_ID), *treeState.getRawParameterValue(HPR_ID));
}

I’m hoping you guys could help me with resolving this problem.

Here are the full files I use (document with links as I can only post 2 links as of now… ):
https://pastebin.com/YT3HSSyJ

What am I doing wrong and how can I improve my code to be more efficient?

thanks

0 Likes

#2

Firstly… is this a Debug or Release build?

Secondly… you’ll have to profile your code to see where it’s using the most CPU cycles

Rail

1 Like

#3

1 release
2. How would i go about doing that? I’m fairly new to juce as this is my first project

0 Likes

#4

VS: https://docs.microsoft.com/en-us/visualstudio/profiling/?view=vs-2019

Xcode: https://developer.apple.com/videos/play/wwdc2016/418/

Rail

1 Like

#5

OK so I’ve let it run inside the VS debugger and I’m getting 0% cpu usage here. it’s only inside the VST

0 Likes

#6

Personally I always prefer starting with Xcode instruments which for me at least is simpler to work with. I do Windows profiling only after knowing the shared code is ok on Mac that for DSP suffice most of the time.

With dlls profiling is tricky. You need to see the relative usage of the threads belong to your plug-in. With one plug-in that’ll be simple though as you’d usually see the difference before/after the plug-in while it process audio or UI is open on the DAW.

However, with current JUCE the stand alone app is a “killer” feature. You can easily test it on just random audio input from your soundcard or inject (by some code modification) desired audio input. This way you can profile your product with a little less noise and faster launch time between code changes.

0 Likes

#7

Maybe also consider whether you’re generating denormals and take a look at JUCE’s ScopedNoDernormals class.

2 Likes

#8

I think you dont need a profiler to see where this code loses CPU…

  1. important: dont use (int)*treeState.getRawParameterValue on every sample,
    better create a “const int” with this value to reuse it within the loop.
  2. less important: dont update your filters when there are no changes.
0 Likes