METHODS OF WRITING CSOUND SCORES
Although the use of Csound real-time has become more prevalent and arguably more important whilst the use if the score has diminished and become less important, composing using score events within the Csound score remains an important bedrock to working with Csound. There are many methods for writing Csound score several of which are covered here, starting with the classical method of writing scores by hand, and concluding with the definition of a user-defined score language.
Writing Score by Hand
In Csound's original incarnation the orchestra and score existed as separate text files. This arrangement existed partly in an attempt to appeal to composers who had come from a background of writing for conventional instruments by providing a more familiar paradigm. The three unavoidable attributes of a note event - which instrument plays it, when, and for how long - were hardwired into the structure of a note event through its first three attributes or 'p-fields'. All additional attributes (p4 and beyond), for example: dynamic, pitch, timbre, were left to the discretion of the composer, much as they would be when writing for conventional instruments. It is often overlooked that when writing score events in Csound we define start times and durations in 'beats'. It just so happens that 1 beat defaults to a duration of 1 second leading to the consequence that many Csound users spend years thinking that they are specifying note events in terms of seconds rather than beats. This default setting can easily be modified and manipulated as shown later on.
The most basic score event as described above might be something like this:
i 1 0 5
which would demand that instrument number '1' play a note at time zero (beats) for 5 beats. After time of constructing a score in this manner it quickly becomes apparent that certain patterns and repetitions recur. Frequently a single instrument will be called repeatedly to play the notes that form a longer phrase therefore diminishing the worth of repeatedly typing the same instrument number for p1, an instrument may play a long sequence of notes of the same duration as in a phrase of running semiquavers rendering the task of inputting the same value for p3 over and over again slightly tedious and often a note will follow on immediately after the previous one as in a legato phrase intimating that the p2 start-time of that note might better be derived from the duration and start-time of the previous note by the computer than to be figured out by the composer. Inevitably short-cuts were added to the syntax to simplify these kinds of tasks:
i 1 0 1 60 i 1 1 1 61 i 1 2 1 62 i 1 3 1 63 i 1 4 1 64
could now be expressed as:
i 1 0 1 60 i . + 1 > i . + 1 > i . + 1 > i . + 1 64
where '.' would indicate that that p-field would reuse the same p-field value from the previous score event, where '+', unique for p2, would indicate that the start time would follow on immediately after the previous note had ended and '>' would create a linear ramp from the first explicitly defined value (60) to the next explicitly defined value (64) in that p-field column (p4).
A more recent refinement of the p2 shortcut allows for staccato notes where the rhythm and timing remain unaffected. Each note lasts for 1/10 of a beat and each follows one second after the previous.
i 1 0 .1 60 i . ^+1 . > i . ^+1 . > i . ^+1 . > i . ^+1 . 64
The benefits offered by these short cuts quickly becomes apparent when working on longer scores. In particular the editing of critical values once, rather than many times is soon appreciated.
Taking a step further back, a myriad of score tools, mostly also identified by a single letter, exist to manipulate entire sections of score. As previously mentioned Csound defaults to giving each beat a duration of 1 second which corresponds to this 't' statement at the beginning of a score:
t 0 60
"At time (beat) zero set tempo to 60 beats per minute"; but this could easily be anything else or evena string of tempo change events following the format of a linsegb statement.
t 0 120 5 120 5 90 10 60
This time tempo begins at 120bpm and remains steady until the 5th beat, whereupon there is an immediate change to 90bpm; thereafter the tempo declines in linear fashion until the 10th beat when the tempo has reached 60bpm.
'm' statements allow us to define sections of the score that might be repeated ('s' statements marking the end of that section). 'n' statements referencing the name given to the original 'm' statement via their first parameter field will call for a repetition of that section.
m verse i 1 0 1 60 i . ^+1 . > i . ^+1 . > i . ^+1 . > i . ^+1 . 64 s n verse n verse n verse
Here a 'verse' section is first defined using an 'm' section (the section is also played at this stage). 's' marks the end of the section definition and 'n' recalls this section three more times.
Just a selection of the techniques and shortcuts available for hand-writing scores have been introduced here (refer to the Csound Reference Manual for a more encyclopedic overview). It has hopefully become clear however that with a full knowledge and implementation of these techniques the user can adeptly and efficiently write and manipulate scores by hand.
Extension of the Score Language: bin="..."
It is possible to pass the score as written through a pre-processor before it is used by Csound to play notes. instead it can be first interpretted by a binary (application), which produces a usual csound score as a result. This is done by the statement bin="..." in the <CsScore> tag. What happens?
- If just a binary is specified, this binary is called and two files are passed to it:
- A copy of the user written score. This file has the suffix .ext
- An empty file which will be read after the interpretation by Csound. This file has the usual score suffix .sco
- If a binary and a script is specified, the binary calls the script and passes the two files to the script.
If you have Python1 installed on your computer, you should be able to run the following examples. They do actually nothing but print the arguments (= file names).
EXAMPLE ...csd: Calling a binary without a script
<CsoundSynthesizer> <CsInstruments> instr 1 endin </CsInstruments> <CsScore bin="python"> from sys import argv print "File to read = '%s'" % argv[0] print "File to write = '%s'" % argv[1] </CsScore> </CsoundSynthesizer>
When you execute this .csd file in the terminal, your output should include something like this:
File to read = '/tmp/csound-idWDwO.ext'
File to write = '/tmp/csound-EdvgYC.sco'
And there should be a complaint because the empty .sco file has not been written:
cannot open scorefile /tmp/csound-EdvgYC.sco
EXAMPLE .... csd: Calling a binary and a script
To test this, first save this file as print.py in the same folder where your .csd examples are:
from sys import argv print "Script = '%s'" % argv[0] print "File to read = '%s'" % argv[1] print "File to write = '%s'" % argv[2]
Then run the ....csd:
<CsoundSynthesizer> <CsInstruments> instr 1 endin </CsInstruments> <CsScore bin="python print.py"> </CsScore> </CsoundSynthesizer>
The output should include these lines:
Script = 'print.py'
File to read = '/tmp/csound-jwZ9Uy.ext'
File to write = '/tmp/csound-NbMTfJ.sco'
And again a complaint about the invalid score file:
cannot open scorefile /tmp/csound-NbMTfJ.sco
csbeats
Scripts
Scripting Language Examples
The following script uses a perl script to allow seeding options in the score. A random seed can be set as a comment; like ";;SEED 123". If no seed has been set, the current system clock is used. So there will be a different value for the first three random statements, while the last two statements will always generate the same values.
<CsoundSynthesizer> <CsInstruments> ;example by tito latini instr 1 prints "amp = %f, freq = %f\n", p4, p5; endin </CsInstruments> <CsScore bin="perl cs_sco_rand.pl"> i1 0 .01 rand() [200 + rand(30)] i1 + . rand() [400 + rand(80)] i1 + . rand() [600 + rand(160)] ;; SEED 123 i1 + . rand() [750 + rand(200)] i1 + . rand() [210 + rand(20)] e </CsScore> </CsoundSynthesizer> # cs_sco_rand.pl my ($in, $out) = @ARGV; open(EXT, "<", $in); open(SCO, ">", $out); while (<EXT>) { s/SEED\s+(\d+)/srand();$&/e; s/rand\(\d*\)/eval $&/ge; print SCO; }
- www.python.org^