ディストーションのDSPアルゴリズム

# 目的
オーディオ・プログラミングにおけるDSPがイマイチ定着していないので、その理解を深めるためにデータ信号処理のアルゴリズムを[JUCE](https://juce.com/)で実装してみようかと思いました。タイトルのとおり、エフェクターがディストーションです。エレキギターにかけて試してみました。

[ソースはこちら](https://github.com/gazayas/DistortionTest2)

# 教材
[このYouTubeの動画](https://www.youtube.com/watch?v=iNCR5flSuDs)の手順に沿って実装しました。JUCEのバージョンは古いのでちょっとだけ調整が必要でした(下の方に詳しく書いています)が、肝心の`Source/PluginProcessor.cpp`内の`processBlock()`のコードは大体そのままで大丈夫でした。

# アルゴリズム
動画で使われたアルゴリズム：

```math
g(x) = (2/\pi)\arctan(x)
```

[Desmos](https://www.desmos.com/calculator)で書いてみる：
![4C60B4CA-3306-4237-8DD1-17EBBF1943F9.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74313/cdb5ab25-05f5-f042-1897-031bd3354902.jpeg)

入力(x)が大きくなるにつれて、エフェクターが効くのがわかります。この関数について他人に聞きましたが、これに正弦波を通したら、波形が矩形波の形になっていき、周波数の倍音が上がります。他にディストーションのアルゴリズムが多くあり、これはその中の一つです。

# アルゴリズムの実装
音源を処理する前に元のデータが`cleanSig`に格納されます。
つまみは４つあるので、各つまみの値が取得され、音に影響を与えます。
そのためにアルゴリズムの計算がここで上記の関数と比べてより長くなっています。

```Source/PluginProcessor.cpp
...省略...
void DistortionTest2AudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

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

    float drive = state.getRawParameterValue("drive")->load();
    float range = state.getRawParameterValue("range")->load();
    float blend = state.getRawParameterValue("blend")->load();
    float volume = state.getRawParameterValue("volume")->load();

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

        for (int sample = 0; sample < buffer.getNumSamples(); sample++)
        {
            float cleanSig = *channelData;

            *channelData *= drive * range;

            // アルゴリズムはここで計算されます
            *channelData = (((((2.0f / float_Pi) * atan(*channelData)) * blend) + (cleanSig * (1.0f / blend))) / 2) * volume;

            channelData++;
        }
    }
}
...省略...
```

正直は僕にとって計算の部分はちょっと見にくかった(DSPのコードに慣れてないせい？)けど、ちゃんと動いていたのでいじりませんでした。

GUIはどう実装したかに興味ある人は、[GitHubのリポジトリ](https://github.com/gazayas/DistortionTest2)をご参照ください。

# 最新版で動作できるための調整
### AudioValueTreeStateの新イニシャライザに合わせる
前は２つの引数がイニシャライザに渡される形(`state(*this, nullptr)`みたいな形)だったけど、今は４つの引数を渡すことになりました。
`PluginProcessor.h`で作ったインスタンス変数は`state`ですので、下記では`state(第１引数...第４引数)`になっています：

```Source/PluginProcessor.cpp
...省略...
DistortionTest2AudioProcessor::DistortionTest2AudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", AudioChannelSet::stereo(), true)
                     #endif
                       ),
state(*this, nullptr, "Parameters",
      {
          std::make_unique<AudioParameterFloat> ("drive", "Drive", NormalisableRange<float> (0.0f, 1.0f, 0.0001f), 0),
          std::make_unique<AudioParameterFloat> ("range", "Range", NormalisableRange<float> (0.0f, 3000.0f, 0.0001f), 0),
          std::make_unique<AudioParameterFloat> ("blend", "Blend", NormalisableRange<float> (0.0f, 1.0f, 0.0001f), 0),
          std::make_unique<AudioParameterFloat> ("volume", "Volume", NormalisableRange<float> (0.0f, 3.0f, 0.0001f), 0),
      })
#endif
{
    state.state = ValueTree ("drive");
    state.state = ValueTree ("range");
    state.state = ValueTree ("blend");
    state.state = ValueTree ("volume");
}
...省略...
```

### ScopedPointerのSliderを普通のインスタンス変数に
`Source/PluginEditor.h`ではつまみを普通に`Slider driveKnob;`として定義しました。そのために、`Source/PluginEditor.cpp`のコードも少し変わったけど、大きな変更はなかったのでここで割愛します。


# 結果
ちゃんと動いた！やっぱりこういうのを書いて実際に使ってみるのはテンションが上がります。が、音自体がそんなに良いとは思いませんでした。これから色んな関数を見て、僕にとって聴きやすい(カッコいい)音を見つけたいです。

# 他に試してみたい・見てみたいもの
[Ivan Cohen - Fifty shades of distortion (ADC'17)](https://www.youtube.com/watch?v=oIChUOV_0w4)