I’m not sure it warrants a tutorial, but I’ll try to answer this as best I can. First things first, the event and schedule family of opcoes are used to trigger score events. UDOs and sub instruments provide a running abstracted or standalone code within an instrument.
event
The event family of opcodes are used to trigger score events. event_i
is used to trigger events at i-time, meaning it will only be called at the very start of an instrument’s lifetime. event
on the other hand is called on every k cycle meaning it will be called continuously over the life-time of an instrument. In Cabbage, one usually uses event opcodes with a changed opcode, i.e,
kButton chnget "button1"
if changed:k(kButton) == 1 then
event "i", 100, 0, 10
endif
schedule
The schedule family of opcodes offer more or less the same functionality, only the the k versions also offer a trigger parameter. This eliminates the needs for an if statement:
kButton chnget "but1"
schedkwhen changed:k(kButton), 0, 100, 2, 0, 1
The schedule family of opcodes also provide streamlined management of instruments by letting the user decide how many instances of an instrument can run at the same time. There will be times when this is important.
UDOs
UDOs, which are created using opcode
provide users with a way of abstracting functionality into self-contained user defined opcodes. They were added to Csound so that users could write their own custom opcodes in Csound. Before they existed, the only way to create custom opcodes was using a lower level language like C. UDO definitions can use all or any opcodes. There are no limitations. They can also be recursive, meaning they can call themselves. Spend some time trying to get your head around this recursive vocoder!
You can also keep all your UDOs in a separate file and include them using an #include statement:
#include "MyUDOs.whatever"
subinstr
subinstr
was a precursor to the UDO construct. It’s not used that often today, but works in a similar kind of way. Instead of defined an custom opcode, you create an instrument for a specific task. You then call this instrument within another one. Since UDOs became a thing, subinstr has gone the way of the dodo. It can still be useful as a pedagogic tool, but I find I rarely use it all these days.
channels
Channels are used to send signals around a Csound orchestra. Host applications can tap into Csound’s channels and send information to then. Cabbage does this all the time. But channels are not just for host application. Csound can use channels to send audio or k-rate signal from one instrument to another. chnget
recieves data while chnset
sends data. chnmix
is a very useful opcode for accumulating signal. Consider the following example:
instr 1
a1 oscil .25, 100
chnset a1, "instr1"
endin
instr 2
a1 oscil .25, 200
chnset a1, "instr2"
endin
instr 3
a1 oscil .25, 300
chnset a1, "instr3"
endin
instr 4
a1 oscil .25, 400
chnset a1, "instr4"
endin
instr 5
a1 chnget "instr1"
a2 chnget "instr2"
a3 chnget "instr3"
a4 chnget "instr4"
outs a1+a2+a3+a4, a1+a2+a3+a4
endin
We send the output of the each instrument to channel called “instrN”. In instrument 5 we pick up the signals sent to those channels using a chnget
. We then mix the signals and send it to the output. Using a chnmix
would make things a little simpler:
instr 1
a1 oscil .25, 100
chnset a1, "instr"
endin
instr 2
a1 oscil .25, 200
chnmix a1, "instr"
endin
instr 3
a1 oscil .25, 300
chnmix a1, "instr"
endin
instr 4
a1 oscil .25, 400
chnmix a1, "instr"
endin
instr 5
a1 chnget "instr"
outs a1, a1
endin
In particular instrument 5, In this case we only need to grab the signal from a single channel. Note that instrument 1 uses a chnset
and not chnmix
. This is purposely used to clear the contents of the channel on each k-cycle. If i used a chnmix
instead the signal would accumulate over time and start to distort. In cases where this is likely to occur, you can use a chnclear
to clear the contents of the channel.
p.s. I’m not sure this is quite tutorial stuff, it’s good info, but I may move it to the cabbage stew section?