MIDIスイッチャを作る:mididingsを使ってシンセを仮想化する

# はじめに

キーボーディストのみなさん、ライブのセッティングどうしてますか？

最近はソフトシンセ一本でライブに臨んだりすることも多いと思うのですが、
ハードシンセも残したいとか、この曲はこの音源使いたいとか、こだわり始めると沼ですね。

たとえば:
- 昔はワークステーション型シンセでキースプリットしてたけど、そこにソフトシンセ混ぜたいんだよなー
- ハードシンセとソフトシンセ組み合わせてスプリットしたりレイヤーしたりしたい
- メインの鍵盤とサブの鍵盤の音色の設定をスイッチ一つでセッティングをまとめてささっと切り替えたい

なーんてことは多々あるんじゃないでしょうか。

今回はmididingsというソフトを使って、複数のシンセを束ねて仮想化してみます。

![synth-virtualization.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111803/8af68a70-05de-7679-954a-634987b8e2f2.png)


たとえば、ハードシンセA+ソフトシンセをレイヤーして、
そのレイヤーしたものをハードシンセBとスプリットして使う、
なんてことが、mididingsのスクリプトを使うと簡単にできます。

#mididingsとは

mididingsはpythonで書かれた高機能なmidiパッチベイみたいなものです。
パッチベイ機能だけじゃなくて、フィルタリングやMIDIイベントのコンバートの機能などもあります。

http://das.nasophon.de/mididings/
http://das.nasophon.de/mididings/doc/index.html

たとえば、こんな風にレイヤーしたい時は

![layer-example.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111803/938213da-c9b5-ca66-cd94-9070c6b47429.png)


こんな風に書けます

```python:example-mididings-01.py

#!/usr/local/bin/mididings -f 
# -*- coding: utf-8 -*-
from mididings import *

#################################################################
# ALSAデバイスの列挙
# aplaymidi -l で表示されるALSAデバイスに対してエイリアス名をつけられる 
# この例では:
#   マスターキーボードを UM-4のポート1に
#   音源をUM-4のポート2に
#################################################################

config(
    backend = 'alsa',
    in_ports = [('MST',   '.*UM-4 MIDI 1')],
    out_ports = [('SC88', '.*UM-4 MIDI 2')],

    data_offset = 0
)

#################################################################
# OSCインターフェースの設定、
# これをやっとくと、外部プログラムからシーン切り替えができるようになる
#################################################################

hook(
    OSCInterface(56418,56419)
)

#################################################################
# フィルタの定義＆実行
#   MIDI入力を拾ってくるPortFilter('MST')を書く必要があるが、
#   MIDI入力デバイスが1つの場合は明記する必要なし
#################################################################

run(
    scenes = {
      0: Scene('layer-example',[
               KeyFilter('c0:e4') >> Output('SC88',0,program=(48)),
               KeyFilter('c0:e4') >> Transpose(12) >> Output('SC88',1,program=(48)),
               KeyFilter('f4:b7') >> Transpose(24) >> Output('SC88',2,program=(89)),
               
         ]),
    }
)



```

フィルタの実行は、以下のコマンドで行います

```

$ mididings -f example-mididings-01.py
```


MIDIイベントのルーティング/フィルタリングはlinux内でやってるのでソフト処理ですが、
ソフトシンセ上で試した限りだと16チャネルレイヤーしても遅れや途切れはなかったです。
（古いハードシンセだと、ハードシンセ側の処理が追いつかないかもしれないです）

mididingsではこのMIDIイベントのルーティング/フィルタリング処理の塊を
「シーン」という単位で管理していて、
プログラムチェンジを受け取るとシーンを切り替えてセッティングを変更する、
などといったことができます。
（この例ではシーン0にlayer-exampleという名前のシーンをひとつだけ定義してますが、
　これらを複数定義できます）



#シーンの切り替え

シーンの切り替えはMIDIキーボードから送られるプログラムチェンジの受信で行えますが、
今回はタブレットPCの画面から行いたかったので、Pythonで簡単なアプリを作りました。

ボタンを押すとOSCインターフェース(pyliblo)経由で
シーンの切り替えを指示する仕組みです。

```python:vmidictl.py

#!/usr/bin/env python

import liblo,sys
import gi
gi.require_version('Gtk','3.0')
from gi.repository import Gtk, Gdk

#####################################
class PrgChgBtnSet(Gtk.Fixed):
    """Program change button set """

    n_cols = 8

    def __init__(self, num_buttons=16,width=800,height=480):
        Gtk.Fixed.__init__(self)
        self.n_btns = num_buttons
        self.btns = {}
        self.oscif = liblo.Address("127.0.0.1",56418)

        # mk grid/frame
        self.grd = Gtk.Grid()
        self.add(self.grd)

        # mk prog.chg buttons
        for i in range(0,self.n_btns):
            self.btns[i] = Gtk.Button("Scene:"+str(i))
            self.btns[i].connect("clicked",self._clicked,i)
            self.grd.attach(self.btns[i],i % self.n_cols,int(i/self.n_cols),1,1)
            self.btns[i].set_size_request(int(width/self.n_cols),
                                             int(height/(self.n_btns/self.n_cols))) 
        # mk quit button
        self.q_btn=Gtk.Button("quit")
        self.q_btn.connect("clicked",Gtk.main_quit)
        self.grd.attach(self.q_btn,0,int(self.n_btns/self.n_cols)+1,self.n_cols,1)

    def _clicked(self, btn, ag):
        liblo.send(self.oscif, '/mididings/switch_scene', int(ag))

#####################################
top = Gtk.Window()
pcb = PrgChgBtnSet()

top.add(pcb)
top.connect("destroy",Gtk.main_quit)
top.show_all()

Gtk.main()


```

で、出来上がった画面はこんな感じ。ボタンを押すとmididingsのシーンが切り替わります
![vmidictl-capture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111803/bc56b18b-b0cc-e5f0-3799-819e8b980363.png)




#おわりに

mididings使い始めるまではmidishという別のパッチベイと
rubyを組み合わせてシンセ仮想化してたのですが、
今回作りなおしてみたら「mididings, なんでもできるやんけ!!」と関心しました。
フォーラム見てると rasberry pi に組み込んでる方なんかもいらっしゃるご様子
僕も秋葉原で仕入れた激安のWindowsタブレットに
UbuntuStudioをインストールしてmididings専用機にしてます。


できればGUIで仮想化したシンセをエディットできると完璧なのですが、
残念ながらmididingsはシーンの変更はできますが、
各シーンのMIDIフィルタの組み合わせをリアルタイムに変更することができない
(毎回再起動が必要)っぽいです。

中身はpythonなので工夫(エディットモードはMIDIイベントフックして個別にフィルタリング処理書いて、実行モード時はmididingsの組み込み関数を使うとか)すればなんとかなりそうですけど、そこはまぁまた別の機会に