JUCEをCMakeとVSCodeで開発する

JUCEはv6.0.0から正式にCMakeを使ったビルドに対応しました。
これまでも[juce-cmake](https://github.com/remymuller/juce-cmake)や[FRUT](https://frut.readthedocs.io/en/latest/)などコミュニティベースのサポートはあったものの、どれもそれなりに一長一短という感じでした。

これで晴れてProjucerにもXCodeにも頼ることなく開発が進められることになりました。

この記事ではまだドキュメントやチュートリアルが少ないJUCEをCMakeで使って運用する方法、またVSCodeをメインの開発環境として利用する方法についてまとめます。

## CMakeを利用するメリット

### サードパーティライブラリの利用が楽になる

JUCEと共にサードパーティ製のライブラリを使用する場合は、ヘッダーオンリーなライブラリはともかく、バイナリをビルドしたりリンクしたりした上で使おうとするとかなりめんどくさいです。詳細はatsushienoさんの[JUCEモジュールを作って外部ライブラリを参照する](https://atsushieno.hatenablog.com/entry/2019/12/03/002804)を読むと分かります。

これがCMakeになると、サードパーティライブラリ自体がCMakeに対応している場合は`add_subdirectory()`で追加するだけで使えるようになったりしますし、バイナリ配布されているライブラリも`find_package()`や`target_link_libraries()`などを使えば楽に使うことができます。

典型的に役立つ例としては[Google Test](https://github.com/google/googletest)や[Catch](https://github.com/catchorg/Catch2)のようなテストフレームワークの導入障壁が下げられます(両者ともにCMake対応)。

### JUCEのバージョン管理

CMakeでJUCEを使用する場合プロジェクトの中にGitのSubmoduleとしてJUCEのリポジトリをクローンすることができます。そのため、複数のプロジェクトで異なるJUCEのバージョンを利用している際にもそれぞれのリポジトリの中で異なるリビジョンにチェックアウトして使用することでバージョンのコンフリクトを避けることができます。

### 開発環境の一本化

CMakeとVisual Studio Codeを利用した開発環境はmacOS、Windows、さらにはLinuxでもほぼ同等に利用することができるので、多プラットフォームでの開発のハードルがより低くできます。
また、特定のOSでのみプリプロセッサの定義やコンパイルオプションを加えたいといった時にもProjucerを開いて書き換えずCMakeの設定ファイルの中で条件分けができるので楽です。

Raspberry PiなどARM向けのアプリケーションのビルドもこれまではまずProjucerをARM向けに自分でビルドする必要がありましたが、CMakeならその手間が減らせます。

試してないですがCMake Toolchainを使えばARM向けバイナリをmacOSやWindowsからクロスコンパイルする、とかもできるはず。

参考: [CMakeでARM用にクロスコンパイルする - simotin13's message](https://mcommit.hatenadiary.com/entry/2017/09/16/224949)

### CIの管理が楽になる

CIなどコマンドラインでの実行しかできない環境ではProjucerをコマンドラインツールとして利用して、OS毎にビルドスクリプトを指定して検証などをしなければいけないですが、これをCMakeのカスタムターゲットなどを使ってCI上では共通のコマンドを叩くだけ、という形にすることでメンテナンスのコストが一段下がります。

## CMakeを利用するデメリット

[CMake自体の学習コストが高い](https://qiita.com/shohirose/items/45fb49c6b429e8b204ac)。

## VScodeとの連携

Visual Studio Codeは[CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools)というMicrosoft謹製の拡張機能を使うことでXCodeやVisual Studio、CLionなどのIDEに匹敵する開発環境にすることができます。

他にもCMakeと連携して開発するためにおすすめの拡張は以下。

- clangd(llvmベースのlanguage server。標準のIntelliSenseより補完がずっと早い)
- CodeLLDB(デバッガ拡張。標準のデバッガよりstd::vectorのコンテナの中身が見られたりと自由度が高い)

## JUCE CMake APIの基礎

JUCEをCMakeで使う上でまとまった資料は今のところ公式docsの中の[JUCE CMake API](https://github.com/juce-framework/JUCE/blob/master/docs/CMake%20API.md)以外ほぼありません。ここに必要な情報は全て揃っています。

### JUCEをadd_subdirectoryする

`libs`以下などに`git submodule clone --recursive https://github.com/juce-framework/JUCE libs/JUCE`などでサブモジュールとしてクローンしてきます。

そして`./CMakeLists.txt`に

```cmake
cmake_minimum_required(VERSION 3.15)

PROJECT(JUCE_CMAKE_EXAMPLE
LANGUAGES CXX
VERSION 0.0.1
)
add_subdirectory(lib/JUCE)
```

のようにしてJUCEのリポジトリを追加すると、JUCEの用意したCMake設定用の関数が利用できるようになります。
ちなみにこれはJUCE自体をCMakeでビルドし、`/usr/local`などグローバルにインストールしていると`find_package(JUCE REQUIRED)`のようにしても読み込むことができます。が当然この場合はプロジェクト毎にJUCEのバージョンを変えるなどはできません。
### プラグインを作る

例えばプラグインを作りたい場合は`juce_add_plugin`という関数を使うことでターゲットを作ることができます。CMakeに慣れている人であればこれが`add_library`や`add_executable`に相当するものだと思ってください。Projucerで指定するプラグインのオプションは基本的に全てここで設定します。

```cmake
juce_add_plugin(ExamplePlugin
    # VERSION ...                                     # Set this if the plugin version is different to the project version
    # ICON_BIG ""   # ICON_* arguments specify a path to an image file to use as an icon for the Standalone
    # ICON_SMALL "${CMAKE_SOURCE_DIR}/assets/icon.png " 
    COMPANY_NAME "Tomoya Matsuura"                    # Specify the name of the plugin's author
    COMPANY_COPYRIGHT "Tomoya Matsuura"
    COMPANY_WEBSITE "https://matsuuratomoya.com/"
    COMPANY_EMAIL "me@matsuuratomoya.com"
    IS_SYNTH FALSE                       # Is this a synth or an effect?
    # NEEDS_MIDI_INPUT TRUE/FALSE               # Does the plugin need midi input?
    # NEEDS_MIDI_OUTPUT TRUE/FALSE              # Does the plugin need midi output?
    # IS_MIDI_EFFECT TRUE/FALSE                 # Is this plugin a MIDI effect?
    # EDITOR_WANTS_KEYBOARD_FOCUS TRUE/FALSE    # Does the editor need keyboard focus?
    # COPY_PLUGIN_AFTER_BUILD TRUE/FALSE        # ビルドするたびにプラグインをインストールするか？
    PLUGIN_MANUFACTURER_CODE CCCC               # 最低一文字大文字でプラグインベンダーのIDを指定
    PLUGIN_CODE XXXX                            # 最低一文字大文字でプラグインのユニークIDを指定
    FORMATS 
    # ここでビルドしたいプラグインの種類を指定する
            Standalone 
            AU
            # AUv3 
            VST3 
            # Unity 
            # AAX           
    VST3_CATEGORIES "Fx" #複数(3つまで)列挙するとホスト上でフォルダにネストされて表示される
    AU_MAIN_TYPE "kAudioUnitType_Effect"
    # AU_SANDBOX_SAFE TRUE
    # AAX_CATEGORY ""
    # HARDENED_RUNTIME_ENABLED # macOS app settings
    # HARDENED_RUNTIME_OPTIONS
    # APP_SANDBOX_ENABLED
    # APP_SANDBOX_INHERIT
    # DESCRIPTION "" #設定するとProtoolsのプラグイン名がこのDescriptionになってしまう（バグ？）
    MICROPHONE_PERMISSION_ENABLED TRUE
    MICROPHONE_PERMISSION_TEXT "This applicaion requires a permission to use an audio input device of your computer. By Default, Built-In microphone will be used."
    PRODUCT_NAME "ExamplePlugin")        # The name of the final 

```

これを作ると、例えばこのExamplePluginという名前なら`ExamplePlugin`という名前のターゲットが、`libExamplePlugin_SharedCode.a`というプラグイン間で共通して利用されるライブラリとしてビルドされるものになります。
加えて、設定に応じて`ExamplePlugin_StandAlone`や`ExamplePlugin_VST3`といった各フォーマット毎のビルドターゲットが作られ、`libExamplePlugin_SharedCode.a`はそれらの依存ターゲットとして設定されることとなります。

この他、`ExamplePlugin_All`というユーティリティターゲットも定義され、これをビルドするとオプションで指定した全てのフォーマットをまとめてビルドしてくれることになります。

### ソースファイルの追加

JUCE CMakeには流石にテンプレートcppファイルをを用意してくれる機能まではありません。なのでここだけProjucerを使用して初期状態のPluginProcessor.h/.cpp、PluginEditor.h/.cppのファイルを用意しました。

普通のCMakeプロジェクトではサブディレクトリ毎に`add_library(LIB_Name1 hoge.cpp fuga.cpp)`のようにしてライブラリを作り、それを最後にまとめて`target_link_libraries(Main_Target PUBLIC LIB_Name1　LIB_Name2)`のようにしてリンクしていくと思いますが、JUCEを使う上では少し違う方法をとります。

`target_sources(ExamplePlugin PluginProcessor.cpp PluginEditor.cpp)`のように一つのターゲットに対してcppファイルのソースを追加していく形になります。これは必要なヘッダファイルなどを全て一つの`JuceHeader.h`で賄うという方法を取っているからなどいくつかの理由があるようです。

さらにJUCEの標準モジュールを`target_link_libraries`でリンクするように指定します。

```cmake
target_link_libraries(ExamplePlugin PUBLIC
juce::juce_audio_basics
juce::juce_audio_devices
juce::juce_audio_formats
juce::juce_audio_plugin_client
juce::juce_audio_processors
juce::juce_audio_utils
juce::juce_core
juce::juce_cryptography
juce::juce_data_structures
juce::juce_dsp
juce::juce_events
juce::juce_graphics
juce::juce_gui_basics
juce::juce_gui_extra
juce::juce_opengl
juce::juce_product_unlocking
)
```
もちろん必要ないライブラリはリンクから外しても良いですが、リンク時間的には似たり寄ったりなのでよくわからなければ全部追加しておいてよいでしょう。3rdパーティライブラリを使用する場合は当然ここでリンクを追加しておく必要があります。

最後に、`JuceHeader.h`を自動生成するために以下を加えます。

```cmake
juce_generate_juce_header(ExamplePlugin)
```

このJuceHeader.hの中には必要なモジュールのヘッダ類や後述するバイナリデータなどが全て含まれるため、JUCE関連の関数はこのヘッダを一つインクルードすれば使えるようになります。注意点としては、このJuceHeader.hはConfigure時ビルドディレクトリの中に生成されるため、Configureを行うまではIntellisenseでの補完などが効かないことになります。


### ビルドする

CMake Toolsがインストールされている状態でプロジェクトを開くと、初回は画像のようにどのコンパイラを使うか聞かれます。macOSの場合は`/usr/bin/clang++`を指しているやつを、Windowsでは`Visual Studio Community 2019 Release - amd64`などのVisual Studioの提供するコンパイラをアーキテクチャに合わせて適当に選びましょう。

![Screen Shot 2020-12-16 at 12.14.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56461/36361a5e-415f-dafa-7e8d-508f53a778d6.png)

CMake Toolsがインストールされていれば左のメニューにCMakeのタブができているので選択します。左から2番目のConfigureボタンを押すと、正常にConfigureができていれば画像のようにターゲットが追加されているはずです（初回はConfigure時にProjucer相当の機能を提供するツールjuceaideをビルドするため少し時間がかかるかも)。

ここがOKなら右隣のBuild Allでビルドします。

![Screen Shot 2020-12-16 at 12.17.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56461/05913f9c-8db3-8fd3-a762-6852834c3726.png)

`build/src/ExamplePlugin_artefacts/Debug`のなかにビルドしたプラグインやアプリケーションが生成されているはずです。

### デバッグする

VSCodeでCMakeのターゲットをデバッグするにはいくつかの方法があります。一つは先ほどの`ExamplePlugin_Standalone`のようなターゲット名を右クリックし、`Debug`をクリックするとデバッガが起動します。
ただしこれは標準のgdbデバッガしか利用することができません。

より高機能なデバッガであるlldbを利用するには次のようにします。
![Screen Shot 2020-12-16 at 12.22.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56461/60e5fc96-1d10-57c0-d75f-8d15fe5136d9.png)

同じようにターゲットを右クリックし、`Set as Launch/Debug Target`を選択します。

`.vscode/launch.json`か、`yourproject.code_workspace`の中の`"launch":{}`プロパティに以下のように追記します。

```json

        "configurations": [
            {
                "type": "lldb",
                "request": "launch",
                "name": "CMake Debug",
                "program": "${command:cmake.launchTargetPath}",
                "args": [],
                "cwd": "${workspaceFolder}"
            },
        ]

```
これで左のメニューからデバッグタブを選ぶと、CMake DebugというConfigurationが表示されていると思うので、Runボタンから立ち上げることができます。

また、独自にユニットテストなどExecutableを追加している場合は別ですが、そもそもStandAlone以外は直接起動することができないので、同様のJSONファイルにconfigurationを追加して`program`にホストアプリケーションのパスを指定するか、ホストを起動してから`Cmd+Shift+P`でコマンドパレットを開き、`LLDB:Attach to Process`を検索して出し、アプリケーションの名前を検索して指定し途中からAttachします。（例えばUnityのNative Audio Pluginの開発とかだと、Unityを直接programに指定しても実際のエディタが立ち上がる時には別プロセスに切り替わっていたりするので、attach以外の方法が取れないこともあります。）

### バイナリファイル（アセット）の追加

JUCEではProjucerや[Binary Builder](https://qiita.com/kawakawa_/items/d604e50f7ff7f4c3dee5)を用いて画像ファイルやwavファイルをC++のソースファイルに変換し、アプリケーションに埋め込むことができますが、これをCMake経由で行うこともできます。例えばJUCEのロゴのsvgファイルをassets以下に保存した場合、以下のようにして読み込みます。

```cmake

juce_add_binary_data(ExamplePluginBinaries
SOURCES 
${CMAKE_SOURCE_DIR}/assets/juce-logo.svg
)

target_link_libraries(ExamplePlugin PUBLIC
ExamplePluginBinaries #ここに追加する
juce::juce_audio_basics
...
)
```

これを例えば次のようにして読み込むことができます。

```cpp

auto svgimg =  juce::Drawable::createFromImageData(BinaryData::jucelogo_svg,
                                              BinaryData::jucelogo_svgSize);
```

ファイル名からドットなど使えない文字をアンダーバーに変換したもの(`BinaryData::jucelogo_svg`)がデータの先頭を表すポインタ、それに`Size`を追加した変数(`BinaryData::jucelogo_svgSize`)の2つを組み合わせて使う形になります。
これはwavファイルやフォントでも使い方は同じで、大体読み込みにこの2つをセットで指定するメソッドが用意されています。

というわけで、空のプラグインにJUCEロゴを追加したのはこんな感じに表示できたりしました。


![Screen Shot 2020-12-16 at 12.41.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56461/18a92d26-59e7-3d39-211b-c5de22bbd18d.png)

## プロジェクトテンプレートを作りました。

ここまで説明したようなことをテンプレートリポジトリの形で公開しておきました。

https://github.com/tomoyanonymous/juce_cmake_vscode_example

`juce_cmake_vscode_example.code-workspace`をVisual Studio Codeで開くと、足りないプラグインがある場合はrecommendしてくれるような設定になっていますし、デバッグのjson設定も既に含まれていますので、XcodeやVisual Studioなどコンパイラ類が用意されていればすぐにでも使えるようになっています。興味がある人はぜひ試してみてください。

ちなみに、VSCode向けの設定はないですがC++20,CMake,Catch2,Github Actionsなどモダンなワークフローを詰め込んだ2020年向けテンプレートリポジトリのPamplejuceというのもあるようです。

https://github.com/sudara/pamplejuce/

## 既知のバグ

プラグインのビルドをStandAlone,VST,AU,AAXなど、組み合わせによって時々CMake Toolsのタブでターゲット一覧が表示されなくなるバグがあるようです。CMakeのビルドそのものに支障はないのでBuild Allからビルドすれば良いのですが、エラーメッセージがタブを開くたびに出て来て邪魔だったりするので一時的にどれかのビルドを外して対処したりしています。原因不明。