แสดง MIDI Track


17

พื้นหลัง

ไฟล์ MIDI นั้นค่อนข้างแตกต่างจากไฟล์เสียง WAV หรือ MP3 ไฟล์ MP3 และ WAV ประกอบด้วยไบต์ที่แสดงถึง "การบันทึก" ของเสียงในขณะที่ไฟล์ MIDI มีชุดข้อความ MIDI ที่เก็บไว้ในเหตุการณ์ MIDI แจ้งให้เครื่องสังเคราะห์ MIDI ทราบว่าเครื่องมือเสมือนใดที่จะเล่นหรือเครื่องมือเล่น MIDI จังหวะการเล่นที่ควรใช้ ข้อความเหล่านี้ถูกเก็บไว้ในแทร็กและชุดของแทร็กประกอบขึ้นเป็นลำดับ MIDI ซึ่งสามารถวิเคราะห์เหตุการณ์โดยซีเควนเซอร์และส่งข้อความจากซีเควนเซอร์ไปยังเครื่องรับซินธิไซเซอร์

เวลาส่วนใหญ่ข้อความ MIDI ที่เก็บไว้ในเหตุการณ์ MIDI คือหมายเหตุบนข้อความที่บอกให้ซินธิไซเซอร์เล่นโน้ตเฉพาะหรือข้อความปิดข้อความซึ่งบอกให้ซินธิไซเซอร์หยุดเล่นโน้ต ข้อความเหล่านี้มี data data สอง bytes สิ่งแรกที่แจ้งให้ synthesizer ของ speed ของ note (ความเร็วที่สูงขึ้นส่งผลให้ note ดังขึ้น) และวินาทีที่ synthesizer note บอกให้เล่น (เช่น Middle C) เหตุการณ์เองยังมีเห็บที่ทำหน้าที่ในการบอกซีเควนเมื่อจะส่งข้อความ

ความท้าทาย

ความท้าทายคือการเขียนโปรแกรมเต็มรูปแบบหรือฟังก์ชั่นที่วิเคราะห์ชุดของ Note On และ Note Off ข้อความ MIDI ในลำดับ MIDI แทร็กเดี่ยวและส่งออกไปยัง STDOUT แผนภูมิที่แสดงเมื่อเปิดบันทึกย่อเมื่อปิดและ ความเร็วของบันทึกเหล่านี้ แกนแนวตั้งของแผนภูมิแสดงถึงค่าบันทึกและควรติดป้ายตามที่อธิบายไว้ด้านล่างและแกนแนวนอนแสดงเวลาในเห็บ MIDI (แม้ว่ามันจะยังคงไม่มีป้ายกำกับเพื่อลดความซับซ้อนและปัญหาระยะห่าง)

ข้อมูลของคุณอาจเป็นสี่อาร์เรย์หรือรายการที่แยกจากกันแต่ละรายการมีชุดของค่าจำนวนเต็ม อาร์เรย์สองมิติหรือรายการที่มีอาร์เรย์ย่อย / รายการย่อยสี่ชุดพร้อมชุดค่าจำนวนเต็ม หรือวิธีการอื่นใดที่สะดวกสบาย สิ่งนี้แสดงถึงเหตุการณ์ MIDI ของคอลเลกชันที่มีข้อความ Note On และ Note Off ในแทร็ก ค่าในอาร์เรย์แรกจะระบุหมายเหตุความเร็วที่สองที่สามบันทึกในเหตุการณ์ติ๊กและที่สี่ปิดบันทึกเหตุการณ์ ตัวอย่างเช่นกำหนดสี่อาร์เรย์เช่นนี้

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

การวิเคราะห์องค์ประกอบแรกของแต่ละอาร์เรย์ให้สองเหตุการณ์: เหตุการณ์ที่ติ๊ก 0 พร้อมข้อความที่มีคำสั่ง Note On, note 60 (Middle C), และ speed velocity ที่ 20; และเหตุการณ์ที่ติ๊ก 2 พร้อมข้อความที่มีคำสั่ง Note Off พร้อมโน้ตและความเร็วเดียวกัน

กฎระเบียบ

แผนภูมิควรแสดงตัวเลข 0 ถึง 127 ตามลำดับที่ลดลงทางด้านซ้าย (แสดงถึงค่าบันทึก) เมื่อโน้ตเริ่มต้นระยะเวลาของโน้ตแต่ละโน้ต (หมายเหตุเห็บออกลบด้วยเครื่องหมายลบหมายเหตุ) และความเร็วของโน้ต สัญลักษณ์ที่เป็นตัวแทนของบันทึกนั้นขึ้นอยู่กับความเร็ว:

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

คุณสามารถสมมติสิ่งต่อไปนี้:

  • ค่าของโน้ตและความเร็วจะอยู่ในช่วง [0, 127]
  • ความยาวของแต่ละแถวทั้งสี่จะเท่ากันเสมอ

นี่คือตัวอย่างบางส่วน:

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

นี่คือตัวอย่างที่แสดงหมายเหตุแรก ๆ ของ Ode to Joy:

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

คุณสามารถลดคะแนนของคุณลง 25%หากการส่งของคุณใช้ลำดับ MIDI จริงเป็นอินพุตวิเคราะห์ข้อความบันทึกย่อและปิดข้อความของแทร็คที่คุณเลือกหากมีอย่างน้อยสี่เหตุการณ์ด้วยข้อความเปิดและปิดหมายเหตุและเอาต์พุต แผนภูมิตามที่อธิบายไว้ข้างต้น

นี่คือรหัสกอล์ฟดังนั้นรหัสที่สั้นที่สุดจึงชนะ โชคดี!

คำตอบ:


6

PHP , 127 + 571 = 698 คะแนนรวม *

โอเคฉันกำลังรับโบนัส :) นี่จะใช้ไฟล์ MIDI มาตรฐานและแสดงผลลัพธ์

ฉันได้แบ่งคะแนนด้านบนเป็นความท้าทายหลัก (วิเคราะห์บันทึกเปิด / ปิดและแสดงเป็นแผนภูมิ) และความท้าทายโบนัส (อ่านอินพุตจาก MIDI มาตรฐาน) เพื่อให้คะแนนเปรียบเทียบกันมากขึ้น

หลัก: 170 ไบต์ - 25% = 127

สำหรับฟังก์ชั่นหลักนั้น$d()จะใช้อาเรย์ที่ต้องการและแสดงเอาต์พุต ASCII รวมคือการทดสอบและการส่งออกของไฟล์ทดสอบ MIDI ด้านล่าง

$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,"
";}}

ลองออนไลน์!

โบนัส: 761 ไบต์ - 25% = 571

ฟังก์ชั่น$m()จะโหลดไฟล์ MIDI มาตรฐาน (ไม่ว่าจะในเครื่องหรือตาม URL) และส่งคืนอาเรย์ของแทร็คแต่ละอันมีอาเรย์ในรูปแบบโน้ตที่ระบุสำหรับแทร็คไฟล์ MIDI ทั้งหมด

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

ดูออนไลน์! เห็นได้ชัดว่า TIO ถูก sandboxed เพื่อไม่อนุญาตการร้องขอจากระยะไกลหรือไฟล์ในเครื่องดังนั้นคุณจะต้องเรียกใช้รหัสนี้ในเครื่องเพื่อดูการทำงาน ครั้งแรก [ทดสอบ] [ติ้ว jrwa60tu] ในฟังก์ชั่นการแสดงผลรวมถึงผลอาร์เรย์จากไฟล์ทดสอบ MIDI

ขั้นตอนการโหลดไฟล์ MIDI ungolfed:

$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

ไฟล์ MIDI ทดสอบของ "กวีจอย" ที่สามารถใช้ดาวน์โหลดได้ที่นี่ ตัวอย่างการใช้:

$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

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

หมายเหตุ

ในรูปแบบ MIDI หมายเหตุเหตุการณ์เปิด / ปิดหมายถึงอะตอมมิกหมายความว่าคุณเห็นเหตุการณ์บันทึกย่อในเวลาที่กำหนดสำหรับบันทึกที่กำหนด (พูด E5) และมันบอกเป็นนัยว่ามันจะเล่นจนกว่าเหตุการณ์บันทึกปิดสำหรับบันทึกย่อ E5 เห็น ดังนั้นจึงจำเป็นต้องวิเคราะห์เหตุการณ์ MIDI และจับคู่ Note On ที่ให้ไว้กับ Note Off ซึ่งโค้ดสำหรับ297184 ไบต์ นอกจากนี้การทำให้ซับซ้อนขึ้นมันเป็นเรื่องธรรมดาในรูปแบบ MIDI มาตรฐานเพื่อดูการจับคู่ที่ตามมาหมายเหตุด้วยความเร็ว 0 ที่แสดงสิ่งเดียวกับ Note Off

ตอนนี้จะเป็นการอ่านไฟล์ที่มี zero-velocity Note On แทนที่จะเป็น Note Off's ดังนั้นควรเปิดไฟล์มาตรฐานส่วนใหญ่

คำเตือน

นี่ไม่ใช่การใช้รูปแบบ MIDI อย่างสมบูรณ์ แต่ฉันได้ทดสอบสิ่งนี้ด้วยการรวบรวมไฟล์ MIDI ที่ค่อนข้างครอบคลุมและมันอ่านมันทั้งหมดอย่างดี

การส่งนี้ยังไม่ได้รับการตีกอล์ฟให้สุดขีดดังนั้นจึงมีความเป็นไปได้ทั้งหมดที่จะทำให้เล็กลง ฉันคิดว่าเป็นไปได้ยากมากที่คะแนนลด 25% จะชดเชยรหัสที่จำเป็นในการอ่านไฟล์ MIDI มาตรฐาน ในฐานะที่เป็นการส่งที่เล็กที่สุด (ปัจจุบัน) ที่เพิ่งแสดง ASCII106 65 ไบต์จะต้องมีการใช้งานรูทีนไฟล์ MIDI 2521 ไบต์เพื่อเอาชนะ ฉันขอท้าให้ทุกคนทำเช่นนั้น (โดยไม่ต้องใช้ภาษาในตัวหรือโมดูล) :)


นี่เป็นคำตอบที่ยอดเยี่ยม เมื่อมองย้อนกลับไปที่ความท้าทายนี้ฉันยอมรับว่าจำนวนโบนัสอาจจะไม่ลดลงคะแนนมากพอที่จะพิจารณาถึงค่าใช้จ่ายในการอ่านไฟล์ MIDI (ฉันคิดว่าโบนัสไม่ได้รับการสนับสนุนในทุกวันนี้) อย่างไรก็ตามฉันรู้สึกประทับใจมากที่คุณได้รับความท้าทายเกี่ยวกับโบนัส ฉันอาจให้ความโปรดปรานที่ดีกับคุณ
TNT

@TNT ขอบคุณ! สนุกกับการทำมันมากและน่าสนใจในการพยายามจัดรูปแบบไฟล์กอล์ฟสำหรับสิ่งที่ไม่ดีเท่า SMF ท้าทายมาก!
640KB

5

Ruby, 106 ไบต์

มันสนุกมาก ฉันไม่แน่ใจว่าทำไมไม่มีใครพยายาม

ฟังก์ชันนี้รับอินพุตเป็นอาร์กิวเมนต์อาเรย์สี่ชุดและส่งกลับอาร์เรย์ของสตริงหนึ่งรายการสำหรับแต่ละบรรทัดของแผนภูมิ

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

หมายเหตุ:กฎเกณฑ์นี้ถือว่ามีจำนวนไม่เกิน 10,000 เห็บ ถ้าคุณเรียกใช้ในเทอร์มินัลของคุณฉันขอแนะนำให้ไปที่ piping เพื่อlessให้คุณสามารถเลื่อนในแนวนอน คุณสามารถเปลี่ยน1e4หากคุณต้องการเห็บเพิ่มขึ้นไปเรื่อย9e9ๆ แต่จะใช้ RAM หนึ่งหรือสองเทราไบต์

ดูได้ที่ repl.it: https://repl.it/Cx4I/1


ขอบคุณสำหรับการส่ง! แต่แปลกพอฉันไม่สามารถเห็นผลลัพธ์ใน repl (ทั้งหมดที่ฉันสามารถเห็นเป็นตัวเลข 127-0 ที่มีจำนวนมากของผลตอบแทนระหว่างพวกเขา) ฉันไม่เคยใช้ repl มาก่อนดังนั้นฉันจึงไม่รู้ว่าทำไม คุณช่วยแนะนำวิธีที่ฉันจะดูผลลัพธ์ได้อย่างถูกต้องหรือไม่?
TNT

มันค่อนข้างแปลก มันใช้งานได้สำหรับฉัน ฉันไม่ได้อยู่ที่คอมพิวเตอร์ตอนนี้ แต่นี่เป็นภาพหน้าจอจากโทรศัพท์ของฉัน: i.stack.imgur.com/3UCyn.jpg
Jordan

ขอบคุณสำหรับภาพหน้าจอ ฉันคิดว่าปัญหาอาจเป็นเว็บเบราว์เซอร์ที่ฉันใช้อยู่ดังนั้นฉันจะลองใช้อีกอันหนึ่งในภายหลัง +1 จากฉัน :)
TNT

2

Python 2, 163 160 156 145 ไบต์

นี่ไม่ใช่วิธีที่นักกอล์ฟจะทำได้ แต่มันเป็นวิธีที่ง่ายที่สุด หากฉันสามารถหาวิธีการแทนที่ส่วนต่างๆของสตริงได้โดยไม่ต้องเปลี่ยนเป็นรายการแทนที่และเปลี่ยนกลับเป็นสตริงนั่นจะเป็นประโยชน์อย่างมากที่นี่ ยินดีต้อนรับคำแนะนำการเล่นกอล์ฟ

แก้ไข: 18 ไบต์ขอบคุณ Leaky Nun ลองใช้กับ Ideone !

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 อ๊ะขอโทษฉันไม่ดี
Loovjo

คุณสามารถใช้การทดแทนนิพจน์ปกติได้หรือไม่? ในบางสิ่งบางอย่างเช่นทับทิมเทียบเท่ากับstr.sub(/(?<=.{20}).{3}/,"foo") str[20,3] = "foo"แน่นอนว่าหมายถึงการสร้าง regexp ด้วยการแก้ไขสตริง / การต่อข้อมูลด้วยตัวแปรดัชนี / ความยาวซึ่งมีราคาถูกในทับทิมไบต์ แต่อาจไม่ได้อยู่ใน Python
Jordan

1

Japt , 65 ไบต์

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

ลองออนไลน์!

[pitch, start_tick, end_tick, velocity]จะเข้าเป็นรายการของบันทึกในรูปแบบที่ หากการป้อนข้อมูลเป็นรายการแยกต่างหากเป็นข้อบังคับ (เช่นหนึ่งรายการที่มีสนามทั้งหมดหนึ่งรายการที่มีความเร็วทั้งหมดเป็นต้น) ซึ่งสามารถทำได้ที่ราคา1 ไบต์ไบต์

คำอธิบาย:

®Æ"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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.