遅延音声をフィルタリングするようです。
遅延音声を加工することで、いろいろなエフェクトが実現できそう~
JUCEチュートリアル、「Create a string model with delay lines」を引き続きやっています。前回までで、ディレイエフェクトが実装できました。今回は、フィルターを挟むことで、遅延した信号の音質を加工するような実装を行います。といっても遅延信号にフィルターを挟むだけなので、実装量は少ないです。
公式チュートリアルはこちらになります。
こんな人の役に立つかも
・JUCE「Create a string model with delay lines」チュートリアルを進めている人
・Projucerテンプレートで「Create a string model with delay lines」チュートリアルの実装を行なっていきたい人
・遅延信号にフィルターをかけ、ディレイエフェクトをよりカスタマイズしたい人
フィルター前とフィルター後の音
前半が、フィルター無しのディレイです。0:16から始まる後半がハイパスフィルターを通したディレイの音です。
※再生前に音量を絞っておいて、適正な音量で再生することをお勧めします。
フィルターを通していないディレイは、残響としては不自然な印象を受けます。
AudioEngineクラスの確認
ディレイのみがエフェクトとしてかかる状態にしたかったので、ディストーション、キャビネットシミュレータ、リバーブのエフェクトをAudioEngineクラスからはずしました。
private:
//==============================================================================
enum
{
//[1]インデックスをディレイのみとしました。
//distortionIndex,
//cabSimulatorIndex,
delayIndex,
//reverbIndex
};
/*juce::dsp::ProcessorChain<Distortion<float>, CabSimulator<float>,
Delay<float>, juce::dsp::Reverb> fxChain;*/
//[2]ProcessorChainはディレイだけとしました。
juce::dsp::ProcessorChain<Delay<float>> fxChain;
また、Midiキーボードで発音する際に、ベロシティによって音量がばらつく点は、テストの際には、一定としたかったので、Voiceクラスの以下を一時的に修正して一定の音量が出力されるように変更しました。
class Voice : public juce::MPESynthesiserVoice
{
//...略...
void noteStarted() override
{
//...略...
//processorChain.get<oscIndex>().setLevel(velocity);
processorChain.get<oscIndex>().setLevel(0.5);
}
遅延音声にフィルターを実装
今回のプログラムの変更点は、2行です。
Delayクラスのprocess関数と、prepare関数を変更するのみとなります。
template <typename Type, size_t maxNumChannels = 2>
class Delay
{
//...略...
template <typename ProcessContext>
void process(const ProcessContext& context) noexcept
{
//...略...
for (size_t ch = 0; ch < numChannels; ++ch)
{
//...略...
for (size_t i = 0; i < numSamples; ++i)
{
//auto delayedSample = dline.get (delayTime);//[1]ここをコメントアウトします。
auto delayedSample = filter.processSample(dline.get(delayTime));//[2]こちらに変更しました。
auto inputSample = input[i];
auto dlineInputSample = std::tanh(inputSample + feedback * delayedSample);
dline.push(dlineInputSample);
auto outputSample = inputSample + wetLevel * delayedSample;
output[i] = outputSample;
}
}
}
[2]のように、遅延信号のdelaydSampleを取得する際の処理を変更しました。
dsp::IIR::FilterクラスのprocessSample関数は、特定のサンプルに対してフィルター処理を行う関数です。以前フィルターモジュールを利用して実装した際には、process関数をprocessブロックから呼び出すのみでした。process関数は、音声処理ブロックを処理するので、今回は、単一サンプルのみを処理できるprocessSample関数を利用しています。
次に、prepare関数で、フィルターの初期化設定で、一次のハイパスフィルターを設定します。
void prepare(const juce::dsp::ProcessSpec& spec)
{
jassert(spec.numChannels <= maxNumChannels);
sampleRate = (Type)spec.sampleRate;
updateDelayLineSize();
updateDelayTime();
//filterCoefs = juce::dsp::IIR::Coefficients<Type>::makeFirstOrderLowPass (sampleRate, Type (1e3));
//↑を↓に変更しました。
filterCoefs = juce::dsp::IIR::Coefficients<Type>::makeFirstOrderHighPass(sampleRate, Type(1e3));
//...略...
一次のハイパスフィルターを設定しました。「filterCoefs」変数は、privateなメンバに定義されているdsp::IIR::Coefficientsクラスのメンバになります。makeFirstOrderHighPass関数で一次のハイパスフィルターの係数を計算して取得できます。引数として、第一引数にサンプルレート、第二引数にカットオフ周波数を取ります。
第二引数のType(1e3)は、AudioEngineクラスでDelay<float>としてテンプレートクラスの型が指定されているので、関数型のキャストでfloat(1e3)となります。また、1e3は10の3乗=1000なので、1kHzがカットオフ周波数になります。