layout update supporting better dynamic resizing, lookandfeel update, ios support

This commit is contained in:
essej 2022-04-11 13:23:10 -04:00
parent 8c4a20ecd6
commit 2084782a79
14 changed files with 3660 additions and 220 deletions

View File

@ -4,7 +4,9 @@
#pragma once #pragma once
void getSafeAreaInsets(void * component, float & top, float & bottom, float & left, float & right); // notchPos is 0=none 1=top 2=bottom, 3=left, 4=right
void getSafeAreaInsets(void * component, float & top, float & bottom, float & left, float & right, int & notchPos);
#if JUCE_MAC #if JUCE_MAC

View 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

View File

@ -19,9 +19,10 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
void getSafeAreaInsets(void * component, float & top, float & bottom, float & left, float & right) void getSafeAreaInsets(void * component, float & top, float & bottom, float & left, float & right, int & notchPos)
{ {
top = bottom = left = right = 0; top = bottom = left = right = 0;
notchPos = 0;
} }

1310
Source/CustomLookAndFeel.cpp Normal file

File diff suppressed because it is too large Load Diff

139
Source/CustomLookAndFeel.h Normal file
View 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;
};

View 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

View 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

View File

@ -21,7 +21,9 @@ www.gnu.org/licenses
#include "PluginProcessor.h" #include "PluginProcessor.h"
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <map>
#include "envelope_component.h" #include "envelope_component.h"
#include "CustomLookAndFeel.h"
class zoom_scrollbar : public Component class zoom_scrollbar : public Component
{ {
@ -103,6 +105,50 @@ private:
Colour m_labeldefcolor; Colour m_labeldefcolor;
}; };
class ParameterGroupComponent : public Component
{
public:
ParameterGroupComponent(const String & name, int groupid, PaulstretchpluginAudioProcessor* proc, bool showtoggle=true);
void resized() override;
void paint(Graphics &g) override;
//void addParameterComponent(std::unique_ptr<ParameterComponent> pcomp);
void addParameterComponent(ParameterComponent * pcomp);
void updateParameterComponents();
void setBackgroundColor(Colour col) { m_bgcolor = col; }
Colour getBackgroundColor() const { return m_bgcolor; }
String name;
int groupId = -1;
int getMinimumHeight(int forWidth);
std::function<void(void)> EnabledChangedCallback;
private:
int doLayout(Rectangle<int> bounds); // returns min height
//uptrvec<ParameterComponent> m_parcomps;
std::vector<ParameterComponent*> m_parcomps;
std::unique_ptr<Label> m_namelabel;
//std::unique_ptr<DrawableButton> m_enableButton;
std::unique_ptr<ToggleButton> m_enableButton;
CriticalSection* m_cs = nullptr;
PaulstretchpluginAudioProcessor* m_proc = nullptr;
int m_slidwidth = 400;
Colour m_bgcolor;
int m_minHeight = 0;
int m_lastForWidth = -1;
int m_lastCompSize = 0;
};
class PerfMeterComponent : public Component, public Timer class PerfMeterComponent : public Component, public Timer
{ {
public: public:
@ -216,6 +262,7 @@ private:
bool m_did_drag = false; bool m_did_drag = false;
int m_cur_index = -1; int m_cur_index = -1;
int m_drag_x = 0; int m_drag_x = 0;
int m_downoffset_x = 0;
std::vector<SpectrumProcess> m_order; std::vector<SpectrumProcess> m_order;
void drawBox(Graphics& g, int index, int x, int y, int w, int h); void drawBox(Graphics& g, int index, int x, int y, int w, int h);
}; };
@ -248,7 +295,7 @@ private:
uptrvec<ParameterComponent> m_parcomps; uptrvec<ParameterComponent> m_parcomps;
CriticalSection* m_cs = nullptr; CriticalSection* m_cs = nullptr;
PaulstretchpluginAudioProcessor* m_proc = nullptr; PaulstretchpluginAudioProcessor* m_proc = nullptr;
int m_slidwidth = 400; int m_slidwidth = 350;
}; };
class MyTabComponent : public TabbedComponent class MyTabComponent : public TabbedComponent
@ -476,10 +523,26 @@ public:
void executeModalMenuAction(int menuid, int actionid); void executeModalMenuAction(int menuid, int actionid);
//SimpleFFTComponent m_sonogram; //SimpleFFTComponent m_sonogram;
String m_last_err; String m_last_err;
std::function<AudioDeviceManager*()> getAudioDeviceManager;
std::function<void(Component*,Component*)> showAudioSettingsDialog;
private: private:
bool isSpectrumProcGroupEnabled(int groupid);
CustomLookAndFeel m_lookandfeel;
PaulstretchpluginAudioProcessor& processor; PaulstretchpluginAudioProcessor& processor;
uptrvec<ParameterComponent> m_parcomps; uptrvec<ParameterComponent> m_parcomps;
std::map<int, std::unique_ptr<ParameterGroupComponent> > m_pargroups;
std::unique_ptr<ParameterGroupComponent> m_posgroup;
std::unique_ptr<ParameterGroupComponent> m_stretchgroup;
std::unique_ptr<Viewport> m_groupviewport;
std::unique_ptr<Component> m_groupcontainer;
//SpectralVisualizer m_specvis; //SpectralVisualizer m_specvis;
PerfMeterComponent m_perfmeter; PerfMeterComponent m_perfmeter;
TextButton m_import_button; TextButton m_import_button;
@ -497,6 +560,7 @@ private:
MyTabComponent m_wavefilter_tab; MyTabComponent m_wavefilter_tab;
Component* m_wave_container=nullptr; Component* m_wave_container=nullptr;
void showAudioSetup();
void showAbout(); void showAbout();
void toggleFileBrowser(); void toggleFileBrowser();
std::vector<int> m_capturelens{ 2,5,10,30,60,120 }; std::vector<int> m_capturelens{ 2,5,10,30,60,120 };

View File

@ -59,7 +59,7 @@ inline AudioParameterFloat* make_floatpar(String id, String name, float minv, fl
//============================================================================== //==============================================================================
PaulstretchpluginAudioProcessor::PaulstretchpluginAudioProcessor(bool is_stand_alone_offline) PaulstretchpluginAudioProcessor::PaulstretchpluginAudioProcessor(bool is_stand_alone_offline)
: AudioProcessor(PaulstretchpluginAudioProcessor::BusesProperties().withInput("Main In", AudioChannelSet::stereo(), true).withOutput ("Main Out", AudioChannelSet::stereo(), true)), : AudioProcessor(PaulstretchpluginAudioProcessor::BusesProperties().withInput("Main In", AudioChannelSet::stereo(), true).withOutput ("Main Out", AudioChannelSet::stereo(), true)),
m_is_stand_alone_offline(is_stand_alone_offline), m_bufferingthread("pspluginprebufferthread") m_bufferingthread("pspluginprebufferthread"), m_is_stand_alone_offline(is_stand_alone_offline)
{ {
m_filechoose_callback = [this](const FileChooser& chooser) m_filechoose_callback = [this](const FileChooser& chooser)
{ {

View File

@ -123,7 +123,7 @@ class PaulstretchpluginAudioProcessorEditor;
struct OfflineRenderParams struct OfflineRenderParams
{ {
OfflineRenderParams(File ofile, double osr, int oformat, double omaxdur, int onumloops, CallOutBox* ocb=nullptr) : OfflineRenderParams(File ofile, double osr, int oformat, double omaxdur, int onumloops, CallOutBox* ocb=nullptr) :
outputfile(ofile), outsr(osr), outputformat(oformat), maxoutdur(omaxdur), numloops(onumloops), cbox(ocb) outputfile(ofile), outsr(osr), maxoutdur(omaxdur), numloops(onumloops), outputformat(oformat), cbox(ocb)
{} {}
File outputfile; File outputfile;
double outsr = 44100.0; double outsr = 44100.0;

View File

@ -256,6 +256,11 @@ void EnvelopeComponent::mouseDown(const MouseEvent & ev)
repaint(); repaint();
}; };
if (!JUCEApplicationBase::isStandaloneApp()) {
opts = opts.withParentComponent(this);
}
menu.showMenuAsync(opts, callback); menu.showMenuAsync(opts, callback);
return; return;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<JUCERPROJECT id="fn1Rg8" name="PaulXStretch" displaySplashScreen="0" reportAppUsage="0" <JUCERPROJECT id="fn1Rg8" name="PaulXStretch" displaySplashScreen="0" reportAppUsage="0"
splashScreenColour="Dark" projectType="audioplug" version="1.2.5" splashScreenColour="Dark" projectType="audioplug" version="1.5.0"
bundleIdentifier="com.xenakios.paulxstretch" includeBinaryInAppConfig="1" bundleIdentifier="com.xenakios.paulxstretch" includeBinaryInAppConfig="1"
cppLanguageStandard="latest" companyCopyright="" buildVST="0" cppLanguageStandard="latest" companyCopyright="" buildVST="0"
buildVST3="1" buildAU="1" buildAUv3="0" buildRTAS="0" buildAAX="0" buildVST3="1" buildAU="1" buildAUv3="0" buildRTAS="0" buildAAX="0"
@ -11,21 +11,9 @@
pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" pluginAUExportPrefix="paulxstretchAU" pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" pluginAUExportPrefix="paulxstretchAU"
aaxIdentifier="com.yourcompany.paulstretchplugin" pluginAAXCategory="2" aaxIdentifier="com.yourcompany.paulstretchplugin" pluginAAXCategory="2"
headerPath="&#10;&#10;" pluginFormats="buildAU,buildStandalone,buildVST3" headerPath="&#10;&#10;" pluginFormats="buildAU,buildStandalone,buildVST3"
jucerFormatVersion="1"> jucerFormatVersion="1" useAppConfig="0" defines="JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1">
<MAINGROUP id="nozXHl" name="PaulXStretch"> <MAINGROUP id="nozXHl" name="PaulXStretch">
<GROUP id="{03DA6B32-F666-FF60-F168-4385D0847058}" name="Source"> <GROUP id="{03DA6B32-F666-FF60-F168-4385D0847058}" name="Source">
<FILE id="zvau5B" name="CrossPlatformUtils.h" compile="0" resource="0"
file="Source/CrossPlatformUtils.h"/>
<FILE id="fjYsFD" name="CrossPlatformUtilsMac.mm" compile="1" resource="0"
file="Source/CrossPlatformUtilsMac.mm"/>
<FILE id="RanaVV" name="RenderSettingsComponent.cpp" compile="1" resource="0"
file="Source/RenderSettingsComponent.cpp"/>
<FILE id="Mz5aVb" name="envelope_component.cpp" compile="1" resource="0"
file="Source/envelope_component.cpp"/>
<FILE id="apM6W6" name="envelope_component.h" compile="0" resource="0"
file="Source/envelope_component.h"/>
<FILE id="qfCc8R" name="jcdp_envelope.h" compile="0" resource="0" file="Source/jcdp_envelope.h"/>
<FILE id="TDOHpE" name="resample.cpp" compile="1" resource="0" file="Source/WDL/resample.cpp"/>
<GROUP id="{3B6D1AF9-E53E-2F78-24A5-D12A34009E6A}" name="PS_Source"> <GROUP id="{3B6D1AF9-E53E-2F78-24A5-D12A34009E6A}" name="PS_Source">
<FILE id="gDsFRp" name="globals.h" compile="0" resource="0" file="Source/PS_Source/globals.h"/> <FILE id="gDsFRp" name="globals.h" compile="0" resource="0" file="Source/PS_Source/globals.h"/>
<FILE id="MOQjrp" name="ProcessedStretch.h" compile="0" resource="0" <FILE id="MOQjrp" name="ProcessedStretch.h" compile="0" resource="0"
@ -40,24 +28,44 @@
<FILE id="a7ur6I" name="StretchSource.cpp" compile="1" resource="0" <FILE id="a7ur6I" name="StretchSource.cpp" compile="1" resource="0"
file="Source/PS_Source/StretchSource.cpp"/> file="Source/PS_Source/StretchSource.cpp"/>
</GROUP> </GROUP>
<FILE id="KcXfhC" name="ps3_BufferingAudioSource.cpp" compile="1" resource="0" <FILE id="zvau5B" name="CrossPlatformUtils.h" compile="0" resource="0"
file="Source/ps3_BufferingAudioSource.cpp"/> file="Source/CrossPlatformUtils.h"/>
<FILE id="oWbh5E" name="ps3_BufferingAudioSource.h" compile="0" resource="0" <FILE id="fjYsFD" name="CrossPlatformUtilsMac.mm" compile="1" resource="0"
file="Source/ps3_BufferingAudioSource.h"/> file="Source/CrossPlatformUtilsMac.mm"/>
<FILE id="TCVLTq" name="CustomLookAndFeel.cpp" compile="1" resource="0"
file="Source/CustomLookAndFeel.cpp"/>
<FILE id="cH0r3U" name="CustomLookAndFeel.h" compile="0" resource="0"
file="Source/CustomLookAndFeel.h"/>
<FILE id="IXtDZ2" name="CustomStandaloneFilterApp.cpp" compile="1"
resource="0" file="Source/CustomStandaloneFilterApp.cpp"/>
<FILE id="Ixh7BI" name="CustomStandaloneFilterWindow.h" compile="0"
resource="0" file="Source/CustomStandaloneFilterWindow.h"/>
<FILE id="Mz5aVb" name="envelope_component.cpp" compile="1" resource="0"
file="Source/envelope_component.cpp"/>
<FILE id="apM6W6" name="envelope_component.h" compile="0" resource="0"
file="Source/envelope_component.h"/>
<FILE id="qfCc8R" name="jcdp_envelope.h" compile="0" resource="0" file="Source/jcdp_envelope.h"/>
<FILE id="lyNyYp" name="PluginEditor.cpp" compile="1" resource="0"
file="Source/PluginEditor.cpp"/>
<FILE id="IO7X2q" name="PluginEditor.h" compile="0" resource="0" file="Source/PluginEditor.h"/>
<FILE id="jdPS0A" name="PluginProcessor.cpp" compile="1" resource="0" <FILE id="jdPS0A" name="PluginProcessor.cpp" compile="1" resource="0"
file="Source/PluginProcessor.cpp"/> file="Source/PluginProcessor.cpp"/>
<FILE id="pt5tX8" name="PluginProcessor.h" compile="0" resource="0" <FILE id="pt5tX8" name="PluginProcessor.h" compile="0" resource="0"
file="Source/PluginProcessor.h"/> file="Source/PluginProcessor.h"/>
<FILE id="lyNyYp" name="PluginEditor.cpp" compile="1" resource="0" <FILE id="KcXfhC" name="ps3_BufferingAudioSource.cpp" compile="1" resource="0"
file="Source/PluginEditor.cpp"/> file="Source/ps3_BufferingAudioSource.cpp"/>
<FILE id="IO7X2q" name="PluginEditor.h" compile="0" resource="0" file="Source/PluginEditor.h"/> <FILE id="oWbh5E" name="ps3_BufferingAudioSource.h" compile="0" resource="0"
file="Source/ps3_BufferingAudioSource.h"/>
<FILE id="RanaVV" name="RenderSettingsComponent.cpp" compile="1" resource="0"
file="Source/RenderSettingsComponent.cpp"/>
<FILE id="TDOHpE" name="resample.cpp" compile="1" resource="0" file="Source/WDL/resample.cpp"/>
</GROUP> </GROUP>
</MAINGROUP> </MAINGROUP>
<EXPORTFORMATS> <EXPORTFORMATS>
<XCODE_MAC targetFolder="Builds/MacOSX" externalLibraries="fftw3f" microphonePermissionNeeded="1" <XCODE_MAC targetFolder="Builds/MacOSX" externalLibraries="fftw3f" microphonePermissionNeeded="1"
microphonePermissionsText="This application requires audio input to capture live audio for processing" microphonePermissionsText="This application requires audio input to capture live audio for processing"
hardenedRuntime="1" hardenedRuntimeOptions="com.apple.security.device.audio-input" hardenedRuntime="1" hardenedRuntimeOptions="com.apple.security.device.audio-input"
xcodeValidArchs="arm64,x86_64" buildNumber="100"> xcodeValidArchs="arm64,x86_64" buildNumber="100" applicationCategory="public.app-category.music">
<CONFIGURATIONS> <CONFIGURATIONS>
<CONFIGURATION name="Debug" enablePluginBinaryCopyStep="1" isDebug="1" optimisation="1" <CONFIGURATION name="Debug" enablePluginBinaryCopyStep="1" isDebug="1" optimisation="1"
linkTimeOptimisation="0" targetName="PaulXStretch" headerPath="Source/PS_Source&#10;Source/WDL&#10;${HOME}/devstatic/include&#10;" linkTimeOptimisation="0" targetName="PaulXStretch" headerPath="Source/PS_Source&#10;Source/WDL&#10;${HOME}/devstatic/include&#10;"

119
paulstretchplugin_ios.jucer Normal file
View 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="&#10;&#10;" 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="&lt;plist version=&quot;1.0&quot;&gt;&#10;&lt;dict&gt;&#10;&#10;&#10;&lt;key&gt;ITSAppUsesNonExemptEncryption&lt;/key&gt;&#10;&#9;&lt;false/&gt;&#10;&#10;&lt;key&gt;UIStatusBarHidden&lt;/key&gt;&#10;&#9;&lt;false/&gt;&#10;&#9;&lt;key&gt;UIStatusBarStyle&lt;/key&gt;&#10;&#9;&lt;string&gt;UIStatusBarStyleLightContent&lt;/string&gt;&#10;&#10;&lt;key&gt;UIViewControllerBasedStatusBarAppearance&lt;/key&gt;&#10;&lt;false/&gt;&#10;&#10;&#10;&lt;key&gt;NSLocalNetworkUsageDescription&lt;/key&gt;&#10;&#9;&lt;string&gt;DrumJamPad uses networking to communicate with other local services&lt;/string&gt;&#10;&#10;&lt;/dict&gt;&#10;&lt;/plist&gt;">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" headerPath="Source/PS_Source&#10;Source/WDL&#10;${HOME}/iosstatic/include"
libraryPath="${HOME}/iosstatic/lib&#10;"/>
<CONFIGURATION isDebug="0" name="Release" libraryPath="${HOME}/iosstatic/lib&#10;"
headerPath="Source/PS_Source&#10;Source/WDL&#10;${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>