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
|
#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
|
#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>
|
#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;
|
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 "PluginProcessor.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
#include "envelope_component.h"
|
#include "envelope_component.h"
|
||||||
|
#include "CustomLookAndFeel.h"
|
||||||
|
|
||||||
class zoom_scrollbar : public Component
|
class zoom_scrollbar : public Component
|
||||||
{
|
{
|
||||||
@ -103,6 +105,50 @@ private:
|
|||||||
Colour m_labeldefcolor;
|
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
|
class PerfMeterComponent : public Component, public Timer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -216,6 +262,7 @@ private:
|
|||||||
bool m_did_drag = false;
|
bool m_did_drag = false;
|
||||||
int m_cur_index = -1;
|
int m_cur_index = -1;
|
||||||
int m_drag_x = 0;
|
int m_drag_x = 0;
|
||||||
|
int m_downoffset_x = 0;
|
||||||
std::vector<SpectrumProcess> m_order;
|
std::vector<SpectrumProcess> m_order;
|
||||||
void drawBox(Graphics& g, int index, int x, int y, int w, int h);
|
void drawBox(Graphics& g, int index, int x, int y, int w, int h);
|
||||||
};
|
};
|
||||||
@ -248,7 +295,7 @@ private:
|
|||||||
uptrvec<ParameterComponent> m_parcomps;
|
uptrvec<ParameterComponent> m_parcomps;
|
||||||
CriticalSection* m_cs = nullptr;
|
CriticalSection* m_cs = nullptr;
|
||||||
PaulstretchpluginAudioProcessor* m_proc = nullptr;
|
PaulstretchpluginAudioProcessor* m_proc = nullptr;
|
||||||
int m_slidwidth = 400;
|
int m_slidwidth = 350;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MyTabComponent : public TabbedComponent
|
class MyTabComponent : public TabbedComponent
|
||||||
@ -477,9 +524,25 @@ public:
|
|||||||
//SimpleFFTComponent m_sonogram;
|
//SimpleFFTComponent m_sonogram;
|
||||||
String m_last_err;
|
String m_last_err;
|
||||||
|
|
||||||
|
std::function<AudioDeviceManager*()> getAudioDeviceManager;
|
||||||
|
std::function<void(Component*,Component*)> showAudioSettingsDialog;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
bool isSpectrumProcGroupEnabled(int groupid);
|
||||||
|
|
||||||
|
CustomLookAndFeel m_lookandfeel;
|
||||||
|
|
||||||
PaulstretchpluginAudioProcessor& processor;
|
PaulstretchpluginAudioProcessor& processor;
|
||||||
uptrvec<ParameterComponent> m_parcomps;
|
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;
|
//SpectralVisualizer m_specvis;
|
||||||
PerfMeterComponent m_perfmeter;
|
PerfMeterComponent m_perfmeter;
|
||||||
TextButton m_import_button;
|
TextButton m_import_button;
|
||||||
@ -497,6 +560,7 @@ private:
|
|||||||
|
|
||||||
MyTabComponent m_wavefilter_tab;
|
MyTabComponent m_wavefilter_tab;
|
||||||
Component* m_wave_container=nullptr;
|
Component* m_wave_container=nullptr;
|
||||||
|
void showAudioSetup();
|
||||||
void showAbout();
|
void showAbout();
|
||||||
void toggleFileBrowser();
|
void toggleFileBrowser();
|
||||||
std::vector<int> m_capturelens{ 2,5,10,30,60,120 };
|
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)
|
PaulstretchpluginAudioProcessor::PaulstretchpluginAudioProcessor(bool is_stand_alone_offline)
|
||||||
: AudioProcessor(PaulstretchpluginAudioProcessor::BusesProperties().withInput("Main In", AudioChannelSet::stereo(), true).withOutput ("Main Out", AudioChannelSet::stereo(), true)),
|
: 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)
|
m_filechoose_callback = [this](const FileChooser& chooser)
|
||||||
{
|
{
|
||||||
|
@ -123,7 +123,7 @@ class PaulstretchpluginAudioProcessorEditor;
|
|||||||
struct OfflineRenderParams
|
struct OfflineRenderParams
|
||||||
{
|
{
|
||||||
OfflineRenderParams(File ofile, double osr, int oformat, double omaxdur, int onumloops, CallOutBox* ocb=nullptr) :
|
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;
|
File outputfile;
|
||||||
double outsr = 44100.0;
|
double outsr = 44100.0;
|
||||||
|
@ -256,6 +256,11 @@ void EnvelopeComponent::mouseDown(const MouseEvent & ev)
|
|||||||
repaint();
|
repaint();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!JUCEApplicationBase::isStandaloneApp()) {
|
||||||
|
opts = opts.withParentComponent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
menu.showMenuAsync(opts, callback);
|
menu.showMenuAsync(opts, callback);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<JUCERPROJECT id="fn1Rg8" name="PaulXStretch" displaySplashScreen="0" reportAppUsage="0"
|
<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"
|
bundleIdentifier="com.xenakios.paulxstretch" includeBinaryInAppConfig="1"
|
||||||
cppLanguageStandard="latest" companyCopyright="" buildVST="0"
|
cppLanguageStandard="latest" companyCopyright="" buildVST="0"
|
||||||
buildVST3="1" buildAU="1" buildAUv3="0" buildRTAS="0" buildAAX="0"
|
buildVST3="1" buildAU="1" buildAUv3="0" buildRTAS="0" buildAAX="0"
|
||||||
@ -11,21 +11,9 @@
|
|||||||
pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" pluginAUExportPrefix="paulxstretchAU"
|
pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" pluginAUExportPrefix="paulxstretchAU"
|
||||||
aaxIdentifier="com.yourcompany.paulstretchplugin" pluginAAXCategory="2"
|
aaxIdentifier="com.yourcompany.paulstretchplugin" pluginAAXCategory="2"
|
||||||
headerPath=" " pluginFormats="buildAU,buildStandalone,buildVST3"
|
headerPath=" " pluginFormats="buildAU,buildStandalone,buildVST3"
|
||||||
jucerFormatVersion="1">
|
jucerFormatVersion="1" useAppConfig="0" defines="JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1">
|
||||||
<MAINGROUP id="nozXHl" name="PaulXStretch">
|
<MAINGROUP id="nozXHl" name="PaulXStretch">
|
||||||
<GROUP id="{03DA6B32-F666-FF60-F168-4385D0847058}" name="Source">
|
<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">
|
<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="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 id="MOQjrp" name="ProcessedStretch.h" compile="0" resource="0"
|
||||||
@ -40,24 +28,44 @@
|
|||||||
<FILE id="a7ur6I" name="StretchSource.cpp" compile="1" resource="0"
|
<FILE id="a7ur6I" name="StretchSource.cpp" compile="1" resource="0"
|
||||||
file="Source/PS_Source/StretchSource.cpp"/>
|
file="Source/PS_Source/StretchSource.cpp"/>
|
||||||
</GROUP>
|
</GROUP>
|
||||||
<FILE id="KcXfhC" name="ps3_BufferingAudioSource.cpp" compile="1" resource="0"
|
<FILE id="zvau5B" name="CrossPlatformUtils.h" compile="0" resource="0"
|
||||||
file="Source/ps3_BufferingAudioSource.cpp"/>
|
file="Source/CrossPlatformUtils.h"/>
|
||||||
<FILE id="oWbh5E" name="ps3_BufferingAudioSource.h" compile="0" resource="0"
|
<FILE id="fjYsFD" name="CrossPlatformUtilsMac.mm" compile="1" resource="0"
|
||||||
file="Source/ps3_BufferingAudioSource.h"/>
|
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 id="jdPS0A" name="PluginProcessor.cpp" compile="1" resource="0"
|
||||||
file="Source/PluginProcessor.cpp"/>
|
file="Source/PluginProcessor.cpp"/>
|
||||||
<FILE id="pt5tX8" name="PluginProcessor.h" compile="0" resource="0"
|
<FILE id="pt5tX8" name="PluginProcessor.h" compile="0" resource="0"
|
||||||
file="Source/PluginProcessor.h"/>
|
file="Source/PluginProcessor.h"/>
|
||||||
<FILE id="lyNyYp" name="PluginEditor.cpp" compile="1" resource="0"
|
<FILE id="KcXfhC" name="ps3_BufferingAudioSource.cpp" compile="1" resource="0"
|
||||||
file="Source/PluginEditor.cpp"/>
|
file="Source/ps3_BufferingAudioSource.cpp"/>
|
||||||
<FILE id="IO7X2q" name="PluginEditor.h" compile="0" resource="0" file="Source/PluginEditor.h"/>
|
<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>
|
</GROUP>
|
||||||
</MAINGROUP>
|
</MAINGROUP>
|
||||||
<EXPORTFORMATS>
|
<EXPORTFORMATS>
|
||||||
<XCODE_MAC targetFolder="Builds/MacOSX" externalLibraries="fftw3f" microphonePermissionNeeded="1"
|
<XCODE_MAC targetFolder="Builds/MacOSX" externalLibraries="fftw3f" microphonePermissionNeeded="1"
|
||||||
microphonePermissionsText="This application requires audio input to capture live audio for processing"
|
microphonePermissionsText="This application requires audio input to capture live audio for processing"
|
||||||
hardenedRuntime="1" hardenedRuntimeOptions="com.apple.security.device.audio-input"
|
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>
|
<CONFIGURATIONS>
|
||||||
<CONFIGURATION name="Debug" enablePluginBinaryCopyStep="1" isDebug="1" optimisation="1"
|
<CONFIGURATION name="Debug" enablePluginBinaryCopyStep="1" isDebug="1" optimisation="1"
|
||||||
linkTimeOptimisation="0" targetName="PaulXStretch" headerPath="Source/PS_Source Source/WDL ${HOME}/devstatic/include "
|
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