ディストーションのDSPアルゴリズム
目的
オーディオ・プログラミングにおけるDSPがイマイチ定着していないので、その理解を深めるためにデータ信号処理のアルゴリズムをJUCEで実装してみようかと思いました。タイトルのとおり、エフェクターがディストーションです。エレキギターにかけて試してみました。
教材
このYouTubeの動画の手順に沿って実装しました。JUCEのバージョンは古いのでちょっとだけ調整が必要でした(下の方に詳しく書いています)が、肝心のSource/PluginProcessor.cpp
内のprocessBlock()
のコードは大体そのままで大丈夫でした。
アルゴリズム
動画で使われたアルゴリズム:
g(x) = (2/\pi)\arctan(x)
Desmosで書いてみる:
入力(x)が大きくなるにつれて、エフェクターが効くのがわかります。この関数について他人に聞きましたが、これに正弦波を通したら、波形が矩形波の形になっていき、周波数の倍音が上がります。他にディストーションのアルゴリズムが多くあり、これはその中の一つです。
アルゴリズムの実装
音源を処理する前に元のデータがcleanSig
に格納されます。
つまみは4つあるので、各つまみの値が取得され、音に影響を与えます。
そのためにアルゴリズムの計算がここで上記の関数と比べてより長くなっています。
...省略...
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のリポジトリをご参照ください。
最新版で動作できるための調整
AudioValueTreeStateの新イニシャライザに合わせる
前は2つの引数がイニシャライザに渡される形(state(*this, nullptr)
みたいな形)だったけど、今は4つの引数を渡すことになりました。
PluginProcessor.h
で作ったインスタンス変数はstate
ですので、下記ではstate(第1引数...第4引数)
になっています:
...省略...
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
のコードも少し変わったけど、大きな変更はなかったのでここで割愛します。
結果
ちゃんと動いた!やっぱりこういうのを書いて実際に使ってみるのはテンションが上がります。が、音自体がそんなに良いとは思いませんでした。これから色んな関数を見て、僕にとって聴きやすい(カッコいい)音を見つけたいです。
他に試してみたい・見てみたいもの
Author And Source
この問題について(ディストーションのDSPアルゴリズム), 我々は、より多くの情報をここで見つけました https://qiita.com/gazayas/items/a9a8cc6266dd95d25d73著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .