Nick Masiuk Jim Mahoney "My Csound Tutorial" Csound is a programming language the sole purpose of which is to generate sound. In this paper, I shall describe what I have learned this semester studying the first chapter of The Csound Book, ÒIntroduction to Sound Design in Csound.Ó There is a duality to writing programs in Csound. You need two different text files to run a Csound program, the orchestra file and the score file. Basically, the orchestra file is where you build your instruments, and the score file is where you tell these instruments what to do. Boulanger recommends that we place three letter suffixes .orc and .sco after our filenames so not as to confuse the two, and I concur. There are two parts to the orchestra file, the header and the instrument sections. The header is where you put this funny little sequence: sr = 44100 kr = 4410 ksmps = 10 nchnls = 1 Which tells Csound that your a-rate variables will be updated 44100 times per second, that your k-rate variables will be updated 4410 times per second, that there is a 10:1 ratio between these two numbers (this I do not understand... why doesn't it already know that? If you try to put another number into ksmps which is not the ratio between sr and kr, it gives you an error), and that the number of audio channels here is 1. If you put the number 2 into nchnls, then we'd have stereo. Apparently Csound can support any number of audio channels. After this this thing goes any global variables you may want to have. Global variables are initialized before any of the instruments are, and while they are informed within particular instruments, they retain their value even outside of them. That looks like this: garvb init 0 gacmb init 0 I should talk some about variables in Csound. As far as I have gotten, there are four different kinds, i-rate, a-rate, k-rate and g-rate. The i-rate variables are updated at the note-rate, which means these things are usually initialized inside an instrument, and are then informed by the score file. More on the score file later, but know now that these i-rate guys hold their values until we tell them otherwise specifically. The a-rate and k-rate variable types pertain to signals generally. The a-rate variables update themselves at whatever speed you set sr to in the header. Correspondingly the k-rate variables to the kr value in that header. The k-rate variables are generally used for control of various parameters, such as low-frequency oscillators which determine pitch in an audible, high-frequency oscillator. Most envelopes are kept in the k-rate. You don't want to use them for actual audio because the resolution is fairly poor, but in control applications this lack of resolution is imperceptible. K-rate variables save computing time by performing the same function as an a-rate but at lower resolutions. Global variables come in three familiar flavors: gi-rates, which are changed and updated at the note rate (i.e. in the score), ga-rates which are changed and updated at the audio rate, and gk-rates, which are changed and updated at the control rate. The same as the other ones, except it holds its value even outside of particular instruments. In this way, instruments can communicate with each other. Let's look at the instrument section of the orchestra file. instr 1 idur = p3 iamp = p4 icps = p5 ifn = p6 iatk = p7 idec = p8 kenv linen iamp, iatk, idur, idec a1 oscil kenv, icps, ifn out a1 endin The individual instrument must begin with an instr (and a unique number) and end with endin. When I say that global varibles hold their values outside of instruments, I mean they can be informed within a particular instrument, and referred to even after endin, something the other variables cannot do. After the prefix i, a, k or ga, gi, or gk, you can put in whatever you like as a variable name. Csound's instrument section seems to come in three columns, the first for the variable, the second for something called an opcode, and the third for that opcode's arguments. The = sign is actually an opcode, which only takes one argument... being the value for the preceding variable. All this p-stuff, p3, p4, p5... all of this pertains to the score file. More on that in a bit. Kenv shall hold our envelope, and it is updated at the k-rate. It uses an opcode called linen, which applies a straight line rise and decay pattern to any input signal. Basically, we input values for all of these i-rate variables in the score file. Linen takes four arguments, iamp (how loud we want this thing to be), iatk (time it takes to get from 0 to the iamp value), idur (how long the entirety of the note-event is) and idec (time it takes to get back to 0 from iamp). We then hook up the kenv variable to our oscil opcode, using it as one of oscil's arguments. Oscil takes three arguments, iamp (same as before), icps (pitch in cycles-per-second), and f# call. More on f#s later. So oscil gets sent out in the second-to-last line. To get something heard, you put its variable next to the out opcode. If we were multichannel, we'd put: outs a1, a1 endin Our oscil's amplitude will be altered at 4410 times per second, according to the parameters we have placed within the kenv variable. We can do this for almost any argument for any opcode. We could have put kenv in oscil's icps argument instead, and rather than have an audio signal whose amplitude rises, sustains and then decays we would have a signal whose pitch would act in a similar manner. iamp values for a-rate variables generally refer to bit depth. 16-bit audio can only express values from 0-32000 (2 to the 16th... 66536... -32768 to +32768). When an a-rate opcode (which you send out) asks for iamp as an argument, your value must reflect this fact, else clipping ensues. The Score file The first thing we put in the score files are all of our f-statements. These things are referred to by the instruments of the score file. They can do various things. So far, all I have been using them for is drawing waveforms for me, and for loading .aiff files from off the hard drive. Here's what an f-statement looks like: f 1 0 4096 10 1 First we have the "f"... then comes this "f" statement's unique number... next is the f-statement's loadtime (this is not discussed at all... It is in seconds? what?) next is the size of the table that this f-statement is going to draw, next is the GEN number, which refers here to GEN 10. The GENs are function generating subroutines, says the book. GEN 10 draws waveforms with harmonic partials. After the GEN number in the f-statement, whatever comes next are that GEN's specific arguments. GEN 10, which I shall use almost exclusively for this paper, asks for the strength, in terms of 0 to 1, of harmonic partials of a waveform starting with the fundamental and continuing up through the overtone series. If we wanted a wave which had up to the 8th overtone, with all overtones having equal, maximum strength, it would look like this: f 1 0 4096 10 1 1 1 1 1 1 1 Apparently these f-statements and GEN subroutines can do a wide variety of things which pertain to digital audio. I only know about GEN 10. After our F-tables we have the note list. Remember all of our p# things from the orchestra file? Here is where they are used. Whatever i-rate variables we assign a p(digit) to get referred to in the score. Here's what it looks like: ;p1 p2 p3 p4 p5 p6 p7 p8 The first three p-fields are reserved by Csound. p1 always refers to the number of the instrument you are currently informing, p2 always refers to the start-time of the note-event you are describing, and p3 always refers to the duration of this note-event. Beyond that, p-field definitions are up to the programmer. For instrument 1: ;i 1 0 5 10000 440 1 .5 .6 where: p1 - instrument number p2 - start time (sec) p3 - duration (sec) p4 - amplitude in bits p5 - number of times per second it cycles through the wave described in the referenced f-table p6 - f-table referred to (waveform to be oscillated) p7 - amplitude attack (rise from 0 to iamp/p4/10000) p8 - amplitude decay (fall from iamp/p4/10000 to 0) Klar? The Csound programming experience is that of 'hooking stuff up', meaning that we describe our instrument in the orchestra file, including variables the values of which we shall input later in the score file. In the orchestra file, we can take these values and run them through a whole bunch of stuff. From here on in I shall present bits of code which have been commented on.