Csound: Userdefinedopcodes
Opcodes are the core units of everything that Csound does. They are like little machines that do a job, and programming is akin to connecting these little machines to perform a larger job. An opcode usually has something which goes into it: the inputs or arguments, and usually it has something which comes out of it: the output which is stored in one or more variables. Opcodes are written in the programming language C (that is where the name "Csound" comes from). If you want to create a new opcode in Csound, you must write it in C. How to do this is described in the Extending Csound chapter of this manual, and is also described in the relevant chapter of the Canonical Csound Reference Manual.
Opcodes son las unidades centrales de todo lo que Csound hace. Son como pequeñas máquinas que hacen un trabajo, y la programación es similar a conectar estas pequeñas máquinas para realizar un trabajo más grande. Un opcode suele tener algo que entra en él: las entradas o argumentos, y por lo general tiene algo que sale de ella: la salida que se almacena en una o más variables. Los códigos de operación se escriben en el lenguaje de programación C (de donde proviene el nombre Csound). Si desea crear un nuevo código de operación en Csound, debe escribirlo en C. Cómo hacerlo se describe en el capítulo Extending Csound de este manual y también se describe en el capítulo correspondiente del Canonical Csound Reference Manual.
There is, however, a way of writing your own opcodes in the Csound Language itself. The opcodes which are written in this way, are called User Defined Opcodes or "UDO"s. A UDO behaves in the same way as a standard opcode: it has input arguments, and usually one or more output variables. They run at i-time or at k-time. You use them as part of the Csound Language after you have defined and loaded them.
Hay, sin embargo, una manera de escribir sus propios opcodes en el propio lenguaje Csound. Los opcodes que se escriben de esta forma, se llaman Opcodes o UDOs definidos por el usuario. Un UDO se comporta de la misma manera que un código de operación estándar: tiene argumentos de entrada, y generalmente una o más variables de salida. Funcionan en el tiempo-i o en el k-tiempo. Los usa como parte del lenguaje Csound después de haberlos definido y cargado.
User Defined Opcodes have many valuable properties. They make your instrument code clearer because they allow you to create abstractions of blocks of code. Once a UDO has been defined it can be recalled and repeated many times within an orchestra, each repetition requiring only a single line of code. UDOs allow you to build up your own library of functions you need and return to frequently in your work. In this way, you build your own Csound dialect within the Csound Language. UDOs also represent a convenient format with which to share your work in Csound with other users.
Las Opcodes definidas por el usuario tienen muchas propiedades valiosas. Hacen que su código de instrumento sea más claro porque le permiten crear abstracciones de bloques de código. Una vez que se ha definido un UDO, puede ser recordado y repetido muchas veces dentro de una orquesta, cada repetición requiere solo una sola línea de código. UDOs le permiten construir su propia biblioteca de funciones que necesita y volver a con frecuencia en su trabajo. De esta manera, usted construye su propio dialecto de Csound dentro del lenguaje Csound. UDOs también representan un formato conveniente con el que compartir su trabajo en Csound con otros usuarios.
This chapter explains, initially with a very basic example, how you can build your own UDOs, and what options they offer. Following this, the practice of loading UDOs in your .csd file is shown, followed by some tips in regard to some unique capabilities of UDOs. Before the "Links And Related Opcodes" section at the end, some examples are shown for different User Defined Opcode definitions and applications.
Este capítulo explica, inicialmente con un ejemplo muy básico, cómo puede construir sus propios UDO y qué opciones ofrecen. Después de esto, se muestra la práctica de cargar UDOs en su archivo .csd, seguido de algunos consejos con respecto a algunas capacidades únicas de UDOs. Antes de la sección Enlaces y Opcodes relacionados al final, se muestran algunos ejemplos para diferentes definiciones y aplicaciones de Opcode definidos por el usuario.
If you want to write a User Defined Opcode in Csound6 which uses arrays, have a look at the end of chapter 03E to see their usage and naming conventions.
Si desea escribir un Opcode definido por el usuario en Csound6 que utiliza matrices, eche un vistazo al final del capítulo 03E para ver su uso y las convenciones de nomenclatura.
Transforming Csound Instrument Code To A User Defined Opcode
Writing a User Defined Opcode is actually very easy and straightforward. It mainly means to extract a portion of usual Csound instrument code, and put it in the frame of a UDO. Let's start with the instrument code:
Escribir un código de usuario definido por el usuario es en realidad muy fácil y sencillo. Significa principalmente extraer una porción del código de instrumento habitual de Csound, y ponerlo en el marco de un UDO. Comencemos con el código del instrumento:
EXAMPLE 03G01_Pre_UDO.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giSine ftgen 0, 0, 2^10, 10, 1 seed 0 instr 1 aDel init 0; initialize delay signal iFb = .7; feedback multiplier aSnd rand .2; white noise kdB randomi -18, -6, .4; random movement between -18 and -6 aSnd = aSnd * ampdb(kdB); applied as dB to noise kFiltFq randomi 100, 1000, 1; random movement between 100 and 1000 aFilt reson aSnd, kFiltFq, kFiltFq/5; applied as filter center frequency aFilt balance aFilt, aSnd; bring aFilt to the volume of aSnd aDelTm randomi .1, .8, .2; random movement between .1 and .8 as delay time aDel vdelayx aFilt + iFb*aDel, aDelTm, 1, 128; variable delay kdbFilt randomi -12, 0, 1; two random movements between -12 and 0 (dB) ... kdbDel randomi -12, 0, 1; ... for the filtered and the delayed signal aOut = aFilt*ampdb(kdbFilt) + aDel*ampdb(kdbDel); mix it outs aOut, aOut endin </CsInstruments> <CsScore> i 1 0 60 </CsScore> </CsoundSynthesizer>
This is a filtered noise, and its delay, which is fed back again into the delay line at a certain ratio iFb. The filter is moving as kFiltFq randomly between 100 and 1000 Hz. The volume of the filtered noise is moving as kdB randomly between -18 dB and -6 dB. The delay time moves between 0.1 and 0.8 seconds, and then both signals are mixed together.
Se trata de un ruido filtrado, y su retardo, que se devuelve de nuevo a la línea de retardo con una determinada relación iFb. El filtro se mueve como kFiltFq aleatoriamente entre 100 y 1000 Hz. El volumen del ruido filtrado se mueve como kdB aleatoriamente entre -18 dB y -6 dB. El tiempo de retardo se mueve entre 0,1 y 0,8 segundos, y luego ambas señales se mezclan entre sí.
Basic Example
If this signal processing unit is to be transformed into a User Defined Opcode, the first question is about the extend of the code that will be encapsulated: where the UDO code will begin and end? The first solution could be a radical, and possibly bad, approach: to transform the whole instrument into a UDO.
Si esta unidad de procesamiento de señales se va a transformar en un Opcode definido por el usuario, la primera pregunta es acerca de la extensión del código que se encapsulará: ¿dónde comenzará y terminará el código UDO? La primera solución podría ser un enfoque radical, y posiblemente malo: transformar todo el instrumento en un UDO.
EXAMPLE 03G02_All_to_UDO.csd
<CsoundSynthesizer> <CsOptions> </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giSine ftgen 0, 0, 2^10, 10, 1 seed 0 opcode FiltFb, 0, 0 aDel init 0; initialize delay signal iFb = .7; feedback multiplier aSnd rand .2; white noise kdB randomi -18, -6, .4; random movement between -18 and -6 aSnd = aSnd * ampdb(kdB); applied as dB to noise kFiltFq randomi 100, 1000, 1; random movement between 100 and 1000 aFilt reson aSnd, kFiltFq, kFiltFq/5; applied as filter center frequency aFilt balance aFilt, aSnd; bring aFilt to the volume of aSnd aDelTm randomi .1, .8, .2; random movement between .1 and .8 as delay time aDel vdelayx aFilt + iFb*aDel, aDelTm, 1, 128; variable delay kdbFilt randomi -12, 0, 1; two random movements between -12 and 0 (dB) ... kdbDel randomi -12, 0, 1; ... for the filtered and the delayed signal aOut = aFilt*ampdb(kdbFilt) + aDel*ampdb(kdbDel); mix it outs aOut, aOut endop instr 1 FiltFb endin </CsInstruments> <CsScore> i 1 0 60 </CsScore> </CsoundSynthesizer>
Before we continue the discussion about the quality of this transformation, we should have a look at the syntax first. The general syntax for a User Defined Opcode is:
Antes de continuar la discusión sobre la calidad de esta transformación, deberíamos echar un vistazo primero a la sintaxis. La sintaxis general para un Opcode definido por el usuario es:
opcode name, outtypes, intypes
... endop
Here, the name of the UDO is FiltFb. You are free to use any name, but it is suggested that you begin the name with a capital letter. By doing this, you avoid duplicating the name of most of the pre-existing opcodes1 which normally start with a lower case letter. As we have no input arguments and no output arguments for this first version of FiltFb, both outtypes and intypes are set to zero. Similar to the instr ... endin block of a normal instrument definition, for a UDO the opcode ... endop keywords begin and end the UDO definition block. In the instrument, the UDO is called like a normal opcode by using its name, and in the same line the input arguments are listed on the right and the output arguments on the left. In the previous a example, 'FiltFb' has no input and output arguments so it is called by just using its name:
Aquí, el nombre de la UDO es FiltFb. Usted es libre de usar cualquier nombre, pero se sugiere que comience el nombre con una letra mayúscula. Al hacer esto, evita duplicar el nombre de la mayoría de los opcodes1 preexistentes que normalmente comienzan con una letra minúscula. Como no tenemos argumentos de entrada ni argumentos de salida para esta primera versión de FiltFb, tanto outtypes como intypes se ponen a cero. Similar a la instr ... endin bloque de una definición de instrumento normal, para un UDO las palabras clave opcode ... endop comienzan y terminan el bloque de definición UDO. En el instrumento, el UDO se llama como un opcode normal usando su nombre, y en la misma línea los argumentos de entrada se enumeran a la derecha y los argumentos de salida a la izquierda. En el ejemplo anterior, FiltFb no tiene argumentos de entrada y salida, por lo que se llama simplemente usando su nombre:
instr 1
FiltFb endin
Now - why is this UDO more or less useless? It achieves nothing, when compared to the original non UDO version, and in fact looses some of the advantages of the instrument defined version. Firstly, it is not advisable to include this line in the UDO:
Ahora - ¿por qué este UDO es más o menos inútil? No alcanza nada, en comparación con la versión original no UDO, y de hecho pierde algunas de las ventajas de la versión definida por el instrumento. En primer lugar, no es aconsejable incluir esta línea en el UDO:
outs aOut, aOut
This statement writes the audio signal aOut from inside the UDO to the output device. Imagine you want to change the output channels, or you want to add any signal modifier after the opcode. This would be impossible with this statement. So instead of including the 'outs' opcode, we give the FiltFb UDO an audio output:
Esta sentencia escribe la señal de audio aDa desde el interior del UDO al dispositivo de salida. Imagine que desea cambiar los canales de salida, o desea agregar cualquier modificador de señal después del código de operación. Esto sería imposible con esta declaración. Así que en lugar de incluir el opcode de salida, le damos al FiltFb UDO una salida de audio:
xout aOut
The xout statement of a UDO definition works like the "outlets" in PD or Max, sending the result(s) of an opcode back to the caller instrument.
La sentencia xout de una definición UDO funciona como las salidas en PD o Max, enviando los resultados de un opcode al instrumento que llama.
Now let us consider the UDO's input arguments, choose which processes should be carried out within the FiltFb unit, and what aspects would offer greater flexibility if controllable from outside the UDO. First, the aSnd parameter should not be restricted to a white noise with amplitude 0.2, but should be an input (like a "signal inlet" in PD/Max). This is implemented using the line:
Ahora consideremos los argumentos de entrada de UDOs, elijamos qué procesos se deben llevar a cabo dentro de la unidad de FiltFb, y qué aspectos ofrecería una mayor flexibilidad si es controlable desde fuera del UDO. En primer lugar, el parámetro aSnd no debe limitarse a un ruido blanco con amplitud 0,2, sino que debe ser una entrada (como una entrada de señal en PD / Max). Esto se implementa usando la línea:
aSnd xin
Both the output and the input type must be declared in the first line of the UDO definition, whether they are i-, k- or a-variables. So instead of "opcode FiltFb, 0, 0" the statement has changed now to "opcode FiltFb, a, a", because we have both input and output as a-variable.
Tanto la salida como el tipo de entrada deben declararse en la primera línea de la definición de UDO, ya sean variables i, k o a. Así que en lugar de opcode FiltFb, 0, 0 la sentencia ha cambiado ahora a opcode FiltFb, a, a, porque tenemos entrada y salida como una variable.
The UDO is now much more flexible and logical: it takes any audio input, it performs the filtered delay and feedback processing, and returns the result as another audio signal. In the next example, instrument 1 does exactly the same as before. Instrument 2 has live input instead.
El UDO es ahora mucho más flexible y lógico: toma cualquier entrada de audio, realiza el procesamiento de retardo y retroalimentación filtrada y devuelve el resultado como otra señal de audio. En el ejemplo siguiente, el instrumento 1 hace exactamente lo mismo que antes. El instrumento 2 tiene entrada en vivo.
EXAMPLE 03G03_UDO_more_flex.csd
<CsoundSynthesizer> <CsOptions> </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giSine ftgen 0, 0, 2^10, 10, 1 seed 0 opcode FiltFb, a, a aSnd xin aDel init 0; initialize delay signal iFb = .7; feedback multiplier kdB randomi -18, -6, .4; random movement between -18 and -6 aSnd = aSnd * ampdb(kdB); applied as dB to noise kFiltFq randomi 100, 1000, 1; random movement between 100 and 1000 aFilt reson aSnd, kFiltFq, kFiltFq/5; applied as filter center frequency aFilt balance aFilt, aSnd; bring aFilt to the volume of aSnd aDelTm randomi .1, .8, .2; random movement between .1 and .8 as delay time aDel vdelayx aFilt + iFb*aDel, aDelTm, 1, 128; variable delay kdbFilt randomi -12, 0, 1; two random movements between -12 and 0 (dB) ... kdbDel randomi -12, 0, 1; ... for the filtered and the delayed signal aOut = aFilt*ampdb(kdbFilt) + aDel*ampdb(kdbDel); mix it xout aOut endop instr 1; white noise input aSnd rand .2 aOut FiltFb aSnd outs aOut, aOut endin instr 2; live audio input aSnd inch 1; input from channel 1 aOut FiltFb aSnd outs aOut, aOut endin </CsInstruments> <CsScore> i 1 0 60 ;change to i 2 for live audio input </CsScore> </CsoundSynthesizer>
Is this now the optimal version of the FiltFb User Defined Opcode? Obviously there are other parts of the opcode definiton which could be controllable from outside: the feedback multiplier iFb, the random movement of the input signal kdB, the random movement of the filter frequency kFiltFq, and the random movements of the output mix kdbSnd and kdbDel. Is it better to put them outside of the opcode definition, or is it better to leave them inside?
¿Es esta la versión óptima del Optcode Definido por el Usuario de FiltFb? Evidentemente hay otras partes de la definición del código de operación que podrían ser controlables desde fuera: el multiplicador de realimentación iFb, el movimiento aleatorio de la señal de entrada kdB, el movimiento aleatorio de la frecuencia de filtro kFiltFq y los movimientos aleatorios de la mezcla de salida kdbSnd y kdbDel . ¿Es mejor ponerlos fuera de la definición de código de operación, o es mejor dejarlos dentro?
There is no general answer. It depends on the degree of abstraction you desire or you prefer to relinquish. If you are working on a piece for which all of the parameters settings are already defined as required in the UDO, then control from the caller instrument may not be necessary . The advantage of minimizing the number of input and output arguments is the simplification in using the UDO. The more flexibility you require from your UDO however, the greater the number of input arguments that will be required. Providing more control is better for a later reusability, but may be unnecessarily complicated.
No hay una respuesta general. Depende del grado de abstracción que usted desea o prefiere renunciar. Si está trabajando en una pieza para la cual todos los ajustes de los parámetros ya están definidos como se requiere en la UDO, entonces el control desde el instrumento que llama no será necesario. La ventaja de minimizar el número de argumentos de entrada y salida es la simplificación en el uso de la UDO. Cuanto mayor sea la flexibilidad que necesite de su UDO sin embargo, mayor será el número de argumentos de entrada que será necesario. Proporcionar más control es mejor para una reutilización posterior, pero puede ser innecesariamente complicado.
Perhaps it is the best solution to have one abstract definition which performs one task, and to create a derivative - also as UDO - fine tuned for the particular project you are working on. The final example demonstrates the definition of a general and more abstract UDO FiltFb, and its various applications: instrument 1 defines the specifications in the instrument itself; instrument 2 uses a second UDO Opus123_FiltFb for this purpose; instrument 3 sets the general FiltFb in a new context of two varying delay lines with a buzz sound as input signal.
Quizás sea la mejor solución tener una definición abstracta que realice una tarea, y crear un derivado - también como UDO - afinado para el proyecto particular que usted está trabajando encendido. El último ejemplo demuestra la definición de un UDO FiltFb general y más abstracto, y sus diversas aplicaciones: el instrumento 1 define las especificaciones en el propio instrumento; El instrumento 2 utiliza un segundo UDO Opus123_FiltFb para este propósito; El instrumento 3 ajusta el FiltFb general en un nuevo contexto de dos líneas de retardo variables con un sonido de zumbido como señal de entrada.
EXAMPLE 03G04_UDO_calls_UDO.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giSine ftgen 0, 0, 2^10, 10, 1 seed 0 opcode FiltFb, aa, akkkia ; -- DELAY AND FEEDBACK OF A BAND FILTERED INPUT SIGNAL -- ;input: aSnd = input sound ; kFb = feedback multiplier (0-1) ; kFiltFq: center frequency for the reson band filter (Hz) ; kQ = band width of reson filter as kFiltFq/kQ ; iMaxDel = maximum delay time in seconds ; aDelTm = delay time ;output: aFilt = filtered and balanced aSnd ; aDel = delay and feedback of aFilt aSnd, kFb, kFiltFq, kQ, iMaxDel, aDelTm xin aDel init 0 aFilt reson aSnd, kFiltFq, kFiltFq/kQ aFilt balance aFilt, aSnd aDel vdelayx aFilt + kFb*aDel, aDelTm, iMaxDel, 128; variable delay xout aFilt, aDel endop opcode Opus123_FiltFb, a, a ;;the udo FiltFb here in my opus 123 :) ;input = aSnd ;output = filtered and delayed aSnd in different mixtures aSnd xin kdB randomi -18, -6, .4; random movement between -18 and -6 aSnd = aSnd * ampdb(kdB); applied as dB to noise kFiltFq randomi 100, 1000, 1; random movement between 100 and 1000 iQ = 5 iFb = .7; feedback multiplier aDelTm randomi .1, .8, .2; random movement between .1 and .8 as delay time aFilt, aDel FiltFb aSnd, iFb, kFiltFq, iQ, 1, aDelTm kdbFilt randomi -12, 0, 1; two random movements between -12 and 0 (dB) ... kdbDel randomi -12, 0, 1; ... for the noise and the delay signal aOut = aFilt*ampdb(kdbFilt) + aDel*ampdb(kdbDel); mix it xout aOut endop instr 1; well known context as instrument aSnd rand .2 kdB randomi -18, -6, .4; random movement between -18 and -6 aSnd = aSnd * ampdb(kdB); applied as dB to noise kFiltFq randomi 100, 1000, 1; random movement between 100 and 1000 iQ = 5 iFb = .7; feedback multiplier aDelTm randomi .1, .8, .2; random movement between .1 and .8 as delay time aFilt, aDel FiltFb aSnd, iFb, kFiltFq, iQ, 1, aDelTm kdbFilt randomi -12, 0, 1; two random movements between -12 and 0 (dB) ... kdbDel randomi -12, 0, 1; ... for the noise and the delay signal aOut = aFilt*ampdb(kdbFilt) + aDel*ampdb(kdbDel); mix it aOut linen aOut, .1, p3, 3 outs aOut, aOut endin instr 2; well known context UDO which embeds another UDO aSnd rand .2 aOut Opus123_FiltFb aSnd aOut linen aOut, .1, p3, 3 outs aOut, aOut endin instr 3; other context: two delay lines with buzz kFreq randomh 200, 400, .08; frequency for buzzer aSnd buzz .2, kFreq, 100, giSine; buzzer as aSnd kFiltFq randomi 100, 1000, .2; center frequency aDelTm1 randomi .1, .8, .2; time for first delay line aDelTm2 randomi .1, .8, .2; time for second delay line kFb1 randomi .8, 1, .1; feedback for first delay line kFb2 randomi .8, 1, .1; feedback for second delay line a0, aDel1 FiltFb aSnd, kFb1, kFiltFq, 1, 1, aDelTm1; delay signal 1 a0, aDel2 FiltFb aSnd, kFb2, kFiltFq, 1, 1, aDelTm2; delay signal 2 aDel1 linen aDel1, .1, p3, 3 aDel2 linen aDel2, .1, p3, 3 outs aDel1, aDel2 endin </CsInstruments> <CsScore> i 1 0 30 i 2 31 30 i 3 62 120 </CsScore> </CsoundSynthesizer>
The good thing about the different possibilities of writing a more specified UDO, or a more generalized: You needn't decide this at the beginning of your work. Just start with any formulation you find useful in a certain situation. If you continue and see that you should have some more parameters accessible, it should be easy to rewrite the UDO. Just be careful not to confuse the different versions you create. Use names like Faulty1, Faulty2 etc. instead of overwriting Faulty. Making use of extensive commenting when you initially create the UDO will make it easier to adapt the UDO at a later time. What are the inputs (including the measurement units they use such as Hertz or seconds)? What are the outputs? - How you do this, is up to you and depends on your style and your preference.
Lo bueno de las diferentes posibilidades de escribir un UDO más específico, o un más generalizado: No necesitas decidir esto al principio de tu trabajo. Comience con cualquier formulación que considere útil en una situación determinada. Si continúa y ve que debería tener algunos parámetros más accesibles, debería ser fácil de reescribir el UDO. Sólo tenga cuidado de no confundir las diferentes versiones que cree. Utilice nombres como Faulty1, Faulty2 etc. en lugar de sobrescribir Faulty. Haciendo uso de comentarios extensos al crear inicialmente el UDO, será más fácil adaptar el UDO en un momento posterior. ¿Cuáles son las entradas (incluyendo las unidades de medida que usan como Hertz o segundos)? ¿Cuáles son las salidas? - ¿Cómo hacer esto, depende de usted y depende de su estilo y su preferencia.
How to Use the User Defined Opcode Facility in Practice
In this section, we will address the main points of using UDOs: what you must bear in mind when loading them, what special features they offer, what restrictions you must be aware of and how you can build your own language with them.
En esta sección, abordaremos los principales puntos del uso de UDO: lo que debe tener en cuenta al cargarlos, qué características especiales ofrecen, qué restricciones debe tener en cuenta y cómo puede construir su propio idioma con ellos.
Loading User Defined Opcodes in the Orchestra Header
As can be seen from the examples above, User Defined Opcodes must be defined in the orchestra header (which is sometimes called "instrument 0").
Como se puede ver en los ejemplos anteriores, las Opcodes definidas por el usuario deben definirse en el encabezado de la orquesta (que a veces se denomina instrumento 0).
You can load as many User Defined Opcodes into a Csound orchestra as you wish. As long as they do not depend on each other, their order is arbitrarily. If UDO Opus123_FiltFb uses the UDO FiltFb for its definition (see the example above), you must first load FiltFb, and then Opus123_FiltFb. If not, you will get an error like this:
Puede cargar tantas Opcodes definidas por el usuario en una orquesta Csound como desee. Mientras no dependan el uno del otro, su orden es arbitraria. Si UDO Opus123_FiltFb utiliza la UDO FiltFb para su definición (vea el ejemplo anterior), primero debe cargar FiltFb, y luego Opus123_FiltFb. Si no es así, obtendrá un error como este:
orch compiler:
opcode Opus123_FiltFb a a error: no legal opcode, line 25: aFilt, aDel FiltFb aSnd, iFb, kFiltFq, iQ, 1, aDelTm
Definitions of User Defined Opcodes can also be loaded into a .csd file by an "#include" statement. What you must do is the following:
Las definiciones de Opcodes definidos por el usuario también se pueden cargar en un archivo .csd
Save your opcode definitions in a plain text file, for instance "MyOpcodes.txt".
Guarde sus definiciones de código de operación en un archivo de texto plano, por ejemplo MyOpcodes.txt.
If this file is in the same directory as your .csd file, you can just call it by the statement:
Si este archivo se encuentra en el mismo directorio que el archivo .csd, sólo puede llamarlo por la instrucción:
#include "MyOpcodes.txt"
If "MyOpcodes.txt" is in a different directory, you must call it by the full path name, for instance: #include "/Users/me/Documents/Csound/UDO/MyOpcodes.txt"
Si MyOpcodes.txt está en un directorio diferente, debe llamarlo por el nombre completo de la ruta de acceso, por ejemplo:
As always, make sure that the "#include" statement is the last one in the orchestra header, and that the logical order is accepted if one opcode depends on another.
Como siempre, asegúrese de que el #include es la última en el encabezado de la orquesta, y que el orden lógico se acepta si un código de operación depende de otro.
If you work with User Defined Opcodes a lot, and build up a collection of them, the #include feature allows you easily import several or all of them to your .csd file.
Si trabaja mucho con las Opcodes definidas por el usuario y crea una colecciòn de ellos, el #include le permite importar fácilmente varios o todos ellos a su archivo .csd.
The setksmps Feature
The ksmps assignment in the orchestra header cannot be changed during the performance of a .csd file. But in a User Defined Opcode you have the unique possibility of changing this value by a local assignment. If you use a setksmps statement in your UDO, you can have a locally smaller value for the number of samples per control cycle in the UDO. In the following example, the print statement in the UDO prints ten times compared to one time in the instrument, because ksmps in the UDO is 10 times smaller:
La asignación de ksmps en el encabezado de orquesta no se puede cambiar durante la ejecución de un archivo .csd. Pero en un Opcode definido por el usuario tiene la posibilidad única de cambiar este valor mediante una asignación local. Si utiliza una sentencia setksmps en su UDO, puede tener un valor localmente menor para el número de muestras por ciclo de control en el UDO. En el siguiente ejemplo, la declaración de impresión en el UDO imprime diez veces en comparación con una vez en el instrumento, porque ksmps en el UDO es 10 veces menor:
EXAMPLE 03G06_UDO_setksmps.csd
<CsoundSynthesizer> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 44100 ;very high because of printing opcode Faster, 0, 0 setksmps 4410 ;local ksmps is 1/10 of global ksmps printks "UDO print!%n", 0 endop instr 1 printks "Instr print!%n", 0 ;print each control period (once per second) Faster ;print 10 times per second because of local ksmps endin </CsInstruments> <CsScore> i 1 0 2 </CsScore> </CsoundSynthesizer>
For i-time arguments, you can use a simple feature to set default values:
Para los argumentos de tiempo-i, puede utilizar una función simple para establecer valores predeterminados:
"o" (instead of "i") defaults to 0
For k-time arguments, you can use since Csound 5.18 these default values:
Para los argumentos k-time, puede utilizar desde Csound 5.18 estos valores predeterminados:
"O" (instead of "k") defaults to 0
So you can omit these arguments - in this case the default values will be used. If you give an input argument instead, the default value will be overwritten:
Así que puede omitir estos argumentos - en este caso los valores por defecto se utilizarán. Si da un argumento de entrada, el valor por defecto será sobrescrito:
EXAMPLE 03G07_UDO_default_args.csd
<CsoundSynthesizer> <CsInstruments> ;Example by Joachim Heintz opcode Defaults, iii, opj ia, ib, ic xin xout ia, ib, ic endop instr 1 ia, ib, ic Defaults print ia, ib, ic ia, ib, ic Defaults 10 print ia, ib, ic ia, ib, ic Defaults 10, 100 print ia, ib, ic ia, ib, ic Defaults 10, 100, 1000 print ia, ib, ic endin </CsInstruments> <CsScore> i 1 0 0 </CsScore> </CsoundSynthesizer>
Recursion means that a function can call itself. This is a feature which can be useful in many situations. Also User Defined Opcodes can be recursive. You can do many things with a recursive UDO which you cannot do in any other way; at least not in a simliarly simple way. This is an example of generating eight partials by a recursive UDO. See the last example in the next section for a more musical application of a recursive UDO.
Recursión significa que una función puede llamarse a sí misma. Esta es una característica que puede ser útil en muchas situaciones. También las Opcodes definidas por el usuario pueden ser recursivas. Puedes hacer muchas cosas con un UDO recursivo que no puedes hacer de ninguna otra manera; Al menos no de una manera sencillamente simple. Este es un ejemplo de generar ocho partiales por un UDO recursivo. Vea el último ejemplo en la siguiente sección para una aplicación más musical de un UDO recursivo.
EXAMPLE 03G08_Recursive_UDO.csd
<CsoundSynthesizer> <CsOptions> </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 opcode Recursion, a, iip ;input: frequency, number of partials, first partial (default=1) ifreq, inparts, istart xin iamp = 1/inparts/istart ;decreasing amplitudes for higher partials if istart < inparts then ;if inparts have not yet reached acall Recursion ifreq, inparts, istart+1 ;call another instance of this UDO endif aout oscils iamp, ifreq*istart, 0 ;execute this partial aout = aout + acall ;add the audio signals xout aout endop instr 1 amix Recursion 400, 8 ;8 partials with a base frequency of 400 Hz aout linen amix, .01, p3, .1 outs aout, aout endin </CsInstruments> <CsScore> i 1 0 1 </CsScore> </CsoundSynthesizer>
We will focus here on some examples which will hopefully show the wide range of User Defined Opcodes. Some of them are adaptions of examples from previous chapters about the Csound Syntax. Much more examples can be found in the User-Defined Opcode Database, editied by Steven Yi.
Nos centraremos aquí en algunos ejemplos que esperamos mostrar la amplia gama de Opcodes definidos por el usuario. Algunos de ellos son adaptaciones de ejemplos de capítulos anteriores sobre la Sintaxis Csound. Pueden encontrarse muchos más ejemplos en la base de datos Opcode definida por el usuario, editada por Steven Yi.
Play A Mono Or Stereo Soundfile
Csound is often very strict and gives errors where other applications might 'turn a blind eye'. This is also the case if you read a soundfile using one of Csound's opcodes: soundin, diskin or diskin2. If your soundfile is mono, you must use the mono version, which has one audio signal as output. If your soundfile is stereo, you must use the stereo version, which outputs two audio signals. If you want a stereo output, but you happen to have a mono soundfile as input, you will get the error message:
Csound es a menudo muy estricto y da errores donde otras aplicaciones podrían hacer la vista gorda. Este es también el caso si usted lee un archivo de sonido usando uno de Csounds opcodes: soundin, diskin o diskin2. Si su archivo de sonido es mono, debe usar la versión mono, que tiene una señal de audio como salida. Si su archivo de sonido es estéreo, debe utilizar la versión estéreo, que emite dos señales de audio. Si desea una salida estéreo, pero tiene un archivo de sonido mono como entrada, obtendrá el mensaje de error:
INIT ERROR in ...: number of output args inconsistent with number
of file channels
It may be more useful to have an opcode which works for both, mono and stereo files as input. This is a ideal job for a UDO. Two versions are possible: FilePlay1 returns always one audio signal (if the file is stereo it uses just the first channel), FilePlay2 returns always two audio signals (if the file is mono it duplicates this to both channels). We can use the default arguments to make this opcode behave exactly as diskin2:
Puede ser más útil tener un opcode que funcione tanto para archivos mono como estéreo como entrada. Este es un trabajo ideal para un UDO. Dos versiones son posibles: FilePlay1 devuelve siempre una señal de audio (si el archivo es estéreo usa sólo el primer canal), FilePlay2 devuelve siempre dos señales de audio (si el archivo es mono, se duplica esto a ambos canales). Podemos usar los argumentos por defecto para hacer que este código de operación se comporte exactamente como diskin2:
EXAMPLE 03G09_UDO_FilePlay.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 opcode FilePlay1, a, Skoooooo ;gives mono output regardless your soundfile is mono or stereo ;(if stereo, just the first channel is used) ;see diskin2 page of the csound manual for information about the input arguments Sfil, kspeed, iskip, iloop, iformat, iwsize, ibufsize, iskipinit xin ichn filenchnls Sfil if ichn == 1 then aout diskin2 Sfil, kspeed, iskip, iloop, iformat, iwsize,\ ibufsize, iskipinit else aout, a0 diskin2 Sfil, kspeed, iskip, iloop, iformat, iwsize,\ ibufsize, iskipinit endif xout aout endop opcode FilePlay2, aa, Skoooooo ;gives stereo output regardless your soundfile is mono or stereo ;see diskin2 page of the csound manual for information about the input arguments Sfil, kspeed, iskip, iloop, iformat, iwsize, ibufsize, iskipinit xin ichn filenchnls Sfil if ichn == 1 then aL diskin2 Sfil, kspeed, iskip, iloop, iformat, iwsize,\ ibufsize, iskipinit aR = aL else aL, aR diskin2 Sfil, kspeed, iskip, iloop, iformat, iwsize,\ ibufsize, iskipinit endif xout aL, aR endop instr 1 aMono FilePlay1 "fox.wav", 1 outs aMono, aMono endin instr 2 aL, aR FilePlay2 "fox.wav", 1 outs aL, aR endin </CsInstruments> <CsScore> i 1 0 4 i 2 4 4 </CsScore> </CsoundSynthesizer>
In example 03C11_Table_random_dev.csd, a function table has been changed at performance time, once a second, by random deviations. This can be easily transformed to a User Defined Opcode. It takes the function table variable, a trigger signal, and the random deviation in percent as input. In each control cycle where the trigger signal is "1", the table values are read. The random deviation is applied, and the changed values are written again into the table. Here, the tab/tabw opcodes are used to make sure that also non-power-of-two tables can be used.
En el ejemplo 03C11_Table_random_dev.csd, una tabla de funciones ha sido cambiada en tiempo de ejecución, una vez por segundo, por desviaciones aleatorias. Esto se puede transformar fácilmente en un Opcode definido por el usuario. Se toma la variable de tabla de funciones, una señal de disparo y la desviación aleatoria en porcentaje como entrada. En cada ciclo de control en el que la señal de disparo es 1, se leen los valores de la tabla. Se aplica la desviación aleatoria y los valores cambiados se escriben de nuevo en la tabla. Aquí, los opcodes tab / tabw se utilizan para asegurarse de que también se pueden usar tablas que no sean de potencia de dos.
EXAMPLE 03G10_UDO_rand_dev.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 441 nchnls = 2 0dbfs = 1 giSine ftgen 0, 0, 256, 10, 1; sine wave seed 0; each time different seed opcode TabDirtk, 0, ikk ;"dirties" a function table by applying random deviations at a k-rate trigger ;input: function table, trigger (1 = perform manipulation), ;deviation as percentage ift, ktrig, kperc xin if ktrig == 1 then ;just work if you get a trigger signal kndx = 0 loop: krand random -kperc/100, kperc/100 kval tab kndx, ift; read old value knewval = kval + (kval * krand); calculate new value tabw knewval, kndx, giSine; write new value loop_lt kndx, 1, ftlen(ift), loop; loop construction endif endop instr 1 kTrig metro 1, .00001 ;trigger signal once per second TabDirtk giSine, kTrig, 10 aSig poscil .2, 400, giSine outs aSig, aSig endin </CsInstruments> <CsScore> i 1 0 10 </CsScore> </CsoundSynthesizer>
Of course you can also change the content of a function table at init-time. The next example permutes a series of numbers randomly each time it is called. For this purpose, first the input function table iTabin is copied as iCopy. This is necessary because we do not want to change iTabin in any way. Next a random index in iCopy is created and the value at this location in iTabin is written at the beginning of iTabout, which contains the permuted results. At the end of this cycle, each value in iCopy which has a larger index than the one which has just been read, is shifted one position to the left. So now iCopy has become one position smaller - not in table size but in the number of values to read. This procedure is continued until all values from iCopy are reflected in iTabout:
Por supuesto, también puede cambiar el contenido de una tabla de funciones en init-time. El ejemplo siguiente permuta una serie de números aleatoriamente cada vez que se llama. Para ello, primero se copia la función de entrada iTabin como iCopy. Esto es necesario porque no queremos cambiar iTabin de ninguna manera. A continuación se crea un índice aleatorio en iCopy y el valor en esta ubicación en iTabin se escribe al principio de iTabout, que contiene los resultados permutados. Al final de este ciclo, cada valor en iCopy que tiene un índice mayor que el que acaba de ser leído, se desplaza una posición hacia la izquierda. Así que ahora iCopy se ha convertido en una posición más pequeña - no en el tamaño de la tabla, sino en el número de valores a leer. Este procedimiento se continúa hasta que todos los valores de iCopy se reflejan en iTabout:
EXAMPLE 03G11_TabPermRnd.csd
<CsoundSynthesizer> <CsInstruments> ;Example by Joachim Heintz giVals ftgen 0, 0, -12, -2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 seed 0; each time different seed opcode TabPermRand_i, i, i ;permuts randomly the values of the input table ;and creates an output table for the result iTabin xin itablen = ftlen(iTabin) iTabout ftgen 0, 0, -itablen, 2, 0 ;create empty output table iCopy ftgen 0, 0, -itablen, 2, 0 ;create empty copy of input table tableicopy iCopy, iTabin ;write values of iTabin into iCopy icplen init itablen ;number of values in iCopy indxwt init 0 ;index of writing in iTabout loop: indxrd random 0, icplen - .0001; random read index in iCopy indxrd = int(indxrd) ival tab_i indxrd, iCopy; read the value tabw_i ival, indxwt, iTabout; write it to iTabout ; -- shift values in iCopy larger than indxrd one position to the left shift: if indxrd < icplen-1 then ;if indxrd has not been the last table value ivalshft tab_i indxrd+1, iCopy ;take the value to the right ... tabw_i ivalshft, indxrd, iCopy ;...and write it to indxrd position indxrd = indxrd + 1 ;then go to the next position igoto shift ;return to shift and see if there is anything left to do endif indxwt = indxwt + 1 ;increase the index of writing in iTabout loop_gt icplen, 1, 0, loop ;loop as long as there is ; ;a value in iCopy ftfree iCopy, 0 ;delete the copy table xout iTabout ;return the number of iTabout endop instr 1 iPerm TabPermRand_i giVals ;perform permutation ;print the result indx = 0 Sres = "Result:" print: ival tab_i indx, iPerm Sprint sprintf "%s %d", Sres, ival Sres = Sprint loop_lt indx, 1, 12, print puts Sres, 1 endin instr 2; the same but performed ten times icnt = 0 loop: iPerm TabPermRand_i giVals ;perform permutation ;print the result indx = 0 Sres = "Result:" print: ival tab_i indx, iPerm Sprint sprintf "%s %d", Sres, ival Sres = Sprint loop_lt indx, 1, 12, print puts Sres, 1 loop_lt icnt, 1, 10, loop endin </CsInstruments> <CsScore> i 1 0 0 i 2 0 0 </CsScore> </CsoundSynthesizer>
There is no opcode in Csound for printing the contents of a function table, but one can be created as a UDO.2 Again a loop is needed for checking the values and putting them into a string which can then be printed. In addition, some options can be given for the print precision and for the number of elements in a line.
No hay código de operación en Csound para imprimir el contenido de una tabla de funciones, pero se puede crear como UDO.2 De nuevo se necesita un bucle para comprobar los valores y colocarlos en una cadena que luego se puede imprimir. Además, se pueden dar algunas opciones para la precisión de la impresión y para el número de elementos en una línea.
EXAMPLE 03G12_TableDumpSimp.csd
<CsoundSynthesizer> <CsOptions> -ndm0 -+max_str_len=10000 </CsOptions> <CsInstruments> ;Example by Joachim Heintz gitab ftgen 1, 0, -7, -2, 0, 1, 2, 3, 4, 5, 6 gisin ftgen 2, 0, 128, 10, 1 opcode TableDumpSimp, 0, ijo ;prints the content of a table in a simple way ;input: function table, float precision while printing (default = 3), ;parameters per row (default = 10, maximum = 32) ifn, iprec, ippr xin iprec = (iprec == -1 ? 3 : iprec) ippr = (ippr == 0 ? 10 : ippr) iend = ftlen(ifn) indx = 0 Sformat sprintf "%%.%df\t", iprec Sdump = "" loop: ival tab_i indx, ifn Snew sprintf Sformat, ival Sdump strcat Sdump, Snew indx = indx + 1 imod = indx % ippr if imod == 0 then puts Sdump, 1 Sdump = "" endif if indx < iend igoto loop puts Sdump, 1 endop instr 1 TableDumpSimp p4, p5, p6 prints "%n" endin </CsInstruments> <CsScore> ;i1 st dur ftab prec ppr i1 0 0 1 -1 i1 . . 1 0 i1 . . 2 3 10 i1 . . 2 6 32 </CsScore> </CsoundSynthesizer>
In the last example of the chapter about Triggering Instrument Events a number of partials were synthesized, each with a random frequency deviation of up to 10% compared to precise harmonic spectrum frequencies and a unique duration for each partial. This can also be written as a recursive UDO. Each UDO generates one partial, and calls the UDO again until the last partial is generated. Now the code can be reduced to two instruments: instrument 1 performs the time loop, calculates the basic values for one note, and triggers the event. Then instrument 11 is called which feeds the UDO with the values and passes the audio signals to the output.
En el último ejemplo del capítulo acerca de los Eventos del Instrumento de Disparo se sintetizaron una serie de partiales, cada uno con una desviación de frecuencia aleatoria de hasta 10% en comparación con las frecuencias del espectro armónico preciso y una duración única para cada parcial. Esto también se puede escribir como UDO recursivo. Cada UDO genera una parcial, y llama a la UDO de nuevo hasta que se genera el último parcial. Ahora el código se puede reducir a dos instrumentos: el instrumento 1 realiza el bucle de tiempo, calcula los valores básicos de una nota y activa el evento. Entonces se llama al instrumento 11 que alimenta el UDO con los valores y pasa las señales de audio a la salida.
EXAMPLE 03G13_UDO_Recursive_AddSynth.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giSine ftgen 0, 0, 2^10, 10, 1 seed 0 opcode PlayPartials, aa, iiipo ;plays inumparts partials with frequency deviation and own envelopes and ;durations for each partial ;ibasfreq: base frequency of sound mixture ;inumparts: total number of partials ;ipan: panning ;ipartnum: which partial is this (1 - N, default=1) ;ixtratim: extra time in addition to p3 needed for this partial (default=0) ibasfreq, inumparts, ipan, ipartnum, ixtratim xin ifreqgen = ibasfreq * ipartnum; general frequency of this partial ifreqdev random -10, 10; frequency deviation between -10% and +10% ifreq = ifreqgen + (ifreqdev*ifreqgen)/100; real frequency ixtratim1 random 0, p3; calculate additional time for this partial imaxamp = 1/inumparts; maximum amplitude idbdev random -6, 0; random deviation in dB for this partial iamp = imaxamp * ampdb(idbdev-ipartnum); higher partials are softer ipandev random -.1, .1; panning deviation ipan = ipan + ipandev aEnv transeg 0, .005, 0, iamp, p3+ixtratim1-.005, -10, 0; envelope aSine poscil aEnv, ifreq, giSine aL1, aR1 pan2 aSine, ipan if ixtratim1 > ixtratim then ixtratim = ixtratim1 ;set ixtratim to the ixtratim1 if the latter is larger endif if ipartnum < inumparts then ;if this is not the last partial ; -- call the next one aL2, aR2 PlayPartials ibasfreq, inumparts, ipan, ipartnum+1, ixtratim else ;if this is the last partial p3 = p3 + ixtratim; reset p3 to the longest ixtratim value endif xout aL1+aL2, aR1+aR2 endop instr 1; time loop with metro kfreq init 1; give a start value for the trigger frequency kTrig metro kfreq if kTrig == 1 then ;if trigger impulse: kdur random 1, 5; random duration for instr 10 knumparts random 8, 14 knumparts = int(knumparts); 8-13 partials kbasoct random 5, 10; base pitch in octave values kbasfreq = cpsoct(kbasoct) ;base frequency kpan random .2, .8; random panning between left (0) and right (1) event "i", 11, 0, kdur, kbasfreq, knumparts, kpan; call instr 11 kfreq random .25, 1; set new value for trigger frequency endif endin instr 11; plays one mixture with 8-13 partials aL, aR PlayPartials p4, p5, p6 outs aL, aR endin </CsInstruments> <CsScore> i 1 0 300 </CsScore> </CsoundSynthesizer>
For some situations it can be very useful to use strings in Csound as a collection of single strings or numbers. This is what programming languages call a list or an array. Csound does not provide opcodes for this purpose, but you can define these opcodes as UDOs. A set of these UDOs can then be used like this:
Para algunas situaciones puede ser muy útil usar cadenas en Csound como una colección de cadenas o números individuales. Esto es lo que los lenguajes de programación llaman una lista o una matriz. Csound no proporciona opcodes para este propósito, pero puede definir estos opcodes como UDOs. Un conjunto de estos UDOs se puede utilizar de esta manera:
ilen StrayLen "a b c d e"
ilen -> 5 Sel StrayGetEl "a b c d e", 0 Sel -> "a" inum StrayGetNum "1 2 3 4 5", 0 inum -> 1 ipos StrayElMem "a b c d e", "c" ipos -> 2 ipos StrayNumMem "1 2 3 4 5", 3 ipos -> 2 Sres StraySetEl "a b c d e", "go", 0 Sres -> "go a b c d e" Sres StraySetNum "1 2 3 4 5", 0, 0 Sres -> "0 1 2 3 4 5" Srev StrayRev "a b c d e" Srev -> "e d c b a" Sub StraySub "a b c d e", 1, 3 Sub -> "b c" Sout StrayRmv "a b c d e", "b d" Sout -> "a c e" Srem StrayRemDup "a b a c c d e e" Srem -> "a b c d e" ift,iftlen StrayNumToFt "1 2 3 4 5", 1 ift -> 1 (same as f 1 0 -5 -2 1 2 3 4 5) iftlen -> 5
You can find an article about defining such a sub-language here, and the up to date UDO code here (or at the UDO repository).
Aquí puede encontrar un artículo sobre la definición de este sub-lenguaje y el código UDO actualizado aquí (o en el repositorio UDO).
Links And Related Opcodes
This is the page in the Canonical Csound Reference Manual about the definition of UDOs.
Esta es la página en el Canonical Csound Reference Manual sobre la definición de UDOs.
The most important resource of User Defined Opcodes is the User-Defined Opcode Database, editied by Steven Yi.
El recurso más importante de las Opcodes definidas por el usuario es la base de datos Opcode definida por el usuario, editada por Steven Yi.
Also by Steven Yi, read the second part of his article about control flow in Csound in the Csound Journal (summer 2006).
También por Steven Yi, leer la segunda parte de su artículo sobre el flujo de control en Csound en el Csound Journal (verano de 2006).
Related Opcodes
opcode: The opcode used to begin a User Defined Opcode definition.
#include: Useful to include any loadable Csound code, in this case definitions of User Defined Opcodes.
setksmps: Lets you set a smaller ksmps value locally in a User Defined Opcode.
There has been error in communication with Booktype server. Not sure right now where is the problem.
You should refresh this page.