プラグインにアンドゥ・リドゥ機能があると便利ですね。
undoManagerクラスをAudioProcessorValueTreeStateで使えるみたいなので、検証してみよう~
JUCEチュートリアルでundoManagerについて学びましたので、早速、作成しているシンプルなハイパスフィルターにアンドゥ、リドゥ機能を実装していきたいと思います。
本記事は、こちらのシンプルなハイパスフィルターの続きの記事となりますが、AudioProcessorValueTreeStateクラスにundoManagerを実装したいという場合にも実装例としてお役に立てるかと思います。
こんな人の役に立つかも
・JUCEプラグインにアンドゥ、リドゥ機能を追加したい人
・AudioProcessorValueTreeStateにundoManagerを追加したい人
・本ブログのシンプルなハイパスフィルターを作成している人
アンドゥ・リドゥボタンを追加
UIに次のようなアンドゥ、リドゥボタンを追加していきます。
pluginEditor.h
テキストボタンクラスのオブジェクトをprivateなメンバとして追加しました。
class _01_panda_filterAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::Timer
{
public:
//...略...
//[1]2つのボタンを追加します。
juce::TextButton undoButton, redoButton;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (_01_panda_filterAudioProcessorEditor)
};
pluginEditor.cpp
まず、コンストラクタのメンバイニシャライザで[1]のようにボタンを初期化します。続くコンストラクタ内[2]で、undoButtonとreduButtonオブジェクトの可視化とラムダ式定義を行います。
_01_panda_filterAudioProcessorEditor::_01_panda_filterAudioProcessorEditor (_01_panda_filterAudioProcessor& p, juce::AudioProcessorValueTreeState& vts)
: AudioProcessorEditor (&p), valueTreeState(vts)
//...略...
, undoButton("Undo"), redoButton("Redo")//[1]
{
//...略...
//[2]ボタンの初期化です。
addAndMakeVisible(undoButton);
addAndMakeVisible(redoButton);
undoButton.onClick = [this] {DBG("undo");};//[3]ラムダ式でハンドラ処理を定義しておきます。
redoButton.onClick = [this] {DBG("redo");};
//...略...
[3]では、ボタンをクリックしたときの処理を定義します。とりあえずDBGでコンソール出力しておきます。
resized関数でボタンを配置します。
void _01_panda_filterAudioProcessorEditor::resized()
{
//...略...
//[1]
undoButton.setBounds(315,260,50,30);
redoButton.setBounds(365, 260, 50, 30);
}
ここまでで、UIにボタンを追加することができました。
undoManagerの追加
undoManagerクラスのオブジェクトをAudioProcessorValueTreeState(APVTS)クラスに渡すことで、APVTSのパラメータのアンドゥ、リドゥが簡単に管理できるようです。
まずは、Processor側にundoManagerを定義するところからです。
プロセッサ側の変更
プラグインの音声処理機能側の「pluginProcessor.h」と「pluginProcessor.cpp」ファイルに関する追加です。
pluginProcessor.h
まずは、UndoManagerクラスのオブジェクトを「undoManager」として、プラグイン本体のクラス、「〇〇AudioProcessor」のprivateなメンバとして追加しました。
class _01_panda_filterAudioProcessor : public juce::AudioProcessor
{
public:
//...略...
//[1]
juce::UndoManager undoManager;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (_01_panda_filterAudioProcessor)
};
PluginProcessor.cpp
コンストラクタでundoManagerをAudioProcessorVlueTreeStateクラスのオブジェクト「parameters」へ渡します。
_01_panda_filterAudioProcessor::_01_panda_filterAudioProcessor()
//...略...
, MyFilter()
, parameters(*this, &undoManager, juce::Identifier("APVTSTutorial"),//[1]第二引数にundoManagerの参照を入れます。
{
[1]のように、コンストラクタのメンバイニシャライザ内でAPVTSのパラメータを定義しているところで、第二引数を今までnullptrとしていたものを、「&undoManager」と変更します。
次に、createEditor関数で、エディタ側にundoManagerを渡します。最初に作成したアンドゥ・リドゥボタンのラムダ式内でundoManagerのインスタンスからundoとredoの関数を呼び出したいので、このようにしてプロセッサ側のundoManagerを渡すことでエディタ側でも利用できるようになります。
juce::AudioProcessorEditor* _01_panda_filterAudioProcessor::createEditor()
{
//[1]引数にundoManager追加しました。
return new _01_panda_filterAudioProcessorEditor (*this, parameters, undoManager);
}
エディタ側の変更
プラグインのUI等に関する機能「pluginEditor.h」「pluginEditor.cpp」ファイルに関する変更です。
pluginEditor.h
[1]のようにコンストラクタの引数にUndoManagerクラスの参照を追加します。
プロセッサ側のundoManagerの参照(アドレス)を受け取ることで、undoManagerをプロセッサ側と共有できるようにします。先ほどのcreateEditor関数で渡した引数と一致するようになりました。
class _01_panda_filterAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::Timer
{
public:
_01_panda_filterAudioProcessorEditor (_01_panda_filterAudioProcessor&, juce::AudioProcessorValueTreeState& vts, juce::UndoManager& um);//[1]参照変数umの追加
//...略...
juce::UndoManager& undoManager;//[2]
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (_01_panda_filterAudioProcessorEditor)
};
また、コンストラクタで受け取った、undoManagerを受け取って保持するため、参照変数として[2]のようにprivateなメンバを定義します。
pluginEditor.cpp
コンストラクタでは、[1]のように、先ほどのヘッダ定義と同じようにUndoManagerの参照を引数として追加します。
_01_panda_filterAudioProcessorEditor::_01_panda_filterAudioProcessorEditor (_01_panda_filterAudioProcessor& p, juce::AudioProcessorValueTreeState& vts, juce::UndoManager& um)//[1]定義と同じように引数追加しました。
: AudioProcessorEditor (&p), valueTreeState(vts)
//...略...
, undoButton("Undo"), redoButton("Redo")
, undoManager(um)//[2]
{
[2]で、privateなメンバとして定義したundoManagerに受け取ったumで、メンバイニシャライザで初期化しています。
引き続き、コンストラクタ内のテキストボタンのラムダ式を定義する部分で、次のように、undoManagerのインスタンスからundo、redo関数を呼び出すようにします。
//...略...
undoButton.onClick = [this] {
undoManager.undo();//[1]undo処理です。
DBG("undo");
};
redoButton.onClick = [this] {
undoManager.redo();//[2]redo処理です。
DBG("redo");
};
setOpaque(true);
startTimerHz(30);
setSize (500, 300);
}
それぞれ、undoボタンでundo関数、redoボタンでredo関数が呼び出されて、プラグイン全体でundoManagerを利用することができるようになりました。