Richard Boulanger
Csound is an incredibly powerful and versatile software synthesis program. Drawing from a toolkit of over 450 signal processing modules, one can use Csound to model virtually any commercial synthesizer or multi-effects processor. Csound literally transforms a personal computer into a high-end digital audio workstation an environment in which the worlds of sound-design, acoustic research, digital audio production and computer music composition all join together in the ultimate expressive instrument. However, as with every musical instrument, true virtuosity is literally the product of both talent and dedication. You will soon discover that Csound is the ultimate musical instrument. But you must practice! In return, it will reward your commitment by producing some of the richest textures and uniquely beautiful timbres you have ever heard. In the audio world of Csound, knowledge and experience are the key... and your imagination the only limitation.
The goal of this chapter is to get you started on Csounds road of discovery and artistry. Along the way well survey a wide range of synthesis and signal processing techniques and well see how theyre implemented in Csound. By the end well have explored a good number of Csounds many possibilities. I encourage you to render, listen, study and modify each of my simple tutorial instruments. In so doing, youll acquire a clear understanding and appreciation for the language while laying down a solid foundation upon which to build your own personal library of original and modified instruments. Furthermore, working through the basics covered here will prepare you to better understand, appreciate and apply the more advanced synthesis and signal processing models which are presented by my colleagues and friends in the subsequent chapters of this book.
Now there are thousands of Csound instruments and hundreds of Csound compositions on the CD-ROM, which accompanies this text. Each opens a doorway into one of Csounds many worlds. In fact, it would take a lifetime to fully explore them all. Clearly, one way to go would be to compile all the orchestras on the CD-ROM, select the ones that sound most interesting to you and merely "sample" them for use in your own compositions. This library of "presets" might be just the collection of unique sounds you were searching for and your journey would be over.
However, I believe a better way to go would be to read, render, listen and then study the synthesis and signal processing techniques that fascinate you most by modifying existing Csound orchestras that employ them. Afterward you should express this understanding through your own compositions your own timbre-based "soundscapes" and "sound collages." Surely through this "active" discovery process, you will begin to develop your own personal Csound library and ultimately your own "voice."
To follow the path I propose, youll need to understand the structure and syntax of the Csound language. But I am confident that with this knowledge, youll be able to translate your personal audio and synthesis experience into original and beautiful Csound-based synthetic instruments and some truly unique and vivid sound sculptures.
To that end, well begin by learning the structure and syntax of Csounds text-based orchestra and score language. Then well move on to explore a variety of synthesis algorithms and Csound programming techniques. Finally well advance to some signal processing examples. Along the way, well cover some basic digital audio concepts and learn some software synthesis programming tricks. To better understand the algorithms and the signal flow, well "block-diagram" most of our Csound "instruments." Also, Ill assign a number of exercises that will help you to fully understand the many ways in which you can actually "work" with the program.
Dont skip the exercises. And dont just read them. Do them! They are the keys to developing real fluency with the language. In fact, you may be surprised to discover that these exercises "teach" you more about how to "work" with Csound than any of the descriptions that precede them. In the end, you should have a good strong foundation upon which to build your own library of Csounds and you will have paved the way to a deeper understanding of the chapters that follow.
So, follow the instructions on the CD-ROM; install the Csound program on your computer; render and listen to a few of the test orchestras to make sure everything is working properly; and then lets get started!
What is Csound and How Does it Work?
Csound is a sound renderer. It works by first translating a set of text-based instruments, found in the orchestra file, into a computer data-structure which is machine-resident. Then, it performs these user-defined instruments by interpreting a list of note events and parameter data which the program "reads" from: a text-based score file, a sequencer-generated MIDI file, a real-time MIDI controller, real-time audio, or a non-MIDI devices such as the ASCII keyboard and mouse.
Depending on the speed of your computer (and the complexity of the instruments in your orchestra file) the performance of this "score" can either be auditioned in real-time, or written directly into a file on your hard disk. This entire process is referred to as "sound rendering" as analogous to the process of "image rendering" in the world of computer graphics.
Once rendered, you will listen to the resulting soundfile by opening it with your favorite sound editor and playing it either through the built-in digital-to-analog converter (DAC) on your motherboard or the DAC on your PC Sound Card.
Thus, in Csound, we basically work with two interdependent and complimentary text files, the orchestra file and the score file. These files can be given any name you would like. Typically, we give the two files the same name and differentiate between them by a unique three letter extension .orc for the orchestra file and .sco for the score file. Naming is up to you. In this chapter I have called the files etude1.orc and etude1.sco, etude2.orc and etude2.sco, etude3.orc and etude3.sco, etc. These "etude" orchestras contain six instruments each (instr 101 106, instr 107 112, instr 113 118, etc.). From these multi-instrument orchestras I have also created a set of single instrument orchestras to make it easier for you to isolate and experiment on individual instruments. These are named after the instrument number itself (101.orc and 101.sco, 102.orc and 102.sco, 117.orc and 117.sco, etc.). Naming the corresponding score file the same as the orchestra file will help you keep your instrument library organized and I highly recommend you do the same. In fact, all the scores and orchestras in The Csound Book and on the accompanying CD-ROM follow this naming convention.
The Orchestra File
The Csound orchestra file consists of two parts: the header section and the instrument section.
The Header Section
In the header section you define the sample and control rates at which the instruments will be rendered and you specify the number of channels in the output. The orchestral header that we will use throughout the text is:
|
Figure 1.1 Csounds default orchestral "header."
The code in this header assigns the sample rate (sr) to 44.1 K (44100), the control rate (kr) to 4410 and ksmps to 10 (ksmps = sr/kr). The header also indicates that this orchestra should render a mono soundfile by setting the number of channels (nchnls) to 1. (If we wanted to render a stereo sound file, we would simply set nchnls to 2).
The Instrument Section
In Csound, instruments are defined (and thus designed) by interconnecting "modules" or opcodes which either generate or modify signals. These signals are represented by symbols, labels or variable names which can be "patched" from one opcode to another. Individual instruments are given a unique instrument number and are delimited by the instr and endin statements. A single orchestra file can contain virtually any number of instruments. In fact, in Csound "everything" is an instrument your 8000 voice sampler, your 4000 voice FM synth, your 2000 voice multi-model waveguide synth, your 1000 band EQ, your 500 channel automated mixer, your 250 tap delay-line, Fractal-flanger, convolution-reverb, vetor-spatializer, whatever... To the Csound program each of these very different pieces of synthesis, signal processing and studio gear are merely instr 1, instr 2, instr 3, instr 4, etc.
The Orchestra Syntax
In the Csound orchestra file, the syntax of a generic opcode statement is:
| Output | Opcode | Arguments | Comments (optional) |
In the case of the oscil opcode, this translates into the following syntax:
| output | amplitude | frequency | function # | ; comment | |
| a1 | oscil | 10000, | 440, | 1 | ; oscillator |
Sound Design Etude 1: A Six Instrument Orchestra
In our first orchestra file instr 101 uses a table-lookup oscillator opcode, oscil, to compute a 440 Hz sine tone with amplitude of 10000. A block diagram of instr 101 is show in figure 1.2 and the actual Csound orchestra code for this instrument is shown in figure 1.3.

Figure 1.2 Block diagram of instr 101, a simple fixed frequency and amplitude table-lookup oscillator instrument.
| instr | 101 | ; SIMPLE OSCIL | |
| a1 | oscil | 10000, 440, 1 | |
| out | a1 | ||
| endin | |||
Figure 1.3 Orchestra code for instr 101, a fixed frequency and amplitude instrument using Csounds table-lookup oscillator opcode, oscil.
The block diagram of instr 101 clearly shows how the output of the oscillator, labeled a1, is "patched" to the input of the out opcode which writes the signal to the hard-disk.
Csound renders instruments line-by-line, from top to bottom. Input arguments are on the right of the opcode name. Outputs are on the left. Words that follow a semi-colon (;) are ignored. They are considered to be comments.
In instr 101, as show in figure 1.3, the input arguments to the oscillator are set at 10000 (amplitude), 440 (frequency) and 1 (for the function number of the waveshape template which the oscillator "reads"). The oscillator opcode renders the sound 44100 times a second with these settings and writes the result into the variable named a1. The sample values in the local-variable a1 can then be read as inputs by subsequent opcodes, such as the out opcode. In this way, variable names function like "patch cords" on a traditional analog synthesizer. And with these "virtual patch cords" one can route audio and control "signals" anywhere in an instruments using them to: set a parameter to a new value, dynamically control a parameter (like turning a knob), or as an audio input into some processing opcode.
In figure 1.4, you can see that instr 102 instr 106 use the same simple instrument design as instr 101 (one signal generator writing to the hard-disk). We have replaced the oscil opcode with more powerful synthesis opcodes such as: foscil a simple 2-oscillator FM synthesizer, buzz an additive set of harmonically-related cosines, pluck a simple waveguide synthesizer based on the Karplus-Strong algorithm, grain an asynchronous granular synthesizer and loscil a sample-based wavetable synthesizer with looping.
![]() |
|||||||||||||||||
|
|||||||||||||||||
![]() |
|||||||||||||||||
|
|||||||||||||||||
![]() |
|||||||||||||||||
|
|||||||||||||||||
![]() |
|||||||||||||||||
|
|||||||||||||||||
![]() |
|||||||||||||||||
|
|||||||||||||||||
Figure 1.4 Block diagrams and orchestra code for instr 102 instr 106, a collection of fixed frequency and amplitude instruments which use different synthesis methods to produce a single note with the same amplitude (10000) and frequency (440).
Clearly the "single signal-generator" structure of these instruments is identical. But once you render them you will hear that their sounds are quite unique. Even though they each play with a frequency of 440 Hz and amplitude of 10000, the underlying synthesis algorithm embodied in each opcode is fundamentally different requiring the specification of a unique set of parameters. In fact, these six signal generating opcodes (oscil, foscil, buzz, pluck, grain and loscil) represent the core synthesis technology behind many of todays most popular commercial synthesizers. One might say that in Csound, a single opcode is an entire synthesizer! Well... maybe not a very exciting or versatile synthesizer, but... in combination with other opcodes, Csound can, and will, take you far beyond any commercial implementation.
The Score File
Now lets look at the Csound score file which "performs" this orchestra of instruments. Like the orchestra file, the score file has two parts: tables and notes. In the first part, we use Csounds mathematical function-drawing subroutines (GENS) to directly "generate" function-tables (f-tables) and/or fill them by "reading" in soundfiles from the hard-disk. In the second part, we type in the note-statements. These note-events "perform" the instruments and pass them performance parameters such as frequency-settings, amplitude-levels, vibrato-rates and attack-times.
The GEN Routines
Csounds function generating subroutines are called GENS. Each of these (more than 20) subroutines is optimized to compute a specific class of functions or wavetables. For example, the GEN5 and GEN7 subroutines construct functions from segments of exponential curves or straight lines; the GEN9 and GEN10 subroutines generate composite waveforms made up of weighted sums of simple sinusoids; the GEN20 subroutine generates standard "window" functions such as the Hamming window and the Kaiser window which are typically used for spectrum analysis and grain envelopes; the GEN21 subroutine computes tables with different random distributions such as Gaussian, Cauchy and Poison; and the GEN1 subroutine will transfer data from a pre-recorded soundfile into a function-table for processing by one Csounds opcodes such as the looping-oscillator loscil.
Which function tables are required, and how the instruments in your orchestra use them, is totally up to you the sound designer. Sometimes its a matter of common sense. Other times its simply a matter of preference or habit. For instance, since instr 106 used the sample-based looping oscillator, loscil, I needed to load a sample into the orchestra. I chose GEN1 to do it. Whereas in instr 102, since I was using the foscil opcode, I could have chosen to frequency modulate any two waveforms, but decided on the traditional approach and modulated two sinewaves as defined by GEN10.
The Score Syntax
In the score file, the syntax of the Csound function statement (f-statement) is:
| f | number | load-time | table-size | GEN Routine | parameter1 | parameter... | ; comment |
If we wanted to generate a 16 point sinewave, we might write the following f-statement:
| f | 101 | 0 | 16 | 10 | 1 | ; a sinewave |
As a result, the f-table (f 101) would generate the function shown in figure 1.5.

Figure 1.5 A 16 point sine function defined by GEN10 with the arguments: f 101 0 16 10 1
As you can see, a sinewave drawn with 16 points of resolution is not particularly smooth. Most functions must be a "power-of-2" in length. For synthesized wavetables, we typically specify function table sizes between 512 (5 K) and 8192 (8 K). In our first score, etude1.sco, we define the following functions using GEN10, GEN20 and GEN1:
| f 1 | 0 | 4096 | 10 | 1 |
| f 2 | 0 | 4096 | 10 | 1 .5 .333 .25 .2 .166 .142 .125 .111 .1 .09 .083 .076 .071 .066 .062 |
| f 3 | 0 | 4097 | 20 | 2 1 |
| f 4 | 0 | 0 | 1 | "sing.aif" 0 4 0 |
Figure 1.6 Function tables defined in the score file etude1.sco.
All four functions are loaded at time 0. Both f 1 and f 2 use GEN10 to fill 4 K tables (4096 values) with: one cycle of a sinewave (f 1) and with the first 16 harmonics of a sawtooth wave (f 2). GEN20 is used to fill a 4 K table (f 3) with a Hanning window for use by the grain opcode. Finally, f 4 uses GEN1 to fill a table with a 44.1 K mono 16-bit AIFF format soundfile of a male vocalist singing the word "la" at the pitch A440 for 3 seconds. This "sample" is used by the loscil opcode. (Note that the length of f 4 is specified as 0. This tells the GEN1 subroutine to "read" the actual length of the file from the "header" of the soundfile "sing.aif." In this specific case, then, that length would be 132300 samples 44100 samples-per-second * 3 seconds.)
The Note List
In the second part of the Csound score file we write the "notes." As in the orchestra file, each note-statement in the score file occupies a single line. Note-statements (or i-statements) call for an instrument to be "made active" at a specific time and for a specific duration. Further, each note-statement can be used to pass along a virtually unlimited number of unique parameter settings to the instrument and these parameters can be changed on a note-by-note basis.
Like the orchestra, which renders a sound line-by-line, the score file is "read" line-by-line, note-by-note. However, "notes" can have the same start-times and thus be "performed" simultaneously. In Csound one must always be aware of the fact that whenever two or more notes are performed simultaneously or whenever they overlap, their amplitudes are added. This can frequently result in "samples-out-of-range" or "clipping." (We will discuss this in detail shortly.)
You may have noticed that in the orchestra, commas separated an opcodes arguments. Here in the score, any number of spaces or tabs separates both the f-table arguments and i-statement parameter fields, (or p-fields). Commas are not used.
In order to keep things organized and clear, sound designers often use tab-stops to separate their p-fields. This practice keeps p-fields aligned in straight columns and facilitates both reading and debugging. This is not required just highly recommended!
The First Three P-Fields
In all note-statements, the "meaning" of the first three p-fields (or columns) is reserved. They specify the instrument number, the start-time and the duration.
| ; | p1 | p2 | p3 |
| i | instrument # | start-time | duration |
You the sound designer, determine the function of all other p-fields. Typically, p4 is reserved for amplitude and p5 is reserved for frequency. This convention has been adopted in this chapter and in this text. In our first score, etude1.sco, a single note with duration of 3 seconds is played consecutively on instr 101 instr 106. Because the start-times of each note are spaced 4 seconds apart, there will be a second of silence between each audio event.
| ; P1 | P2 | P3 |
| ; instrument # | start-time | duration |
| i 101 | 0 | 3 |
| i 102 | 4 | 3 |
| i 103 | 8 | 3 |
| i 104 | 12 | 3 |
| i 105 | 16 | 3 |
| i 106 | 20 | 3 |
Figure 1.7 Simple score used to play instruments 101 through 106 as shown in figure 1.2 and 1.4.
Exercises for Etude 1
Render the Csound orchestra and score files: etude1.orc & etude1.sco.
Play and listen to the different sound qualities of each instrument.
Modify the score file and change the duration of each note.
Make all the notes start at the same time.
Comment out several of the notes so that they do not "play" at all.
Cut and Paste multiple copies of the notes, change the start times (p2) and duration (p3) of the copies to make the same instruments start and end at different times.
Create a canon at the unison with instr 106.
Look up and read about the opcodes used in instr 101 106 in the Csound Reference Manual.
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In the orchestra file, modify the frequency and amplitude arguments of each instrument.
Change the frequency ratios of the carrier and modulator in the foscil instrument.
Change the number of harmonics in the buzz instrument.
Change the initial function for the pluck instrument.
Change the density and duration of the grain instrument.
Make three copies of f 4 and renumber them f 5, f 6 and f 7. Load in your own samples ("yoursound1.aif," "yoursound2.aif," "yoursound3.aif"). Create multiple copies of instr 106 and number them instr 66, instr 67 and instr 68. Edit the instruments so that they each "read" a different soundfile at a different pitch. Play the different samples simultaneously.
In the file etude1.orc duplicate and renumber each duplicated instrument. Set different parameter values for each version of the duplicated instruments. Play all twelve instruments simultaneously. Adjust the amplitudes so that you have no samples-out-of-range.
Sound, Signals and Sampling
To better appreciate whats going on in Csound, it might be best to make sure we understand the acoustic properties of sound and how it is represented in a computer.
The experience of sound results from our eardrums sympathetic response to the compressions and rarefactions of the air molecules projected outward in all directions from a vibrating source. This vibrating pattern of pressure variations is called a waveform. Looking at figure 1.8, we can see that the air molecules would be compressed when the waveform is above the x-axis (positive) and rarefacted when below (negative). Figure 1.8 shows a single cycle of a square wave in both the time and frequency domains.
![]() |
Figure 1.8 A time domain (a) and frequency domain (b) representation of a square wave.
The time domain representation (a) plots time on the x-axis and amplitude on the y-axis a. The frequency domain representation (b) plots frequency on the x-axis and amplitude on the y-axis.
We experience sound in the time-domain as pressure variations, but we perceive sound in the frequency domain as spectral variations. The ear acts as a transducer, converting the mechanical motion of the eardrum (through the ossicles: the hammer, anvil and stirrup) to the oval window membrane which causes a traveling wave to propagate in the fluid of the cochlea and stimulate the hair cells on the basilar membrane. These hair cells are like a high resolution frequency analyzer that transmits this complex set of frequency information to the brain through nerves attached to each of the hair cells. With this incredibly sensitive set of sensors, transducers and transmitters we actively and continuously analyze, codify, classify and perceive the complex frequency characteristics of soundwaves as we resonate with the world around us.
Typically we employ a different transducer (a microphone) to convert acoustic waveforms into signals that we can visualize and manipulate in the computer. The process is referred to as sampling and is illustrated in figure 1.9.
When sampling a waveform, we use a microphone first to convert an acoustic pressure wave into an analogous electrical pressure wave or an analog signal. Then we pass this signal through an anti-aliasing lowpass filter to remove the frequency components above 1/2 the sampling-rate. In fact, a digital system can not accurately represent a signal above 1/2 the sampling rate (this "frequency mirror" is known as the Nyquist frequency). So then, after we lowpass filter out the highs, which we cant accurately represent, we proceed to "measure" or "sample" the amplitude of the signal with an analog-to-digital converter (ADC).
![]() |
Figure 1.9 Digital recording ("sampling") and playback.
If you have a 16 bit linear system, you would sample the analog waveform with 16 bits of precision (in a range from -32768 to 32767 or 216) taking a new measurement at the sample-rate (44100 times per second as defined by our default header). In essence we have "quantized" this continuous analog signal into a series of little snapshots (or steps) literally we are taking thousands of "little samples" from the signal. You can clearly see the "quantization" of the sinewave in figure 1.5 where each address corresponds with the amplitude of the signal at that point in time.
To hear a sound from our computer, we convert the digital signal (this sequence of "samples") back into an analog signal (a continuously varying voltage) using a digital-to-analog converter (DAC) followed by a smoothing lowpass filter. Clear? Well, enough of the basics for now. Lets get back to Csound.
Sound Design Etude 2: Parameter Fields in the Orchestra and Score
In our second orchestra, we modify instr 101 106 so that they can be updated and altered from the score file. Rather than setting each of the opcodes arguments to a fixed value in the orchestra, as we did in etude1.orc, we set them to "p" values that correspond to the p-fields (or column numbers) in the score. Thus each argument can be sent a completely different setting from each note-statement.
In instr 107, for example, p-fields are applied to each of the oscil arguments: amplitude (p4), frequency (p5) and wavetable (p6) as shown in figure 1.10.

Figure 1.10 Block diagram of instr 107, a simple oscillator instrument with p-field substitutions.
| instr | 107 | ; P-Field Oscil | |
| a1 | oscil | p4, p5, p6 | |
| out | a1 | ||
| endin | |||
Figure 1.11 Orchestra code for instr 107, a simple oscillator instrument with p-field arguments.
Thus from the score file in figure 1.12, we are able to re-use the same instrument to play a sequence of three descending octaves followed by an A major arpeggio.
| ; P1 | P2 | P3 | P4 | P5 | P6 |
| ; ins | strt | dur | amp | freq | waveshape |
| i 107 | 0 | 1 | 10000 | 440 | 1 |
| i 107 | 1.5 | 1 | 20000 | 220 | 2 |
| i 107 | 3 | 3 | 10000 | 110 | 2 |
| i 107 | 3.5 | 2.5 | 10000 | 138.6 | 2 |
| i 107 | 4 | 2 | 5000 | 329.6 | 2 |
| i 107 | 4.5 | 1.5 | 6000 | 440 | 2 |
Figure 1.12 Note-list for instr 107, which uses p-fields to "perform" 6 notes (some overlapping) with different frequencies, amplitudes and waveshapes.
In our next p-field example, shown in figures 1.13, 1.14 and 1.15, our rather limited instr 102 has been transformed into a more musically versatile instr 108 an instrument capable of a large array of tone colors.
![]() |
Figure 1.13 Block diagram of instr 108, a simple FM instrument with p-fields for each parameter.
| instr | 108 | ; P-Field FM | |
| a1 | foscil | p4, p5, p6, p7, p8, p9 | |
| out | a1 | ||
| endin | |||
Figure 1.14 Orchestra code for instr 108, a simple FM instrument with p-field substitutions.
| ; p1 | p2 | p3 | p4 | p5 | p6 | p7 | p8 | p9 |
| ; ins | strt | dur | amp | frEq | c | m | indEx | waveshape |
| i 108 | 7 | 1 | 10000 | 440 | 1 | 2 | 3 | 1 |
| i 108 | 8.5 | 1 | 20000 | 220 | 1 | .5 | 8 | 1 |
| i 108 | 10 | 3 | 10000 | 110 | 1 | 1 | 13 | 1 |
| i 108 | 10.5 | 2.5 | 10000 | 130.8 | 1 | 2.001 | 8 | 1 |
| i 108 | 11 | 2 | 5000 | 329.6 | 1 | 3.003 | 5 | 1 |
| i 108 | 11.5 | 1.5 | 6000 | 440 | 1 | 5.005 | 3 | 1 |
Figure 1.15 Note-list for instr 108 in which nine p-fields are used to "play" an FM synthesizer with different start-times, durations, amplitudes, frequencies, frequency-ratios, and modulation indices.
In the score excerpt shown in figure 1.15, each of the foscil arguments has been assigned to a unique p-field and can thus be altered on a note-by-note basis. In this case p4 = amplitude, p5 = frequency, p6 = carrier ratio, p7 = modulator ratio, p8 = modulation index and p9 = wavetable. Thus, starting 7 seconds into etude2.sco, instr 108 plays six consecutive notes. All six notes use f 1 (a sine wave in p9 ). The first two notes, for example, are an octave apart (p5 = 440 and 220) but have different c:m ratios (p7 = 2 and 13) and different modulation indexes (p8 = 3 and 8) resulting in two very different timbres. Obviously, p-fields in the orchestra allow us to get a wide variety of pitches and timbres from even the simplest of instruments.
Exercises for Etude 2
Render the Csound orchestra and score: etude2.orc & etude2.sco.
Play and listen to the different sound qualities of each note and instrument.
Modify the score file and change the start-times, durations, amplitudes and frequencies of each note.
Again look up and read about the opcodes used in instr 107 112 in the Csound Reference Manual and focus your study and experimentation on one synthesis technique at a time.
Explore the effect of different C:M rations in instr 108.
Without changing the C:M ratio, explore the effect of a low and a high modulation index.
Compare the difference in timbre when you modulate with a sine (f 1) and a sawtooth (f 2).
Using instr 109, compose a 4-part chord progression in which the bass and tenor have more harmonics than the alto and soprano.
Using instr 109 and 112 simultaneously, play the same score you composed for instr 9 by doubling the parts.
Using instr 110, experiment with the various pluck methods. (See the Csound Reference Manual for additional arguments).
Using instr 110, experiment with different initialization table functions f 1 and f 2. Also try initializing with noise and compare the timbre.
Explore the various parameters of the grain opcode.
Create a set of short etudes for each of the instruments alone.
Create a set of short etudes for several of the instruments in combination. Remember to adjust your amplitude levels so that you do not have any samples-out-of- range!
Lower the sample-rate and the control rate in the header. Recompile some of your modified instruments. Do you notice any difference in sound quality? Do you notice a change in brightness? Do you notice any noise artifacts? Do you notice any "aliasing?" (We will discuss the theory behind these phenomena a little later.)
Amplitudes and Clipping
As stated previously, if you have a 16 bit converter in your computer system (which is typical), then you can express 216 possible raw amplitude values (i.e. 65536 in the range -32768 to +32767). This translates to an amplitude range of over 90 dB (typically you get about 6 dB of range per bit of resolution). But if you have been doing the exercises, then you have probably noticed that note amplitudes in Csound are additive. This means that if an instrument has an amplitude set to 20000 and you simultaneously play two notes on that instrument, you are asking your converter to produce a signal with an amplitude of ± 40000. The problem is that your 16 bit converter can only represent values up to about 32000 and therefore your Csound "job" will report that there are samples-out-of-range and the resulting soundfile will literally be "clipped" as show in figure 1.16.
![]() |
Figure 1.16 Clipping as a result of adding two high-amplitude waveforms together.
Dealing with amplitudes is one of the most problematic aspects of working with Csound. There is no easy answer. The problem lies in the fact that Csound amplitudes are a simple mathematical representation of "the signal." These measurements take no account of the acoustical or perceptual nature of "the sound."
Simply put, two times the linear displacement of amplitude will not necessarily be "perceived" as two times as loud. A good book on Acoustics will help you appreciate the complexity of this problem. In the Csound world, do remember that whenever two or more notes are sounding, their amplitudes are added. If the numbers add up to anything greater than 32000 your signal will be clipped. Now Csound does have some opcodes and tools that will help you "deal" with this "samples-out-of-range" problem but none of the current opcodes or value converters truly solve it. Most of the time you will just have to set the levels lower and render the file again (and again and again) until you get the amplitudes into a range that your system can handle.
Data Rates
As you have seen in the first two etude orchestras, we can set and update parameters (arguments) with "floating-point constants" either directly in the orchestra or remotely through parameter-fields. But the real power of Csound is derived from the fact that one can update parameters with variables at any of four update or data-rates: setup, i-rate, k-rate, or a-rate, where:
i-rate variables are changed and updated at the note-rate.
k-rate variables are changed and updated at the control-rate (kr).
a-rate variables are changed and updated at the audio-rate (sr).
Both i-rate and k-rate variables are "scalars." Essentially, they take on one value at a given time. The i-rate variables are primarily used for setting parameter values and note durations. They are "evaluated" at "initialization-time" and remain constant throughout the duration of the note-event.
The k-rate variables are primarily used for storing and "updating" envelopes and sub-audio control signals. These variables are recomputed and at the control-rate (4410 times per second) as defined by kr in the orchestra header. The a-rate variables are "arrays" or "vectors" of information. These variables are used to store and update data such as the output signals of oscillators and filters that change at the audio sampling-rate (44100 times per second) as defined by sr in the header.
One can determine and identify the rate at which a variable will be "updated" by the "first letter" of the variable name. For example, the only difference between the two oscillators below is that one is computed at the audio-rate and the other at the control-rate. Both use the same opcode, oscil and both have the same arguments. What is different, then, is the sample resolution (precision) of the "output" signal.
| ; outPUT | opcode | amp, | frq, | func | ; comment |
| ksig | oscil | 10000, | 1000, | 1 | ; 1000 Hz Sine - f 1 |
| asig | oscil | 10000, | 1000, | 1 | ; 1000 Hz Sine - f 1 |
Figure 1.17 Two oscil opcodes with asig versus ksig outputs.
Given our default header settings of sr = 44100 and kr = 4410, ksig would be rendered at a sample-rate of 4 K and asig will be rendered at a sample-rate of 44.1K. In this case, the resulting output would sound quite similar because both would have enough "sample resolution" to accurately compute the 1000 HZ sinewave. However, if the arguments were different and the waveforms had additional harmonics, such as the sawtooth wave defined by f 2 in figure 1.18, the k-rate setting of 4410 samples-per-second would not accurately represent the waveform and "aliasing" would result. (We will cover this in more detail later.)
| ; output | opcode | amp, | frq, | func | ; comment |
| ksig | oscil | 10000, | 1000, | 2 | ; 1000 Hz Saw - f 2 |
| asig | oscil | 10000, | 1000, | 2 | ; 1000 Hz Saw - f 2 |
Figure 1.18 An "under-sampled" sawtooth wave (given kr = 4410 and a frequency setting of 1000), resulting in an "aliased" ksig output.
You should note that it is left up to you, the sound designer, to decide the most appropriate, efficient and effective rate at which to render your opcodes. For example, you could render all of your low frequency oscillators (LFOs) and envelopes at the audio-rate, but it would take longer to compute the signals and the additional resolution would, in most cases, be imperceptible.
Variable Names
In our instrument designs so far we have been talking about and used a1, asig, k1 and ksig in many cases interchangeably! Whats with these different names for the same thing? Csound is difficult enough. Why not be consistent?
Well, when it comes to naming variables, Csound only requires that the variable names you use begin with the letter i, k, or a. This is so that the program can determine at which rate to render that specific line of code. Anything goes after that.
For instance, you could name the output of the loscil opcode below a1, asig, asample, or acoolsound. Each variable name would be recognized by Csound and would run without error. In fact, given that the lines of code each have the same parameter settings, they would all sound exactly the same when rendered no matter what name you gave them! Therefore, it is up to you, the sound designer, to decide on a variable naming scheme that is clear, consistent and informative... to you.
| a1 | loscil | 10000, 440, 4 | ; sample playback of f 4 at A440 |
| out | a1 | ||
| asig | loscil | 10000, 440, 4 | ; sample playback of f 4 at A440 |
| out | asig | ||
| asample | loscil | 10000, 440, 4 | ; sample playback of f 4 at A440 |
| out | asample | ||
| acoolsound | loscil | 10000, 440, 4 | ; sample playback of f 4 at A440 |
| out | acoolsound | ||
Aliasing and the Sampling Theorem
Lets review a bit more theory before getting into our more advanced instrument designs. As we stated earlier, the "under-sampled sawtooth" (ksig) in figure 1.18 is an example of "aliasing" and a proof of the "Sampling Theorem." Simply put, the Sampling Theorem states that, in the digital domain, to accurately reconstruct (plot, draw or reproduce) a waveshape at a given frequency, you need twice as many "samples" as the highest frequency you are trying to render. This "hard" upper limit at 1/2 the sampling rate is known as the Nyquist frequency. With an audio-rate of 44100 Hz, you can accurately render tones with frequencies (and partials) up to 22050 Hz arguably far above the human range of hearing. And with a control-rate set to 4410 Hz, you can accurately render tones up to 2205 Hz. Certainly this would be an incredibly fast LFO and seems a bit high for slowly changing control signals, but you should recognize that certain segments of amplitude envelopes change extremely rapidly. "High-resolution" controllers can reduce the "zipper-noise" sometimes resulting from these rapid transitions.
Figure 1.19 illustrates graphically the phenomena known as "aliasing." Here, because a frequency is "under-sampled" an "alias" or alternate frequency results. In this specific case our original sinewave is at 5 Hz. We are sampling this wave at 4 Hz (remember that the minimum for accurate reproduction would be 10 Hz 2 time the highest frequency component) and what results is a 1 Hz tone! As you can see from the figure, the values that were returned from the sampling process trace the outline of a 1 Hz sinewave not a 5 Hz one. The actual aliased frequency is the "difference" between the sampling frequency and the frequency of the sample (or its partials too).
![]() |
Figure 1.19 Aliasing. A 5 Hz sine (a) is under-sampled at 4 times a second (b) resulting in the incorrect reproduction of a of a 1 Hz sine (c).
To totally understand and experience this phenomena, it would be very informative to go back to the earlier instruments in this chapter and experiment with different rate variables. (I recommend that you duplicate and renumber all the instruments. Then change all the asig and a1 variables to ksig and k1 variables and render again. Youll be surprised, and even pleased, with some of the lo-fi results!) For now lets move on.
Sound Design Etude 3: Four Enveloping Techniques
It is often stated that a computer is capable of producing "any" sound imaginable. And "mathematically," this is true. But if this is true, why is it that computerized and synthesized sounds are often so "sterile," "monotonous," and "dull?" To my ear, what makes a sound interesting and engaging is the subtle, dynamic and interdependent behavior of its three main parameters pitch, timbre and loudness. And what makes Csound a truly powerful software synthesis language, is the fact that one can literally patch the output of any opcode into virtually any input argument of another opcode thereby achieving an unsurpassed degree of dynamic parameter control. By subtly (or dramatically) modifying each of your opcodes input arguments, your "synthetic," "computerized" sounds will spring to life!
Up to this point we have essentially "gated" our Csound instruments simply turning them on at full volume. Im not sure that "any" acoustic instrument works like that. Clearly, applying some form of overall envelope control to these instruments would go a long way toward making them more "musical." And by adding other "dynamic parameter controls" well render sounds that are ever more enticing.
In instr 113, shown in figure 1.20 and 1.21, Csounds linen opcode is used to dynamically control the amplitude argument of the oscillator and thus functions as a typical attack-release (AR) envelope generator.

Figure 1.20 Block diagram of instr 113, an example one opcodes output controlling the input argument of another. In this case we are realizing dynamic amplitude control by modifying the amplitude argument of the oscil opcode with the output of another the linen.
| instr | 113 | ; SIMPLE OSCIL WITH ENVELOPE | ||
| k1 | linen | p4, p7, p3, p8 | ; p3=dur, p4=AMP, p7=attack, p8=release | |
| a1 | oscil | k1, p5, p6 | ; p5=freq, p6=waveshape | |
| out | a1 | |||
| endin | ||||
Figure 1.21 Orchestra code for instr 113, a simple oscillator instrument with amplitude envelope control.
In instr 115, shown in figures 1.22 and 1.23, a linen is again used to apply a dynamic amplitude envelope. But this time the "enveloping" is done by multiplying the output of the linen opcode (k1) with the output of the buzz opcode (a1). In fact, the multiplication is done in the input argument of the out opcode (k1 * a1). Here we not only see a different way of applying an envelope to a signal (multiplying it by a controller), but we also see that it is possible to perform mathematical operations on variables within an opcodes argument.
In figure 1.22, it can also be seen that an expon opcode is used in this instrument to move exponentially from the value in p10 to the value in p11 over the duration of the note (p3) and thereby sweeping the number of harmonic-cosines which buzz produces. The effect is very much like slowly closing a resonant lowpass filter and is another simple means of realizing dynamic timbre control.

Figure 1.22 Block Diagram of instr 115 showing amplitude control by multiplying two outputs and dynamic control of an argument.
| instr | 115 | ; Sweeping Buzz with Envelope | |
| k1 | linen | p4, p7, p3, p8 | |
| k2 | expon | p9, p3, p10 | |
| a1 | buzz | 1, p5, k2+1, p6 | |
| out | k1*a1 | ||
| endin | |||
Figure 1.23 Orchestra code for instr 115, an instrument with dynamic amplitude and harmonic control.
If youve been looking through the Csound Reference Manual you probably noticed that many opcodes, such as oscil, have both k-rate and a-rate versions. In instr 117, shown in figure 1.24, we use an audio-rate linen as an envelope generator. To do this, we "patch" the output of the grain opcode into the amplitude input of the linen, as can be seen in boldface in figure 1.25. Clearly this approach uses the linen to put an "envelope" on the signal coming from the granular synthesizer. In fact, we literally stuff the signal into an "envelope" before sending it out!

Figure 1.24 Block Diagram of instr 117 showing amplitude control by passing the signal (a1) through an a-rate envelope (a2).
| instr | 117 | ; Grains through an Envelope | |
| k2 | linseg | p5, p3/2, p9, p3/2, p5 | |
| k3 | line | p10, p3, p11 | |
| k4 | line | p12, p3, p13 | |
| k5 | expon | p14, p3, p15 | |
| k6 | expon | p16, p3, p17 | |
| a1 | grain | p4, k2, k3, k4, k5, k6, 1, p6, 1 | |
| a2 | linen | a1, p7, p3, p8 | |
| out | a2 | ||
| endin | |||
Figure 1.25 Orchestra code for instr 117, a granular synthesis instrument with dynamic control of many parameters. Note that the output of grain (a1) is "patched" into the amplitude argument of an a-rate linen to shape the sound with an overall amplitude envelope.
Envelopes
I have to admit that as a young student of electronic music, I was always "confused" by the use of the term "envelope" in audio and synthesis. I thought of "envelopes" as thin paper packages in which you could "enclose" a letter to a friend or a check to the phone company and could never quite make the connection. But the algorithm used in instr 117 makes the metaphor clear to me and hopefully to you. Here we see that the linen opcode completely "packages" or "folds" the signal into this "odd shaped" AR container and then sends it to the output. Figure 1.26 is another way to visualize the process. First we see the raw bipolar audio signal. Then we see the unipolar attack-decay-sustain-release (ADSR) amplitude envelope. Next we see the envelope applied to the audio signal. In the final stage we see the bipolar audio signal whose amplitude has been proportionally modified by the "contour" of the ADSR.
![]() |
Figure 1.26 "Enveloping" a signal.
Another way of looking at figure 1.26 would be that our bipolar signal is scaled (multiplied) by a unipolar ADSR (Attack-Decay-Sustain-Release) envelope which symmetrically "contours" the unipolar signal. The result is that the unipolar signal is "enveloped" in the ADSR package. Lets apply this new level of understanding in another instrument design.
In instr 118, shown in figure 1.27 and 1.28, we illustrate yet another way of applying an envelope to a signal in Csound. In this case, we are using an oscillator whose frequency argument is set to 1/p3. Lets plug in some numbers and figure out how this simple expression will help us compute the correct sub-audio frequency that will transform our periodic oscillator into an "aperiodic" envelope generator.
For example, if the duration of the note was 10 seconds and the frequency of our oscillator was set to 1/10 Hz it would take 10/10 Hz to completely "read" 1 cycle of the function table found in p7. Thus, setting the frequency of an oscillator to 1 divided by the note-duration, or 1/p3, guarantees that this periodic signal generator will compute only 1 period, or read only 1 complete cycle of its f-table during the course of each note event.

Figure 1.27 Block diagram of instr 118, an instrument with an oscillator for an envelope generator.
| instr | 118 | ; Loscil with Oscil Envelope | |
| k1 | oscil | p4, 1/p3, p7 | |
| k2 | expseg | p5, p3/3, p8, p3/3, p9, p3/3, p5 | |
| a1 | loscil | k1, k2, p6 | |
| out | a1 | ||
| endin | |||
Figure 1.28 Orchestra code for instr 118, an sample-playback instrument with and oscillator-based envelope and dynamic pitch modulation.
In instr 118 the envelope functions called by p7 (f 6, f 7 and f 8) use GEN7 and GEN5 to draw a variety of unipolar linear and exponential contours. It is very important to note that it is illegal to use a value of 0 in any exponential function such as those computed by the GEN5 subroutine or by the expseg opcode. You will notice therefore, that f 8, which uses GEN5, begins and ends with a value of .001 rather than 0.
| f 6 | 0 | 1024 | 7 | 0 10 1 1000 1 14 0 | ; linear AR envelope |
| f 7 | 0 | 1024 | 7 | 0 128 1 128 .6 512 .6 256 0 | ; linear ADSR envelope |
| f 8 | 0 | 1024 | 5 | .001 256 1 192 .5 256 .5 64 .001 | ; exponential ADSR |
Figure 1.29 Linear and exponential envelope functions using GEN5 and GEN7.
The enveloping technique employed in instr 118 (using an oscillator as an envelope generator) has several advantages. First, you can create an entire library of preset envelope shapes and change them on a note-by-note basis. Second, since the envelope generator is in fact an oscillator, you can have the envelope "loop" or retrigger during the course of the note event to create interesting "LFO-based amplitude-gating" effects. In instr 119, shown in figure 1.30, p8 determines the number of repetitions that will occur during the course of the note. If p8 is set to 10 and p3 is 5 seconds, the instrument will "retrigger" the envelope 2 times per second. Whereas, if the duration of the note was 1 second (p3 = 1), then the envelope would be re-triggered 10 times per second.
| instr | 119 | ; Retriggering Foscil with Oscil Envelope | |
| k1 | oscil | p4, 1/p3 * p8, p7 | ; P8=retrigger rate per note duration |
| k2 | line | p11, p3, p12 | |
| a1 | foscil | k1, p5, p9, p10, k2, p6 | |
| out | a1 | ||
| endin | |||
Figure 1.30 Orchestra code for instr 119, an FM instrument with an oscillator envelope in which p8 determines the "retriggering" frequency.
Unipolar and Bipolar Functions
Typically we think of an oscillator as something that makes a "sound" by "playing" different waveshapes or samples. However, we have seen that Csounds table-lookup oscillator is capable of reading any unipolar or bipolar function at any rate. Clearly this signal generator can be utilized equally as a control source or an audio source. Unlike commercial synthesizers, in Csound the "function" of an opcode is defined by the use and by the user. So far, we have been using several of Csounds GEN routines to compute unipolar and bipolar functions and it is important that we make sure we understand the difference.
Most audio waveforms, such as those created with GEN10 are bipolar having a symmetrical excursion above and below zero. On the other hand, most envelope functions, such as those we have created using GEN5 and GEN7, are unipolar having an excursion in one direction only, typically positive. In Csound, by default, all bipolar functions are normalized in a range of -1 to +1 and all unipolar functions are normalized in a range from 0 to +1 as shown in figure 1.31.
![]() |
Figure 1.31 A bipolar (-1 to +1) and a unipolar (0 to +1) function.
If you wish to bypass Csounds default normalization process, you must use a minus sign ( - ) before the GEN number as shown in f 3 and f 4 in figure 1.32.
| f 1 | 0 | 512 | 10 | 1 | ; Normalized bipolar Sine |
| f 2 | 0 | 512 | 7 | 0 6 1 500 1 6 0 | ; Normalized unipolar envelope |
| f 3 | 0 | 512 | -10 | .3 .013 .147 .026 | ; non-normalized bipolar SUM-of-SInes |
| f 4 | 0 | 512 | -7 | 440 256 220 256 440 | ; non-Normalized unipolar envelope |
Figure 1.32 Two Normalized functions (f 1 and f 2) and two non-normalizing functions (f 3 and f 4).
Exercises for Etude 3
Render the third Csound orchestra and score: etude3.orc & etude3.sco.
Play and listen to the different sound qualities and envelope shapes of each note and each instrument.
Modify the orchestra file and change the variable names to more meaningful ones. For instance, rename all a1 variables asig1 and k1 variables kenv1.
In the Csound Reference Manual, look up the new opcodes featured in instr 113 119:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|