Este capítulo se divide en tres partes. La Parte I ofrece una introducción general a los conceptos relacionados con los números aleatorios y sobre cómo trabajar con ellos en Csound. La Parte II se centra en un enfoque más matemático. La Parte III introduce una serie de opcodes para la generación de números aleatorios, funciones y distribuciones y se demuestra su uso en algunos ejemplos musicales.
El término random (aleatorio) históricamente refiere a una situación en la que un caballo corre tan rápido que pasa a estar 'fuera de control' o 'más allá de lo predecible'.1 Sin embargo, hay diferentes maneras en que se puede correr rápido y estar fuera de control; por lo tanto, hay diferentes tipos de aleatoriedad.
Podemos dividir los tipos de aleatoriedad en dos clases. El primero contiene sucesos aleatorios que son independientes de los eventos anteriores. El ejemplo más común es el lanzamiento de un dado. Incluso si acaba de lanzar tres 1s de manera consecutiva, cuando se lanza de nuevo, el 1 tiene la misma probabilidad que antes (y que cualquiera de los otros números). La segunda clase de números aleatorios implica sucesos aleatorios que dependen de alguna manera de los números o estados anteriores. Ejemplos de esta última clase son las cadenas de Markov y las 'random walks' o caminatas aleatorias.
El uso de aleatoriedad en la música electrónica es un fenómeno muy extendido. En este capítulo, vamos a tratar de explicar cómo los diferentes "caballos" aleatorios se mueven, y cómo Ud. puede crearlos y modificarlos a su gusto. Aún más, hay muchos opcodes para trabajar la aleatoriedad pre-construidos en Csound, que pueden ser utilizados desde un primer momento (ver el overview en el Manual de Csound). En la sección final de este capítulo se presentan algunas aplicaciones musicales interesantes en base a ellos.
Un ordenador es típicamente capaz de realizar sólo cálculos. Los cálculos son procesos deterministas: una entrada generará siempre la misma salida, pero un evento aleatorio no es predecible. Para generar algo que se parezca a un evento aleatorio, el equipo utiliza un generador pseudo-aleatorio.
El generador pseudo-aleatorio tiene un número como entrada, y genera otro número como salida. Esta salida es luego la entrada para la próxima generación. Utilizando una gran cantidad de números, se ven como si estuvieran distribuidos aleatoriamente, aunque todo depende de la primera entrada: la semilla. A partir de una semilla dada, es posible predecir los valores siguientes.
La salida de un generador pseudo-aleatorio clásico posee una distribución uniforme: cada valor de un rango dado tiene la misma probabilidad de ocurrencia. El primer ejemplo muestra la influencia de una semilla fija (utilizando la misma cadena de números y comenzando desde la misma ubicación en la cadena cada vez) en contraste con una semilla tomada del reloj del sistema (la forma más habitual de imitar un fenónmeno imprevisible). Los tres primeros grupos de cuatro notas siempre serán los mismos debido a la utilización de la misma semilla, mientras que los tres últimos grupos deberían tener siempre una altura diferente.
EXAMPLE 01D01_different_seed.csd
<CsoundSynthesizer> <CsOptions> -d -odac -m0 </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 instr generate ;obtener la semilla: 0 = semilla a partir del reloj del sistema ; cualquier otro número = semilla fija seed p4 ;generar cuatro notas a ser interpretadas desde el subinstrumento iNoteCount = 0 until iNoteCount == 4 do iFreq random 400, 800 event_i "i", "play", iNoteCount, 2, iFreq iNoteCount += 1 ;aumentar la cuenta de notas enduntil endin instr play iFreq = p4 print iFreq aImp mpulse .5, p3 aMode mode aImp, iFreq, 1000 aEnv linen aMode, 0.01, p3, p3-0.01 outs aEnv, aEnv endin </CsInstruments> <CsScore> ;repetir tres veces con una semilla fija r 3 i "generate" 0 2 1 ;repetir tres veces con una semilla tomada del reloj del sistema r 3 i "generate" 0 1 0 </CsScore> </CsoundSynthesizer> ;ejemplo por joachim heintz
Tenga en cuenta que un generador pseudo-aleatorio repetirá su serie de números después de tantos pasos como sean dados por el tamaño del generador. Si se genera un número de 16 bits, la serie se repetirá después de 65536 pasos. Si escucha con atención el siguiente ejemplo, se escuchará una repetición en la estructura del ruido blanco (que es el resultado de las amplitudes uniformemente distribuidas) después de 1.5 segundos en la primera nota.2 En la segunda nota, no hay repetición que sea perceptible ya que el generador aleatorio ahora trabaja con un número de 31 bits.
EXAMPLE 01D02_white_noises.csd
<CsoundSynthesizer> <CsOptions> -d -odac </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 instr white_noise iBit = p4 ;0 = 16 bit, 1 = 31 bit ;entrada de rand: amplitud, semilla fija (0.5), tamaño de bit aNoise rand .1, 0.5, iBit outs aNoise, aNoise endin </CsInstruments> <CsScore> i "white_noise" 0 10 0 i "white_noise" 11 10 1 </CsScore> </CsoundSynthesizer> ;ejemplo por joachim heintz
Dos cosideraciones generales sobre esto:
<CsInstruments> sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 seed = 0 ;semilla a partir del tiempo actual
...
1) ires random imin, imax 2) kres random kmin, kmax 3) ares random kmin, kmax
La distribución uniforme es aquella que cada equipo puede producir a través de su generador pseudo-aleatorio. Sin embargo, hay muchas situaciones en que no se querrá un azar distribuido uniformemente, sino de cualquier otra forma. Algunas de estas formas son bastante comunes, pero de hecho se pueden construir formas propias utilizando Csound con bastante facilidad. Los siguientes ejemplos muestran cómo hacer esto. Están basados en el capítulo hallado en Dodge/Jerse3 que también sirvió como modelo para muchos opcodes generadores de números aleatorios en Csound.4
Una distribución lineal significa que los valores menores o los valores mayores en un rango dado serán más probables:
Para conseguir este comportamiento, se generan dos números aleatorios uniformes, y se selecciona el más bajo para lograr la primera forma. Si se necesita la segunda forma en donde prevalecen los valores más altos, el número que se toma es el mayor. El siguiente ejemplo implementa estos generadores aleatorios como Opcodes Definidos por el Usuario (en inglés, UDO). Primero oímos una distribución uniforme, a continuación, una distribución lineal con preferencia por los tonos más bajos (pero de mayor duración) y finalmente una distribución lineal con preferencia de tonos más altos (pero duraciones más cortas).
EXAMPLE 01D03_linrand.csd
<CsoundSynthesizer> <CsOptions> -d -odac -m0 </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 seed 0 ;****CÓDIGOS OPERATIVOS DEFINIDOS PARA UNA DISTRIBUCIÓN LINEAL**** opcode linrnd_low, i, ii ;linear random with precedence of lower values
;aleatoriedad lineal con precedencia de los valores más bajos iMin, iMax xin ;generate two random values with the random opcode
;generar dos valores aleatorios con el código operativo random iOne random iMin, iMax iTwo random iMin, iMax ;comparar y devolver el más bajo iRnd = iOne < iTwo ? iOne : iTwo xout iRnd endop opcode linrnd_high, i, ii ;aleatoriedad lineal con precedencia de los valores más altos iMin, iMax xin ;generar dos valores aleatorios con el código operativo random iOne random iMin, iMax iTwo random iMin, iMax ;comparar y devolver el más alto iRnd = iOne > iTwo ? iOne : iTwo xout iRnd endop ;****INSTRUMENTOS PARA LAS DISTINTAS DISTRIBUCIÓNES**** instr notes_uniform prints "... instr notes_uniform ejecutándose:\n" prints "IGUAL PROBABILIDAD PARA TODAS LAS ALTURAS Y DURACIONES\n" ;cuántas notas se reproducirán iHowMany = p4 ;ejecutar tantas instancias del instrumento play como sean necesarias iThisNote = 0 iStart = 0 until iThisNote == iHowMany do iMidiPch random 36, 84 ;nota midi iDur random .5, 1 ;duración event_i "i", "play", iStart, iDur, int(iMidiPch) iStart += iDur ;aumentar el tiempo de inicio
iThisNote += 1 ;aumentar la cuenta enduntil ;reset the duration of this instr to make all events happen
;reconfigurar la duración de este instrumento para hacer que todos los eventos ocurran
p3 = iStart + 2 ;trigger next instrument two seconds after the last note
;ejecutar el siguiente instrumento pasados dos segundos desde la última nota event_i "i", "notes_linrnd_low", p3, 1, iHowMany endin instr notes_linrnd_low prints "... instr notes_linrnd_low ejecutándose:\n" prints "SE PREFIEREN LAS NOTAS MÁS BAJAS Y LAS DURACIONES MAS LARGAS\n" iHowMany = p4 iThisNote = 0 iStart = 0 until iThisNote == iHowMany do iMidiPch linrnd_low 36, 84 ;se prefieren notas más bajas iDur linrnd_high .5, 1 ;se prefieren duraciones más largas event_i "i", "play", iStart, iDur, int(iMidiPch) iStart += iDur iThisNote += 1 enduntil ;reconfigurar la duración de este instrumento para hacer que todos los eventos ocurran p3 = iStart + 2 ;trigger next instrument two seconds after the last note
;ejecutar el siguiente instrumento pasados dos segundos desde la última nota event_i "i", "notes_linrnd_high", p3, 1, iHowMany endin instr notes_linrnd_high prints "... instr notes_linrnd_high ejecutándose:\n" prints "SE PREFIEREN LAS NOTAS MÁS ALTAS Y LAS DURACIONES MÁS CORTAS\n" iHowMany = p4 iThisNote = 0 iStart = 0 until iThisNote == iHowMany do iMidiPch linrnd_high 36, 84 ;higher pitches preferred iDur linrnd_low .3, 1.2 ;shorter durations preferred event_i "i", "play", iStart, iDur, int(iMidiPch) iStart += iDur iThisNote += 1 enduntil ;reconfigurar la duración de este instrumento para hacer que todos los eventos ocurran p3 = iStart + 2 ;llamar al instrumento exit para salir de Csound event_i "i", "exit", p3+1, 1 endin ;****INSTRUMENTOS PARA REPRODUCIR LOS SONIDOS Y PARA ABANDONAR CSOUND**** instr play ;aumentar la duración en el rango aleatorio iDur random p3, p3*1.5 p3 = iDur ;obtener nota midi y convertirla a frecuencia iMidiNote = p4 iFreq cpsmidinn iMidiNote ;generar nota con el algoritmo de karplus-strong aPluck pluck .2, iFreq, iFreq, 0, 1 aPluck linen aPluck, 0, p3, p3 ;filtro aFilter mode aPluck, iFreq, .1 ;mezclar aPluck y aFilter de acuerdo a MidiNote ;(las notas altas se filtrarán más) aMix ntrpol aPluck, aFilter, iMidiNote, 36, 84 ;paneo también de acuerdo a MidiNote ;(bajo = izquierda, alto = derecha) iPan = (iMidiNote-36) / 48 aL, aR pan2 aMix, iPan outs aL, aR endin instr exit exitnow endin </CsInstruments> <CsScore> i "notes_uniform" 0 1 23 ;establecer el número de notas por instrumento ;los instrumentos linrnd_low y linrnd_high se ejecutan automáticamente e 99999 ;posibilita ejecutar durante mucho tiempo (el abandono del programa será automático) </CsScore> </CsoundSynthesizer> ;ejemplo por joachim heintz
En una distribución triangular los valores ubicados más hacia el centro del rango dado tienen más probabilidades que los que están en los extremos. La transición de probabilidad entre el centro y los extremos es lineal:
El algoritmo para conseguir esta distribución es también muy simple. Se generan dos números aleatorios uniformes y se toma la media de ellos. El siguiente ejemplo muestra la diferencia entre una distribución uniforme y una distribución triangular en el mismo entorno que el ejemplo anterior.
EXAMPLE 01D04_trirand.csd
<CsoundSynthesizer> <CsOptions> -d -odac -m0 </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 seed 0 ;****UDO PARA DISTRIBUCIÓN TRIANGULAR**** opcode trirnd, i, ii iMin, iMax xin ;se generan dos valores aleatorios con el código operativo random iOne random iMin, iMax iTwo random iMin, iMax ;get the mean and output iRnd = (iOne+iTwo) / 2 xout iRnd endop ;****INSTRUMENTOS PARA UNA DISTRIBUCIÓN UNIFORME Y TRIANGULAR**** instr notes_uniform prints "... instr notes_uniform ejecutándose:\n" prints "IGUAL PROBABILIDAD PARA TODAS LAS ALTURAS Y DURACIONES\n" ;how many notes to be played iHowMany = p4 ;disparar tantas instancias del instrumento play como sean necesarias iThisNote = 0 iStart = 0 until iThisNote == iHowMany do iMidiPch random 36, 84 ;nota midi iDur random .25, 1.75 ;duración event_i "i", "play", iStart, iDur, int(iMidiPch) iStart += iDur ;aumentar el tiempo de inicio iThisNote += 1 ;aumentar el contador enduntil ;reconfigurar la duración de este instrumento para hacer que todos los eventos ocurran p3 = iStart + 2 ;ejecutar el siguiente instrumento pasados dos segundos desde la última nota event_i "i", "notes_trirnd", p3, 1, iHowMany endin instr notes_trirnd prints "... instr notes_trirnd ejecutándose:\n" prints "SE PREFIEREN NOTAS Y DURACIONES DE LA MEDIA\n" iHowMany = p4 iThisNote = 0 iStart = 0 until iThisNote == iHowMany do iMidiPch trirnd 36, 84 ;se prefieren las alturas del medio iDur trirnd .25, 1.75 ;se prefieren las duraciones del medio event_i "i", "play", iStart, iDur, int(iMidiPch) iStart += iDur iThisNote += 1 enduntil ;reconfigurar la duración de este instrumento para hacer que todos los eventos ocurran p3 = iStart + 2 ;llamar al instrumento correspondiente para abandonar Csound event_i "i", "exit", p3+1, 1 endin ;****INSTRUMENTOS PARA REPRODUCIR LOS SONIDOS Y PARA ABANDONAR CSOUND**** instr play ;aumentar la duración en el rango aleatorio iDur random p3, p3*1.5 p3 = iDur ;obtener nota midi y convertirla a frecuencia iMidiNote = p4 iFreq cpsmidinn iMidiNote ;generar nota con el algoritmo karplus-strong aPluck pluck .2, iFreq, iFreq, 0, 1 aPluck linen aPluck, 0, p3, p3 ;filtro aFilter mode aPluck, iFreq, .1 ;mezclar aPluck y aFilter de acuerdo a MidiNote ;(las notas altas se filtrarán más) aMix ntrpol aPluck, aFilter, iMidiNote, 36, 84 ;paneo también de acuerdo a MidiNote
;(bajo = izquierda, alto = derecha)
iPan = (iMidiNote-36) / 48 aL, aR pan2 aMix, iPan outs aL, aR endin instr exit exitnow endin </CsInstruments> <CsScore> i "notes_uniform" 0 1 23 ;establecer el número de notas por instrumento ;el instrumento trirnd se ejecutará automáticamente e 99999 ;posibilita ejecutar durante mucho tiempo (el abandono del programa será automático) </CsScore> </CsoundSynthesizer> ;ejemplo por joachim heintz
Más sobre Lineal y Triangular
Después de haber escrito esto utilizando algunos UDOs muy simples, resulta sencillo enfatizar los picos de probabilidad de las distribuciones mediante la generación de más de dos números al azar. Si generan tres números y se elige el más pequeño de ellos, se obtendrán muchos más números cerca del mínimo para la distribución lineal. Si se generan tres números al azar y se toma la media de ellos, el resultado final se aproximará a más números cercanos a la media para la distribución triangular.
Si queremos escribir UDOs con un número flexible de números sub-generados, deberíamos escribir el código de una manera ligeramente diferente. En lugar de tener una línea de código para cada generador aleatorio, utilizaremos un bucle, que a su vez llamará al generador tantas veces como unidades se deseen obtener. Una variable almacenará los resultados de esta acumulación. Reescribir el código operativo para el UDO trirnd nos llevaría a la siguiente formulación:
opcode trirnd, i, ii iMin, iMax xin ;establecer un contador y una cuenta máxima iCount = 0 iMaxCount = 2 ;establecer el acumulador en cero como valor inicial iAccum = 0 ;ejecutar bucle y acumular until iCount == iMaxCount do iUniRnd random iMin, iMax iAccum += iUniRnd iCount += 1 enduntil ;obtener la media y retornar. iRnd = iAccum / 2 xout iRnd endop
Para tornar ésto completamente flexible, es suficiente con configurar iMaxCount como argumento de entrada. El código para los UDOs de distribución lineal es bastante similar. -- El siguiente ejemplo muestra estos pasos:
En lugar de utilizar diferentes instrumentos para las distintas distribuciones, el siguiente ejemplo combina todas las posibilidades en un solo instrumento. Dentro del bucle que genera tantas notas como se deseen por medio del argumento iHowMany, una sentencia condicional if calcula el tono y la duración de una nota en función del tipo de distribución y el número de sub-unidades utilizados. La secuencia entera (cuál tipo primero, cuál siguiente, etc.) se almacena en el vector global giSequence. Cada instancia del instrumento "notes", incrementa el puntero giSeqIndx, de modo que en la próxima ejecución se leerá el elemento siguiente del vector. Si el puntero ha alcanzado el final del mismo, el instrumento que ocasiona la salida de Csound es llamado en lugar de una nueva instancia de "notes".
EXAMPLE 01D05_more_lin_tri_units.csd
<CsoundSynthesizer> <CsOptions> -d -odac -m0 </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 seed 0 ;****SECUENCIAS DE UNIDADES COMO ARRAYS****/ giSequence[] array 0, 1.2, 1.4, 2.2, 2.4, 3.2, 3.6 giSeqIndx = 0 ;índice de comienzo ;****DEFINICIONES DE UDOs**** opcode linrnd_low, i, iii ;aleatoriedad lineal con prevalencia de los valores más bajos iMin, iMax, iMaxCount xin ;establecer contador y resultado inicial (absurdo) iCount = 0 iRnd = iMax ;bucle y reseteo de iRnd until iCount == iMaxCount do iUniRnd random iMin, iMax iRnd = iUniRnd < iRnd ? iUniRnd : iRnd iCount += 1 enduntil xout iRnd endop opcode linrnd_high, i, iii ;aleatoriedad lineal con prevalencia de los valores más altos iMin, iMax, iMaxCount xin ;establecer contador y resultado inicial (absurdo) iCount = 0 iRnd = iMin ;bucle y reseteo de iRnd until iCount == iMaxCount do iUniRnd random iMin, iMax iRnd = iUniRnd > iRnd ? iUniRnd : iRnd iCount += 1 enduntil xout iRnd endop opcode trirnd, i, iii iMin, iMax, iMaxCount xin ;establecer un contador y un acumulador iCount = 0 iAccum = 0 ;ejecutar bucle y acumular until iCount == iMaxCount do iUniRnd random iMin, iMax iAccum += iUniRnd iCount += 1 enduntil ;obtener la media y retornar iRnd = iAccum / iMaxCount xout iRnd endop ;****UN INSTRUMENTO QUE REALIZA TODAS LAS DISTRIBUCIONES**** ;0 = uniforme, 1 = linrnd_low, 2 = linrnd_high, 3 = trirnd ;la parte fraccionaria denota el número de unidades, por ejemplo, ;3.4 = distribución triangular con cuatro sub-unidades instr notes ;cuántas notas se ejecutarán iHowMany = p4 ;por qué distribución con cuántas unidades iWhich = giSequence[giSeqIndx] iDistrib = int(iWhich) iUnits = round(frac(iWhich) * 10) ;establecer duración mínima y máxima iMinDur = .1 iMaxDur = 2 ;establecer altura mínima y máxima iMinPch = 36 iMaxPch = 84 ;disparar tantas instancias del instrumento play como sea necesario iThisNote = 0 iStart = 0 iPrint = 1 ;por cada nota a ejecutarse until iThisNote == iHowMany do ;calcular iMidiPch y iDur dependiendo del tipo if iDistrib == 0 then printf_i "%s", iPrint, "... distribución uniforme:\n" printf_i "%s", iPrint, "IGUAL PROBABILIDAD PARA TODAS LAS ALTURAS Y DURACIONES\n" iMidiPch random iMinPch, iMaxPch ;nota midi iDur random iMinDur, iMaxDur ;duración elseif iDistrib == 1 then printf_i "... distribución lineal baja con %d unidades:\n", iPrint, iUnits printf_i "%s", iPrint, "SE PREFIEREN NOTAS MÁS BAJAS Y DURACIONES MÁS LARGAS\n" iMidiPch linrnd_low iMinPch, iMaxPch, iUnits iDur linrnd_high iMinDur, iMaxDur, iUnits elseif iDistrib == 2 then printf_i "... distribución lineal alta con %d unidades:\n", iPrint, iUnits printf_i "%s", iPrint, "SE PREFIEREN NOTAS MÁS ALTAS Y DURACIONES MÁS CORTAS\n" iMidiPch linrnd_high iMinPch, iMaxPch, iUnits iDur linrnd_low iMinDur, iMaxDur, iUnits else printf_i "... distribución triangular con %d unidades:\n", iPrint, iUnits printf_i "%s", iPrint, "SE PREFIEREN ALTURAS Y DURACIONES MEDIAS\n" iMidiPch trirnd iMinPch, iMaxPch, iUnits iDur trirnd iMinDur, iMaxDur, iUnits endif ;llamar al subinstrumento para interpretar la nota event_i "i", "play", iStart, iDur, int(iMidiPch) ;aumentar tiempo de inicio y contador iStart += iDur iThisNote += 1 ;evitar la impresión a pantalla continua iPrint = 0 enduntil ;restablecer la duración de este instrumento para hacer que todas las ejecuciones sucedan. p3 = iStart + 2 ;aumentar el índice para la secuencia giSeqIndx += 1 ;llamar al instrumento de nuevo si la secuencia no ha sido terminada if giSeqIndx < lenarray(giSequence) then event_i "i", "notes", p3, 1, iHowMany ;o salir else event_i "i", "exit", p3, 1 endif endin ;****INSTRUMENTOS PARA EJECUTAR LOS SONIDOS Y ABANDONAR CSOUND**** instr play ;aumentar la duración en el rango aleatorio iDur random p3, p3*1.5 p3 = iDur ;obtener nota midi y convertirla a frecuencia iMidiNote = p4 iFreq cpsmidinn iMidiNote ;generar nota con el algoritmo de karplus-strong aPluck pluck .2, iFreq, iFreq, 0, 1 aPluck linen aPluck, 0, p3, p3 ;filtro aFilter mode aPluck, iFreq, .1 ;mezclar aPluck y aFilter de acuerdo a MidiNote ;(las notas más altas se filtrarán más) aMix ntrpol aPluck, aFilter, iMidiNote, 36, 84 ;paneo de acuerdo a MidiNote ;(bajo = izquierda, alto = derecho) iPan = (iMidiNote-36) / 48 aL, aR pan2 aMix, iPan outs aL, aR endin instr exit exitnow endin </CsInstruments> <CsScore> i "notes" 0 1 23 ;establecer el número de notas por instrumento aquí e 99999 ;posibilita ejecutar durante mucho tiempo (la salida de Csound será automática) </CsScore> </CsoundSynthesizer> ;ejemplo por joachim heintz
Con este método podemos construir distribuciones de probabilidad muy similares a las distribuciones exponenciales y gaussianas.5 La forma puede configurarse fácilmente mediante el número de sub-unidades utilizadas.
La aleatoriedad es un ámbito complejo y sensible. Hay tantas maneras para dejar que el caballo vaya, corra o baile -las condiciones que se establecen para este "modo de moverse" son mucho más importantes que el hecho de que un solo movimiento no sea predecible. ¿Cuáles son las condiciones de esta aleatoriedad?
En el ejemplo anterior hemos utilizado dos escalamientos implícitos. Las alturas han sido delimitadas a las de las teclas de un piano o teclado. ¿Por qué? No estamos tocando un piano aquí, obviamente, ... -¿Qué otras posibilidades podrían haberse elegido en su lugar? Una de ellas sería: ningun escalamiento en absoluto. Esta es la forma más fácil de proseguir -si realmente es la mejor, o simplemente actuamos por pereza, sólo puede ser decidido por el compositor o el oyente.
En lugar de utilizar la escala cromática temperada, o de usar ninguna escala en absoluto, se puede utilizar cualquier otra manera de seleccionar o cuantificar las alturas. Trátese de cualquiera que haya o esté siendo usada en cualquier parte del mundo, o sea una de su propia invención, por cualquier fantasía, invención o sistema.
En cuanto a la duración, el ejemplo anterior no ha mostrado escalamiento en absoluto. Esto es sin dudas por pereza ..
El siguiente ejemplo es esencialmente el mismo que el anterior, pero utilizando una escala de alturas que representa la serie de armónicos. Comenzando en el segundo parcial se extiende hacia arriba al parcial número 32. Esta escala está escrita dentro de un vector mediante una declaración en el instrumento 0. Las duraciones poseen valores posibles fijos que también están escritas en otro vector (de la más larga a la más corta) de manera manual. Los valores en ambas vector son luego llamados de acuerdo a su posición.
EXAMPLE 01D06_scalings.csd
<CsoundSynthesizer> <CsOptions> -d -odac -m0 </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 seed 0 ;****DURACIONES POSIBLES DENTRO DE UN ARRAY**** giDurs[] array 3/2, 1, 2/3, 1/2, 1/3, 1/4 giLenDurs lenarray giDurs ;****ALTURAS POSIBLES DENTRO DE UN ARRAY**** ;inicializar array con 31 pasos/escalones giScale[] init 31 giLenScale lenarray giScale ;iterar para llenar desde 65 hz en adelante iStart = 65 iDenom = 3 ;inicio con 3/2 iCnt = 0 until iCnt = giLenScale do giScale[iCnt] = iStart iStart = iStart * iDenom / (iDenom-1) iDenom += 1 ;la siguiente proporción es 4/3 etc. iCnt += 1 enduntil ;****SECUENCIA DE UNIDADES DENTRO DE UN ARRAY**** giSequence[] array 0, 1.2, 1.4, 2.2, 2.4, 3.2, 3.6 giSeqIndx = 0 ;startindex ;****UDO DEFINITIONS**** opcode linrnd_low, i, iii ;aleatoriedad lineal con preferencia de valores más bajos iMin, iMax, iMaxCount xin ;establecer contador y resultado inicial (absurdo) iCount = 0 iRnd = iMax ;realizar bucle y restablecer iRnd until iCount == iMaxCount do iUniRnd random iMin, iMax iRnd = iUniRnd < iRnd ? iUniRnd : iRnd iCount += 1 enduntil xout iRnd endop opcode linrnd_high, i, iii ;aleatoriedad lineal con preferencia de valores más altos iMin, iMax, iMaxCount xin ;establecer contador y resultado inicial (absurdo) iCount = 0 iRnd = iMin ;realizar bucle y restablecer iRnd until iCount == iMaxCount do iUniRnd random iMin, iMax iRnd = iUniRnd > iRnd ? iUniRnd : iRnd iCount += 1 enduntil xout iRnd endop opcode trirnd, i, iii iMin, iMax, iMaxCount xin ;establecer un contador y un acumulador iCount = 0 iAccum = 0 ;realizar bucle y acumular until iCount == iMaxCount do iUniRnd random iMin, iMax iAccum += iUniRnd iCount += 1 enduntil ;obtener la media y retornar iRnd = iAccum / iMaxCount xout iRnd endop ;****UN INSTRUMENTO QUE REALIZA TODAS LAS DISTRIBUCIONES****
;0 = uniforme, 1 = linrnd_low, 2 = linrnd_high, 3 = trirnd
;la parte fraccionaria denota el número de unidades, por ejemplo,
;3.4 = distribución triangular con cuatro sub-unidades instr notes ;cuántas notas deberan ejecutarse iHowMany = p4 ;mediante qué distribución y con cuántas unidades iWhich = giSequence[giSeqIndx] iDistrib = int(iWhich) iUnits = round(frac(iWhich) * 10) ;disparar tantas instancias del instrumento play como sea necesario iThisNote = 0 iStart = 0 iPrint = 1 ;para cada nota a ser ejecutada until iThisNote == iHowMany do ;calcular iMidiPch e iDur dependiendo del tipo if iDistrib == 0 then printf_i "%s", iPrint, "... distribución uniforme:\n" printf_i "%s", iPrint, "IGUAL PROBABILIDAD PARA TODAS LAS ALTURAS Y DURACIONES\n" iScaleIndx random 0, giLenScale-.0001 ;nota midi iDurIndx random 0, giLenDurs-.0001 ;duración elseif iDistrib == 1 then printf_i "... distribución lineal baja con %d unidades:\n", iPrint, iUnits printf_i "%s", iPrint, "SE PREFIEREN NOTAS MÁS BAJAS Y DURACIONES MÁS LARGAS\n" iScaleIndx linrnd_low 0, giLenScale-.0001, iUnits iDurIndx linrnd_low 0, giLenDurs-.0001, iUnits elseif iDistrib == 2 then printf_i "... distribución lineal con %d unidades:\n", iPrint, iUnits printf_i "%s", iPrint, "SE PREFIEREN NOTAS MÁS ALTAS Y DURACIONES MÁS CORTAS\n" iScaleIndx linrnd_high 0, giLenScale-.0001, iUnits iDurIndx linrnd_high 0, giLenDurs-.0001, iUnits else printf_i "... distribución triangular con %d unidades:\n", iPrint, iUnits printf_i "%s", iPrint, "SE PREFIEREN NOTAS Y DURACIONES MEDIAS\n" iScaleIndx trirnd 0, giLenScale-.0001, iUnits iDurIndx trirnd 0, giLenDurs-.0001, iUnits endif ;llamar al subinstrumento para ejecutar la nota iDur = giDurs[int(iDurIndx)] iPch = giScale[int(iScaleIndx)] event_i "i", "play", iStart, iDur, iPch ;incrementar el tiempo de inicio y el contador iStart += iDur iThisNote += 1 ;evitar imprimir a pantalla continuadamente iPrint = 0 enduntil ;restablecer la duración de este instrumento para hacer que todos los eventos sucedan p3 = iStart + 2 ;incrementar el índice de la secuencia giSeqIndx += 1 ;llamar al instrumento nuevamente si la secuencia no ha sido terminada if giSeqIndx < lenarray(giSequence) then event_i "i", "notes", p3, 1, iHowMany ;o salir de Csound else event_i "i", "exit", p3, 1 endif endin ;****INTRUMENTOS PARA INTERPRETAR LOS SONIDOS Y SALIR DE CSOUND**** instr play ;incrementar la duración en el rango aleatorio iDur random p3*2, p3*5 p3 = iDur ;obtener frecuencia iFreq = p4 ;generar nota con el algoritmo de karplus-strong aPluck pluck .2, iFreq, iFreq, 0, 1 aPluck linen aPluck, 0, p3, p3 ;filtro aFilter mode aPluck, iFreq, .1 ;mezclar aPluck y aFilter de acuerdo a la frecuencia ;(las notas altas se filtrarán más) aMix ntrpol aPluck, aFilter, iFreq, 65, 65*16 ;paneo también de acuerdo a la frecuencia ;(bajo = izquierda, alto = derecha) iPan = (iFreq-65) / (65*16) aL, aR pan2 aMix, iPan outs aL, aR endin instr exit exitnow endin </CsInstruments> <CsScore> i "notes" 0 1 23 ;establecer número de notas por instrumento aquí e 99999 ;posibilita ejecutar durante mucho tiempo (la salida de Csound será automática) </CsScore> </CsoundSynthesizer> ;ejemplo por joachim heintz
Hay muchas maneras en que un valor determinado dentro de una progresión de números aleatorios puede influir en el valor siguiente. Hay dos que se utilizan con frecuencia. Una cadena de Markov se basa en un número de estados posibles, y define una probabilidad diferente para cada uno de estos estados. Un paseo aleatorio considera al último estado como una posición en una rango o campo, y permite sólo ciertas desviaciones respecto de esta posición.
Un caso típico de una cadena de Markov en la música es el de una secuencia de ciertos tonos o notas. Para cada nota, la probabilidad de la siguiente nota se escribe en una tabla como esta:
Esto significa: la probabilidad de que se repita un elemento a es de 0,2; la probabilidad de que b siga después de a es 0,5; la probabilidad de que c sigua después de a es 0,3. La suma de todas las probabilidades debe, por convención, ser igual a 1. El siguiente ejemplo muestra el algoritmo básico que evalúa la primera línea de la tabla de Markov ilustrada anteriormente, en el caso en que el elemento anterior haya sido 'a'.
EXAMPLE 01D07_markov_basics.csd
<CsoundSynthesizer> <CsOptions> -ndm0 </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 0dbfs = 1 nchnls = 1 seed 0 instr 1 iLine[] array .2, .5, .3 iVal random 0, 1 iAccum = iLine[0] iIndex = 0 until iAccum >= iVal do iIndex += 1 iAccum += iLine[iIndex] enduntil printf_i "Número aleatorio = %.3f, siguiente elemento = %c!\n", 1, iVal, iIndex+97 endin </CsInstruments> <CsScore> r 10 i 1 0 0 </CsScore> </CsoundSynthesizer> ;ejemplo por joachim heintz
Las probabilidades son 0.2, 0.5 y 0.3. En primer lugar se genera un número aleatorio distribuido uniformemente entre 0 y 1. Un acumulador es establecido utilizando el primer elemento de la línea (en este caso 0.2). Luego es interrogado en cuanto a si es más grande que el número aleatorio. Si es así, se devuelve el índice, si no, se añade el segundo elemento (0,2 + 0,5 = 0,7), y el proceso se repite hasta que el acumulador sea mayor o igual al valor aleatorio. La salida del ejemplo debería mostrar algo como esto:
Número aleatorio = 0.850, siguiente elemento = c!
Número aleatorio = 0.010, siguiente elemento = a!
Número aleatorio = 0.805, siguiente elemento = c!
Número aleatorio = 0.696, siguiente elemento = b!
Número aleatorio = 0.626, siguiente elemento = b!
Número aleatorio = 0.476, siguiente elemento = b!
Número aleatorio = 0.420, siguiente elemento = b!
Número aleatorio = 0.627, siguiente elemento = b!
Número aleatorio = 0.065, siguiente elemento = a!
Número aleatorio = 0.782, siguiente elemento = c!
El siguiente ejemplo pone este algoritmo en un opcode definido por el usuario (UDO). Su entrada es una tabla de Markov en forma de un vector bidimensional, y la línea anterior como índice (empezando por el 0). Su salida es el elemento siguiente, también como índice. -- Hay dos cadenas de Markov en este ejemplo: siete alturas y tres duraciones. Ambas se definen en vectores bidimensionales: giProbNotes y giProbDurs. Ambas cadenas de Markov se ejecutan de forma independiente la una de la otra.
EXAMPLE 01D08_markov_music.csd
<CsoundSynthesizer> <CsOptions> -dnm128 -odac </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 0dbfs = 1 nchnls = 2 seed 0 ;****CÓDIGOS OPERATIVOS DEFINIDOS POR EL USUARIO PARA CADENAS DE MARKOV**** opcode Markov, i, i[][]i iMarkovTable[][], iPrevEl xin iRandom random 0, 1 iNextEl = 0 iAccum = iMarkovTable[iPrevEl][iNextEl] until iAccum >= iRandom do iNextEl += 1 iAccum += iMarkovTable[iPrevEl][iNextEl] enduntil xout iNextEl endop opcode Markovk, k, k[][]k kMarkovTable[][], kPrevEl xin kRandom random 0, 1 kNextEl = 0 kAccum = kMarkovTable[kPrevEl][kNextEl] until kAccum >= kRandom do kNextEl += 1 kAccum += kMarkovTable[kPrevEl][kNextEl] enduntil xout kNextEl endop ;****DEFINICIONES PARA LAS NOTAS**** ;notas como proporciones y una frecuencia base giNotes[] array 1, 9/8, 6/5, 5/4, 4/3, 3/2, 5/3 giBasFreq = 330 ;probabilidad de notas como una matriz de markov: ;primera -> sólo tercero y cuarto ;segunda -> cualquiera menos sí mismo ;tercera -> alta probabilidad de repeticiones ;cuarta -> idem ;quinta -> cualquiera menos tercero y cuarto ;sexta -> más que nada el septimo ;séptima -> más que nada el sexto giProbNotes[][] init 7, 7 giProbNotes array 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 0.2, 0.0, 0.2, 0.2, 0.2, 0.1, 0.1, 0.1, 0.1, 0.5, 0.1, 0.1, 0.1, 0.0, 0.0, 0.1, 0.1, 0.5, 0.1, 0.1, 0.1, 0.2, 0.2, 0.0, 0.0, 0.2, 0.2, 0.2, 0.1, 0.1, 0.0, 0.0, 0.1, 0.1, 0.6, 0.1, 0.1, 0.0, 0.0, 0.1, 0.6, 0.1 ;****DEFINICIONES PARA DURACIONES**** ;duraciones posibles gkDurs[] array 1, 1/2, 1/3 ;probabilidad de duraciones como matriz de markov: ;primera -> cualquiera ;segunda -> más que nada sí misma ;tercera -> más que nada la segunda gkProbDurs[][] init 3, 3 gkProbDurs array 1/3, 1/3, 1/3, 0.2, 0.6, 0.3, 0.1, 0.5, 0.4 ;****ESTABLECER PRIMERA ALTURA Y NOTA PARA EL PROCESO DE MARKOV**** giPrevNote init 1 gkPrevDur init 1 ;****INSTRUMENO PARA LAS DURACIONES**** instr trigger_note kTrig metro 1/gkDurs[gkPrevDur] if kTrig == 1 then event "i", "select_note", 0, 1 gkPrevDur Markovk gkProbDurs, gkPrevDur endif endin ;****INSTRUMENTO PARA LAS ALTURAS**** instr select_note ;choose next note according to markov matrix and previous note ;and write it to the global variable for (next) previous note
;elegir siguiente nota según la matriz de Markov y la nota previa
;y escribirla en la variable global como la (siguiente) nota anterior
giPrevNote Markov giProbNotes, giPrevNote ;llamar al instrumento para ejecutar esta nota event_i "i", "play_note", 0, 2, giPrevNote ;apagar este instrumento turnoff endin ;****INSTRUMENTO PARA EJECUTAR UNA NOTA**** instr play_note ;get note as index in ginotes array and calculate frequency
;obtener nota como índice en la matriz ginots y calcular la frecuencia
iNote = p4 iFreq = giBasFreq * giNotes[iNote] ;elección aleatoria para la cualidad del filtro mode y el paneo iQ random 10, 200 iPan random 0.1, .9 ;generar tono y volcar a la salida de audio aImp mpulse 1, p3 aOut mode aImp, iFreq, iQ aL, aR pan2 aOut, iPan outs aL, aR endin </CsInstruments> <CsScore> i "trigger_note" 0 100 </CsScore> </CsoundSynthesizer> ;ejemplo por joachim heintz
En el contexto del movimiento entre los valores aleatorios, la 'caminata' puede ser pensada como lo opuesto al 'salto'. Si se salta dentro de los límites A y B, sepuede terminar en cualquier lugar entre esos límites, pero si se camina entre A y B habrá una limitación impuesta por el alcance de los pasos -cada paso aplica una desviación con respecto al anterior. Si el rango de desviación es ligeramente tendiente hacia lo positivo (por caso, de -0,1 a 0,2), la trayectoria general de su caminata estará en la dirección positiva (pero los pasos individuales no estarán necesariamente en la dirección positiva). Si el rango de desviación se inclina hacia lo negativo (digamos de -0,2 a 0,1), entonces la caminata expresará una trayectoria generalmente negativa.
Una forma de implementar una caminata aleatoria podría ser tomando el estado actual, derivando una desviación aleatoria y luego derivando el siguiente estado mediante la adición de esta desviación al estado actual. El siguiente ejemplo muestra dos formas de hacer esto.
La caminata aleatoria de altura comienza en la altura 8 según la notación de octava. La desviación general de altura gkPitchDev se establece en 0.2, de modo que la siguiente altura podría ser de entre 7.8 y 8.2. Pero también hay una dirección de altura gkPitchDir que se establece como valor inicial en 0.1. Esto significa que el límite superior de la siguiente altura aleatoria es 8.3 en lugar de 8.2, por lo que la altura se moverá hacia arriba en un mayor número de pasos. Cuando se ha cruzado el límite superior giHighestPitch, la variable gkPitchDir cambia de 0.1 a -0.1, así que después de un número de pasos determinado, la altura se tornará más baja. Cada vez que un cambio de dirección como este ocurre, la consola informa de esto con un mensaje impreso en el terminal.
La densidad de las notas se define en término de notas por segundo, y se aplica como frecuencia al opcode metro en el instrumento 'walk'. La densidad más baja posible giLowestDens se establece en 1, la más alta en 8 notas por segundo, y la densidad inical giStartDens se establece en 3. La desviación aleatoria posible para la siguiente densidad se define en un rango que va de cero a uno: cero significa que no hay desviación en absoluto, uno significa que la siguiente densidad puede alterar la densidad actual en un rango que va de la mitad del valor actual hasta dos veces dicho valor. Por ejemplo, si la densidad actual es 4, con gkDensDev = 1 se obtendría una densidad de entre 2 y 8. La dirección de las densidades gkDensDir en esta caminata aleatoria sigue el mismo rango 0...1. Asumiendo que no se tiene ninguna desviación de las densidades en absoluto (gkDensDev = 0), gkDensDir = 0 producirá impulsos siempre a la misma velocidad, mientras que gkDensDir = 1 producirá un aumento muy rápido de velocidad. Al igual que en la caminata de altura, los parámetros de dirección alternan de más a menos, si el borde superior ha sido cruzado, y viceversa.
EXAMPLE 01D09_random_walk.csd
<CsoundSynthesizer> <CsOptions> -dnm128 -odac </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 0dbfs = 1 nchnls = 2 seed 1 ;cambiar a 0 para resultados siempre cambiantes ;****CONFIGURACIONES PARA LAS ALTURAS**** ;define the pitch street(?) in octave notation
;definir el rango de la altura en notación de octava
giLowestPitch = 7 giHighestPitch = 9 ;set pitch startpoint, deviation range and the first direction
;establecer la altura inicial, el rango de desviación y la primera dirección
giStartPitch = 8 gkPitchDev init 0.2 ;rango aleatorio para la próxima altura gkPitchDir init 0.1 ;positivo = en dirección superior ;****CONFIGURACIONES PARA LAS DENSIDADES**** ;define the maximum and minimum density (notes per second)
;definir la densidad máxima y mínima(notas por segundo)
giLowestDens = 1 giHighestDens = 8 ;set first density
;establecer la densidad inicial
giStartDens = 3 ;set possible deviation in range 0..1 ;0 = no deviation at all ;1 = possible deviation is between half and twice the current density
;establecer la posible desviación en el rango de 0..1
;0 = sin desviación
;1 = la desviación posible está entre la mitad y el doble de la densidad actual
gkDensDev init 0.5 ;set direction in the same range 0..1 ;(positive = more dense, shorter notes)
;establecer la dirección en el mismo rango 0..1
;(positivo = notas más densas, más cortas)
gkDensDir init 0.1 ;****INSTRUMENTO PARA LA CAMINATA ALEATORIA**** instr walk ;set initial values
;establecer los valores iniciales
kPitch init giStartPitch kDens init giStartDens ;trigger impulses according to density
;disparar impulsos de acuerdo a la densidad
kTrig metro kDens ;if the metro ticks
;si el metro marca un tick
if kTrig == 1 then ;1) play current note ;1) reproducir la nota actual
event "i", "play", 0, 1.5/kDens, kPitch ;2) calculate next pitch ;define boundaries according to direction ;2) calcular la altura siguiente
;definir los límites de acuerdo a la dirección
kLowPchBound = gkPitchDir < 0 ? -gkPitchDev+gkPitchDir : -gkPitchDev kHighPchBound = gkPitchDir > 0 ? gkPitchDev+gkPitchDir : gkPitchDev ;get random value in these boundaries
;obtener el valor aleatorio en estos límites
kPchRnd random kLowPchBound, kHighPchBound ;add to current pitch ;agregar a la altura actual
kPitch += kPchRnd ;change direction if maxima are crossed, and report ;cambiar la dirección si se cruza el máximo, e informar
if kPitch > giHighestPitch && gkPitchDir > 0 then gkPitchDir = -gkPitchDir printks " La altura alcanzó el máximo - yendo hacia abajo ahora.\n", 0 elseif kPitch < giLowestPitch && gkPitchDir < 0 then gkPitchDir = -gkPitchDir printks "La altura alcanzó el mínimo - yendo hacia arriba ahora.\n", 0 endif ;3) calculate next density (= metro frequency) ;define boundaries according to direction ;3) calcular la próxima densidad( = frecuencia del metro)
;definir los límites de acuerdo a la dirección
kLowDensBound = gkDensDir < 0 ? -gkDensDev+gkDensDir : -gkDensDev kHighDensBound = gkDensDir > 0 ? gkDensDev+gkDensDir : gkDensDev ;get random value in these boundaries ;obtener el valor aleatorio en estos límites
kDensRnd random kLowDensBound, kHighDensBound ;get multiplier (so that kDensRnd=1 yields to 2, and kDens=-1 to 1/2) ;obtener multiplicador (de modo que kDensRnd=1 produzca 2 y kDens=-1, 1/2)
kDensMult = 2 ^ kDensRnd ;multiply with current duration ;multiplicar con la duración actual
kDens *= kDensMult ;avoid too high values and too low values ;evitar los valores demasiado altos y demasiado bajos
kDens = kDens > giHighestDens*1.5 ? giHighestDens*1.5 : kDens kDens = kDens < giLowestDens/1.5 ? giLowestDens/1.5 : kDens ;change direction if maxima are crossed ;cambiar dirección si se cruza el máximo
if (kDens > giHighestDens && gkDensDir > 0) || (kDens < giLowestDens && gkDensDir < 0) then gkDensDir = -gkDensDir if kDens > giHighestDens then printks " La densidad tocó el borde superior - volviéndose menos denso ahora.\n", 0 else printks " La densidad tocó el borde inferior - volviéndose más denso ahora.\n", 0 endif endif endif endin ;****INSTRUMENTO PARA REPRODUCIR UNA NOTA**** instr play ;get note as octave and calculate frequency and panning ;obtener nota como octava y calcular la frecuencia y el paneo
iOct = p4 iFreq = cpsoct(iOct) iPan ntrpol 0, 1, iOct, giLowestPitch, giHighestPitch ;calculate mode filter quality according to duration ;calcular la calidad del modo del filtro de acuerdo a la duración
iQ ntrpol 10, 400, p3, .15, 1.5 ;generate tone and throw out
;generar tono y dar salida
aImp mpulse 1, p3 aMode mode aImp, iFreq, iQ aOut linen aMode, 0, p3, p3/4 aL, aR pan2 aOut, iPan outs aL, aR endin </CsInstruments> <CsScore> i "walk" 0 999 </CsScore> </CsoundSynthesizer> ;ejemplo por joachim heintz
La frecuencia relativa de aparición de una variable aleatoria puede ser descrita por una función de probabilidad (para variables aleatorias discretas) o por funciones de densidad (para variables aleatorias contínuas).
Cuando dos dados son lanzados simultáneamente, la suma x de sus números puede ser 2, 3, ... 12. La siguiente figura muestra la función de probabilidad p(x) de estos posibles resultados. p(x) es siempre menor o igual a 1. La suma de las probabilidades de todos los resultados posibles es 1.
Para las variables aleatorias continuas la probabilidad de obtener un valor x específico es 0. Sin embargo, la probabilidad de obtener un valor dentro de un cierto intervalo puede ser indicado por un área que corresponde a esta probabilidad. La función f(x) sobre estas áreas se denomina función de densidad. Respecto a la siguiente densidad, la probabilidad de obtener un número menor que 0 es 0, de obtener un número entre 0 y 0.5 es de 0.5, de obtener un número entre 0.5 y 1 es de 0.5, etc. Las funciones de densidad f(x) pueden alcanzar valores superiores a 1, pero el área bajo la función es 1.
Generando Números Aleatorios Con una Probabilidad o Densidad Dada
Csound proporciona opcodes para algunas densidades específicas, pero no hay medios para producir números aleatorios con funciones de probabilidad o densidad definidas por el usuario. Los opcodes rand_density y rand_probability (véase más adelante) generan números al azar con probabilidades o densidades dadas por tablas. Se realizan mediante el llamado método de muestreo de rechazo.
Muestreo de Rechazo
El principio del muestreo de rechazo se basa en -primero- generar números aleatorios uniformemente distribuidos en el rango requerido y luego aceptar estos valores de acuerdo a una función de densidad dada (o de otra forma rechazarlos). Veamos un ejemplo de este método usando la función de densidad mostrada en la siguiente figura. (Dado que el método de muestreo de rechazo sólo utiliza la "forma" de la función, el área bajo la función no tiene que ser 1). En primer lugar, generamos números aleatorios uniformemente distribuidos rnd1 en el intervalo [0, 1]. De éstos aceptamos una proporción correspondiente a f(rnd1). Por ejemplo, el valor 0,32 sólo será aceptado en la proporción de f(0,32) = 0.82. Realizamos esto mediante la generación de un nuevo número aleatorio rand2 entre 0 y 1 y aceptamos rnd1 sólo si rand2 < f(rnd1); de lo contrario lo rechazamos. (Ver Señales, Sistemas y Síntesis de Sonido6, capítulo 10.1.4.4)
EXAMPLE 01D10_Rejection_Sampling.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;ejemplo por martin neukom sr = 44100 ksmps = 10 nchnls = 1 0dbfs = 1 ; random number generator to a given density function
; kout random number; k_minimum,k_maximum,i_fn for a density function ; generador de númeror aleatorios para una función de densidad dada
; kout número aleatrorio; k_minimum,k_maximum,i_fn para una función de densidad
opcode rand_density, k, kki kmin,kmax,ifn xin loop: krnd1 random 0,1 krnd2 random 0,1 k2 table krnd1,ifn,1 if krnd2 > k2 kgoto loop xout kmin+krnd1*(kmax-kmin) endop ; random number generator to a given probability function ; kout random number ; in: i_nr number of possible values ; i_fn1 function for random values ; i_fn2 probability functionExponential: Generate a uniformly distributed number between 0 and 1 and take its natural logarithm. ; generador de números aleatorios para una función de probabilidad dada
; kout número aleatorio
; in: i_nr número de valores posibles
; i_fn1 función para valores aleatorios
; i_fn2 función de probabilida exponencial: Genera un número uniformemente distribuido entre 0 y 1 y toma su logaritmo natural.
opcode rand_probability, k, iii inr,ifn1,ifn2 xin loop: krnd1 random 0,inr krnd2 random 0,1 k2 table int(krnd1),ifn2,0 if krnd2 > k2 kgoto loop kout table krnd1,ifn1,0 xout kout endop instr 1 krnd rand_density 400,800,2 aout poscil .1,krnd,1 out aout endin instr 2 krnd rand_probability p4,p5,p6 aout poscil .1,krnd,1 out aout endin </CsInstruments> <CsScore> ;sine
;seno
f1 0 32768 10 1 ;density function
;función de densidad
f2 0 1024 6 1 112 0 800 0 112 1 ;random values and their relative probability (two dice)
;valores aleatorios y su probabilidad relativa (dos dados)
f3 0 16 -2 2 3 4 5 6 7 8 9 10 11 12 f4 0 16 2 1 2 3 4 5 6 5 4 3 2 1 ;random values and their relative probability
;valores aleatorios y su probabilidad relativa
f5 0 8 -2 400 500 600 800 f6 0 8 2 .3 .8 .3 .1 i1 0 10 i2 0 10 4 5 6 </CsScore> </CsoundSynthesizer>
Caminata Aleatoria
En una serie de números aleatorios los números particulares son independientes el uno del otro. Los tipos parámetro (figura de la izquierda) o caminos en la habitación (trayectoria de dos dimensiones en la figura de la derecha) creados por números aleatorios se mueven de manera descontrolada.
Ejemplo 1
Table[RandomReal[{-1, 1}], {100}];
Obtenemos un camino más suave, denominada caminata aleatoria, mediante la adición en cada paso de tiempo de un número aleatorio r a la posición real de x (x += r).
Ejemplo 2
x = 0; caminata = Table[x += RandomReal[{-.2, .2}], {300}];
El camino se hace aún más suave mediante la adición de un número aleatorio r a la velocidad real v.
v += r
x += v
El camino puede estar limitado a un área (figura de la derecha) mediante la inversión de la velocidad si el camino excede los límites (min, max):
vif(x < min || x > max) v *= -1
El movimiento puede ser amortiguado mediante la disminución de la velocidad en cada paso de tiempo por un pequeño factor d
v *= (1-d)
Ejemplo 3
x = 0; v = 0; caminata = Table[x += v += RandomReal[{-.01, .01}], {300}];
El camino se vuelve de nuevo más suave mediante la adición de un número aleatorio r a la aceleración corriente a, el cambio de la aceleración, etc.
a += r
v += a
x += v
Ejemplo 4
x = 0; v = 0; a = 0;
Table[x += v += a += RandomReal[{-.0001, .0001}], {300}];
(Ver Martin Neukom, Señales, Sistemas y Síntesis de Sonido, capítulo 10.2.3.2)
EXAMPLE 01D11_Random_Walk2.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;ejemplo por martin neukom sr = 44100 ksmps = 128 nchnls = 1 0dbfs = 1 ; random frequency
; frecuencia aleatoria
instr 1 kx random -p6, p6 kfreq = p5*2^kx aout oscil p4, kfreq, 1 out aout endin ; random change of frequency
; variación aleatoria de la frecuencia
instr 2 kx init .5 kfreq = p5*2^kx kv random -p6, p6 kv = kv*(1 - p7) kx = kx + kv aout oscil p4, kfreq, 1 out aout endin ; random change of change of frequency
; variacion aleatoria de la variación de la frecuencia
instr 3 kv init 0 kx init .5 kfreq = p5*2^kx ka random -p7, p7 kv = kv + ka kv = kv*(1 - p8) kx = kx + kv kv = (kx < -p6 || kx > p6?-kv : kv) aout oscili p4, kfreq, 1 out aout endin </CsInstruments> <CsScore> f1 0 32768 10 1 ; i1 p4 p5 p6 ; i2 p4 p5 p6 p7 ; amp c_fr rand damp ; i2 0 20 .1 600 0.01 0.001 ; amp c_fr d_fr rand damp ; amp c_fr rand ; i1 0 20 .1 600 0.5 ; i3 p4 p5 p6 p7 p8 i3 0 20 .1 600 1 0.001 0.001 </CsScore> </CsoundSynthesizer>
Csound tiene una serie de opcodes y rutinas GEN para la creación de diversas funciones y distribuciones aleatorias. Tal vez el más simple de ellos es random que simplemente genera un valor aleatorio dentro de un límite mínimo y máximo definido por el usuario y en tiempo-i, ciclo-k o ciclo-a de acuerdo al tipo de variable de su salida (ubicado a la izquierda del opcode):
ires random imin, imax kres random kmin, kmax ares random kmin, kmax
Los valores se generan de acuerdo con una distribución aleatoria uniforme, lo que significa que cualquier valor dentro de los límites tiene la misma probabilidad de ocurrencia. Las distribuciones no uniformes en las que ciertos valores tienen mayor probabilidad de ocurrencia por sobre otros son a menudo más útiles y musicales. Para estos propósitos, Csound incluye los opcodes generadores de números aleatorios betarand, bexprand, cauchy, exprand, gauss, linrand, pcauchy, poisson, trirand, unirand y weibull. Las distribuciones generadas a partir de varios de estos opcodes se ilustran a continuación:
Además de estos denominados 'generadores de ruido de clase x' Csound proporciona generadores de funciones aleatorias, proveyendo valores que cambian con el tiempo en diversas formas.
randomh genera nuevos números aleatorios a un ritmo definido por el usuario. El valor anterior se mantiene hasta que un nuevo valor sea generado, y luego la salida inmediatamente asume ese valor.
La instrucción:
kmin = -1 kmax = 1 kfreq = 2 kout randomh kmin,kmax,kfreq
producirá y generará una salida similar a:
randomi es una versión de randomh con interpolación. En lugar de "saltar" hacia los nuevos valores que se generan, randomi produce una interpolación lineal hacia el nuevo valor, alcanzándolo justo en el momento en que un próximo valor se genera. Al remplazar randomh con randomi en el código de ejemplo presentado más arriba se logrará el siguiente resultado:
En la práctica, los cambios angulares aleatorios en la dirección a medida que se generan nuevos valores aleatorios podrían ser audibles dependiendo de cómo se use. rsplsine (o más simple aún, jsplsine) permite especificar no sólo una sola frecuencia, sino una frecuencia mínima y una frecuencia máxima, y la función resultante es una spline suave entre los valores mínimo y máximo y estas frecuencias mínimas y máximas. La siguiente entrada:
kmin = -0.95 kmax = 0.95 kminfrq = 1 kmaxfrq = 4 asig rspline kmin, kmax, kminfrq, kmaxfrq
generará algo similar a esto:
Tenemos que tener cuidado con lo que hacemos con la salida de rspline ya que ésta puede superar los límites establecidos por kmin y kmax. Los valores mínimo y máximo se pueden establecer de forma conservadora o el opcode limit podría ser utilizado para evitar valores fuera de rango que podrían causar problemas.
El ejemplo siguiente utiliza rspline para humanizar un sintetizador simple. Se toca una melodía corta, primero sin ninguna humanización y luego con humanización. La variación aleatoria de rspline se añade a la amplitud y al tono de cada nota además de un desplazamiento aleatorio sobre el tiempo-i.
EXAMPLE 01D12_humanising.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 seed 0 giWave ftgen 0, 0, 2^10, 10, 1,0,1/4,0,1/16,0,1/64,0,1/256,0,1/1024 instr 1 ; an instrument with no 'humanising' inote = p4 aEnv linen 0.1,0.01,p3,0.01 aSig poscil aEnv,cpsmidinn(inote),giWave outs aSig,aSig endin instr 2 ; an instrument with 'humanising' inote = p4 ; generate some i-time 'static' random paramters iRndAmp random -3,3 ; amp. will be offset by a random number of decibels iRndNte random -5,5 ; note will be offset by a random number of cents ; generate some k-rate random functions kAmpWob rspline -1,1,1,10 ; amplitude 'wobble' (in decibels) kNteWob rspline -5,5,0.3,10 ; note 'wobble' (in cents) ; calculate final note function (in CPS) kcps = cpsmidinn(inote+(iRndNte*0.01)+(kNteWob*0.01)) ; amplitude envelope (randomisation of attack time) aEnv linen 0.1*ampdb(iRndAmp+kAmpWob),0.01+rnd(0.03),p3,0.01 aSig poscil aEnv,kcps,giWave outs aSig,aSig endin </CsInstruments> <CsScore> t 0 80 #define SCORE(i) # i $i 0 1 60 i . + 2.5 69 i . + 0.5 67 i . + 0.5 65 i . + 0.5 64 i . + 3 62 i . + 1 62 i . + 2.5 70 i . + 0.5 69 i . + 0.5 67 i . + 0.5 65 i . + 3 64 # $SCORE(1) ; play melody without humanising b 17 $SCORE(2) ; play melody with humanising e </CsScore> </CsoundSynthesizer> ;example by Iain McCurdy
El ejemplo final implementa un generador simple de notas algorítmicas. Hace uso de GEN17 para generar histogramas que definen las probabilidades de ciertas notas y ciertas brechas rítmicas que ocurren.
EXAMPLE 01D13_simple_algorithmic_note_generator.csd
<CsoundSynthesizer> <CsOptions> -odac -dm0 </CsOptions> <CsInstruments> sr = 44100 ksmps = 32 nchnls = 1 0dbfs = 1 giNotes ftgen 0,0,-100,-17,0,48, 15,53, 30,55, 40,60, 50,63, 60,65, 79,67, 85,70, 90,72, 96,75 giDurs ftgen 0,0,-100,-17,0,2, 30,0.5, 75,1, 90,1.5 instr 1 kDur init 0.5 ; initial rhythmic duration kTrig metro 2/kDur ; metronome freq. 2 times inverse of duration kNdx trandom kTrig,0,1 ; create a random index upon each metro 'click' kDur table kNdx,giDurs,1 ; read a note duration value schedkwhen kTrig,0,0,2,0,1 ; trigger a note! endin instr 2 iNote table rnd(1),giNotes,1 ; read a random value from the function table aEnv linsegr 0, 0.005, 1, p3-0.105, 1, 0.1, 0 ; amplitude envelope iPlk random 0.1, 0.3 ; point at which to pluck the string iDtn random -0.05, 0.05 ; random detune aSig wgpluck2 0.98, 0.2, cpsmidinn(iNote+iDtn), iPlk, 0.06 out aSig * aEnv endin </CsInstruments> <CsScore> i 1 0 300 ; start 3 long notes close after one another i 1 0.01 300 i 1 0.02 300 e </CsScore> </CsoundSynthesizer> ;example by Iain McCurdy
There has been error in communication with Booktype server. Not sure right now where is the problem.
You should refresh this page.