Crear una envolvente

Para que cada uno de los eventos generados en el audio no tenga clics al comienzo y al final, debemos darle una envolvente que crezca y disminuya gradualmente. Debemos recordar que en el momento de modificar o crear un evento sonoro, nosotros modificamos o creamos cada una de sus muestras individualmente. Entonces la función que cree una envolvente debe devolver un valor que tenga que ver con la muestra que estamos manejando actualmente.

Para textura usaremos una envolvente muy sencilla.

Nuestra envolvente más sencilla tendrá forma de trapecio. O sea que tendremos una porción de ataque que irá desde 0 hasta 1, una parte estacionaria que se mantedrá en 1, y una parte correspondiente a la caída, que irá desde 1 a 0. Además, debido a que será usada para audio, directamente usaremos los valores en muestras.

dibujo de envolvente en forma de trapecio

Para aplicar ésta envolvente a un evento sonoro, nos basta con crear una función que devuelva un valor entre 0 y 1, el cual será un multiplicador para cada muestra de dicho evento. Cuando el valor sea 0, significará que nos encontramos o bien al comienzo o bien al final del evento y cuando sea 1, es porque estamos en la parte estacionaria de la envolvente y todas las muestras de ésta parte del evento permanecerán sin modificar.

Debemos definir cuales serán los argumentos de la función y sus significados. La forma que usaremos será la de indicar la cantidad de muestras para el ataque y para la caída; la función calculará automáticamente hasta donde aplicar el ataque y desde donde aplicar la caída. Pero para eso es necesario conocer también la duración en muestras del evento y en que muestra nos encontramos actualmente.

Argumentos

Salida

Un multiplicador al valor de la muestra actual.

Proceso

En la porción de ataque el valor devuelto aumenta, en la parte estacionaria se mantiene y en la caída disminuye. Por lo tanto debemos usar algún mecanismo para determinar en que porción de la envolvente nos encontramos. Esto es muy facil usando condicionales.

El lugar donde termina el ataque surge inmediatamente del argumento del ataque que se le pasa a la función, debido a que nuestra envolvente siempre comienza en la muestra 0. Las muestras que estén entre 0 y ataque serán multiplicadas por un valor entre 0 y 1, por lo tanto éste valor resulta de dividir el lugar actual por la cantidad de muestras que dura el atauqe, entonces cuando el lugar actual esté en la misma muestra que el final del ataque, el resultado será 1.

multiplicador

El argumento sobre la caída que se pasa a la función es la cantidad de muestras que la misma dura, pero nosotros necesitamos saber el lugar donde comienza para poder aplicarla. Ese valor es justamente la duración del evento menos la duración de la caída.

comienzo

Pero ahora el proceso es inverso al del ataque, nuestra fracción debe ir desde 1 a 0, y cuando la muestra actual esté al final del evento, el resultado debe ser 0. Por lo tanto debemos dividir entre valores que al comienzo sean iguales y el numerador disminuya hasta ser cero. Esto se logra haciendo que el numerador sea la resta de la duración y el lugar actual, entonces la porción resultante de ésta resta será igual a la porción de la caida. Como el lugar actual siempre aumenta, al final del evento éste será igual a la duración y la resta dara 0.

multiplicador

Para los valores que estén entre el ataque y la caída siempre devolvemos 1.

Por último, como el comienzo de la caída puede ser ántes de que termine el ataque, los condicionales en la función no deben ser excluyentes. De éste modo nos aseguramos que el último valor del evento sea cero. En éste caso la función nunca llegará a devolver 1, y el evento no llegará a su pico de amplitud. Se podría establecer otras condiciones para que la caída siempre comience después de finalizado el ataque y calcular la llegada a 0 al final del evento pero no lo haremos aquí.

Diagrama de flujo

Diagrama de flujo Envolvente

Código

float Envolvente(int ataque, int caida, int dur, int lg_act)
{
  float mult = 0.0;
  int com_caida = dur - caida;
	
  if(lg_act < ataque)
    mult = (float)lg_act / (float)ataque;
  if(lg_act > com_caida)
    mult = (float)(dur - lg_act) / (float)caida;
  if(lg_act >= ataque && lg_act <= com_caida)
    mult = 1.0;
  return mult;
}

Anterior  Inicio  Siguiente
Valor actual de la onda   Procesar los eventos