Eastman Csound Tutorial
END of this chapter -- NEXT CHAPTER (Chapter 4) -- Table of Contents CHAPTER 1 -- CHAPTER 2 -- CHAPTER 3 -- CHAPTER 4 -- CHAPTER 5 -- CHAPTER 6 APPENDIX 1 -- APPENDIX 2

Chapter3
AMPLITUDE and FREQUENCY MODULATION ; PROGRAMMING CONTROLS

By now we have begun to patch together unit generators and other mathematical operations and utilities to form more complex, and therefore (we hope) musically more interesting instrument algorithms. In an example within the last chapter, we made a passing reference to sub-audio amplitude modulation. In this chapter, we will take a look at basic modulation procedures , applied first to amplitude and then to frequency. Some of you likely already are familiar with these concepts, others perhaps not. If you have little prior experience with modulation procedures, or erroneously surmise that they might involve moving from G minor to B-flat major, you may find it helpful to consult the tutorial introductions to modulationprocedures within Curtis Roads' Computer Music Tutorial, or some other primer on sound synthesis techniques, to supplement or clarify the material presented here.

Modulation results from periodic (regular and recurring) changes to some parameter of a sound. The audio signal is modulated by a control signal, often (but not necessarily) a sinusoid. In amplitude modulation the amplitude of a tone is modified by a (usually sinusoidal) signal to produce more rapid increases and decreases in intensity, called beats or tremolo. String players produce tremolos by rapid down and up strokes; flute players and singers create tremolos by dropping and raising their jaws while producing a tone.

In frequency modulation, by contrrast, it is the frequency (pitch) of the tone that undergoes periodic (recurring) upward and downward variation, often at a rate of between four and seven hertz, to create vibrato. String players create vibrato by wiggling the left hand finger that is stopping the string to vary the vibrating length of the string and thus the wavelength and resulting pitch of the tone. Trombonists rapdily move the slide back and forth to produce the same result.

The terms tremolo and vibrato sometimes are loosely (and incorrectly) used interchangeably, but although they both produce a type of pulsing or warble within a tone they are quite distinct, and should not be confused. Note that it is easy for woodwind players to produce a tremolo, but more difficult (and less common) for them to produce a true vibrato, which requires varying the length of the vibrating air column inside the instrument with half hole or half key finger movements. However, a trill also is a type of frequency modulation in which the modulating control signal is a square wave rather than a sine wave, producing an alternation between two pitches.

The audio signal that is being modulated can be a synthetic oscillator signal, such as a sine wave, in which case it is called a carrier, a term derived from FM and AM radio broadcasting. However, modulation also can be applied to acoustic signals, such as a vocal or cello tone, which generally is called simply the audio signal rather than the carrier.

While our examination of modulation will focus on amplitude and frequency, the two most common varieties, modulation also can be applied to many other sonic parameters of tones. If a periodic oscillator signal is applied to a filter it will modulate the timbral spectrum, perhaps producing a shimmer to the tone; if applied to pan location, modulation can "choreograph" the movement of a sound between two (or more) perceived locations. If you learn how to amplitude or frequency modulate signals, you should easily be able to apply the same procedures to other parameters of sound.

There is an important distinction in digital synthesis and signal processing between

An oscillator that produces sub-audio modulation often is called an LFO ("low frequency oscillator"), and typically is unsed to "animate" (or give "life" or "movement" to) an otherwise "flat" or "stationary" tone. If we begin by modulating an audio signal at a rate of, say, five hertz, and then gradually and continuously increase this modulation rate, something remarkable happens. When the modulation rate is between about 10 to 20 hertz, we no longer can hear the individual variations in amplitude or frequency, but rather hear a fluttertongue or buzzing effect. Then, as the modulation rate rises above 20 hertz, and continues to rise, new frequenices, often not present in either the audio signal or the modulator, begin to appear. These frequenices are called sidebands," and they are sum and difference frequencies between the audio and the modulating sources.

If our modulating oscillator is a sinusoid, the upper sideband will consist of frequencies at the sum of the modulator and each of the partial frequencies present in the audio signal. The lower sideband will consist of the difference frequencies between each of the partials of the audio signal and the modulating frequency. If either the carrier or the modulating signal is a complex waveform, such as a sawtooth wave, or a soprano tone, rather than a sinusoid, the upper and lower sidebands will include sum and difference frequencies between every combination of frequencies present in the two signals, and a dense timbre may result. If both signals are acosutically complex this can easily get out of hand, resulting in hundreds of sidebands and a resulting wall of noise. For this reason sinusoids are the most common modulating waveform.

Carrier to modulator ( c:m) frequency ratios

Another important aspect of audio rate modulation concerns the ratio between the frequency of the carrier and the frequency of the modulating oscillator. Simple integer frequency ratios -- say, a carrier at 200 hertz and a modulator at 50 hertz, or 40 hertz, or 600 hertz -- will produce harmonically related sidebands and, in most cases, a fairly "simple" timbre with a well-defined pitch.

We will obtain similar timbres by using nearly harmonic c:m (carrier to modulator) ratios, such as 1:1.002 , 1:2.99, 1:.498, or 2:3.003 . However, the resulting sidebands will be very slightly "out of tune" (like the harmonics produced by most natural acoustic instruments), and thus will frequently be out of phase. That is, the "harmonics" will alternately come in phase, starting together on a new cycle, and go out of phase, starting some new cycles at different times. This will cause amplitude beats, which will be clearly audible, and can be seen on an oscilloscope. In general, nearly-harmonic c:m ratios are preferred to exactly harmonic ratios, because of the added "warmth" and "life" imparted to the tone by the amplitude beats.

Complex carrier-to-modulator ratios, such as a carrier at 200 hertz and a modulator at 137 hertz, or 333 hertz, will produce inharmonic sidebands and in most cases, a a more "clangorous" or "metallic" or "noisy" timbral spectrum.

Finally, the depth of modulation -- a measure of how much of the modulator is applied to the carrier -- will affect the perceived timbral brightness and density of the resulting sound. In frequency modulation, for example, we might vary the frequency of the carrier by plus and minus 50 hertz, or 200 hertz, or 1000 hertz. The greater the frequency deviation, the more sidebands that will result. These concepts will be clearer when illustrated below by specific musical examples.

3.1. Amplitude Modulation and Ring Modulation

Amplitude modulation and ring modulation are closely related, differing in only one aspect. With amplitude modulation, the modulating signal is unipolar,, which means that it include only positive values. With ring modulation, on the other hand, the modulating signal is bipolar, a term that refers here not to a manic depressive psychiatric disorder, but rather to a waveshape that includes both positive and negative values, and thus has two poles (or peaks), one positive and one negative.

Recall that the GEN10 and GEN9 function table generators, which we used in Chapter 1 to create audio waveforms, create bipolar waveforms, with table values usually ranging between -1. and +1. This is necessary, or at least highly desirable, for audio signals. Audio signals that consist entirely of positive (or negative) values have what is termed a DC offset, a term taken from electronics and referring to direct current as opposed to alternating current. At the same amplitude level, an audio signal with DC offset will have only half the energy and resolution (clarity) of a corresponding bipolar signal, and may create artifacts when subjected to certain common types of signal processing.

(An aside: DC offset can result from defective recording procedures or recording equipment, or from certain types of signal processing. DC offset removers are common signal processing and plug-in modules. Csound has one called dcblock)

For control signals, however, we frequently may need or want to use unipolar (positive only) waveshapes, such as a sine wave that varies between 0 and +1 rather than between -1 and +1. The easiest way to do this is to use a function table generator that allows us to specify a DC offset. Csound's GEN19 table generator will do the trick. GEN19 is identical to GEN9, but includes an extra DC offset parameter fo each partial:

GEN9:
f #  time size  9 pna  stra  phsa        pnb strb phsb      etc.  
GEN19:
f # time size  19  pna stra  phsa  dcoa  pnb strb  phsb  dcob  etc.

Bipolar sine waves, values between -1. and +1
*f1 0 1024 10 1. ;   < bipolar sine wave created with GEN10
*f2 0 1024 9 1. 1. 0;   < bipolar sine wave created with GEN9
*f3 0 1024 19 1. 1 0 0;   < bipolar sine wave created with GEN19

Unipolar sine wave, values between 0 to +1
*f4 0 1024 19 1. .5 0 .5;  < unipolar sine wave created with GEN19

Function number 4 above creates a sine wave with values ranging between +.5 and -.5, then adds a DC offset to these values so that they wind up ranging between 0 and 1. This is the function that we want to use for amplitude modulation. Function number 1, 2 or 3 above, which produce an identical result, are what we need to perform ring modulation.

We now will use the following orchestra file to illustrate the audible difference between amplitude and ring modulation at both subaudio and audio modulating rate:

;  #############################################################
;  Orchestra file ex3-1, used to create soundfiles ex3-1-1, ex3-1-2, ex3-1-3 & ex3-1-4
;  instrument for performing subaudio and audio rate amplitude & ring modulation
;  #############################################################

nchnls = 1    ; mono output

instr 1
; init. variables:
   idur = p3   ; duration of the note
   ipitch=cpspch(p4) ; carrier frequency
   imodfreq= p6 ; modulating frequency, given in hertz
   imodfunc=p7 ; modulating waveshape; 1 = amp. mod., 2 = ring mod.
kampenv expseg .005, .2*idur,1, .5*idur, .6, .3*idur,.005; amplitude envelope
kampenv = kampenv * p5 ;  multiply this envelope by p5 (peak amplitude for each note)

amod oscil3 1, imodfreq, imodfunc ; modulating oscillator
  ; sine wave carrier oscillator with amplitude or ring modulation:
acarrier oscil3  amod*kampenv, ipitch, 1 
out acarrier
endin

------------------------------------------------------- 
Sub-audio amplitude modulation

The first of our four scores for this instrument creates sub-audio amplitude modulation (tremolo).

< score example ex3-1-1, sub-audio amplitude modulation
*f1 0 1025 19 1. .5 0 .5; < sine wave unipolar 0 to 1  for amp. modulation
*f2 0 1025 10 1; < sine wave bipolar, +1 to -1 for ring modulation 
i1 0 0 4; 
p3 3;
du .9;
p4 no bf3/a/c4/b3;     < carrier output pitch, B-A-C-H motive
p5 nu 20000;           < amplitude
p6 nu 3./6./9./15.;    < modulating frequency
p7 1;                < function for amp. modulator
end;

The score produces four notes, each lasting 2.7 seconds (see p3 and du). To give our ears a clear reference point in comparing the aural results of this score with those that follow I have employed the BACH motive (bf3/a/c4/b3) for the pitches of the four notes (p4). The tremolo (modulating frequency) rate, set in p6, is rather slow (3 hertz) for the first note, and increases with each note until it is quite rapid (15 pulses per second) on the final note. The waveshape (function table number) for the modulating oscillator, provided in p7, is set to 1, specifying that f1, a unipolar sine wave, be used.

Example score ex3-1-2 is identical to ex3-1-1 in every respect except that p7 is set to 2:

 p7 2;          < function for amp.l modulator
This specifies that function table 2, a bipolar sinusoid,be used for the modulating waveshape. As you listen to the result, you will notice that the tremolo rate for each note has doubled, and that we actually hear 6 pulsations per second for the first note, 12 beats per second for the second note, and so on, because the modulating waveform contains two poles per cycle. Note that the fourth note now creates new sideband frequencies, because the actual modulation rate is 30 hertz. Because bipolar amplitude modulation (ring modulation) produces twice the number of amplitude beats per second that we specify, it is rarely used, and the term "ring modulation" almost always implies audio rate modulation.

Audio rate amplitude and ring modulation

Example score ex3-1-3 is identical to our original ex3-1-3 score except that p6 now specifies modulating frequencies in the audio range, which create sidebands:

< score example ex3-1-3, audio rate amplitude modulation

*f1 0 1025 19 1. .5 0 .5; < sine wave unipolar 0 to 1  for amp. modulation
*f2 0 1025 10 1; < sine wave bipolar, +1 to -1 for ring modulation 
i1 0 0 4; 
p3 3;
du .9;
p4 no bf3/a/c4/b3;     < carrier output pitch, B-A-C-H motive
p5 nu 20000;           < amplitude 
p6 nu 44/133/631/2700 ; < modulating frequency 
p7 1;                < function for amp. modulator
end;

A defining feature of amplitude modulation is that even when 100 % of the carrier signal is modulated, as here, some of the original carrier frequencies are still heard (at reduced amplitude) along with the new sideband frequencies, and in this example we still can clearly hear the BACH motive.

Example score ex3-1-4 is identical to the preceding ex3-1-3 score in every way except that p7 now specifies that bipolar function number 2 be used for the modulating oscillator waveshape:

 p7 2;          < function for amp. modulator

This creates ring modulation rather than amplitude modualtion. As you listen to the resulting soundfile /sflib/x/ex3-1-4.wav created by this orc/sco pair, the BACH motive will no longer be audible. With ring modulation, the original carrier frequencies no longer survive; all of their amplitude energy is absorbed by the new sidebands.

Using the preceding orchestra and score files as templates or models, you might try modifying them, introducing additional features into the instrument, trying out more complex waveforms, rather than simple sinusoids, for the carrier and modulating oscillators, and experiment with a wider range of both carrier and modulating frequencies.

Because I designed instrument ex3-1 above as an all-purpose, sub audio and audio rate amplitude and ring modulation algorithm, it is not optimized for any of these particular tasks. For high quality sideband generation I have employed oscil3, the highest quality (and computationally slowest) variant of the oscil opcode, for the modulating as well as carrier oscillator. If we simply want to add the capability for subaudio tremolo to an instrument, we could use oscil rather than oscil3, and run this opcode at the control rate rather than audio rate, with no loss in audio quality:

kmod oscil 1, imodfreq, imodfunc ; modulating oscillator
  ; sine wave carrier oscillator with amplitude or ring modulation:
acarrier oscil3  kmod*kampenv, ipitch, 1 

Furthermore, Csound includes a unit generator named lfo that is optimized for sub audio amplitude modulation that provides us with a choice of six built-in modulating waveforms, obviating the need to supply fenction table definitions in our score for the modulating waveshape:

kmod lfo 1, imodfreq, imodfunc ; modulating oscillator
acarrier oscil3  kmod*kampenv, ipitch, 1

In our instrument the frequency of the modulating oscillator must be specified in hertz in our score. This works fine for sub-audio tremolos, but for audio rate amplitude or ring modulation in generally is better to specify the modulating frequency in terms of its ratio to the carrier frequency. This way, when we want modulated sounds that still have a well-defined pitch, we can specify simple integer and (often better) near-integer carrier-to-modulator frequency ratios such as 2., .51, 3.02 or 5. Conversely, if we want more complex or 'noisy" sounds, we can specify correspondingly more complex carrier-to-modulator frequency ratios, such as 1.414, 3.752 or .635.

Another limitation of this instrument is that both the modulating frequency and the depth of modulation remain constant throughout each tone. For sub audio amplitude modulation (tremolo) this will tend to sound mechanical or "electronic." Human performers typically apply a gradual increase in both tremolo rate and depth, as well as slight random variations in both of these parameters when employing tremolos (and vibratos). These time varying nuances make the modulation more expressive and less obvious.

For audio rate amplitude, ring and frequency modulation, varying the depth of modulation, usually with an envelope that rises at the beginning of each note and decreases near the end, will usually make the resulting timbre more life-like and interesting.

Our next example is designed for only audio rate AM and RM, and illustrates the effects of varying the modulation depth during each tone to produce a timbral envelope. Note that the modulating frequency here is specified in terms of a carrier-to-modulator ratio, rather than in hertz.

Example ex3-2 present an orchestra and a companion score11 input file that are optimized for audio rate amplitude or ring modulation. The modulating frequency here is specified in terms of a carrier-to-modulator ratio, rather than in hertz. The frequency ratio between the modulating and carrier oscillators still remains constant throughout a note (the pitches of both oscillators remain fixed). However, the depth (percentage) of the amplitude that is modulated is varied by means of a control oscillator, resulting in time varying timbral changes.

(The lines of Csound code have been numbered in this example so that we can refer quickly to particular features. Obviously, such line numbers would not be included in an actual Csound orchestra file.)
############################################################# ;
soundfile ex3-2  :    Orchestra file used to create this soundfile ;
############################################################# 1   ;
Audio-Rate Amplitude or Ring Modulation Instrument : 2   ;  p3 =
duration   p4 = pitch   p5 = amplitude  p6 = Attack time  p7 = decay time
3   ;   p8 = ratio of modulating freq. to p4 4   ;   p9 = 1st
amplitude modulation % 5   ;   p10 = 2nd  amplitude modulation %
6   ;   p11 = Function number controlling change in a. m. % 7   ;
p12 = Function number for modulating oscillator

8   sr = 44100 ; (not really needed)
9   kr = 2205 ; (not really needed)
10  ksmps = 20 ; (not really needed)
11  nchnls = 1

12  instr 1
13  ipitch = cpspch(p4)
14  kenv  linen  p5,  p6,  p3,  p7       ; bareboned amplitude envelope
15  ; amplitude modulation :
16      kmod oscil (p10 - p9),  1/p3,  p11  ; difference between 1st & 2nd a. m. %
17         kmod = (kmod + p9) * kenv           ; % of signal to be amp. modulated
18         knomod = kenv - kmod                  ; % of signal NOT amp. modulated
19      ampmod  oscil3  kmod, ipitch * p8, p12   ; modulating oscillator

20  audio  oscil3  ampmod + knomod, ipitch , 1   ; carrier oscillator
21      out audio
22  endin
-------------------------------------------------------

Score for above instrument :
-------------------------------------------------------
< score for ex3-2 : ring modulation, time varying modulation depth
 < audio waveform functions :
*f1 0 1024 10 1.;             < bipolar sine wave for carrier & for RM
*f2 0 1025 19 1. .5 0 .5; < unipolar sine wave ; use for AM
*f3 0 1024 10 0 1. .3 0 .15;  < more complex wave, harmonics 2,3,5
< control functions for change in amplitude modulation % {p12}
*f10 0 65 7  0  64  1. ;         < linear change between p9 & p10
*f11 0 65 5  .01  64  1.;        < exponential pyramid change between p9 & p10
*f12 0 65 7  0  32  1.  32  0;   < linear pyramid  {p9 to p10 to p9}
>*f13 0 65 5  .01  32  1.  32  .01;  < exponential pyramid  {p9 to p10 to p9}
rc

i1 0 0 4;           lt; 4 output notes
p3 4;                    < start  times
du .95;
p4 no c4;                < pitch
p5 10000;                < amplitude
p6 .2;                   < attack time
p7 .5;                   < decay time
p8 nu 1./ 1.4 / .255/ 3.3;    < ratio of modulating frequency to p4
p9 nu .05/ .05 ;            < 1st % of a.m.
p10 nu .95/ .95;            < 2nd % of a.m.
p11 nu 10/ 11 / 12/ 13 ;   < function number for change in a.m. %
p12 nu 1/2/  3 // ;         < function number for modulating oscillator
end;
-------------------------------------------------------

Appendix Csound score file examples : Chapter 3

This instrument algorithm has been scrupulously commented, almost to the point of loquacity. However, we well may appreciate this documentation later, and anyone else who looks at our orchestra file will be grateful for the assistance. As your instruments get more complicated, it becomes harder to follow your own logic. What seems obvious today may cause head-scratching in a few weeks.

linen (line 14) creates a simple attack-sustain-decay amplitude envelope for each note. This little fellow is adequate for this didactic example, but probably is not something we would want to employ in an instrument from which we hope to coax some usable soundfiles, because it offers us almost no ability to shape unique or contrasting amplitude envelopes from note to note.

On lines 16-18 we split this original amplitude signal into two paths. kmod is the percent of the amplitude signal that will be modulated; knomod is the percent that will not be modulated. The greater the depth of modulation, up to a maximum of 1. (100 %), the greater the resulting strength of the sidebands, and thus the greater the timbral change.

Let's look more carefully at how the depth of modulation is varied, because you can apply this kind of procedure frequently, not just to varying modulation depth, but in many other cases when you want to create time-varying control signals:

15 ; amplitude modulation :
16   kmod oscili (p10 - p9), 1/p3, p11    ; difference between 1st & 2nd a. m. %
17   kmod = (kmod + p9) * kenv          ; % of signal to be amp. modulated
18   knomod = kenv - kmod                 ; % of signal NOT amp. modulated
19   ampmod oscili kmod, ipitch * p8, p12 ; modulating oscillator

The oscil control oscillator kmod (line 16) computes the changing difference between the first and second modulation percentages. Changes in this value follow the shape of the function given in score p-field 11. The difference is then added to our initial (p9) percentage in line 17 and multiplied times the original amplitude signal. This gives us the total, time varying amplitude signal level that will be sent to the modulating oscillator. This amplitude is then subtracted from the original output of linen (line 18) to define the non-modulated amplitude signal, and the result is written to a RAM memory location we call knomod.

The modulating oscillator (ampmod) generates the modulating signal. It's three arguments are :

1) the amplitude to be modulated (signal kmod)
2) frequency : p8 times the (p4) frequency of the carrier oscillator
3) function : the audio waveshape function specified in score p-field 12 (a sine wave for the first two notes, a more complex waveform for the final two notes).

The actual amplitude modulation occurs within the carrier (audio) oscillator on line 20. The output of the modulator (ampmod) is added to the non-modulated amplitude (knomod) within the amplitude argument to the carrier oscillator.

Some function tables to control time varying changes between two values
Our score file provides 8 function tables (f10 through f17) that can be used to control changes in modulation depth. Postscript displays of these 8 function table shapes are provided in an appendix to this chapter.
These 8 functions are used in succession for the 8 notes created in our score (see p11 in the score). While listening to /sflib/x soundfile example ex3-2, look at the displays of these functions and follow how they control the change in timbre between the simple carrier sinusoid and the compex modulated sound. You might want to make a copy of these 8 control functions for possible use in the future.

Modulation techniques have been important resources in digital as well as analog sound synthesis because they are efficient, enabling us to create many different types of timbres with only two oscillators. James Dashow, for example, has composed works in which the timbre of almost every note results from amplitude modulation procedures. To experiment with these procedures, grab a copy of the orchestra and score files above, or of others in this chapter, and try adding some modifications or additions to these algorithms, or else create your own instrument algorithms and scores based upon the models provided here. In ex3-2, for example, you might replace the bare-boned linen envlope generator on line 14 with a more powerful amplitude envelope created with envlpx or expseg. (This may require adding some additional score p-fields. You might create some other audio waveforms in addition to, or in place of, f1 and f2 for the modulating oscillator, or change the carrier oscillator waveform from the current sine wave to something more complex. Or, the pitch of the modulating and/or carrier oscillators might be varied by a control signal. A stereo version of the instrument could be created with an added p-field for spatial localization between right and left channels :

outs sqrt(p13)*a1, sqrt(1-p13)*a1I

Our final amplitude/ring modulation example illustrates some transformations that could be applied to a vocal tone with sub audio amplitude modulation and audio rate ring modulation. The instrument uses unit generator soundin to stream a soundfile from the disk into Csound for processing. Before we can use this orc/sco pair, we need to create a link file that points to the desired soundfile, which in this case is /sflib/voice/bass2.d3.wav. We can do this with the command

         sflink  /sflib/voice/bass2.d3.wav  4       or else       sflinksfl  bass2.d3.wav  4 
This will create a link file named soundin.4 in our current working soundfile directory. The suffix 4 is referenced in p4 of our score to access this soundfile.
;  #############################################################
;  soundfile ex3-3 : sub audio amplitude & ring modulation of a soundfile
;  #############################################################
  ;  score p-fields:
  ;  p4 =  soundin.# number {source soundfile} , p5 = skip time into soundfile
 ; p6 = amplitude multiplier for input soundfile
 ; p7 = fade-in time, p8 = fade-out time
 ;  amplitude modulation :
 ;     p9 =  opening % of audio signal to be modulated
 ;     p10 =  closing % of signal to be modulated
 ;     p11 =  modulating frequency (can be sub-audio or audio rate)

instr 1
asig  soundin  p4, p5    ; read in a soundfile
    ; add a fade-in/fade-out amplitude envelope & multiplier
   p7 = (p7 = 0 ? .0001 : p7)  ; protect against illegal 0 values
   p8 = (p8 = 0 ? .0001 : p8)  ; protect against illegal 0 values
kamp expseg  .01, p7 , p6 , p3 - (p7 + p8) , p6 , p8 , .01
asig  =  asig * kamp

   ; apply amplitude modulation to these samples
kmod linseg   p9, .1*p3, p9, .4*p3,p10,.5*p3,p10 ;  controls % of signal to be modulated
  knomod =  (1. - kmod )        ; non-modulated 
  ampmod   oscili  kmod , p11 , p12   ; amplitude modulation oscillator
asig =  (knomod * asig)  +  (ampmod * asig)
out  asig
endin
-------------------------------------------------------

Score for above instrument :
-------------------------------------------------------
<< Score file used to create soundfile "ex3-3"
<< For this orc/sco pair to work you must first type in a shell window:
<<             sflinksfl  bass2.d3.wav 4
<< this creates a link file called soundin.4, pointing to
<< /sflib/voice/bass2.d3.wav, which is used in p4 of this score
*f1  0 2048 19 1 .5 0 .5;  <  unipolar sine wave for subaudio amp. mod
*f2  0 2048 10 1 ;  <  bipolar sine wave for ring mod.

i1 0 0  7;   < create 7 output "notes"
p3 nu 5;
du 304.8;   < output duration of each note is 4.8 seconds
p4 4 ;   < soundin.# number : soundin.4 points to /sflib/voice/bass2.d3.wav
p5  0;             < skip time into soundfiles
<  fade-in/fade-out envelope and amplitude scalar
p6 nu .3/ .9/.5*5;          < multiplier for output amplitude
p7 nu .2 * 3/ .1 * 4;           < amp. fade-in time in seconds
p8 nu .5 * 3 / .25 * 4;         < amp. fade-out time in seconds
< amplitude modulation :
p9 nu 0;           < opening % of signal to be modulated
p10 nu 1.;        < ending % of signal to be modulated
p11 nu 6. / 15. /    < frequency of amplitude modulator oscillator
 36.7/146.8/220/1022/2200;
p12 nu 1//2*10;  < function for modulator waveshape, 1 = unipolar, 2=bipolar
end;
-------------------------------------------------------

This is another didactic example. Each of the 7 notes begins with the unaltered bass tone, then over 40 % of the note duration "cross-fades" top 100 % modulation. The first two notes create a sub audio tremolo, respectively at 6 hertz and 15 hertz (p11). The final 5 notes employ audio rate ring modulation to produce sidebands.

The fundamental frequency of the bass2.d3, d3, is 146.8 hertz. I have chosen the 5 frequencies for ring modulation carefully, so that each bears a harmonic or near-harmonic ratio to the fundamental frequency of the bass tone:

modulating               bass tone          ratio of modulating  
frequency (p11)        fundamental freq.    frequency to bass tone fundamental
36.7                     146.8              .25  
146.8                    146.8              1. (unison)
220                      146.8              1.498                         
1022                     146.8              6.96                     
2200                     146.8             14.98
For this reason each of the ring modulated tones has a well-defined pitch, as well as a somewhat hollow but also metallic quality that is characteristic of ring modulation.

3.2. Frequency (phase) Modulation

Frequency modulation has been a widely used resource in electroacoustic music for more than thirty years. The classic article on uses of F.M. for timbral control is "The synthesis of Complex Audio Spectra by Means of Frequency Modulation" by John Chowning, who first explored these procedures in the 1970s. During the 1980s Yamaha's popular dx, tx, tz and sy series synthesizers employed frequency modulation as the principal means of generating timbres.

To summarize F.M. briefly :

FM can be employed either to produce periodic pitch fluctuations, such as vibrato, or else a wide range of harmonic and inharmonic timbres, although these timbres will tend to share a "signature" FM quality.

One or more modulating oscillators are created, and the resulting control signal(s) then are added to the sampling increment (S.I.) of a carrier (audio) oscillator. This added value to the S.I. causes the carrier oscillator to skip around, back and forth, within the table, rather than marching straight through the table from beginning to end for each pitch cycle. For this reason, digital F.M. is sometimes called phase modulation, since positive and negative values from the modulating oscillator are added to the current phase (position) within the function table. (Actually there are subtle differences between F.M. and true phase modulation.) The greater the amplitude of the modulating oscillator, the greater the forward and backward jumps within the function table, and the greater the resulting change in pitch or timbre. In "classical F.M.," the waveshapes of both the modulating and carrier oscillaotrs are sinusoids.

There are two distinct types of F.M. : sub-audio rate and audio rate. When the frequency of the modulating oscillator is in the sub-audio range (below about 16 hertz), we can hear the individual variations in frequency. If the modulating oscillator is producing a smooth-shaped, symmetrical waveform, such as a sinusoid or triangle wave, a vibrato results. If the modulator is producing a square wave (with abrupt alterations between two values), a trill results.

When the frequency of the modulator is in the audio range (above 20 hertz), however, sidebands (sum and difference tones) are created, resulting in a more complex timbre, and often a perceived change in the pitch. The timbral change is usually more drastic than with amplitude modulation. Whereas a sine wave amplitude modulating another sine wave will produce a single set of sidebands (the sum and difference frequencies of the carrier and modulator), a sine wave frequency modulating another sine wave generally will produce several sets of sum and difference tones:

c+m, c-m :(carrier frequency plus the modulating frequency ; carrier minus mod. frequency)
c+2m, c-2m :(carrier freq. plus double the mod. freq. ; carrier minus two times the modulating frequency)
c+3m, c-3m : (carrier freq. plus and minus three times the modulating freq.)
c+4m, c-4m : (carrier freq. plus and minus four times the modulating freq.)
etc.

The table below shows the first four upper and lower sideband frequencies that result when a modulating frequency of 200 hertz is applied to carrier frequencies of

        100 hertz (a c:m ratio of 1:2)
        101 hertz (a c:m ratio of 1.001:2, or  1:1.980198)
         74 hertz (a c:m ratio of approximately 2.7)

---------------------4th order--------------------
| -----------3rd order------------------ |
| | ------2nd order----------- | |
| | | ----1st order-- | | |
| | | | | | | |
| | | | (carrier) | | | |
c-4m c-3m c-2m c-m c c+m c+2m c+3m c+4m
---- ---- ---- ---- --- ---- ---- ---- ----
-700 -500 -300 -100 100 300 500 700 900
-699 -499 -299 -99 101 301 501 701 901
-726 -526 -326 -126 74 274 474 674 874

How many of these sidebands will be present in the modulated signal will depend upon the depth of the modulation. We are familiar with subaudio "wide vibrato" (where the pitch alternately is raised and lowered by a significant amount, such as a semitone or more on both sides of the center pitch), and of "narrow" vibrato, in which the upward and downward pitch deviation is smaller and more subtle. In audio rate F.M. the depth of modulation is very wide indeed -- generally more than an octave above and below the base carrier frequency.`

The term modulation index is a measure of the depth of audio rate frequency modulation, indicating how much "distortion" (or "non-linear waveshaping") is applied to the carrier signal. table. Technically, "modulation index" is defined as

the ratio of the peak frequency deviation to the modulating frequency.

For example, if the modulating frequency is 440 hertz, and it drives the carrier frequency upward and downward by 880 hertz, the modulation index is 2:

       Peak Deviation / Modulating Frequency = Index
            880       /          440         =  2.0
As we raise the modulation index, more sidebands appear, at further distances above and below the carrier frequency, and the timbre becomes denser and brighter.

The following simple F.M. instrument and score illustrate basic facets of both sub audio and audio rate F.M.:

;  #############################################################
;  ex3-4   :  Simple F.M. instrument   Eastman Csound Tutorial
;  #############################################################
nchnls=1
instr 1
ipitch = cpspch(p4)  ; carrier frequency
imodwave=p10  ; p10 points to the function number for the modulating waveshape

kenv expseg  1 , p6 , p5 , p3 - (p6+p7), p5, p7, 1 ; amplitude envelope

amod  oscil3   p9*ipitch,  p8,  imodwave   ; modulating oscillator
acar  oscil3  kenv,  ipitch + amod, 100  ; carrier oscillator
icmratio= p8 / ipitch ; ratio of the modulating freq. to the carrier freq.
print ipitch,p8,icmratio ; print out carrier & modulating freqs. & c:m ratio
    out acar
endin
--------------------------------------------------------------
< score11 input file used to create  soundfile "ex.3-4" :
* f100 0 1024 10 1.;     < sine wave for carrier & vibrato
*f101 0 64 7 0 31 0 1 1. 31 1. 1 0 ; < square wave for trill
*f102 0 128 7   0 25 0  0 2. 26 2. 0 0 26 0 0 -1 26 -1 0 1. 25 1;< turn

i1 0 0 10;
p3 3;
du .96;
p4 no a3;
p5 8000;		< amplitude
p6 .2;			< attack time
p7 .5;			< decay time
 < p8 = modulating frequency
p8 nu 4.2/6./2. / < subaudio rate
             220/ 73.26/ 311.  / 330/339.0/446./ 1125; < audio rate
< c:m ratio: 1:1|1:.33| 1:1.414| 1.5| 1.54 |2.027| 5.11

p9 nu .06/.12/.122/          < depth of modulation (*p4 frequency)
 3.*7;    < much higher modulation depth to create audio rate sidebands
  < p10 = func. num. for modulating waveform
p10 nu 100/101/102/       < subaudio: sine/trill/turn/
 100 * 7;  < sine wave for audio rate modulation
end;
--------------------------------------------------------------

Comments on ex3-4 :

The first three notes produced by this score create subaudio FM, while the final seven notes create audio rate FM, in which the sidebands alter the timbre and in some cases also the perceived pitch. For all 10 notes the pitch of the carrier oscillator is set to a3 (220 hertz) in p4 and is given the variable name ipitch.

Let's take a close look at how vibrato is created in the first note. Here is the code for the modulating oscillator:

amod oscil3 p9*ipitch, p8, imodwave
The frequency of the modulator is set in p8 (4.2 hertz for the first note), and the amplitude of the modulator is set to p9 * ipitch, or .06*220= 13.2 hertz for the first note. Since the modulating oscillator is reading a sine wave table (p10, and the resulting variable imodwave point to f100), its output, the variable amod, will vary sinusoidally between +13.2 and -13.2 hertz at a rate of 4.2 times per second. This modulating signal is added to the base pitch (220 hertz) of the carrier oscillator:
acar oscil3 kenv, ipitch + amod, 100 ; carrier oscillator
The resulting pitch vaibrato will vary sinusoidally between 233.2 (220+13.2) hertz and 206.8 (220-13.2) hertz. The depth of modulation is about a semitone above and below the base carrier pitch.

The following table sumamrizes the values for the modulating frequency, carrier-to-modulator frequency ratio (for audio rate modulation), modulation depth, and resulting modification in the carrier wave for all ten notes in the score:

Note   Mod.     c:m    Mod.        Mod.                      Result
       Freq.   ratio   depth     waveform
-----------------------------------------------------------------------------------
  1     4.2     --      .06    f100 (sine)     semitone vibrato
  2     6.      --      .12    f101 (square)   trill 6 times/second
  3     2.      --      .12    f102 (turn)     turn figure twice per second
-----------------------------------------------------------------------------------
  4    220.      1       3     f100 (sine)     perceived pitch: a3
  5    73.26    .33      ""     ""             perceived pitch: d2
  6    311.    1.414     ""     ""             inharmonic
  7    330.     1.5      ""     ""             perceived pitch: a2
  8     339    1.54      ""     ""             inharmonic
  9    446.    2.027     ""     ""             perceived pitch: a3 with tremolo
 10    1125.   5.11      ""     ""             a3 w/ strong, sharp 5th harm.
The second note produces a whole step upward trill while the third note introduces a 5-part turn figure that encircles the carrier pitch twice per second.

For the seven audio rate output notes (notes 4-10) note that:

For note 4, the modulating oscillator varies sinusoidally in frequency between 880 (220 + 660) hertz and -440 (220 - 660) hertz 220 times per second, and sideband components up to about 880 hertz will be significant components of the resulting spectrum. Notice that the final note will drive the carrier oscillator frequency quite high, creating high frequency components in the spectrum and a resulting hollow sound.

3.2.1. Unit generator foscili

Because "classical" F.M. procedures have been so common, Csound provides a unit generator, named foscili which combines the modulating and carrier oscillators into a single unit generator. The arguments to foscili are :

In example ex3-4 above, the modulating and carrier oscillators

amod  oscil3   p9*ipitch,  p8,  imodwave 
acar  oscil3  kenv,  ipitch + amod, 100
could be replaced by a call to foscili, and our instrument would look like this:
instr 1 ;  orchestra file ex3-4 modified to use foscili ---------
ipitch = cpspch(p4)  ; carrier frequency
imodwave=p10  ; p10 points to the function number for the modulating waveshape
kenv expseg  1 , p6 , p5 , p3 - (p6+p7), p5, p7, 1 ; amplitude envelope
acar  foscili  kenv,  ipitch,  1,  p8,  p9,  100
icmratio= p8 / ipitch ; ratio of the modulating freq. to the carrier freq.
print ipitch,p8,icmratio ; print out carrier & modulating freqs. & c:m ratio
    out acar
endin ; ------------------------

We also would need to eliminate the first three notes from our score (foscili is designed only for audio rate FM, not for sub audio rate modulation). Additionally, we would need to modify p8 in our score file so that instead of providing the modulating frequency it provides the modulator-to-carrier frequency ratio. Our modified score would look like this:

*f101 0 64 7 0 31 0 1 1. 31 1. 1 0 ; < square wave for trill
*f102 0 128 7   0 25 0  0 2. 26 2. 0 0 26 0 0 -1 26 -1 0 1. 25 1;< turn
i1 0 0 7;
p3 3;
du .96;
p4 no a3;
p5 8000;                < amplitude
p6 .2;                  < attack time
p7 .5;                  < decay time
p8 nu 1/.33 /1.414 /1.5 /1.54 / 2.027 /5.11;  < c:m ratio
p9 3.;   < modulation index (depth)
  < p10 = func. num. for modulating waveform
p10 nu 100;   < sine wave for audio rate modulation
end;

Tones produced in the lower registers of most instruments contain a richer spectrum -- more partials -- than tones produced in higher registers. Thus, it usually is best to use higher modulation index values for lower pitched tones, and to reduce the modulation index for higher pitched tones. It can be tedious to do this by hand, but it is possible to build in scalar controls (discussed later) to automate this adjustment.

3.2.3. Time varying modulation index envelopes

So far we have used a constant (init value for the modulation index, and the resulting timbres have remained fixed, and therefore rather "dull," "artificial" or "electronic-sounding," not unlike the fixed waveform oscillator examples in chapters 1 and 2. However, an important resource of F.M., or of any modulation procedure, is the ability to vary the index (modulation depth) with an envelope to produce changes in spectral "brightness" within a tone. Typically, an F.M. index will resemble an amplitude envelope (though with lower values, of course), since our perception of "loudness", and of note articulations, accents and phrasing, often are psychoacoustic responses to changes in both amplitude and in spectrum (especially in high frequency energy). However, timbres often are more interesting if the amplitude and modulation envelopes do not coincide too precisely.

Generally, we construct modulation depth envelopes to simulate spectral changes (changes in the percentage of high and low frequency energy) that occur within acoustic sounds. Idiophonic and membranophonic sounds (including pizzicato and piano tones), for example, generally contain the greatest amount of high frequency energy at the very beginning of the attack (the click, scrape or pop which articulates the onset of the sound). Thus, an FM index for such a sound should begin at a high value, decrease rapidly after the attack and then taper off further during the rest of the tone. In bowed string, aerophone and vocal tones, by contrast, higher partials typically have a longer rise time, and a more rapid decay, than lower partials, with the timbre reaching its brightest point midway through the tone. Crescendi require an increase in the modulation depth, while decrescendi call for a decrease in the index.

Here is an F.M. instrument that, in addition to the amplitude envelope, includes a second envelope that controls changes in the F.M. index:

 #############################################################
 soundfile ex3-5  : "Classical" FM with modulation index envelope : ECMC Csound Tutorial
 #############################################################
; FM - single carrier, single modulator "classical fm" instrument
; p3 = duration ; p4 = pitch (cps or pch); p5 = amplitude
; p6 = attack time ; p7 = decay time ; p8 = atss amplitude multiplier
; frequency modulation : p9 freq. ratio of modulator to carrier
; fm index envelope values : p10 beginning; p11 after p6; p12 before p7
nchnls = 1

instr 38
ipitch = ( p4 > 13.0 ? p4 : cpspch(p4) ) ; conditional evaluation of p4
p8 = (p8 > 9.99? p8 : p8*p5) ; p8 can be an absolute value or * p5
kamp expseg 1, p6, p5, p3-(p6+p7), p8, p7, 1 ; amplitude envelope

kfmindex expseg p10, p6, p11, p3-(p6+p7), p12, p7, .3*p12 ; fm index envelope

a1 foscili kamp, ipitch , 1, p9, kfmindex , 100
out a1
endin
--------------------------------------------------------------
< score11 input file used to create soundfile "ex3-5" * f100 0 1024 10 1.; < SINE WAVE i38 0 0 4; p3 4.; du 304; < du = Duration p4 no ef3; < p4 = frequency : use notes,pch or cps < amplitude envelope p5 nu 10000/1500/10000//; < p5 = amplitude p6 nu .05/ 1. / .08/ .05; < p6 =Attack time p7 nu 3.8 /.2 / 3.4 / 3.6; < p7 = Decay time p8 nu .5 /7.0 / .6 / .4 ; < atss - amplitude before p7 < absolute or *p5 if < 10.00 < F.M. values : p9-p13 p9 nu 1.005/1.996/1.4/2.7; < modulating frequency (*p4 or fixed if > 9.99) p10 nu 7. / .1 / .5/8.; < Index at beginning p11 nu 2. / .4 /1.6/3.5; < Index after p6 p12 nu .8 / 4.5 / .5/ .8; < Index before p7 end --------------------------------------------------------------

Note, in this example, that we can change the value of any score p-field except p2 (note start time) within an orchestra file, as in the following conditional evaluations of p4 and p8:

ipitch = ( p4 > 13.0 ? p4 : cpspch(p4) ) ; conditional evaluation of p4
p8 = (p8 > 9.99? p8 : p8*p5) ; p8 can be an absolute value or * p5
Our discussion of conditional evaluations just below will clarify how these two lines work.

By this point some of you may have had enough of FM, thank you very much. But for those who would like to try some extensions to "classical FM" procedures, Appendix 2 provides examples of multiple carrier and multiple modulator algorithms, through which one can create more complex timbres that, unlike simple FM sounds, don't generally sound as though they have a sinus infection.

Logical operations, comparisons and decision making

Logical operations enable an algorithm to evaluate a variable or expression and, based upon this evaluation or comparison, to perform one of two or more operations or sets of several related operations. One might liken such decision-making operations to forks in a path. The principal types of logical operations available within Csound 5 are conditional statements and if...else constructions.

3.3.1 Conditional Statements

Our instruments thus far have assumed that the frequency argument to oscillators will be given in the score either in cps or in pch format. Suppose, however, that we would like to be able to use both of these types of frequency input within an instrument, sometimes feeding it cps, but at other times a pch value. We could, of course, create two nearly identical copies of our instrument algorithm, one accepting cps input, the other pch. But this would clutter our directory with needless extra files. Besides, what if we want to use both cps and pch inputs within a single score?

The solution is to build a little "intelligence", or decision-making ability, into our algorithm, so that it can evaluate the input data we provide and make suitable decisions on how to interpret and operate upon this data. Conditional values enable instruments to make such decisions. The first example in the Csound manual :

(a > b ? v1 : v2)

is read by Csound (and by most other programming languages) as follows :

Is "a" greater than "b" ? If so, return (use) value 1 : Otherwise, return value 2.

In ex3-5 we included the following conditional variable within our algorithm to enable our instrument to distinguish between cps and pch data:

               (a    >   b      ?   v1   :  v2)
      ipitch = (p4   >   13.0   ?   p4   :  cpspch(p4)  )
      asound oscili kenv , ipitch , 100

The highest pitch class C on the piano (4186 hertz) is specified as 12.00 in pch notation. With most algorithms it is highly unlikely that we ever would want to call for a pitch more the one octave higher than this note (13.00 in pch notation, or 8372 hertz). Likewise, we cannot hear fundamental frequencies below about 20 hertz, so it is uncommon to call for a fundamental frequency below 13.0 hertz. (However, using subaudio fundamentals with rich waveforms that contain higher partials which are in the audible range can produce many intriguing types of pulsating, throbbing and motoristic sounds. See ex4-4.)

In the example above, therefore, we define the variable ipitch as follows :

If p4 is greater than 13.00, assume that it is already in cps format, and assign this value to the variable ipitch. However, if p4 is less than 13.0, assume that it is in pch format. Convert this p4 value to cps, and assign the resulting cps value to ipitch.

Note that the whole evaluation operation to the right of the "=" (assignment) sign must be enclosed in parentheses. The parentheses tell the compiler to do nothing until the entire expression within the parentheses has been evaluated.

In addition to the > ("greater than") sign, the other comparison symbols that can be used in such evaluations are :

               <       less than
               >=      greater than or equal to
               <=      less than or equal to
               ==      equal to
               !=      not equal to

Here's another example :

irise = (p6 > 0 ? p6 : abs(p6) * p3
idecay = (p7 > 0 ? p7 : abs(p7) * p3
kenv expseg 1, irise , p5 , p3 - (irise + idecay) , .5* p5, i3, 1
a1 oscili kenv , i1, 1

Here we tell the compiler to evaluate the p6 (amplitude envelope rise time) and the p7 (decay time) score values. If p6 is positive (greater than 0), use this value for attack time. If p6 is negative (less than 0), however, use the absolute value of p6 (ignore the minus sign) as a multiplier for (or decimal percentage of) p3. Thus, a p6 value of -.33 would return a value of .33 * p3, or an attack time lasting one-third the total duration of the note. If our score provides a p7 value of -.5, the decay would last one-half the total duration of the note. It also would be possible to mix these two types of values in our score:

p6 nu .25*3 / -.4 / .1 / -.5;

One more example: We have an instrument for which p5 specifies the amplitude for an audio oscillator, as has been our custom. We would like to be able to provide these amplitude values either in integers, on a scale of 0 to 32767, or else in floats, on a scale of 0 to 1., where float 1.0, like integer 32767, represents maxamp. Here is one way to give ourselves this option:

    iamp = (p5 < 10 ? p5*32767 : p5)
Now we can supply the variable iamp, which will be on a 16 bit integer scale (0 to 32767) to the amplitude input to our oscillator(s).

3.3.2. if...else constructions

There is another way to build conditional (alternative) operations into an instrument that is particularly useful when more than two alternatives are possible, or when several operations must be performed when a given condition is met. If...elseif...else constructions, which are available in most programming languages, enable us to build several alternative "paths" into an algorithm while still keeping the code clean and readable.

Suppose that we want to be able to specify pitch in p4 either in terms of pch notation (probably using score11's notes keyword), OR in terms of MIDI note number, OR directly in cycles per second (hertz). To accomplish this, we first need to break this three-way decision into a series of true-false logical operations, and then implement these operations within an if...else construction.

pch values typically range between about 4.00 (an octave below the lowest C on the piano) up to 12.00 (the highest C on the piano). MIDI note numbers range between 0 and 27, but values below 21 (the lowest A on the piano) are rare. Therefore, we can choose to reserve p4 values between 0 and 13.0 for pch values, numbers between 13.01 and 127 for MIDI note numbers, and negative values for hertz. Here is an if...else code that implements this process:

1   if (p4 > 0 && p4 <= 13)  then ; p4 is given in pch
2      ipitch = cpspch(p4)   ; converts pch to cps
3   elseif (p4 < 0) then   ; negative p4 = cps
4      ipitch = abs(p4)
5   elseif (p4 > 13 && p4 <= 127) then ; p4 gives MIDI note number for the note
6      ioct = (p4/12) + 3.  ; converts MIDI note number to octave decimal
7      ipitch =  cpsoct(ioct) ; converts octave decimal to cps
8   else
9      printks "ERROR. p4 value is out of range. Aborting.", 1
10      print p4
11      turnoff ; abort compilation of this note
12  endif
13  print p4, ipitch

On line 1 p4 is evaluated. If this number is greater than zero and and less than or equal to 13.0 it is converted to hertz on line 2. Upon arriving at a true condition, performing any oeprations that follow, and then encountering an elseif or else statement, the compiler has completed this if...else operation, and skips ahead to the endif statement that terminates the construction. However, if the evaluation on line 1 is false, Csound skips ahead to the next elseif or else statement, which occurs on line 3. Another evaluation is made of the current value of p4, this time whether or not the value is negative ("less than zero"). If so, the value is stripped of its minus sign on line 4 by the abs opcode, so that -400 becomes 400, the value of 400 is assigned to the variable ipitch, and the compiler moves forward to the endif statement.

Still no luck? In this case the compiler moves from line 3 to line 5 and determines whether p4 lies between 13.001 and 127. If true, the compiler converts the p4 value in a two-step process into cps on lines 6 and 7, then skips ahead to the endif statement. Finally, if none of the conditions on lines 1, 3 or 5 have been met (for example, if p8 is set to 300, or to 0, the compiler reaches the "last resort" else statement on line 8, and executes the code on lines 9 through 11. These lines print out an error message along with the invalid value of p4 and then shut down the copy of the instrument for this note. This does not abort the entire Csound job, but only the compilation of this note.

If...else constructions can be simpler than the one above, sometimes consisting of a single if statement, operations to be performed if the statement is true, and an endif. If the statement is not true, the compiler does nothing. On the other hand, it is possible to include entire subroutines with many lines of code after an if, elseif or else statement.

Here's another example:

We want to create a passage in which most of the notes last between 2 and 5 seconds, and have slow amplitude attacks and decays that take up much of each note's total duration. However, using a random selection process in score11, we want about 20 % of the notes to be much shorter, lasting only between .2 and .3 seconds, and to have rapid attacks with a quick initial decay. These "grace notes" will be shorter than the combined durations of the note attacks and decays we are specifying in p6 and p7, and thus will result in annoying clicks and blips. Here is a solution that allows us to override the attack and decay times specified in our score:

if (p3 < .31) then  ;for short notes, ignore p6 & p7, create a rapidly decaying envelope
   kampenv expseg .01, .2*p3, 1, .3 * p3, .3,  .5*p3, .005
else                  ; for longer notes, apply a "smoother" envelope
  iattacktime = p6
  idecaytime = p7
   kampenv expseg .01, iattacktime, p3, p3-(iattacktime + idecaytime), 1, idecaytime, .005
endif
kampenv = kampenv * p5

Note: Whenever possible, it is best to put only operations involving i (init) values within if...else constructions, rather than control and audio rate signal operations, as we have done (of necessity) in the example above. When only i operations are involved, the logical operation will only be made once, rather than on every control signal.

[Historial note: Before the introduction of if...else constructions into Csound several years ago it was necessary to perform logical operations like those illustrated above with an older, more cumbersome system of goto opcodes, which required a system of labels and jumping around, sometimes backwards as well as forward, in the code and making it more cluttered and more difficult to read. For backwards compatibility (so that older instruments will still work, and so that Csound users familiar and comfortable with these goto opcodes can continue to use them), and also because on rare occasions they still can be useful, these goto opcodes have been retained in Csound 5, and occasionally you may encounter them when looking at Csound orchestra files.)

3.3.3. Scaling Values By Register

Here is a common problem in synthesis. Low pitched tones generally require more energy (amplitude) than middle and higher register tones in order to sound equally "loud." If we are working with a group of, say, cello multisamples, all normalized to near maxamp, and are creating a melody with these samples that has a wide pitch range, and specify approximately equal amplitudes for all of the notes, the low tones may sound pallid and "weak." We could waste a lot of time on "custom cabinetry" while working on our score, fussing with the amplitude values for each note in trying to create a smooth dynamic arc, without wolf tones that pop out and other melodic notes that "hide in the shadows." It would be a great relief if our instrument algorithm could relieve us of this drudgery. Often, we can accomplish this by creating an amplitude scalar.

ipitch=(p4 < 13 ? cpspch(p4) : p4) ; pitch given in cps or pch
iamp = (p5 < 10 ? p5 * 32767 : p5) ; p5 amplitude can be given in floats or ints
ioct = octcps(ipitch) ; convert from hertz to octave decimal notation where
       ; c4 = 8.0, fs4 = 8.5, c5 = 9.0, c3 = 7.0, c2 = 6.0 etc.
iscale = (18.0 - ioct) * .1 ; amplitude scalar; c4 = 1.0, c5=.9, c3=1.1, c2=1.2 etc.
;  iscale=iscale*iscale ; remove comment to increase the scaling
iamp = iamp * iscale ; apply the scalar to amplitude; progressively boost the amp. of notes
         ; below c4 and progressively attenuate the amplitude of notes above c4 

The variable ioct on the third line above converts the frequency of the note (the variable ipitch) into octave decimal notation, where the octave is divided into 100 equal steps and e4 has a value of 8.25, fs4 has a value of 8.50, a4 has a value of 8.75 and so on. Next, the variable iscalesubtracts the current value of ioct from 18.0s. For middle C (ioct = 8.0) the value of iscale will be 1.0. For c5, an octave higher, it will be .9. For c3, an octave below middle C, it will be 1.1. In other words, the lower the note, the greater the value of iscale.

The amplitude value for each note then is modified by this scalar

          iamp = iamp * iscale
so that lower notes get a boost and higher tones are attenuated. If this proves inadequate -- if the lowest tones still sound too weak -- we could remove the comment from the line
    ;  iscale=iscale*iscale ; remove comment to increase the scaling
This line will square the iscale value, exaggerating the amplitude boost for low notes and further attenuating high notes -- something that could be very useful for processing certain bellowing operatic singers who come to mind. However, we must be careful not to suck the life out of higher register tones by doing this.

To compound this issue, however, certain acoustic instruments, such as the guitar, sound thin in their higher registers, and could benefit from an amplitude boost above, say, a4 (A 440 sounding), as well as a modest boost in its lowest register. Such instruments would require us to devise a different scalar.

Timbral scaling is sometimes even more important than amplitude scaling in achieving a "well-modulated" instrument. In general, the lower the pitch, the greater should be the strength of its higher partials. This acoustic principle holds true for most orchestral instruments. A flute, for example, produces somewhere between eight and ten significant partials for notes around middle C, but only about four significant particals for notes a couple of octave higher.

With some instruments, scaling by register of other parameters, such as vibrato or tremolo depth, is also helpful. One reason that some synthesizer and sampler "patches" (timbres) work well only within a restricted portion of the keyboard range (say, over an octave or two), and sound progressively more artificial, flaccid or irritating as one approaches the keyboard extremes, is that constants are used for such parameters.

3.4 An instrument to granulate soundfiles

Our final instrument algorithm in this chapter, ex3-6 employs conditional values and an if...else construction. The algorithm is more complicated than most of those we have presented so far, so take a deep, cleansing breath, and if the going gets tough take a break for a glass of fine French wine, or perhaps a Hersey bar, or perform some tai chi. If you understand the overall logic of this algorithm and its score p-fields you will be able to use it, or to use it as a model for an algorithm of your own design, even if you do not understand the full inner workings of each line of code.

One of the first steps in designing many instruments is determining exactly what capabilities you want it to have. Generally, the more capabilities, the more complicated the algorithm will become.

This instrument is designed to read in monophonic soundfiles at any sampling rate, and to produce an output soundfile at any rate. (In other words, sample rate conversion will be performed if necessary so that, for example, we can use 44.1k input soundfiles with a 96k output sampling rate and everything will work as it should).

The user should be able to specify a time within the input soundfile to begin reading (that is, a skip value), and also an end point anywhere later in the file to end reading, so that if we wish we can extract precisely defined segments of the input soundfile, a feature especially useful if we want to extract a particular segment of a spoken or sung text. The user should be able to specify the start read and end read points either in seconds, or, alternatively, as percentages of the total duration of the input soundfile (e.g. begin reading 25 % of the way into the soundfile and stop reading 55 % of the way into the soundfile). The user should not be bothered with the task of figuring out what the resulting p3 output duration should be for each note. In her score file she should be able to provide a dummy value for the duration of each note and let the instrument figure out the required output duration for the desired segment and alter this dummy value.

Pitch tranposition should be provided, but this can raise problems, since this also will alter the duration of the chosen segment. The instrument should take care of this annoyance, so that only the begin read to end read times specified in the score will be played, regardless of the pitch transposition.

However, in certain cases the user should have the option to specify the exact p3 output duration she wants, even though this means that the instrument may need to read less of or more than the segment specified by the start read and end read arguments. For example, we might want to string together several input notes into a continuous monophonic output, with no overlapping of notes or silences between notes that could result if the instrument alters the user's p3 durational values.

Finally, in cases where the output soundfile does not sound as expected, the user should have the option of printing diagnostic information about the parameter values the isntrument is using for a note, which could be helpful in determining why she is not getting the desired result. However, for most notes, there is no reason to print all of this information.

Note that in order to implement all of these options our instrument algorithm will need to incorporate several if...else and conditional constructions.

The key players within this instrument are unit generators table3 and phasor, and score file function table generator gen01. One of the first issues we must address is how best to read soundfile samples into Csound with this orchestra. Csound provides several ways to do this -- by streaming samples from the hard disk (soundin and diskin2, by means of oscillators such as oscil3 that are designed to read tables filled with soundfile samples rather than synthetic waveforms, and by using table-reading opcodes such as table3. We may be skipping around a lot within our input soundfiles with this instrument, so diskin2 and soundin, which are comparatively slow in jumping around in a soundfile, are not good choices here.

The best choice for our current purposes is the table-reading opcode table3, which reads in the values from a function table loaded into RAM. Like oscil3, table3 employs high quality cubic interpolation between consecutive table sample values to produce very high audio signal resolution. (Csound unit generators table and tablei work similarly, but for our pitch transpositions we will want the extra audio resolution provided by table3.) The companion opcode phasor provides table3 with a sample-by-sample moving pointer to locations within this table. Its output tells table3 which adjacent values within the table to use in calculating each output sample.

In our score file, we will need to provide a function table definition for each soundfile that we might wish table3 to read. To copy the samples from a hard disk soundfile into a function table loaded into RAM, we employ Csound function table generator GEN01, which is designed expressly for this purpose. In ex3-6 we will employ a single input soundfile, the stirring spoken recitation file /sflib/x/voicetest.wav.

On the ECMC systems the easiest way to create a gen1 function definition of this soundfile for inclusion within a score11 input file is with the local script

        mksffuncs     ("makesoundfile function definitions").
Typing: mksffuncs voicetest
will return the gen1 function definition statement below, which we have copied into the beginning of our score11 input file:
(f#  time  size gen01     filcod               skiptime format channels) 
* f1 0 524288 -1 "/sflib/x/voicetest.wav" 0     0     0 ; < dur = 7.27

The -1 (minus 1) call to gen1 in the fourth argument above tells gen 1 not to normalize the samples when loading them into the table. Had we placed a 1 instead of -1 in this argument, gen01 would have rescaled the sample values so that the highest input sample value in the table would have been maxamp (floating point 1. or integer 32767). This would require a little processing time. voicetest.wav already has a fairly high peak value, so there is no need to normalize the table values for this soundfile every time we run a Csound compile job with this score.

(Note: gen01 also is used to create function definitions for input soundfiles read by loscil family of Csound unit generators.
(Note also that the largest permitted table size, 16,777,216, will hold a 44.1k mono soundfile of 380.436 seconds, or about 6 minutes and 20 seconds duration.)
;  ########################################################
;  Orchestra file ex3-6   : reading in soundfiles with tablei & phasor 
; Orchestra file used to create soundfile examples 3-6-1 and 3-6-2:
;  #######################################################
 ; p4 : gen1 function number
 ; p5 : amplitude  {.001 - 10. = multiplier ; 10.1 - 32767 = new raw amplitude}
 ; p6 : skip time (if positive) OR (if negative, between -.001 & -.99) index
        ; into gen1 func
 ; p7 : OPTIONAL end time (if positive) OR (if negative, between -.001 & -.99) index
 ; p8  flag: if -1, soundfile read backwards from p7 to p6
 ; p9 flag: if -1, alter output dur. so only original p7to p8
 ; sample range is used when with transposition
 ; p10 : pitch multiplier ; p11 flag: if p11=1 print diagostics
 ; p12 = fade-in time, p12 = fade-out time
 ; p14 : stereo pan location {for stereo only}

nchnls= 1

instr 1
 ; --- [1] init values : get header information about input soundfile ---
isound = p4 ; gen01 function number of input sound
ilength = ftlen(isound) ; returns length of the gen01 input soundfile function
isfsr= ftsr(isound) ; sampling rate of input soundfile
isfdur   = ilength/sr ; duration in seconds of input soundfile
iamp    =  (p5 = 0 ? 1. : p5) ; amplitude multiplier; 0 = no attenuation or boost

 ; ----- pitch transpostion and sample rate conversion -------
; p10 specifies pitch transposition ratio of output to input pitch levels
itransp	 = (p10 = 0 ? 1. : p10 )
   ; if samp. rate of input soundfile does not match sr of orchestra,
   ; adjust phasor speed  for correct reading of input samples:
itransp = (isfsr == sr?  itransp : itransp * (isfsr/sr)) ; s.r. conversion
   iphspeed  = sr/ilength  ; phasor speed to read soundfile at original pitch

 ; ---- [2] get start and end points for reading input soundfile -------
 ; if p6 is negative, between -.001 and -1., it indicates % of soundfile skipped
 ; if positive, it indicates skip time in seconds
istart = (p6 < 0 ? abs(p6) *isfdur : p6) ; time in seconds to begin
                ; reading in samples from the input soundfile
index    =  (p6 < 0 ? abs(p6) : p6/isfdur)  ; beginning index into gen 1 table
iend = (p7 = 0 ? isfdur : p7)
iend = (iend < 0 ? abs(p7) * isfdur : iend)
 ; -----  optional adjustment of p3 output duration -----------
if (p9 = 1) then
  ioutdur = (iend - istart) / itransp ; compute actual output duration
else
   ioutdur = p3
endif

; ------[3]  optional printing of diagnostic information ---------
; if p11 = 1, print diagnostics about input soundfile & processing
if (p11 == 1) then ; flag to print diagnostics
 inumsamp = nsamp(isound) ; returns the number of samples in the input soundfile
 isfdur = inumsamp/isfsr ; duration of the inputsoundfile
; create terms for diagnostics that are more meaningful to the user:
   igen1func=isound
   inputsfdur=isfdur
   inputsfsr=isfsr
   istartread=istart
   iendread=iend
   ioutputdur=ioutdur
print p2, igen1func, inputsfdur, inputsfsr ; print diagnostic info.
print istartread, iendread, itransp, ioutputdur ; print diagnostic info.
endif
  p3 = ioutdur ; change score p3 value to computed output duration for note

; ----- [4] read in the soundfile segment either forwards or backwards -----
if (p8 != 1) then
 ; read soundfile forwards:
     apointer  phasor   iphspeed*itransp   ;transpose pitch by p10
     asound  tablei   (index + apointer)*ilength, isound  ;read the table at current phasor index 
else
; backwards:  ; read soundfile backwards beginning at p6 time
        ibacklength = p3 * itransp * sr
        ibackspeed = sr/ibacklength
        apointer  phasor  ibackspeed * itransp
        apointer = 1. - apointer
        iratio = ( ibacklength/ilength)
        apointer = apointer * iratio
        asound   tablei  (index + apointer) *ilength, isound 
endif

; - - -  [5]  apply fade-in and/or fade-out to guard against clicks - - 
ifadein=(p12 = 0 ? .001 : p12)
ifadeout=(p13 = 0 ? .001 : p13)
kampfades  expseg  .005, ifadein, p5, p3-(ifadein+ifadeout),1,ifadeout,.005
asound = asound * iamp * kampfades
; mono out:
 out  asound
    ; stereo out
    ;    outs sqrt(p14) * asound, sqrt(1. - p14) * asound
endin
-----------------------------------------------------------
< score11 file example  "ex3-6-1" :
* f1 0 524288 -1 "/sflib/x/voicetest.wav" 0 0 0 ; < dur = 7.27

i1  0 0 8;
< reseed
rs 7777;
p3  nu  1.5;
du  301.; < dummy value ; instrument will determine exact durations
   < p4 = gen1 function number 
p4  1;
   < p5 = output amplitude ;default 0 = same amplitude as input soundfile
p5  nu .33/.5/.75/1./ .9/.7/.5/.33;
   < p6 = skip time into gen1 func : if positive p6  = skip time in seconds
      < if negative {between -.001 and -.999}, p6 = % of soundfile skipped
p6  nu 0//.885//1.535// 3.32// 5.74//;
   < p7 = OPTIONAL time in input soundfile to STOP reading
      < if positive p7  = time in seconds
      < if negative {between -.001 and -.999}, = % of total soundfile duration
p7  nu .885//1.535//2.46// 3.88// 5.78//;
 < if p8 = 1 read soundfile backwards, beginning at p6 skip point or EOF
p8  nu 0/1; 
 < p9 flag: if p9=1 change p3 output duration so that only original
 < p6 to p7 sample range used
p9 1; < 0;
   < p10 = pitch multiplier {default 0 = no pitch change}
p10  nu 1/.9/1.122/.84/ .67/1.25/.95/1.05/ 1.33/.75;  
   < for stereo output only p12 = pan location {1. = hard left, 0 = hard right}
 < p11 flag: if 1, print diagnostics
p11 nu 1*2/0*8;
p12 .05;    < option fade-in time
p13 .05;    < option fade-out time
< for stereo output only p14 = pan location {1. = hard left, 0 = hard right
 < p14  
end; 
-----------------------------------------------------------

The instrument is divided into five main sections.

  1. In section [1] the instrument gets information from the header of the input soundfile that is specified in p4. Unit generator ftlen returns the length (number of samples) in the soundfile, ftsr returns its sampling rate (so that this can be compared with the output sampling ate that has been specified, and sampling rate conversion performed if necessary).
  2. In section [2] the start read and end read points within the input soundfile are calculated.
  3. Section [3] prints out diagnostic values if the user has requested this information by placing a 1 in p11.
  4. In section [4] the instrument reads in the samples from the table, either forwards or, if p8 is set to 1, backwards between the points specified in p6 and p7.
  5. In section [5] an optional fade-in and fade-out are applied. to create a stereo version of this instrument, all the user must do is change nchnls to 1, put a ; comment symbol at the beginning of the line
      ;   out asound
    and remove the comment from the beginning of the line
         outs sqrt(p14) * asound, sqrt(1. - p14) * asound
    and then use p14 in the score to specify a pan location for each note.

In score file ex3-6-1 above, note that the first two notes read between the beginning of the input soundfile (p6, which specifies the time in the input soundfile to begin reading, is set to 0) and .885 seconds (indicated in p7, which provides the time to stop reading.) The other 8 notes of the score similarly specify fragments of the soundfile to be played twice.

However, if p8 is set to 0 the soundfile is read forwards from the begin read point, but if p8 is set to 1 the file instead is read backwards from the "end read" point (p7) to the beginning read point (p6). Since the values in p8 alternate between 0 and 1, every other note is played "backwards."

p9 is a flag field. When set to 1, as in this score, it specifies that when pitch transposition is performed, as it is for most of the notes in this score, the duration of the note will be modifed so that only the range of samples between p6 and p7 be used. Pitch transposition ratios are provided in p10.

Example score file ex3-6-1 is the type of score file we typically create in order to test out an instrument, or to test additions or modifications to portions of an existing instrument, but it is unlikely to add much glitter to our professional reputation or to be of much use to us musically. Once we have tested and debugged the various features of an instrument with such a utilitarian score, however, it is time to move on to the creation of some "real" scores for the instrument, such as ex3-6-2 below (although perhaps some of you will have aesthetic objections to this next example, as well).

-----------------------------------------------------------
< score11 file example  "ex3-6-2, also  for orchestra file "ex3-6" :
* f1 0 524288 -1 "/sflib/x/voicetest.wav" 0 0 0 ; < 7.269 sec
* f2 0 65536 -1 "/sflib/perc/bongo1.roll.wav" 0 0 0 ; < 1.175 sec
* f3 0 131072 -1 "/sflib/env/iceskate.wav" 0 0 0 ; < 2.543 sec
* f4 0 1048576 -1 "/sflib/env/seagulls.wav" 0 0 0 ; < 13.124 sec
* f5 0 262144 -1 "/sflib/worldstring/guit.knock.groove.1.wav" 0 0 0 ; < 3.040 sec
* f6 0 131072 -1 "/sflib/perc/ratchet.wav" 0 0 0 ; < 1.961 sec
* f7 0 131072 -1 "/sflib/perc/sdrum1.broll.wav" 0 0 0 ; < 1.633 sec

i1  0 0 16;
< reseed
rs 6726;
p3  .5;
du  300.75;
   < p4 = gen1 function number 
p4  1. 1 7;
   < p5 = output amplitude ;default 0 = same amplitude as input soundfile
p5  mx 3. .3 .4 1. .7/1. 1. .7 1. .7/3. 1. .7 .2; < cresc. & dim.        
   < p6 = skip time into gen1 func : if positive p6  = skip time in seconds
      < if negative {between -.001 and -.999}, p6 = % of soundfile skipped
p6   1. -.1 -.3;  < begin reading somewhere betwenn 10 % & 30 % into soundfile
   < p7 = OPTIONAL time in input soundfile to STOP reading
      < if positive p7  = time in seconds
      < if negative {between -.001 and -.999}, = % of total soundfile duration
p7  1. -.6 -.9;  < end reading somewhere between 60 & 90 % into soundfile
 < if p8 = 1 read soundfile backwards, beginning at p6 skip point or EOF
p8   .5 0 0 .5 1 1; < read soundfile backwards  for about 1/2 the notes
 < p9 flag: if p9=1 change p3 output duration so that only original
 < p6 to p7 sample range used
p9  0;
   < p10 = pitch multiplier {default 0 = no pitch change}
p10  mx 4. .85 .95 1.12 1.05/4. 1.12 1.05 .85; < gradual pitchrise, then fall
   < for stereo output only p12 = pan location {1. = hard left, 0 = hard right}
 < p11 flag: if 1, print diagnostics
p11  0;
p12 .1;           < option fade-in time
p13 .15;          < option fade-out time
< for stereo output only p14 = pan location {1. = hard left, 0 = hard right
p14   mo 3. .95 .6 .05 .4/3. .4 .05 .95;
end; 

Assignment

From this point on your work with Csound should be divided into two tracks:

1) Create short orchestra and companion score files that try out the major new resources discussed within each chapter of this tutorial. For this chapter you should explore:

2) You should also begin more concentrated work on two or three more complex instruments, which will be useful to you in your compositional work in the studio. Step-by-step, incorporate additional features into these algorithms to make them more powerful and flexible, and incorporating refinements discussed in subsequent chapters of this tutorial.

Tip: When you want to add a new feature to an instrument or orchestra, make a copy of the orchestra file, and edit the copy. Always keep the previous (working) version of the orchestra file on hand, in case you don't like the results of your changes.


Eastman Csound Tutorial: End of Chapter 3

TOP of this chapter -- NEXT CHAPTER (Chapter 4) -- Table of Contents CHAPTER 1 -- CHAPTER 2 -- CHAPTER 3 -- CHAPTER 4 -- CHAPTER 5 -- CHAPTER 6 APPENDIX 1 -- APPENDIX 2