Browse Source

Refactored StandaloneFilterWindow, breaking it up so that there's a separate class StandalonePluginHolder which can be used inside more customised windows.

tags/2021-05-28
jules 12 years ago
parent
commit
2db263c122
1 changed files with 238 additions and 171 deletions
  1. +238
    -171
      modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h

+ 238
- 171
modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h View File

@@ -29,152 +29,58 @@ extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
//==============================================================================
/**
A class that can be used to run a simple standalone application containing your filter.
An object that creates and plays a standalone instance of an AudioProcessor.
Just create one of these objects in your JUCEApplicationBase::initialise() method, and
let it do its work. It will create your filter object using the same createPluginFilter() function
that the other plugin wrappers use.
The object will create your processor using the same createPluginFilter()
function that the other plugin wrappers use, and will run it through the
computer's audio/MIDI devices using AudioDeviceManager and AudioProcessorPlayer.
*/
class StandaloneFilterWindow : public DocumentWindow,
public ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
class StandalonePluginHolder
{
public:
//==============================================================================
/** Creates a window with a given title and colour.
/** Creates an instance of the default plugin.
The settings object can be a PropertySet that the class should use to
store its settings - the object that is passed-in will be owned by this
class and deleted automatically when no longer needed. (It can also be null)
*/
StandaloneFilterWindow (const String& title,
Colour backgroundColour,
PropertySet* settingsToUse)
: DocumentWindow (title, backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
settings (settingsToUse),
optionsButton ("options")
StandalonePluginHolder (PropertySet* settingsToUse)
: settings (settingsToUse)
{
setTitleBarButtonsRequired (DocumentWindow::minimiseButton | DocumentWindow::closeButton, false);
Component::addAndMakeVisible (optionsButton);
optionsButton.addListener (this);
optionsButton.setTriggeredOnMouseDown (true);
createFilter();
if (filter == nullptr)
{
jassertfalse // Your filter didn't create correctly! In a standalone app that's not too great.
JUCEApplicationBase::quit();
}
filter->setPlayConfigDetails (JucePlugin_MaxNumInputChannels,
JucePlugin_MaxNumOutputChannels,
44100, 512);
deviceManager = new AudioDeviceManager();
deviceManager->addAudioCallback (&player);
deviceManager->addMidiInputCallback (String::empty, &player);
player.setProcessor (filter);
ScopedPointer<XmlElement> savedState;
if (settings != nullptr)
savedState = settings->getXmlValue ("audioSetup");
deviceManager->initialise (filter->getNumInputChannels(),
filter->getNumOutputChannels(),
savedState,
true);
if (settings != nullptr)
{
MemoryBlock data;
if (data.fromBase64Encoding (settings->getValue ("filterState"))
&& data.getSize() > 0)
{
filter->setStateInformation (data.getData(), (int) data.getSize());
}
}
setContentOwned (filter->createEditorIfNeeded(), true);
if (settings != nullptr)
{
const int x = settings->getIntValue ("windowX", -100);
const int y = settings->getIntValue ("windowY", -100);
if (x != -100 && y != -100)
setBoundsConstrained (juce::Rectangle<int> (x, y, getWidth(), getHeight()));
else
centreWithSize (getWidth(), getHeight());
}
else
{
centreWithSize (getWidth(), getHeight());
}
createPlugin();
setupAudioDevices();
reloadPluginState();
startPlaying();
}
~StandaloneFilterWindow()
~StandalonePluginHolder()
{
if (settings != nullptr)
{
settings->setValue ("windowX", getX());
settings->setValue ("windowY", getY());
if (deviceManager != nullptr)
{
ScopedPointer<XmlElement> xml (deviceManager->createStateXml());
settings->setValue ("audioSetup", xml);
}
}
deviceManager->removeMidiInputCallback (String::empty, &player);
deviceManager->removeAudioCallback (&player);
deviceManager = nullptr;
if (settings != nullptr && filter != nullptr)
{
MemoryBlock data;
filter->getStateInformation (data);
settings->setValue ("filterState", data.toBase64Encoding());
}
deleteFilter();
deletePlugin();
shutDownAudioDevices();
}
//==============================================================================
AudioProcessor* getAudioProcessor() const noexcept { return filter; }
AudioDeviceManager* getDeviceManager() const noexcept { return deviceManager; }
void createFilter()
void createPlugin()
{
AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Standalone);
filter = createPluginFilter();
processor = createPluginFilter();
jassert (processor != nullptr); // Your createPluginFilter() function must return a valid object!
AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Undefined);
processor->setPlayConfigDetails (JucePlugin_MaxNumInputChannels,
JucePlugin_MaxNumOutputChannels,
44100, 512);
}
/** Deletes and re-creates the filter and its UI. */
void resetFilter()
void deletePlugin()
{
deleteFilter();
createFilter();
if (filter != nullptr)
{
if (deviceManager != nullptr)
player.setProcessor (filter);
setContentOwned (filter->createEditorIfNeeded(), true);
}
if (settings != nullptr)
settings->removeValue ("filterState");
stopPlaying();
processor = nullptr;
}
/** Pops up a dialog letting the user save the filter's state to a file. */
void saveState()
//==============================================================================
/** Pops up a dialog letting the user save the processor's state to a file. */
void askUserToSaveState()
{
FileChooser fc (TRANS("Save current state"),
settings != nullptr ? File (settings->getValue ("lastStateFile"))
@@ -183,7 +89,7 @@ public:
if (fc.browseForFileToSave (true))
{
MemoryBlock data;
filter->getStateInformation (data);
processor->getStateInformation (data);
if (! fc.getResult().replaceWithData (data.getData(), data.getSize()))
{
@@ -194,8 +100,8 @@ public:
}
}
/** Pops up a dialog letting the user re-load the filter's state from a file. */
void loadState()
/** Pops up a dialog letting the user re-load the processor's state from a file. */
void askUserToLoadState()
{
FileChooser fc (TRANS("Load a saved state"),
settings != nullptr ? File (settings->getValue ("lastStateFile"))
@@ -207,7 +113,7 @@ public:
if (fc.getResult().loadFileAsData (data))
{
filter->setStateInformation (data.getData(), (int) data.getSize());
processor->setStateInformation (data.getData(), (int) data.getSize());
}
else
{
@@ -218,16 +124,29 @@ public:
}
}
/** Shows the audio properties dialog box modally. */
virtual void showAudioSettingsDialog()
//==============================================================================
void startPlaying()
{
player.setProcessor (processor);
}
void stopPlaying()
{
player.setProcessor (nullptr);
}
//==============================================================================
/** Shows an audio properties dialog box modally. */
void showAudioSettingsDialog()
{
DialogWindow::LaunchOptions o;
o.content.setOwned (new AudioDeviceSelectorComponent (*deviceManager,
filter->getNumInputChannels(),
filter->getNumInputChannels(),
filter->getNumOutputChannels(),
filter->getNumOutputChannels(),
true, false, true, false));
o.content.setOwned (new AudioDeviceSelectorComponent (deviceManager,
processor->getNumInputChannels(),
processor->getNumInputChannels(),
processor->getNumOutputChannels(),
processor->getNumOutputChannels(),
true, false,
true, false));
o.content->setSize (500, 450);
o.dialogTitle = TRANS("Audio Settings");
@@ -239,66 +158,214 @@ public:
o.launchAsync();
}
//==============================================================================
/** @internal */
void closeButtonPressed() override
void saveAudioDeviceState()
{
JUCEApplicationBase::quit();
if (settings != nullptr)
{
ScopedPointer<XmlElement> xml (deviceManager.createStateXml());
settings->setValue ("audioSetup", xml);
}
}
/** @internal */
void buttonClicked (Button*) override
void reloadAudioDeviceState()
{
ScopedPointer<XmlElement> savedState;
if (settings != nullptr)
savedState = settings->getXmlValue ("audioSetup");
deviceManager.initialise (processor->getNumInputChannels(),
processor->getNumOutputChannels(),
savedState,
true);
}
//==============================================================================
void savePluginState()
{
if (filter != nullptr)
if (settings != nullptr && processor != nullptr)
{
PopupMenu m;
m.addItem (1, TRANS("Audio Settings..."));
m.addSeparator();
m.addItem (2, TRANS("Save current state..."));
m.addItem (3, TRANS("Load a saved state..."));
m.addSeparator();
m.addItem (4, TRANS("Reset to default state"));
switch (m.showAt (&optionsButton))
{
case 1: showAudioSettingsDialog(); break;
case 2: saveState(); break;
case 3: loadState(); break;
case 4: resetFilter(); break;
default: break;
}
MemoryBlock data;
processor->getStateInformation (data);
settings->setValue ("filterState", data.toBase64Encoding());
}
}
/** @internal */
void resized() override
void reloadPluginState()
{
DocumentWindow::resized();
optionsButton.setBounds (8, 6, 60, getTitleBarHeight() - 8);
if (settings != nullptr)
{
MemoryBlock data;
if (data.fromBase64Encoding (settings->getValue ("filterState")) && data.getSize() > 0)
processor->setStateInformation (data.getData(), (int) data.getSize());
}
}
private:
//==============================================================================
ScopedPointer<PropertySet> settings;
ScopedPointer<AudioProcessor> filter;
ScopedPointer<AudioDeviceManager> deviceManager;
ScopedPointer<AudioProcessor> processor;
AudioDeviceManager deviceManager;
AudioProcessorPlayer player;
TextButton optionsButton;
void deleteFilter()
private:
void setupAudioDevices()
{
player.setProcessor (nullptr);
deviceManager.addAudioCallback (&player);
deviceManager.addMidiInputCallback (String::empty, &player);
reloadAudioDeviceState();
}
void shutDownAudioDevices()
{
saveAudioDeviceState();
deviceManager.removeMidiInputCallback (String::empty, &player);
deviceManager.removeAudioCallback (&player);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandalonePluginHolder)
};
//==============================================================================
/**
A class that can be used to run a simple standalone application containing your filter.
Just create one of these objects in your JUCEApplicationBase::initialise() method, and
let it do its work. It will create your filter object using the same createPluginFilter() function
that the other plugin wrappers use.
*/
class StandaloneFilterWindow : public DocumentWindow,
public ButtonListener // (can't use Button::Listener due to VC2005 bug)
{
public:
//==============================================================================
/** Creates a window with a given title and colour.
The settings object can be a PropertySet that the class should use to
store its settings - the object that is passed-in will be owned by this
class and deleted automatically when no longer needed. (It can also be null)
*/
StandaloneFilterWindow (const String& title,
Colour backgroundColour,
PropertySet* settingsToUse)
: DocumentWindow (title, backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
optionsButton ("options")
{
setTitleBarButtonsRequired (DocumentWindow::minimiseButton | DocumentWindow::closeButton, false);
Component::addAndMakeVisible (optionsButton);
optionsButton.addListener (this);
optionsButton.setTriggeredOnMouseDown (true);
pluginHolder = new StandalonePluginHolder (settingsToUse);
createEditorComp();
if (PropertySet* props = pluginHolder->settings)
{
const int x = props->getIntValue ("windowX", -100);
const int y = props->getIntValue ("windowY", -100);
if (x != -100 && y != -100)
setBoundsConstrained (juce::Rectangle<int> (x, y, getWidth(), getHeight()));
else
centreWithSize (getWidth(), getHeight());
}
else
{
centreWithSize (getWidth(), getHeight());
}
}
if (filter != nullptr && getContentComponent() != nullptr)
~StandaloneFilterWindow()
{
if (PropertySet* props = pluginHolder->settings)
{
filter->editorBeingDeleted (dynamic_cast <AudioProcessorEditor*> (getContentComponent()));
props->setValue ("windowX", getX());
props->setValue ("windowY", getY());
}
pluginHolder->stopPlaying();
deleteEditorComp();
pluginHolder = nullptr;
}
//==============================================================================
AudioProcessor* getAudioProcessor() const noexcept { return pluginHolder->processor; }
AudioDeviceManager& getDeviceManager() const noexcept { return pluginHolder->deviceManager; }
void createEditorComp()
{
setContentOwned (getAudioProcessor()->createEditorIfNeeded(), true);
}
void deleteEditorComp()
{
if (AudioProcessorEditor* ed = dynamic_cast<AudioProcessorEditor*> (getContentComponent()))
{
pluginHolder->processor->editorBeingDeleted (ed);
clearContentComponent();
}
}
filter = nullptr;
/** Deletes and re-creates the plugin, resetting it to its default state. */
void resetToDefaultState()
{
pluginHolder->stopPlaying();
deleteEditorComp();
pluginHolder->deletePlugin();
if (PropertySet* props = pluginHolder->settings)
props->removeValue ("filterState");
pluginHolder->createPlugin();
createEditorComp();
pluginHolder->startPlaying();
}
//==============================================================================
void closeButtonPressed() override
{
JUCEApplicationBase::quit();
}
void buttonClicked (Button*) override
{
PopupMenu m;
m.addItem (1, TRANS("Audio Settings..."));
m.addSeparator();
m.addItem (2, TRANS("Save current state..."));
m.addItem (3, TRANS("Load a saved state..."));
m.addSeparator();
m.addItem (4, TRANS("Reset to default state"));
switch (m.showAt (&optionsButton))
{
case 1: pluginHolder->showAudioSettingsDialog(); break;
case 2: pluginHolder->askUserToSaveState(); break;
case 3: pluginHolder->askUserToLoadState(); break;
case 4: resetToDefaultState(); break;
default: break;
}
}
void resized() override
{
DocumentWindow::resized();
optionsButton.setBounds (8, 6, 60, getTitleBarHeight() - 8);
}
ScopedPointer<StandalonePluginHolder> pluginHolder;
private:
//==============================================================================
TextButton optionsButton;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandaloneFilterWindow)
};
#endif // JUCE_STANDALONEFILTERWINDOW_H_INCLUDED

Loading…
Cancel
Save