In a way, control structures are the core of a programming language. The fundamental element in each language is the conditional if branch. Actually all other control structures like for-, until- or while-loops can be traced back to if-statements.
So, Csound provides mainly the if-statement; either in the usual if-then-else form, or in the older way of an if-goto statement. These ones will be covered first. Though all necessary loops can be built just by if-statements, Csound's loop facility offers a more comfortable way of performing loops. They will be introduced in the Loop section of this chapter. At least, time loops are shown, which are particulary important in audio programming languages.
The fundamental difference in Csound between i-time and k-time which has been explained in a previous chapter, must be regarded very carefully when you work with control structures. If you make a conditional branch at i-time, the condition will be tested just once for each note, at the initialization pass. If you make a conditional branch at k-time, the condition will be tested again and again in each control-cycle.
For instance, if you test a soundfile whether it is mono or stereo, this is done at init-time. If you test an amplitude value to be below a certain threshold, it is done at performance time (k-time). If you get user-input by a scroll number, this is also a k-value, so you need a k-condition.
Thus, all if and loop opcodes have an "i" and a "k" descendant. In the next few sections, a general introduction into the different control tools is given, followed by examples both at i-time and at k-time for each tool.
The use of the if-then-else statement is very similar to other programming languages. Note that in Csound, "then " must be written in the same line as "if" and the expression to be tested, and that you must close the if-block with an "endif" statement on a new line:
if <condition> then ... else ... endif
It is also possible to have no "else" statement:
if <condition> then ... endif
Or you can have one or more "elseif-then" statements in between:
if <condition1> then ... elseif <condition2> then ... else ... endif
If statements can also be nested. Each level must be closed with an "endif". This is an example with three levels:
if <condition1> then; first condition opened if <condition2> then; second condition openend if <condition3> then; third condition openend ... else ... endif; third condition closed elseif <condition2a> then ... endif; second condition closed else ... endif; first condition closed
A typical problem in Csound: You have either mono or stereo files, and want to read both with a stereo output. For the real stereo ones that means: use soundin (diskin / diskin2) with two output arguments. For the mono ones it means: use soundin / diskin / diskin2 with one output argument, and throw it to both output channels:
EXAMPLE 03C01.csd
<CsoundSynthesizer>
<CsOptions>
-o dac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
instr 1
Sfile = "/Joachim/Materialien/SamplesKlangbearbeitung/Kontrabass.aif" ;your soundfile path here
ifilchnls filenchnls Sfile
if ifilchnls == 1 then ;mono
aL soundin Sfile
aR = aL
else ;stereo
aL, aR soundin Sfile
endif
outs aL, aR
endin
</CsInstruments>
<CsScore>
i 1 0 5
</CsScore>
</CsoundSynthesizer>
If you use QuteCsound, you can browse in the widget panel for the soundfile. See the corresponding example in the QuteCsound Example menu.
The following example establishes a moving gate between 0 and 1. If the gate is above 0.5, the gate opens and you hear a tone. If the gate is equal or below 0.5, the gate closes, and you hear nothing.
EXAMPLE 03C02.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
seed 0; random values each time different
giTone ftgen 0, 0, 2^10, 10, 1, .5, .3, .1
instr 1
kGate randomi 0, 1, 3; moves between 0 and 1 (3 new values per second)
kFreq randomi 300, 800, 1; moves between 300 and 800 hz (1 new value per sec)
kdB randomi -12, 0, 5; moves between -12 and 0 dB (5 new values per sec)
aSig oscil3 1, kFreq, giTone
kVol init 0
if kGate > 0.5 then; if kGate is larger than 0.5
kVol = ampdb(kdB); open gate
else
kVol = 0; otherwise close gate
endif
kVol port kVol, .02; smooth volume curve to avoid clicks
aOut = aSig * kVol
outs aOut, aOut
endin
</CsInstruments>
<CsScore>
i 1 0 30
</CsScore>
</CsoundSynthesizer>
If you need an if-statement to give a value to an (i- or k-) variable, you can also use a traditional short form in parentheses: (a v b ? x : y). It asks whether the condition a or b is true. If a, the value is set to x; if b, to y. For instance, the last example could be written in this way:
EXAMPLE 03C03.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
seed 0
giTone ftgen 0, 0, 2^10, 10, 1, .5, .3, .1
instr 1
kGate randomi 0, 1, 3; moves between 0 and 1 (3 new values per second)
kFreq randomi 300, 800, 1; moves between 300 and 800 hz (1 new value per sec)
kdB randomi -12, 0, 5; moves between -12 and 0 dB (5 new values per sec)
aSig oscil3 1, kFreq, giTone
kVol init 0
kVol = (kGate > 0.5 ? ampdb(kdB) : 0); short form of condition
kVol port kVol, .02; smooth volume curve to avoid clicks
aOut = aSig * kVol
outs aOut, aOut
endin
</CsInstruments>
<CsScore>
i 1 0 20
</CsScore>
</CsoundSynthesizer>
An older way of performing a conditional branch - but still useful in certain cases - is an "if" statement which is not followed by a "then", but by a label name. The "else" construction follows (or doesn't follow) in the next line. Like the if-then-else statement, the if-goto works either at i-time or at k-time. You should declare the type by either using igoto or kgoto. Usually you need an additional igoto/kgoto statement for omitting the "else" block if the first condition is true. This is the general syntax:
i-time
if <condition> igoto this; same as if-then igoto that; same as else this: ;the label "this" ... ... igoto continue ;skip the "that" block that: ; ... and the label "that" must be found ... continue: ;go on after the conditional branch ...
k-time
if <condition> kgoto this; same as if-then kgoto that; same as else this: ;the label "this" ... ... kgoto continue ;skip the "that" block that: ; ... and the label "that" must be found ... continue: ;go on after the conditional branch ...
This is the same example as above in the if-then-else syntax for a branch depending on a mono or stereo file. If you just want to know whether a file is mono or stereo, you can use the "pure" if-igoto statement:
EXAMPLE 03C04.csd
<CsoundSynthesizer>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
instr 1
Sfile = "/Joachim/Materialien/SamplesKlangbearbeitung/Kontrabass.aif"
ifilchnls filenchnls Sfile
if ifilchnls == 1 igoto mono; condition if true
igoto stereo; else condition
mono:
prints "The file is mono!%n"
igoto continue
stereo:
prints "The file is stereo!%n"
continue:
endin
</CsInstruments>
<CsScore>
i 1 0 0
</CsScore>
</CsoundSynthesizer>
But if you want to play the file, you must also use a k-rate if-kgoto, because you have not just an action at i-time (initializing the soundin opcode) but also at k-time (producing an audio signal). So the code in this case is much more cumbersome than with the if-then-else facility shown previously.
EXAMPLE 03C05.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
instr 1
Sfile = "/Joachim/Materialien/SamplesKlangbearbeitung/Kontrabass.aif"
ifilchnls filenchnls Sfile
if ifilchnls == 1 kgoto mono
kgoto stereo
if ifilchnls == 1 igoto mono; condition if true
igoto stereo; else condition
mono:
aL soundin Sfile
aR = aL
igoto continue
kgoto continue
stereo:
aL, aR soundin Sfile
continue:
outs aL, aR
endin
</CsInstruments>
<CsScore>
i 1 0 5
</CsScore>
</CsoundSynthesizer>
This is the same example as above in the if-then-else syntax for a moving gate between 0 and 1:
EXAMPLE 03C06.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
seed 0
giTone ftgen 0, 0, 2^10, 10, 1, .5, .3, .1
instr 1
kGate randomi 0, 1, 3; moves between 0 and 1 (3 new values per second)
kFreq randomi 300, 800, 1; moves between 300 and 800 hz (1 new value per sec)
kdB randomi -12, 0, 5; moves between -12 and 0 dB (5 new values per sec)
aSig oscil3 1, kFreq, giTone
kVol init 0
if kGate > 0.5 kgoto open; if condition is true
kgoto close; "else" condition
open:
kVol = ampdb(kdB)
kgoto continue
close:
kVol = 0
continue:
kVol port kVol, .02; smooth volume curve to avoid clicks
aOut = aSig * kVol
outs aOut, aOut
endin
</CsInstruments>
<CsScore>
i 1 0 30
</CsScore>
</CsoundSynthesizer>
Loops can be built either at i-time or at k-time just with the "if" facility. The following example shows an i-rate and a k-rate loop created using the if-i/kgoto facility:
EXAMPLE 03C07.csd
<CsoundSynthesizer>
<CsInstruments>
;Example by Joachim Heintz
instr 1 ;i-time loop: counts from 1 until 10 has been reached
icount = 1
count:
print icount
icount = icount + 1
if icount < 11 igoto count
prints "i-END!%n"
endin
instr 2 ;k-rate loop: counts in the 100th k-cycle from 1 to 11
kcount init 0
ktimek timeinstk ;counts k-cycle from the start of this instrument
if ktimek == 100 kgoto loop
kgoto noloop
loop:
printks "k-cycle %d reached!%n", 0, ktimek
kcount = kcount + 1
printk2 kcount
if kcount < 11 kgoto loop
printks "k-END!%n", 0
noloop:
endin
</CsInstruments>
<CsScore>
i 1 0 0
i 2 0 1
</CsScore>
</CsoundSynthesizer>
But Csound offers a slightly simpler syntax for this kind of i-rate or k-rate loops. There are four variants of the loop opcode. All four refer to a label as the starting point of the loop, an index variable as a counter, an increment or decrement, and finally a reference value (maximum or minimum) as comparision:
The following .csd provides a simple example for all four loop opcodes:
EXAMPLE 03C08.csd
<CsoundSynthesizer>
<CsInstruments>
;Example by Joachim Heintz
instr 1 ;loop_lt: counts from 1 upwards and checks if < 10
icount = 1
loop:
print icount
loop_lt icount, 1, 10, loop
prints "Instr 1 terminated!%n"
endin
instr 2 ;loop_le: counts from 1 upwards and checks if <= 10
icount = 1
loop:
print icount
loop_le icount, 1, 10, loop
prints "Instr 2 terminated!%n"
endin
instr 3 ;loop_gt: counts from 10 downwards and checks if > 0
icount = 10
loop:
print icount
loop_gt icount, 1, 0, loop
prints "Instr 3 terminated!%n"
endin
instr 4 ;loop_ge: counts from 10 downwards and checks if >= 0
icount = 10
loop:
print icount
loop_ge icount, 1, 0, loop
prints "Instr 4 terminated!%n"
endin
</CsInstruments>
<CsScore>
i 1 0 0
i 2 0 0
i 3 0 0
i 4 0 0
</CsScore>
</CsoundSynthesizer>
The next example produces a random string of 10 characters and prints it out:
EXAMPLE 03C09.csd
<CsoundSynthesizer>
<CsInstruments>
;Example by Joachim Heintz
instr 1
icount = 0
Sname = ""; starts with an empty string
loop:
ichar random 65, 90.999
Schar sprintf "%c", int(ichar); new character
Sname strcat Sname, Schar; append to Sname
loop_lt icount, 1, 10, loop; loop construction
printf_i "My name is '%s'!\n", 1, Sname; print result
endin
</CsInstruments>
<CsScore>
; call instr 1 ten times
r 10
i 1 0 0
</CsScore>
</CsoundSynthesizer>
You can also use an i-rate loop to fill a function table (= buffer) with any kind of values. In the next example, a function table with 20 positions (indices) is filled with random integers between 0 and 10 by instrument 1. Nearly the same loop construction is used afterwards to read these values by instrument 2.
EXAMPLE 03C10.csd
<CsoundSynthesizer>
<CsInstruments>
;Example by Joachim Heintz
giTable ftgen 0, 0, -20, -2, 0; empty function table with 20 points
seed 0; each time different seed
instr 1 ; writes in the table
icount = 0
loop:
ival random 0, 10.999 ;random value
tableiw int(ival), icount, giTable ;writes in giTable at first, second, third ... position
loop_lt icount, 1, 20, loop; loop construction
endin
instr 2; reads from the table
icount = 0
loop:
ival tablei icount, giTable ;reads from giTable at first, second, third ... position
print ival; prints the content
loop_lt icount, 1, 20, loop; loop construction
endin
</CsInstruments>
<CsScore>
i 1 0 0
i 2 0 0
</CsScore>
</CsoundSynthesizer>
The next example performs a loop at k-time. Once per second, every value of an existing function table is changed by a random deviation of 10%. Though there are special opcodes for this task, it can also be done by a k-rate loop like the one shown here:
EXAMPLE 03C11.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 441
nchnls = 2
0dbfs = 1
giSine ftgen 0, 0, 256, 10, 1; sine wave
seed 0; each time different seed
instr 1
ktiminstk timeinstk ;time in control-cycles
kcount init 1
if ktiminstk == kcount * kr then; once per second table values manipulation:
kndx = 0
loop:
krand random -.1, .1;random factor for deviations
kval table kndx, giSine; read old value
knewval = kval + (kval * krand); calculate new value
tablew knewval, kndx, giSine; write new value
loop_lt kndx, 1, 256, loop; loop construction
kcount = kcount + 1; increase counter
endif
asig poscil .2, 400, giSine
outs asig, asig
endin
</CsInstruments>
<CsScore>
i 1 0 10
</CsScore>
</CsoundSynthesizer>
Until now, we have just discussed loops which are executed "as fast as possible", either at i-time or at k-time. But, in an audio programming language, time loops are of particular interest and importance. A time loop means, repeating any action after a certain amount of time. This amount of time can be equal to or different to the previous time loop. The action can be, for instance: playing a tone, or triggering an instrument, or calculating a new value for the movement of an envelope.
In Csound, the usual way of performing time loops, is the timout facility. The use of timout is a bit intricate, so some examples are given, starting from very simple to more complex ones.
Another way of performing time loops is by using a measurement of time or k-cycles. This method is also discussed and similar examples to those used for the timout opcode are given so that both methods can be compared.
The timout opcode refers to the fact that in the traditional way of working with Csound, each "note" (an "i" score event) has its own time. This is the duration of the note, given in the score by the duration parameter, abbreviated as "p3". A timout statement says: "I am now jumping out of this p3 duration and establishing my own time." This time will be repeated as long as the duration of the note allows it.
Let's see an example. This is a sine tone with a moving frequency, starting at 400 Hz and ending at 600 Hz. The duration of this movement is 3 seconds for the first note, and 5 seconds for the second note:
EXAMPLE 03C12.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
giSine ftgen 0, 0, 2^10, 10, 1
instr 1
kFreq expseg 400, p3, 600
aTone poscil .2, kFreq, giSine
outs aTone, aTone
endin
</CsInstruments>
<CsScore>
i 1 0 3
i 1 4 5
</CsScore>
</CsoundSynthesizer>
Now we perform a time loop with timout which is 1 second long. So, for the first note, it will be repeated three times, and for the second note five times:
EXAMPLE 03C13.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
giSine ftgen 0, 0, 2^10, 10, 1
instr 1
loop:
timout 0, 1, play
reinit loop
play:
kFreq expseg 400, 1, 600
aTone poscil .2, kFreq, giSine
outs aTone, aTone
endin
</CsInstruments>
<CsScore>
i 1 0 3
i 1 4 5
</CsScore>
</CsoundSynthesizer>
This is the general syntax of timout:
first_label:
timout istart, idur, second_label
reinit first_label
second_label:
... <any action you want to have here>
The first_label is an arbitrary word (followed by a colon) for marking the beginning of the time loop section. The istart argument for timout tells Csound, when the second_label section is to be executed. Usually istart is zero, telling Csound: execute the second_label section immediately, without any delay. The idur argument for timout defines how many seconds the second_label section is to be executed before the time loop begins again. Note that the "reinit first_label" is necessary to start the second loop after idur seconds with a resetting of all the values. (See the explanations about reinitialization in the chapter Initilalization And Performance Pass.)
As usual when you work with the reinit opcode, you can use a rireturn statement to constrain the reinit-pass. In this way you can have both, the timeloop section and the non-timeloop section in the body of an instrument:
EXAMPLE 03C14.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
giSine ftgen 0, 0, 2^10, 10, 1
instr 1
loop:
timout 0, 1, play
reinit loop
play:
kFreq1 expseg 400, 1, 600
aTone1 oscil3 .2, kFreq1, giSine
rireturn ;end of the time loop
kFreq2 expseg 400, p3, 600
aTone2 poscil .2, kFreq2, giSine
outs aTone1+aTone2, aTone1+aTone2
endin
</CsInstruments>
<CsScore>
i 1 0 3
i 1 4 5
</CsScore>
</CsoundSynthesizer>
In a time loop, it is very important to change the duration of the loop. This can be done either by referring to the duration of this note (p3) ...
EXAMPLE 03C15.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
giSine ftgen 0, 0, 2^10, 10, 1
instr 1
loop:
timout 0, p3/5, play
reinit loop
play:
kFreq expseg 400, p3/5, 600
aTone poscil .2, kFreq, giSine
outs aTone, aTone
endin
</CsInstruments>
<CsScore>
i 1 0 3
i 1 4 5
</CsScore>
</CsoundSynthesizer>
... or by calculating new values for the loop duration on each reinit pass, for instance by random values:
EXAMPLE 03C16.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
giSine ftgen 0, 0, 2^10, 10, 1
instr 1
loop:
idur random .5, 3 ;new value between 0.5 and 3 seconds each time
timout 0, idur, play
reinit loop
play:
kFreq expseg 400, idur, 600
aTone poscil .2, kFreq, giSine
outs aTone, aTone
endin
</CsInstruments>
<CsScore>
i 1 0 20
</CsScore>
</CsoundSynthesizer>
The applications discussed so far have the disadvantage that all the signals inside the time loop must definitely be finished or interrupted, when the next loop begins. In this way it is not possible to have any overlapping of events. For achieving this, the time loop can be used just to trigger an event. This can be done with event_i or scoreline_i. In the following example, the time loop in instrument 1 triggers each half to two seconds an instance of instrument 2 for a duration of 1 to 5 seconds. So usually the previous instance of instrument 2 will still play when the new instance is triggered. In instrument 2, some random calculations are executed to make each note different, though having a descending pitch (glissando):
EXAMPLE 03C17.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
giSine ftgen 0, 0, 2^10, 10, 1
instr 1
loop:
idurloop random .5, 2 ;duration of each loop
timout 0, idurloop, play
reinit loop
play:
idurins random 1, 5 ;duration of the triggered instrument
event_i "i", 2, 0, idurins ;triggers instrument 2
endin
instr 2
ifreq1 random 600, 1000 ;starting frequency
idiff random 100, 300 ;difference to final frequency
ifreq2 = ifreq1 - idiff ;final frequency
kFreq expseg ifreq1, p3, ifreq2 ;glissando
iMaxdb random -12, 0 ;peak randomly between -12 and 0 dB
kAmp transeg ampdb(iMaxdb), p3, -10, 0 ;envelope
aTone poscil kAmp, kFreq, giSine
outs aTone, aTone
endin
</CsInstruments>
<CsScore>
i 1 0 30
</CsScore>
</CsoundSynthesizer>
The last application of a time loop with the timout opcode which is shown here, is a randomly moving envelope. If you want to create an envelope in Csound which moves between a lower and an upper limit, and has one new random value in a certain time span (for instance, once a second), the time loop with timout is one way to achieve it. A line movement must be performed in each time loop, from a given starting value to a new evaluated final value. Then, in the next loop, the previous final value must be set as the new starting value, and so on. This is a possible solution:
EXAMPLE 03C18.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
giSine ftgen 0, 0, 2^10, 10, 1
seed 0
instr 1
iupper = 0; upper and ...
ilower = -24; ... lower limit in dB
ival1 random ilower, iupper; starting value
loop:
idurloop random .5, 2; duration of each loop
timout 0, idurloop, play
reinit loop
play:
ival2 random ilower, iupper; final value
kdb linseg ival1, idurloop, ival2
ival1 = ival2; let ival2 be ival1 for next loop
rireturn ;end reinit section
aTone poscil ampdb(kdb), 400, giSine
outs aTone, aTone
endin
</CsInstruments>
<CsScore>
i 1 0 30
</CsScore>
</CsoundSynthesizer>
Note that in this case the oscillator has been put after the time loop section (which is terminated by the rireturn statement. Otherwise the oscillator would start afresh with zero phase in each time loop, thus producing clicks.
The metro opcode outputs a "1" at distinct times, otherwise it outputs a "0". The frequency of this "banging" (which is in some way similar to the metro objects in PD or Max) is given by the kfreq input argument. So the output of metro offers a simple and intuitive method for controlling time loops, if you use it to trigger a separate instrument which then carries out another job. Below is a simple example for calling a subinstrument twice a second:
EXAMPLE 03C19.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
instr 1; triggering instrument
kTrig metro 2; outputs "1" twice a second
if kTrig == 1 then
event "i", 2, 0, 1
endif
endin
instr 2; triggered instrument
aSig oscils .2, 400, 0
aEnv transeg 1, p3, -10, 0
outs aSig*aEnv, aSig*aEnv
endin
</CsInstruments>
<CsScore>
i 1 0 10
</CsScore>
</CsoundSynthesizer>
The example which is given above (0337.csd) as a flexible time loop by timout, can be done with the metro opcode in this way:
EXAMPLE 03C20.csd
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
;Example by Joachim Heintz
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
giSine ftgen 0, 0, 2^10, 10, 1
seed 0
instr 1
kfreq init 1; give a start value for the trigger frequency
kTrig metro kfreq
if kTrig == 1 then ;if trigger impulse:
kdur random 1, 5; random duration for instr 2
event "i", 2, 0, kdur; call instr 2
kfreq random .5, 2; set new value for trigger frequency
endif
endin
instr 2
ifreq1 random 600, 1000; starting frequency
idiff random 100, 300; difference to final frequency
ifreq2 = ifreq1 - idiff; final frequency
kFreq expseg ifreq1, p3, ifreq2; glissando
iMaxdb random -12, 0; peak randomly between -12 and 0 dB
kAmp transeg ampdb(iMaxdb), p3, -10, 0; envelope
aTone poscil kAmp, kFreq, giSine
outs aTone, aTone
endin
</CsInstruments>
<CsScore>
i 1 0 30
</CsScore>
</CsoundSynthesizer>
Note the differences in working with the metro opcode compared to the timout feature:
Steven Yi: Control Flow (Part I = Csound Journal Spring 2006, Part 2 = Csound Journal Summer 2006)