Primeros pasos con Reaktor - 5ª parte

Aquí estamos de nuevo, esta vez para dar el primer paso en el nivel Core de Reaktor en esta quinta entrada del tutorial.

Pues como decía en la anterior entrada, a mí personalmente me encanta Core: en general me resulta la parte más intuitiva de Reaktor, y es MUY potente.

Me parece necesario mencionar un poco de la historia del nivel Core, que en principio no formaba parte de Reaktor y fue incluido después (si no recuerdo mal a partir de la versión 5 de Reaktor inclusive), cuando el desarrollador del software Sync Modular, pasó a formar parte de los empleados de NI y su software se adaptó al entorno de Reaktor, quedando al mismo tiempo lo ya hecho como un freeware que podéis descargar aquí.

A partir de entonces el nivel Core quedó incluido en Reaktor como una parte más del software, accesible a través de las Core Cell (tranqui, las veremos en la entrada ;) ), que básicamente son una adaptación del entorno de trabajo de Sync Modular al nivel primario de Reaktor, y que le otorgan otra dimensión y un montón de posibilidades.

Esta vez, como en otras entradas, os van a hacer falta unos conocimientos matemáticos mínimos, ya que para empezar, y aunque algunos me llamen copión (en el manual de Reaktor también se hace esto xD, pero yo lo voy a explicar de diferente manera y hacer más cosas ;) ), vamos a cambiar parte del oscilador de nuestro sinte por uno hecho desde cero por nosotros... sí, desde cero ;)

5. LO BASICO DEL NIVEL CORE

5.1. CORE CELL

Como te iba diciendo, las Core Cell son una adaptación de un entorno de trabajo ajeno a Reaktor, al nivel primario que hemos estado viendo hasta ahora en este tutorial. Para lograr esto, NI ha optado por “encapsular” este entorno en unas macros especiales, y las ha llamado Core Cell (celda core, célula core)... y bueh, son unas macros que cuando entras en ellas NO vas al contenido normal y corriente de una macro normal y corriente; cuando entras en ellas vas al nivel Core.

Como siempre, vamos a la práctica añadiendo en nuestra macro OSC1, un módulo Core Cell->New Audio... aunque mira, voy a aprovechar y enseñarte otra cosilla útil añadida en la versión 5.6 de Reaktor.

En vez de acceder por el menú como te acabo de decir, haz botón derecho como siempre, y elige la opción Insert Module..., tras lo cual te aparecerá un cuadro de texto donde debes poner el nombre del módulo que estás buscando, en este caso New Audio.

Tal cual aparece en la foto, es el típico buscador que va buscando según escribes, super útil para no perderse entre tanto menú. Yo ya estoy acostumbrado al botón derecho y al final nunca me acuerdo de usarlo :P , pero es realmente útil, aunque yo lo usaría más si no tuviera que darle al botón derecho y apareciese el buscador solo con escribir en la ventana de estructura, pero bueno, sigamos.

Ya deberías tener tu primera Core Cell en la macro OSC1, que en este caso es de audio.

¿Porqué de audio? Bueno, en Reaktor hay dos tipos de Core Cell, las de audio y las de event (¿Otra vez el rollo de audio y eventos? Sí, otra vez ¬¬ , pero a medias... sigue leyendo) que se distinguen por varias características que iremos viendo poco a poco, de las cuales hay una que nos resulta fundamental para lo que queremos hacer.

Pero empecemos desde el principio, que para eso he abierto un tocho de teoría ;) .

En el nivel Core de Reaktor, TODOS los datos se tratan a una velocidad igual a la frecuencia que uses como Sample Rate. Una vez dentro de una Core Cell no hay diferencias, todo son números con los que operar yendo de un lado para otro.

Creo que te he dejado más confundido, porque si dentro de la Core Cell todo es igual, para qué una de audio y otra de eventos. Pues la de audio tiene varias cosas que la de eventos no tiene, y lo que nos interesa ahora mismo es que en la de audio puedes recibir un “click”, un trigger, un valor, como quieras llamarlo, al comienzo de cada sample.

Esto quiere decir, que si tienes una Sample Rate de 44100, recibirás 44.100 clicks en un segundo, y podrás usar esos clicks para lo que quieras... seguro que ya se te han ocurrido unas cuantas cosas xD , pero si no es así, por ejemplo puedes usarlo para dar un valor diferente al audio en cada sample, haciendo de esta forma un oscilador según una fórmula dada.

Ahora entra a la Core Cell (igual que una macro cualquiera, doble click encima), y verás esto:

Las Core Cell están divididas en tres zonas, de izquierda a derecha: una parte donde van las entradas (1), otra parte donde van todos los módulos que metas (2), y una última parte donde van las salidas (3). Como en cualquier otra macro, las entradas y salidas que añadas aquí serán las que veras en la macro cuando salgas de ella al nivel superior.

Añadamos un par de entradas que van a ser necesarias, para ello haz click con el botón derecho en la primera zona, y menú New->In, y repite para la segunda entrada.

Verás que las dos entradas tienen un punto negro a su izquierda, y uno blanco a la derecha. El punto blanco es exactamente lo mismo que los puntos que antes tenías que unir para conectar salidas y entradas, en el nivel Core se representan de esta manera.

Ahora selecciona una de las entradas, la de arriba por ejemplo, ve a la pestaña FUNCTION de sus propiedades, y selecciona la opción Event que se encuentra bajo el título SIGNAL TYPE.

Verás que el punto negro ahora es rojo.

Como dije antes en el tocho de teoría, una vez que una señal entra a una Core Cell es tratada igual ya sea audio o eventos... una vez entra, no antes de entrar, y esto implica muchas cosas.

Reaktor lee las entradas de audio como lee cualquier señal de audio, a una velocidad igual a tu Sample Rate, sin embargo las entradas de eventos las lee a la frecuencia de eventos, la Control Rate (tocho de teoría en el capítulo 1.4), por tanto a la Core Cell no le va a llegar la señal de una manera adecuada si no eliges la opción correcta, y por otro lado una entrada de audio siempre gastará más recursos que una de eventos.

Y con esto llego a otra diferencia entra las Core Cell de audio y las de eventos: las Core Cell de eventos no permiten la posibilidad de tener entradas de audio, solo tienes entradas de eventos. Así mismo, las salidas de las Core Cell de audio son solo de audio, y viceversa, de esta forma se conecta el nivel Core con el nivel primario.

En nuestro caso, por ahora necesitamos una entrada de audio y una de eventos, porque tenemos una señal de audio (la amplitud) y una señal de eventos (el pitch o tono), así que renombra la entrada de eventos a P y la de audio a A, super típico y entendible a primera vista.

Añade un módulo Built In-Module->Macro (funcionan igual que siempre), un módulo Standard Macro->Convert->P2F, y conecta la salida de P a la entrada de P2F.

En realidad este módulo, como el nombre del menú indica, es simplemente una macro, si le haces doble click entrarás en ella y verás qué hace el módulo.

Además, es más pequeña que la otra macro que has añadido, y esto es por la opción que te señalo en la foto: Look->Small en la pestaña FUNCTION de las propiedades. En Core las macros pueden tener dos tamaños, uno grande y otro pequeño, lo cual ayuda muchísimo para ordenar las macros en la pantalla, por eso te lo señalo.

5.2. OSCILADOR

Como te he adelantado en el tocho de teoría, en Core las señales se tratan a una velocidad igual a tu Sample Rate, y se opera con ellas a esa misma velocidad, con lo cual, para hacer un oscilador tenemos que decirle a Reaktor qué valor debe tomar el audio en cada sample... y viene teoría sin enmarcar, porque es indispensable que la entiendas para hacer el oscilador.

Creo que si estás leyendo este tutorial, ya sabrás que en una onda de audio se repite un patrón a una frecuencia determinada por el tono. Esa frecuencia nosotros la obtenemos por la entrada P, directamente del módulo NotePitch, pero este módulo no nos da la frecuencia en Hertzios, la da en notas Midi (osea, igual que un teclado controlador, de 0 a 127), que como ya he dicho en el tutorial un par de veces tiene una relación logarítmica con respecto a los hertzios. El módulo P2F que hemos añadido nos da la frecuencia en hertzios, que es la cantidad de veces que se repite el patrón de la onda en un segundo... lo cual implica que tarda en hacer dicho recorrido un número de segundos igual a 1/Frecuencia.

Para el tuto nos vamos a situar en un caso concreto, una onda facilita como es una onda de sierra de toda la vida.

Hasta aquí creo que todos llegamos, y si no llegas, antes de leer esto deberías estar leyendo sobre síntesis ;) .

Muy fácil, una linea que sube desde 0 hasta A, tardando en subir (1/Frecuencia) segundos. Esto sería fuera del ordenador, claro, porque a partir de ahora tienes que tener en cuenta SIEMPRE que el ordenador nunca va a hacer una recta perfecta: en cada sample dará un valor al audio, cada valor será un poquito mayor al anterior, hasta llegar a la amplitud.

Por un lado, con una sample rate típica de 44100, cada segundo tienes 44.100 valores distintos, y por otro lado, cada segundo la onda tiene que haber hecho una recta hasta A un número de veces igual a la frecuencia en Hertzios.

Olvídate de mis, a estas alturas obviamente nulos, conocimientos de diseño gráfico por ordenador XD, y fíjate en la recta por pasos que he dibujado en verde: en cada sample de los 44.100, el audio toma un valor, formando una recta a pasos que sirve como aproximación a una onda de sierra.

¿Porqué insisto tanto en algo que ya debería saberse? Por que precisamente, cada uno de esos valores es lo que tenemos que decirle a Reaktor, así es como trabaja Core: sample a sample.

Todo esto para decirle a Reaktor que:

El valor del audio debe ir subiendo desde 0 hasta la amplitud seleccionada por el knob.

Que debe subir de forma lineal, es decir, lo mismo a cada sample que suba.

Que debe hacer este recorrido exactamente en un tiempo de 1/Frecuencia, medido en segundos.

Tenemos, como te decía antes, por un lado la Sample Rate, 44.100 samples en un segundo; y por otro lado la frecuencia, cada ciclo de la onda debe tardar (1/Frecuencia) segundos en completarse.

Queremos averiguar cuánto debe aumentar el audio cada sample, para que llegue a A en el tiempo marcado por la frecuencia.

Tan fácil como dividir y multiplicar: (F/SR)*A. Sabiendo que eso es lo que tiene que aumentar el audio a cada sample, solo necesitamos un bucle que sume ese número a la corriente de audio en cada sample, hasta que llegue a un valor mayor o igual que A, en cuyo caso deberá sustituir su valor por 0, y empezar de nuevo.

Esto es lo que los ingleses llaman una "Naive saw", es la forma más fácil de hacer una onda de sierra, la que menos recursos consume... y la que provoca un mayor efecto de aliasing xD

Aún sabiendo esto último, supongo que es obvio porqué he optado por esta forma. Primero, no soy ningún gurú del DSP, y para hacer formas de onda con algoritmos anti-aliasing prácticamente hay que serlo (u, obvio, tener acceso al algoritmo de alguno de ellos); segundo, por que si a estas alturas os meto a hacer algo así con Reaktor, más de uno se dará la vuelta y se largará corriendo :P ; y tercero, pues qué coño, si lees en las páginas de los mismos supuestos Gurús que conocen los algoritmos, que sintes antiguos y míticos no usaban estos métodos y metían ondas de sierra a pelo a su sample rate, ¿porqué nosotros que estamos aprendiendo no deberíamos hacerlo? ;)

Bien, antes de empezar el bucle, hay que aclarar una última cosa, y así ya la sabéis a partir de ahora, por que de las operaciones simples (suma, resta, multiplicación, división) la que más le cuesta al ordenador siempre va a ser la división, y en nuestra fórmula tenemos que hacer una cada sample, así que vamos a buscar una solución matemática para esto.

Teníamos que el incremento por sample era igual a:

(F/SR)*A = (1/SR)*F*A

Lo bueno de este cambio es que, si bien la frecuencia y la amplitud pueden cambiar en cualquier momento, con lo cual estás obligado a operar con ellas en cada sample, la sample rate no va a cambiar tan fácilmente, así que podemos hacer UNA operación de división, 1/SR, al principio, y en cada sample solo tenemos que multiplicar 3 datos.

Volvemos a la Core Cell, donde ya teníamos el módulo P2F que nos da la frecuencia, y una macro para hacer el bucle dentro de ella. Te pongo la foto otra vez para que te sitúes:

Entra a la macro (doble click como siempre), y vamos a empezar añadiendo las entradas que nos hacen falta, que son dos, y se añaden igual que antes: botón derecho en la zona de entradas, menú New->In.

Te habrás dado cuenta que ahora nos salen más opciones en el menú New, y es que las entradas a las macros de las Core Cell pueden ser de varios tipos. Ya las iremos viendo, por ahora añade la que nos interesa, New->In, y nómbrala F, después añade otra debajo que se va a llamar A.

Fijate que en el nivel core, las entradas y salidas no aparecen en el orden en que las añades, como pasaba en el nivel primario, aparecen ordenadas según están en la zona de entrada/salida, una encima de otra. Si ahora pones A encima de F y sales de la macro, verás que en el nivel superior también ha cambiado el orden de las entradas. El método es mucho más útil para ordenar la estructura de las macros.

5.3. BUCLE

Vale, primero vamos a calcular la única división que tenemos en el bucle, para ello añade un módulo de división, con el botón derecho en la zona de módulos (la central) y menú Built-In Module->Math->/.

Ahora pincha con el botón derecho en la entrada inferior del módulo de división, y elige la opción Connect to SR.R.

Verás que aparece un texto en blanco que dice SR.R, que está conectado con una tímida línea a la entrada donde pinchaste. Esto es algo nuevo de Core, las QuickConst y los QuickBus, por lo que hay que dar gracias no sé si al creador de Sync Modular o a NI, pero hay que darle las gracias de la manera más sincera posible, por que son conexiones inalámbricas que funcionan taaan fácil como acabas de ver.

Los QuickBus son conexiones de toda la vida, por las que pasan datos de un extremo del bus a todas las conexiones que se hagan (ya lo veremos, tranqui). Las QuickConst son constantes, igual que habíamos visto en el nivel primario, que en core también existen en forma de módulo... aunque yo no las he usado nunca, por que en cuestión de ordenar y entender una estructura no se puede comparar, es mucho más rápido y limpio de esta forma.

En fin, la QuickConst que acabas de añadir es especial, puedes usarla en cualquier Core Cell y siempre te dará el valor de la Sample Rate del sistema en el que estés trabajando, ya sea la elegida en Reaktor en modo Stand-Alone o la que tengas elegida en tu daw cuando usas Reaktor como vst; así que no puedes cambiar su valor, siempre te dará la Sample Rate.

Ahora en la otra entrada vas a añadir otra QuickConst, pero esta vez su valor va a ser 1. Botón derecho en la entrada y elige la opción Connect to new QuickConst.

Te conectará a la entrada una constante con valor 0 (es así por defecto, cuando la añades siempre tiene valor 0). Para cambiar su valor, pínchale encima (al texto, no a la entrada), y ve a la pestaña FUNCTION de sus propiedades.

Ahí lo tienes, pinchas en el "0", pestaña FUNCTION de las propiedades, y cambias el valor que aparece en el cuadro de texto Value enmarcado en el título MODULE PARAMETERS, le pones un 1 y listo, la QuickConst también cambiará su texto a 1 y a partir de entonces dará un valor de 1, con lo cual tenemos nuestra división, 1/SR, lista para usar.

Para seguir haciendo las cosas con orden y tener la estructura entendible, como siempre, vamos a meter el incremento por sample en una macro, así que vamos a hacer unas cuantas cosas de golpe: añade una macro igual que al principio (Built-In Module->Macro) y nómbrala Inc; corta nuestra división (como te he dicho antes, las QuickConst ahora son parte del módulo, así que selecciona el módulo de división y corta con Ctrl+x sin miedo), entra a la macro y pégala dentro; ya dentro de la macro añade dos módulos de multiplicación en el menú Built-In Module->Math->*, dos entradas y una salida (igual que las entradas pero el botón derecho en su zona).

La macro debería quedarte así:

Conecta las dos entradas a un módulo de multiplicación, y ya tenemos la parte F*A de nuestra fórmula. Ahora, al siguiente módulo de multiplicación vas a conectar la salida del primero, y la salida de nuestra división. El resultado de todo esto es la salida del segundo módulo de multiplicación, que hay que conectar a la salida de la macro, quedando esto:

En las últimas dos fotos te he señalado la ruta de la estructura, que en core funciona igual que en el resto.

Pues ya tenemos nuestro incremento por sample en una macro propia, que por cierto, ponla en tamaño pequeño como te señalé al principio de esta entrada, en la pestaña FUNCTION de las propiedades, elige la opción Small del menú Look.

Ahora mismo, tenemos una entrada que nos da la frecuencia, otra que nos da la amplitud, y la salida de la macro que nos da el incremento por sample. Es hora de plantearnos el algoritmo que va a seguir nuestro bucle, aunque sea de una forma mínima, para no perdernos cuando estemos haciéndolo.

Sería algo como esto:

En cada sample:

Sumamos al audio el incremento

Si el resultado de esta suma es mayor o igual que la amplitud, le obligamos a tener valor 0, y sigue el bucle.

El audio sale de la Core Cell

En pseudo código, para los que estén acostumbrados:

Cada sample

Audio += Incremento

If Audio >= Amplitud entonces Audio = 0

Sale Audio

Teniendo esto en mente, nos va a hacer falta guardar el valor del audio entre sample y sample, para lo cual usaremos una variable que Reaktor se ocupará de guardar en memoria.

Para los que vienen de lenguajes de programación, no hace falta especificar el tipo de variable. Aunque en Core hay dos tipos de variables, enteros que es equivalente al Int de toda la vida, y decimal que es un Float cuyo tipo no se especifica de la manera habitual, cuando defines la variable, sino que en cada módulo puedes especificar en las propiedades si es de 32 o 64 bits, nada más.

Resumiendo, exceptuando casos muy específicos, osea practicamente nunca, no habrá que preocuparse del tipo de variable. Es más, yo recomendaría que si vienes de otros lenguajes de programación, te coartaras en andar cambiando el tipo de variable de float a int, por que si cambias una variable a int, en algún momento vas a tener que operar con ella Y con una variable float, y en ese momento Reaktor te cambiará el tipo, y si no, cuando la variable finalmente salga de la Core Cell al nivel primario, reaktor la va a convertir en float igualmente, con lo que solo conseguirás comer recursos innecesariamente a base de cambios de tipo, y usar un tipo int no va a compensarte tanto como estás acostumbrado a que pase.

Así que, mi consejo es que dejes el tipo int solo para lo estrictamente necesario, y para todo lo demás uses el estándar de Reaktor.

5.4. MEMORIA

Añade esta variable con el botón derecho en la zona central, en el menú Built-In Module->Memory->Write, y en el menú Built-In Module->Memory->Read. Obviamente, el primer módulo es para escribir en la memoria, y el segundo para leerla.

Vamos a ver con calma estos dos módulos:

  1. Entrada al módulo de escritura. No hay demasiado que explicar, la memoria guarda el valor que llegue a esta entrada.

  2. Entradas y salidas "Latch" (cuadradas). Los dos módulos tienen estas entradas y salidas, el módulo de lectura tiene que estar conectado de esta manera a un módulo de escritura, así Reaktor sabe qué variable tiene que leer. El orden en que estén conectadas (derecha o izquierda), según el manual no importa, pero esto es incompleto: no importa a la hora de saber cual variable hay que leer, pero si pones un módulo de lectura antes que (a la izquierda) un módulo de escritura, lo más normal es que leas antes de escribir, y esto puede dar lugar a que se te fastidie el bucle. Cuidado con esto por que Reaktor es muy pijotero con los bucles, en nuestro caso da igual, pero bueno, ahí queda como aviso a navegantes ;) .

  3. Entrada al módulo de lectura. Esta entrada sirve como trigger para que el módulo lea la memoria: en el momento en que "algo" llegue a esta entrada, el módulo lee la memoria. El valor que llegue a la entrada no importa.

  4. Salida del módulo de lectura. Obviamente, por aquí sale el valor de la memoria en el momento en que es leída.

Vale, si observas el esquema que nos hemos hecho de nuestro bucle, verás que a cada sample necesitamos saber el valor del audio para sumarle el incremento, por lo que a cada sample vamos a tener que leer la variable.

Para esto vamos a usar algo que vimos en el primer capítulo de esta entrada, cuando te explicaba las diferencias entra las Core Cell de audio y de eventos: el QuickBus SR.C.

El SR.C envía un evento (recuerda que en core los eventos son diferentes, sé que puede ser confuso al principio, pero en core un evento es simplemente un número cualquiera) al principio de cada sample. Por tanto, si trabajas con una Sample Rate de 44100, enviará 44.100 eventos cada segundo... esto es perfecto para nuestro bucle.

Para usar el SR.C solo tienes que conectarlo a una entrada igual que hiciste con SR.R, botón derecho en la entrada y menú Connect to SR.C, hazlo para la entrada del módulo de lectura de memoria, y ya que estás, conecta la salida Latch de este módulo a la entrada del módulo de escritura:

Tal y como lo tenemos ahora mismo, en cada sample, Reaktor va a leer nuestra memoria, y el valor que esté en la memoria en ese momento, va a ser enviado por la salida del módulo de lectura.

Según nuestro esquema, ahora hay que sumarle el incremento, así que antes que anda, haz algo que deberíamos haber hecho antes: hay que conectar las entradas de la frecuencia y amplitud a sus correspondientes en nuestra macro Inc, pero lo vamos a hacer inalámbrico para ahorrarnos cables molestando en la estructura.

Botón derecho en la entrada F y elige la opción Connect to New QuickBus, y repite para la entrada A.

Verás que ha pasado algo parecido a cuando añadimos la constante, pero al revés, la entrada se ha conectado a un texto en blanco, que es el nombre de nuestro QuickBus. El nombre será el mismo que el módulo al que lo conectes, y si este no tiene nombre Reaktor le pondrá uno estándar (QBus... originales que son).

Ahora botón derecho sobre la entrada superior de la macro Inc, y menú Connect to QuickBus->F, repite con la entrada inferior y menú Connect to QuickBus->A. Al pinchar en este menú en cualquier entrada, te saldrán todos los QuickBus disponibles para que la conectes al que quieras, y cada vez que hagas uno nuevo, este aparecerá también en el menú.

Cuando conectas una entrada a un QuickBus, es exactamente lo miso que si lo conectaras con un cable, lo que llegue al principio del QuickBus llegará también a cada uno de los sitios donde lo tengas conectado.

Pues tenemos listo nuestro incremento, que hay que sumarlo al valor de nuestra variable, para eso añade un módulo de suma en el menú Built-In Module->Math->+, y conecta a sus entradas la salida de Inc y la salida del módulo de lectura de memoria:

5.5. COMPARE

Y empieza lo bueno :P , tenemos que comprobar si el resultado de esto es mayor o igual a la amplitud, y hacer cosas diferentes según lo sea o no lo sea (un If de toda la vida). Vale, me vas a permitir que para facilitar un poco las cosas, trabajemos suponiendo que la amplitud sea uno, así a partir de ahora todo será mayor o igual que uno y nos ahorramos la amplitud dando por saco a cada momento. Más adelante corregiremos esto.

Añade un módulo Bulit-In Module->Flow->Compare y un módulo Bulit-In Module->Flow->Router. Estos dos módulos no sirven de nada el uno sin el otro, están hechos para funcionar juntos... hasta tal punto, que si te fijas tenemos un nuevo tipo de conexión que casi solo sirve para ellos dos, el triangulo amarillo:

Este tipo de conexión solo sirve para comparaciones, que es lo que queremos nosotros, así que conecta el módulo Compare y el Router con esta conexión, y lo que queremos comparar es si el resultado de nuestra suma es mayor o igual ( >= ) a uno... pero, no sé si te habías fijado, ese módulo no es de mayor o igual, es solo de mayor. Para cambiarlo ve a la pestaña FUNCTIONS de las propiedades del módulo y, como siempre, bajo el título MODULE PARAMETERS, cambia la comparación a la que nos interesa ( >= ) en el menú Criterion.

Después, conecta la salida de la suma a la entrada superior del módulo de comparación, y a la entrada inferior ponle una QuickConst de valor 1... te voy a poner la foto con todo ya hecho, a ver si puedes hacerlo tú solo :P

Volvemos a pararnos para ver qué hemos hecho (esta entrada va lenta, lo sé, pero es que todo lo que estamos viendo es nuevo, y mejor lentito y con buena letra).

Cada sample, Reaktor lee la memoria y envía su valor al módulo de suma; al mismo tiempo, calcula el incremento según los cálculos que hemos establecido en la macro Inc, cuyo resultado también se envía al módulo de suma; el resultado del módulo de suma se compara con 1, y esta comparación se envía al módulo Router.

Pues tenemos un fallo, que he dejado ahí hasta ahora. Cuando he descrito lo que va a pasar cada sample según tenemos montada la estructura de la Core Cell, te he dicho que al mismo tiempo que se lee la memoria, Reaktor hace los cálculos del incremento y los manda al módulo de suma.

Esto no es totalmente correcto, por que estos cálculos no se hacen cada vez que se inicia un sample... por el lado de la memoria tenemos conectado un SR.C así que por este lado lo tenemos seguro: se va a hacer una vez por sample; pero el lado de los cálculos del incremento no es igual, estos cálculos están conectados a las entradas F y A, que si haces memoria, en última instancia estarán conectados al módulo NotePitch y al knob Ampl respectivamente. Entonces, estos cálculos se harán cuando el módulo NotePitch o el knob Ampl cambien sus valores, al cambiar el valor de cualquiera de estos dos, la macro recibirá ese valor y hará los cálculos, lo cual se verá reflejado en nuestro módulo de suma.

Hasta aquí todo bien: como tiene que ser, cuando hayan cambios se verán reflejados; pero es que el módulo de suma está conectado directamente al resto del bucle, así que cada vez que cambie uno de estos dos valores, el nuevo valor va a llegar al módulo de suma y va a "seguir palante", lo que va a generar que tooodo el bucle se ponga en funcionamiento... no queremos que pase eso, queremos que el bucle funcione a cada sample, no cuando movamos el knob de la amplitud.

En nuestro caso puede que no tenga tanta importancia, porque el SR.C ya se encarga de que el bucle tenga lugar a cada sample, sin excepciones, así que lo que pase cuando cambien esos valores, pues bueno, el bucle va a tener lugar igualmente. Así todo, esto no es razón para dejarlo mal, hay un montón de situaciones en las que algo así te va a jugar una mala pasada y te estropeará un ensemble entero, así que vamos a asegurarnos de que, cuando estos valores cambien, el valor se tenga en cuenta en la suma y no más allá, así el bucle no continuará por estos valores, sino únicamente cuando reciba el SR.C.

Es tan importante, que NI ya nos tiene hecho un módulo a los efectos, que puedes encontrar en el menú Expert Macro->Modulation->x + a, que vas a añadir a continuación.

Otra vez, el módulo no es como los del nivel primario, es una macro normal y corriente a la que puedes entrar si haces doble click... como te dije al principio, los módulos de core son más elementales, el resto son simplemente cosas ya hechas.

Si entras a la macro y la exploras un poquito, verás que lo único que hace es almacenar el valor de a (la entrada inferior) en una variable cada vez que recibe un valor del mismo, y cuando recibe un valor por la otra entrada (la superior), lee la variable a, la suma al valor que ha recibido, y envía el resultado por la salida.

Total, que hace exactamente lo que queremos, el valor cambia y es almacenado, pero no sigue más allá hasta que la otra entrada recibe un valor.

Sustituyendo el módulo de suma por este último, debería quedarte algo como esto:

5.6. ROUTER

A nuestra comparación le falta algo, que es el otro extremo de la conexión, el módulo Router. Este módulo envía los datos que le llegan por su entrada inferior, por una salida distinta dependiendo de si la comparación que le llega por la entrada superior es verdadera o falsa.

En el capítulo anterior te dije que esta conexión amarilla entre el Compare y el Router era nueva, pero no te dije más. Es una conexión de un tipo llamado Bool, osea de tipo lógico, y por lo tanto solo puede tomar dos valores: verdadero y falso.

Si vienes de algún lenguaje de programación te resultará más que familiar lo de bool, y es exactamente lo mismo.

La diferencia estriba en que en otros lenguajes estos dos valores son 0 y 1, pero en Reaktor esta equivalencia no sirve de mucho ya que directamente no puedes conectar esta conexión a un módulo de multiplicación, a un módulo de operación binaria, o a cualquier módulo que no sea un Router o alguno más que hay por ahí.

Como no puedes conectarla, no puedes usar el 0 y el 1 en una operación matemática, así que en realidad tampoco importa.

Si el resultado de la comparación es verdadero, el Router mandará los datos de la entrada inferior por la salida superior; si el resultado es falso, los mandará por la salida inferior.

Con lo cual, tenemos que mandar nuestro audio por el Router, y tener en cuenta por donde va a ser enviado en cada ocasión. Por ahora lo que está claro es que hay que conectar la salida del módulo x+a a la entrada del Router.

Con esto tenemos dos posibilidades en nuestro bucle, cada una representada por una salida del Router, a saber, de arriba a abajo:

1. El audio es mayor o igual que uno. Entonces el audio es enviado por la salida superior.

2. El audio es menor que uno. Entonces el audio es enviado por la salida inferior.

Pues acudamos de nuevo a nuestro esquema del bucle... si el audio es mayor o igual que uno... bien, ¿recuerdas que al principio del capítulo 5 de esta entrada, cambiamos amplitud por uno? Es momento de volver a cambiar esto y dejarlo todo bien puesto, cambia ese uno por el QuickBus de nombre A, igual que si no tuviera nada conectado: botón derecho en la entrada, menú Connect to QuickBus->A.

Vale, ahora sí que cuadra todo, tenemos dos posibilidades según las salidas del Router:

1. El audio es mayor o igual que la amplitud. Entonces el audio es enviado por la salida superior.

2. El audio es menor que la amplitud. Entonces el audio es enviado por la salida inferior.

Y si revisas el esquema que hicimos para el bucle, verás que cuando el audio es mayor o igual a la amplitud, debemos darle al audio un valor de 0; si es menor no hay que hacerle nada; y en ambos casos debemos mandar el audio fuera de la Core Cell.

Hay varias formas de "resetear" el audio dándole un valor de 0 (o el que quieras).

La primera es obvia: restarle por sí mismo (creo que no hacen falta más explicaciones al respecto ;) ). El problema de esto es duplicar una corriente de datos.

La segunda, ya que en el momento de hacer esta operación sabemos que es mayor o igual que la amplitud, es restarle la amplitud. Nos quitamos de encima el problema de duplicar una corriente de datos, por que la amplitud es casi una constante, pero tenemos un problema matemático porque no sabemos si el último valor es exactamente igual a la amplitud...

Te pongo de nuevo la gráfica de la onda de sierra dividida en samples:

No se si te fijaste la primera vez que la puse, pero te lo voy a señalar ahora: el punto donde la onda de sierra corta a la amplitud, no está dentro del último sample, sino en el siguiente.

Esto es inevitable, no fue un fallo al dibujar la gráfica, es consecuencia de tener que dividir una recta continua en pasos discretos.

Acuerdate como hemos hallado el incremento por sample, la fórmula que hicimos fue:

(1/SR)*F*A = A*F/SR

El número de samples que tiene un ciclo de la onda, es justo la inversa de esta fórmula, que despejando se convierte en:

1 / (A*F/SR) = SR / (A*F)

Si te has quedado boquiabierto, piensa que tienes que repartir los samples que hay en un segundo, entre la cantidad de veces que se va a repetir el ciclo en ese mismo segundo... y si repartes algo entre otro algo, siempre tienes que dividir ;)

Es muy poco probable, por no decir prácticamente imposible, que esta división te de un número entero de samples, la gran mayoría de veces el resultado será un número con una burrada de decimales. Para que lo veas claro, vamos a considerar que la amplitud es 1, y hacer el cálculo (calculadora de windows) para un caso típico: una frecuencia de 440 Hz y una sample rate de 44.100 Hz.

Número de samples = 44.100/(1*440) = 44.100 / 440 = 100,227272727...

Como te habrás fijado, el resultado son 100,227 periódico (los últimos dos decimales, "27", se repiten hasta el infinito; perdón pero no sé como hacer el símbolo de periódico para dos números) samples cada ciclo de onda hasta llegar a una amplitud de 1.

En la práctica esto es imposible, el ordenador no puede dividir un sample en varias partes, directamente no entiende ese concepto de continuidad según el cual el punto "cae donde cae" en la recta, el sample es la unidad más pequeña que es capaz de manejar, y hasta ahí llega.

Por tanto, solo quedan dos posibilidades: que el ordenador pare en el sample número 100, o que pare en el número 101.

Si para en el número 100, lógicamente, no va a llegar a 1 por un poquito. Si se para en el número 101, se pasará por otro poquito.

Sin embargo, no podemos obligarle a que se pase de 1 porque corremos el riesgo de que clipee, así que tenemos que quedarnos en el sample anterior y como se dice en mi tierra, la diferencia nos la comemos con papas.

Ahora bien, si no llegamos a 1, y para resetear el bucle restamos 1, pues está claro que no vamos a 0, sino a algún punto un poquito por encima de 0. La solución es restar por sí mismo, así siempre reseteamos a 0.

En cualquier caso, todo esto es teoría, en la práctica estas diferencias son tan nimias, que es imposible que notes una diferencia en el sonido elijas la opción que elijas. La diferencia en el espectro porque la onda llegue a 0,99773242630385487528344671201814... (si multiplicas para el mismo ejemplo que te he puesto, verás que este número es a donde llega la onda con los mismos valores) en vez de llegar a 1, es absurda, y es imposible notar alguna diferencia.

Si se pasara de 1 y llegara a clipear sería otra cuestión, pero como podrás imaginar, no puedes escuchar una diferencia de amplitud de un orden de milésimas (teniendo en cuenta que todo esto está normalizado a uno, no son milésimas de decibelios, en decibelios puede ser todavía menos). Además, este error se corrige por sí solo cada cierto número de ciclos, aumentando y disminuyendo la diferencia, sin alejarse nunca lo suficiente.

Por otro lado, y como ya dije antes, también se puede implementar un algoritmo de anti-aliasing, pero un algoritmo de anti-aliasing no solo corrige este ínfimo error en el último sample del ciclo de la onda, sino que interpola los valores de cada uno de los samples que va generando el ordenador en cada uno de los ciclos de la onda, y aquí sí que puedes conseguir una diferencia importante.

Total, aunque sé que más de un purista pondrá el grito en el cielo y se pondrá a discutir sobre si Nyquist esto y los armónicos aquello, y acabará lapidándome en algún subforo diciéndome que qué tengo yo de ingeniero de sonido (y en esto último sí que tendría razón xD ), por mucho que se diga NO vas a poder notar la diferencia a no ser que tengas el oído de Superman, así que mejor guarda recursos y resetea el bucle restando la amplitud directamente... aunque, por supuesto, si eres de los puristas, eres perfectamente libre de elegir la otra opción ;)

Dicho lo cual, hay que restar la amplitud al audio cuando sale por la salida superior, así que añade un módulo de resta en el menú Built-In Module->Math->-, conecta la salida superior del Router, a la entrada superior del módulo de resta, y la entrada inferior conéctala al QuickBus A.

5.7. FINAL DEL BUCLE. MERGE

Y ahora tenemos un pequeño problema, y es que pase lo que pase con la comparación, en ambos casos tenemos que mandar el resultado a dos sitios: al out y a la memoria; al out para que se escuche, obvio :P , y a la memoria para que cuando se inicie el bucle de nuevo obtenga el último valor que tenía la onda y el bucle siga sin problemas.

Como siempre, una salida puede conectarse a varias entradas diferentes, pero cada entrada solo puede conectarse a una salida concreta.

Para resolver esto está el módulo Merge, que de la misma forma que su homónimo del nivel primario, manda todos los eventos que le llegan, en el orden que le llegan, por su única salida. Añádelo en el menú Built-In Module->Flow->Merge y conecta a una de sus entradas la salida de la resta, y a otra entrada la salida inferior del Router.

Y como habrás podido observar en la foto, me he adelantado un par de pasos que te describo ahora mismo.

Primero, ya has visto que el Merge está conectado a todo lo que tiene que estar conectado: las entradas una a la salida de la resta, y otra a la salida inferior del Router; y la salida a una salida de la macro, que he añadido, y a la entrada del módulo de escritura de memoria. Con lo cual queda completado nuestro bucle según el esquema que habíamos hecho.

Pero hay otra cosa, y es una tercera entrada que le he puesto al Merge, que está ahí, y desconectada, para inicializar el bucle cuando cargues el ensemble.

Esto está en el manual, pero te haré un pequeño resumen. Cuando cargas un ensemble, Reaktor inicializa todos los módulos que tiene. En el nivel primario sigue un orden y unas reglas extrañísimas que yo mismo ni conozco ni entiendo al 100%, en core es mucho más fácil.

Para más detalles lee el manual, pero sí te comento que en core lo primero que hace Reaktor, es mandar un evento con valor 0 por cada entrada desconectada, además de mandar un evento por cada constante o QuickConst con el valor que tenga asignado. De esta forma, nuestro bucle se inicializa a 0, que es como tiene que ser.

Pues solo queda salir de la macro, conectar la frecuencia y amplitud a sus respectivas entradas, crear una salida de la CoreCell y conectar la salida de la macro a esta salida. Con lo cual tenemos lista nuestra onda de sierra, que sustituirá a la que ya teníamos, para ser conectada en el nivel primario como cualquier otro módulo de Reaktor... todo esto te lo dejo a ti y lo vemos en la próxima entrada ;)

Con esto terminamos esta entrada, la más larga hasta ahora... veintidós páginas de word sin fotos :S

Deciros que en esta entrada, además de hacer lo típico de describiros módulos, conexiones y demás, he intentado que os llegue un poco el cambio de mentalidad que hay que hacer para trabajar en Core. Hay muchos desarrolladores de Reaktor que no se encuentran cómodos en Core precisamente por eso, sobre todo cuando llevan años trabajando en nivel primario y ya se saben de memoria todos los entresijos y particularidades del programa, sin embargo, a mí core me parece algo excepcional, la mejor parte de Reaktor. Su inmediatez y su acercamiento a un lenguaje de programación per se, lo hacen super agradecido y divertido, por no hablar de las posibilidades que tiene al modificar el audio sample a sample, así que ya sabéis, esperimentadlo vosotros mismos ;)

Saludos a todos.

¿Te gustó este artículo?
3
OfertasVer todas
  • GForce OB-X
    -49%
    GForce OB-X
    66 €
    Ver oferta
  • Focusrite Scarlett 18i20 3rd Gen
    -20%
    Focusrite Scarlett 18i20 3rd Gen
    398 €
    Ver oferta
  • Focusrite Scarlett 18i8 3rd Gen
    -25%
    Focusrite Scarlett 18i8 3rd Gen
    298 €
    Ver oferta
Comentarios

Regístrate o para poder comentar