layout update supporting better dynamic resizing, lookandfeel update, ios support
This commit is contained in:
		@@ -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
 | 
			
		||||
@@ -477,9 +524,25 @@ public:
 | 
			
		||||
	//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>
 | 
			
		||||
		Reference in New Issue
	
	Block a user