I’ve made it as far as configuring juce and getting my GUI set up without asking any questions on the forum but this has me totally stumped.
I’m trying to use the IRRFilter class to just test the waters and see how processBlock works, but i’m getting a bitcrusher-like effect on the output that gets worse as I decrease the buffer size in my host.
my processBlock function is pretty straightforward so I don’t think this can eb a matter of the code being too slow to process everything in time.
[code]void ReverbAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
// This is the place where you’d normally do the guts of your plugin’s
// audio processing…
for (int channel = 0; channel < getNumInputChannels(); ++channel)
// ..do something to the data...
I’ve called the setHighPass function in prepareToPlay. I thought perhaps the sample rate is changing slightly, causing the filter to not process the end of some of the blocks; but the effect just gets worse if i call setHighPass in processBlock().
I could post an audio example of this if it would be helpful, any ideas on how to get it sounding smooth?
Yeah, that’s exactly it. I assumed at first, while reading through the tutorials, that all I needed to do to change parameters in the plugin was to implement a listener in the GUI. I quickly discovered that was the wrong approach as there’s no way that the audioprocessor and the GUI can “talk” to each other by using this method.
Basically, what I want to do is to change the cutoff frequency of a filter by turning a rotary Slider on the GUI. Should be pretty straightforward right?
Yes, your use-case is straightforward, and I address it directly in the DspFilters Demo.
The problem boils down to how do you communicate information from one thread to another? There are two schools of thought:
1) Protect access to shared variables using a mutex (i.e. juce::CriticalSection).
In your case, the shared variable is the cutoff frequency + filter coefficients. Basically the juce::IIRFilter. The general technique is to put a lock around any code that accesses the variable. So when you want to change the cutoff from the UI thread (also known as the Message thread in juce terms), take the mutex (juce::ScopedLock), recalculate the IIR filter, and release the mutex.
Locking always comes in pairs, so then in the audio callback when you apply the IIR filter you will need to take the mutex, apply the filter, and release the mutex.
This is the easiest way to implement “synchronization” (threads communicating) but unfortunately the worst-performing.
The second school of thought, which is my preference, is
2) Modify shared variables asynchronously using a thread-safe queue
Instead of changing the IIR filter directly, put a message into a thread-safe queue telling the other thread to recalculate the cutoff/coefficients of the IIR filter the next time it gets a chance. A simple implementation of the thread-safe queue will use a mutex to protect the queue, but since the mutex is held for constant time (the time for a linked list insertion usually), it is extremely CPU friendly.
void AudioOutput::audioDeviceIOCallback (const float** inputChannelData,
m_queue.process(); // <-- process Thread Queue
// Called from the UI thread (message thread)
void AudioOutput::setFilterParameters (Dsp::Params parameters)
m_queue.call (bond (&AudioOutput::doSetFilterParameters, this, parameters));
If you don’t understand any part of it feel free to post your questions.
Sophisticated programmers will use a wait-free MPSC (multiple producer, single consumer) queue with a custom allocator to avoid the “ABA” problem. For more information on advanced synchronization, please visit: http://groups.google.com/group/lock-free