クーの自由研究

素朴な疑問を実験します。ボクと一緒に螺旋階段を頂上まで登ってみませんか?貴方の影についていきます。

自由研究の準備(その6)PythonでのMIDI操作(リアルタイム編)

おおまかな内容

PythonでリアルタイムにMIDIの操作が、どのようにできるか確認します。pygame.midiを使います。

なぜやるのか・どうなると思うか

サウンドの次は音列の実験をするつもりです。
せっかくなので外部のMIDI機器も操作できるようにしておこうと思いました。

準備と実験のやり方

MIDIポートの確認、MIDI入力と出力がPythonで簡単にできるか確認をします。

実験の結果

MIDIポートの確認

リアルタイムにMIDIを扱うときは、pygameというライブラリがよいようです。

まずはボクのパソコンのMIDIポートを確認します。

import pygame
import
pygame.midi

pygame.init()
pygame.midi.init()
count = pygame.midi.get_count()
print("get_default_input_id:%d" % pygame.midi.get_default_input_id())
print("get_default_output_id:%d" % pygame.midi.get_default_output_id())
print("No:(interf, name, input, output, opened)")
for i in range(count):
print("%d:%s" % (i, pygame.midi.get_device_info(i)))

get_default_input_id:1
get_default_output_id:0
No:(interf, name, input, output, opened)
0:('MMSystem', 'Microsoft MIDI Mapper', 0, 1, 0)
1:('MMSystem', 'QUAD-CAPTURE', 1, 0, 0)
2:('MMSystem', 'Microsoft GS Wavetable Synth', 0, 1, 0)
3:('MMSystem', 'QUAD-CAPTURE', 0, 1, 0)

4つポートがあって、外部との接続ができるMIDIインターフェースのQUAD-CAPTUREは[Port:1]がInput、[Port:3]がOUTPUTでした。

デフォルト入力は1なのでQUAD-CAPTURE (MIDI IN)でした。

MIDI入力の確認

次に、[Port:1]からの情報を拾ってみます。

QUAD-CAPTURE (MIDI IN)には外部のMIDIキーボードを接続してキーを押してみました。(テストなので14イベントで終了するようにしました)

# -*- coding: utf-8 -*-
import pygame.midi

pygame.init()
pygame.midi.init()
input_id = pygame.midi.get_default_input_id()
print("input MIDI:%d" % input_id)
i = pygame.midi.Input(input_id)

print ("starting")
print ("full midi_events:[[[status,data1,data2,data3],timestamp],...]")

going = True
count = 0
while going:
if i.poll():
midi_events = i.read(10)
print "full midi_events:" + str(midi_events)
count += 1
if count >= 14:
going = False

i.close()
pygame.midi.quit()
pygame.quit()
exit()

input MIDI:1
starting
full midi_events:[[[status,data1,data2,data3],timestamp],...]
full midi_events:[[[144, 60, 48, 0], 2289]]
full midi_events:[[[144, 60, 0, 0], 2970]]
full midi_events:[[[144, 62, 53, 0], 3132]]
full midi_events:[[[144, 62, 0, 0], 3745]]
full midi_events:[[[144, 64, 68, 0], 3899]]
full midi_events:[[[144, 64, 0, 0], 4546]]
full midi_events:[[[144, 65, 72, 0], 4733]]
full midi_events:[[[144, 65, 0, 0], 5345]]
full midi_events:[[[144, 64, 72, 0], 5520]]
full midi_events:[[[144, 64, 0, 0], 6108]]
full midi_events:[[[144, 62, 56, 0], 6313]]
full midi_events:[[[144, 62, 0, 0], 6889]]
full midi_events:[[[144, 60, 68, 0], 7175]]
full midi_events:[[[144, 60, 0, 0], 7996]]

MIDI入力からの情報をひろえています。2つ目の数字(data1)が音高で、3つ目(data2)がベロシティです。ベロシティがゼロのものはノートオフです。

情報だけではつまらないので、MIDIソフトにつないで音を出して貼ってみます。(操作に手間取ったので最初の2~3秒は無音です。音量にご注意ください。)


MIDI出力の確認

Pythonで動作するソフトキーボードからMIDI出力し、MIDIソフト音源(これはPythonではありません)を鳴らしてみました。内部的なMIDI接続ではなく、一旦MIDI OUTし、ケーブルでMIDI IN につないで確認しました。

上のサイトのプログラムをそのまま貼り付けさせて頂きました。

# -*- coding: utf-8 -*-
'''MIDI keyboard.

マウス左ボタンで発音、右ボタンで完全5度を同時に発音。
スペースバーを押しながらだとサステイン。
鍵盤の下の方を押すほど大音量。
'''
import pygame
import pygame.midi
from pygame.locals import *

INSTRUMENT = 19 # 楽器の種類 (0-127)
# 0:Piano, 19:Organ, 56:Trumpet, 91:Voice 等
KEY_WIDTH = 20 # 鍵盤の幅
WIDTH, HEIGHT = 800, 128 # 画面の大きさ

FPS = 60
NOTE_CENTER = 60 # 中央の音。C(ド)の音を指定
COLOR = 0, 255, 200 # 色
WHITE_COLOR = 255, 255, 255 # 白鍵の色
BLACK_COLOR = 0, 0, 50 # 黒鍵の色
BG_COLOR = 100, 0, 50 # 背景色

KEY_COLOR = 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0 # 0=白鍵, 1=黒鍵
NOTE_NAME = ('C', 'C#', 'D', 'D#', 'E',
'F', 'F#', 'G', 'G#', 'A', 'A#', 'B')


def main():
pygame.init()
pygame.midi.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
#midiout = pygame.midi.Output(pygame.midi.get_default_output_id())
midiout = pygame.midi.Output(3)
midiout.set_instrument(INSTRUMENT)
clock = pygame.time.Clock()
clock.tick(FPS)
keys = WIDTH // KEY_WIDTH + 1
keylist = [False] * (keys + 7)
note_start = NOTE_CENTER - keys // 2
note_no = None
vel = 0
sustain = False
while True:
for e in pygame.event.get():
if e.type is QUIT:
return
elif e.type is KEYDOWN and e.key is K_ESCAPE:
return
elif e.type is KEYDOWN and e.key is K_SPACE:
sustain = True
elif e.type is KEYUP and e.key is K_SPACE:
sustain = False
note_no = None
for key, b in enumerate(keylist):
if b:
midiout.note_off(note_start + key, 0)
keylist[key] = False
elif e.type is MOUSEBUTTONDOWN and (
e.button == 1 or e.button == 3):
x, y = e.pos
vel = 128 * y // HEIGHT
key = x // KEY_WIDTH
keylist[key] = True
note_no = note_start + key
midiout.note_on(note_no, vel)
if e.button == 3:
keylist[key + 7] = True
midiout.note_on(note_no + 7, vel)
elif e.type is MOUSEBUTTONUP and (
e.button == 1 or e.button == 3):
if not sustain:
note_no = None
for key, b in enumerate(keylist):
if b:
midiout.note_off(note_start + key, 0)
keylist[key] = False
screen.fill(BG_COLOR)
for key in range(keys):
x = key * KEY_WIDTH
pygame.draw.rect(
screen,
(WHITE_COLOR, BLACK_COLOR)[
KEY_COLOR[(note_start + key) % 12]],
(x + 1, 0, KEY_WIDTH - 2, HEIGHT))
if keylist[key]:
pygame.draw.rect(
screen, COLOR, (x, 0, KEY_WIDTH, HEIGHT), 3)
clock.tick(FPS)
pygame.display.flip()
notes = []
for key, b in enumerate(keylist):
if b:
nn = note_start + key
notes.append('{0}:{1}'.format(NOTE_NAME[nn % 12], nn))
pygame.display.set_caption(', '.join(notes))

try:
main()
finally:
pygame.quit()

# Public Domain. 好きに流用してください。

f:id:np2LKoo:20160831024106p:plain

が画面に出るので、マウスで押します。

問題なく音がでることを確認しました。

やっぱり音をはらないとつまらないので、ソフト音源を鳴らして録音しました。録音は準備(その5)で紹介したPythonプログラムを使用しました。WindowsはASIOインターフェースではないとソフト音源の遅延がひどいので、未導入の方はASIOをお勧めします。(操作に手間取ったので最初の2~3秒は無音です。音量にご注意ください。)

 

まとめ

MIDIの操作も問題なくできることがわかりました。

感想・思ったこと・考えたこと

毎度ですが、Python(とライブラリ)ってすごい、と思いました。 

好きです 好きです ヨシコさん どうもすいあせん  P^_^)

音楽の切り口で検索いただく方が増えて、とても嬉しいです。

注文していた「コンピュータ音楽」歴史・テクノロジー・アート  がとどきました。すごすぎです。

ところで、コンピュータとは全然関係ないですが、桑田佳祐の「ヨシ子さん」は歴史的名曲だと思います。時代が真価を評価すれば、21世紀を代表する1曲となるかもしれません。本当にどこをどうとっても最高です。もう完全に別次元です。知性と混沌と欲望と遊びと脱力と緻密と狂気の融合で、絶妙のバランス感覚。しかも誰にでも受け入れられる要素があります。きくたびにあらたな発見があります。これを聞くと音楽とは計算できない領域にあるのでは、と思ってしまいます。桑田佳祐に注目していなかったことを反省します。どうもすいません。