歪みの効果で音作りが広がります。
歪みの作り方が具体的に実感できるよね〜
JUCEチュートリアル、DSPの「Add distortion through waveshaping and convolution」、「Integrate the Waveshaper」の項目を進めていきます。前回までに勉強したウェーブシェーピングを利用して、波形に歪みを加えていく実装になります。
公式のチュートリアルページはこちらになります。
今回の内容は、前回実装したプログラムを基本として実装していきます。前回のデモアプリの基本の実装記事はこちらをご参照ください。
デモアプリに歪みを加えることで、次のような音の変化となりました。そもそもデモアプリの音色がノイズ多くて気持ち悪いのですが^^;
ギターのディストーションみたいに歪んでいることがわかります。今回のチュートリアルは、歪ませて元の音色のアラを目立たなくする、ということでいいでしょうか・・・
実装
PluginProcessor.hの空の状態のDistortionクラスに実装を行っていきます。
空のDistortionクラス
前回実装したでもアプリのPluginProcessor.hファイルに、次のような空の状態のDistortionクラスが準備されています。このクラスにウェーブシェーピングクラスによる歪みを追加していきます。
template <typename Type>
class Distortion
{
public:
//==============================================================================
Distortion() {}
//==============================================================================
void prepare(const juce::dsp::ProcessSpec& spec)
{
juce::ignoreUnused(spec);
}
//==============================================================================
template <typename ProcessContext>
void process(const ProcessContext& context) noexcept
{
juce::ignoreUnused(context);
}
//==============================================================================
void reset() noexcept {}
private:
//==============================================================================
};
Distortionクラスへの追加
まずは、Distortionクラスのprivateなメンバとして、ProcessorChainクラスのメンバ、processirChainと、そのインデックスに利用するenumを定義します。
private:
//[1]enumの定義です。
enum
{
waveshaperIndex
};
//[2]ProcessorChainクラスの定義です。
juce::dsp::ProcessorChain<juce::dsp::WaveShaper<Type>> processorChain;
};
[2]のProcessorChainクラスは、今まで何度も利用している、DSPモジュールを数珠繋ぎできるような便利なクラスです。今回は、WaveShaperモジュールを1つだけ入れています。今回は数珠繋ぎにならず、単発でWaveShaperクラスを利用するのと同じですが、今後、ここにゲインなど、他のDSPモジュールの追加も簡単にできるようにprocessorChainクラスを利用しています。
次に、processorChainクラスをリセットします。reset関数に次のように追記しました。
void reset() noexcept
{
processorChain.reset();
}
prepare関数にも、次のようにprocessorCahinのprepareを呼び出します。
void prepare (const juce::dsp::ProcessSpec& spec)
{
//juce::ignoreUnused(spec);//こちらは削除します。
processorChain.prepare (spec);
}
そして、コンストラクタです。
public:
//==============================================================================
Distortion()
{
auto& waveshaper = processorChain.template get<waveshaperIndex>();//[1]
//[2]waveshaperの関数を定義します。
waveshaper.functionToUse = [] (Type x)
{
return juce::jlimit (Type (-0.1), Type (0.1), x);
};
}
[1]のように、get関数で、waveshaperモジュールのインデックス(enum)を指定して、waveshaperの参照を取得します。これで、processorChainクラス内のwaveshaperクラスのオブジェクトにアクセスできます。[2]では、waveshaperの関数を定義します。dsp::WaveShaperは構造体のテンプレートになります。このコンストラクタで定義しているように、functionToUseに入力信号に反映させる処理を定義します。ここでは、jlimt関数で、-0.1または0.1を越えた信号を強制的に切り落とします。
最後に、音声処理を行うprocess関数を変更します。processChainのprocess関数を入れ子で呼び出すのみです。
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
//juce::ignoreUnused(context);//こちらは削除します。
processorChain.process (context);//追記します。
}
例題
functionToUseで-0.1~0.1という範囲を超えた波形を削り取りました。チュートリアルのExerciseにある-0.5~0.5にした時の音の変化を見てみたいと思います。
波形が削り取られるゲインレベルに余裕ができたので、より元の波形に近くなりました。この部分の数値パラメータを小さくすることで、削り取られる波形の頭が大きくなり、歪んだ感じになり、大きくすることで、波形が原型に近くなり、歪みが少なくなります。パラメータ化すると、歪み量を調整できるようになりそうです。
Distortionクラス全体
今回のDistortionクラス全体のプログラムです。コピペなどしてご利用くださいませ。
template <typename Type>
class Distortion
{
public:
Distortion()
{
auto& waveshaper = processorChain.template get<waveshaperIndex>();
waveshaper.functionToUse = [](Type x)
{
return juce::jlimit(Type(-0.1), Type(0.1), x);
};
}
void prepare(const juce::dsp::ProcessSpec& spec)
{
processorChain.prepare(spec);
}
template <typename ProcessContext>
void process(const ProcessContext& context) noexcept
{
processorChain.process(context);
}
void reset() noexcept
{
processorChain.reset();
}
private:
enum
{
waveshaperIndex
};
juce::dsp::ProcessorChain<juce::dsp::WaveShaper<Type>> processorChain;
};