Help with understanding some code

Hi all,

I’m very new to JUCE and I’ve been following Josh Hodges (The Audio Programmer) tutorial series on Youtube and there is some code to do frequency modulation on a signal, which works, but I can’t figure out why it works!

In the function getNextAudioBlock it appears that the variable fmMod is repeatedly updated for each sample and its final value is computed from the last sample, but not used until in the function setWaveFrequency. The process function is then called on the audio block, but fmMod isn’t used.

So, if I understand correctly, fmMod is always going to be the value of the last sample in the audio block, or have I misunderstood something?

Code snippets …

void OscData::setWaveFrequency (const int midiNoteNumber)
    {
        setFrequency (juce::MidiMessage::getMidiNoteInHertz (midiNoteNumber) + fmMod);
        lastMidiNote = midiNoteNumber;
    }

void OscData::getNextAudioBlock (juce::dsp::AudioBlock<float>& block)
{
    for (int ch = 0; ch < block.getNumChannels(); ++ch)
    {
        for (int s = 0; s < block.getNumSamples(); ++s)
        {
            fmMod = fmOsc.processSample(block.getSample(ch, s)) * fmDepth;
        }
    }

    process (juce::dsp::ProcessContextReplacing<float> (block));
}

Hi @jspink56 !

I suppose that setWaveFrequency is called when a noteOn message comes from the MIDI buffer. In this way, it’s important to take into account the last value of the frequency modulation part in order to not listen to ‘weird’ jumps in the oscillator frequency

Hi @jspink56

I read better the code and I suppose that there is a little issue in the code (or by using better words: a ‘strange’ implementation of FM).
The for statement in the getNextAudioBlock is used to evolve the FM oscillator (which in this code it is supposed to run at very low rate: 5Hz or 10Hz I think).
Before the processing, I suppose that some updateParameters function is called. In this function the frequency of the main oscillator is updated with the last value of the FM oscillator. In this way there is a ‘jump’ in the frequency of the main oscillator controlled by the offset between the previous frequency and the next frequency of the FM oscillator. If the rate of the FM oscillator increases this offset is more ‘prone’ to be large.

I suppose that works in this way!

However looking into the dsp::Oscillator reveals that the frequency is a SmoothedValue. So it is by no means steppy.

It has certain issues with the arbitrary buffer boundaries and that the LFO signal is smoothed and not reconstructed, which would make it a perfect sine. But I would say it is neglible.

If you want to improve that you could wrap the loop in the getNextAudioBlock in another loop processing sub blocks of say 16 samples, which doesn’t hurt performance too much but improves the accuracy of the modulation.

1 Like

Thanks for the info @karota and @daniel

I’m afraid I still don’t fully understand what is going on, I’m very new to JUCE and DSP in general. I suppose the main thing is it is doing what I want it to do, I’ll leave it and move on to the next bit of my synth project, getting the LFO to modulate the filter frequency.

Reading the code from Josh again there is a mistake:

The loop only processes the LFO in form of fmMod. That is rather pointless if you don’t call setWaveFrequency (sorry, I think @karota pointed that out).

But another mistake is, the fmOsc is processed per channel in a loop, which brings us to:

The fmMod is advancing twice (or channels times) as fast as it should. Since it doesn’t call setWaveFrequency, we don’t know if it would do only once after the loop is done or each time inside.

Given that osc.process() is done outside, the only problem is the double speed of the LFO.

I really should read more carefully, you noted some of the issues yourself already… sorry, need more coffee :wink:

2 Likes

Hi @daniel

I suppose that bug has been fixed in future version of the synth

Thanks for looking into that. I must admit I didn’t follow the synth tutorial. I just answered to the snippet given by the OP.

1 Like