[Function]
(fm-spectrum carrier ratio index {keyword value}*)

Returns a list of frequencies, and optionally amplitudes, of the FM sidebands generated from a carrier frequency in Hertz, a carrier/modulator ratio and an FM index. Frequency components in the output spectrum can be Hertz values, key numbers or note names, as specified by keyword arguments to the function.

fm-spectrum supports the following keyword arguments:

:spectrum {:raw | :hertz | :keynum | :note}
Determines the format of the frequency components returned in the output spectrum. If the value is :raw then values in the output spectrum are in Hertz but may contain negative values. If the value is :hertz then negative frequency components are reflected into the positive frequency domain with their amplitudes inverted. Otherwise floating point key numbers or note names are returned. The default value is :raw.
:amplitudes {boolean | :normalized | :weights}
Specifies whether sideband amplitudes are included in the output spectrum or not. If the value is false then only frequency components are returned. If the value is true then each sideband component is a list (frequency amplitude). If the value is :normalized then the amplitude value of each component is normalized between 0 and 1. If the value is :weights then the output spectrum can be passed directly to a random pattern with amplitudes converted to probability weights. The default value is false.
:ignore-zero boolean
If true then sidebands with zero amplitude are ignored, otherwise their frequency will be set to 0.0 or set to the rest symbol R if notes or key numbers are generated. The default value is true if the output spectrum contains Hertz values, key numbers or note names otherwise it is false.
:sideband-scaler {false | number}
If the value is false then the number of sidebands in the output spectrum is 1+round(index). If the value is a number then the number of sidebands generated is round(index*scaler). According to Dick Moore (pg. 325, Elements of Computer Music) a value of 1.7 will give all sidebands down to 60 dB; use 1.5 for 40 dB down. The default value is false.
:invert boolean
If true then the spectrum is inverted before it is returned. The default value is false.
:minimum freq
If specified, then components in the output spectrum that are lower than freq will be octave-shifted upwards until they are equal to or greater than this value. Freq should be the same frequency format as the output components.
:maximum freq
If specified, then components in the output spectrum that are higher than freq will be octave-shifted downwards until they are equal to or less than this value. Freq should be the same frequency format as the output components. The default value is false.
:scale-order {:up | :down | :random}
Determines the ordering of the components in the output spectrum: :up for low-to-high, :down for high-to-low and :random for random (shuffled) order. The default value is :up.
:remove-duplicates boolean
If true then the output spectrum will not contain any duplicate components. The default value is false.

Examples:

Example 1. Frequency modulated spectra.

(fm-spectrum 100 1.4 3)
 (-460.0 -320.0 -180.0 -40.0 100.0 240.0 380.0 520.0 660.0)

(fm-spectrum 100 1.4 3 :spectrum ':hertz)
 (40.0 100.0 180.0 240.0 320.0 380.0 460.0 520.0 660.0)

(fm-spectrum 100 1.4 3 :spectrum ':keynum)
 (27.48682 43.349957 53.525925 58.506374 63.486816 66.46195 69.76957 71.892105 76.019554)

(fm-spectrum 100 1.4 3 :spectrum ':note :scale-order ':down)
 (e5 c5 bf4 fs4 ef4 b3 fs3 g2 ef1)

(fm-spectrum 100 1.4 3 :spectrum ':note :minimum 'c4 :maximum 'b4)
 (ef4 e4 fs4 g4 bf4 b4 c5)

Example 2. FM chords.

(define (fm-bell tone )
  (let ((cen (hertz tone))
        (rat (between 1.1 1.6))
        (ind (pick 2 2.4 3)))
    (fm-spectrum cen rat ind :spectrum ':note
                 :minimum (transpose tone -12)
                 :maximum (transpose tone 12))))

(define (ring-bells reps rate)
  (let ((tones (new random
                 :of '((a4 :weight 3)
                       (a3 :max 1)))))
    (process repeat reps
             for tone = (next tones)
             for bell = (fm-bell tone) 
             output (new midi :time (now)
                         :keynum tone
                         :duration 5
                         :amplitude .8)
             when (odds .65)
             each k in bell
             output (new midi :time (now)
                         :keynum k
                         :duration 5
                         :amplitude .5)
             wait rate)))

(events (ring-bells 20 1.25) "bells.mid")
 "bells.mid"

See also: