56 short numIns, numOuts;
75 const String &preferredDefaultDeviceName = String(),
76 const AudioDeviceManager::AudioDeviceSetup *preferredSetupOptions =
nullptr,
77 const Array<PluginInOuts> &channels = Array<PluginInOuts>(),
78#
if JUCE_ANDROID || JUCE_IOS
79 bool shouldAutoOpenMidiDevices =
true
81 bool shouldAutoOpenMidiDevices =
false
85 : settings(settingsToUse, takeOwnershipOfSettings), channelConfiguration(channels),
86 autoOpenMidiDevices(shouldAutoOpenMidiDevices)
88 shouldMuteInput.addListener(
this);
89 shouldMuteInput = !isInterAppAudioConnected();
93 auto inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
94 : processor->getMainBusNumInputChannels());
96 if (preferredSetupOptions !=
nullptr)
97 options.reset(
new AudioDeviceManager::AudioDeviceSetup(*preferredSetupOptions));
99 auto audioInputRequired = (inChannels > 0);
101 if (audioInputRequired && RuntimePermissions::isRequired(RuntimePermissions::recordAudio) &&
102 !RuntimePermissions::isGranted(RuntimePermissions::recordAudio))
103 RuntimePermissions::request(
104 RuntimePermissions::recordAudio,
105 [
this, preferredDefaultDeviceName](
bool granted) { init(granted, preferredDefaultDeviceName); });
107 init(audioInputRequired, preferredDefaultDeviceName);
110 void init(
bool enableAudioInput,
const String &preferredDefaultDeviceName)
112 setupAudioDevices(enableAudioInput, preferredDefaultDeviceName, options.get());
116 if (autoOpenMidiDevices)
120 ~StandalonePluginHolder()
override
124 handleDeletePlugin();
125 shutDownAudioDevices();
129 virtual void createPlugin()
131 handleCreatePlugin();
134 virtual void deletePlugin()
136 handleDeletePlugin();
139 int getNumInputChannels()
const
141 if (processor ==
nullptr)
144 return (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
145 : processor->getMainBusNumInputChannels());
148 int getNumOutputChannels()
const
150 if (processor ==
nullptr)
153 return (channelConfiguration.size() > 0 ? channelConfiguration[0].numOuts
154 : processor->getMainBusNumOutputChannels());
157 static String getFilePatterns(
const String &fileSuffix)
159 if (fileSuffix.isEmpty())
162 return (fileSuffix.startsWithChar(
'.') ?
"*" :
"*.") + fileSuffix;
166 Value &getMuteInputValue()
168 return shouldMuteInput;
170 bool getProcessorHasPotentialFeedbackLoop()
const
172 return processorHasPotentialFeedbackLoop;
174 void valueChanged(Value &value)
override
176 muteInput = (bool)value.getValue();
180 File getLastFile()
const
184 if (settings !=
nullptr)
185 f = File(settings->getValue(
"lastStateFile"));
188 f = File::getSpecialLocation(File::userDocumentsDirectory);
193 void setLastFile(
const FileChooser &fc)
195 if (settings !=
nullptr)
196 settings->setValue(
"lastStateFile", fc.getResult().getFullPathName());
203 std::make_unique<FileChooser>(TRANS(
"Save current state"), getLastFile(), getFilePatterns(fileSuffix));
204 auto flags = FileBrowserComponent::saveMode | FileBrowserComponent::canSelectFiles |
205 FileBrowserComponent::warnAboutOverwriting;
207 stateFileChooser->launchAsync(flags, [
this](
const FileChooser &fc) {
208 if (fc.getResult() == File{})
214 processor->getStateInformation(data);
216 if (!fc.getResult().replaceWithData(data.getData(), data.getSize()))
218 auto opts = MessageBoxOptions::makeOptionsOk(AlertWindow::WarningIcon, TRANS(
"Error whilst saving"),
219 TRANS(
"Couldn't write to the specified file!"));
220 messageBox = AlertWindow::showScopedAsync(opts,
nullptr);
229 std::make_unique<FileChooser>(TRANS(
"Load a saved state"), getLastFile(), getFilePatterns(fileSuffix));
230 auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles;
232 stateFileChooser->launchAsync(flags, [
this](
const FileChooser &fc) {
233 if (fc.getResult() == File{})
240 if (fc.getResult().loadFileAsData(data))
242 processor->setStateInformation(data.getData(), (
int)data.getSize());
246 auto opts = MessageBoxOptions::makeOptionsOk(AlertWindow::WarningIcon, TRANS(
"Error whilst loading"),
247 TRANS(
"Couldn't read from the specified file!"));
248 messageBox = AlertWindow::showScopedAsync(opts,
nullptr);
256 player.setProcessor(processor.get());
258#if JucePlugin_Enable_IAA && JUCE_IOS
259 if (
auto device =
dynamic_cast<iOSAudioIODevice *
>(deviceManager.getCurrentAudioDevice()))
261 processor->setPlayHead(device->getAudioPlayHead());
262 device->setMidiMessageCollector(&player.getMidiMessageCollector());
269 player.setProcessor(
nullptr);
276 DialogWindow::LaunchOptions o;
278 int maxNumInputs = 0, maxNumOutputs = 0;
280 if (channelConfiguration.size() > 0)
282 auto &defaultConfig = channelConfiguration.getReference(0);
284 maxNumInputs = jmax(0, (
int)defaultConfig.numIns);
285 maxNumOutputs = jmax(0, (
int)defaultConfig.numOuts);
288 if (
auto *bus = processor->getBus(
true, 0))
289 maxNumInputs = jmax(0, bus->getDefaultLayout().size());
291 if (
auto *bus = processor->getBus(
false, 0))
292 maxNumOutputs = jmax(0, bus->getDefaultLayout().size());
294 auto content = std::make_unique<SettingsComponent>(*
this, deviceManager, maxNumInputs, maxNumOutputs);
295 content->setSize(500, 550);
296 content->setToRecommendedSize();
298 o.content.setOwned(content.release());
300 o.dialogTitle = TRANS(
"Audio Settings");
301 o.dialogBackgroundColour = o.content->getLookAndFeel().findColour(ResizableWindow::backgroundColourId);
302 o.escapeKeyTriggersCloseButton =
true;
303 o.useNativeTitleBar =
true;
309 void saveAudioDeviceState()
311 if (settings !=
nullptr)
313 auto xml = deviceManager.createStateXml();
315 settings->setValue(
"audioSetup", xml.get());
317#if !(JUCE_IOS || JUCE_ANDROID)
318 settings->setValue(
"shouldMuteInput", (
bool)shouldMuteInput.getValue());
323 void reloadAudioDeviceState(
bool enableAudioInput,
const String &preferredDefaultDeviceName,
324 const AudioDeviceManager::AudioDeviceSetup *preferredSetupOptions)
326 std::unique_ptr<XmlElement> savedState;
328 if (settings !=
nullptr)
330 savedState = settings->getXmlValue(
"audioSetup");
332#if !(JUCE_IOS || JUCE_ANDROID)
333 shouldMuteInput.setValue(settings->getBoolValue(
"shouldMuteInput",
true));
337 auto inputChannels = getNumInputChannels();
338 auto outputChannels = getNumOutputChannels();
340 if (inputChannels == 0 && outputChannels == 0 && processor->isMidiEffect())
346 deviceManager.initialise(enableAudioInput ? inputChannels : 0, outputChannels, savedState.get(),
true,
347 preferredDefaultDeviceName, preferredSetupOptions);
351 void savePluginState()
353 if (settings !=
nullptr && processor !=
nullptr)
356 processor->getStateInformation(data);
358 settings->setValue(
"filterState", data.toBase64Encoding());
362 void reloadPluginState()
364 if (settings !=
nullptr)
368 if (data.fromBase64Encoding(settings->getValue(
"filterState")) && data.getSize() > 0)
369 processor->setStateInformation(data.getData(), (
int)data.getSize());
374 void switchToHostApplication()
377 if (
auto device =
dynamic_cast<iOSAudioIODevice *
>(deviceManager.getCurrentAudioDevice()))
378 device->switchApplication();
382 bool isInterAppAudioConnected()
385 if (
auto device =
dynamic_cast<iOSAudioIODevice *
>(deviceManager.getCurrentAudioDevice()))
386 return device->isInterAppAudioConnected();
392 Image getIAAHostIcon([[maybe_unused]]
int size)
394#if JUCE_IOS && JucePlugin_Enable_IAA
395 if (
auto device =
dynamic_cast<iOSAudioIODevice *
>(deviceManager.getCurrentAudioDevice()))
396 return device->getIcon(size);
406 OptionalScopedPointer<PropertySet> settings;
407 std::unique_ptr<AudioProcessor> processor;
408 AudioDeviceManager deviceManager;
409 AudioProcessorPlayer player;
410 Array<PluginInOuts> channelConfiguration;
413 bool processorHasPotentialFeedbackLoop =
true;
414 std::atomic<bool> muteInput{
true};
415 Value shouldMuteInput;
416 AudioBuffer<float> emptyBuffer;
417 bool autoOpenMidiDevices;
419 std::unique_ptr<AudioDeviceManager::AudioDeviceSetup> options;
420 Array<MidiDeviceInfo> lastMidiDevices;
422 std::unique_ptr<FileChooser> stateFileChooser;
423 ScopedMessageBox messageBox;
427 void handleCreatePlugin()
429 processor = createPluginFilterOfType(AudioProcessor::wrapperType_Standalone);
430 processor->disableNonMainBuses();
431 processor->setRateAndBufferSizeDetails(44100, 512);
433 processorHasPotentialFeedbackLoop = (getNumInputChannels() > 0 && getNumOutputChannels() > 0);
436 void handleDeletePlugin()
456 class CallbackMaxSizeEnforcer :
public AudioIODeviceCallback
459 explicit CallbackMaxSizeEnforcer(AudioIODeviceCallback &callbackIn) : inner(callbackIn)
463 void audioDeviceAboutToStart(AudioIODevice *device)
override
465 maximumSize = device->getCurrentBufferSizeSamples();
466 storedInputChannels.resize((
size_t)device->getActiveInputChannels().countNumberOfSetBits());
467 storedOutputChannels.resize((
size_t)device->getActiveOutputChannels().countNumberOfSetBits());
469 inner.audioDeviceAboutToStart(device);
472 void audioDeviceIOCallbackWithContext(
const float *
const *inputChannelData,
473 [[maybe_unused]]
int numInputChannels,
float *
const *outputChannelData,
474 [[maybe_unused]]
int numOutputChannels,
int numSamples,
475 const AudioIODeviceCallbackContext &context)
override
477 jassert((
int)storedInputChannels.size() == numInputChannels);
478 jassert((
int)storedOutputChannels.size() == numOutputChannels);
482 while (position < numSamples)
484 const auto blockLength = jmin(maximumSize, numSamples - position);
486 initChannelPointers(inputChannelData, storedInputChannels, position);
487 initChannelPointers(outputChannelData, storedOutputChannels, position);
489 inner.audioDeviceIOCallbackWithContext(storedInputChannels.data(), (
int)storedInputChannels.size(),
490 storedOutputChannels.data(), (
int)storedOutputChannels.size(),
491 blockLength, context);
493 position += blockLength;
497 void audioDeviceStopped()
override
499 inner.audioDeviceStopped();
503 struct GetChannelWithOffset
507 template <
typename Ptr>
auto operator()(Ptr ptr)
const noexcept -> Ptr
513 template <
typename Ptr,
typename Vector>
void initChannelPointers(Ptr &&source, Vector &&target,
int offset)
515 std::transform(source, source + target.size(), target.begin(), GetChannelWithOffset{offset});
518 AudioIODeviceCallback &inner;
520 std::vector<const float *> storedInputChannels;
521 std::vector<float *> storedOutputChannels;
524 CallbackMaxSizeEnforcer maxSizeEnforcer{*
this};
527 class SettingsComponent :
public Component
531 int maxAudioInputChannels,
int maxAudioOutputChannels)
532 #if !JucePlugin_IsSynth
533 : owner(pluginHolder), deviceSelector(deviceManagerToUse, 0, maxAudioInputChannels, 0,
534 maxAudioOutputChannels,
false,
false,
false,
false),
536 : owner(pluginHolder), deviceSelector(deviceManagerToUse, 0, maxAudioInputChannels, 0,
537 maxAudioOutputChannels,
true, (pluginHolder.processor.get() !=
nullptr && pluginHolder.processor->producesMidi()),
true,
false),
539 shouldMuteLabel(
"Feedback Loop:",
"Feedback Loop:"), shouldMuteButton(
"Mute audio input")
543 shouldMuteButton.setClickingTogglesState(
true);
544 shouldMuteButton.getToggleStateValue().referTo(owner.shouldMuteInput);
546 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::ComboBox::backgroundColourId,
547 juce::Colours::black);
548 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::ListBox::backgroundColourId,
549 juce::Colours::black);
550 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::TextButton::buttonColourId,
551 juce::Colours::black);
552 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::ScrollBar::thumbColourId, juce::Colours::white);
553 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::PopupMenu::backgroundColourId,
554 juce::Colours::black);
556 addAndMakeVisible(deviceSelector);
568 void paint(Graphics &g)
override
571 g.fillAll(juce::Colours::black);
574 void resized()
override
576 const ScopedValueSetter<bool> scope(isResizing,
true);
578 auto r = getLocalBounds();
580 if (owner.getProcessorHasPotentialFeedbackLoop())
582 auto itemHeight = deviceSelector.getItemHeight();
583 auto extra = r.removeFromTop(itemHeight);
585 auto seperatorHeight = (itemHeight >> 1);
586 shouldMuteButton.setBounds(Rectangle<int>(extra.proportionOfWidth(0.35f), seperatorHeight,
587 extra.proportionOfWidth(0.60f),
588 deviceSelector.getItemHeight()));
590 r.removeFromTop(seperatorHeight);
593 deviceSelector.setBounds(r);
596 void childBoundsChanged(Component *childComp)
override
598 if (!isResizing && childComp == &deviceSelector)
599 setToRecommendedSize();
602 void setToRecommendedSize()
604 const auto extraHeight = [&] {
605 if (!owner.getProcessorHasPotentialFeedbackLoop())
608 const auto itemHeight = deviceSelector.getItemHeight();
609 const auto separatorHeight = (itemHeight >> 1);
610 return itemHeight + separatorHeight;
613 setSize(getWidth(), deviceSelector.getHeight() + extraHeight);
619 AudioDeviceSelectorComponent deviceSelector;
621 Label shouldMuteLabel;
622 ToggleButton shouldMuteButton;
623 bool isResizing =
false;
626 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SettingsComponent)
630 void audioDeviceIOCallbackWithContext(
const float *
const *inputChannelData,
int numInputChannels,
631 float *
const *outputChannelData,
int numOutputChannels,
int numSamples,
632 const AudioIODeviceCallbackContext &context)
override
637 inputChannelData = emptyBuffer.getArrayOfReadPointers();
640 player.audioDeviceIOCallbackWithContext(inputChannelData, numInputChannels, outputChannelData,
641 numOutputChannels, numSamples, context);
644 void audioDeviceAboutToStart(AudioIODevice *device)
override
646 emptyBuffer.setSize(device->getActiveInputChannels().countNumberOfSetBits(),
647 device->getCurrentBufferSizeSamples());
650 player.audioDeviceAboutToStart(device);
651 player.setMidiOutput(deviceManager.getDefaultMidiOutput());
654 void audioDeviceStopped()
override
656 player.setMidiOutput(
nullptr);
657 player.audioDeviceStopped();
658 emptyBuffer.setSize(0, 0);
662 void setupAudioDevices(
bool enableAudioInput,
const String &preferredDefaultDeviceName,
663 const AudioDeviceManager::AudioDeviceSetup *preferredSetupOptions)
665 deviceManager.addAudioCallback(&maxSizeEnforcer);
666 deviceManager.addMidiInputDeviceCallback({}, &player);
668 reloadAudioDeviceState(enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
671 void shutDownAudioDevices()
673 saveAudioDeviceState();
675 deviceManager.removeMidiInputDeviceCallback({}, &player);
676 deviceManager.removeAudioCallback(&maxSizeEnforcer);
679 void timerCallback()
override
681 auto newMidiDevices = MidiInput::getAvailableDevices();
683 if (newMidiDevices != lastMidiDevices)
685 for (
auto &oldDevice : lastMidiDevices)
686 if (!newMidiDevices.contains(oldDevice))
687 deviceManager.setMidiInputDeviceEnabled(oldDevice.identifier,
false);
689 for (
auto &newDevice : newMidiDevices)
690 if (!lastMidiDevices.contains(newDevice))
691 deviceManager.setMidiInputDeviceEnabled(newDevice.identifier,
true);
693 lastMidiDevices = newMidiDevices;
723 bool takeOwnershipOfSettings,
const String &preferredDefaultDeviceName = String(),
724 const AudioDeviceManager::AudioDeviceSetup *preferredSetupOptions =
nullptr,
725 const Array<PluginInOuts> &constrainToConfiguration = {},
726#if JUCE_ANDROID || JUCE_IOS
727 bool autoOpenMidiDevices =
true
729 bool autoOpenMidiDevices =
false
732 : DocumentWindow(title, backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
733 optionsButton(
"Options")
735 setConstrainer(&decoratorConstrainer);
737#if JUCE_IOS || JUCE_ANDROID
738 setTitleBarHeight(0);
741 setUsingNativeTitleBar(
true);
742 setTitleBarButtonsRequired(DocumentWindow::minimiseButton | DocumentWindow::closeButton,
false);
744 Component::addAndMakeVisible(optionsButton);
745 optionsButton.addListener(
this);
746 optionsButton.setTriggeredOnMouseDown(
true);
750 preferredDefaultDeviceName, preferredSetupOptions,
751 constrainToConfiguration, autoOpenMidiDevices));
753#if JUCE_IOS || JUCE_ANDROID
759 const auto windowScreenBounds = [
this]() -> Rectangle<int> {
760 const auto width = getWidth();
761 const auto height = getHeight();
763 const auto &displays = Desktop::getInstance().getDisplays();
765 if (
auto *props = pluginHolder->settings.get())
767 constexpr int defaultValue = -100;
769 const auto x = props->getIntValue(
"windowX", defaultValue);
770 const auto y = props->getIntValue(
"windowY", defaultValue);
772 if (x != defaultValue && y != defaultValue)
774 const auto screenLimits = displays.getDisplayForRect({x, y, width, height})->userArea;
777 jlimit(screenLimits.getX(), jmax(screenLimits.getX(), screenLimits.getRight() - width), x),
778 jlimit(screenLimits.getY(), jmax(screenLimits.getY(), screenLimits.getBottom() - height), y),
783 const auto displayArea = displays.getPrimaryDisplay()->userArea;
785 return {displayArea.getCentreX() - width / 2, displayArea.getCentreY() - height / 2, width, height};
788 setBoundsConstrained(windowScreenBounds);
790 if (
auto *processor = getAudioProcessor())
791 if (
auto *editor = processor->getActiveEditor())
792 setResizable(editor->isResizable(),
false);
796 ~StandaloneFilterWindow()
override
798#if (!JUCE_IOS) && (!JUCE_ANDROID)
799 if (
auto *props = pluginHolder->settings.get())
801 props->setValue(
"windowX", getX());
802 props->setValue(
"windowY", getY());
806 pluginHolder->stopPlaying();
807 clearContentComponent();
808 pluginHolder =
nullptr;
812 AudioProcessor *getAudioProcessor() const noexcept
814 return pluginHolder->processor.get();
816 AudioDeviceManager &getDeviceManager() const noexcept
818 return pluginHolder->deviceManager;
824 pluginHolder->stopPlaying();
825 clearContentComponent();
826 pluginHolder->deletePlugin();
828 if (
auto *props = pluginHolder->settings.get())
829 props->removeValue(
"filterState");
831 pluginHolder->createPlugin();
833 pluginHolder->startPlaying();
837 void closeButtonPressed()
override
839 pluginHolder->savePluginState();
841 JUCEApplicationBase::quit();
844 void handleMenuResult(
int result)
849 pluginHolder->showAudioSettingsDialog();
852 pluginHolder->askUserToSaveState();
855 pluginHolder->askUserToLoadState();
867 if (button !=
nullptr && result != 0)
868 button->handleMenuResult(result);
871 void resized()
override
873 DocumentWindow::resized();
874 optionsButton.setBounds(8, 6, 60, getTitleBarHeight() - 8);
877 virtual StandalonePluginHolder *getPluginHolder()
879 return pluginHolder.get();
882 std::unique_ptr<StandalonePluginHolder> pluginHolder;
887 auto *content =
new MainContentComponent(*
this);
888 decoratorConstrainer.setMainContentComponent(content);
890#if JUCE_IOS || JUCE_ANDROID
891 constexpr auto resizeAutomatically =
false;
893 constexpr auto resizeAutomatically =
true;
896 setContentOwned(content, resizeAutomatically);
899 void buttonClicked(Button *)
override
902 m.addItem(1, TRANS(
"Audio/MIDI Settings..."));
904 m.addItem(2, TRANS(
"Save current state..."));
905 m.addItem(3, TRANS(
"Load a saved state..."));
907 m.addItem(4, TRANS(
"Reset to default state"));
909 m.showMenuAsync(PopupMenu::Options(), ModalCallbackFunction::forComponent(menuCallback,
this));
913 class MainContentComponent :
public Component,
914 private Value::Listener,
915 private Button::Listener,
916 private ComponentListener
920 : owner(filterWindow), notification(this),
921 editor(owner.getAudioProcessor()->hasEditor()
922 ? owner.getAudioProcessor()->createEditorIfNeeded()
923 : new GenericAudioProcessorEditor(*owner.getAudioProcessor()))
925 inputMutedValue.referTo(owner.pluginHolder->getMuteInputValue());
927 if (editor !=
nullptr)
929 editor->addComponentListener(
this);
930 handleMovedOrResized();
932 addAndMakeVisible(editor.get());
935 addChildComponent(notification);
937 if (owner.pluginHolder->getProcessorHasPotentialFeedbackLoop())
939 inputMutedValue.addListener(
this);
940 shouldShowNotification = inputMutedValue.getValue();
943 inputMutedChanged(shouldShowNotification);
946 ~MainContentComponent()
override
948 if (editor !=
nullptr)
950 editor->removeComponentListener(
this);
951 owner.pluginHolder->processor->editorBeingDeleted(editor.get());
956 void resized()
override
961 ComponentBoundsConstrainer *getEditorConstrainer()
const
963 if (
auto *e = editor.get())
964 return e->getConstrainer();
969 BorderSize<int> computeBorder()
const
971 const auto nativeFrame = [&]() -> BorderSize<int> {
972 if (
auto *peer = owner.getPeer())
973 if (
const auto frameSize = peer->getFrameSizeIfPresent())
979 return nativeFrame.addedTo(owner.getContentComponentBorder())
980 .addedTo(BorderSize<int>{shouldShowNotification ? NotificationArea::height : 0, 0, 0, 0});
985 class NotificationArea :
public Component
993 NotificationArea(Button::Listener *settingsButtonListener)
994 : notification(
"notification",
"Audio input is muted to avoid feedback loop"),
995#if JUCE_IOS || JUCE_ANDROID
996 settingsButton(
"Unmute Input")
998 settingsButton(
"Settings...")
1003 notification.setColour(Label::textColourId, Colours::black);
1005 settingsButton.addListener(settingsButtonListener);
1007 addAndMakeVisible(notification);
1008 addAndMakeVisible(settingsButton);
1011 void paint(Graphics &g)
override
1013 auto r = getLocalBounds();
1015 g.setColour(Colours::darkgoldenrod);
1016 g.fillRect(r.removeFromBottom(1));
1018 g.setColour(Colours::lightgoldenrodyellow);
1022 void resized()
override
1024 auto r = getLocalBounds().reduced(5);
1026 settingsButton.setBounds(r.removeFromRight(70));
1027 notification.setBounds(r);
1032 TextButton settingsButton;
1036 void inputMutedChanged(
bool newInputMutedValue)
1038 shouldShowNotification = newInputMutedValue;
1039 notification.setVisible(shouldShowNotification);
1041#if JUCE_IOS || JUCE_ANDROID
1044 if (editor !=
nullptr)
1046 const int extraHeight = shouldShowNotification ? NotificationArea::height : 0;
1047 const auto rect = getSizeToContainEditor();
1048 setSize(rect.getWidth(), rect.getHeight() + extraHeight);
1053 void valueChanged(Value &value)
override
1055 inputMutedChanged(value.getValue());
1057 void buttonClicked(Button *)
override
1059#if JUCE_IOS || JUCE_ANDROID
1060 owner.pluginHolder->getMuteInputValue().setValue(
false);
1062 owner.pluginHolder->showAudioSettingsDialog();
1067 void handleResized()
1069 auto r = getLocalBounds();
1071 if (shouldShowNotification)
1072 notification.setBounds(r.removeFromTop(NotificationArea::height));
1074 if (editor !=
nullptr)
1076 const auto newPos = r.getTopLeft().toFloat().transformedBy(editor->getTransform().inverted());
1078 if (preventResizingEditor)
1079 editor->setTopLeftPosition(newPos.roundToInt());
1081 editor->setBoundsConstrained(
1082 editor->getLocalArea(
this, r.toFloat()).withPosition(newPos).toNearestInt());
1086 void handleMovedOrResized()
1088 const ScopedValueSetter<bool> scope(preventResizingEditor,
true);
1090 if (editor !=
nullptr)
1092 auto rect = getSizeToContainEditor();
1094 setSize(rect.getWidth(), rect.getHeight() + (shouldShowNotification ? NotificationArea::height : 0));
1098 void componentMovedOrResized(Component &,
bool,
bool)
override
1100 handleMovedOrResized();
1103 Rectangle<int> getSizeToContainEditor()
const
1105 if (editor !=
nullptr)
1106 return getLocalArea(editor.get(), editor->getLocalBounds());
1113 NotificationArea notification;
1114 std::unique_ptr<AudioProcessorEditor> editor;
1115 Value inputMutedValue;
1116 bool shouldShowNotification =
false;
1117 bool preventResizingEditor =
false;
1119 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainContentComponent)
1134 class DecoratorConstrainer :
public BorderedComponentBoundsConstrainer
1137 ComponentBoundsConstrainer *getWrappedConstrainer()
const override
1139 return contentComponent !=
nullptr ? contentComponent->getEditorConstrainer() :
nullptr;
1142 BorderSize<int> getAdditionalBorder()
const override
1144 return contentComponent !=
nullptr ? contentComponent->computeBorder() : BorderSize<int>{};
1147 void setMainContentComponent(MainContentComponent *in)
1149 contentComponent = in;
1153 MainContentComponent *contentComponent =
nullptr;
1157 TextButton optionsButton;
1158 DecoratorConstrainer decoratorConstrainer;