JUCEでPCMシンセサイザープラグイン（VSTi）を作る

# はじめに
PCMシンセサイザープラグイン（VSTi）を作ってみたいと思い作り方を調べていたところ、JUCEというオーディオアプリケーションを作るためのフレームワークがあることを知りました。

JUCEでPCMシンセサイザープラグイン（VSTi）を作ってみたいと思い作り方を調べていたところ、スタンドアロンのPCMシンセサイザーを作る記事はありましたが、プラグイン（VSTi）形式のPCMシンセサイザーを作る記事があまりありませんでした。

そこで今回は、プラグイン（VSTi）形式のPCMシンセサイザーの作り方について紹介してみようと思います。

作るにあたっては以下の記事を参考にしました。基本的に元記事と流れは同じですが、そのままでは上手くいかない点があったため一部改変・追記しています。
https://vaporsoft.net/creating-a-very-simple-sampler-audio-plugin-in-juce-5/

# 開発環境
- Visual Studio 2022
- JUCE 7.0.1

# プロジェクトの準備
## プロジェクトの作成
Projucer.exeを開きメニューバーから File -> New Project...を選びます

左のメニューからPlugin -> Basicを選びます。Project Nameに`SimpleSampler`を入力しCreate Projectを押します。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945857/582feb4b-eb5a-e476-7f9f-4d97193b383d.png)
保存場所の選択画面が表示されるので、任意のフォルダを選択します。

## プロジェクトの設定
左上の歯車マークをクリックしプロジェクトの設定を開きます。
左の一覧のPlugin Characteristicsの`Plugin is a Synth`および`Plugin MIDI Input`にチェックを入れます。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945857/997c33d8-7218-f250-2b02-687c7d127045.png)

## 空のソースファイルの追加
左のメニューのFile Explolerを選択しSourceを表示します。
Sourceを右クリックし、`Add New CPP & Header File...`をクリックします。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945857/89429900-ac20-3189-d3d6-e0fcf98555fa.png)

ファイル名に`SimpleSynth`を入力し保存します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945857/eea7181a-775f-55ab-89e4-129c07c14abd.png)

Ctrl+Sを押し、プロジェクトの設定を保存します。

## Visual Studio 2022の起動
`Select exporter`からVisual Studio 2022を選択し、右のVisual Studioのマークをクリックします。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945857/fc2091ae-35a5-e672-7a77-4acfc7b14e0c.png)


# 実装
## SimpleSynthクラスの実装
以下のように、`SimpleSynth.h`にjuceのライブラリを読み込むために`JuceHeader.h`をincludeします。次に`Synthesiser`クラスを継承した新しいクラス`SimpleSynth`を定義します。

```c++:SimpleSynth.h
// SimpleSynth.h

#pragma once
#include <JuceHeader.h>

using namespace juce;

class SimpleSynth : public Synthesiser {
public:
	void setup();
private:
	// manager object that finds an appropriate way to decode various audio files.  Used with SampleSound objects.
	AudioFormatManager audioFormatManager;
};
```

SimpleSynthの`setup()`関数に、シンセサイザーの初期設定を行うための処理を実装します。
```c++:SimpleSynth.cpp
// SimpleSynth.cpp

#include "SimpleSynth.h"
#define MAX_VOICES 16

void SimpleSynth::setup() {
	// add voices to our sampler
	for (int i = 0; i < MAX_VOICES; i++) {
		addVoice(new SamplerVoice());
	}

	// set up our AudioFormatManager class as detailed in the API docs
	// we can now use WAV and AIFF files!
	audioFormatManager.registerBasicFormats();

	// now that we have our manager, lets read a simple file so we can pass it to our SamplerSound object.
	File* file = new File("file.wav");
	ScopedPointer<AudioFormatReader> reader = audioFormatManager.createReaderFor(*file);

	// allow our sound to be played on all notes
	BigInteger allNotes;
	allNotes.setRange(0, 128, true);

	// finally, add our sound
	addSound(new SamplerSound("default", *reader, allNotes, 60, 0, 10, 10.0));
}
```
補足として、ファイル`file.wav`のパスは、VSTホストアプリケーションのカレントディレクトリからの相対パスになります。

## SimpleSamplerAudioProcessorクラスの実装
DAWからMIDIを受け取り`SimpleSynth`クラスを操作するための`SimpleSamplerAudioProcessor`クラスを実装します。
`PluginProcessor.h`を開き、以下のように追記します。
```c++:PluginProcessor.h

/* 前略 */

#pragma once

#include <JuceHeader.h>
#include "SimpleSynth.h" // 追記1

/* 中略 */

private:
    //==============================================================================
    SimpleSynth synth; // 追記2
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleSamplerAudioProcessor)
};
```

次に、`SimpleSamplerAudioProcessor`クラスの`processBlock`関数内の処理を全て削除します。そして、オーディオをレンダリングするために必要な処理を`processBlock`関数に以下の通り定義します。
```c++:PluginProcessor.cpp
void SimpleSamplerAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
    juce::ScopedNoDenormals noDenormals;
    auto totalNumInputChannels = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

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

    synth.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples());
}
```

次に`SimpleSamplerAudioProcessor`クラスの関数`prepareToPlay`でサンプルレートの設定を行います。以下の通り追記します。
```c++:PluginProcessor.cpp
void SimpleSamplerAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    synth.setCurrentPlaybackSampleRate(sampleRate); // 追記
}
```

最後に`SimpleSamplerAudioProcessor`クラスのコンストラクタでシンセサイザーの初期設定を行います。以下の通り追記します。
```c++:PluginProcessor.cpp
SimpleSamplerAudioProcessor::SimpleSamplerAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  juce::AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", juce::AudioChannelSet::stereo(), true)
                     #endif
                       )
#endif
{
    synth.setup(); // 追記
}
```

# ビルド
Visual Studio 2022のメニューからビルド -> ソリューションのビルドを実行します。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945857/8fe170f9-3d3f-6999-350b-2ff12fe9f20a.png)

以下のように出力されたら成功です。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945857/6d95ba29-1ad3-fec6-c6d0-1ee9268da490.png)

# vst3およびwavファイルの配置
1. `SimpleSampler\Builds\VisualStudio2022\x64\Debug\VST3`にSimpleSampler.vst3ファイルが生成されているので、SimpleSampler.vst3を`C:\Program Files\Common Files\VST3`にコピーします。
2. シンセサイザーで再生するwavファイルを`file.wav`にリネームし、VSTホストアプリケーションのカレントディレクトリにコピーします。通常はVSTホストアプリケーションの`.exe`ファイルが配置されているディレクトリです。

# 動作確認
VSTホストアプリケーション（DAW）で`SimpleSampler`という名前のプラグインを開いて確認します。VSTホストアプリケーションのカレントディレクトリに配置したwavファイルが再生されれば成功です。

# おわりに
VSTi形式のPCMシンセサイザーを作成できました。今回のサンプルでは、`.vst3`ファイルの他に、シンセサイザーが再生する`.wav`ファイルも適切なパスへ配置する必要がありました。
`.wav`ファイルは`.vst3`にバイナリとして同梱したほうが使い勝手が良いため、次の記事では`.wav`ファイルをバイナリとして組み込む方法を紹介します。

https://qiita.com/shoshoi/items/4eeea1091fd49ff17cff

今回作成したプロジェクトは以下リポジトリに置いているので、興味のある人は見てみてください。

[リポジトリ](https://github.com/shoshoi/SimpleSampler)