INITIALIZATION AND PERFORMANCE PASS
Not only for beginners, but also for experienced Csound users, many problems result from the misunderstanding of the so-called i-rate and k-rate. You want Csound to do something just once, but Csound does it continuously. You want Csound to do something continuously, but Csound does it just once. If you experience such a case, you will most probably have confused i- and k-rate-variables.
The concept behind this is actually not complicated. But it is something which is more implicitly mentioned when we think of a program flow, whereas Csound wants to know it explicitely. So we tend to forget it when we use Csound, and we do not notice that we ordered a stone to become a wave, and a wave to become a stone. This chapter tries to explicate very carefully the difference between stones and waves, and how you can profit from them, after you understood and accepted both qualities.
The Init Pass
Whenever a Csound instrument is called, all variables are set to initial values. This is called the initialization pass.
There are certain variables, which stay in the state in which they have been put by the init-pass. These variables start with an i if they are local (= only considered inside an instrument), or with a gi if they are global (= considered overall in the orchestra). This is a simple example:
<CsoundSynthesizer> <CsInstruments> giGlobal = 1/2 instr 1 iLocal = 1/4 print giGlobal, iLocal endin instr 2 iLocal = 1/5 print giGlobal, iLocal endin </CsInstruments> <CsScore> i 1 0 0 i 2 0 0 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
The output should include these lines:
new alloc for instr 1:
instr 1: giGlobal = 0.500 iLocal = 0.250
new alloc for instr 2:
instr 2: giGlobal = 0.500 iLocal = 0.200
As you see, the local variables iLocal do have different meanings in the context of their instrument, whereas giGlobal is known everywhere and in the same way. It is also worth mentioning that the performance time of the instruments (p3) is zero. This makes sense, as the instruments are called, but only the init-pass is performed.1
The Performance Pass
After having assigned initial values to all variables, Csound starts the actual performance. As music is a variation of values in time,2 audio signals are producing values which vary in time. In all digital audio, the time unit is given by the sample rate, and one sample is the smallest possible time atom. For a sample rate of 44100 Hz,3 one sample comes up to the duration of 1/44100 = 0.0000227 seconds.
So, performance for an audio application means basically: calculate all the samples which are finally being written to the output. You can imagine this as the cooperation of a clock and a calculator. For each sample, the clock ticks, and for each tick, the next sample is calculated.
Most audio applications do not perform this calculation sample by sample. It is much more efficient to collect some amount of samples in a "block" or "vector", and calculate them all together. This means in fact, to introduce another internal clock in your application; a clock which ticks less frequently than the sample clock. For instance, if (always assumed your sample rate is 44100 Hz) your block size consists of 10 samples, your internal calculation time clock ticks every 1/4410 (0.000227) seconds. If your block size consists of 441 samples, the clock ticks every 1/100 (0.01) seconds.
The following illustration shows an example for a block size of 10 samples. The samples are shown at the bottom line. Above are the control ticks, one for each ten samples. The top two lines show the times for both clocks in seconds. In the upmost line you see that the first control cycle has been finished at 0.000227 seconds, the second one at 0.000454 seconds, and so on.4
The rate (frequency) of these ticks is called the control rate in Csound. By historical reason,5 it is called "kontrol rate" instead of control rate, and abbreviated as "kr" instead of cr. Each of the calculation cycles is called a "k-cycle". The block size or vector size is given by the ksmps parameter, which means: how many samples (smps) are collected for one k-cycle.6
Let us see some code examples to illustrate these basic contexts.
<CsoundSynthesizer> <CsInstruments> sr = 44100 ksmps = 4410 instr 1 kCount init 0; set kcount to 0 first kCount = kCount + 1; increase at each k-pass printk 0, kCount; print the value endin </CsInstruments> <CsScore> i 1 0 1 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
Your output should contain the lines:
i 1 time 0.10000: 1.00000
i 1 time 0.20000: 2.00000
i 1 time 0.30000: 3.00000
i 1 time 0.40000: 4.00000
i 1 time 0.50000: 5.00000
i 1 time 0.60000: 6.00000
i 1 time 0.70000: 7.00000
i 1 time 0.80000: 8.00000
i 1 time 0.90000: 9.00000
i 1 time 1.00000: 10.00000
A counter (kCount) is set here to zero as initial value. Then, in each control cycle, the counter is increased by one. What we see here, is the typical behaviour of a loop. The loop has not been set explicitely, but works implicitely because of the continuous recalculation of all k-variables. So we can also speak about the k-cycles as an implicit (and time-triggered) k-loop.7 Try changing the ksmps value from 4410 to 8820 and to 2205 and observe the difference.
The next example reads the incrementation of kCount as rising frequency. The first instrument, called Rise, sets the k-rate frequency kFreq to the initial value of 100 Hz, and then adds 10 Hz in every new k-cycle. As ksmps=441, one k-cycle takes 1/100 second to perform. So in 3 seconds, the frequency rises from 100 to 3100 Hz. At the last k-cycle, the final frequency value is printed out.8 - The second instrument, Partials, increments the counter by one for each k-cycle, but only sets this as new frequency for every 100 steps. So the frequency stays at 100 Hz for one second, then at 200 Hz for one second, and so on. As the resulting frequencies are in the ratio 1 : 2 : 3 ..., we hear partials based on a 100 Hz fundamental, from the first partial up to the 31st. The opcode printk2 prints out the frequency value whenever it has changed.
<CsoundSynthesizer> <CsOptions> -o dac </CsOptions> <CsInstruments> sr = 44100 ksmps = 441 0dbfs = 1 nchnls = 2 ;build a table containing a sine wave giSine ftgen 0, 0, 2^10, 10, 1 instr Rise kFreq init 100 aSine poscil .2, kFreq, giSine outs aSine, aSine ;increment frequency by 10 Hz for each k-cycle kFreq = kFreq + 10 ;print out the frequency for the last k-cycle kLast release if kLast == 1 then printk 0, kFreq endif endin instr Partials ;initialize kCount kCount init 100 ;get new frequency if kCount equals 100, 200, ... if kCount % 100 == 0 then kFreq = kCount endif aSine poscil .2, kFreq, giSine outs aSine, aSine ;increment kCount kCount = kCount + 1 ;print out kFreq whenever it has changed printk2 kFreq endin </CsInstruments> <CsScore> i "Rise" 0 3 i "Partials" 4 31 </CsScore> </CsoundSynthesizer>
;example by joachim heintz
Init versus Equals
A frequently occuring error is that instead of setting the k-variable as kCount init 0, it is set as kCount = 0. The meaning of both statements has one significant difference. kCount init 0 sets the value for kCount to zero only in the init pass, without affecting it during the performance pass. kCount = 1 sets the value for kCount to zero again and again, in each performance cycle. So the increment always starts from the same point, and nothing really happens:
<CsoundSynthesizer> <CsInstruments> sr = 44100 ksmps = 4410 instr 1 kcount = 0; sets kcount to 0 at each k-cycle kcount = kcount + 1; does not really increase ... printk 0, kcount; print the value endin </CsInstruments> <CsScore> i 1 0 1 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
i 1 time 0.10000: 1.00000
i 1 time 0.20000: 1.00000
i 1 time 0.30000: 1.00000
i 1 time 0.40000: 1.00000
i 1 time 0.50000: 1.00000
i 1 time 0.60000: 1.00000
i 1 time 0.70000: 1.00000
i 1 time 0.80000: 1.00000
i 1 time 0.90000: 1.00000
i 1 time 1.00000: 1.00000
A Look at the Audio Vector
There are different opcodes to print out k-variables.9 There is no opcode in Csound to print out the audio vector directly, but you can use the vaget opcode to see what is happening inside one control cycle with the audio samples.
<CsoundSynthesizer> <CsInstruments> sr = 44100 ksmps = 5 0dbfs = 1 instr 1 aSine oscils 1, 2205, 0 kVec1 vaget 0, aSine kVec2 vaget 1, aSine kVec3 vaget 2, aSine kVec4 vaget 3, aSine kVec5 vaget 4, aSine printks "kVec1 = % f, kVec2 = % f, kVec3 = % f, kVec4 = % f, kVec5 = % f\n",\ 0, kVec1, kVec2, kVec3, kVec4, kVec5 endin </CsInstruments> <CsScore> i 1 0 [1/2205] </CsScore> </CsoundSynthesizer> ;example by joachim heintz
The output shows these lines:
kVec1 = 0.000000, kVec2 = 0.309017, kVec3 = 0.587785, kVec4 = 0.809017, kVec5 = 0.951057
kVec1 = 1.000000, kVec2 = 0.951057, kVec3 = 0.809017, kVec4 = 0.587785, kVec5 = 0.309017
kVec1 = -0.000000, kVec2 = -0.309017, kVec3 = -0.587785, kVec4 = -0.809017, kVec5 = -0.951057
kVec1 = -1.000000, kVec2 = -0.951057, kVec3 = -0.809017, kVec4 = -0.587785, kVec5 = -0.309017
In this example, the number of audio samples in one k-cycle is set to five by the statement ksmps=5. The first argument to vaget specifies which sample of the block you get. For instance,
kVec1 vaget 0, aSine
gets the first value of the audio vector and writes it into the variable kVec1. For a frequency of 2205 Hz at a sample rate of 44100 Hz, you need 20 samples to write one complete cycle of the sine. So we call the instrument for 1/2205 seconds, and we get 4 k-cycles. The printout shows exactly one period of the sine wave.
A Summarizing Example
After having put so much attention to the different single aspects of initialization, performance and audio vectors, the next example tries to summarize and illustrate all the aspects in their practical mixture.
<CsoundSynthesizer> <CsOptions> -o dac </CsOptions> <CsInstruments> sr = 44100 ksmps = 441 nchnls = 2 0dbfs = 1 instr 1 iAmp = p4 ;amplitude taken from the 4th parameter of the score line iFreq = p5 ;frequency taken from the 5th parameter ; --- move from 0 to 1 in the duration of this instrument call (p3) kPan line 0, p3, 1 aNote oscils iAmp, iFreq, 0 ;create an audio signal aL, aR pan2 aNote, kPan ;let the signal move from left to right outs aL, aR ;write it to the output endin </CsInstruments> <CsScore> i 1 0 3 0.2 443 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
As ksmps=441, each control cycle is 0.01 seconds long (441/44100). So this happens when the instrument call is performed:
Accessing the Initialization Value of a k-Variable
It has been said that the init pass sets initial values to all variables. It must be emphasized that this indeed concerns all variables, not only the i-variables. It is only the matter that i-variables are not affected by anything which happens later, in the performance. But also k- and a-variables get their initial values.
As we saw, the init opcode is used to set initial values for k- or a-variables explicitely. On the other hand, you can get the initial value of a k-variable which has not been set explicitely, by the i() facility. This is a simple example:
<CsoundSynthesizer> <CsOptions> -o dac </CsOptions> <CsInstruments> instr 1 gkLine line 0, p3, 1 endin instr 2 iInstr2LineValue = i(gkLine) print iInstr2LineValue endin instr 3 iInstr3LineValue = i(gkLine) print iInstr3LineValue endin </CsInstruments> <CsScore> i 1 0 5 i 2 2 0 i 3 4 0 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
new alloc for instr 1:
B 0.000 .. 2.000 T 2.000 TT 2.000 M: 0.0
new alloc for instr 2:
instr 2: iInstr2LineValue = 0.400
B 2.000 .. 4.000 T 4.000 TT 4.000 M: 0.0
new alloc for instr 3:
instr 3: iInstr3LineValue = 0.800
B 4.000 .. 5.000 T 5.000 TT 5.000 M: 0.0
Instrument 1 produces a rising k-signal, starting at zero and ending at one, over a time of five seconds. The values of this line rise are written to the global variable gkLine. After two seconds, instrument 2 is called, and examines the value of gkLine at its init-pass via i(gkLine). The value at this time (0.4), is printed out at init-time as iInstr2LineValue. The same happens for instrument 3, which prints out iInstr3LineValue = 0.800, as it has been started at 4 seconds.
The i() feature is particularily useful if you need to examine the value of any control signal from a widget or from midi, at the time when an instrument starts.
As we saw above, an i-value is not affected by the performance loop. So you cannot expect this to work as an incrementation:
<CsoundSynthesizer> <CsInstruments> sr = 44100 ksmps = 4410 instr 1 iCount init 0 ;set iCount to 0 first iCount = iCount + 1 ;increase print iCount ;print the value endin </CsInstruments> <CsScore> i 1 0 1 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
The output is nothing but:
instr 1: iCount = 1.000
But you can advise Csound to repeat the initialization of an i-variable. This is done with the reinit opcode. You must mark a section by a label (any name followed by a colon). Then the reinit statement will cause the i-variable to refresh. Use rireturn to end the reinit section.
<CsoundSynthesizer> <CsInstruments> sr = 44100 ksmps = 4410 instr 1 iCount init 0 ; set icount to 0 first reinit new ; reinit the section each k-pass new: iCount = iCount + 1 ; increase print iCount ; print the value rireturn endin </CsInstruments> <CsScore> i 1 0 1 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
instr 1: iCount = 1.000
instr 1: iCount = 2.000
instr 1: iCount = 3.000
instr 1: iCount = 4.000
instr 1: iCount = 5.000
instr 1: iCount = 6.000
instr 1: iCount = 7.000
instr 1: iCount = 8.000
instr 1: iCount = 9.000
instr 1: iCount = 10.000
instr 1: iCount = 11.000
What happens here more in detail, is the following. In the actual init-pass, iCount is set to zero via iCount init 0. Still in this init-pass, it is incremented by one (iCount = iCount+1) and the value is printed out as iCount = 1.000. Now starts the first performance pass. The statement reinit new advices Csound to initialise again the section labeled as "new". So the statement iCount = iCount + 1 is executed again. As the current value of iCount at this time is 1, the result is 2. So the printout at this first performance pass is iCount = 2.000. The same happens in the next nine performance cycles, so the final count is 11.
Order Of Calculation
In this context, it can be very important to observe the order in which the instruments of a Csound orchestra are evaluated. This order is determined by the instrument numbers. So, if you want to use during the same performance pass a value in instrument 10 which is generated by another instrument, you must not give this instrument the number 11 or higher. In the following example, first instrument 10 uses a value of instrument 1, then a value of instrument 100.
<CsoundSynthesizer> <CsInstruments> sr = 44100 ksmps = 4410 instr 1 gkcount init 0 ;set gkcount to 0 first gkcount = gkcount + 1 ;increase endin instr 10 printk 0, gkcount ;print the value endin instr 100 gkcount init 0 ;set gkcount to 0 first gkcount = gkcount + 1 ;increase endin </CsInstruments> <CsScore> ;first i1 and i10 i 1 0 1 i 10 0 1 ;then i100 and i10 i 100 1 1 i 10 1 1 </CsScore> </CsoundSynthesizer> ;Example by Joachim Heintz
The output shows the difference:
new alloc for instr 1:
new alloc for instr 10:
i 10 time 0.10000: 1.00000
i 10 time 0.20000: 2.00000
i 10 time 0.30000: 3.00000
i 10 time 0.40000: 4.00000
i 10 time 0.50000: 5.00000
i 10 time 0.60000: 6.00000
i 10 time 0.70000: 7.00000
i 10 time 0.80000: 8.00000
i 10 time 0.90000: 9.00000
i 10 time 1.00000: 10.00000
B 0.000 .. 1.000 T 1.000 TT 1.000 M: 0.0
new alloc for instr 100:
i 10 time 1.10000: 0.00000
i 10 time 1.20000: 1.00000
i 10 time 1.30000: 2.00000
i 10 time 1.40000: 3.00000
i 10 time 1.50000: 4.00000
i 10 time 1.60000: 5.00000
i 10 time 1.70000: 6.00000
i 10 time 1.80000: 7.00000
i 10 time 1.90000: 8.00000
i 10 time 2.00000: 9.00000
B 1.000 .. 2.000 T 2.000 TT 2.000 M: 0.0
Instrument 10 can use the values which instrument 1 has produced in the same control cycle, but it can only refer to values of instrument 100 which are produced in the previous control cycle. By this reason, the printout shows values which are one less in the latter case.
It has been said in chapter 02B (Quick Start) that instead of a number you can also use a name for an instrument. This is mostly preferable, because you can give meaningful names, leading to a better readable code. But what about the order of calculation in named instruments?
The answer is simple: Csound calculates them in the same order as they are written in the orchestra. So if your instrument collection is like this ...
<CsoundSynthesizer> <CsOptions> -nd </CsOptions> <CsInstruments> instr Grain_machine prints " Grain_machine\n" endin instr Fantastic_FM prints " Fantastic_FM\n" endin instr Random_Filter prints " Random_Filter\n" endin instr Final_Reverb prints " Final_Reverb\n" endin </CsInstruments> <CsScore> i "Final_Reverb" 0 1 i "Random_Filter" 0 1 i "Grain_machine" 0 1 i "Fantastic_FM" 0 1 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
... you can count on this output:
new alloc for instr Grain_machine:
new alloc for instr Fantastic_FM:
new alloc for instr Random_Filter:
new alloc for instr Final_Reverb:
Note that the score has not the same order. But internally, Csound transforms all names to numbers, in the order they are written from top to bottom. The numbers are reported on the top of Csound's output:10
instr Grain_machine uses instrument number 1
instr Fantastic_FM uses instrument number 2
instr Random_Filter uses instrument number 3
instr Final_Reverb uses instrument number 4
About "i-time" And "k-rate" Opcodes
It is often confusing for the beginner that there are some opcodes which only work at "i-time" or "i-rate", and others which only work at "k-rate" or "k-time". For instance, if the user wants to print the value of any variable, (s)he thinks: "OK - print it out." But Csound replies: "Please, tell me first if you want to print an i- or a k-variable".11
The print opcode just prints variables which are updated at each initialization pass ("i-time" or "i-rate"). If you want to print a variable which is updated at each control cycle ("k-rate" or "k-time"), you need its counterpart printk. (As the performance pass is usually updated some thousands times per second, you have an additional parameter in printk, telling Csound how often you want to print out the k-values.)
So, some opcodes are just for i-rate variables, like filelen or ftgen. Others are just for k-rate variables like metro or max_k. Many opcodes have variants for either i-rate-variables or k-rate-variables, like printf_i and printf, sprintf and sprintfk, strindex and strindexk.
Most of the Csound opcodes are able to work either at i-time or at k-time or at audio-rate, but you have to think carefully what you need, as the behaviour will be very different if you choose the i-, k- or a-variante of an opcode. For example, the random opcode can work at all three rates:
ires random imin, imax : works at "i-time" kres random kmin, kmax : works at "k-rate" ares random kmin, kmax : works at "audio-rate"
If you use the i-rate random generator, you will get one value for each note. For instance, if you want to have a different pitch for each note you are generating, you will use this one.
If you use the k-rate random generator, you will get one new value on every control cycle. If your sample rate is 44100 and your ksmps=10, you will get 4410 new values per second! If you take this as pitch value for a note, you will hear nothing but a noisy jumping. If you want to have a moving pitch, you can use the randomi variant of the k-rate random generator, which can reduce the number of new values per second, and interpolate between them.
If you use the a-rate random generator, you will get as many new values per second as your sample rate is. If you use it in the range of your 0 dB amplitude, you produce white noise.
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 0dbfs = 1 nchnls = 2 seed 0 ;each time different seed giSine ftgen 0, 0, 2^10, 10, 1 ;sine table instr 1 ;i-rate random iPch random 300, 600 aAmp linseg .5, p3, 0 aSine poscil aAmp, iPch, giSine outs aSine, aSine endin instr 2 ;k-rate random: noisy kPch random 300, 600 aAmp linseg .5, p3, 0 aSine poscil aAmp, kPch, giSine outs aSine, aSine endin instr 3 ;k-rate random with interpolation: sliding pitch kPch randomi 300, 600, 3 aAmp linseg .5, p3, 0 aSine poscil aAmp, kPch, giSine outs aSine, aSine endin instr 4 ;a-rate random: white noise aNoise random -.1, .1 outs aNoise, aNoise endin </CsInstruments> <CsScore> i 1 0 .5 i 1 .25 .5 i 1 .5 .5 i 1 .75 .5 i 2 2 1 i 3 4 2 i 3 5 2 i 3 6 2 i 4 9 1 </CsScore> </CsoundSynthesizer> ;example by joachim heintz
Possible Problems with k-Rate Tick Size
It has been said that usually the k-rate clock ticks much slower than the sample (a-rate) clock. For a common size of ksmps=32, one k-value remains the same for 32 samples. This can lead to problems, for instance if you use k-rate envelopes. Let us assume that you want to produce a very short fade-in of 3 milliseconds, and you do it with the following line of code:
kFadeIn linseg 0, .003, 1
Your envelope will look like this:
Such a "staircase-envelope" is what you hear in the next example as zipper noise. The transeg opcode produces a non-linear envelope with a sharp peak:
The rise and the decay are each 1/100 seconds long. If this envelope is produced at k-rate with a blocksize of 128 (instr 1), the noise is clearly audible. Try changing ksmps to 64, 32 or 16 and compare the amount of zipper noise. - Instrument 2 uses an envelope at audio-rate instead. Regardless the blocksize, each sample is calculated seperately, so the envelope will always be smooth.
<CsoundSynthesizer> <CsOptions> -o dac </CsOptions> <CsInstruments> sr = 44100 ;--- increase or decrease to hear the difference more or less evident ksmps = 128 nchnls = 2 0dbfs = 1 instr 1 ;envelope at k-time aSine oscils .5, 800, 0 kEnv transeg 0, .1, 5, 1, .1, -5, 0 aOut = aSine * kEnv outs aOut, aOut endin instr 2 ;envelope at a-time aSine oscils .5, 800, 0 aEnv transeg 0, .1, 5, 1, .1, -5, 0 aOut = aSine * aEnv outs aOut, aOut endin </CsInstruments> <CsScore> r 5 ;repeat the following line 5 times i 1 0 1 s ;end of section r 5 i 2 0 1 e </CsScore> </CsoundSynthesizer> ;example by joachim heintz
There are two internal clocks in Csound. The sample rate (sr) determines the audio-rate, whereas the control rate (kr) determines the rate, in which a new control cycle can be started and a new block of samples can be performed. In general, Csound can not start any event in between two control cycles, nor end.
With Csound6, the possibilities of these "in between" are enlarged via the --sample-accurate option.
The next example chooses an extreme small control rate (only 10 k-cycles per second) to illustrate this.
<CsoundSynthesizer> <CsOptions> -o test.wav -d </CsOptions> <CsInstruments> sr = 44100 ksmps = 4410 nchnls = 1 0dbfs = 1 instr 1 aPink oscils .5, 430, 0 out aPink endin </CsInstruments> <CsScore> i 1 0.05 0.1 i 1 0.4 0.15 </CsScore> </CsoundSynthesizer>
The first call advices instrument 1 to start performance at time 0.05. But this is impossible as it lies between two control cycles. The second call starts at a possible time, but the duration of 0.15 again does not coincident with the control rate. So the result starts the first call at time 0.1 and extends the second call to 0.2 seconds:
When to Use i- or k- Rate
When you code on your Csound instrument, you may sometimes wonder whether you shall use an i-rate or a k-rate opcode. From what is said, the general answer is clear: Use i-rate if something has to be done only once, or in a somehow punctual manner. Use k-rate if something has to be done continuously, or if you must regard what happens during the performance.
- You would not get any other result if you set p3 to 1 or any other value, as nothing is done here except initialization.^
- For the physical result which comes out of the loudspeakers or headphones, the variation is the variation of air pressure.^
- 44100 samples per second^
- These are by the way the times which Csound reports if you ask for the control cycles. The first control cycle in this example (sr=44100, ksmps=10) would be reported as 0.00027 seconds, not as 0.00000 seconds.^
- As Richard Boulanger explains, in early Csound a line starting with 'c' was a comment line. So it was not possible to abbreviate control variables as cAnything (http://csound.1045644.n5.nabble.com/OT-why-is-control-rate-called-kontrol-rate-td5720858.html#a5720866). ^
- As the k-rate is directly depending on sample rate (sr) and ksmps (kr = sr/ksmps), it is probably the best style to specify sr and ksmps in the header, but not kr. ^
- This must not be confused with a 'real' k-loop where inside one single k-cycle a loop is performed. See chapter 03C (section Loops) for examples.^
- The value is 3110 instead of 3100 because it has already been incremented by 10.^
- See the manual page for printk, printk2, printks, printf to know more about the differences.^
- If you want to know the number in an instrument, use the nstrnum opcode. ^
- See the following section 03B about the variable types for more on this subject.^