Raspberry PiでVSTプラグインを開発する話


本記事は Raspberry Pi Advent Calendar 2017 の12月18日向けに投稿した記事です。

VSTプラグインとは?

VST(Steinberg's Virtual Studio Technology)とは、ドイツのSteinberg(スタインバーグ)社が提供している仮想音響技術のことです。音源(シンセサイザー)として機能するものはVSTi(VST instrument)とも呼ばれます。C++のダイナミックライブラリで実装され、DAW(Digital Audio Workstation)等のホストアプリケーションに組み込むことができ、フィルタや仮想楽器をシミュレートすることができます。仕様そのものはクロスプラットフォームなC++ライブラリで提供されているため、プラットフォーム上でコンパイルしてしまえば、そのプラットフォーム上で楽器やエフェクターとして使用することが出来ます。

本記事では、そのVSTプラグインを実装するにあたり、より簡単かつGUI用のコンポーネントが充実しているJUCEフレームワークを利用します。

JUCEとは?

JUCE (Jules' Utility Class Extensions)は、C++言語によるマルチメディア系アプリケーションの開発を支援するフレームワークです。クロスプラットフォーム設計のライブラリと、付属されているプロジェクトジェネレータ『Projucer』から各種IDE(VisualStudio, Xcode, Makefile)向けにプロジェクトファイルを出力することで、ワンソースからWindows, macOS, Linux, iOS, Android で動作するアプリケーションを作成することができます。
公式サイト

JUCEの最大の特徴として、オーディオプラグインを開発するためのテンプレートが充実していることが挙げられます。VST/AudioUnit/AAX/RTASプラグインといった、DTMユーザーにはお馴染みのプラグインフォーマットを開発するのに長けており、日本国内外で多くの採用事例があります。
JUCEについてより詳しく知りたい場合は、こちらの記事JUCE ハンズオン 〜JUCEをはじめよう〜や、JUCE Advent Calendar 2017をご覧いただければと思います。

JUCEライブラリ と Projucer

JUCEフレームワークは、各種機能を提供する『JUCEライブラリ』と、各種プラットフォーム向けのプロジェクトを出力するジェネレータ『Projucer』によって構成されます。
『JUCEライブラリ』には機能別に『モジュール』という単位で構成が分かれており、『Projucer』はプロジェクトに必要なモジュールを取り込んで、IDE用のプロジェクトファイルやビルドスクリプトに紐付ける作業を担当します。

開発環境

◆ハードウェア
Raspberry Pi 3 Model B

◆OS
Raspbian Stretch (2017/11/29)

ARM用のProjucerをビルドする

公式サイトから配布されたLinux用のProjucerはx86アーキテクチャをターゲットにしたものであるため、ARMアーキテクチャのRaspberry Pi上では動作しません。そこで、Raspberry Pi用のProjucerを自身の端末上でビルドします。

レシピ

Linuxに手慣れている方はこちらのコマンドリストだけでも説明は十分かと思います。


// JUCEリポジトリをクローン
$ git clone https://github.com/WeAreROLI/JUCE.git

// 依存パッケージをインストール
$ sudo apt-get install clang freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 ladspa-sdk

// 作業ディレクトリに移動
$ cd JUCE/extras/Projucer/Builds/LinuxMakefile

// GCCでコンパイルする場合
$ make CXX=g++ -j4

// LLVM-Clangでコンパイルする場合
$ make CXX=clang++ "TARGET_ARCH=-march=armv7-a" -j4

1. 準備

まず、JUCEをダウンロードしましょう。公式ダウンロードサイト
Windows/macOS/Linuxから選択することができます。Linux向けを選択すれば良いです。
※Linux用のProjucerが同梱されていますがx86アーキテクチャをターゲットにしているため、ARMアーキテクチャのRaspberry Piでは起動することができません。

また別の方法として、GitHubリポジトリからDLすることもできます。ターミナルで次のコマンドを打ちましょう。


$ git clone https://github.com/WeAreROLI/JUCE.git

2. 依存パッケージのインストール


$ sudo apt-get install clang freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 ladspa-sdk

JUCEライブラリのビルドに必要な依存パッケージをインストールします。ついでにLLVM-Clangコンパイラをインストールします。ディストリビューションに付属のGCCでもコンパイルすることができます。

3. 作業ディレクトリへの移動


$ cd JUCE/extras/Projucer/Builds/LinuxMakefile

リポジトリ内に含まれるプロジェクトの中から、ProjucerプロジェクトのLinuxMakefileが置かれたディレクトリに移動します。
※ディレクトリは各自の環境に合わせて入力してください。
※サンプルプロジェクトの一部に”LinuxMakefile”用プロジェクトが同梱されています。

4. コンパイルしてProjucerをビルドする

4-1. GCCでコンパイルする場合


 $ make CXX=g++

LinuxMakefileからビルドを実行します。
ビルドモードは”Debug”がデフォルトです。
各種オプションについては以下の通りです。

・CXX=g++
… LinuxMakefileオプションです。C++コンパイラにg++を使用します

4-2. LLVM-Clangでコンパイルする場合


$ make CXX=clang++ "TARGET_ARCH=-march=armv7-a"

LinuxMakefileからビルドを実行します。
ビルドモードは”Debug”がデフォルトです。
各種オプションについては以下の通りです。

・CXX=clang++
… LinuxMakefileオプションです。C++コンパイラにclang++を使用します

・“TARGET_ARCH=-march=armv7-a”
… LinuxMakefileオプションです。ターゲットアーキテクチャを明示的に指定します

4-3. 補足:Releaseビルド時のコマンド


// GCCでコンパイルする場合
$ make CXX=g++ CONFIG=Release

// LLVM-Clangでコンパイルする場合
$ make CXX=clang++ "TARGET_ARCH=-march=armv7-a" CONFIG=Release

・CONFIG=Release
… LinuxMakefileオプションです。”Release”モードでビルドします。

5. Projucerを起動する

ビルドが完了したらProjucerを起動してみましょう。
初回起動時ではこちらの画面のように、サインイン画面が表示されます。
サインインにROLIアカウントが必要となるので、ROLIアカウントも登録しておきましょう。

サインインが成功すると、以下の画面となりプロジェクト作成が可能になります。
※こちらの記事“JUCE ハンズオン 〜JUCEをはじめよう〜”『補足:GPLライセンスで使う方法』に書かれた手順でGPLv3ライセンスで使用することで、ROLIアカウントの登録を免除することが出来ます。

VSTプラグインを作成する

JUCEフレームワークの開発環境が整ったので、実際にVSTプラグインを作ってみましょう。
本記事では、オーディオプログラミング界隈のHello World!こと、Hello Sine Waveを作成します。

1. 新規プロジェクトを作成

Projucerを起動したら、[File] → [New Project...]をクリックして、テンプレートを選択する画面を開きます。
今回は、"Audio Plug-In"テンプレートから新規プロジェクトを作成します。

新規作成メニューでは、[Target Platforms]の項目を確認します。ここを自身の開発環境に合わせてチェックを入れます。
Linuxでの開発なので、Linux Makefileにチェックを入れます。
※Linux MakefileはmacOSのmakeコマンドでもビルドできます。

新規プロジェクトを作成すると、"PluginEditor.h/cpp"と"PluginProcessor.h/cpp"のソースファイルが自動的に作成されます。

2. プロジェクト設定を確認する

プロジェクト設定ツールから、ビルドするプラグインフォーマット形式や、バイナリに付随するメタデータを編集することができます。
対応するプラグインフォーマットは、VST2, VST3, AudioUnit, AudioUnit v3, AAX, RTASに対応しています。(VST3, AAX, RTASは要SDK)

  1. 歯車アイコンをクリックすると、プロジェクト設定画面が表示されます。
  2. [Build VST]にチェックが付いていることを確認します。
  3. [Build Standalone Plug-In]にチェックが付いていることを確認します。(必須ではありませんが、後々便利なので)

3. モジュール設定:JUCEライブラリをローカルにコピーする

JUCEライブラリのソースファイルをプロジェクトディレクトリのローカルにコピーする処理を有効にしておきます。
この設定を有効にしておくことで、JUCEリポジトリのディレクトリのパーミッション設定が原因でライブラリを参照できないというトラブルを回避することができます。

  1. Modules設定の歯車アイコンをクリック
  2. [Set all modules to copy locally]をクリック
  3. [Make Local Copy]の項目が"NO"→"YES"になったら有効

4. プロジェクトの保存とソースコードの編集

[File] → [Save Project]でプロジェクトを保存します。

プロジェクトが保存されると、各種ソースコードやライブラリが追加されます。

5. サイン波を鳴らす処理を実装する

プロジェクトが保存されてることが確認できたら、サイン波を鳴らす処理を実装してみましょう。"PluginProcessor.cpp"の関数"processBlock"が、オーディオバッファの更新処理を実装することで実現できます。
関数"void HelloSineWaveAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)"は予め記述されているので、以下のようにその内部実装を変更します。


"PluginProcessor.cpp"

~~~中略~~~

void HelloSineWaveAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    const int totalNumInputChannels  = getTotalNumInputChannels();
    const int totalNumOutputChannels = getTotalNumOutputChannels();

    for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
        float* channelData = buffer.getWritePointer (channel);

#define M_PI 3.14159265358979323846
        const float level = 0.5f;
        for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
        {
            channelData[sample] = sinf(M_PI * 2 * sample/ buffer.getNumSamples() * 2) * level;
        }
    }
}

~~~中略~~~

このソースコードを記述すると、オーディオバッファを更新する度に、オーディオバッファ全体でサイン波一周分(360°=2π)を書き込む処理が実行されます。

6. ビルドの実行とスタンドアローン版での動作確認

上記のソースコードを記述したら、Makefileが置かれたディレクトリまで移動してmakeコマンドを実行します。


// GCCでコンパイルする場合
~/HelloSineWave/Builds/LinuxMakefile$ make CXX=g++ CONFIG=Release

// LLVM-Clangでコンパイルする場合
~/HelloSineWave/Builds/LinuxMakefile$ make CXX=clang++ "TARGET_ARCH=-march=armv7-a" CONFIG=Release

ビルドが成功したらアプリケーションを起動してみましょう。
『2.プロジェクト設定項目』の通りに[Build Standalone Plug-In]にチェックが付いていれば、スタンドアローンの実行プログラムも一緒にビルドされます。
以下のコマンドから起動、若しくはファイラーからダブルクリックで実行プログラムを起動してみましょう。


~/HelloSineWave/Builds/LinuxMakefile$ ./build/HelloSineWave

アプリケーションが起動すると、ウインドウが表示されます。
オーディオデバイスとの接続が正常にできていれば、スピーカーからサイン波が鳴っていることを確認できます。

ホストアプリケーションでVSTプラグインを使用する

それでは、本記事で作成したVSTプラグインをホストアプリケーション(DAW)で実際に楽器として使用してみましょう。

1. ホストアプリケーションの紹介

今回は、Tracktion Waveformを動作確認に使用します。Waveformは公式でRaspberry Piに対応したDAWです。
DAWの中でも比較的低価格ですし、デモ版も用意されていますので、是非お試しください。
http://www.minet.jp/brand/tracktion/waveform/

2. VSTプラグインのファイル本体をスキャン対象ディレクトリに移動する

ビルドしたファイルのうち、拡張子が".so"のファイルがVSTプラグイン本体です。

このファイルを、ホストアプリケーションがスキャン対象としているディレクトリに移動しておきます。
Tracktion Waveformでは、初期値として"/home/pi/.vst"が設定されているので、フォルダを作成しその中にVSTプラグインのファイル本体を移動しておきましょう。

3. ホストアプリケーションでVSTプラグインをスキャンする

Tracktion WaveformではVSTプラグインをスキャンする機能があります。
スキャンを実行すると、対象ディレクトリに置かれたVSTプラグインがリストに追加されます。

4. VSTプラグインをトラックにインサートする

VSTプラグインをリストから選択してトラックにインサートします。正常に動作するとサイン波がトラックに流れます。
また、プラグインのGUIを表示すると、先ほどスタンドアローン形式と同様のGUIが表示されます。

以上で、Raspbery PiでVSTプラグインを開発する環境が整いました。
VSTプラグインのAPIを理解することで様々な機能を持ったVSTプラグインを作ることができます。
ラズパイで動く楽器やエフェクターを自作してみたいという方には、JUCEフレームワークがお勧めです。Windows/macOSで開発したオーディオアプリケーションがそのままラズパイで動くので、開発効率も高いです。
JUCEに興味を持った方は、JUCE Advent Calendar 2017にてシンセサイザーの作り方から、カラフルなGUIの実装など、様々なTipsをご覧いただき、お使いいただければと思います。