Functional syntax is very common in many programming languages. It takes the form of fun(), where fun is any function which encloses its arguments in parentheses. Even in "old" Csound, there existed some rudiments of this functional syntax in some mathematical functions, such as sqrt(), log(), int(), frac(). For instance, the following code
iNum = 1.234 print int(iNum) print frac(iNum)
would print:
instr 1: #i0 = 1.000
instr 1: #i1 = 0.230
Here the integer part and the fractional part of the number 1.234 are passed directly as an argument to the print opcode, without needing to be stored at any point as a variable.
This alternative way of formulating code can now be used with many opcodes in Csound61. In the future many more opcodes will be incorporated into this system. First we shall look at some examples.
The traditional way of applying a fade and a sliding pitch (glissando) to a tone is something like this:
EXAMPLE 03I01_traditional_syntax.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> sr = 44100 nchnls = 1 ksmps = 32 0dbfs = 1 instr 1 kFade linseg 0, p3/2, 1, p3/2, 0 kSlide expseg 400, p3/2, 800, p3/2, 600 aTone poscil kFade, kSlide out aTone endin </CsInstruments> <CsScore> i 1 0 5 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
In plain English what is happening is:
Each of these four lines can be considered as a "function call", as we call the opcodes (functions) linseg, expseg, poscil and out with certain arguments (input parameters). If we now transform this example to functional syntax, we will avoid storing the result of a function call in a variable. Rather we will feed the function and its arguments directly into the appropriate slot, by means of the fun() syntax.
If we write the first line in functional syntax, it will look like this:
linseg(0, p3/2, 1, p3/2, 0)
And the second line will look like this:
expseg(400, p3/2, 800, p3/2, 600)
So we can reduce our code from four lines to two lines:
EXAMPLE 03I02_functional_syntax_1.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> sr = 44100 nchnls = 1 ksmps = 32 0dbfs = 1 instr 1 aTone poscil linseg(0, p3/2, 1, p3/2, 0), expseg(400, p3/2, 800, p3/2, 600) out aTone endin </CsInstruments> <CsScore> i 1 0 5 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
Or would you prefer the "all-in-one" solution?
EXAMPLE 03I03_functional_syntax_2.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> sr = 44100 nchnls = 1 ksmps = 32 0dbfs = 1 instr 1 out poscil(linseg(0, p3/2, 1, p3/2, 0), expseg(400, p3/2, 800, p3/2, 600)) endin </CsInstruments> <CsScore> i 1 0 5 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
Most of the Csound opcodes work not only at one rate. You can, for instance, produce random numbers at i-, k- or a-rate:3
ires random imin, imax kres random kmin, kmax ares random kmin, kmax
Let us assume we want to change the highest frequency in our example from 800 to a random value between 700 and 1400 Hz, so that we hear a different movement for each tone. In this case, we can simply write random(700, 1400), because the context demands an i-rate result of the random operation here:4
EXAMPLE 03I04_functional_syntax_rate_1.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> sr = 44100 nchnls = 1 ksmps = 32 0dbfs = 1 instr 1 out poscil(linseg(0, p3/2, 1, p3/2, 0), expseg(400, p3/2, random(700, 1400), p3/2, 600)) endin </CsInstruments> <CsScore> r 5 i 1 0 3 </CsScore> </CsoundSynthesizer> ;example by joachim heintzBut it is much clearer both, for the Csound parser and for the Csound user, if you explicitly declare at which rate a function is to be performed. This code claims that poscil runs at a-rate, linseg and expseg run at k-rate, and random runs at i-rate here:
EXAMPLE 03I05_functional_syntax_rate_2.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> sr = 44100 nchnls = 1 ksmps = 32 0dbfs = 1 instr 1 out poscil:a(linseg:k(0, p3/2, 1, p3/2, 0), expseg:k(400, p3/2, random:i(700, 1400), p3/2, 600)) endin </CsInstruments> <CsScore> r 5 i 1 0 3 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
As you can see, rate declaration is done with simply :a, :k or :i after the function. It would represent good practice to include it all the time, to be clear about what is happening.
Currently, there is a limitation in that only opcodes which have one or no outputs can be written using functional syntax. For instance, reading a stereo file using soundin
aL, aR soundin "my_file.wav"
cannot be written using functional syntax. This limitation is likely to be removed in the future.
EXAMPLE 03I06_functional_syntax_udo.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> sr = 44100 nchnls = 1 ksmps = 32 0dbfs = 1 opcode FourModes, a, akk[] ;kFQ[] contains four frequency-quality pairs aIn, kBasFreq, kFQ[] xin aOut1 mode aIn, kBasFreq*kFQ[0], kFQ[1] aOut2 mode aIn, kBasFreq*kFQ[2], kFQ[3] aOut3 mode aIn, kBasFreq*kFQ[4], kFQ[5] aOut4 mode aIn, kBasFreq*kFQ[6], kFQ[7] xout (aOut1+aOut2+aOut3+aOut4) / 4 endop instr 1 kArr[] fillarray 1, 2000, 2.8, 2000, 5.2, 2000, 8.2, 2000 aImp mpulse .3, 1 out FourModes(aImp, 200, kArr) endin </CsInstruments> <CsScore> i 1 0 10 </CsScore> </CsoundSynthesizer> ;example by joachim heintz, based on an example of iain mccurdy
Only you, and perhaps your spiritual consultant, can know ...
But seriously, this is mostly a matter of style. Some people consider it most elegant if all is written in one single expression, whilst others prefer to see the signal flow from line to line. Certainly excessive numbers of parentheses may not result in the best looking code ...
At least the functional syntax allows the user to emphasize his or her own personal style and to avoid some awkwardness:
"If i new value of kIn has been received, do this and that", can be written:
if changed(kIn)==1 then <do this and that> endif
"If you understand what happens here, you will have been moved to the next level", can be written:
EXAMPLE 03I07_functional_syntax_you_win.csd
<CsoundSynthesizer> <CsOptions> -odac -m128 </CsOptions> <CsInstruments> sr = 44100 nchnls = 1 ksmps = 32 0dbfs = 1 seed 0 opcode FourModes, a, akk[] ;kFQ[] contains four frequency-quality pairs aIn, kBasFreq, kFQ[] xin aOut1 mode aIn, kBasFreq*kFQ[0], kFQ[1] aOut2 mode aIn, kBasFreq*kFQ[2], kFQ[3] aOut3 mode aIn, kBasFreq*kFQ[4], kFQ[5] aOut4 mode aIn, kBasFreq*kFQ[6], kFQ[7] xout (aOut1+aOut2+aOut3+aOut4) / 4 endop instr ham gkPchMovement = randomi:k(50, 1000, (random:i(.2, .4)), 3) schedule("hum", 0, p3) endin instr hum if metro(randomh:k(1, 10, random:k(1, 4), 3)) == 1 then event("i", "play", 0, 5, gkPchMovement) endif endin instr play iQ1 = random(100, 1000) kArr[] fillarray 1*random:i(.9, 1.1), iQ1, 2.8*random:i(.8, 1.2), iQ1*random:i(.5, 2), 5.2*random:i(.7, 1.4), iQ1*random:i(.5, 2), 8.2*random:i(.6, 1.8), iQ1*random:i(.5, 2) aImp mpulse ampdb(random:k(-30, 0)), p3 out FourModes(aImp, p4, kArr)*linseg(1, p3/2, 1, p3/2, 0) endin </CsInstruments> <CsScore> i "ham" 0 60 </CsScore> </CsoundSynthesizer> ;example by joachim heintz, with thanks to steven and iain
So enjoy, and stay in contact with the spirit ...
There has been error in communication with Booktype server. Not sure right now where is the problem.
You should refresh this page.