NEXT CHAPTER (Chapter 3) -- Table of Contents CHAPTER 1 -- CHAPTER 2 -- CHAPTER 3 -- CHAPTER 4 -- CHAPTER 5 -- CHAPTER 6 -- APPENDIX 1 -- APPENDIX 2
Much of this chapter will be devoted to the creation of envelopes (time varying upward and downward curves), which can be applied to amplitude, to pitch, and to most other parameters of our sounds. Because envelope values vary within a note, they must be produced by means of control signals, which we can create with oscillators, or with other unit generators specifically designed for this purpose. A second major topic we will consider, at the conclusion of this chapter, will be some methods of reading soundfiles into Csound for processing.
First, however, we will look at some alternative ways to specify pitch, and at initialization variables.
[ See the discussion of PITCH CONVERTERS in the Csound reference manual ]
If we want equal-tempered pitches, typing in the frequency of each pitch in our scores grows old very quickly. Csound provides three alternative ways (actually four ways, if we include MIDI note numbers) to specify frequency or pitch. Here, we will consider:
Each of these pitch specification methods is useful in certain circumstances.
Csound provides several pitch converter utilities that enable us to convert pitch specifications between these three formats. The (ugly) names of these pitch converter utilities consist of two of these 3-letter groups butted together. The first three letters specify the output format of the pitch notation, while the second three letters indicate the input format. Thus, a line of code like this:
cpspch(p4)
means: The argument in p4 is currently in pch notation. Convert this value into its cps equivalent, and use this cps value to compute the oscillator's output value.
Here are some other examples of pitch-conversion operations :
(operation input value) (output value) octpch(8.01) returns 8.083 (a half-step above middle C) pchoct(9.5) returns 9.06 (F#, 18semitones above middle C) cpspch(7.09) returns 220 (A below middle C) octcps(294) returns 8.166 ( D above middle C) cpsoct(7.5) returns 184.99 (F# below middle C)
If you would like some additional help with conversions between pch, cps and some other common pitch notation formats, consult the following:---------------------------------------
---------------------------------------
Next question: How do we use these things? Let's go back to our sample orchestra from Chapter 1 and modify the oscillator's second (frequency) argument as follows:
Now the value in p4 will be converted from pch to cps, before the oscillator performs any computation. This illustrates an important feature of unit generator arguments in Csound. An argument may consist of an expression - one or more mathematical or logical operations to be performed, such as addition, multiplication, substitution or (as here) conversion.
With this cpspch converter in place in our orchestra file, we can now specify equal-tempered pitches in our score:
Orchestra file: sr= 22050 kr= 2205 ksmps= 10 nchnls= 1 instr 1 asound oscili p5,cpspch(p4),p6 out asound endin ----------------------------------------------- Score11 input file : *f1 0 1024 10 1.; *f2 0 1024 10 .3 1. .4 .15 .05; *f3 0 1024 9 1. 1. 0 2.7 .67 0 5.4 .3 0 8.1 .1 0; i1 0 0 3; p3 2; p4 no c4/ df3/ g5; p5 1. 8000 12000; p6 nu 1/ 2/ 3; end; ----------------------------------------------- Csound score file output produced by score11 from the input file above: f1 0 1024 10 1. f2 0 1024 10 .3 1. .4 .15 .05 f3 0 1024 9 1. 1. 0 2.7 .67 0 5.4 .3 0 8.1 .1 0 i1 0.000 2.000 0 9723 1 i1 2.000 2.000 0 8752 2 i1 4.000 2.000 0 9369 3 e -----------------------------------------------
Suppose that we want to use the value of cpspch(p4) more than once in our instrument block. We might wish, for example, to create several oscillators, each producing some multiple of the frequency specified in p4. Typing the magic incantation cpspch(p4) several times is not an appealing prospect, nor is it necessary. By creating an initialization variable, we can direct Csound to compute this value once, give the result a name (which must begin with the character i), store the result in RAM, and return the value of this variable whenever we call it by name. Here is an example:
ipitch = cpspch(p4) a1 oscili .5 * p5, ipitch, p6 a2 oscili .3 * p5, 2.001 * ipitch, p6 a3 oscili .2 * p5, 4.98 * ipitch, p6 a1 = a1 + a2 + a3 ; add 'em up out a1 ; and spit 'em out
The first line of code above declares the variable ipitch and assigns to it the value of the operation cpspch(p4). The letter i that begins this output name signifies an operation that is performed at initialization time, before the production of samples begins. Unlike a-rate (audio) and k-rate (control) signals, i-rate (initialization) values are computed only once, at the very beginning of a note.
Data from score p-fields are examples of initialization values. But, as here, initialization values can also be created within an instrument, and can be the result of a mathematical or logical expression (operation). These initialization values remain constant throughout a note, but often change from one note to the next. Each subsequent use of an init variable within the instrument algorithm actually consists of taking the current value of a particular memory location and patching it into an argument in some unit generator. Any string of numbers and/or letters after the initial "i" can be used to label the variable. We could just as well have called it ivalue, i1 or ilikethis as ipitch.
In the example above, all three oscillators access the value of ipitch within their frequency arguments. The second oscillator multiplies the ipitch value for this note by 2.001 (producing a slightly sharp second harmonic), while the third oscillator by multiplies the variable by 4.98 (producing a slightly flat fifth harmonic). The three oscillator signals are mixed together in the proportions 50 % (a1), 30 % (a2) and 20 % (a3), as specified in the amplitude arguments to these oscillators. The result of this mix is then assigned to the audio-rate variable a1. Note that this overwrites the previous value of a1, which can no longer be accessed and presumably is no longer needed. (It had better no longer be needed!)
On the final two lines in this example, we have added comment (which begin with semicolons, the Csound comment symbol) to congratulate ourselves on this successful additive synthesis. The function for the oscillators, supplied in p6 of our score, might well be a sine wave. However, it might also be a more complex signal, with many partial frequencies. If we like the results we get, we might also add several more oscillators to this additive synthesis algo- rithm.
Now that we have greater flexibility in specifying pitch, and at least some control over timbre, our instrument is somewhat more useful, though probably still not something to which we would entrust a performance realization of, say, the complete Goldberg Variations. The most important feature it still lacks is time-varying amplitude control. Currently, the amplitude remains fixed at some level throughout a note. Not only is this oppressive to the ear, it also causes clicks at the beginnings and ends of each note. We are asking the loudspeaker cones to start and stop oscillating at this amplitude level almost instantaneously. Since inertia makes this impossible, they will flap around wildly for an instant at the onset and conclusion of each note, producing noise artifacts (clicks).
What we need is a means to shape the amplitude over the duration of each note, so that it rises from 0 (or near 0) to some maximum level determined in our score (probably in p5), then eventually falls back down to 0 at the end of the note. Such a rise and fall (or attack and decay) amplitude pattern is called an envelope.
Characteristically, musical sounds have amplitude envelopes with the following properties: There is generally a peak, or "spike" near the beginning of a note, as energy build up and the amplitude rises to its peak value. This rise in physical intensity is called the attack. Then, there is usually some attenuation, down to a steady-state level (typically between 50 and 90 % of the peak level). Actually, the steady-state is rarely very "steady," but usually includes random and/or quasi-periodic variations. Finally, at the end of the note, after a violinist lifts her bow off the string, or a clarinetist stops blowing into the instrument, the amplitude falls off to zero. This is known as the decay, Idiophonic percussive sounds usually contain no "steady state" segment, but rather consist entirely of attack and decay.
What we want to do then, is to create a signal at the control (or, less often, audio) rate that defines such a shape, and then feed this control signal to the amplitude input of our oscillator. There are several ways to create envelopes in Csound. Here, we will survey some of the most commonly used envelope generators.
[ See the discussion of LINE and EXPON in the Csound reference manual ]
The simplest envelope generators available in Csound are line and expon. Unit generator line creates a linear (straight line) connection between two values over a specified duration. Thus, there are three required arguments : (1) a beginning value; (2) the duration over which to move between the two values (generally this duration will be the entire duration of a note); and (3) the closing value. Consider the following modification to our sample orchestra :
(header omitted here)
instr 1
ipitch = cpspch(p4)
kmart line 0, p3, p5
asound oscili kmart, ipitch, p6
out asound
endin
Here we use the unit generator line to create a gradual, linear increase in amplitude from 0, at the start of the note, to our p5 value, at the very end of the note. Since this is a rather prosaic, off-the-shelf envelope, we call the output of this operation kmart. (Not all that funny, but I like it anyway.) Remember that the output name of a unit generator or mathematical operation must begin with an "i," a "k", or an "a," specifying the rate at which the operation is performed, but that the remainder of the name can consist of any string of numbers and/or alphabetical characters.
Next, we patch the current contents of memory location kmart into the amplitude argument of oscili. Note that the operations performed by line must be done before we call oscili, since the oscillator is looking for a control-rate value named kmart. In our instrument code, therefore, the call to line must precede the call to oscili. If we reversed the order of these two lines of code, oscili would be unable to find kmart. Csound would complain and quit.
Note, too, that the Csound manual indicates that line and expon can be run either at the control rate or at the audio rate (but NOT at the i-rate, since the values CHANGE within a note.) Why did we choose to run line at the k-rate? A single line segment is a simple shape, and not much resolution is needed to get the desired result.
Occasionally, if a control value changes very rapidly (such as an attack that lasts just a few milliseconds) or has a complex shape, better audio quality can be achieved by running this control variable at the audio rate. The general rule, however, is to run control signals at the k-rate. (Audio signals, of course, MUST be run at the audio rate.)
A final observation before we bid a fond adieu to unit generator line. Note in the Csound manual that the mnemonic names for the three arguments to line ("ia," "idur1," and ib) all begin with the letter i. This tells us that each of these inputs is an i-time value which CANNOT change within a note.
Back to work.....
One problem with using line is that linear amplitude changes (and linear pitch changes) often sound rather abrupt or unrealistic. In natural acoustic sounds, changes in amplitude (and also in pitch) more typically follow exponential curves. To obtain a more natural-sounding exponential increase in amplitude intensity, we could substitute expon for line in our instrument algorithm:
Notice that we have changed the first argument value from a 0 to a 1. Exponential segments are computed as ratios between two values. There is no ratio between zero and some number. Therefore, zeros are illegal in any exponential operation. If we had used a value of .1 instead of 1.0 for the first argument, the slope of the exponential curve would be much steeper, with most of the increase coming at the very end of the note. The ratio of .1 to p5 is ten times greater than the ratio of 1.0 to p5. A value of .01 would give us an extremely steep curve. If we were using line, however, there would be no audible difference between using 1, 0, .1 or .01 for the first argument.
There is another restriction that applies to all exponential operations. One cannot go from a positive to a negative value, or vice versa. There is no ratio between positive and negative numbers.
Actually, the line and expon amplitude envelope examples above, while possibly instructive, are academic for our purposes here. The simple one-segment slopes produced by these two unit generators clearly are inadequate for the generation of amplitude envelopes, which must include at least two segments (a rise and a decay). However, line and expon sometimes are adequate for the creation of other types of control signals. In the following stereo orchestra file, expon is used to create a simple pitch glissando, and line is employed to produce a moving stereo pan:
------------------------------------------------------------
Orchestra file : sr=44100 kr=2205 ksmps=20 nchnls=2 instr 1 ipitch = cpspch(p4) kpitch expon ipitch, p3, p7 * pitch ;glissando control signal asound oscili p5, kpitch , p6 ; now add a moving stereo pan : kpan line p8 , p3 , p9 ; pan envelope outs kpan * a1 , (1. - kpan) * a1 endin ------------------------------------------------------------ Score p-field values for glissando and pan: p7 nu 2./.97 ; < multiplier (*p4) for ending pitch p8 nu 1./.5 ; < starting % of signal sent to left channel p9 nu 0/.25 ; < end % of signal sent to left channel ---------------Results:
The panning operation above will work fairly well, but could be improved. For psychoacousical reasons, a sound emanating mid way between two loudspeakers (a kpan value of .5 or so in the example above) tends to sound softer than the same sound panned hard left or hard right. The panpot circuits on most hardware mixing consoles compensate for this nonlinearity by logarithmically varying the output intensity of signals as the potentiometer position is moved, applying 3 dB of gain when the panpot is centered and no gain when the pot is moved all the way to the right or left.
To improve our panning subroutine in the example above, therefore, we could employ the Csound value converter sqrt, which returns the square root of an input value or expression:
; now add a moving stereo pan : kpan line p8 , p3 , p9 ; pan envelope kleft = sqrt(kpan) kright = sqrt(1. kpan) outs kleft * a1 , kright * kpan Now: when kpan = 1.000 then kleft = 1.000 and kright = 0.000 when kpan = 0.900 then kleft = 0.949 and kright = 0.316 when kpan = 0.800 then kleft = 0.894 and kright = 0.447 when kpan = 0.700 then kleft = 0.837 and kright = 0.548 when kpan = 0.600 then kleft = 0.775 and kright = 0.632 when kpan = 0.500 then kleft = 0.707 and kright = 0.707 when kpan = 0.400 then kleft = 0.632 and kright = 0.775 when kpan = 0.300 then kleft = 0.548 and kright = 0.837 when kpan = 0.200 then kleft = 0.447 and kright = 0.894 when kpan = 0.100 then kleft = 0.316 and kright = 0.949 when kpan = 0.000 then kleft = 0.000 and kright = 1.000Often, this will result in better stereo imaging and "smoother" panning operations.
[ See the discussion of LINSEG and EXPSEG in the Csound reference manual ]
linseg and expseg work much like their little sisters line and expon, except that they enable us to include any number of linear (linseg) or exponential (expseg) envelope segments within a note. Thus, they do not have a fixed number of input arguments. Here is an example of how we can "draw" an amplitude envelope with expseg:
k1 expseg 1, .15, 12000, .10, 10000, p3-.5, 6000, .25, 1 (value 1) (duration1) (val 2) (dur 2) (val 3) (dur 3) (val 4) (dur 4) (val 5)
The arguments to expseg or linseg, called break-points, present an alternating series of intensity levels and of durations between adjacent intensities. As with expon and line,the first and last values must be intensity levels. The expseg example above creates a series of four exponentially shaped connections ("curves") derived from the ratios between values 1 and 2, then values 2 and 3, and so on, and and zero amplitude values are illegal. If we had used linseg rather than expseg in the line above, then the segments between each pair of values would be linear instead of exponential, and zeros would be permissable.
You might be wondering how we derived the sixth argument, p3-.5, in the example above. Since we want the envelope to last the exact duration of the note, all of the durational arguments must add up to p3. Thus, by adding up the other three durations (.15 , .1 and .25), and getting a total of .5, we simply make the rest of the note the "steady-state" time, (p3 - .5). One thing to watch for: this envelope will not work with notes shorter than .5 seconds. Why?
Here's a more flexible envelope that can vary from note to note:
k1 expseg 1, p7, p5, p3 - (p7+p8) , p9 * p5, p8, 1 (value 1) (duration1) (val 2) (dur 2) (val 3) (dur 3) (val 4)
To use this amplitude envelope in our instrument, we would have to add p-fields 7,8 and 9 to our score. p7 will determine attack time, p8 decay time. p9 will indicate the "steady-state" amplitude level as a percentage of p5. Let's assume the following score values :
p3 3 ; duration (here, 3 seconds) p5 10000 ; peak amplitude p7 .2 ; attack time p8 1. ; decay time p9 .5 ; "steady-state" amplitude level
The resulting amplitude will rise over .2 seconds to our p5 level of 10000. Over the next 2.3 seconds - the duration of p3-(p7+p8) - the amplitude will decrease to a value of 5000 (p9*p5), and then decay over the final .5 seconds to a final value of 1 (inaudible).
Many different types of amplitude patterns are now possible. If p9 is greater than 1., for example, the amplitude will rise during the "steady-state," and the maximum amplitude for the note will exceed our p5 value. Attack and decay times can be individually long or short. We can employ random selection procedures in score p-fields 5, 7, 8 and 9 to vary the envelope of each note. The only thing we must watch is that p7+p8 does not exceed p3. Our instrument algorithm is now more powerful. On the down side, we must make more decisions when creating scores for it to compute.
At this point it would be a good idea to try out some of the procedures discussed above in sample orchestras and scores of your own. It is important not only to understand these procedures, but also to be able to incorporate them into your own instrument algorithms, and to see what problems arise in using them at each step, before the bulk of accumulated information becomes too great or confusing.
[ See the discussion of LINEN and ENVLPX in the Csound reference manual ]
There are two more envelope generators that clamor for our attention. One is simple, and therefore rather limited. The other is powerful, and therefore more cumbersome to use. Let's take the simple unit generator, linen, first. The four arguments to linen are, respectively :
(1) amplitude, or level (2) rise (attack) time (3) duration (almost always p3) (4) decay time. (peak amplitude) (rise time) (duration) (decay time) kamp linen 10000, .15 , p3 , .35 (every note in our score will have an identical envelope) kamp linen p5 , p6 , p3 , p7 (this enables us to vary amplitude, attack and decay values for each note)
In the first example above, the amplitude will start at 0, rise linearly to 10000 over .15 seconds, hold steady at 10000, then fall linearly to 0 over the last .35 seconds. With linen, we really do get a "steady-state" level. However, note that the amplitude (first) argument can be a k-variable - that is, the amplitude value fed to linen can change during a note. Consider the following :
SETUP(44100,2205,1) instr 1 ktrem oscil .2, 4/p3, 100 ;tremolo control oscillator kamp linen (ktrem * p5)+(.8 * p5), .25, p3, .5 ;amplitude envelope audio oscili kamp, cpspch(p4), p6 ;audio oscillator out audio endin
The sound produced by this instrument will include a tremolo - alternating increases and decreases in amplitude. Assuming that function 100 (specified in the third argument to the control oscillator) is a sine wave, the control oscillator will produce a stream of numbers at the k-rate that move in the shape of a sine wave from 0 to .2, back to 0, then to -.2, then back to 0. It will produce this shape four times per note ( 4/p3, the frequency input to this oscillator).
The amplitude argument to linen is a complex expression in two parts. The first part directs linen to multiply the output of the tremolo oscillator (ktrem) by p5, producing a tremolo of +/- 20 % of our p5 value. To this tremolo, linen adds 80 % of our p5 value without alteration. Thus the maximum amplitude we will get, when the ktrem oscillator is at the top of its sine wave curve, still will equal p5. linen also provides here a .25 second fade-in at the beginning of each note and a .5 second decay at the end.
When an expression includes two or more operations, as in the first argument to linen above, it is always a good idea to use parentheses to guarantee that the operations are performed in the correct order. Read the discussion on ARITHMETIC OPERATIONS in the Csound manual to learn the normal order of precedence for algebraic and logical operations. (Strictly speaking, the parentheses were not necessary in the example above - the operations would have been done in the correct order even had they been omitted - but they make the code easier to read and help us to avoid mistakes.) Every "(" must be balanced by a ")" or your orchestra file will not pass the initial Csound syntax check.
Make certain that you understand this example. It is the first instance of a control signal (kamp) which itself includes a time-varying control signal input (ktrem). Such "stacking" or "banking" of control signals is frequently necessary to produce desired changes in amplitude, pitch, timbre, and other musical qualities.
envlpx is the most powerful of the Csound envelope segment generators, and therefore the most complicated to use, so get some coffee if necessary before reading on. envlpx requires seven (count 'em) input arguments. An eighth argument (ixmod) is optional. Here is an example, with typical values filled in for the seven required arguments:
kenv envlpx 10000 , .15 , p3 , .25 , 3 .7 , .001 , -.5 amp. rise duration decay function atss atdec [xmod] time time number
Arguments one through four are the same as for linen, respectively determining peak amplitude, rise time, duration (almost always p3) and decay time. The remaining arguments are unique to envlpx :
[1] As of this writing, a long-standing bug in Csound necessitates that the xmod argument be used only if envlpx is running at the k-rate. If envlpx is running at the a-rate, including any xmod value likely will result in amplitudes far exceeding 32767. ECMC users may wish to check the helpfile csound to see if this bug still exists in the version of Csound we currently are running at Eastman.
[ See the discussions of GEN5 and GEN7 in the Csound reference manual ]
So, we need to be able to create attack function shapes for envlpx. Function generators gen9 and gen10, which compute audio waveforms, are of no help to us here. We need a control shape, not frequency and amplitude ratios. For this purpose, we would use gen5, which creates a table of numbers tracing exponential changes, or, less often, gen7, which creates tables defining linear changes in level.
Let's assume that we want a simple rise shape, which increases exponentially from near 0 to our peak amplitude. Our table of numbers, then, should rise from near 0 to 1., since each of these table values will be multiplied by our peak amplitude. Such a function might look like this :
The table we are calling function 3 is computed at time 0, has 65 numbers, and is created by gen5 (p-fields one through four). Since we want this function to be read only once per note (during the attack), rather than repetitively by an oscillator, we have included an "extended guard point" in the table. Thus, the table size is a power-of-two-plus-one (65), rather than a simple power-of-two (64). The purpose of the extra table number ("guard point") is this : after envlpx (or some other unit generator) has read to the last number in the table, but still needs values for the last batch of samples within the attack, it will use the last value in the table for these final samples, rather than wrapping around the table and interpolating between the first and last entries.
The remaining p-fields in the function definition work somewhat like the break-points in an expseg or linseg statement. Instead of durations, however, we use numbers of points within the table to define the distance between values. To illustrate in terms of our example, we start at a value of .01, then rise over 64 points to a value of 1. 64 points covers the entire duration during which the function is read. If we wanted it to rise during half the duration, then come back down to to our starting value, the function definition would look like this:
|-- rise --| |---fall ---| *f3 0 65 5 .01 32 1 32 .01;
Here, the function starts at a value of .01, moves exponentially over 32 points (half the duration during which the function is read) to a value of 1, then decreases over 32 points to .01. Notice that the sum of the points again totals 64, as it did in our first example, and not 65. Also note that this would not be a usable rise shape for envlpx.
Since ECMC users normally use score11 to generate our Csound scores, our function definition examples above have included the mandatory score11 asterisk the beginnings of each definition, and semi-colons to delineate the ends of lines of code.
gen7 works just like gen05, except linearly. Zero values, while illegal with gen5, are permissable (and common) with gen7. Using gen5 and gen7, we can create arbitrarily complex exponential and linear shapes for use by envlpx, oscillators and other unit generators. Suppose, for example, that we would like an attack shape that includes three preliminary rising and falling spikes before finally reaching its peak. The following envelope would do the trick:
|- rise -| |- fall -| |- rise -| |- fall -| |- rise -| |- fall -| |- rise -| *f1 0 65 5 .01 10 .6 5 .3 15 .8 5 .4 15 .9 4 .5 10 1.;
Although most often used to create control shapes, these two function generators can also be used to create audio waveforms. Here are some common audio waveshapes, similar to those produced by "vintage" analog synthesizers, that we can create with gen7:
* f1 0 64 7 -1 64 1 ; < sawtooth wave * f1 0 64 7 -1 32 1 32 -1 ; < triangle wave (odd harmonics) * f1 0 64 7 1 32 1 0 -1 32 -1 ; < square wave (odd harmonics) * f1 0 64 7 1 4 1 0 -1 60 -1 ; < pulse train waveform
Note, in the boldface parameters within the square and pulse wave examples, that we can specify discontinuities in waveforms by "telling" the function generator to move from one value to another over zero points in the table.
One must be careful when employing "sharp edged" waveforms, like those above, in digital synthesis, however, because these waveforms are not band limited in frequency. Each of the waveforms above includes a great many harmonics, and aliasing can easily result when the pitch exceeds A 440 or so. Subsequent filtering would not eliminate the these artifacts.
; ############################################################# ; soundfile ex2-1 : envlpx example 1 Csound Tutorial ; ############################################################# ; Orchestra file used to create example soundfiles "ex2-1" and "ex2-2" :------------------------------------------------- ;Fixed wave form instrument, using envlpx to create an amplitude envelope sr=44100 kr=2205 ksmps=20 nchnls=1 instr 1 ipitch = cpspch(p4) ; convert p4 from pch to cps kenv envlpx p5, p6, p3, p7, 60, p8, .01, p9 ; amplitude envelope display kenv , p3 ; show envelope shape on sterr asignal oscili kenv, ipitch, p10 out asignal endin ------------------------------------------------- < Score11 file used to create soundfile ex2-1 : < score example ex2-1 : envlpx : various envelope shapes and audio functions * f1 0 64 7 -1 64 1 ; < sawtooth wave * f2 0 64 7 -1 32 1 32 -1 ; < triangle wave (odd harmonics) * f3 0 64 7 1 32 1 0 -1 32 -1 ; < square wave (odd harmonics) * f4 0 64 7 1 4 1 0 -1 60 -1 ; < pulse train waveform < function 5 has harmonics 1,4,7,11,15,18 * f5 0 256 10 1. 0 0 .4 0 0 .65 0 0 0 .3 0 0 0 .18 0 0 .08; < function 6 has harmonics 3,4,7,8,11,12,15,16 * f6 0 256 10 0 0 .5 .8 0 0 .7 1. 0 0 .3 .4 0 0 .2 .12; < function 7 has a strange assortment of widely spaced partials *f7 0 256 9 1. 1. 0 9 .7 0 5. 15 .45 0 23 .3 0 31 .15 0 39 .08 0; < function 8 has only high harmonics (20 through 28) * f8 0 256 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .2 .4 .6 1. .7 .5 .3 .1; * f60 0 65 5 .005 64 1; < envlpx rise shape function i1 0 0 8; p3 2; du .95; p4 no c3; p5 nu 6000*3/4000; < p6 = rise time p6 nu .5/ .35/ .2 / .1 / .07 / .03 / .012 / .005; < rise time p7 mo 14. .5 .1 ; < decay time p8 mo 14. .9 .1; < atss p9 0; < ixmod p10 nu 1/2/3/4/5/6/7/8; < audio waveform function end; -------------------------------------------------
Appendix Csound score file examples : Chapter 2
Comment on example ex2-1 : The envelope shapes specified in p5 through p9 become progressively sharper, beginning with fairly lengthy attacks and decays, and ending with very short transients. A different audio timbral function is used for each of the eight notes. The first four waveshapes (functions 1-4) are reminiscent of those employed in analog synthesizers of the 1960s and 1970s -- ugly as sin to my own aging ears, though in fashion in retro pop music circles today -- while in the concluding four audio functions (functions 5-8) we specify somehwat more interesting (or at least less common) harmonic spectra. Unit generator display will include a graphical "picture" of the amplitude envelope for each note within the sterr terminal output. This can be a useful diagnostic tool when we do not get the results we anticipate. Once we have the instrument algorithm and a usable range of score p-field values for envlpx working to our satisfaction we probably would comment out the call to display by placing a semicolon at the beginning of this line.
############################################################# soundfile ex2-2 : envlpx example 2 Csound Tutorial ############################################################# < Score11 file used to create soundfile ex2-2 : < xmod envelope modifications to a fixed envelope shape Eastman Csound Tutorial * f3 0 512 7 -1 220 -1 36 1 512 1 36 -1; < square wave, sloped sides * f60 0 65 5 .005 64 1; i1 0 0 4; p3 2; p4 no fs2; p5 6000; p6 .1; < rise time p7 .5; < decay time p8 .1; < atss p9 nu .95/.3/-.3/-.95; < xmod p10 3; < audio waveform function end; -------------------------------------------------
Comments on example ex2-2 : All of the score p-fields are constants except for the ixmod value, which modifies the slope of the change from p5 ("peak" amplitude) to p8*p5 ("attenuation of steady state"). Note that there is some audible difference in the envelopes, but, in the absence of corresponding timbral changes, these differences are rather subtle.
As has become our custom, we will now harangue you again on the need to try out the resources discussed and illustrated on the preceding pages.
In addition to such Csound envelope generators as envlpx and expseg, there is another way that we can create envelope patterns: by using oscillators What do you think of this?
Here we are using a control oscillator (kenv) to read function number 5 once over the entire duration of each note (more on this below). Function 5 includes both a rise and a fall:
*f5 0 65 5 .001 10 .8 10 1. 10 .9 34 .001; |-------- rise ---------||--------- decay --------| p-fields: 1 2 3 4 5 6 7 8 9 10 11 12 13
p-fields 5 through 9 specify a rise shape, p-fields 9 through 13 a (longer, more gradual) decay shape. p-fields 5, 7, 9, 11 and 13 within this table will be multiplied by our p5 amplitude. The envelope created by our control oscillator is then passed as the amplitude input to our audio oscillator (a1).
The frequency argument to the control oscillator kenv, 1/p3, assures that function 5 will be read exactly once per note. If p3 is 4 seconds, the frequency argument to the control oscillator is .25 herz (1/p3, or, here, 1/4), so that it will take exactly four seconds for oscillator kenv to march through the table.
When using an oscillator as an envelope generator, as here, there is an important point to consider. The attack and decay times will always be dependent upon the duration of the note (since they are percentages of the length of the function table, which is being read once). Thus, rise and fall times will vary proportionately with the total durations of various notes. We have no way to specify exact attack or decay times. (If we want to be able to specify exact attack and decay durations, we should use one of the Csound envelope generators, such as expseg or envlpx.) For some applications, however, oscillators can create useful envelopes, especially if we want to read an envelope more than once per note :
This control oscillator would give us triplets (3/p3) for every note in our score.
The instrument used to create soundfiles ex2-3 (a didactic example) and ex2-4 (musically more interesting) contains a control oscillator that generates an amplitude envelope, and an audio oscillator that reads various audio functions:
############################################################# Soundfile examples "ex2-3" and "ex2-4" ############################################################# Orchestra file used to create these two soundfiles:: ----------------------------------------------------- sr=44100 kr=2205 ksmps=20 nchnls=1 ; control oscillator (kenv) supplies time envelope to audio oscillator ; score p-fields: ; p4 = pitch, in pch notation, ; p5 = peak amplitude ; p6 = function number for control oscillator envelope ; p7 = function number for audio oscillator waveshape instr 1 kenv oscili p5, 1/p3, p6 ; control oscillator -- creates amplitude envelope a1 oscili kenv, cpspch(p4), p7 ; audio oscillator out a1 endin ----------------------------------------------------- < score11 file used for "ex2-3" : ----------------------------------------------------- < different audio waveshapes and time envelope functions < Envelope functions : * f52 0 65 7 0 32 1. 32 0; < linear pyramid: rise & fall * f62 0 65 5 .01 32 1. 32 .01; < exponential pyramid rise & fall * f21 0 65 5 .001 4 .8 10 1. 10 .7 40 .001; * f22 0 65 5 .001 10 1. 10 .2 10 .8 10 .1 10 .6 14 .001; < Audio waveshape functions : * f11 0 1024 10 1. .8 0 .6 0 .4 0 .2 ; < fundamental & even harmonics * f12 0 1024 10 1. 0 .33 0 .13 0 .07; < fundamental & odd harmonics * f13 0 64 7 -1 32 -1 0 1. 32 1. ; < square wave, not band-limited * f14 0 64 7 -1 30 -1 2 1. 30 1. 2 -1.; < square wave, band-limited i1 0 0 4; p3 3.; p4 no c3; < pitch (in pch) p5 8000; < amplitude p6 nu 52/ 62/ 21/ 22; < function number for time envelope p7 nu 11/ 12/ 13/ 14; < function number for audio waveshape end; < score11 file used to create "ex2-4" : alternating audio & envelope functions : ----------------------------------------------------- < Envelope functions : * f21 0 65 5 .001 4 .8 10 1. 10 .7 40 .001; * f23 0 65 5 .001 6 1. 58 .001; < rapid attack, long decay * f24 0 65 5 .001 12 1. 28 .05 10 .8 24 .001; < 2 attacks per note < Audio waveshape functions : * f12 0 512 10 1. 0 .33 0 .13 0 .07; < fundamental & odd harmonics * f15 0 512 10 0 1. .8 0 .5 .3 0 .15 .1; < harmonics 2,3,5,6,8,9 i1 0 6; p3 mx 5. .5 .1/1. 1.; du mx 5. 1. 2. 4./1. 3; p4 mx 5. c2 c4 b4 b5/1. ds6; < pitch (in pch) p5 mx 5. 2000 4000 12000/1. 13000; < amplitude p6 nu 21/23/24; < function number for time envelope p7 nu 12/15; < function number for audio waveshape end; -----------------------------------------------------
Comment : No, it won't win a Pulitzer Prize, but we are beginning to progress on our Gradus ad Parnassus.
The important point in all of this is that oscillators can be used in many different ways, to generate various kinds of audio and control signals. Oscillators can cycle through function tables repetitively (generating audio from scratch), once per note (creating envelope control signals, which can be applied to amplitude, pitch, or other musical parameters), or even less than once per note (reading only a portion of a table, if the period of oscillation in greater than the duration of a note). Given enough resourcefulness, we could create a variety of intriguing musical sounds and gestures solely by means of oscillators.
Most of the signal processing operations we perform on synthetic waveforms created by oscillators also can be performed on samples read into Csound from existing soundfiles. Csound provides several ways in which we can read in soundfiles. These methods include:
The soundin opcode has one required and one optional argument. The required argument specifies the directory path and the name of the soundfile to be accessed. An optional second argument indicates a skip time (in seconds) into this soundfile. The diskin unit generator works in similar fashion, but offers additional soundfile processing capabilities that we will consider later.
Within the first (ifilcod) argument to soundin or diskin there are two ways in which one can tell the unit generator input soundfile(s) to use:
This method has an obvious severe disadvantage. Since the input soundfile argument is a constant within the orchestra file, rather than a variable, this orchestra file can only access a single input soundfile - babycries1.
At the ECMC, such soundfile links are most easily created by means of the local utility sflink -- or, for sflib soundfiles, with the sflinksflib (sflinksfl) variant. Some of you may already have used these commands in working with the sf family of Eastman Csound Library instrument algorithms. See the manual page for sflink for usage details. The link files to three soundfiles in the /sflib/env directory, used in example soundfile ex2-5 which follows, were created by means of the following command line:
soundin.1 which points to the soundfile /sflib/env/wind.low (low pitched blowing wind) soundin.2 which points to the soundfile /sflib/env/caridle.low (car engine noise) soundin.3 which points to the soundfile /sflib/env/riverlock (gushing water)In example ex2-5, portions of these three soundfiles are read into Csound by soundin:
; ############################################################# ; soundfile ex2-5 : soundin Csound Tutorial ; ############################################################# Orchestra file used to create this example: ------------------------------------------------- sr= 44100 kr = 4410 ksmps = 10 nchnls = 2 ; score p-fields: ; p4 = soundin.# number {source soundfile} , p6 = skip time ; new amplitude envelope {p5, 7, 8 & 9} ; p5 = "peak" level multiplier ; p7 = rise {fade-in} time ; p8 = decay {fade-out} time ; p9 = "steady-state" level multiplier ; moving stereo pan {p10 & p11} : ; p10 = number of left-right pans per note ; p11 = function number for moving pans instr 1 asig soundin p4, p6 ; read in the soundfile ; now apply a new envelope to these samples kamp expseg .005 ,p7 , p5 , p3 - (p7 + p8) , p9 , p8 , .005; < amp. envelope asig = asig * kamp ; supply a moving stereo pan between left & right channels kpan oscili 1. , p10/p3 , p11 ; panning control signal outs sqrt(kpan) * asig , sqrt(1. - kpan) * asig endin ------------------------------------------------- Eastman Csound Tutorial Score file used to create "ex2-5" : ------------------------------------------------- < three panning functions : *f1 0 65 7 0 32 1. 32 0 ; < linear rise & fall *f2 0 129 5 .01 64 1. 64 .01 ; < exponential rise & fall *f3 0 1024 9 .5 1. 0; < 1st half of a sine wave {rise & fall} i1 0 0 3; < create 3 output "notes" p3 4; < the three notes start at 4 second intervals du 306. ; < each output note lasts 6 seconds p4 nu 1 / 2 / 3 ; < soundin.# number : all soundfiles are from /sflib/env < 1 = wind.low, 2 = caridle , 3 = riverlock p5 nu .5 / .9 / .2 ; < amplitude multipler p6 1. .5 2.; < skip time into soundfiles p7 nu 2. / 1. / .5 ; < rise {fade-in} times p8 nu 2. / 1.2 / 2. ; < decay {fade-out} times p9 nu .9 / .6 / .2; < steady state amplitude multiplier p10 nu 2. / 8. / 3. ; < number of left-right pans per note p11 nu 1 / 2 / 3 ; < pan function table number end; -------------------------------------------------
Comments on ex2-5: Two signal processing operations are applied to the three source soundfiles:
*f3 0 1024 9 .5 1 0; < 1st half of a sine wave {rise & fall}This also produces uneven movement (alternately speeding up and slowing down)between the speakers.
Note in the Csound reference manual that the soundfiles accessed by soundin or diskin can be mono, stereo or quad. For stereo soundfiles, the left and right channel inputs are retained as separate audio signals within Csound, and each requires a soundin output name. Example:
aleft , aright soundin 3
Unfortunately, however, early versions of diskin have proven quite buggy, at least on the ECMC SGI systems. For some reason, this opcode currently cannot be the first operation within an orchestra file, or else Csound aborts with a bus error message. Preceding diskin with certain types of init variables -- even variables not accessed by diskin -- can result in a segmentation fault error. Backwards reading of input samples sometimes works fine, but sometimes produces glitches or stuttering. And certain skip values result in maxamp white noise. Thus, for the time being, I recommend that diskin be used only to perform pitch transposition, and not to skip into a soundfile or to read input samples in reverse order. Example algorithm ex3-6 in the next chapter provides a much more reliable way to skip into a soundfile and to read it backwards.
To add pitch transposition capabilities to the ex2-5 instrument algorithm we could substitute unit generator diskin for soundin in our orchestra file, and add an additional p-field (p12, the next available p-field) to our score to control pitch transposition:
Orchestra file: substitute these 2 lines: print p2 ; this dummy line, which shouldn't be needed, corrects a current bug in diskin asig diskin p4, p12, p6 ; read in & transpose soundfiles for this line: asig soundin p4, p6 ; read in soundfiles Score11 input file: add this line: p12 nu 1. / 1.059 / .75; < pitch transposition ratioResult: The first input soundfile will be untransposed. The second input soundfile will be transposed up a semitone (a transposition ratio of 1.059), and the duration of the output samples will be only .944 (1./1.059) that of the original sound. The third input soundfile will be transposed down a perfect fourth, and its output duration will be 1.333 (1./.75) times the input duration.
Some notes on pitch shifting:
diskin, like the pitch shifting algorithms within almost all hardware
samplers, performs pitch transposition by resampling the input samples, in much the same manner that
oscillators resample the values within a function table to produce different
pitches. Some resampling programs employ interpolation (slower but more accurate)
while others employ truncation. (To review these concepts, see section
1.5.1. Interpolating and Truncating Oscillators.)
Pitch shifting by resampling always changes not only the pitch of the sound, but also its duration, decreasing the duration for upward transpositions (since some of the input samples are skipped in order to produce more cycles per second) and increasing the duration for downward pitch shifts (where additional samples are added to "stretch out" the waveform):
output_duration = (1. / pitch_shift_ratio) * input_duration
In the non-looping mode, diskin (and also loscil) will output zeros when they reach the end of an input sample stream. Thus, we do not need to compute the exact output duration for each note in our score file, but merely need to make sure that the p3 output durations we specify will be sufficiently long to hold all of the output samples computed by diskin or loscil (unless, of course, we do not want to read in a complete soundfile, but only a portion of it).
Example: Assume that we want to read in a complete 4 second soundfile five times, with the following pitch shifts:
p12 nu 1.059 / .5 / 1.122 / .75 / .89 ; < pitch shift ratios
du 308;
Formants: When we pitch shift a sound by resampling, we also will be shifting the formants -- the resonant (emphasized) frequency bands produced by many acoustic sound sources -- by the same transposition interval, and thus changing the timbre, or apparent size, of the sound source. For unpitched or quasi-pitched idiophones and membranophones, and for many other types of percussive or environmental sounds, the timbral change often will be acceptable. However, for sound sources such as the human voice, which contains prominent formants, pitch shifting by more than a minor third or so will result in the "munchkin effect" (for upward transpositions) or the "sick cow effect" (for downward pitch shifts).
To change the pitch of a sound without changing its duration, or vice versa, one must employ some technique other than resampling. Most often, this is achieved either by
Construct a few new orchestra files, similar to those in this chapter, which make use of some combination of unit generators line, expon, linseg, expseg and envlpx. (using gen5 and gen7 to create different attack shapes for envlpx), as well as control oscillators. Use oscillators to create audio signals in some of your instruments, and soundfiles (read into Csound with soundin or diskin) as signal sources in other instruments. Create some scores for these instrument algorithms, then compile soundfiles with Csound, listen to the results, and improve your orchestra and score files until you begin to get something that resembles music (broadly - but not too broadly - defined).
NEXT CHAPTER (Chapter 3) -- Table of Contents CHAPTER 1 -- CHAPTER 2 -- CHAPTER 3 -- CHAPTER 4 -- CHAPTER 5 -- CHAPTER 6 -- APPENDIX 1 -- APPENDIX 2