This is my first time implementing oversampling. I am dynamically changing the oversampling factor at the beginning of the processBlock().
I’m just looking for confirmation that I am doing things right.
if (oversamplingmodule) {
oversamplingmodule->initProcessing(samplesPerBlock); }
processBlock()
juce::dsp::AudioBlock<float> block(buffer);
//if user has changed oversampling factor then
oversamplingmodule.reset(new juce::dsp::Oversampling<float>(2, newOversamplingFactorValue, juce::dsp::Oversampling<float>::filterHalfBandPolyphaseIIR));
oversamplingmodule->processSamplesUp(block);
//rest of processing
oversamplingmodule->processSamplesDown(block);
That’s an allocation in process block which is a no-no. The way I do it currently is call suspendProcessing(), then create the object, then resume processing. This also gives you an opportunity to set the latency to the new value.
i personally prefer to let processor inherit from timer and check for oversampling parameter changes in timerCallback. if it changed this method, which is on the message thread, suspends the processing on the audio thread, manually calls prepareToPlay and then resumes processing. in prepareToPlay you simply give the oversampler its intended factor. in processBlock you just use the oversampler. this disrupts the audio stream but it’s safe
It is correct (at least to me) if you put this block of code within a listener callback (or something similar) instead of processBlock() (of course remove the first line).
This is what I do too. As you say, you get a potential dropout, but I think it’s acceptable for something like oversampling, which changes infrequently and is not automated.
If you wanted to make it seamless while maintaining correct delay compensation, things get a bit complicated.
@Mrugalla I’ve gone for the timercallback() approach.
Here’s my code below. I’ve got a couple of questions:
Do I need to suspendProcessing() in the timercallback() or not?
I have my timer set to 200 milliseconds, I assume this is an OK period of time.
I’ve not called preparetoplay(), because I’ve got a lot of other DSP initialization going on there, but in preparetoplay() I’ve called the initializeOversampling() method, and in timercallback() too. I trust this is fine.
void myplugin::timerCallback()
{
if (hasOversamplingChanged() == true)
{
suspendProcessing(true);
oversamplingmodule.reset(new juce::dsp::Oversampling<float>(2, newOversamplingFactor, juce::dsp::Oversampling<float>::filterHalfBandPolyphaseIIR));
initializeOversampling(getBlockSize());
//set hasOversamplingChanged flag to false
suspendProcessing(false);
}
}
void myplugin::initializeOversampling(int samplesPerBlock)
{
if (oversamplingmodule) {
oversamplingmodule->initProcessing(samplesPerBlock);
}
}
yeah. seems pretty much the same. just make sure that all dsp objects that are in the oversampled part of processBlock also get prepared for the changed samplerate and blocksize and it should be alright
edit: and I find when I engage oversampling, I find my filters and processing, the audio seems muffled and slightly lower volume even when I comment out the filters. I think I am updating the spec in preparetoplay() when oversampling is engaged:
I looked here, but it wasn’t defined as far as I can see
IIRC, reset() on a unique_ptr is similar to re-assign by make_unique. Therefore the factor is defined in the constructor:
factor: the processing will perform 2 ^ factor times oversampling
the audio seems muffled and slightly lower volume
That sounds like a filter effect. Could you please confirm that you process the oversampled audio block instead of the original block? The code should be something like this:
auto oversampled_block = overSamplers[idxSampler]->processSamplesUp(context.getInputBlock());
// do something on the oversampled_block
overSamplers[idxSampler]->processSamplesDown(context.getOutputBlock());
I think I’m getting closer to the cause of the crash.
Its when I come to downsample at the end of my processblock(). I checked numsamples after I upsampled, and its reporting the correct amount. e.g. 256 samples becomes 512 when I am on 2x etc.
Could you please confirm that you pass the original block (or the outputblock of the original context) to the function above? It seems that you pass the up-sampled block instead (after inserting the code you provided).
This is the block I am passing to downsample. Its pointing to the upsampled buffer, not the original buffer.
processblock()
juce::dsp::AudioBlock<float> block(buffer); //block assigned to original non-upsampled buffer at start of processblock()
...
if (//oversampling is on)
{
block = oversamplingmodule->processSamplesUp(block); //enlarge block of samples to process by the oversampling factor, and assign `block` to point to that upsampled block
}
int numSamplesToProcess = block.getNumSamples();
...
for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
auto* channelData = block.getChannelPointer(channel);
for (auto sample = 0; sample < numSamplesToProcess; sample++)
{
... //processing
...
if (//oversampling is on) { //downsample if we've upsampled
oversamplingmodule->processSamplesDown(block);
}
...
So is this now correct logic? (It doesn’t crash now).
I think I can make it more elegant than this but here’s how I altered it so far.
juce::dsp::AudioBlock<float> originalBlock(buffer); //assigned to original non-upsampled buffer
juce::dsp::AudioBlock<float> block(buffer); //block assigned to original non-upsampled buffer at start of processblock()
...
if (//oversampling is on)
{
block = oversamplingmodule->processSamplesUp(block); //enlarge block of samples to process by the oversampling factor, and assign `block` to point to that upsampled block
}
int numSamplesToProcess = block.getNumSamples();
...
for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
auto* channelData = block.getChannelPointer(channel);
for (auto sample = 0; sample < numSamplesToProcess; sample++)
{
... //processing
...
if (//oversampling is on) { //downsample if we've upsampled
oversamplingmodule->processSamplesDown(originalBlock); //block changed to originalblock
}
...