domingo, 20 de julio de 2014

Snippet - Notas musicales con "Beep"

Beep


¡Hola de nuevo!
Como ya hacía lo suyo que no sacaba nada, pues me dió por poner una función cortita, pero curiosa: Reproducir música con los "Beeps" de Windows.

Pues venga, empiezo con una introducción:

Seguro que habéis escuchado alguna vez un "Beep". Antes se escuchaban más, ahora ya no tanto, pero un Beep es ese sonido que solía escucharse cuando nos salía la ventanita de "StickyKeys", que saltaba al pulsar  5 veces Shift (creo).
Esta función puede sacar el sonido por la placa de sonido, o por el altavoz (Windows 7 y superiores generalmente). Quien quiera tener más claro su funcionamiento, puede leer toda su inforación en la página de MSDN: MSDN - Beep.

Ahora al tema.
La función Beep() de la librería windows.h (no olvidéis el include), coge 2 parámetros: La frecuencia del sonido a reproducir, en hertzios, y la duración del sonido, en milisegundos.
Quedaría así: Beep(unsigned int hz, unsigned int ms).
Ahora, bien, dado que nosotros (al menos yo) solo conozco la frecuencia de la nota LA (440 Hz), si quiero componer una "cancioncilla" con esta función, me va a costar mucho. Para facilitar este proceso, dejo aquí uno de los snippets del día:

enum notas{
    DO=1,
    DO_S=2,
    RE,
    RE_S,
    MI,
    FA,
    FA_S,
    SOL,
    SOL_S,
    LA,
    LA_S,
    SI
};

void note(int octava, int nota, int ms){
    if(octava<=0 || nota<0 ||ms<=0) return;
    double oct = octava, no = nota;
    double n = 440.0 * exp(((oct-4)+(no-10)/12)*log(2));
    if(n<37 || n>32767) return;
    Beep(n, ms);
}


Vamos a desglosar la función note(int octava, int nota, int ms).
Esta función coge la nota y la octava (mirad en wikipedia por ejemplo para saber qué es), las transforma a una frecuencia, y se la pasa a Beep().
El primer "if", comprueba que ningún valor sea negativo ni 0. No tiene mucho más.
Ahora, creamos 2 variables que lo único que harán será convertir int a double, ya que sinó las operaciones no guardarán decimales.
La variable double n será el contenedor para nuestra ecuación. Esa ecuación la podéis encontrar por ahí. (Por si os lo preguntáis, yo no me la sé).
El segundo "if" tiene una finalidad bastante concreta, y es la función Beep() debe recibir una frecuencia de entre 37 y 32767 hertzios.
Y bueno, finalmente llamamos a la función, pasándole los hertzios que hemos obtenido, y los milisegundos.

¡No nos olvidemos de la parte más importante del código! Sin el enum sería algo complicado saber qué numero corresponde a cada nota.

Para finalizar esta entrada, os paso una cancioncilla muy conocida que convertí a "Beep".

Ciao y ¡nos vemos en otra entrada!


void tetris(int velocity=200){
    note(4, LA, velocity*2);
    note(4, MI, velocity*1);
    note(4, FA, velocity*1);

    note(4, SOL, velocity*2);
    note(4, FA, velocity*1);
    note(4, MI, velocity*1);

    note(4, RE, velocity*2);
    note(4, RE, velocity*1);
    note(4, FA, velocity*1);

    note(4, LA, velocity*2);
    note(4, SOL, velocity*1);
    note(4, FA, velocity*1);

    note(4, MI, velocity*2);
    note(4, MI, velocity*1);
    note(4, FA, velocity*1);

    note(4, SOL, velocity*2);
    note(4, LA, velocity*2);

    note(4, FA, velocity*2);
    note(4, RE, velocity*2);

    note(4, RE, velocity*4);

    thSleep(velocity);
    note(4, SOL, velocity*2);
    note(4, LA_S, velocity*1);

    /*****************************************************/

    note(5, RE, velocity*1);
    note(5, RE, velocity*1);
    note(4, DO, velocity*1);
    note(4, LA_S, velocity*1);

    note(4, LA, velocity*3);
    note(4, FA, velocity*1);

    note(4, LA, velocity*1);
    note(4, LA, velocity*1);
    note(4, SOL, velocity*1);
    note(4, FA, velocity*1);

    note(4, MI, velocity*2);
    note(4, MI, velocity*1);
    note(4, FA, velocity*1);

    note(4, SOL, velocity*2);
    note(4, LA, velocity*2);

    note(4, FA, velocity*2);
    note(4, RE, velocity*2);

    note(4, RE, velocity*4);
}

8 comentarios:

  1. Hola, como se pueden reproducir don notas al mismo tiempo?

    ResponderEliminar
  2. Está muy interesante esto. Sería útil para algún juego sencillo con una melodía tipo juegos antiguos. XD. Sigue publicando más asiduamente. Un buen blog necesita contenido cada cierto tiempo.

    ResponderEliminar
    Respuestas
    1. Me encantaría escribir más, pero no encuentro tema.
      Es probable que reviva el blog de aquí a poco...

      Eliminar
  3. thSleep(velocity); no será Sleep(velocity);? Mola =)

    ResponderEliminar
    Respuestas
    1. void thSleep(uint32_t ms){
      this_thread::sleep_for(chrono::milliseconds(ms));
      }

      Esa era la función. Más que nada es para no tener que escribir todo eso.
      No uso Sleep(), para evitar usar la WinApi, aunque bueno, en ese ejemplo hay que usarla de todas maneras, así que jaja

      Eliminar
  4. Te pasaste con esta entrada Ivan :)

    Todo el blog es muy interesante, un saludo.

    ResponderEliminar