サイン波を出力するようなプラグインを作ってみようかと、思いました。
これは、FX系じゃなくてインストゥルメンツ系のプラグインになるのかな?
特定の周波数のサイン波を出力するようなプラグインを作成してみました。(DAW標準でついているのを使えばよいのですが^^;)
とりあえず、自作でいろいろとやってみたかったので、まずは、以前の記事を参考にして、サイン波が出るアプリをプラグイン化してみました。
実は、wavetable方式で実装しようとしたのですが、よくわからない倍音がたくさん出てきて、現状一番シンプルな実装にとどまっています…
こんな人の役にたつかも?
・JUCEプラグインを作成している人
・JUCEでオシレータプラグインを実装したい人
・JUCEでサイン波を出力したい人
プロジェクトの作成
今回、「TheBasicSine」という名称でプロジェクトを作成しました。そのため、実装のときのプラグイン本体クラスは、「TheBasicSineAudioProcessor」などというクラス名で記載していきます。
実装
PluginProcessor.h
以前の記事で実装したSineOscillatorクラスをそのまま利用します。PluginProcessor.hに以下のようにクラスを追加しました。
class SineOscillator
{
public:
SineOscillator() {}
void setFrequency(float frequency, float sampleRate)
{
auto cyclesPerSample = frequency / sampleRate;
angleDelta = cyclesPerSample * juce::MathConstants<float>::twoPi;
}
forcedinline void updateAngle() noexcept
{
currentAngle += angleDelta;
if (currentAngle >= juce::MathConstants<float>::twoPi)
currentAngle -= juce::MathConstants<float>::twoPi;
}
forcedinline float getNextSample() noexcept
{
auto currentSample = std::sin(currentAngle);
updateAngle();
return currentSample;
}
private:
float currentAngle = 0.0f, angleDelta = 0.0f;
};
プラグイン本体のAudioProcessorクラスに以下のprivateなメンバを追加しました。
class TheBasicSineAudioProcessor : public juce::AudioProcessor
{
//...略...
private:
//以下のメンバを追加しました。
float level = 0.0f;
juce::OwnedArray<SineOscillator> oscillators;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TheBasicSineAudioProcessor)
};
PluginProcessor.cpp
prepareToPlay関数に以下のように追記しました。
ここは、以前の記事から少しだけ変更しています。
void TheBasicSineAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
//[1]
auto numberOfOscillators = 1;
for (auto i = 0; i < numberOfOscillators; ++i)
{
auto* oscillator = new SineOscillator();
//[2]
auto frequency = 440.0f;
oscillator->setFrequency((float)frequency, (float)sampleRate);
oscillators.add(oscillator);
}
//[3]
level = 0.1f / (float)numberOfOscillators;
}
[1]では、以前の実装が、ここを変更することでいくつものオシレータを作成できるような実装となっていますので、1として一つのサイン波を出力するようにしています。
[2]で、frequencyを固定値、440としています。
[3]では、レベルを0.1に変更しました。オシレータ数で割る部分は以前の複数オシレータの場合の実装をそのまま利用してます。今回は1なので、level変数は0.1から変化しません。
次に、processBlock関数を実装します。
void TheBasicSineAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
//テンプレートで作成される部分です。
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
//ここまでを残します。
//ここから実装を追加します。
//[1]バッファを準備します。
auto* leftBuffer = buffer.getWritePointer(0);
auto* rightBuffer = buffer.getWritePointer(1);
for (auto oscillatorIndex = 0; oscillatorIndex < oscillators.size(); ++oscillatorIndex)
{
auto* oscillator = oscillators.getUnchecked(oscillatorIndex);
for (auto sample = 0; sample < buffer.getNumSamples(); ++sample)
{
auto levelSample = oscillator->getNextSample() * level;
leftBuffer[sample] += levelSample;
rightBuffer[sample] += levelSample;
}
}
}
以前の実装は、アプリだったので、bufferToFillというオブジェクトでしたが、プラグインの実装では、buffer(AudioBufferクラス)を直接扱うので、このように変更しています。サンプル数を取得する際も、「buffer.getNumSamples()」としています。
課題点
以下の2点でかなり現在進行形でハマっています。
1)ウェーブテーブル方式で以前実装したクラスを流用していますが、うまくいかず、倍音が同時に出力されるようになってしまいました。そのため、一番シンプルな方法でサイン波を出力しています。
2)Logic Pro Xで認識させようとすると、クラッシュします^^;
Logic Pro Xのプラグイン認識、めんどくさいです・・・
ということで、とりあえず今回のプラグインは、AudioPluginHostで動作検証を行っています。
いつかまた、クリアしていきたいです。