Afinador de Guitarra – Online Guitar Tuner

Hace unos años hice un Afinador de Guitarra Online en Flash para la web de Liyonel Guitars.

Flash va a morir, de hecho está agonizando ya… en el año 2016 estuvo presente solo en el 10% de las webs y a partir de 2017 Firefox y Chrome empezarán a bloquear el contenido Flash.

Por eso, pensé en hacer un nuevo afinador en HTML5, con algo de CSS3 y con Javascript nativo (para evitar la carga de librerías externas).

La idea original era usar canvas para las animaciones, pero lo dejaremos para la siguiente versión y aquí las haremos con CSS y Javascript.

Ver Demo

HTML

El HTML se divide en tres partes para las que usaremos elementos de HTML5 :

header
Aqui pondremos el titulo que iluminaremos con CSS3 cuando suene alguna cuerda, para ello añadiremos con Javascript una clase extra al elemento h1 modificando sus estilos CSS.

<header>
  <h1 id="title">Online Guitar Tuner</h1>
</header>

section
En esta sección van las cuerdas que serán elementos button , también hemos puesto las notas que se iluminarán al tocar cada cuerda. Igualmente con Javascript añadiremos una clase extra a cada elemento para modificar su CSS y aplicar el efecto de iluminado al tocar las cuerdas.

<section id="guitarBody">
  <button id="s6" onclick="clickString(this.id)"></button>
  <!-- un button para cada cuerda -->
  <div class="notes">
    <span id="s6Note">E<span class="notas">MI</span></span>
    <!-- un span para cada cuerda -->
   </div>
</section>

footer
En la última parte vamos a poner los controles del afinador; un botón para detener el sonido, otro botón para mantener el sonido, y la púa que hará que suenen las cuerdas onmouseover.

<footer>
  <button onclick="stopStrings()" id="btnStop">STOP</button>
  <button onclick="usePick()" id="btnPick">PICK</button>
  <button onclick="holdSound()" id="btnHold">HOLD</button>
</footer>
Esto es solo una parte del código HTML final. En la demo encontrarás el código completo.

CSS / CSS3

Lo único destacable de lo estilos, más allá de imágenes de fondo y posiciones de los elementos, es el iluminado de las notas y el efecto metalizado del texto.

Iluminado de notas

Muy simple, con las propiedades color y text-shadow combinadas con la propiedad transition:

.notes span {
    width: 50px;
    display: inline-block;
    color: #171717;
    text-align: center;
    font-size: 50px;
    z-index: 0;
    text-shadow: -1px -1px 1px #000000, 0px 0px 1px #6f6f6f;
}
.notes span.lightOn {
    color: #71abeb;
    text-shadow: 0px 0px 15px #3779c5, 0px 0px 25px #3779c5, 0px 0px 35px #71abeb, -1px -1px 1px #000000, 0px 0px 1px #6f6f6f;
	transition: all 0.2s ease;
}

Efecto metalizado del texto

Algo más elaborado, pero muy fácil tambien, la clave está en las propiedades colorbackground-colorbackground-clip y text-shadow.
Para aplicar el efecto de iluminado basta con cambiar las propiedaes background-color y text-shadow en el selector :hover, o añadiendo al elemento una clase con Javascript como en este caso:

h1 {
    margin: 0.5vh 0;
    font-size: 2em;
    padding: 3vh 0;
    background-color: #333333;
    -webkit-background-clip: text;
    -moz-background-clip: text;
    background-clip: text;
    color: transparent;
    text-shadow: rgba(255,255,255,0.5) 0px 1px 3px;  
}
h1.imgTitle{
	text-shadow: 0 0 25px rgba(20, 139, 255, 0.8), 0 0 15px rgba(30, 144, 255, 0.54);
    background-color: rgba(77, 134, 189, 0.8);
}
Esto es solo una parte del código CSS final. En la demo encontrarás el código completo.

Javascript

El Javascript son solo un puñado de variables y algunas funciones:

Variables

Creamos seis variables para almacenar los sonidos de las cuerdas:

var s1 = new Audio('../audio/s1.mp3');
var s2 = new Audio('../audio/s2.mp3');
var s3 = new Audio('../audio/s3.mp3');
var s4 = new Audio('../audio/s4.mp3');
var s5 = new Audio('../audio/s5.mp3');
var s6 = new Audio('../audio/s6.mp3');

Después podremos acceder a cada sonido para reproducirlo o detenerlo usando el método window.

Funciones

Los nombres de las funciones son bastante descriptivos, lo que hacen básicamente es reproducir o detener los sonidos y añadir o quitar clases a elementos dependiendo de las cuerdas que toquemos, con ello cambiamos los estilos de los elementos para, por ejemplo, iluminarlos, y para ello usamos los métodos getElementById y querySelectorAll.

getElementById

Como vamos a usar bastante el método getElementById lo podemos acortar en una función, y pasar de esto:

document.getElementById('id');

A esto otro:

elem(id);

Con esta simple función:

function elem(id){
  var element = document.getElementById(id);
  return element;
}

En el resto de funciones usaremos nuestra función elem() acortando el código resultante.

Que suenen las cuerdas…

Una de las funciones clave es clickString que se activará al tocar una cuerda, y funciona de la siguiente manera:

En el HTML se le pasa como argumento el valor del atributo id de la cuerda que se ha tocado:

<button onclick="clickString(this.id)" class="string" id="s6"></button>

La función identifica la cuerda y hace el resto:

function clickString(thisString) {
  elem('title').className='imgTitle';
  elem(thisString).className = "string";
  window[thisString].currentTime = 0;
  window[thisString].play();
  var thisNote = thisString+"Note";
  elem(thisString).className = "string playingSound";
  elem(thisNote).className = "lightOn";
  setTimeout(function(){ 		
    if(window[thisString].loop != true){
      elem(thisString).className = "string";
      elem(thisNote).className = "lightOff";
      if(document.querySelectorAll('.playingSound').length==0){
        elem('title').className='';
      }
    }
  }, 4500);
}

La función, explicada línea a línea, trabaja de la siguiente manera:

  1. Nombre de la función clickString y el argumento que será cada cuerda thisString.
  2. Añadimos la clase imgTitle al título para encenderlo.
  3. Nos aseguramos de que el botón solo tiene la clase string, para evitar duplicar clases.
  4. Usamos el atributo id del botón para seleccionar el sonido de la cuerda elegida mediante el objeto window y lo detenemos al inicio del sonido con currentTime.
  5. Reproducimos el sonido con el método play().
  6. Creamos la variable thisNote para encender las notas.
  7. Le añadimos la clase playingSound al botón de la cuerda que está sonando, para encenderla.
  8. Le añadimos la clase lightOn a la nota de la cuerda que está sonando, para encenderla.
  9. Llamamos al método setTimeout, que se ejecutará 4 segundos y medio después…
  10. – Condicional if: Si el sonido no está en bucle:
  11.   – Quitamos la clase playingSound a la cuerda, para apagarla.
  12.   – Cambiamos la clase lightOn por lightOff a la nota, para apagarla.
  13.   – Condicional if: Si no hay ningún elemento con la clase playingSound :
  14.     – Quitamos la clase imgTitle al título, para apagarlo.
  15.   – Cierre de if linea 13
  16. – Cierre de if linea 10
  17. Ejecutamos el contenido del método setTimeOut 4 segundos y medio después de llamarlo (cuando el sonido ha dejado de sonar).
  18. Cierre de la función clickString linea 1

querySelectorAll

El método querySelectorAll devuelve, en una lista de nodos, todos los elementos del documento que tienen un determinado selector CSS. Se puede acceder a estos nodos mediante números de índice. De manera que:

document.querySelectorAll(".notas");

Nos devolverá una lista de elementos con la clase notas:

[span.notas, span.notas, span.notas, span.notas, span.notas, span.notas]

Y podemos acceder a ellos mediante números de índice:

var elemNotas = document.querySelectorAll('.notas');
elemNotas[0].className='primera';
elemNotas[1].className='segunda';
// uno para cada nota...

Que paren las cuerdas…

La función stopStrings funciona con el método querySelectorAll :

function stopStrings(){
  elem('title').className='';
  s1.pause(); s2.pause(); s3.pause(); s4.pause(); s5.pause(); s6.pause();
  var auxString = document.querySelectorAll('.playingSound');	
  for (var x in auxString) {
    if(auxString[x]!==undefined){
      auxString[x].className = "string";
    }
  }
  var auxNote = document.querySelectorAll(".lightOn");
  for (x in auxNote) {
    if(auxNote[x]!==undefined){
      auxNote[x].className = "lightOff";
    }
  }
}

La función, explicada línea a línea, trabaja de la siguiente manera:

  1. Nombre de la función stopStrings:
  2. Quitamos la clase imgTitle del título para apagarlo.
  3. Detenemos todos los sonidos con el método pause().
  4. Creamos la variable auxString que almacena las cuerdas que están sonando.
  5. Con un bucle for recorremos la variable:
  6. – Condicional if: Si la variable, con su índice, está definida: (esto es para evitar errores en firefox)
  7.   – Quitamos la variable playingSound a cada elemento que la tenga, apagándolo.
  8. – Cierre de if linea 6
  9. Cierre de for linea 5
  10. Repetimos el proceso para apagar las notas buscando los elementos con la clase lightOn
Esto es solo una parte del código Javascript final. En la demo encontrarás el código completo.

Demo

Es posible experimentar errores al mostar el código en la demo, es por el ancho del blog que todo a la vez no cabe… ya que el afinador necesita un mínimo de 320 pixels de ancho…

See the Pen Online Guitar Tuner by Josetxu (@josetxu) on CodePen.

En Codepen podéis ver la demo a tamaño completo.