54 short numIns, numOuts;
73 const String &preferredDefaultDeviceName = String(),
74 const AudioDeviceManager::AudioDeviceSetup *preferredSetupOptions =
nullptr,
75 const Array<PluginInOuts> &channels = Array<PluginInOuts>(),
76#
if JUCE_ANDROID || JUCE_IOS
77 bool shouldAutoOpenMidiDevices =
true
79 bool shouldAutoOpenMidiDevices =
false
83 : settings(settingsToUse, takeOwnershipOfSettings), channelConfiguration(channels),
84 autoOpenMidiDevices(shouldAutoOpenMidiDevices)
86 shouldMuteInput.addListener(
this);
87 shouldMuteInput = !isInterAppAudioConnected();
91 auto inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
92 : processor->getMainBusNumInputChannels());
94 if (preferredSetupOptions !=
nullptr)
95 options.reset(
new AudioDeviceManager::AudioDeviceSetup(*preferredSetupOptions));
97 auto audioInputRequired = (inChannels > 0);
99 if (audioInputRequired && RuntimePermissions::isRequired(RuntimePermissions::recordAudio) &&
100 !RuntimePermissions::isGranted(RuntimePermissions::recordAudio))
101 RuntimePermissions::request(
102 RuntimePermissions::recordAudio,
103 [
this, preferredDefaultDeviceName](
bool granted) { init(granted, preferredDefaultDeviceName); });
105 init(audioInputRequired, preferredDefaultDeviceName);
108 void init(
bool enableAudioInput,
const String &preferredDefaultDeviceName)
110 setupAudioDevices(enableAudioInput, preferredDefaultDeviceName, options.get());
114 if (autoOpenMidiDevices)
118 ~StandalonePluginHolder()
override
122 handleDeletePlugin();
123 shutDownAudioDevices();
127 virtual void createPlugin()
129 handleCreatePlugin();
132 virtual void deletePlugin()
134 handleDeletePlugin();
137 int getNumInputChannels()
const
139 if (processor ==
nullptr)
142 return (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
143 : processor->getMainBusNumInputChannels());
146 int getNumOutputChannels()
const
148 if (processor ==
nullptr)
151 return (channelConfiguration.size() > 0 ? channelConfiguration[0].numOuts
152 : processor->getMainBusNumOutputChannels());
155 static String getFilePatterns(
const String &fileSuffix)
157 if (fileSuffix.isEmpty())
160 return (fileSuffix.startsWithChar(
'.') ?
"*" :
"*.") + fileSuffix;
164 Value &getMuteInputValue()
166 return shouldMuteInput;
168 bool getProcessorHasPotentialFeedbackLoop()
const
170 return processorHasPotentialFeedbackLoop;
172 void valueChanged(Value &value)
override
174 muteInput = (bool)value.getValue();
178 File getLastFile()
const
182 if (settings !=
nullptr)
183 f = File(settings->getValue(
"lastStateFile"));
186 f = File::getSpecialLocation(File::userDocumentsDirectory);
191 void setLastFile(
const FileChooser &fc)
193 if (settings !=
nullptr)
194 settings->setValue(
"lastStateFile", fc.getResult().getFullPathName());
201 std::make_unique<FileChooser>(TRANS(
"Save current state"), getLastFile(), getFilePatterns(fileSuffix));
202 auto flags = FileBrowserComponent::saveMode | FileBrowserComponent::canSelectFiles |
203 FileBrowserComponent::warnAboutOverwriting;
205 stateFileChooser->launchAsync(flags, [
this](
const FileChooser &fc) {
206 if (fc.getResult() == File{})
212 processor->getStateInformation(data);
214 if (!fc.getResult().replaceWithData(data.getData(), data.getSize()))
216 auto opts = MessageBoxOptions::makeOptionsOk(AlertWindow::WarningIcon, TRANS(
"Error whilst saving"),
217 TRANS(
"Couldn't write to the specified file!"));
218 messageBox = AlertWindow::showScopedAsync(opts,
nullptr);
227 std::make_unique<FileChooser>(TRANS(
"Load a saved state"), getLastFile(), getFilePatterns(fileSuffix));
228 auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles;
230 stateFileChooser->launchAsync(flags, [
this](
const FileChooser &fc) {
231 if (fc.getResult() == File{})
238 if (fc.getResult().loadFileAsData(data))
240 processor->setStateInformation(data.getData(), (
int)data.getSize());
244 auto opts = MessageBoxOptions::makeOptionsOk(AlertWindow::WarningIcon, TRANS(
"Error whilst loading"),
245 TRANS(
"Couldn't read from the specified file!"));
246 messageBox = AlertWindow::showScopedAsync(opts,
nullptr);
254 player.setProcessor(processor.get());
256#if JucePlugin_Enable_IAA && JUCE_IOS
257 if (
auto device =
dynamic_cast<iOSAudioIODevice *
>(deviceManager.getCurrentAudioDevice()))
259 processor->setPlayHead(device->getAudioPlayHead());
260 device->setMidiMessageCollector(&player.getMidiMessageCollector());
267 player.setProcessor(
nullptr);
274 DialogWindow::LaunchOptions o;
276 int maxNumInputs = 0, maxNumOutputs = 0;
278 if (channelConfiguration.size() > 0)
280 auto &defaultConfig = channelConfiguration.getReference(0);
282 maxNumInputs = jmax(0, (
int)defaultConfig.numIns);
283 maxNumOutputs = jmax(0, (
int)defaultConfig.numOuts);
286 if (
auto *bus = processor->getBus(
true, 0))
287 maxNumInputs = jmax(0, bus->getDefaultLayout().size());
289 if (
auto *bus = processor->getBus(
false, 0))
290 maxNumOutputs = jmax(0, bus->getDefaultLayout().size());
292 auto content = std::make_unique<SettingsComponent>(*
this, deviceManager, maxNumInputs, maxNumOutputs);
293 content->setSize(500, 550);
294 content->setToRecommendedSize();
296 o.content.setOwned(content.release());
298 o.dialogTitle = TRANS(
"Audio Settings");
299 o.dialogBackgroundColour = o.content->getLookAndFeel().findColour(ResizableWindow::backgroundColourId);
300 o.escapeKeyTriggersCloseButton =
true;
301 o.useNativeTitleBar =
true;
307 void saveAudioDeviceState()
309 if (settings !=
nullptr)
311 auto xml = deviceManager.createStateXml();
313 settings->setValue(
"audioSetup", xml.get());
315#if !(JUCE_IOS || JUCE_ANDROID)
316 settings->setValue(
"shouldMuteInput", (
bool)shouldMuteInput.getValue());
321 void reloadAudioDeviceState(
bool enableAudioInput,
const String &preferredDefaultDeviceName,
322 const AudioDeviceManager::AudioDeviceSetup *preferredSetupOptions)
324 std::unique_ptr<XmlElement> savedState;
326 if (settings !=
nullptr)
328 savedState = settings->getXmlValue(
"audioSetup");
330#if !(JUCE_IOS || JUCE_ANDROID)
331 shouldMuteInput.setValue(settings->getBoolValue(
"shouldMuteInput",
true));
335 auto inputChannels = getNumInputChannels();
336 auto outputChannels = getNumOutputChannels();
338 if (inputChannels == 0 && outputChannels == 0 && processor->isMidiEffect())
344 deviceManager.initialise(enableAudioInput ? inputChannels : 0, outputChannels, savedState.get(),
true,
345 preferredDefaultDeviceName, preferredSetupOptions);
349 void savePluginState()
351 if (settings !=
nullptr && processor !=
nullptr)
354 processor->getStateInformation(data);
356 settings->setValue(
"filterState", data.toBase64Encoding());
360 void reloadPluginState()
362 if (settings !=
nullptr)
366 if (data.fromBase64Encoding(settings->getValue(
"filterState")) && data.getSize() > 0)
367 processor->setStateInformation(data.getData(), (
int)data.getSize());
372 void switchToHostApplication()
375 if (
auto device =
dynamic_cast<iOSAudioIODevice *
>(deviceManager.getCurrentAudioDevice()))
376 device->switchApplication();
380 bool isInterAppAudioConnected()
383 if (
auto device =
dynamic_cast<iOSAudioIODevice *
>(deviceManager.getCurrentAudioDevice()))
384 return device->isInterAppAudioConnected();
390 Image getIAAHostIcon([[maybe_unused]]
int size)
392#if JUCE_IOS && JucePlugin_Enable_IAA
393 if (
auto device =
dynamic_cast<iOSAudioIODevice *
>(deviceManager.getCurrentAudioDevice()))
394 return device->getIcon(size);
404 OptionalScopedPointer<PropertySet> settings;
405 std::unique_ptr<AudioProcessor> processor;
406 AudioDeviceManager deviceManager;
407 AudioProcessorPlayer player;
408 Array<PluginInOuts> channelConfiguration;
411 bool processorHasPotentialFeedbackLoop =
true;
412 std::atomic<bool> muteInput{
true};
413 Value shouldMuteInput;
414 AudioBuffer<float> emptyBuffer;
415 bool autoOpenMidiDevices;
417 std::unique_ptr<AudioDeviceManager::AudioDeviceSetup> options;
418 Array<MidiDeviceInfo> lastMidiDevices;
420 std::unique_ptr<FileChooser> stateFileChooser;
421 ScopedMessageBox messageBox;
425 void handleCreatePlugin()
427 processor = createPluginFilterOfType(AudioProcessor::wrapperType_Standalone);
428 processor->disableNonMainBuses();
429 processor->setRateAndBufferSizeDetails(44100, 512);
431 processorHasPotentialFeedbackLoop = (getNumInputChannels() > 0 && getNumOutputChannels() > 0);
434 void handleDeletePlugin()
454 class CallbackMaxSizeEnforcer :
public AudioIODeviceCallback
457 explicit CallbackMaxSizeEnforcer(AudioIODeviceCallback &callbackIn) : inner(callbackIn)
461 void audioDeviceAboutToStart(AudioIODevice *device)
override
463 maximumSize = device->getCurrentBufferSizeSamples();
464 storedInputChannels.resize((
size_t)device->getActiveInputChannels().countNumberOfSetBits());
465 storedOutputChannels.resize((
size_t)device->getActiveOutputChannels().countNumberOfSetBits());
467 inner.audioDeviceAboutToStart(device);
470 void audioDeviceIOCallbackWithContext(
const float *
const *inputChannelData,
471 [[maybe_unused]]
int numInputChannels,
float *
const *outputChannelData,
472 [[maybe_unused]]
int numOutputChannels,
int numSamples,
473 const AudioIODeviceCallbackContext &context)
override
475 jassert((
int)storedInputChannels.size() == numInputChannels);
476 jassert((
int)storedOutputChannels.size() == numOutputChannels);
480 while (position < numSamples)
482 const auto blockLength = jmin(maximumSize, numSamples - position);
484 initChannelPointers(inputChannelData, storedInputChannels, position);
485 initChannelPointers(outputChannelData, storedOutputChannels, position);
487 inner.audioDeviceIOCallbackWithContext(storedInputChannels.data(), (
int)storedInputChannels.size(),
488 storedOutputChannels.data(), (
int)storedOutputChannels.size(),
489 blockLength, context);
491 position += blockLength;
495 void audioDeviceStopped()
override
497 inner.audioDeviceStopped();
501 struct GetChannelWithOffset
505 template <
typename Ptr>
auto operator()(Ptr ptr)
const noexcept -> Ptr
511 template <
typename Ptr,
typename Vector>
void initChannelPointers(Ptr &&source, Vector &&target,
int offset)
513 std::transform(source, source + target.size(), target.begin(), GetChannelWithOffset{offset});
516 AudioIODeviceCallback &inner;
518 std::vector<const float *> storedInputChannels;
519 std::vector<float *> storedOutputChannels;
522 CallbackMaxSizeEnforcer maxSizeEnforcer{*
this};
525 class SettingsComponent :
public Component
529 int maxAudioInputChannels,
int maxAudioOutputChannels)
530 #if !JucePlugin_IsSynth
531 : owner(pluginHolder), deviceSelector(deviceManagerToUse, 0, maxAudioInputChannels, 0,
532 maxAudioOutputChannels,
false,
false,
false,
false),
534 : owner(pluginHolder), deviceSelector(deviceManagerToUse, 0, maxAudioInputChannels, 0,
535 maxAudioOutputChannels,
true, (pluginHolder.processor.get() !=
nullptr && pluginHolder.processor->producesMidi()),
true,
false),
537 shouldMuteLabel(
"Feedback Loop:",
"Feedback Loop:"), shouldMuteButton(
"Mute audio input")
541 shouldMuteButton.setClickingTogglesState(
true);
542 shouldMuteButton.getToggleStateValue().referTo(owner.shouldMuteInput);
544 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::ComboBox::backgroundColourId,
545 juce::Colours::black);
546 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::ListBox::backgroundColourId,
547 juce::Colours::black);
548 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::TextButton::buttonColourId,
549 juce::Colours::black);
550 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::ScrollBar::thumbColourId, juce::Colours::white);
551 juce::LookAndFeel::getDefaultLookAndFeel().setColour(juce::PopupMenu::backgroundColourId,
552 juce::Colours::black);
554 addAndMakeVisible(deviceSelector);
566 void paint(Graphics &g)
override
569 g.fillAll(juce::Colours::black);
572 void resized()
override
574 const ScopedValueSetter<bool> scope(isResizing,
true);
576 auto r = getLocalBounds();
578 if (owner.getProcessorHasPotentialFeedbackLoop())
580 auto itemHeight = deviceSelector.getItemHeight();
581 auto extra = r.removeFromTop(itemHeight);
583 auto seperatorHeight = (itemHeight >> 1);
584 shouldMuteButton.setBounds(Rectangle<int>(extra.proportionOfWidth(0.35f), seperatorHeight,
585 extra.proportionOfWidth(0.60f),
586 deviceSelector.getItemHeight()));
588 r.removeFromTop(seperatorHeight);
591 deviceSelector.setBounds(r);
594 void childBoundsChanged(Component *childComp)
override
596 if (!isResizing && childComp == &deviceSelector)
597 setToRecommendedSize();
600 void setToRecommendedSize()
602 const auto extraHeight = [&] {
603 if (!owner.getProcessorHasPotentialFeedbackLoop())
606 const auto itemHeight = deviceSelector.getItemHeight();
607 const auto separatorHeight = (itemHeight >> 1);
608 return itemHeight + separatorHeight;
611 setSize(getWidth(), deviceSelector.getHeight() + extraHeight);
617 AudioDeviceSelectorComponent deviceSelector;
619 Label shouldMuteLabel;
620 ToggleButton shouldMuteButton;
621 bool isResizing =
false;
624 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SettingsComponent)
628 void audioDeviceIOCallbackWithContext(
const float *
const *inputChannelData,
int numInputChannels,
629 float *
const *outputChannelData,
int numOutputChannels,
int numSamples,
630 const AudioIODeviceCallbackContext &context)
override
635 inputChannelData = emptyBuffer.getArrayOfReadPointers();
638 player.audioDeviceIOCallbackWithContext(inputChannelData, numInputChannels, outputChannelData,
639 numOutputChannels, numSamples, context);
642 void audioDeviceAboutToStart(AudioIODevice *device)
override
644 emptyBuffer.setSize(device->getActiveInputChannels().countNumberOfSetBits(),
645 device->getCurrentBufferSizeSamples());
648 player.audioDeviceAboutToStart(device);
649 player.setMidiOutput(deviceManager.getDefaultMidiOutput());
652 void audioDeviceStopped()
override
654 player.setMidiOutput(
nullptr);
655 player.audioDeviceStopped();
656 emptyBuffer.setSize(0, 0);
660 void setupAudioDevices(
bool enableAudioInput,
const String &preferredDefaultDeviceName,
661 const AudioDeviceManager::AudioDeviceSetup *preferredSetupOptions)
663 deviceManager.addAudioCallback(&maxSizeEnforcer);
664 deviceManager.addMidiInputDeviceCallback({}, &player);
666 reloadAudioDeviceState(enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
669 void shutDownAudioDevices()
671 saveAudioDeviceState();
673 deviceManager.removeMidiInputDeviceCallback({}, &player);
674 deviceManager.removeAudioCallback(&maxSizeEnforcer);
677 void timerCallback()
override
679 auto newMidiDevices = MidiInput::getAvailableDevices();
681 if (newMidiDevices != lastMidiDevices)
683 for (
auto &oldDevice : lastMidiDevices)
684 if (!newMidiDevices.contains(oldDevice))
685 deviceManager.setMidiInputDeviceEnabled(oldDevice.identifier,
false);
687 for (
auto &newDevice : newMidiDevices)
688 if (!lastMidiDevices.contains(newDevice))
689 deviceManager.setMidiInputDeviceEnabled(newDevice.identifier,
true);
691 lastMidiDevices = newMidiDevices;
721 bool takeOwnershipOfSettings,
const String &preferredDefaultDeviceName = String(),
722 const AudioDeviceManager::AudioDeviceSetup *preferredSetupOptions =
nullptr,
723 const Array<PluginInOuts> &constrainToConfiguration = {},
724#if JUCE_ANDROID || JUCE_IOS
725 bool autoOpenMidiDevices =
true
727 bool autoOpenMidiDevices =
false
730 : DocumentWindow(title, backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
731 optionsButton(
"Options")
733 setConstrainer(&decoratorConstrainer);
735#if JUCE_IOS || JUCE_ANDROID
736 setTitleBarHeight(0);
739 setUsingNativeTitleBar(
true);
740 setTitleBarButtonsRequired(DocumentWindow::minimiseButton | DocumentWindow::closeButton,
false);
742 Component::addAndMakeVisible(optionsButton);
743 optionsButton.addListener(
this);
744 optionsButton.setTriggeredOnMouseDown(
true);
748 preferredDefaultDeviceName, preferredSetupOptions,
749 constrainToConfiguration, autoOpenMidiDevices));
751#if JUCE_IOS || JUCE_ANDROID
757 const auto windowScreenBounds = [
this]() -> Rectangle<int> {
758 const auto width = getWidth();
759 const auto height = getHeight();
761 const auto &displays = Desktop::getInstance().getDisplays();
763 if (
auto *props = pluginHolder->settings.get())
765 constexpr int defaultValue = -100;
767 const auto x = props->getIntValue(
"windowX", defaultValue);
768 const auto y = props->getIntValue(
"windowY", defaultValue);
770 if (x != defaultValue && y != defaultValue)
772 const auto screenLimits = displays.getDisplayForRect({x, y, width, height})->userArea;
775 jlimit(screenLimits.getX(), jmax(screenLimits.getX(), screenLimits.getRight() - width), x),
776 jlimit(screenLimits.getY(), jmax(screenLimits.getY(), screenLimits.getBottom() - height), y),
781 const auto displayArea = displays.getPrimaryDisplay()->userArea;
783 return {displayArea.getCentreX() - width / 2, displayArea.getCentreY() - height / 2, width, height};
786 setBoundsConstrained(windowScreenBounds);
788 if (
auto *processor = getAudioProcessor())
789 if (
auto *editor = processor->getActiveEditor())
790 setResizable(editor->isResizable(),
false);
794 ~StandaloneFilterWindow()
override
796#if (!JUCE_IOS) && (!JUCE_ANDROID)
797 if (
auto *props = pluginHolder->settings.get())
799 props->setValue(
"windowX", getX());
800 props->setValue(
"windowY", getY());
804 pluginHolder->stopPlaying();
805 clearContentComponent();
806 pluginHolder =
nullptr;
810 AudioProcessor *getAudioProcessor() const noexcept
812 return pluginHolder->processor.get();
814 AudioDeviceManager &getDeviceManager() const noexcept
816 return pluginHolder->deviceManager;
822 pluginHolder->stopPlaying();
823 clearContentComponent();
824 pluginHolder->deletePlugin();
826 if (
auto *props = pluginHolder->settings.get())
827 props->removeValue(
"filterState");
829 pluginHolder->createPlugin();
831 pluginHolder->startPlaying();
835 void closeButtonPressed()
override
837 pluginHolder->savePluginState();
839 JUCEApplicationBase::quit();
842 void handleMenuResult(
int result)
847 pluginHolder->showAudioSettingsDialog();
850 pluginHolder->askUserToSaveState();
853 pluginHolder->askUserToLoadState();
865 if (button !=
nullptr && result != 0)
866 button->handleMenuResult(result);
869 void resized()
override
871 DocumentWindow::resized();
872 optionsButton.setBounds(8, 6, 60, getTitleBarHeight() - 8);
875 virtual StandalonePluginHolder *getPluginHolder()
877 return pluginHolder.get();
880 std::unique_ptr<StandalonePluginHolder> pluginHolder;
885 auto *content =
new MainContentComponent(*
this);
886 decoratorConstrainer.setMainContentComponent(content);
888#if JUCE_IOS || JUCE_ANDROID
889 constexpr auto resizeAutomatically =
false;
891 constexpr auto resizeAutomatically =
true;
894 setContentOwned(content, resizeAutomatically);
897 void buttonClicked(Button *)
override
900 m.addItem(1, TRANS(
"Audio/MIDI Settings..."));
902 m.addItem(2, TRANS(
"Save current state..."));
903 m.addItem(3, TRANS(
"Load a saved state..."));
905 m.addItem(4, TRANS(
"Reset to default state"));
907 m.showMenuAsync(PopupMenu::Options(), ModalCallbackFunction::forComponent(menuCallback,
this));
911 class MainContentComponent :
public Component,
912 private Value::Listener,
913 private Button::Listener,
914 private ComponentListener
918 : owner(filterWindow), notification(this),
919 editor(owner.getAudioProcessor()->hasEditor()
920 ? owner.getAudioProcessor()->createEditorIfNeeded()
921 : new GenericAudioProcessorEditor(*owner.getAudioProcessor()))
923 inputMutedValue.referTo(owner.pluginHolder->getMuteInputValue());
925 if (editor !=
nullptr)
927 editor->addComponentListener(
this);
928 handleMovedOrResized();
930 addAndMakeVisible(editor.get());
933 addChildComponent(notification);
935 if (owner.pluginHolder->getProcessorHasPotentialFeedbackLoop())
937 inputMutedValue.addListener(
this);
938 shouldShowNotification = inputMutedValue.getValue();
941 inputMutedChanged(shouldShowNotification);
944 ~MainContentComponent()
override
946 if (editor !=
nullptr)
948 editor->removeComponentListener(
this);
949 owner.pluginHolder->processor->editorBeingDeleted(editor.get());
954 void resized()
override
959 ComponentBoundsConstrainer *getEditorConstrainer()
const
961 if (
auto *e = editor.get())
962 return e->getConstrainer();
967 BorderSize<int> computeBorder()
const
969 const auto nativeFrame = [&]() -> BorderSize<int> {
970 if (
auto *peer = owner.getPeer())
971 if (
const auto frameSize = peer->getFrameSizeIfPresent())
977 return nativeFrame.addedTo(owner.getContentComponentBorder())
978 .addedTo(BorderSize<int>{shouldShowNotification ? NotificationArea::height : 0, 0, 0, 0});
983 class NotificationArea :
public Component
991 NotificationArea(Button::Listener *settingsButtonListener)
992 : notification(
"notification",
"Audio input is muted to avoid feedback loop"),
993#if JUCE_IOS || JUCE_ANDROID
994 settingsButton(
"Unmute Input")
996 settingsButton(
"Settings...")
1001 notification.setColour(Label::textColourId, Colours::black);
1003 settingsButton.addListener(settingsButtonListener);
1005 addAndMakeVisible(notification);
1006 addAndMakeVisible(settingsButton);
1009 void paint(Graphics &g)
override
1011 auto r = getLocalBounds();
1013 g.setColour(Colours::darkgoldenrod);
1014 g.fillRect(r.removeFromBottom(1));
1016 g.setColour(Colours::lightgoldenrodyellow);
1020 void resized()
override
1022 auto r = getLocalBounds().reduced(5);
1024 settingsButton.setBounds(r.removeFromRight(70));
1025 notification.setBounds(r);
1030 TextButton settingsButton;
1034 void inputMutedChanged(
bool newInputMutedValue)
1036 shouldShowNotification = newInputMutedValue;
1037 notification.setVisible(shouldShowNotification);
1039#if JUCE_IOS || JUCE_ANDROID
1042 if (editor !=
nullptr)
1044 const int extraHeight = shouldShowNotification ? NotificationArea::height : 0;
1045 const auto rect = getSizeToContainEditor();
1046 setSize(rect.getWidth(), rect.getHeight() + extraHeight);
1051 void valueChanged(Value &value)
override
1053 inputMutedChanged(value.getValue());
1055 void buttonClicked(Button *)
override
1057#if JUCE_IOS || JUCE_ANDROID
1058 owner.pluginHolder->getMuteInputValue().setValue(
false);
1060 owner.pluginHolder->showAudioSettingsDialog();
1065 void handleResized()
1067 auto r = getLocalBounds();
1069 if (shouldShowNotification)
1070 notification.setBounds(r.removeFromTop(NotificationArea::height));
1072 if (editor !=
nullptr)
1074 const auto newPos = r.getTopLeft().toFloat().transformedBy(editor->getTransform().inverted());
1076 if (preventResizingEditor)
1077 editor->setTopLeftPosition(newPos.roundToInt());
1079 editor->setBoundsConstrained(
1080 editor->getLocalArea(
this, r.toFloat()).withPosition(newPos).toNearestInt());
1084 void handleMovedOrResized()
1086 const ScopedValueSetter<bool> scope(preventResizingEditor,
true);
1088 if (editor !=
nullptr)
1090 auto rect = getSizeToContainEditor();
1092 setSize(rect.getWidth(), rect.getHeight() + (shouldShowNotification ? NotificationArea::height : 0));
1096 void componentMovedOrResized(Component &,
bool,
bool)
override
1098 handleMovedOrResized();
1101 Rectangle<int> getSizeToContainEditor()
const
1103 if (editor !=
nullptr)
1104 return getLocalArea(editor.get(), editor->getLocalBounds());
1111 NotificationArea notification;
1112 std::unique_ptr<AudioProcessorEditor> editor;
1113 Value inputMutedValue;
1114 bool shouldShowNotification =
false;
1115 bool preventResizingEditor =
false;
1117 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainContentComponent)
1132 class DecoratorConstrainer :
public BorderedComponentBoundsConstrainer
1135 ComponentBoundsConstrainer *getWrappedConstrainer()
const override
1137 return contentComponent !=
nullptr ? contentComponent->getEditorConstrainer() :
nullptr;
1140 BorderSize<int> getAdditionalBorder()
const override
1142 return contentComponent !=
nullptr ? contentComponent->computeBorder() : BorderSize<int>{};
1145 void setMainContentComponent(MainContentComponent *in)
1147 contentComponent = in;
1151 MainContentComponent *contentComponent =
nullptr;
1155 TextButton optionsButton;
1156 DecoratorConstrainer decoratorConstrainer;