Frequency Modulation Synthesis (FM) - part 2 of 2

by Nicky Hind

List of Topics


In FM Synthesis - part 1 some methods are explored in which sidebands can be positioned with the strategic use of different carrier:modulator ratios, in order to produce various musical timbres. So far however, the c:m ratios have been such that both c and m are integers - and the resulting spectra have thus been of a harmonic nature. In part 2, we explore some of the effects that can be produced when the carrier- and/or modulator-ratio is not an integer; and also what happens when we combine more than one carrier/modulator pair in the instrument definition. Non-integer c:m ratios can be an effective way of producing inharmonic spectra, such as those found in bells, and many percussion instruments.

The Rule of Thumb Method of Calculating the Position of Sidebands

In FM Synthesis - part 1, a basic rule of thumb was given to determine the frequency of sidebands. That is, they can be determined by using the following equation:

carrier-frequency ± (k * modulator-frequency)

and taking k from 0 to the modulation-index plus 2. Of course when k is at 0, there is no modulation as such, so the only frequency component then is the actual frequency of the carrier. While the frequencies of sidebands are fairly easy to calculate, thier amplitudes is a much more complex story, and typically involves the constructive and destructive interference of negative frequencies rebounding into the positive frequency domain. To really understand this means taking into account the Bessel functions, and since these are no trivial element of mathematics, we will be content for now to confine this discussion of sidebands to the calculation of their frequencies, and assume that their amplitudes are progressively weaker, the higher the order of the sideband (which, in general, is true).

To - hopefully - make matters simpler, here is a LISP function for computing the frequencies of sidebands given as arguments, a basic frequency, carrier- and modulator-ratios, and a modulation index. (Note: the frequency content at k = 0 is not given out by this function).

(defun compute-sidebands (frequency c-ratio m-ratio m-index)
  (let ((upper-sideband) (lower-sideband))
    (loop for k from 1 to (+ m-index 2) do
      (setf upper-sideband (+ (* frequency c-ratio) (* k frequency m-ratio)))
      (setf lower-sideband (abs (- (* frequency c-ratio) 
                                   (* k frequency m-ratio))))
      (format t "~%with k at ~A, upper sideband: ~A Hz; lower sideband: ~A Hz" 
		             k   upper-sideband         lower-sideband))))

So, before we move on on to more complex examples, let's put the function to the test by calling it with some simple values: a frequency of 100 Hz; a carrier-ratio of 4; a modulator-ratio of 1; and a modulation index of 2. In other words, the same numbers that were used in the table in the introduction section of FM Synthesis - part 1.

Here is the result:

	<cl> (compute-sidebands 100 4 1 2)

	with k at 1, upper sideband: 500 Hz; lower sideband: 300 Hz
	with k at 2, upper sideband: 600 Hz; lower sideband: 200 Hz
	with k at 3, upper sideband: 700 Hz; lower sideband: 100 Hz
	with k at 4, upper sideband: 800 Hz; lower sideband: 0 Hz

Simulation of a Drum Sound using FM

With the combination of a non-integer c:m ratio, and a fairly high modulation index (producing a lot of sidebands), even fairly convincing drum sounds can be created with a single carrier/modulator pair. Here is an instrument that does just that. But before examining the code, it will be useful to find out from the instruments default values, where the sidebands will appear. The instrument has a c-ratio of 1.0; an m-ratio of 1.4; and (assuming a frequency of 130Hz, and an amplitude of 0.5) a modulation index of 6.4:

	<cl> (compute-sidebands 130 1.0 1.4 6.4)

	with k at 1, upper sideband: 312.0 Hz; lower sideband: 52.0 Hz
	with k at 2, upper sideband: 494.0 Hz; lower sideband: 234.0 Hz
	with k at 3, upper sideband: 676.0 Hz; lower sideband: 416.0 Hz
	with k at 4, upper sideband: 858.0 Hz; lower sideband: 598.0 Hz
	with k at 5, upper sideband: 1040.0 Hz; lower sideband: 780.0 Hz
	with k at 6, upper sideband: 1222.0 Hz; lower sideband: 962.0 Hz
	with k at 7, upper sideband: 1404.0 Hz; lower sideband: 1144.0 Hz
	with k at 8, upper sideband: 1586.0 Hz; lower sideband: 1326.0 Hz

As can now be seen, the sidebands fall in fairly inharmonic positions.

Example 1: FM Drum With One Carrier/Modulator Pair

(definstrument drum (start-time frequency amplitude
    ;; NB there is no duration arg -- this is computed later, 
    ;; as a function of amplitude and frequency
    ;; the m-ratio is set to the very `un-integer-like' value of 1.4
        (c-ratio 1.0) (m-ratio 1.4)
        (index-min 0.0) (indexscl 1.0)
    ;; see below for a graph of the amp-env and index-env envelope
        (amp-env '(0.000 0.831 17.372 1.000 30.073 0.488 57.810 0.164 100.000 0.000))
        (index-env '(0.000 1.000 0.219 0.670 0.696 0.307 10.007 0.000 100.000 0.000))
        (degree 0.0) (distance 1.0) (reverb-amount 0.005))
  (let* (
    ;; duration is in this way dependent on frequency and amplitude:
    ;; the higher above 130 Hz, the shorter the duration;
    ;; the lower the freq below 130 Hz, the longer the duration;
    ;; and the greater the amplitude the longer the duration.
         (duration (* 0.75 (/ 130 frequency) (+ 0.9 amplitude)))
         (beg (floor (* start-time sampling-rate)))
	 (end (+ beg (floor (* duration sampling-rate))))
    ;; again the value of the modulation index is given a dependency
    ;; with respect to amplitude and frequency: the frequency relationship
    ;; is as in the duration calculation, and the amplitude relation is now
    ;; a multiple.
	 (index-max  (* indexscl 8 (* 1.6 amplitude) (/ 130 frequency)))
    ;; all that follows is as per last week's instrument
	 (carrier (make-oscil :frequency (* c-ratio frequency)))
	 (modulator (make-oscil :frequency (* m-ratio frequency)))
	 (amp-env (make-env :envelope amp-env
                            :scaler amplitude 
                            :start-time start-time 
                            :duration duration))
	 (index-env (make-env :envelope index-env
                              :offset (in-Hz (* index-min m-ratio frequency))
                              :scaler (in-Hz (* (- index-max index-min) 
					        m-ratio frequency))
			      :start-time start-time 
			      :duration duration))
	 (loc (make-locsig :degree degree 
                           :distance distance 
                           :revscale reverb-amount)))
     (loop for i from beg to end do
	   (locsig loc i (* (env amp-env)
			    (oscil carrier (* (env index-env)
                                               (oscil modulator)))))))))

amplitude (carrier) envelope: index (modulator) envelope:
Notice the immediate onset of high modulation index values, followed by a fairly quick decay.

Some Calls to the drum Instrument

1. Accepting all the defaults in the instrument definition:

	(with-sound () (drum 0 130 0.75))

2. Notice the effect on duration and amplitude produced by a higher frequency:

	(with-sound () (drum 0 260 0.75))

3. And with a lower frequency:

	(with-sound () (drum 0 65 0.75))

4. Finally, the effect on duration of a smaller amplitude:

	(with-sound () (drum 0 130 0.25))

Simulation of a Tubular Bell Sound Using FM

Like all bell tones, the tubular bell has partials at decidedly non-integer ratios to the fundamental. Since the tubular bell spectrum is essentially a combination of harmonic and inharmonic partials, we will need to use more than one carrier/modulator pair to adequately convey its timbral characteristics. Basically, the effect of combining several carrier/modulator pairs is an additive one: their respective spectra simply pile up, one on top of the other.

In the instrument that follows there are three c/m pairs, and their ratios are fairly irregular. It is not easy to see from these values just where the sidebands will fall. To clarify, we will first run the compute-sidebands function on the respective c:m ratios and get an idea of what sidebands are being produced. We will use a base frequency of 100 Hz, although quite low for a tubular bell, it will help reveal which sidebands are harmonic, and which are not.

The first Carrrier/Modulator Pair has a c-ratio of 2.0; an m-ratio of 5.0; and an INDEXSCL (which for now we will take as the modulator index) of 3.0:

	<cl>  (compute-sidebands 100 2.0 5.0 3.0)

	with k at 1, upper sideband: 700.0 Hz; lower sideband: 300.0 Hz
	with k at 2, upper sideband: 1200.0 Hz; lower sideband: 800.0 Hz
	with k at 3, upper sideband: 1700.0 Hz; lower sideband: 1300.0 Hz
	with k at 4, upper sideband: 2200.0 Hz; lower sideband: 1800.0 Hz
	with k at 5, upper sideband: 2700.0 Hz; lower sideband: 2300.0 Hz

Clearly, these are all harmonic partials, albeit with a good few gaps from one partial to the next.

The second Carrrier/Modulator Pair has a c-ratio of 0.6; an m-ratio of 4.8; and an INDEXSCL of 3.0:

	<cl>  (compute-sidebands 100 0.6 4.8 3.0)

	with k at 1, upper sideband: 540.00006 Hz; lower sideband: 420.00003 Hz
	with k at 2, upper sideband: 1020.00006 Hz; lower sideband: 900.00006 Hz
	with k at 3, upper sideband: 1500.0 Hz; lower sideband: 1380.0 Hz
	with k at 4, upper sideband: 1980.0001 Hz; lower sideband: 1860.0001 Hz
	with k at 5, upper sideband: 2460.0 Hz; lower sideband: 2340.0 Hz

These seem fair inharmonic, although significantly some fall quite near to the harmonics, and one sideband in particular (the one at 1500 Hz) is dead on the 15th harmonic.

The third Carrrier/Modulator Pair has a c-ratio of 0.22; an m-ratio of 0.83; and an INDEXSCL of 1.0:

	<cl>  (compute-sidebands 100 0.22 0.83 1.0)

	with k at 1, upper sideband: 105.0 Hz; lower sideband: 61.0 Hz
	with k at 2, upper sideband: 188.0 Hz; lower sideband: 144.0 Hz
	with k at 3, upper sideband: 271.0 Hz; lower sideband: 227.0 Hz

Again, although many of these sidebands are a long way from the harmonics, some are fairly close, and the 1st upper sideband is close enough to cause beating with the fundamental.

Example 2: Tubular Bell Instrument With 3 Carrier/Modulator Pairs

(definstrument  tubular-bell (start-time duration frequency amplitude 
   ;; individual amplitude scalings for each of the carrier amp envelopes
   ;; this enables the balance of their respective contributions to the overall
   ;; timbre to be adjusted if need be. 
   (amp1 0.5) (amp2 0.25) (amp3 0.25)
   (c1-ratio 2.0) (m1-ratio 5.0)
   (c2-ratio 0.6) (m2-ratio 4.8)
   (c3-ratio 0.22) (m3-ratio 0.83)
   (index-min 0.15)
   ;; individual scalings for each of the modulator indexes -
   ;; thier final values has some dependency on amplitude and frequency
   ;; as can be seen below
   (index1-scl 3.0) (index2-scl 2.0) (index3-scl 1.0)
   (amp-env '(0.0 1.2 3.066 1.0 22.0 0.4 35.0 0.17 50.0 0.05 75.0 0.02 100.0 0.0))
   (index-env '(0.000 1.000 5.109 0.413 13.577 0.144 34.161 0.015 100.000 0.000))
   (degree 0.45) (distance 1.0) (reverb-amount 0.005))
  (let* ((beg (floor (* start-time sampling-rate)))
	 (end (+ beg (floor (* duration sampling-rate))))
   ;; to obtain the maximum index value for each modulator, the index-scl
   ;; (above) is multiplied by the amplitude, and the cube of a reference
   ;; frequency (261.5 Hz, c4) divided by the input frequency. This 
   ;; latter operation producing exponentially greater values for frequencies
   ;; lower than c4, and exponentially smaller values for frequencies
   ;; higher than c4 -- clearly the higher the tone the more `pure' it
   ;; will tend to be.
	 (index1-max (* amplitude index1-scl (expt (/ 261.5 frequency) 3)))
	 (index2-max (* amplitude index2-scl (expt (/ 261.5 frequency) 3)))
	 (index3-max (* amplitude index3-scl (expt (/ 261.5 frequency) 3)))
   ;; what follows is simply an duplication of structures already seen
	 (carrier1 (make-oscil :frequency (* c1-ratio frequency)))
	 (carrier2 (make-oscil :frequency (* c2-ratio frequency)))
	 (carrier3 (make-oscil :frequency (* c3-ratio frequency)))	 
	 (modulator1 (make-oscil :frequency (* m1-ratio frequency)))
	 (modulator2 (make-oscil :frequency (* m2-ratio frequency)))
	 (modulator3 (make-oscil :frequency (* m3-ratio frequency)))
	 (car-1-env (make-env :envelope amp-env
			      :scaler (* amplitude amp1) 
			      :start-time start-time 
			      :duration duration))
	 (ind-1-env (make-env :envelope index-env
			    :offset (in-Hz (* index-min m1-ratio frequency))
			    :scaler (in-Hz (* (- index1-max index-min) 
				              m1-ratio frequency))
			    :start-time start-time 
			    :duration duration))
	 (car-2-env (make-env :envelope amp-env
			      :scaler (* amplitude amp2) 
			      :start-time start-time 
			      :duration duration))
	 (ind-2-env (make-env :envelope index-env
			    :offset (in-Hz (* index-min m2-ratio frequency))
			    :scaler (in-Hz (* (- index2-max index-min) 
				              m2-ratio frequency))
			    :start-time start-time 
			    :duration duration))
	 (car-3-env (make-env :envelope amp-env
			      :scaler (* amplitude amp3) 
			      :start-time start-time 
			      :duration duration))
	 (ind-3-env (make-env :envelope index-env
			    :offset (in-Hz (* index-min m3-ratio frequency))
			    :scaler (in-Hz (* (- index3-max index-min) 
				              m3-ratio frequency))
			    :start-time start-time 
			    :duration duration))
	 (loc (make-locsig :degree degree 
			   :distance distance 
			   :revscale reverb-amount)))  

     (loop for i from beg to end do
       (locsig loc i (+  ;; the output of each c/m pair is summed together
		 	(* (env car-1-env)
			   (oscil carrier1 (* (env ind-1-env)
					      (oscil modulator1))))
			(* (env car-2-env)
			   (oscil carrier2 (* (env ind-2-env)
					      (oscil modulator2))))
			(* (env car-3-env)
			   (oscil carrier3 (* (env ind-3-env)
					      (oscil modulator3))))))))))

Some Calls to the tubular-bell Instrument

1. Accepting all the defaults in the instrument definition:

	(with-sound () (tubular-bell 0 5 (pitch 'c4) 0.25))

[the common music function PITCH converts from quoted note names to Hz frequencies.
This will not work unless you are concurrently running a common music image.]

2. With a higher frequncy argument:

	(with-sound () (tubular-bell 0 5 (pitch 'a4) 0.25))

3. Exaggerating the inharmonicity aspects a little:

	(with-sound () (tubular-bell 0 5 (pitch 'c4) 0.4
			:amp2 0.5 :amp3 0.5 :index2-scl 3.0 :index3-scl 3.0))

Table of Contents
The Basics | Additive Synthesis (1/2) | Additive Synthesis (2/2) | Frequency Modulation Synthesis (1/2)
Frequency Modulation Synthesis (2/2) | Sound Processing (1/2) | Sound Processing (2/2) | Physical Modelling