今回は、オシレータを実装していきました。
オシレータといえば、音を出す部分
JUCEプログラミングで、音声処理を数珠繋ぎするプラグインを作成しています。前回の記事で、プラグイン内でDSP処理を追加することができるようになり、またエフェクト処理のベースとなるクラスを作成しました。今回は、音声処理にオシレータを実装していく部分を進めていきたいと思います。DSPなど、まだ学んでない部分は多々ありましたが、とりあえずそのようなものか、という理解で突き進んでいる点はご了承ください^^;
行っているチュートリアルページは次の「Implementing an oscillator」の項目になります。
こんな人の役に立つかも
・JUCEフレームワークに入門したい人
・VST、AUプラグイン開発の最初の一歩を踏み出したい人
・JUCEのチュートリアルをやっている人
オシレータの実装
オシレータは、シンセサイザーなどに使われる発音部分になります。今回は、音声処理で、440Hzのサインはを発音するオシレータを作成します。
今回のプログラムはすべて「PluginProcessor.h」に記載していきます。
オシレータプロセッサクラスの作成
前回定義したProcessBaseクラスのすぐ下に次のように「OscillatorProcessor」クラスを作成しました。
class OscillatorProcessor : public ProcessorBase
{
public:
//...
const juce::String getName() const override { return "Oscillator"; }
private:
juce::dsp::Oscillator<float> oscillator;
};
publicなメンバ関数として、getName関数をオーバーライドして、「Oscillator」という文字列を返すようにしておきます。
また、privateなメンバ変数として、dspのOscillatorオブジェクトである「oscillator」を定義しておきます。
オシレータのコンストラクタ
次のコンストラクタ(初期化関数)をpublicな関数として作成します。
public:
//↓以下のコンストラクタを追加しました。
OscillatorProcessor()
{
//①出力する音の周波数は440ヘルツを指定します。
oscillator.setFrequency(440.0f);
//②出力する波形をサイン波に設定します。
oscillator.initialise([](float x) { return std::sin(x); });
}
//...以下略...
①で、oscillatorオブジェクトの「setFrequency」関数を呼び出し、音の出力周波数で440ヘルツを設定します。数値は、小数(float)で与えます。先ほど準備したprivateなメンバ変数、oscillatorオブジェクトの関数を呼び出しています。
440Hzは音叉の音でもあり、A(ラ)の音です。440Hzって、ISOだったんですね^^;
②では、initialize関数で、このようにして、sin関数でサイン波波形を設定することができるようです。引数として「std::function」を渡すようです。「std::function」は、C++の機能です。
今回は、ここにsin関数を渡しています。
prepareToPlayでの設定
コンストラクタに引き続き、prepareToPlay関数をオーバーライドしていきます。
//...略...
void prepareToPlay(double sampleRate, int samplesPerBlock) override
{
//①サンプルレートとオーディオバッファサンプル数の設定を行います。
juce::dsp::ProcessSpec spec{ sampleRate, static_cast<juce::uint32> (samplesPerBlock) };
//②設定します。
oscillator.prepare(spec);
}
①では、dspのProcessSpecクラスのオブジェクト、「spec」を作成して、サンプルレートとオーディオバッファサイズを設定しています。これは、DAW側からわたされる数値をそのまま受け渡すだけですので、関数の引数に入ってきたものを渡します。
②で、設定したspecオブジェクトを引数に、oscillatorの「prepare」関数でoscillatorにサンプルレートとバッファサイズを設定します。
processBlockの作成
音声処理のprocessBlockを先ほどのprepareToPlayの下に作成していきます。
//...略...
void processBlock(juce::AudioSampleBuffer& buffer, juce::MidiBuffer&) override
{
juce::dsp::AudioBlock<float> block(buffer);
juce::dsp::ProcessContextReplacing<float> context(block);
oscillator.process(context);
}
音声のバッファに対してデータの変更などの処理を行う部分になります。
まずは、バッファをAudioBlockクラスの「block」に入れています。
さらにProcessContextReplacingクラスの「context」に「block」を入れています。(ここでいう入れるは、それぞれのコンストラクタに初期化処理の引数として与えています。)
最後に、contextをoscillatorのprocess関数に与えることでDSP処理(ここでは440Hzの波形データの書き込み)が行われている模様です。
resetの実装
続いて、以下のreset関数もオーバーライドしておきます。oscillatorのreset関数で、プロセッサの内部状態をリセットするような機能だそうです。今後、DSPを学ぶときにはしっかり理解していきたいです。
//...略...
void reset() override
{
oscillator.reset();
}
JUCEのDSPについて
音声処理のライフタイムサイクルというものがあるらしく、(DSPの章に書いてありました。)先ほど、oscillatorでは、「prepare」と「process」、「reset」という三段階の関数を呼び出して処理を行うようです。
完成したオシレータクラス
完成したオシレータクラスの全体を以下に掲載しておきます。
class OscillatorProcessor : public ProcessorBase
{
public:
OscillatorProcessor()
{
oscillator.setFrequency(440.0f);
oscillator.initialise([](float x) { return std::sin(x); });
}
void prepareToPlay(double sampleRate, int samplesPerBlock) override
{
juce::dsp::ProcessSpec spec{ sampleRate, static_cast<juce::uint32> (samplesPerBlock) };
oscillator.prepare(spec);
}
void processBlock(juce::AudioSampleBuffer& buffer, juce::MidiBuffer&) override
{
juce::dsp::AudioBlock<float> block(buffer);
juce::dsp::ProcessContextReplacing<float> context(block);
oscillator.process(context);
}
void reset() override
{
oscillator.reset();
}
const juce::String getName() const override { return "Oscillator"; }
private:
juce::dsp::Oscillator<float> oscillator;
};
文体が教科書的になってきてすみません・・・