Quelle est la fréquence de cette note?


21

Rafraîchissement musical rapide:

Le clavier de piano se compose de 88 notes. Sur chaque octave, il y a 12 notes, C, C♯/D♭, D, D♯/E♭, E, F, F♯/G♭, G, G♯/A♭, A, A♯/B♭et B. Chaque fois que vous frappez un «C», le motif se répète une octave plus haut.

entrez la description de l'image ici

Une note est identifiée de manière unique par 1) la lettre, y compris les aigus ou plats, et 2) l'octave, qui est un nombre compris entre 0 et 8. Les trois premières notes du clavier sont A0, A♯/B♭et B0. Après cela vient la pleine échelle chromatique sur les octaves 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♭1et B1. Après cela vient une gamme chromatique complète sur les octaves 2, 3, 4, 5, 6 et 7. Ensuite, la dernière note est a C8.

Chaque note correspond à une fréquence dans la gamme 20-4100 Hz. Avec un A0démarrage à exactement 27,500 hertz, chaque note correspondante est la note précédente multipliée par la douzième racine de deux, soit environ 1,059463. Une formule plus générale est la suivante:

entrez la description de l'image ici

où n est le numéro de la note, A0 étant 1. (Plus d'informations ici )

Le défi

Écrivez un programme ou une fonction qui prend une chaîne représentant une note et imprime ou renvoie la fréquence de cette note. Nous utiliserons un signe #dièse pour le symbole pointu (ou un hashtag pour vous les jeunes) et un minuscule bpour le symbole plat. Toutes les entrées ressembleront (uppercase letter) + (optional sharp or flat) + (number)sans espace blanc. Si l'entrée est en dehors de la plage du clavier (inférieure à A0 ou supérieure à C8), ou s'il y a des caractères non valides, manquants ou supplémentaires, il s'agit d'une entrée non valide et vous n'avez pas à la gérer. Vous pouvez également supposer en toute sécurité que vous n'obtiendrez pas d'entrées étranges telles que E # ou Cb.

Précision

Comme une précision infinie n'est pas vraiment possible, nous dirons que tout ce qui se trouve à moins d' un cent de la valeur réelle est acceptable. Sans entrer dans les détails, un cent est la 1200e racine de deux, ou 1.0005777895. Prenons un exemple concret pour le rendre plus clair. Disons que votre entrée était A4. La valeur exacte de cette note est de 440 Hz. Une fois que cent est plat 440 / 1.0005777895 = 439.7459. Une fois que cent est net 440 * 1.0005777895 = 440.2542, tout nombre supérieur à 439.7459 mais inférieur à 440.2542 est suffisamment précis pour compter.

Cas de 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.

Gardez à l'esprit que vous n'avez pas à gérer les entrées non valides. Si votre programme prétend qu'il s'agit de véritables entrées et affiche une valeur, cela est acceptable. Si votre programme plante, c'est également acceptable. Tout peut arriver quand vous en avez un. Pour la liste complète des entrées et sorties, voir cette page

Comme d'habitude, il s'agit de code-golf, donc les lacunes standard s'appliquent et la réponse la plus courte en octets l'emporte.


9
"H # 4 -> H n'est pas une vraie note, entrée invalide." Sauf en Europe.
Lui

6
@Lui, quelle est cette chose à propos de l'Europe comme si toute l'Europe utilise H? Hce qui signifie que B est AFAIK uniquement utilisé dans les pays germanophones. (où Bsignifie Bb au fait.) Ce que les Britanniques et les Irlandais appellent B est appelé Si ou Ti en Espagne et en Italie, comme dans Do Re Mi Fa Sol La Si.
Level River St

3
J'ai déjà joué un B♯2 sur un alto, c'est une note parfaitement raisonnable et pas bizarre du tout.
Neil

3
@steveverrill Hest utilisé en Allemagne, en République tchèque, en Slovaquie, en Pologne, en Hongrie, en Serbie, au Danemark, en Norvège, en Finlande, en Estonie et en Autriche, selon Wikipedia . (Je peux également le confirmer pour la Finlande moi-même.)
PurkkaKoodari

6
@Neil C'était probablement juste accidentel. ;)
bécher

Réponses:


21

Japt, 41 37 35 34 octets

J'ai enfin une chance d'en faire ¾bon usage! :-)

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

Essayez-le en ligne!

Comment ça marche

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

Cas de test

Tous les cas de test valides passent bien. Ce sont les invalides où ça devient bizarre ...

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
+ ¾ pour utiliser ¾ :)
anatolyg

1
N'est-ce pas en fait 38 octets ?
Patrick Roberts

@PatrickRoberts Il s'agit de 38 octets en UTF-8, mais Japt utilise le codage ISO-8859-1 , dans lequel chaque caractère fait exactement un octet.
ETHproductions

8

Pyth, 46 44 43 42 39 35 octets

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

Essayez-le en ligne. Suite de tests.

Le code utilise maintenant un algorithme similaire à la réponse Japt d'ETHproductions , alors merci à lui pour cela.

Explication

                                            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)

Ancienne version (42 octets, 39 avec chaîne compressée)

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

Explication


C'est intéressant. Comment Pyth compresse-t-il les chaînes?
Luis Mendo

@LuisMendo Vous pouvez trouver des informations à ce sujet dans la documentation . Fondamentalement, il trouve la plus petite base pour convertir les données et ensuite encode le résultat dans la base 256.
PurkkaKoodari

7

Mathematica, 77 octets

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

Explication :

L'idée principale de cette fonction est de convertir la chaîne de notes à sa hauteur relative, puis de calculer sa fréquence.

La méthode que j'utilise est d'exporter le son vers midi et d'importer les données brutes, mais je soupçonne qu'il existe une manière plus élégante.


Cas de test :

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
Habituellement, je suis triste de voir des buildins Mathematica qui résolvent trivialement des problèmes, mais c'est en fait une façon assez inspirée de le faire.
Robert Fraser

4

MATL , 56 53 50 49 48 octets

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

Utilise la version actuelle (10.1.0) , antérieure à ce défi.

Essayez-le en ligne !

Explication

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 octets

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

Utilise la même technique que ma réponse Japt .


3

Rubis, 69 65

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

Non testé dans le programme de 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]]}

Production

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 octets

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

Renvoie 130.8127826502993 à l'entrée de "B # 2" comme prévu.

Edit: sauvé 3 octets grâce à @ user81655.


@ user81655 2*3**3*2est 108 dans la console du navigateur de Firefox, ce qui est d'accord 2*(3**3)*2. Notez également que cette page indique également que la ?:priorité est plus élevée que, =mais qu'ils ont en fait une priorité égale (à considérer a=b?c=d:e=f).
Neil

Ah ok. Mon Firefox n'en a pas **donc je n'ai jamais pu le tester. Je pense ?:que la priorité est plus élevée que =dans la mesure où, dans votre exemple, ale résultat est le ternaire, plutôt que l' bexécution du ternaire. Les deux autres affectations sont incluses dans le ternaire, elles sont donc un cas spécial.
user81655

@ user81655 Comment est l' e=fintérieur du ternaire?
Neil

Considérez a=b?c=d:e=f?g:h. S'ils avaient la même priorité et que le premier ternaire se terminait à la =fin e, cela provoquerait une erreur d'affectation gauche non valide.
user81655

@ user81655 Mais ce serait aussi un problème s'il ?:avait une priorité plus élevée que de =toute façon. L'expression doit se regrouper comme si elle l'était a=(b?c=d:(e=(f?g:h))). Vous ne pouvez pas faire cela s'ils n'ont pas la même priorité.
Neil

2

C, 123 octets

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

Usage:

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

La valeur calculée est toujours environ 0,8 cents inférieure à la valeur exacte, car j'ai coupé autant de chiffres que possible des nombres à virgule flottante.

Aperçu du code:

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 octets

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

Avec retrait et nouvelles lignes:

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

Usage:

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

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

Basé sur l'ancienne approche de Pietu1998 (et d'autres) consistant à rechercher l'index de la note dans la chaîne 'C@D@EF@G@A@B'pour un caractère vierge ou un autre. J'utilise le déballage itérable pour analyser la chaîne de notes sans condition. J'ai fait un peu d'algèbre à la fin pour simplifier l'expression de conversion. Je ne sais pas si je peux le raccourcir sans changer mon approche.


1
Je pense que b==['#']pourrait être raccourci à '#'in b, et not bà b>[].
Zgarb

Bons points! Fonctionne pour ma suite de tests, merci. Je pense que je peux améliorer les conditions de golf en Python un peu, merci.
Ogaday

1

Wolfram Language (Mathematica), 69 octets

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

Utiliser le package musical , avec lequel la simple saisie d'une note en tant qu'expression évalue sa fréquence, comme ceci:

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

Pour enregistrer les octets en évitant d'importer le paquet avec <<Music, j'utilise les noms qualifiés: Music`Eflat3. Cependant, je dois encore remplacer bpar flatet #par sharppour correspondre au format d'entrée de la question, ce que je fais avec un simple StringReplace.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.