テンポ同期している場合と、手動が切り替えられるようにしたいですね
久しぶりにトグルボタンを使ってみよう~
JUCEでのディレイプラグインの開発を進めています。現状、ディレイエフェクトがDAWのBPMに同期するのみの処理となってしまったので、トグルボタンを追加して、DAWのテンポ同期するか、手動のパラメータでディレイタイムを設定するかを選択できるようにします。
赤い枠のトグルボタン(一度押すとオン状態、もう一度押すとオフ状態となるようなボタン)を使って、テンポと同期するかどうかを選択できるようになります。テンポシンク機能と名付けてみます。
こんな人の役に立つかも
・JUCEプラグイン開発をしている人
・JUCEでディレイプラグインを作成したい人
・JUCEディレイプラグインで、テンポ同期と手動を切り替えたい人
エディタ側
まずはトグルボタンを準備します。
class TheDelayAudioProcessorEditor : public juce::AudioProcessorEditor
{
//...略...
//トグルボタンを追加しました。
juce::ToggleButton tempoSyncButton;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TheDelayAudioProcessorEditor)
};
コンストラクタにトグルボタンの初期化を行いました。
TheDelayAudioProcessorEditor::TheDelayAudioProcessorEditor (TheDelayAudioProcessor& p, juce::AudioProcessorValueTreeState& vts, juce::UndoManager& um)//20211104
//...略...
//トグルボタンの初期化を行います。
addAndMakeVisible(tempoSyncButton);
tempoSyncButton.onStateChange = [this] {
audioProcessor.setTempoSyncState(tempoSyncButton.getToggleState());
};
setSize (400, 300);
}
トグルボタンの状態変化時のラムダ式を「onStateChange」関数で登録しました。ラムダ式では、プロセッサ側に準備したbool変数へトグルボタンの状態を渡すような内容です。setSyncTmpoState関数は、後程プロセッサ側に実装するprivateなbool変数へのアクセサになります。トグルボタンの自身の状態をgetToggleState()関数で取得できますので、この返り値のbool型の値をアクセサの引数へと渡します。
そして、resized関数でトグルボタンを配置します。
void TheDelayAudioProcessorEditor::resized()
{
//...略...
//GUI上に配置します。
tempoSyncButton.setBounds(10, 240, 30, 30);
}
プロセッサ側
テンポシンクのトグルボタンの状態を受け取るための変数と、アクセサを追加します。
class TheDelayAudioProcessor : public juce::AudioProcessor, private juce::Timer
{
//...略...
//[2]アクセサでtempopSyncStateを変更できるようにします。
void setTempoSyncState(bool state) {
bool val = state;
tempopSyncState = val;
}
private:
//...略...
//[1]bool型の変数tempopSyncStateを準備します。
bool tempopSyncState = false;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TheDelayAudioProcessor)
};
[1]のbool型、privateメンバにアクセスするため、[2]のアクセサを作成しています。
次に、トグルボタンの状態によって、パラメータ更新処理を変化させます。まずは、パラメータを一定間隔で更新しているTimerCallback関数の処理を見直しました。
class TheDelayAudioProcessor : public juce::AudioProcessor, private juce::Timer
{
public:
//タイマーコールバック関数を次の内容に変更しました。
void timerCallback() override
{
//[1]ローカル変数val2とval3を先頭にもってきました。
float val2 = 0.0f;
float val3 = 0.0f;
//[2]テンポシンク時のディレイタイム計算です。
if(tempopSyncState == true){
playHead = this->getPlayHead();
playHead->getCurrentPosition(currentPositionInfo);
float selected_delayNum = *delayNumerator;
float delayNum = 0.0f;
if (selected_delayNum == 1.0f) {
delayNum = 1.0f;
}
else if (selected_delayNum == 2.0f) {
delayNum = 2.0f;
}
else if (selected_delayNum == 3.0f) {
delayNum = 4.0f;
}
else if (selected_delayNum == 4.0f) {
delayNum = 8.0f;
}
val2 = ((float)60.0 / current_bpm) * (4.0f / delayNum);
if (val2 >= 2.0f) {
val2 = 1.99f;
}
val3 = val2;
}
//[3]手動でディレイタイムを設定する処理です。
else {
val2 = *delayTime_L;
val3 = *delayTime_R;
if (val2 >= 2.0f) {
val2 = 1.99f;
}
if (val3 >= 2.0f) {
val3 = 1.99f;
}
}
//[4]最後に、パラメータを反映します。
processorChain.get<DelayIndex>().setDelayTime(0, val2);
processorChain.get<DelayIndex>().setDelayTime(1, val3);
}
[1]と[4]は共通の処理となるため、外に出しました。[2]の場合、トグルボタンがオンの時でDAWのテンポ情報からディレイタイムを計算します。[2]は、手動設定のスライダーからの値を取得してLとRそれぞれのディレイタイムを設定します。
prepare関数でも、同じようにトグルボタンの状態によってパラメータを設定するようにしました。同様の処理を行うだけです。
void TheDelayAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
processorChain.prepare({ sampleRate, (juce::uint32) samplesPerBlock, 2 });
//以下の処理を追加しました。(上記タイマーと同様の処理です。)
float val2 = 0.0f;
float val3 = 0.0f;
if (tempopSyncState == true) {
playHead = this->getPlayHead();
playHead->getCurrentPosition(currentPositionInfo);
float selected_delayNum = *delayNumerator;
float delayNum = 0.0f;
if (selected_delayNum == 1.0f) {
delayNum = 1.0f;
}
else if (selected_delayNum == 2.0f) {
delayNum = 2.0f;
}
else if (selected_delayNum == 3.0f) {
delayNum = 4.0f;
}
else if (selected_delayNum == 4.0f) {
delayNum = 8.0f;
}
val2 = ((float)60.0 / current_bpm) * (4.0f / delayNum);
if (val2 >= 2.0f) {
val2 = 1.99f;
}
val3 = val2;
}
else {
val2 = *delayTime_L;
val3 = *delayTime_R;
if (val2 >= 2.0f) {
val2 = 1.99f;
}
if (val3 >= 2.0f) {
val3 = 1.99f;
}
}
//ここまでの処理を追加しました。
float val4 = *wetLevel;
float val5 = *feedback;
//processorChain.get<DelayIndex>().setMaxDelayTime(val1);
processorChain.get<DelayIndex>().setDelayTime(0, val2);
processorChain.get<DelayIndex>().setDelayTime(1, val3);
processorChain.get<DelayIndex>().setWetLevel(val4);
processorChain.get<DelayIndex>().setFeedback(val5);
}