layout update supporting better dynamic resizing, lookandfeel update, ios support
This commit is contained in:
parent
8c4a20ecd6
commit
2084782a79
@ -4,7 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
void getSafeAreaInsets(void * component, float & top, float & bottom, float & left, float & right);
|
||||
// notchPos is 0=none 1=top 2=bottom, 3=left, 4=right
|
||||
|
||||
void getSafeAreaInsets(void * component, float & top, float & bottom, float & left, float & right, int & notchPos);
|
||||
|
||||
|
||||
#if JUCE_MAC
|
||||
|
62
Source/CrossPlatformUtilsIOS.mm
Normal file
62
Source/CrossPlatformUtilsIOS.mm
Normal file
@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPLv3-or-later WITH Appstore-exception
|
||||
// Copyright (C) 2020 Jesse Chappell
|
||||
|
||||
|
||||
|
||||
#include "CrossPlatformUtils.h"
|
||||
|
||||
//#include "JuceLibraryCode/AppConfig.h"
|
||||
|
||||
#include <juce_core/system/juce_TargetPlatform.h>
|
||||
|
||||
#if JUCE_IOS
|
||||
|
||||
|
||||
|
||||
|
||||
#import <UIKit/UIView.h>
|
||||
|
||||
//#include "DebugLogC.h"
|
||||
|
||||
|
||||
|
||||
//#include "../JuceLibraryCode/JuceHeader.h"
|
||||
|
||||
|
||||
// notchPos is 0=none 1=top 2=bottom, 3=left, 4=right
|
||||
void getSafeAreaInsets(void * component, float & top, float & bottom, float & left, float & right, int & notchPos)
|
||||
{
|
||||
top = bottom = left = right = 0;
|
||||
notchPos = 0;
|
||||
|
||||
if ([(id)component isKindOfClass:[UIView class]]) {
|
||||
UIView * view = (UIView *) component;
|
||||
|
||||
if (@available(iOS 11, *)) {
|
||||
UIEdgeInsets insets = view.safeAreaInsets;
|
||||
top = insets.top;
|
||||
bottom = insets.bottom;
|
||||
left = insets.left;
|
||||
right = insets.right;
|
||||
|
||||
UIDeviceOrientation orient = [UIDevice currentDevice].orientation;
|
||||
if ( orient == UIDeviceOrientationPortrait) {
|
||||
notchPos = 1;
|
||||
} else if (orient == UIDeviceOrientationPortraitUpsideDown) {
|
||||
notchPos = 2;
|
||||
} else if (orient == UIDeviceOrientationLandscapeLeft) {
|
||||
notchPos = 3;
|
||||
} else if (orient == UIDeviceOrientationLandscapeRight) {
|
||||
notchPos = 4;
|
||||
}
|
||||
}
|
||||
|
||||
//DebugLogC("Safe area insets of UIView: t: %g b: %g l: %g r:%g notch: %d", top, bottom, left, right, notchPos);
|
||||
}
|
||||
else {
|
||||
top = bottom = left = right = 0;
|
||||
//DebugLogC("NOT A UIVIEW");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -19,9 +19,10 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
void getSafeAreaInsets(void * component, float & top, float & bottom, float & left, float & right)
|
||||
void getSafeAreaInsets(void * component, float & top, float & bottom, float & left, float & right, int & notchPos)
|
||||
{
|
||||
top = bottom = left = right = 0;
|
||||
notchPos = 0;
|
||||
}
|
||||
|
||||
|
||||
|
1310
Source/CustomLookAndFeel.cpp
Normal file
1310
Source/CustomLookAndFeel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
139
Source/CustomLookAndFeel.h
Normal file
139
Source/CustomLookAndFeel.h
Normal file
@ -0,0 +1,139 @@
|
||||
// SPDX-License-Identifier: GPLv3-or-later WITH Appstore-exception
|
||||
// Copyright (C) 2020 Jesse Chappell
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "JuceHeader.h"
|
||||
|
||||
|
||||
class CustomLookAndFeel : public LookAndFeel_V4
|
||||
{
|
||||
public:
|
||||
CustomLookAndFeel();
|
||||
|
||||
void setLanguageCode(const String & lang);
|
||||
|
||||
//void fillWithBackgroundTexture (Graphics&);
|
||||
//static void fillWithBackgroundTexture (Component&, Graphics&);
|
||||
|
||||
void setLabelCornerRadius(float val) { labelCornerRadius = val; }
|
||||
float getLabelCornerRadius() const { return labelCornerRadius; }
|
||||
|
||||
void drawTabButton (TabBarButton& button, Graphics&, bool isMouseOver, bool isMouseDown) override;
|
||||
void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) override;
|
||||
void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int, int) override;
|
||||
void drawTabButtonText (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown) override;
|
||||
int getTabButtonSpaceAroundImage() override;
|
||||
|
||||
int getTabButtonBestWidth (TabBarButton&, int tabDepth) override;
|
||||
juce::Rectangle<int> getTabButtonExtraComponentBounds (const TabBarButton&, juce::Rectangle<int>& textArea, Component& extraComp) override;
|
||||
|
||||
void createTabTextLayout (const TabBarButton& button, float length, float depth,
|
||||
Colour colour, TextLayout& textLayout);
|
||||
|
||||
//Typeface::Ptr getTypefaceForFont (const Font& font) override;
|
||||
|
||||
Font getMenuBarFont (MenuBarComponent& menuBar, int /*itemIndex*/, const String& /*itemText*/) override;
|
||||
|
||||
|
||||
Button* createSliderButton (Slider&, const bool isIncrement) override;
|
||||
Label* createSliderTextBox (Slider&) override;
|
||||
|
||||
Font getTextButtonFont (TextButton&, int buttonHeight) override;
|
||||
void drawButtonText (Graphics& g, TextButton& button, bool /*isMouseOverButton*/, bool /*isButtonDown*/) override;
|
||||
|
||||
void drawButtonTextWithAlignment (Graphics& g, TextButton& button, bool /*isMouseOverButton*/, bool /*isButtonDown*/, Justification textjust = Justification::centred) ;
|
||||
|
||||
void drawBubble (Graphics&, BubbleComponent&, const Point<float>& tip, const juce::Rectangle<float>& body) override;
|
||||
|
||||
|
||||
void drawFileBrowserRow (Graphics&, int width, int height,
|
||||
const File& file, const String& filename, Image* icon,
|
||||
const String& fileSizeDescription, const String& fileTimeDescription,
|
||||
bool isDirectory, bool isItemSelected, int itemIndex,
|
||||
DirectoryContentsDisplayComponent&) override;
|
||||
|
||||
Button* createFileBrowserGoUpButton() override;
|
||||
|
||||
void layoutFileBrowserComponent (FileBrowserComponent&,
|
||||
DirectoryContentsDisplayComponent*,
|
||||
FilePreviewComponent*,
|
||||
ComboBox* currentPathBox,
|
||||
TextEditor* filenameBox,
|
||||
Button* goUpButton) override;
|
||||
|
||||
|
||||
void drawTreeviewPlusMinusBox (Graphics& g, const juce::Rectangle<float>& area,
|
||||
Colour backgroundColour, bool isOpen, bool isMouseOver) override;
|
||||
|
||||
|
||||
void drawToggleButton (Graphics& g, ToggleButton& button,
|
||||
bool isMouseOverButton, bool isButtonDown) override;
|
||||
|
||||
|
||||
void drawTickBox (Graphics&, Component&,
|
||||
float x, float y, float w, float h,
|
||||
bool ticked, bool isEnabled, bool isMouseOverButton, bool isButtonDown) override;
|
||||
|
||||
void drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
|
||||
const float rotaryStartAngle, const float rotaryEndAngle, Slider& slider) override;
|
||||
|
||||
void drawLinearSlider (Graphics&, int x, int y, int width, int height,
|
||||
float sliderPos, float minSliderPos, float maxSliderPos,
|
||||
const Slider::SliderStyle, Slider&) override;
|
||||
|
||||
Font getSliderPopupFont (Slider&) override;
|
||||
int getSliderPopupPlacement (Slider&) override;
|
||||
|
||||
int getSliderThumbRadius (Slider& slider) override;
|
||||
|
||||
Slider::SliderLayout getSliderLayout (Slider& slider) override;
|
||||
|
||||
void drawDrawableButton (Graphics& g, DrawableButton& button,
|
||||
bool /*isMouseOverButton*/, bool /*isButtonDown*/) override;
|
||||
|
||||
|
||||
void drawCallOutBoxBackground (CallOutBox& box, Graphics& g,
|
||||
const Path& path, Image& cachedImage) override;
|
||||
|
||||
Font getLabelFont (Label& label) override;
|
||||
void drawLabel (Graphics& g, Label& label) override;
|
||||
|
||||
PopupMenu::Options getOptionsForComboBoxPopupMenu (ComboBox&, Label&) override;
|
||||
|
||||
Justification sliderTextJustification = Justification::centred;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
Font myFont;
|
||||
float fontScale;
|
||||
|
||||
float labelCornerRadius = 4.0f;
|
||||
String languageCode;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
};
|
||||
|
||||
class CustomBigTextLookAndFeel : public CustomLookAndFeel
|
||||
{
|
||||
public:
|
||||
CustomBigTextLookAndFeel(float maxTextSize=32.0f);
|
||||
|
||||
Font getTextButtonFont (TextButton&, int buttonHeight) override;
|
||||
Button* createSliderButton (Slider&, const bool isIncrement) override;
|
||||
Label* createSliderTextBox (Slider&) override;
|
||||
|
||||
void drawToggleButton (Graphics& g, ToggleButton& button,
|
||||
bool isMouseOverButton, bool isButtonDown) override;
|
||||
|
||||
Justification textJustification = Justification::centred;
|
||||
|
||||
protected:
|
||||
float maxSize;
|
||||
|
||||
};
|
||||
|
219
Source/CustomStandaloneFilterApp.cpp
Normal file
219
Source/CustomStandaloneFilterApp.cpp
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
|
||||
#include "juce_core/system/juce_TargetPlatform.h"
|
||||
#include "juce_audio_plugin_client/utility/juce_CheckSettingMacros.h"
|
||||
|
||||
#include "juce_audio_plugin_client/utility/juce_IncludeSystemHeaders.h"
|
||||
#include "juce_audio_plugin_client/utility/juce_IncludeModuleHeaders.h"
|
||||
#include "juce_audio_plugin_client/utility/juce_FakeMouseMoveGenerator.h"
|
||||
#include "juce_audio_plugin_client/utility/juce_WindowsHooks.h"
|
||||
|
||||
#include <juce_audio_devices/juce_audio_devices.h>
|
||||
#include <juce_gui_extra/juce_gui_extra.h>
|
||||
#include <juce_audio_utils/juce_audio_utils.h>
|
||||
|
||||
// #include "DebugLogC.h"
|
||||
|
||||
// You can set this flag in your build if you need to specify a different
|
||||
// standalone JUCEApplication class for your app to use. If you don't
|
||||
// set it then by default we'll just create a simple one as below.
|
||||
//#if ! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP
|
||||
|
||||
extern juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter();
|
||||
|
||||
#include "CustomStandaloneFilterWindow.h"
|
||||
#include "CustomLookAndFeel.h"
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class StandaloneFilterApp : public JUCEApplication
|
||||
{
|
||||
public:
|
||||
StandaloneFilterApp()
|
||||
{
|
||||
PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_Standalone;
|
||||
|
||||
PropertiesFile::Options options;
|
||||
|
||||
options.applicationName = getApplicationName();
|
||||
options.filenameSuffix = ".settings";
|
||||
options.osxLibrarySubFolder = "Application Support/" + getApplicationName();
|
||||
#if JUCE_LINUX
|
||||
options.folderName = "~/.config/paulxstretch";
|
||||
#else
|
||||
options.folderName = "";
|
||||
#endif
|
||||
|
||||
appProperties.setStorageParameters (options);
|
||||
|
||||
LookAndFeel::setDefaultLookAndFeel(&sonoLNF);
|
||||
|
||||
}
|
||||
|
||||
const String getApplicationName() override { return JucePlugin_Name; }
|
||||
const String getApplicationVersion() override { return JucePlugin_VersionString; }
|
||||
bool moreThanOneInstanceAllowed() override { return true; }
|
||||
void anotherInstanceStarted (const String&) override {}
|
||||
|
||||
CustomLookAndFeel sonoLNF;
|
||||
|
||||
|
||||
virtual StandaloneFilterWindow* createWindow()
|
||||
{
|
||||
#ifdef JucePlugin_PreferredChannelConfigurations
|
||||
StandalonePluginHolder::PluginInOuts channels[] = { JucePlugin_PreferredChannelConfigurations };
|
||||
#endif
|
||||
|
||||
AudioDeviceManager::AudioDeviceSetup setupOptions;
|
||||
setupOptions.bufferSize = 512;
|
||||
|
||||
return new StandaloneFilterWindow (getApplicationName(),
|
||||
LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId),
|
||||
appProperties.getUserSettings(),
|
||||
false, {}, &setupOptions
|
||||
#ifdef JucePlugin_PreferredChannelConfigurations
|
||||
, juce::Array<StandalonePluginHolder::PluginInOuts> (channels, juce::numElementsInArray (channels))
|
||||
#else
|
||||
, {}
|
||||
#endif
|
||||
#if JUCE_DONT_AUTO_OPEN_MIDI_DEVICES_ON_MOBILE
|
||||
, false
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const String&) override
|
||||
{
|
||||
mainWindow.reset (createWindow());
|
||||
|
||||
#if JUCE_STANDALONE_FILTER_WINDOW_USE_KIOSK_MODE
|
||||
Desktop::getInstance().setKioskModeComponent (mainWindow.get(), false);
|
||||
#endif
|
||||
|
||||
mainWindow->setVisible (true);
|
||||
|
||||
Desktop::getInstance().setScreenSaverEnabled(false);
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
DBG("shutdown");
|
||||
if (mainWindow.get() != nullptr)
|
||||
mainWindow->pluginHolder->savePluginState();
|
||||
|
||||
mainWindow = nullptr;
|
||||
appProperties.saveIfNeeded();
|
||||
}
|
||||
|
||||
void suspended() override
|
||||
{
|
||||
DBG("suspended");
|
||||
if (mainWindow.get() != nullptr)
|
||||
mainWindow->pluginHolder->savePluginState();
|
||||
|
||||
appProperties.saveIfNeeded();
|
||||
|
||||
Desktop::getInstance().setScreenSaverEnabled(true);
|
||||
}
|
||||
|
||||
void resumed() override
|
||||
{
|
||||
Desktop::getInstance().setScreenSaverEnabled(false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
DBG("Requested quit");
|
||||
if (mainWindow.get() != nullptr)
|
||||
mainWindow->pluginHolder->savePluginState();
|
||||
|
||||
appProperties.saveIfNeeded();
|
||||
|
||||
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
|
||||
{
|
||||
Timer::callAfterDelay (100, []()
|
||||
{
|
||||
if (auto app = JUCEApplicationBase::getInstance())
|
||||
app->systemRequestedQuit();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
quit();
|
||||
}
|
||||
}
|
||||
|
||||
void memoryWarningReceived() override
|
||||
{
|
||||
DBG("Memory warning");
|
||||
}
|
||||
|
||||
protected:
|
||||
ApplicationProperties appProperties;
|
||||
std::unique_ptr<StandaloneFilterWindow> mainWindow;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
||||
#if JucePlugin_Build_Standalone && JUCE_IOS
|
||||
|
||||
using namespace juce;
|
||||
|
||||
bool JUCE_CALLTYPE juce_isInterAppAudioConnected()
|
||||
{
|
||||
if (auto holder = StandalonePluginHolder::getInstance())
|
||||
return holder->isInterAppAudioConnected();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE juce_switchToHostApplication()
|
||||
{
|
||||
if (auto holder = StandalonePluginHolder::getInstance())
|
||||
holder->switchToHostApplication();
|
||||
}
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_basics
|
||||
Image JUCE_CALLTYPE juce_getIAAHostIcon (int size)
|
||||
{
|
||||
if (auto holder = StandalonePluginHolder::getInstance())
|
||||
return holder->getIAAHostIcon (size);
|
||||
|
||||
return Image();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
START_JUCE_APPLICATION (StandaloneFilterApp);
|
||||
|
||||
//#endif
|
982
Source/CustomStandaloneFilterWindow.h
Normal file
982
Source/CustomStandaloneFilterWindow.h
Normal file
@ -0,0 +1,982 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
//#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
|
||||
//extern juce::AudioProcessor* JUCE_API JUCE_CALLTYPE createPluginFilterOfType (juce::AudioProcessor::WrapperType type);
|
||||
//#endif
|
||||
|
||||
#include "CrossPlatformUtils.h"
|
||||
|
||||
#include "PluginEditor.h"
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An object that creates and plays a standalone instance of an AudioProcessor.
|
||||
|
||||
The object will create your processor using the same createPluginFilter()
|
||||
function that the other plugin wrappers use, and will run it through the
|
||||
computer's audio/MIDI devices using AudioDeviceManager and AudioProcessorPlayer.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class StandalonePluginHolder : private AudioIODeviceCallback,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Structure used for the number of inputs and outputs. */
|
||||
struct PluginInOuts { short numIns, numOuts; };
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an instance of the default plugin.
|
||||
|
||||
The settings object can be a PropertySet that the class should use to store its
|
||||
settings - the takeOwnershipOfSettings indicates whether this object will delete
|
||||
the settings automatically when no longer needed. The settings can also be nullptr.
|
||||
|
||||
A default device name can be passed in.
|
||||
|
||||
Preferably a complete setup options object can be used, which takes precedence over
|
||||
the preferredDefaultDeviceName and allows you to select the input & output device names,
|
||||
sample rate, buffer size etc.
|
||||
|
||||
In all instances, the settingsToUse will take precedence over the "preferred" options if not null.
|
||||
*/
|
||||
StandalonePluginHolder (PropertySet* settingsToUse,
|
||||
bool takeOwnershipOfSettings = true,
|
||||
const String& preferredDefaultDeviceName = String(),
|
||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr,
|
||||
const Array<PluginInOuts>& channels = Array<PluginInOuts>(),
|
||||
#if JUCE_ANDROID || JUCE_IOS
|
||||
bool shouldAutoOpenMidiDevices = false
|
||||
#else
|
||||
bool shouldAutoOpenMidiDevices = false
|
||||
#endif
|
||||
)
|
||||
|
||||
: settings (settingsToUse, takeOwnershipOfSettings),
|
||||
channelConfiguration (channels),
|
||||
shouldMuteInput (! isInterAppAudioConnected()),
|
||||
autoOpenMidiDevices (shouldAutoOpenMidiDevices)
|
||||
{
|
||||
createPlugin();
|
||||
|
||||
auto inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
|
||||
: processor->getMainBusNumInputChannels());
|
||||
|
||||
if (preferredSetupOptions != nullptr)
|
||||
options.reset (new AudioDeviceManager::AudioDeviceSetup (*preferredSetupOptions));
|
||||
|
||||
auto audioInputRequired = (inChannels > 0);
|
||||
|
||||
if (audioInputRequired && RuntimePermissions::isRequired (RuntimePermissions::recordAudio)
|
||||
&& ! RuntimePermissions::isGranted (RuntimePermissions::recordAudio))
|
||||
RuntimePermissions::request (RuntimePermissions::recordAudio,
|
||||
[this, preferredDefaultDeviceName] (bool granted) { init (granted, preferredDefaultDeviceName); });
|
||||
else
|
||||
init (audioInputRequired, preferredDefaultDeviceName);
|
||||
}
|
||||
|
||||
void init (bool enableAudioInput, const String& preferredDefaultDeviceName)
|
||||
{
|
||||
setupAudioDevices (enableAudioInput, preferredDefaultDeviceName, options.get());
|
||||
reloadPluginState();
|
||||
startPlaying();
|
||||
|
||||
if (autoOpenMidiDevices)
|
||||
startTimer (500);
|
||||
}
|
||||
|
||||
virtual ~StandalonePluginHolder()
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
deletePlugin();
|
||||
shutDownAudioDevices();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
virtual void createPlugin()
|
||||
{
|
||||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
|
||||
processor.reset (::createPluginFilterOfType (AudioProcessor::wrapperType_Standalone));
|
||||
#else
|
||||
AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Standalone);
|
||||
processor.reset (createPluginFilter());
|
||||
AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Undefined);
|
||||
#endif
|
||||
jassert (processor != nullptr); // Your createPluginFilter() function must return a valid object!
|
||||
|
||||
processor->disableNonMainBuses();
|
||||
processor->setRateAndBufferSizeDetails (44100, 512);
|
||||
|
||||
int inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
|
||||
: processor->getMainBusNumInputChannels());
|
||||
|
||||
int outChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numOuts
|
||||
: processor->getMainBusNumOutputChannels());
|
||||
|
||||
processorHasPotentialFeedbackLoop = (inChannels > 0 && outChannels > 0);
|
||||
}
|
||||
|
||||
virtual void deletePlugin()
|
||||
{
|
||||
stopPlaying();
|
||||
processor = nullptr;
|
||||
}
|
||||
|
||||
static String getFilePatterns (const String& fileSuffix)
|
||||
{
|
||||
if (fileSuffix.isEmpty())
|
||||
return {};
|
||||
|
||||
return (fileSuffix.startsWithChar ('.') ? "*" : "*.") + fileSuffix;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Value& getMuteInputValue() { return shouldMuteInput; }
|
||||
bool getProcessorHasPotentialFeedbackLoop() const { return processorHasPotentialFeedbackLoop; }
|
||||
|
||||
//==============================================================================
|
||||
File getLastFile() const
|
||||
{
|
||||
File f;
|
||||
|
||||
if (settings != nullptr)
|
||||
f = File (settings->getValue ("lastStateFile"));
|
||||
|
||||
if (f == File())
|
||||
f = File::getSpecialLocation (File::userDocumentsDirectory);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void setLastFile (const FileChooser& fc)
|
||||
{
|
||||
if (settings != nullptr)
|
||||
settings->setValue ("lastStateFile", fc.getResult().getFullPathName());
|
||||
}
|
||||
|
||||
/** Pops up a dialog letting the user save the processor's state to a file. */
|
||||
void askUserToSaveState (const String& fileSuffix = String())
|
||||
{
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
FileChooser fc (TRANS("Save current state"), getLastFile(), getFilePatterns (fileSuffix));
|
||||
|
||||
if (fc.browseForFileToSave (true))
|
||||
{
|
||||
setLastFile (fc);
|
||||
|
||||
MemoryBlock data;
|
||||
processor->getStateInformation (data);
|
||||
|
||||
if (! fc.getResult().replaceWithData (data.getData(), data.getSize()))
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
TRANS("Error whilst saving"),
|
||||
TRANS("Couldn't write to the specified file!"));
|
||||
}
|
||||
#else
|
||||
ignoreUnused (fileSuffix);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Pops up a dialog letting the user re-load the processor's state from a file. */
|
||||
void askUserToLoadState (const String& fileSuffix = String())
|
||||
{
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
FileChooser fc (TRANS("Load a saved state"), getLastFile(), getFilePatterns (fileSuffix));
|
||||
|
||||
if (fc.browseForFileToOpen())
|
||||
{
|
||||
setLastFile (fc);
|
||||
|
||||
MemoryBlock data;
|
||||
|
||||
if (fc.getResult().loadFileAsData (data))
|
||||
processor->setStateInformation (data.getData(), (int) data.getSize());
|
||||
else
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
TRANS("Error whilst loading"),
|
||||
TRANS("Couldn't read from the specified file!"));
|
||||
}
|
||||
#else
|
||||
ignoreUnused (fileSuffix);
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void startPlaying()
|
||||
{
|
||||
player.setProcessor (processor.get());
|
||||
|
||||
#if JucePlugin_Enable_IAA && JUCE_IOS
|
||||
if (auto device = dynamic_cast<iOSAudioIODevice*> (deviceManager.getCurrentAudioDevice()))
|
||||
{
|
||||
processor->setPlayHead (device->getAudioPlayHead());
|
||||
device->setMidiMessageCollector (&player.getMidiMessageCollector());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void stopPlaying()
|
||||
{
|
||||
player.setProcessor (nullptr);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Shows an audio properties dialog box modally. */
|
||||
void showAudioSettingsDialog(Component * calloutTarget=nullptr, Component * calloutParent=nullptr)
|
||||
{
|
||||
|
||||
int minNumInputs = std::numeric_limits<int>::max(), maxNumInputs = 0,
|
||||
minNumOutputs = std::numeric_limits<int>::max(), maxNumOutputs = 0;
|
||||
|
||||
auto updateMinAndMax = [] (int newValue, int& minValue, int& maxValue)
|
||||
{
|
||||
minValue = jmin (minValue, newValue);
|
||||
maxValue = jmax (maxValue, newValue);
|
||||
};
|
||||
|
||||
if (channelConfiguration.size() > 0)
|
||||
{
|
||||
auto defaultConfig = channelConfiguration.getReference (0);
|
||||
updateMinAndMax ((int) defaultConfig.numIns, minNumInputs, maxNumInputs);
|
||||
updateMinAndMax ((int) defaultConfig.numOuts, minNumOutputs, maxNumOutputs);
|
||||
}
|
||||
|
||||
if (auto* bus = processor->getBus (true, 0))
|
||||
updateMinAndMax (bus->getDefaultLayout().size(), minNumInputs, maxNumInputs);
|
||||
|
||||
if (auto* bus = processor->getBus (false, 0))
|
||||
updateMinAndMax (bus->getDefaultLayout().size(), minNumOutputs, maxNumOutputs);
|
||||
|
||||
minNumInputs = jmin (minNumInputs, maxNumInputs);
|
||||
minNumOutputs = jmin (minNumOutputs, maxNumOutputs);
|
||||
|
||||
auto * content = new SettingsComponent (*this, deviceManager,
|
||||
minNumInputs,
|
||||
maxNumInputs,
|
||||
minNumOutputs,
|
||||
maxNumOutputs);
|
||||
if (calloutTarget && calloutParent) {
|
||||
|
||||
auto wrap = std::make_unique<Viewport>();
|
||||
wrap->setViewedComponent(content, true); // takes ownership of content
|
||||
|
||||
//std::unique_ptr<SettingsComponent> contptr(content);
|
||||
int defWidth = 450;
|
||||
int defHeight = 550;
|
||||
|
||||
#if JUCE_IOS
|
||||
defWidth = 320;
|
||||
defHeight = 400;
|
||||
#endif
|
||||
|
||||
content->setSize (defWidth, defHeight);
|
||||
wrap->setSize(jmin(defWidth, calloutParent->getWidth() - 20), jmin(defHeight, calloutParent->getHeight() - 24));
|
||||
|
||||
auto bounds = calloutParent->getLocalArea(nullptr, calloutTarget->getScreenBounds());
|
||||
CallOutBox::launchAsynchronously(std::move(wrap), bounds, calloutParent);
|
||||
}
|
||||
else {
|
||||
DialogWindow::LaunchOptions o;
|
||||
|
||||
o.content.setOwned (content);
|
||||
o.content->setSize (500, 550);
|
||||
|
||||
o.dialogTitle = TRANS("Audio/MIDI Settings");
|
||||
o.dialogBackgroundColour = o.content->getLookAndFeel().findColour (ResizableWindow::backgroundColourId);
|
||||
o.escapeKeyTriggersCloseButton = true;
|
||||
o.useNativeTitleBar = true;
|
||||
o.resizable = false;
|
||||
|
||||
o.launchAsync();
|
||||
}
|
||||
}
|
||||
|
||||
void saveAudioDeviceState()
|
||||
{
|
||||
if (settings != nullptr)
|
||||
{
|
||||
std::unique_ptr<XmlElement> xml (deviceManager.createStateXml());
|
||||
|
||||
settings->setValue ("audioSetup", xml.get());
|
||||
|
||||
#if ! (JUCE_IOS || JUCE_ANDROID)
|
||||
settings->setValue ("shouldMuteInput", (bool) shouldMuteInput.getValue());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void reloadAudioDeviceState (bool enableAudioInput,
|
||||
const String& preferredDefaultDeviceName,
|
||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
|
||||
{
|
||||
std::unique_ptr<XmlElement> savedState;
|
||||
|
||||
if (settings != nullptr)
|
||||
{
|
||||
savedState = settings->getXmlValue ("audioSetup");
|
||||
|
||||
#if ! (JUCE_IOS || JUCE_ANDROID)
|
||||
shouldMuteInput.setValue (settings->getBoolValue ("shouldMuteInput", true));
|
||||
#endif
|
||||
}
|
||||
|
||||
auto totalInChannels = processor->getMainBusNumInputChannels();
|
||||
auto totalOutChannels = processor->getMainBusNumOutputChannels();
|
||||
|
||||
if (channelConfiguration.size() > 0)
|
||||
{
|
||||
auto defaultConfig = channelConfiguration.getReference (0);
|
||||
totalInChannels = defaultConfig.numIns;
|
||||
totalOutChannels = defaultConfig.numOuts;
|
||||
}
|
||||
|
||||
deviceManager.initialise (enableAudioInput ? totalInChannels : 0,
|
||||
totalOutChannels,
|
||||
savedState.get(),
|
||||
true,
|
||||
preferredDefaultDeviceName,
|
||||
preferredSetupOptions);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void savePluginState()
|
||||
{
|
||||
if (settings != nullptr && processor != nullptr)
|
||||
{
|
||||
MemoryBlock data;
|
||||
processor->getStateInformation (data);
|
||||
|
||||
settings->setValue ("filterState", data.toBase64Encoding());
|
||||
}
|
||||
}
|
||||
|
||||
void reloadPluginState()
|
||||
{
|
||||
if (settings != nullptr)
|
||||
{
|
||||
MemoryBlock data;
|
||||
|
||||
if (data.fromBase64Encoding (settings->getValue ("filterState")) && data.getSize() > 0)
|
||||
processor->setStateInformation (data.getData(), (int) data.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void switchToHostApplication()
|
||||
{
|
||||
#if JUCE_IOS
|
||||
if (auto device = dynamic_cast<iOSAudioIODevice*> (deviceManager.getCurrentAudioDevice()))
|
||||
device->switchApplication();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isInterAppAudioConnected()
|
||||
{
|
||||
#if JUCE_IOS
|
||||
if (auto device = dynamic_cast<iOSAudioIODevice*> (deviceManager.getCurrentAudioDevice()))
|
||||
return device->isInterAppAudioConnected();
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_basics
|
||||
Image getIAAHostIcon (int size)
|
||||
{
|
||||
#if JUCE_IOS && JucePlugin_Enable_IAA
|
||||
if (auto device = dynamic_cast<iOSAudioIODevice*> (deviceManager.getCurrentAudioDevice()))
|
||||
return device->getIcon (size);
|
||||
#else
|
||||
ignoreUnused (size);
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
static StandalonePluginHolder* getInstance();
|
||||
|
||||
//==============================================================================
|
||||
OptionalScopedPointer<PropertySet> settings;
|
||||
std::unique_ptr<AudioProcessor> processor;
|
||||
AudioDeviceManager deviceManager;
|
||||
AudioProcessorPlayer player;
|
||||
Array<PluginInOuts> channelConfiguration;
|
||||
|
||||
// avoid feedback loop by default
|
||||
bool processorHasPotentialFeedbackLoop = true;
|
||||
Value shouldMuteInput;
|
||||
AudioBuffer<float> emptyBuffer;
|
||||
bool autoOpenMidiDevices = false;
|
||||
|
||||
std::unique_ptr<AudioDeviceManager::AudioDeviceSetup> options;
|
||||
Array<MidiDeviceInfo> lastMidiDevices;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class SettingsComponent : public Component
|
||||
{
|
||||
public:
|
||||
SettingsComponent (StandalonePluginHolder& pluginHolder,
|
||||
AudioDeviceManager& deviceManagerToUse,
|
||||
int minAudioInputChannels,
|
||||
int maxAudioInputChannels,
|
||||
int minAudioOutputChannels,
|
||||
int maxAudioOutputChannels)
|
||||
: owner (pluginHolder),
|
||||
deviceSelector (deviceManagerToUse,
|
||||
minAudioInputChannels, maxAudioInputChannels,
|
||||
minAudioOutputChannels, maxAudioOutputChannels,
|
||||
true,
|
||||
(pluginHolder.processor.get() != nullptr && pluginHolder.processor->producesMidi()),
|
||||
true, false),
|
||||
shouldMuteLabel ("Feedback Loop:", "Feedback Loop:"),
|
||||
shouldMuteButton ("Mute audio input")
|
||||
{
|
||||
setOpaque (true);
|
||||
|
||||
shouldMuteButton.setClickingTogglesState (true);
|
||||
shouldMuteButton.getToggleStateValue().referTo (owner.shouldMuteInput);
|
||||
|
||||
addAndMakeVisible (deviceSelector);
|
||||
|
||||
if (owner.getProcessorHasPotentialFeedbackLoop())
|
||||
{
|
||||
addAndMakeVisible (shouldMuteButton);
|
||||
addAndMakeVisible (shouldMuteLabel);
|
||||
|
||||
shouldMuteLabel.attachToComponent (&shouldMuteButton, true);
|
||||
}
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
|
||||
if (owner.getProcessorHasPotentialFeedbackLoop())
|
||||
{
|
||||
auto itemHeight = deviceSelector.getItemHeight();
|
||||
auto extra = r.removeFromTop (itemHeight);
|
||||
|
||||
auto seperatorHeight = (itemHeight >> 1);
|
||||
shouldMuteButton.setBounds (Rectangle<int> (extra.proportionOfWidth (0.35f), seperatorHeight,
|
||||
extra.proportionOfWidth (0.60f), deviceSelector.getItemHeight()));
|
||||
|
||||
r.removeFromTop (seperatorHeight);
|
||||
}
|
||||
|
||||
deviceSelector.setBounds (r);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
StandalonePluginHolder& owner;
|
||||
AudioDeviceSelectorComponent deviceSelector;
|
||||
Label shouldMuteLabel;
|
||||
ToggleButton shouldMuteButton;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples) override
|
||||
{
|
||||
const bool inputMuted = shouldMuteInput.getValue();
|
||||
|
||||
if (inputMuted)
|
||||
{
|
||||
emptyBuffer.clear();
|
||||
inputChannelData = emptyBuffer.getArrayOfReadPointers();
|
||||
}
|
||||
|
||||
player.audioDeviceIOCallback (inputChannelData, numInputChannels,
|
||||
outputChannelData, numOutputChannels, numSamples);
|
||||
}
|
||||
|
||||
void audioDeviceAboutToStart (AudioIODevice* device) override
|
||||
{
|
||||
emptyBuffer.setSize (device->getActiveInputChannels().countNumberOfSetBits(), device->getCurrentBufferSizeSamples());
|
||||
emptyBuffer.clear();
|
||||
|
||||
player.audioDeviceAboutToStart (device);
|
||||
player.setMidiOutput (deviceManager.getDefaultMidiOutput());
|
||||
|
||||
#if JUCE_IOS
|
||||
if (auto iosdevice = dynamic_cast<iOSAudioIODevice*> (deviceManager.getCurrentAudioDevice())) {
|
||||
processorHasPotentialFeedbackLoop = !iosdevice->isHeadphonesConnected() && device->getActiveInputChannels() > 0;
|
||||
shouldMuteInput.setValue(processorHasPotentialFeedbackLoop);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void audioDeviceStopped() override
|
||||
{
|
||||
player.setMidiOutput (nullptr);
|
||||
player.audioDeviceStopped();
|
||||
emptyBuffer.setSize (0, 0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setupAudioDevices (bool enableAudioInput,
|
||||
const String& preferredDefaultDeviceName,
|
||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
|
||||
{
|
||||
deviceManager.addAudioCallback (this);
|
||||
deviceManager.addMidiInputDeviceCallback ({}, &player);
|
||||
|
||||
reloadAudioDeviceState (enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
|
||||
}
|
||||
|
||||
void shutDownAudioDevices()
|
||||
{
|
||||
saveAudioDeviceState();
|
||||
|
||||
deviceManager.removeMidiInputDeviceCallback ({}, &player);
|
||||
deviceManager.removeAudioCallback (this);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
auto newMidiDevices = MidiInput::getAvailableDevices();
|
||||
|
||||
if (newMidiDevices != lastMidiDevices)
|
||||
{
|
||||
for (auto& oldDevice : lastMidiDevices)
|
||||
if (! newMidiDevices.contains (oldDevice))
|
||||
deviceManager.setMidiInputDeviceEnabled (oldDevice.identifier, false);
|
||||
|
||||
for (auto& newDevice : newMidiDevices)
|
||||
if (! lastMidiDevices.contains (newDevice))
|
||||
deviceManager.setMidiInputDeviceEnabled (newDevice.identifier, true);
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandalonePluginHolder)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class that can be used to run a simple standalone application containing your filter.
|
||||
|
||||
Just create one of these objects in your JUCEApplicationBase::initialise() method, and
|
||||
let it do its work. It will create your filter object using the same createPluginFilter() function
|
||||
that the other plugin wrappers use.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class StandaloneFilterWindow : public DocumentWindow,
|
||||
public Button::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
typedef StandalonePluginHolder::PluginInOuts PluginInOuts;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a window with a given title and colour.
|
||||
The settings object can be a PropertySet that the class should use to
|
||||
store its settings (it can also be null). If takeOwnershipOfSettings is
|
||||
true, then the settings object will be owned and deleted by this object.
|
||||
*/
|
||||
StandaloneFilterWindow (const String& title,
|
||||
Colour backgroundColour,
|
||||
PropertySet* settingsToUse,
|
||||
bool takeOwnershipOfSettings,
|
||||
const String& preferredDefaultDeviceName = String(),
|
||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr,
|
||||
const Array<PluginInOuts>& constrainToConfiguration = {},
|
||||
#if JUCE_ANDROID || JUCE_IOS
|
||||
bool autoOpenMidiDevices = false
|
||||
#else
|
||||
bool autoOpenMidiDevices = false
|
||||
#endif
|
||||
)
|
||||
: DocumentWindow (title, backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
|
||||
optionsButton ("Options")
|
||||
{
|
||||
|
||||
|
||||
#if JUCE_IOS || JUCE_ANDROID
|
||||
setTitleBarHeight (0);
|
||||
#else
|
||||
setTitleBarButtonsRequired (DocumentWindow::minimiseButton | DocumentWindow::closeButton, false);
|
||||
|
||||
Component::addAndMakeVisible (optionsButton);
|
||||
optionsButton.addListener (this);
|
||||
optionsButton.setTriggeredOnMouseDown (true);
|
||||
setUsingNativeTitleBar(true);
|
||||
#endif
|
||||
|
||||
setResizable (true, false);
|
||||
|
||||
pluginHolder.reset (new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings,
|
||||
preferredDefaultDeviceName, preferredSetupOptions,
|
||||
constrainToConfiguration, autoOpenMidiDevices));
|
||||
|
||||
#if JUCE_IOS || JUCE_ANDROID
|
||||
setFullScreen (true);
|
||||
setContentOwned (new MainContentComponent (*this), false);
|
||||
#else
|
||||
setContentOwned (new MainContentComponent (*this), true);
|
||||
|
||||
if (auto* props = pluginHolder->settings.get())
|
||||
{
|
||||
const int x = props->getIntValue ("windowX", -100);
|
||||
const int y = props->getIntValue ("windowY", -100);
|
||||
|
||||
if (x != -100 && y != -100)
|
||||
setBoundsConstrained ({ x, y, getWidth(), getHeight() });
|
||||
else
|
||||
centreWithSize (getWidth(), getHeight());
|
||||
}
|
||||
else
|
||||
{
|
||||
centreWithSize (getWidth(), getHeight());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
~StandaloneFilterWindow()
|
||||
{
|
||||
#if (! JUCE_IOS) && (! JUCE_ANDROID)
|
||||
if (auto* props = pluginHolder->settings.get())
|
||||
{
|
||||
props->setValue ("windowX", getX());
|
||||
props->setValue ("windowY", getY());
|
||||
}
|
||||
#endif
|
||||
|
||||
pluginHolder->stopPlaying();
|
||||
clearContentComponent();
|
||||
pluginHolder = nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
AudioProcessor* getAudioProcessor() const noexcept { return pluginHolder->processor.get(); }
|
||||
AudioDeviceManager& getDeviceManager() const noexcept { return pluginHolder->deviceManager; }
|
||||
|
||||
/** Deletes and re-creates the plugin, resetting it to its default state. */
|
||||
void resetToDefaultState()
|
||||
{
|
||||
pluginHolder->stopPlaying();
|
||||
clearContentComponent();
|
||||
pluginHolder->deletePlugin();
|
||||
|
||||
if (auto* props = pluginHolder->settings.get())
|
||||
props->removeValue ("filterState");
|
||||
|
||||
pluginHolder->createPlugin();
|
||||
setContentOwned (new MainContentComponent (*this), true);
|
||||
pluginHolder->startPlaying();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
pluginHolder->savePluginState();
|
||||
|
||||
JUCEApplicationBase::quit();
|
||||
}
|
||||
|
||||
void buttonClicked (Button*) override
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, TRANS("Audio/MIDI Settings..."));
|
||||
m.addSeparator();
|
||||
m.addItem (2, TRANS("Save current state..."));
|
||||
m.addItem (3, TRANS("Load a saved state..."));
|
||||
m.addSeparator();
|
||||
m.addItem (4, TRANS("Reset to default state"));
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options(),
|
||||
ModalCallbackFunction::forComponent (menuCallback, this));
|
||||
}
|
||||
|
||||
void handleMenuResult (int result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case 1: pluginHolder->showAudioSettingsDialog(); break;
|
||||
case 2: pluginHolder->askUserToSaveState(); break;
|
||||
case 3: pluginHolder->askUserToLoadState(); break;
|
||||
case 4: resetToDefaultState(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
static void menuCallback (int result, StandaloneFilterWindow* button)
|
||||
{
|
||||
if (button != nullptr && result != 0)
|
||||
button->handleMenuResult (result);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
DocumentWindow::resized();
|
||||
optionsButton.setBounds (8, 6, 60, getTitleBarHeight() - 8);
|
||||
}
|
||||
|
||||
virtual StandalonePluginHolder* getPluginHolder() { return pluginHolder.get(); }
|
||||
|
||||
std::unique_ptr<StandalonePluginHolder> pluginHolder;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class MainContentComponent : public Component,
|
||||
private Value::Listener,
|
||||
private Button::Listener,
|
||||
private ComponentListener
|
||||
{
|
||||
public:
|
||||
MainContentComponent (StandaloneFilterWindow& filterWindow)
|
||||
: owner (filterWindow), notification (this),
|
||||
editor (owner.getAudioProcessor()->createEditorIfNeeded())
|
||||
{
|
||||
// because the plugin editor may have changed this
|
||||
//filterWindow.setBackgroundColour(LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
||||
filterWindow.setBackgroundColour(Colours::black);
|
||||
|
||||
Value& inputMutedValue = owner.pluginHolder->getMuteInputValue();
|
||||
|
||||
if (editor != nullptr)
|
||||
{
|
||||
// hack to allow editor to get devicemanager
|
||||
if (auto * sonoeditor = dynamic_cast<PaulstretchpluginAudioProcessorEditor*>(editor.get())) {
|
||||
sonoeditor->getAudioDeviceManager = [this]() { return &owner.getDeviceManager(); };
|
||||
sonoeditor->showAudioSettingsDialog = [this](Component* calloutTarget, Component* calloutParent) { owner.pluginHolder->showAudioSettingsDialog(calloutTarget, calloutParent); };
|
||||
}
|
||||
|
||||
editor->addComponentListener (this);
|
||||
componentMovedOrResized (*editor, false, true);
|
||||
|
||||
addAndMakeVisible (editor.get());
|
||||
}
|
||||
|
||||
addChildComponent (notification);
|
||||
|
||||
if (owner.pluginHolder->getProcessorHasPotentialFeedbackLoop())
|
||||
{
|
||||
inputMutedValue.addListener (this);
|
||||
shouldShowNotification = inputMutedValue.getValue();
|
||||
}
|
||||
|
||||
inputMutedChanged (shouldShowNotification);
|
||||
}
|
||||
|
||||
~MainContentComponent()
|
||||
{
|
||||
if (editor != nullptr)
|
||||
{
|
||||
editor->removeComponentListener (this);
|
||||
owner.pluginHolder->processor->editorBeingDeleted (editor.get());
|
||||
editor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
|
||||
bool portrait = getWidth() < getHeight();
|
||||
bool tall = getHeight() > 500;
|
||||
|
||||
float safetop=0.0f, safebottom=0.0f, safeleft=0.0f, saferight=0.0f;
|
||||
int notchPos = 0;
|
||||
getSafeAreaInsets(getWindowHandle(), safetop, safebottom, safeleft, saferight, notchPos);
|
||||
|
||||
if (portrait != isPortrait || isTall != tall || orientation != notchPos) {
|
||||
isPortrait = portrait;
|
||||
isTall = tall;
|
||||
orientation = notchPos;
|
||||
|
||||
// call resized again if on iOS, due to dumb stuff related to safe area insets not being updated
|
||||
#if JUCE_IOS
|
||||
Timer::callAfterDelay(150, [this]() {
|
||||
this->resized();
|
||||
});
|
||||
//return;
|
||||
#endif
|
||||
}
|
||||
|
||||
topInset = safetop;
|
||||
bottomInset = safebottom;
|
||||
leftInset = safeleft * (notchPos == 3 ? 0.75f : 0.5f);
|
||||
rightInset = saferight * (notchPos == 4 ? 0.75f : 0.5f);
|
||||
|
||||
r.removeFromTop(topInset);
|
||||
r.removeFromBottom(bottomInset);
|
||||
r.removeFromLeft(leftInset);
|
||||
r.removeFromRight(rightInset);
|
||||
|
||||
|
||||
if (shouldShowNotification) {
|
||||
notification.setBounds (r.removeFromTop (NotificationArea::height));
|
||||
topInset += NotificationArea::height;
|
||||
}
|
||||
|
||||
editor->setBounds (r);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool isPortrait = false;
|
||||
bool isTall = false;
|
||||
int orientation = 0;
|
||||
|
||||
//==============================================================================
|
||||
class NotificationArea : public Component
|
||||
{
|
||||
public:
|
||||
enum { height = 60 };
|
||||
|
||||
NotificationArea (Button::Listener* settingsButtonListener)
|
||||
: notification ("notification", "Audio input is muted to avoid\nfeedback loop.\nHeadphones recommended!"),
|
||||
#if JUCE_IOS || JUCE_ANDROID
|
||||
settingsButton ("Unmute Input")
|
||||
#else
|
||||
settingsButton ("Settings...")
|
||||
#endif
|
||||
{
|
||||
setOpaque (true);
|
||||
|
||||
notification.setColour (Label::textColourId, Colours::black);
|
||||
|
||||
settingsButton.addListener (settingsButtonListener);
|
||||
|
||||
addAndMakeVisible (notification);
|
||||
addAndMakeVisible (settingsButton);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
|
||||
g.setColour (Colours::darkgoldenrod);
|
||||
g.fillRect (r.removeFromBottom (1));
|
||||
|
||||
g.setColour (Colours::lightgoldenrodyellow);
|
||||
g.fillRect (r);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds().reduced (5);
|
||||
|
||||
settingsButton.setBounds (r.removeFromRight (70));
|
||||
notification.setBounds (r);
|
||||
}
|
||||
private:
|
||||
Label notification;
|
||||
TextButton settingsButton;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void inputMutedChanged (bool newInputMutedValue)
|
||||
{
|
||||
shouldShowNotification = newInputMutedValue;
|
||||
notification.setVisible (shouldShowNotification);
|
||||
|
||||
#if JUCE_IOS || JUCE_ANDROID
|
||||
resized();
|
||||
#else
|
||||
setSize (editor->getWidth(),
|
||||
editor->getHeight()
|
||||
+ (shouldShowNotification ? NotificationArea::height : 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
void valueChanged (Value& value) override { inputMutedChanged (value.getValue()); }
|
||||
void buttonClicked (Button*) override
|
||||
{
|
||||
#if JUCE_IOS || JUCE_ANDROID
|
||||
owner.pluginHolder->getMuteInputValue().setValue (false);
|
||||
#else
|
||||
owner.pluginHolder->showAudioSettingsDialog();
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void componentMovedOrResized (Component&, bool, bool wasResized) override
|
||||
{
|
||||
if (wasResized && editor != nullptr)
|
||||
setSize (editor->getWidth() + leftInset + rightInset,
|
||||
editor->getHeight() + topInset + bottomInset);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StandaloneFilterWindow& owner;
|
||||
NotificationArea notification;
|
||||
std::unique_ptr<AudioProcessorEditor> editor;
|
||||
bool shouldShowNotification = false;
|
||||
|
||||
int topInset = 0;
|
||||
int bottomInset = 0;
|
||||
int leftInset = 0;
|
||||
int rightInset = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
TextButton optionsButton;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandaloneFilterWindow)
|
||||
};
|
||||
|
||||
inline StandalonePluginHolder* StandalonePluginHolder::getInstance()
|
||||
{
|
||||
#if JucePlugin_Enable_IAA || JucePlugin_Build_Standalone
|
||||
if (PluginHostType::getPluginLoadedAs() == AudioProcessor::wrapperType_Standalone)
|
||||
{
|
||||
auto& desktop = Desktop::getInstance();
|
||||
const int numTopLevelWindows = desktop.getNumComponents();
|
||||
|
||||
for (int i = 0; i < numTopLevelWindows; ++i)
|
||||
if (auto window = dynamic_cast<StandaloneFilterWindow*> (desktop.getComponent (i)))
|
||||
return window->getPluginHolder();
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace juce
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,9 @@ www.gnu.org/licenses
|
||||
#include "PluginProcessor.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "envelope_component.h"
|
||||
#include "CustomLookAndFeel.h"
|
||||
|
||||
class zoom_scrollbar : public Component
|
||||
{
|
||||
@ -103,6 +105,50 @@ private:
|
||||
Colour m_labeldefcolor;
|
||||
};
|
||||
|
||||
class ParameterGroupComponent : public Component
|
||||
{
|
||||
public:
|
||||
ParameterGroupComponent(const String & name, int groupid, PaulstretchpluginAudioProcessor* proc, bool showtoggle=true);
|
||||
|
||||
void resized() override;
|
||||
void paint(Graphics &g) override;
|
||||
|
||||
//void addParameterComponent(std::unique_ptr<ParameterComponent> pcomp);
|
||||
void addParameterComponent(ParameterComponent * pcomp);
|
||||
|
||||
void updateParameterComponents();
|
||||
|
||||
void setBackgroundColor(Colour col) { m_bgcolor = col; }
|
||||
Colour getBackgroundColor() const { return m_bgcolor; }
|
||||
|
||||
String name;
|
||||
int groupId = -1;
|
||||
|
||||
int getMinimumHeight(int forWidth);
|
||||
|
||||
std::function<void(void)> EnabledChangedCallback;
|
||||
|
||||
private:
|
||||
int doLayout(Rectangle<int> bounds); // returns min height
|
||||
|
||||
//uptrvec<ParameterComponent> m_parcomps;
|
||||
std::vector<ParameterComponent*> m_parcomps;
|
||||
std::unique_ptr<Label> m_namelabel;
|
||||
//std::unique_ptr<DrawableButton> m_enableButton;
|
||||
std::unique_ptr<ToggleButton> m_enableButton;
|
||||
|
||||
CriticalSection* m_cs = nullptr;
|
||||
PaulstretchpluginAudioProcessor* m_proc = nullptr;
|
||||
int m_slidwidth = 400;
|
||||
|
||||
Colour m_bgcolor;
|
||||
|
||||
int m_minHeight = 0;
|
||||
int m_lastForWidth = -1;
|
||||
int m_lastCompSize = 0;
|
||||
};
|
||||
|
||||
|
||||
class PerfMeterComponent : public Component, public Timer
|
||||
{
|
||||
public:
|
||||
@ -216,6 +262,7 @@ private:
|
||||
bool m_did_drag = false;
|
||||
int m_cur_index = -1;
|
||||
int m_drag_x = 0;
|
||||
int m_downoffset_x = 0;
|
||||
std::vector<SpectrumProcess> m_order;
|
||||
void drawBox(Graphics& g, int index, int x, int y, int w, int h);
|
||||
};
|
||||
@ -248,7 +295,7 @@ private:
|
||||
uptrvec<ParameterComponent> m_parcomps;
|
||||
CriticalSection* m_cs = nullptr;
|
||||
PaulstretchpluginAudioProcessor* m_proc = nullptr;
|
||||
int m_slidwidth = 400;
|
||||
int m_slidwidth = 350;
|
||||
};
|
||||
|
||||
class MyTabComponent : public TabbedComponent
|
||||
@ -476,10 +523,26 @@ public:
|
||||
void executeModalMenuAction(int menuid, int actionid);
|
||||
//SimpleFFTComponent m_sonogram;
|
||||
String m_last_err;
|
||||
|
||||
|
||||
std::function<AudioDeviceManager*()> getAudioDeviceManager;
|
||||
std::function<void(Component*,Component*)> showAudioSettingsDialog;
|
||||
|
||||
private:
|
||||
|
||||
bool isSpectrumProcGroupEnabled(int groupid);
|
||||
|
||||
CustomLookAndFeel m_lookandfeel;
|
||||
|
||||
PaulstretchpluginAudioProcessor& processor;
|
||||
uptrvec<ParameterComponent> m_parcomps;
|
||||
std::map<int, std::unique_ptr<ParameterGroupComponent> > m_pargroups;
|
||||
std::unique_ptr<ParameterGroupComponent> m_posgroup;
|
||||
std::unique_ptr<ParameterGroupComponent> m_stretchgroup;
|
||||
|
||||
std::unique_ptr<Viewport> m_groupviewport;
|
||||
std::unique_ptr<Component> m_groupcontainer;
|
||||
|
||||
|
||||
//SpectralVisualizer m_specvis;
|
||||
PerfMeterComponent m_perfmeter;
|
||||
TextButton m_import_button;
|
||||
@ -497,6 +560,7 @@ private:
|
||||
|
||||
MyTabComponent m_wavefilter_tab;
|
||||
Component* m_wave_container=nullptr;
|
||||
void showAudioSetup();
|
||||
void showAbout();
|
||||
void toggleFileBrowser();
|
||||
std::vector<int> m_capturelens{ 2,5,10,30,60,120 };
|
||||
|
@ -59,7 +59,7 @@ inline AudioParameterFloat* make_floatpar(String id, String name, float minv, fl
|
||||
//==============================================================================
|
||||
PaulstretchpluginAudioProcessor::PaulstretchpluginAudioProcessor(bool is_stand_alone_offline)
|
||||
: AudioProcessor(PaulstretchpluginAudioProcessor::BusesProperties().withInput("Main In", AudioChannelSet::stereo(), true).withOutput ("Main Out", AudioChannelSet::stereo(), true)),
|
||||
m_is_stand_alone_offline(is_stand_alone_offline), m_bufferingthread("pspluginprebufferthread")
|
||||
m_bufferingthread("pspluginprebufferthread"), m_is_stand_alone_offline(is_stand_alone_offline)
|
||||
{
|
||||
m_filechoose_callback = [this](const FileChooser& chooser)
|
||||
{
|
||||
|
@ -123,7 +123,7 @@ class PaulstretchpluginAudioProcessorEditor;
|
||||
struct OfflineRenderParams
|
||||
{
|
||||
OfflineRenderParams(File ofile, double osr, int oformat, double omaxdur, int onumloops, CallOutBox* ocb=nullptr) :
|
||||
outputfile(ofile), outsr(osr), outputformat(oformat), maxoutdur(omaxdur), numloops(onumloops), cbox(ocb)
|
||||
outputfile(ofile), outsr(osr), maxoutdur(omaxdur), numloops(onumloops), outputformat(oformat), cbox(ocb)
|
||||
{}
|
||||
File outputfile;
|
||||
double outsr = 44100.0;
|
||||
|
@ -256,6 +256,11 @@ void EnvelopeComponent::mouseDown(const MouseEvent & ev)
|
||||
repaint();
|
||||
};
|
||||
|
||||
if (!JUCEApplicationBase::isStandaloneApp()) {
|
||||
opts = opts.withParentComponent(this);
|
||||
}
|
||||
|
||||
|
||||
menu.showMenuAsync(opts, callback);
|
||||
|
||||
return;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<JUCERPROJECT id="fn1Rg8" name="PaulXStretch" displaySplashScreen="0" reportAppUsage="0"
|
||||
splashScreenColour="Dark" projectType="audioplug" version="1.2.5"
|
||||
splashScreenColour="Dark" projectType="audioplug" version="1.5.0"
|
||||
bundleIdentifier="com.xenakios.paulxstretch" includeBinaryInAppConfig="1"
|
||||
cppLanguageStandard="latest" companyCopyright="" buildVST="0"
|
||||
buildVST3="1" buildAU="1" buildAUv3="0" buildRTAS="0" buildAAX="0"
|
||||
@ -11,21 +11,9 @@
|
||||
pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" pluginAUExportPrefix="paulxstretchAU"
|
||||
aaxIdentifier="com.yourcompany.paulstretchplugin" pluginAAXCategory="2"
|
||||
headerPath=" " pluginFormats="buildAU,buildStandalone,buildVST3"
|
||||
jucerFormatVersion="1">
|
||||
jucerFormatVersion="1" useAppConfig="0" defines="JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1">
|
||||
<MAINGROUP id="nozXHl" name="PaulXStretch">
|
||||
<GROUP id="{03DA6B32-F666-FF60-F168-4385D0847058}" name="Source">
|
||||
<FILE id="zvau5B" name="CrossPlatformUtils.h" compile="0" resource="0"
|
||||
file="Source/CrossPlatformUtils.h"/>
|
||||
<FILE id="fjYsFD" name="CrossPlatformUtilsMac.mm" compile="1" resource="0"
|
||||
file="Source/CrossPlatformUtilsMac.mm"/>
|
||||
<FILE id="RanaVV" name="RenderSettingsComponent.cpp" compile="1" resource="0"
|
||||
file="Source/RenderSettingsComponent.cpp"/>
|
||||
<FILE id="Mz5aVb" name="envelope_component.cpp" compile="1" resource="0"
|
||||
file="Source/envelope_component.cpp"/>
|
||||
<FILE id="apM6W6" name="envelope_component.h" compile="0" resource="0"
|
||||
file="Source/envelope_component.h"/>
|
||||
<FILE id="qfCc8R" name="jcdp_envelope.h" compile="0" resource="0" file="Source/jcdp_envelope.h"/>
|
||||
<FILE id="TDOHpE" name="resample.cpp" compile="1" resource="0" file="Source/WDL/resample.cpp"/>
|
||||
<GROUP id="{3B6D1AF9-E53E-2F78-24A5-D12A34009E6A}" name="PS_Source">
|
||||
<FILE id="gDsFRp" name="globals.h" compile="0" resource="0" file="Source/PS_Source/globals.h"/>
|
||||
<FILE id="MOQjrp" name="ProcessedStretch.h" compile="0" resource="0"
|
||||
@ -40,24 +28,44 @@
|
||||
<FILE id="a7ur6I" name="StretchSource.cpp" compile="1" resource="0"
|
||||
file="Source/PS_Source/StretchSource.cpp"/>
|
||||
</GROUP>
|
||||
<FILE id="KcXfhC" name="ps3_BufferingAudioSource.cpp" compile="1" resource="0"
|
||||
file="Source/ps3_BufferingAudioSource.cpp"/>
|
||||
<FILE id="oWbh5E" name="ps3_BufferingAudioSource.h" compile="0" resource="0"
|
||||
file="Source/ps3_BufferingAudioSource.h"/>
|
||||
<FILE id="zvau5B" name="CrossPlatformUtils.h" compile="0" resource="0"
|
||||
file="Source/CrossPlatformUtils.h"/>
|
||||
<FILE id="fjYsFD" name="CrossPlatformUtilsMac.mm" compile="1" resource="0"
|
||||
file="Source/CrossPlatformUtilsMac.mm"/>
|
||||
<FILE id="TCVLTq" name="CustomLookAndFeel.cpp" compile="1" resource="0"
|
||||
file="Source/CustomLookAndFeel.cpp"/>
|
||||
<FILE id="cH0r3U" name="CustomLookAndFeel.h" compile="0" resource="0"
|
||||
file="Source/CustomLookAndFeel.h"/>
|
||||
<FILE id="IXtDZ2" name="CustomStandaloneFilterApp.cpp" compile="1"
|
||||
resource="0" file="Source/CustomStandaloneFilterApp.cpp"/>
|
||||
<FILE id="Ixh7BI" name="CustomStandaloneFilterWindow.h" compile="0"
|
||||
resource="0" file="Source/CustomStandaloneFilterWindow.h"/>
|
||||
<FILE id="Mz5aVb" name="envelope_component.cpp" compile="1" resource="0"
|
||||
file="Source/envelope_component.cpp"/>
|
||||
<FILE id="apM6W6" name="envelope_component.h" compile="0" resource="0"
|
||||
file="Source/envelope_component.h"/>
|
||||
<FILE id="qfCc8R" name="jcdp_envelope.h" compile="0" resource="0" file="Source/jcdp_envelope.h"/>
|
||||
<FILE id="lyNyYp" name="PluginEditor.cpp" compile="1" resource="0"
|
||||
file="Source/PluginEditor.cpp"/>
|
||||
<FILE id="IO7X2q" name="PluginEditor.h" compile="0" resource="0" file="Source/PluginEditor.h"/>
|
||||
<FILE id="jdPS0A" name="PluginProcessor.cpp" compile="1" resource="0"
|
||||
file="Source/PluginProcessor.cpp"/>
|
||||
<FILE id="pt5tX8" name="PluginProcessor.h" compile="0" resource="0"
|
||||
file="Source/PluginProcessor.h"/>
|
||||
<FILE id="lyNyYp" name="PluginEditor.cpp" compile="1" resource="0"
|
||||
file="Source/PluginEditor.cpp"/>
|
||||
<FILE id="IO7X2q" name="PluginEditor.h" compile="0" resource="0" file="Source/PluginEditor.h"/>
|
||||
<FILE id="KcXfhC" name="ps3_BufferingAudioSource.cpp" compile="1" resource="0"
|
||||
file="Source/ps3_BufferingAudioSource.cpp"/>
|
||||
<FILE id="oWbh5E" name="ps3_BufferingAudioSource.h" compile="0" resource="0"
|
||||
file="Source/ps3_BufferingAudioSource.h"/>
|
||||
<FILE id="RanaVV" name="RenderSettingsComponent.cpp" compile="1" resource="0"
|
||||
file="Source/RenderSettingsComponent.cpp"/>
|
||||
<FILE id="TDOHpE" name="resample.cpp" compile="1" resource="0" file="Source/WDL/resample.cpp"/>
|
||||
</GROUP>
|
||||
</MAINGROUP>
|
||||
<EXPORTFORMATS>
|
||||
<XCODE_MAC targetFolder="Builds/MacOSX" externalLibraries="fftw3f" microphonePermissionNeeded="1"
|
||||
microphonePermissionsText="This application requires audio input to capture live audio for processing"
|
||||
hardenedRuntime="1" hardenedRuntimeOptions="com.apple.security.device.audio-input"
|
||||
xcodeValidArchs="arm64,x86_64" buildNumber="100">
|
||||
xcodeValidArchs="arm64,x86_64" buildNumber="100" applicationCategory="public.app-category.music">
|
||||
<CONFIGURATIONS>
|
||||
<CONFIGURATION name="Debug" enablePluginBinaryCopyStep="1" isDebug="1" optimisation="1"
|
||||
linkTimeOptimisation="0" targetName="PaulXStretch" headerPath="Source/PS_Source Source/WDL ${HOME}/devstatic/include "
|
||||
|
119
paulstretchplugin_ios.jucer
Normal file
119
paulstretchplugin_ios.jucer
Normal file
@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<JUCERPROJECT id="fn1Rg8" name="PaulXStretch" displaySplashScreen="0" reportAppUsage="0"
|
||||
splashScreenColour="Dark" projectType="audioplug" version="1.5.0"
|
||||
bundleIdentifier="com.sonosaurus.paulxstretch" includeBinaryInAppConfig="1"
|
||||
cppLanguageStandard="latest" companyCopyright="" buildVST="0"
|
||||
buildVST3="0" buildAU="0" buildAUv3="1" buildRTAS="0" buildAAX="0"
|
||||
buildStandalone="1" enableIAA="1" pluginName="PaulXStretch" pluginDesc="PaulXStretch"
|
||||
pluginManufacturer="Sonosaurus" pluginManufacturerCode="XenS"
|
||||
pluginCode="Fn1r" pluginChannelConfigs="{2,2},{2,4}, {2,8}, {8,8} "
|
||||
pluginIsSynth="0" pluginWantsMidiIn="0" pluginProducesMidiOut="0"
|
||||
pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" pluginAUExportPrefix="paulxstretchAU"
|
||||
aaxIdentifier="com.sonosaurus.paulxstretch" pluginAAXCategory="8192"
|
||||
headerPath=" " pluginFormats="buildAUv3,buildStandalone,enableIAA"
|
||||
jucerFormatVersion="1" useAppConfig="0" defines="JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1">
|
||||
<MAINGROUP id="nozXHl" name="PaulXStretch">
|
||||
<GROUP id="{03DA6B32-F666-FF60-F168-4385D0847058}" name="Source">
|
||||
<FILE id="H0XjG1" name="CrossPlatformUtilsIOS.mm" compile="1" resource="0"
|
||||
file="Source/CrossPlatformUtilsIOS.mm"/>
|
||||
<FILE id="RsxiMD" name="CustomStandaloneFilterApp.cpp" compile="1"
|
||||
resource="0" file="Source/CustomStandaloneFilterApp.cpp"/>
|
||||
<FILE id="EUS9Ff" name="CustomStandaloneFilterWindow.h" compile="0"
|
||||
resource="0" file="Source/CustomStandaloneFilterWindow.h"/>
|
||||
<FILE id="rHA1fu" name="CustomLookAndFeel.cpp" compile="1" resource="0"
|
||||
file="Source/CustomLookAndFeel.cpp"/>
|
||||
<FILE id="Y4DgKq" name="CustomLookAndFeel.h" compile="0" resource="0"
|
||||
file="Source/CustomLookAndFeel.h"/>
|
||||
<FILE id="zvau5B" name="CrossPlatformUtils.h" compile="0" resource="0"
|
||||
file="Source/CrossPlatformUtils.h"/>
|
||||
<FILE id="fjYsFD" name="CrossPlatformUtilsMac.mm" compile="1" resource="0"
|
||||
file="Source/CrossPlatformUtilsMac.mm"/>
|
||||
<FILE id="RanaVV" name="RenderSettingsComponent.cpp" compile="1" resource="0"
|
||||
file="Source/RenderSettingsComponent.cpp"/>
|
||||
<FILE id="Mz5aVb" name="envelope_component.cpp" compile="1" resource="0"
|
||||
file="Source/envelope_component.cpp"/>
|
||||
<FILE id="apM6W6" name="envelope_component.h" compile="0" resource="0"
|
||||
file="Source/envelope_component.h"/>
|
||||
<FILE id="qfCc8R" name="jcdp_envelope.h" compile="0" resource="0" file="Source/jcdp_envelope.h"/>
|
||||
<FILE id="TDOHpE" name="resample.cpp" compile="1" resource="0" file="Source/WDL/resample.cpp"/>
|
||||
<GROUP id="{3B6D1AF9-E53E-2F78-24A5-D12A34009E6A}" name="PS_Source">
|
||||
<FILE id="gDsFRp" name="globals.h" compile="0" resource="0" file="Source/PS_Source/globals.h"/>
|
||||
<FILE id="MOQjrp" name="ProcessedStretch.h" compile="0" resource="0"
|
||||
file="Source/PS_Source/ProcessedStretch.h"/>
|
||||
<FILE id="TcGuiz" name="Stretch.h" compile="0" resource="0" file="Source/PS_Source/Stretch.h"/>
|
||||
<FILE id="yz0SM3" name="StretchSource.h" compile="0" resource="0" file="Source/PS_Source/StretchSource.h"/>
|
||||
<FILE id="G6XXRZ" name="AInputS.h" compile="0" resource="0" file="Source/PS_Source/Input/AInputS.h"/>
|
||||
<FILE id="jSSkH5" name="InputS.h" compile="0" resource="0" file="Source/PS_Source/Input/InputS.h"/>
|
||||
<FILE id="EIKlL1" name="ProcessedStretch.cpp" compile="1" resource="0"
|
||||
file="Source/PS_Source/ProcessedStretch.cpp"/>
|
||||
<FILE id="x1qOMW" name="Stretch.cpp" compile="1" resource="0" file="Source/PS_Source/Stretch.cpp"/>
|
||||
<FILE id="a7ur6I" name="StretchSource.cpp" compile="1" resource="0"
|
||||
file="Source/PS_Source/StretchSource.cpp"/>
|
||||
</GROUP>
|
||||
<FILE id="KcXfhC" name="ps3_BufferingAudioSource.cpp" compile="1" resource="0"
|
||||
file="Source/ps3_BufferingAudioSource.cpp"/>
|
||||
<FILE id="oWbh5E" name="ps3_BufferingAudioSource.h" compile="0" resource="0"
|
||||
file="Source/ps3_BufferingAudioSource.h"/>
|
||||
<FILE id="jdPS0A" name="PluginProcessor.cpp" compile="1" resource="0"
|
||||
file="Source/PluginProcessor.cpp"/>
|
||||
<FILE id="pt5tX8" name="PluginProcessor.h" compile="0" resource="0"
|
||||
file="Source/PluginProcessor.h"/>
|
||||
<FILE id="lyNyYp" name="PluginEditor.cpp" compile="1" resource="0"
|
||||
file="Source/PluginEditor.cpp"/>
|
||||
<FILE id="IO7X2q" name="PluginEditor.h" compile="0" resource="0" file="Source/PluginEditor.h"/>
|
||||
</GROUP>
|
||||
</MAINGROUP>
|
||||
<EXPORTFORMATS>
|
||||
<XCODE_IPHONE targetFolder="Builds/iOS" externalLibraries="fftw3f" iosDevelopmentTeamID="XCS435894D"
|
||||
microphonePermissionNeeded="1" iosBackgroundAudio="1" buildNumber="100"
|
||||
iosScreenOrientation="UIInterfaceOrientationLandscapeLeft,UIInterfaceOrientationLandscapeRight,UIInterfaceOrientationPortrait,UIInterfaceOrientationPortraitUpsideDown"
|
||||
iPadScreenOrientation="UIInterfaceOrientationLandscapeLeft,UIInterfaceOrientationLandscapeRight,UIInterfaceOrientationPortrait,UIInterfaceOrientationPortraitUpsideDown"
|
||||
UIStatusBarHidden="0" UIRequiresFullScreen="0" customPList="<plist version="1.0"> <dict> <key>ITSAppUsesNonExemptEncryption</key> 	<false/> <key>UIStatusBarHidden</key> 	<false/> 	<key>UIStatusBarStyle</key> 	<string>UIStatusBarStyleLightContent</string> <key>UIViewControllerBasedStatusBarAppearance</key> <false/> <key>NSLocalNetworkUsageDescription</key> 	<string>DrumJamPad uses networking to communicate with other local services</string> </dict> </plist>">
|
||||
<CONFIGURATIONS>
|
||||
<CONFIGURATION isDebug="1" name="Debug" headerPath="Source/PS_Source Source/WDL ${HOME}/iosstatic/include"
|
||||
libraryPath="${HOME}/iosstatic/lib "/>
|
||||
<CONFIGURATION isDebug="0" name="Release" libraryPath="${HOME}/iosstatic/lib "
|
||||
headerPath="Source/PS_Source Source/WDL ${HOME}/iosstatic/include"/>
|
||||
</CONFIGURATIONS>
|
||||
<MODULEPATHS>
|
||||
<MODULEPATH id="juce_gui_extra"/>
|
||||
<MODULEPATH id="juce_gui_basics"/>
|
||||
<MODULEPATH id="juce_graphics"/>
|
||||
<MODULEPATH id="juce_events"/>
|
||||
<MODULEPATH id="juce_dsp"/>
|
||||
<MODULEPATH id="juce_data_structures"/>
|
||||
<MODULEPATH id="juce_cryptography"/>
|
||||
<MODULEPATH id="juce_core"/>
|
||||
<MODULEPATH id="juce_audio_utils"/>
|
||||
<MODULEPATH id="juce_audio_processors"/>
|
||||
<MODULEPATH id="juce_audio_plugin_client"/>
|
||||
<MODULEPATH id="juce_audio_formats"/>
|
||||
<MODULEPATH id="juce_audio_devices"/>
|
||||
<MODULEPATH id="juce_audio_basics"/>
|
||||
</MODULEPATHS>
|
||||
</XCODE_IPHONE>
|
||||
</EXPORTFORMATS>
|
||||
<MODULES>
|
||||
<MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_plugin_client" showAllCode="1" useLocalCopy="0"
|
||||
useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_cryptography" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_dsp" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
</MODULES>
|
||||
<JUCEOPTIONS JUCE_QUICKTIME="disabled" JUCE_ASIO="1" JUCE_VST3_CAN_REPLACE_VST2="0"/>
|
||||
<LIVE_SETTINGS>
|
||||
<OSX/>
|
||||
<WINDOWS/>
|
||||
</LIVE_SETTINGS>
|
||||
</JUCERPROJECT>
|
Loading…
Reference in New Issue
Block a user