Problem with oversampling beat synced LFOs

I’m having trouble using the beat synced LFOs with the oversampler. It produces a stepped result instead of a smooth line, where the first part of the oversampled block is repeated by the oversampling rate. I’m calling SetSampleRate in the LFO and the free frequency LFO works as expected and retains the correct frequency at all sample rates. It’s almost as if the PPQ position is resetting within

Here’s my code:

void IPlugEffect::ProcessBlock(sample** inputs, sample** outputs, int nFrames)
{
    // Update oversampler if value changed
    if (mFactorChanged || GetSampleRate() != prevSampleRate)
        UpdateOversampling();
    prevSampleRate = GetSampleRate();
    
    // Calculate number of channels to send to the oversampler
    numInChans = NInChansConnected() / 2;
    numOutChans = NOutChansConnected();
    if (GetParam(kEnvFromSidechain)->Value() == kEnvfromSidechain)
        numSCChans = numInChans;
    else
        numSCChans = 0;
    
    // Oversampled Processing routine
    mOverSampler.ProcessBlock(inputs, outputs, nFrames, numInChans + numSCChans, numOutChans, [&](sample** inputs, sample** outputs, int nFrames)
                              {
        
        mLFOAL.ProcessBlock(mModulations.GetList()[kModLFO1], nFrames, mTimeInfo.mPPQPos, mTimeInfo.mTransportIsRunning, mTimeInfo.mTempo);
        LFODataGeneratedA[0] = mModulations.GetList()[kModLFO1];
        
        for (auto s = 0; s < nFrames; s++) {
            skipcount++;
            if (skipcount >= limit)
            {
                mEnvVisSenderTEMP.PushData({ kCtrlTagEnvSlow, {float(LFODataGeneratedA[0][s])} });
                skipcount -= limit;
            }
        }
        
        for (auto s = 0; s < nFrames; s++)
            outputs[0][s] = LFODataGeneratedA[0][s];
    });
    // END OF OVERSAMPLING
    // *******************
}

void IPlugEffect::UpdateOversampling()
{
    mOverSampler.SetOverSampling((EFactor)GetParam(kOverSampling)->Int()); // Warning, this could allocate.
    mFactorChanged = false;
    
    // Update Sampling Rate
    switch (GetParam(kOverSampling)->Int())
    {
        case 0:
            oversamplingscalar = 1; break;
        case 1:
            oversamplingscalar = 2; break;
        case 2:
            oversamplingscalar = 4; break;
        case 3:
            oversamplingscalar = 8; break;
        case 4:
            oversamplingscalar = 16; break;
        case 5:
            oversamplingscalar = 32; break;
    }
    
    oversampledsamplerate = GetSampleRate() * oversamplingscalar;
    oversampledsamplerate1div = 1.0 / oversampledsamplerate;
    
    mOverSampler.Reset(GetBlockSize());
    Reset(GetSampleRate(), GetBlockSize());
    
    
    // Update Modulation
    mLFOAL.SetSampleRate(oversampledsamplerate);
    mLFORateA = (GetParam(kLFOARateFreq)->Value()) * (44140.0 / GetSampleRate());
    
    mLFOAL.SetQNScalarFromDivision(static_cast<int>(GetParam(kLFOARateSynced)->Value()));
}

void IPlugEffect::Reset(double sampleRate, int blockSize)
{
    mModulationsData.Resize(blockSize * kNumModulations);
    mModulations.Empty();
    for (int i = 0; i < kNumModulations; i++)
    {
        mModulations.Add(mModulationsData.Get() + (blockSize * i));
    }
}

Why are you oversampling an LFO ?

It’s not isolated, I’ve stripped out all the processing. I’m allowing the LFO to go up into audio rates and the plugin will benefit from user-selectable oversampling.

Maybe it’s not as useful to oversample the synced LFOs, but I’m not sure how to get them into the oversampled processing block otherwise.

Unless the output of the lfo is going to be audible then I think you’re taking the wrong approach. You should only over sample what’s necessary.

You can pass the output of the LFO Into the oversampled section via the input buffers, passed to the oversampler

That is the case though - the LFO does get pretty audible due to the nature of the parameters it controls. The upper range used to be 20khz as that produced some desirable clangy tones. I’ve since dropped it down to 10khz, but I still want that clanginess!

I did have ‘check if oversampling on LFOs is necessary’ in my notes for this project but that has since been removed. Unfortunately I don’t know if that was because it didn’t help the sound, it did help the sound, or I couldn’t reach a solution with the old oversampler so went for generating the LFO inside the oversampler anyway. (I need to log these things rather than just delete the entry)

Also this part of the code was written last year, before it was possible to process a different number of input and output channels with the oversampler. How would I actually process data from different arrays with the oversampler’s ProcessBlock?
As I see it I’m just processing with the “mOverSampler.ProcessBlock(inputs, outputs, nFrames…” call but don’t know how to combine inputs with inputs and LFOs.

Sorry, but I’m not sure how to progress here. How would I combine inputs and LFOData in the mOverSampler.ProcessBlock(inputs, outputs, nFrames…” line?

In the IPlugInstrument example, you can see how multiple buffers for modulation sources are collected together into a WDL_Ptrlist passed to the synth inputs. It would work the same for the oversampler.

Thanks, that makes sense, I’ll experiment with that