¿Cuál es la frecuencia de esta nota?

21

Actualización musical rápida:

El teclado del piano consta de 88 notas. En cada octava, hay 12 notas, C, C♯/D♭, D, D♯/E♭, E, F, F♯/G♭, G, G♯/A♭, A, A♯/B♭y B. Cada vez que golpeas una 'C', el patrón se repite una octava más arriba.

ingrese la descripción de la imagen aquí

Una nota se identifica de forma exclusiva por 1) la letra, incluidos los objetos punzantes o planos, y 2) la octava, que es un número del 0 al 8. Las primeras tres notas del teclado son A0, A♯/B♭y B0. Después de esto viene la escala cromática completa en la octava 1. C1, C♯1/D♭1, D1, D♯1/E♭1, E1, F1, F♯1/G♭1, G1, G♯1/A♭1, A1, A♯1/B♭1y B1. Después de esto viene una escala cromática completa en las octavas 2, 3, 4, 5, 6 y 7. Luego, la última nota es a C8.

Cada nota corresponde a una frecuencia en el rango de 20-4100 Hz. Con un A0comienzo exacto de 27.500 hertzios, cada nota correspondiente es la nota anterior multiplicada por la duodécima raíz de dos, o aproximadamente 1.059463. Una fórmula más general es:

ingrese la descripción de la imagen aquí

donde n es el número de la nota, con A0 siendo 1. (Más información aquí )

El reto

Escriba un programa o función que tome una cadena que represente una nota e imprima o devuelva la frecuencia de esa nota. Usaremos un signo de libra #para el símbolo agudo (o el hashtag para ustedes, youngins) y una minúscula bpara el símbolo plano. Todas las entradas se verán (uppercase letter) + (optional sharp or flat) + (number)sin espacios en blanco. Si la entrada está fuera del rango del teclado (menor que A0 o mayor que C8), o si hay caracteres no válidos, faltantes o extra, esta es una entrada no válida y no tiene que manejarla. También puede asumir con seguridad que no obtendrá entradas extrañas como E # o Cb.

Precisión

Como la precisión infinita no es realmente posible, diremos que cualquier cosa dentro de un centavo del valor verdadero es aceptable. Sin entrar en detalles excesivos, un centavo es la raíz número 1200 de dos, o 1.0005777895. Usemos un ejemplo concreto para hacerlo más claro. Digamos que su entrada fue A4. El valor exacto de esta nota es 440 Hz. Una vez que el centavo es plano 440 / 1.0005777895 = 439.7459. Una vez que el centavo está afilado, por lo 440 * 1.0005777895 = 440.2542tanto, cualquier número mayor que 439.7459 pero menor que 440.2542 es lo suficientemente preciso como para contar.

Casos de prueba

A0  --> 27.500
C4  --> 261.626
F#3 --> 184.997
Bb6 --> 1864.66
A#6 --> 1864.66
A4  --> 440
D9  --> Too high, invalid input.
G0  --> Too low, invalid input.
Fb5 --> Invalid input.
E   --> Missing octave, invalid input
b2  --> Lowercase, invalid input
H#4 --> H is not a real note, invalid input.

Tenga en cuenta que no tiene que manejar las entradas no válidas. Si su programa finge que son entradas reales e imprime un valor, eso es aceptable. Si su programa falla, eso también es aceptable. Cualquier cosa puede suceder cuando obtienes uno. Para ver la lista completa de entradas y salidas, consulte esta página

Como de costumbre, este es el código de golf, por lo que se aplican las lagunas estándar y gana la respuesta más corta en bytes.

DJMcMayhem
fuente
99
"H # 4 -> H no es una nota real, una entrada inválida". Excepto en Europa.
Lui
66
@Lui, ¿qué es esto de Europa como si toda Europa lo usa H? Hlo que significa B es AFAIK solo utilizado en países de habla alemana. (donde Bsignifica Bb por cierto.) Lo que los británicos e irlandeses llaman B se llama Si o Ti en España e Italia, como en Do Re Mi Fa Sol La Si.
Level River St el
3
He jugado un B♯2 con una viola antes, es una nota perfectamente razonable y nada rara.
Neil
3
@steveverrill Hse usa en Alemania, República Checa, Eslovaquia, Polonia, Hungría, Serbia, Dinamarca, Noruega, Finlandia, Estonia y Austria, según Wikipedia . (También puedo confirmarlo para Finlandia.)
PurkkaKoodari
66
@Neil Probablemente fue accidental. ;)
vaso de precipitados

Respuestas:

21

Japt, 41 37 35 34 bytes

¡Finalmente tengo la oportunidad de darle ¾un buen uso! :-)

55*2pU¬®-1¾ª"C#D EF G A B"bZ /C} x

Pruébalo en línea!

Cómo funciona

          // Implicit: U = input string, C = 12
U¨    }  // Take U, split into chars, and map each item Z by this function:
-1¾       //  Subtract 1.75 from Z. This produces NaN for non-digits.
ª"..."bZ  //  If the result is falsy (NaN), instead return the index of Z in this string.
          //  C produces 0, D -> 2, E -> 4, F -> 5, G -> 7, A -> 9, B -> 11.
          //  # -> 1, and b -> -1, so we don't need to calculate them separately.
/C        //  Divide the index by 12.
x         // Sum.
2p        // Take 2 to the power of the result.
55*       // Multiply by 55.

Casos de prueba

Todos los casos de prueba válidos vienen bien. Son los inválidos donde se pone raro ...

input --> output       (program's reasoning)
A0  --> 27.5           (Yep, I can do that for you!)
C4  --> 261.625565...  (Yep, I can do that for you!)
F#3 --> 184.997211...  (Yep, I can do that for you!)
Bb6 --> 1864.6550...   (Yep, I can do that for you!)
A#6 --> 1864.6550...   (Yep, I can do that for you!)
A4  --> 440            (Yep, I can do that for you!)
D9  --> 9397.27257...  (Who says that's too high?)
G0  --> 24.49971...    (I've heard that note before.)
Fb5 --> 659.25511...   (Wait, Fb isn't supposed to be a note?)
E   --> 69.295657...   (I'm gonna guess that the missing octave is 1¾.)
b2  --> 61.735412...   (I assume that b means Cb...)
H#4 --> 261.625565...  (H# is C!)
ETHproducciones
fuente
13
+ ¾ por usar ¾ :)
anatolyg
1
¿No es esto realmente 38 bytes ?
Patrick Roberts
@PatrickRoberts Esto es 38 bytes en UTF-8, pero Japt usa la codificación ISO-8859-1 , en la que cada carácter es exactamente un byte.
ETHproductions
8

Pyth, 46 44 43 42 39 35 bytes

*55^2tsm.xsdc-x"C D EF GbA#B"d9 12z

Pruébalo en línea. Banco de pruebas.

El código ahora usa un algoritmo similar a la respuesta Japt de ETHproductions , así que acéptelo por eso.

Explicación

                                            implicit: z = input
       m                          z         for each character in input:
          sd                                  try parsing as number
        .x                                    if that fails:
               "C D EF GbA#B"                   string "C D EF GbA#B"
              x              d                  find index of character in that
             -                9                 subtract 9
            c                   12              divide by 12
      s                                     sum results
     t                                      decrement
   ^2                                       get the correct power of 2
*55                                         multiply by 55 (frequency of A1)

Versión anterior (42 bytes, 39 con cadena empaquetada)

*55^2+tsezc+-x"C D EF G A B"hz9-}\#z}\bz12

Explicación

PurkkaKoodari
fuente
Esto es interesante. ¿Cómo empaqueta las cuerdas Pyth?
Luis Mendo
@LuisMendo Puede encontrar información sobre eso en los documentos . Básicamente, encuentra la base más pequeña para convertir los datos y luego codifica el resultado en la base 256.
PurkkaKoodari
7

Mathematica, 77 bytes

2^((Import[".mid"~Export~Sound@SoundNote@#,"RawData"][[1,3,3,1]]-69)/12)440.&

Explicacion :

La idea principal de esta función es convertir la cadena de nota a su tono relativo y luego calcular su frecuencia.

El método que uso es exportar el sonido a midi e importar los datos en bruto, pero sospecho que hay una forma más elegante.


Casos de prueba :

f=%; (* assign the function above to f *)
f["A4"]    (* 440.    *)
f["A5"]    (* 880.    *)
f["C#-1"]  (* 8.66196 *)
f["Fb4"]   (* 329.628 *)
f["E4"]    (* 329.628 *)
f["E"]     (* 329.628 *)
njpipeorgan
fuente
2
Por lo general, me entristece ver que Mathematica incorpora soluciones que resuelven problemas trivialmente, pero en realidad esta es una forma bastante inspirada de hacerlo.
Robert Fraser
4

MATL , 56 53 50 49 48 bytes

Hj1)'C D EF G A B'=f22-'#b'"G@m]-s+ 12/G0)U+^55*

Utiliza la versión actual (10.1.0) , que es anterior a este desafío.

Pruébalo en línea !

Explicación

H                   % push 2
j1)                 % get input string. Take first character (note)
'C D EF G A B'=f    % find index of note: 1 for C, 3 for D...
22-                 % subtract 22. This number comes from three parts:
                    % 9; 1 for 0-based indexing; 12 to subtract 1 octave
'#b'"G@m]-s         % For loop. Gives 1 if input contains '#', -1 if 'b', 0 otherwise
+                   % add to previous number. Space needed to separate from next literal
12/                 % divide by 12
G0)                 % push input and get last character (octave)
U+                  % convert to number and add to previous number
^                   % raise 2 (that was initially pushed) to accumulated number 
55*                 % multiply by 55 (=27.5*2). Implicitly display
Luis Mendo
fuente
3

JavaScript ES7, 73 70 69 bytes

x=>[...x].map(c=>t+=c-1.75||"C#D EF G A B".search(c)/12,t=0)&&2**t*55

Utiliza la misma técnica que mi respuesta Japt .

ETHproducciones
fuente
3

Rubí, 69 65

->n{2**((n.ord*13/8%12-n.size+(n=~/#/?7:5))/12.0+n[-1].to_i)*55/4}

Sin golf en el programa de prueba

f=->n{
  2**(                    #raise 2 to the power of the following expression:
   (
     n.ord*13/8%12-       #note name C..B maps to number 0..11 calculated from the ascii code of n[0] 
     n.size+(n=~/#/?7:5)  #Correction for flat: length of n is 1 more for flat (or sharp) than for natural. Then apply correction for sharp
                          #now we have a number 3..14 for C..B (so 12 for A, will be a whole number when divided)
   )/12.0+                #divide by 12 to convert into a fraction of an octave

  n[-1].to_i              #add the octave number, last character in n
  )*                      #end of power expression, now we have A0=2,A1=4,A2=4 etc

  55/4                    #multiply to get correct frequency, this is shorter than 13.75 or 440/32                      
}

#complete octave test case
puts %w{A0 A#0 Bb0 B0 C1 C#1 Db1 D1 D#1 Eb1 E1 F1 F#1 Gb1 G1 G#1 Ab1 A1 A#1}.map{|e|[e,f[e]]}

#test case per OP
puts %w{A0 C4 F#3 Bb6 A#6}.map{|e|[e,f[e]]}

Salida

A0
27.5
A#0
29.13523509488062
Bb0
29.13523509488062
B0
30.867706328507758
C1
32.70319566257483
C#1
34.64782887210901
Db1
34.64782887210901
D1
36.70809598967595
D#1
38.890872965260115
Eb1
38.890872965260115
E1
41.20344461410875
F1
43.653528929125486
F#1
46.2493028389543
Gb1
46.2493028389543
G1
48.999429497718666
G#1
51.91308719749314
Ab1
51.91308719749314
A1
55.0
A#1
58.27047018976123
A0
27.5
C4
261.6255653005986
F#3
184.9972113558172
Bb6
1864.6550460723593
A#6
1864.6550460723593
Level River St
fuente
2

ES7, 82 bytes

s=>55*2**(+s.slice(-1)+("C D EF G A B".search(s[0])+(s[1]<'0')-(s[1]>'9')-21)/12)

Devuelve 130.8127826502993 en la entrada de "B # 2" como se esperaba.

Editar: Guardado 3 bytes gracias a @ user81655.

Neil
fuente
@ user81655 2*3**3*2es 108 en la consola del navegador de Firefox, lo cual concuerda 2*(3**3)*2. También tenga en cuenta que esa página también dice que ?:tiene mayor prioridad que, =pero en realidad tienen la misma prioridad (considere a=b?c=d:e=f).
Neil
Ah ok Mi Firefox no tiene, **así que nunca he podido probarlo. Creo ?:que tiene una precedencia más alta que =aunque porque en su ejemplo ase establece el resultado del ternario, en lugar de bejecutar el ternario. Las otras dos tareas están encerradas en el ternario, por lo que son un caso especial.
user81655
@ user81655 ¿Cómo está e=fdentro del ternario?
Neil
Considere a=b?c=d:e=f?g:h. Si tuvieran la misma precedencia y el primer ternario terminara =después e, causaría un error de asignación de mano izquierda no válido.
user81655
@ user81655 Pero eso también sería un problema si ?:tuviera mayor prioridad que de =todos modos. La expresión necesita agruparse como si lo fuera a=(b?c=d:(e=(f?g:h))). No puedes hacer eso si no tienen la misma precedencia.
Neil
2

C, 123 bytes

float d(char*s){int n=*s++,m=(n*12+(n<67?90:6))/7,o=*s++,a=o^35?o^98?0:-1:1;return exp((m+(a?*s++:o)*12+a)/17.3123-37.12);}

Uso:

#include <stdio.h>
#include <math.h>

float d(char*s){int n=*s++,m=(n*12+(n<67?90:6))/7,o=*s++,a=o^35?o^98?0:-1:1;return exp((m+(a?*s++:o)*12+a)/17.3123-37.12);}

int main()
{
    printf("%f\n", d("A4"));
}

El valor calculado siempre es aproximadamente 0,8 centavos menos que el valor exacto, porque corté tantos dígitos como sea posible de los números de coma flotante.

Descripción general del código:

float d(char*s){
    int n=*s++,        // read the letter
        m=(n*12+       // multiply by 12/7 to convert from A...G to 0...11
        (n<67?90:6)    // if A or B, add 1 octave; also add some fix-up rounding value
        )/7,

        o=*s++,        // read next char: the octave digit or accidental

        a=o^35?o^98?0:-1:1; // if accidental, convert it into +1 or -1; else 0

        return exp((m+ // I adjusted the factors to use exp instead of pow
            (a?*s++:o) // if was accidental, now read the octave digit
            *12+a)/
            17.3123-   // a more exact value is 17.3123404447
            37.12);    // a more exact value is 37.1193996632
}
anatolyg
fuente
1

R, 157 150 141 136 bytes

f=function(x){y=strsplit(x,"")[[1]];55*2^(as.double(y[nchar(x)])-1+(c(10,12,1,3,5,6,8)[LETTERS==y[1]]-switch(y[2],"#"=9,"b"=11,10))/12)}

Con sangría y líneas nuevas:

f=function(x){
     y=strsplit(x,"")[[1]]
     55 * 2^(as.double(y[nchar(x)]) - 1 + 
         (c(10,12,1,3,5,6,8)[LETTERS==y[1]] - 
         switch(y[2],"#"=9,"b"=11,10))/12)
     }

Uso:

> f("A0")
[1] 27.5
> f("C8")
[1] 4186.009
> sapply(c("C4","Bb6","A#6","A4"),f)
       C4       Bb6       A#6        A4 
 261.6256 1864.6550 1864.6550  440.0000 
plannapus
fuente
1

Python, 97 95 bytes

def f(n):a,*b,c=n;return 2**(int(c)+('C@D@EF@G@A@B'.find(a)-(21,(22,20)['#'in b])[b>[]])/12)*55

Basado en el antiguo enfoque de Pietu1998 (y otros) de buscar el índice de la nota en la cadena 'C@D@EF@G@A@B'para algunos caracteres en blanco u otros. Utilizo el desempaquetado iterable para analizar la cadena de notas sin condicionales. Hice un poco de álgebra al final para simplificar la expresión de conversión. No sé si puedo acortarlo sin cambiar mi enfoque.

Ogaday
fuente
1
Creo que b==['#']podría acortarse a '#'in b, y not ba b>[].
Zgarb
Bonitos puntos! Funciona para mi suite de prueba, gracias. Creo que puedo mejorar un poco las condiciones de golf en Python, gracias.
Ogaday
1

Wolfram Language (Mathematica), 69 bytes

ToExpression@("Music`"<>StringReplace[#,{"b"->"flat","#"->"sharp"}])&

Usando el paquete de música , con el cual simplemente ingresando una nota como una expresión se evalúa su frecuencia, así:

 In[1]:= Eflat3
Out[1]:= 155.563

Para guardar bytes evitando importar el paquete con <<Music, estoy usando los nombres completos: Music`Eflat3. Sin embargo, todavía tengo que reemplazar bcon flaty #con sharppara que coincida con el formato de entrada de la pregunta, lo que hago con un simple StringReplace.

vasilescur
fuente