Author
コイデマサヒロ
今回はRaspberry Pi上のソフトシンセなどの既存のMIDI対応ソフトウェアをGPIO経由かつMIDIでコントロールすることを考えます。サンプルとして簡易的なMIDIコントローラ的なものを作ってみます。
今回のポイントと注意事項
技術的なポイントとしては、
- Raspberry PiのGPIOの入力をPythonで受け取る。
- Raspberry Piにはアナログ入力が無いので、ADC(アナログ・デジタル・コンバーター)のICを使ってアナログ入力を実現します。
- PythonとソフトシンセをMIDI接続。
というところです。Pythonを選んだのは、Raspberry Pi上の開発ではよく使われているからです。スクリプトベースなら非プログラマな自分でも使えるかもという思惑もあります。
【注意事項】プログラムソース、回路全て公開しますので再利用いただいて構いませんが、あくまでもテスト用であり、ある程度の汎用性は考慮しているものの実用的なものではないことはご承知おきください。
回路図(配線図)
こんな感じの回路を組みました。
可変抵抗でMIDI CCをコントロールします。2つのタクトスイッチはオマケ的なもので、プログラムチェンジ切替用としています。左右のどちらからのボタンを押すと現在のプログラム番号から±1したプログラム・チェンジを送信します。シンセやMIDIコンによくある機能ですね。
実際にブレッドボード上に回路を組んだ様子がこちら。継ぎ足しで組んでいったので効率的な配置になっていませんが...
回路の説明
Analog to Degitalコンバーター(ADC)を使ってアナログ入力を実現
Raspberry PiのGPIOには残念なことにアナログ入力がありません。そこで専用のICを使って機能を実現します。今回はよく使われるMCP3008というICを選択しました。SPI方式でRaspberry Piと接続します。
MCP3008のアナログからデジタルへの変換の解像度は10bitです。MIDIのコントロール・チェンジは基本8bitですのでこれで十分です。またMCP3008のアナログ入力は8chあります。今回は2つしか使いませんが、ツマミを8個使えるわけですから、実用的にも問題ないでしょう。
配線図では省いていますが、MCP3008の使わない入力ちゃんねるはGNDに落としておいた方が良いです。
スイッチはデジタル入力
タクトスイッチは、デジタル入力用なので、直接GPIOピンに接続しています。プルアップ/プルダウンの抵抗がありませんが、Raspberry Piの機能を使って内部的にプルアップします。
ソフトウェアのセットアップ
使用するライブラリ
PythonでMCP3008の値の取得するために、
というMCP3008用のライブラリを使います。このライブラリで取得したデータをMIDI化してソフトシンセなどに渡したいわけですが、今回は、以下のライブラリを使います。
rtmidiという有名なMIDI IO関連のC++用ライブラリをPythonから使うためのバインディングです。当然rtmidi本体も必要です。apt-getでもインストールできますが、Jack関連の依存関係がjackd1の方になっており、jackd2が強制的にアンインストールされてしまいます。これは避けたいため自前でインストールします。
ソフトウェアのセットアップ
ホーム直下に任意のフォルダを作ってターミナルで作業を行います。
メインで使うライブラリで必要になるツール、ライブラリ類をインストールします。
$ sudo apt-get install libasound2-dev git build-essential python-dev libpython2.7-dev libpython3.4-dev libjack-jackd2-dev cython
もしそれぞれのライブラリのインストール時にまだ必要なライブラリが無いといわれたら適時インストールしてください。
rtmidiのビルドとインストールします。
$ wget http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-2.1.1.tar.gz $ tar zxvf rtmidi-2.1.1.tar.gz $ cd rtmidi-2.1.1 $ ./configure $ make -j2 $ sudo make install $ cd ..
Python-rtmidiをインストールします。最新版をgitから落としてきます。
$ git clone —recursive —depth 1 https://github.com/SpotlightKid/python-rtmidi.git $ cd python-rtmidi $sudo python setup.py install
examplesフォルダ以下にサンプルがありますので使い方の参考になります。
MCP3008用のライブラリをインストールします
$ cd .. $ git clone https://github.com/adafruit/Adafruit_Python_MCP3008.git $ cd Adafruit_Python_MCP3008 $ sudo python setup.py install
最後にOSの設定を行います。
設定ツールを起動して 、
$ sudo rasps-config
Interfacing Options -> SPI -> Yes
として、デフォルトではSPIがオフになっているのでオンに変えます。これでMCP3008が使えるよう(SPIが有効)になります。
ソースコードと利用法
以下にソースを示します。Python2.7用です。難しいことはしていませんのでコメントをみていただければ理解できると思います。(節操の無いコメントの書き方でスミマセン)
# -*- coding: utf-8 -*- # GPIO to MIDI テストプログラム # 必須ライブラリ:rtMIDI python-rtmidi # (C)2017 ART Teknika Inc.(Masahiro Koide) # 引数:a b c d -> midi ch cc1 cc2 pgnum import time import rtmidi import sys # Adafruitのライブラリをインポート import Adafruit_GPIO.SPI as SPI import Adafruit_MCP3008 # GPIO import RPi.GPIO as GPIO # ハードウェアSPIを利用する SPI_PORT = 0 SPI_DEVICE = 0 mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE)) # MIDI関連設定 midiout = rtmidi.MidiOut() midiout.open_virtual_port("GPIO2MIDI") # 仮想MIDIポートの名前 #初期値指定 midich = 0 # MIDI Channel (0~) cc_no = [7, 10] # Control Change midi_prog = 0 # Program No.(0~) midi_data = [0]*5 # 引数要;1番目は無視, Midi ch, CC Number1, CC Number2, Program Number argc = len(sys.argv) if argc == 5 : for num in xrange(1, 4) : midi_data[num] = int(sys.argv[num]) if midi_data[1] >= 0 and midi_data[1] <= 15 : midich = midi_data[1] # コマンドライン引数の1番目をMIDI Chとして if midi_data[2] >= 0 and midi_data[2] <= 127 : cc_no[0] = midi_data[2] # コマンドライン引数の2番目をCC番号1として if midi_data[3] >= 0 and midi_data[3] <= 127 : cc_no[1] = midi_data[3] # CC2 if midi_data[4] >= 0 and midi_data[4] <= 127 : midi_prog = midi_data[4] #コマンドライン引数の3番目はプログラム・チェンジ send_cc = 0xb0 + midich # midi Status Byteの計算 send_prog = 0xc0 + midich #プログラムチェンジを送るMIDI CH pre_value = [0]*2 #値比較用 # おもなMIDIステータスバイトについて # 1000nnnn (0x8m): Note On # 1001nnnn (0x9m): Note Off # 1011nnnn (0xBm): Control Change # 1100nnnn (0xCm): Program Change # nnnn/m = midi ch # GPIOの初期化 # ボタンでプログラム・チェンジの増減とする GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP) # inc (プルアップで初期化) GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP) # dec print('*** GPIO to MIDI Test ****') print('Press Ctrl-C to quit...') # 起動時にプログラム・チェンジを送信 midiout.send_message([send_prog, midi_prog]) print 'Program Change:',"{0:03d}".format(midi_prog) # --- 初期化終わり # --- メインループ try: while True: # ノブの読み取り for i in xrange(2): value = mcp.read_adc(i) / 8 # ADCの値を取得しつつ0-127の範囲に丸める if pre_value[i] != value : cc_data = [send_cc, cc_no[i], value] # MIDI OUTするデータ midiout.send_message(cc_data) # MIDI OUTする pre_value[i] = value # 送信するデータを表示 print '0:CH',"{0:02d}".format(midich),':CC',"{0:03d}".format(cc_no[i]),':Val',"{0:03d}".format(value) # read switch status if GPIO.input(17) == 0 and GPIO.input(18) != 0 and midi_prog < 127 : midi_prog += 1 midiout.send_message([send_prog, midi_prog]) print 'Program Change:',"{0:03d}".format(midi_prog) if GPIO.input(18) == 0 and GPIO.input(17) !=0 and midi_prog > 0 : midi_prog -= 1 midiout.send_message([send_prog, midi_prog]) print 'Program Change:',"{0:03d}".format(midi_prog) # Pause for half a second. time.sleep(0.1) except KeyboardInterrupt: pass GPIO.cleanup()
実行すると「GPIO2MIDI」という名前の仮想MIDIポートを作成します。AlsaからもJackからもアクセス可能です。Jackからはrtmidiのポートとして見えます。
この仮想ポートとソフトシンセなどを接続すれば良いわけです。
今回は汎用性を考えて仮想MIDIポートを作成していますが、直接ソフトシンセのポートを指定することも可能です。
プログラムは、
$ python adc2midi.py x1 x2 x3 x4
と引数を付けて実行します。引数がない場合は、プログラム内で設定されているデフォルト値になります。引数を指定する場合は、全引数を指定します。
引数は以下。
x1:ボタンやノブのMIDIチャンネル(0-15)
x2:MCP3008のチャンネル1の可変抵抗に割り当てるMIDI CCの番号(0-127)です。
x3:MCP3008のチャンネル2の可変抵抗に割り当てるMIDI CCの番号(0-127)です。
x4:プログラムを実行すると最初にプログラム・チェンジを送るようになっています。そのプログラムチェンジ番号です。
動作確認
実行してノブを回したり、ボタンを押すと仮想ポートから出力されるMIDIメッセージがターミナルに表示されます。表示されない場合は回路が正しく組めているか確認してください。ラブラリが足りなかったりするとエラーでプログラムが終了してしまうと思います。エラーメッセージで足りないものが判別できる(エラーメッセージでググる)場合が多いです。
さらに実際にソフトシンセなどと接続して動作を確認して見てください。
まとめ
低機能なMIDIコントローラーとしてとして使う分にはこのままでも、ちゃんとした基板で回路を組めば実用に耐えると思います。8個までノブを増やしても大丈夫でしょう。
しかし、いろいろな機能をもつ複雑なプログラムの中で使うのであれば根本的に仕組みを見直したほうが良いかもしれません。例えば、このプログラム内では、ノブを止めている場合はMIDI出力しないようにしていますが、実際には常にノブの動きを監視している状態です。本来であれば割り込みなどを使って、ノブが動いたときだけイベントが発生するというような作りにすべきかもしれません。あるいはRaspberry Piであればマルチスレッド処理にする方が良いかもしれません。
また、タクトスイッチはチャタリング対策をしていないので、より実用的にするなら、誤動作を防ぐためにもしておいたほうが良いでしょう。対策方法としてはソフトウェア的なアプローチ、ハードウェア的なアプローチがありますが、それは別の機会にも。
はじめてPythonを使ってみましたが、スクリプトベースでこのくらい気楽に簡単にできるのは良いですね。ちょっとした回路を組んで数行のコードだけでオリジナルなMIDIコンが簡単に作れそうです。是非試してみてください。
コイデマサヒロ
ディレクター、プロデューサー、ギタリスト。mimiCopyをはじめ弊社リリースの全てソフトウェア製品の企画を担当。ネイティブアプリ開発がメインでOS問わず対応可。dubバンドのギタリストとしても活動中。