Sentencias if-else incrustadas en cascada

0

Estoy diseñando un afinador de guitarra acústica basado en MCU. He roto el espectro de frecuencias de la nota C2 (65.41 Hz) a la nota C5 (523.25 Hz) en muchas subbandas para darle al usuario una descripción precisa de la nota que está pulsando. La letra de la nota, el número y una indicación de si están ligeramente afilados, ligeramente planos o bien afinados aparecerán en una pantalla LCD.

El problema es que no puedo ver una manera de evitar una cadena masiva if-else para determinar en qué banda se encuentra la frecuencia de entrada. Tengo 66 casos de 'else if ()', y esto me parece muy lento / ineficiente / tonto. Necesito saber qué nota han tocado y enviarla a la pantalla LCD antes de que llegue el siguiente pulso. En la frecuencia más alta, esto me da un poco menos de 2 ms. El simple hecho de registrar 32 bits en serie para el controlador LCD consume una cantidad decente de esos 2 ms, por lo que debo averiguar en qué banda se encuentra la frecuencia con bastante rapidez. ¿Puedo hacer algo para mejorar el software y evitar una cantidad ridícula de declaraciones if-else?

Editar para más información:

Estoy programando en C en un AVR ATmega. Para asociar una frecuencia con una nota, estoy usando la fórmula: frecuencia = 440 * 2 ^ (x / 12), donde x es el número de medios pasos alejados de A4 (440 Hz). Una entrada está bien sintonizada si está dentro de +/- 0.25 medios pasos desde su frecuencia central. Se considera plano si está a menos de 0.25 medios pasos desde el centro pero más de 1 medio paso desde el centro (donde los medios pasos son negativos con respecto al centro). Es agudo si está a más de 0.25 pasos intermedios desde el centro pero a menos de 1 paso medio desde el centro (donde los medios pasos son positivos con respecto al centro).

Por ejemplo:

G2 plano = [92.5 Hz, 96.6 Hz]

G2 bueno = [96.6 Hz, 99.4 Hz]

G2 sharp = [99.4 Hz, 103.8 Hz]

Donde la frecuencia central de G2 es 98 Hz.

    
pregunta pr871

7 respuestas

1

Además de mis comentarios, tiene algunos problemas musicales con su enfoque.

Figura1.La gStrings chromatic sintonizador muestra la precisión de sintonía en centavos. Observe las marcas grandes alrededor de +/- 15 céntimos para indicar los límites de ajuste aceptable.

  • La mayoría de los sintonizadores dividen un semitono en 100 centavos. El sintonizador de Android gStrings, por ejemplo, indica aceptablemente en sintonía de +/- 12 centavos y la mayoría de nosotros tratamos de hacerlo mucho mejor que eso. Vas a usar +/- 50 centavos, lo que será terrible, ya que permitiría errores de hasta medio semitono.
  • La mayoría de los buenos sintonizadores permitirán que el usuario establezca una desviación del estándar A-440 para permitir la sintonización de un instrumento no ajustable (por ejemplo, un piano antiguo) que es ligeramente plano. Esto también es útil cuando se reproduce con grabaciones que están ligeramente por encima o por debajo del tono de concierto estándar, posiblemente debido a una velocidad incorrecta en el equipo de grabación. Debe incorporar esta función en la suya para que sea útil.

Figura2.Armónicosdeguitarra.Fuente: Hiperfísica .

  • La cuerda de la guitarra vibra simultáneamente en varios modos: cuerda completa, mitad, tercero, cuarto, quinto, etc., dando los armónicos mezclados con el fundamental. Los guitarristas pueden "tocar armónicos" al tirar de la cuerda mientras la tocan ligeramente en la mitad o en la tercera, etc., punto a lo largo de la cuerda. Con un poco de práctica, puedes escuchar que la armónica estuvo allí todo el tiempo cuando la nota se tocó normalmente. En efecto, el guitarrista está suprimiendo lo fundamental y permitiendo que pasen los armónicos. Salta a través de este video armónicos de guitarra para ver esto en acción.

La relación de frecuencia entre semitonos adyacentes en la escala de buen carácter es \ $ \ sqrt [12] {2} \ $. Si su sistema tiene suficiente poder de cómputo, puede convertir la frecuencia fundamental a \ $ log_ {12} \ $, lo que linealizará la escala de semitono en una secuencia aritmética simple.

Tabla 1: tenga en cuenta que la diferencia entre los valores de registro de las notas adyacentes es una constante.

Note    Frequency       Log base 12     Diff of logs
A       440             2.4494983453    
A#      466.1637615181  2.4727435908    0.0232452455
B       493.8833012561  2.4959888363    0.0232452455
C       523.2511306012  2.5192340817    0.0232452455
C#      554.3652619537  2.5424793272    0.0232452455
D       587.3295358348  2.5657245727    0.0232452455
D#      622.2539674442  2.5889698181    0.0232452455
E       659.2551138257  2.6122150636    0.0232452455
F       698.456462866   2.6354603091    0.0232452455
F#      739.9888454233  2.6587055546    0.0232452455
G       783.9908719635  2.6819508       0.0232452455
G#      830.6093951599  2.7051960455    0.0232452455
A       880             2.728441291     0.0232452455

Al restar el registro de la frecuencia de referencia (A = 440 Hz, A = 339 Hz, etc.), el valor del registro será 0 en la frecuencia de referencia.

Tenga en cuenta que si divide el valor \ $ log_ {12} \ $ por 0.0232452455, los valores se incrementarán en 1 por semitono. Ahora tu búsqueda de notas es

  • Redondea al entero más cercano.
  • División entera por 12 para encontrar el número de octava.
  • División de módulo 12 para encontrar el desplazamiento de la nota en una matriz [A, Bb, B, C, C #, etc.].
respondido por el Transistor
2

Supongamos que tiene un valor numérico 'x' (una frecuencia medida) y bandas con límites inferiores y superiores definidos, y desea encontrar qué banda contiene 'x'. Esto puede pensarse como un problema de viabilidad matemática en una dirección, y se podrían usar muchos algoritmos. En esta respuesta, te describiré cómo utilizar una búsqueda de estilo bissection usando un poco de pseudocódigo para resolver tu problema.

Enumerar posibles bandas:

enum band_e = {B1, B2, B3 ... Bn};

Y escribe una función como esta, por ejemplo:

band_e getBandFromFreq(double x)

En la función, describe los límites de tus bandas:

const double bands_lb[] = {LB1, LB2, LB3 ... LBn};
const double bands_ub[] = {UB1, UB2, UB3 ... UBn};

Ahora adivina una banda en la que podría ser 'x', digamos B3. Si se cumplen las dos restricciones de B3 (LB3 < = x < = UB3), adivinaste correctamente y tu función puede devolver B3.

Si la suposición es incorrecta, al menos una de las restricciones se cumplirá (por ejemplo, LB3 < = x, pero x > UB3). Esto le dará una dirección para continuar su búsqueda (hacia LB4 a LBn en este caso). La búsqueda de Bissection significa repetir la conjetura en la banda media de este nuevo rango. Cada "adivinanza y comprobación" (o iteración) progresiva reducirá las múltiples bandas posibles que aún quedan por comprobar.

Espero que esto ayude! Sin embargo, se pueden implementar muchos otros algoritmos de búsqueda, elige tu veneno. Bissection suele ser el más intuitivo.

    
respondido por el Vicente Cunha
2

Su problema está buscando el intervalo que contiene un valor dado. A menos que pueda encontrar una forma específica de problema para construir una función de mapa, el algoritmo de búsqueda más rápido es búsqueda binaria . La búsqueda consecutiva evitará que escribas esas 66 declaraciones (y ahorrarás mucho en tamaño de código), pero será casi tan lento.

    
respondido por el Dmitry Grigoryev
0

Con respecto a su pregunta, puede considerar usar una declaración de cambio o escanear a través de una matriz. Tampoco será mucho más rápido que un montón de declaraciones if pero podrían ser más fáciles de mantener.

No has especificado las especificaciones de tu controlador de pantalla, pero volver a dibujar la pantalla cada 2 ms es muy rápido. A menos que su controlador de pantalla no tenga memoria (no puede simplemente escribir en la pantalla y persistirá indefinidamente), intentaría reducir la velocidad de esa manera, a 100-200 ms.

Además, ¿cómo está determinando la frecuencia de su señal de entrada?

    
respondido por el C_Elegans
0

Recuerde que la frecuencia de C3 es el doble que la de C3 (una octava hacia arriba), y así sucesivamente hasta C5. Primero, me gustaría saber qué nota estás tratando de usar modulo ( % ):

/* C2 through B2 */
const float note_bases[7] = [65.41, 73.42, 82.41, 87.31, 98.00, 110.00, 123.47];

/* Your method of measuring your fundamental frequency */
f_meas = measure_frequency();

char note_base = find_note_base( f_meas );

/* ... */
char find_note_base ( float f )
{
    float remainder, min_remainder = 100.0;
    char note;

    for( note = 0; note < 7; note++ )
    {
        remainder = f % note_bases[ note ];

        if( remainder < min_remainder ) min_remainder = remainder;
        else break;
    }

    return note;
}

Luego determinaría qué octava con matemáticas simples:

import <math.h>

char oct = (char)round( f_meas / note_bases[ note_base ] );

También sugeriría no actualizar tu pantalla tan rápido. Cada vez que mi sintonizador cambia los colores del LED en las decenas de milisegundos, mi cerebro empieza a doler. Debería hacer un cambio cada 100 ms o menos; puedes bajar desde allí si es demasiado lento.

    
respondido por el calcium3000
0

Si entiendo correctamente, el objetivo es seleccionar la nota más cercana para una frecuencia determinada, y la frecuencia puede considerarse esencialmente como un número de punto flotante.

Creo que una solución posible sería tener una matriz ordenada precalculada que contenga la media geométrica de las frecuencias de notas exactas adyacentes y luego realizar una búsqueda binaria para encontrar el punto en el que insertaría la nueva frecuencia medida en la lista . Una frecuencia entre un par de elementos de matriz se asocia solo con una sola nota, ya que está en el rango desde la mitad antes de la nota hasta la mitad después de la nota. Encontrar el punto de inserción te dice la nota más cercana. Tenga en cuenta (es probable que tenga sentido) que probablemente pueda escalar las frecuencias en la matriz para que sean enteros sin signo de 16 bits y escalar de manera similar la frecuencia medida antes de la búsqueda para que solo tenga que hacer comparaciones de enteros.

Puede encontrar el punto de inserción en una lista ordenada con una búsqueda binaria en la mayoría de las comparaciones de tope (log2 (N)), donde N es el número de notas. Para 88 llaves puedes encontrarlo en 7 comparaciones como máximo. Google puede encontrar muchos ejemplos de códigos de búsqueda binarios.

El número de nota más cercano técnicamente podría calcularse específicamente al dividir la frecuencia por 440 y llevar el registro a la base de la 12ª raíz de 2, pero los cálculos implicados llevarían más tiempo que la búsqueda.

    
respondido por el crj11
0

Puedo imaginar un par de formas de hacer esto de manera rápida y eficiente.

La primera es una búsqueda estándar con algunos IF..ELSE..THEN pero no muchos. Digamos que hay 64 notas para comprobar. Tome el valor de entrada (por ejemplo, coincide con el número 29): ¿Es más grande o más pequeño que el número 32? Menor. ¿Es más grande o más pequeño que el número 16? Más grande. ¿Qué tal el número 24? Más grande. Número 28? Más grande. Número 30? Menor. Hecho.     Cinco o 6 pruebas según el conjunto de instrucciones. Luego prueba la parte plana / afilada. Esta es una búsqueda binaria y utiliza potencias de dos, al igual que contar en binario. Corta el área de búsqueda a la mitad con cada paso.

Hay otra forma que puede ser casi instantánea si funciona. Utilice una tabla de búsqueda. Su número de entrada puede ser el índice en una matriz (o un desplazamiento en una tabla de búsqueda). Si tienes un montón de PROM / FLASH esto es especialmente fácil. No se moleste en intentar visualizar los datos, solo suelte un par de bits de orden inferior. Esto "reducirá" los datos que usará como índice. El contenido de la tabla solo puede ser la nota que está buscando, como A flat. O puede ser algo más útil para tus cálculos.

Tenga en cuenta que puede obtener pequeños chips de flash serie de 8 pines con montaje en superficie con 16 MBytes por menos de $ 2. Por lo tanto, son posibles enormes tablas de búsqueda. En su caso, una de esas búsquedas puede dar la respuesta exacta que desea sin perder bits y sin calcular en tiempo real. Puede indicarle la nota y cuánto está apagada. Por lo tanto, es su manera de obtener resultados básicamente instantáneos al pre-calcular los datos una vez y ponerlos en flash.

    
respondido por el C. Towne Springer

Lea otras preguntas en las etiquetas