ノイズジェネレータに音量を付けます。
音声バッファを直接処理する良い例だね~
JUCEプログラミング、チュートリアルのSynthの項目、「Control audio levels」を進めていきます。ホワイトノイズを発生させるアプリを作成してきましたが、今回のチュートリアルで音量の調整ができるようになります。
公式のチュートリアルページは、次のページでです。
ノイズジェネレータのプログラムは、GitHubリポジトリに配置しておりますので、ご利用ください。
こんな人の役に立つかも
・JUCEフレームワークに入門したい人
・JUCEで音声プログラミングを学びたい人
・JUCEのAudioチュートリアルを進めている人
改良後のアプリ概要
今までのチュートリアルで作成してきた、ホワイトノイズジェネレータは、アプリ起動時に音を出すという単純なものでした。今回のチュートリアルでは、次のようにSliderをインターフェースに追加することで、ホワイトノイズの音量を変化できるものとなります。
注意点
今回のチュートリアルでは、スライダーオブジェクトの値変化を、getNextAudioBuffer関数内で直接取得する( auto level = (float)levelSlider.getValue(); の部分)方法をとっています。
今回のチュートリアルでは、プログラムの簡略化、という点で、直接取得しているようです。そのため、オーディオスレッドでのGUIパラメータ読み込みとなるので、推奨されるやり方でない点はご理解ください。また、このオーディオスレッドではGUIの値(今回はスライダーの値)を書き換えないようにするという点が注意のようです。
MainComponent.hへの追加
「MinComponent.h」に以下のprivateなメンバ変数を定義します。
private:
juce::Random random;
//★SliderとLabelを追加します。
juce::Slider levelSlider;
juce::Label levelLabel;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
SliderオブジェクトとLabelオブジェクトを定義します。
MainComponent.cppの追加
コンストラクタ
コンストラクタでは、先ほどのスライダーとラベルを初期化して、それぞれaddAndMakeVisible関数でGUIの可視化を行います。
MainComponent::MainComponent()
{
//★sliderとlabelを初期化して可視化します。
levelSlider.setRange(0.0, 0.25);
levelSlider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 100, 20);
levelLabel.setText("Noise Level", juce::dontSendNotification);
addAndMakeVisible(levelSlider);
addAndMakeVisible(levelLabel);
setSize(800, 600);
setAudioChannels(0, 2); // no inputs, two outputs
}
スライダーは、setRange関数でスライダーの最大値と最小値を与えます。
ラベルは、setTextBoxStyle関数でテキストボックスの位置、サイズを与えます。また、テキストの内容としてsetText関数で「NoiseLevel」というテキストを与えます。
getNextAudioBlock関数
音声処理部分です。以前の処理をコメントアウトしまして、処理を比較してみるとどのように変化したかがわかりやすいと思い、このようにしています。
void MainComponent::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
{
/*
for (auto channel = 0; channel < bufferToFill.buffer->getNumChannels(); ++channel)
{
auto* buffer = bufferToFill.buffer->getWritePointer(channel, bufferToFill.startSample);
for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
buffer[sample] = random.nextFloat() * 0.25f - 0.125f;
}
*/
//↑の処理を以下の処理に変更します。
auto level = (float)levelSlider.getValue();
//[1]
for (auto channel = 0; channel < bufferToFill.buffer->getNumChannels(); ++channel)
//[2]
{
auto* buffer = bufferToFill.buffer->getWritePointer(channel, bufferToFill.startSample);
//[3]
for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)//
[4]
{
//[5]ノイズを発生させます。
auto noise = random.nextFloat() * 2.0f - 1.0f;
buffer[sample] = noise * level;
}
}
}
[1]の部分で、スライダーの値を取得しています。これは簡易的なやり方で、スライダーの値をgetNextAudioBuffer関数が呼び出される一定のタイミングで読み取りに行くようなやり方です。この関数の中でスライダーの値を変更してはいけません。
[2]のループで、オーディオバッファの処理をチャンネル数(今回はLとR2チャンネル)繰り返します。[3]と[4]のループは以前と同様の処理です。この部分は、次の記事で詳細を書きましたので、どうぞ、ご参照ください。
[5]が今回の要所で、音声バッファを計算して処理しています。音声バッファはそのままつなげると波形になります。縦軸が音量、ゲインとかいう呼び方で表現されます。音の大きさになります。チュートリアルではきれいな波形ですが、今回は私の手書きで^^;
nextFloatの最小値0の時:0×2-1=-1
nextFloatの中間値0.5の時:0.5×2-1=0
nextFloatの最大値1の時:1×2-1=1
ということで、[5]では、計算式で0~1を出力するnextFloatを-1~1というフルスケールの音声データ-1~1の範囲の値に変換しています。
また、スライダーの値0~0.25の値を最後にかけることで、スライダの値が最大の「0.25」の時、-0.25~0.25までの音量に変換しています。音量という点では、このように、音声データのマイナスの最大値とプラスの最大値の上限、下限を同時に変更する必要がある点が注意です。
resized関数
結構、毎回チュートリアルの文章中にはないので、GUIが表示されない~となります 笑
resized関数でGUIの表示位置を設定して完了です。
void MainComponent::resized()
{
levelLabel.setBounds(10, 10, 90, 20);
levelSlider.setBounds(100, 10, getWidth() - 110, 20);
}
音量のスケーリング、理解できました!
GitHubリポジトリにプログラムを配置しましたので、ご利用ください。