Trying a simple FM synth but getting noise

Hey,

I am trying to modulate the frequency of an juce::dsp::Oscillator with another juce::dsp::Oscillator
sounds like I am doing something wrong since I am getting a lot of noise and my values are totally out of range. This is as far as my mind can go, please tell me what I’m doing wrong and sorry if it is something obvious.

void renderNextBlock(juce::AudioBuffer<float> &outputBuffer, int startSample, int numSamples)
{
  
        jassert (isPrepared);
        
        if (! isVoiceActive())
            return;
        
        synthBuffer.setSize (outputBuffer.getNumChannels(), numSamples, false, false, true);
        fmBuffer.setSize(outputBuffer.getNumChannels(), numSamples, false, false, true);
        synthBuffer.clear();
        fmBuffer.clear();
          
        gain.setGainLinear(volumeParam);
        
        juce::dsp::AudioBlock<float> audioBlock { synthBuffer };
        juce::dsp::AudioBlock<float> fmBlock { fmBuffer };
        fmOsc.process(juce::dsp::ProcessContextReplacing<float> (fmBlock));
        
        float processedSample;
        double newFreq;
          
        for (int channel = 0; channel < synthBuffer.getNumChannels(); ++channel)
        {
            auto* buffer = synthBuffer.getWritePointer (channel, 0);
            
            for (int sample = 0;sample < synthBuffer.getNumSamples(); ++sample){
                newFreq = freq + fmBuffer.getSample(channel, sample);
                osc.setFrequency(newFreq, true);
                processedSample = osc.processSample(sample);
                buffer[sample] = processedSample;
            }
        }
          
        gain.process(juce::dsp::ProcessContextReplacing<float> (audioBlock));
        
        for (int channel = 0; channel < synthBuffer.getNumChannels(); ++channel)
        {
        
            for (int sample = 0;sample < synthBuffer.getNumSamples(); ++sample){
                std::cout << "Sample Value:" <<  synthBuffer.getSample(channel, sample) << std::endl;
            }
  }

The JUCE Oscillator class isn’t really suitable for doing FM as it cannot have a negative phase increment. Plus it does other stuff you don’t really want, such as smoothing of frequencies.

1 Like

Thank you,
is this why I am getting the noise though ?
Could you provide an example that would work ?
And last but not least could you provide some good reads where such concepts are explained ?

For a demo of a basic FM synth, see the DX10 project here: GitHub - hollance/mda-plugins-juce: JUCE implementations of the classic MDA audio plug-ins

1 Like

I’m trying yo understand what you mean. The JUCE Oscillator class outputs in the range -1 to 1 does it not? So why can’t we simply take that signal as a frequency modulator signal multiplier?

Because when the modulator is between -1 and 0, the phase counter of the carrier needs to go backwards and the JUCE Oscillator does not allow negative phase increments.

I don’t understand that part maybe I’m missing something obvious?
modulator: returns values from -1 to 1 at a modulation frequency
Then I’m calling JUCE Osc setFrequency() on the carrier (on control rate) doing this:
baseFrequency + jmax(20, jmin(20000,(modulator output * mod width))).

The carrier only receieves setFrequency() commands with a positive value?

modOutput [-1,1]
modWidth [0, 20k]
modOutput * modWidth [-20k, 20k]

Yes I know.
But the width will never so large.

modOutput [-1,1]
modWidth: 100
modSignal: modOutput * modWidth [-100, 100]
carrierFreq: 1600 + modSignal.

That’s what I’m doing now but it sounds wrong. I’m 100% certain the argument of the setFrequency() method is positive. That should not be the issue. Maybe it is because of the SmoothedValue used in Juce Osc setFrequency method?

setFrequency also has the bool parameter that should force the frequency to change immediately if you pass true into that. (OTOH I haven’t tested if it really works like that.)

Another problem you may be having : you mentioned you are updating the carrier frequency at “control rate”? A proper FM synth should probably update the carrier frequency at audio rate.

Heh yeah I just realised that :sweat_smile:. I already tried the bool parameter but when I change the modulation frequency with my UI slider it creates even more noise now.

Ok I think I know what the problem is, I should process the FM signal and process the Carrier in the same loop! But right now I’m using juce’s ProcessChain using a context from the buffer… Can I process 1 sample individually on the chain? my code:

void OscillatorsModule::updateDSP(AudioBlock<float> &subBlock)
{
    size_t numBlockSamps = subBlock.getNumSamples();

    for (unsigned int i=0; i<amps.size->get(); i++)
    {
        
        //process the fm signals
        for (int s=0; s<numBlockSamps; s++)
        {
            m_modulationValues[i] = m_modulators[i].processSample(0);
            
            float freq = m_frequencyValues[i] + m_modulationValues[i] * m_fmDepthValues[i];
            freq = jmax(20.f, jmin(20000.f, freq));
            m_processorChains[i].get<OscID>().setFrequency(freq, false);
        }
        
        //process the DSP
        AudioBlock<float> tempBlock = m_tempProcessBlock.getSubBlock(0, numBlockSamps);
        m_tempProcessBlock.clear();
        
        ProcessContextReplacing<float> context(tempBlock);
        m_processorChains[i].process(context);
        
        //we add each processed oscillator block to the final output
        subBlock.add(tempBlock);
    }
}

Ok I just ditched the processorChains and got it working like this!

void OscillatorsModule::updateDSP(AudioBlock<float> &subBlock)
{
    size_t numBlockSamps = subBlock.getNumSamples();

    for (unsigned int i=0; i<oscillarors.size(); i++)
    {
        //prepare audio block
        AudioBlock<float> tempBlock = m_tempProcessBlock.getSubBlock(0, numBlockSamps);
        m_tempProcessBlock.clear();
        
        //process signals
        for (int s=0; s<numBlockSamps; s++)
        {
            //advance the FM modulation oscillators
            m_modulationValues[i] = m_modulators[i].processSample(0);
            float mod = jmax(0.f, m_modulationValues[i] * m_fmDepthValues[i]);
            
            //advance the carrier oscillators
            float freq = m_frequencyValues[i] + mod;
            m_carriers[i].setFrequency(freq);
            
            //add to the temp buffer
            float val = m_carriers[i].processSample(0) * m_gains[i];
            tempBlock.setSample(0, s, val);
        }
        
        //we add each processed oscillator block to the final output
        subBlock.add(tempBlock);
    }
}