3.4 Sampling

3.4.1 Teoría

Aclaremos un tema potencialmente problemático para evitar alguna confusión: ya aprendió que un sample es la unidad mas pequeña usada para medir y generar sonido en la computadora. Desafortunadamente la palabra `sample` tiene otro significado en música electrónica: significa una pequeña sección (normalmente de un par de segundos de duración) de sonido grabado. Este capitulo cubre el procesamiento de pequeños segmentos de sonido grabado. Primero debemos aprender como funciona “array” en Pd, esto requiere una considerable explicación.


3.4.1.1 Guardando sonido

3.4.1.1.1 Archivos de sonido

Hay varios lugares donde guardar archivos en la computadora; memoria principal y disco rígido. El acceso a la memoria principal es muy rápido comparado con el disco rígido, aunque tiene menor espacio disponible.

En Pd puede guardar sonido en ambos lugares. Guardar en el disco rígido significa que esta guardando un archivo de sonido fijo. Formatos WAV o AIFF son típicamente usados. Use el objeto “writesf~” para guardar sonido en el disco rígido. El argument es la cantidad de canales; esto crea el correspondiente numero de inlets donde conectar las fuentes de sonido que desea grabar. Primero debe usar el message “open [name]” donde elije el nombre del archivo que va a crear. Comience a grabar usando el message “start” y finalice con “stop”.


3.4.1.1.2 Buffers

El otro lugar posible es la memoria principal. Cree un lugar para un sonido usando “array” (Put Array y clickee "ok"). También es una visualización del sonido.

Primero pensemos en un “array” simplemente como un lugar donde guardar números. Un array tiene un numero limitado de lugares donde guardar números. Puede setear este numero haciendo click-derecho sobre el array y seleccionando “properties”. Esto abre dos ventanas: una etiquetada “array” y la otra “canvas”. En la ventana “array” puede setear el tamaño. Este numero significa la cantidad de lugares donde guardar números. Un numero puede ser guardado en cada lugar.

Puede asignar estas celdas usando “tabwrite”. El inlet de la derecha determina la posición y el inlet de la izquierda el valor que desea guardar (como siempre: de derecha a izquierda). En el array, el eje-x es la posición y el eje-y muestra el valor:

Puede usar “array” para representar funciones:

patches/3-4-1-1-2-function1.pd

O sin estiramiento temporal (si este ejemplo causa un stack overflow, inserte “del 0” entre “spigot” y “f”):

patches/3-4-1-1-2-function2.pd

También puede dibujar sobre el array con el mouse, digamos `a mano`. Si mueve el mouse a un valor en el array, el cursor (con forma de flecha) cambiara su dirección y podrá dibujar moviendo el mouse con el botón presionado.

En “properties” (click derecho sobre el array), puede setear lo siguiente:

En la ventana “array”:

  • Name: Como todos los nombres en Pd, use caracteres alfanuméricos sin espacios, no solo numeros.

  • Size: Como describimos arriba.

  • "save contents": Cuando esta marcado, todos los valores del array seran guardados. Si usa arrays largos o muchos arrays el patch se cargara muy lentamente.

  • "draw as points" / "polygon" / "bezier curve": Diferentes tipos de visualización.

  • "delete me": Cuando marcado, esto borra el array! Aunque la caja vacia permanece y tambien debe ser borrada.

  • "view list": Esto muestra todos los valores en una lista.

En la ventana “canvas”:

  • "graph on parent": Esto lo veremos mas adelante en 5.1.1.2. (en otro contexto).

  • X range: Puede setear el rango del eje-x.

  • Y range: Puede setear el rango del eje-y. Los valores fuera de este rango también se guardaran, pero esto agrandara la ventana para que estos valores se muestren.

  • "size": Tamaño visual en el patch.

Un array tambien recibe mensajes y “sends”.

Renombrar:

Un mismo valor en todas las celdas:

Cambiar el tamaño (cantidad de celdas):

El tamaño puede mostrarse en la ventana principal del Pd:

Escribe el contenido a un archivo de texto:

Lee un archivo de texto:

Tambien puede ingresar valores asi: El primer numero determina donde comenzar a guardar, todos los otros valores corresponden a las siguientes posiciones desde ahí en adelante:

También puede crear marcas en los ejes y etiquetarlas:

El primer argument es la posición inicial; el segundo la distancia entre lineas; el tercero la distancia entre las lineas mas grandes.

También las puede numerar:

Primero la posición es etiquetada; después los números que desea mostrar.

Nota: Las lineas y las etiquetas no se guardan en el patch, asi que las debe ingresar cada vez que abra el patch.

Por supuesto también puede guardar sonido en un array. Para la computadora el sonido es – como varias veces mencionamos – una sucesión de números, 44100 números por segundo para ser preciso. Puede guardar un sonido de un segundo en un array; solo necesita 44100 lugares donde guardar. Aquí interviene el objeto “tabwrite~”. Recibe la fuente de sonido y también un bang. A diferencia que “tabwrite” (sin tilde ~) donde ingresa el lugar de guardado en el inlet derecho y el correspondiente valor en el izquierdo, cuando “tabwrite~” recibe un bang, automáticamente comienza desde el primer lugar de guardado y procede a la velocidad del sample (44100 samples/seg). Al mismo tiempo a cada lugar se le asigna el valor recibido en el inlet izquierdo (el sonido), resultando en un total de 44100 números guardados. Todo sonido que siga después no sera guardado. Si desea interrumpir la grabación prematuramente, puede enviar el message “stop”.

patches/3-4-1-1-2-normalize.pd

Si “tabwrite~” recibe un numero en su inlet, este indica desde que lugar (en el array) debe comenzar a grabar (offset).

Una función útil es que es posible aumentar el volumen general luego de grabar. Si por ej. la grabación original es de volumen muy bajo (por ej. la membrana del micrófono no vibro lo suficientemente fuerte y resulto en valores relativamente pequeños), los puede amplificar. Esto se denomina “normalizing”. Para efectuar esto use el siguiente message:

También puede hacer una conexión entre archivos ubicados en la memoria principal y archivos ubicados en el disco rígido con array. Esto se logra con el objeto “soundfiler”. Este objeto le permite cargar un archivo de sonido guardado en el disco rígido a un array, o guardar los contenidos de un array en el disco rígido en forma de archivo de sonido. El comando “read” se usa para cargar un archivo de sonido. Los argumentos para el comando son: el nombre del archivo (con la ruta si es necesario) y el nombre del array donde se desea cargar.

patches/3-4-1-1-2-load-soundfile.pd

Cuando el archivo fue exitosamente cargado, el tamaño en samples del archivo de sonido se emite por el outlet del objeto “soundfiler”.

También puede incluir otros comandos (llamados “flags”) en el message:

El comando resize cambia el tamaño del array para que iguale en samples al archivo de sonido. (esto esta limitado a 4000000 samples – casi 90 segundos, aunque esto se puede cambiar con “maxsize”).

Contrariamente, el comando “write” guarda el contenido de un array en un archivo de sonido en el disco rígido. En este caso, el formato (WAV o AIFF) debe ser ingresado en el flag, luego el nombre (con la ruta si es necesario) del archivo de sonido que desea crear, y luego el nombre del array.

Otros “flags” importantes en este contexto son:

  • normalize: Optimiza la amplitud de niveles en el archivo, como explicamos previamente.

  • rate: Usado para setear el sample rate del archivo.

Como alternativa a un array, también puede usar “table”. Cree un objeto “table”; ingrese un nombre como primer argument y el tamaño en samples para el segundo. Esto crea un un array dentro de un subpatch (clickee sobre el objeto en execute mode) como un array normal. Esto tiene la siguiente ventaja: el grafico para el array normal puede ser muy complejo. Notara esto cuando mueva un array grande dentro del canvas: se movera muy lentamente. Pero cuando la representación gráfica del array esta en un subpatch, el objeto se puede mover mas libremente.


3.4.1.2 Reproducir sonidos guardados

Los archivos de sonido que estan guardados en dispositivos externos (no en la memoria principal) como el disco rígido, pueden ser leídos – es decir reproducidos – en Pd con el objeto “readsf~”. Como con “writesf~” use los mensajes “start” y “stop” (también puede usar “1” y “0”). Ingrese el numero de canales como argument. El outlet mas a la derecha emite un bang al finalizar la reproducción.

patches/3-4-1-2-play-file.pd

Miremos un poco el control level de un `array`: digamos que tiene un array de 10 lugares. Puede usar “tabread” pera leer cada uno de estos lugares:

patches/3-4-1-2-read-array1.pd

El principio es básicamente el mismo para signals, excepto que debe recibir los valores guardados a la frecuencia de 44100 numeros por segundo. Por eso existe “tabread~”. Si desea por ej. reproducir un sonido en un array que dura 1.5 segundos ( = 66149 samples), debe leer los valores en el array de 0 a 66149 a una frecuencia de 44100 valores por segundo. Puede hacer esto con “line~”:

patches/3-4-1-2-read-array2.pd

“tabread~” recibe el nombre del array en su argument. También puede indicar que array desea leer con el message “set [nombre del array]”.

Ahora puede empezar a jugar con estos valores. Por ejemplo, puede reproducirlo al doble de tiempo:

Esto corta la velocidad de reproducción a la mitad. Esto causa que todo suene una octava mas baja por que el estiramiento en el tiempo hace las ondas de sonido el doble de largas, lo que significa que sus frecuencias se cortaran a la mitad por lo tanto sonaran una octava mas abajo.

Esto desemboca en el problema de que cada sample que se usa debe ser reproducido dos veces, de lo contrario habría cortes. Para evitar este problema existe una forma modificada de “tabread~” que es el objeto “tabread4~”, que interpola valores intermedios que genera usando información de los valores que inmediatamente lo preceden y suceden. (información mas especifica de esta función esta disponible en 3.4.4.) En casi todos los casos “tabread4~” es mas apropiado para leer arrays. Esto requiere un espectro de lectura desde 1 a n -2, donde `n` es el tamaño del array que desea leer.

Por supuesto también puede reproducir algo mas rápido, lo que aumentara la frecuencia:

Luego, cuando expliquemos granular synthesis, aprenderemos como alterar tempo y altura independientemente.

Reproducir un sample a la inversa también es posible:

Tambien puede usar el objeto “phasor~”:

“phasor” genera una serie de números desde 0 a 1 como signal. Si multiplica estos valores por 66148, tiene una serie de numeros desde 0 a 66148. Ingrese 0.75 como frecuencia para que la serie ocurra exactamente en 1.5 segundos.

Otra posibilidad es crear otro array con una disposición para la lectura y usarla para reproducir el array anterior (el sonido):

patches/3-4-1-2-read-array3.pd

También puede usar un array para controlar la amplitud:

O para controlar la frecuencia:

Nuevamente podemos ver que: solo estamos usando números para controlar varios parámetros.

Puede usar tantos “tabread~” como desee para leer del mismo array, pero nunca debe tener dos arrays con el mismo nombre, esto claramente provocara errores.


3.4.1.3 Audio delay

En el Capitulo 2.2.3.1.2 mencionamos como números o series de números pueden ser retrasados. También se puede hacer esto con signals. Esto se logra creando un buffer donde se escriben signals y estas se leen seguidos de cierto retraso. Para crear este buffer cree un objeto “delwrite~”. El primer argument es un nombre elegido libremente; el siguiente es el tamaño en milisegundos. Como input dele la señal que desea escribir en el buffer. Cuando el buffer se llena, se vuelve a escribir desde el comienzo. Si el buffer es de 1000 milisegundos, los últimos 1000 milisegundos de la señal entrante están guardadas en el buffer.

Use “delread~” para leer el buffer. El primer argument también es el nombre del buffer; el segundo es el retraso (en milisegundos; puede ser modificado usando un valor control data en su inlet):

patches/3-4-1-3-delay.pd

Lógicamente la cantidad de retraso en “delread~” debe ser menor o igual al tamaño del buffer. Si tiene un delay de 2000 milisegundos pero tiene un buffer de 1000 milisegundos, claramente no funcionara. Usar un intervalo negativo también es imposible, incluso el Pd no puede ver el futuro. Puede usar cuantos objetos “delread~” quiera para leer simultáneamente el delay buffer. No se puede visualizar el patron de onda de un buffer.

Aunque puede modificar el intervalo de retraso de un “delread~”, debe usar control data y existe la posibilidad de error exediendo determinada velocidad (otra vez un coflicto entre control data y signals). Por esta razon existe un objeto especial para una lectura variable del delay buffer llamado “vd~” (por `variable delay`). Ud le da el intervalo de retraso (en milisegundos) como signal y puede modificarla cuanto quiera (nuevamente, no puede usar valores negativos ni exeder el tamño del buffer):

“vd~”, como “tabread4~”, crea una interpolación.


3.4.2 Aplicaciones

3.4.2.1 Un simple sampler

patches/3-4-2-1-simple-sampler.pd


3.4.2.2 Con velocidad variable

patches/3-4-2-2-sampler2.pd


3.4.2.3 Cualquier posición

Aquí una forma de seleccionar cualquier posición en el sample:

patches/3-4-2-3-sampler3.pd


3.4.2.4 Sampler-player

Si cambia la representación gráfica como describimos previamente en (2.2.4), su patch puede verse así:

patches/3-4-2-4-sampler-big.pd

Cuatro objetos canvas forman el colorido fondo de los sliders y el array. Nota: Los gráficos que fueron creados últimos siempre aparecen encima de los otros. Si por ej. ud. tiene un “array1” y luego hace un objeto canvas colorido; debe crear “array1” una vez mas (simplemente copie, luego borre la anterior y pegue la que copio) para que aparezca sobre el objeto canvas.

Para explicar exactamente como fue hecho esto:

Y en el subpatch:


3.4.2.5 Generador de loop

patches/3-4-2-5-loop-generator1.pd

Pero con este loop generator, pueden ocurrir clicks. Primero es altamente recomendado que ponga el array en un subpatch, por que el grafico requiere un montón de procesamiento. Segundo en el final del loop debe brevemente ir hasta 0 para que no halla un un salto abrupto en el valor (lo que causaria un click). Para esto, necesita programar un `windowing`. Sincronizado con la lectura del array, esto esta determinado en las amplitudes de otro array (aquí, “crown”) que controla la envoltura dinámica (dynamic envelope). Este envelope tiene un valor de cero al principio y al final para asegurar que no halla un cambio brusco cuando el loop se repite. En lugar de un window “crown”, también puede usar una ventana “Hanning”, que usa parte de la función seno (esto lo veremos mas adelante). Por supuesto que el array “crown” podría estar en otra ventana también, pero aquí lo dejamos así para ser mas claros.

Una versión mas simple es crear un feedback (retroalimentacion):

patches/3-4-2-5-loop-generator2.pd

Una contra es que aquí hay una duración máxima del loop; aquí de 10000 milisegundos.


3.4.2.6 Reverb

Puede simular un efecto `reverb` si la señal de retroalimentacion es cada vez mas débil:


3.4.2.7 Textura

O puede crear una textura:

patches/3-4-2-7-texture.pd

Debe tener cuidado de no `explotar` la retroalimentacion, que su volumen no se incremente exponencialmente.


3.4.2.8 Comb filter

Puede construir un comb filter usando un delay de audio. La idea es que sume una señal retrasada a la original. Esto resulta en amplificaciones y cancelaciones a intervalos regulares, lo que da al espectro la apariencia de un peine:

patches/3-4-2-8-combfilter.pd


3.4.2.9 Doblador de octavas

Si conoce la frecuencia fundamental de una señal, puede construir un doblador de octavas de la siguiente forma: Tomemos una onda...

… y esta señal es retrasada por la mitad de un periodo...

… al sumarlas resultan en 0 (= cancelación). Si retrasa una señal periodica por la mitad de un periodo y la suma a la original, el tono fundamental (y todos los parciales par) son cancelados. Esto se vería así:

Pero no funciona muy bien de esta forma. Debe recordar que el Pd procesa todo la data de audio en bloques de 64 samples (al menos que cambie la configuración), por que es mas eficiente que procesar individualmente cada sample (cf. 3.1.1.3.2). En el patch de arriba, tiene un delay de 1,136 milisegundos, o 50 samples. Puede saltear este problema usando un buffer con un retraso de un-bloque (64 samples = 1,451 ms) para leer el original; lo mismo para el offset del delay:

patches/3-4-2-9-oktavedoubler.pd


3.4.2.10 Algoritmo Karplus-Strong

Un uso especial del loopeo es el algoritmo Karplus-Strong. Es uno de los primeros ejemplos de síntesis de modelado físico, un proceso que intenta replicar lo que ocurre cuando un objeto físico vibra. En nuestro ejemplo, el modelo físico es una cuerda pulsada. Cuando una cuerda es pulsada, primero vibra caoticamente y luego se ajusta al largo de la cuerda. También pierde energía, la vibración se extingue. Esto puede ser reconstruido matemáticamente tomando un extracto de ruido blanco y reproduciendolo periódicamente una y otra vez escribiéndolo y leyéndolo desde un buffer:

patches/3-4-2-10-karplus-strong1.pd

El efecto de cuerda puede ser realzado si el material con que comienza vibra cada vez mas `suavemente`. Esto se logra calculando el promedio: el promedio de cada dos samples se toma y este resultado es escrito en el buffer en lugar de los valores originales. La vibración se vuelve cada vez menos `angular`. Use el objeto “z~” (Pd-extended) para retrasar un sample; ingrese el numero en samples como argument:

patches/3-4-2-10-karplus-strong2.pd

El tono es diferente cada vez. Esto es por que “noise~” produce números aleatoriamente, los que son diferentes cada vez, claro. También podemos agregar unos cálculos para las frecuencias resultantes:

patches/3-4-2-10-karplus-strong3.pd


3.4.2.11 Mas ejercicios

a) Construya una función de grabación para el sample player.

b) Cree un patch para reverb o textura con diferentes tiempos de delay para la señal de entrada, por ej, con múltiplos de la serie de Fibonacci (en la que el próximo numero es siempre la suma de los dos previos:0 1 1 2 3 5 8 13).

c) Use diferentes sonidos Karplus-Strong para hacer texturas de densidad variable.

d) Aplique un comb filter a los patches presentados anteriormente.


3.4.3 Apéndice

3.4.3.1 Oscilador de arrays

Una forma de simplificar la combinación de “tabread~” y una señal multiplicada de “phasor~” es “tabosc4~”. Este lee un array a la frecuencia que ud. ingresa. Una limitación en este método es que el tamaño del array debe se potencia de dos (ej., 128, 512, 1024) mas tres puntos (aquí, 1027 = 1024 + 3).

patches/3-4-3-1-arrayoscillator.pd

Esto se acerca a “wave shaping”, tema que sera luego explicado en el capitulo 3.5. Puede dibujar cualquier onda en el array usando el mouse.


3.4.3.2 Reproducción de array

También otra simplificación es “tabplay~”; simplemente reproduce el array a la velocidad original (cuando recibe un bang). También puede indicar el lugar de inicio y fin de reproducción (punto de partida y duración en samples):

patches/3-4-3-2-simply-play-array.pd


3.4.3.3 Reproduciendo un array en un bloque

Los objetos “tabwrite~” y “tabread~” tambien tienen otra forma especial: “tabsend~” y “tabreceive~”. Leen y escriben un array en syncro con cada bloque (“block”) (3.1.1.3.2). “tabwrite~” escribe cada bloque en un array (por supuesto este debe ser del tamaño del block, que predeterminadamente es de 64 samples en Pd). “tabreceive~” lee el array en cada block. Regresaremos a esto luego en el capitulo de FFT (3.8).


3.4.3.4 Glissandi de samples

Ya sabe que puede reproducir un array a velocidad normal o a una octava mas arriba, etc. Pero que si desea un glissando desde la octava hasta la frecuencia original? Para esto, necesita subdividir en “indicador principal” y “adición”. El “indicador principal” corre a velocidad normal sobre el array. Usemos un array de 132300 puntos como ejemplo, esto equivale a 3000 milisegundos:

Luego viene la “adición”, que es lo que hace el glissando. Usemos un glissando que comience cinco semitonos sobre la frecuencia original y regrese a la frecuencia original en 500 milisegundos. Necesita hacer un “line~” de esto en reversa, y luego elevar al cuadrado sus valores:

Sobre esto, debe determinar el factor para las frecuencias de los cinco pasos cromaticos (cf. 3.1.1.4.3)...

… y finalmente hacer el siguiente calculo:

Esta es la “adición” que es sumada al “indicador principal”:

patches/3-4-3-4-sample-glissando1.pd

Puede usar cualquier glissando desde la altura que desee; también puede usar valores negativos:

patches/3-4-3-4-sample-glissando2.pd

También, para alejarse de la nota original:

patches/3-4-3-4-sample-glissando3.pd


3.4.3.5 Síntesis aditiva con array

Como una funcion especial en Pd, puede crear una suma de tonos sinusoidales en un array – síntesis aditiva como se describió en el Capitulo 3.2. Esto se logra usando el message “sinesum”. El primer argument es el (nuevo) tamaño del array (debe ser potencia de 2; tres puntos serán agregados a este numero automáticamente para asegurar una conexión optima entre el principio y el final de la fase) y también los factores de volumen para cualquier cantidad de parciales:

patches/3-4-3-5-sinesum.pd

En lugar de ondas sinusoidales, también puede usar “cosinesum” para trabajar con ondas coseno.


3.4.3.6 Latencia

Audio delay (retraso de audio) a veces ocurre cuando uno no lo desea. Puede escucharlo cuando tiene un micrófono conectado y produce un sonido extremadamente corto cerca del micrófono, por ej. un chasquido de dedos:

La placa de sonido y especialmente el sistema operativo determina el largo de esta “latencia”. Idealmente la latencia es tan corta (menos de 5 ms) que el oido humano no percibe el retraso. Esto requiere un procesador de computadora rápido, una buena placa de sonido y un sistema operativo apropiado. Puede ajustar la latencia en Media Audio settings:

En Microsoft Windows, no se puede tener latencias menores a 50 ms sin errores (junio 2008). Nota del traductor: 12 ms con ASIO4ALL tambien en Microsoft Windows (noviembre 2011).


3.4.4 Para los especialmente interesados

3.4.4.1 4-point interpolation

En este ejemplo puede ver como trabaja la interpolación de “tabread4~”:

patches/3-4-4-1-four-point-interpolation.pd

El salto de 1 a -1 es `suavizado` por una especie de interpolación sinusoidal. Como su nombre implica, cuatro puntos son usados y alterados: los dos directamente hacia adelante y los dos directamente detrás del intervalo que desea interpolar.


3.4.4.2 Sample-wise delay

Una forma de retrasar algo por determinada cantidad de samples con “delread~” y “vd~” es usando un subpatch (sino el problema del tamaño del bloque, previamente descripto en relación a doblador de octavas).

patches/3-4-4-2-samplewise-delay.pd