augene: MML compiler for sequencer engine

MMLコンパイラからオーディオプラグイン中心の楽曲を生成できるようなツールを作れないものか…と思っていたのですが、ある程度技術的に実現の目処が立ったので、とりあえず限りなく構想段階に近いですが出してみます。コードは現状ntractiveプロジェクトの下でサンプルとして作られています。

github.com

augeneはMMLコンパイラーmugeneを組み込んでオーディオプラグイン中心のシーケンサーエンジン用の楽曲を生成する複合型コンパイラーです。v1リリースでは、オープンソースの楽曲再生エンジンであるところのtracktion_engineを利用した楽曲プレイヤーと、Tracktion用楽曲データ生成ライブラリNTractiveで実装したSMF取り込み機能、それとJUCEのサンプルにあるAudioPluginHostを組み合わせて機能を実現します。

Rationale

mugeneはSMFを生成するMMLコンパイラーです。これはつまり、mugeneの可能性はSMFの範囲に限られるということを意味します。

筆者の作曲作業フローでは、まずMIDI音源を使用してMMLからコンパイルしたSMFを再生していましたが、GM準拠の命令のみではリズムトラックを2つ指定することはできず、ドラムパートのまともな打ち込みは実現が困難でした。コントロールチェンジもエンベロープやカットオフなどが必ずしも使える状態ではなく、また音源が変わると打ち込んだ内容が再利用できないものでした。

チャネル数が16までというのも、本格的に打ち込みが進んでくると問題になってきます。トラック数とチャネル数は異なり、同じ楽器はたいがいチャネルを共有することになり、特にリズムトラックは同じチャネルを共有することになるので、16チャネルあればそれなりの楽曲が作れるのですが、それでも創作がそこで制限されるのはばかばかしい話です。

打ち込んだMMLから生成されたSMFは、その後DAWにインポートして編曲する作業フローとなっていました。この辺りがウォーターフォールモデルなのはまだ許容範囲なのですが、Roland SC-8820など20世紀のハードウェア音源を利用していたときは、音源に特化した命令で調整していた部分は全て打ち込み直しになりました。部分的にMMLからコンパイルしてインポートしたり、極端な場合はXML形式のtracktion楽曲データを直接加工していました。この辺はMMLから一気通貫で変換できれば楽だったというのは間違いありません。

最初からオーディオプラグインを前提とした楽曲が生成できれば良いのに…。Augeneはそのような動機で作られました。

Augene楽曲データフォーマット

Augeneの楽曲データは、それ自体はどちらかといえばプロジェクトファイルとして捉えられるべき、ファイル参照のリストとなります。

<AugeneProject xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Tracks>
    <AugeneTrack>
      <Id>1</Id>
      <AudioGraph>Unnamed.filtergraph</AudioGraph>
    </AugeneTrack>
  </Tracks>
  <MmlFiles>
    <MmlFile>foobar.mugene</MmlFile>
  </MmlFiles>
  <MmlStrings>
    <MmlString>![CDATA[ 1 @0 V110 v100 o5 l8 cegcegeg  > c1 ]]></MmlString>
  </MmlStrings>
</AugeneProject>

雑なフォーマットですね(!) NTractiveは.NETのプロジェクトで、Augeneはまだ雑にやっつけでXmlSerializerを使ってプロジェクトをシリアライズしているのでこのざまです。これでもDataContractSerializerを使ってやっつけていた時よりはだいぶマシになりました…。JSONではなくXMLをあえて選んだのは、MML中に " やら < やら > やらが出てくるたびにエスケープする羽目になるのは望ましくないからです。XMLならCDATA Sectionが使えるので。実のところ、]]>が出現する可能性はゼロではないのですが、まあ頻繁ではないでしょうし、スペース1つ入れるだけで回避できる問題なので、我慢してもらえればと思っています。

ともあれ、内容は単純で、MMLをファイルあるいは文字列で渡したものと、トラックのリストだけになります。各トラックにはAudioPluginHostで定義して保存したファイルを指定するAudioGraphの情報が必要になります。

Augeneはこのプロジェクトファイルを引数として実行されると、まずMMLコンパイルしてNTractiveのMIDIコンバーターに渡します。

オーディオプラグイン設定はMMLではどうしようもないので、AudioPluginHostで事前にルーティングを設定してもらい、トラックごとにインポートできるように指定するやり方になっています。AudioPluginHostが生成する.filtergraph形式からTracktionの形式へはAugeneが変換します。SMFにはトラックIDが存在しないので、トラックの物理的な位置に対応するIDをユーザーに指定してもらいます(!)。

なおAudioPluginHostでは複雑なルーティングを構築できますが、Augeneは単純な直列接続しかサポートしません。tracktion_engineも複雑なルーティングはサポートしません。

filtergraphもファイル参照ではなく文字列で埋め込めるようにできなくはないのですが、stateのバイナリデータを文字列で書ける強者はそうそういないのではないかと思います…まあstateに頼らないオーディオプラグインもあるかもしれませんし、プログラムで生成できる可能性が無いわけではないので、サポートする可能性もゼロではないです。

実行方法: 楽曲を再生するまで

  • AugeneプロジェクトXMLファイルを作成します。
  • トラックごとに、AudioPluginHostを実行して、フィルターグラフを作成します。各AugeneTrack要素のAudioGraph子要素にそのファイル名を指定します。
  • MMLを作成します。直接プロジェクトに書いても、別のファイルに書いても良いです。MmlStringあるいはMmlFileとして記述します。
  • Augene.exeにプロジェクトXMLを引数として渡すと、それぞれをロードして、tracktioneditファイルとして生成されるべき内容を標準出力に吐き出すので、リダイレクトで保存します。
  • tracktion_engineのPlaybackDemoに、このファイルを渡して実行します。
    • PlaybackDemoの初期状態では、プラグインが何一つロードされていないので、まずプラグインリストを更新する必要があります。
    • また、PlaybackDemoのデフォルトのオーディオ設定では、マイク入力が有効になっていたり、オーディオ出力がspatial audio対応のように多数チャンネルが有効になっていたりして、特にLinuxデスクトップではほぼノイズしか聞こえないような状態になっています。設定を変更して、channel 1 + channel 2だけ有効にすれば、まともにオーディオ再生が機能するようになります。

サポート対象プラットフォームの課題

MMLコンパイラが生成するものは、可能な限り最終的なアウトプットをそのまま生成できることが望ましいです。ただ、これは次に説明するプラットフォーム依存性の問題があるため、妥協的な解決策を導入します。

オーディオプラグインを利用できる環境はクロスプラットフォーム化しつつありますが、オーディオプラグインそのものはまだほとんどがクロスプラットフォーム化できておらず、特に筆者が主に使用しているLinuxデスクトップのサポートが壊滅的です。

また、オーディオプラグイン機構のクロスプラットフォーム化はまだ「実現中」のものが少なからず存在します。JUCEはLinuxのVST3をサポートできていません。Augeneの動作確認は、Waveform上でVST2であるところのCollectiveを使用した楽曲を、VST2SDKを有効にしたtracktion_engine(プロジェクトのjuce_audio_plugin_processorsの設定に変更を加える必要があります)のPlaybackDemoに渡すことで成功しています。

オーサリング環境

実のところオーサリング環境のサポートが無いところで楽曲を打ち込むのはまだだいぶ苦痛だと思うので、何かしらツールを用意したほうが良いかと思っています。さすがにPlaybackDemoではまともなプレイヤーになっているとは言い難いですし。ただNTractiveやmugeneは.NETツール、PlaybackDemoはネイティブ(C++)のツールと、分かれているものをある程度統合する必要が出てくるかもしれません。とりあえずは子プロセスを起動するやり方でしのぐつもりです。

またPlaybackDemoだけがプレイヤーになっている現状はかなり残念なので、Waveform10にもロードできるように、プロジェクトファイル(*.tracktion)も生成できるようにしたいのですが、このファイルはバイナリなので、tracktion_engineがやっていることを自前で(?)再現する必要があります。理想を言えばYeoman generatorあたりを実装して yo tracktion くらいで生成できるようにしたいところです(!?)。

あるいはもっと単純にAugeneにWaveformプロジェクトの一部を書き換えるだけの機能を実装してもよいのですが、その辺はもう少しいろいろ試してみたからにしようと思います。