AudioParameterFloat returning 'inf' in processBlock


#1

This is probably a beginner question, as I am a beginner in JUCE. I’m whipping up an audio plugin. I have created an AudioParameterFloat, and I’ve poked at it with the debugger. In the constructor in my PluginProcessor.cpp I set the default value, and can get the value fine there using get() . When I enter my processingBlock method and ask for the value, I get ‘inf’.

I change it’s value using a slider, which is functioning correctly according to the debugger. Am I misunderstanding something? I can post my code if it will help.


#2

Yes please…
The few lines around where you try to read the parameter (i.e. where it is returning inf.) and eventually the line where you say it works should do…


#3

This is my PluginProcessor.cpp

FishTankAudioProcessor::FishTankAudioProcessor()
{
	// If the sample rate cannot be found, set it to 44.1 kHz; its the most common.
	// Add functionality for sending a message to the user somehow, stating so.
	float aExp;
	float sampRate = AudioProcessor::getSampleRate();
 
	// Get rid of this when you figure out how to fix the getSampleRate from audacity correctly.
	//addParameter(m_sampleRate = new juce::AudioParameterFloat("m_sampleRate", "Sample Rate", 0.0f, 1000.0f, sampRate));
	addParameter(m_sampleRate = new juce::AudioParameterFloat("m_sampleRate", "Sample Rate", 0.0f, 1000.0f, 44.1f));
	addParameter(m_wetDry = new juce::AudioParameterFloat("m_wetDry", "Wet/Dry", 0.0f, 100.0f, 0.0f));
	addParameter(m_lowPassSmoothingRate = new juce::AudioParameterFloat("m_lowPassSmoothingRate", "Smoothing Rate", 1.0f, 1000.0f, 100.0f));
	addParameter(m_a = new juce::AudioParameterFloat("m_a", "a", -100.0f, 100.0f, 0.0f));
	addParameter(m_b = new juce::AudioParameterFloat("m_b", "b", -1.0f, 1.0f, 1.0f - m_a->get()));
	m_c = 0.0f;
	calculate();
}
 
void FishTankAudioProcessor::calculate()
{
	float test = m_sampleRate->get();
	float aExp = -2 * M_PI / (m_lowPassSmoothingRate->get() * 0.001f * m_sampleRate->get());
	*m_a = exp(aExp);
	*m_b = 1.0f - m_a->get();
}

void FishTankAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
	const int totalNumInputChannels  = getTotalNumInputChannels();
	const int totalNumOutputChannels = getTotalNumOutputChannels();
 
	// If it ever becomes the point that there are more ouputs then inputs,
	// Then regenerate this file, and see what they did for that sitch :)
 
	// This is the place where you'd normally do the guts of your plugin's
	// audio processing...
 
	// Recalculate the member variables
	calculate();
 
	// Set up variables
	//float writer;
	float sample;
	float newVal;
 
	AudioSampleBuffer mainInputOutput;
	mainInputOutput.makeCopyOf(buffer);
 
	float sr = m_sampleRate->get();
 
	float a = m_a->get();
	float b = m_b->get();
	float c = m_c;
 
	for (int chan = 0; chan < buffer.getNumChannels(); chan++)
	{
 
		float* const writer = buffer.getWritePointer(chan);
		for (int samp = 0; samp < mainInputOutput.getNumSamples(); samp++)
		{
			sample = mainInputOutput.getReadPointer(chan)[samp];
			newVal = (sample * b) + (c * a);
			writer[samp] = newVal;
		}
	}
 
}

The first time I call calculate from the constructor, the variable sr = 44.099. The second time calculate is called, from processBlock, it’s value is inf. Does this help, or do you need more?

And this is a rough cut of the code, I will be able to fix all of my issues once I get help passing this hurdle :slight_smile:


#4

…I’m not hundred percent sure, what happens, but a few things that look suspicous:

m_sampleRate is a parameter, that can be changed by the host at any time. Also 0 is an allowed value for the host, and some hosts may not work correctly with the automation (e.g. a controller is plugged in which is set to write, then you may end up with zero).
Then in calculate you divide by that value, so it’s an illegal division by zero, which would lead to inf. But you said m_sampleRate->get() would return inf, that’s strange.

Second, you have your sampleRate parameter in kHz, so to get into samples you probably wanted to multiply with 1000.0 in your divisor rather than with 0.001.
I would rather use a mSampleRateFactor and multiply with the actual samplerate, which you get after prepareToPlay is called the first time. and if the factor is 0 simply clear the buffer, that’s probably what the user expects for that case.

So if you fixed your division and the error persists, we can look further…


#5

On the third line of the constructor:

addParameter(m_sampleRate = new juce::AudioParameterFloat("m_sampleRate", "Sample Rate", 0.0f, 1000.0f, 44.1f));

I hardcode the default value of m_sampleRate to 44.1, because AudioProcessor::getSampleRate() was always returning zero. Are you saying hosts can freely change all AudioParameterFloats, or the host can freely change the sample rate returned by AudioProcessor::getSampleRate()?

What I am really getting at is that when I set the default value of the m_sampleRate() to 44.1, am I avoiding the divide by zero error?

Another question that I have: are the nested for loops the most efficient way of processing? I ask because in some of the examples included with the API use nested for loops and there are comments in the code calling the method inefficient. Should I consider using a thread library like OpenMP for this, or would that cause conflicts?

I greatly appreciate your help.


#6

Yes, not by it’s own measure but by automation. Because this is not a static value but a time-dependant, the host has full control on the parameters and is allowed to set any value inside the range. If it’s a result of a bug, a connected hardware controller or a saved automation that got copied from one parameter to another, you don’t know. But your code needs to be able to cope with all values inside the range.

It can change during lifetime, some hosts allow changing it on the fly, or the sample rate might changed because a device was connected, that needs a different samplerate. But you can be sure you get a prepareToPlay() call, so there you get the accredited samplerate.

That’s up to your maths and what you want to achieve. that’s why I proposed to expose only a factor to the user, that makes the code more portable between devices, samplerates and hosts… But that’s up to you, how you check, that you don’t devide by zero.

You can do that, but at the point where you are now I don’t think it’s necessary. You probably want to watch Timur’s talks, the first one is about audio programming in general including thread safety and locking Youtube: Timur at C++con 2015, and lately he talked exactly about nesting and cache alignement Youtube: Timur Doumler: C++ Performance in Practice: Align, Vectorise, Cache, Jump!

For the start I would suggest to use as many methods of AudioBuffer, because they are vectorized and therefore kind of hardware accelerated.


#7

I will watch those videos, thank you very much for the links.

One last question: should I be using AudioParameterFloats, or am I misunderstanding what the object is for? Because of their dynamic nature, should I be using normal floats for some of my math?


#8

The AudioParameter(Float|Int|etc) classes serve as interface to the host. Before you had to use callbacks, normalize yourself etc.
So you use them for setting default values, the range and alike. But be aware, that this is also a meeting point of the message thread and the audio thread. So you have to consider what to do to be threadsafe (see the first video). You can either use an atomic in your processing code and set it via the parameterListener callback of the AudioProcessorValueTreeState, or there are several other solutions, everybody has it`s favourite, some of them work, some work most of the time and glitches are not noticable etc… Space for long considerations, discussions etc…