Archive for April, 2007

Síntesis musical para mí (o para torpes en general :) II

2) Senos!!!

Después del ruido del capitulo anterior, vamos a pasar a algo ligeramente más interesante: la onda senoidal. Como podéis ver si seguís el enlace a la pagina sobre la onda senoidal en la wikipedia, junto con dibujitos y explicaciones interesantes, la formula es:

y = A·sin(w·t + fase)

donde A es la amplitud de la onda (la distancia entre una cresta y un valle), w es la frecuencia en radianes/segundo y t es el tiempo. Vamos a ignorar la fase porque no nos hace falta :)

Como nos gusta trabajar con frecuencias en hercios, multiplicamos por 2·pi. Además, tenemos que t = samples generados / frecuencia de muestreo. Por lo tanto en el código tendremos algo como:

y[i] = A*sin(2*pi*frecuencia*(i/sampling_rate))

para el sample i-ésimo desde el inicio del sonido. Vamos a ver el trozo de código correspondiente (lo que falta es, básicamente, la inicialización del capítulo anterior):

void play(void *userdata, Uint8 *stream, int len)
{
    int num_samples = len / 2;
    Sint16 *dst_buf = (Sint16*) stream;
    for (int i=0; i<num_samples ; ++i)
        buffer[i] = sin(2.0*M_PI*440.0f*(i+pos)/(float)SAMPLING_RATE);
    
    // Clipping y conversion a Sint16
    for (int i=0; i<num_samples; ++i)
    {
        float v = buffer[i];
        if (v > 1.0f)
            v = 1.0f;
        else if (v< -1.0f)
            v = -1.0f;

        dst_buf[i] = (Sint16)(32767.0f*v);
    }
    pos += num_samples;

}

Detalles destacables de esta función:

  • se trabaja con un buffer de floats y se convierte a entero al final. No mola ir perdiendo precisión por el camino, sobre todo cuando hagamos cosas mas complejas
  • hay clipping a [-1.0, 1.0]. Ahora mismo es completamente innecesario porque sin() devuelve valores en ese intervalo, pero cuando empecemos a mezclar varios sonidos vendrá bien :)
  • en la llamada a sin(), el 440.0f es la frecuencia de la nota que suena (un La4), y pos es una variable global que guarda el índice del ultimo sample generado entre llamadas a play(). Es feo, pero para un ejemplo pequeño va bien.

En el próximo episodio (ahora es tarde y tengo sueño), veremos otros osciladores típicos y cosas interesantes que se pueden hacer con ellos.

EDIT: herotyc me ha enviado una versión stand-alone del ejemplo de este capítulo. ¡Muchas gracias! :)

Comments (14)

Síntesis musical para mí (o para torpes en general :)

Por petición popular (hola pplux) voy a hacer una pequeña introducción a la síntesis de audio desde el punto de vista de un coder sin experiencia previa en DSP que pretende hacer un sintetizador para intros de 4k (yo). Por supuesto, incluirá ejemplos de código. C99 y SDL for teh win.

0) Disclaimer
Esto no tiene por qué ser la forma correcta de hacer las cosas. Hasta donde yo sé funciona, pero seguro que hay muchos fallos en muchos sitios. Son bienvenidos los comentarios, tanto para corregirme los fallos como para comentar cualquier otra cosa al respecto :). Y como se suele decir en estos casos, si alguno de los ejemplos de código rompe algo o deja sordo a alguien yo no me hago responsable.

1) Haciendo ruido

En primer lugar, el clásico trozo de codigo de inicialización que todo el mundo copia y pega y nadie lee:

#include "SDL.h"

#define BUFFER_SIZE 1024    // Longitud del buffer, en samples

// Este comentario esta extraido de SDL_audio.h, porque me ha molado
/* This function is called when the audio device needs more data.
 * 'stream' is a pointer to the audio data buffer
 * 'len' is the length of that buffer in bytes.
 * Once the callback returns, the buffer will no longer be valid.
 * Stereo samples are stored in a LRLRLR ordering.
 */
void play(void *userdata, Uint8 *stream, int len)
{
    int num_samples = len / 2;
    Sint16 *buf = (Sint16*) stream;
    for (int i = 0; i<num_samples ; ++i)
        buf[i] = rand()%65535-32768;

}

int main(int argc, char **argv)
{
    SDL_AudioSpec desired;
    desired.freq     = 44100;        // Frecuencia de muestreo                             
    desired.format   = AUDIO_S16SYS; // Formato de las muestras
    desired.channels = 1;            // Numero de canales
    desired.samples  = BUFFER_SIZE;  // Tamaño del buffer en samples
                                     // (potencia de 2)
    desired.callback = play;         // Callback
    desired.userdata = NULL;         // Puntero a datos
    
    SDL_Init(SDL_INIT_AUDIO);        
    SDL_OpenAudio(&desired, NULL);
    SDL_PauseAudio(0);
    getchar();
    SDL_PauseAudio(1);
    SDL_CloseAudio();
    SDL_Quit();
}


La parte importante esta en buf[i] = rand()%65535-32768; Como hemos elegido AUDIO_S16SYS significa que utilizamos muestras de 16 bits con signo [-32768, 32767], con endianismo dependiente del sistema (como accedemos al buffer con punteros a entero de 16 bits, el endianismo no será problema). Generamos numeros aleatorios en ese rango y ya tenemos ruido blanco saliendo de nuestros altavoces.

No se pierdan el proximo episodio.

EDIT: He cambiado el titulo, de “sintesis de audio” a “sintesis musical” porque lo primero era demasiado general :)

Comments (777)

Night of the proms + Breakpoint

Por fin llegó el dia de volver a ver a Mike Oldfield en España. Habia que aprovechar la ocasión y estuve en los conciertos de Valencia y Madrid.

El primer dia en Valencia estuve en la primera fila (fotos proximamente :D), y en el concierto de Madrid me vi relegado a la grada por comprar la entrada a ultima hora. Ambos estuvieron geniales, y me parece que el año que viene iré al NOTP toque quien toque.

Ahora solo falta que sea verdad lo que dijo el señor Oldfield de venir proximamente a dar algun concierto mas largo.

En otras noticias, estoy en la breakpoint. Shash y yo hemos presentado 4k, y la compo es dentro de 1:40h. Ahora que el partycoding ya ha pasado… FIESTA! \o/

Seguiremos informando.

Comments (1)

This blog is protected by Dave\'s Spam Karma 2: 17770 Spams eaten and counting...