Essentially, FM is fast vibrato. When a vibrato rate moves into the audio range, and its depth is well in excess of that generally used in vibrato, the effect - instead of being one of fast up and down repeated glissandi, sliding above and below a base frequency - is to distort the waveform of the modulated oscillator ("carrier"), producing instead what is known as "sidebands" above and below the base frequency. Sidebands are additional spectral components which, depending on the ratio between the frequency of the carrier and the frequency of its modulation, will either form harmonic or inharmonic relationships with the base (ie. the carrier) frequency. The positioning of sidebands is a function of the carrier:modulator ratio, and their number and amplitude vary in proportion to the amplitude of the modulator (ie. the depth of modulation). While the precise relationship between the sidebands, and the carrier:modulator ratio and the amplitude of the modulator (modulation index), can be determined by the use of Bessel functions, in general, it is reasonable to say that the number of sidebands produced on either side of the carrier will be equal to the modulation index plus 2. Further, the position (ie. frequency) of the sidebands will follow the basic rule:
carrier-frequency ± (k * modulator-frequency)
where k is the order of the sideband and generally ranges from 0 to the modulation index + 2
Thus, for example, a carrier frequency of 400Hz with a modulator frequency of 100Hz and modulation index of 1, will produce a spectrum with frequencies as shown in the following table:
carrier: | 400 Hz | |
first sideband: | 400 - 100 = 300Hz | 400 + 100 = 500Hz |
second sideband: | 400 - 200 = 200Hz | 400 + 200 = 600Hz |
third sideband: | 400 - 300 = 100Hz | 400 + 300 = 700Hz |
The sidebands would in this case seem like harmonics of a 100Hz tone, with partials at:
100Hz, 200Hz, 300Hz, 400Hz, 500Hz, 600Hz, 700Hz
In order to grasp the implementation of Frequency Modulation in CLM (or in any other system for that matter), it is important to understand something about phase increment. Phase increment is expressed in terms of radians per second - indeed at some lower level, all the frequencies of oscillators in CLM are expressed in terms of radians per second, but usually we don't have to confront this directly. In the case of vibrato, or FM (or any time we need to specify values to the optional 2nd argument of some oscillator), we - unfortunately - need to deal explicitly in radians per second. The function "in-Hz" converts converts from frequencies expressed in cycles per second (Hz) to frequencies expressed in radians per second, with respect to the sampling rate.
For example, we know that there are 2*Pi radians in one cycle, so therefore, if we express the Hertz sampling rate in terms of radians per second, the result should be equal to 2*Pi. The CLM package includes some of the constants we will use:
<cl> sampling-rate 22050 <cl> two-pi 6.2831855 <cl> (in-Hz sampling-rate) 6.2831855 <cl> (= two-pi (in-Hz sampling-rate)) T
From this, we can deduce that:
So what does phase increment do to the basic frequency of an oscillator?
To illustrate, here is an instrument which is almost exactly the same as the
very first instrument in A Basic Introduction to CLM,
only this is bestowed with the
capability of applying a constant phase increment to our sinusoidal oscillator.
(definstrument constant-phase-inc (start-time duration frequency amplitude &optional (phase-inc 0)) (let* ((beg (floor (* start-time sampling-rate))) (end (+ beg (floor (* duration sampling-rate)))) (sine-wave (make-oscil :frequency frequency)) ;; this converts a phase increment specified in Hz to ;; radians per second (phase-inc-radians-per-sec (in-Hz phase-inc))) (Run (loop for i from beg to end do ;; the phase increment value is applied as the 2nd argument to OSCIL (outa i (* amplitude (oscil sine-wave phase-inc-radians-per-sec))))))) |
1. Accepting the defaults, ie. no phase increment.
(with-sound () (constant-phase-inc 0 1 440 0.3))
2. With a phase increment of 440:
(with-sound () (constant-phase-inc 0 1 440 0.3 440))
produces a tone one octave higher than 440Hz, ie. at 880Hz
3. With a phase increment of 220:
(with-sound () (constant-phase-inc 0 1 440 0.3 220))
produces a tone a fifth higher than 440Hz, ie. at 660Hz
4. With a phase increment of -220:
(with-sound () (constant-phase-inc 0 1 440 0.3 -220))
produces a tone one octave lower than 440Hz, ie. at 220Hz
Clearly, the phase increment adds to the frequency of our basic oscillator.
Now what happens when the phase-increment value is not a constant, but rather changes during the course of a note. Here is a further modification of the instrument to illustrate:
(definstrument enveloped-phase-inc (start-time duration frequency amplitude &key (phase-inc 0) ;; a simple triangle-shaped envelope (phase-inc-func '(0 0 50 1 100 0))) (let* ((beg (floor (* start-time sampling-rate))) (end (+ beg (floor (* duration sampling-rate)))) (sine-wave (make-oscil :frequency frequency)) ;; the envelope structure, the output of which is scaled ;; by the radians-per-second value of phase-inc (phase-env (make-env :envelope phase-inc-func :scaler (in-Hz phase-inc) :start-time start-time :duration duration))) (Run (loop for i from beg to end do (outa i (* amplitude (oscil sine-wave (env phase-env)))))))) |
1. Accepting the defaults, ie. no phase increment.
(with-sound () (enveloped-phase-inc 0 1 440 0.3))
2. With a phase increment of 440 and the default envelope shape:
(with-sound () (enveloped-phase-inc 0 1 440 0.3 :phase-inc 440))
produces a glissando up to one octave higher and back again
3. With a phase increment of -220 and the default envelope shape:
(with-sound () (enveloped-phase-inc 0 1 440 0.3 :phase-inc -220))
produces a glissando down to one octave below and back again
In the case of vibrato and FM, the input to the phase increment term is niether a constant, nor an enveloped series of values, but is in fact itself oscillating (modulating) at some number of times per second, causing the frequency to alternately increase and decrease on each cycle. When this happens fast enough (ie. above the audio rate, say 20Hz), we get Frequency Modulation, causing not vibrato, but the appearance of sidebands above and below the base frequency.
Here is an implementation in CLM of a `simple' FM instrument, ie. having one carrier oscillator and one modulator oscillator.
(definstrument fm (start-time duration frequency amplitude ;; Some additions to the list of user arguments. ;; Firstly, the carrier- and modulator-ratios. These are ;; values which are multiplied by the main frequency argument ;; to arrive at the frequencies of the carrier and modulator ;; oscillators, respectively. &key (car-ratio 1.0) (mod-ratio 1.0) ;; Controls for the modulator index: these ;; are a nominal minimum value for minimum index, and a scaler ;; which defaults to 1. (index-min 0.0) (index-scl 1.0) ;; Individual envelope controls for carrier and modulator (index-env '(0 0 50 1 100 0)) (amp-env '(0 0 50 1 100 0)) ;; variables for the specification of absolute attack and decay times. ;; (as per Additive Synthesis - Part 1) (att-time .1) (dec-time .1) ;; These values relate to a form of output specification called ;; locsig, which is a more sophisticated method than the simple OUTA, ;; which we have used so far. OUTA simply sends the output to one channel, ;; and OUTB to the other. Here, in the words of Bill Schottstaedt, is what ;; LOCSIG does: ;; "it tries to place a signal between outa and outb in an extremely ;; dumb manner P it just scales the respective amplitudes ("that ;; old trick never works"). Revscale determines how much ;; of the direct signal gets sent to the reverberator. ;; Distance tries to imitate a distance ;; cue by fooling with the relative amounts of direct and reverberated ;; signal (independent of Revscale)." ;; DEGREE is the balance control between stereo left and right, ;; and (unlike MusicKit's BEARING parameter, which ranges from -45 to +45) ;; ranges from 0 to 90. A value of 45 thus being in the middle. (degree 45.0) (distance 1.0) (reverb-amount 0.005)) ;; END OF `USER' ARGUMENTS LIST ;; Now here is a new - and probably more sensible 1 way of getting ;; values for the first and last sample to be computed. The CLM function ;; GET-BEG-END does this automatically when supplied with ;; the start-time and duration values. The only catch is that ;; since it returns two values (first and last sample number), we ;; are obliged to use the Lisp function MULTIPLE-VALUE-BIND, which, ;; within the scope of its body, binds our sample values to the ;; variables, BEG and END. (multiple-value-bind (beg end) (get-beg-end start-time duration) ;; These next two lines are straight out of the discussion on ;; absolute attack and decay times in Additive Synthesis - Part 1 (let* ((att-point (* 100 (/ att-time duration))) (dec-point (* 100 (- 1.0 (/ dec-time duration)))) ;; The next line provides for an explicit relationship between ;; the modulation index, and the amplitude and frequency of the ;; sound to be generated. Basically, it is saying that louder ;; tones will also be brighter; and that higher tones (than 100Hz) ;; will be increasingly less bright, and lower tones (than 100Hz) ;; will be increasingly more bright. Thus whatever value is supplied ;; for the index-scl argument, it is going to be scaled according ;; to these relationships, and offset by 0.25 -- the latter ;; just to ensure that we get some reasonable energy into our output sound. (index-max (* index-scl (+ 0.25 (* amplitude (/ 100 frequency))))) ;; The next two lines set up the oscillator structures for the ;; carrier and modulator. Notice that the frequency terms to ;; these oscillators are the product of the respective carrier- ;; and modulator-ratios with the `user'-specified tone frequency. (carrier (make-oscil :frequency (* car-ratio frequency))) (modulator (make-oscil :frequency (* mod-ratio frequency))) ;; Setting up an envelope structure for amplitude - ie. of the carrier. ;; Likewise, DIVSEG was covered in Additive Synthesis - Part 1 (amp-env (make-env :envelope (divseg amp-env 25 att-point 75 dec-point) :scaler amplitude :start-time start-time :duration duration)) ;; The amplitude envelope for the modulator, otherwise known as ;; the FM envelope, or modulator-index envelope. It is this envelope ;; which controls the extent to which the carrier will be modulated ;; during the course of a note, and thus, how the spectrum of a ;; generated tone will vary over time. (fm-env (make-env :envelope (divseg index-env 25 att-point 75 dec-point) ;; the offset allows for a base minimum FM value :offset (in-Hz (* index-min mod-ratio frequency)) ;; the scaler is the amount of FM above the base minimum :scaler (in-Hz (* (- index-max index-min) mod-ratio frequency)) :start-time start-time :duration duration)) ;; The LOCSIG structure is assigned to the variable LOC, and values ;; are specified (as defined in the `user' argument list) ;; for each of its 3 arguments, DEGREE, DISTANCE, and REVSCALE. (loc (make-locsig :degree degree :distance distance :revscale reverb-amount))) ;; END OF INITIALIZATION LIST (Run (loop for i from beg to end do (locsig loc i (* (env amp-env) ;; as indicated, the FM input is the 2nd argument to OSCIL ;; and is itself the product of the FM-envelope wilth ;; the modulator output (oscil carrier (* (env fm-env) (oscil modulator)))))))))) |
1. Accepting all the defaults in the instrument definition:
(with-sound () (fm 0 2 440 0.5))
2. With a steeper amplitude envelope than the fm-envelope, slower attack and decay times, and an increased INDEX-SCL factor it is possible to hear the effect of the FM gradually emerge.
(with-sound () (fm 0 4 100 0.5 :amp-env '(0 0 10 1 90 1 100 0) :att-time 2 :dec-time 2 :index-scl 10.0))