Skip to content

Instantly share code, notes, and snippets.

@takeru
Created June 3, 2025 13:21
Show Gist options
  • Select an option

  • Save takeru/bda56568aaa6117e6c7c4989c557ee77 to your computer and use it in GitHub Desktop.

Select an option

Save takeru/bda56568aaa6117e6c7c4989c557ee77 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
"""
純粋 Python だけで **SMF (Standard MIDI File)-format 0** を書き出す最小サンプル。
- MThd / MTrk チャンクを手動組み立て
- Set-Tempo → Note On → Note Off → End-of-Track という典型的な並び
- 可変長数量 (VLQ) も自前実装
gist に貼り付けてそのまま動かせます(`example.mid` が生成されます)。
"""
import struct
from typing import List
def write_vlq(value: int) -> bytes:
"""Variable-Length Quantity (最大 4 byte) を big-endian で符号化"""
bytes_: List[int] = [value & 0x7F]
value >>= 7
while value:
bytes_.insert(0, (value & 0x7F) | 0x80) # 先頭側に 1 で継続ビットを立てる
value >>= 7
return bytes(bytes_)
def create_midi(
path: str = "example.mid",
ticks_per_quarter: int = 480,
tempo_us_per_qn: int = 500_000, # 120 BPM
note: int = 60, # Middle-C
velocity: int = 64,
duration_ticks: int = 480 # 4 分音符
) -> None:
"""単一トラックに 1 音だけ鳴らす format 0 ファイルを生成"""
events = bytearray()
# --- Meta: Set Tempo (推奨) ------------------------------
events += write_vlq(0) # Δ-time
events += b"\xFF\x51\x03" # type 0x51, length 3
events += struct.pack(">I", tempo_us_per_qn)[1:] # 上位 3 byte
# --- Note On --------------------------------------------
events += write_vlq(0) # Δ-time
events += b"\x90" # 0x9n, ch = 0
events += bytes([note, velocity])
# --- Note Off -------------------------------------------
events += write_vlq(duration_ticks) # Δ-time
events += b"\x80" # 0x8n, ch = 0
events += bytes([note, 0])
# --- End-of-Track (必須) ---------------------------------
events += write_vlq(0) # Δ-time
events += b"\xFF\x2F\x00"
# ---- チャンク組み立て ----------------------------------
track_chunk = b"MTrk" + struct.pack(">I", len(events)) + events
header_chunk = (
b"MThd"
+ struct.pack(">IHHH",
6, # 以降のヘッダ長
0, # format = 0
1, # track 数
ticks_per_quarter)
)
with open(path, "wb") as f:
f.write(header_chunk)
f.write(track_chunk)
if __name__ == "__main__":
create_midi()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment