MThd 분석
앞에서 mid file은 chunk의 연속이라고 하였습니다. 그중에 MThd라고 불리는 chunk가 있는데 헤더를 저장하고 있는 chunk입니다.
내용은 간단하지만 그중에 ticks_per_beat (또는 time division)이라고 불리는 개념이 있어서 설명하려고 합니다.
Header chunk (of a MIDI file)
내용은 아래와 같습니다.
여기서 생소한 트랙이라는 개념이 나오는데 트랙이라는 건 다른 연주자의 악보(또는 다른 악기의 악보)라고 생각 하면 됩니다.
format type : 0,1,2인데 0은 트랙이 하나일때 1은 트랙이 여러개 이면서 동기적일때, 2는 트랙이 여러개이나 비동기적으로 play가 될때 사용합니다. 보통은 1이라고 생각하면 됩니다.
number of tracks : 전체 트랙의 숫자 입니다.
time division : 이 값은 4분 음표(4분 음표를 1박자라고 표현)를 여기 에서는 몇개의 틱으로 나타내는지를 나타내는 숫자 입니다.
(여기 값이 480이라고 한다면 나중에 음표를 연주할때 480틱이라는 시간 동안 연주를 하라는 명령이 내려오면 1박자 동안 연주하면 됩니다. )
MidFile 클래스
기존 소스를 변형하여 chunk 단위를 분석할 수 있는 process_chunk() 함수를 만들었습니다.
코드를 약간 정리하였고 주석을 넣었습니다.
소스
import numpy as np class MidFile: # https://faydoc.tripod.com/formats/mid.htm # https://github.com/mido/mido/blob/main/mido/midifiles/midifiles.py def __init__(self): self.ticks_per_beat = None self.trackcount = None self.type = None def process_chunk(self, type, length, body): if type == 'MThd': ''' * type (2) 0:single-track 1:multiple tracks, synchronous 2:multiple tracks, asynchronous * trackcount (2) * ticks_per_beat (2) is the number of delta-time ticks per quarter note. 4분 노트(음표)당 델타-타임 틱들의 갯수 https://www.recordingblogs.com/wiki/header-chunk-of-a-midi-file 16비트의 상위 비트가 0이면 "박자당 틱"(또는 "4분음표당 펄스(틱수)")입니다. 상위 비트가 1이면 시간 분할은 "초당 프레임 수"입니다. - BPM = 1분당 bit수 120BPM은 1분에 120Bit로 표현 = 4분 음표(1박)가 1분에 120번 나올 수 있음(1번 나오는데 0.5초) ticks_per_beat = 480 이면, time delta가 480인 경우 4분 음표로 표현됨 ''' self.type = int.from_bytes(body[0:2].tobytes(), 'big') self.trackcount = int.from_bytes(body[2:4].tobytes(), 'big') self.ticks_per_beat = int.from_bytes(body[4:6].tobytes(), 'big') print(self.type, self.trackcount, self.ticks_per_beat) def read(self, filename): with open(filename) as f: rectype = np.dtype(np.uint8) bdata = np.fromfile(f, dtype=rectype) # MID 파일은 chunk의 연속으로 되어 있습니다. # Chunk Type(4), chunk body length (4), body(chunk body length) read_pos = 0 while True: chunk_type = bdata[read_pos:read_pos + 4].tobytes() chunk_type = chunk_type.decode('utf-8') chunk_length = int.from_bytes(bdata[read_pos + 4:read_pos + 8].tobytes(), 'big') print(chunk_type, chunk_length) self.process_chunk(chunk_type, chunk_length, bdata[read_pos + 8:read_pos + 8 + chunk_length]) read_pos = read_pos + chunk_length + 8 if len(bdata) <= read_pos: break if __name__ == "__main__": mid = MidFile() mid.read("../00_data/tests_for-elise.mid")
실행결과
MThd 6
1 2 480
MTrk 3909
MTrk 3034
헤더 분석으로 통하여 ticks_per_beat (또는 time division) 값이 480이라는 숫치를 알았는데요
120BPM으로 연주를 하면 (120BPM은 1분에 120bit이니 120bit/60sec = 2 bit/sec 여기서 1bit라는 것은 쿵쿵(120bpm이므로 음악에서 0.5초당 쿵쿵.. )거리는 것을 의미하게 됩니다. 1bit / 0.5sec 가 됩니다.
1박=1bit=480 이 엘리제를 위하여라는 mid파일에서 설정한 값입니다. 480tick 동안 특정음을 낸다면 그건 한박자 짜리(4분 음표)가 된다는 의미가 됩니다.
댓글 없음:
댓글 쓰기