6.1.6 framework update and tooltip enhancements
This commit is contained in:
parent
29bce70503
commit
5adcad4a7a
7 changed files with 432 additions and 151 deletions
|
@ -113,7 +113,7 @@ public:
|
||||||
startTimer (500);
|
startTimer (500);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~StandalonePluginHolder() override
|
~StandalonePluginHolder() override
|
||||||
{
|
{
|
||||||
stopTimer();
|
stopTimer();
|
||||||
|
|
||||||
|
@ -128,13 +128,7 @@ public:
|
||||||
processor->disableNonMainBuses();
|
processor->disableNonMainBuses();
|
||||||
processor->setRateAndBufferSizeDetails (44100, 512);
|
processor->setRateAndBufferSizeDetails (44100, 512);
|
||||||
|
|
||||||
int inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
|
processorHasPotentialFeedbackLoop = (getNumInputChannels() > 0 && getNumOutputChannels() > 0);
|
||||||
: processor->getMainBusNumInputChannels());
|
|
||||||
|
|
||||||
int outChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numOuts
|
|
||||||
: processor->getMainBusNumOutputChannels());
|
|
||||||
|
|
||||||
processorHasPotentialFeedbackLoop = (inChannels > 0 && outChannels > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void deletePlugin()
|
virtual void deletePlugin()
|
||||||
|
@ -143,6 +137,24 @@ public:
|
||||||
processor = nullptr;
|
processor = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getNumInputChannels() const
|
||||||
|
{
|
||||||
|
if (processor == nullptr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
|
||||||
|
: processor->getMainBusNumInputChannels());
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNumOutputChannels() const
|
||||||
|
{
|
||||||
|
if (processor == nullptr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (channelConfiguration.size() > 0 ? channelConfiguration[0].numOuts
|
||||||
|
: processor->getMainBusNumOutputChannels());
|
||||||
|
}
|
||||||
|
|
||||||
static String getFilePatterns (const String& fileSuffix)
|
static String getFilePatterns (const String& fileSuffix)
|
||||||
{
|
{
|
||||||
if (fileSuffix.isEmpty())
|
if (fileSuffix.isEmpty())
|
||||||
|
@ -179,11 +191,18 @@ public:
|
||||||
/** Pops up a dialog letting the user save the processor's state to a file. */
|
/** Pops up a dialog letting the user save the processor's state to a file. */
|
||||||
void askUserToSaveState (const String& fileSuffix = String())
|
void askUserToSaveState (const String& fileSuffix = String())
|
||||||
{
|
{
|
||||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
stateFileChooser = std::make_unique<FileChooser> (TRANS("Save current state"),
|
||||||
FileChooser fc (TRANS("Save current state"), getLastFile(), getFilePatterns (fileSuffix));
|
getLastFile(),
|
||||||
|
getFilePatterns (fileSuffix));
|
||||||
|
auto flags = FileBrowserComponent::saveMode
|
||||||
|
| FileBrowserComponent::canSelectFiles
|
||||||
|
| FileBrowserComponent::warnAboutOverwriting;
|
||||||
|
|
||||||
if (fc.browseForFileToSave (true))
|
stateFileChooser->launchAsync (flags, [this] (const FileChooser& fc)
|
||||||
{
|
{
|
||||||
|
if (fc.getResult() == File{})
|
||||||
|
return;
|
||||||
|
|
||||||
setLastFile (fc);
|
setLastFile (fc);
|
||||||
|
|
||||||
MemoryBlock data;
|
MemoryBlock data;
|
||||||
|
@ -193,20 +212,23 @@ public:
|
||||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||||
TRANS("Error whilst saving"),
|
TRANS("Error whilst saving"),
|
||||||
TRANS("Couldn't write to the specified file!"));
|
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. */
|
/** Pops up a dialog letting the user re-load the processor's state from a file. */
|
||||||
void askUserToLoadState (const String& fileSuffix = String())
|
void askUserToLoadState (const String& fileSuffix = String())
|
||||||
{
|
{
|
||||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
stateFileChooser = std::make_unique<FileChooser> (TRANS("Load a saved state"),
|
||||||
FileChooser fc (TRANS("Load a saved state"), getLastFile(), getFilePatterns (fileSuffix));
|
getLastFile(),
|
||||||
|
getFilePatterns (fileSuffix));
|
||||||
|
auto flags = FileBrowserComponent::openMode
|
||||||
|
| FileBrowserComponent::canSelectFiles;
|
||||||
|
|
||||||
if (fc.browseForFileToOpen())
|
stateFileChooser->launchAsync (flags, [this] (const FileChooser& fc)
|
||||||
{
|
{
|
||||||
|
if (fc.getResult() == File{})
|
||||||
|
return;
|
||||||
|
|
||||||
setLastFile (fc);
|
setLastFile (fc);
|
||||||
|
|
||||||
MemoryBlock data;
|
MemoryBlock data;
|
||||||
|
@ -217,10 +239,7 @@ public:
|
||||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||||
TRANS("Error whilst loading"),
|
TRANS("Error whilst loading"),
|
||||||
TRANS("Couldn't read from the specified file!"));
|
TRANS("Couldn't read from the specified file!"));
|
||||||
}
|
});
|
||||||
#else
|
|
||||||
ignoreUnused (fileSuffix);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -264,15 +283,11 @@ public:
|
||||||
if (auto* bus = processor->getBus (false, 0))
|
if (auto* bus = processor->getBus (false, 0))
|
||||||
maxNumOutputs = jmax (0, bus->getDefaultLayout().size());
|
maxNumOutputs = jmax (0, bus->getDefaultLayout().size());
|
||||||
|
|
||||||
o.content.setOwned (new SettingsComponent (*this, deviceManager, maxNumInputs, maxNumOutputs));
|
auto content = std::make_unique<SettingsComponent> (*this, deviceManager, maxNumInputs, maxNumOutputs);
|
||||||
|
content->setSize (500, 550);
|
||||||
|
content->setToRecommendedSize();
|
||||||
|
|
||||||
LookAndFeel_V4 *lfv4 = dynamic_cast<LookAndFeel_V4*>(&o.content->getLookAndFeel());
|
o.content.setOwned (content.release());
|
||||||
|
|
||||||
if (lfv4) {
|
|
||||||
lfv4->setColour(ResizableWindow::backgroundColourId, Colour::fromHSV (0.0f, 0.0f, 0.1f, 1.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
o.content->setSize (500, 400);
|
|
||||||
|
|
||||||
o.dialogTitle = TRANS("Audio/MIDI Settings");
|
o.dialogTitle = TRANS("Audio/MIDI Settings");
|
||||||
o.dialogBackgroundColour = o.content->getLookAndFeel().findColour (ResizableWindow::backgroundColourId);
|
o.dialogBackgroundColour = o.content->getLookAndFeel().findColour (ResizableWindow::backgroundColourId);
|
||||||
|
@ -312,18 +327,17 @@ public:
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
auto totalInChannels = processor->getMainBusNumInputChannels();
|
auto inputChannels = getNumInputChannels();
|
||||||
auto totalOutChannels = processor->getMainBusNumOutputChannels();
|
auto outputChannels = getNumOutputChannels();
|
||||||
|
|
||||||
if (channelConfiguration.size() > 0)
|
if (inputChannels == 0 && outputChannels == 0 && processor->isMidiEffect())
|
||||||
{
|
{
|
||||||
auto defaultConfig = channelConfiguration.getReference (0);
|
// add a dummy output channel for MIDI effect plug-ins so they can receive audio callbacks
|
||||||
totalInChannels = defaultConfig.numIns;
|
outputChannels = 1;
|
||||||
totalOutChannels = defaultConfig.numOuts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceManager.initialise (enableAudioInput ? totalInChannels : 0,
|
deviceManager.initialise (enableAudioInput ? inputChannels : 0,
|
||||||
totalOutChannels,
|
outputChannels,
|
||||||
savedState.get(),
|
savedState.get(),
|
||||||
true,
|
true,
|
||||||
preferredDefaultDeviceName,
|
preferredDefaultDeviceName,
|
||||||
|
@ -403,7 +417,93 @@ public:
|
||||||
std::unique_ptr<AudioDeviceManager::AudioDeviceSetup> options;
|
std::unique_ptr<AudioDeviceManager::AudioDeviceSetup> options;
|
||||||
Array<MidiDeviceInfo> lastMidiDevices;
|
Array<MidiDeviceInfo> lastMidiDevices;
|
||||||
|
|
||||||
|
std::unique_ptr<FileChooser> stateFileChooser;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/* This class can be used to ensure that audio callbacks use buffers with a
|
||||||
|
predictable maximum size.
|
||||||
|
|
||||||
|
On some platforms (such as iOS 10), the expected buffer size reported in
|
||||||
|
audioDeviceAboutToStart may be smaller than the blocks passed to
|
||||||
|
audioDeviceIOCallback. This can lead to out-of-bounds reads if the render
|
||||||
|
callback depends on additional buffers which were initialised using the
|
||||||
|
smaller size.
|
||||||
|
|
||||||
|
As a workaround, this class will ensure that the render callback will
|
||||||
|
only ever be called with a block with a length less than or equal to the
|
||||||
|
expected block size.
|
||||||
|
*/
|
||||||
|
class CallbackMaxSizeEnforcer : public AudioIODeviceCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CallbackMaxSizeEnforcer (AudioIODeviceCallback& callbackIn)
|
||||||
|
: inner (callbackIn) {}
|
||||||
|
|
||||||
|
void audioDeviceAboutToStart (AudioIODevice* device) override
|
||||||
|
{
|
||||||
|
maximumSize = device->getCurrentBufferSizeSamples();
|
||||||
|
storedInputChannels .resize ((size_t) device->getActiveInputChannels() .countNumberOfSetBits());
|
||||||
|
storedOutputChannels.resize ((size_t) device->getActiveOutputChannels().countNumberOfSetBits());
|
||||||
|
|
||||||
|
inner.audioDeviceAboutToStart (device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audioDeviceIOCallback (const float** inputChannelData,
|
||||||
|
int numInputChannels,
|
||||||
|
float** outputChannelData,
|
||||||
|
int numOutputChannels,
|
||||||
|
int numSamples) override
|
||||||
|
{
|
||||||
|
jassertquiet ((int) storedInputChannels.size() == numInputChannels);
|
||||||
|
jassertquiet ((int) storedOutputChannels.size() == numOutputChannels);
|
||||||
|
|
||||||
|
int position = 0;
|
||||||
|
|
||||||
|
while (position < numSamples)
|
||||||
|
{
|
||||||
|
const auto blockLength = jmin (maximumSize, numSamples - position);
|
||||||
|
|
||||||
|
initChannelPointers (inputChannelData, storedInputChannels, position);
|
||||||
|
initChannelPointers (outputChannelData, storedOutputChannels, position);
|
||||||
|
|
||||||
|
inner.audioDeviceIOCallback (storedInputChannels.data(),
|
||||||
|
(int) storedInputChannels.size(),
|
||||||
|
storedOutputChannels.data(),
|
||||||
|
(int) storedOutputChannels.size(),
|
||||||
|
blockLength);
|
||||||
|
|
||||||
|
position += blockLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void audioDeviceStopped() override
|
||||||
|
{
|
||||||
|
inner.audioDeviceStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct GetChannelWithOffset
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
template <typename Ptr>
|
||||||
|
auto operator() (Ptr ptr) const noexcept -> Ptr { return ptr + offset; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Ptr, typename Vector>
|
||||||
|
void initChannelPointers (Ptr&& source, Vector&& target, int offset)
|
||||||
|
{
|
||||||
|
std::transform (source, source + target.size(), target.begin(), GetChannelWithOffset { offset });
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioIODeviceCallback& inner;
|
||||||
|
int maximumSize = 0;
|
||||||
|
std::vector<const float*> storedInputChannels;
|
||||||
|
std::vector<float*> storedOutputChannels;
|
||||||
|
};
|
||||||
|
|
||||||
|
CallbackMaxSizeEnforcer maxSizeEnforcer { *this };
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
class SettingsComponent : public Component
|
class SettingsComponent : public Component
|
||||||
{
|
{
|
||||||
|
@ -414,7 +514,7 @@ private:
|
||||||
int maxAudioOutputChannels)
|
int maxAudioOutputChannels)
|
||||||
: owner (pluginHolder),
|
: owner (pluginHolder),
|
||||||
deviceSelector (deviceManagerToUse,
|
deviceSelector (deviceManagerToUse,
|
||||||
0, 0, // maxAudioInputChannels, // force zero to fully disable input selection
|
0, maxAudioInputChannels,
|
||||||
0, maxAudioOutputChannels,
|
0, maxAudioOutputChannels,
|
||||||
true,
|
true,
|
||||||
(pluginHolder.processor.get() != nullptr && pluginHolder.processor->producesMidi()),
|
(pluginHolder.processor.get() != nullptr && pluginHolder.processor->producesMidi()),
|
||||||
|
@ -436,17 +536,6 @@ private:
|
||||||
|
|
||||||
shouldMuteLabel.attachToComponent (&shouldMuteButton, true);
|
shouldMuteLabel.attachToComponent (&shouldMuteButton, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i =0; i < deviceSelector.getNumChildComponents(); i ++){
|
|
||||||
|
|
||||||
LookAndFeel_V4 *lfv4 = dynamic_cast<LookAndFeel_V4*>(&deviceSelector.getChildComponent(i) ->getLookAndFeel());
|
|
||||||
if (lfv4) {
|
|
||||||
lfv4->setColour(ComboBox::ColourIds::backgroundColourId, Colour::fromHSV (0.0f, 0.0f, 0.2f, 1.0f));
|
|
||||||
lfv4->setColour(ListBox::ColourIds::backgroundColourId, Colour::fromHSV (0.0f, 0.0f, 0.2f, 1.0f));
|
|
||||||
lfv4->setColour(TextButton::ColourIds::buttonColourId, Colour::fromHSV (0.0f, 0.0f, 0.1f, 1.0f));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void paint (Graphics& g) override
|
void paint (Graphics& g) override
|
||||||
|
@ -456,6 +545,8 @@ private:
|
||||||
|
|
||||||
void resized() override
|
void resized() override
|
||||||
{
|
{
|
||||||
|
const ScopedValueSetter<bool> scope (isResizing, true);
|
||||||
|
|
||||||
auto r = getLocalBounds();
|
auto r = getLocalBounds();
|
||||||
|
|
||||||
if (owner.getProcessorHasPotentialFeedbackLoop())
|
if (owner.getProcessorHasPotentialFeedbackLoop())
|
||||||
|
@ -473,12 +564,34 @@ private:
|
||||||
deviceSelector.setBounds (r);
|
deviceSelector.setBounds (r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void childBoundsChanged (Component* childComp) override
|
||||||
|
{
|
||||||
|
if (! isResizing && childComp == &deviceSelector)
|
||||||
|
setToRecommendedSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setToRecommendedSize()
|
||||||
|
{
|
||||||
|
const auto extraHeight = [&]
|
||||||
|
{
|
||||||
|
if (! owner.getProcessorHasPotentialFeedbackLoop())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const auto itemHeight = deviceSelector.getItemHeight();
|
||||||
|
const auto separatorHeight = (itemHeight >> 1);
|
||||||
|
return itemHeight + separatorHeight;
|
||||||
|
}();
|
||||||
|
|
||||||
|
setSize (getWidth(), deviceSelector.getHeight() + extraHeight);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
StandalonePluginHolder& owner;
|
StandalonePluginHolder& owner;
|
||||||
AudioDeviceSelectorComponent deviceSelector;
|
AudioDeviceSelectorComponent deviceSelector;
|
||||||
Label shouldMuteLabel;
|
Label shouldMuteLabel;
|
||||||
ToggleButton shouldMuteButton;
|
ToggleButton shouldMuteButton;
|
||||||
|
bool isResizing = false;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComponent)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComponent)
|
||||||
|
@ -522,7 +635,7 @@ private:
|
||||||
const String& preferredDefaultDeviceName,
|
const String& preferredDefaultDeviceName,
|
||||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
|
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
|
||||||
{
|
{
|
||||||
deviceManager.addAudioCallback (this);
|
deviceManager.addAudioCallback (&maxSizeEnforcer);
|
||||||
deviceManager.addMidiInputDeviceCallback ({}, &player);
|
deviceManager.addMidiInputDeviceCallback ({}, &player);
|
||||||
|
|
||||||
reloadAudioDeviceState (enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
|
reloadAudioDeviceState (enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
|
||||||
|
@ -533,7 +646,7 @@ private:
|
||||||
saveAudioDeviceState();
|
saveAudioDeviceState();
|
||||||
|
|
||||||
deviceManager.removeMidiInputDeviceCallback ({}, &player);
|
deviceManager.removeMidiInputDeviceCallback ({}, &player);
|
||||||
deviceManager.removeAudioCallback (this);
|
deviceManager.removeAudioCallback (&maxSizeEnforcer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void timerCallback() override
|
void timerCallback() override
|
||||||
|
@ -568,8 +681,9 @@ private:
|
||||||
@tags{Audio}
|
@tags{Audio}
|
||||||
*/
|
*/
|
||||||
class StandaloneFilterWindow : public DocumentWindow,
|
class StandaloneFilterWindow : public DocumentWindow,
|
||||||
public Button::Listener,
|
private Button::Listener,
|
||||||
public MenuBarModel
|
public MenuBarModel
|
||||||
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -588,46 +702,46 @@ public:
|
||||||
const String& preferredDefaultDeviceName = String(),
|
const String& preferredDefaultDeviceName = String(),
|
||||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr,
|
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr,
|
||||||
const Array<PluginInOuts>& constrainToConfiguration = {},
|
const Array<PluginInOuts>& constrainToConfiguration = {},
|
||||||
#if JUCE_ANDROID || JUCE_IOS
|
#if JUCE_ANDROID || JUCE_IOS
|
||||||
bool autoOpenMidiDevices = true
|
bool autoOpenMidiDevices = true
|
||||||
#else
|
#else
|
||||||
bool autoOpenMidiDevices = false
|
bool autoOpenMidiDevices = true
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
#if JUCE_MAC
|
#if JUCE_MAC
|
||||||
: DocumentWindow ("", backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
|
: DocumentWindow("", backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ! JUCE_MAC
|
#if ! JUCE_MAC
|
||||||
: DocumentWindow (title, Colour::fromHSV (0.0f, 0.0f, 0.1f, 0.5f), DocumentWindow::minimiseButton | DocumentWindow::closeButton),
|
: DocumentWindow(title, juce::Colours::black, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
menuBar(this)
|
menuBar(this)
|
||||||
#if ! JUCE_MAC
|
#if ! JUCE_MAC
|
||||||
, optionsButton ("Settings")
|
, optionsButton("Settings")
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
#if JUCE_IOS || JUCE_ANDROID
|
#if JUCE_IOS || JUCE_ANDROID
|
||||||
setTitleBarHeight (0);
|
setTitleBarHeight(0);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
LookAndFeel_V4 *lfv4 = dynamic_cast<LookAndFeel_V4*>(&getLookAndFeel());
|
LookAndFeel_V4* lfv4 = dynamic_cast<LookAndFeel_V4*>(&getLookAndFeel());
|
||||||
if (lfv4) {
|
if (lfv4) {
|
||||||
lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widgetBackground, Colour::fromHSV (0.0f, 0.0f, 0.1f, 1.0f));
|
lfv4->getCurrentColourScheme().setUIColour(LookAndFeel_V4::ColourScheme::widgetBackground, Colour::fromHSV(0.0f, 0.0f, 0.1f, 1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitleBarButtonsRequired (DocumentWindow::minimiseButton | DocumentWindow::closeButton, false);
|
setTitleBarButtonsRequired(DocumentWindow::minimiseButton | DocumentWindow::closeButton, false);
|
||||||
|
|
||||||
#if JUCE_MAC
|
#if JUCE_MAC
|
||||||
setUsingNativeTitleBar(true);
|
setUsingNativeTitleBar(true);
|
||||||
menu.addItem (1, TRANS("Audio/MIDI Settings..."));
|
menu.addItem(1, TRANS("Audio/MIDI Settings..."));
|
||||||
MenuBarModel::setMacMainMenu (this, &menu);
|
MenuBarModel::setMacMainMenu(this, &menu);
|
||||||
#else
|
#else
|
||||||
Component::addAndMakeVisible (optionsButton);
|
Component::addAndMakeVisible(optionsButton);
|
||||||
optionsButton.addListener (this);
|
optionsButton.addListener(this);
|
||||||
optionsButton.setTriggeredOnMouseDown (true);
|
optionsButton.setTriggeredOnMouseDown(true);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pluginHolder.reset (new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings,
|
pluginHolder.reset (new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings,
|
||||||
preferredDefaultDeviceName, preferredSetupOptions,
|
preferredDefaultDeviceName, preferredSetupOptions,
|
||||||
|
@ -635,32 +749,55 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
|
||||||
|
|
||||||
#if JUCE_IOS || JUCE_ANDROID
|
#if JUCE_IOS || JUCE_ANDROID
|
||||||
setFullScreen (true);
|
setFullScreen (true);
|
||||||
setContentOwned (new MainContentComponent (*this), false);
|
updateContent();
|
||||||
#else
|
#else
|
||||||
setContentOwned (new MainContentComponent (*this), true);
|
updateContent();
|
||||||
|
|
||||||
|
const auto windowScreenBounds = [this]() -> Rectangle<int>
|
||||||
|
{
|
||||||
|
const auto width = getWidth();
|
||||||
|
const auto height = getHeight();
|
||||||
|
|
||||||
|
const auto& displays = Desktop::getInstance().getDisplays();
|
||||||
|
|
||||||
if (auto* props = pluginHolder->settings.get())
|
if (auto* props = pluginHolder->settings.get())
|
||||||
{
|
{
|
||||||
const int x = props->getIntValue ("windowX", -100);
|
constexpr int defaultValue = -100;
|
||||||
const int y = props->getIntValue ("windowY", -100);
|
|
||||||
|
|
||||||
if (x != -100 && y != -100)
|
const auto x = props->getIntValue ("windowX", defaultValue);
|
||||||
setBoundsConstrained ({ x, y, getWidth(), getHeight() });
|
const auto y = props->getIntValue ("windowY", defaultValue);
|
||||||
else
|
|
||||||
centreWithSize (getWidth(), getHeight());
|
if (x != defaultValue && y != defaultValue)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
centreWithSize (getWidth(), getHeight());
|
const auto screenLimits = displays.getDisplayForRect ({ x, y, width, height })->userArea;
|
||||||
|
|
||||||
|
return { jlimit (screenLimits.getX(), jmax (screenLimits.getX(), screenLimits.getRight() - width), x),
|
||||||
|
jlimit (screenLimits.getY(), jmax (screenLimits.getY(), screenLimits.getBottom() - height), y),
|
||||||
|
width, height };
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto displayArea = displays.getPrimaryDisplay()->userArea;
|
||||||
|
|
||||||
|
return { displayArea.getCentreX() - width / 2,
|
||||||
|
displayArea.getCentreY() - height / 2,
|
||||||
|
width, height };
|
||||||
|
}();
|
||||||
|
|
||||||
|
setBoundsConstrained (windowScreenBounds);
|
||||||
|
|
||||||
|
if (auto* processor = getAudioProcessor())
|
||||||
|
if (auto* editor = processor->getActiveEditor())
|
||||||
|
setResizable (editor->isResizable(), false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
~StandaloneFilterWindow() override
|
~StandaloneFilterWindow() override
|
||||||
{
|
{
|
||||||
#if JUCE_MAC
|
#if JUCE_MAC
|
||||||
setMacMainMenu(nullptr);
|
MenuBarModel::setMacMainMenu(nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (! JUCE_IOS) && (! JUCE_ANDROID)
|
#if (! JUCE_IOS) && (! JUCE_ANDROID)
|
||||||
if (auto* props = pluginHolder->settings.get())
|
if (auto* props = pluginHolder->settings.get())
|
||||||
{
|
{
|
||||||
|
@ -689,7 +826,7 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
|
||||||
props->removeValue ("filterState");
|
props->removeValue ("filterState");
|
||||||
|
|
||||||
pluginHolder->createPlugin();
|
pluginHolder->createPlugin();
|
||||||
setContentOwned (new MainContentComponent (*this), true);
|
updateContent();
|
||||||
pluginHolder->startPlaying();
|
pluginHolder->startPlaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,29 +840,29 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
|
||||||
|
|
||||||
StringArray getMenuBarNames() override
|
StringArray getMenuBarNames() override
|
||||||
{
|
{
|
||||||
// StringArray menuBarNames;
|
// StringArray menuBarNames;
|
||||||
// menuBarNames.add("Options");
|
// menuBarNames.add("Options");
|
||||||
// return menuBarNames;
|
// return menuBarNames;
|
||||||
const char *menuNames[] = { 0 };
|
const char* menuNames[] = { 0 };
|
||||||
|
|
||||||
return StringArray (menuNames);
|
return StringArray(menuNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupMenu getMenuForIndex (int topLevelMenuIndex, const String& menuName) override
|
PopupMenu getMenuForIndex(int topLevelMenuIndex, const String& menuName) override
|
||||||
{
|
{
|
||||||
PopupMenu m;
|
PopupMenu m;
|
||||||
// m.addItem (1, TRANS("Audio Settings..."));
|
// m.addItem (1, TRANS("Audio Settings..."));
|
||||||
// m.addSeparator();
|
// m.addSeparator();
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
void menuItemSelected (int menuItemID, int topLevelMenuIndex) override
|
void menuItemSelected(int menuItemID, int topLevelMenuIndex) override
|
||||||
{
|
{
|
||||||
handleMenuResult(menuItemID);
|
handleMenuResult(menuItemID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void menuBarActivated (bool isActive) override {};
|
void menuBarActivated(bool isActive) override {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -750,9 +887,7 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
|
||||||
void resized() override
|
void resized() override
|
||||||
{
|
{
|
||||||
DocumentWindow::resized();
|
DocumentWindow::resized();
|
||||||
#if ! JUCE_MAC
|
|
||||||
optionsButton.setBounds (8, 6, 60, getTitleBarHeight() - 8);
|
optionsButton.setBounds (8, 6, 60, getTitleBarHeight() - 8);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual StandalonePluginHolder* getPluginHolder() { return pluginHolder.get(); }
|
virtual StandalonePluginHolder* getPluginHolder() { return pluginHolder.get(); }
|
||||||
|
@ -762,20 +897,23 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
|
||||||
PopupMenu menu;
|
PopupMenu menu;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void updateContent()
|
||||||
|
{
|
||||||
|
auto* content = new MainContentComponent (*this);
|
||||||
|
decoratorConstrainer.setMainContentComponent (content);
|
||||||
|
|
||||||
|
#if JUCE_IOS || JUCE_ANDROID
|
||||||
|
constexpr auto resizeAutomatically = false;
|
||||||
|
#else
|
||||||
|
constexpr auto resizeAutomatically = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setContentOwned (content, resizeAutomatically);
|
||||||
|
}
|
||||||
|
|
||||||
void buttonClicked (Button*) override
|
void buttonClicked (Button*) override
|
||||||
{
|
{
|
||||||
pluginHolder->showAudioSettingsDialog();
|
pluginHolder->showAudioSettingsDialog();
|
||||||
|
|
||||||
//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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -790,7 +928,7 @@ private:
|
||||||
editor (owner.getAudioProcessor()->hasEditor() ? owner.getAudioProcessor()->createEditorIfNeeded()
|
editor (owner.getAudioProcessor()->hasEditor() ? owner.getAudioProcessor()->createEditorIfNeeded()
|
||||||
: new GenericAudioProcessorEditor (*owner.getAudioProcessor()))
|
: new GenericAudioProcessorEditor (*owner.getAudioProcessor()))
|
||||||
{
|
{
|
||||||
Value& inputMutedValue = owner.pluginHolder->getMuteInputValue();
|
inputMutedValue.referTo (owner.pluginHolder->getMuteInputValue());
|
||||||
|
|
||||||
if (editor != nullptr)
|
if (editor != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -829,9 +967,31 @@ private:
|
||||||
notification.setBounds (r.removeFromTop (NotificationArea::height));
|
notification.setBounds (r.removeFromTop (NotificationArea::height));
|
||||||
|
|
||||||
if (editor != nullptr)
|
if (editor != nullptr)
|
||||||
editor->setBounds (editor->getLocalArea (this, r.toFloat())
|
{
|
||||||
.withPosition (r.getTopLeft().toFloat().transformedBy (editor->getTransform().inverted()))
|
const auto newPos = r.getTopLeft().toFloat().transformedBy (editor->getTransform().inverted());
|
||||||
.toNearestInt());
|
|
||||||
|
if (preventResizingEditor)
|
||||||
|
editor->setTopLeftPosition (newPos.roundToInt());
|
||||||
|
else
|
||||||
|
editor->setBoundsConstrained (editor->getLocalArea (this, r.toFloat()).withPosition (newPos).toNearestInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentBoundsConstrainer* getEditorConstrainer() const
|
||||||
|
{
|
||||||
|
if (auto* e = editor.get())
|
||||||
|
return e->getConstrainer();
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BorderSize<int> computeBorder() const
|
||||||
|
{
|
||||||
|
const auto outer = owner.getContentComponentBorder();
|
||||||
|
return { outer.getTop() + (shouldShowNotification ? NotificationArea::height : 0),
|
||||||
|
outer.getLeft(),
|
||||||
|
outer.getBottom(),
|
||||||
|
outer.getRight() };
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -846,7 +1006,7 @@ private:
|
||||||
#if JUCE_IOS || JUCE_ANDROID
|
#if JUCE_IOS || JUCE_ANDROID
|
||||||
settingsButton ("Unmute Input")
|
settingsButton ("Unmute Input")
|
||||||
#else
|
#else
|
||||||
settingsButton ("Settings")
|
settingsButton ("Settings...")
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
setOpaque (true);
|
setOpaque (true);
|
||||||
|
@ -893,10 +1053,9 @@ private:
|
||||||
#else
|
#else
|
||||||
if (editor != nullptr)
|
if (editor != nullptr)
|
||||||
{
|
{
|
||||||
auto rect = getSizeToContainEditor();
|
const int extraHeight = shouldShowNotification ? NotificationArea::height : 0;
|
||||||
|
const auto rect = getSizeToContainEditor();
|
||||||
setSize (rect.getWidth(),
|
setSize (rect.getWidth(), rect.getHeight() + extraHeight);
|
||||||
rect.getHeight() + (shouldShowNotification ? NotificationArea::height : 0));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -914,6 +1073,8 @@ private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void componentMovedOrResized (Component&, bool, bool) override
|
void componentMovedOrResized (Component&, bool, bool) override
|
||||||
{
|
{
|
||||||
|
const ScopedValueSetter<bool> scope (preventResizingEditor, true);
|
||||||
|
|
||||||
if (editor != nullptr)
|
if (editor != nullptr)
|
||||||
{
|
{
|
||||||
auto rect = getSizeToContainEditor();
|
auto rect = getSizeToContainEditor();
|
||||||
|
@ -935,15 +1096,87 @@ private:
|
||||||
StandaloneFilterWindow& owner;
|
StandaloneFilterWindow& owner;
|
||||||
NotificationArea notification;
|
NotificationArea notification;
|
||||||
std::unique_ptr<AudioProcessorEditor> editor;
|
std::unique_ptr<AudioProcessorEditor> editor;
|
||||||
|
Value inputMutedValue;
|
||||||
bool shouldShowNotification = false;
|
bool shouldShowNotification = false;
|
||||||
|
bool preventResizingEditor = false;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* This custom constrainer checks with the AudioProcessorEditor (which might itself be
|
||||||
|
constrained) to ensure that any size we choose for the standalone window will be suitable
|
||||||
|
for the editor too.
|
||||||
|
|
||||||
|
Without this constrainer, attempting to resize the standalone window may set bounds on the
|
||||||
|
peer that are unsupported by the inner editor. In this scenario, the peer will be set to a
|
||||||
|
'bad' size, then the inner editor will be resized. The editor will check the new bounds with
|
||||||
|
its own constrainer, and may set itself to a more suitable size. After that, the resizable
|
||||||
|
window will see that its content component has changed size, and set the bounds of the peer
|
||||||
|
accordingly. The end result is that the peer is resized twice in a row to different sizes,
|
||||||
|
which can appear glitchy/flickery to the user.
|
||||||
|
*/
|
||||||
|
struct DecoratorConstrainer : public ComponentBoundsConstrainer
|
||||||
|
{
|
||||||
|
void checkBounds (Rectangle<int>& bounds,
|
||||||
|
const Rectangle<int>& previousBounds,
|
||||||
|
const Rectangle<int>& limits,
|
||||||
|
bool isStretchingTop,
|
||||||
|
bool isStretchingLeft,
|
||||||
|
bool isStretchingBottom,
|
||||||
|
bool isStretchingRight) override
|
||||||
|
{
|
||||||
|
auto* decorated = contentComponent != nullptr ? contentComponent->getEditorConstrainer()
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
if (decorated != nullptr)
|
||||||
|
{
|
||||||
|
const auto border = contentComponent->computeBorder();
|
||||||
|
const auto requestedBounds = bounds;
|
||||||
|
|
||||||
|
border.subtractFrom (bounds);
|
||||||
|
decorated->checkBounds (bounds,
|
||||||
|
border.subtractedFrom (previousBounds),
|
||||||
|
limits,
|
||||||
|
isStretchingTop,
|
||||||
|
isStretchingLeft,
|
||||||
|
isStretchingBottom,
|
||||||
|
isStretchingRight);
|
||||||
|
border.addTo (bounds);
|
||||||
|
bounds = bounds.withPosition (requestedBounds.getPosition());
|
||||||
|
|
||||||
|
if (isStretchingTop && ! isStretchingBottom)
|
||||||
|
bounds = bounds.withBottomY (previousBounds.getBottom());
|
||||||
|
|
||||||
|
if (! isStretchingTop && isStretchingBottom)
|
||||||
|
bounds = bounds.withY (previousBounds.getY());
|
||||||
|
|
||||||
|
if (isStretchingLeft && ! isStretchingRight)
|
||||||
|
bounds = bounds.withRightX (previousBounds.getRight());
|
||||||
|
|
||||||
|
if (! isStretchingLeft && isStretchingRight)
|
||||||
|
bounds = bounds.withX (previousBounds.getX());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ComponentBoundsConstrainer::checkBounds (bounds,
|
||||||
|
previousBounds,
|
||||||
|
limits,
|
||||||
|
isStretchingTop,
|
||||||
|
isStretchingLeft,
|
||||||
|
isStretchingBottom,
|
||||||
|
isStretchingRight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMainContentComponent (MainContentComponent* in) { contentComponent = in; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
MainContentComponent* contentComponent = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#if ! JUCE_MAC
|
|
||||||
TextButton optionsButton;
|
TextButton optionsButton;
|
||||||
#endif
|
DecoratorConstrainer decoratorConstrainer;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandaloneFilterWindow)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandaloneFilterWindow)
|
||||||
};
|
};
|
||||||
|
|
15
OB-Xd.jucer
15
OB-Xd.jucer
|
@ -9,11 +9,12 @@
|
||||||
pluginAUExportPrefix="" pluginRTASCategory="2048" aaxIdentifier="com.discodsp.obxd"
|
pluginAUExportPrefix="" pluginRTASCategory="2048" aaxIdentifier="com.discodsp.obxd"
|
||||||
companyName="discoDSP" companyWebsite="https://www.discodsp.com/"
|
companyName="discoDSP" companyWebsite="https://www.discodsp.com/"
|
||||||
pluginIsMidiEffectPlugin="0" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn"
|
pluginIsMidiEffectPlugin="0" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn"
|
||||||
pluginFormats="buildAU,buildStandalone,buildVST,buildVST3" buildVST="1"
|
pluginFormats="buildStandalone,buildVST3" buildVST="0" buildVST3="1"
|
||||||
buildVST3="1" buildAU="1" buildAUv3="0" buildRTAS="0" buildAAX="0"
|
buildAU="0" buildAUv3="0" buildRTAS="0" buildAAX="0" buildStandalone="1"
|
||||||
buildStandalone="1" enableIAA="0" jucerFormatVersion="1" pluginChannelConfigs="{0,2}"
|
enableIAA="0" jucerFormatVersion="1" pluginChannelConfigs="{0,2}"
|
||||||
companyCopyright="discoDSP" companyEmail="contactus@discodsp.com"
|
companyCopyright="discoDSP" companyEmail="contactus@discodsp.com"
|
||||||
pluginAAXCategory="2048" pluginVSTCategory="kPlugCategSynth">
|
pluginAAXCategory="2048" pluginVSTCategory="kPlugCategSynth"
|
||||||
|
displaySplashScreen="1">
|
||||||
<MAINGROUP id="NZ3n4V" name="OB-Xd">
|
<MAINGROUP id="NZ3n4V" name="OB-Xd">
|
||||||
<GROUP id="{90740217-84AB-FD0D-FBC4-CA9EA2C68D5E}" name="Source">
|
<GROUP id="{90740217-84AB-FD0D-FBC4-CA9EA2C68D5E}" name="Source">
|
||||||
<GROUP id="{E11C29DD-69D5-DA26-5CFF-B65751876DEE}" name="MTS">
|
<GROUP id="{E11C29DD-69D5-DA26-5CFF-B65751876DEE}" name="MTS">
|
||||||
|
@ -91,7 +92,7 @@
|
||||||
<EXPORTFORMATS>
|
<EXPORTFORMATS>
|
||||||
<XCODE_MAC targetFolder="Builds/MacOSX" bigIcon="nnY63W" smallIcon="nnY63W"
|
<XCODE_MAC targetFolder="Builds/MacOSX" bigIcon="nnY63W" smallIcon="nnY63W"
|
||||||
vstLegacyFolder="Modules/vstsdk2.4" vst3Folder="Modules/vstsdk3"
|
vstLegacyFolder="Modules/vstsdk2.4" vst3Folder="Modules/vstsdk3"
|
||||||
aaxFolder="Modules/aax">
|
aaxFolder="Modules/aax" extraDefs="JUCE_MODAL_LOOPS_PERMITTED=1">
|
||||||
<CONFIGURATIONS>
|
<CONFIGURATIONS>
|
||||||
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="OB-Xd"
|
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="OB-Xd"
|
||||||
stripLocalSymbols="0" linkTimeOptimisation="0"/>
|
stripLocalSymbols="0" linkTimeOptimisation="0"/>
|
||||||
|
@ -116,7 +117,7 @@
|
||||||
</MODULEPATHS>
|
</MODULEPATHS>
|
||||||
</XCODE_MAC>
|
</XCODE_MAC>
|
||||||
<LINUX_MAKE targetFolder="Builds/LinuxMakefile" extraLinkerFlags="-no-pie"
|
<LINUX_MAKE targetFolder="Builds/LinuxMakefile" extraLinkerFlags="-no-pie"
|
||||||
vstLegacyFolder="Modules/vstsdk2.4">
|
vstLegacyFolder="Modules/vstsdk2.4" extraDefs="JUCE_MODAL_LOOPS_PERMITTED=1">
|
||||||
<CONFIGURATIONS>
|
<CONFIGURATIONS>
|
||||||
<CONFIGURATION name="Release64" libraryPath="/usr/X11R6/lib/" isDebug="0" optimisation="3"
|
<CONFIGURATION name="Release64" libraryPath="/usr/X11R6/lib/" isDebug="0" optimisation="3"
|
||||||
targetName="OB-Xd" linuxArchitecture="-m64" headerPath="../../JuceLibraryCode ../../Source ../Modules/vstsdk2.4 /usr/include/freetype2 /usr/include"
|
targetName="OB-Xd" linuxArchitecture="-m64" headerPath="../../JuceLibraryCode ../../Source ../Modules/vstsdk2.4 /usr/include/freetype2 /usr/include"
|
||||||
|
@ -140,7 +141,7 @@
|
||||||
</LINUX_MAKE>
|
</LINUX_MAKE>
|
||||||
<VS2019 targetFolder="Builds/VisualStudio2019" smallIcon="nnY63W" bigIcon="nnY63W"
|
<VS2019 targetFolder="Builds/VisualStudio2019" smallIcon="nnY63W" bigIcon="nnY63W"
|
||||||
vstLegacyFolder="Modules/vstsdk2.4" vst3Folder="Modules/vstsdk3"
|
vstLegacyFolder="Modules/vstsdk2.4" vst3Folder="Modules/vstsdk3"
|
||||||
aaxFolder="Modules/aax">
|
aaxFolder="Modules/aax" extraDefs="JUCE_MODAL_LOOPS_PERMITTED=1">
|
||||||
<CONFIGURATIONS>
|
<CONFIGURATIONS>
|
||||||
<CONFIGURATION isDebug="1" name="Debug" targetName="OB-Xd" headerPath="../../Modules/asiosdk2.3.2/common"
|
<CONFIGURATION isDebug="1" name="Debug" targetName="OB-Xd" headerPath="../../Modules/asiosdk2.3.2/common"
|
||||||
useRuntimeLibDLL="0" enablePluginBinaryCopyStep="1"/>
|
useRuntimeLibDLL="0" enablePluginBinaryCopyStep="1"/>
|
||||||
|
|
|
@ -15,4 +15,4 @@ Latest binaries can be downloaded at https://www.discodsp.com/obxd/
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
Source code can be compiled with [JUCE 6.0.5](https://github.com/juce-framework/JUCE/releases/tag/6.0.5) and VST3 SDK.
|
Source code can be compiled with [JUCE 6.1.6](https://github.com/juce-framework/JUCE/releases/tag/6.1.6) and VST3 SDK.
|
||||||
|
|
|
@ -61,7 +61,6 @@ public:
|
||||||
h2 = fh;
|
h2 = fh;
|
||||||
w2 = kni.getWidth();
|
w2 = kni.getWidth();
|
||||||
numFr = kni.getHeight() / h2;
|
numFr = kni.getHeight() / h2;
|
||||||
setPopupDisplayEnabled(true, true, getParentComponent());
|
|
||||||
setLookAndFeel(&lookAndFeel);
|
setLookAndFeel(&lookAndFeel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,8 +122,15 @@ void ObxdAudioProcessorEditor::resized() {
|
||||||
if (mappingComps[name] != nullptr){
|
if (mappingComps[name] != nullptr){
|
||||||
if (auto* knob = dynamic_cast<Knob*>(mappingComps[name])){
|
if (auto* knob = dynamic_cast<Knob*>(mappingComps[name])){
|
||||||
knob->setBounds(transformBounds(x, y, d,d));
|
knob->setBounds(transformBounds(x, y, d,d));
|
||||||
if (!tooltipEnabled)
|
const auto tooltipBehavior = processor.getTooltipBehavior();
|
||||||
|
if (tooltipBehavior == Tooltip::Disable ||
|
||||||
|
(tooltipBehavior == Tooltip::StandardDisplay && !tooltipEnabled))
|
||||||
|
{
|
||||||
knob->setPopupDisplayEnabled(false, false, nullptr);
|
knob->setPopupDisplayEnabled(false, false, nullptr);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
knob->setPopupDisplayEnabled(true, true, knob->getParentComponent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (dynamic_cast<ButtonList*>(mappingComps[name])){
|
else if (dynamic_cast<ButtonList*>(mappingComps[name])){
|
||||||
mappingComps[name]->setBounds(transformBounds(x, y, w, h));
|
mappingComps[name]->setBounds(transformBounds(x, y, w, h));
|
||||||
|
@ -954,6 +961,24 @@ void ObxdAudioProcessorEditor::createMenu ()
|
||||||
scaleMenu.addItem(menuScaleNum+2, "2x", true, getScaleFactor() == 2.0f);
|
scaleMenu.addItem(menuScaleNum+2, "2x", true, getScaleFactor() == 2.0f);
|
||||||
menu->addSubMenu("GUI Size", scaleMenu, true);
|
menu->addSubMenu("GUI Size", scaleMenu, true);
|
||||||
|
|
||||||
|
PopupMenu tooltipMenu;
|
||||||
|
tooltipMenu.addItem("Disabled", true, processor.getTooltipBehavior() == Tooltip::Disable, [&]
|
||||||
|
{
|
||||||
|
processor.setTooltipBehavior(Tooltip::Disable);
|
||||||
|
resized();
|
||||||
|
});
|
||||||
|
tooltipMenu.addItem("Standard Display", true, processor.getTooltipBehavior() == Tooltip::StandardDisplay, [&]
|
||||||
|
{
|
||||||
|
processor.setTooltipBehavior(Tooltip::StandardDisplay);
|
||||||
|
resized();
|
||||||
|
});
|
||||||
|
tooltipMenu.addItem("Full Display", true, processor.getTooltipBehavior() == Tooltip::FullDisplay, [&]
|
||||||
|
{
|
||||||
|
processor.setTooltipBehavior(Tooltip::FullDisplay);
|
||||||
|
resized();
|
||||||
|
});
|
||||||
|
menu->addSubMenu("Tooltips", tooltipMenu, true);
|
||||||
|
|
||||||
#ifdef LINUX
|
#ifdef LINUX
|
||||||
menu->addItem(1, String("Release ") + String(JucePlugin_VersionString).dropLastCharacters(2), false);
|
menu->addItem(1, String("Release ") + String(JucePlugin_VersionString).dropLastCharacters(2), false);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -74,6 +74,7 @@ ObxdAudioProcessor::ObxdAudioProcessor()
|
||||||
config = std::unique_ptr<PropertiesFile> (new PropertiesFile (getDocumentFolder().getChildFile ("Skin.xml"), options));
|
config = std::unique_ptr<PropertiesFile> (new PropertiesFile (getDocumentFolder().getChildFile ("Skin.xml"), options));
|
||||||
showPresetBar = config->getBoolValue("presetnavigation");
|
showPresetBar = config->getBoolValue("presetnavigation");
|
||||||
gui_size = config->getIntValue("gui_size", 1);
|
gui_size = config->getIntValue("gui_size", 1);
|
||||||
|
tooltipBehavior = static_cast<Tooltip>(config->getIntValue("tooltip", 1));
|
||||||
currentSkin = config->containsKey("skin") ? config->getValue("skin") : "Ilkka Rosma Dark";
|
currentSkin = config->containsKey("skin") ? config->getValue("skin") : "Ilkka Rosma Dark";
|
||||||
currentBank = "000 - FMR OB-Xa Patch Book";
|
currentBank = "000 - FMR OB-Xa Patch Book";
|
||||||
|
|
||||||
|
@ -824,6 +825,19 @@ void ObxdAudioProcessor::setGuiSize(const int gui_size) {
|
||||||
config->setValue("gui_size", gui_size);
|
config->setValue("gui_size", gui_size);
|
||||||
config->setNeedsToBeSaved(true);
|
config->setNeedsToBeSaved(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tooltip ObxdAudioProcessor::getTooltipBehavior() const
|
||||||
|
{
|
||||||
|
return tooltipBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObxdAudioProcessor::setTooltipBehavior(const Tooltip tooltip)
|
||||||
|
{
|
||||||
|
this->tooltipBehavior = tooltip;
|
||||||
|
config->setValue("tooltip", static_cast<int>(tooltip));
|
||||||
|
config->setNeedsToBeSaved(true);
|
||||||
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
String ObxdAudioProcessor::getEngineParameterId (size_t index)
|
String ObxdAudioProcessor::getEngineParameterId (size_t index)
|
||||||
{
|
{
|
||||||
|
@ -1017,7 +1031,7 @@ String ObxdAudioProcessor::getTrueParameterValueFromNormalizedRange(size_t index
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return String{ value, 2 };
|
return String{ static_cast<int>(jmap(value, 0.f, 127.f)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
int ObxdAudioProcessor::getParameterIndexFromId (String paramId)
|
int ObxdAudioProcessor::getParameterIndexFromId (String paramId)
|
||||||
|
|
|
@ -110,6 +110,12 @@ static inline float fxbSwapFloat (const float x) noexcept
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class Tooltip
|
||||||
|
{
|
||||||
|
Disable = 0,
|
||||||
|
StandardDisplay,
|
||||||
|
FullDisplay
|
||||||
|
};
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@ -196,6 +202,8 @@ public:
|
||||||
File getCurrentSkinFolder() const;
|
File getCurrentSkinFolder() const;
|
||||||
void setCurrentSkinFolder(const String& folderName);
|
void setCurrentSkinFolder(const String& folderName);
|
||||||
void setGuiSize(const int gui_size);
|
void setGuiSize(const int gui_size);
|
||||||
|
Tooltip getTooltipBehavior() const;
|
||||||
|
void setTooltipBehavior(const Tooltip tooltip);
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
static String getEngineParameterId (size_t);
|
static String getEngineParameterId (size_t);
|
||||||
static String getTrueParameterValueFromNormalizedRange(size_t, float normalizedValue);
|
static String getTrueParameterValueFromNormalizedRange(size_t, float normalizedValue);
|
||||||
|
@ -251,6 +259,7 @@ public:
|
||||||
private:
|
private:
|
||||||
Array<File> bankFiles;
|
Array<File> bankFiles;
|
||||||
Array<File> skinFiles;
|
Array<File> skinFiles;
|
||||||
|
Tooltip tooltipBehavior;
|
||||||
|
|
||||||
std::unique_ptr<PropertiesFile> config;
|
std::unique_ptr<PropertiesFile> config;
|
||||||
InterProcessLock configLock;
|
InterProcessLock configLock;
|
||||||
|
|
Loading…
Reference in a new issue