オーディオプラグインとプロセス分離に関する覚書

ここ数日ばかり、DAWのオーディオエンジンがあまりプラグインをプロセス分離でロードしていないのは何でだろう?というのが気になっていて、いろいろ調べていたので、覚書としてまとめてみる。

目次

プロセス分離モデル

DAWというものは安定して動作させるのが難しいソフトウェアの類型である。その最大の理由はオーディオプラグインを動作させる方式にかかる制約にある。

DAWは、オーディオプラグインのホストとして、さまざまな開発者によって提供されるオーディオプラグインを、エンドユーザーのさまざまなプラットフォーム上でロードして実行する。オーディオプラグインの開発者の視点で言えば、エンドユーザーはさまざまなDAWを使用して、さまざまなプラットフォーム上でそのプラグインをロードして実行する。

一般的に、このようなプラグイン機構においては、プロセスを分離して、プラグインのプロセスがクラッシュしても、ホスト側はアプリケーションを継続して実行できるようにするほうが安全だ。このようなモデルはアウトオブプロセス実行と呼ばれる(アウトプロセスと呼ぶ分野もあるようだ)。Webブラウザプラグイン機構やIDEプラグイン機構には、このモデルを採用するものが少なくない。実際には、諸条件を考慮して同一プロセスの中でライブラリを動的にロードして実行することもある。このようなモデルはインプロセス実行と呼ばれる。

リアルタイム制約

オーディオプラグインの場合には、問題を複雑化する要因がある。オーディオプラグインのパイプラインはリアルタイム優先度で処理されなければならないので、オーディオプラグイン自体がリアルタイム優先度で処理を完了できなければならないし、パイプライン処理にも時間をかけすぎることはできない。

しかし現実には、DAWはかなりの数のオーディオプラグインを処理しなければならない。オーディオトラックが50、100とあるような本格的な楽曲になると、オーディオプラグインが数百件処理されることにもなる。AppleWWDC 2019では最新のMac ProのデモでLogic Pro上で1000トラック作成してKontaktを割り当てて再生するものがあったらしいが、1000トラックもあればさすがに一般的な音楽制作では十分すぎるほどだ。しかし1000という数字が圧倒的ということは、ひと桁減らして100トラックではデモとして足りないということでもある。いずれにしろ、DTMは業務用チーズシュレッダーを使うことができない一般大衆のためにあるべきだ。実際の制作作業ではトラックのフリーズ等の処理を施しながら作業することでパフォーマンス上の問題を回避するのが一般的だと思われるが、オーディオプラグインのパイプラインにシビアな処理速度が求められることに変わりはない。

インプロセスでオーディオパイプラインを処理する場合は、単にリストを辿りながら単純なループでプラグインの処理を実行するだけで足りる。しかしアウトオブプロセスで処理する場合は、呼び出しと応答をプロセス間通信のようなかたちで連結しなければならないことになる。単純なプロセス間のコンテキストスイッチでもそれなりのコストがかかるし、IPCのフレームワークも厳しく選択しなければならない。

CLAPというオーディオプラグイン機構が考案されたときにhacker newsに寄せられたコメントのひとつで、このプロセス分離とパフォーマンスの両立が難しいという問題が挙げられていて、これがやや詳しく解説していたので、参考までに言及しておきたい。1プラグインプロセスの切り替えにつき6-7μsかかるというのはそれなりのインパクトがありそうだ。

リアルタイム処理で重要なのは、遅延が無いという意味での安定的な処理モデルだが、処理速度の観点を無視できるということはなく、パフォーマンスが良いほどより多くのオーディオプラグインを処理できるという利点はある(前述のとんでもない処理速度をもつMac Proの利点はここにある)。

これがもしネットワーク越しに行われるとなるとさらに大規模な遅延が生じうる。YAMAHAのNetDuettoなどはこれを実現しているということになろうが、一般的には同じマシンで動作していることがほぼ前提であると考える必要がありそうだ。Web経由でこれを処理できるようになる状況は、まだまだ期待できなそうに思えてしまう。

2019/06/08追記: Bitwig Studio 2.5には、オーディオプラグインごとにプロセス分離モデルを選択できるオプションがある。

f:id:atsushieno:20190608001806p:plain

アプリケーションの制限されたリソースアクセス

デスクトップPCの環境においては、まだアプリケーションの動作環境に対する制約は小さい。DAWなどのオーディオプラグインホストとVSTなどのオーディオプラグインは別々のベンダーが開発するものであり、ホストのプロセス内部にオーディオプラグインのライブラリをロードして実行するのはごく一般的だ。

しかしモバイル環境におけるリソースのアクセスは、デスクトップ環境ほど自由ではない。一般的には、アプリケーションのそれぞれには、コンテナのようにアクセス制限が課されていて、他のアプリケーションのリソースには自由にアクセスできない。DAWのアプリケーションが、他のオーディオプラグインのアプリケーションに含まれるライブラリを動的にインプロセスにロードして実行することができないとなると、必然的にオーディオプラグインとのやり取りもアウトオブプロセスで行われることになる。

また、デスクトップOSであっても、macOSにはアプリケーションのサンドボックス実行という概念があり、主にMac App Storeで配信されるアプリケーションに課せられる制約だが、一般的には、サンドボックス下にあるアプリケーションには、PC上のファイルやリソースに無制限にアクセスすることはできない。他所のアプリケーションの共有ライブラリをロードして呼び出すことも無条件にはできないし、ライブラリだけをロードできたとしても、別のアプリケーションのリソースにアクセスすることはできない。

この関係で面白いのはmacOSiOSを同様にサポートしているAppleのAudioUnit v3(以下単にAU、v3が前提)なのだけど、長くなるので節を改めて続ける。

AudioUnit v3のアプローチ

AUAppleプラットフォームで(プラットフォームのレベルで)サポートされているオーディオプラグインのための仕様だ。OSの一部なのかよ!というのが多分正しいリアクションだが、OSの機能が拡張される理由の一因になっている側面はありそうだ。というのは、macOSのセキュリティ機構の一部であるアプリケーション サンドボックスと、オーディオプラグインのようなプログラムは、先に軽く言及したとおり相性が悪いので、何かしらの対応策が必要になったからだ。

AUホストとなるアプリケーションをMac App Storeで配布する場合にはサンドボックス環境をサポートする必要がある。サンドボックス環境では、外部のアプリケーションとなる従来のAUではロードできない。このため、AUはv3で任意のアプリケーションでロードできるApp Extensionとして実装することになった。

App Extensionの動作原理については、ある程度のレベルまではUnderstand How an App Extension Worksというドキュメントに詳しくまとめられている。また、AU v3については、WWDC 2015におけるセッション資料が詳しい。

AUホストは、AUをアウトオブプロセスで動作させることもインプロセスで動作させることもでき、アウトオブプロセスで動作する場合は、XPCと呼ばれるプロセス間通信の仕組みに基づいてメッセージングが行われるようだ。ホスト側はkAudioComponentInstantiation_LoadInProcessを、AU側はAudioComponentBundleの内容を調整するかたちで設定する。アウトオブプロセスで動作する場合は、レンダリング処理1つあたり40μsくらいまでのレイテンシーが加わるようだ(macOSでの話と思われる)。

インプロセスで動作させることが可能になっているというのは、App Extensionという特殊な機構に基づいているがゆえの特権である、といえそうだ。Appleは自らGarage BandやLogic Proもリリースしているので、低レイテンシーオーディオにOSのレベルで積極的にコミットしていて、AU v3の仕様はこの現れであるといえる。

AUホストやAUプラグインの開発については、特にサンドボックスとの関連も含めて、以下のドキュメント群が詳しい。

もっともプロセス分離モデルに関する詳細な解説は見当たらなかった。インプロセスのAUがどのようにサンドボックスを実現しているのか、あるいはリソースにアクセスできてしまうのか、といった疑問は解決されなかった(試せばわかることだとは思うが、普段Mac上で活動していない筆者にはあまり本気で調べる意思が無い)。

AUサポートに関しては、macOSiOSで大きな違いは無さそうだ。OSによるセキュリティ機構としてもおそらく大きな違いは無いのだろう。

VST3とsandboxing

ちなみに、こういったmacOSの状況を鑑みると、「ということはVST3もAUみたいな機構を実装しているのか…?」という疑問も浮かんでくる。AUサンドボックスの都合でApp Extensionとなることを強いられたのだとしたら、VST3も同様にインプロセスにアクセスできるApp Extensionとして設計される必要があるはずだ(これがユーザーランドで可能なのかどうかはわからないが)。

どうなっているのか…と思って"App" "Extension" でvst3sdkのソースツリーを検索してみたが、出てきたのがAddVST3AuV3.cmakeというcmakeモジュールくらいであり、これはiOSビルドにのみ適用されるようだ。

sandboxingせずにsandboxed hostでプラグインとして使用することは出来ないだろうし、sandboxed hostで使えるようにすることは諦めているのだろうか…と思ったところでふと気付いてApp StoreDAWを検索してみると、Logic Pro以外はほぼ無いという状況だった。音楽アプリケーションがApp Storeで公開されていないのであれば、いくらApp Storeでsandboxingを強制しても無駄なことだ。Logic ProではもともとAUしか使えないわけだし、MacをサポートしているオーディオプラグインはほとんどVSTにもAUにも対応しているし、VSTホスト(DAW)のベンダーにとってはVSTをサポートしているだけでLogic Proに対して優位にあるといえるし、App Storeに置けないことにはほぼデメリットがない。

Androidのアウトオブプロセスによる処理の可能性

iOSと同様にアプリケーションごとに異なるLinuxユーザーアカウントを作成しているAndroid環境で、サンドボックスAUのようなことは実現できるのか。筆者が現時点で思いつく範囲では、以下のような課題がある。

(1) オーディオプラグイン同士のインタラクション: オーディオパイプラインとプラグインホストの間で渡されるオーディオバッファへの読み書きが問題なく行えなければならない。これは共有メモリへの読み書き(shm, ashmemなど)でおこなれそうだ。ashmemはandroid-15以降でサポートされている(Java APIのSharedMemoryは27以降のみ)。

(2) リアルタイム処理可能なIPCのサポート - Androidは8.0でBinder IPCのリアルタイム優先度を実現しているようだ(ただしユーザーに開放されているかは未確認)。リアルタイム優先度のbinderメッセージングは、native MIDI APIの実現のためには必須だったと考えられるが、AAudioにおけるlow latencyモードのオーディオ処理でも必要だったと考えるのが妥当そうだ。

いずれにしろ、この辺りの課題をクリアできれば、AndroidでもAUのようなアウトオブプロセスのプラグイン機構が実現できるかもしれない。

6/12追記: ELK(Linuxベースのオーディオ用リアルタイムOS)の開発者が今年になって書き始めた一連のブログ投稿(現時点で未完成)で、今回のネタに関連するLinux方面での取り組みなどがよくまとめられている。

5月のmusic tooling hacks

あたし5月って苦手なのよね…無駄に爽やかだから…あと天気が変わりすぎて肌荒れしちゃうし。

戯言はさておき、5月に読み書きしていたコードの話を雑に書きます。

JUCE/VST3

VST3まわりをいじって音が出せるようになっていたJUCEですが、とりあえず試しにtracktion_engineで使われているやつでパッチビルドを使ってみたらちゃんとPlaybackDemoで再生するところまでいけたので、これだけでも使いたい人はいるかもしれんと思って結局PRにして投げるだけ投げました(ROLIはPRを受け取らないと表明しているはずですが)。

VSTまわりを少し調べてわかったのですが、AudioPluginHostでmda-~なプラグインのUIが表示されるのは、VSTGUIをサポートしていない場合にJUCEがVST3プラグインのパラメーター情報などをもとに最低限?のUIを表示しているようです。おそらく同じことが「VST3をサポートしている」Bitwig Studioについても言えるのではないでしょうか。VST3で作成されたプラグインとしてLinux上でGUI表示までサポートしているものを知らないので(何しろVST3をサポートしているホストを日常的に使っていないので!)、VST3に深入りすることがあればもう少し調べてみるか…みたいなステータスです。個人的にはGUIに行くより前に調べることがいろいろあるので…

LV2, lilv, lilv-sharp

VST3方面が(主にupstreamが)あまり芳しくないので、5月は少し方向性を変えてLV2を調べたりしていました。LV2というのはLADSPA v2のことです。要はLinuxネイティブで動かせるプラグインに手を出しておこうと思ったわけです。実のところLV2はLinux方面でもそんなに使われていないので、こっちに手を出す意味がどれくらいあるかは怪しいところですが、今知っておけば日本の第一人者に近い立ち位置になれるなーというお気持ちで調べたりしています。

http://lv2plug.in/

個人的には、Chrome OSLinuxALSAをサポートし始めた2019年から、米国の教育機関ではマーケットシェア60%にまで至るこのOSで動作するオーディオプラグインをサポートする流れは確実に出来ていくだろうと思っているので、この辺に手を出すなら早いほうがいいと思っています。

そんなわけでLV2ですが、(おそらく日本で知ってる人はほとんどいないと思うので)ゼロから説明しないと分かってもらえないところだとは思うのですが、これを真面目に説明しようと思うと中で使われているRDFやらTurtleやらいろいろめんどくさいところから始めなければならず(この辺が参入障壁を無駄に高くしている要因だと思う)、このエントリでそこまでする気には到底なれないので、いずれ暇を見て何か書くと思います。

とりあえず、いま興味があるのはオーディオプラグインホスティング方面なので、LV2のホスティング実装であるところのlilvで遊んでいます。lilvは純粋なCライブラリなので(LV2のエコシステム自体が割とそうなっている)、C#バインディングを作ったりしていました。P/Invoke部分は例によってnclangを使ってほぼ自動生成です(ちょいちょいコマンドラインオプションでごまかしている部分がある)。

github.com

nclang/generated-natives

lilv-sharpを作っているときにnclangをいじっていて気付いたのですが、MSにもClangSharpというclangのバインディングがあって(これ自体は知っていたのですが)、nclangと同様にPInvoke自動生成ツールが付いているんですね。ClangSharpはそれ自体がlibclangのバインディングをコレで生成していて、dogfoodingとしてはなかなか良いと思うのですが、nclangでもそういうブランチを作って移行してみようと思った結果「これはあんま意味ないな…」となって放置しています。libclang自体はAPI後方互換性があるので現状ほとんど困らないですし。むしろClangSharpは自動生成APIを用意しているだけで、生成されたAPIが.NETらしく便利に使えるものになっているとは言い難いので、ClangSharpは使わずにnclangを使い続けようと思いました。

.NET開発はほぼ継続不能状態

ただ、最近はそもそも.NET開発をほとんど中止している状態です。というのはmsbuildLinuxパッケージングが全然まともにメンテされていなくて使い物にならないからです。実際にはdotnet/source-build(dotnet関連プロジェクトのビルドに使われているビルドツール)の細かい問題と、msbuildスクリプトが参照できないアセンブリを見に行って失敗する問題と、msbuildのビルドにバンドルされているRoslynがちょっとしたプロジェクトのビルドでも簡単にクラッシュしてビルド自体が続行できないようなレベルになっている問題など、いろいろ累積的で、.NET Coreチームがまともに仕事していないことに起因しているのですが、とりあえず諸方面で状態を問い合わせるだけでうんざりしたので、ビルドが正常化するまで放置することにしました。MSBuildが開発のエコシステムに食い込んでいなければMSBuildを捨てればいいだけの話なのですが、Riderとか使っているとビルド時にはMSBuildを呼び出すんですよね…

https://github.com/atsushieno/managed-midi/issues/42

あと開発を単純に放置できる話であればよいのですが、MIDI関連ツールはわたしが日常的に使っているものなので、こんなリスクをかかえたまま開発を継続することはできないので、他の作業が落ち着いたらC++にでも移植することになると思います。managed-midiの一部はKotlinに移植済なのですが、unsignedがまだ標準でサポートされていないKotlinで開発を継続するのはイマイチだなと思って躊躇しているところです。CLionのKotlin/Native・MPPサポートも特にライブラリ開発に関しては現状ほぼ無いので。

幸いなことに、modern C++はそれなりに面白そうなので、C#とあまり変わらないノリで開発できるんじゃないかという気もしています。C#でもP/Invokeとの絡みでリソース管理を細かく気にするようなコードばかり書いていたので。むしろC#のほうがいろいろpinnedにしないといけない分かえってめんどくさかったりして。lilvのPoCコードを書いているときも、lilv-sharpではこの関連で無駄にいろいろハマりました。lldb + monobt.pyでデバッグしてもいろいろ実行時情報不足だったりするし…。この辺C/C++でPoC書いたら1日で済んだので、最初からこっちでやろうと思うようになりました。

反省と展望

そんなわけで今後はC/C++方面に進んでいくんじゃないかなと思っています。知らんけど。

6月こそはもう少し音楽打ち込み作業に時間を確保したい…(フラグ)

Android AMidi (Native MIDI) APIについて

概要

Android Qで新しく追加されるNative MIDI APIは、Android NDKを利用してビルドされるネイティブコードで、Androidシステム上に登録されている「MIDIバイス」にアクセスするためのAPIだ。ネイティブコードで記述することで、ある種のMIDI関連タスクの処理負荷が軽減され、リアルタイム性が向上することが期待される(「リアルタイム性」についてはこの過去記事などを参照)。

Native MIDI APIはC APIとして公開されており、amidi/AMidi.hをインクルードして、libamidi.soを共有ライブラリとして参照してビルドする。毎回Native MIDI APIと書くのは長いので、以降はAMidiと記す。

Android MIDI API (Java) のおさらい

Android MIDI APIAndroid 6.0(Marshmallow)で追加された。AndroidMIDIバイスには、USBデバイス、BLEデバイス、ユーザー/アプリケーション実装によるMIDIバイスサービスの3種類がある。よくiOSMIDIバイスとして販売されているBLE製品は、Appleの仕様に基づいてMMAMIDI Manifacturers Association)で規定されたMIDI-BLE仕様を実装しているAndroidでも使えるはずだ。

JavaAndroid MIDI APIandroid.media.midiパッケージにある。Android上でシステムに登録されたMIDIバイスを利用するためには、Androidのシステムサービスとして登録されたMidiManagerにアクセスして、Javaオブジェクトとして存在するMidiDeviceを取得する必要があり、ART上のJavaオブジェクトを経由せずにAndroidシステムが管理するMIDIバイスにアクセスすることはできない(C/C++のコードだけで完結するとしてもJNIEnvオブジェクトやMidiManagerオブジェクト、MidiDeviceオブジェクトを扱う必要がある)。

Androidのシステム登録と無関係にMIDIバイスを接続するのであれば、システムが実装しているUSB接続やBLE接続に相当するコードを自分で用意しなければならない。仮想MIDIバイスなら特に難しいことはないだろう。*1

機能

AMidiはJavaandroid.media.midiパッケージのAPIに相当する機能の全てを提供しているわけではない。AMidiの機能の範囲は、次のように、ほぼMIDIアプリケーション(クライアント)側の、入出力ポートにおけるMIDIメッセージの送受信に限定されている。

  • MIDIバイスをあらわす構造体AMidiDeviceの入出力ポートAMidiInputPortAMidiOutputPortを開閉する
  • ポートの数は取得できるが、ポート情報の詳細は一切取得できない
  • MIDIバイスの詳細情報は一切取得できない
  • 入力ポートAMidiOutputPortからMIDIメッセージを受け取る
  • 出力ポートAMidiInputPortMIDIメッセージを送る

AMidi.hで宣言されている型は以下の3つ(内容は未公開)、関数は以下の13件しかない。

型:

typedef struct AMidiDevice AMidiDevice
typedef struct AMidiInputPort AMidiInputPort
typedef struct AMidiOutputPort AMidiOutputPort

関数:

media_status_t AMIDI_API AMidiDevice_fromJava(
    JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr)
media_status_t AMIDI_API AMidiDevice_release(
    const AMidiDevice *midiDevice)
int32_t AMIDI_API AMidiDevice_getType(
    const AMidiDevice *device)
ssize_t AMIDI_API AMidiDevice_getNumInputPorts(
    const AMidiDevice *device)
ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(
    const AMidiDevice *device)
media_status_t AMIDI_API AMidiOutputPort_open(
    const AMidiDevice *device, int32_t portNumber, AMidiOutputPort **outOutputPortPtr)
void AMIDI_API AMidiOutputPort_close(
    const AMidiOutputPort *outputPort)
ssize_t AMIDI_API AMidiOutputPort_receive(
    const AMidiOutputPort *outputPort, int32_t *opcodePtr, uint8_t *buffer, size_t maxBytes,
    size_t* numBytesReceivedPtr, int64_t *outTimestampPtr)
media_status_t AMIDI_API AMidiInputPort_open(
    const AMidiDevice *device, int32_t portNumber, AMidiInputPort **outInputPortPtr)
ssize_t AMIDI_API AMidiInputPort_send(
    const AMidiInputPort *inputPort, const uint8_t *buffer, size_t numBytes)
ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(
    const AMidiInputPort *inputPort, const uint8_t *buffer, size_t numBytes, int64_t timestamp)
media_status_t AMIDI_API AMidiInputPort_sendFlush(
    const AMidiInputPort *inputPort)
void AMIDI_API AMidiInputPort_close(
    const AMidiInputPort *inputPort)

バイス詳細情報やポート詳細情報など、文字列が関連してくるAPIについては、C/C++のコードでどのようなデータ表現を用いるのか、定まった解がなく、AMidi APIはこれらをうまいこと回避しているともいえる。

ひとつ重要なことを指摘しておく必要があろう。MidiDeviceインスタンスは、アプリケーション側であるMidiManagerを経由してのみ取得できるものなので、MidiDeviceServiceの実装でこれを取得することはできない。つまり、MidiDeviceServiceではAMidiは使用できないということである。後述するfluidsynth-midi-serviceでこれを利用できる可能性はゼロだ。

Javaコードとの棲み分け

AMidiは処理速度が重要になるMIDIメッセージの送受信に特化したネイティブライブラリであり、それ以外の機能についてはJavaAPIが使われることが想定される。 AMidiのエントリーポイントはAMidiDevice_fromJava()関数であり、AMidiを使うためには少なくともJava側でandroid.media.midi.MidiDeviceインスタンスを取得する必要がある。

そこから先は、MIDIアプリケーションの目的によって、利用できるAPIの向きが変わる。

  • (1) 外部MIDIバイスからのMIDIメッセージを受け取って処理するアプリケーションでは、MIDI入力ポートからのメッセージの受け取りをandroid.media.midi.MidiOutputPortからではなくAMidiOutputPort_receive()で受け取って処理するようにすると、Native MIDI APIによってMIDIメッセージの送受信が軽量化される可能性がある。(MIDI音源と接続したり仮想MIDI音源を操作するタイプのMIDIバイスサービス実装では、MidiDeviceインスタンスを取得できないので、この可能性は無い。)

  • (2) 外部MIDI音源MIDIメッセージを送るアプリケーションでは、MIDI出力ポートへのメッセージの送信をandroid.media.midi.MidiInputPortではなくAMidiInputPort_send()あるいはAMidiInputPort_sendWithTimestamp()で処理するようにすると、内部的に軽量化される可能性がある。(カスタムMIDIバイスと接続するMIDIバイスサービス実装では、MidiDeviceインスタンスを取得できないので、この可能性は無い。)

いずれの場合も、従来のJavaandroid.media.midiAPIでは、対象MIDIバイスとの接続にはAndroidのServiceの仕組みに基づいてメッセージがParcelにシリアライズされ、Parcelとして送受信され、Parcelから元のデータがデシリアライズされる必要があった。この部分がAMidiで最適化されている可能性はある。

この節で何度も「可能性がある」と留保付きの表現を用いているのは、ここはプレビュー時点でソース未公開のAndroid Qのフレームワーク実装の部分なので情報がないためである。一般的には異なるプロセス同士でメモリエリアを共有することはできないが、Parcelが内部的に使用しているbinderの仕組みでサービス側とメモリを直接やり取りしている可能性はある。Android Qで新たに追加された機能ということは、フレームワークのレベルで改良される必要があったということがうかがわれる。

Android 8.0から、binderのドライバーがrealtimeの優先度で動作できるようになっている。これがMidiDeviceServiceを経由するMIDIメッセージのやり取りに使用されている可能性もある、と筆者は想像している。

実用未遂: fluidsynth mdriverのAMidiサポート

筆者は(実のところさほど明確な目的もなく)Fluidsynthにパッチを送り続けているのだが、今回もFluidsynthにAMidiを応用するMIDIドライバーを作ってみた。自前のgithub fork上で公開しており、公式にもissueとして登録してあった。(ただ、前述のとおりMidiDeviceServiceで使える目処が立たなかったので、このissueは閉じてある。)

Fluidsynthは、その起源はLinux用のソフトウェア・シンセサイザーだが、クロスプラットフォームで動作するように、プラットフォーム固有の「ドライバー」の上に音声合成の共通コードが乗っかっているかたちになっている。ドライバーには、プラットフォームのオーディオAPIにアクセスするオーディオドライバー(以降ソースコードの名前に合わせてadriver)と、MIDI APIにアクセスしてそこから受け取ったMIDIメッセージをそのまま合成エンジンに高速に渡すことができるMIDIドライバー(以降mdriver)がある。mdriverは、fluidsynth(.exe)で内部的に利用できるオプション機能だ(使わなくてもよい)。

mdriverを使わない場合、合成エンジンに対してnote on, program change, control changeなどの機能を実装するC APIを呼び出すかたちになる。この場合、MIDIメッセージとして受信したバイト列をアプリケーション側が自前で解析して、対応するAPIを呼び出す必要がなる。

mdriverはnew_fluid_midi_driver()という関数で生成するが、このときコールバック関数としてhandle_midi_event_func_tを渡すことになる。この中で、別途生成したsynthエンジン(fluid_synth_t)で直接合成させてもよいし(fluidsynth(.exe)が行っているのはこれだ)、別の処理を挟んでもよい。Android上では、OpenSLESやOboeが有効になっていないsynthオブジェクトにMIDIドライバーが受け取ったMIDIメッセージを処理させても、あまり意味がない(MIDIメッセージのフィルタリングくらいはできる)が、幸いなことに最新のfluidsynth masterには筆者のOboe/OpenSLESドライバーが含まれているので、恩恵を受けることが出来る。

今回考えたのはNative MIDI APIを応用して、AMidiのmdriverを実装する、というものである。Android MIDI API経由でFluidsynthのMidiReceiverが受信したMIDIメッセージを、JNIマーシャリングコストなしでfluidsynthの合成エンジンが処理できるか試してみる、という感じである。しかしMidiDeviceServiceからAMidiへのエントリーポイントが全く存在しないため、実現可能性は無かった。

潜在的な需要: オーディオプラグイン機構のサポート

前述した話だが、Android 8.0ではリアルタイム優先度のbinderドライバーが利用可能になっている。これによって潜在的に大きな恩恵を受けることが出来るのがオーディオルーティングを伴うアプリケーションだ。オーディオ編集アプリケーションが、オーディオエフェクトをサービスとして実装するアプリケーションを、オーディオプラグインとして利用できたら、なかなかおもしろいことになるのではないかと筆者は想像している。(前節でも重ねて「可能性がある」と書いてきたのと同じで、まだソースが公開されていないので、ここは想定でしかない。)

Android 8.0未満の環境では、binderを経由してデータを渡して処理させたら、リアルタイム優先度を維持することが不可能だった。リアルタイム優先度を維持できないと、オーディオ処理に遅延が生じることになる。それを受け容れるというのも一つのあり方だが、レコーディング時やライブコーディング時など、限りなく「絶対に」に近いかたちで遅延を回避したい状況というのはあるだろう。

オーディオ? Native MIDI APIMIDIAPIではないのか? という疑問が湧いてくると思うが、音楽制作で一般的に使われるVSTAU (Audio Unit)、LADSPAなどオーディオプラグインの世界では、一般的にMIDIメッセージがプラグインの制御に使われている(もう少し正確にいえば、MIDIメッセージを変換して各オーディオプラグインのパラメーターを制御するようになっている。オーディオプラグインの制御範囲はMIDIの表現できる範囲に限定されない)。このメッセージング機構がリアルタイム優先度で行われるようになれば(もちろんオーディオ処理のほうがはるかに大きな制約になるわけだが)、オーディオホスト/プラグイン機構を実現するための壁がひとつ取り払われることになるのではないか、と思っている。

ついでに自分の予言者ごっこtweetも貼っておこう。

既存プラットフォームをサポートしつつamidiサポートを追加する

AMIDIサポートをネイティブコードで実装してするとなると、amidi.soをshared libraryとしてビルド時に参照することになる。これは、実行時にAndroid P以前のプラットフォームでは「使わない」ようにする必要がある。Javaのコードであれば、android.os.Build.VERSIONなどを使って条件分岐すれば足りるが、ネイティブの共有ライブラリの場合はそこまで単純ではないので注意が必要だ。

dlopen()で共有ライブラリがロードされるとき、デフォルトではそのライブラリが参照している共有ライブラリが連動してロードされる。Cの呼び出しを直接制御しているのであれば、dlopenの引数にRTLD_LAZYを指定すれば、シンボルの解決だけは先送りすることができるが、参照ライブラリのロード自体は即時に行われるので、Build.VERSIONの比較によって「実行されない」ようにコードを書いていても、JavaでいうところのUnsatisfiedLinkErrorが生じることは避けられない。

また、筆者のfluidsynthjnaのように、JNAeratorのような自動バインディング機構を活用していると、シンボル解決もロード時に全て行われる実装になるため、RTLD_LAZYにも効果がない。ダミーとなるライブラリを用意するとしても、シンボル自体が全て存在していなければならないことになる。

実のところこの問題には先例となるソリューションがある。GoogleはOboeでaaudioをサポートしているが、aaudioをサポートしたoboeがAndroid 27以前の端末でもUnsatisfiedLinkErrorを起こさずに実行できるのは、共有ライブラリとしてlibaaudio.soにリンクすることなく、dlopen()を使ってAAudioの関数を動的にロードしているためである。

https://github.com/google/oboe/blob/c278091a/src/aaudio/AAudioLoader.h#L67

古いプラットフォームでも動作するようにamidiを部分的にサポートするライブラリをビルドしようと思ったら、同様の仕組みを実現しなければならない。というわけで、自分で実装してみた。ただし一度も使っていないコードなので動作しない可能性が割とある。

https://github.com/atsushieno/fluidsynth-fork/blob/d92cbc5/src/drivers/fluid_android_amidi.c#L40

*1:たとえば、Google/music-synthesizer-for-AndroidにはMIDIメッセージを処理する機能があるが、Android MIDI APIとは無関係だ。

4月のmusic tooling hacks

4月というか平成も終わろうとしている今日この頃ですが、相変わらず時代をまたぐ無職生活エンジョイ勢です。相変わらず謎の方向性でコードを書いています。そんなわけでこのひと月くらい?のコーディング活動を雑にまとめます。

JUCE/VST3/Linuxの高い壁

楽曲制作環境の改善手順として、SMFベースのMIDIプレイヤーからオーディオプラグイン中心の音源ドライバーに移行しようと思っているのですが、2019年はLinuxデスクトップ開発者にとってたいへん難易度の高い年です(断言)。

昨年からSteinbergがVST2 SDKを廃止してダウンロードできない状態にしてしまい、VST3 SDKのみが入手可能な状態になったのですが、VST3 SDKGPLで入手できるものの、各種のネイティブVST*1プラグインDAW等のホストアプリケーションが追従していない状態です。VST2とVST3は根本的に別物に近いんですよね。

特にJUCEを使っているプロジェクトが、ホストもオーディオプラグインも全滅です。JUCEはLinux版VST3に対応しておらず、公式フォーラムに「おいおいLinux用VSTプラグインを作れると思って使ってきたけど詰んでるじゃん?」というスレッドが立っている状態です。 割と新鮮なUI設計で面白いhelioシーケンサーも、Waveformで使われているtracktion_engineも、Googleのmusic-synthesizer-for-AndroidベースのDX7互換FMシンセサイザーDexedも、サウンドフォントが使えるjuicysfpluginも動きません。*2

JUCEのプラグインならLADSPAでビルドすれば動くんじゃん?とか思うわけですが、dexedみたいにJUCE 5.2.1くらいのソースを取り込んでいるやつではProjucerでちょいちょいっとjuce_audio_processorsをいじってもVST2が無いだけでビルドエラーになるみたいな理不尽な状態なので、VST3でちゃんとビルドできるようになっていないとメンテナンス出来ない状態になるなあと思います。

そういうわけでたまにJUCEのソースを読みながら、Linux上でVST3を使えるようなブランチを作ろうとしているのですが、ちゃんと動くものが出来るまでには時間がかかりそうな気がしています。その間に本家で対応していてほしいけど、先のissueにsurprisingly difficultなどという供述があり…。JUCE、外部からのcontributionを受け付けない伽藍プロジェクトなので、今後こういうフレームワークに乗っかっていくことにはLinuxデスクトップユーザーとしては危機感があります。

オーディオプラグインフレームワークとしては、VSTの他にもLADSPA V2 (LV2) などがあって、各種ホストでこっちが標準でサポートされるようになってほしいところです。ちなみに今のところVST3をLinux上のDAWで使おうと思ったらBitwig StudioかArdour5訂正: 対応していると思っていたけどLXVSTのみでしたという選択肢しか無いと思います。Bitwig Studioえらい。

f:id:atsushieno:20190430211123p:plain

このスクショはJUCEのextraにあるAudioPluginHostなのですが、リストアップされているオーディオプラグインの中にVST3が含まれているのがミソです。juicysfpluginはguiが表示できなくて詰むのですが、mdaほげほげの類は音も出てGUIも表示され、パッチを作っていた自分がまずビックリでした…

fluidsynth-midi-service-j

github.com

先月までずっとfluidsynthのAndroidビルド用forkを本体にマージするための開発にかかっていたわけですが、ひと区切りついたので、Androidアプリケーション側の開発にシフトしました。まだ最初だけノイズが乗るみたいな問題があるのですが(ゴミのあるオーディオバッファが再生されている、だけでもなさそう…)、それなりに使える状態のアプリにはなってきたと思います。proof of conceptレベルでもちゃんとJava/Kotlinでアプリ開発するのはx年ぶり(思い出せない)なので*3なので、AACやらdata-bindingやらconstraint layoutやらをごく当たり前のように使ったり、DeployGateにも数年ぶりにうpしたりと、今らしいスタイルで開発できるのは新鮮です。これはこれで楽しい〜

先月の時点では単に和音を鳴らすだけのボタンがあるMainActivityしか無かったのですが、fluidsynth初期化設定をUIで調整できるようにして、Oboe/AAudioのLowLatencyモードやExclusiveモードも試せるようになったので、この辺に興味のある人にとってはちょっと楽しい感じです。

あと先月ノリでmanaged-midi (C#) から移植したKotilnのktmidiが、主にbyteのsigned/unsignedまわりとIntへの型変換を含めた比較で大爆死だったので、ちゃんと動くように全面的に手を加えて、SMFもちゃんと再生できるようになっています。KotlinにはexperimentalでunsignedTypesというのがあるのですが、仮にそれがkotlincまでシームレスに統合されるようになっても、Int定数との比較などでIntへの型変換などが必要になってくると、常にunsignedなのか否かを意識したコードを書かなければならず、KotlinでMIDIアプリケーションを安全に書くのは極めて難しいのではないかと思うようになりました(experimentalが外れてUByteの定数が書けるようになれば、この問題は少し緩和されることでしょう)。少なくとも、MMLコンパイラをKotlinに移植しようという気持ちはなくなりました。

あとアプリで実践的にfluidsynthを使うようになった関係で、ちゃんとSF3 (compressed soundfonts) をサポートするためにlibsndfileをビルドできるように本家fluidsynthに変更を加えたり、bitriseやローカルのdockerでビルドを確認できるようにしたり、といったこともやっていました。Android用fluidsynthがビルドできないという問い合わせ、地味によく来ていたので…(しかもなぜかメールが多い)。

あとこの方面の問い合わせに連動して北米方面のDAWの開発会社からうちで仕事しないか?みたいなのが来て、ktkr! とか思って前向きに返事したのですが、「よっしゃ連絡するわ」という返事を最後に通信途絶した()ので相変わらず無職です。(どうでもいい)

英語では他にもいろいろ書いたのですが(Android Native MIDI APIの話とか)、めんどくさくなったのでこっちでは省略します(何)。

soundfont-player-cs

github.com

MIDI中心からオーディオプラグイン中心の制作環境に移行するためには、スムーズにリズムパートを打ち込めるような環境と音源が必要です(前回のイベントで頒布した作品では、実はリズムパートが皆無に近いんです)。これまでもMMLで打ち込んできていて、ノートベロシティを踏まえた打ち込みは簡単だし、DAWでやると面倒くさい3連符もごく簡単に打ち込めるし、MMLでの打ち込みで続けたいのですが、手元にあるSC-8820にある音は限られていて、リズムパートを希望通りの音色で打ち込むのは無理ではないかと思うようになりました。

オーディオプラグインであればSerumとかEZDrummerとか、手元にあるだけでもOmnisphereとかBatteryとかあるわけですが、Linux環境で使えるものがほしいわけです。ドラムパートくらいならサウンドフォントでもいけるのではないか(加工系のプラグインならLADSPA等でいろいろあるし)と思ったのですが、サウンドフォント自体は大量に手に入る時代とはいえ、それぞれのサウンドフォントにどんな音があるのかを簡単に知る術がないぞ…と思ったわけです。

とりあえず、GUI上に手持ちのサウンドフォントにある音色(プリセット = プログラム / バンク)のリストをありったけ表示して、選ばれたやつを広大な鍵盤リストのようなものに表示して、どこでも叩けるようにしたら、音色の確認は簡単にできるようになるのではないかと思って、Xwt, Fluidsynth, NAudioを組み合わせてざっくり作りました。意外とこういうツールが無いんだよな…

f:id:atsushieno:20190430204056p:plain

やっている事自体は難しくないので、それこそ仕組みだけならWeb Audioとかでも作れなくはないのですが、サウンドフォントはファイルがでかいのでWebには向いていません。サウンドフォントに限らず、音楽系ツールは膨大なデータを伴うことが多いので、全般的にWebには向いていないですね。ローカルに各種リソースがあることが前提になると思います。

今回はたまたま C# で組みましたが、既にUIにかなり不満があって(特にListViewあたり)、これを解消するにはもう言語ごと他所に引っ越すしか無いかなと思っているのですが、なかなか自分の需要に合うものが少なそうです。とりあえずQML.NETあたりを試してみようかなとは思っています(これもnugetパッケージ版のLinuxビルドがおかしくて試せなかった)。

managed-midi: CoreMIDI on .NET Core, etc.

もうひとつ、managed-midiの話なのですが、ひとつ長い間どうにかしたいと思っていた問題が解決しました。.NET Frameworkや.NET Coreのプロジェクトでmanaged-midiを使うと、Macで使えるネイティブバックエンドが無いという問題です。

MIDIアクセスはプラットフォーム固有のAPIアクセスが必要になるのですが、Mac上のCoreMIDIを使うにはXamarin.Macに依存するしか無かったわけです。そうするとプロジェクトそのものがXamarin.MacのProjectTypeGuidを前提とするものになり、デスクトップ用のmonoを使ったり(.NET Frameworkが全体的にdeprecatedになりそうなので忘れられているかもしれませんが、.NET Coreの範囲を超えるデスクトップ用アプリを作ろうと思ったらmonoを使うことになるのです)、.NET Coreを使ったりということを考えると、Xamarin.Macは難しくなってきます。Xamarin.Macにはfull profileがあって、これはnet4x互換なのですが、いずれにしろわたしがLinux上でビルドするときに「無いとビルドエラーになる」ようなものでは困るわけです。

いろいろ考えた結果、もうXamarin.Macに含まれるCoreMIDI API相当の部分を自前実装しちゃったほうが早いだろう(これ自体はMonoMacで実現していたわけだし)、となって、今のmasterには実装が含まれています。IMidiAccess APIを実装する範囲の最小限のもので雑ですが。

これで自分のプロジェクトがちゃんと単一バイナリでもクロスプラットフォームで動作するといえる状態になったので、気が向いたらバイナリパッケージも作るかもしれません(managed-midi自体はずっとNuGetで配布しています)。

あと最近はWindows方面の開発者がUWP実装やwinmm実装のissueを報告してくれたりパッチを送ったりしてくれているので、managed-midiユーザーいたんだ…?みたいな気持ちでありがたくcontributionを受けています。自分が普段全然使っていない環境をケアしてくれる開発者がいるのはありがたいですね。

残っている課題?で多く来るのが「Unityで使いたい」なのですが、どうしようかな…Unity普段使っていないし、今さら.NET方面に時間使うのもったいないな…という感じで「やる気があったらcontributeしてくれ〜」みたいな感じで雑に返しているのが現状です。(モバイルまわりになると割と大変なので自分で作ってあげたほうがいいのかなーみたいに思ったりはしますが…) MIDIJackとかで対応してくれればそっちに誘導するんだけどなあ。

managed-midiの仮想ポート作成API

あと、仮想MIDIポートを作成するAPIを追加したので、これを関連プロジェクトであるところの仮想MIDIキーボードアプリxmmkでMIDI入力用ポートを追加して、Tracktion Waveformから接続して譜面に入力をペーストできるようにしました*4

実のところ送信内容をキー入力に限定する必要はなく、xmmkにはmugene MMLコンパイルしてMidiPlayerでバックグラウンド演奏できる機能も追加してあり、この内容を仮想入力ポートに送るだけでDAWMMLからペーストできるようになったともいえます*5。ピアノロールに打ち込めるのもまあ面白いのですが、一番「これは便利」と思っているのは、DAWから開いているオーディオプラグインで試しに音を出すとき、自分の好きなように入力UIを設計できることですね。物理MIDIキーボードと違って、ソフトウェアMIDIキーボードならいくらでも工夫の余地があるので…

ちなみにCoreMIDI APIでの実装はまだ何やら機能せず、WinMMには仮想ポートを定義する機能そのものが無いので対応しません。UWPは知らんけど多分無いんでしょう。

f:id:atsushieno:20190430211756p:plain

that's all

とりあえずこんなところでしょうか。本当はもっと音楽制作とかやって遊んでいるつもりだったのですが、ちょっと易きに流れてしまった感じで割と反省しています。来月はもちっと打ち込みに興じたいところです。(フラグ?)

*1:ユーザーとしてLinux上でVSTを動かすアプローチは2つあって、ひとつはLinux用のビルドを使う方式、もうひとつはwineを利用してWindows用のバイナルを動かす方式です。後者は商用のめんどくさいやつ(Kontaktとか)が全く使えないので、VST開発者にとっては前者のサポートが必須といえます。

*2:正確にはjuicysfpluginなどは古いJUCEを依存関係に含んでいるのか未だにビルドできるのですが、ライセンス的に配布できなくなっている状態です。

*3:ここでも何度か書いているかと思いますが、フレームワーク開発者がアプリを開発しない問題…

*4:特別にコードで実装しなくても、OSの機能としてMIDI Throughなどのポートにroutingかければいけたのですが

*5:個人的には楽曲の「土台作り」をDAW上でやりたくないので、ここに大きな可能性はあんまり見ていません

最近のOSS contribution活動

2月までずっと音楽制作…というしかないやつ…にかかりきりでだいぶフラストレーションがたまっていたのですが、それがひと区切りついたこともあって、3月は主にプログラムを書いて過ごしていました。長い間取り掛かっていたのが昨日終わったので、軽い気持ちでまとめていきます。

Xamarin.AndroidAndroid Q APIサポートを追加

github.com

(長大なcommitコメントはわたしではなくリーダーがまとめたものですが)

今月上旬にAndroid Qのfirst previewが出ました。わたしがXamarin.Androidチームにいた時の暗黙的な仕事のひとつが新APIへの対応だったのですが、わたしが抜けた後これに対応するメンバーがおらず、もともとcontract的な仕事として投げたいかも…実現できるかまだわからんけど…みたいに内々に言われたりしていました。ただわたしのほうは、まあそんな必要ないんじゃないかなー、だってこんなの1日2日あれば終わるでしょ…みたいな気持ちだったので、ちょうど暇になったこともあって手を出してみたら、案の定1日でほぼ終わってしまいました。(まあ経験があるから1日で済んだっていう話はありますが。)

そんなわけで「5月頃はもしかしたら受託で仕事しているかもしれないなー」などと言っていたのですがそれも発生する予定がなくなったので、仕事探し(?)が気楽にできるようになった感じです。

Fluidsynth for Android (OpenSLES/Oboe)

github.com

ここはてなも含め各所で何度か言及しているのですが、FluidsynthにAndroidのオーディオドライバー実装を追加したブランチを開発してメンテしていたやつが、ようやくupstream masterにマージされました。Fluidsynth、Android上で動くコードはあったのですが、何も音が出ないダミードライバーしか無くてほとんど意味がなかったのが、これでちゃんと音が出るようになりました。Fluidsynth 2.1の目玉機能のひとつとしてリリースされることになると思います。

VolcanoMobileなど独自にFluidsynthに手を加えてGoogle Play上で公開されているものもあるのですが、Androidドライバ部分のソースコードが非公開で(これLGPLには準拠していないってことなんですが、個別に許諾もらっているのかなぁ…)、ちゃんとOSSとして使えるコードが無かったので、これでまたひとつAndroidの音楽ソフトウェアの可能性を広げられたかな、と思っています。ちゃんとしたソフトシンセがほしい、という時に使ってやってください。

glibのビルドのために使っているcerberoが、Android NDKに変更が生じるたびに追従できなくなったり、手元のデスクトップ環境が変わるとビルドできなくなったりで、とにかくビルドが面倒だったやつです。仕事をやめてからようやく1週間単位で時間を割り当てられるようになって、それから整備して11月にpull requestを送って、でも今月までずっとCD制作で忙しくて先送りしていたやつのでした。

アプリケーションがproof of conceptであるfluidsynth-midi-serviceしかない状態で、実のところXamarin.AndroidLinuxビルドが死んでいて*1IDEも無い状態でXamarin.Androidを使い続けるのはデメリットしか無いので(あとAndroidの最先端に追従していきたいわたしとしては、いつまで経ってもXamarinにandroidxをサポートしていく姿勢が見えないのはリスクでしかない)、コレのMIDI基盤であるところのmanaged-midiひと通りKotlin化してPoCもKotlinでfluidsynth-midi-service-jとして作成しようと思っています。

あとfluidsynthと直接は関係ないところで、google/Oboeをshared libraryとしてビルドして使いたかったので、またone linerパッチを作って取り込んでもらいました。Oboeはカジュアルに変更を取り込んでくれるので支援しがいがありますね。*2

その他のコーディングとか

今月は2つ外向けにそれなりにでかいのがあったので外向けのやつがメインの話になってしまいましたが、自分のリポジトリだとmanaged-midiに設計思想メモを書いているのはそれなりに意味があるかなと思っています。MIDI playerをどう実装するかとか、割と試行錯誤感あるので。

他にもJUCEで遊んだりTracktionで遊んだりとかしているのですが、まだ特にアウトプットがあるわけでもないです。

いつもならこの時期はたぶん技術書を書く作業で死んでいたのですが、今回はそれがないので*3、たいへんゆとっています。雑に仕事を探したりとかするかもしれませんが、基本的には次の制作に向けて、開発環境を構築していくつもりです。(制作?開発?)

*1:すぐ上でxamarin-androidにPR作ったって話書いているのに…!

*2:同じgoogleでもmusic-synthesizer-for-androidなんかは完全にスルーなので…

*3:あと今回はイベントスタッフ業もやっていません。

幻想音楽祭サークル参加のKPT

https://dl2.pushbulletusercontent.com/mDQ1Wd6QAKKlr2JyX3u6mrsdw0XjPFml/20190302105736_p.jpg

幻想音楽祭の出展は無事終了しました。音楽サークルとして創作したのもイベント参加も初めて、他サークルの人々も一般参加者も知り合いがほぼ皆無、しかも単独サークルという99.9%アウェイな状況でしたが(!)、ブースでは多くの方に興味をもって音楽を聞いてもらえたり、展示していたMMLの解説に興味を持って話を聞いてもらえたりと、たいへん楽しく過ごすことが出来ました。来てくださった皆様ありがとうございました…!

「事前にPVを見てこれを買いに会場まで来ました」という方がいらっしゃって、これは「こんなことがあるのか…!」とちょっと信じられないくらい嬉しかったです。これを原動力として買ってくれた方の期待にまた応えられるような作品を作りたいと思いました。

10枚売れたときは「おおっ」ってなりました。売り子をお手伝いいただいた id:Colloid に「2桁行くって割といい数字ですよ」というコメントをいただいて、多分そうだろうな?とは思いつつ、事後になってどんなもんなんだろって思ってネットの海を探してみましたが、「2桁もいかなかった経験がある人は結構多いと思うので」という記述を見て、なるほどやはりそういうもんなんだな…と思いました。前述した今回のうちの状況を鑑みると、これはやっぱりけっこういい数字なのではないでしょうか…!

売上を支出と比較して考えると、今回の作品は用意していったCDが全部はけても赤字というラインだったので、打ち込みツールのプロモーション活動というか、この方向で開発活動を進めていくための足場作りであると割り切っています。もちろん、だからといって音楽制作の手を抜いたつもりはありません。(時間的に間に合わなかった作業はもちろんありますし、テクニックが身についていればアレもコレも出来た…みたいなことはもちろんあります。レベル上げが必要なやつですね…!)

KPT

同人音楽サークルは今回が初めてでしたが、M3などには何度も一般参加していますし、ネットの海にはいろいろな情報が流れています。いろいろ参考にしながらやったことがありますが、事前告知のエントリーですでに長々と書いた辺りはカットして、今回はいろいろKPTのかたちで振り返ってみたいと思います。

  • (K) ダウンロードコンテンツにMP3だけでなくMIDIソースとTracktionデータを含めた(「うわあ、こんなの入れちゃっていいんですか?」という話もされましたがw いいです。どんなものをどうやって作ったのかを広めるために作っていますし、これで「引き」がよくなりました)
  • (K) 制作過程を紹介したペラ紙とMML本の見本誌を用意した(これを読むために立ち止まってくれた方がたくさんいた)
  • (K) クロスフェードを作成してyoutubesoundcloudに登録した(どちらのアカウントも初利用)
  • (K) musicbrainzCDDBデータとMP3 song fingerprintingに基づくタイトル情報等を登録した
  • (P) ジャンルの最初にProgressiveと書いていたけど、最初の曲は3/4固定で、残りの曲もインパクトのある部分は後半に集中していて、試聴では不利な構成で、各曲1分ずつなどそれなりの時間をかけて視聴した人ほど買わない傾向があった(期待はずれだと思われた可能性が高い)
    • (T) 次回は譜面を用意してこようと思う(タブレットでも可)
  • (P) スケジューリングが思い通りにならなかった: これは事前の見通しの問題ですが、たとえば(1)アルバムジャケットを印刷依頼するためには、その時点で裏ジャケットに載せる曲名リストが確定していなければならない(最後の追い込みがうまくいったら1曲追加できる、みたいなことはできない)、(2)PVをアップロードするためには、その時点で作品がだいたい完成していなければならないが、告知用のPVが必要になった時点でまだ仕上がっていない、といった問題
  • (P) 参加サークルによるサンプル曲の募集に対応できなかった(何一つ完成していなかったので…!)
    • (T) なるべく1曲くらいはこういう用途に対応できるように音源を用意しておきたい
  • (P) ついたてが無かったのでPCの背中を使って紹介を掲示したのだけど、手作り感が目立ってしまった
    • (T) 次回はついたてを用意しておく or 組み立て台を調達する(隣のサークルさんが用意していて「いいな〜」ってなった)
  • (P) 名刺的なものを何も用意していなかったので、不在の時に「連絡先は…?」みたいなことになった(とはいえ週休七日だし名刺的なものを用意するには完全にサークル用の何かが必要)
    • (T) サークルと肩書をでっちあげる or 無職を失う

音楽は試聴するまで内容がわからないので、試聴しか伝達手段が無いと、じっくり聴く人でもない限りは、最初のせいぜい1分くらいで次の曲に進む人が多いです。その1分の間にインパクトのある内容(プログレであれば癖のある変拍子や音色選び)が無ければ、期待通りの内容とは思ってもらえません。ただ、曲の構成を「試聴に最適化されたかたちに」変えるというのは避けたいと思っています。全体的にどのような楽曲なのかは、楽譜になっていればある程度見えるはずです(特に変拍子など)。

実のところ、最初はSMFからmusescore経由で譜面を印刷して閲覧用に持っていくつもりだったのですが、最初の楽曲がすでに50ページを超える内容だったので、今回は見送ったのでした。結果的には「あったほうがよかった」と思っています。タブレットがこの目的にはちょうど良さそうですが、調達が必要な案件です。

今後の予定

実のところ、サークル出展自体が初めてだったこともあって、他のイベントに目を向ける余裕は全くありませんでした。M3春なども参加できる予定はありません。コミケはちょっとしんどそうなのでスルーすると思います。というわけでリアルイベントには、出られるとしても秋ですかね…(!) Apolloには気軽に出られそうなので多分参戦するんじゃないかと思っています。いずれにしても、じっくり制作に時間をかけられる体制を確立しておくことが必要ですね…!

今からでも入手したい

…という方のために、CDの無いダウンロード版としてのデータをboothから入手できるようにしました。もしかしたらCDもプラス500円+送料くらいで販売するかもしれません。

xamaritans.booth.pm

"幻想音楽祭" 出展のお知らせ (+ MMLによる音楽制作の実践)

gensouongaku.info

3/2に川崎市産業振興会館で行われる同人音楽の即売会「幻想音楽祭」に個人サークルとして出展します。オリジナル楽曲のアルバムCD・音楽データDLカード等を販売する予定です*1。サークル配置番号はA-4 "ginga" です。*2

ここを見ている人は大半が開発者だと思うので、ここで告知してもかなり「かぶらない」と思うのですが、個人的には半分くらいは作っているソフトウェアの展示とか、音楽の作り方をどうするかみたいな話に興味のある方に来てもらえればと思っています。イベント自体も初めてのようですし、ぜひ遊びに来てください。

TL;DR

目次

音楽作ってる? は? なぜに?

ここを読まれている人の大半はこんな反応になるかと思いますが(そうでもない?)*3、音楽ソフトウェアの技術同人誌とかではなく、同人音楽CDです。大昔にMIDI音源をいじっていたことを除くと完全に未知の分野なのですが、手探りで辛うじてやっています。

見せて

はい…(抵抗感) youtubesoundcloudにうpしてあります。

youtu.be

soundcloud.com

人知れずひっそり出すか、ずっと悩んでいたのですが、自作ツールのポートフォリオとするためには隠してやっても仕方ないよな…となって、表に出すことにしました。今後は何か作るとしても別名でやるかもしれん…

発端

昨年にもちらっとここで書いたのですが、11月にAudio Developers Conference 2018に参加して、参加者の何人かと雑談していた時にふと「おまいらも自分で音楽を作ったりするの?」と聞いてみたら、返答の大半がYESだったんですね。自分で音楽制作しているわけでもないのに音楽ソフトの開発やら仕事やらにシフトしてみたい、って言うことには、自分でも何か落ち着かないコンプレックスのような気持ちがあったわけです。

それで、一度くらいちゃんと音楽ソフトを使って制作とかやってみて、どういうものが必要で求められていたりするのか勉強してみようと思ったのでした。個人的にはM3とかボーマスとか、何度か行ったことがあるので、とりあえず一度何か同人音楽イベントに参加してみようと思って、それで11月末頃に今回のイベントが申込受付中であるのを発見しました。

「ファンタジー音楽」というのは何となくわかる気がするし*4、第1回だし、右も左もわからない雑魚サークルでも参加できそうな気もするし、参加できたらラッキー…くらいのノリで出来心で応募したら、あっさり通ってしまい、それならやるっきゃない…!と肚を括ったのが12月のことです。

制作環境の決定

参加が決まった時点での方向性はほぼゼロで、出せるコンテンツとしては20世紀の頃に作ったSMFとして作った楽曲がわずかにあるばかり…という状態でした。DAWなどを使ってみたほうがいいとは思っていましたが、経験ゼロのところからちゃんとイベントまでにCDを作るところまでもっていけるのか…!?という感じで、正直ゼロからDAWでやっていたら完成しなかったと思います(!?)

とりあえず、20世紀以来ほとんどやっていなかった「音楽を打ち込む」感覚を取り戻さないといけない…というところからやり直しました。とりあえず当時慣れ親しんでいた楽器であるRoland SC-8820を毎日持ち歩いて自作のソフトウェアMIDIキーボードxmmkで音を鳴らしてみたり、Tracktion Waveform9(面倒なので以降単にTracktionと書きます)を起動して標準添付のCollectiveで音色を探して4小節くらい和音を鳴らして終わり、みたいな感じで、たいへんに雑魚ったところからスタートしました。

ちなみにTracktionだったりCollectiveだったりするのは、わたしの普段使いのPCがLinuxなのでKontaktなどが一切使えなかったためです(Kompleteを9の頃に買っていて、「もっとWindowsUbuntuみたいに快適に動いてくれたらなあ…」などと言い続けながら、今回Macで使うまで何年も眠らせていたのでした…)。

最初の頃は、Tracktionの操作に慣れる目的で、過去に打ち込んでいたMIDIファイルを、まずはコピー曲などからインポートして、VSTに置き換える練習から始めました。Tracktion、それなりの頻度で落ちるので、Bitwig Studio*5などに変えようかなあ…などとも考えたのですが、この一見風変わりだけどロジカルに出来ているUIがやっぱり好みだったので、今でも使っています。

この時点でわたしが「わかる」と言える音色はSC-8820までのGM/GS音源の音色くらいだったので、Collectiveで音色を選んでそのパラメーターを調整するか、Collectiveにない音(かなりあります…)については、juicysfpluginサウンドフォントFluidR3_GM.sf2でGMにフォールバックする感じでした*6。ただ、これは早々に行き詰まりそうだったので、SC-8820のフル音色を徹底的に活用してMMLMIDI楽曲としてまず完成させつつ、その後にWindowsなりMacなりでKontaktなどの音源版を作って移植しよう、とざっくり方針を決めました。

この頃は一方で、ADC2018で持ち帰ったソフトウェア(JUCEやらTracktionやら)の知見を学ぶために*7いろいろコードをいじっていたのですが、それらが「動作する」「Linux以外の」環境が比較用に無いと作業が行き詰まるというところまで来ていたので、仕方なく*8Mac mini環境を構築したのでした。たまたま自宅に10インチくらいの液晶モニターがあったため、以降このMac miniはまるごとモバイル環境になり、電源のある環境でちょいちょい使われています(!)。

Linux環境でMMLで作曲、その後MacのTracktionに移植してマスタリング、という方針がざっくり決まりました。これが年明けくらいの話です。

MML打ち込み環境のブラッシュアップ

ここで改めて言及するのも何ですが、MMLというのは20世紀に使われていたDTMにおける音楽データ制作のツールであって、2019年にしかもLinux環境で使うというのは半ば正気の沙汰ではありません。とはいえ、これを実現するための土台は用意してありました。具体的には、今までこれらを開発していました:

少し前にここ2ヶ月ばかりのコーディング活動について書いたのですが、これは完全にこの音楽制作のためにやっていたことでした(単独のブログエントリとして見ると謎すぎましたね)。音楽制作と言いつつ、それなりの時間がプログラムの改良に当てられています。

2018年末にはVGM作曲家の古代祐三さんが使っているMMLコンパイラツールであるmucom88が公開されましたが、あれも主に自作環境の公開という雰囲気なので、わたしがここで公開しているのと(完成度とポピュラリティは違えど)似たようなノリだと思っています。

制作の勉強

この辺までの作業はツール開発者の視点で楽しく出来ることだったのですが、作曲ばかりは本当に自分のメンタルや創造性とのたたかい…になる側面が強くて、今でも進捗は圧倒的に悪いです。とはいえ、本当はそれなりにフレージングなどのテクニックがあれば進められることなので、勉強次第でどうにか出来るはず(それが足りない)という側面のほうが大きいことでしょう。まあ人間いちどに出来ることには限りがある…!

いくつか参考にした本などを挙げます。

nextpublishing.jp

まず手元にあった「ボカロでDTM入門」(Vocaloidは今回全く使っていませんが)。このエントリを書くために探して改訂版が出ていることに気付きましたが、うちにあったのは初版でした。アルバム(と呼ぶしかなさそうなのでそう呼びます)のテーマ作りみたいなことを考える時に参考になる部分がありました。

今回の制作にあたって、イラストレーターにカバーイラストをお願いするときにコンセプトをでっち上げる必要があったり、曲想が全く浮かばない日々が大半だったので(実のところ今でもそうで日々血涙を流しています)、何とかしないと…みたいな感じだったりで、解決の道筋になりそうなものを探し求めていた(いる)のです。ワタクシどう考えても情緒の豊かな人種でもクリエイティブな人種でもないので…

管弦楽法

管弦楽法

11月頃に、Tracktionでクラシックの管弦楽曲を打ち込もうとしていたことがありました。音楽制作については、職業DTMerの人にいろいろ教えてもらうことがあるのですが、この時に移調譜の読み方などを教わりながら「ピストンの『管弦楽法』を読むといい」と言われ、これを入手して読んでいました。

作業の過程で必要な部分だけつまみ食いで読んでいるのですが、その後KontaktでSession Stringsなどを使って音色を調整する時に、奏法などの未知のキーワードをこの本で調べたりしています。また、GM音源を使っているときにはもやっとしか知らなかった管弦楽器の編成についても、このあたりを知ることで一歩踏み込んで打ち込めるようになったと思います。

www.gcmstyle.com

VSTプラグインをひたすら紹介してくれる同人誌です。コミケ同人音楽イベントでも入手できると思います。CollectiveどころかKompleteを使っていても「あの音色が足りない…」みたいな場面がちょいちょい出てくるので、これを参考にしていろいろな音源を知ることが出来ました。

制作作業

…さて、ファンタジー系音楽、たぶんゲームとかそっち系映画のサントラ的な音楽が出来上がればいいんだろうと考えました。馴染みはあるジャンルなのですが、過去にそういう作品を作ったことは無かったようです(そもそも完成した楽曲と言えるようなものを作ったことが無かった…)。このジャンルならこういうのが好きなので100年後くらいには作れるようになりたいですね…

kai-you.net

今回は過去に打ち込んでいたものを何曲か使い回しているのですが、ファンタジー系というので、何かひとつ大仰なやつをゼロから作ってみたい…!と思って、ある時ちょっと20秒くらい浮かんだフレーズから作り始めました(1曲目)。最終的にはそこまでの大編成にはなっていないはずですが、オーケストラのスタンダードな音色でそれっぽい楽曲が打ち込めた気がします。まあほんの3分くらいなのですが…! もっとも、MIDIファイル上ではトランペットやトロンボーンが(知見が無くて)音域を逸脱したところまで演奏しているのを、Tracktion上ではSession Hornsにフォローさせている…みたいなところはちょいちょいあります。*10

昔打ち込んでいた自作曲を引っ張ってきたものは、当時プログレに傾倒していた(今でもか…?)こともあって、今回の収録作品には妙な変拍子が多いです(極端なのだと23/8とかあります…)。一方で楽曲の作り込みの甘いところが多いので(テクニック不足の問題…!)、まあ多分こういうちぐはぐなのはありがちですよね…

あとは…不気味な雰囲気の曲も作ってみたのですが(デモ2曲目)、これは全音音階とかを見よう見まねで使ってみました。音楽理論どころかコードも身についていないのはちょっとコンプレックスがあるので、そのうちどうにかしたいですね。

作業は全てgithubのプライベートリポジトリ上で記録されています。gitでこまめにcommit/pushしているので、何ならブランチも作ったりしているので、思いついたアレンジを気軽に試すことが出来ますし、特にMMLは意味のあるdiffが取れるので完全に便利です。Tracktionのほうは、前回のエントリでも書きましたが、tracktioneditファイルはXMLなのでこれも差分が見えます(あのエントリの内容もこの制作作業の一環として行われたわけです)。もっとも、一番知りたいのはオーディオプラグインのパラメーター設定などであって、これはオーディオプラグイン依存のバイナリなので、ろくに比較できませんしdiffで内容を追う時にたいへん邪魔です。

デジタルデータのダウンロードカードによる配布をメインにする予定なのですが、ここには技術同人誌を作成していたときと同じノウハウが使われることになるでしょう。

MMLおよびTracktionデータの公開(予定)

今回のアルバムのソースとなる打ち込みデータは公開する予定です。運良く(?)わたしが使っているSC-8820をお持ちの方は、MIDI版の楽曲をそのままフルで再現できることになります。MMLだけ公開としてコンパイラを使ってほしい…と言ってもいいのですが、多分SMFも公開すると思います。MMLコンパイラのサンプルとなるので、楽曲はMITライセンスで公開されることになるでしょう。

公開する理由はシンプルで、MMLコンパイラの実用例であることを示し、それによってコンパイラの価値を高めるためです。あと、MMLを公開することには、ソースから知見を吸い取って再利用してほしい、かつてJASRACが「MIDI潰し」を行う以前にあった「古き良きMIDI文化」、ジットレインが「生成的インターネット」と呼んだものを、少しでも取り戻せるように…という思いもあります。

同様に、Tracktionで打ち込んだデータも公開するつもりなのですが、こちらはオーディオプラグインを多用しているので、同じプラグインを全部持っている人にしか再現できないものになるでしょう。このプロプラエタリに染まったデータをどのようなライセンスで公開するかは未定です。Tracktion自体は7まで無料で公開されています。

いずれの版も、「自作MMLコンパイラでここまで出来る」ことを示す、ポートフォリオ的な意味合いがあるので、単独でも聴く価値のある…というと大袈裟なので「破綻していない」くらいの…楽曲を作るというのは重要な目的のひとつです。(という文を書いている現時点で全楽曲が完成しているわけではないので、実のところ今も焦っているわけですが…)

next steps

この自作MMLコンパイラは、少なくとも対象をMIDIとして、21世紀にふさわしい利便性*11クロスプラットフォームで実現するという目的を実現しつつあると思っています。一方で、音楽制作のメインストリームであるオーディオプラグインなどを活用した楽曲制作の置き換えまでには至っていません。この辺りは完全に別の世界として棲み分けがあるというのが現状でしょう。

DAW全盛期と言うのが相応しそうな現代ですが、ソフトウェアが古臭くなっていく一方で、Web MIDI APIやWeb Audio APIの登場、それらを踏まえたWeb Audio Modulesのような新しい技術*12の登場を考えると、DAWのように「個人ではとても作れない」巨大なソフトウェアを因数分解して、部品ごとに再利用し、打ち込みスタイルも多様化していく時代が来ても良いのではないかと思っています。MMLはその手法のひとつに過ぎず、またMMLの適用先としてDAWと同等の音楽を制作できるオーディオプラグインだらけの世界があっても良いと思っています。

制作過程のところでも言及しましたが、テキスト表現はgitなどVCSとの相性が抜群に良いです。思いついたアレンジをコメントアウトして残しておくのも簡単ですし、加工もそれなりに柔軟にできると思います(まだまだDAWのほうが得意な場面もたくさんありますが)。

そしてオーディオプラグインを利用した音楽制作のシーンは、もっとオープンに行われてほしいし、短期的にはわたしが日々使っているLinuxデスクトップ環境や新しいChromeOSのような環境でもシームレスに行えるようになってほしいと思っています。VST3はLinuxでもビルドできますし、JUCEならLADSPAもいけるし、オーディオプラグインのベンダーにもどんどん参入してもらいたいですね。

この音楽制作が終わったら余裕ができるので、こういう方向性で新しい仕事を探そうかなと思っています。*13

*1:サークル案内にあるように「作り方」みたいな本を出すつもりだったのですが、これはナシかもしれないです…少なくとも電子版になるでしょう(__ あとCDはジャケット印刷が間に合わないかもしれない…

*2:近いジャンルで名前がかぶったのですが、はてなに来る前のアカウント名であってgingaレーベルではないです

*3:最近rebuildfmでこっそり話していたんですよね…

*4:サークルの募集要項を見ると分かるのですが、定義は主観的でもやっとしています

*5:Linux環境で動作するDAWの選択肢は「ある」けどそれなりに限られるのです

*6:サウンドフォントについては、以前からfluidsynthにパッチを送っている程度にはいじっていて使い方がわかるのです。偏った知識…!

*7:特にLinux版JUCEで全くサポートされていないVST3を動かせるように

*8:わたしはPC所有者が自由にソフトウェアを動かすことの出来ないOSを作る会社がきらいなんですよね…

*9:とは言うものの、Mac環境のサポートはまだ新しい方向性で開発が始まったばかりです

*10:逆に知見が無くてもこの辺を使うと何とかなるのか…!という感じでした

*11:まだテキスト音楽サクラに及ばないところがちょいちょいあるのですが

*12:WAMs自体はC++などを前提とした従来のスタイルに見えるので、新しいと言ってしまうと実は個人的には抵抗感がありますが…

*13:とはいえ、私の知る限りそんな仕事は皆無ですし、まだ週休七日でもやれることはたくさんあるので、今のままでも良いのですが。