Chapter 16

MIDI와 음악 데이터

MIDI는 소리가 아니라 연주 동작을 보내는 프로토콜입니다. "60번 건반을 세기 100으로 눌렀다", "페달을 밟았다" 같은 사건이 3바이트 남짓한 메시지로 오갑니다. 1983년에 정한 31.25 kbaud 직렬 규격이 40년 넘게 거의 그대로 쓰이고 있다는 점에서, MIDI는 공학사에서 보기 드문 장수 인터페이스입니다. 이 장에서는 그 바이트를 직접 만들고, 해독하고, 파일로 저장하고, 다시 소리로 바꿉니다.

1983년의 31.25 kbaud

1981년 시퀀셜 서킷의 데이브 스미스와 롤랜드의 가케하시 이쿠타로가 제안한 범용 신시사이저 인터페이스는 1983년 1월 NAMM 쇼에서 두 회사의 신시사이저(Prophet-600과 JX-3P)를 케이블 한 가닥으로 연결해 시연되었고, 같은 해 MIDI 1.0 규격으로 발표되었습니다. 규격은 지금 미국의 MMA(MIDI Manufacturers Association, 현재 MIDI Association)와 일본의 AMEI가 함께 관리합니다.

물리 계층은 5핀 DIN 커넥터와 5 mA 전류 루프입니다. 수신 측은 옵토아이솔레이터로 전기적으로 절연되어 있어 접지 루프 험이 생기지 않습니다. 직렬 형식은 비동기 UART 그대로입니다: 시작 비트 1 + 데이터 8비트(LSB 먼저) + 정지 비트 1 = 10비트/바이트. 전송 속도 31,250 bit/s는 당시 흔하던 1 MHz 클록을 32로 나눈 값이어서 정해졌습니다.

$$t_{\text{byte}} = \frac{10\ \text{bit}}{31\,250\ \text{bit/s}} = 320\ \mu\text{s}, \qquad t_{\text{Note On}} = 3 \times 320\ \mu\text{s} = 0.96\ \text{ms}, \qquad R_{\max} = 3\,125\ \text{byte/s}$$
한 케이블의 최대 처리량은 초당 약 1,000개의 노트 온 메시지다. 10음 화음을 '동시에' 누르면 마지막 음은 첫 음보다 약 9 ms(러닝 스테이터스를 쓰면 약 6 ms) 늦게 도착한다. 이 지연을 화음 번짐(Chord Smear)이라 한다.
그림 16-1. 바이트 0x90(노트 온, 1번 채널)이 MIDI 케이블을 지나는 모양. 쉬는 상태는 논리 1(전류 없음)이고, 시작 비트 0으로 프레임이 시작된다. 데이터는 LSB부터 보내므로 0x90 = 1001 0000₂는 0,0,0,0,1,0,0,1 순서로 나간다. 비트당 32 µs, 프레임 320 µs.
SIMULATOR

직렬 전송과 화음 번짐

시간 늘이기(귀로 확인용)
총 바이트—
마지막 음 지연—
러닝 스테이터스 절약—
위 막대는 케이블 위를 지나는 바이트(보라=상태, 회색=데이터)이고, 아래 점은 각 노트 온 메시지가 완전히 도착해 수신기가 소리를 낼 수 있는 시각입니다. 실제 배율(×1)에서 8음 화음의 번짐(약 5 ms)은 대부분 '조금 부드러운 어택' 정도로 들리지만, ×5–×20으로 늘이면 아르페지오처럼 흩어지는 것이 분명히 들립니다. 사람이 두 클릭의 순서를 구분하는 한계가 대략 수 ms–20 ms라서, 큰 화음과 드럼이 겹치는 순간 MIDI 1.0의 직렬 지연은 실제로 문제가 되었습니다. 아래 USB-MIDI 그림은 1 ms 프레임 묶음을 가정한 단순 모형입니다. USB-MIDI 1.0의 벌크 전송은 지연 상한을 예약하지 않으며 장치·드라이버·OS 버퍼링을 포함한 지터가 1 ms 이하라고 보장하지 않습니다.

메시지의 해부: 상태 바이트와 데이터 바이트

MIDI 바이트는 최상위 비트(MSB)로 두 종류로 나뉩니다. MSB가 1(0x80–0xFF)이면 상태 바이트(Status Byte)로 메시지 종류를 알리고, MSB가 0(0x00–0x7F)이면 데이터 바이트(Data Byte)입니다. 그래서 모든 데이터 값은 7비트, 즉 0–127이고, 수신기는 스트림 중간에 끼어들어도 다음 상태 바이트에서 즉시 동기를 다시 잡을 수 있습니다(자기 동기화 부호). 채널 메시지의 상태 바이트는 위 4비트가 종류, 아래 4비트가 채널(0–15, 사람에게는 1–16으로 표시)입니다.

그림 16-2. "1번 채널에서 C4(60)를 세기 100으로" = 90 3C 64. 상태 바이트의 위 니블 1001은 노트 온, 아래 니블 0000은 채널 1. 데이터 바이트는 MSB가 늘 0이다.
상태메시지데이터내용
8n노트 오프 (Note Off)2노트 번호, 릴리스 세기
9n노트 온 (Note On)2노트 번호, 세기(velocity). 세기 0은 노트 오프로 취급
An폴리 애프터터치 (Polyphonic Key Pressure)2노트 번호, 누르는 압력
Bn컨트롤 체인지 (Control Change)2컨트롤러 번호(0–119), 값. 120–127은 채널 모드 메시지
Cn프로그램 체인지 (Program Change)1음색 번호 0–127
Dn채널 애프터터치 (Channel Pressure)1채널 전체의 압력
En피치 벤드 (Pitch Bend)2LSB, MSB (14비트, 중앙 8192 = 00 40)
F0…F7시스템 공통·SysEx가변3절
F8…FF시스템 실시간0클럭, 시작/정지 등(6절). 다른 메시지 중간에 끼어들 수 있다

노트 번호는 반음 단위의 선형 눈금이고, 평균율 A4 = 440 Hz 기준으로 주파수는 다음과 같습니다(5장). 0번은 C−1(8.18 Hz), 127번은 G9(12,543.9 Hz)로 피아노 88건반(21–108번)을 넉넉히 덮습니다.

$$f(n) = 440 \cdot 2^{(n-69)/12}\ \text{Hz}, \qquad n = 69 + 12\log_2\frac{f}{440}$$
60번을 이 책처럼 C4라 부르는 것이 과학적 음높이 표기(SPN)지만, 야마하 계열 장비와 일부 DAW는 60번을 C3로 표시한다. 같은 바이트 0x3C에 이름만 한 옥타브 다르게 붙는 것이라, 장비 설명서를 볼 때 늘 확인해야 한다.

벨로시티는 건반이 눌린 속도(대개 두 접점 사이 통과 시간)를 1–127로 양자화한 값입니다. 규격은 세기→음량 곡선을 정하지 않지만, 흔히 진폭을 \((v/127)^2\)에 비례하게(약 \(40\log_{10}(v/127)\) dB) 둡니다. 이 장의 멜로디 합성기는 확인하기 쉬운 소리를 위해 피크 이득 0.28(v/127)²+0.015를 사용하므로 순수 제곱 곡선과 달리 작은 벨로시티에도 바닥 이득이 있습니다.

SIMULATOR

MIDI 메시지 모니터


          
노트 오프 방식
마지막 메시지—
보낸 바이트0
최근 1초 대역폭 사용0%
건반을 누르거나 A–K 키를 치면 만들어지는 바이트가 위 로그에 쌓이고(시간차, 16진, 상태 바이트의 2진, 해석), 같은 바이트가 이 페이지의 작은 GM 합성기로 들어가 소리가 납니다. 채널을 10으로 바꾸면 GM 드럼 채널이 되어 C2(36)=킥, D2(38)=스네어, F♯2(42)=닫힌 하이햇… 이 됩니다. 피치 벤드 슬라이더는 놓으면 스프링처럼 0으로 돌아갑니다. 러닝 스테이터스를 켜면 앞과 같은 상태 바이트가 생략되어 괄호로 표시되고, 이때는 '9n 세기 0' 방식이 노트 온과 상태를 공유해 바이트를 가장 많이 아낍니다. 합성기 모델: 프로그램 가족별 파형+저역통과 필터, 모듈레이션은 5.6 Hz 비브라토(최대 ±50 ¢).

자주 쓰는 컨트롤 체인지

CC이름비고
0 / 32뱅크 선택 MSB / LSB프로그램 체인지 전에 보내 128개 넘는 음색 선택
1모듈레이션 휠보통 비브라토 깊이
7채널 볼륨믹서 페이더 역할
10팬0=왼쪽, 64=가운데, 127=오른쪽
11익스프레션볼륨에 곱해지는 연주용 음량(스웰 페달)
64서스테인(댐퍼) 페달≤63 꺼짐, ≥64 켜짐
74밝기(필터 컷오프)MPE에서는 세로축 '음색(Y)'
100 / 101RPN LSB / MSB등록 파라미터 선택. 0/0 = 피치 벤드 범위
6 / 38데이터 엔트리 MSB / LSB선택한 RPN의 값
120All Sound Off릴리스 무시하고 즉시 무음
121Reset All Controllers벤드·모듈레이션·페달 등 초기화
123All Notes Off'패닉' 버튼. 노트 오프를 잃어버려 음이 멈추지 않을 때(stuck note)
14비트 컨트롤러 CC 0–31은 MSB, CC 32–63은 짝이 되는 LSB로 정의되어 있어 두 메시지로 14비트 값을 보낼 수 있다. 실제로는 대부분의 장비가 MSB만 쓰며, 7비트(128단계) 볼륨·필터 스윕의 '계단'이 들리는 문제가 MIDI 2.0의 32비트 컨트롤러로 이어졌다.

러닝 스테이터스와 시스템 메시지

대역폭을 아끼기 위해 MIDI 1.0은 러닝 스테이터스(Running Status)를 허용합니다. 같은 상태 바이트가 이어지면 두 번째부터 생략하고 데이터 바이트만 보냅니다. 수신기는 마지막 채널 상태 바이트를 기억해 두었다가 상태 없이 들어온 데이터에 적용합니다. 노트 오프를 '세기 0인 노트 온'으로 보내는 관례는 바로 이 러닝 스테이터스를 끊지 않기 위한 것입니다.

$$\underbrace{\texttt{90 3C 64}}_{\text{C4 on}}\ \underbrace{\texttt{40 64}}_{\text{E4 on}}\ \underbrace{\texttt{43 64}}_{\text{G4 on}}\ \cdots\ \underbrace{\texttt{3C 00}}_{\text{C4 off}} \qquad \text{바이트 수: } 3 + 2(N-1)$$
스트림 파서의 규칙: ① 채널 상태 바이트(80–EF)는 러닝 스테이터스를 갱신한다. ② 시스템 공통 메시지(F0–F7)는 러닝 스테이터스를 지운다. ③ 실시간 메시지(F8–FF)는 한 바이트짜리로, 다른 메시지의 데이터 바이트 사이에도 끼어들 수 있으며 러닝 스테이터스에 영향을 주지 않는다. ④ SysEx는 F0으로 시작해 F7(EOX)로 끝나며 사이에는 7비트 데이터만 온다.
바이트이름데이터용도
F0 … F7시스템 익스클루시브(SysEx)가변제조사 ID로 시작하는 임의 데이터(음색 덤프, 펌웨어). F0 7E 7F 09 01 F7 = GM System On
F1MTC 쿼터 프레임1SMPTE 타임코드를 8조각으로 나눠 전송
F2송 포지션 포인터2곡 위치(14비트, 16분음표 = 클럭 6개 단위)
F3송 셀렉트1곡 번호
F6튠 리퀘스트0아날로그 신시사이저 오실레이터 자동 조율
F8타이밍 클럭04분음표당 24번(24 PPQN)
FA / FB / FC스타트 / 컨티뉴 / 스톱0시퀀서 운전
FE액티브 센싱0300 ms마다 '살아 있음' 신호. 끊기면 수신기가 모든 음을 끈다
FF시스템 리셋0전원 켤 때 상태로 (파일 안에서는 메타 이벤트 접두어로 쓰인다)
SIMULATOR

바이트 스트림 해독기

#바이트해석
예제
재생 속도
바이트 / 메시지—
전송 시간(31.25 kbaud)—
오류—
16진 바이트를 직접 써 넣거나 예제를 고른 뒤 '해독'을 누르세요(입력하면 자동으로도 해독). 괄호 친 상태는 러닝 스테이터스로 추정한 것입니다. '클럭 끼어들기' 예제는 F8이 노트 온의 데이터 바이트 사이에 들어와도 메시지가 깨지지 않는 것을 보여 줍니다. '재생'은 해독한 메시지를 순서대로 합성기에 보냅니다(끝나고 1.5초 뒤 남은 음은 모두 끔). 오류 처리 방식은 이 해독기의 선택입니다. 규격은 잘못된 스트림에 대한 동작을 정하지 않습니다.

피치 벤드와 해상도

피치 벤드는 MIDI 1.0에서 드물게 처음부터 14비트인 메시지입니다. 두 데이터 바이트 중 LSB가 먼저 옵니다. 값 0–16383의 중앙 8192(E0 00 40)가 '벤드 없음'이고, 실제 음높이 변화는 수신기의 벤드 범위에 달려 있습니다. 기본값은 ±2반음이며 RPN 0으로 바꿉니다(B0 65 00 B0 64 00 B0 06 0C = ±12반음).

$$v = 128\cdot\text{MSB} + \text{LSB}, \qquad \Delta c = \frac{v-8192}{8192}\times 100R\ \text{cent}, \qquad \text{분해능} = \frac{200R}{2^{b}}\ \text{cent/단계}$$
\(R\): 벤드 범위(반음), \(b\): 실제로 쓰는 비트 수. \(R=2\), 14비트면 0.024 ¢/단계로 충분히 매끄럽다. 그러나 MSB만 보내는(7비트) 컨트롤러는 3.1 ¢/단계, \(R=12\)이면 18.75 ¢/단계로, 느린 글리산도에서 계단(zipper)이 들린다. 음높이 변별 한계(JND)는 중간 음역에서 약 5–10 ¢다. 양의 최댓값은 8191이라 \(+R\)에 1단계 모자라는 비대칭도 있다.
SIMULATOR

피치 벤드 계산기와 계단 글리산도

전송 해상도
메시지 바이트—
음높이 변화—
단계 크기—
글리산도 중 서로 다른 값—
위: 벤드 값 \(v\)가 LSB·MSB로 쪼개지는 모양. 아래: 0에서 +R반음까지 올라가는 글리산도를, 정해진 간격의 메시지와 해상도로 보냈을 때 수신기가 만드는 음높이(보라 계단)와 이상적인 직선(회색). 재생음은 220 Hz 톱니파입니다. 해볼 것: R=12, 7비트, 글리산도 4초에서 '양자화된 글리산도'를 들으면 18.75 ¢ 계단이 뚜렷하게 들립니다. 14비트로 바꾸면 사라지고, 반대로 메시지 간격을 40 ms로 늘리면 해상도와 무관하게 시간 방향의 계단이 생깁니다.

클럭과 동기

여러 장비가 같은 템포로 움직이려면 시간을 공유해야 합니다. MIDI 클럭은 가장 단순한 방법으로, 마스터가 4분음표당 24번(24 PPQN, pulses per quarter note) F8 한 바이트를 보냅니다. 슬레이브는 펄스 간격으로 템포를 추정하고, FA(스타트)·FC(스톱)·F2(송 포지션)로 운전합니다. 템포 값 자체는 보내지 않으므로, 슬레이브는 결국 잡음 섞인 주기 측정으로 BPM을 추정하는 문제를 풀게 됩니다.

$$T_{\text{tick}} = \frac{60}{24\cdot\text{BPM}}\ \text{s}\quad(120\ \text{BPM에서 } 20.83\ \text{ms}), \qquad \widehat{\text{BPM}}_k = \frac{60}{24\,\overline{\Delta t}_k}$$
수신 시각의 지터가 \(\sigma_j\)이면 한 간격의 측정 오차는 \(\sqrt2\,\sigma_j\)이고, 1 ms 지터만으로도 순간 추정 BPM은 120 ± 8 정도로 흔들린다. \(N\)개 간격을 평균하면(이동 평균) 끝점만 남으므로 오차가 \(1/N\)로 줄지만 템포 변화에 늦게 반응한다. 실제 장비는 PLL(위상 고정 루프)로 이 트레이드오프를 조정한다.
SIMULATOR

MIDI 클럭 지터와 템포 추정

틱 간격—
순간 추정 σ—
24틱 이동평균 σ—
클럭 대역폭—
마스터는 4분음표마다 킥을 정확한 시각에 칩니다. 슬레이브는 F8을 지터가 섞인 시각에 받고, 6틱(16분음표)마다 하이햇을 칩니다. 그래프는 최근 4초의 템포 추정입니다: 흐린 점=틱 하나의 간격으로 계산한 순간값, 보라=24틱(한 박) 이동평균, 청록=지수 평활(α=0.05). 마스터 템포를 재생 중에 바꿔 보면 평활 추정이 늦게 따라오는 것(지연)이 보입니다. 지터를 8 ms 이상으로 올리면 하이햇이 비틀거리는 것이 들립니다. 아래 줄은 케이블에 흐르는 바이트입니다.
타임코드와 정밀도 영상과 맞출 때는 박이 아니라 절대 시각이 필요해 MTC(MIDI Time Code, F1 쿼터 프레임)로 SMPTE 시:분:초:프레임을 보낸다. 하나의 시각을 8개 쿼터 프레임에 나눠 보내므로 2프레임마다 완전한 시각이 갱신된다. 오늘날 스튜디오의 샘플 단위 동기는 워드 클럭이나 네트워크 오디오(AES67, PTP/IEEE 1588)가 맡고, MIDI 클럭은 박 단위 음악 동기에 남아 있다. UMP의 JR Clock·JR Timestamp는 선택 기능이며 MIDI 1.0·2.0 메시지에 모두 적용할 수 있다. 송수신 지원·시계 해석·예약 재생이 있어야 지터를 줄일 수 있고 고정 지연·장치 동기까지 자동 해결하지는 않는다.

General MIDI: 번호에 이름 붙이기

MIDI 1.0은 "프로그램 5번이 무슨 소리인가"를 정하지 않았습니다. 그래서 같은 파일이 장비마다 엉뚱한 악기로 연주되었고, 1991년 General MIDI(GM1)가 128개 음색의 순서, 10번 채널 = 드럼, 노트 번호별 타악기, 최소 동시 발음 24음을 표준화했습니다(롤랜드 GS, 야마하 XG가 이를 확장했고 1999년 GM2로 이어짐). 128개 음색은 8개씩 16가족으로 묶입니다.

프로그램가족프로그램가족
1–8피아노65–72리드(목관 리드: 색소폰, 오보에, 클라리넷)
9–16반음계 타악기(첼레스타, 비브라폰, 마림바)73–80파이프(피콜로, 플루트, 리코더)
17–24오르간81–88신스 리드
25–32기타89–96신스 패드
33–40베이스97–104신스 효과
41–48현악기(바이올린, 첼로, 하프, 팀파니)105–112민속 악기(시타르, 반조, 고토, 백파이프)
49–56앙상블(현악 합주, 합창)113–120타악기(팅클벨, 스틸 드럼, 우드블록)
57–64금관(트럼펫, 트롬본, 호른)121–128효과음(기타 프렛 잡음, 바다, 헬리콥터, 총소리)

표는 사람이 쓰는 1부터의 번호이고, 프로그램 체인지 바이트에는 0부터의 값이 들어갑니다(어쿠스틱 그랜드 피아노 = C0 00, 트럼펫 57번 = C0 38). 10번 채널에서는 프로그램 대신 노트 번호가 타악기를 고릅니다.

노트GM 타악기노트GM 타악기노트GM 타악기
35 B1Acoustic Bass Drum42 F♯2Closed Hi-Hat49 C♯3Crash Cymbal 1
36 C2Bass Drum 1 (킥)43 G2High Floor Tom50 D3High Tom
37 C♯2Side Stick44 G♯2Pedal Hi-Hat51 D♯3Ride Cymbal 1
38 D2Acoustic Snare45 A2Low Tom53 F3Ride Bell
39 D♯2Hand Clap46 A♯2Open Hi-Hat54 F♯3Tambourine
40 E2Electric Snare47 B2Low-Mid Tom56 G♯3Cowbell
41 F2Low Floor Tom48 C3Hi-Mid Tom57 A3Crash Cymbal 2
왜 하이햇이 F♯·G♯·A♯일까 닫힘(42)·페달(44)·열림(46) 하이햇이 검은 건반에 몰려 있어 한 손으로 쉽게 번갈아 칠 수 있게 배치되었다. 킥(36)과 스네어(38)도 흰 건반 두 개라 키보드 드러머가 '쿵–짝'을 손가락 두 개로 친다. 12장의 드럼 패턴을 MIDI로 옮기면 이 번호들이 그대로 쓰인다.

표준 MIDI 파일: 틱, VLQ, 템포 맵

케이블의 MIDI는 '지금' 일어나는 사건이지만, 파일에는 언제를 함께 적어야 합니다. 1988년의 표준 MIDI 파일(Standard MIDI File, SMF)은 청크 구조로 되어 있습니다. 헤더 청크 MThd 하나와 트랙 청크 MTrk 여러 개이며, 모든 정수는 빅 엔디언입니다.

4D 54 68 64 "MThd" 00 00 00 06길이 = 6 00 00형식 0/1/2 00 01트랙 수 01 E0PPQ = 480 4D 54 72 6B "MTrk" 00 00 00 2B이후 바이트 수 ⟨Δt⟩ 이벤트 ⟨Δt⟩ 이벤트 … ⟨Δt⟩ FF 2F 00 Δt (VLQ) MIDI 이벤트 90 3C 64 메타 FF 51 03 07 A1 20 SysEx F0 ⟨len⟩ … F7 이벤트 앞에는 늘 직전 이벤트로부터의 틱 수 Δt가 가변 길이 수(VLQ)로 붙는다. 템포 07 A1 20 = 500,000 µs/4분음표 = 120 BPM. 형식 0: 트랙 1개에 모든 채널 · 형식 1: 동시에 재생되는 여러 트랙(첫 트랙이 템포 맵) · 형식 2: 독립된 패턴들
그림 16-3. SMF의 구조. 헤더의 division이 양수(최상위 비트 0)이면 PPQ(4분음표당 틱 수)이고, 음수면 SMPTE 프레임 기반 시간이다. 파일 안에서도 러닝 스테이터스를 쓸 수 있다.

Δt는 대부분 작지만 가끔 아주 큽니다(긴 쉼표). 그래서 SMF는 가변 길이 수(Variable-Length Quantity, VLQ)를 씁니다. 7비트씩 끊어 큰 자리부터 내보내고, 마지막 바이트를 뺀 모든 바이트의 MSB를 1(계속)로 둡니다. 0–127은 1바이트, 최대 0x0FFFFFFF는 4바이트입니다. 같은 발상이 UTF-8, Protocol Buffers의 varint, LEB128에도 쓰입니다(바이트 순서는 다름).

값VLQ 바이트값VLQ 바이트
0x00000x3FFFFF 7F
0x7F7F0x400081 80 00
0x8081 000x1FFFFFFF FF 7F
0x2000C0 000x0FFFFFFFFF FF FF 7F

틱을 초로 바꾸려면 템포가 필요합니다. 템포는 메타 이벤트 FF 51 03 tt tt tt에 4분음표 하나의 마이크로초(24비트)로 적힙니다. BPM이 아니라 주기를 적는 것은 정수로 정밀하게 쓰기 위해서입니다. 템포가 곡 중간에 바뀌면 구간별로 나눠 더합니다(템포 맵).

$$\mu = \frac{60\,000\,000}{\text{BPM}}, \qquad t(\text{tick}) = \sum_{j} \frac{\big(\min(\text{tick}, k_{j+1}) - k_j\big)^{+}\,\mu_j}{\text{PPQ}\cdot10^{6}}\ \text{s}$$
\(k_j\): \(j\)번째 템포 이벤트의 틱 위치, \(\mu_j\): 그 구간의 µs/4분음표. 예) PPQ 480, 120 BPM에서 1틱 = 500,000/480 ≈ 1.04 ms. 960 PPQ면 약 0.52 ms로, 사람의 타이밍 변별(수 ms)보다 충분히 세밀하다.
SIMULATOR

VLQ 인코더와 틱 ↔ 시간 계산기

템포 1 메타 이벤트—
틱 → 시각—
1틱 길이 (템포 1/2)—
위: 입력 값을 7비트씩 끊은 모양. 노란 비트가 '계속' 표시(MSB)이고, 보라가 값을 이루는 7비트입니다. 아래: 4박째에 템포가 바뀌는 템포 맵에서 틱→초 그래프(구간마다 기울기 = µs/틱). 점은 '틱 위치' 슬라이더 값이고, 재생하면 메트로놈이 템포 맵대로 8박을 치며 점이 따라 움직입니다. 템포가 바뀌는 지점에서 그래프가 꺾이는 것이 곧 '틱은 박자 단위, 초는 벽시계 단위'라는 뜻입니다.
SIMULATOR

표준 MIDI 파일 만들기 (.mid 내려받기)

청크 헤더Δt (VLQ)상태데이터메타 이벤트
⬇ .mid
파일 크기—
이벤트 수—
러닝 스테이터스 절약—
격자(8분음표 16칸 × C4–C5)를 눌러 선율이나 화음을 찍으면 그 즉시 형식 0, 트랙 1개짜리 SMF 바이트가 만들어집니다: MThd → MTrk → 트랙 이름(FF 03) → 템포(FF 51) → 박자(FF 58 04 04 02 18 08 = 4/4) → 프로그램 체인지 → 노트 이벤트 → 트랙 끝(FF 2F 00). 바이트 위에 마우스를 올리면 설명이 나옵니다. '재생'은 격자가 아니라 방금 만든 바이트를 다시 해석해서 연주하므로, 파서와 작성기가 서로 맞는지(round trip) 확인하는 셈입니다. 내려받은 파일은 DAW나 OS의 미디 플레이어에서 열립니다.

표현력의 확장: MPE, MIDI 2.0, OSC

MIDI 1.0의 근본적 한계는 세 가지입니다. 7비트 해상도, 채널당 하나뿐인 피치 벤드(화음 중 한 음만 굽힐 수 없음), 그리고 단방향 통신입니다. 이를 넘으려는 시도들은 다음과 같습니다.

MIDI 1.0 (1983)MPE (2018)MIDI 2.0 (2020)OSC (1997)
단위바이트 스트림MIDI 1.0 메시지 그대로UMP(Universal MIDI Packet) 32/64/96/128비트UDP/TCP 패킷, 주소 경로 /synth/1/freq
세기·컨트롤 해상도7비트 (벤드만 14)7/14비트세기 16비트, CC·벤드 32비트32비트 정수/실수 등 형식 지정
음 하나만 굽히기불가(채널 단위)음마다 채널 하나씩 배정(단일 Lower Zone은 마스터 1·멤버 2–16, 단일 Upper Zone은 마스터 16·멤버 15부터 감소; 두 존 사용 시 각각 마스터 예약), 벤드 범위 기본 ±48반음노트별 피치 벤드·노트별 컨트롤러주소 설계에 달림
채널16존(zone)당 15음16그룹 × 16 = 256제한 없음
양방향아니오(SysEx 관례)아니오MIDI-CI로 기능 협상·프로파일·속성 교환네트워크 그대로

MPE(MIDI Polyphonic Expression)는 MIDI 1.0 메시지를 바꾸지 않고 음 하나 = 채널 하나로 배정하는 규약입니다. 루미(ROLI Seaboard) 같은 압력 감지 건반은 음마다 피치 벤드(X, 좌우 미끄러짐), CC74(Y, 앞뒤 위치), 채널 애프터터치(Z, 누르는 힘)를 보냅니다. MIDI 2.0은 1.0과의 호환을 유지하면서(UMP는 1.0 메시지도 담음) 해상도와 메시지 종류를 늘렸고, 7비트 값을 16비트로 올릴 때는 0→0, 64→0x8000(중앙), 127→0xFFFF가 정확히 대응하도록 비트를 반복 채우는 변환 규칙을 정했습니다. OSC(Open Sound Control, Wright & Freed 1997, UC 버클리 CNMAT)는 MIDI가 아닌 네트워크 프로토콜로, 사람이 읽는 주소와 형식 있는 인자를 써서 Max/MSP, SuperCollider, TouchOSC 같은 환경에서 널리 쓰입니다.

브라우저에서 MIDI: Web MIDI API

W3C의 Web MIDI API는 자바스크립트에서 운영체제의 MIDI 포트에 직접 접근하게 해 줍니다. 보안 컨텍스트(https 또는 localhost)에서만 동작하고, 사용자 권한 확인을 거치며, SysEx는 별도 권한({sysex: true})이 필요합니다. 2024년 기준 크롬·엣지·오페라 등 크로미움 계열과 파이어폭스(108 이후, 사이트 권한 애드온 방식)가 지원하고, 사파리는 지원하지 않습니다.

const access = await navigator.requestMIDIAccess(); // 권한 요청 for (const input of access.inputs.values()) input.onmidimessage = (e) => { // e.data: Uint8Array, e.timeStamp: ms const [st, d1, d2] = e.data; if ((st & 0xF0) === 0x90 && d2 > 0) noteOn(st & 0x0F, d1, d2); }; const out = [...access.outputs.values()][0]; out.send([0x90, 60, 100]); out.send([0x80, 60, 0], performance.now() + 500); // 타임스탬프 예약

브라우저가 바이트 스트림을 이미 메시지 단위로 잘라서(러닝 스테이터스 복원 포함) 넘겨 주므로, 3절의 파서는 직렬 포트나 파일을 다룰 때 필요합니다. 출력의 send(data, timestamp)는 performance.now() 기준으로 미래 시각을 지정할 수 있어, 웹 오디오의 '앞서 예약하기'(11장의 스케줄러)와 같은 방식으로 정확한 타이밍을 얻습니다.

SIMULATOR

Web MIDI 입력 모니터


          
아직 요청하지 않았습니다.
받은 메시지0
마지막 메시지 간격—
USB MIDI 건반을 연결하고 '연결 요청'을 누른 뒤 브라우저의 권한 창에서 허용하세요. 들어오는 메시지가 로그에 해석되어 쌓이고 건반에 표시되며, 체크하면 이 장의 합성기로 소리를 냅니다(브라우저가 붙인 timeStamp 간격도 표시). 장치가 없거나 브라우저가 Web MIDI를 지원하지 않으면 '가상 입력 데모'로 같은 처리 경로를 시험할 수 있습니다. 데모는 실제 입력 포트와 똑같이 {data, timeStamp} 이벤트를 만들어 같은 처리 함수에 넣습니다.

핵심 정리

  1. MIDI 1.0은 31.25 kbaud, 8-N-1 직렬이다. 바이트당 320 µs, 노트 온 하나에 약 1 ms가 걸려 큰 화음은 수 ms 번진다.
  2. MSB가 1이면 상태, 0이면 데이터 바이트다. 데이터는 7비트이고, 채널 메시지 상태 바이트는 '종류 니블 + 채널 니블'이다. 세기 0인 노트 온은 노트 오프다.
  3. 러닝 스테이터스는 같은 상태 바이트를 생략한다. 실시간 메시지(F8–FF)는 어디든 끼어들 수 있고 러닝 스테이터스를 지우지 않으며, 시스템 공통 메시지는 지운다.
  4. 피치 벤드는 LSB 먼저인 14비트 값(중앙 8192)이다. 분해능은 200R/2^b ¢이므로, 벤드 범위가 넓거나 7비트만 쓰면 계단이 들린다.
  5. MIDI 클럭은 4분음표당 24개의 F8이다. 템포는 펄스 간격에서 추정하므로 지터와 반응 속도 사이의 트레이드오프가 생긴다.
  6. SMF는 MThd + MTrk 청크이고, 이벤트마다 VLQ로 Δt(틱)가 붙는다. 시간 = 틱 × (µs/4분음표) / PPQ를 템포 맵 구간별로 더한 것이다.
  7. GM은 128음색과 10번 채널 드럼 배치를 표준화했고, MPE·MIDI 2.0·OSC는 음별 표현, 고해상도, 양방향성, 네트워크로 MIDI 1.0의 한계를 넘는다.

확인 퀴즈

3번 채널에서 E4(64)를 세기 80으로 누르는 노트 온 메시지는?

채널 3은 아래 니블 2(0부터 셈)이므로 상태 0x92. 64 = 0x40, 80 = 0x50. 데이터 바이트에 0x80을 쓰면 MSB가 1이라 상태 바이트로 오인된다.

러닝 스테이터스를 써서 같은 채널로 노트 온 6개를 보낼 때 필요한 바이트 수와 전송 시간은?

첫 메시지 3바이트 + 나머지 5개 × 2바이트 = 13바이트. 바이트당 10비트 / 31,250 bit/s = 320 µs이므로 13 × 0.32 = 4.16 ms.

피치 벤드 메시지 E0 00 60은 벤드 범위 ±2반음에서 몇 센트인가?

v = 0x60 × 128 + 0x00 = 96 × 128 = 12288. (12288 − 8192)/8192 = 0.5이므로 0.5 × 200 ¢ = +100 ¢, 즉 반음 하나 위.

값 200을 VLQ로 인코딩하면?

200 = 1·128 + 72. 큰 자리 7비트 1에 계속 비트를 붙여 0x81, 마지막 7비트 72 = 0x48은 MSB 0. 그래서 81 48.

PPQ 480, 템포 메타 이벤트 FF 51 03 09 27 C0(600,000 µs)일 때 960틱은 몇 초인가?

960틱 = 4분음표 2개. 4분음표 하나가 0.6 s(100 BPM)이므로 1.2 s.

MIDI 클럭(F8)으로 120 BPM을 보낼 때 클럭 바이트 간격과 그것이 쓰는 대역폭 비율에 가장 가까운 것은?

초당 120/60 × 24 = 48개, 간격 1/48 s = 20.8 ms. 48바이트/s ÷ 3,125바이트/s ≈ 1.5%.