Cross frequency modulation
Source code and result:
Cross frequency modulation
Cross frequency modulation is realized by cross coupling two oscillators through a feedback loop : oscillator #1 is modulating oscillator #2 which is modulating oscillator #1. Each oscillator is in the same time a carrier and a modulator and so we use a symetric diagram. To respect this symetry, we replace the frequency ratio of the simple frequency modulation by a frequency multiplier for each oscillator, the reference frequency being the one from the note to play.
Implementation as a Csound plugin opcode
For such an algorithm, it is interessant to implement the opcode as a C written Csound plugin. Csound manual can be checked about plugins, and you can also read the paper by Victor Lazzarini about the question. Let's only say here that the crossfm plugin is made up of two files: a header file called crossfm.h and a source code file called crossfm.c. I use this Makefile file to compile the plugin on my own system (Ubuntu Hardy). You should change the lines H = $(HOME)/cvs/csound5/H and LDFLAGS = -L. -L/usr/lib for your own system.
Once the plugin has been compiled and installed in the Csound plugins directory, one can use in Csound the opcode crossfm and the opcode crossfmi similar to the later but with linear interpolation for reading the samples from the function tables. The syntax is:
a1, a2 crossfm xfrq1, xfrq2, xndx1, xndx2, kcps, ifn1, ifn2 [, iphs1] [, iphs2]
a1, a2 crossfmi xfrq1, xfrq2, xndx1, xndx2, kcps, ifn1, ifn2 [, iphs1] [, iphs2]
where the parameters are:
xfrq1 -- a factor that, when multipled by the kcps parameter, gives the frequency of oscillator #1.
xfrq2 -- a factor that, when multipled by the kcps parameter, gives the frequency of oscillator #2.
xndx1 -- the index of the modulation of oscillator #2 by oscillator #1.
xndx2 -- the index of the modulation of oscillator #1 by oscillator #2.
kcps -- a common base value, in cycles per second, for both oscillators frequencies.
ifn1 -- function table number for oscillator #1.
ifn2 -- function table number for oscillator #2.
iphs1 (optional, default=0) -- initial phase of waveform in table ifn1, expressed as a fraction of a cycle (0 to 1). A negative value will cause phase initialization to be skipped.
iphs2 (optional, default=0) -- initial phase of waveform in table ifn2, expressed as a fraction of a cycle (0 to 1). A negative value will cause phase initialization to be skipped.
In frequency modulation with a carrier having many partiels, a large modulation index will produce high frequencies with aliases artefacts around the Nyquist frequency. In the case of cross frequency modulation, both oscillators are simultaneously carrier and modulator and thus will frequently yield this aliasing. As a consequence, some of the results of this algorithm, while musically interesting, depend upon the sampling rate. You must consider this if you create a sound result at one sampling rate and if you want to save it in a soundfile with a different sampling rate.
Thus the above example is produced with sr=48000 while the soundfile of the example has a 44100 Hz sampling rate. Now the sound produced by this example with sr=44100 is different of the one produced with sr=48000 as can be heard from those two commands:
csound -r 44100 -k 4410 cfm01.csd
In order to save the right result a soundfile at 48000 Hz was first created with the following command:
csound -Wdo cfm01a.wav cfm01.csd
Then this soundfile was converted to a soundfile at 44100 Hz with the command:
srconv -W -r 44100 -Q 4 -o cfm01.wav cfm01a.wav