Blog

<- All Blog Posts

#Faust #PureData

Modular Fm Operator

A simple modular FM operator written in Faust with built in DX style amplitude envelope, its own frequency envelope, and feedback. Built this to create simple FM sounds faster in PureData, but could be helpful for other things.

op.dsp
import("stdfaust.lib");

declare author "Jeremy WY";
declare copyright "GRAME";
declare license "LGPL with exception";

freq = hslider("freq", 440, 20, 2000, 0.01);
gate = button("gate");
gain = hslider("gain", 0.5, 0, 1, 0.001);

// Feedback function 
fb(ops, mul) = ops ~ mean *(mul) 
with {
    mean(x) = (x + x') / 2;
};

// envelope dx style
envelopeDX(g) = en.dx7envelope(aR, dR, sR, rR, aL, dL, sL, rL, g)
with {
    aL =  hslider("aL", 0.9, 0, 1, 0.001);
    aR =  hslider("aR", 0.006, 0.001, 12, 0.001);
    dL =  hslider("dL", 0.1, 0, 1, 0.001);
    dR =  hslider("dR", 1, 0.001, 12, 0.001);
    sL =  hslider("sL", 0.5, 0, 1, 0.001);
    sR =  hslider("sR", 1, 0.001, 12, 0.001);
    rL =  hslider("rL", 0, 0, 1, 0.001);
    rR =  hslider("rR", 0.1, 0.001, 12, 0.001);

};

// Operator definition with modulation input and LFO input
operator(modInput, lfoIn) = 
    
        (phaseMod(min(max(theFreq,20), 20000), fbAmt, modInput) 
        * amp
        * vgroup("amp-envelope", envelopeDX(gate)))
        * ((theLFO*lfoAmt)+(1-lfoAmt))
     : outlets
with {
    lfoPhase = hslider("lfo-phase", 0, 0, 1, 0.001);
    lfoAmt = hslider("lfo-amt", 0, 0, 1, 0.01);
    theLFO = (lfoIn + lfoPhase) * (ma.PI*2) : cos : (_ * 0.5) + 0.5;

    theFreq = ba.midikey2hz(ba.hz2midikey(freq * index) + freqEnv) + offset;
    pan = hslider("pan", 0.5, 0, 1, 0.01);
    outlets = _ <: _ * sqrt(1 - pan), _ * sqrt(pan);
    
    // operator parameters 
    offset = hslider("offset", 0, -1000, 1000, 0.001);
    index = hslider("index", 1, 0.001, 12, 0.001);
    amp = hslider("amp", 0.5, 0, 5, 0.01);
    fbAmt = hslider("fb", 0, 0, 18, 0.1);
    depth = hslider("freq-depth", 0, 0, 48, 0.01);
    center = hslider("freq-center", 0, -48, 48, 0.01);
    
    // Frequency envelope in semitones
    freqEnv = vgroup("freq-envelope", (envelopeDX(gate) * depth) + center);
    
    // phase modulation (FM) oscillator with feedback algo
    phaseMod(f, fb, modd) = modd : fbFeedback(op(f), fb)
    with { 
        op(f, mod) = _, (mod + 0) : + : os.oscp(f) * 0.25;

        fbFeedback(x, mul) = x ~ mean * mul 
        with {
            mean(x) = (x + x') / 2;
        };
    };
};

process = operator;

Example usage

A pure data screenshot of how the modular fm operator can be routed to make a complex fm sound
a simple LFO sound

Arguments:

default faust synth: freq, gate, gain.

lfo-phase (reset phase from LFO input)

lfo-amt (amount from LFO input)

pan (stereo pan)

offset (hz frequency offset)

index (frequency index)

amp (over all amplitude)

fbAmt (amount of feedback on operator)

freq-center (center of frequency envelope)

freq-depth (amount of frequency for envelope, in midi)

amplitude envelope:

amp-envelope/aR, aL ...

frequency envelope:

freq-envelope/aR, aL ...