dsp::Oversampling setup

Hi, I am having trouble configuring dsp::Oversampling in my plug-in. Here’s what I have:

PluginProcessor.h

Private:
juce::dsp::Oversampling<float> oversampler;

PluginProcessor.cpp

//Constructor
oversampler(1, 3, juce::dsp::Oversampling<float>::FilterType::filterHalfBandFIREquiripple, true )

//Prepare to Play
oversampler.setUsingIntegerLatency(true);
oversampler.reset();

//Process Block
oversampler.initProcessing(buffer.getNumSamples());

oversampler.addOversamplingStage(juce::dsp::Oversampling<float>::FilterType::filterHalfBandFIREquiripple, 0.1f, -18.0f, 0.1f, -96.0f);
    
auto returnBlock = oversampler.processSamplesUp(juce::dsp::AudioBlock<float>(buffer));

The error I am receiving in Logic Pro is about not having the right number of channels in these lines:

//juce_Oversampling.cpp::159
void processSamplesUp ( const AudioBlock< const SampleType>& inputBlock) override
{
    jassert (inputBlock.getNumChannels() <= static_cast <size_t> (ParentType::buffer.getNumChannels()));

//juce_AudioSampleBuffer.h:322
Type* getWritePointer (int channelNumber) noexcept
{
    jassert (isPositiveAndBelow (channelNumber, numChannels));

I think this has something to do with the buffer translating to .processSamplesUp().

Any idea where I am going wrong?

Thank you,
Andy

It looks like your Oversampling instance is only being prepared to work with a single channel (the first constructor argument). If your plugin accepts multiple channels, you must initialise the Oversampling instance with the same number of channels.

There are a few other issues in the snippet you posted that might prevent the Oversampling class from working as expected: Normally, initProcessing would be called from prepareToPlay. Calling it in each block may cause issues. Also, calling addOversamplingStage inside processBlock will add a new stage on each audio callback, which is unlikely to be what you want. Finally, you need to call oversampler.processSamplesDown (outputBlock) after doing your processing on the returnBlock in order to return the block to the host’s sampling rate.

Thank you for your help! I switched the channel number to 2 from 1 and Logic still gives errors on those channel-based jassert lines. Is there something else I am missing?

I appreciate your help with the organization!

It sounds like the audio buffer being passed into processSamplesUp still has more channels than the oversampler is expecting. When you hit the assertion in processSamplesUp, you should be able to see the number of channels in the inputBlock in your debugger. (You could also add a DBG (inputBlock.getNumChannels()) statement to print the number of channels in the block).

If your plugin only supports certain channel counts, you should override isBusesLayoutSupported in your AudioProcessor to prevent the plugin from being instantiated with an unsupported channel layout.

I’ve configured isBussesLayoutSupported to only support stereo processing, and have configured my oversampler in PluginProcessor.h to look like this. The jasserts still come up. The channels of the inputBlock and the buffer read 2 and 2. Is there something I am missing?

juce::dsp::Oversampling<float> oversampler { 2, 1, juce::dsp::Oversampling<float>::FilterType::filterHalfBandFIREquiripple, true };

When you hit the first assertion, try checking all of the values involved to work out why the assertion is firing. Then, you should be able to look up the call stack to find out where the incorrect value is coming from.

Are you sure that you’re initialising the oversampler correctly? If the input block definitely has only two channels, you should check ParentType::buffer.getNumChannels() to make sure that the oversampler is prepared to process that many channels.

Thank you for your help. How do I check the ParentType::buffer.getNumChannels()? I have done oversampler.numChannels and compared it to buffer.getNumChannels() and the two match in my cout statement. Even when I set the oversampler to 0 channels, I still get the flag in Logic that

Thanks!

You can either look at the number of channels in the buffer member using a debugger, or you could add a line like this to the oversampler implementation:

DBG ("input block: " << inputBlock.getNumChannels() << " parent buffer: " << ParentType::buffer.getNumChannels());

OK! So it says the parent buffer is 0, and the input buffer is 2. How do I adjust the parent buffer? I have my set up to look like this:

//Initialization
 oversampler( 2, 1, juce::dsp::Oversampling< float >::FilterType::filterHalfBandFIREquiripple, true, true )

//Prepare to Play
oversampler.numChannels = getTotalNumInputChannels();
oversampler.factorOversampling = 1;
oversampler.initProcessing(samplesPerBlock);
oversampler.setUsingIntegerLatency(true);
oversampler.reset();

oversampler.clearOversamplingStages();    
oversampler.addOversamplingStage(juce::dsp::Oversampling<float>::FilterType::filterHalfBandFIREquiripple, 0.05f, -90.0f, 0.05f, -90.0f);

setLatencySamples(oversampler.getLatencyInSamples());

What is the value of getTotalNumInputChannels() in prepareToPlay()?

It says 2!

Hi @baintonaj !

Might I ask you where you initialize your oversampler? Because it does not have a default constructor and so you should initialize it in the initializer list of your PluginProcessor constructor.

By the way, I used the oversampler for one of my plugins and I never got that issue. I can send you my own implementation if you would like to see: CutTheMooog/LockWavefolder.cpp at develop · maurizdeb/CutTheMooog · GitHub

1 Like

I think the problem is that the number of channels in the oversampling stage’s buffer is only set during initProcessing. Moving the call to initProcessing after the call to addOversamplingStage should resolve the issue.

2 Likes

Hey! This worked! Thank you so much!

I am now having an issue with processSamplesDown() where line 202 in juce_Oversampling.cpp throws the following error, even thought outputBlock.getNumSamples() * ParentType::factor == 4096 and ParentType::buffer.getNumSamples() == 32768. Is the second number higher than I should be expecting for a buffer? Thank you!

jassert (outputBlock.getNumSamples() * ParentType::factor <= static_cast<size_t> (ParentType::buffer.getNumSamples()));

Hi! I initialized the oversampler in the top most part of the default JUCE constructor. I think that is the right place. Thank you for your example! :slight_smile:

1 Like

I can’t reproduce this problem, so I’m not sure exactly what’s going on. It’s easier for other people to help if you provide a small program which exhibits the problem.

I don’t think that assertion would fire if the left-hand of the <= is really smaller than the right-hand side. I’d recommend adding some logging to make absolutely sure of the values involved in the assertion. If you put the logging directly before the assertion, then at the point that you hit the assertion, you’ll be able to see the values involved in the console window.

It looks a bit suspicious that you’re writing to the factorOversampling data member. Modifying this member directly may break the invariants of the Oversampling instance. I’d recommend removing all writes to factorOversampling and checking whether that resolves your problem.

1 Like

Might I see how and where you call processSamplesUp() and processSamplesDown()?

Here’s how I am calling the two functions now. I am not getting any logical errors in Logic, but I am not seeing the effects of the oversampling filter in DDMF’s PluginDoctor.

auto returnBlock = oversampler.processSamplesUp(buffer);
juce::AudioBuffer<float> buffer2;
returnBlock.copyTo(buffer2);

//Processing using buffer2

juce::dsp::AudioBlock<float> outputBlock;
outputBlock.copyFrom(buffer2);
oversampler.processSamplesDown(outputBlock);
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
    buffer.clear (i, 0, buffer.getNumSamples());
outputBlock.copyTo(buffer);

Hi @baintonaj !

So first of all, why are u calling always audioblock.copyTo(audioBuffer) ?

After the upsampling process you can use directly the returnBlock. That audioblock already contains a reference to the upsampled buffer! Moreover, you can get the audiosamples directly by calling returnBlock.getChannelPointer(channel)[numSample].

I think that creating a new buffer and calling copyTo function, could allocate new memory in the buffer2 (but I am not sure if this happens, I’ve never used copyTo() ) . And allocating new memory on the audio thread is always a not-safe operation :slight_smile:

What you could do is simply

dsp::AudioBlock<float> inputBlock (buffer);
auto returnBlock = oversampler.processSamplesUp(inputBlock);

// Processing using returnBlock

oversampler.processSamplesDown(inputBlock);

// Continue processing using inputBlock

Hey! Thank you for your help! I got it to work! I didn’t realize you could use getChannelPointer() in an audio block like getWritePointer() in a buffer. That solved all my problems!