Qual è la frequenza di questa nota?


21

Aggiornamento musicale rapido:

La tastiera del piano è composta da 88 note. Su ciascuna ottava, ci sono 12 note C, C♯/D♭, D, D♯/E♭, E, F, F♯/G♭, G, G♯/A♭, A, A♯/B♭e B. Ogni volta che colpisci una "C", il pattern si ripete di un'ottava più in alto.

inserisci qui la descrizione dell'immagine

Una nota è identificata in modo univoco da 1) la lettera, inclusi eventuali oggetti taglienti o piatti, e 2) l'ottava, che è un numero compreso tra 0 e 8. Le prime tre note della tastiera sono A0, A♯/B♭e B0. Dopo questo arriva la scala cromatica completa sull'ottava 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. Dopo questo arriva una scala cromatica completa sulle ottave 2, 3, 4, 5, 6 e 7. Quindi, l'ultima nota è a C8.

Ogni nota corrisponde a una frequenza nell'intervallo 20-4100 Hz. Con A0partendo esattamente 27.500 hertz, ogni nota corrispondente è precedenti volte nota dodicesimo radice di due, o approssimativamente 1,059,463 mila. Una formula più generale è:

inserisci qui la descrizione dell'immagine

dove n è il numero della nota, con A0 pari a 1. (Ulteriori informazioni qui )

La sfida

Scrivi un programma o una funzione che accetta una stringa che rappresenta una nota e stampa o restituisce la frequenza di quella nota. Useremo un cancelletto #per il simbolo appuntito (o un hashtag per voi giovani) e un minuscolo bper il simbolo piatto. Tutti gli input appariranno (uppercase letter) + (optional sharp or flat) + (number)senza spazi bianchi. Se l'input è al di fuori dell'intervallo della tastiera (inferiore a A0 o superiore a C8) o se sono presenti caratteri non validi, mancanti o extra, si tratta di un input non valido e non è necessario gestirlo. Puoi anche presumere che non otterrai input strani come E # o Cb.

Precisione

Dal momento che la precisione infinita non è davvero possibile, diremo che qualsiasi cosa all'interno di un centesimo del valore reale è accettabile. Senza entrare nei dettagli in eccesso, un centesimo è la 1200a radice di due, ovvero 1.0005777895. Facciamo un esempio concreto per renderlo più chiaro. Supponiamo che il tuo input sia stato A4. Il valore esatto di questa nota è 440 Hz. Una volta che cent piatto è 440 / 1.0005777895 = 439.7459. Una volta 440 * 1.0005777895 = 440.2542che il segno di spunta è quindi, qualsiasi numero maggiore di 439.7459 ma inferiore a 440.2542 è abbastanza preciso da contare.

Casi test

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.

Tieni presente che non è necessario gestire input non validi. Se il tuo programma finge che siano input reali e stampa un valore, questo è accettabile. Se il programma si arresta in modo anomalo, anche questo è accettabile. Tutto può succedere quando ne ottieni uno. Per l'elenco completo di ingressi e uscite, vedere questa pagina

Come al solito, si tratta di code-golf, quindi si applicano scappatoie standard e vince la risposta più breve in byte.


9
"H # 4 -> H non è una nota reale, input non valido." Tranne in Europa.
Lui

6
@Lui cos'è questa cosa sull'Europa come se l'intera Europa usasse H? Hsignifica che B è AFAIK utilizzato solo nei paesi di lingua tedesca. (dove Bsignifica comunque Bb.) Ciò che gli inglesi e gli irlandesi chiamano B si chiama Si o Ti in Spagna e in Italia, come in Do Re Mi Fa Sol La Si.
Level River St

3
Ho suonato un B♯2 su una viola in precedenza, è una nota perfettamente ragionevole e per nulla strana.
Neil,

3
@steveverrill Hè utilizzato in Germania, Repubblica Ceca, Slovacchia, Polonia, Ungheria, Serbia, Danimarca, Norvegia, Finlandia, Estonia e Austria, secondo Wikipedia . (Posso anche confermarlo per la Finlandia.)
PurkkaKoodari,

6
@Neil Probabilmente è stato solo accidentale. ;)
becher

Risposte:


21

Japt, 41 37 35 34 byte

Finalmente ho la possibilità di mettere ¾a frutto! :-)

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

Provalo online!

Come funziona

          // 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.

Casi test

Tutti i casi di test validi sono validi. Sono quelli non validi in cui diventa strano ...

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

13
+ ¾ per l'utilizzo di ¾ :)
anatolyg

1
Non sono effettivamente 38 byte ?
Patrick Roberts,

@PatrickRoberts Questo è 38 byte in UTF-8, ma Japt utilizza la codifica ISO-8859-1 , in cui ogni carattere è esattamente un byte.
ETHproductions

8

Pyth, 46 44 43 42 39 35 byte

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

Provalo online. Suite di test.

Il codice ora utilizza un algoritmo simile alla risposta Japt di ETHproductions , quindi attribuiscigli credito.

Spiegazione

                                            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)

Vecchia versione (42 byte, 39 con stringa imballata)

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

Spiegazione


Questo è interessante. In che modo Pyth impacchetta le stringhe?
Luis Mendo,

@LuisMendo Puoi trovare informazioni al riguardo nei documenti . Fondamentalmente, trova la base più piccola in cui convertire i dati e quindi codifica il risultato in base 256.
PurkkaKoodari

7

Mathematica, 77 byte

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

Spiegazione :

L'idea principale di questa funzione è quella di convertire la stringa di note nella sua tonalità relativa e quindi calcolarne la frequenza.

Il metodo che uso è esportare l'audio su midi e importare i dati grezzi, ma sospetto che esista un modo più elegante.


Casi di prova :

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

2
Di solito sono triste vedere i built-in di Mathematica che risolvono banalmente i problemi, ma questo è in realtà un modo piuttosto ispirato per farlo.
Robert Fraser,

4

MATL , 56 53 50 49 48 byte

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

Utilizza la versione corrente (10.1.0) , precedente a questa sfida.

Provalo online !

Spiegazione

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

3

JavaScript ES7, 73 70 69 byte

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

Utilizza la stessa tecnica della mia risposta Japt .


3

Ruby, 69 65

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

Non registrato nel programma di test

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

Produzione

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

2

ES7, 82 byte

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

Restituisce 130.8127826502993 sull'input di "B # 2" come previsto.

Modifica: salvato 3 byte grazie a @ user81655.


@ user81655 2*3**3*2è 108 nella console del browser Firefox, che è d'accordo 2*(3**3)*2. Si noti inoltre che quella pagina dice anche che ?:ha una precedenza maggiore rispetto a quella =ma in realtà hanno la stessa precedenza (considerare a=b?c=d:e=f).
Neil,

Ah ok. Il mio Firefox non ha **quindi non sono mai stato in grado di testarlo. Penso ?:che abbia una precedenza superiore rispetto a quella =perché, nel tuo esempio, aè impostato sul risultato del ternario, piuttosto che sull'esecuzione bdel ternario. Gli altri due incarichi sono racchiusi nel ternario, quindi sono un caso speciale.
user81655

@ user81655 Com'è l' e=finterno del ternario?
Neil,

Prendere in considerazione a=b?c=d:e=f?g:h. Se avessero la stessa precedenza e il primo ternario finisse =dopo e, ciò causerebbe un errore di assegnazione della mano sinistra non valido.
user81655

@ user81655 Ma ciò costituirebbe anche un problema se ?:avesse una precedenza più elevata che =comunque. L'espressione deve raggrupparsi come se fosse a=(b?c=d:(e=(f?g:h))). Non puoi farlo se non hanno la stessa precedenza.
Neil,

2

C, 123 byte

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"));
}

Il valore calcolato è sempre circa 0,8 cent in meno rispetto al valore esatto, perché ho tagliato il maggior numero possibile di cifre dai numeri in virgola mobile.

Panoramica del codice:

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
}

1

R, 157 150 141 136 byte

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 trattino e righe:

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 

1

Python, 97 95 byte

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

Basato sul vecchio approccio di Pietu1998 (e altri) di cercare l'indice della nota nella stringa 'C@D@EF@G@A@B'per un carattere vuoto o un altro. Uso il disimballaggio iterabile per analizzare la stringa della nota senza condizionali. Alla fine ho fatto un po 'di algebra per semplificare l'espressione di conversione. Non so se posso accorciarlo senza cambiare approccio.


1
Penso che b==['#']potrebbe essere abbreviato in '#'in be not bper b>[].
Zgarb,

Bei punti! Funziona con la mia suite di test, grazie. Penso di poter migliorare un po 'di golf sui condizionali in Python, grazie.
Ogaday,

1

Wolfram Language (Mathematica), 69 byte

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

Utilizzando il pacchetto musicale , con il quale semplicemente inserendo una nota come espressione si valuta la sua frequenza, in questo modo:

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

Per salvare byte evitando di importare il pacchetto con <<Music, sto usando i nomi completi: Music`Eflat3. Tuttavia, devo ancora sostituire bcon flate #con sharpper abbinare il formato di input della domanda, che faccio con un semplice StringReplace.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.