(max) fm synthesis, two implementations

demo patchers:

fm synthesis

Plenty of articles and resources exist describing frequency modulation (fm) synthesis. If you want to investigate online, I recommend Jeff Haas’s Intro to Computer Music, Vol 1, chapter 4. I’m going to focus on implementing fm synthesis in Max in a way that produces predictable, and therefore controllable, sidebands. The examples are modeled after the implementation of fm synthesis in the Yamaha DX series of synthesizers, which paired each oscillator with its own envelope generator.

important points

To understand the implementation of fm synthesis, you must keep in mind these important points:

  • The output of the modulator is added to the frequency input of the carrier. (frequency of note + modulator oscillator out)
  • The amplitude of the modulator does not range from 0 to 1. You multiply the envelope of the modulator (range: 0 – 1) by the number of sidebands you want (range: 0 – someNumber) by the frequency of the modulator. For example, if you want to produce 5 sidebands with a modulating oscillator frequency of 440 Hz, you multiply the envelope*5*modulatorFrequency to get the modulator amplitude.

FMsynth1~

<FMsynth1.maxpat> demonstrates a simple, two oscillator frequency modulation algorithm, designed to be used inside a [poly~] object. It makes extensive use of [p] subpatchers, named according to function. Pitch modulation from pitch bend, LFO, or envelope occurs like before. The [p modulatorOp] subpatcher implements the amplitude scaling for a modulating oscillator. The [p carrierOp] subpatcher accepts that modulator output and adds it (at audio rate) to its frequency input.

Within the standalone, mono version you can specify the ratio (multiple of the input frequency) of the modulator frequency and carrier frequency. Envelope controls for each are in subpatchers [p xxxxxEnvelopeControls], where you can specify [adsr~] settings. Receive objects also allow for these settings to be sent as a list from the parent polyphonic patcher. There is also an input object for setting the modulation index, which specifies the maximum number of sidebands possible.

FMPolySynth1 and [pak]

<FMPolySynth1.maxpat> demonstrates controls for <FMsynth1.maxpat>. Following the send objects should make its functions clear. All changeable parameters are connected to a [preset] object to store settings. The only new object is [pak], which is like [pack] except that input in any inlet causes the output of the entire list. If you change the release time, for example, the entire list is sent. Sending the envelope settings as lists makes for cleaner programming, as you don’t need a send/receive object pair for every envelope parameter of every envelope.

FMSynth2~

<FMsynth2~.maxpat> extends the functionality of <FMsynth1~.maxpat> by creating 8 operators that can each be either carriers or modulators. Two operators ([p operator2] and [p operator1]) are connected in the same algorithm as the default settings of <FMsynth1~.maxpat>. All of the operator subpatchers have 2 outlets. If you open any of the operator subpatchers you will see that the left outlet has amplitude scaled only by the its envelope generator, in the range of 0 – 1. This left outlet is for carrier signal output. The right outlet has its amplitude scaled by the number of sidebands and the oscillator frequency, which means that the right outlet is used for modulating the frequency of other operators. DON’T CONNECT THE RIGHT OUTLET TO AUDIO OUTPUT! 

Additional receive objects have been added to the operator subpatchers to reduce the number of needed inputs.

No parent polyphonic patcher has been provided, as you can implement it given your experience with the first patcher in this post. You will have to create the appropriate send objects in your parent patcher for each parameter you want to control.

You can arrange the operators in any fm configuration. For example:

  • 2 > 1 and 4 > 3, for a pair of modulator/carriers.
  • 4 > 3 > 2 > 1, for very bright and possibly noisy timbres. You can even use all in a single series.
  • 3 > 1 and 2 > 1, for an instrument that has a different timbre at the attack from the one at the sustain.

You can even use each operator as a carrier only, with no frequency modulation. Such a configuration yields an additive synthesis instrument.

other possibilities

With the previously demonstrated LFO capabilities of the amplitude modulation patcher you can modulate the output of any modulator to create a fluctuating number of sidebands present. You can also use multiple modulators/carriers and slightly detune them over longer periods of time for interesting results.

You can (and should) add filters to the instrument patchers (FMsynth1~ and FMsynth2~) to further controls timbres over time.


Comments

Leave a Reply