미디 트랙 표시


17

배경

MIDI 파일은 WAV 또는 MP3 오디오 파일과 상당히 다릅니다. MP3 및 WAV 파일에는 오디오의 "레코딩"을 나타내는 바이트가 포함되어있는 반면, MIDI 파일에는 MIDI 신시사이저에게 어떤 가상 악기를 연주해야하는지 또는 MIDI 시퀀서에게 사용해야하는 재생 템포를 알려주는 MIDI 이벤트에 저장된 일련의 MIDI 메시지가 있습니다. 이 메시지는 트랙에 저장되며 트랙 모음은 MIDI 시퀀스를 구성하며,이 시퀀스의 이벤트는 시퀀서로 분석 할 수 있으며 시퀀서에서 신시사이저의 수신기로 메시지를 전송합니다.

MIDI 이벤트에 저장된 MIDI 메시지는 대부분 신디사이저에게 특정 음을 연주하라고 알려주는 Note On 메시지 또는 신디사이저가 음표 연주를 중지하도록 지시하는 Note Off 메시지입니다. 이 메시지는 두 개의 데이터 바이트를 포함하는데, 첫 번째 바이트는 신디사이저에 음의 속도를 알려주고 (높은 속도로 인해 더 큰 음이 나옵니다), 두 번째는 신시사이저에 음을 연주하도록 알려줍니다 (즉, 중간 C). 이벤트 자체에는 시퀀서에게 메시지를 보낼시기를 알려주는 틱도 포함됩니다.

도전

문제는 전체 트랙 또는 일련의 음표 켜짐 및 음표 꺼짐 MIDI 메시지를 단일 트랙 MIDI 시퀀스로 분석하고 특정 음표가 켜져있을 때, 꺼져있을 때를 나타내는 차트를 STDOUT에 출력하는 기능 또는 전체 프로그램을 작성하는 것입니다. 이 음의 속도. 차트의 세로 축은 음표 값을 나타내며 아래에 설명 된대로 레이블을 지정해야하며 가로 축은 시간을 MIDI 틱 단위로 나타냅니다 (복잡성과 간격 문제를 줄이기 위해 레이블이없는 상태로 유지해야 함).

입력은 각각 일련의 정수 값을 포함하는 4 개의 개별 배열 또는 목록 일 수 있습니다. 일련의 정수 값을 갖는 4 개의 서브 어레이 / 서브리스트를 포함하는 2 차원 어레이 또는리스트; 또는 다른 편리한 수단; 트랙의 음표 켜짐 및 음표 꺼짐 메시지가있는 컬렉션 MIDI 이벤트를 나타냅니다. 이 배열의 첫 번째 값은 음표, 두 번째 속도, 세 번째 음표 켜짐 이벤트 틱 및 네 번째 음표 꺼짐 이벤트 틱을 지정합니다. 예를 들어 다음과 같은 4 개의 배열이 있습니다.

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

각 배열의 첫 번째 요소를 분석하면 다음과 같은 두 가지 이벤트가 발생합니다. Note On 명령, Note 60 (중간 C) 및 20의 음표 속도를 가진 메시지가있는 tick 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  |

제출물이 실제 MIDI 시퀀스를 입력으로 취하고 선택한 트랙의 음표 켜짐 및 음표 꺼짐 메시지를 분석하면 음표 켜짐 및 음표 꺼짐 메시지가있는 이벤트가 4 개 이상 있고 출력이있는 경우 점수를 25 % 줄일 수 있습니다 위에서 설명한 차트.

이것은 코드 골프이므로 가장 짧은 코드가 승리합니다. 행운을 빕니다!

답변:


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가 샌드 박스로되어 있으므로이 코드를 로컬에서 실행하여 작동하는지 확인해야합니다. 디스플레이 기능의 첫 번째 [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 인 후속 음표 켜짐을 보는 것이 일반적입니다.

이제 Note Off 대신 속도가 0 인 Note On이있는 파일을 올바르게 읽으므로 대부분의 표준 파일을 열어야합니다.

경고

이것은 결코 MIDI 형식의 완전한 구현은 아니지만, 상당히 광범위한 MIDI 파일 모음으로 이것을 테스트하여 모두 잘 읽습니다.

이 제출물은 아직 극한까지 골프화되지 않았으므로 완전히 축소 될 수 있습니다. 25 %의 점수 감소 보너스가 표준 MIDI 파일을 읽는 데 필요한 코드를 상쇄 할 가능성은 매우 낮습니다. ASCII 표시 만하는 (현재) 최소 제출은106 65 바이트, MIDI 파일 루틴을 구현해야합니다. 25이길 21 바이트. 언어 내장 또는 모듈을 사용하지 않고 누군가에게 그렇게하도록 도전합니다. :)


이것은 멋진 답변입니다. 이 과제를 되돌아 보면 보너스 금액이 MIDI 파일을 읽는 오버 헤드를 설명하기에 충분한 점수를 얻지 못할 것이라는 데 동의합니다. (어쨌든 보너스는 어쨌든 권장하지 않는다고 생각합니다.) 그럼에도 불구하고 보너스 도전에 참여한 것에 대해 깊은 감명을 받았습니다. 나는 당신에게 좋은 현상금을 줄 수 있습니다.
TNT

@TNT, 감사합니다! SMF와 같은 것을 위해 파일 형식 루틴을 골프화하려고 시도하는 것이 정말 즐거웠습니다. 큰 도전!
640KB

5

루비, 106 바이트

재미있었습니다. 아무도 시도하지 않은 이유가 확실하지 않습니다.

이 함수는 입력을 4 개의 배열 인수로 가져 와서 차트의 각 라인마다 하나씩 문자열 배열을 리턴합니다.

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

꽤 이상합니다. 그것은 나를 위해 작동합니다. 나는 지금 컴퓨터에 있지 않지만 여기 내 휴대폰의 스크린 샷이 있습니다. i.stack.imgur.com/3UCyn.jpg
Jordan

스크린 샷 주셔서 감사합니다. 문제는 내가 사용하는 웹 브라우저 일 수 있다고 생각하므로 나중에 다른 브라우저에서 시도해 보겠습니다. 그래도 +1합니다. :)
TNT

2

파이썬 2 163 160 156 145 바이트

이것이 가장 골치 아픈 방법은 아니지만 가장 간단한 방법이었습니다. 문자열 부분을 목록으로 바꾸거나 문자열을 바꾸거나 다시 문자열로 바꾸지 않고 문자열 부분을 바꾸는 방법을 알아낼 수 있다면 여기에서 매우 도움이 될 것입니다. 골프 제안을 환영합니다.

편집 : 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 Whoops, 나의 나쁜
Loovjo

정규식 대체를 사용할 수 있습니까? 루비에서는 이와 비슷한 str.sub(/(?<=.{20}).{3}/,"foo")것이 str[20,3] = "foo"입니다. 물론, 이는 인덱스 / 길이 변수를 사용하여 문자열 보간 / 연결을 통해 정규 표현식을 구성하는 것을 의미합니다. 루비 바이트는 저렴하지만 파이썬에서는 그렇지 않습니다.
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.