
MIDIのコアエンジンは LongMessageとかMMC/MTC などのニッチ機能を除いてほぼプロトタイプが完成した。
宿敵なるは、VST のSDKだ。
本題に入る前に、まず現状について。
プロトタイピングの目標のひとつに、スクリプトエンジンの検証が項目としてあり、その実現性について少し。
スクリプトの使いどころとしては以下の3パターンを考えている。
1. シーケンサ自体のマクロ(一般的な操作の自動化などに使うもの)
2. フィルタ
3. シーケンスジェネレータ
今回検証した 3. について、以下に実際に動作する Squirrel スクリプトを掲載する。
c <- rand();
function callback(pos, range)
{
local length = 60;
for(local a=pos;a<(pos+range);a++ )
{
if (a%length==0)
{
AddNote(a, c %128, 64, length);
}
}
}
このスクリプトは、32分音符のノートを連続的に生成する。
midiom2 は以前とは異なり、バッファリング再生を基本としたエンジン設計となっているため、時間軸の未来方向へのデータ生成が可能となっている。
MIDIディレイのようなエフェクトを実現したければ、入力データを受け取るコールバック内で、未来のTick時刻に AddNote を行うだけでよい。
スクリプト内でいちいち入力データをディレイタイム分保持しておく必要がない。(まだ入力コールバックの機構は実装していないが)
CC の使われていないコントローラをVMの内部状態の遷移用のスイッチとして使うことも可能だろう。(アルペジエータのパターン切り替えとか)
MIDIフィルタの開発に仰々しいSDK引っ張ってきて開発するなんて手間とは以後おさらばできることになる。
ただひとつ心配があるとしたら処理速度だろうか。Core2DuoのCPUでは上記のスクリプトを100個くらい同時に動かしたところで1%未満のCPU使用率だったので、VM自体の動作は相当軽いものと期待できそうだ。
フィルタを簡単に作れることにどれだけニーズがあるのか知らないが、自分にとっては音楽の作り方のアプローチを変えるファクターだと思う。
プログラムやっている人に音楽やっている人はかなり多い気がするので自分だけではないと思っているが(僕のまわりだけ?)
完成のころには内部のインスタンスを直接触れるインターフェイスを提供して、便利なジェネレータAPIセットを実装して、複雑なフィルタを構築できるようになっている、はず。
VSTの開発をする場合、まずSteinberg の開発者ページよりSDKをダウンロードして、サンプルを動かす。
minihost というサンプルプロジェクトがVST のホストアプリケーションのサンプルになっている。
素直なコードで好感を持てた。成功する規格というのはこういう部分が良くできていると思う。
別配布されている ASIO SDK もサンプルをビルド。
サイン波にアンプモジュレーションをかける生成関数を書いてバッファに渡してみるとちゃんと音が鳴る。
ASIOはVSTよりもかなり簡単。内部でスレッドを立てているらしく、コールバック内でアクティブでないバッファにデータを書き込むだけの単純な構成となっている。
DirectXAudioとはだいぶ毛色が異なっている。
ロードしたVSTEffect に ASIOのバッファサイズ分のデータを要求してリングバッファに書き戻してやると、音が鳴った。
難しい印象を持っていたが、特に信号処理の深い知識も必要とせず、肩透かしを食らった気分。
おそらく大変なのはこの先のSDKではサポートしてくれない部分だろう。
midiom のエンジン部となる、EMIDI(エミディ)というライブラリを先行して開発している。
EMIDIはそれ自身でスレッドを生成し、MIDIシーケンスの再生のスケジューリングや、データの編集、モジュールの結線、ASIO/VSTのホスティング、物理デバイスのモジュール化、シリアライズ機構など、シーケンサに必要な GUI 以外のすべての機能を保有している。(することになる)
この手のシーケンサは時間軸の単位が何種類も共存することが混乱の元となる。
必要な時間表現を考えてみると、
MIDIはTickという拍子や拍などに依存した時間軸で表現し、Tickを制御するためにテンポという時間表現を用い、もちろんms単位の時間軸も必要であり、オーディオを扱いだすとサンプルというこれまた周波数によって可変の時間軸が入ってきて、さらにMTCでは SMPTEで時間をフレームで表現しなければならなくなり、そしてこれらすべてをCPUの周波数やOSのタスクスイッチに乗っかって扱わなければならない。
同期祭り。
これら時間にかかわるすべての処理はリアルタイムでなければならないのだが、もうひとつ即応性というものが要求される。
バッファリングは、ストリーム安定化の手段として用いられる。
「どうせ遅れがあるんなら一番遅いやつに合わせてみんなで手をつないでいこうぜ」
このことでリアルタイムな再生環境が作られるのだが、あくまでもこれは再生だけである。
録音をするときは、これらバッファリングされた(つまりは遅延して再生している)演奏にあわせて録音するわけで、快適な録音機能を実装するためには、バッファリング再生しつつ、入力されたデータは即座に処理しなければならない。MIDIのフィルタをひとつ考えても、バッファリング再生のために先読みしたデータにフィルタをかけることができる、かつ、入力されたデータは即座に処理しなければならない
ASIOのレイヤーでは必ず一定値の遅延があるため、VSTiを演奏する場合は低レイテンシーのオーディオデバイスが必要となる。
EMIDI側でこの遅延にあわせて補正をかけるかどうかは選択できるようにするのが良いと考えている。
スレッドが三つも回っているため、なかなか動いてくれなかったが。同期コードをはさみ何とか手元にあるいくつかのVSTiで演奏ができた。
再生した音楽データは、上記の squirrel スクリプト。
複数VSTiの起動も成功した。
VSTi / ASIO は面倒な実装がまだまだ残っていそうなのだが、とりあえずここでいったん終了。
なんかごちゃごちゃあるオペレーションとかは根気が必要そうだ。
VST SDK に付属ののサンプルは必要十分だけども、アプリケーションのレベルに持っていくにはやることが相当あるという結論。
低レイテンシのオーディオアプリとか仮想音源とかが使えるようになるという点に対しては十分価値があると思う。
複雑性と直感性を共存させたこれらの楽器は、デジタル楽器の楽しさを再認識させてくれるだろう。
世界中のほとんどデジタル楽器を制御できるMIDIは、こういった新しい楽器の中にも十数年間ほとんど形を変えず、いまだに動脈として機能している。
MIDIに関しては今も昔もなにも変わっていないが、コンセプトやアイデアが良いとこんな製品がでてくるものだと感動した。
そこで、最近こっそり開発を進めている midiom2について初露出したいとおもう。
midiom の次バージョンの構想が固まりつつある。
そのコンセプト概要を以下書き連ねていこうと思う。
これはmidiomのコンセプトであり、意味するものはデータリストの編集のみで楽曲を構築するポテンシャルを持つことである。
midiom version1 では設計上の限界に直面したため、プログラマブルな側面は中途半端なものとなってしまった。
時系列のMIDIデータとC言語などのような手続き処理系の言語の多くの共通点から、データリスト上にロジックを閉じ込めることができるのではないかと考えたのが事の発端であったが、そもそもその前提が間違っていたように今となっては思う。
一番大きな間違いは、MIDIの時系列のデータの連続性と、手続き処理の連続性を混同していたことだ。
N88BASICの時代からDATA文があるように、データ構造とアルゴリズムは分離されるべきである(このあたりの解決策に関してはFlashの実装が参考になる)。データ中にロジックを記述するというのは、N88BASICのDATA文中にマシン語を記述するようなもので、書きやすいはずがない。しかも、midiomは単純な命令しか提供できなかったので、機能はあるが、利用価値がないという状態に陥ってしまった。
そこで、次世代のmidiomでは、データとロジックを完全に分離する。多くのアプリケーションがマクロ機能などでロジックを分離し、データに対してロジックを適用するというアプローチをとっているように、バーチャルマシンをシーケンサの要所に取り込む。
VMには、実行速度の面と組み込みの容易さから squirrel を候補としている。もちろんRubyなどの贅沢なスクリプトを組み込むポテンシャルも持ち合わせる。
(MMLに関してはこれ自体処理系ではなくてデータであるため、違うアプローチが必要であるため、採用するかどうかは未定である。)
モジュールとして振舞うオブジェクトは、データの入力と出力をサポートし、シーケンサ内でほかのモジュールと結合する。外部とのデータのやりとりはMIDIポートをモジュール化したオブジェクトを利用することとなる。適度に抽象化されたMIDIデータはシーケンサ内を縦横無尽に飛び回り、生成され、加工され、フィルタリングされポートに届けられる。このことによって、アルペジエータやスライサーなどといったMIDIエフェクトが実現可能となる。アルペジエータやスライサーの実体は、スクリプトの VM モジュール、つまり、スクリプトで記述されたフィルタプログラムである。
フレームは DAW で実現されているようなトラック上に配置されるデータの塊である。
midiom version1 では、すべてのトラックは 0 Tick から開始するため、データのシフトをする場合、余分な余白データをデータの先頭に挟まなければならなかった。また、シーケンスは分離不可能であったため、データ途中のある区間のみをシフトしようとする場合の操作は煩雑であった。
フレームは、モジュールと同様に多様性を許容するので、トラックに配置できるフレームは何もMIDIシーケンスに限らない。この部分はmidiom2のキモとなる。このアイデアによって自動作曲なんかが簡単に行えるようになるかもしれない。
ACIDのようなデータ操作ができるようにすること
また、Kaossilatorのようなリアルタイムループレコーディングができるようにすること
まだ何も手をつけていないに等しいので、確定はできないが、VSTiをホスティングして動かすことは検証段階で確認済み。ただし、隙のない実装ができるかについては、コストとの相談となる。
VSTi対応がなくても、MIDI Yokeなどの内部結線で、フリーのホストアプリケーションと組み合わせてるだけでよいのだが、同じフレームワーク内で利用できることが、モチベーションに影響すると考えている。
スクラッチを起こすのに使用するアプリケーションはひとつで済ませたい。セーブデータもひとつでよいので簡単に復帰できるわけだし利便性は高い。
数値入力シーケンサという意味では必要ではないが、ライブでDJが開いているPCの中で動いていても様になるレベルを目指す。見た目も、ライブでの実用性も。
GUIは思い付きではなくてデザイン作業からちゃんとやろうと思っている。
かっちょよくないツールなんて使いたくない。
ただし、かっこよくなくてもいい。
フルスクラッチで書き起こしているエンジン部は現段階で version1 の何百倍ものパフォーマンスを発揮している(当社比)。データがどれだけ大きくても(たとえば100万ステップのシーケンス)エンジンは耐えてくれる。あくまでもエンジンの話ではあるが。
スクリプトの実行環境を提供する上で重要なのは軽いことである。重いスクリプトを実行する場合、CPU資源をフィルタ処理に集中させることができる。
また再生時の遅延についても解決策を用意している。スクリプトの実行に1msのレイテンシが存在していても、データの生成速度がその処理速度を追いこさない限り、その環境でベストなタイミングで(といっても1msがMIDIの限界だが…)演奏をしてくれる。モジュールやスクリプトの遅延に引きずられることはない仕様となっている。
これも midiom のコンセプトである。
どこまで貫けるかは一考しなければならない。
ユーザーインターフェイスは確実に以前より複雑になるので、考えたら吐きそう。
GUIプログラミング(デザイン・設計以外の実装)はスポーツである。要求されるスキルは根性が主である。