MIDI Parçası Görüntüleme


17

Arka fon

MIDI dosyaları WAV veya MP3 ses dosyalarından oldukça farklıdır. MP3 ve WAV dosyaları sesin bir "kaydını" temsil eden baytlar içerirken, MIDI dosyaları MIDI olaylarında depolanan bir MIDI sentezleyicisine hangi sanal aletin çalınacağını veya kullanılması gereken bir MIDI sıralayıcısını bildiren bir dizi MIDI mesajı içerir. Bu mesajlar parçalar halinde saklanır ve bir parça koleksiyonu, olayları bir sekanslayıcı tarafından analiz edilebilen ve mesajlarının sekansörden bir sentezleyicinin alıcısına iletilebildiği bir MIDI sekansı oluşturur.

Çoğu zaman MIDI olaylarında depolanan MIDI mesajları Not Sentezleyiciye belirli bir notayı çalmasını söyleyen mesajlar veya sentezleyiciye nota çalmayı durdurmasını söyleyen Not Kapalı mesajlarıdır. Bu mesajlar iki veri baytı içerir, bunlardan ilki sentezleyiciye notanın hızı hakkında bilgi verir (daha yüksek hız daha yüksek bir nota neden olur) ve ikincisi sentezleyiciye nota çalmasını söyler (yani Orta C). Olayların kendileri, sıralayıcıya mesajları ne zaman göndereceğini bildirme amacına hizmet eden keneler de içerir.

Meydan okuma

Zorluk, tam izli bir program veya tek izli MIDI dizisindeki bir dizi Not Açık ve Not Kapalı MIDI mesajını analiz eden ve belirli notların ne zaman açık olduğunu, ne zaman kapandığını ve bu notların hızı. Grafiğin dikey ekseni not değerini temsil eder ve aşağıda açıklandığı gibi etiketlenmelidir ve yatay eksen MIDI kenelerinde zamanı temsil eder (ancak karmaşıklığı ve boşluk sorunlarını azaltmak için etiketsiz kalması gerekir).

Girişiniz, her biri bir dizi tamsayı değeri içeren dört ayrı dizi veya liste olabilir; bir dizi tamsayı değerine sahip dört alt dizi / alt liste içeren iki boyutlu bir dizi veya liste; veya diğer uygun araçlar; bu, parçadaki Not Açık ve Not Kapalı mesajlarıyla toplama MIDI olaylarını temsil eder. Bu dizilerin ilkindeki değerler notu, ikincisi hızı, üçüncüsü olay işaretindeki notu ve dördüncü notu olay işaretini belirtir. Örneğin, aşağıdakiler gibi dört dizi verildi:

{60, 62, 64, 65,  67}
{20, 40, 60, 80, 100}
{ 0,  4,  8, 12,  16}
{ 2,  6, 10, 14,  18}

Her dizinin ilk öğesini analiz etmek iki olay sağlar: 0 notunda, Not Açık komutu, 60 notu (Orta C) ve 20 notu notu olan bir olay; ve aynı not ve hızda bir Not Kapalı komutuna sahip bir mesaj içeren 2. kutucuktaki bir olay.

kurallar

Grafikte, sol tarafta (not değerini temsil eden) azalan sırada görüntülenen 0 ile 127 arasındaki sayılar, not başladığında, her notun süresi (Not Off tick eksi Note On tick) ve notun hızı bulunmalıdır. Notları temsil eden semboller hızlarına bağlıdır:

  • 0-15: O
  • 16-31: =
  • 32-47: #
  • 48-63: -
  • 64-79: @
  • 80-95: +
  • 96-111: 0
  • 112-127: *

Aşağıdakileri varsayabilirsiniz:

  • Not ve hız değerleri [0, 127] aralığında olacaktır.
  • Dört dizinin her birinin uzunluğu her zaman birbirine eşit olacaktır.

İşte birkaç örnek:

{60, 62, 64, 65,  67}
{20, 40, 60, 80, 100}
{ 0,  4,  8, 12,  16}
{ 2,  6, 10, 14,  18}

127|
126|
125|
...
67 |                00
66 |
65 |            ++
64 |        --
63 |
62 |    ##
61 |
60 |==
59 |
...
2  |
1  |
0  |


{60, 48, 62, 47, 64, 45,  65,  43, 67, 41, 65, 43, 64, 45,  62, 47, 60, 48}
{63, 31, 75, 90, 12, 23, 122, 104, 33, 19, 57, 42,  5, 82, 109, 86, 95, 71}
{0,   0,  2,  2,  4,  4,   6,   6,  8,  8, 10, 10, 12, 12,  14, 14, 16, 16}
{2,   2,  4,  4,  6,  6,   8,   8, 10, 10, 12, 12, 14, 14,  16, 16, 18, 18}

127|
126|
...
68 |
67 |        ##
66 |
65 |      **  --
64 |    OO      OO
63 |
62 |  @@          00
61 |
60 |--              ++
59 |
...
49 |
48 |==              @@
47 |  ++          ++
46 |
45 |    ==      ++
44 |
43 |      00  ##
42 |
41 |        ==
40 |
...
1  |
0  |

İşte Ode to Joy'un ilk birkaç notunu gösteren bir örnek:

{48, 55, 64, 64, 65, 67, 55, 67, 65, 64, 62, 52, 55,  60,  60,  62,  64,  55, 64, 62, 62}
{45, 45, 63, 63, 63, 63, 89, 66, 66, 66, 66, 30, 30, 103, 103, 103, 103, 127, 55, 55, 55}
{ 0,  0,  0,  4,  8, 12, 16, 16, 20, 24, 28, 32, 32,  32,  36,  40,  44,  48, 48, 54, 56}
{16, 16,  2,  6, 10, 14, 32, 18, 22, 26, 30, 48, 48,  34,  38,  42,  46,  64, 50, 55, 64}

127|
...
67 |            --  @@
66 |
65 |        --          @@
64 |--  --                  @@                  00  --
63 |
62 |                            @@          00            - --------
61 |
60 |                                00  00
59 |
58 |
57 |
56 |
55 |################++++++++++++++++================****************
54 |
53 |
52 |                                ================
51 |
50 |
49 |
48 |################
...
0  |

Sen edebilirsiniz % 25 puanınızı azaltmak için gönderme, girdi olarak gerçek bir MIDI dizisi alır On Not analiz eder ve sizin seçtiğiniz herhangi bir parçanın mesajlar Kapalı Not en az dört günü Not etkinliklerle Not mesajlar Kapalı ve çıkış mevcuttur verdiyse Yukarıda açıklandığı gibi bir grafik.

Bu kod golf, bu yüzden en kısa kod kazanır. İyi şanslar!

Yanıtlar:


6

PHP , 127 + 571 = 698 toplam puan *

Tamam, bonusu talep ediyorum. :) Bu standart bir MIDI dosyası alır ve çıktıyı görüntüler.

Puanları daha karşılaştırılabilir hale getirmek için ana puanı (notu açma / kapama ve grafik olarak görüntüleme) ve bonus meydan okumayı (standart MIDI girişini okuyun) yukarıdaki puanı ayırdım.

Ana: 170 bayt -% 25 = 127

Main için, işlev $d()gerekli diziyi alır ve ASCII çıkışını görüntüler. Tüm testler ve test MIDI dosyasının çıktısı aşağıda verilmiştir.

$d=function($a){for($l=max($n=$a[0]);$l>=min($n);){$r=' |';foreach($n as$c=>$e)while($e==$l&$a[2][$c]<$a[3][$c])$r[++$a[2][$c]+1]='O=#-@+0*'[$a[1][$c]/16];echo$l--,$r,"
";}}

Çevrimiçi deneyin!

Bonus: 761 bayt -% 25 = 571

İşlev $m()standart bir MIDI dosyasını (yerel olarak veya URL ile) yükler ve her biri tüm MIDI dosya parçaları için belirtilen not biçiminde bir dizi içeren bir parça dizisi döndürür.

$m=function($f){$a=function($f){do$s=($s<<7)+(($c=unpack(C,fread($f,1))[1])&127);while($c&128);return$s;};$r=function($n){foreach($n as$e){if($e[4]==9&&$e[1]>0)foreach($n as$y=>$f)if($f[0]==$e[0]&&($f[4]==8||($f[4]==9&&$f[1]==0))){$o[0][]=$e[0];$o[1][]=$e[1];$o[2][]=$e[2];$o[3][]=$f[2];$n[$y][4]=0;break;}}return$o;};$m=fopen($f,r);while($b=fread($m,8)){$z=unpack(N2,$b)[2];if($b[3]==d){$k=unpack(n3,fread($m,$z))[3]/4;}else{$t=0;$n=[];$d=ftell($m)+$z;while(ftell($m)<$d){$t+=$a($m);if(($e=unpack(C,fread($m,1))[1])==255){fread($m,1);if($w=$a($m))fread($m,$w);}else{if($e>127)list(,$e,$h)=unpack('C*',fread($m,($y=(240&$e)>>4)==12?1:2));else$h=unpack(C,fread($m,1))[1];if($y==9|$y==8)$n[]=[$e,$h,(int)round($t/$k),0,$y];}}if($n)$u[]=$r($n);}}fclose($m);return$u;};

Çevrimiçi görün! Açıkçası, TIO uzak isteklere veya yerel dosyalara izin vermeyecek şekilde korumalı olarak yerleştirilmiştir, bu nedenle eylemde görmek için bu kodu yerel olarak çalıştırmanız gerekir. Görüntüleme işlevindeki ilk [testler] [TIO-jrwa60tu], test MIDI dosyasından dizi sonucunu içerir .

MIDI dosya yükleme yordamı çözülmemiş:

$m=fopen($f,'r');                           // m = midi file handle
while($b=fread($m,8)){                      // read chunk header
    $z=unpack('N2',$b)[2];                  // z = current chunk size
    if($b[3]=='d'){                         // is a header chunk?
        $k=unpack('n3',fread($m,$z))[3]/4;  // k = ticks per quarter note (you can change the 4 to 8 or 16 to "zoom in" so each char represents eights or sixteenth notes)
    }else{                                  // is a track chunk?
        $t=0;                               // track/chunk time offset starts at 0
        $d=ftell($m)+$z;                    // d = end of chunk file pos
        while(ftell($m)<$d){                // q = current file pos
            $t+=$a($m);                     // decode var length for event offset and add to current time
            if(($e=unpack('C',fread($m,1))[1])==255){ // is a META event 
                fread($m,1);                // read and discard meta event type
                if($w=$a($m))
                    fread($m,$w);
            }else{                          // is a MIDI event
                if($e>127) {                // is a new event type
                    list(,$e,$h)=unpack('C*',  // if is a prog change (0x0c), event is 1 byte
                        fread($m,($y=(240&$e)>>4)==12?1:2)); // otherwise read 2 bytes
                } else {                    // is a MIDI "streaming" event, same type as last
                    $h=unpack('C',fread($m,1))[1];
                }
                if($y==9|$y==8)             // if is a Note On or Note Off
                    $n[]=[$e,$h,(int)round($t/$k),0,$y];  // add note to output
            }
        }
        if($n)                              // if this track has notes,
            $u[]=$r($n);                    // add to array of output tracks ($u)
    }
}
fclose($m); // yes, could golf this out and rely on PHP GC to close it

"Ode to Joy" test MIDI dosyası buradan indirilebilir . Örnek kullanım:

$d( $m( 'beethoven_ode_to_joy.mid' )[0] );      // display first track

$d( $m( 'https://www.8notes.com/school/midi/piano/beethoven_ode_to_joy.mid' )[0] );

foreach( $m( 'multi_track_song.mid' ) as $t ) {  // display all tracks
    $d( $t );
}

"Ode to Joy" MIDI dosya çıktısı

67 |            0000++++                                                        00000000                                                                                                                        00000000
66 |
65 |        0000        ++++                                                0000        0000                                                              @@              @@                                0000        ++++
64 |++++++++                ++++                0000000000          00000000                0000                0000                        @@@@        @@  ----        @@  ----                ++++++++++++                ++++                0000
63 |
62 |                            ++++        0000          00++++++++                            ++++        0000    000000          @@@@----        ----            @@@@        ----    ----                                    ++++        0000    000000
61 |
60 |++++                            ++++0000                        0000                            ++++0000              ++00000000            ----            ----                ----            00000000                        ++++0000    ****      ++00000000
59 |                                                        ++++++++
58 |                                                                                                                                                                                                        00000000
57 |                                                                                                                                                                                ----                            ++++++++
56 |                                                                                                                                                                        --------
55 |++++++++++++++++++++++++00000000000000000000000000000000++++++++00000000000000000000000000000000000000000000000000000000        ----------------------------------------                --------                                        0000    ++++++++00000000
54 |                                                                                                                                                                                    ----
53 |                                                                                                                                                                                                                        ++++++++
52 |                                0000000000000000                                                0000000000000000                                                                                                                ++++0000                00000000
51 |
50 |
49 |
48 |++++++++++++++++                0000000000000000                0000000000000000                0000000000000000        ++++++++                                                                                                                        00000000

notlar

MIDI formatında, Not Açık / Not Kapalı olayları atomiktir, yani belirli bir not için belirli bir zamanda bir Not Açık olayı görürsünüz (örneğin E5) ve başka bir E5 notu için bir Not Kapalı olayına kadar çalınacağı ima edilir görülür. Bu nedenle, MIDI olaylarını analiz etmek ve belirli bir Not Açık'ı Not Kapalı ile eşleştirmek gerekir;297184 bayt. Bunu daha da karmaşık hale getiren, standart MIDI biçiminde, Not Off ile aynı şeyi temsil eden bir hız 0 ile eşleşen bir Not Açık'ı görmek oldukça yaygındır.

Bu, şimdi sıfır hıza sahip dosyaları doğru bir şekilde okuyacaktır. Not Kapalı yerine Not Açık, bu yüzden çoğu standart dosyayı açmalıdır.

Uyarılar

Bu hiçbir şekilde MIDI formatının tam bir uygulaması değildir, ancak bunu oldukça kapsamlı bir MIDI dosyaları koleksiyonu ile test ettim ve hepsini güzelce okuyor.

Bu sunum henüz aşırı bir seviyeye ulaşmadı, bu yüzden bu muhtemelen daha küçük hale getirilebilir. % 25 puan azaltma bonusunun standart bir MIDI dosyasını okumak için gereken kodu dengeleyeceğini düşünmüyorum. ASCII ekranı yapan (güncel) en küçük gönderim olduğu gibi106 65 bayt olması durumunda, MIDI dosya yordamlarının 2521 bayt yenilecek. Bunu yapmak için herkese meydan okuyorum (yerleşik bir dil veya modül kullanmadan). :)


Bu harika bir cevap. Bu soruna geri dönüp baktığımda, bonus miktarının muhtemelen bir MIDI dosyasını okuma yükünü hesaba katacak kadar puanları azaltmayacağını kabul ediyorum. (Sanırım bugünlerde ikramiye cesaretini kırıldı.) Yine de bonus mücadelesine katıldığın için çok etkilendim. Sana bunun için iyi bir ödül verebilirim.
TNT

@TNT, teşekkürler! Gerçekten bunu yapmak zevk ve smf gibi aptal bir şey için golf dosya biçimi rutinleri çalışırken ilginçti. Büyük meydan okuma!
640KB

5

Ruby, 106 bayt

Bu komikti. Neden kimsenin denemediğinden emin değilim.

Bu işlev, girdiyi dört dizi bağımsız değişkeni olarak alır ve grafiğin her satırı için bir tane olmak üzere bir dizgi dizisi döndürür.

->a,*r{q=(0..z=127).map{|i|"%3d|"%(z-i)+" "*1e4}
a.zip(*r){|n,v,o,f|q[z-n][o+4]="O=#-@+0*"[v/16]*(f-o)}
q}

Not: Bu, keyfi olarak 10.000'den fazla kenenin olmayacağını varsayar. Terminalinizde çalıştırırsanız less, yatay olarak kaydırmak için boru tesisatını kullanmanızı öneririm . Daha 1e4fazla keneler istiyorsanız değiştirebilirsiniz 9e9, ancak bu bir terabayt veya iki RAM alır.

Repl.it sitesinde görün: https://repl.it/Cx4I/1


Gönderdiğiniz için teşekkürler! Ama garip yeterince repl çıktı göremiyorum (tüm görebiliyorum aralarında döndürme bir sürü 127-0 sayıları ). Daha önce hiç repl kullanmadım, bu yüzden nedenini bilmiyorum. Çıktıyı doğru görmem için bir yol önerebilir misiniz?
TNT

Bu oldukça garip. Benim için çalışıyor. Şu anda bir bilgisayar başında değilim, ama telefonumdan bir ekran görüntüsü: i.stack.imgur.com/3UCyn.jpg
Ürdün

Ekran görüntüsü için teşekkürler. Sorunun kullandığım web tarayıcısı olabileceğini düşünüyorum, bu yüzden daha sonra farklı bir tarayıcıda deneyeceğim. Ama benden +1. :)
TNT

2

Python 2, 163 160 156 145 bayt

Bu bunu yapmanın en golf yolu değil, ama en basitlerinden biriydi. Dizelerin parçalarını listelere dönüştürmeden, değiştirmeden ve tekrar dizgilere dönüştürmeden nasıl değiştirebileceğimi anlayabilirsem, bu çok yardımcı olacaktır. Golf önerileri hoş geldiniz.

Düzenleme: Leaky Nun sayesinde 18 bayt. Ideone üzerinde deneyin !

a=input();z=[" "*max(a[3])]*128
for n,v,b,e in zip(*a):z[n]=z[n][:b]+"O=#-@+0*"[v/16]*(e-b)+z[n][e:]
for i in range(128)[::-1]:print"%3d|"%i+z[i]

@LeakyNun Whoops, my bad
Loovjo

Düzenli ifade ikamesi kullanabilir misiniz? Yakut şey ise böyle str.sub(/(?<=.{20}).{3}/,"foo")eşdeğerdir str[20,3] = "foo". Elbette bu, regexp'nin Ruby byte'larında ucuz, ancak belki de Python'da olmayan, dizin / uzunluk değişkenleriyle dize enterpolasyonu / birleştirme yoluyla oluşturulması anlamına gelir.
Ürdün

1

Japt , 65 bayt

®Æ"O=#-@+0*"gXzG
#€Çs ú3 +'|ÃúUmg2 rÔ+5
£VhXÎVgXv)hXÎ+4Xo pXra
Vw

Çevrimiçi deneyin!

Girişi biçimdeki notların bir listesi olarak alır [pitch, start_tick, end_tick, velocity]. Eğer ayrı listeler olarak girdi alınması zorunlu ise (yani tüm perdelerin bulunduğu bir liste, tüm hızları içeren bir liste vb.), Bu 1 bayt maliyetiyle gerçekleştirilebilir .

Açıklama:

®Æ"O=#-@+0*"gXzG          #Gets the velocity character to use for each note
®                         # For each note in the input
 Æ                        # Replace the last item X with:
             XzG          #  Integer divide X by 16
  "O=#-@+0*"g             #  Get the character at that index in the string "O=#-@+0*"

#€Çs ú3 +'|ÃúUmg2 rÔ+5    #Generate the blank chart
#€Ç        à              # For each number X in the range [0...127]:
   s                      #  Turn X into a string
     ú3                   #  Right-pad with spaces until it is 3 characters long
        +'|               #  Add "|" to the end
            ú             # Right pad each of those with spaces to this length:
             Umg2         #  Get all the end_tick values
                  rÔ      #  Find the largest one
                    +5    #  Add 5

£VhXÎVgXv)hXÎ+4Xo pXra    #Put the notes into the chart
£                         # For each note:
     VgXv)                #  Get a line from the chart based on the note's pitch
          h               #  Overwrite part of that line:
           XÎ+4           #   Starting at index start_tick +4
               Xo         #   Overwrite characters with the velocity character
                  pXra    #   For the next end_tick - start_tick characters
 VhXÎ                     #  Put the modified line back into the chart

Vw                        #Print the chart
V                         # Get the chart
 w                        # Reverse it (so 127 is the first line)
                          # Implicitly print it
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.