JUCEとvcpkgではじめるアプリケーション開発

本記事は[JUCE Advent Calendar 2022](https://qiita.com/advent-calendar/2022/juce) の12月8日向けに投稿した記事です。

# 対象バージョン
+ JUCE 7.0.3
+ Visual Studio 2022
+ Xcode 14.1
+ CMake 3.15以上
+ Git 2.0以上

# まとめ
+ JUCEにvcpkgを組み合わせることで、コマンドラインからC/C++パッケージをJUCEプロジェクトに追加することができます。
+ 実例として、vcpkgからSoundTouchを取得してJUCEプロジェクトに組み込んだオーディオプラグインを作ってみました。

# JUCEとは
JUCE (Jules' Utility Class Extensions)は、C++言語によるマルチメディア系アプリケーションの開発を支援するフレームワークです。
ライブラリの一部モジュールとして独自のGUIシステムを実装しており、開発者はそのAPIを利用することで、クロスプラットフォームなGUIアプリケーションを統一したコードベースで実装することができます。
[公式サイト](https://juce.com/)

# vcpkgとは
vcpkgは、Microsoft社が開発・保守しているC/C++言語向けのパッケージマネージャです。クロスプラットフォーム設計のパッケージマネージャとなっており、Windows、macOS、Linuxから利用することができます。
vcpkgを使用することで、ライブラリ毎に個別にビルド、インクルード、リンクする作業の手間をvcpkgが代わりに行ってくれます。複数のライブラリを利用するような大規模なアプリケーション開発で効果を発揮します。
[公式サイト](https://vcpkg.io/en/index.html)

# CMakeとは
CMakeは、主にC言語、C++言語などC系言語のビルドにおけるクロスプラットフォームビルド環境を提供するビルドマネジメントツールの一つです。ソフトウェアのビルドにおける一連の処理をCMakeLists.txtに記述し、CMakeプログラムを通じて各種プラットフォーム用のビルドツールに向けたプロジェクトを出力することができます。
[公式サイト](https://cmake.org/)

## JUCEライブラリ と CMake
2020年にリリースされたJUCE 6から、ビルドマネジメントツール『CMake』のサポートが追加されました。
JUCEがCMakeをサポートすることで以下のメリットを得ることができるようになりました。

+ ビルド設定をCMake用のフォーマットで記述することができる
+ CMakeのエコシステムの恩恵を受けることができる
+ CMakeが独自に提供する機能を利用することができる
+ CMakeに対応した他のライブラリとのインテグレーションが容易になる

# JUCEとvcpkgを組み合わせるにあたって
本記事を執筆している時点では、JUCEをvcpkg経由で取得することはできません。そのため、JUCEをインテグレーションするという作業については、以下で解説するように自前でCMakeListsを書く必要があります。

# 実例のソースコードリポジトリ

JUCEとvcpkgを組み合わせる方法の事例を示すリポジトリを以下に用意しました。

https://github.com/COx2/juce_meets_soundtouch

このリポジトリに含まれるソースをビルドすると、以下のようなGUIを持つオーディオプラグインが生成されます。
任意のWAVファイルをロードすることができるワンショット・サンプラーとなっており、シンセサイザーのサウンドに対してSoundTouchライブラリによるピッチシフト処理を適用しています。

・音声サンプルデータの経路
シンセサイザー(juce::Synthesiser)→ピッチシフター(SoundTouch)→オーディオバッファ出力（プラグイン・インターフェース）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93566/76e11f4a-25d3-8997-4245-0ef4cb5aeb41.png)

本記事では、アプリケーションの実装については解説せず、プロジェクトを構成するCMake用ファイルおよびシェルスクリプトの要点について解説します。

## リポジトリのクローン操作について

当ソースコードリポジトリでは、GitHubに置かれた`vcpkg`,`JUCE`リポジトリをGitサブモジュールとして取り込んでいます。
そのため、リポジトリのクローン時にはサブモジュールも併せてクローンするようにしてください。

```
git clone --recursive https://github.com/COx2/juce_meets_soundtouch.git
```

## リポジトリのディレクトリ構成について

```txt
juce_meets_soundtouch
├── External ......... Gitサブモジュールを配置するディレクトリ
├── Resources ........ バイナリ化してアプリケーションで利用するリソースファイルを配置するディレクトリ
├── Scripts .......... ビルド用のスクリプトを配置するディレクトリ
└── Source ........... ソースファイルを配置するディレクトリ
```

## プロジェクトのビルド手順について

当ソースコードリポジトリでは、vcpkgのインストール処理とCMakeのビルド処理をシェルスクリプトにまとめています。

### Windows
```
./Scripts/install_vcpkg_x64-windows.bat
./Scripts/build_windows_vs2022.bat
```

### macOS
```
./Scripts/install_vcpkg_x64-osx.command
./Scripts/build_macos_xcode.command
```

### Linux
```
./Scripts/install_vcpkg_x64-linux.sh
./Scripts/build_linux_ninja.sh
```

## vcpkgのインストールスクリプト

vcpkgのインストール処理における主要な処理について紹介します。

### Windows

```
@echo off

set SCRIPT_DIRECTORY=%~dp0

cd "%SCRIPT_DIRECTORY%\..\External\vcpkg"

call .\bootstrap-vcpkg.bat

.\vcpkg install soundtouch:x64-windows-static

exit /B 0
```

#### vcpkgの初期化処理

```
call .\bootstrap-vcpkg.bat
```

#### SoundTouchパッケージ（Windows用静的ライブラリ）の追加処理

```
.\vcpkg install soundtouch:x64-windows-static
```

### macOS

```
#!/bin/bash

set -e -u -o pipefail

realpath() {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

SCRIPT_NAME="$(basename "$(realpath "$0")")"
SCRIPT_DIR="$(dirname "$(realpath "$0")")"

cd "${SCRIPT_DIR}/../External/vcpkg"
./bootstrap-vcpkg.sh
./vcpkg install soundtouch:x64-osx
```

#### vcpkgの初期化処理

```
./bootstrap-vcpkg.sh
```

#### SoundTouchパッケージ（macOS用）の追加処理

```
./vcpkg install soundtouch:x64-osx
```


### Linux

```
#!/bin/bash

set -e -u -o pipefail

realpath() {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

SCRIPT_NAME="$(basename "$(realpath "$0")")"
SCRIPT_DIR="$(dirname "$(realpath "$0")")"

cd "${SCRIPT_DIR}/../External/vcpkg"
./bootstrap-vcpkg.sh
./vcpkg install soundtouch:x64-linux
```

#### vcpkgの初期化処理

```
./bootstrap-vcpkg.sh
```

#### SoundTouchパッケージ（Linux用）の追加処理

```
./vcpkg install soundtouch:x64-linux
```


## CMakeLists.txt

当ソースコードリポジトリのルートディレクトリに置かれたCMakeLists.txtの一部を示します。
移行、vcpkgとのインテグレーションに関連する重要な点について解説します。

```
cmake_minimum_required(VERSION 3.15)

# To be enabled integrate vcpkg installed packages.
# NOTE: CMAKE_TOOLCHAIN_FILE is should setted in startup
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/External/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE PATH "toolchain file")

project(JUCE_MEETS_SOUNDTOUCH VERSION 0.0.1)

# static linking in Windows
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

# Adds all the module sources so they appear correctly in the IDE
set_property(GLOBAL PROPERTY USE_FOLDERS YES)
option(JUCE_ENABLE_MODULE_SOURCE_GROUPS "Enable Module Source Groups" ON)

# Add JUCE Package
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/External/JUCE)

# Add SoundTouch package
find_package(SoundTouch CONFIG REQUIRED)

juce_add_plugin(JuceMeetsSoundTouch
    VERSION 0.0.1 
    # ICON_BIG ...
    # ICON_SMALL ...
    # COMPANY_NAME ...
    IS_SYNTH TRUE
    NEEDS_MIDI_INPUT TRUE
    # NEEDS_MIDI_OUTPUT TRUE/FALSE
    # IS_MIDI_EFFECT TRUE/FALSE
    # EDITOR_WANTS_KEYBOARD_FOCUS TRUE/FALSE
    # COPY_PLUGIN_AFTER_BUILD TRUE/FALSE
    PLUGIN_MANUFACTURER_CODE COx2
    PLUGIN_CODE Jmst
    FORMATS AU VST3 Standalone
    PRODUCT_NAME "JUCE meets SoundTouch")

juce_generate_juce_header(JuceMeetsSoundTouch)

set_property(TARGET JuceMeetsSoundTouch PROPERTY CXX_STANDARD 17)

...中略

target_sources(JuceMeetsSoundTouch
    PRIVATE
    ${APP_SOURCES}
    )

target_compile_definitions(JuceMeetsSoundTouch
    PRIVATE
        # JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them.
        JUCE_WEB_BROWSER=0
        JUCE_USE_CURL=0
        JUCE_VST3_CAN_REPLACE_VST2=0
        JUCE_APPLICATION_NAME_STRING="$<TARGET_PROPERTY:JuceMeetsSoundTouch,JUCE_PRODUCT_NAME>"
        JUCE_APPLICATION_VERSION_STRING="$<TARGET_PROPERTY:JuceMeetsSoundTouch,JUCE_VERSION>"
        DONT_SET_USING_JUCE_NAMESPACE=1)

...中略

juce_add_binary_data(PcmSamplesData SOURCES ${PCM_SAMPLES_RESOURCES})

target_link_libraries(JuceMeetsSoundTouch
    PRIVATE
        SoundTouch::SoundTouch
        PcmSamplesData
        juce::juce_audio_utils
    PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_lto_flags
        juce::juce_recommended_warning_flags)
```

### ツールチェインファイルの指定

このJUCEプロジェクトが参照するvcpkgおよびパッケージ一式を指定します。
コマンドラインから引数として`-DCMAKE_TOOLCHAIN_FILE=/Path/to/vcpkg`渡すことで指定することも可能です。

```
# To be enabled integrate vcpkg installed packages.
# NOTE: CMAKE_TOOLCHAIN_FILE is should setted in startup
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/External/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE PATH "toolchain file")
```

### SoundTouchパッケージをCMakeプロジェクトに取り込む

```
# Add SoundTouch package
find_package(SoundTouch CONFIG REQUIRED)
```

この記述内容は、vcpkgからパッケージを追加する操作をした際に実行結果の表示で案内されます。
上記ツールチェインファイルで指定したvcpkg配下からパッケージを探してもらうことができます。

vcpkgコマンドラインツールからの案内
```
soundtouch provides CMake targets:

    # this is heuristically generated, and may not be correct
    find_package(SoundTouch CONFIG REQUIRED)
    target_link_libraries(main PRIVATE SoundTouch::SoundTouch)
```

この案内に従って、該当する記述をCMakeLists.txtに追加しています。

### SoundTouchライブラリを実行可能プログラムにリンクさせる

```
target_link_libraries(JuceMeetsSoundTouch
    PRIVATE
        SoundTouch::SoundTouch
        ...
```

この記述内容も上記と同様に、vcpkgからパッケージを追加する操作をした際に実行結果の表示で案内されます。
上記ツールチェインファイルで指定したvcpkg配下からパッケージを探してもらうことができます。

vcpkgコマンドラインツールからの案内
```
soundtouch provides CMake targets:

    # this is heuristically generated, and may not be correct
    find_package(SoundTouch CONFIG REQUIRED)
    target_link_libraries(main PRIVATE SoundTouch::SoundTouch)
```

この案内に従って該当する記述をCMakeLists.txtに追加しています。

## ビルド実行命令の記述

CMakeのGenerate処理を実行する際にの引数として`-DVCPKG_TARGET_TRIPLET`を指定してください。引数として渡す値はvcpkg操作時に選択したパッケージと同じtripletを渡すことでvcpkgでインストールしたパッケージとリンクされます。

### Windows

```
…中略

cmake -B Build-Release -G "%GENERATOR_VS2022%" . -DCMAKE_BUILD_TYPE=Release -DVCPKG_TARGET_TRIPLET=x64-windows-static
cmake --build Build-Release --config Release

…中略
```

### macOS

```
…中略

cmake -B Build-Release  -G "Xcode" . -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64 -DVCPKG_TARGET_TRIPLET=x64-osx
cmake --build Build-Release --config Release

…中略
```

### Linux

```
…中略

cmake -B Build-Release  -G "Ninja" . -DCMAKE_BUILD_TYPE=Release -DVCPKG_TARGET_TRIPLET=x64-linux
cmake --build Build-Release --config Release

…中略
```