cppの実装に入っていきます。
デシベルとゲインと二つある点が混乱ポイントだね
JUCEプログラミング、引き続きチュートリアルページの「Synth」の項の「Control audio levels using decibels」を進めていきます。
今回は、cppの実装の内容となります。
公式のチュートリアルページはこちらのページとなります。
こんな人の役に立つかも
・JUCEプログラミングを勉強している人
・JUCEの「Control audio levels using decibels」チュートリアルを進めている人
・JUCEでスライダーのデシベル表示をしたい人
MainComponent.cppの実装
「.h」に引き続き、「.cpp」ファイルの変更を行っていきます。
コンストラクタ
以前のホワイトノイズアプリの内容から、[1]の内容に書き換えました。
MainComponent::MainComponent()
{
//↓変更する前のプログラムです。
/*
levelSlider.setRange(0.0, 0.25);
levelSlider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 100, 20);
levelLabel.setText("Noise Level", juce::dontSendNotification);
addAndMakeVisible(levelSlider);
addAndMakeVisible(levelLabel);
*/
//↓[1]変更後のプログラムです。
//[1-1]範囲をデシベルで定義します。
decibelSlider.setRange(-100, -12);
decibelSlider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 100, 20);
//[1-2]値変化時のコールバック関数をラムダ式で定義します。
decibelSlider.onValueChange = [this] { level = juce::Decibels::decibelsToGain((float)decibelSlider.getValue()); };
//[1-3]levelの初期値0.0fをデシベルに変換して値をセットします。
decibelSlider.setValue(juce::Decibels::gainToDecibels(level));
decibelLabel.setText("Noise Level in dB", juce::dontSendNotification);
addAndMakeVisible(decibelSlider);
addAndMakeVisible(decibelLabel);
setSize(800, 600);
setAudioChannels(0, 2); // no inputs, two outputs
}
[1]で書き換えた内容としては、基本的に「decibelSlider」と「decibelLabel」の初期化とした点です。setSizeはウィンドウサイズですし、setAudioChannnels関数はアプリの入出力を出力2チャンネルを有効とするだけなので、変更していません。
スライダーオブジェクトを「decibelSlider」へと変更したことで、スライダーに与える値が「ゲイン値」から「デシベル」へと変化します。
ゲイン値について
デシベル、と聞いて、なんとなくイメージできるんですが、ゲインというものが逆に何なのか、ちょっとイメージできなかったので、整理です。
「ゲイン値」というのは、今まで与えてきた音量の数値と理解しています。0.0fにすると無音になり、1.0fに近づけると音が大きくなる、という数値で以前の記事でも紹介したように、1.0を超えないようにして出力するような音量データの値でした。(音量のスケーリングの記事で紹介しました。)
今までのチュートリアルをやってきたところのイメージです。
JUCEはDecibelクラスというデシベルを便利に扱うことができるクラスがあります。この「ゲイン値」をデシベルへ変換するいい感じの関数を持っています。(計算方法などの数学的な部分は省略しますね)
「decibelsToGain」というデシベルからゲインへ変換する関数と、「gainToDecibels」というゲインからデシベルへ変換する関数が存在しています。
便利な世の中です
本当に・・・
ちょっと話が遠回りになってしまいましたが、[1-1]では、decibelSliderへ-100dBから-12dBまでの上下限を設定します。[1-2]では、スライダクラスの値が変化したときのハンドラをラムダ式で登録しています。そして、[1-3]でdecibelSliderの値を0.0fに設定しました。「juce::Decibels::gainToDecibels(level)」のように、DecibelクラスのgainToDecibal関数で0.0fのゲイン値を設定しています。ゲイン値の0.0fは無音状態なので、そのようなデシベル値に変換されてスライダーの値(今回は-100dB)となります。
getNextAudioBlock関数
getNextAudioBlock関数は、一部をコメントアウト(削除)して変更しました。
void MainComponent::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
{
//auto level = (float)levelSlider.getValue();
//[1]以下のようにlevelの取得を変更しました。
auto currentLevel = level;
auto levelScale = currentLevel * 2.0f;
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)
{
/*auto noise = random.nextFloat() * 2.0f - 1.0f;
buffer[sample] = noise * level;*/
//[2]以下の1行に変更しました。
buffer[sample] = random.nextFloat() * levelScale - currentLevel;
}
}
}
getNextAudioBlock関数では、スライダーから直接値を取りに行くという方法ではなく、levelというメンバ変数を見に行くような方式に変更しています。これは以前のチュートリアルの改善項目でもありましたが、音声処理スレッドでGUI関連のことをしないようにする工夫の一つでもあります。
また、もう一つの改善点、[1]の時点でcurrentLevelに2をかけることで、音声処理の内部での×2をしないようにし、[2]でcurrentLevelを引くことで、音声処理の計算量を削減する工夫をした処理をしています。
この音声処理の工夫の詳細については、こちらの記事で詳細に記載していますので、ご参照ください。
resized関数
resized関数については、変更したインスタンス名として書き直しています。
void MainComponent::resized()
{
/*levelLabel.setBounds(10, 10, 90, 20);
levelSlider.setBounds(100, 10, getWidth() - 110, 20);*/
//changed
decibelLabel.setBounds(10, 10, 120, 20);
decibelSlider.setBounds(130, 10, getWidth() - 140, 20);
}
アプリでのデシベルの使われ方
本アプリでデシベルとゲイン値の2つを利用していますので、アプリの構造的にどこの部分でデシベルが、ゲイン値が利用されているのかの整理です。
デシベルは人間の聴覚的な音量の増減に合わせたものなので、まずは人がデシベルで音を与えて、スライダーの値とします。
スライダーに設定された数値は単位がデシベルなので、この値をゲイン値に変換して音声処理などで利用します。最終的には音声処理ではゲイン値を音量としている点が混乱するポイントだとおもいましたので、メモしておきました。
今回も最後まで行っていないのですが、長くなってしまったので、残りの実装を次の記事でやっていきたいと思います。