Csound y Python tienen diversas formas posibles de interactuar entre sí. Una de ellas es embeber código de Python dentro de la orquesta de Csound, aprovechando que Csound dispone de un intérprete interno de Python.
IMPORTANTE: Csound interpreta código de Python 2, que tiene algunas diferencias de sintaxis con Python 3, que es la versión que hemos utilizado en este curso.
Hay 6 operadores básicos de Python en Csound:
A su vez, cada uno de ellos puede tener distintas variantes, generando una colección de operadores.
El nombre de los operadores de Python en Csound sigue esta lógica:
"py" + [(opcional) prefijo de contexto] + [nombre de la acción] + [(opcional) sufijo de tiempo de ejecución]
Ejemplos:
Cuando se quiera utilizar operadores de Python en Csound, debe incluirse siempre el operador pyinit en el encabezamiento de la orquesta, antes de invocar a cualquier otro operador Python.
El operador pyinit no tiene ninguna de las variantes explicadas arriba.
El operador pyrun y sus variantes simplemente ejecutan un bloque de código Python dentro de la orquesta de Csound.
Si el código es una instrucción que ocupa sólo una línea, puede encerrarse entre comillas (" ").
Si ocupa varias líneas, deberá delimitarse con doble llaves ({{ }}).
La siguiente orquesta de Csound utiliza el operador pyruni en el encabezamiento de la orquesta para ejecutar código Python al tiempo de inicialización de la orquesta.
Obsérvese el uso del operador pyinit al comienzo.
%load py01.csd
<CsoundSynthesizer>
<CsOptions>
-ndm0
</CsOptions>
<CsInstruments>
; se inicializa el intérprete de Python
pyinit
; se ejecuta código Python a init-time
pyruni "print '*************************'"
pyruni "print '** Hola Csound Python! **'"
pyruni {{
print 'Se inicializa la orquesta'
}}
pyruni {{print '*************************' }}
; instrumento vacío
instr 1
endin
</CsInstruments>
<CsScore>
; se prenden notas para disparar el proceso
i 1 0 0
i 1 1 0
</CsScore>
</CsoundSynthesizer>
<pre>
<CsoundSynthesizer>
<CsOptions>
-ndm0
</CsOptions>
<CsInstruments>
; se inicializa el intérprete de Python
pyinit
; se ejecuta código Python a init-time
pyruni "print '*************************'"
pyruni "print '** Hola Csound Python! **'"
pyruni {{
print 'Se inicializa la orquesta'
}}
pyruni {{print '*************************' }}
; instrumento vacío
instr 1
endin
</CsInstruments>
<CsScore>
; se prenden notas para disparar el proceso
i 1 0 0
i 1 1 0
</CsScore>
</CsoundSynthesizer>
</pre>
!csound py01.csd
La siguiente orquesta utiliza el operador pyruni en el encabezamiento y dentro de los instrumentos, en su variante global (pyruni) y local (pylruni).
%load py02.csd
<CsoundSynthesizer>
<CsOptions>
-ndm0
</CsOptions>
<CsInstruments>
; se inicializa el intérprete de Python
pyinit
; se ejecuta código Python en el encabezamiento
pyruni {{
a = 1
b = 2
print "********************************"
print "en el encabezamiento: a + b = " + str(a+b)
print "********************************"
}}
; se ejecuta código Python dentro del instrumento 1
; el valor de las variables es global
instr 1
pyruni {{a = 3
b = 4
print "en el instrumento 1: a + b = " + str(a+b)}}
endin
; se ejecuta código Python dentro del instrumento 2
; el valor de las variables es local
instr 2
pylruni {{a = 5
b = 6
print "en el instrumento 2: a + b = " + str(a+b)}}
endin
; se ejecuta código Python dentro del instrumento 2
instr 3
pyruni {{print "en el instrumento 3: a + b = " + str(a+b)}}
endin
</CsInstruments>
<CsScore>
i1 0 0
i2 1 0
i3 2 0
i1 3 0
</CsScore>
</CsoundSynthesizer>
!csound py02.csd
Se puede observar que:
El operador pyrun, sin sufijo de tiempo de ejecución, opera a tiempo de frecuencia de control (k-rate).
%load py03.csd
<CsoundSynthesizer>
<CsOptions>
-ndm0
</CsOptions>
<CsInstruments>
sr=44100
kr=100
; se inicializa el intérprete de Python
pyinit
; se ejecuta código Python a init-time
pyruni "print '*************************'"
pyruni "print '** Se inicializa a = 0 **'"
pyruni "print '*************************'"
pyruni "a = 0"
instr 1
; se incrementa la variable "a" a k-rate
pyrun "a = a + 1"
endin
instr 2
; imprime el valor de a
pyruni {{print "a = " + str(a)}}
endin
</CsInstruments>
<CsScore>
i 1 0 1 ; incrementa a durante un segundo
i 2 1 0 ; imprime el valor de a
i 1 2 1 ; incrementa a durante otro segundo
i 2 2.5 0 ; imprime el valor de a
</CsScore>
</CsoundSynthesizer>
!csound py03.csd
Con el operador pyrunt podemos controlar que la ejecución del código Python se realice disparada por un gatillo o trigger.
La sintaxis es:
pyrunt ktrigger, "expresion"
%load py04.csd
<CsoundSynthesizer>
<CsOptions>
-ndm0
</CsOptions>
<CsInstruments>
sr=44100
kr=100
; se inicializa el intérprete de Python
pyinit
instr 1
; se inicializa la variable "a"
pyruni "print 'se inicializa a = 0'"
pyruni "a = 0"
; se genera un gatillo con frecuencia p4
ktrig metro p4
; se incrementa "a" controlado por el gatillo
pyrunt ktrig, "a = a + 1"
; y se imprime su valor
pyrunt ktrig, "print a"
endin
</CsInstruments>
<CsScore>
i1 0 1 5 ; incrementa la variable 5 veces por segundo
i1 1 1 10 ; incrementa la variable 10 veces por segundo
</CsScore>
</CsoundSynthesizer>
!csound py04.csd
El operador pyeval y sus variantes sirven para evaluar una expresión de Python y asignar su valor a una variable de Csound.
Hay que cuidar que la expresión evalúe a un número de coma flotante.
%load py05.csd
<CsoundSynthesizer>
<CsOptions>
-d
</CsOptions>
<CsInstruments>
sr=44100
ksmps=1
0dbfs = 1
; se inicializa el intérprete de Python
pyinit
; se importa en módulo random
pyruni "import random"
instr 1
; se genera un gatillo con frecuencia p4
ktrig metro p4
; el gatillo dispara la generación de un número aleatorio
pyrunt ktrig, "a = float(random.randrange(200, 500))"
pyrunt ktrig, "print a"
; la variable "a" se asigna a la variable "krand"
krand pyeval "a"
kfreq port krand, 0.001
asig oscil 0.5, kfreq, 1
kamp linen 1, 0.05, p3, 0.1
out asig*kamp
endin
</CsInstruments>
<CsScore>
f1 0 16384 10 1
i1 0 1 4 ; incrementa la variable 5 veces por segundo
i1 1 1 16 ; incrementa la variable 10 veces por segundo
</CsScore>
</CsoundSynthesizer>
!csound py05.csd
El operador pyassign y sus variantes asignan un valor a una variable de Python, creándola en caso de que no exista previamente.
%load py06.csd
<CsoundSynthesizer>
<CsOptions>
-nd
</CsOptions>
<CsInstruments>
pyinit
instr 1 ; asigna p4 a la variable cociente
pyassigni "cociente", p4
endin
instr 2 ; calcula la distancia en cents
pyruni {{
from math import log
cents = log(cociente,2)*1200
print "cociente: " + str(cociente)
print "cents: " + str(cents)
}}
endin
</CsInstruments>
<CsScore>
i1 0 0 1.5
i2 0 0
i1 1 0 2
i2 1 0
</CsScore>
</CsoundSynthesizer>
!csound py06.csd
El operador pycall y sus variantes pueden llamar a un objeto llamable (callable oject) de Python, por ejemplo, una función.
Si el objeto recibe argumentos, éstos pueden pasarse como parámetros adicionales del operador, según la siguiente sintaxis:
pycall "objeto" [, val1] [, val2] [, ...]
El siguiente ejemplo muestra una orquesta con una función que recibe un argumento e imprime su valor.
La función no devuelve ningún valor (objeto None).
%load py07.csd
<CsoundSynthesizer>
<CsOptions>
-dnm0
</CsOptions>
<CsInstruments>
pyinit
pyruni {{
def instr_num(arg):
'''función que imprime el parámetro recibido'''
print "se prende una nota en el instrumento " + str(arg)
return None
}}
instr 1
; llama a la función con el parámetro p1, número de instrumento
pycalli "instr_num", p1
endin
</CsInstruments>
<CsScore>
i1.1 0 1
i1.2 1 1
</CsScore>
</CsoundSynthesizer>
!csound py07.csd
El objeto llamado puede devolver uno o más valores, hasta un máximo de 8. En este caso deberá usarse respectivamente alguno de los operadores pycall1 a pycall8 (o sus variantes), para poder asignar los valores que devuelve el objeto a variables de Csound.
Las sintaxis respectivas siguen el modelo de estos ejemplos:
ires pycall1i "funcion" [, iarg1, .....]
kr1, kr2 pycall2 "funcion" [, karg1, .....]
kr1, kr2, kr3 pycall3t ktrigger, "funcion" [, karg1, .....]
Etcétera.
En el siguiente ejemplo se define la función "promedio", que recibe dos argumentos y devuelve un valor.
Se invoca con pycall1i.
%load py08.csd
<CsoundSynthesizer>
<CsOptions>
-dnm0
</CsOptions>
<CsInstruments>
pyinit
pyruni {{
def promedio(a, b):
avrg = (a+b)/2
return avrg
}}
instr 1
iavrg pycall1i "promedio", p4, p5
print iavrg
endin
</CsInstruments>
<CsScore>
i 1 0 1 100 200
i 1 1 1 1000 2000
</CsScore>
</CsoundSynthesizer>
!csound py08.csd
En el siguiente ejemplo la función recibe un argumento y devuelve dos valores
Es invocada con pycall2i.
%load py09.csd
<CsoundSynthesizer>
<CsOptions>
-dnm0
</CsOptions>
<CsInstruments>
pyinit
pyruni {{
def oct_pc(nota_midi):
'''devuelve la octava y la clase de altura de una nota midi'''
oct = (nota_midi//12)-1
pc = nota_midi%12
return oct, pc
}}
instr 1
ioct, ipc pycall2i "oct_pc", p4
print ioct, ipc
endin
</CsInstruments>
<CsScore>
i 1 0 1 60
i 1 1 1 66
</CsScore>
</CsoundSynthesizer>
!csound py09.csd
El operador pyexec y sus derivados pueden ejecutar un script de Python guardado como un archivo en el disco duro.
Recibe como argumento el nombre del archivo, como ruta relativa o absoluta.
La siguiente orquesta de Csound ejecuta el script fecha.py, que imprime la fecha y hora al momento de ser ejecutado.
%load fecha.py
# imprime la fecha y hora local al momento de ejecutarse
from datetime import datetime
fecha = datetime.now().strftime('%Y-%m-%d, %H:%M:%S')
print "*****************************************"
print "fecha y hora actual: " + str(fecha)
print "*****************************************"
%load py10.csd
<CsoundSynthesizer>
<CsOptions>
-dnm0
</CsOptions>
<CsInstruments>
pyinit
; se ejecuta el script Python "fecha.py"
pyexeci "fecha.py"
instr 1
endin
</CsInstruments>
<CsScore>
i1 0 0
</CsScore>
</CsoundSynthesizer>
!csound py10.csd
Andrés Cabrera: Using Python Inside Csound - An introduction to the Python opcodes
Csound Journal - Issue 6, 2007
http://csounds.com/journal/issue6/pythonOpcodes.html
Csound FLOSS Manual: Python inside Csound
http://en.flossmanuals.net/csound/using-python-inside-csound/