MIDI와 음악 데이터
MIDI는 소리가 아니라 연주 동작을 보내는 프로토콜입니다. "60번 건반을 세기 100으로 눌렀다", "페달을 밟았다" 같은 사건이 3바이트 남짓한 메시지로 오갑니다. 1983년에 정한 31.25 kbaud 직렬 규격이 40년 넘게 거의 그대로 쓰이고 있다는 점에서, MIDI는 공학사에서 보기 드문 장수 인터페이스입니다. 이 장에서는 그 바이트를 직접 만들고, 해독하고, 파일로 저장하고, 다시 소리로 바꿉니다.
- MIDI 1.0의 물리 계층(31.25 kbaud, 8-N-1)에서 바이트당 320 µs를 계산하고, 화음 번짐(chord smear)을 예측한다.
- 상태 바이트와 데이터 바이트(MSB)로 메시지를 구분하고, 노트 온/오프·CC·프로그램 체인지·피치 벤드·애프터터치를 손으로 인코딩한다.
- 러닝 스테이터스, 실시간 메시지 끼어들기, SysEx를 처리하는 스트림 파서를 이해한다.
- 14비트 피치 벤드의 분해능과 벤드 범위(RPN 0)의 관계를 계산한다.
- 표준 MIDI 파일(MThd/MTrk)의 가변 길이 수(VLQ), PPQ, 템포 메타 이벤트로 틱과 초를 상호 변환하고, 실제 .mid 파일을 만든다.
- MPE, MIDI 2.0(UMP), OSC, Web MIDI API의 위치를 안다.
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로 나눈 값이어서 정해졌습니다.
직렬 전송과 화음 번짐
메시지의 해부: 상태 바이트와 데이터 바이트
MIDI 바이트는 최상위 비트(MSB)로 두 종류로 나뉩니다. MSB가 1(0x80–0xFF)이면 상태 바이트(Status Byte)로 메시지 종류를 알리고, MSB가 0(0x00–0x7F)이면 데이터 바이트(Data Byte)입니다. 그래서 모든 데이터 값은 7비트, 즉 0–127이고, 수신기는 스트림 중간에 끼어들어도 다음 상태 바이트에서 즉시 동기를 다시 잡을 수 있습니다(자기 동기화 부호). 채널 메시지의 상태 바이트는 위 4비트가 종류, 아래 4비트가 채널(0–15, 사람에게는 1–16으로 표시)입니다.
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) | 2 | LSB, 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번)을 넉넉히 덮습니다.
벨로시티는 건반이 눌린 속도(대개 두 접점 사이 통과 시간)를 1–127로 양자화한 값입니다. 규격은 세기→음량 곡선을 정하지 않지만, 흔히 진폭을 \((v/127)^2\)에 비례하게(약 \(40\log_{10}(v/127)\) dB) 둡니다. 이 장의 멜로디 합성기는 확인하기 쉬운 소리를 위해 피크 이득 0.28(v/127)²+0.015를 사용하므로 순수 제곱 곡선과 달리 작은 벨로시티에도 바닥 이득이 있습니다.
MIDI 메시지 모니터
자주 쓰는 컨트롤 체인지
| CC | 이름 | 비고 |
|---|---|---|
| 0 / 32 | 뱅크 선택 MSB / LSB | 프로그램 체인지 전에 보내 128개 넘는 음색 선택 |
| 1 | 모듈레이션 휠 | 보통 비브라토 깊이 |
| 7 | 채널 볼륨 | 믹서 페이더 역할 |
| 10 | 팬 | 0=왼쪽, 64=가운데, 127=오른쪽 |
| 11 | 익스프레션 | 볼륨에 곱해지는 연주용 음량(스웰 페달) |
| 64 | 서스테인(댐퍼) 페달 | ≤63 꺼짐, ≥64 켜짐 |
| 74 | 밝기(필터 컷오프) | MPE에서는 세로축 '음색(Y)' |
| 100 / 101 | RPN LSB / MSB | 등록 파라미터 선택. 0/0 = 피치 벤드 범위 |
| 6 / 38 | 데이터 엔트리 MSB / LSB | 선택한 RPN의 값 |
| 120 | All Sound Off | 릴리스 무시하고 즉시 무음 |
| 121 | Reset All Controllers | 벤드·모듈레이션·페달 등 초기화 |
| 123 | All Notes Off | '패닉' 버튼. 노트 오프를 잃어버려 음이 멈추지 않을 때(stuck note) |
러닝 스테이터스와 시스템 메시지
대역폭을 아끼기 위해 MIDI 1.0은 러닝 스테이터스(Running Status)를 허용합니다. 같은 상태 바이트가 이어지면 두 번째부터 생략하고 데이터 바이트만 보냅니다. 수신기는 마지막 채널 상태 바이트를 기억해 두었다가 상태 없이 들어온 데이터에 적용합니다. 노트 오프를 '세기 0인 노트 온'으로 보내는 관례는 바로 이 러닝 스테이터스를 끊지 않기 위한 것입니다.
| 바이트 | 이름 | 데이터 | 용도 |
|---|---|---|---|
F0 … F7 | 시스템 익스클루시브(SysEx) | 가변 | 제조사 ID로 시작하는 임의 데이터(음색 덤프, 펌웨어). F0 7E 7F 09 01 F7 = GM System On |
F1 | MTC 쿼터 프레임 | 1 | SMPTE 타임코드를 8조각으로 나눠 전송 |
F2 | 송 포지션 포인터 | 2 | 곡 위치(14비트, 16분음표 = 클럭 6개 단위) |
F3 | 송 셀렉트 | 1 | 곡 번호 |
F6 | 튠 리퀘스트 | 0 | 아날로그 신시사이저 오실레이터 자동 조율 |
F8 | 타이밍 클럭 | 0 | 4분음표당 24번(24 PPQN) |
FA / FB / FC | 스타트 / 컨티뉴 / 스톱 | 0 | 시퀀서 운전 |
FE | 액티브 센싱 | 0 | 300 ms마다 '살아 있음' 신호. 끊기면 수신기가 모든 음을 끈다 |
FF | 시스템 리셋 | 0 | 전원 켤 때 상태로 (파일 안에서는 메타 이벤트 접두어로 쓰인다) |
바이트 스트림 해독기
| # | 바이트 | 해석 |
|---|
피치 벤드와 해상도
피치 벤드는 MIDI 1.0에서 드물게 처음부터 14비트인 메시지입니다. 두 데이터 바이트 중 LSB가 먼저 옵니다. 값 0–16383의 중앙 8192(E0 00 40)가 '벤드 없음'이고, 실제 음높이 변화는 수신기의 벤드 범위에 달려 있습니다. 기본값은 ±2반음이며 RPN 0으로 바꿉니다(B0 65 00 B0 64 00 B0 06 0C = ±12반음).
피치 벤드 계산기와 계단 글리산도
클럭과 동기
여러 장비가 같은 템포로 움직이려면 시간을 공유해야 합니다. MIDI 클럭은 가장 단순한 방법으로, 마스터가 4분음표당 24번(24 PPQN, pulses per quarter note) F8 한 바이트를 보냅니다. 슬레이브는 펄스 간격으로 템포를 추정하고, FA(스타트)·FC(스톱)·F2(송 포지션)로 운전합니다. 템포 값 자체는 보내지 않으므로, 슬레이브는 결국 잡음 섞인 주기 측정으로 BPM을 추정하는 문제를 풀게 됩니다.
MIDI 클럭 지터와 템포 추정
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 B1 | Acoustic Bass Drum | 42 F♯2 | Closed Hi-Hat | 49 C♯3 | Crash Cymbal 1 |
| 36 C2 | Bass Drum 1 (킥) | 43 G2 | High Floor Tom | 50 D3 | High Tom |
| 37 C♯2 | Side Stick | 44 G♯2 | Pedal Hi-Hat | 51 D♯3 | Ride Cymbal 1 |
| 38 D2 | Acoustic Snare | 45 A2 | Low Tom | 53 F3 | Ride Bell |
| 39 D♯2 | Hand Clap | 46 A♯2 | Open Hi-Hat | 54 F♯3 | Tambourine |
| 40 E2 | Electric Snare | 47 B2 | Low-Mid Tom | 56 G♯3 | Cowbell |
| 41 F2 | Low Floor Tom | 48 C3 | Hi-Mid Tom | 57 A3 | Crash Cymbal 2 |
표준 MIDI 파일: 틱, VLQ, 템포 맵
케이블의 MIDI는 '지금' 일어나는 사건이지만, 파일에는 언제를 함께 적어야 합니다. 1988년의 표준 MIDI 파일(Standard MIDI File, SMF)은 청크 구조로 되어 있습니다. 헤더 청크 MThd 하나와 트랙 청크 MTrk 여러 개이며, 모든 정수는 빅 엔디언입니다.
Δt는 대부분 작지만 가끔 아주 큽니다(긴 쉼표). 그래서 SMF는 가변 길이 수(Variable-Length Quantity, VLQ)를 씁니다. 7비트씩 끊어 큰 자리부터 내보내고, 마지막 바이트를 뺀 모든 바이트의 MSB를 1(계속)로 둡니다. 0–127은 1바이트, 최대 0x0FFFFFFF는 4바이트입니다. 같은 발상이 UTF-8, Protocol Buffers의 varint, LEB128에도 쓰입니다(바이트 순서는 다름).
| 값 | VLQ 바이트 | 값 | VLQ 바이트 |
|---|---|---|---|
| 0x00 | 00 | 0x3FFF | FF 7F |
| 0x7F | 7F | 0x4000 | 81 80 00 |
| 0x80 | 81 00 | 0x1FFFFF | FF FF 7F |
| 0x2000 | C0 00 | 0x0FFFFFFF | FF FF FF 7F |
틱을 초로 바꾸려면 템포가 필요합니다. 템포는 메타 이벤트 FF 51 03 tt tt tt에 4분음표 하나의 마이크로초(24비트)로 적힙니다. BPM이 아니라 주기를 적는 것은 정수로 정밀하게 쓰기 위해서입니다. 템포가 곡 중간에 바뀌면 구간별로 나눠 더합니다(템포 맵).
VLQ 인코더와 틱 ↔ 시간 계산기
표준 MIDI 파일 만들기 (.mid 내려받기)
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 이후, 사이트 권한 애드온 방식)가 지원하고, 사파리는 지원하지 않습니다.
브라우저가 바이트 스트림을 이미 메시지 단위로 잘라서(러닝 스테이터스 복원 포함) 넘겨 주므로, 3절의 파서는 직렬 포트나 파일을 다룰 때 필요합니다. 출력의 send(data, timestamp)는 performance.now() 기준으로 미래 시각을 지정할 수 있어, 웹 오디오의 '앞서 예약하기'(11장의 스케줄러)와 같은 방식으로 정확한 타이밍을 얻습니다.
Web MIDI 입력 모니터
timeStamp 간격도 표시). 장치가 없거나 브라우저가 Web MIDI를 지원하지 않으면 '가상 입력 데모'로 같은 처리 경로를 시험할 수 있습니다. 데모는 실제 입력 포트와 똑같이 {data, timeStamp} 이벤트를 만들어 같은 처리 함수에 넣습니다.핵심 정리
- MIDI 1.0은 31.25 kbaud, 8-N-1 직렬이다. 바이트당 320 µs, 노트 온 하나에 약 1 ms가 걸려 큰 화음은 수 ms 번진다.
- MSB가 1이면 상태, 0이면 데이터 바이트다. 데이터는 7비트이고, 채널 메시지 상태 바이트는 '종류 니블 + 채널 니블'이다. 세기 0인 노트 온은 노트 오프다.
- 러닝 스테이터스는 같은 상태 바이트를 생략한다. 실시간 메시지(F8–FF)는 어디든 끼어들 수 있고 러닝 스테이터스를 지우지 않으며, 시스템 공통 메시지는 지운다.
- 피치 벤드는 LSB 먼저인 14비트 값(중앙 8192)이다. 분해능은 200R/2^b ¢이므로, 벤드 범위가 넓거나 7비트만 쓰면 계단이 들린다.
- MIDI 클럭은 4분음표당 24개의 F8이다. 템포는 펄스 간격에서 추정하므로 지터와 반응 속도 사이의 트레이드오프가 생긴다.
- SMF는 MThd + MTrk 청크이고, 이벤트마다 VLQ로 Δt(틱)가 붙는다. 시간 = 틱 × (µs/4분음표) / PPQ를 템포 맵 구간별로 더한 것이다.
- GM은 128음색과 10번 채널 드럼 배치를 표준화했고, MPE·MIDI 2.0·OSC는 음별 표현, 고해상도, 양방향성, 네트워크로 MIDI 1.0의 한계를 넘는다.
확인 퀴즈
3번 채널에서 E4(64)를 세기 80으로 누르는 노트 온 메시지는?
러닝 스테이터스를 써서 같은 채널로 노트 온 6개를 보낼 때 필요한 바이트 수와 전송 시간은?
피치 벤드 메시지 E0 00 60은 벤드 범위 ±2반음에서 몇 센트인가?
값 200을 VLQ로 인코딩하면?
PPQ 480, 템포 메타 이벤트 FF 51 03 09 27 C0(600,000 µs)일 때 960틱은 몇 초인가?
MIDI 클럭(F8)으로 120 BPM을 보낼 때 클럭 바이트 간격과 그것이 쓰는 대역폭 비율에 가장 가까운 것은?