PythonでMIDI

PythonでMIDIを扱うために、MIDI機器からのMIDIイベントを逐次的にモニターできるものを探していました。pygameというモジュールが使えそうなので試してみることにします。

私の環境はWindows 10 Home, Python 3.8.6です。Pythonはwww.python.orgからダウンロードしました。pygameは以下のコマンドでインストールできます。


> pip install pygame

余談ですが、pipインストールで以下のようなSSLのエラーに遭遇しました。いろいろ試したのですがpythonをインストールし直すことでしか解決できませんでした。

ERROR: No matching distribution found for pprint
Could not fetch URL https://pypi.org/simple/pip/: There was a problem confirming
the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries
exceeded with url: /simple/pip/ (Caused by SSLError(SSLCertVerificationError
(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get
local issuer certificate (_ssl.c:1108)'))) - skipping

pygameのドキュメントは、www.pygame.orgにあります。以下に関数の簡単な説明を書いておきます。

  • pygame.midi
    MIDI入力および出力を扱うpygameモジュール。
  • pygame.midi.init
    MIDIモジュールの初期化
  • pygame.midi.quit
    MIDIモジュールを解除
  • pygame.midi.get_init
    MIDIモジュールの初期化をチェック。初期化されている場合はTrueを返す
  • pygame.midi.Input
    入力は、midiデバイスからmidi入力を取得
  • pygame.midi.Output
    出力は、MIDIを出力デバイスに送信
  • pygame.midi.get_count
    接続されているMIDIデバイスの数を取得
  • pygame.midi.get_default_input_id
    デフォルトの入力デバイス番号を取得
  • pygame.midi.get_default_output_id
    デフォルトの出力デバイス番号を取得します
  • pygame.midi.get_device_info
    MIDIデバイスの情報を返す
    戻り値 (interf、name、input、output、opened)
    interf:デバイスインターフェイスの説明 MMSystem:Windows
    name:デバイス名
    input: 入力デバイスは1 output: 出力デバイスは1 opened: デバイスが開いている時1
  • pygame.midi.midis2events
    MIDIイベントをpygameイベントに変換
  • pygame.midi.time
    PortMidiタイマーの現在の時刻をミリ秒単位で取得
  • pygame.midi.frequency_to_midi
    周波数をMIDIノートに変換
  • pygame.midi.midi_to_frequency
    MIDIノートを周波数に変換
  • pygame.midi.midi_to_ansi_note
    MIDI番号のANSIで音階名を返す

以下にサンプルプログラムを掲載します。

デバイスの情報表示
import pygame.midi as m

m.init()            # MIDIデバイスを初期化
i_num=m.get_count() # MIDIデバイスの数
for i in range(i_num):
	print(m.get_device_info(i)) # MIDIデバイスの情報を表示

[結果]
pygame 2.0.0 (SDL 2.0.12, python 3.8.6)
Hello from the pygame community. https://www.pygame.org/contribute.html
(b'MMSystem', b'Microsoft MIDI Mapper', 0, 1, 0) // 入力
(b'MMSystem', b'loopMIDI Port', 1, 0, 0)         // 入力
(b'MMSystem', b'nanoKEY2', 1, 0, 0)              // 入力
(b'MMSystem', b'Microsoft GS Wavetable Synth', 0, 1, 0) // 出力
(b'MMSystem', b'loopMIDI Port', 0, 1, 0)                // 出力
(b'MMSystem', b'nanoKEY2', 0, 1, 0)                     // 出力
MIDIの受信
import pygame.midi as m

m.init()
i = m.Input(2) # デバイス番号を2とする
while True:
  if i.poll(): # MIDIが受信されると1
    midi_events = i.read(4) # 読み取る入力イベントの数 
    print ("midi_events:" + str(midi_events))


[結果]
pygame 2.0.0 (SDL 2.0.12, python 3.8.6)
Hello from the pygame community. https://www.pygame.org/contribute.html
midi_events:[[[144, 48, 55, 0], 1396]]
midi_events:[[[128, 48, 64, 0], 1644]]
midi_events:[[[144, 48, 54, 0], 2331]]
midi_events:[[[128, 48, 64, 0], 2495]]
...
...

このプログラムで、18バイトのSystem Exclusive (0xF0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xF7)を受信してみます。読み取る入力イベントの数を4と設定しているので、1回目は0xF0から0x0Eまでのは4x4の16バイト、2回目に0x0Fと0xF7の2バイトの2回に分けて受信します。

midi_events:[[[240, 0, 1, 2], 41131], [[3, 4, 5, 6], 41131],
[[7, 8, 9, 10], 41131], [[11, 12, 13, 14], 41131]] //1回目の受信 midi_events:[[[15, 247, 0, 0], 41131]] //2回目の受信
MIDIを受信し、ソフトシンセへ送信
import pygame.midi as m
m.init()
i = m.Input(2)
o = m.Output(0)

while True:
  if i.poll(): # MIDIが受信されると1
    midi_events = i.read(4)
    for j in range(len(midi_events)):
      status=midi_events[j][0][0]
      data1 =midi_events[j][0][1]
      data2 =midi_events[j][0][2]
       if(status==0x80 or status==0x90): #Note OnかOFFならMIDI送信
        o.write_short(status,data1,data2)
i.close()
MIDIの音階情報
import pygame.midi as m

for note in range(128):
	print(note," : ",end="")
	print(m.midi_to_ansi_note(note)," : ",end="")
	print(m.midi_to_frequency(note), " Hz")


[結果]
...
60  : C4  : 261.6  Hz
61  : C#4  : 277.2  Hz
62  : D4  : 293.7  Hz
63  : D#4  : 311.1  Hz
64  : E4  : 329.6  Hz
65  : F4  : 349.2  Hz
66  : F#4  : 370.0  Hz
67  : G4  : 392.0  Hz
68  : G#4  : 415.3  Hz
69  : A4  : 440.0  Hz
70  : A#4  : 466.2  Hz
71  : B4  : 493.9  Hz
72  : C5  : 523.3  Hz
...