6月のmusic tools hacks, あるいはLV2 for Android

もっと早くに実現していたはずだったのですが、体調不良などに引きずられて今頃になって台湾に渡ってフラフラ過ごしています。人に会う用事が毎日のようにあって、日本の引きこもり生活とは雲泥の差が…

無駄話はさておき、5月にAndroid Native MIDI APIについて書いたときに、こんなことを書いていました。

Google I/Oでこの方面でなんか発表してほしいなあというお気持ちの表明だったのですが、どうやら何も出てきませんでした。まあGoogleがやらないのであれば自分でやるしかない感じですよね…

とはいえ、何もないところから仕組みを作るのはいささか心もとないので、まずは5月にいじっていたLV2やlilvをAndroidに移植するところから着手しました(ホントはこれウソ入ってるんですが、まあ今全部書かなくてもいいでしょう)。

Androidサポートとは具体的にはFILEからの読み込みをAndroid Assetからの読み込みに変更するとか、direntを使ったディレクトリベースのプラグイン検索を無効化するとか(今回この実装を書いていて初めて気付いたのですが、Androidのassetはディレクトリ構造になっているくせにディレクトリのiteratorは無いんですね…)、ちまちまとした変更ではあります。しかし、元のライブラリが慣れないRDFやTurtleを扱うものなので勝手がわからない、慣れないCコード、ビルドがcerbero経由でしかなくgithubに確認なしでcommitしないとpullしてビルドできない、lv2の有用そうなやつをビルドするにはlibsndfileやcairoもビルドしなければならない、lv2まわりではcerberoが想定していないwafが使われている…といった諸事情もあって、割と手間取りました。たぶんここまでAndroid NDKでビルドできるようにしたやつ、cerberoのAndroid方面担当者以外では自分くらいしかいないのでは…

ソースはだいたいこの辺にあります。

LV2には、ドキュメントでも言及されている公式サンプルと、vst3sdk用のサンプルであるmdaというこの方面では割とポピュラーなもの(?)を移植したものがあり、今回Android向けにビルドした際にはこれらもビルドしています(正直プラグインはコードまで確認していません。どうせやっていることはオーディオバッファの加工くらいだろうし)。

今試験的に作っているサンプルアプリ(これはまだソース公開していない)をDeployGateで公開しているのですが(ってこのリンクもしかしたら無効なのかも…ログインしないと見られないのですが、わたしは自分のアカウントでしかログインできないので確認できない…)追記: やっぱ開けなさそうなのでとりあえずdropboxに一時うpしました、UIがまだメチャクチャで…まあそのうち使えるようになると思います(!?)。アプリ内のプラグインのスイッチを入れるとそのプラグインが適用されたwavの波形が描画されて再生もできるという感じです(再生は加工前のwavにもできるのですがwav表示がおかしい…)。また、このアプリ、諸般の事情からminSdkVersion 29なので、動作させることが出来る端末は世の中に1%も無いという…このへんは本番が出せる頃には改善したいと思っています(!?)。

また、これらのLV2プラグインをサービスとして列挙する仕組みまでは作ったのですが、それ以上は現状何もできていません。サービス側のスイッチを入れてもダミーのforeground serviceが開始されるだけです。

そんなわけで来月もこの辺のコードを書くことになると思いますが、忙しくなりそうなので手が回らないかも…とりあえず、LV2がAndroidで動く、というだけでも(当然プラットフォーム別に実装されるGUIのサポートは無くても)割と面白いんじゃないかと思います。ただもう少し一般化したいところではあります。LV2ほとんど利用例が無いので。

ちなみに、似たようなことをvst3sdkでも出来ないかと思ってビルドを試みたのですが、(X11なvstguiは論外として)Android NDK r20で実装されていないstd::experimental::filesystemLLVMではlibc++fs)に依存していたり、GCC固有の__gnu_cxx::__**atomic**_add()に依存していたり(Android NDKにはもうGCCが含まれていないのです)と、まだビルドできる状況ではないということが分かりました…(atomicはstd::**atomic**_fetch_add()で書き換えられそうではありますが)。幸いlibc++fsはNDK r21を目標に実装されそうなので、待っておこうと思っています。

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

ここ数日ばかり、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