12-18 tracktion_engine

https://adventar.org/calendars/3353 の18日目のエントリー、と https://qiita.com/advent-calendar/2018/juce のクロスポストです。

## tracktion_engineとは

TracktionはTracktion社が開発している、クロスプラットフォーム（Windows/Mac/Linux/RasPi）で動作するDAWで、この分野ではそれなりに新しいものです。現在はWaveformという製品名になっており、バージョンはtracktionという名前だった頃から継続して9になっています。Tracktion7までのバージョンは無償ダウンロードで利用できます。わたしは現在、DAWを使う時は基本的にWaveform9で作業しています（作業と言えるほどのことはまだほとんど出来ていないのですが）。以降、このエントリーではDAWについては Tracktion の表記とします。

先月、このTracktionの音楽演奏・編集のエンジン部分が、驚くべきことに(?)フリーソフトウェアとしてGPLv3で公開されました。

https://github.com/Tracktion/tracktion_engine/

TracktionはJUCEを利用しており、tracktion_engineもJUCEに類する[商用ライセンスとのデュアル構成](https://www.tracktion.com/develop/tracktion-engine)です。GUI部分は製品として販売するが、エンジン部分は開放するので、音楽プレイヤーなどはユーザーに自由に作ってもらいたい、という考え方でしょう。ただライセンスまわりについては、正確な内容が不透明であるというgithub issueが並んでおり（[例](https://github.com/Tracktion/tracktion_engine/issues/3)）、開発者は「12月中に情報をアップデートする」と表明しています。

## どんなことができるのか

Tracktionは機能的にはいわゆるDAWであり、オーディオサンプリングやMIDI楽器、VSTやAUなどのオーディオプラグインを利用できる統合作曲環境です。打ち込みでは典型的なピアノロールが使われます。

OSS部分であるtracktion_engineは、全体としてはDAWのGUI編集機能を除く部分を実現しています。その中には楽曲プロジェクトのデータモデル、オーディオI/O、MIDIメッセージのI/O、オーディオ設定の調整、プラグイン管理、楽曲の再生機能なども含まれます。さらにUIの無いエディタのオブジェクトモデルも含まれています。もちろん、コマンドラインツールでDAWの膨大な機能を表出させているGUIに相当するものがあるわけではないので、ライブラリとして機能提供しているだけです。

いわゆるロジックとUIの分離という文脈で考えれば、ロジックの部分がOSS化されたと考えることができます。テキストエディタやIDEもそうですが、一般的には、高度にユーザーインタラクティブなアプリケーションでは、UIコードのプラットフォーム個別化やテスタビリティ向上といった目的で、ビューとロジックを分離します。この考え方はDAWでも適用されるべきものです。（Tracktion自身も、もしかしたら「GUIをプラットフォーム別に実装したい／作り直したい」等の理由でロジックをリポジトリレベルで切り離したのかもしれません。）

実際のところ、Tracktionの全機能を追いかけてそれに対応するコードがtracktion_engineに存在することを確認したわけではないので、全機能がtracktion_engineで実現しているかどうかは分かりません。少なくとも、ユーザーライセンス管理などの機能は含まれていないでしょう。

それぞれについては以降でもう少し掘り下げますが、先にサンプルを見ておきましょう。

## サンプルプログラム

現時点でtracktion_engineのサンプルプログラムが3つあります。

- PlaybackDemo: 楽曲データをコマンドライン引数で渡して実行するとウィンドウが開いて楽曲が再生されます（ウィンドウは主にオーディオ設定を調整するために出現します）。
- PitchAndTimeDemo: オーディオファイルの再生周波数を変更したり再生速度を変えたりできるデモです。
- StepSequencerDemo: 独自UIで簡易ステップシーケンサーを実現します。内部的にはパターンエディットの機能が使われます。

サンプルプログラム自体は`examples`ディレクトリにありますが、`tutorials`ディレクトリにそれぞれのサンプルの解説が詳しく書かれているので、これを読みながらコードを読み解くとよいでしょう。

tracktion_engineはJUCEモジュールの流儀に則ってアプリケーションのビルド時にビルドされることになります（JUCEはGPLv3で公開されています）。ビルドはgithubのリポジトリをsubmodule込みでチェックアウトして`./tests/{OS}/build_examples`で行います。ディレクトリは`tests`ですがサンプルも含めて全部ビルドされます。

## tracktion_engineのモジュール一覧

tracktion_engineのソースの`modules`ディレクトリには以下のモジュールがあります（取り込んでいるsoundtouchを除く）。最上位項目はJUCEモジュールで、サブディレクトリは単にソースのディレクトリ分けです。一見してもそれぞれが何のためのモジュールなのかは、tracktionの楽曲データの構成やアプリケーションとしての動作を知らないとわからないので、ここは「今は分からなくても読み進めれば十分」な部分です:

- audio_files : オーディオファイルのキャッシュやサムネイルの生成、各種フォーマットの読み書き、ファイルのI/Oなどを担うモジュールです
- control_surfaces : MIDI入力コントローラーとのインタラクションを担うモジュールです（MackieやNovationなどの特定デバイスのサポートのためのコードも含まれています）
- midi : MIDIイベント・MIDIメッセージなどのオブジェクトモデルです
- model : 主として楽曲データ編集機能のモデルです
  - automation - オートメーション、マクロパラメーター、MIDI Learnなど
  - clips : オーディオクリップ、コードクリップ、クリップエフェクト、エディットクリップ、マーカークリップ、MIDIクリップ、ステップエディットクリップなど、トラック内の各種クリップを表すモデルです
  - edit : 楽曲を表すトップレベルのモデルで、エディットのファイルI/O、グルーブテンプレート、マーカー管理、ピッチシーケンス関連処理、クオンタイゼーション、テンポシーケンス関連処理など、さまざまな機能がこのレベルで実装されています（この単位でundo, redoなどの操作も可能です）
  - export : プロジェクトをオーディオファイルや他のプロジェクトにエクスポートする機能を実装します
  - tracks : オーディオトラック、オートメーショントラック、クリップトラック、マーカートラックなど、楽曲内の各トラックを表すモデルです
- playback : 楽曲のプレイヤーを実装します（オーディオ ノードのルーティング、オーディオ/MIDIのI/O部分との繋ぎこみも実装します）
- plugins : 各種プラグイン（VST2, VST3, AUなど）を操作するモジュールです
- project : ひとつの楽曲を構成するファイルの集合体とそのプロジェクトファイルを操作するモジュールです
- selection : クリップボード管理や選択処理などを担うモジュールです
- timestretch : 拍子検出、テンポ検出、再生時間の加工などを担うモジュールです（内部的にはsoundtouchなどが使用されます）
- utilities : これらにカテゴライズされない各種コードが雑に含まれています

モジュール分けがきれいに行われているかどうかはわたしも評価しづらいところなのですが（"model"とは楽曲のモデルなのか、だとしたらなぜexportまわりがmodelにあってaudio_filesやplaybackが外にあるのか、など）、今回はmodelを中心に掘り下げていくことにします。

## 楽曲のオブジェクトモデル

Tracktionでは、ひとつの楽曲はひとつのプロジェクトとして、（通常はそのためのディレクトリ上に）作成されます。中には`*.tracktion`という拡張子のファイルと`*.tracktionedit`という拡張子のファイル、それとエクスポートされたファイルやアーカイブされたファイルなどのディレクトリが作成されています。

`*.tracktion`が楽曲プロジェクトをまとめたファイルと思われるもので、これをたとえばサンプルのPlaybackDemoの実行時に引数として渡すと、それを再生してくれるはずですが、筆者は諸事情（「JUCEがLinux上でVST3をサポートしない」「OSS版のJUCEはVST2サポートを除外している」の合わせ技）でまだ動作確認できていません。

いずれにせよ、重要な情報が含まれているのは、`*.tracktionedit`のほうです。内容はXMLになっており、データがどのようなツリー構造になっているかを確認できます。

```
<EDIT>
  <TRACK>
    <CLIP>
      <MIDISEQUENCE>
        <NOTE p="64" b="1.0000000000..." l="0.25000..." v="100" c="0" ... />
        ...
      </MIDISEQUENCE>
    </CLIP>
    <FILTER type="vst" state="{huge base64 binary}" ... />
    <OUTPUTDEVICES ... />
```

## サンプル: playbackの実装

サンプルプロジェクトのPlaybackDemoのコードに、指定されたファイルを再生するコードがあるので、これだけコードの一部を追跡してみましょう。`examples/PlaybackDemo.h`に実装があります。`*.h`ファイルにC++の実装があるのがJUCEのやり方です…マジで…

```
edit = std::make_unique<te::Edit> (engine, te::loadEditFromFile (editFile, {}), te::Edit::forEditing, nullptr, 0);
auto& transport = edit->getTransport();
transport.setLoopRange ({ 0.0, edit->getLength() });
transport.looping = true;
transport.play (false);
transport.addChangeListener (this);
```

主要なプレイバック機能を提供しているのはTransportControlクラスです。Editのインスタンスを作って、コンストラクタでFileを渡してやると、そのファイルのEditが作成されます。そこからTransportControlを`getTransport()`で取得すると、そのplay()メソッドなどで楽曲を再生できるようになるはずです。

この引用部分のコードにはありませんが、PlaybackDemo自体はコンソールだけで機能が完了しても良いはずのものですが、オーディオ設定もプラグイン設定も割と複雑なので、それぞれJUCEの既成のウィンドウを表示するAPIを呼び出して設定処理を行っています。Tracktionも含め、JUCEのこの機能を使っているアプリケーションは、この設定ダイアログを使っていることが多く（便利なので…）、自然と実装に察しがついてしまうやつです。