はじめに
大学のイベントでmidiキーボードを使ってなんか作ってくれとの指令を受けたので作ってみました。midiの扱い方の勉強になってよかったです。
材料
ハード
- ヤマハのDisklavier
自動演奏ピアノ、midiデータ送れば勝手に鍵盤が動く。すげぇ - Raspberry Pi
なんと今の御時世に無印のRaspi。初期モデルなのかな? 研究室にあったから使っている。ぜんっぜん情報がなくてたまにハマる。
ソフト
- python3
制作物
まず、以下のコードでポートを取得します。ここはちょっとハマりました。くわしくはこちら
outport = mido.open_output("USB Device 0x499:0x2003:USB Device 0x499:0x2003 MIDI 1")
次に選択用の画面を作成します。
screen = pygame.display.set_mode((400, 330))
pygame.display.set_caption("keyboard event")
この画面を選択している間にキーボードを打つとキーが取得できるようにし ています。
while true:
screen.fill((0, 0, 0))
for event in pygame.event.get():
あとはこのfor文の中でキーボードイベントを取得します。
くわしくは省きますが、例として以下のコードはキーボードF1を押したときにelifにはいるという流れです。
elif event.key == K_F1:
さてキーボードのイベントを取得したあとはピアノを動かします。具体的にはmidiデータを送信します。例えば以下のようなmessageを作成し、send関数で送信するとピアノが動きます。
msg = Message('note_on', note=60)
outport.send(msg)
この場合はnoteナンバー60のドが動きます。
あとはキーボードのイベントによって条件わけをしてnoteナンバーを変更し、send関数で送信すればOKです。
以上の知識から機能やらなにやら追加して、できたコードがこちらです。
作成したコード
import mido
import time
import pygame
import sys
import random
import json
from mido import MidiFile, Message
from pygame.locals import *
DEFAULT_KEY = 48
FILE_list = ["like-piano.json", "lowson-demo.json"]
mode = 0
long_sound = False
outport = mido.open_output("USB Device 0x499:0x2003:USB Device 0x499:0x2003 MIDI 1")
table_list = []
def main():
global mode, long_sound
pygame.init() # Pygameを初期化
screen = pygame.display.set_mode((400, 330))
pygame.display.set_caption("keyboard event")
set_table()
while True:
screen.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
long_sound = not long_sound
elif event.key == K_F1:
mode = 0
elif event.key == K_F2:
mode = 1
else:
octave = 0
if mode == 0:
if event.mod & pygame.KMOD_LSHIFT:
octave = octave - 1
if event.mod & pygame.KMOD_RSHIFT:
octave = octave + 1
key = pygame.key.name(event.key)
elif mode == 1:
key = pygame.key.name(event.key)
if event.mod & pygame.KMOD_SHIFT:
key = key.upper()
print(octave)
send_midi(key, octave, "down")
elif event.type == KEYUP:
send_midi(key, octave, "up")
pygame.display.update()
def send_midi(key, octave, move):
num = key2num(key)
if num == None:
return
num = num + DEFAULT_KEY + 12 * octave
if move == "down":
msg = Message('note_on', note=num, velocity=90, time=50)
outport.send(msg)
if long_sound == False:
msg = Message('note_off', note=num, velocity=90, time=50)
outport.send(msg)
elif move=="up" and long_sound == True:
msg = Message('note_off', note=num, velocity=0)
outport.send(msg)
msg = Message('note_on', note=num, velocity=0)
outport.send(msg)
def key2num(key):
if key in table_list[mode]:
return table_list[mode][key]
def set_table():
for file in FILE_list:
with open(file) as f:
global table_list
table_list.append(json.load(f))
if __name__ == '__main__':
main()
outport.close()
仕様書っぽいもの
機能: キーボードを押すことでピアノを動かす
仕様:
Keyboardには2つのモードがある。ピアノライクモードとmoji2musicモードである。
デフォルトはピアノライクモードになっている。
ピアノライクモード:
名前通り通常のピアノと同じようなキー配置になっている。「asdfghjkl」は 白鍵の「ドレミファソラシドレ」になっている。また「wetyuo」は黒鍵である。この押したキーと動くキーはjsonファイルで指定可能である。また左shiftを押しながら押すと一オクターブ下がり、右shiftを押しながら押すと一オクターブ上がった音が鳴る。
moji2musicモード:
これは「SITintamachi」とリズミカルに打つとファミマの音楽になるモードである。このモードでは 「SITintamachi」以外のキーを押しても音がならない。ピアノライクモードと同様、jsonファイルで指定できるので拡張可能である。
以上の2つのモードはF1(デフォルト)、F2キーを押すことで変更できる。
また鍵盤の押し方には2種類あり、単発と連続である。
デフォルトは単発である。escキーでとトグル式に変更できる。
単発ではキーを押すと一音が鳴り、終了する。キーを長く、あるいは短く押しても鳴る音は一定期間である。
連続ではキーを押している間、ピアノのキーも押され続ける。そしてキーを話したときにピアノのキーは離される。
こちらの方がピアノらしいがバグが存在する。2つ以上のキーを押すと、キーを離した際に全てのキーが離されず、押されっぱなしになってしまう。ただ、時間経過でキーは離される。
実機で検証してみた結果、midiキーボード(キーは動かない)では音の遅延はなかったが、Disklavier では約0.5秒の遅延が存在する。
おわりに
midiを動かせるといろいろできる幅が増えるのでいい経験だったと思う。
コメント