Qual é a frequência desta nota?

21

Atualização musical rápida:

O teclado do piano consiste em 88 notas. Em cada oitava, há 12 notas C, C♯/D♭, D, D♯/E♭, E, F, F♯/G♭, G, G♯/A♭, A, A♯/B♭e B. Cada vez que você pressiona um 'C', o padrão repete uma oitava mais alta.

insira a descrição da imagem aqui

Uma nota é identificada exclusivamente por 1) a letra, incluindo objetos pontiagudos ou planos, e 2) a oitava, que é um número de 0 a 8. As três primeiras notas do teclado são A0, A♯/B♭e B0. Depois disso, vem a escala cromática completa na oitava 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♭1e B1. Depois disso, aparece uma escala cromática completa nas oitavas 2, 3, 4, 5, 6 e 7. Em seguida, a última nota é a C8.

Cada nota corresponde a uma frequência na faixa de 20-4100 Hz. Com o A0início de exatamente 27.500 hertz, cada nota correspondente é a nota anterior multiplicada pela décima segunda raiz de duas, ou aproximadamente 1.059463. Uma fórmula mais geral é:

insira a descrição da imagem aqui

onde n é o número da nota, com A0 sendo 1. (Mais informações aqui )

O desafio

Escreva um programa ou função que use uma sequência que represente uma nota e imprima ou retorne a frequência dessa nota. Usaremos um sinal de libra #para o símbolo nítido (ou hashtag para os jovens) e uma minúscula bpara o símbolo plano. Todas as entradas terão aparência (uppercase letter) + (optional sharp or flat) + (number)sem espaço em branco. Se a entrada estiver fora do alcance do teclado (menor que A0 ou maior que C8) ou se houver caracteres inválidos, ausentes ou extras, esta é uma entrada inválida e você não precisa lidar com isso. Você também pode assumir com segurança que não receberá entradas estranhas, como E # ou Cb.

Precisão

Como a precisão infinita não é realmente possível, diremos que qualquer coisa dentro de um centavo do valor real é aceitável. Sem entrar em detalhes em excesso, um centavo é a 1200a raiz de dois, ou 1.0005777895. Vamos usar um exemplo concreto para torná-lo mais claro. Digamos que sua entrada foi A4. O valor exato desta nota é 440 Hz. Uma vez centavo é 440 / 1.0005777895 = 439.7459. Uma vez que o centavo é nítido 440 * 1.0005777895 = 440.2542, portanto, qualquer número maior que 439.7459, mas menor que 440.2542, é preciso o suficiente para contar.

Casos de teste

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.

Lembre-se de que você não precisa lidar com as entradas inválidas. Se o seu programa fingir que são entradas reais e imprimir um valor, isso é aceitável. Se o seu programa falhar, isso também é aceitável. Tudo pode acontecer quando você recebe um. Para a lista completa de entradas e saídas, consulte esta página

Como de costume, esse é um código de golfe, então as brechas padrão se aplicam e a resposta mais curta em bytes vence.

DJMcMayhem
fonte
9
"H # 4 -> H não é uma nota real, entrada inválida." Exceto na Europa.
Lui
6
@Lui O que é isso na Europa, como se toda a Europa usasse H? Ho significado de B é AFAIK usado apenas em países de língua alemã. (onde Bsignifica Bb, a propósito.) O que os britânicos e irlandeses chamam de B é chamado Si ou Ti na Espanha e na Itália, como em Do Re Mi Fa Sol La Si.
Level River St
3
Eu já toquei um B♯2 em uma viola antes, é uma nota perfeitamente razoável e nada estranha.
Neil
3
O @steveverrill Hé usado na Alemanha, República Tcheca, Eslováquia, Polônia, Hungria, Sérvia, Dinamarca, Noruega, Finlândia, Estônia e Áustria, de acordo com a Wikipedia . (Eu também posso confirmá-lo para a Finlândia).
PurkkaKoodari
6
@ Neil Provavelmente foi apenas acidental. ;)
copo

Respostas:

21

Japt, 41 37 35 34 bytes

Finalmente tenho a chance de fazer ¾bom uso! :-)

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

Experimente online!

Como 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 teste

Todos os casos de teste válidos são excelentes. São os inválidos onde fica estranho ...

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!)
ETHproductions
fonte
13
+ ¾ para usar ¾ :)
anatolyg
11
Isso não é realmente 38 bytes ?
Patrick Roberts
@PatrickRoberts São 38 bytes em UTF-8, mas Japt usa a codificação ISO-8859-1 , na qual cada caractere tem exatamente um byte.
ETHproductions
8

Pitão, 46 44 43 42 39 35 bytes

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

Experimente online. Suíte de teste.

O código agora usa um algoritmo semelhante à resposta japonesa da ETHproductions , portanto, agradeça a ele por isso.

Explicação

                                            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)

Versão antiga (42 bytes, 39 com sequência compactada)

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

Explicação

PurkkaKoodari
fonte
Isto é interessante. Como Pyth embala as strings?
Luis Mendo
@LuisMendo Você pode encontrar informações sobre isso nos documentos . Basicamente, ele encontra a menor base para converter os dados e, em seguida, codifica o resultado na base 256.
PurkkaKoodari
7

Mathematica, 77 bytes

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

Explicação :

A idéia principal dessa função é converter a seqüência de notas em sua afinação relativa e calcular sua frequência.

O método usado é exportar o som para o midi e importar os dados brutos, mas suspeito que exista uma maneira mais elegante.


Casos de teste :

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
fonte
2
Normalmente, fico triste ao ver os recursos do Mathematica que resolvem problemas trivialmente, mas essa é realmente uma maneira bastante inspirada de fazê-lo.
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*

Usa a versão atual (10.1.0) , anterior a esse desafio.

Experimente online !

Explicação

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
fonte
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 a mesma técnica da minha resposta em japonês .

ETHproductions
fonte
3

Ruby, 69 65

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

Ungolfed in program program

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]]}

Saída

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
fonte
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)

Retorna 130.8127826502993 na entrada de "B # 2" conforme o esperado.

Editar: salvou 3 bytes graças a @ user81655.

Neil
fonte
@ user81655 2*3**3*2é 108 no console do navegador Firefox, o que concorda 2*(3**3)*2. Observe também que essa página também diz que ?:tem precedência maior do que, =mas na verdade eles têm precedência igual (considere a=b?c=d:e=f).
Neil
Ah ok. Meu Firefox não possui, **portanto nunca fui capaz de testá-lo. Eu acho ?:que tem uma precedência mais alta do =que no entanto, porque no seu exemplo aé definido o resultado do ternário, em vez de bexecutar o ternário. As outras duas tarefas estão incluídas no ternário, portanto são um caso especial.
user81655
@ user81655 Como está o e=finterior do ternário?
Neil
Considere a=b?c=d:e=f?g:h. Se eles tivessem a mesma precedência e o primeiro ternário terminasse =depois e, isso causaria um erro de atribuição à esquerda inválido.
user81655
@ user81655 Mas isso também seria um problema se ?:tivesse maior precedência do que =qualquer outra forma. A expressão precisa agrupar como se fosse a=(b?c=d:(e=(f?g:h))). Você não pode fazer isso se eles não tiverem a mesma precedência.
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"));
}

O valor calculado é sempre cerca de 0,8 centavos a menos do que o valor exato, porque cortei o maior número possível de dígitos nos números de ponto flutuante.

Visão geral do 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
fonte
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)}

Com recuo e novas linhas:

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
fonte
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

Com base na antiga abordagem de Pietu1998 (e de outros), de procurar o índice da nota na string 'C@D@EF@G@A@B'por algum caractere em branco ou outro. Eu uso a descompactação iterável para analisar a sequência de notas sem condicionais. Fiz um pouco de álgebra no final para simplificar a expressão de conversão. Não sei se posso diminuí-lo sem alterar minha abordagem.

Ogaday
fonte
11
Eu acho que b==['#']poderia ser reduzido para '#'in be not bpara b>[].
Zgarb
Pontos agradáveis! Funciona para minha suíte de testes, obrigado. Acho que posso melhorar bastante o condicionamento de golfe em Python, obrigado.
Ogaday
1

Wolfram Language (Mathematica), 69 bytes

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

Usando o pacote de músicas , com o qual simplesmente digitar uma nota como expressão avalia sua frequência, desta forma:

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

Para salvar bytes, evitando importar o pacote com <<Music, estou usando os nomes totalmente qualificados: Music`Eflat3. No entanto, ainda tenho que substituir bpor flate #com sharppara corresponder ao formato de entrada da pergunta, o que faço com uma simples StringReplace.

vasilescur
fonte