JUCEでカスタムAudioPluginFormatをホストする

そろそろ忘れそうなので備忘録的に書いておく。

自分で独自のオーディオプラグインフレームワークを開発していて課題になるのは、自分でフレームワークからアプリケーションまで全てを開発するのは不可能だということだ。そういうわけで可能な限り既存のリソースを使い回すためにJUCEのバックエンドとしてサポートを追加しようと考えるのは割と自然なことだろう*1

JUCEではオーディオプラグインをホストする機能はjuce_audio_processorsというモジュールでコア機能とVST, AU, LADSPAのホスティングをサポートしている。幸いJUCEに独自のオーディオプラグインを追加するのは難しくない。今回はこれをざっくり解説する。

念のため明記しておくが、今回の主題はホスト側のみであって、プラグインを作る側ではない。プラグイン側は後編としてこちらにまとめた。

atsushieno.hatenablog.com

目次

自分のAudioPluginFormat派生クラスを作る

JUCEでは、ホストする対象となるオーディオプラグイン仕様は、juce::AudioPluginFormatから派生するクラスとして定義する。VST3PluginFormatとかAudioUnitPluginFormatといったクラスがこのモジュールの中でも具体的に定義されている。juce_audio_processors内部に自分のクラスを追加する必要はないので、自分のモジュールを作ってメンテナンスするのが一番楽だろう*2。モジュールの作り方は以前に解説してある。

atsushieno.hatenablog.com

AudioPluginFormatクラスにはいくつかオーバーライドすべきメンバーがあるが、このクラスの基本的な役割は2つだ:

  • (主に)システムにインストールされているオーディオプラグインを検索する
    • そのために必要な検索パスのリストを指定する
    • 対象パスからプラグインを含むファイルを絞り込む
    • 対象ファイルから含まれているプラグイン(群)の情報をPluginDescriptionクラスのインスタンスとして格納する
  • 指定されたPluginInstanceオブジェクトからAudioPluginInstanceインスタンスを生成する

前者はより具体的に書くと

  • getDefaultLocationsToSearch()でデフォルト検索パスを設定し、
  • searchPathsForPlugins()プラグインを含む可能性のあるファイル群をリストアップして、
  • fileMightContainThisPluginType()で引数ファイルにプラグインが含まれている可能性があるか判断し、
  • findAllTypesForFile()で指定されたファイルに含まれるプラグイン(群)を取得する

といった流れで実装する。ファイルベースのOSでない等の事情でこの一連の流れが面倒な場合は、その環境でどういう流れになるべきか、少し挙動を調査・検討する必要があるだろう。

後者はcreatePluginInstance()で実装する。この関数は非同期で戻り、生成したインスタンスは引数にあるPluginCreationCallbackに渡して呼び出すことになる。

PluginDescriptionにプラグインメタデータを格納する

AudioPluginFormat::findAllTypesForFile()を実装する時点で、プラグインメタデータPluginDescriptionというクラスのインスタンスとして返す必要がある。OwnedArrayの引数に結果を追加するので、メモリ解放を心配する必要はない(解放できるメモリのポインタを渡す必要がある)。PluginDescriptionは派生して定義するものではない。プラグインフレームワークでメモリ管理すべきものをここに含めるのは適切ではないからだ。

個々のプラグインPluginDescriptionのメンバーの値で識別できる必要がある。fileOrIdentifieruidが有用だろう。またKnownPluginListクラスなどで検索結果をローカルにキャッシュする仕組みを活用することから、識別できる値はプロセスごとに変わってもいけない。

自分のAudioPluginInstance派生クラスを作る

メタデータ処理が終わったらいよいよjuce::AudioPluginInstanceクラスを派生させてプラグインインスタンスを生成する。このクラスはjuce::AudioProcessorの派生クラスで、実際にはオーディオ処理の大半はこちらで行われている。

生成されたインスタンスは次のような流れでオーディオ処理を走らせることになる。いずれもアプリケーションからコールバックされる前提で実装する。

  • prepareToPlay()プラグインをオーディオを処理できる状態にする
  • processBlock()でオーディオバッファとMIDIバッファを処理する
  • releaseResource()で最初に準備したリソースを解放する

またエディタUIを表示したりUIで編集したデータを読み書きすることもある

  • createEditor()でエディタUIを表示する
  • getStateInformation()でstateをプラグインから取得する
  • setStateInformation()でstateをプラグインに反映する
  • getNumPrograms(), getCurrentProgram(), getProgramName()などでプログラム(プリセットなど)を取得する(set...()で設定もできる)

最低限のプラグインフォーマットのサポートは、このクラスの純粋仮想関数を全部実装するだけでいける。ただ純粋でない仮想関数の中にも絶対に実装すべき重要なものが含まれている可能性は多分にある。Busの設定やチャンネルレイアウトの変更通知などが純粋仮想関数になっていない。いったん定義してしまった抽象クラスに後から純粋仮想関数を追加するとAPI互換性を破壊することになるので、後からメンバーが追加される時は必須であるべきものであっても純粋仮想関数にならない。

ちなみにprocessBlockMIDIメッセージを処理するにはタイムスタンプを付加し考慮する必要がある。一般的なMIDIアプリケーションであればあまり問題にならないかもしれないが、オーディオプラグインではオーディオ処理のためのと合わせてMIDIメッセージも流れてくるのが一般的だ。なぜなら関数呼び出しというのはコストが比較的高いので、何回も頻繁に呼び出せるものではないからだ。しかしオーディオバッファはそれなりの大きいチャンクに分けられて呼び出されるので、MIDIメッセージにタイムスタンプがないと、長い場合は数百ミリ秒のレベルでしかイベントが処理されないことになる。これでは音楽にならないため、MIDIイベントにはタイムスタンプが付加されて、プラグインがこれをよろしく処理する、というのが一般的だ。

自分のAudioPluginParameter派生クラスを作る

ここまでのタスクをこなすだけでも、JUCEアプリケーションからオーディオ処理を呼び出して実行できるようにはなっている。ただしプラグインのパラメーターを調整しようと思って、たとえばプラグインパラメーターを調整するUI*3を表示しても何も表示されない。パラメーターの定義はAudioPluginInstanceに追加してやる必要があるのだ。

言い換えれば、パラメーター情報のクエリはメタデータのレベルでは不可能で、インスタンス化しないと出来ない、ということでもある。これはVSTのような仕様でサポートされていないということだろう。

いずれにせよ、AudioPluginInstanceインスタンスを生成するたびに、AudioPluginParameterというパラメーター情報と操作の両方を実装するクラスのインスタンスを生成して追加してやらなければならない。一般的には自分のAudioPLuginInstance派生クラスのコンストラクターで実装することになるだろう。

このクラスではgetValue()setValue()getName()などを実装するだけで、難しいことは特に無い。もっとも、「パラメーター」のセマンティクスは必ずしもオーディオプラグイン仕様によっては存在しないので(たとえばLV2にはportの概念しかなく、portはデータを送信するためにあるのであって値を取得することは前提となっていない)、仕様次第では何らかの調整が必要になる可能性はある。

自分のAudioPluginFormatをアプリケーションから使う

JUCEはアプリケーションを全てソースからビルドするような仕組みになっている。JUCEのフレームワークを直接参照しているプロジェクトに後付けで自分のプラグインフォーマットをサポートさせることはできない。既存のアプリケーションで自分のプラグインフォーマットをサポートしようと思ったら、アプリケーションのコードにサポート追加のためのコードを書かなければならない。

幸い、独自プラグインをサポートするためのAPIは用意されている。AudioPluginFormatManager::addFormat()を呼び出すだけだ。AudioPluginFormatManagerは一般的にはオーディオプラグインを扱うアプリケーションごとに生成されているはずなので、その部分に1行追加するだけで足りる。

*1:JUCE以外のプラグイン開発フレームワークでもアプリケーション資産があれば対応を考えるところだ

*2:一旦内部に作ってしまうと自分のforkを延々とメンテし続けることになってしまって維持するのがしんどくなるだろう

*3:自分で実装する必要はない。JUCEにはパラメーター定義から設定コントロールをシンプルにリストアップできるクラスがある

1月までの開発記録

前哨戦

2019年はちょっと仕事をしてすぐ辞めてまた無職に戻るという謎ムーブをしていましたが、それは主として自分のやりかけのプロジェクトをかたちにしてそっちの道を進んだほうが良いと考えたからでした。

11月下旬から週休5日になってだいぶ余裕は出来たのですが、ADC2019に参加した後TechBoosterの冬コミ(C97)向け同人誌のためにJetpack Composeについて調べて25ページくらいの解説記事を書いていて、自分の本来の活動は12月中旬になってようやく再開しています。

そういえばここで宣伝していなかったんだけど、この同人誌はboothから入手できます。Jetpack Compose、Flutterが実現できなかったwidgetをクラスでメモリ中に残さずに関数で処理できるモデルを実現しているのは最先端で面白いところで、それをKotlin Compiler Pluginで実現しているんだよ、みたいな話をComposeのソース(androidx.uiとandroidx.composeの両方)を追っかけながら解説しているやつで、多分まだそうそう書かれていない内容だと思います。さいわい前章もComposeでComposeのコードがどんなもんなのか解説されているので単体では置いてきぼりになりそうな人も安心です(?)

techbooster.booth.pm

12月までの半年分の地下活動

本題に戻って。去年の5月くらいからAndroidでも動作するオーディオプラグインの仕組みがあるべきだし無いなら自分で作るしかねーかなーとか思っていました。っていう話を6月に書いていました。これまでの時点でAndroidでリアルタイムオーディオでメッセージングするための基盤は出来ていたし、Google I/O 2019で何かしら発表されていてほしかったけど何も出なかった、という話を書いていました。

atsushieno.hatenablog.com

オーディオプラグインの仕組み、プラグイン側のAPIはずいぶんシンプルな内容みたいだし、自分でも何か作れるんじゃないか?と思って、5月からJUCEで独自のAudioPluginFormatを作る辺りから実験的に非公開でコードを書き始めていました。

https://github.com/atsushieno/android-audio-plugin-framework/tree/1f28c8f

JUCE APIの中身を埋めながら、LV2を組み込めるような感じで実装していました。ただLV2のバックエンドを作りたいわけではないので、自分のフレームワークのブリッジとして実装しています。ひと月くらい後にREADMEを書いた時は、まだガワしかないのですがだいぶソースツリーのリポジトリっぽくなりました。(こんな状態だったのか…みたいなのが見えるようにチマチマ書いてます)

https://github.com/atsushieno/android-audio-plugin-framework/tree/1431d9e

この辺から、ちゃんとガワだけじゃなくて実装も作ってAndroidアプリとして動かそう…みたいになっていって、2ヶ月目が終わるくらいにはVSTからLV2に移植されたmda-lv2のエフェクトプラグインを適用できるくらいになっていました。AndroidでLV2がちゃんと使えることがわかったので、方向性としては適切だったこともわかりました(これが6月の上記エントリーの段階)。

https://github.com/atsushieno/android-audio-plugin-framework/tree/7c0715e

その後この辺の作業は7月上旬からお試しで始めた仕事でだいぶテンポダウンしてしまったのですが、半ば週末プロジェクトになっていきましたが、いよいよホストとプラグインのアプリケーションを分離して、DAWとオーディオプラグインが別々のアプリケーションベンダーになっても問題なくいけるような、AIDLとndkbinderを使った通信の実装に入りました。ndkbinderが入るとminSdkVersion 29になって未来感が溢れてきますね。ndkbinderは特にまだbindService()に相当する機能がまだ無い…ということがAPIドキュメントからは読み取れず、これを実装しようと躍起になってたくさんの時間が消えていきました…。

https://github.com/atsushieno/android-audio-plugin-framework/tree/5a4b697

プラグインフレームワークとしての最低限の体裁も整えていましたし、ここまで出来てくると、そろそろ本業にできるんじゃないかコレ…?みたいな気持ちになってきますよね。とはいえこの頃から仕事がフルタイムになって開発が完全に止まってしまい、どうしたものか…となっていました。10月に踏ん切りをつけてこっちをやっていこうってなるまでは。実際この後11月までノーコミットです。

11月中旬にADCでAndroidオーディオチームに1 on 1で相談するチャンスがあったので、この時点までで出来ているものを見せながら、このアーキテクチャでオーディオプラグインの仕組みを実現しようと思っている、基本的にはiOSのAUv3と同じようなもんだからいけるんじゃないかと思っている、みたいな話をしてきたのでした。実際には1 on 1というのはウソで、チーム全員vs.わたし1人で、裁判か…!みたいな感じでしたが、控えめに言えばだいぶ受けが良かったです。後で「うちで仕事しないか」って言われるくらいには。まあソースのスパゲッティを見てないから()

(およそ)1月の活動記録

さて週休5日になってゆとりが出来た…かと思いきや、冒頭に書いた別のタスクに追われ、11月にもちょっとだけコミットがあるのですが、ほぼ「8月には出来ていたビルドがもうできなくなっていたから直し続けた」状態でした。なので開発が本格的に再開できたのは12月下旬になってからでした。

直近のおよそひと月は、まずmonorepoを解体してcerbero依存まわりのビルドの簡略化を図るところから始めました。Android向けのLV2関連バイナリはcerberoの依存部分も含めて全て切り離してビルドしています。GitHub Actionsで自動的にバイナリをリリースできるようにしたので、以降ここはノータッチでいけるようになって心理的負担が軽くなりました。他人にも試してもらいやすくなったし(cerbero依存になった時点でほぼ自分しかビルドできなかったはず)。AndroidでLV2を使いたい人はここからバイナリを拾っていけます。

github.com

ビルドが軽くなったので、いよいよJUCE統合に取り掛かりました。プロジェクトの発端になったJUCE用モジュールがついに組み込まれるわけです。juce_gui_basicsがAndroid上で動作するとわかったので(これは知らずに始めていて、単にオーディオ部分が使い回せるだけだと思っていた)、付属のAudioPluginHostも動かせるのではないかと思ってやってみたら、フレームワークの修正ゼロで動かせたので、光明が見えました。JUCE…神なのでは…!?

この頃から、そろそろソースを公開しても良いだろうと思うようになりました。もともとオープンなフレームワークとして使えるようになるといいなと思って作っていたものだったので、クローズドのままでは意味がなかったわけです。ただソースはぐちゃぐちゃだし、まだ外部の人にいじってもらえるような状況ではない(バグも罠もたくさんあるしガンガン破壊的変更が入る)ので、あまり宣伝はできないし早すぎる公開が予期しない採用と持続困難をもたらすのは好ましくないと思っていました。しばらく悩みましたが、ソースは公開して、宣伝はしない、という方向性にしました。

この時点でソースを公開するというのは、どちらかといえば必要な時に他人に見てもらいたい時(たとえばADCで会ったGooglerに相談したい時)に有用なのでそうしたわけですが、他の誰にもビルドできない状態で公開してもちゃんとした相談にはならないので、この頃からいろいろCIセットアップを試行錯誤しています。現在はBitriseでフレームワークとKotlinのサンプルがビルドできている状態です。実のところJUCEまわりは未だに解決しておらず厳しいのですが、これはBitriseのUbuntuのDocker imageがUbuntu 16.04なのとAndroid SDKにndk-bundleがある頃の古い仕組みに基づいているせいなので、いずれ何とかしたいところです。

公開問題が片付いた(?)のでJUCE統合の作業に戻りましたが、これがビルドは通っても全く期待通りに動いてくれない…そして問題の切り分けが超絶困難なわけです。JUCE統合のやり方が間違っているかもしれないし、LV2やlilvの使い方が間違っているかもしれないし、何ならAndroidオーディオの使い方もおかしいかもしれない。しかもLV2関連バイナリはビルドに手を加えるのが困難でデバッガでも追えない…。

この状態では何も進展しないので、そろそろ頃合いかと思ってLinuxデスクトップ版をビルドできるようにしました。プラグイン開発者としても、デスクトップで開発してそれをAndroidに移植したほうがおそらく楽でしょうし。デスクトップで使う場合のプラグイン環境構築の方法なども規定しました。デスクトップにはAndroid binderの仕組みがないので、代替をどうするか考えたのですが、とりあえずインプロセスで全てロードしてやり過ごすことにしました。このほうがデバッグも楽ですし。アウトプロセスモデルデバッグする必要が出てきたときのために、いずれスタンドアローンサーバーは用意しようとは思います…。

フレームワーク本体がデスクトップで使えるようになって、JUCEのAudioPluginHost移植版もデスクトップで動くようになったので(デスクトップ版にAndroidプラグイン機構の加工を加えてからデスクトップに戻したというわけです)、問題をいろいろ切り分けられるようになったのですが、LV2とMIDIメッセージのやり取りをする部分がだいぶ難解(というか仕様が不鮮明)で、ここに多くの時間をとられました。

そもそもオーディオプラグインMIDIメッセージを処理するのは単純なタスクではありません。一般的には、オーディオバッファとMIDIバッファは1回のオーディオ処理サイクルでまとめて処理することになります。なぜならMIDIメッセージの処理だけを個別に回すのはスレッド切り替えコストがもったいないからです。そのため、単純なMIDIイベントのバッファを渡すだけでは足りません。MIDIサポートのためにLV2では7,8年ほど前に仕様に破壊的変更を加えてAtomというメッセージフォーマットを規定しました。MIDIメッセージにタイムスタンプを付加しないとリアルタイムで渡ってきたものを適切に処理できないためです。

ただMIDIメッセージのタイムスタンプの扱いは難しく、LV2では文面上は規定がありますがbeatTimeのセマンティクスは不明ですし、JUCEに至ってはホスト依存となっています。この辺は自分のフレームワークでも完全には整備できていません。とりあえずdivisionとdeltaTimeがSMF互換になるデータとして規定しています。

ともあれ、多くの時間を費やしはしたものの、全てのレイヤーに存在していた諸問題を解決して、ついにJUCEの自分用AudioPluginHostでキーボードからMIDIメッセージを送信して、エフェクトプラグインとチェインしたシンセを鳴らすことに成功しました。LV2で作られたオーディオプラグインについては、GUIサポートは無いものの音声合成に使うことが可能になり、JUCE製のホストなどを使えばアプリケーションから呼び出してパラメーターを調整することで操作可能なところまでは出来ています。

www.youtube.com

ここまでで開発のマイルストーンの一つ(個人的には大きなやつ)をクリアしたことになります。なので今回これをまとめた、という側面もあります。まあ月末なので活動報告を書くにはいいタイミングです(それがメイン)。

これからどうするか

このプロジェクトは、プロジェクトとしてはまだまだやることがたくさんあります。GUI統合について何かしらのソリューションを用意する(個人的にはFlutterが適切な解になるかと思っているのですが)、ちゃんとOboeのコールバックを組み込んでndkbinderでリアルタイム処理が可能なのか検証する、ちゃんとしたサンプラーを動かす(juicysfpluginあたり?)、tracktion_engineを使った再生くらいまでは使い込む、などなど…いろいろissuesに書いているので、ちまちまとやっていくことになると思います。

このプロジェクトの最大の目的は、実用的な楽器プラグインDAWAndroidLinux(あるいはそれに準ずる自由なソフトウェアのOS)の世界に持ってきてもらって、自分がWindowsMacに縛られること無く音楽を打ち込めるようにすることなので、いずれ既存のMMLコンパイラを中心とした制作環境からも使えるようにしていきたいと思っています。がそれはまた別の話かな。

そんなわけで、毎日フルタイムで開発業務をやっているようなものなので(給料も何も出ないわけですが)、実はわりと暇してはいない感じです。

Music Tech Meetup #1 (Night)を開催します

ここ1年くらいずっと「音楽ソフトウェア系の開発者とユーザーの両方をターゲットにした音楽技術の勉強会をやりたい〜」などと言っていたのですが、ようやく時は満ちた…! ということで来月2/6に開催します。

connpass.com

今日の正午くらいに募集開始したのですが、マニヤックな分野の勉強会にもかかわらず既に20人くらい申し込みいただいていてありがたいかぎりです。

経緯

1年も前から言っていたというのは割と本当で、去年もちょうど年末くらいから似たようなことを言っていたのでした。2年続けてロンドンのADCに参加していたので、自分たちはその知見を持ち帰って共有できるし、音楽関係のソフトウェアやSDK(JUCEとかVSTとか)などで面白いセッショントークが出来る人が何人かいたので、開催したらきっと面白いことになると思っていました。

去年はそうこうしているうちにmusic.dev勉強会が発表されたので、とりあえず自分で開催しなくてもいいや…!となったのでした。music.devは一般参加が10人しか入れなかったこと以外は理想的で、バリエーションに飛んだトークが集まって楽しく勉強になる集まりでした。

去年はさらにJUCEもくもく会みたいなことをもう少し音楽技術全般をターゲットにして開催してもいいかなと思っていたら、ちょうどMake Music Things!というまさに音楽全般もくもく会のようなものが始まったので、これまた自分で運営しなくてもいい…!となって、2019年は周りの皆さんの活動におんぶにだっこ状態でのうのうとやってこられたわけです(!)

今回は、セッションを伴う勉強会みたいなことがしばらくなかったのと、music.devは主催のケロさんが2月は東京に来られる準備で忙しそうで#2がかぶることはなさそうだったので、ついに自分で開催しようとなったわけです。

せっかくなので数十人規模で開催したいなーと思ったのですが、Make Music Things!の会場は最大10人ちょいになってしまうので、主催の小出さんと開催は一緒にやっていただいて、会場はどこかを借りてやろうとなりました。ちょうどその頃にLAPRAS社の伊藤さん(まだお会いしたことはないです)のツイートがTLに流れてきまして、

これは会場の大きさ的にも雰囲気としても割とピッタリで天佑なのでは…!?となって、ご相談してお願いすることになりました。勉強会は、いつも会場探しが一番しんどいので、ここを簡単にクリアできたのは本当にありがたいです。

勉強会の方針

今回の勉強会は、ある程度自分たちが知っている人に来てもらえる一方で、知らない人だらけだけど日頃からあるような勉強会ではない(けど興味ある)からちょっと顔を出してみよう、という方がそれなりに来られると思っています。人数もどうがんばっても50人くらいなので、なるべく会場でカジュアルに誰でも話せるようなふいんきなぜか変換できない)にしたいと思っています。

もうひとつ、IT系技術の勉強会はプログラミング言語やプラットフォーム、開発ツールなどに関連するものが多く、ほとんどが開発者「だけ」をターゲットにしているのですが、わたしみたいに開発フレームワークを提供する立場にいた人間としては、参加する「開発者」にも「ツールの開発者側」と「ツールのユーザー側」がいると思っています。両方がいて勉強会が成り立っているわけですね。これは音楽系のソフトウェアの場合でも当てはまるはずなので、この勉強会では技術やツールの開発者と同様にユーザーとしてのミュージシャンやDTMerの人たちをターゲットにする、音楽を制作するための技術や知見を広めるセッションもあります*1。ふだんこの方面のセッションを聴けることはそうそう無いと思うので、割と貴重な場になると思います。

今回はセッションがありませんが、音楽技術のとくにゲーム開発の分野では、音楽の制作と同時にサウンドドライバーを制作したり、古くはMMLのようなプログラム類似の言語で作曲したでしょうし(今でも商業でやっている人がいますね)、今でもスクリプトサウンドオブジェクトを制御することもあるかと思います。あるいはミドルウェアを導入したりといった知見も広く求められることがあり、この意味ではサウンドエンジニアも開発者との境界線が割と曖昧だと思います。この分野だとCRI ADX2などの勉強会も別途さかんですが、知見を共有できる人がいたらぜひ来てほしいと思っています。あとはVRなどで3Dサウンド処理もホットなトピックですよね。

この集まりは現状スポンサーがいるわけでもないので、夜の勉強会ですが軽食を用意するのもしんどいです…というのを逆手に取って、参加される方にお気に入りの食べ物などを持ち寄っていただくことにしました。開始と同時に食べ物をつまみながら話を聞いていただこうと思っています。発表が終わるのを待ち続けてフードが冷めちゃったら悲しいので、食べておいしい時に食べましょう。もちろん手ぶらでも大丈夫です。全員がたくさん食べ物を持ってきたら余るでしょうし…!

今回の勉強会では、可能な限り多くの人に抵抗感なく気配りしてもらえるようなCode of Conductを考えて設計してあります。困ったことがあったら相談してください。

セッション紹介

Audio Developers Conference 2019で見てきたオーディオ開発最新動向

わたしが勉強会を開こうと思った発端が「ADCのセッションとかで発表されているような話を紹介して、詳しく知っていそうな人とかに参加してもらってわいわい話したい」みたいな感じなので、同じくADCに2年連続で参加しているJUCE Japanのしおざわさん @COx2 と2人で漫談することになりました。(ってタイトルをわたしが勝手に付けてしまっているのですが、このへんは諸々調整する予定です…!)

ADCにはさまざまなプレイヤーが参加しています。RolandYAMAHAKORGなどの楽器メーカー、Native Instrumentsみたいなオーディオプラグインベンダー、ROLIやSteinbergのようなオーディオプラグインフレームワークベンダー、AppleGoogleあるいはELKのようなプラットフォームベンダー、音楽と機械学習のソリューションを提供するベンチャー企業など、バックグラウンドはさまざまです。2日間で行われたセッションのほとんどはyoutubeで公開されています(1日目2日目)。

現地でも見ましたがフルパワーで見ていたわけではないので、わたしはいろいろチェックして臨むつもりです。

エフェクターを作ろう

Make Music Things!を運営している小出さんによるセッションです。小出さんには他のセッション予定とかぶらなそうなトピックの狭間で決めていただいた感じなのですが(!)、結果的にハードウェア寄りでエフェクターを作るための回路設計についてお話しいただく予定です。お仕事がその方面みたいです。うらやましい…!*2

Make Music Things!の集まりは、ソフトウェアだけしかやらないわたしみたいな参加者も何人かいるし、Maker Faireとかに出展するような物を制作している参加者も何人かいるという感じで、独特のバランスでまとまっています。1人で黙々と作業することもできますし、他の人と相談やら雑談やらしながら過ごす人もいます。割と物理層の話で盛り上がったりすることもあります。

概ね毎月やっていて1/25にも第6回があるので、この辺の開発に携わっている人とわいわいやりたい人がいたらぜひ来てみてください。

変拍子兄さん & 腕毛の人 の変態音楽理論(タイトル未定)

タイトルはまだ未定なのですが、変態音楽理論アカデミーというnoteのページを公開されている変拍子兄さん @OrangeTheKeyqa と、一緒に音楽活動されている腕毛の人 @stereonoah のお二人に、独自に構築されている音楽理論に関する発表をお願いしました(もしかしたらしゃべるのは2人ではないかも)。内容はほぼ白紙でお任せしてあるのですが、たぶん微分音を活かした作曲の方法論などが内容となるのではないかと思います。

微分音というのは説明なしでは「??」となるかと思います…。ふだんわれわれが音楽を打ち込む時に使う音階は12平均律(一般的には12も抜いて単に平均律)とよばれ、周波数の比率で1オクターブの音をド〜シの12音に分割構成しています。しかし世の中には他に純正律と呼ばれる、特定の和音だけがよりきれいに響くような周波数で構成された音階などもあります。さらにはそもそも1オクターブを12で割る必要だって無いはずでは…?と考えると、1オクターブを24分割したり31分割(!?)したりといったことも出来るわけです。MIDIメッセージはどう考えても12音階なわけで、どうやってこれを乗り越えて作曲するのか…? など、いろいろ刺激的な話が聴けるかと思います(白紙委任ですが!)

ちなみに、このおふたりとは昨年の同人音楽イベントでサークルスペースが隣同士だっただけです(ゆ〜くれというこれらの音楽理論などによる音楽を発表しているこのお二人のユニットです)。普段から勉強会でセッションやっているようには見えなかったのですが、音楽理論系のセッションをお願いする場としてはピッタリだと思ったので、おっかなびっくりお願いして受けていただきました(ありがたい〜)。

ライトニングトーク

今回もう1つセッションがあるかもしれませんが、無いかもしれません。その代わりに、というわけではありませんが、LTタイムを設けてあります。おそらく自分が開発したり使っていたりするソフトウェアや楽器、あるいは音楽制作手法などについて喋りたいという方が少なからずいるのではないでしょうか。と思って募集に含めたのですが、既に申し込みいただいていて、「計画通り…!」(画像略)という顔をしています。まだ枠があるのでやりたい方はぜひ申し込んでください。

ちなみに、勉強会参加者枠を別にすることも考えたのですが、LTで何かしゃべれることあるかな…?って悩んで参加枠を迷っているうちに人数がいっぱいになって悲しい思いをすることが稀によくあるので、LT申込み枠は分けずにコメントで書いていただくという設計にしました。いろいろ考えてます…!

Music Tech Meetup #2 (Day) もやりたい

今回は1回目ということで、お試しという感じで平日の夜開催ということにしました。会場も平日限定ですし(そういうところが多いはずです)、まずは平日に開催して感触を得ておこうという腹づもりです。

まだ第1回を公開したばかりなのですが、他にもあの人やこの人にもしゃべってもらいたい…!みたいなリストを(勝手に)かかえていて、「でもこの話なら50分とかでフルタイムでお願いしたい…」みたいな状態になっているので、そのうち土日開催してフルタイムセッションを容赦なく詰め込むのも開催したいと思っています。

そういうわけで今後の展望も意識の隅におきつつ、まずは2/6の勉強会をうまいこと開催したいと思っていますので、この分野に興味のある方は(まだあまり機会が無いと思うので)ぜひ参加してください。

*1:昨年のmusic.devもそうなっていました。

*2:いや無職はひとの仕事を羨ましがらないぞ…!!

さいきょうのCode of Conductを求めて

こんど新しく勉強会を立ち上げるのだけど、2020年に新規コミュニティを立ち上げるのは、自分が今までやってきたような勉強会(Xamarin.Forms読書会とかmono meetingとか)を作ってきたようなのとはだいぶ状況が違うようだ。特に最近よく話題になるのはCommunity Code of Conduct(CCoC, CoC)に対する何かしらの意思決定を求められる、というところだろう。

今回いろいろ考えてCoCをまとめることにしたのだけど、一般的なCoCに比べてだいぶ大きな改善を施したので、それに至るまでにいろいろ検討したことを参考までにまとめておきたい。

目次

完成品

この行動規範は、コミュニティイベント参加者のコミュニティイベントに関連する行為についてのみ適用されます(被害者や視聴者がコミュニティ参加者に限定されることはありません)。

このコミュニティのイベントにおいては、参加者には、性別、性的自認・性的指向、年齢、障害、身体的特徴、人種、民族、宗教(あるいは無宗教)、技術的指向などを理由としたハラスメントの無い状態を維持すべく行動していただきます。コミュニティ主催者は、コミュニティのために、コミュニティ参加者によるいかなる種類のハラスメントも歓迎しません。性的な意図での画像等の表現は、イベント会場での発表や歓談、Fediverseその他のオンラインメディアを含め、コミュニティイベントに関連するものとしては不適切であり提示しないものとします。

違反行為の態様によっては、このコミュニティでは退席を求めることもあり、また以降の参加を認めないこともあります。有償イベントの場合、払い戻しを受けることはできません。しかし、誰もが問題を意識せずに違反行為を行う可能性があります。この行動規範は問題を改善するためのものであり、実効的な改善が期待できるうちは違反を繰り返さないよう注意するに留めることを目指します。

この行動規範に反する問題のある行為を受けたり、困っていそうな人を発見した場合は、コミュニティ運営メンバーに連絡し対応を求めてください。主催は参加者のみなさんが安心して参加できるために必要な処置を、予防的なものも含めて実施します。コミュニティ運営メンバーによる違反行為についても同様に情報提供を求めますが、十分な対応が期待できない場合はFediverseなどのソーシャルメディアに助けを求めてください。ハラスメント処分について不満がある人も同様です。ハラスメントに該当するか否かは規範的な判断が不可避であるため、主催者と当事者では判断が異なることがあり得るためです。

契約書などの規約に著作権は存在しないので(船荷証券事件)、無断で自由に複製したり、自分たちのCoCに取り入れたり、好きなように使っていただいて問題ない。

2022/12/19追記: 本文を一部改定し、"Twitter"をSNSの例示から除外し、Fediverseに置き換えました。

2023/02/03追記: 「主催は参加者のみなさんが安心して参加できるために必要な処置を、予防的なものも含めて実施します。」という一文を途中に追加しました。コミュニティに安心して参加してもらえるためには、主催はCoC違反と判断するか否かに関わらず「当事者の安心を確保するため」に(合理的な範囲で)対応することを明示的に宣言するものです。

CoCの本質は何か

CoCの策定について論じる前に、CoCの本質的な性格について少し前提を共有しなければならないだろう。一般的なCoCとは、実効性(処分)を伴う私的社会集団の規則として実装された法あるいは規範である。

「実効性(処分)を伴う」というのは、CoCに違反した者に対して、コミュニティへの不参加や、法的に有効に支払った参加費の返還を伴わない役務(債務)の不履行が正当化される、ということだ。当事者の合意が無いのに債務を不履行とするのは、法律的には不法行為だ。法的な不利益処分を伴うのだから、本来は「CoCは単なるガイドラインである」という(主にあいまいさに対する)釈明は通用しない。CoC違反による処分は、コミュニティ活動を違反行為によって妨げられるという損害を抑止するという理由で法的な後ろ盾を得る。

「法あるいは規範」というやや中途半端な表現を用いたのは、ローレンス・レッシグがCODEで提示した古典的な四規制力(法、規範、市場、アーキテクチャ)では自明に分類できなかったためなのだが、法律のような強制力を伴う一方で、その私的社会構成員の意思を反映しなければならないという憲法も無く、恣意的に策定される危険性がある、という理解にもとづく表現だ。これを規範に近づけると、究極的にはCoCが無い状態である(構成員の良識に100%依存する、あるいは主催者がそう言明するような事態)。

CoCを策定するのであれば、それは健全な法とならなければならず、参加者の合意(すくなくとも理解)が得られる必要がある。2020年現在は、CoCのありかたを巡って、さまざまな立場があり、それらが衝突している状態にあり、このような闘争状況においては、規範に近いルールの制定であっても、策定内容をめぐる合意について、法律と同レベルの正統性(規制根拠)が必要になるはずだからだ。

また、人の自由を拘束する、特に表現の自由を拘束するものでもあるから、法律に関する表現の自由を制限する場合の正当化に要求される二重の基準なども、適切に満たすことを目指さなければならない。

規則とは雰囲気で身勝手に規定してはならないものであり、策定者は規則の策定を拘束する規則や理念(法律であれば憲法)を遵守しなければならない。「CoCを策定しろ」と要求する者は、コミュニティの代表者に対してそれだけの覚悟を示せ、と言っているのに等しいということを自覚しなければならない。そして残念なことに(?)、現状ではその要求はどちらかといえば妥当なものであると自分は考えている。

CoCを策定することに対する批判的議論とそれらへの対応

CoCの策定にあたっては、そもそもCoCを策定すべきか否か、という意思決定がある。最近はCoCを策定しないだけで非難される・ペナルティを受ける・「差別」される*1という風潮があり、CoCを制定「しない」という決定それ自体が意味のある意思決定になっている状態だ。

CoCを「策定しない」ほうが望ましいと考える立場は、(おそらくアナーキズムにも類する議論が既に多数あるだろうが)自分が理解している範囲だと概ね次のような論理がある。

他人の行為を「禁止」できる「コミュニティ」とは一体何者なのか?

この問題を解決するためには、「コミュニティ」における活動とは何を指すのか、について明確に定義する必要がある。CoC違反に基づく処分を執行することができないのであれば、それはもはや規則ではないし、正当な権利もなく他人の行為を禁止する行為は厳しく制限されなければならない(究極的には業務妨害罪にもなり得る)。

具体的には、コミュニティ活動とは無関係なTwitter上の投稿内容が差別的であったり性的であったとしても、それを理由にコミュニティから排斥することは許されない。それはコミュニティ活動ではないからだ。コミュニティ参加の場たとえばセッショントークにおいて差別的な発言をする「おそれ」がある、と主張する場合には、その「おそれ」は具体的なものでなければ、法律における表現の自由の規制を正当化する「二重の基準」を満たしていない。

なお、CoCを遵守すべき主体はコミュニティ参加でありその守備範囲であるイベント等に付随する場合に限られるが、言及される対象はコミュニティ参加者に限られない。これは現時点で参加していない人の新規参入を妨害してはならないためである。

参加者が合意していないものを強制できるのか?

民主主義社会において、法律は主権者たる国民を代表する国会(日本国の実装の場合)が策定する(そして国会議員は憲法に拘束され、その立法内容は憲法に反してはならない)。法律の場合、国民に法律を遵守する義務があるのは、それが間接的には自らが定めたためである、という理屈で正当化される。憲法学には憲法制定権力は何であったか、という論点で議論がなされるし、刑法で言えば、罪刑法定主義明確性の原則といったかたちで実装されている。

CoCにはこれが無く、あくまでコミュニティ運営者が身勝手に策定しているものだから、少なくとも参加にあたって事前に十分に提示して同意してもらう必要がある。

強制力があるかないか、という問題で言えば、CoCを「執行」出来る場面は限られている、ということは言える。限られてはいるが無いわけではない、という程度のものなので「それならば無いほうがマシ」という再反論は有効だ。

一方で「CoCは憲章であり違反に対応する手続きを定めていることで差別等への抑止力になる」という指摘はあり、この点では具体的な処分が機能しなくても意味があるといえる。

CoCに合意できない一部のメンバーを積極的に排除するために規定するものなのではないか?

CoCを策定する行為そのものが差別的な意図に基づいていないことは必要条件だ。特定個人を狙い撃ちで排除するために定めるCoCの条項は原則として無効になるべきものだ。排除された者が「差別的だ」と感じたら、その主張をきちんと取り上げて判断する公正な機関が必要だし、無ければSNS上でコミュニティが非難されることになる。

ここには三権分立の機能していないコミュニティという組織に特有の問題があり、コミュニティ運営者が自ら定めたCoCおよびそれに基づく処分について、自らが違反であると判断することはおそらく稀なので、公正な判断機関に準ずるものとしてSNS等における「裁判」が機能する余地がある。SNSポピュリズムで動く側面があり公正であるとは言いがたいが、少なくとも別の視点をもたらし得る存在ではある。排除される者がSNSに助けを求められるということを、少なくとも明記しておくことは、より公正なルールを担保することに繋がる。

善良なコミュニティには「ルール」を規定する必要はない。「真面目な」生徒が大半を占める中学校・高校には校則がほとんど無いではないか?

これには部分的に正しい側面があるが、試験等で生徒をフィルターしてきた学校とは異なり、CoCを規定しなくても問題がないというような議論が出てくるくらい一般的なコミュニティはほぼ「差別的であってはならない」だろうし、どんな参加者でも最初は歓迎する義務があるはずだ。となると、「善良な」構成員しか集まらない、という主張は、一般的には論理として成り立たないといえる。

違反の判断基準が恣意的ではないか?

正当な批判であり、100%対策されているので問題ないと反論することは論理的に不可能であると考えられる。

これは刑事法では「規範的」判断といわれるもので、たとえばわいせつ物陳列罪における「わいせつ」の範囲は何か、といった、明確な答えのない問題になる(そのため同罪には根拠が無いという批判も有力だ)。CoCの場合は、単に「差別はいけません」のような言い方では明確性の原則に反するので、少なくともどういう項目で差別該当性が判断されるかを列挙することが望ましい。

ただ限定列挙にするほど厳密にするのは難しいかもしれない。理想をいえばCoCは常に更新されるべきであるが、現実には追いつかないだろう。フィクションを前提にした規則は、空洞化し悪用される可能性が上がる。

いずれにしても、問題となりうる「曖昧さ」を可能な限り縮小して、CoCが無いことによる不利益と比較衡量して納得してもらうしかないだろう。

処分の有無の判断基準が恣意的ではないか?

正当な批判であり、100%対策されているので問題ないと反論することは論理的に不可能であると考えられる。

これは刑事法でも同様なのだが、犯罪に対して処罰が固定されることは通常はあり得ず*2、通常は情状を酌量される。また通常は初犯に対しては執行猶予がつく。法律に恣意的な運用の問題が無いわけではないが、法律でも同程度の問題が生じているので、少なくともCoC固有の問題ではない(規則を定めれば定めるだけ恣意的な処分が世の中に増えるということになるのだから、「法律もそうだから問題ない」という反論は成り立たない)。

一方で「公平性を担保するため、常に退会処分とする」といった運用に傾くと、同じ違反行為を犯すならより悪質な行為に走るということにもなる(饅頭を盗み食いしても殺して財布を奪っても死刑なら、後者のほうが利益が大きい)。「注意」のような処分すなわち執行猶予がつくほうがコミュニティ全体としての安心感が上がるはずだ。

こういった対策で問題の生じる可能性をなるべく縮小して、CoCが無いことによる不利益と比較衡量して納得してもらうしかないだろう。

CoCで策定されるべき事項とそうでない事項を検討する

2020年時点で議論されている「CoCは必要である」という議論に一般的に欠落しているのが、次のような問題に対する真剣な検討である。

  • CoC適用の公平性担保
  • 正当な理由のない禁止の禁止

これらはいずれも「コミュニティ運営を拘束し負担を大きくする」ものである。自己中心的なコミュニティ運営者がこれらの問題や議論を避けるとしてもそれは本能だが、正当化される本能ではない。これらの議論を回避することで被害を受けるのはコミュニティ参加者なのである。

CoC適用の公平性担保、特にSNS等に訴えかけられるという次善策の提示

三権が分立した法律の世界のイデアにおいては、立法者と司法者は別個の機関であり、これによって法の公平な執行に対する期待値が上がる。CoCの対象であるコミュニティには通常そのような体制は無い。しかし、コミュニティ運営者が「差別はいけません」などと言いながら、一方では自分の嫌いな参加者に対して差別的に待遇していたら、CoCは正当化しえない。こういう場合に備えて、まず運営者もCoCに違反する可能性があることや、そのような場合に被害者がどのような対応を取ることが出来るのか、提示しておくことは重要だ(もっとも「SNSで訴えかけると良い」というくらいしかできないが)。

SNSで訴えかけることが出来る」と言明しておくことにはもうひとつの効果がある。「SNSで公に指摘するべきではなく、運営に相談すべきである」という言い分を否定することができるのだ。SNSで指摘するのは本人がしたいからそうするのであって、それを強制的に隠蔽するべきではない。もちろん運営が適切に対処していて、被害を受けたという主張が正しくなさそうであれば、虚偽を拡散したという非難を免れることはできないが、それは本人の責任だし、「被害を100%立証できなければ公に発言すべきではない」という主張は、個別の事実関係なしには語れない以上、妥当ではない。被害者が被害を受けたという事実を、本人の意思に反してコミュニティ運営者が隠蔽するような状態を、コミュニティ運営が正当化することはありません、という意思表明だ。

もちろん、運営が相談されたら対応するのは当然のこととして、CoCに規定する。

正当な理由のない禁止の禁止と「非技術的なノイズ」問題

CoCは強制力のある法としての側面があるのだから、正当な理由のない禁止条項は排除されなければならない。一方で、CoCは行為規範として容姿等に言及する行為を禁止対象として列挙するのが一般的だ。

この禁止を正当化する理由としてよく挙げられるのは「外見・容姿に関する言及は非技術的な評価であり、(技術的な勉強会においては)ノイズであるから有害である」という立て付けの議論だ。もうひとつ「外見・容姿に関する言及は身体的特徴に関する差別的なものである」という考え方がある。自分は、一般的には後者を否定する余地はなく、禁止は妥当であると考えている。前者については、状況に応じて禁止すべき時とそうでない時があると考えている。

非技術的な情報が技術勉強会においてノイズになることは間違いない。しかし「ノイズであれば排除して良い」ということは必ずしも言えない。二重の基準論で考えれば、ノイズを排除しなければ他の利益が損なわれる(損なわれた)など、表現を規制するに足る正当な理由が必要になる。外見に(非差別的に)言及する*3参加者がいた結果、まともに勉強会として参加できる雰囲気ではなくなった、という事態があれば、それは禁止に値する有害なノイズであったと言える。

何がノイズになり、何がノイズにならないかを事前に予測するのは、多くの場合は容易ではないが、一方で法の執行には「予見可能性」が無ければならない。独自の視点で「この発言は差別的ではないか」と気付く参加者もいるかもしれないし、言われれば同意する参加者もいることだろうが、発言者にそのような意思が無かった可能性をきちんと考慮し、まずは配慮を促すという対応が必要である。「望ましい」ではなく「必要である」とする根拠は「予見可能性」だ。人は自らが予見できなかった行為・結果について責任を問われることはない。故意が認められないためだ。

また発言者も含め他の参加者が不同意を表明する自由は、必ず守らなければならない。

「非技術的なノイズ」の問題は、参加者がSNSなどで外見・容姿について言及する場合だけでなく、講演者が自ら非技術的な興味を引く目的で外見を調整する行為にも当てはまる。これを「どこまで許されるか」という議論にすると答えの見えない問題になるが、視点を変えて、たとえば外見を整えるために参加者の手を借りたり「いい写真」の撮影を依頼するような行為があれば、それは非技術的なノイズを増やす故意があると言える。

同様に、外見・容姿にかかる言及は「優遇」であることも多く、「被害者の同意」がある場合もある。これは他の講演者や参加者と相対的に比較する状況や側面があれば明確に有害だし、そうでない場合でも参加者が自らの容姿・外見等を意識させられるような状況が生じるのであれば、それは差別的言及である。特定メンバーに対する外見的な優遇は外見的な差別であることを意識すべし。

いずれにしても、容姿・外見等に関する言及があった場合は、当人らがネガティブに感じるようなものであれば十分にCoC違反の条件を満たすし、そうでない状況でも当人の故意の有無などをきちんと個別具体的に把握して、実害のあるものは排除するよう「毅然と」=公正に対応する必要がある。単に積極的に科罰的に振る舞うことを「毅然とした対応」と勘違いしてはならない

障害からの回復という目的を忘れない

人は誰でも失敗するし、これはCoC違反についても同様だ。CoCは違反者を糾弾するために存在するのではなく、問題を是正し再発を抑止するために存在する。CoCに違反した人は(その判断に同意するなら)反省等を示して再発を防ぐよう気をつければ良い。「気をつける」以外の対策がなければそれは単なる精神論であり再発する可能性があるのだから、具体的な改善案が無いのなら再発を弾劾してはならない。

ここまでの項目ではコミュニティ運営者により大きな責任が課せられるような内容が続いたが、運営者も万全ではないし、CoCはコミュニティ運営者が間違えることを想定していなければならない。判断ミスの回復に務めるべきではあるが、単に判断を誤ったことを理由に直ちに退任させられたらそれは運営者に対する不正なサンクションである。クビは改善が期待できなくなってからで十分だし、処分の公平性は運営者に対しても妥当する。

カジュアルに失敗し、カジュアルに問題を指摘し、カジュアルに改善することが、CoCを据えるコミュニティが目指すべき状態である。

さいきょうを目指して実装されたCoC

以上のようなことを考えながら、最初は3年前に自分が翻訳したcommunitycodeofconduct.comのやつをコピって多少いじるだけにしようと思っていたのだけど、冒頭に挙げたもののように、だいぶ書き換わることになった。CoCは法の世界のように憲法と法律が分離していることはなく、すべてが単体で完結しているので、その中に憲法的な「代表を縛る」ための条項を公平性の担保としていくつか追加することになった。実体法と手続法も一体化しているが、これは一般的なCoCも同様だろう。

近々公開する勉強会でこれが採用されるかどうかは未定だが(コミュニティは自分が作るとしても自分が恣意的に全て決めて良いものではない)、採用されなくても誰かしらの指針になると良いなと思っている。

最強のルールとは、みんなに気持ちよく遵守してもらえるようにフェアに設計され、違反行為にもインクルーシブに対応できるようなソフトローであると自分は考える。

追記: フィードバック

内容の語句にはいろいろ選択肢があるはずなので、自分のスタイルに合わせて調整するのが良いでしょう。

*1:「差別を受けたと感じた者がそう表明した」場合は、少なくともその気持ちをリスペクトすることが求められる。差別に配慮する人間ならそう考えるはずだ。

*2:是非はともかくとして外患誘致罪のような例外がある

*3:差別的に言及する行為が容認されないことは書くまでもないだろう。

juce_emscripten: 最新のJUCE on WebAssembly

English version

目次

2020年元旦からわれわれの界隈には刺激的なニュースが出てきました。

www.dtmstation.com

この記事で触れられているDreamtonics社がgithubで公開しているというのは、このjuce_emscriptenというJUCEのforkプロジェクトです。emscriptenを活用してwasmおよび周辺ファイルを生成します。

github.com

ちょっとだけ(本当にちょっとだけ)開発の手伝いをしていたので、詳しい解説をまとめようと思います。

Dreamtonics/juce_emscriptenは、JUCEオリジナルからのforkではなく、もともと5年くらい前にemscriptenでJUCEのWeb版を作ろうとしていた先人のプロジェクトのコードに基づくものです。これはかなり古いJUCEのバージョンに基づいているので、Dreamtonics/juce_emscriptenは最新版のJUCEにアップデートして、自社プロダクト(完全にJUCEで作られたアプリケーション)をビルド出来るようにした、ということになります。

(JUCEアプリケーションがWebブラウザで動作する先例としては、WebAudioModules (WAMs)を使用したWebDexedやWebOBXDといったアプリケーションがありました。WAMについては https://qiita.com/COx2/items/1f19d045936eebc3b82d でもう少し詳しく触れられているので、これを読むと良いと思いますが、WebAudioModules APIemscriptenを使用してJS上とネイティブコードの相互運用のごく基本的な部分だけを規定して、あとはC/C++のアプリケーションをそのまま移植できるようにしたものといえます。これらを実際にJUCEと統合した部分のソースコードWebOBXDのリポジトリで見られます。)

この最新のjuce_emscriptenがどれだけ実用的になっているか、まだ試していない人はここで試してみてください。

synthesizerv.com

juce_emscriptenを体験する

juce_emscriptenでは、JUCE本家のサンプルであるexamples/DemoRunnerがビルドできるように設定されています(後で言及しますが、juce_emscriptenはProjucerが対応していないので、Emscriptenでビルド出来るように調整されたMakefileが用意されています)。わたしが確認した限りではLinuxMacでビルドできます。WindowsならWSLでもできるでしょう。

emsdkがローカルで使える状態になっていない場合は、まずemmakeを使えるようにセットアップする必要があります。juce_emscriptenREADME.mdにあるビルドの説明が簡単です。

$ git clone https://github.com/emscripten-core/emsdk.git
$ cd emsdk
$ ./emsdk install latest
$ ./emsdk activate latest

emsdkがセットアップできたら、Emscriptenのツールチェインを呼び出せるように環境変数を設定します。インストールは1回で十分ですが、環境変数はその後いつでもemmakeを呼び出す前にシェル上で設定しておく必要があります。

$ source ./emsdk_env.sh

あとはemmakeでビルドするだけです。Emscripten用のビルドはexamples/DemoRunner/Builds/Emscriptenディレクトリ上にあります。生成済みのものがあるので、Projucer --resaveで生成する必要はありませんし、実のところ上書きするとビルドできなくなるので注意が必要です。

$ cd /path/to/juce_emscripten/examples/DemoRunner/Builds/Emscripten
$ emmake make

ビルドしたWebアプリケーションはローカルのChrome/Chromiumで実行できます。公式ドキュメントではcd build && python -m SimpleHTTPServerですが、わたしはnpx http-server buildで動かしています。HTTPサーバなら何でも良いです。

f:id:atsushieno:20200101003632p:plain
DemoRunner sshot

2021.12.30追記: このエントリーを書いてから、SPECTRE対策でSharedArrayBufferは無効化されるようになり、この手順では使えなくなりました。2021年現在、SharedArrayBufferはサイト側でCORS対策していないと利用できません。npx http-serverだと対応できない(--corsオプションだと足りない)ので、この辺のやり方を真似するとよいでしょう。

JUCEのプラットフォームバックエンドの基本

一体何をどうやったらデスクトップアプリケーション向けのフレームワークであるJUCEがWebブラウザで動作するようになるのでしょうか? 普段からクロスプラットフォームライブラリの実装を追いかけている人から見れば自明のことですが、どのクロスプラットフォームのライブラリも、プラットフォーム固有の部分を個別に実装して、(一般的には)共通のAPIのみを安定APIとして公開する、というアプローチが採られます。

JUCEの場合、本家がすでにWindows, Mac, Linux, iOS, Androidをサポートしており、そのプラットフォーム固有部分はJUCE本体の各モジュールで実装されています。JUCEはわれわれがよく目にするReact Native, Xamarin, Flutterといったクロスプラットフォーム開発フレームワークとは異なり、オーディオアプリケーション開発が主眼にあり、オーディオ/MIDI IO部分もクロスプラットフォームで実現する必要があります。オーディオ/MIDI IOはjuce_audio_devicesというモジュールで、GUIjuce_gui_basicsというモジュールで、主に実現しています。

また、GUIは主にメインスレッド上で動作するメッセージループのコールバックに基づいて動作する仕組みがどのOSにも存在しており、これはjuce_eventsというモジュールに独立して存在しています。メッセージループのコールバックの仕組みはオーディオI/Oでも用いられており、このjuce_eventsjuce_audio_basicsにとっても依存モジュールのひとつです。GNOMEデスクトップ開発に慣れている人であれば、gtkとglibが分かれていることを知っているかもしれませんが、juce_eventsはjuce_coreと合わせるとglibのような位置付けになります。

これらのモジュールのソースコードの中に、プラットフォーム固有の実装が含まれています。juce_audio_devicesの場合はjuce_audio_devices/nativeに、juce_gui_basicsの場合はjuce_gui_basics/nativeにあります。それぞれのディレクトリにAndroidだのiOSだのwin32だのといったファイルが含まれていることがわかるでしょう。

そして、プラットフォーム固有の実装は、自分で追加することもできるのです(そういえば今年のJUCE Advent Calendar1日目の記事もそういう内容でしたね)。juce_emscriptenは、ここに新しくemscripten / wasm用の実装を追加するものです。

自分のプロジェクトをjuce_emscriptenでビルドする際の注意点

juce_emscriptenは、条件が合えば自分のプロジェクトでも利用することが可能です。DemoRunnerとSynthesizer Vが動くというだけではもったいないので、他のアプリケーションも動くようにしたいですよね(!?)

ただし、現状ではjuce_emscriptenはSynthesizer Vが動作するために必要最低限のコードが実装されているものであって、まだいろいろ足りない部分があり、またビルドしたアプリケーションが動作するまではいくつかの手作業が必要になります。以下で少しずつ詳しく説明していきます。

missing modules

まず、README.mdのStatusセクションを見ると、どのモジュールがサポートされていないかがわかります(流動的に増えるかもしれないのでリンクだけ示しておきます)。

特によく引っかかりそうなのは、JUCEでよく使われていそうなオーディオプラグインサポートのためのモジュールjuce_audio_plugin_clientで、これはまだサポートされていません。juce_audio_plugin_clientを使っているアプリケーションはstandaloneでもビルドに失敗します。

もしサポートされていないものを使っている場合は、そのモジュールの機能を使わないように改造するか、ビルドエラーをもとに自分で実装する(!)しかありません。ゼロから自分でGUIもオーディオも実装するよりは、はるかに楽になっているはずですし、移植のための知見も本家との差分から調べやすくなっているとは思います。

missing Projucer builder type

また、現状ProjucerでプロジェクトファイルをEmscripten用に生成することはできません。Emscriptenの一般的な使い方と同様に、ProjucerでいったんLinuxMakefileを生成して、その内容に手を加えることになるでしょう。DemoRunner.jucerには、Emscripten用の出力ターゲットが追加されているので、これを再利用するのが一番手っ取り早いです。

注意すべきは、Projucerで出力したMakefileは、そのままでは使えないということです。DemoRunner.jucerには次のようなコメントがあります:

After resaving, do the following by hand:
Remove $(shell pkg-config ...) from the Makefile
Change JUCE library path to juce_emscripten
Add .html extension to target app
Optionally, add -s SAFE_HEAP=1 -s ASSERTIONS=1 to JUCE_CFLAGS for Debug build
Optionally, add -s WASM_OBJECT_FILES=0 to JUCE_CFLAGS and
 --llvm-lto 1 to JUCE_LDFLAGS for Release build.

重要なのは(1)JUCEモジュールのパスをProjucerの指定するmodulesのパスからjuce_emscriptenリポジトリmodulesに置き換え、(2)pkg-configを呼び出している部分を全部消すことと、(3)JUCE_TARGET_APPに拡張子.htmlを追加することです(こうすることでemmakeのビルド結果が.html、.js、.wasm等のセットになります)。これはProjucerで保存するたびに行う必要があります。

2020/02/02追記: これらに加え、JUCE_CPPFLAGSに -I/usr/include/freetype2 を追加してやる必要もありそうです。(本当は/usr/以下のパスを指定するのは良くないような気もしますが…)

LinuxMakefileのビルドファイルを手作業で編集するのが面倒になったら、Projucerに手を加えるような変更をcontributeすると良いかもしれません(!)

ちなみに、Projucerで新規GUIアプリケーションを作成して生成されたプロジェクトは、実のところビルドできません。利用モジュールの中にサポートされていないjuce_openglが含まれているためです。Projucer上でこのモジュールを外すと良いでしょう。

missing resources

もうひとつEmscriptenの制限でちょっと面倒なのは、フォントファイルをはじめとする各種リソースをemccの--preload-fileオプションで組み込まなければならないということです。実のところフォントを組み込まないとシンプルなASCII文字列すら描画してくれません。先のDemoRunnerでは、Linuxビルドが参照しているX11のフォントリソースを取り込んでビルドするようになっています。

DemoRunnerのビルダーを使い回す場合は(使いまわしたほうが楽だと思います)、DemoRunner固有の--preload-fileオプションは外しても大丈夫ですが、X11R6のフォントはコピーした上で--preload-fileを指定するのを忘れないようにしましょう。

足りない機能は自分で移植できる

いかがでしたか?(キュレーションサイト風) juce_emscriptenを使うと、既存のJUCEアプリケーションをWebブラウザ上にもってくることが、だいぶ現実的に可能になってきたことが見て取れたのではないでしょうか。

ただ、JUCE本体は幅広いツールチェインでサポートするプロジェクトの種類も多様です。おそらく"GUI Application" 以外の種類のプロジェクトを移植するには、juce_emscripten本体をちまちまとハックする必要があるかもしれません。とはいえ、おそらく最も面倒であろうGUIとオーディオI/Oのコア部分は、オリジナル開発者とDreamtonicsによって移植されているので、足りないところは問題にぶち当たった時にちまちまと実装してcontributeしていくと、完成度が上がっていくのではないでしょうか。

おまけ

2019年の仕事振り返り

仕事と言っても無職だけどな…!

2019年は、1年半前に始まった大無職時代をどのように生きるか、という問題に立ち向かった長い1年でした(こう書くと物は言いようっぽい)。音楽ソフトウェア開発者として、何もなかった頃の1年前と比べると、割といろいろ勉強できたと思います。

目次

1-2月 音楽制作環境(特にDAW)に慣れる

3/2に幻想音楽祭という音楽同人イベントがあり、第1回っぽいし初参加するにはちょうど良さそうな規模だったので、一度まじめに音楽制作というやつに取り組んでみてもいいかな〜と気軽に応募したのが通っていたため、昨年末から今年の最初の2ヶ月はそのために費やされました。

今でもそうといえばそうなのですが、特段「創作したい」という意識があるわけではなくて、自分の作っているMMLコンパイラ中心の制作環境がどこまでプロダクションで使えるのか試しておきたかったですし、自分の実用に耐えるレベルになる、というところまでは作り込んでおきたい、そうでないとこのアプローチを推奨することはできない、と考えていました(います)。

また完成品を作るにはどこかの時点でDAWを使わないといけないはずなので、DAWの使い方もちゃんと覚えよう、というhigh意識で進めていました。使っているDAWはこの時点でTracktion Waveformだったわけですが*1、この使い方は実際だいぶ勉強できていい経験になったと思います。

それまでほぼ創作活動は行っていなかったので、さすがにフルタイム無職とはいえ2ヶ月で完成度の高い作品を作り上げるのは無理がある、とわかっていたので、まずは過去作品を掘り出して使える部分を使いまわしながら2,3曲でっち上げたミニアルバムを出して、あとは技術書典4の時に書いたMMLコンパイラの本を展示・公開してブースの体裁を整えよう、と思いました。

「昔作っていた」を使い回すために、昔書いたMMLを自分のコンパイラMML文法に書き直す作業から始めたのですが(MMLはツールごとに文法が全然違う、インド・ヨーロッパ語族の言語みたいなもんです)、さすがにコンパイラ自身についてバグフィックスなどすることはあまり無く*2、どちらかといえばビジュアルMIDIプレイヤーでのコンパイルから再生を自動化するワークフローや、仮想MIDIキーボードへのMMLサポート組み込みといった、周辺ツールの改造がだいぶ進みました。

MMLでの打ち込みにはRoland SC-8820を使っていたのですが、これをやりこんでも最終的なプロダクションには使えないだろう、Waveform上でVST/AUに置き換えて完成させる必要があるだろう、とは思っていました。JUCEで作られているWaveformはLinux環境でもほぼ問題なく動きますが、VSTプラグインはほぼ全滅でWaveform付属のCollectiveくらいしか無かったので、最終的にはMacを調達してKontaktなどに置き換えました。

WaveformのMIDIインポート機能には拍子とテンポまわりにバグがあって、変拍子だらけの曲を作っていた自分としては致命傷だったのですが、対応が難しそうだったので、データ形式XMLだったのをいいことに、WaveformのデータモデルのライブラリをC#作って自前でMIDIインポートも自作して乗り切りました。

これが割と良い経験になったのでした。それまでWaveformにどんな機能があってどんな内容のデータが楽曲データとして保存されているのか、そもそもオーディオプラグインにどんな機能があるものなのか、全然わかっていなかったんですね。モデルの情報は特に無くて、Waveformの楽曲データから全て推測で作成したのですが(まあいざとなればtracktion_engineはOSSで公開されていますし)、十分にいろいろな情報が含まれていたので、Waveformのデータモデルを扱う機能は概ね把握できたと思っています。

3-5月 オーディオプラグインまわりをいじり始める

幻想音楽祭のための創作活動が終わって一息ついたのですが、2月にAndroid QのAPIが初登場して、人手不足だったXamarin.Androidチームから新APIバインディングを作る仕事を請負でやってくれないかみたいな話を内々に相談されていたのでそれを片付けたり(結局2日でほぼ終わってしまったので無償で)、去年公式リポジトリに投げてアップデート対応が必要だったFluidsynthのAndroidサポートにOboe(低レイテンシーオーディオ)対応なども追加したりと、いろいろ雑務(?)をこなして過ごしていました。

その一方で、DAWを使った制作プロセスの反省も含めて、もう少しオーディオプラグインをこちら側に近づけて、最初の打ち込み過程からMMLで直接いじりたいと思うようになりました。最初から作り込まずにベタ打ちで続けてもやる気出ないですし、2回作り込むの二度手間ですし(しかも最初に作り込んだのが邪魔になる)。今使われているDAWは作り込むのに向いていないんですよね。

楽曲再生エンジンだけならtracktion_engineが使えるわけですが、再生部分などはどうなっているのか相変わらずブラックボックスだったので、まずオーディオプラグインがどんなものでどこまでできるのか、今度は開発者としての視点で、VST3とJUCEを中心にいろいろ機能を調べるようになりました(特にJUCEはAUにも対応しているので)。Waveformのデータモデルから、オーディオプラグインでどんなことが実現できるのかはある程度見えていたので、良い動機づけになりました。

ただVST3はJUCEでサポートされておらず、最低限ホストを動かすためのパッチまで作ったりしましたが、進展が見られず、自分で進めたところでROLIの開発者と作業がかぶって無駄になるし、これじゃ良くても次のADC2019の頃まで出なさそうだなと踏んで、LV2をいじり始めるようになりました。LV2をいじったところで楽器が出てくるわけではないのですが、調べものとしては悪くなかったと思います。この辺の成果はひとつ10月に同人誌として結実しました。

6-8月 無職を失う

この頃は無職の意識高いツイートに腐心していたのですが(ツイッターはやってません)、

からの…

特段隠すつもりはあんまりなくてリアルではペラペラしゃべっているのですが、音楽系ソフトウェアのベンチャーで手伝いを始めて、最初3か月くらいしたら公知にするか…という感じでした。7月に始めて3か月くらい実験的に契約の予定だったのですが、ひと月でもうフルタイム社員にしちゃえという流れになりました。「ちょっと早くないかな…こっちは事前通告でいつでも辞められるけど会社側はそうもいかないし、3か月くらい試せば…?」などと言っていたのですが、実際3ヶ月で無職に戻ることにしたので(主に「自分で作りたいやつ」を作れる時間が全然とれなかったため)、結局謎のままで終わったのでした。

仕事は主にJUCEを使ったデスクトップアプリの開発で、C++開発者が求められていたのですが、5年くらいのキャリアが求められているところにC++キャリア0年での入社でしたが(!)、C++自体がわからなくてハマったのは最初の数週間くらいで、後はまあ言語で困ることは特に無かったです。JUCEは概ねモダンC++の世界なので、言語的なつまづきポイントは少なかったと思います(そういうものか…?)。まあもともと.NET開発者だった時もP/Invokeしまくってたし…

会社自体は面白いものを開発していてこの方面ではニュースバリューもあると思うので、気が向いたら年始に何か書くと思います。辞めてからもまだ手伝ったりしているし(謎)

JUCEをがっつり触れたのは良かったし本家にパッチを書いて送れる程度には分かってきたので、今後も機会があるたびに使える部分を使っていこうと思っています。

9-10月 M3に追われる

M3に初めてサークル参加しました。本当はこの時までにもう1枚CDとして新作を作るつもりだったのですが、わたしの仕事を始めるムーブが本当に良くなくて、当選してから創作するための時間が全く取れず(好きな時に創作できることはもちろん無く)、結局5月頃に少し書き出していたDAWのエンジン部分に関するテキストに手を加えてシーケンサーエンジンを支える技術として同人誌頒布と春先の新譜(!)の2本でサークルとしての体裁を整えることになりました(MML本も見本としては置きました)。

次回は技術書典8と日程まるかぶりなのと、開発しておきたいものがあってそっちに注力していたら当日までに創作する時間をとれる気がしないのとあって、申し込んでいません。ただM3で何も出来なかったというダメージが大きく、それが仕事を辞めた大きな要因のひとつです。別にただのフルタイム労働であって長時間労働させられていたということは全然無いのですが、それで時間が取られすぎるということは労働すること自体が問題だったということでしょう。もちろんいずれ労働しないと食っていけなくなるわけですが、自由なうちは自由にします。

11月 ADC2019〜原点回帰

11月半ばまではフルタイムで仕事していたので特段なにもしていなかったのですが、下旬にADC2019に行ってきました。2018の時は、無職になってまだ3ヶ月くらいの新人の頃だったので、どんなカンファレンスなのかもあまりわかっていなかったのですが、今回はそれなりに勝手を知っていたこともあって、かなりエンジョイ勢でした。今年はオーディオプラグインまわりでいろいろ遊んでいたので、SteinbergのブースでVST3の話を質問したり、NIのブースでKontaktの質問したり、AppleのブースでAUの仕組みを教えてもらったり…といった感じでした。あとGoogleのオーディオチームと1on1で話せるというので、Androidで自分が作っているものを見せてじっくり相談してきました。ROLIの人ともLinuxサポートまわりの話ができたし、収穫はいろいろありました。ADCの話は来年何かしらの発表の場を設ける予定です。

12月になってもまだ実は仕事が続いていて、週2日だけ手伝っていたのですが、それもひと区切りついたので、最近ようやく5月頃の状態に戻って自分のソフトウェアの開発に本腰を入れられる状態です。11月下旬から12月はAndroid同人誌の原稿やらアドベント カレンダー用の調査執筆が多発して、あんまり進められなかったので、これからだ…!という気持ちで年末を迎えています。

2020年は…?

2020年をどう過ごすのか、自分でもまだわかっていません。今個人で作っているソフトウェア次第ですが、どこかしらに所属して開発するかもしれないし、そうでなければ自分で何とかするかもしれません。ある程度は自由な時間を確保したいので、他によほど面白いものでも無い限り、フルタイムで無関係な仕事をすることは無いと思います。まずは有職中にほとんど進展が無かったプロジェクト(こんな感じ)を進めるところからですね…

f:id:atsushieno:20191230004611p:plain

とりあえずはかなり柔軟に時間を使える予定で、自分のプロジェクトもフルタイムで進める必要があるわけではないので、適当に面白そうな仕事の話とかあったら相談してください〜

*1:ちなみにライセンスだけはFL StudioだのCubaseだのも無駄にいろいろ持っていたりして…

*2:ただlanguage serverまわりはvscode自体の更新に伴う問題が多く、創作上のメリットが皆無だったのでほぼ放棄することになりました

libinstpatchとは

音楽技術etc. Advent Calendar 2019の…うーん…とりあえず21日目くらいにしようかな…のエントリーです。残りは上手く埋まったら埋めていきます。とりあえず今回はlibinstpatchについてです。短めに。

github.com

libinstpatchはswami projectの一環として公開されているインストゥルメンタルパッチの抽象表現ライブラリです。インストゥルメンタルパッチとは何かというと、具体的には次の3つの実装があるので、これを見ると何となくでもすぐに理解できると思います:

要するにサウンドフォントやDLSはどちらも性格が似ているので、両方ともまとめて扱えるようにしてしまおう、というものです(GigaSamplerはどの辺のコミュニティで使われているのか正直よくわかりません)。どちらも懐かしいといえば懐かしい存在ではありますが、現役で使われていなくもないものですね。

どういうところで使われるかというと、サウンドフォントベースのサンプラーや仮想MIDIバイスのソフトウェアで使うことができます…そうです、Fluidsynthの最新版2.1でこのlibinstpatchがサポートされた結果、FluidsynthでもDLSが使えるようになりました。実のところ作者がFluidsynthのメンテナなので、推して知るべしではあります。libinstpatchをどうやって使うかも、ここから使われ方を見てみれば分かるかと思います。

SFもDLSも(たぶんGigaSamplerも)単なるインストゥルメンタルパッチのデータストレージにすぎないので、このライブラリ自体がやっていることは単純です。サウンドフォントを読み書きするライブラリのAPIと機能的には大して変わらないはずです。

データロードの部分だけはIpatchConvert_から始まる名前のフォーマット別実装が含まれています。特に難しいことはないでしょう。