キーボードピアノ【本格49鍵!】

Python

Python でキーボードピアノを作る方法について、スクリプトをまとめ、公開しておきます。
マウスクリックだけでなく、パソコンのキーからも演奏ができます。

以下の環境で動作を確認しています。
環境:
・ Windows 10 パソコン(Anaconda、Python 3、pygame インストール済み)
※ Python と pygame は必須です。pygame をインストールしていない場合は、下記の関連リンクを参照してみてください。

背景 ~ パソコンをキーボードピアノ代わりにする!

Windows パソコンで Python 経由で MIDI 音源が自由に扱えるようになると、ちょっとした音楽を簡単に作成できるようになり、パソコンや Python の活用の幅が格段に広がります。
ということで、Python でキーボードピアノを作るスクリプトをまとめ、公開しておくことにします。

設定のしかた

① パソコンにフォルダを作成し、keyboard_piano1.py というテキストファイルを作成してください。
例: C:\user\keyboard_piano\
② ①のフォルダ内に keyboard_piano1.py という名前でテキストファイルを作成し、下記のスクリプトをコピー&ペーストして保存してください。
例: C:\user\keyboard_piano\keyboard_piano1.py

キーボードピアノの使い方:パソコンのキーで鍵盤代わり

③ コマンドプロンプト(または、Anaconda Prompt)を起動して、以下のコマンドを実行してください。キーボードピアノの GUI が起動します。
python C:\user\keyboard_piano\keyboard_piano1.py

※ 鍵盤をクリックすると音が鳴ります。PC のキーボードのキーを押しても音が鳴ります。
※ ピアノの白鍵と黒鍵の位置関係にほぼ対応するよう、パソコンのキーを割りつけています。
パソコンのキーボードのキーをいくつか押してみてください。

音の大きさの調整

音量は「volume」欄の「+」、「-」ボタンをクリックして調整してください。
数値を直接入力しても設定できます。

MIDI の楽器を設定する

楽器を設定・変更するには、ソフトウェアの画面 GUI 上、「instrument」欄の「+」、「-」ボタンをクリックしてみてください。
楽器の種類は、以下の表を参照してください。
設定している数値が instrument 番号に対応しています。簡単のため、下記のリストは抜粋としていますが、表に示した前後の数値を入れると、類似の楽器が設定できると思います。なお、数値はテキストボックスに直接半角数値で入力できますが、設定を反映させるため、「+」「-」ボタンで値を前後させてみてください。

instrument 番号 楽器
001 Acoustic Grand Piano グランドピアノ
007 Harpsichord ハープシコード
013 Marimba マリンバ/木琴
017 Drawbar Organ 電気オルガン
022 Accordion アコーディオン
023 Harmonica ハーモニカ
025 Acoustic Guitar アコースティックギター
033 Acoustic Bass アコースティックベース
041 Violin バイオリン
043 Chello チェロ
053 Choir Aahs 合唱(アー)
057 Trumpet トランペット
061 French Horn フレンチホルン
065 Soprano Sax ソプラノサックス
069 Oboe オーボエ
072 Clarinet クラリネット
073 Piccolo ピッコロ
074 Flute フルート
078 Shakuhachi 尺八
081 Lead 1 シンセサイザー
107 Shamisen 三味線
108 Koto 琴
113 Tinkle Bell ティンカベル
117 Taiko Drum 和太鼓
123 Seashore 海辺
124 Bird Tweet 鳥のさえずり
127 Applause 拍手

好きな楽器で音階をつけて自由に演奏できます。
※ MIDI は社団法人音楽電子事業協会(AMEI)の登録商標です。

まとめ

Python のキーボードピアノのサンプルスクリプトをまとめました。
これで、任意の音源も扱えるようになりました。

なお、スクリプトの解説については、以下の「ドレミファソラシドを鳴らすサンプル」を参照してください。
プログラムが長くなりすぎたため詳細は省略しますが、MIDI 音源を鳴らすポイントの部分はこのリンク先と同等です。
他に、カノン進行、Just the Tow of Us 進行など、任意のコード進行を演奏するスクリプトなどについてもまとめています。
音楽関連のプログラミングなど、もし関心があるようでしたら、参考にしてみてください。

関連リンク
・ ドレミファソラシドを鳴らすサンプル【pygame】
・ Python で Just the Two of Us 進行 【pygame】
・ 【tkinter】 テキストボックスとボタンを使う方法 コピペ用サンプル
・ PC の音声を録音する方法
・ 
Anaconda 環境で Pygame をインストールする手順【Python】
・ Pygame をインストールする手順

サンプルスクリプト keyboard_piano1.py

import tkinter as tk1 
import threading as th1 
import time as tm1 
import pygame.midi

class tone_thread1( th1.Thread ): 
    def __init__( self ): 
        super( tone_thread1, self ).__init__()
    def run( self ):
        tone1() 

def tone1(): 
    global n1 
    global vol1 
    out1.note_on( n1, vol1 )
    tm1.sleep( 2.0 ) 
    out1.note_off( n1, vol1 ) 

def btn1( n0 ): 
    global n1 
    n1 = n0 - 25 + 60 
    thr1 = tone_thread1() 
    thr1.start() 

def key1( event ): 
    global n1 
    c1 = event.char 
    n0 = 0 
    if c1 == 'a': 
        n0 = 60
    elif c1 == 'w': 
        n0 = 61 
    elif c1 == 's': 
        n0 = 62 
    elif c1 == 'e': 
        n0 = 63 
    elif c1 == 'd': 
        n0 = 64 
    elif c1 == 'f': 
        n0 = 65 
    elif c1 == 'y': 
        n0 = 66
    elif c1 == 'g': 
        n0 = 67 
    elif c1 == 'y': 
        n0 = 68 
    elif c1 == 'h': 
        n0 = 69 
    elif c1 == 'u': 
        n0 = 70 
    elif c1 == 'j': 
        n0 = 71 
    elif c1 == 'k': 
        n0 = 72 
    elif c1 == 'o': 
        n0 = 73 
    elif c1 == 'l': 
        n0 = 74 
    elif c1 == 'p': 
        n0 = 75 
    elif c1 == ';': 
        n0 = 76 
    elif c1 == ':': 
        n0 = 77 
    elif c1 == '[': 
        n0 = 78
    elif c1 == ']': 
        n0 = 79
    if n0 > 0: 
        n1 = n0 
        thr1 = tone_thread1()
        thr1.start() 

def inst1( n1 ): 
    global ins1 
    global chn1 
    ins0 = textbox1.get() 
    if ins0.isdigit(): 
        ins0 = int( ins0 )
    else:
        ins0 = 1
    if n1 > 0: 
        ins0 = ins0 + 1 
    else:
        ins0 = ins0 - 1 
    if ins0 < 1: ins0 = 1 elif ins0 > 128: 
        ins0 = 128
    ins1 = ins0 - 1 
    textbox1.delete(0, tk1.END) 
    textbox1.insert(0, str(ins0) ) 
    btn_inst1.focus_set() 
    out1.set_instrument(ins1, chn1) 

def volu1( n1 ): 
    global vol1 
    vol0 = textbox2.get() 
    if vol0.isdigit(): 
        vol0 = int( vol0 )
    else:
        vol0 = 127 
    if n1 > 0: 
        vol0 = vol0 + 1 
    else:
        vol0 = vol0 - 1 
    if vol0 < 0: vol0 = 0 elif vol0 > 127: 
        vol0 = 127
    vol1 = vol0 
    textbox2.delete(0, tk1.END) 
    textbox2.insert(0, str(vol1) ) 
    btn_inst3.focus_set() 

root = tk1.Tk()
root.title('midi_piano v0.2')

root.geometry("910x140")
root["bg"] = "#202020" 

root.bind( "", key1 ) 

btn01 = tk1.Button(root, text='', bg='white', command=lambda:btn1(1))
btn01.place(x=20, y=30, height=100, width=30)
btn02 = tk1.Button(root, text='', bg='white', command=lambda:btn1(3))
btn02.place(x=50, y=30, height=100, width=30)
btn03 = tk1.Button(root, text='', bg='white', command=lambda:btn1(5))
btn03.place(x=80, y=30, height=100, width=30)
btn04 = tk1.Button(root, text='', bg='white', command=lambda:btn1(6))
btn04.place(x=110, y=30, height=100, width=30)
btn05 = tk1.Button(root, text='', bg='white', command=lambda:btn1(8))
btn05.place(x=140, y=30, height=100, width=30)
btn06 = tk1.Button(root, text='', bg='white', command=lambda:btn1(10))
btn06.place(x=170, y=30, height=100, width=30)
btn07 = tk1.Button(root, text='', bg='white', command=lambda:btn1(12))
btn07.place(x=200, y=30, height=100, width=30)

btn08 = tk1.Button(root, text='', bg='white', command=lambda:btn1(13))
btn08.place(x=230, y=30, height=100, width=30)
btn09 = tk1.Button(root, text='', bg='white', command=lambda:btn1(15))
btn09.place(x=260, y=30, height=100, width=30)
btn10 = tk1.Button(root, text='', bg='white', command=lambda:btn1(17))
btn10.place(x=290, y=30, height=100, width=30)
btn11 = tk1.Button(root, text='', bg='white', command=lambda:btn1(18))
btn11.place(x=320, y=30, height=100, width=30)
btn12 = tk1.Button(root, text='', bg='white', command=lambda:btn1(20))
btn12.place(x=350, y=30, height=100, width=30)
btn13 = tk1.Button(root, text='', bg='white', command=lambda:btn1(22))
btn13.place(x=380, y=30, height=100, width=30)
btn14 = tk1.Button(root, text='', bg='white', command=lambda:btn1(24))
btn14.place(x=410, y=30, height=100, width=30)

btn15 = tk1.Button(root, text='', bg='white', command=lambda:btn1(25))
btn15.place(x=440, y=30, height=100, width=30)
btn16 = tk1.Button(root, text='', bg='white', command=lambda:btn1(27))
btn16.place(x=470, y=30, height=100, width=30)
btn17 = tk1.Button(root, text='', bg='white', command=lambda:btn1(29))
btn17.place(x=500, y=30, height=100, width=30)
btn18 = tk1.Button(root, text='', bg='white', command=lambda:btn1(30))
btn18.place(x=530, y=30, height=100, width=30)
btn19 = tk1.Button(root, text='', bg='white', command=lambda:btn1(32))
btn19.place(x=560, y=30, height=100, width=30)
btn20 = tk1.Button(root, text='', bg='white', command=lambda:btn1(34))
btn20.place(x=590, y=30, height=100, width=30)
btn21 = tk1.Button(root, text='', bg='white', command=lambda:btn1(36))
btn21.place(x=620, y=30, height=100, width=30)

btn22 = tk1.Button(root, text='', bg='white', command=lambda:btn1(37))
btn22.place(x=650, y=30, height=100, width=30)
btn23 = tk1.Button(root, text='', bg='white', command=lambda:btn1(39))
btn23.place(x=680, y=30, height=100, width=30)
btn24 = tk1.Button(root, text='', bg='white', command=lambda:btn1(41))
btn24.place(x=710, y=30, height=100, width=30)
btn25 = tk1.Button(root, text='', bg='white', command=lambda:btn1(42))
btn25.place(x=740, y=30, height=100, width=30)
btn26 = tk1.Button(root, text='', bg='white', command=lambda:btn1(44))
btn26.place(x=770, y=30, height=100, width=30)
btn27 = tk1.Button(root, text='', bg='white', command=lambda:btn1(46))
btn27.place(x=800, y=30, height=100, width=30)
btn28 = tk1.Button(root, text='', bg='white', command=lambda:btn1(48))
btn28.place(x=830, y=30, height=100, width=30)

btn29 = tk1.Button(root, text='', bg='white', command=lambda:btn1(49))
btn29.place(x=860, y=30, height=100, width=30)

btn51 = tk1.Button(root, bg='black', command=lambda:btn1(2))
btn51.place(x=40, y=30, height=60, width=20)
btn52 = tk1.Button(root, bg='black', command=lambda:btn1(4))
btn52.place(x=70, y=30, height=60, width=20)
btn53 = tk1.Button(root, bg='black', command=lambda:btn1(7))
btn53.place(x=130, y=30, height=60, width=20)
btn54 = tk1.Button(root, bg='black', command=lambda:btn1(9))
btn54.place(x=160, y=30, height=60, width=20)
btn55 = tk1.Button(root, bg='black', command=lambda:btn1(11))
btn55.place(x=190, y=30, height=60, width=20)

btn56 = tk1.Button(root, bg='black', command=lambda:btn1(14))
btn56.place(x=250, y=30, height=60, width=20)
btn57 = tk1.Button(root, bg='black', command=lambda:btn1(16))
btn57.place(x=280, y=30, height=60, width=20)
btn58 = tk1.Button(root, bg='black', command=lambda:btn1(19))
btn58.place(x=340, y=30, height=60, width=20)
btn59 = tk1.Button(root, bg='black', command=lambda:btn1(21))
btn59.place(x=370, y=30, height=60, width=20)
btn60 = tk1.Button(root, bg='black', command=lambda:btn1(23))
btn60.place(x=400, y=30, height=60, width=20)

btn61 = tk1.Button(root, bg='black', command=lambda:btn1(26))
btn61.place(x=460, y=30, height=60, width=20)
btn62 = tk1.Button(root, bg='black', command=lambda:btn1(28))
btn62.place(x=490, y=30, height=60, width=20)
btn63 = tk1.Button(root, bg='black', command=lambda:btn1(31))
btn63.place(x=550, y=30, height=60, width=20)
btn64 = tk1.Button(root, bg='black', command=lambda:btn1(33))
btn64.place(x=580, y=30, height=60, width=20)
btn65 = tk1.Button(root, bg='black', command=lambda:btn1(35))
btn65.place(x=610, y=30, height=60, width=20)

btn66 = tk1.Button(root, bg='black', command=lambda:btn1(38))
btn66.place(x=670, y=30, height=60, width=20)
btn67 = tk1.Button(root, bg='black', command=lambda:btn1(40))
btn67.place(x=700, y=30, height=60, width=20)
btn68 = tk1.Button(root, bg='black', command=lambda:btn1(43))
btn68.place(x=760, y=30, height=60, width=20)
btn69 = tk1.Button(root, bg='black', command=lambda:btn1(45))
btn69.place(x=790, y=30, height=60, width=20)
btn70 = tk1.Button(root, bg='black', command=lambda:btn1(47))
btn70.place(x=820, y=30, height=60, width=20)

label1 = tk1.Label(text='instrument :')
label1.place(x=310, y=5, height=20, width=80) 
label1["bg"] = "#202020" 
label1["foreground"] = "#FFFFFF" 
textbox1 = tk1.Entry( master=root ) 
textbox1.place( x=390, y=5, height=20, width=30) 
btn_inst1 = tk1.Button(root, text='-', command=lambda:inst1(-1))
btn_inst1.place(x=420, y=5, height=20, width=20)
btn_inst1["bg"] = "#444444" 
btn_inst1["foreground"] = "#FFFFFF" 
btn_inst2 = tk1.Button(root, text='+', command=lambda:inst1(1))
btn_inst2.place(x=440, y=5, height=20, width=20)
btn_inst2["bg"] = "#444444" 
btn_inst2["foreground"] = "#FFFFFF" 

label2 = tk1.Label(text='volume :')
label2["bg"] = "#202020" 
label2["foreground"] = "#FFFFFF" 
label2.place(x=510, y=5, height=20, width=70) 
textbox2 = tk1.Entry( master=root ) 
textbox2.place( x=580, y=5, height=20, width=30) 
btn_inst3 = tk1.Button(root, text='-', command=lambda:volu1(-1))
btn_inst3.place(x=610, y=5, height=20, width=20)
btn_inst3["bg"] = "#444444" 
btn_inst3["foreground"] = "#FFFFFF" 
btn_inst4 = tk1.Button(root, text='+', command=lambda:volu1(1))
btn_inst4.place(x=630, y=5, height=20, width=20)
btn_inst4["bg"] = "#444444" 
btn_inst4["foreground"] = "#FFFFFF" 

ins1 = 1 
vol1 = 127 
oct1 = 0 
chn1 = 0 
n1 = 60
pygame.midi.init()
out1 = pygame.midi.Output(0)
out1.set_instrument(ins1, chn1)
textbox1.insert(0, ins1) 
textbox2.insert(0, vol1) 

root.mainloop()

out1.close()
pygame.midi.quit()

 

タイトルとURLをコピーしました