El principal problema de utilizar el Nodo Analizador como elemento base en la detección de frecuencias, es la resolución en la parte baja del espectro. Nuesto ContextodeAudio utiliza una frecuencia de muestreo de 44100Hz, y ajustando el valor del tamaño de la Trasformada de Fourier a 2048, podemos crear vectores de 1024 muestras, que en este caso se cuantifican con 1Byte. En este contexto, podemos rastrear en nuestro array de datos la secuencia armónica de cada nota y crear un filtro comparador para aproximar un valor. Las desviaciones de frecuencia de la muestra tomada, respecto al valor armónico original, están categorizadas en 3 tipos (±1Hz, ±5Hz y ±10Hz). a partir de las desviaciones de los cuatro primeros armónicos de cada cuerda, podemos aproximar una nota.
En el display superior aparecen destacados los picos de frecuencia que superan el umbral de amplitud de 128. Este vector de picos es el que compararemos con un banco de notas. Por ejemplo un MI de la sexta cuerda se mostrará cuando los 3 picos de más amplitud, correspondan a las posiciones 8, 15 y 31.
Las desviaciones de ¼ de tono sobre la nota, las determinará los armónicos superiores, en este caso el que ocupa la posición 31, centrado en 667 Hz. La precisión aumentaría a un ⅙ de tono si añadiéramos el valor del siguiente armonico (61) y podría mejorar si añadiésemos el siguiente (122), pero son armónicos de poco nivel que se enmascaran rápido.
Correspondencia de frecuencias | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
C | C# | D | Eb | E | F | F# | G | G# | A | Bb | B | |
2 | 65.41 | 69.30 | 73.42 | 77.78 | 82.41 | 87.31 | 92.50 | 98.00 | 103.8 | 110.0 | 116.5 | 123.5 |
3 | 130.8 | 138.6 | 146.8 | 155.6 | 164.8 | 174.6 | 185.0 | 196.0 | 207.7 | 220.0 | 233.1 | 246.9 |
4 | 261.6 | 277.2 | 293.7 | 311.1 | 329.6 | 349.2 | 370.0 | 392.0 | 415.3 | 440.0 | 466.2 | 493.9 |
5 | 523.3 | 554.4 | 587.3 | 622.3 | 659.3 | 698.5 | 740.0 | 784.0 | 830.6 | 880.0 | 932.3 | 987.8 |
6 | 1047 | 1109 | 1175 | 1245 | 1319 | 1397 | 1480 | 1568 | 1661 | 1760 | 1865 | 1976 |
7 | 2093 | 2217 | 2349 | 2489 | 2637 | 2794 | 2960 | 3136 | 3322 | 3520 | 3729 | 3951 |
8 | 4186 | 4435 | 4699 | 4978 | 5274 | 5588 | 5920 | 6272 | 6645 | 7040 | 7459 | 7902 |
Código de colores: | Nota- | Nota | Nota+ | Armónico centrado en ±1Hz | Armónico centrado en ±5Hz | Armónico centrado en ±10Hz |
El tamaño de cada muestra es de 21.53Hz, que es la máxima resolución del analizador. Esta resolución es insuficiente para medir pasos <5Hz en la parte baja del espectro de la guitarra, pero si seguimos el rastro armónico de la nota en la tabla de frecuencia y comparamos este grupo de valores con los armónicos de cada nota podemos mostrar en pantalla un resultado bastante aproximado.
.webkitGetUserMedia
y creada la fuente de audio, podemos enrutar la señal en nuestra cadena de audio y comenzar el procesado.
El nodo analizador es clave para el análisis, pues nos proveerá los datos necesarios para realizar el cálculo o aproximación de nota, y utilizaremos la función requestAnimationFrame
para realizar el análisis y el dibujo, solicitaremos los datos de la salida del nodo analizador para trabajar con ellos.
//Tomamos un byte de enteros, array de 256 valores de amplitud y 1028 muestras
var ByteDatosFrec = new Uint8Array(analizador.frequencyBinCount);
analizador.getByteFrequencyData(ByteDatosFrec);
Una vez contamos con un array de datos de la ventana de frecuencia, podemos recorrerlo para obtener sus picos, que serán los valores máximos del vector que superen un umbral. Para optimizar el funcionamiento y descartar sonidos de fondo, se ha establecido un umbral en la detección de pico de valor 128 en la escala de amplitud de la señal, esto es la mitad del valor máximo que se puede detectar. Este sería el primer bloque del algoritmo de análisis.
//Recorrer el array de datos de frecuencia Comparar valores mayores que el umbral
for (var m = 0; m <= (ByteDatosFrec.length); m++) {
var magnitud = ByteDatosFrec[m];
var siguiente = ByteDatosFrec[m + 1];
var anterior = ByteDatosFrec[m - 1];
// se establece el umbral a 128.
if ((magnitud > maximo) && (magnitud > siguiente) && (magnitud > anterior) && (magnitud > 128)) {
pico = magnitud;
//Creamos nuestra matriz de valores y posiciones
valores.push(pico);
posiciones.push(m);
var pos = m;
var frecuencia = ((pos * contextoDeAudio.sampleRate) / contextoDeAudio.analizador.fftSize);
}
Podemos enumerar las etapas del algoritmo de detección, de la siguiente manera:
//A
var A5 = [5, 10, 20, 41];
var A5b = [5, 10, 20, 40];
var A5bb = [5, 10, 20, 39];
var A5a = [5, 10, 20, 42];
var A5aa = [5, 10, 21, 42];
var A2 = [10, 20, 41];
En este fragmento de código se observa la declaración de la nota LA. Esta declaración de variables se ha optimizado para las notas susceptibles de mostrarse en la guitarra. En el ejemplo se observa la declaración de A5, que corresponde al LA de la 5ª cuerda tocada al aire, este valor es imprescindible durante el proceso de afinación general de una guitarra, así como el LA de la segunda cuerda al aire, por el que posiblemente pasemos al tensar la cuerda hacia el SI que corresponde a la 2ª cuerda en la afinación estándar. Los sufijos a, aa y b, bb indican los valores altos de esta nota que mostraremos en el display y que servirán al usuario para ajustar la tensión de la cuerda en una dirección y aproximarse al valor correcto de afinación.
requestAnimationFrame
, por lo que el parpadeo es rápido. Esto es una buena cualidad para variaciones de tensión rápidas en la cuerda, que generen cambios de afinación repentinos.