opcode — Defines the start of user-defined opcode block.
The opcode and endop statements allow defining a new opcode that can be used the same way as any of the built-in Csound opcodes. These opcode blocks are very similar to instruments (and are, in fact, implemented as special instruments), but cannot be called as a normal instrument e.g. with the i statements
A user-defined opcode block must precede the instrument (or other opcode) from which it is used. But it is possible to call the opcode from itself. This allows recursion of any depth that is limited only by available memory. Additionally, there is an experimental feature that allows running the opcode definition at a higher control rate than the kr value specified in the orchestra header.
Similarly to instruments, the variables and labels of a user-defined opcode block are local and cannot be accessed from the caller instrument (and the opcode cannot access variables of the caller, either).
Some parameters are automatically copied at initialization, however:
Also, the release flag (see the release opcode) is copied at performance time.
Modifying the note duration in the opcode definition by assigning to p3, or using ihold, turnoff, xtratim, linsegr, or similar opcodes will also affect the caller instrument. Changes to MIDI controllers (for example with ctrlinit) will also apply to the instrument from which the opcode was called.
Use the setksmps opcode to set the local ksmps value.
The xin and xout opcodes copy variables to and from the opcode definition, allowing communication with the calling instrument.
The types of input and output variables are defined by the parameters intypes and outtypes.
Tip | |
---|---|
You can create UDOs which take no inputs or outputs by using 0 instead of a string. |
Notes | |
---|---|
|
name -- name of the opcode. It may consist of any combination of letters, digits, and underscore but should not begin with a digit. If an opcode with the specified name already exists, it is redefined (a warning is printed in such cases). Some reserved words (like instr and endin) cannot be redefined.
intypes -- list of input types, any combination of the characters: a, f, k, O, P, V, K, i, o, p, and j. A single 0 character can be used if there are no input arguments. Double quotes and delimiter characters (e.g. comma) are not needed.
The meaning of the various intypes is shown in the following table:
Type | Description | Variable Types Allowed | Updated At |
---|---|---|---|
a | a-rate variable | a-rate | a-rate |
f | f-sig variable | f-sig | k-rate |
i | i-rate variable | i-rate | i-time |
j | optional i-time, defaults to -1 | i-rate, constant | i-time |
k | k-rate variable | k- and i-rate, constant | k-rate |
O | optional k-rate variable, defaults to 0 | k- and i-rate, constant | k-rate |
P | optional k-rate variable, defaults to 1 | k- and i-rate, constant | k-rate |
V | optional k-rate variable, defaults to 0.5 | k- and i-rate, constant | k-rate |
J | optional k-rate variable, defaults to -1 | k- and i-rate, constant | k-rate |
K | k-rate with initialization | k- and i-rate, constant | i-time and k-rate |
o | optional i-time, defaults to 0 | i-rate, constant | i-time |
p | optional i-time, defaults to 1 | i-rate, constant | i-time |
S | string variable | k- and i-rate string, constant | i-time and k-rate |
The maximum allowed number of input arguments is 256.
outtypes -- list of output types. The format is the same as in the case of intypes.
Here are the available outtypes:
Type | Description | Variable Types Allowed | Updated At |
---|---|---|---|
a | a-rate variable | a-rate | a-rate |
f | f-sig variable | f-sig | k-rate |
i | i-rate variable | i-rate | i-time |
k | k-rate variable | k-rate | k-rate |
K | k-rate with initialization | k-rate | i-time and k-rate |
S | string variable | k- and i-rate string | i-time and k-rate |
The maximum allowed number of output arguments is 256.
iksmps (optional, default=0) -- sets the local ksmps value. Must be a positive integer, and also the ksmps of the calling instrument or opcode must be an integer multiple of this value. For example, if ksmps is 10 in the instrument from which the opcode was called, the allowed values for iksmps are 1, 2, 5, and 10.
If iksmps is set to zero, the ksmps of the caller instrument or opcode is used (this is the default behavior).
Note | |
---|---|
The local ksmps is implemented by splitting up a control period into smaller sub-kperiods and temporarily modifying internal Csound global variables. This also requires converting the rate of k-rate input and output arguments (input variables receive the same value in all sub-kperiods, while outputs are written only in the last one). |
Warning about local ksmps | |
---|---|
When the local ksmps is not the same as the orchestra level ksmps value (as specified in the orchestra header), global a-rate operations must not be used in the user-defined opcode block. These include:
In general, the local ksmps should be used with care as it is an experimental feature, although it works correctly in most cases. |
The setksmps statement can be used to set the local ksmps value of the user-defined opcode block. It has one i-time parameter specifying the new ksmps value (which is left unchanged if zero is used, see also the notes about iksmps above). setksmps should be used before any other opcodes (but allowed after xin), otherwise unpredictable results may occur.
The input parameters can be read with xin, and the output is written by xout opcode. Only one instance of these units should be used, as xout overwrites and does not accumulate the output. The number and type of arguments for xin and xout must be the same as in the declaration of the user-defined opcode block (see tables above).
The input and output arguments must agree with the definition both in number (except if the optional i-time input is used) and type. An optional i-time input parameter (iksmps) is automatically added to the intypes list, and (similarly to setksmps) sets the local ksmps value.
The syntax of a user-defined opcode block is as follows:
opcode name, outtypes, intypes xinarg1 [, xinarg2] [, xinarg3] ... [xinargN] xin [setksmps iksmps] ... the rest of the instrument's code. xout xoutarg1 [, xoutarg2] [, xoutarg3] ... [xoutargN] endop
The new opcode can then be used with the usual syntax:
[xoutarg1] [, xoutarg2] ... [xoutargN] name [xinarg1] [, xinarg2] ... [xinargN] [, iksmps]
Note | |
---|---|
The opcode call is always executed both at initialization and performance time, even if there are no a- or k-rate arguments. If there are many user opcode calls that are known to have no effect at performance time in an instrument, then it may save some CPU time to jump over groups of such opcodes with kgoto |
Here is an example of a user-defined opcode. It uses the file Play opcode.csd
Example 678. Example of a user-defined opcode.
See the sections Real-time Audio and Command Line Flags for more information on using command line flags.
<CsoundSynthesizer> <CsOptions> ; Select audio/midi flags here according to platform -odac ;;;realtime audio out ;-iadc ;;;uncomment -iadc if realtime audio input is needed too ; For Non-realtime ouput leave only the line below: ; -o opcode_example.wav -W ;;; for file output any platform </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 /* example opcode 1: simple oscillator */ opcode Oscillator, a, kk kamp, kcps xin ; read input parameters a1 vco2 kamp, kcps ; sawtooth oscillator xout a1 ; write output endop /* example opcode 2: lowpass filter with local ksmps */ opcode Lowpass, a, akk setksmps 1 ; need sr=kr ain, ka1, ka2 xin ; read input parameters aout init 0 ; initialize output aout = ain*ka1 + aout*ka2 ; simple tone-like filter xout aout ; write output endop /* example opcode 3: recursive call */ opcode RecursiveLowpass, a, akkpp ain, ka1, ka2, idep, icnt xin ; read input parameters if (icnt >= idep) goto skip1 ; check if max depth reached ain RecursiveLowpass ain, ka1, ka2, idep, icnt + 1 skip1: aout Lowpass ain, ka1, ka2 ; call filter xout aout ; write output endop /* example opcode 4: de-click envelope */ opcode DeClick, a, a ain xin aenv linseg 0, 0.02, 1, p3 - 0.05, 1, 0.02, 0, 0.01, 0 xout ain * aenv ; apply envelope and write output endop /* instr 1 uses the example opcodes */ instr 1 kamp = .7 ; amplitude kcps expon 50, p3, 500 ; pitch a1 Oscillator kamp, kcps ; call oscillator kflt linseg 0.4, 1.5, 0.4, 1, 0.8, 1.5, 0.8 ; filter envelope a1 RecursiveLowpass a1, kflt, 1 - kflt, 10 ; 10th order lowpass a1 DeClick a1 outs a1, a1 endin </CsInstruments> <CsScore> i 1 0 4 e5 ;extra second before quitting </CsScore> </CsoundSynthesizer>
Here is another example of a user-defined opcode. It uses the file Play opcode_f.csd
Example 679. Second example of a user-defined opcode.
<CsoundSynthesizer> <CsOptions> ; Select audio/midi flags here according to platform -odac ;;;realtime audio out ;-iadc ;;;uncomment -iadc if realtime audio input is needed too ; For Non-realtime ouput leave only the line below: ; -o opcode_f.wav -W ;;; for file output any platform </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 0dbfs = 1 nchnls = 2 ; example by Luis Salgueiro and joachim heintz opcode Transpose, a, fk ; transpose a monophonic input signal through FFT. fsig, kInterval xin fScaled pvscale fsig, semitone(kInterval) aTransposed pvsynth fScaled xout aTransposed endop opcode Stretch,f,ik iTab, kSpeed xin fStretch pvstanal kSpeed, 1, 1, iTab xout fStretch endop giFox ftgen 0, 0, 0, 1, "fox.wav", 0, 0, 0 instr 1 aSnd diskin2 "fox.wav" fSnd pvsanal aSnd, 1024, 256, 1024, 1 aTransposed Transpose fSnd, p4 ; p4 = transposition in semitones outall aTransposed/2 endin instr 2 fStretch Stretch giFox, transeg:k(1,p3,-3,0) aStretch pvsynth fStretch outall aStretch endin </CsInstruments> <CsScore> i1 0 3 0 i1 . . 7 i1 . . 12 i2 4 8 e </CsScore> </CsoundSynthesizer>