Created
June 3, 2025 13:21
-
-
Save takeru/bda56568aaa6117e6c7c4989c557ee77 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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