Plugin de aprendizaje LV2: Sejavu (Delay)

  • 1
#1 por veguita el 16/07/2014
Acabo de programar un pequeño plugin que he llamado Sejavu, porque es una especie de Deja vu sonoro.
Es un delay típico que produce un efecto similar a un Eco.
No admite ninguna configuración y su funcionamiento está limitado a una frecuencia de muestreo de 44100 Hz.

Aquí está el código:
https://www.dropbox.com/sh/blfe2mzx1dmeu02/AACrTgtQd5_TT8vQSNkpGGJra

Nótese que es un plugin con un único propósito de aprendizaje, aunque puede servir para alguien que desee un Delay totalmente simple.
Básicamente tomé el plugin eg-amp que viene entre los ejemplos de LV2, le borré lo que consideré innecesario, y lo modifiqué para que agregue un Delay.
Me interesaría agregarle un control de tiempo que permita reducir o aumentar el tiempo del Delay. Pero por el momento tengo muy poco tiempo disponible y también quiero aprender otras cosas.

Ojala pronto esté preparado para programar un plugin real y no sólo por aprender.

PD: Encuentro que hace mucha falta tener tutoriales de LV2, según dicen con la documentación existente más los plugins de ejemplo los tutoriales no serían necesarios. Sin embargo la documentación está en un lenguaje muy técnico, y hay cosas básicas que no se explican en los ejemplos y uno debe aprender mirando código de otros plugins (no es sencillo leer el código de un software).

"tengo una soledad tan concurrida que puedo organizarla como una procesión"
Mario Benedetti

Subir
#2 por vagar el 17/07/2014
Mucho ánimo, todo lo que merece la pena lleva tiempo. En principio se supone que todo el que se propone programar plugins tiene un mínimo de conocimientos sobre programación y DSP, ésa es una barrera que es imposible bajar y esos conocimientos están explicados en otros sitios. Para hacer un buen plugin hay que ser un progamador de muchísimo nivel, controlando a la perfección las matemáticas de proceso de la señal y conociendo a fondo las características del hardware sobre el que se va a ejecutar para conseguir optimizar el rendimiento.

Supongo que conocerás los tutoriales de Harry van Haaren:

http://harryhaaren.blogspot.co.uk/2012/06/writing-lv2-plugins-lv2-overview.html
http://harryhaaren.blogspot.ie/2012/07/writing-lv2-guis-making-it-look-snazzy.html

Si tienes alguna pregunta concreta respecto a LV2 hazla por aquí e intentaré responderla.

Otra opción, si sólo quieres dedicarte a los plugins, es lo que te comenté de Faust, que te simplifica la parte más mecánica del trabajo y te permite portarlos a varios formatos. Sin embargo aprender C++ es más genérico y tiene más aplicaciones.

Lo mejor para compartir código es hacerlo mediante un repositorio de código fuente, hoy en día el que está más de moda es github. El uso básico de git no es complicado y muchos entornos de programación lo llevan integrado.

Ars longa, vita brevis.
Mi colección de enlaces web en diigo.

Subir
#3 por veguita el 19/07/2014
Gracias.

Sí, había visto los tutoriales de van Haaren, hay poca explicación, aunque me sirvió bastante para aprender, por ejemplo aprendí a rescatar el Sample Rate y usarlo para algunos cálculos al generar ondas en un sintetizador o determinar cuantas muestras hay que grabar para hacer un delay de 1 segundo.

Sin embargo, estos tutoriales pecan de ser demasiado simples, hay muy poca explicación y uno tiene que sacar conclusiones de los ejemplos.

Yo tengo un montón de dudas, algunas se relacionan con lv2, y otras se relacionan con el lenguaje c, que tampoco lo entiendo muy bien.

--- Voy a pasar a resumir mis dudas para ver si me puedes ayudar ---

Básicamente lo que yo hice es un array de 44100 datos:
Alguien escribió:
float record[44100];

Luego voy grabando output en el array record.
El eco se produce al sumar la entrada (input) al array record con un retraso de 44099 muestras (o sea 1 segundo aprox).

Mi primera duda se relaciona más con el lenguaje c, con el cual no estoy totalmente familiarizado.
El problema que tengo es cómo cambiar dinámicamente el tamaño del array para grabar 1 segundo o 2 segundos o medio segundo, según requiera el usuario o grabar según la frecuencia de muestreo.
Según lo que he googleado para esto debería usar malloc.

--- o ---

Mi segunda duda es con la estructura del código de un plugin lv2.
Yo hago mis plugins básicamente copiando la estructura de los plugins de ejemplo.

Primero creo dos typedef. Un typedef enum dónde se definen los números de los puertos, luego un typedef struct dónde se definen las variables globales para el plugin.
Luego creo 7 funciones que no tengo muy claro que es lo que hace cada una: instantiate, connect_port, activate, run, deactivate, cleanup y extension_data.

Por regla general yo defino las variables en el struct, y asigno los valores en la función activate y rescato el sample rate en intantiate. Lo hago de esta forma porque de esta forma se hace en los plugins e ejemmplo.

La pregunta es ¿es obligatorio este esquema?, ¿puedo, por ejemplo, definir una variable en intantiate o en activate para asegurarme de conocer el sample rate antes de definir la variable?

"tengo una soledad tan concurrida que puedo organizarla como una procesión"
Mario Benedetti

Subir
#4 por vagar el 19/07/2014
veguita escribió:
El problema que tengo es cómo cambiar dinámicamente el tamaño del array para grabar 1 segundo o 2 segundos o medio segundo, según requiera el usuario o grabar según la frecuencia de muestreo.


Tienes que crear un buffer correspondiente al máximo tiempo del delay que vayas a permitir a la frecuencia de muestreo. Según la especificación el mejor sitio para reservar esa memoria es con malloc() o alguna función similar en activate() y liberar esa memoria con free() en deactivate().

veguita escribió:
Luego creo 7 funciones que no tengo muy claro que es lo que hace cada una


Es un esquema obligatorio y muy bien definido, tienes que empollarte la especificación:

http://lv2plug.in/ns/lv2core/

Tal como lo hacen los plugins de ejemplo es la manera correcta.

veguita escribió:
¿puedo, por ejemplo, definir una variable en intantiate o en activate para asegurarme de conocer el sample rate antes de definir la variable?


Sí, define una variable accesible por instantiate() y activate(). En instantiate() escribes el valor de la frecuencia que te pase el host y en activate() lo lees para calcular el tamaño del buffer que vas a crear.

También podrías omitir ese paso y crear el buffer en instantiate() y destruirlo en cleanup(), pero la especificación recomienda que uses activate() y deactivate(). Es para que los recursos que reservas no estén ocupados más de lo estrictamente imprescindible. Típicamente un plugin se crea cuando el usuario se lo pide al host, pero no se activa hasta que realmente tiene que funcionar (porque se está reproduciendo o monitorizando en tiempo real el bus en el que está insertado).

Ars longa, vita brevis.
Mi colección de enlaces web en diigo.

Subir
1
#5 por veguita el 20/07/2014
Muchas gracias lgarrido. He logrado resolver los problemas que tenía. Aún faltan cosas por resolver, pero todo a su tiempo.
He resuelto crear el array con malloc() en activate() y liberar la memoria con free() en deactivate().

Le añadí dos controles:
- Tiempo: Controla el tiempo del delay entre 0 y 3 segundos.
- Porcentaje: Porcentaje de la ganancia que se conserva en cada ciclo.

(Estos cambios están actualizados en el dropbox)

Estoy conforme con el resultado porque he conseguido lo que me propuse. Básicamente crear un delay sencillo con un esquema recursivo, o sea, va grabando cíclicamente una pista y la reproduce atenuada en un porcentaje de la ganancia (en parte he logrado esto deliberadamente y en parte ha sido pura chiripa).

Agregué a la carpeta de dropbox un comprimido con el plugin compilado en 64 bits:
https://www.dropbox.com/s/kiv7hzltx3yxysp/sejavu.lv2.tar.bz2
Para los que no cachan esto de compilar bastaría con bajar este archivo y pegarlo en ~/.lv2

En fin como aprendizaje ha sido interesante, y puede ser funcional a más de alguien. Para hacer un efecto de eco similar al que usa Pink Floyd en el tema "Us and Them" este plugin es más que suficiente, y mucho más sencillo de lo que hay disponible.

-- Problemas Pendientes --

Cuando se varía muy rápido el tiempo del delay se escuchan unos ruidos molestos. He leído un poco de teoría sobre como se resuelve este problema, pero no sé como implementarlo en la práctica. Toca seguir aprendiendo :D

"tengo una soledad tan concurrida que puedo organizarla como una procesión"
Mario Benedetti

Subir
#6 por vagar el 20/07/2014
veguita escribió:
Cuando se varía muy rápido el tiempo del delay se escuchan unos ruidos molestos.


Sí, es lo que se llama zipper noise. La forma más común de resolverlo es, cuando detectes un cambio en el control de tiempo, en vez de modificarlo inmediatamente le vas añadiendo o restando una cantidad pequeña en cada ciclo de ejecución hasta que llegues al objetivo.

Ars longa, vita brevis.
Mi colección de enlaces web en diigo.

Subir
#7 por veguita el 20/07/2014
#6
Sí, tuve este mismo problema cuando estaba siguiendo un tutorial de Pure Data. El autor del tutorial (Johanes Kreidler) sugería la misma solución. Sin embargo, pese a que creo haber seguido correctamente las instrucciones, el resultado no fue el esperado. Probaré nuevamente, a ver si esta vez consigo solucionar este problema.

"tengo una soledad tan concurrida que puedo organizarla como una procesión"
Mario Benedetti

Subir
#8 por vagar el 20/07/2014
Con un delay se complica la cosa más todavía, porque cualquier salto en ese control introduce discontinuidad en la onda que le sumas a la original. Si lo quieres hacer bien, bien, además de escalonar la variación del control tienes que pasarle un filtro paso bajo de unos 20 KHz a la onda reconstituida para suavizar el salto.

Ars longa, vita brevis.
Mi colección de enlaces web en diigo.

Subir
#9 por veguita el 20/07/2014
Ahí se me complica más la cosa. Como decía, por el momento, mi objetivo es el aprendizaje. Pero vamos aprendiendo poco a poco. Un filtro superaría demasiado mis conocimientos en este momento.

"tengo una soledad tan concurrida que puedo organizarla como una procesión"
Mario Benedetti

Subir
#10 por vagar el 21/07/2014
Me quedé ayer dándole vueltas y yo creo que lo que he dicho del paso bajo de 20 KHz es una tontería, eso ya lo lleva el DAC. #-o :oops:

A lo mejor se puede disimular el salto haciendo un fade out de la onda con el retardo original y un fade in de la onda con el nuevo retardo, una vez que el nuevo valor esté estabilizado.

Cuando se detecte un cambio se empieza a hacer fade out. Cuando el nuevo valor lleve un tiempo estable se hace un fade in.

Ars longa, vita brevis.
Mi colección de enlaces web en diigo.

Subir
#11 por vagar el 21/07/2014
O esperas a que el nuevo valor se estabilice y haces el fade out seguido inmediatamente por el fade in, no sé lo que funcionará mejor de cara al usuario. La cosa es definir los tiempos para determinar cuando un nuevo valor es estable y la duración de los fundidos. Idealmente deberían ser lo más cortos posibles, es cuestión de ir haciendo pruebas con distintos valores hasta que haya un buen equilibrio entre agilidad de la respuesta del control y el ruido introducido.

Ars longa, vita brevis.
Mi colección de enlaces web en diigo.

Subir
#12 por veguita el 22/07/2014
lgarrido escribió:
yo creo que lo que he dicho del paso bajo de 20 KHz es una tontería

Jajaja, suele pasar. Yo he escuchado que cuando el sonido rebota contra una montaña, esta absorbe parte del sonido. Siendo así, agregar un filtro en un efecto de eco le daría un poco más de realismo. Evidentemente un filtro de 20 KHz con un sample rate de 44,1 KHz no tiene mucho sentido :D Habría que investigar cual debe ser la frecuencia de corte del filtro.
http://www.hispasonic.com/tutoriales/efectos-retardo/38370

lgarrido escribió:
A lo mejor se puede disimular el salto haciendo un fade out de la onda con el retardo original y un fade in de la onda con el nuevo retardo, una vez que el nuevo valor esté estabilizado


No sé si ubicas algo de PureData. Adjunté un patch en PureData que hace lo mismo que este plugin. Hice una prueba usando [line~] para suavizar la transición del delay (como recomiendan en un foro). Básicamente [line~] genera una transición lineal de 50 milisegundos de duración. Con esto ya no suena el zipper noise. Pero suena un ruido similar a una tornamesa haciendo scratching.

Creo que la idea de un fade out, fade in sería una mejor solución. Pero necesito más tiempo para hacer pruebas.
Archivos adjuntos:
Eco PureData.png
BBCode:

"tengo una soledad tan concurrida que puedo organizarla como una procesión"
Mario Benedetti

Subir
#13 por veguita el 24/07/2014
Listo. Ya está arreglado el tema del zipper noise, al final hice un crossfade cuando se estabiliza el valor. El plugin considera el valor como estable cuando se mantiene por 100 ms y el crossfade dura 50 ms.

El código, eso sí, quedó sumamente desordenado, pero lamentablemente tengo mucho sueño y mañana debo trabajar.

Estoy muy contento con el resultado. He hecho un par de pruebas y funciona perfecto :yuju:

Está todo subido al dropbox.

Gracias por toda la ayuda lgarrido :D

"tengo una soledad tan concurrida que puedo organizarla como una procesión"
Mario Benedetti

Subir
#14 por vagar el 24/07/2014
¡Gracias a ti por compartir! Cuando pongas un repo le echo un vistazo al código, a ver si hay algo que pueda contribuir.

Ars longa, vita brevis.
Mi colección de enlaces web en diigo.

Subir
#15 por veguita el 25/07/2014
El fin de semana organizo todo para subir mis cosas a sourceforge.
Sin embargo, no sé si estoy capacitado para hacer un proyecto serio de todo esto.

"tengo una soledad tan concurrida que puedo organizarla como una procesión"
Mario Benedetti

Subir
Respuesta rápida

Regístrate o para poder postear en este hilo