Procesar los eventos

Ahora viene una de las dos partes principales de la aplicación: pasar a audio los eventos generados.

Debemos transformar todos los parámetros de cada evento a muestras o a valores que puedan ser usados para ser generado el audio. Ya tenemos un archivo de audio abierto y dispuesto a ser usado y hemos creado un bufer que guardará las muestras creadas a partir de los eventos.

Si bien textura tiene acoplado las dos grandes partes de generación (la de datos y la de audio), ésta parte, la de generación de audio, se podría establecer como una aplicación independiente para que la generación de los eventos pueda ser cualquiera. Esa es una de las razones de que como argumento de la función se le pasa el conjunto de eventos.

Argumentos

Salida

No hay valor de retorno. Pero si la función termina bien, los eventos se habrán guardado en un archivo de audio.

Proceso

Debido a que los eventos se pueden superponer en el tiempo, una muestra puede tener la suma de más de un valor de evento. Esto provocará con seguridad, que las muestras tengan un valor mayor que 1 o que -1. Para solucionar ésto, primero creamos una matriz temporal de decimales, que será igual al bufer de salida para el audio. Guardaremos en éste bufer temporal todos los valores generados y luego lo normalizaremos. Entonces copiamos estos valores al bufer de salida convertidos a muestras, para luego escribirlos en el archivo de salida.

Un detalle importante en cuanto al tamaño del bufer, es que no sabemos realmente cual es la duración del audio. El usuario ingresa una duración, pero como la generación de los comienzos de cada evento es aleatoria dentro de ésta duración, la suma del comienzo de un evento y de su duración puede caer más allá de la duración dada. Debemos calcular ésto usando una función auxiliar.

La síntesis usará una onda periódica, entonces primero debemos crear la tabla, que será usada más adelante.

Para acceder a cada muestra, iteramos por cada canal, por cada evento de cada canal y por cada muestra de cada evento. Para lo último, convertimos el comienzo y la duración de cada evento a muestras, multiplicando sus valores por la frecuencia de muestreo. A cada muestra le asignamos un valor que tendrá que ver con la onda periódica creada anteriormente, con el paneo y con la envolvente que apliquemos.

Cuando hallamos escrito todo al audio de salida, habremos terminado y la aplicación finalizará con un archivo de audio pronto para ser escuchado y disfrutado.

Diagrama de flujo

Diagrama de flujo ProcesarEventos

Código

void ProcesarEventos(DAUDIO *sal, EVENTO *evs, int ev_cant, mus_sample_t **sbuf)
{
  int i, j, ev, inicio, final;
  float mues, norm = 0.0, pan;

  float *tabla;
  int largo = 4096;
  float **buf_temp;
  float lg_act = 0.0;
  int dur;

  buf_temp = (float **)calloc(sal->canales, sizeof(float *));
  for(i = 0; i < sal->canales; i++)
    buf_temp[i] = (float *)calloc(sal->mu_can, sizeof(float));

  tabla = CrearTabla(0.0, largo, FuncionSeno);     

  printf("\nUsando una onda sinusoidal para la sintesis con interpolacion lineal ...\n");

  for(i = 0; i < sal->canales; i++)
    {
    for(j = 0; j < ev_cant; j++)
      {
      inicio = evs[j].comienzo * sal->frec_muestreo;
      final = inicio + evs[j].duracion * sal->frec_muestreo;
      dur = final - inicio;
      for(ev = inicio; ev < final; ev++)
        {
        mues = ValorActualOnda(evs[j].amplitud, tabla, largo, lg_act, InterpLineal);
        LugarActualTabla(&lg_act, evs[j].frecuencia, largo, sal->frec_muestreo);

        /* paneo por intensidad */
        if(i == 0)
          pan = evs[j].paneo;
        else
          pan = 1 - evs[j].paneo;
        /* fin paneo por intensidad */

        buf_temp[i][ev] += (mues * pan * Envolvente((int)(dur * .2),(int)(dur * .2), dur, (ev - inicio)));
        /* esto es para normalizar */
       if((buf_temp[i][ev] >= 1.0 || buf_temp[i][ev] <= -1.0) && abs(buf_temp[i][ev]) > norm)
         norm = abs(buf_temp[i][ev]);
        }
      }
    }
  CopiarNormalizado(buf_temp, sbuf, sal, norm);  /* normalizo y copio los datos */

  mus_sound_write(sal->na, 0, sal->mu_can - 1, sal->canales, sbuf);
  mus_sound_close_output(sal->na, sal->muestras * mus_bytes_per_sample(sal->formato));
  free(buf_temp);
  free(tabla);
}

Función CopiarNormalizado

Normaliza cada valor y lo copia al bufer que será usado por sndlib para escribir en el audio. La función es muy sencilla y recorre cada elemento del bufer de valores y del bufer de audio. Antes de copiar al bufer de audio, los valores se convierten a muestras usando MUS_FLOAT_TO_SAMPLE que es una macro de sndlib.

Los argumentos son el bufer de valores, el bufer de muestras, datos del audio y un número por el cual se divide cada valor para normalizarlo.

Código

void CopiarNormalizado(float **ebuf, mus_sample_t **sbuf, DAUDIO *sal, float norm)
{
  int i, j;

  for(i = 0; i < sal->canales; i++)
    for(j = 0; j < sal->mu_can; j++)
      sbuf[i][j] = MUS_FLOAT_TO_SAMPLE((ebuf[i][j] / (norm + 1.0)));
}

Anterior  Inicio  
Envolvente