Berapa frekuensi catatan ini?

21

Penyegaran musik cepat:

Keyboard piano terdiri dari 88 catatan. Pada setiap oktaf, ada 12 nada, C, C♯/D♭, D, D♯/E♭, E, F, F♯/G♭, G, G♯/A♭, A, A♯/B♭dan B. Setiap kali Anda menekan 'C', polanya mengulang satu oktaf lebih tinggi.

masukkan deskripsi gambar di sini

Sebuah not diidentifikasi secara unik oleh 1) huruf, termasuk benda tajam atau flat, dan 2) oktaf, yang merupakan angka dari 0 hingga 8. Tiga not pertama keyboard, adalah A0, A♯/B♭dan B0. Setelah ini muncul skala kromatik penuh pada oktaf 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♭1dan B1. Setelah ini muncul skala kromatik penuh pada oktaf 2, 3, 4, 5, 6, dan 7. Kemudian, nada terakhir adalah a C8.

Setiap nada berhubungan dengan frekuensi dalam kisaran 20-4100 Hz. Dengan A0mulai tepat pada 27.500 hertz, setiap not yang sesuai adalah not sebelumnya dikalikan akar kedua belas, atau sekitar 1.059463. Formula yang lebih umum adalah:

masukkan deskripsi gambar di sini

di mana n adalah jumlah not, dengan A0 menjadi 1. (Informasi lebih lanjut di sini )

Tantangan

Tulis program atau fungsi yang mengambil string yang mewakili catatan, dan mencetak atau mengembalikan frekuensi catatan itu. Kami akan menggunakan tanda pound #untuk simbol tajam (atau tagar untuk Anda anak muda) dan huruf kecil buntuk simbol datar. Semua input akan terlihat seperti (uppercase letter) + (optional sharp or flat) + (number)tanpa spasi. Jika input berada di luar kisaran keyboard (lebih rendah dari A0, atau lebih tinggi dari C8), atau ada karakter yang tidak valid, hilang atau tambahan, ini adalah input yang tidak valid, dan Anda tidak harus menanganinya. Anda juga dapat dengan aman berasumsi bahwa Anda tidak akan mendapatkan input aneh seperti E #, atau Cb.

Presisi

Karena ketepatan tanpa batas tidak benar-benar mungkin, kami akan mengatakan bahwa apa pun dalam satu sen dari nilai sebenarnya dapat diterima. Tanpa masuk ke rincian berlebih, satu sen adalah akar ke dua dari 1200, atau 1.0005777895. Mari kita gunakan contoh nyata untuk membuatnya lebih jelas. Katakanlah input Anda adalah A4. Nilai pasti dari catatan ini adalah 440 Hz. Sekali datar sen 440 / 1.0005777895 = 439.7459. 440 * 1.0005777895 = 440.2542Oleh karena itu, tajam satu sen adalah angka yang lebih besar dari 439.7459 tetapi lebih kecil dari 440.2542 cukup tepat untuk dihitung.

Uji kasus

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.

Ingatlah bahwa Anda tidak harus menangani input yang tidak valid. Jika program Anda berpura-pura itu adalah input nyata dan mencetak nilai, itu dapat diterima. Jika program Anda macet, itu juga dapat diterima. Apa pun bisa terjadi ketika Anda mendapatkannya. Untuk daftar lengkap input dan output, lihat halaman ini

Seperti biasa, ini adalah kode-golf, sehingga celah standar berlaku, dan jawaban tersingkat dalam byte menang.

DJMcMayhem
sumber
9
"H # 4 -> H bukan not nyata, input tidak valid." Kecuali di Eropa.
Lui
6
@Lui apa ini tentang Eropa seolah-olah seluruh Eropa menggunakan H? Hartinya B adalah AFAIK hanya digunakan di negara-negara berbahasa Jerman. (di mana Bartinya Bb omong-omong.) Apa yang disebut orang Inggris dan Irlandia B disebut Si atau Ti di Spanyol dan Italia, seperti dalam Do Re Mi Fa Sol La Si.
Level River St
3
Saya telah memainkan B♯2 pada biola sebelumnya, ini adalah nada yang sangat masuk akal dan tidak aneh sama sekali.
Neil
3
@steveverrill Hdigunakan di Jerman, Republik Ceko, Slovakia, Polandia, Hongaria, Serbia, Denmark, Norwegia, Finlandia, Estonia, dan Austria, menurut Wikipedia . (Saya juga bisa mengkonfirmasinya untuk Finlandia sendiri.)
PurkkaKoodari
6
@ Neil Itu mungkin hanya kebetulan. ;)
gelas kimia

Jawaban:

21

Japt, 41 37 35 34 byte

Saya akhirnya memiliki kesempatan untuk memanfaatkan ¾dengan baik! :-)

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

Cobalah online!

Bagaimana itu bekerja

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

Uji kasus

Semua test case yang valid datang dengan baik. Itu yang tidak valid di mana ia menjadi aneh ...

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!)
Produksi ETH
sumber
13
+ ¾ untuk menggunakan ¾ :)
anatolyg
1
Bukankah ini sebenarnya 38 byte ?
Patrick Roberts
@ PatrickRoberts Ini adalah 38 byte di UTF-8, tetapi Japt menggunakan pengkodean ISO-8859-1 , di mana setiap karakter tepat satu byte.
ETHproduk
8

Pyth, 46 44 43 42 39 35 byte

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

Cobalah online. Suite uji.

Kode sekarang menggunakan algoritma yang mirip dengan jawaban Japt ETHproductions , jadi kredit kepadanya untuk itu.

Penjelasan

                                            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 lama (42 byte, 39 w / string yang dipadatkan)

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

Penjelasan

PurkkaKoodari
sumber
Ini menarik. Bagaimana cara Pyth mengemas?
Luis Mendo
@LuisMendo Anda dapat menemukan informasi tentang itu di dokumen . Pada dasarnya, ia menemukan basis terkecil untuk mengkonversi data ke dan kemudian mengkodekan hasilnya dalam basis 256.
PurkkaKoodari
7

Mathematica, 77 byte

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

Penjelasan :

Gagasan utama dari fungsi ini adalah untuk mengubah string nada ke nada relatifnya, dan kemudian menghitung frekuensinya.

Metode yang saya gunakan adalah mengekspor suara ke midi dan mengimpor data mentah, tetapi saya curiga ada cara yang lebih elegan.


Kasus uji :

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
sumber
2
Biasanya saya sedih melihat Mathematica bawaan yang memecahkan masalah sepele, tapi ini sebenarnya cara yang cukup terinspirasi untuk melakukannya.
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*

Menggunakan rilis saat ini (10.1.0) , yang lebih awal dari tantangan ini.

Cobalah online !

Penjelasan

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

Menggunakan teknik yang sama dengan jawaban Japt saya .

Produksi ETH
sumber
3

Ruby, 69 65

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

Tidak digabungkan dalam program uji

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

Keluaran

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

Mengembalikan 130.8127826502993 pada input "B # 2" seperti yang diharapkan.

Sunting: Disimpan 3 byte berkat @ user81655.

Neil
sumber
@ user81655 2*3**3*2adalah 108 di konsol browser Firefox, yang setuju dengan 2*(3**3)*2. Perhatikan juga bahwa halaman itu juga mengatakan bahwa ?:memiliki prioritas lebih tinggi daripada =tetapi mereka sebenarnya memiliki prioritas yang sama (pertimbangkan a=b?c=d:e=f).
Neil
Ah, baiklah. Firefox saya tidak punya, **jadi saya tidak pernah bisa mengujinya. Saya pikir ?:memang memiliki prioritas lebih tinggi daripada =karena karena dalam contoh Anda adiatur ke hasil ternary, daripada b, kemudian mengeksekusi ternary. Dua penugasan lain terlampir di ternary sehingga menjadi kasus khusus.
user81655
@ user81655 Bagaimana kabar e=fdi dalam ternary?
Neil
Pertimbangkan a=b?c=d:e=f?g:h. Jika mereka diutamakan yang sama dan terner pertama berakhir =setelah e, itu akan menyebabkan kesalahan tugas kiri tidak valid.
user81655
@ user81655 Tapi itu juga akan menjadi masalah jika ?:memiliki prioritas lebih tinggi daripada yang sebenarnya =. Ekspresi perlu dikelompokkan seolah-olah a=(b?c=d:(e=(f?g:h))). Anda tidak dapat melakukan itu jika mereka tidak memiliki prioritas yang sama.
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);}

Pemakaian:

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

Nilai yang dihitung selalu sekitar 0,8 sen kurang dari nilai yang tepat, karena saya memotong angka sebanyak mungkin dari angka floating-point.

Tinjauan umum kode:

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

Dengan indentasi dan baris baru:

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

Pemakaian:

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

Berdasarkan pendekatan lama Pietu1998 (dan lainnya) dalam mencari indeks catatan dalam string 'C@D@EF@G@A@B'untuk beberapa char kosong atau yang lain. Saya menggunakan iterable unpacking untuk mengurai string catatan tanpa persyaratan. Saya melakukan sedikit aljabar pada akhirnya untuk menyederhanakan ekspresi konversi. Tidak tahu apakah saya bisa membuatnya lebih pendek tanpa mengubah pendekatan saya.

Ogaday
sumber
1
Saya pikir b==['#']bisa disingkat menjadi '#'in b, dan not buntuk b>[].
Zgarb
Poin yang bagus! Bekerja untuk test suite saya, terima kasih. Saya pikir saya bisa meningkatkan pada bermain kondisional di Python sedikit, terima kasih.
Ogaday
1

Bahasa Wolfram (Mathematica), 69 byte

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

Menggunakan paket musik , yang dengannya hanya memasukkan catatan sebagai ekspresi mengevaluasi frekuensinya, seperti ini:

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

Untuk menyimpan byte dengan menghindari untuk mengimpor paket dengan <<Music, saya menggunakan nama-nama yang memenuhi syarat: Music`Eflat3. Namun, saya masih harus mengganti bdengan flatdan #dengan sharpuntuk mencocokkan format input pertanyaan, yang saya lakukan dengan sederhana StringReplace.

vasilescur
sumber