MIDIトラックを表示する


17

バックグラウンド

MIDIファイルは、WAVまたはMP3オーディオファイルとはまったく異なります。MP3およびWAVファイルには、オーディオの「録音」を表すバイトが含まれています。一方、MIDIファイルには、MIDIイベントに格納された一連のMIDIメッセージがあります。これらのメッセージはトラックに保存され、トラックのコレクションがMIDIシーケンスを構成します。MIDIシーケンスのイベントはシーケンサーによって分析され、そのメッセージをシーケンサーからシンセサイザーの受信機に送信できます。

ほとんどの場合、MIDIイベントに保存されるMIDIメッセージは、特定のノートを演奏するようシンセサイザーに指示するノートオンメッセージ、またはノートの再生を停止するようシンセサイザーに指示するノートオフメッセージです。これらのメッセージには2つのデータバイトが含まれており、1つ目はシンセサイザーにノートのベロシティを通知し(ベロシティーが大きいと音が大きくなります)、2つ目はシンセサイザーに再生するノートを伝えます(つまり、ミドルC)。イベント自体には、メッセージをいつ送信するかをシーケンサーに伝える目的に役立つチックも含まれています。

チャレンジ

課題は、シングルトラックMIDIシーケンスで一連のノートオンおよびノー​​トオフMIDIメッセージを分析し、特定のノートがオンのとき、オフのとき、およびこれらのノートの速度。チャートの垂直軸は音価を表し、以下に説明するようにラベル付けする必要があり、水平軸はMIDIティックで時間を表します(ただし、複雑さと間隔の問題を減らすためにラベルは付けないでください)

入力は、それぞれが一連の整数値を含む4つの個別の配列またはリストです。一連の整数値を持つ4つのサブ配列/サブリストを含む2次元配列またはリスト。またはその他の便利な手段。これは、トラック内のノートオンおよびノー​​トオフメッセージを含むコレクションMIDIイベントを表します。これらの配列の最初の値は、音符、2番目のベロシティ、3番目のノートオンイベントティック、4番目のノートオフイベントティックを指定します。たとえば、次のような4つの配列がある場合:

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

各配列の最初の要素を分析すると、次の2つのイベントが得られます。NoteOnコマンド、note 60(中央C)、およびノー​​ト速度20のメッセージを持つティック0のイベント。ティック2のイベントには、同じノートとベロシティを持つノートオフコマンドを持つメッセージがあります。

ルール

チャートは、左側に0から127までの数字(音符の値を表す)、音符の開始時、各音符の長さ(音符のオフティックから音符のオンティックを引いたもの)、および音符のベロシティを表示する必要があります。ノートを表すシンボルは、ベロシティに依存します。

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

次のことを想定できます。

  • ノートとベロシティの値は、[0、127]の範囲内になります。
  • 4つの配列のそれぞれの長さは、常に互いに等しくなります。

以下に例を示します。

{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シーケンスを取る場合、ノートオンを分析し、注意、それは少なくとも4つのメッセージオフノートオンとノートとイベント、および出力が含まれて上記のチャート。

これはコードゴルフなので、最短のコードが勝ちます。幸運を!

回答:


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()は(ローカルまたはURLのいずれかで)標準MIDIファイルを読み込み、トラックの配列を返します。各配列には、すべての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は明らかにリモートリクエストやローカルファイルを許可しないようにサンドボックス化されているため、このコードをローカルで実行して動作を確認する必要があります。表示機能の最初の[tests] [TIO-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

ここからダウンロードして使用できる「Ode to Joy」のテスト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イベントを分析し、指定されたノートオンをノートオフと一致させる必要があります。297184バイト。これをさらに複雑にしているのは、標準のMIDIフォーマットでは、ノートオフと同じことを表すベロシティ0を持つ後続のノートオンを確認することです。

これにより、ノートオフではなく速度ゼロのノートオンを持つファイルが正しく読み取られるようになりました。したがって、ほとんどの標準ファイルを開く必要があります。

注意事項

これは決してMIDIフォーマットの完全な実装ではありませんが、MIDIファイルのかなり広範なコレクションでこれをテストし、それらをすべてうまく読みました。

この提出物はまだ極端にゴルフされていないので、これはもっと小さくすることができる可能性が高いです。25%のスコア削減ボーナスが標準MIDIファイルの読み取りに必要なコードを相殺する可能性は非常に低いと思います。ASCII表示を行う(現在の)最小の送信は106 65バイト、MIDIファイルルーチンを実装する必要があります 25ビートする21バイト。私は誰かにそれをするように挑戦します(組み込みの言語やモジュールを使わずに)。:)


これは素晴らしい答えです。この課題を振り返ってみると、ボーナスの量はおそらくMIDIファイルの読み取りのオーバーヘッドを考慮に入れるのに十分なスコアを下げないことに同意します。(とにかく、ボーナスは今のところお勧めできません。)それにもかかわらず、あなたがボーナスチャレンジに参加したことには非常に感銘を受けました。私はあなたにそれのための良い報奨金を与えるかもしれません。
TNT

@TNT、ありがとう!本当にそれを楽しんでいて、SMFのような間抜けなもののためにファイル形式のルーチンをゴルフしようとすると、面白かったです。素晴らしい挑戦です!
640 KB

5

ルビー、106バイト

これは楽しかった。誰もそれを試みなかった理由がわかりません。

この関数は、4つの配列引数として入力を受け取り、チャートの各行に1つずつ、文字列の配列を返します。

->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ティックを超えないことを任意に想定しています。ターミナルで実行する場合は、less水平方向にスクロールできるようにパイピングすることをお勧めします。1e4より多くのティックが必要な場合は、最大でを変更できます9e9が、テラバイトまたは2つのRAMが必要です。

repl.itで参照してください:https ://repl.it/Cx4I/1


提出いただきありがとうございます!しかし、奇妙なことに、replで出力を確認することはできません(表示できるのは、それらの間に多くのリターンがある127-0の数字のみです)。私は前にreplを使用したことがないので、理由はわかりませんが。出力を適切に表示する方法を提案してもらえますか?
TNT

それはかなり奇妙です。わたしにはできる。私は今コンピューターにいるわけではありませんが、携帯電話のスクリーンショットを次に示し
ヨルダン

スクリーンショットをありがとう。問題は、使用しているWebブラウザーにある可能性があると考えているため、後で別のWebブラウザーで試してみます。私からの+1。:)
TNT

2

パイソン2、163の 160 156 145バイト

これは最も簡単な方法ではありませんが、最も簡単な方法の1つでした。リストに変換せずに文字列の一部を置換し、置換してから文字列に戻す方法を理解できれば、ここで非常に役立ちます。ゴルフの提案を歓迎します。

編集: Leaky Nunのおかげで18バイト。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

正規表現の置換を使用できますか?Rubyではのようなものstr.sub(/(?<=.{20}).{3}/,"foo")はと同等str[20,3] = "foo"です。もちろん、これは、インデックス/長さ変数を使用した文字列補間/連結によって正規表現を構築することを意味します。これは、Rubyバイトでは安価ですが、Pythonではそうではないかもしれません。
ヨルダン

1

ジャプト、65バイト

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

オンラインでお試しください!

形式のノートのリストとして入力を受け取ります[pitch, start_tick, end_tick, velocity]。入力を個別のリストとして取得することが必須である場合(つまり、すべてのピッチを含む1つのリスト、すべての速度を含む1つのリストなど)、次のコストで実現できます。 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.