Crea una aplicación para piano con JavaScript

Mi objetivo es hacer un piano funcional. Una vez que haya hecho el piano, quiero ser capaz de construir una base de datos de canciones jugables, para poder seguir el juego. Nada elegante, como la banda de rock, sino simplemente una aplicación de piano con canciones con las que puedes jugar. Si desea seguir, aquí está mi prototipo viable para el que trabajaremos: https://my-little-piano-app.herokuapp.com/

Este es un proyecto divertido, y sería un pequeño juego divertido para tus hijos. Entonces empecemos. Estoy dividiendo este proyecto en 3 componentes principales:

1 El HTML

2 El JavaScript

3 La base de datos Node.JS

Para el HTML, originalmente quería hacer clic en cada tecla, por lo que la nota se reproducía al hacer clic. Más tarde, descubrí que al hacer clic en cada tecla conseguía una ejecución mucho más lenta y un sonido poco realista. Así que lo cambié y asigné cada sonido a una pulsación de tecla individual. Esto permitió melodías más complejas y un sonido más realista. Sin embargo, hay ventajas de hacer ambas cosas, por lo que en este tutorial, voy a mostrar cómo puede hacer cualquiera de estos métodos, en caso de que quiera seguirlos.

1a – Teclas HTML

Si decides hacer clic en las teclas, un problema con el que podrías toparte muy rápidamente es que las teclas son formas complejas. Se ajustan unos a otros como piezas de rompecabezas. En HTML y CSS, es fácil hacer cuadrados y rectángulos, pero es un poco más difícil hacer formas más complejas. Como quería tener efectos de mouseover, necesitaba tener cada clave por separado. En lugar de tratar de hacer rectángulos simples y luego averiguar la alineación y los índices z, fui con un enfoque mucho más difícil e innecesario: svg polygons. La etiqueta "SVG" le permite crear formas que de otro modo serían imposibles. Así que creé cada clave con puntos específicos en las formas que quería. Para que no tenga que darse cuenta usted mismo, incluyo esos elementos aquí:

 <svg class = "piano" height = "230" width = "1000"> 
<puntos del polígono = "200,10 230,10 230,100 245,100 245,220 200,220 200,10" clase = "blanco" id = "c" clave de datos = "65" />


<puntos poligonales = "245,100 260,100 260,10 275,10 275,100 290,100 290,220 245,220 245,100" class = "white-data-key =" 83 "id =" d "/>


<puntos poligonales = "305,10 335,10 335,220 290,220 290,100 305,100 305,10" class = "white-data-key =" 68 "id =" e "/>


<puntos poligonales = "335,10 365,10 365,100 380,100 380,220 335,220 335,10" class = "white-data-key =" 70 "id =" f "/>


<puntos poligonales = "380,100 395,100 395,10 410,10 410,100 425,100 425,220 380,220 380,100" class = "white-data-key =" 71 "id =" g "/>


<puntos poligonales = "425,100 440,100 440,10 455,10 455,100 470,100 470,220 425,220 425,100" class = "white-data-key =" 72 "id =" a "/>

<puntos poligonales = "470,100 485,100 485,10 515,10 515,220 470,220 470,100" class = "white-data-key =" 74 "id =" b "/>

<puntos poligonales = "515,10 545,10 545,100 560,100 560,220 515,220 515,10" class = "white-data-key =" 82 "id =" key5 "/>

<puntos poligonales = "560,100 575,100 575,10 590,10 590,100 605,100 605,220 560,220" class = "white-data-key =" 84 "id =" key5 "/>

<puntos del polígono = "605,100 620,100 620,10 650,10 650,220 605,220 605,100" class = "white-data-key =" 89 "id =" key5 "/>

<puntos del polígono = "650,10 680,10 680,100 695,100 695,220 650,220 650,10" class = "white-data-key =" 85 "id =" key5 "/>

<puntos poligonales = "695,100 710,100 710,10 725,10 725,100 740,100 740,220 695,220 695,100" class = "white-data-key =" 73 "id =" key5 "/>

<puntos poligonales = "740,100 755,100 755,10 770,10 770,100 785,100 785,220 740,220 740,100" class = "white-data-key =" 79 "id =" key5 "/>

<puntos poligonales = "785,100 800,100 800,10 830,10 830,220 785,220 785,100" class = "white-data-key =" 80 "id =" key5 "/>

<puntos poligonales = "230,10 260,10 260,100 230,100 230,10" class = "black-data-key =" 49 "id =" c_sharp "/>
<puntos poligonales = "275,10 305,10 305,100 275,100 275,10" class = "black" data-key = "50" id = "d_sharp" />
<puntos poligonales = "365,10 395,10 395,100 365,100 365,10" class = "black" data-key = "51" id = "f_sharp" />
<puntos poligonales = "410,10 440,10 440,100 410,100 410,10" class = "black-data-key =" 52 "id =" g_sharp "/>
<polígono puntos = "455,10 485,10 485,100 455,100 455,10" class = "black" data-key = "53" id = "a_sharp" />
<puntos poligonales = "545,10 575,10 575,100 545,100 545,10" class = "black-data-key =" 54 "id =" key4 "/>
<puntos poligonales = "590,10 620,10 620,100 590,100 590,10" class = "black-data-key =" 55 "id =" key4 "/>
<puntos poligonales = "680,10 710,10 710,100 680,100 680,10" class = "black" data-key = "56" id = "key4" />
<puntos poligonales = "725,10 755,10 755,100 725,100 725,10" class = "black" data-key = "57" id = "key4" />
<puntos poligonales = "770,10 800,10 800,100 770,100 770,10" class = "black" data-key = "48" id = "key4" />
</ svg>

Cada elemento poligonal está encerrado en la etiqueta SVG y está definido por una serie de puntos. El polígono Eacn también tiene asignada una clase, ya sea en blanco o negro, que corresponde a teclas negras o blancas. Aquí están los estilos de clase que hice para aquellos:

 .white { 
relleno: blanco;
trazo: negro;
ancho de carrera: 1;
cursor: puntero;
margen: 2px;
}
 .white: hover { 
fill: # 9e9e9e;
trazo: azul claro;
cursor: puntero;
ancho de carrera: 1;
contorno: sólido negro 1px;
}
 .black { 
relleno: negro;
ancho de carrera: 1;
cursor: puntero;
margen: 2px;
}
 .black: hover { 
llenar: # 515151;
trazo: azul claro;
ancho de carrera: 1;
contorno: sólido negro 1px;
}

Esto define el color y el borde, así como los efectos de desplazamiento para cada una de las teclas. Aunque las teclas en blanco y negro están una junto a la otra en un teclado, para mí fue más fácil agrupar las teclas en blanco y negro por separado en el documento HTML. Cada polígono también tiene una ID. La mayoría de los ID se corresponden con la nota asociada con ese polígono, comenzando desde la "C" media hasta la "B" de la siguiente octava. También notará que, además de una ID, cada elemento del polígono también tiene una clave de datos, con un aspecto como este:

 data-key = "80" 

Lo que hace esto es asociar cada elemento con presionar la tecla. Cada tecla en el teclado tiene un número. Entonces, en este punto podríamos hacer clic en la tecla en la pantalla, o presionar una tecla del teclado para que el programa reproduzca un sonido para nosotros. Sin embargo, dado que el usuario no sabrá instintivamente qué tecla del teclado corresponde a cada sonido, he incluido una pantalla en el HTML para mostrarlo al usuario.

 <div class = "keysNotes"> 
<h3> nota </ ??h3>
<h3> c </ h3>
<h3> d </ h3>
<h3> e </ h3>
<h3> f </ h3>
<h3> g </ h3>
<h3> a </ h3>
<h3> b </ h3>
<h3> c </ h3>
<h3> d </ h3>
<h3> e </ h3>
<h3> f </ h3>
<h3> g </ h3>
<h3> a </ h3>
<h3> b </ h3>
</ div>
 <div id = "keysshow" class = "keysNumbers"> 
<h3> teclas </ h3>
<h3> A </ h3>
<h3> S </ h3>
<h3> D </ h3>
<h3> F </ h3>
<h3> G </ h3>
<h3> H </ h3>
<h3> J </ h3>
<h3> R </ h3>
<h3> T </ h3>
<h3> Y </ h3>
<h3> U </ h3>
<h3> I </ h3>
<h3> O </ h3>
<h3> P </ h3>
</ div>

Luego, en el CSS para estas clases, tengo lo siguiente:

 .keysNumbers { 
color: gris claro;
tamaño de letra: 40px;
font-family: monoespacio;
font-weight: negrita;
índice z: 10;
ancho: 740px;
pantalla: flexión;
flex-flow: row nowrap;
justify-content: space-between;
margin-left: 80px;
margin-top: -110px;
}
 .keysNotes { 
color gris;
tamaño de letra: 40px;
font-family: monoespacio;
font-weight: negrita;
índice z: 13;
ancho: 740px;
pantalla: flexión;
flex-flow: row nowrap;
justify-content: space-between;
margin-left: 80px;
margin-top: -150px;
}

Esto alinea perfectamente el número y la visualización de teclas superpuestos en la parte superior de las teclas en pantalla. Ahora, tenemos que traer nuestros archivos de audio. Busqué en Internet y encontré notas de piano para cada tecla que necesitaba, excepto para F sharp, A sharp y both Fs. Como tenía los otros sonidos clave, tomé esas claves en un programa gratuito llamado audacity, y cambié el tono al tono más alto o más bajo, y volví a guardar el archivo para obtener el sonido que necesitaba. Audacity es una gran herramienta para eso, especialmente si quisieras hacer algo como un guitarrista en lugar de un piano, pero tenías grabaciones limitadas disponibles para hacer tus sonidos iniciales. En cualquier caso, no quiero que tengas que pasar por todos esos problemas, así que estoy proporcionando los sonidos que reuní aquí gratis: https://www.dropbox.com/sh/buuc6h937s62vw2/AADHdRxmmDCfcatwjxvdwAvNa?dl= 0

A continuación, coloco cada uno de estos archivos en el HTML con etiquetas de audio y también los códigos de pulsación de teclas que utilizamos anteriormente en los elementos del polígono.

 <audio data-key = "65" id = "c_octave1_audio" src = "/ middle_c.mp3"> </ audio> 
<audio data-key = "49" id = "c_octave1_sharp_audio" src = "/ mid_c_sharp.mp3"> </ audio>
<audio data-key = "83" id = "d_octave1_audio" src = "/ middle_d.mp3"> </ audio>
<audio data-key = "50" id = "d_octave1_sharp_audio" src = "/ mid_d_sharp.mp3"> </ audio>
<audio data-key = "68" id = "e_octave1_audio" src = "/ middle_e.mp3"> </ audio>
<audio data-key = "70" id = "f_octave1_audio" src = "/ middle_f.mp3"> </ audio>
<audio data-key = "51" id = "f_octave1_sharp_audio" src = "/ mid_f_sharp.mp3"> </ audio>
<audio data-key = "71" id = "g_octave1_audio" src = "/ middle_g.mp3"> </ audio>
<audio data-key = "52" id = "g_octave1_sharp_audio" src = "/ mid_g_sharp.mp3"> </ audio>
<audio data-key = "72" id = "a_octave1_audio" src = "/ middle_a.mp3"> </ audio>
<audio data-key = "53" id = "a_octave1_sharp_audio" src = "/ mid_a_sharp.mp3"> </ audio>
<audio data-key = "74" id = "b_octave1_audio" src = "/ middle_b.mp3"> </ audio>

<audio data-key = "82" id = "c_octave2_audio" src = "/ high_c.mp3"> </ audio>
<audio data-key = "54" id = "c_octave2_sharp_audio" src = "/ high_c_sharp.mp3"> </ audio>
<audio data-key = "84" id = "d_octave2_audio" src = "/ high_d.mp3"> </ audio>
<audio data-key = "55" id = "d_octave2_sharp_audio" src = "/ high_d_sharp.mp3"> </ audio>
<audio data-key = "89" id = "e_octave2_audio" src = "/ high_e.mp3"> </ audio>
<audio data-key = "85" id = "f_octave2_audio" src = "/ high_f.mp3"> </ audio>
<audio data-key = "56" id = "f_octave2_sharp_audio" src = "/ high_f_sharp.mp3"> </ audio>
<audio data-key = "73" id = "g_octave2_audio" src = "/ high_g.mp3"> </ audio>
<audio data-key = "57" id = "g_octave2_sharp_audio" src = "/ high_g_sharp.mp3"> </ audio>
<audio data-key = "79" id = "a_octave2_audio" src = "/ high_a.mp3"> </ audio>
<audio data-key = "48" id = "a_octave2_sharp_audio" src = "/ high_a_sharp.mp3"> </ audio>
<audio data-key = "80" id = "b_octave2_audio" src = "/ high_b.mp3"> </ audio>

Probablemente quiera incluir los archivos de audio en algún lugar de su proyecto, para que su HTML realmente tenga acceso a ellos. Ahora que tenemos nuestro audio, podemos codificar las pulsaciones de teclas o los clics para activar el sonido, o ambos.

Si hacemos la ruta onclick, nuestro JavaScript podría verse más o menos así:

 function play(){ 
var audio = document.getElementById("audio");
audio.play();
}

Por supuesto, reemplazar "audio" con cualquier elemento que desee activar la función. Una nota al margen, si no hacemos nada más que esto, se encontrará con un problema, donde, si hace clic en un botón, y luego un segundo, el segundo no se reproducirá hasta que el primero haya terminado. Por lo tanto, es agradable incluir un poco más en la función para detener cualquier sonido anterior, si se hace clic en un botón, y luego reproducir solo el sonido que se ha hecho clic.

¿Qué camino es mejor? OnClick o KeyPress? Eso depende completamente de ti, pero aquí están los argumentos para ambos. OnClick es mejor si su objetivo es ayudarlo a usted u otra persona a aprender el piano. Y He aquí los motivos: si su programa depende de la pulsación de teclas, entonces el usuario asociará con más frecuencia la tecla que se reproduce con la letra que se muestra en la tecla, y no la letra asociada a la nota. Así que podría hacer que la tecla "C" toque la "C media" y demás, pero eso solo funciona si solo tiene una octava. Y luego "C sharp", etc. también es complicado. Eventualmente, el usuario presionará "G" para reproducir "high F" y "S" para reproducir "A sharp". Puedes ver cómo esto podría ser confuso para alguien que está tratando de aprender el piano. OnClick le permite al usuario asociar solo el sonido con la letra que se muestra, no la letra en el teclado. Sin embargo, al hacer clic para cada sonido se produce un sonido de piano mucho más chasqueante, que no es muy fluido ni fácil de tocar. En mi opinión, ninguna de las dos es realmente una gran herramienta para aprender un instrumento musical, por lo que utilicé el método de pulsación de tecla, que es más divertido de jugar. Tomé algo de JavaScript para eso de un kit de batería en uno de los proyectos de Wes Bos, pero es un gran enfoque para este tipo de proyecto. Esto es lo que parece:

 window.addEventListener ('keydown', función (e) { 
const audio = document.querySelector (`audio [data-key =" $ {e.keyCode} "]`);
const key = document.querySelector (`.key [data-key =" $ {e.keyCode} "]`);
if (! audio) return;
audio.currentTime = 0;
audio.play ();
key.classList.add ('activo')

En lugar de tener que obtener cada elemento por ID, simplemente escucha la pulsación de una tecla y la combina con la clave de datos correspondiente, que ya tenemos en nuestro HTML. Reproduce el sonido asociado con esa pulsación de tecla, que también tenemos en nuestro HTML. Finalmente, estamos diciendo que si no hay audio, reprodúzcalo, y estamos ajustando el audio.currentTime a 0, lo que ayuda con la espera que mencioné anteriormente, permitiendo que se presione la tecla para reproducir y detener cualquier otro sonido. actualmente todavía está jugando.

En este punto, deberías poder hacer que tu piano funcione con pulsaciones de teclas o clics, lo que prefieras, y tu piano se puede reproducir, con suerte. La única otra cosa que hice extra fue iniciar una base de datos de "partituras", para que pueda seleccionar una canción para reproducir y le dará las notas para esa canción. Puedes ver eso en mi ejemplo anterior.

La parte difícil para esto es que no hay un atajo fácil. Si crea una base de datos similar, tendrá que agregar manualmente las notas para cada canción allí mismo. Sin embargo, si alguien tiene una excelente manera de transportar partituras a datos JSON … soy todo "oídos".

Una vez que guardé las notas en una base de datos, solo podía hacer una llamada de API cuando se seleccionaba la canción y recuperar las notas asociadas con la canción seleccionada. También puede dar un paso más y dejar que la canción se reproduzca primero, o cualquier cantidad de complementos adicionales. De todos modos, espero que disfrutes y te diviertas tanto como yo. Si tiene algún comentario, ¡por favor hágamelo saber!