Clase 5

// Taller de SuperCollider Básico

BUAP

 

Sesión 5

  07/11/09

   

 

*Tdefs

*ifs y relojes

*Iteraciones

*Switches

*Buffers

*Ejercicios

 

Baja esta clase en los formatos PDF y docx

 

//Tdefs

 

.do

 

Primero necesitamos conocer una herramienta que nos sirve como núcleo de nuestro Tdef. Estamos hablando del n.do. Veamos un ejemplo:

 

“gggggg9g9g9g”.postln

 

3.do{ “ggggggggggggggggg9999ggg999ggg999gg”.postln}

 

Intuitivamente podemos pensar que la línea anterior escribe 3 veces el string “ggggggggggggggggg9999ggg999ggg999gg”. En realidad así es. Entonces definamos .do como un método que nos hace n veces lo que le pongamos dentro de las llaves { }. Para hacer más interesante la cosa agreguemos el mensaje .scramble que desordena una cadena de caracteres o los elementos de un array:

 

[0,1,2,3,4,5];

[0,1,2,3,4,5].scramble


“ggggggggggggggggg9999ggg999ggg999gg”.postln


“ggggggggggggggggg9999999999999999”.scramble.postln

 

Ahora con el .do


500.do{“ggggggggggggggggg9999ggg999ggg999gg”.scramble.postln}


50.do{ [0,1,2,3,4,5].scramble.postln}

 

Podemos poner mas de una acción dentro de la función. Sólo hay que escribir punto y coma ; entre cada acción distinta.

 

50.do{ 

“ggggggggggggggggg9999ggg999ggg999gg”.scramble.postln;

[0,1,2,3,4,5].scramble.postln;

[1,2,3,4,5,6].choose.postln;

}

 

Podemos poner cualquier cosa dentro de las llaves del .do. Pongamos un oscilador:

 

{SinOsc.ar(rrand(300,5000),0,1/10)}.play

 

3.do{{SinOsc.ar(rrand(300,5000).postln,0,1/10)}.play}

 

O podemos llamar a un Synth. Primero hagamos el SynthDef y luego lo llamamos dentro de un .do.

 

SynthDef(\hacer, {|freq=400| 

Out.ar(0,SinOsc.ar(freq,0,1/10)*EnvGen.kr(Env.perc(0.01,5),doneAction:2))}

).send(s)

 

 

5.do{Synth(\hacer, [\freq, rrand(300,5000).postln])}

 

10.do{Synth(\hacer, [\freq, rrand(300,5000)])}

 

 

Otro ejemplo de cómo usar el .do lo tenemos dentro de un SynthDef con reverb:

 

(

SynthDef(\rev, {|gate=1, pulso=1|

var sen, env;

sen=Impulse.ar(pulso);

10.do{sen=AllpassN.ar(sen, 0.1, rrand(0.03,0.04), 1.5)};

env=EnvGen.kr(Env.asr(0,1,3),gate, doneAction:2);

Out.ar(0,sen*env);

}).send(s);

)

 

r=Synth(\rev)

r.set(\pulso,10)

r.set(\gate, 0)

 

Tdef, que es?, para qué sirve?

 

1-Tdef es el nombre que se le da a la estructura que nos permite generar loops o repeticiones.

2-Es de gran utilidad para la creación de patrones rítmicos o para modificar, de manera automática, los argumentos de nuestros Synths.

3-Los Tdef funcionan por medio de un reloj al que le podemos manipular la velocidad.

4-Los Tdef pueden modificarse sin necesidad de ser apagados.

 

Como vimos en los ejemplos anteriores el .do realiza n veces una acción que está dentro de las llaves de función {} y lo hace inmediatamente. Pero que pasa si queremos que nos lo haga con una pausa entre cada vez? Para eso necesitamos recurrir a otra de las estructuras o Templates que el SuperCollider nos brinda: Tdef.

Tdef viene de las palabras Task definition y es similar en su construcción al SynthDef. Veamos cómo se construye y para qué sirven cada una de sus partes:

 

(

Tdef(\x, { // Escribimos Tdef, abrimos paréntesis y ponemos un string o symbol con el nombre con el que queremos identificar el Tdef.  // Ponemos coma y abrimos una llave que englobará todo el contenido de nuestro Tdef.

50.do{                // Después ponemos un .do como hemos hecho anteriormente.

  [0,1,2,3,4].scramble.postln;  //ponemos una acción que sera ejecutada 50 veces

  0.05.wait;         // Añadimos otra acción mas que será un número con el mensaje .wait, este número representa el tiempo en segundos que esperará antes de volver a hacer la acción.

  } 

});  // Cerramos la llave englobadora del Tdef y el paréntesis.

)

 

Después, para echar a andar el Tdef lo hacemos refiriéndonos al Tdef por su nombre  con el mensaje .play como se muestra a continuación: 

 

Tdef(\x).play

Tdef(\x).stop

 

Para detener el Tdef usamos el mensaje .stop. 

 

Si queremos que el Tdef tenga un .do con un número infinito de repeticiones utilizamos la palabra inf en vez de un número finito:

 

(

Tdef(\x, {

inf.do{ 

“ggggg_____ggggsupercollider rules!gggggg****gg9999ggg999ggg999gg”.scramble.postln; 

0.15.wait

})

)

 

Tdef(\x).play

Tdef(\x).stop

 

 

Es MUY IMPORTANTE  que cuando usemos inf.do nos aseguremos de escribir un .wait dentro por que si no, le estaremos pidiendo a SuperCollider que haga un número infinito de acciones en una cantidad infinitamente pequeña de tiempo.

 

Un sinónimo de inf.do es loop.

(

Tdef(\x, {

loop{ 

“ggggggggggggggggg9999ggg999ggg999gg”.scramble.postln; 

0.05.yield; 

});

)

un sinonimo de .wait es .yield 

 

Tdef(\x).play

Tdef(\x).stop

 

Dentro de un .do podemos poner otros .do en lo que llamamos un arreglo anidado.

 

(

1.do{

     4.do{ “———////———————-“.postln;};

     10.do{“======;;;;;=======”.postln;};

     5.do{“*******////#######”.postln;}

     }

     )

 

Observemos cómo se comporta el arreglo anidado dentro de un Tdef con inf.do. Nótese que cada .do tiene su propio .wait.

 

(

Tdef(\x, {

   inf.do{

           4.do{

             “———////———————-“.scramble.postln; 

            0.25.wait;

            };

          10.do{

            “======;;;;;=======”.scramble.postln;

            0.05.wait;

            };

           5.do{

           “*******////#######”.scramble.postln; 

           0.15.wait;

           }

           }

          });

)

 

Tdef(\x).play

Tdef(\x).stop

 

Ahora viene lo bueno. Podemos llamar a un Synth dentro de un Tdef para que no tengamos que estar picándole como locos con el mouse.

 

(

SynthDef(\uno,{|out=0|

    var sen, env;

        sen=SinOsc.ar(820,0,0.8);

           env=EnvGen.kr(Env.perc(0,0.1),doneAction:2);

               Out.ar(out,sen*env)

               }).send(s)

)

 

Synth(\uno)

 

Este Tdef dispara el Synth(\uno) dos veces por segundo o cada 0.5 segundos.

 

(

Tdef(\x,{

     inf.do{

      Synth(\uno);

               0.5.wait;

               }

        })

)

 

Tdef(\x).play 

Tdef(\x).stop

 

Probablemente se habrán dado cuenta de que al darle play al Tdef este comienza a funcionar con una pequeña latencia. Para evitar esto escribimos el mensaje .quant . Ejemplo:

 

Tdef(\x).quant_(0).play

Tdef(\x).stop

 

Como pueden ver, el mensaje .quant lleva escrito un argumento que en este caso es 0 indicando que no haya latencia el comenzar el Tdef. Notar el guión bajo antes de los paréntesis para el argumento.

 

Otro ejemplo sencillo incorporando un XLine dentro del Synth. El XLine hace una línea de números con tres argumentos:

(punto de partida, punto final, tiempo que se tarda en ir de un punto a otro)

 

(

SynthDef(\cuatro2,{|out=0,gate=1,frecuencia=100,mul=0.6|

    var sen, env;

       sen=Saw.ar(XLine.kr(12200,120,0.13),mul)!2;

          env=EnvGen.kr(Env.perc(0,0.15),gate,doneAction:2);

              Out.ar(out,sen*env)

              }).send(s)

              )

Synth(\cuatro2)

 

(

Tdef(\gh,{

     inf.do{

      Synth(\cuatro2);

             0.58.wait;

             }}

             )

            )

 

 Tdef(\gh).play

 Tdef(\gh).stop

                    

Hagamos otro Synth con la capacidad de cambiar su frecuencia desde afuera con un argumento “frecuencia”.

 

(

SynthDef(\dos,{|out=0,frecuencia=100|

    var sen, env;

       sen=SinOsc.ar(frecuencia,0,0.8)!2;

            env=EnvGen.kr(Env.perc(0,0.1),doneAction:2);

              Out.ar(out,sen*env)

              }).send(s)

)

 

 

Este Tdef es como un LFNoise0.kr modulando a un SinOsc.ar cambiando su frecuencia cada 0.18 seg.

 

(

Tdef(\x1,{

       inf.do{

          Synth(\dos,[\frecuencia,rrand(100,4000)]); 

                 0.18.wait

                 }

         })

)

 

Tdef(\x1).play

Tdef(\x1).stop

 

///////////////////////////////////////////////

 

Ahora fíjense cómo se comporta un Tdef con .do anidados disparando Synths relacionados con strings que se escriben en la post window.

 

(

SynthDef(\tres,{|out=0,frecuencia=100,mul=0.8|

      var sen, env;

                sen=SinOsc.ar(frecuencia,0,mul);

                env=EnvGen.kr(Env.perc(0,0.1),doneAction:2);

                Out.ar(out,sen*env)

                }).send(s)

)

(

Tdef(\x2,{

     inf.do{

     3.do{

      Synth(\tres,[\frecuencia,rrand(200,300),\mul,rrand(0,0.8)]);

         “———////———————-“.scramble.postln;      0.5.wait;

         };

     3.do{

      Synth(\tres,[\frecuencia,1300,\mul,rrand(0,0.8)]);

             “======;;;;;=======”.scramble.postln;

          0.07.wait;

          };

     5.do{

          Synth(\tres,[\frecuencia,rrand(2700,3500),\mul,rrand(0,0.8)]);

             “***////##”.scramble.postln;     0.5.wait;

             }

 

                }

                })

)

 

Tdef(\x2).play

Tdef(\x2).stop

////////////////////////////////////////////////

 

Lo mismo pero sólo los Synth

 

(

SynthDef(\cuatro,{|out=0,gate=1,frecuencia=100,mul=0.8|

                  var sen, env;

                  sen=Saw.ar(frecuencia,mul)!2;

                  env=EnvGen.kr(Env.perc(0.06,0.15),gate,doneAction:2);

                  Out.ar(out,sen*env)

                  }).send(s)

)

(

Tdef(\x2,{

     inf.do{

     3.do{

      Synth(\cuatro,[\frecuencia,rrand(200,3000),\mul,rrand(0,0.4)]);               0.1.wait;

      };

                  12.do{

                        Synth(\cuatro,[\frecuencia,130,\mul,rrand(0,0.4)]);

                        0.06.wait;

                        };

                  4.do{

                       Synth(\cuatro,[\frecuencia,rrand(270,350),\mul,rrand(0,0.4)]);                 0.17.wait;

                       }

                }

         })

)

 

Tdef(\x2).play

Tdef(\x2).stop

 

(

Tdef(\x2,{

        inf.do{

                 3.do{

                     Synth(\cuatro,[\frecuencia,4000,\mul,rrand(0,0.4)]);

                     0.071.wait;

                     };

                 8.do{

                      Synth(\cuatro,[\frecuencia,1300,\mul,rrand(0,0.4)]);

                      0.057.wait;

                      };

                 4.do{

                      Synth(\cuatro,[\frecuencia,345,\mul,rrand(0,0.4)]);

                      0.17.wait;

                      };

                 14.do{

                       Synth(\cuatro,[\frecuencia,45,\mul,rrand(0,0.4)]);

                       0.07.wait;

                       }

                 }

           })

)

 

 

Tdef(\x2).play

Tdef(\x2).stop

 

 

////////////////////////////////////////////////

***********************************************

////////////////////////////////////////////////

               

Hasta ahora hemos trabajado con Tdefs que disparan y controlan los argumentos de Synths con envolventes percusivas. Esto implica que nuestros Tdefs tienen que disparar los Synths. Si queremos usar un Tdef para controlar un Synth con envolvente del tipo ASR entonces tenemos que disparar el Synth sólo una vez al principio y luego el Tdef modificará los argumentos de nuestro Synth mientras esté prendido. Para detenerlo hay que apagar el Tdef y luego el SynthDef.

 

 

(

SynthDef(\cinco,{|out=0,frecuencia=100,cutoff,amp=0.3,gate=1|

     var sen,env;

       sen=Saw.ar(frecuencia,amp)!2;

           env=EnvGen.kr(Env.asr(0.3,1,2),gate,doneAction:2);

            Out.ar(out,sen*env)

            }).send(s)

            )

a=Synth(\cinco)

a.set(\gate,0)

 

 

Fijense cómo podemos prender el Synth desde el Tdef si lo colocamos antes del .do

(

Tdef(\x3, {  a=Synth(\cinco); 

     inf.do{

     a.set(\frecuencia,rrand(1000,120));

     0.3.wait

                 }

          })

)

 

Tdef(\x3).play

Tdef(\x3).stop;a.set(\gate,0)

a.set(\gate,0) // Hay que apagar el Synth despues del Tdef si ya no queramos usarlo.

 

/////////////////////////////////////////////////////////

(

SynthDef(\cinco,{|out=0,frecuencia=100,cutoff=300,amp=0.3,gate=1|

     var sen,env,filtro;

       sen=Saw.ar(frecuencia,amp)!2;

         filtro=RLPF.ar(sen,cutoff,0.9);

           env=EnvGen.kr(Env.asr(0.3,1,2),gate,doneAction:2);

            Out.ar(out,filtro*env)

            }).send(s)

            )

d=Synth(\cinco)

d.set(\gate,0)

 

En este ejemplo prendemos el Synth desde afuera y antes de prender el Tdef.

 

a=Synth(\cinco);

 

(

Tdef(\x3,{

     inf.do{

     a.set(\frecuencia,rrand(100,220),\cutoff,8000);

     0.1.wait

                 }

         }

    )

)

 

Tdef(\x3).play

Tdef(\x3).stop;

a.set(\gate,0)

 

 

Observen qué pasa si prendemos un Tdef que modifica los argumentos de un Synth que aún no ha sido prendido.

 

(

Tdef(\x3,{

     inf.do{

     a.set(\frecuencia,rrand(200,420),\cutoff,[12000,8400,9600,7500,3500,6700,6000].choose);

     0.1.wait;

                 }

          }

    )

)

 

 

Tdef(\x3).play

 

Nos dice que no encuentra el Synth : FAILURE /n_set Node not found. 

 

Prendamos el Synth y la falla desaparece.

 

a=Synth(\cinco); 

Tdef(\x3).stop;

a.set(\gate,0)

 

 

A continuación se muestran varios ejemplos de la mancuerna TdefSynthDef

 

(

Tdef(\x3,{

     inf.do{a.set(\frecuencia,[60,62,64,65,67,69,71].choose,\cutoff,[200,400,600,1400,1500,2700,3000].choose);0.1.wait

     }}

    )

    )   

 

a=Synth(\cinco);Tdef(\x3).play

Tdef(\x3).stop;a.set(\gate,0)

 

 

(

Tdef(\x3,{

     inf.do{

     3.do{a.set(\frecuencia,[60.3453,62.204,64.32333,65.5324,67.209,69.42,71].choose,\cutoff,[2300,2400,600,3400,1500,2700,3000].choose);0.21.wait};

     4.do{a.set(\frecuencia,[120,632,614,65,67,269,741].choose,\cutoff,[14200,4040,6500,8400,5500,6700,3000].choose);0.1.wait};

     12.do{a.set(\frecuencia,[610,622,64,635,367,69,71].choose,\cutoff,[2200,4400,6600,14040,1500,2700,3000].choose);0.071.wait};

     3.do{a.set(\frecuencia,[640,62,64,665,67,639,71].choose,\cutoff,[12200,400,600,1400,15300,2700,3000].choose);0.11.wait}

     }}

    )

    )  

 

a=Synth(\cinco);

Tdef(\x3).play

Tdef(\x3).stop;

a.set(\gate,0)

 

///////////////////////////////////////////////////////////////////

(

SynthDef(\seis,{|out=0,frecuencia=60,cutoff=300,amp=0.21,gate=1|

     var sen,env,filtro;

       sen=Saw.ar(frecuencia,amp)!2;

         filtro=RLPF.ar(sen,MouseX.kr(200,15000),0.9);

           env=EnvGen.kr(Env.asr(0.3,1,2),gate,doneAction:2);

            Out.ar(out,filtro*env)

            }).send(s)

            )

 

b=Synth(\seis);

b.set(\gate,0)

Aqui vemos como el número que antecede al .do puede ser aleatorio y se define de modo diferente en cada vuelta. Utilizamos un +1 en cada .do para evitar el 0.

 

(

Tdef(\x4,{

     inf.do{

     1+(10.rand).do{b.set(\frecuencia,[60,65].choose);0.19.wait};

     1+(5.rand).do{b.set(\frecuencia,[66,67].choose*4);0.19.wait};

     1+(7.rand).do{b.set(\frecuencia,[61,65].choose*16);0.19.wait}

 

     }}

    )

    )

Tdef(\x4).play

Tdef(\x4).stop;

b.set(\gate,0)


////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

Ahora veamos cómo podemos construir una batería acompañando a un instrumento melódico por medio de Tdefs. Primero hacemos nuestros SynthDefs para cada elemento de la batería como bombo, tarola y hi-hat y también el SynthDef para nuestro instrumento melódico que aqui tiene el revelador nombre de “seis”.

 

(

(

SynthDef(\bombo,{|out=0,gate=1,frecuencia=60|

    var sen, env;

       sen=Pan2.ar(SinOsc.ar(frecuencia,pi*0.25,0.8),0,0.9);

          env=EnvGen.kr(Env.perc(0,0.13),gate,doneAction:2);

              Out.ar(out,sen*env)

              }).send(s)

              );

 

 

(

SynthDef(\tarola,{|out=0,gate=1,frecuencia=660|

    var sen, env,ruido;

    ruido=LPF.ar(WhiteNoise.ar(0.34),XLine.kr(12000,200,0.2),0.9);

       sen=Pan2.ar(SinOsc.ar(frecuencia,0,0.114)+ruido,0,0.8);

          env=EnvGen.kr(Env.perc(0,0.153),gate,doneAction:2);

              Out.ar(out,sen*env)

              }).send(s)

              );

 

 

(

SynthDef(\hi,{|out=0,gate=1|

   var sen, env;

      sen=HPF.ar(WhiteNoise.ar(0.5),2000,0.9);

      env=EnvGen.kr(Env.perc(0,0.13),gate,doneAction:2);

      Out.ar(out,Pan2.ar(sen*env,0,0.5))

      }).send(s)

      )

     ;

 

(

SynthDef(\seis,{|out=0,frecuencia=60,cutoff=300,amp=0.21,gate=1|

     var sen,env,filtro;

       sen=Saw.ar(frecuencia,amp);

         filtro=RLPF.ar(sen,MouseX.kr(200,15000),0.9);

           env=EnvGen.kr(Env.asr(0.3,1,2),gate,doneAction:2);

            Out.ar(out,filtro*env)

            }).send(s)

            )

 

            )

 

Como ven los Synth de la batería tienen una envolvente percusiva y el de la melodía tiene envolvente ASR.

 

 

Prendemos el Synth “seis”

 

 

 

Vámonos!!!

 

(

b=Synth(\seis);

(

Tdef(\bat,{

     inf.do{

     2.do{Synth(\bombo);0.19.wait};

     1.do{Synth(\tarola);(0.19*2).wait}

          }}

)

);

Tdef(\bat).play;

(

Tdef(\hihat,{

     inf.do{Synth(\hi);0.19.wait

          }}

             )

             ); 

Tdef(\hihat).play;

(

Tdef(\x4,{

     inf.do{

     1+(10.rand).do{b.set(\frecuencia,[60,65].choose);0.19.wait};

     1+(5.rand).do{b.set(\frecuencia,[66,67].choose);0.19.wait};

     1+(7.rand).do{b.set(\frecuencia,[61,65].choose);0.19.wait}

 

     }}

    )

    );

    Tdef(\x4).play

)            

 

 

// Bueno, ya.

 

Tdef(\bat).stop;Tdef(\hihat).stop;Tdef(\x4).stop;b.set(\gate,0)

 

///////////////

grabarlo!

///////////////

          

 

 

//ifs y relojes

 

Con el mensaje if podemos condicionar la ejecución de funciones. Este mensaje recibe una expresión que al ser evaluada debe regresar los valores true o false (valores booleanos). 

 

Si la expresión es cierta, se ejecutará la función que se encuentra en el segundo argumento. Si es falsa, se ejecutará la función que se encuentra en el tercer argumento.

 

El mensaje if  nos regresará el resultado de la función que haya sido evaluada. Si no hay ninguna función para ser evaluada en el caso de que la expresión haya sido falsa, el resultado del mensaje será nil.

 

if ( expression, { trueFunction }, { falseFunction } )

 

 

(

if(

2.0.rand.postln > 1 , // la expresión booleana

 

{“la expresion fue cierta” }, // la función a evaluar si

// la expresión es cierta

{“la expresion fue falsa”};

// la función a evaluar si

// la expresión es falsa

  )

)

 

 

// si la expresión es falsa y no hay ninguna función que sea evaluada,

// if nos dará nil

 

if ( 1000.rand > 900, {“mayor que 900”})

 

 

// evalúa el siguiente código varias veces

 

(

{

var opciones;

 

opciones = [ \aditiva, \substractiva ];

 

if ( opciones.choose == \aditiva,

{“aditiva”.postln;

a = Mix.fill(10, {SinOsc.ar(exprand(30.0, 300.0), 0, 0.07)}) },

{“substractiva”.postln;

a = Mix.fill(10, {BPF.ar(BrownNoise.ar, f = exprand(300.0, 3000.0),

0.5/f, 4)})}

);

a ! 2 }.scope

 

)

 

// evalúa las siguientes líneas

Expresiones booleanas

 

1 < 2 // menor que

 

1 > 2 // mayor que

 

1 == 2 // igual que (Atención: es muy común cometer el error de 

// sólo poner un signo de igual ( = ) en vez de dos ( == )

1 != 2 // distinto que

 

1 <= 2 // menor o igual que

 

1 >= 2 // mayor o igual que

 

1.odd // ¿es un número impar?

 

1.even // ¿es un número par?

 

///////////////////////////////////////////////////////////////////////////

 

Evaluando algunas funciones

 

Si quisieramos evaluar una función es necesario seguir algunos pasos.

primero construimos la función que queremos evaluar, por ejemplo:

 

x={|n| if(n>10,{“es mayor a 10”},{“es menor a 10”})} //construimos la funcion x en donde n (argumento) es nuestra incognita;

        //compilamos el codigo

        //collider nos contesta a Function

para darle valores a la funcion x es necesario la siguiente linea de codigo:

 

x.value(1) //en donde dentro de los parentesis asignamos el valor que tomara n

 

Supongamos que queremos evaluar

 

 

a={|n| if(n>10,{“es mayor a 10”},{“es menor a 10”})}

a.value(45)

 

a={|n| if(n.even,{“es numero par”},{“es numero impar”})}

a.value(8)

 

a={|n| if(n!=10,{es distinto a 10″},{“es 10”})}

a.value(10)

 

//ahora con Tdef

(

a={|n| if(n>10,{“es mayor a 10”},{“es menor a 10”})};

Tdef(\hu,{var contador=0;

     inf.do{a.value(rrand(2,20)).postln;

                      0.5.wait

                      }}

                      )

                      )

                      Tdef(\hu).play

 

 

///////////////////////////////////////////////////

 

 

//Iteraciones

 

 

Una operación presenta valores de entrada y de salida. En el siguiente ejemplo a y b toman los valores de entrada y salida

 

a = 2

 

b = a + 1

 

Una iteración es un proceso en el que el valor de salida de una operación se utiliza como un nuevo valor de entrada. Ejemplo:

 

a=0

 

b=a+1

 

a=b

 

b=a+1

 

a=b

 

b=a+1

 

a=b

 

b=a+1

 

a=b

 

b=a+1

 

a=b

 

b=a+1

 

a=b

 

b=a+1

 

Pero este algoritmo se simplifica de esta manera.

 

b=0

 

b=b+1

 

Si no damos la información previa éste algoritmo podría interpretarse como una contradicción, por lo cual es importante que seamos concientes de lo que implica el algoritmo de iteración dentro del lenguaje de Collider. Matemáticamente el algoritmo simplificado significa esto:

 

b=0

 

b=b+1  => 0=0+1 ! Contradicción

 

Pero dentro del lenguaje de Collider y otros varios, este algoritmo significa la iteración que ya hemos explicado.

 

///////////////////////////////////////////////////////////////////////////////////////

 

Mediante una simple iteración como la que vimos, podemos construir una estructura que nos será muy útil. 

Esta estructura simula un contador que nos devuelve una secuencia de números extraída del algoritmo x=x+1

Veamos como funciona dentro de un Tdef:

 

(

Tdef(\reloj,{var contador=0;

inf.do{contador=contador+1;

       contador.postln;

        0.1.wait

        }}

        )

        )

        Tdef(\reloj).play

 

 

Pero esta estructura nos devuelve una lista de números infinitos!!!

En ciertas circunstancias necesitaremos limitar la secuencia a cierto número máximo después del cual se reinicia la cuenta.

Para esto es necesario la utilización de módulo (%)

 

(

Tdef(\reloj,{var contador=0;

inf.do{contador=contador+1;

       (contador%5).postln;

        0.5.wait

        }}

        )

        )

        Tdef(\reloj).play  

 

 

///////////////////////////////////////////

       

 

//Switches

 

Un switch una herramienta que nos permite evaluar una variable y, dependiendo de su valor, asignar acciones en respuesta. Observen cómo en el ejemplo siguiente podemos asignar un valor a la variable ‘a’ y el switch realizará las acciones que le indiquemos dependiendo si ‘a’ vale 0,1,2,3 ó 4.

 

(

 

a=0;

 

switch(a, 0,{‘a es igual a cero’.postln;},

1,{‘a es igual a uno’.postln},

2,{‘a es igual a dos’.postln},

3,{‘a es igual a tres’.postln},

4,{‘a es igual a cuatro’.postln}

);

)

 

Podemos asignar mas de una acción dentro de la función de respuesta. Llamemos a un Synth además del postln.

 

(

SynthDef(\switch, {|freq=100|

    var sig, env;

    sig=Pulse.ar(freq,Line.kr(0.5,0.01,0.1))!2;

    env=EnvGen.kr(Env.perc(0,01,0.1),doneAction:2);

    Out.ar(0,sig*env);

    }).send(s);

);

 

Synth(\switch)

 

(

 

a=4;

 

switch(a, 0,{‘a es igual a cero’.postln; Synth(\switch, [\freq, 100])},

1,{‘a es igual a uno’.postln; Synth(\switch, [\freq, 2000])},

2,{‘a es igual a dos’.postln; Synth(\switch, [\freq, 300])},

3,{‘a es igual a tres’.postln; Synth(\switch, [\freq, 400])},

4,{‘a es igual a cuatro’.postln; Synth(\switch, [\freq, 500])},

24,{‘a es igual a venticuatro’.postln; Synth(\switch, [\freq, 500])}

);

)

 

En definitiva el Switch puede verse como un conjunto de varios if pero que nos ahorra trabajo de escritura. Vean cómo resolvemos el código anterior pero hecho con if.

 

(

 

a=4;

 

if(a==0,{‘a es igual a cero’.postln; Synth(\switch, [\freq, 100])});

if(a==1,{‘a es igual a uno’.postln; Synth(\switch, [\freq, 200])});

if(a==2,{‘a es igual a dos’.postln; Synth(\switch, [\freq, 300])});

if(a==3,{‘a es igual a tres’.postln; Synth(\switch, [\freq, 400])});

if(a==4,{‘a es igual a cuatro’.postln; Synth(\switch, [\freq, 500])});

)

 

 

////////////////////////////////////////////////////////////////////////

(

(

SynthDef(\puls, {|freq|

    var sig, env;

    sig=Pulse.ar(300,Line.kr(0.5,0.01,0.1));

    env=EnvGen.kr(Env.perc(0,01,0.1),doneAction:2);

    Out.ar(0,sig*env);

    }).send(s);

);

 

(

SynthDef(\sin, {|freq|

    var sig, env;

    sig=SinOsc.ar(2000,0,0.3);

    env=EnvGen.kr(Env.perc(0,01,0.1),doneAction:2);

    Out.ar(0,sig*env);

    }).send(s);

);

 

(

SynthDef(\saw, {|freq|

    var sig, env;

    sig=Saw.ar(2000,0.8);

    env=EnvGen.kr(Env.perc(0,01,0.1),doneAction:2);

    Out.ar(0,sig*env);

    }).send(s);

);

)

 

 

(

Tdef(\sw,{var contador=0;

    inf.do{contador=contador+1;

           (contador%3).postln;

switch(contador%3,  0,{Synth(\puls)},

          1,{Synth(\sin)},

          2,{Synth(\saw)}

    );

    0.3.wait

    }}

    )

    )

    Tdef(\sw).play

    

//////

 

(

SynthDef(\saw, {|freq|

    var sig, env;

    sig=Saw.ar(freq,0.8)!2;

    env=EnvGen.kr(Env.perc(0,01,0.1),doneAction:2);

    Out.ar(0,sig*env);

    }).send(s);

);

 

(

Tdef(\sw,{var contador=0;

    inf.do{contador=contador+1;

           (contador%3).postln;

    Synth(\saw,[\freq,((contador%[3,10].choose)+1)*100]);

    

    0.1.wait

    }}

    )

    )

 

Tdef(\sw).play

 

 

 

//BUFFERS

 

Los Buffers son arrays de números de punto flotante con un pequeño encabezado descriptivo.

Son colocados en un array global y están indexados por números enteros, empezando por el cero.

Los vamos a utilizar para accesar o escribir: archivos de audio, tablas, líneas de delay, envolventes o para cualquier otra cosa que pueda utilizar un array de números de punto flotante.

 

 

Solo vamos a ver una de las dos formas de cargar un archivo de audio:

1.- Object style con Buffer.read

 

 

1.- Object Style

Buffer.read asigna un buffer e inmediatamente lee un archivo de audio en él.

    Buffer.read(server, path, startFrame = 0, numFrames = -1, completionMessage);

 

 

 

server – el servidor en donde crearemos el buffer. Generalmente pondremos: s

path – la dirección del archivo de audio

startFrame – a partir de qué cuadro del archivo vamos a leer.  0 si queremos leerlo desde el principio (éste es el default).

numFrames – número de cuadros que vamos a leer. -1 si queremos que lea todo el archivo.  (éste es el default).

completionMessage – una función que será evaluada al terminar de cargar el archivo de audio.

 

path

Hay tres formas de indicar la dirección de un archivo de audio:

1.- Si el archivo de audio se encuentra en el directorio sounds que se halla dentro del directorio SuperCollider:

“sounds/nombre_del_archivo”

2.- Si el archivo se encuentra en algún subdirectorio de tu directorio de usuario:

“~/Documents/Audios/nombre_del_archivo”.standardizePath

3.- Si prefieres escribir la dirección completa:

“/Users/s/Documents/Audios/nombre_del_archivo”

(

b = // asignamos la variable b

// al objeto que será

// creado por la clase

// Buffer, para después poder

// mantener la comunicación

// con él

 

Buffer.read(

s, // el servidor

 

“sounds/a11wlk01-44_1.aiff” // la dirección del archivo de 

// audio. 

// (este archivo de audio

// viene con el programa)

 

// generalmente vamos a 

// utilizar los valores 

// default de los demás

// argumentos

)

)


 

b=Buffer.read(s,“sounds/a11wlk01-44_1.aiff”)

 

Si mandamos el mensaje .bufnum al objeto que acabamos de crear con Buffer, éste nos responderá el número de buffer que fue asignado a nuestro archivo de audio:

b.bufnum

Si no estamos seguros del número de canales de nuestro archivo de audio, podemos mandar el mensaje .numChannels a nuestro buffer.

b.numChannels

Para tocar nuestro buffer vamos a utilizar el UGen PlayBuf:

 

PLAYBUF

 

Con PlayBuf leemos un sample que ya se encuentre cargado en la memoria.

 

 

PlayBuf.ar(numChannels, bufnum, rate, trigger, startPos, loop)

 

numChannels – número de canales del sample que va a ser leído. Hay que tener cuidado, ya que si ponemos un número de canales erróneo, PlayBuf va a fallar silenciosamente, esto es, no va a marcar error, pero tampoco generará señal alguna.

 

bufnum – el número de buffer del sample

 

rate – la velocidad a la que será reproducido el sample:

 

  1.0  – velocidad normal

  0.5  – mitad de velocidad, por lo que el sample sonará una octava abajo

  2.0  – doble de velocidad (una octava arriba)

  -1.0 – el sample será leído al revés, con la velocidad normal

   

  Si nuestro archivo de audio tiene una velocidad de sampleo distinta a 44100, para que la velocidad de reproducción sea la correcta tendremos que utilizar el UGen BufRateScale.

   

trigger – cuando recibe una señal que cambie de 0.0, o menos que 0.0, a mayor que 0.0, PlayBuf brinca a la startPos

 

startPos – el número de cuadro en donde se iniciará la reproducción del sample. Cada segundo tienen 44100 cuadros (también conocidos como samples).

 

loop – si ponemos 1, cada vez que PlayBuf llegue al final del sample regresará al principio. Si ponemos 0, cuando llegue al final del sample se detendrá.

 

 

 

Primero cargamos un archivo de audio en la memoria:

 

b = Buffer.read(s, “sounds/a11wlk01-44_1.aiff”)

 

 

 

(

 {

  var numeroDeCanales, numeroDeBuffer;

  

  numeroDeCanales =  b.numChannels; //al mandar el mensaje 

  //.numChannels al objeto que 

  //representa a nuestro buffer, 

  //éste nos responderá 

  //el número de canales del

  //archivo de audio que cargamos

  //en la memoria

  

  numeroDeBuffer =  b.bufnum; //con el mensaje .bufnum, pedimos

  //el número de buffer en donde se

  //encuentra el archivo de audio

  

 

 

  PlayBuf.ar(numeroDeCanales, numeroDeBuffer)

 

 }.play

)

 

b.bufnum

PlayBuf.ar(1, 0).play

 

 

//Evalúa las siguientes líneas:

 

b.bufnum

b.numChannels

 

 

 

 

Si quisiéramos ver toda la información del buffer, mandamos el mensaje .query:

 

b.query

 

 

 

 

Para que PlayBuf toque nuestro archivo de audio en loop, hay que asignar el valor 1 al argumento loop:

 

(

 {

  var numeroDeCanales, numeroDeBuffer;

  

  numeroDeCanales =  b.numChannels;

  numeroDeBuffer =  b.bufnum;

 

 

  PlayBuf.ar(numeroDeCanales, numeroDeBuffer, loop: 1)

 

 }.play

)

 

 

 

 

Si queremos tocar nuestro archivo de audio a la mitad de velocidad, hay que poner 0.5 en el argumento rate:

 

(

 {

  var numeroDeCanales, numeroDeBuffer, velocidad;

  

  numeroDeCanales = b.numChannels;

  numeroDeBuffer = b.bufnum;

 

  velocidad = 0.15;

 

  PlayBuf.ar(numeroDeCanales, numeroDeBuffer, velocidad, loop: 1)

 

 }.play

)

 

 

Si queremos reproducir el sample al revés, a velocidad normal, ponemos -1.0 en el argumento rate:

 

(

 {

  var numeroDeCanales, numeroDeBuffer, velocidad;

  

  numeroDeCanales = b.numChannels;

  numeroDeBuffer = b.bufnum;

 

  velocidad = -1.0;

 

 

  PlayBuf.ar(numeroDeCanales, numeroDeBuffer, velocidad, loop: 1)

 

 }.play

)

 

 

 

 

Ahora vamos a controlar la velocidad con el mouse:

 

(

 {

  var numeroDeCanales, numeroDeBuffer, velocidad;

  

  numeroDeCanales = b.numChannels;

  numeroDeBuffer = b.bufnum;

 

  velocidad = MouseX.kr(0.125, 2.0);

 

 

  PlayBuf.ar(numeroDeCanales, numeroDeBuffer, velocidad, loop: 1)

 

 }.play

)

 

 

 

 

Vamos a utilizar un Impulse.kr como trigger para el argumento que lleva el mismo nombre. Cada vez que PlayBuf reciba un trigger, va a regresar al número de cuadro especificado en startPos, cuyo valor default es 0 (el primer cuadro o sample del buffer).

 

(

 {

  var numeroDeCanales, numeroDeBuffer, velocidad, trigger;

  

  numeroDeCanales = b.numChannels;

  numeroDeBuffer = b.bufnum;  

 

  velocidad = 1.0;

 

  trigger = Impulse.kr(MouseX.kr(0.7,4)); //vamos a mandar un trigger

  //cada segundo.

 

 

  PlayBuf.ar(numeroDeCanales, numeroDeBuffer, velocidad, trigger)

  

  /* ya no va a ser necesario poner 1 en el argumento loop */

 

 

 }.play

)

 

 

 

 

Con el mensaje .query podemos saber el número de cuadros o samples de nuestro archivo de audio:

 

 

b.query

 

 

 

Entre los datos que fueron desplegados en  la Post Window vemos:

 

 

numFrames   : 107520

 

 

 

Este es el número de cuadros de nuestro archivo.

 

 

Ahora vamos a hacer que cada vez que PlayBuf reciba un trigger, éste brinque a la mitad del buffer y no al principio de éste:

 

(

 {

  var numeroDeCanales, numeroDeBuffer, velocidad, trigger,

   posicionInicial;

  

  numeroDeCanales = b.numChannels;

  numeroDeBuffer = b.bufnum;  

 

  velocidad = 1.0;

 

  trigger = Impulse.kr(1);

 

  posicionInicial = 107520/2; // vamos a dividir el

  // el número total de

  // cuadros entre dos,

  // para obtener el 

  // número de cuadro que

  // se encuentra a la

  // mitad del buffer

 

 

  PlayBuf.ar(numeroDeCanales, numeroDeBuffer, velocidad, trigger, posicionInicial)

   

 }.play

)

 

 

 

En el ejemplo anterior escuchamos un click cada vez que el audio regresa a la mitad del buffer. Esto es causado por las diferencia entre la amplitud del audio antes de recibir el trigger y la amplitud después del trigger, después de que PlayBuf brincó a la mitad del archivo. 

 

Para quitar el click tenemos que poner un envolvente a la señal, que inicie con una amplitud de  cero y que regrese a cero justo antes de que el trigger cambie la posición del buffer.

 

2.reciprocal

 

(

 {

  var numeroDeCanales, numeroDeBuffer, velocidad, trigger,

   posicionInicial, sig, env, duracionEnSegundos,frecuenciadeltrigger;

  

  numeroDeCanales = b.numChannels;

  numeroDeBuffer = b.bufnum;  

 

  velocidad = 1.0;

 

  frecuenciadeltrigger   =  10;

 

  trigger = Impulse.kr(frecuenciadeltrigger);

  posicionInicial = 107520/2;

 

  duracionEnSegundos = 1/frecuenciadeltrigger;

  

  /* como regresamos a la startPos antes de que PlayBuf haya 

  llegado al final del archivo, nuestro envolvente deberá

  durar el tiempo en segundos que haya entre dos triggers */

  

 

  sig = PlayBuf.ar(numeroDeCanales, numeroDeBuffer, velocidad, 

  trigger, posicionInicial);

  

  env = EnvGen.kr(Env([ 0.0, 1.0, 1.0, 0.0 ],

   [ duracionEnSegundos * 0.025, 

     duracionEnSegundos * 0.95,

     duracionEnSegundos * 0.025 ]), trigger);  

    

  /* también vamos a utilizar al trigger del PlayBuf, 

   como trigger para el argumento gate del EnvGen. EnvGen 

   generará un envolvente cada vez que reciba un trigger  */

 

  sig * env

   

 }.play

)

 

 

 

 

 

BufFrames.kr

 

 

Este UGen calcula el número de cuadros de un buffer y con él es más fácil calcular la posición incial (startPos) de PlayBuf.

 

 

BufFrames.kr(bufnum)

 

 

bufnum – el número del buffer

 

 

Este UGen no nos va a dar directamente el número de cuadros de un buffer, sino que se lo comunicará a otros UGens

 

Si quisieramos utilizarlo para que PlayBuf regrese al primer tercio del archivo de audio, tendremos que poner lo siguiente en el argumento startPos:

 

BufFrames.kr(bufnum) * (1/3)

 

1000*0.99999

 

 

(

 {

  var numeroDeCanales, numeroDeBuffer, velocidad, trigger,

   posicionInicial, sig, env, duracionEnSegundos;

  

  numeroDeCanales = b.numChannels;

  numeroDeBuffer = b.bufnum;  

 

  velocidad = 1;

 

  trigger = Impulse.kr(MouseY.kr(30,0.5));

 

  posicionInicial = BufFrames.kr(numeroDeBuffer) * MouseX.kr(0,0.9999);

                  

  duracionEnSegundos = 1;

 

  sig = PlayBuf.ar(numeroDeCanales, numeroDeBuffer, velocidad, 

  trigger, posicionInicial);

  

  env = EnvGen.kr(Env( [ 0.0, 1.0, 1.0, 0.0 ],

   [ duracionEnSegundos * 0.05, 

     duracionEnSegundos * 0.9,

     duracionEnSegundos * 0.05 ]), trigger);  

 

  sig * env

   

 }.play

)

 

 

 

Ahora vamos a utilizar el mouse para cambiar la posición inicial del archivo de audio y la velocidad de reproducción:

 

(

 {

  var trigger, sig, env, frecuencia = 6;

  

  trigger = Impulse.kr(frecuencia);

 

  sig = PlayBuf.ar(

  b.numChannels, 

  b.bufnum, 

  MouseY.kr(-1.0, 1.0), 

  trigger, 

  MouseX.kr(0, BufFrames.kr(b.bufnum))

  );

 

  env = EnvGen.kr(Env( [ 0.0, 1.0, 1.0, 0.0 ],

  [ frecuencia.reciprocal * 0.05, 

    frecuencia.reciprocal * 0.9,

    frecuencia.reciprocal * 0.05 ]), trigger);  

 

  sig * env

   

 }.play

)

 

 

///////////////////////////////////////////////////

 

s.boot

 

 

a=Buffer.read(s,“sounds/a11wlk01-44_1.aiff”)

 

 

a.numFrames

(

SynthDef(\sample,{|frecuencia=1,posc=0,trigger=1,rate=1|

       var sen,env;

       sen=PlayBuf.ar(1,a.bufnum,rate,Impulse.kr(frecuencia),posc);

       sen=sen*SinOsc.ar(MouseY.kr(20,1));

       sen=Pan2.ar(sen,LFNoise2.kr(10),0.9);

           env=EnvGen.kr(Env([ 0.0, 1.0, 1.0, 0.0 ],

  [ frecuencia.reciprocal * 0.05, 

    frecuencia.reciprocal * 0.9,

    frecuencia.reciprocal * 0.05 ]), trigger,doneAction:2);

  Out.ar(0,sen*env)

  }).send(s)

  )

  Synth(\sample)

 

s.play

(

Tdef(\hu,{var suma=0;

    inf.do{suma=suma+1;

          Synth(\sample,[\frecuencia,rrand(2,5),\posc,rrand(0,107520),

          \rate,(suma%2+0.5)*1.2]);

              0.2.wait

              }}

              )

              )

 

 

    (

Tdef(\hu,{var suma=0;

    inf.do{suma=suma+1;

          Synth(\sample,[\frecuencia,0.1,\posc,suma%50*10,

          \rate, (suma%10 -5)*0.5 ]);

              0.5.wait

              }}

              )

              )

 

 

 

    (

Tdef(\hu,{var suma=0;

    inf.do{suma=suma+1;

          Synth(\sample,[\frecuencia,(suma%7+0.3)*1.3,\posc,suma%50*14,

          \rate,1.3 ]);

              0.25.wait

              }}

              )

              )    

 

 

  Tdef(\hu).quant_(0).play

  Tdef(\hu).stop

 

{LPF.ar(PlayBuf.ar(1,a.bufnum,0.2,loop:1)*SinOsc.ar(MouseY.kr(1400,1)),MouseX.kr(100,1000))}.play

 

 



 

 

 



 

Creative Commons License
Taller de SuperCollider Básico by https://basicsupercollider.wordpress.com is licensed under a Creative Commons Attribution-Noncommercial 2.5 Mexico License.1)}

One Response to “Clase 5”

  1. […] Lista la quinta clase! Con todo lo que necesitan para hacer sus primeros Tdefs y sus primeros Buffers. El próximo […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: