最も簡単にVST/AudioUnitプラグインを作ってDAWで使ってみる方法

VSTプラグイン。SteinbergのSDKを使って、C++コードを書いて開発するのは結構敷居が高いと思います。JUCEというフレームワークもありますが、SDKのパス通したりしてセットアップするだけで結構大変だったりするし、結局アルゴリズムはC言語で書かないといけないので、そこで躓いてしまう・・・　またはプログラムなんて難しそうで作ろうなんて考えたこともない・・・
そんな方に、とっても簡単にVST(Effect)/AUプラグインを開発できちゃう方法を、1～6のステップで説明したいと思います。

今回はモジュレーション系エフェクトの基本であるChorusを作ってみましょう。これを応用すれば、Modulation Delay, Tape Echo（風）, Analog（風） Delay, Flangerなど色んなものが作れます。

## 0. Chorusのアルゴリズム概要
Chorusは、複数の楽器で演奏するのを模擬するために、信号の遅延とピッチのズレを生じさせるエフェクトです。処理としては、メモリーに書き込んだ信号を、少し遅らせて読み出すことで遅延を生じさせ、さらにその読み出すスピードをSin波状（メーカーによっては三角波の場合もある）に揺らすことで、ピッチのズレを発生させています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/db36f99f-c413-a3e5-fadb-9c937a998270.png)

## 1. 環境構築
必要なツールをダウンロードしてインストールしましょう。

##### MATLABをダウンロードしてインストール
学生の方はStudentバージョン、社会人の方はHomeバージョン、またはトライアルバージョンで試しましょう。必要なオプションはSignal Processing Toolbox, DSP System Toolbox, Audio Toolboxです。Homeだとこれらのオプション全部込みで28,970円で買えます。
> トライアルバージョンのダウンロード
https://jp.mathworks.com/products/get-matlab.html
現在の最新版はR2020a

基本的には有償ツールなので、本格的に使うときは買ったほうが良さそうです。

##### Visual StudioまたはXcodeのインストール
Windowsマシンで作る人はVisual Studioを、Macで作る人はXcodeを入手します。これはフリーで入手できますね。

> Visual Studioのダウンロード
https://visualstudio.microsoft.com/ja/vs/older-downloads/
フリー版で良いので、Visual Studioをインストール。MATLAB R2020aに対応しているVisual Studioは2015, 2017, 2019であればどれでも良い。

> Xcodeのダウンロード
https://apps.apple.com/jp/app/xcode/id497799835?mt=12
Xcodeはversion 9.x～11.xのどれかをインストールしましょう。

これに興味を持つ人はDAWは既に持っていると思いますが、DAWを持っていなければインストールしておきましょう。
FreeのDAWはReaperが有名です。
https://www.reaper.fm/

## 2. セットアップ
MATLABで次のコマンドを実行して使用するコンパイラの設定を行います。
` mex -setup `
すると以下のようなメッセージが出てくるので、Visual C++を選択する。
**mex -setup C++** のところがリンクになっているので、それをクリックして、C++のほうも同じくVisual C++を選択します。
> 
MEX は C 言語のコンパイルに 'Microsoft Visual C++ 2017 (C)' を使用するよう設定されています。
別の C コンパイラを選択するには、次のいずれかを選択してください。
MinGW64 Compiler (C)  mex -setup:C:\MATLAB\R2020a\bin\win64\mexopts\mingw64.xml C
Microsoft Visual C++ 2017 (C)  mex -setup:C:\Users\amatsumo\AppData\Roaming\MathWorks\MATLAB\R2020a\mex_C_win64.xml C
> 
> 別の言語を選択するには、次のいずれかを選択してください。
 **[mex -setup C++ ]()**
 mex -setup FORTRAN

## 3. コードを書く準備
MATLABの「ホーム」から、「新規スクリプト」をクリックして、コードのエディタを立ち上げます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/ccf790d9-91c1-6bc1-03f3-e2b9dd7ffaae.png)
次に「新規作成」をクリックして、System Object > 標準テンプレートを開きましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/6a84d838-1b14-660a-b6c0-8858452786aa.png)

propertiesやmethodsの枠組みが書かれたファイルが生成されので、適当なファイル名で保存しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/cd9dac53-a4b7-6f92-e2f7-0e27f19e36e3.png)

propertiesというのは、VST作るときのパラメータを書くところです。
属性を ```(Access = private) ```というように指定すると、ユーザからはアクセスできず、内部処理からしかアクセスできない変数として扱うことができます。
methodsの下にはいくつかfunctionが並んでいますが、それぞれ以下の役割を持っています。

 - setupImpl : VST起動時に初期化する処理を書くところ。フィルタ使うんだったら係数設定するとか、パラメータ設定するとかをここに書く。
 - stepImpl : VSTを実行中に行う処理を書くところ。ここがサンプル時間（※1）ごとに呼ばれて処理される。
 - resetImpl : VSTの実行終了後に行う処理を書くところ。通常は必要ない。

※1 正確にはフレーム単位で処理するので、フレーム時間ごとですが、ここではあまり細かいことは気にする必要はありません。

## 4. コードを書く
コードを書いてみましょう。
中で使用する関数は4つだけです。

1. getSampleRate: コードが何Hzのサンプリング周波数で動作しているか取得します。
2. dsp.VariableFractionalDelay: 信号を遅延させるための関数。遅延量はダイナミックに可変させることができます。
3. audioOscillator: Sin波を生成します。遅延量をSin波状に揺らすために使います。
4. size: 信号や変数の行列サイズを取得します。入力信号とSin波の行列サイズを合わせるために使用します。

パラメータはSeparation(Pre Delay), Depth, Rate, WetDryMixの4つです。

まず、先ほど作成したSystem Objectの最初の行に 「& audioPlugin」を追加します。プログラミング用語ではクラスを継承すると言ったりしますが、理解できなくても問題ありません。これはVSTプラグインを作成するためのおまじないのようなもので、これを書いておくことでVSTに関連する色々な機能がこのファイルの中で使えるようになります。

次に、ユーザがアクセスできるパラメータをコード内で使う変数としてpropertiesの中に書きます。ここではコード内の変数名を書けば良いので、必ずしもパラメータ名と一致させる必要はありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/39dc9d1f-6415-c848-03ca-eb6899c7eb51.png)

次はこれらの変数とユーザパラメータを紐づけて、``` properties (Constant) ``` の中に定義します。
入出力のチャンネル数や、VSTにしたときのエフェクト名、ベンダー名などもここで定義できます。

あとはユーザはアクセスしない、プログラム内からのみアクセスされる変数を ``` properties(Access = private) ``` 定義します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/d2874864-e0d7-021e-b0d5-172de0958d08.png)

これでプロパティの記述は終わりです。次にmethodsに処理内容を書いていきます。
今回はシンプルに作成したいので、setupImplとresetImplは削除してしまいましょう。
その代わり、このSystem Objectが呼ばれたときにイニシャライズをするためのmethodを作成します。

getSampleRateでサンプリング周波数を取得し、audioOscillatorでSin波生成用のオブジェクトを定義します。振幅が0.5、オフセットが0.5なので、0～1のレンジで上下するSin波になります。そして、dsp.VariableFractionalDelayで遅延させるためのオブジェクトを定義します。ここでは使用する処理用のオブジェクトを定義し、初期パラメータを設定しているだけで、まだ処理は行っていません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/23437d3b-664c-a541-1215-02c9d905fa0c.png)

さて、次が一番肝心な処理部分stepImplですが、たったの7行書くだけです。
``` size ```で入力信号の行列サイズを取得（42行目）し、audioOscillatorで生成するSin波信号のサイズと合わせて（43行目）います。Lch出力は入力音をそのまま出力するため、47行目では入力uの1列目（Lch）をそのまま出力yの1列目（Lch）に代入しています。48行目で入力uの2列目（Rch）をpreDelay+Sin波サンプル数分遅延させて、それを49行目でWet/Dryミックスして出力yの2列目（Rch）しています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/d12495e6-7634-5c36-1a92-2c5aded678ba.png)

## 5. テスト
コードが完成したら音楽ソースにエフェクトをかけてテストしてみましょう。
MATLABコマンドを実行してオーディオのテスト用GUIを起動します。
``` >> audioTestBench ```
Object Under Testに作成したmyChorusを指定し、Input/Outputソースを設定したら Runボタンを押すと音楽ソースの再生が始まり、パラメータを調整して所望の動作をしているか確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/001ca0f9-75c4-8467-6ade-ce5b5301e624.png)

右のほうにある Generate Audio Pluginボタンを押すと、ファイル名*.dllのVSTプラグインが生成されています。

## 6. DAWで使う
生成されたVSTプラグインファイルを、DAWのエフェクトプラグイン保存フォルダにコピーします。（DAWソフトによって設定方法が異なるので、各ソフトの使用方法に従って設定して下さい。）

以下はフリーのDAW Reaperに読み込んだところです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/f261d319-6428-8d28-7824-f3fa41eb0d18.png)

これで完成しました。どうだったでしょうか？これまでC言語で書いていた人には驚くほど簡単だったんじゃないでしょうか？
このサンプルをベースに、色んな面白いエフェクトを作成してもらえると幸いです。是非僕にも教えて下さい。

## 参考
### MATLABのドキュメント
ドキュメントには、プラグインのサンプルが沢山入っているので、これらのソースコードが参考になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/b189ce2a-89f9-680b-aa96-227377993cf1.png)

MATLABからだとこのコマンドで開きます。
` web(fullfile(docroot, 'audio/ug/audio-plugin-example-gallery.html')) `

Webページ
https://www.mathworks.com/help/releases/R2020a/audio/ug/audio-plugin-example-gallery.html

### コードサンプル
```matlab: 
classdef myChorus < matlab.System & audioPlugin
    
    % Public, tunable properties
    properties
        PreDelay = 10   % 0 - 10ms
        Depth = 0.5    % 0 - 1
        Rate = 3    % 0 - 10 Hz
        WetDryMix = 50 % 0 - 100
    end
    
    properties (Constant)
        PluginInterface = audioPluginInterface(...
            'InputChannels',2,...
            'OutputChannels',2,...
            'PluginName','myChorus',...
            'VendorName','Your Name',...
            'VendorVersion','1.0.0',...
            audioPluginParameter('PreDelay','DisplayName','Separation','Label','ms','Mapping',{'lin' 0 10}),...
            audioPluginParameter('Depth','DisplayName','Depth','Label','','Mapping',{'lin' 0 1}),...
            audioPluginParameter('Rate','DisplayName','Rate','Label','Hz','Mapping',{'lin' 0 10}),...
            audioPluginParameter('WetDryMix','DisplayName','Wet/dry mix','Label','%','Mapping',{'lin' 0 100}));
    end
    
    properties(Access = private)
        fs
        osc
        delayLine
    end
    
    methods
        function obj = myChorus
            fs = getSampleRate(obj);
            obj.fs = fs;
            obj.osc = audioOscillator('Frequency', 0.1,...
                'Amplitude', 0.5, 'DCOffset',0.5, 'SampleRate', fs);
            obj.delayLine = dsp.VariableFractionalDelay(...
                'MaximumDelay',1000);
        end
    end
    methods(Access = protected)
        function y = stepImpl(obj,u)
            numSamples = size(u);     % uのサイズを取得
            obj.osc.SamplesPerFrame = numSamples(1); % audioOscillatorの行数とuの行数を合わせる
            obj.osc.Frequency = obj.Rate; % Sin波の周波数を設定      
            preDelay = obj.PreDelay * 0.001 * obj.fs;  % Pre Delay time (ms)を設定
            y = zeros(numSamples);  % 出力yの領域確保
            y(:,1) = u(:,1);    % Lch: Dry out
            temp = obj.delayLine(u(:,2), preDelay+obj.osc()*obj.Depth*44.1); % 遅延
            y(:,2) =  temp * obj.WetDryMix*0.01 + u(:,2)* (1-obj.WetDryMix*0.01); % Rch: Wet mix out
        end
    end
end

```

おわり。