fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).

This commit is contained in:
Alex Birch
2018-06-17 13:34:53 +01:00
parent a2be47c887
commit dff4d13a1d
1563 changed files with 601601 additions and 3466 deletions

View File

@ -0,0 +1,170 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#include "../utility/juce_IncludeSystemHeaders.h"
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../utility/juce_FakeMouseMoveGenerator.h"
#include "../utility/juce_WindowsHooks.h"
#include <juce_audio_devices/juce_audio_devices.h>
#include <juce_gui_extra/juce_gui_extra.h>
#include <juce_audio_utils/juce_audio_utils.h>
// You can set this flag in your build if you need to specify a different
// standalone JUCEApplication class for your app to use. If you don't
// set it then by default we'll just create a simple one as below.
#if ! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP
extern juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter();
#include "juce_StandaloneFilterWindow.h"
namespace juce
{
//==============================================================================
class StandaloneFilterApp : public JUCEApplication
{
public:
StandaloneFilterApp()
{
PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_Standalone;
PropertiesFile::Options options;
options.applicationName = getApplicationName();
options.filenameSuffix = ".settings";
options.osxLibrarySubFolder = "Application Support";
#if JUCE_LINUX
options.folderName = "~/.config";
#else
options.folderName = "";
#endif
appProperties.setStorageParameters (options);
}
const String getApplicationName() override { return JucePlugin_Name; }
const String getApplicationVersion() override { return JucePlugin_VersionString; }
bool moreThanOneInstanceAllowed() override { return true; }
void anotherInstanceStarted (const String&) override {}
virtual StandaloneFilterWindow* createWindow()
{
#ifdef JucePlugin_PreferredChannelConfigurations
StandalonePluginHolder::PluginInOuts channels[] = { JucePlugin_PreferredChannelConfigurations };
#endif
return new StandaloneFilterWindow (getApplicationName(),
LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId),
appProperties.getUserSettings(),
false, {}, nullptr
#ifdef JucePlugin_PreferredChannelConfigurations
, juce::Array<StandalonePluginHolder::PluginInOuts> (channels, juce::numElementsInArray (channels))
#else
, {}
#endif
#if JUCE_DONT_AUTO_OPEN_MIDI_DEVICES_ON_MOBILE
, false
#endif
);
}
//==============================================================================
void initialise (const String&) override
{
mainWindow.reset (createWindow());
#if JUCE_STANDALONE_FILTER_WINDOW_USE_KIOSK_MODE
Desktop::getInstance().setKioskModeComponent (mainWindow.get(), false);
#endif
mainWindow->setVisible (true);
}
void shutdown() override
{
mainWindow = nullptr;
appProperties.saveIfNeeded();
}
//==============================================================================
void systemRequestedQuit() override
{
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
{
Timer::callAfterDelay (100, []()
{
if (auto app = JUCEApplicationBase::getInstance())
app->systemRequestedQuit();
});
}
else
{
quit();
}
}
protected:
ApplicationProperties appProperties;
std::unique_ptr<StandaloneFilterWindow> mainWindow;
};
} // namespace juce
#if JucePlugin_Build_Standalone && JUCE_IOS
using namespace juce;
bool JUCE_CALLTYPE juce_isInterAppAudioConnected()
{
if (auto holder = StandalonePluginHolder::getInstance())
return holder->isInterAppAudioConnected();
return false;
}
void JUCE_CALLTYPE juce_switchToHostApplication()
{
if (auto holder = StandalonePluginHolder::getInstance())
holder->switchToHostApplication();
}
#if JUCE_MODULE_AVAILABLE_juce_gui_basics
Image JUCE_CALLTYPE juce_getIAAHostIcon (int size)
{
if (auto holder = StandalonePluginHolder::getInstance())
return holder->getIAAHostIcon (size);
return Image();
}
#endif
#endif
#endif

View File

@ -0,0 +1,883 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
extern juce::AudioProcessor* JUCE_API JUCE_CALLTYPE createPluginFilterOfType (juce::AudioProcessor::WrapperType type);
#endif
namespace juce
{
//==============================================================================
/**
An object that creates and plays a standalone instance of an AudioProcessor.
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.
@tags{Audio}
*/
class StandalonePluginHolder : private AudioIODeviceCallback,
private Timer
{
public:
//==============================================================================
/** Structure used for the number of inputs and outputs. */
struct PluginInOuts { short numIns, numOuts; };
//==============================================================================
/** Creates an instance of the default plugin.
The settings object can be a PropertySet that the class should use to store its
settings - the takeOwnershipOfSettings indicates whether this object will delete
the settings automatically when no longer needed. The settings can also be nullptr.
A default device name can be passed in.
Preferably a complete setup options object can be used, which takes precedence over
the preferredDefaultDeviceName and allows you to select the input & output device names,
sample rate, buffer size etc.
In all instances, the settingsToUse will take precedence over the "preferred" options if not null.
*/
StandalonePluginHolder (PropertySet* settingsToUse,
bool takeOwnershipOfSettings = true,
const String& preferredDefaultDeviceName = String(),
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr,
const Array<PluginInOuts>& channels = Array<PluginInOuts>(),
#if JUCE_ANDROID || JUCE_IOS
bool shouldAutoOpenMidiDevices = true
#else
bool shouldAutoOpenMidiDevices = false
#endif
)
: settings (settingsToUse, takeOwnershipOfSettings),
channelConfiguration (channels),
shouldMuteInput (! isInterAppAudioConnected()),
autoOpenMidiDevices (shouldAutoOpenMidiDevices)
{
createPlugin();
auto inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
: processor->getMainBusNumInputChannels());
if (preferredSetupOptions != nullptr)
options.reset (new AudioDeviceManager::AudioDeviceSetup (*preferredSetupOptions));
if (inChannels > 0 && RuntimePermissions::isRequired (RuntimePermissions::recordAudio)
&& ! RuntimePermissions::isGranted (RuntimePermissions::recordAudio))
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this, preferredDefaultDeviceName] (bool granted) { init (granted, preferredDefaultDeviceName); });
else
init (true, preferredDefaultDeviceName);
}
void init (bool enableAudioInput, const String& preferredDefaultDeviceName)
{
setupAudioDevices (enableAudioInput, preferredDefaultDeviceName, options.get());
reloadPluginState();
startPlaying();
if (autoOpenMidiDevices)
startTimer (500);
}
virtual ~StandalonePluginHolder()
{
stopTimer();
deletePlugin();
shutDownAudioDevices();
}
//==============================================================================
virtual void createPlugin()
{
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
processor.reset (::createPluginFilterOfType (AudioProcessor::wrapperType_Standalone));
#else
AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Standalone);
processor.reset (createPluginFilter());
AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Undefined);
#endif
jassert (processor != nullptr); // Your createPluginFilter() function must return a valid object!
processor->disableNonMainBuses();
processor->setRateAndBufferSizeDetails (44100, 512);
int inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
: processor->getMainBusNumInputChannels());
int outChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numOuts
: processor->getMainBusNumOutputChannels());
processorHasPotentialFeedbackLoop = (inChannels > 0 && outChannels > 0);
}
virtual void deletePlugin()
{
stopPlaying();
processor = nullptr;
}
static String getFilePatterns (const String& fileSuffix)
{
if (fileSuffix.isEmpty())
return {};
return (fileSuffix.startsWithChar ('.') ? "*" : "*.") + fileSuffix;
}
//==============================================================================
Value& getMuteInputValue() { return shouldMuteInput; }
bool getProcessorHasPotentialFeedbackLoop() const { return processorHasPotentialFeedbackLoop; }
//==============================================================================
File getLastFile() const
{
File f;
if (settings != nullptr)
f = File (settings->getValue ("lastStateFile"));
if (f == File())
f = File::getSpecialLocation (File::userDocumentsDirectory);
return f;
}
void setLastFile (const FileChooser& fc)
{
if (settings != nullptr)
settings->setValue ("lastStateFile", fc.getResult().getFullPathName());
}
/** Pops up a dialog letting the user save the processor's state to a file. */
void askUserToSaveState (const String& fileSuffix = String())
{
#if JUCE_MODAL_LOOPS_PERMITTED
FileChooser fc (TRANS("Save current state"), getLastFile(), getFilePatterns (fileSuffix));
if (fc.browseForFileToSave (true))
{
setLastFile (fc);
MemoryBlock data;
processor->getStateInformation (data);
if (! fc.getResult().replaceWithData (data.getData(), data.getSize()))
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
TRANS("Error whilst saving"),
TRANS("Couldn't write to the specified file!"));
}
#else
ignoreUnused (fileSuffix);
#endif
}
/** Pops up a dialog letting the user re-load the processor's state from a file. */
void askUserToLoadState (const String& fileSuffix = String())
{
#if JUCE_MODAL_LOOPS_PERMITTED
FileChooser fc (TRANS("Load a saved state"), getLastFile(), getFilePatterns (fileSuffix));
if (fc.browseForFileToOpen())
{
setLastFile (fc);
MemoryBlock data;
if (fc.getResult().loadFileAsData (data))
processor->setStateInformation (data.getData(), (int) data.getSize());
else
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
TRANS("Error whilst loading"),
TRANS("Couldn't read from the specified file!"));
}
#else
ignoreUnused (fileSuffix);
#endif
}
//==============================================================================
void startPlaying()
{
player.setProcessor (processor.get());
#if JucePlugin_Enable_IAA && JUCE_IOS
if (auto device = dynamic_cast<iOSAudioIODevice*> (deviceManager.getCurrentAudioDevice()))
{
processor->setPlayHead (device->getAudioPlayHead());
device->setMidiMessageCollector (&player.getMidiMessageCollector());
}
#endif
}
void stopPlaying()
{
player.setProcessor (nullptr);
}
//==============================================================================
/** Shows an audio properties dialog box modally. */
void showAudioSettingsDialog()
{
DialogWindow::LaunchOptions o;
int minNumInputs = std::numeric_limits<int>::max(), maxNumInputs = 0,
minNumOutputs = std::numeric_limits<int>::max(), maxNumOutputs = 0;
auto updateMinAndMax = [] (int newValue, int& minValue, int& maxValue)
{
minValue = jmin (minValue, newValue);
maxValue = jmax (maxValue, newValue);
};
if (channelConfiguration.size() > 0)
{
auto defaultConfig = channelConfiguration.getReference (0);
updateMinAndMax ((int) defaultConfig.numIns, minNumInputs, maxNumInputs);
updateMinAndMax ((int) defaultConfig.numOuts, minNumOutputs, maxNumOutputs);
}
if (auto* bus = processor->getBus (true, 0))
updateMinAndMax (bus->getDefaultLayout().size(), minNumInputs, maxNumInputs);
if (auto* bus = processor->getBus (false, 0))
updateMinAndMax (bus->getDefaultLayout().size(), minNumOutputs, maxNumOutputs);
minNumInputs = jmin (minNumInputs, maxNumInputs);
minNumOutputs = jmin (minNumOutputs, maxNumOutputs);
o.content.setOwned (new SettingsComponent (*this, deviceManager,
minNumInputs,
maxNumInputs,
minNumOutputs,
maxNumOutputs));
o.content->setSize (500, 550);
o.dialogTitle = TRANS("Audio/MIDI Settings");
o.dialogBackgroundColour = o.content->getLookAndFeel().findColour (ResizableWindow::backgroundColourId);
o.escapeKeyTriggersCloseButton = true;
o.useNativeTitleBar = true;
o.resizable = false;
o.launchAsync();
}
void saveAudioDeviceState()
{
if (settings != nullptr)
{
std::unique_ptr<XmlElement> xml (deviceManager.createStateXml());
settings->setValue ("audioSetup", xml.get());
#if ! (JUCE_IOS || JUCE_ANDROID)
settings->setValue ("shouldMuteInput", (bool) shouldMuteInput.getValue());
#endif
}
}
void reloadAudioDeviceState (bool enableAudioInput,
const String& preferredDefaultDeviceName,
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
{
std::unique_ptr<XmlElement> savedState;
if (settings != nullptr)
{
savedState.reset (settings->getXmlValue ("audioSetup"));
#if ! (JUCE_IOS || JUCE_ANDROID)
shouldMuteInput.setValue (settings->getBoolValue ("shouldMuteInput", true));
#endif
}
auto totalInChannels = processor->getMainBusNumInputChannels();
auto totalOutChannels = processor->getMainBusNumOutputChannels();
if (channelConfiguration.size() > 0)
{
auto defaultConfig = channelConfiguration.getReference (0);
totalInChannels = defaultConfig.numIns;
totalOutChannels = defaultConfig.numOuts;
}
deviceManager.initialise (enableAudioInput ? totalInChannels : 0,
totalOutChannels,
savedState.get(),
true,
preferredDefaultDeviceName,
preferredSetupOptions);
}
//==============================================================================
void savePluginState()
{
if (settings != nullptr && processor != nullptr)
{
MemoryBlock data;
processor->getStateInformation (data);
settings->setValue ("filterState", data.toBase64Encoding());
}
}
void reloadPluginState()
{
if (settings != nullptr)
{
MemoryBlock data;
if (data.fromBase64Encoding (settings->getValue ("filterState")) && data.getSize() > 0)
processor->setStateInformation (data.getData(), (int) data.getSize());
}
}
//==============================================================================
void switchToHostApplication()
{
#if JUCE_IOS
if (auto device = dynamic_cast<iOSAudioIODevice*> (deviceManager.getCurrentAudioDevice()))
device->switchApplication();
#endif
}
bool isInterAppAudioConnected()
{
#if JUCE_IOS
if (auto device = dynamic_cast<iOSAudioIODevice*> (deviceManager.getCurrentAudioDevice()))
return device->isInterAppAudioConnected();
#endif
return false;
}
#if JUCE_MODULE_AVAILABLE_juce_gui_basics
Image getIAAHostIcon (int size)
{
#if JUCE_IOS && JucePlugin_Enable_IAA
if (auto device = dynamic_cast<iOSAudioIODevice*> (deviceManager.getCurrentAudioDevice()))
return device->getIcon (size);
#else
ignoreUnused (size);
#endif
return {};
}
#endif
static StandalonePluginHolder* getInstance();
//==============================================================================
OptionalScopedPointer<PropertySet> settings;
std::unique_ptr<AudioProcessor> processor;
AudioDeviceManager deviceManager;
AudioProcessorPlayer player;
Array<PluginInOuts> channelConfiguration;
// avoid feedback loop by default
bool processorHasPotentialFeedbackLoop = true;
Value shouldMuteInput;
AudioBuffer<float> emptyBuffer;
bool autoOpenMidiDevices;
std::unique_ptr<AudioDeviceManager::AudioDeviceSetup> options;
StringArray lastMidiDevices;
private:
//==============================================================================
class SettingsComponent : public Component
{
public:
SettingsComponent (StandalonePluginHolder& pluginHolder,
AudioDeviceManager& deviceManagerToUse,
int minAudioInputChannels,
int maxAudioInputChannels,
int minAudioOutputChannels,
int maxAudioOutputChannels)
: owner (pluginHolder),
deviceSelector (deviceManagerToUse,
minAudioInputChannels, maxAudioInputChannels,
minAudioOutputChannels, maxAudioOutputChannels,
true, false,
true, false),
shouldMuteLabel ("Feedback Loop:", "Feedback Loop:"),
shouldMuteButton ("Mute audio input")
{
setOpaque (true);
shouldMuteButton.setClickingTogglesState (true);
shouldMuteButton.getToggleStateValue().referTo (owner.shouldMuteInput);
addAndMakeVisible (deviceSelector);
if (owner.getProcessorHasPotentialFeedbackLoop())
{
addAndMakeVisible (shouldMuteButton);
addAndMakeVisible (shouldMuteLabel);
shouldMuteLabel.attachToComponent (&shouldMuteButton, true);
}
}
void paint (Graphics& g) override
{
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
}
void resized() override
{
auto r = getLocalBounds();
if (owner.getProcessorHasPotentialFeedbackLoop())
{
auto itemHeight = deviceSelector.getItemHeight();
auto extra = r.removeFromTop (itemHeight);
auto seperatorHeight = (itemHeight >> 1);
shouldMuteButton.setBounds (Rectangle<int> (extra.proportionOfWidth (0.35f), seperatorHeight,
extra.proportionOfWidth (0.60f), deviceSelector.getItemHeight()));
r.removeFromTop (seperatorHeight);
}
deviceSelector.setBounds (r);
}
private:
//==============================================================================
StandalonePluginHolder& owner;
AudioDeviceSelectorComponent deviceSelector;
Label shouldMuteLabel;
ToggleButton shouldMuteButton;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComponent)
};
//==============================================================================
void audioDeviceIOCallback (const float** inputChannelData,
int numInputChannels,
float** outputChannelData,
int numOutputChannels,
int numSamples) override
{
const bool inputMuted = shouldMuteInput.getValue();
if (inputMuted)
{
emptyBuffer.clear();
inputChannelData = emptyBuffer.getArrayOfReadPointers();
}
player.audioDeviceIOCallback (inputChannelData, numInputChannels,
outputChannelData, numOutputChannels, numSamples);
}
void audioDeviceAboutToStart (AudioIODevice* device) override
{
emptyBuffer.setSize (device->getActiveInputChannels().countNumberOfSetBits(), device->getCurrentBufferSizeSamples());
emptyBuffer.clear();
player.audioDeviceAboutToStart (device);
}
void audioDeviceStopped() override
{
player.audioDeviceStopped();
emptyBuffer.setSize (0, 0);
}
//==============================================================================
void setupAudioDevices (bool enableAudioInput,
const String& preferredDefaultDeviceName,
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
{
deviceManager.addAudioCallback (this);
deviceManager.addMidiInputCallback ({}, &player);
reloadAudioDeviceState (enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
}
void shutDownAudioDevices()
{
saveAudioDeviceState();
deviceManager.removeMidiInputCallback ({}, &player);
deviceManager.removeAudioCallback (this);
}
void timerCallback() override
{
auto newMidiDevices = MidiInput::getDevices();
if (newMidiDevices != lastMidiDevices)
{
for (auto& oldDevice : lastMidiDevices)
if (! newMidiDevices.contains (oldDevice))
deviceManager.setMidiInputEnabled (oldDevice, false);
for (auto& newDevice : newMidiDevices)
if (! lastMidiDevices.contains (newDevice))
deviceManager.setMidiInputEnabled (newDevice, true);
}
}
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.
@tags{Audio}
*/
class StandaloneFilterWindow : public DocumentWindow,
public Button::Listener
{
public:
//==============================================================================
typedef StandalonePluginHolder::PluginInOuts PluginInOuts;
//==============================================================================
/** 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 (it can also be null). If takeOwnershipOfSettings is
true, then the settings object will be owned and deleted by this object.
*/
StandaloneFilterWindow (const String& title,
Colour backgroundColour,
PropertySet* settingsToUse,
bool takeOwnershipOfSettings,
const String& preferredDefaultDeviceName = String(),
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr,
const Array<PluginInOuts>& constrainToConfiguration = {},
#if JUCE_ANDROID || JUCE_IOS
bool autoOpenMidiDevices = true
#else
bool autoOpenMidiDevices = false
#endif
)
: DocumentWindow (title, backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
optionsButton ("Options")
{
#if JUCE_IOS || JUCE_ANDROID
setTitleBarHeight (0);
#else
setTitleBarButtonsRequired (DocumentWindow::minimiseButton | DocumentWindow::closeButton, false);
Component::addAndMakeVisible (optionsButton);
optionsButton.addListener (this);
optionsButton.setTriggeredOnMouseDown (true);
#endif
pluginHolder.reset (new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings,
preferredDefaultDeviceName, preferredSetupOptions,
constrainToConfiguration, autoOpenMidiDevices));
#if JUCE_IOS || JUCE_ANDROID
setFullScreen (true);
setContentOwned (new MainContentComponent (*this), false);
#else
setContentOwned (new MainContentComponent (*this), true);
if (auto* props = pluginHolder->settings.get())
{
const int x = props->getIntValue ("windowX", -100);
const int y = props->getIntValue ("windowY", -100);
if (x != -100 && y != -100)
setBoundsConstrained ({ x, y, getWidth(), getHeight() });
else
centreWithSize (getWidth(), getHeight());
}
else
{
centreWithSize (getWidth(), getHeight());
}
#endif
}
~StandaloneFilterWindow()
{
#if (! JUCE_IOS) && (! JUCE_ANDROID)
if (auto* props = pluginHolder->settings.get())
{
props->setValue ("windowX", getX());
props->setValue ("windowY", getY());
}
#endif
pluginHolder->stopPlaying();
clearContentComponent();
pluginHolder = nullptr;
}
//==============================================================================
AudioProcessor* getAudioProcessor() const noexcept { return pluginHolder->processor.get(); }
AudioDeviceManager& getDeviceManager() const noexcept { return pluginHolder->deviceManager; }
/** Deletes and re-creates the plugin, resetting it to its default state. */
void resetToDefaultState()
{
pluginHolder->stopPlaying();
clearContentComponent();
pluginHolder->deletePlugin();
if (auto* props = pluginHolder->settings.get())
props->removeValue ("filterState");
pluginHolder->createPlugin();
setContentOwned (new MainContentComponent (*this), true);
pluginHolder->startPlaying();
}
//==============================================================================
void closeButtonPressed() override
{
pluginHolder->savePluginState();
JUCEApplicationBase::quit();
}
void buttonClicked (Button*) override
{
PopupMenu m;
m.addItem (1, TRANS("Audio/MIDI 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"));
m.showMenuAsync (PopupMenu::Options(),
ModalCallbackFunction::forComponent (menuCallback, this));
}
void handleMenuResult (int result)
{
switch (result)
{
case 1: pluginHolder->showAudioSettingsDialog(); break;
case 2: pluginHolder->askUserToSaveState(); break;
case 3: pluginHolder->askUserToLoadState(); break;
case 4: resetToDefaultState(); break;
default: break;
}
}
static void menuCallback (int result, StandaloneFilterWindow* button)
{
if (button != nullptr && result != 0)
button->handleMenuResult (result);
}
void resized() override
{
DocumentWindow::resized();
optionsButton.setBounds (8, 6, 60, getTitleBarHeight() - 8);
}
virtual StandalonePluginHolder* getPluginHolder() { return pluginHolder.get(); }
std::unique_ptr<StandalonePluginHolder> pluginHolder;
private:
//==============================================================================
class MainContentComponent : public Component,
private Value::Listener,
private Button::Listener,
private ComponentListener
{
public:
MainContentComponent (StandaloneFilterWindow& filterWindow)
: owner (filterWindow), notification (this),
editor (owner.getAudioProcessor()->createEditorIfNeeded())
{
Value& inputMutedValue = owner.pluginHolder->getMuteInputValue();
if (editor != nullptr)
{
editor->addComponentListener (this);
componentMovedOrResized (*editor, false, true);
addAndMakeVisible (editor.get());
}
addChildComponent (notification);
if (owner.pluginHolder->getProcessorHasPotentialFeedbackLoop())
{
inputMutedValue.addListener (this);
shouldShowNotification = inputMutedValue.getValue();
}
inputMutedChanged (shouldShowNotification);
}
~MainContentComponent()
{
if (editor != nullptr)
{
editor->removeComponentListener (this);
owner.pluginHolder->processor->editorBeingDeleted (editor.get());
editor = nullptr;
}
}
void resized() override
{
auto r = getLocalBounds();
if (shouldShowNotification)
notification.setBounds (r.removeFromTop (NotificationArea::height));
editor->setBounds (r);
}
private:
//==============================================================================
class NotificationArea : public Component
{
public:
enum { height = 30 };
NotificationArea (Button::Listener* settingsButtonListener)
: notification ("notification", "Audio input is muted to avoid feedback loop"),
#if JUCE_IOS || JUCE_ANDROID
settingsButton ("Unmute Input")
#else
settingsButton ("Settings...")
#endif
{
setOpaque (true);
notification.setColour (Label::textColourId, Colours::black);
settingsButton.addListener (settingsButtonListener);
addAndMakeVisible (notification);
addAndMakeVisible (settingsButton);
}
void paint (Graphics& g) override
{
auto r = getLocalBounds();
g.setColour (Colours::darkgoldenrod);
g.fillRect (r.removeFromBottom (1));
g.setColour (Colours::lightgoldenrodyellow);
g.fillRect (r);
}
void resized() override
{
auto r = getLocalBounds().reduced (5);
settingsButton.setBounds (r.removeFromRight (70));
notification.setBounds (r);
}
private:
Label notification;
TextButton settingsButton;
};
//==============================================================================
void inputMutedChanged (bool newInputMutedValue)
{
shouldShowNotification = newInputMutedValue;
notification.setVisible (shouldShowNotification);
#if JUCE_IOS || JUCE_ANDROID
resized();
#else
setSize (editor->getWidth(),
editor->getHeight()
+ (shouldShowNotification ? NotificationArea::height : 0));
#endif
}
void valueChanged (Value& value) override { inputMutedChanged (value.getValue()); }
void buttonClicked (Button*) override
{
#if JUCE_IOS || JUCE_ANDROID
owner.pluginHolder->getMuteInputValue().setValue (false);
#else
owner.pluginHolder->showAudioSettingsDialog();
#endif
}
//==============================================================================
void componentMovedOrResized (Component&, bool, bool wasResized) override
{
if (wasResized && editor != nullptr)
setSize (editor->getWidth(),
editor->getHeight() + (shouldShowNotification ? NotificationArea::height : 0));
}
//==============================================================================
StandaloneFilterWindow& owner;
NotificationArea notification;
std::unique_ptr<AudioProcessorEditor> editor;
bool shouldShowNotification = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};
//==============================================================================
TextButton optionsButton;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandaloneFilterWindow)
};
inline StandalonePluginHolder* StandalonePluginHolder::getInstance()
{
#if JucePlugin_Enable_IAA || JucePlugin_Build_Standalone
if (PluginHostType::getPluginLoadedAs() == AudioProcessor::wrapperType_Standalone)
{
auto& desktop = Desktop::getInstance();
const int numTopLevelWindows = desktop.getNumComponents();
for (int i = 0; i < numTopLevelWindows; ++i)
if (auto window = dynamic_cast<StandaloneFilterWindow*> (desktop.getComponent (i)))
return window->getPluginHolder();
}
#endif
return nullptr;
}
} // namespace juce