git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce

subrepo:
  subdir:   "deps/juce"
  merged:   "b13f9084e"
upstream:
  origin:   "https://github.com/essej/JUCE.git"
  branch:   "sono6good"
  commit:   "b13f9084e"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
This commit is contained in:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

View File

@ -0,0 +1,292 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
PluginDescription AudioPluginInstance::getPluginDescription() const
{
PluginDescription desc;
fillInPluginDescription (desc);
return desc;
}
void* AudioPluginInstance::getPlatformSpecificData() { return nullptr; }
void AudioPluginInstance::getExtensions (ExtensionsVisitor& visitor) const { visitor.visitUnknown ({}); }
String AudioPluginInstance::getParameterID (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
// Currently there is no corresponding method available in the
// AudioProcessorParameter class, and the previous behaviour of JUCE's
// plug-in hosting code simply returns a string version of the index; to
// maintain backwards compatibility you should perform the operation below
// this comment. However the caveat is that for plug-ins which change their
// number of parameters dynamically at runtime you cannot rely upon the
// returned parameter ID mapping to the correct parameter. A comprehensive
// solution to this problem requires some additional work in JUCE's hosting
// code.
return String (parameterIndex);
}
float AudioPluginInstance::getParameter (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getValue();
return 0.0f;
}
void AudioPluginInstance::setParameter (int parameterIndex, float newValue)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
param->setValue (newValue);
}
const String AudioPluginInstance::getParameterName (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getName (1024);
return {};
}
String AudioPluginInstance::getParameterName (int parameterIndex, int maximumStringLength)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getName (maximumStringLength);
return {};
}
const String AudioPluginInstance::getParameterText (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getCurrentValueAsText();
return {};
}
String AudioPluginInstance::getParameterText (int parameterIndex, int maximumStringLength)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getCurrentValueAsText().substring (0, maximumStringLength);
return {};
}
float AudioPluginInstance::getParameterDefaultValue (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getDefaultValue();
return 0.0f;
}
int AudioPluginInstance::getParameterNumSteps (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getNumSteps();
return AudioProcessor::getDefaultNumParameterSteps();
}
bool AudioPluginInstance::isParameterDiscrete (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->isDiscrete();
return false;
}
bool AudioPluginInstance::isParameterAutomatable (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->isAutomatable();
return true;
}
String AudioPluginInstance::getParameterLabel (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getLabel();
return {};
}
bool AudioPluginInstance::isParameterOrientationInverted (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->isOrientationInverted();
return false;
}
bool AudioPluginInstance::isMetaParameter (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->isMetaParameter();
return false;
}
AudioProcessorParameter::Category AudioPluginInstance::getParameterCategory (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getCategory();
return AudioProcessorParameter::genericParameter;
}
void AudioPluginInstance::assertOnceOnDeprecatedMethodUse() const noexcept
{
if (! deprecationAssertiontriggered)
{
// If you hit this assertion then you are using at least one of the
// methods marked as deprecated in this class. For now you can simply
// continue past this point and subsequent uses of deprecated methods
// will not trigger additional assertions. However, we will shortly be
// removing these methods so you are strongly advised to look at the
// implementation of the corresponding method in this class and use
// that approach instead.
jassertfalse;
}
deprecationAssertiontriggered = true;
}
bool AudioPluginInstance::deprecationAssertiontriggered = false;
AudioPluginInstance::Parameter::Parameter()
{
onStrings.add (TRANS("on"));
onStrings.add (TRANS("yes"));
onStrings.add (TRANS("true"));
offStrings.add (TRANS("off"));
offStrings.add (TRANS("no"));
offStrings.add (TRANS("false"));
}
AudioPluginInstance::Parameter::~Parameter() = default;
String AudioPluginInstance::Parameter::getText (float value, int maximumStringLength) const
{
if (isBoolean())
return value < 0.5f ? TRANS("Off") : TRANS("On");
return String (value).substring (0, maximumStringLength);
}
float AudioPluginInstance::Parameter::getValueForText (const String& text) const
{
auto floatValue = text.retainCharacters ("-0123456789.").getFloatValue();
if (isBoolean())
{
if (onStrings.contains (text, true))
return 1.0f;
if (offStrings.contains (text, true))
return 0.0f;
return floatValue < 0.5f ? 0.0f : 1.0f;
}
return floatValue;
}
void AudioPluginInstance::addHostedParameter (std::unique_ptr<HostedParameter> param)
{
addParameter (param.release());
}
void AudioPluginInstance::addHostedParameterGroup (std::unique_ptr<AudioProcessorParameterGroup> group)
{
#if JUCE_DEBUG
// All parameters *must* be HostedParameters, otherwise getHostedParameter will return
// garbage and your host will crash and burn
for (auto* param : group->getParameters (true))
{
jassert (dynamic_cast<HostedParameter*> (param) != nullptr);
}
#endif
addParameterGroup (std::move (group));
}
void AudioPluginInstance::setHostedParameterTree (AudioProcessorParameterGroup group)
{
#if JUCE_DEBUG
// All parameters *must* be HostedParameters, otherwise getHostedParameter will return
// garbage and your host will crash and burn
for (auto* param : group.getParameters (true))
{
jassert (dynamic_cast<HostedParameter*> (param) != nullptr);
}
#endif
setParameterTree (std::move (group));
}
AudioPluginInstance::HostedParameter* AudioPluginInstance::getHostedParameter (int index) const
{
// It's important that all AudioPluginInstance implementations
// only ever own HostedParameters!
return static_cast<HostedParameter*> (getParameters()[index]);
}
} // namespace juce

View File

@ -0,0 +1,199 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
// MSVC does not like it if you override a deprecated method even if you
// keep the deprecation attribute. Other compilers are more forgiving.
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
//==============================================================================
/**
Base class for an active instance of a plugin.
This derives from the AudioProcessor class, and adds some extra functionality
that helps when wrapping dynamically loaded plugins.
This class is not needed when writing plugins, and you should never need to derive
your own sub-classes from it. The plugin hosting classes use it internally and will
return AudioPluginInstance objects which wrap external plugins.
@see AudioProcessor, AudioPluginFormat
@tags{Audio}
*/
class JUCE_API AudioPluginInstance : public AudioProcessor
{
public:
//==============================================================================
/** Destructor.
Make sure that you delete any UI components that belong to this plugin before
deleting the plugin.
*/
~AudioPluginInstance() override = default;
//==============================================================================
/** Fills-in the appropriate parts of this plugin description object. */
virtual void fillInPluginDescription (PluginDescription&) const = 0;
/** Returns a PluginDescription for this plugin.
This is just a convenience method to avoid calling fillInPluginDescription.
*/
PluginDescription getPluginDescription() const;
/** Allows retrieval of information related to the inner workings of a particular plugin format,
such as the AEffect* of a VST, or the handle of an AudioUnit.
To use this, create a new class derived from ExtensionsVisitor, and override
each of the visit member functions. If this AudioPluginInstance wraps a VST3 plugin
the visitVST3() member will be called, while if the AudioPluginInstance wraps an
unknown format the visitUnknown() member will be called. The argument of the visit function
can be queried to extract information related to the AudioPluginInstance's implementation.
*/
virtual void getExtensions (ExtensionsVisitor&) const;
/**
A parameter with functions which are useful for plugin hosts.
*/
struct HostedParameter : public AudioProcessorParameter
{
/** Returns an ID which is unique to this parameter.
Parameter indices are unstable across plugin versions, which means that the
parameter found at a particular index in one version of a plugin might move
to a different index in the subsequent version.
Unlike the parameter index, the ID returned by this function should be
somewhat stable (depending on the format of the plugin), so it is more
suitable for storing/recalling automation data.
*/
virtual String getParameterID() const = 0;
};
/** Adds a parameter to this instance.
@see AudioProcessor::addParameter()
*/
void addHostedParameter (std::unique_ptr<HostedParameter>);
/** Adds multiple parameters to this instance.
In debug mode, this will also check that all added parameters derive from
HostedParameter.
@see AudioProcessor::addParameterGroup()
*/
void addHostedParameterGroup (std::unique_ptr<AudioProcessorParameterGroup>);
/** Adds multiple parameters to this instance.
In debug mode, this will also check that all added parameters derive from
HostedParameter.
@see AudioProcessor::setParameterTree()
*/
void setHostedParameterTree (AudioProcessorParameterGroup);
/** Gets the parameter at a particular index.
If you want to find lots of parameters by their IDs, you should probably build and
use a map<String, HostedParameter*> by looping through all parameters.
*/
HostedParameter* getHostedParameter (int index) const;
#ifndef DOXYGEN
/** Use the new typesafe visitor-based interface rather than this function.
Returns a pointer to some kind of platform-specific data about the plugin.
E.g. For a VST, this value can be cast to an AEffect*. For an AudioUnit, it can be
cast to an AudioUnit handle.
*/
[[deprecated ("Use the new typesafe visitor-based interface rather than this function.")]]
virtual void* getPlatformSpecificData();
// Rather than using these methods you should call the corresponding methods
// on the AudioProcessorParameter objects returned from getParameters().
// See the implementations of the methods below for some examples of how to
// do this.
//
// In addition to being marked as deprecated these methods will assert on
// the first call.
[[deprecated]] String getParameterID (int index) override;
[[deprecated]] float getParameter (int parameterIndex) override;
[[deprecated]] void setParameter (int parameterIndex, float newValue) override;
[[deprecated]] const String getParameterName (int parameterIndex) override;
[[deprecated]] String getParameterName (int parameterIndex, int maximumStringLength) override;
[[deprecated]] const String getParameterText (int parameterIndex) override;
[[deprecated]] String getParameterText (int parameterIndex, int maximumStringLength) override;
[[deprecated]] int getParameterNumSteps (int parameterIndex) override;
[[deprecated]] bool isParameterDiscrete (int parameterIndex) const override;
[[deprecated]] bool isParameterAutomatable (int parameterIndex) const override;
[[deprecated]] float getParameterDefaultValue (int parameterIndex) override;
[[deprecated]] String getParameterLabel (int parameterIndex) const override;
[[deprecated]] bool isParameterOrientationInverted (int parameterIndex) const override;
[[deprecated]] bool isMetaParameter (int parameterIndex) const override;
[[deprecated]] AudioProcessorParameter::Category getParameterCategory (int parameterIndex) const override;
#endif
protected:
//==============================================================================
/** Structure used to describe plugin parameters */
struct Parameter : public HostedParameter
{
public:
Parameter();
~Parameter() override;
String getText (float value, int maximumStringLength) const override;
float getValueForText (const String& text) const override;
private:
StringArray onStrings, offStrings;
};
AudioPluginInstance() = default;
AudioPluginInstance (const BusesProperties& ioLayouts) : AudioProcessor (ioLayouts) {}
template <size_t numLayouts>
AudioPluginInstance (const short channelLayoutList[numLayouts][2]) : AudioProcessor (channelLayoutList) {}
private:
// It's not safe to add a plain AudioProcessorParameter to an AudioPluginInstance.
// Instead, all parameters must be HostedParameters.
using AudioProcessor::addParameter;
using AudioProcessor::addParameterGroup;
using AudioProcessor::setParameterTree;
void assertOnceOnDeprecatedMethodUse() const noexcept;
static bool deprecationAssertiontriggered;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance)
};
JUCE_END_IGNORE_WARNINGS_MSVC
} // namespace juce

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,230 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p)
{
initialise();
}
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p)
{
// the filter must be valid..
jassert (p != nullptr);
initialise();
}
AudioProcessorEditor::~AudioProcessorEditor()
{
splashScreen.deleteAndZero();
// if this fails, then the wrapper hasn't called editorBeingDeleted() on the
// filter for some reason..
jassert (processor.getActiveEditor() != this);
removeComponentListener (resizeListener.get());
}
void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {}
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }
bool AudioProcessorEditor::supportsHostMIDIControllerPresence (bool) { return true; }
void AudioProcessorEditor::hostMIDIControllerIsAvailable (bool) {}
void AudioProcessorEditor::initialise()
{
/*
==========================================================================
In accordance with the terms of the JUCE 6 End-Use License Agreement, the
JUCE Code in SECTION A cannot be removed, changed or otherwise rendered
ineffective unless you have a JUCE Indie or Pro license, or are using
JUCE under the GPL v3 license.
End User License Agreement: www.juce.com/juce-6-licence
==========================================================================
*/
// BEGIN SECTION A
splashScreen = new JUCESplashScreen (*this);
// END SECTION A
attachConstrainer (&defaultConstrainer);
resizeListener.reset (new AudioProcessorEditorListener (*this));
addComponentListener (resizeListener.get());
}
//==============================================================================
void AudioProcessorEditor::setResizable (bool allowHostToResize, bool useBottomRightCornerResizer)
{
resizableByHost = allowHostToResize;
const auto hasResizableCorner = (resizableCorner.get() != nullptr);
if (useBottomRightCornerResizer != hasResizableCorner)
{
if (useBottomRightCornerResizer)
attachResizableCornerComponent();
else
resizableCorner = nullptr;
}
}
void AudioProcessorEditor::setResizeLimits (int newMinimumWidth,
int newMinimumHeight,
int newMaximumWidth,
int newMaximumHeight) noexcept
{
if (constrainer != nullptr && constrainer != &defaultConstrainer)
{
// if you've set up a custom constrainer then these settings won't have any effect..
jassertfalse;
return;
}
resizableByHost = (newMinimumWidth != newMaximumWidth || newMinimumHeight != newMaximumHeight);
defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight,
newMaximumWidth, newMaximumHeight);
if (constrainer == nullptr)
setConstrainer (&defaultConstrainer);
if (resizableCorner != nullptr)
attachResizableCornerComponent();
setBoundsConstrained (getBounds());
}
void AudioProcessorEditor::setConstrainer (ComponentBoundsConstrainer* newConstrainer)
{
if (constrainer != newConstrainer)
{
attachConstrainer (newConstrainer);
if (constrainer != nullptr)
resizableByHost = (newConstrainer->getMinimumWidth() != newConstrainer->getMaximumWidth()
|| newConstrainer->getMinimumHeight() != newConstrainer->getMaximumHeight());
if (resizableCorner != nullptr)
attachResizableCornerComponent();
}
}
void AudioProcessorEditor::attachConstrainer (ComponentBoundsConstrainer* newConstrainer)
{
if (constrainer != newConstrainer)
{
constrainer = newConstrainer;
updatePeer();
}
}
void AudioProcessorEditor::attachResizableCornerComponent()
{
resizableCorner = std::make_unique<ResizableCornerComponent> (this, constrainer);
Component::addChildComponent (resizableCorner.get());
resizableCorner->setAlwaysOnTop (true);
editorResized (true);
}
void AudioProcessorEditor::setBoundsConstrained (Rectangle<int> newBounds)
{
if (constrainer == nullptr)
{
setBounds (newBounds);
return;
}
auto currentBounds = getBounds();
constrainer->setBoundsForComponent (this,
newBounds,
newBounds.getY() != currentBounds.getY() && newBounds.getBottom() == currentBounds.getBottom(),
newBounds.getX() != currentBounds.getX() && newBounds.getRight() == currentBounds.getRight(),
newBounds.getY() == currentBounds.getY() && newBounds.getBottom() != currentBounds.getBottom(),
newBounds.getX() == currentBounds.getX() && newBounds.getRight() != currentBounds.getRight());
}
void AudioProcessorEditor::editorResized (bool wasResized)
{
// The host needs to be able to rescale the plug-in editor and applying your own transform will
// obliterate it! If you want to scale the whole of your UI use Desktop::setGlobalScaleFactor(),
// or, for applying other transforms, consider putting the component you want to transform
// in a child of the editor and transform that instead.
jassert (getTransform() == hostScaleTransform);
if (wasResized)
{
bool resizerHidden = false;
if (auto* peer = getPeer())
resizerHidden = peer->isFullScreen() || peer->isKioskMode();
if (resizableCorner != nullptr)
{
resizableCorner->setVisible (! resizerHidden);
const int resizerSize = 18;
resizableCorner->setBounds (getWidth() - resizerSize,
getHeight() - resizerSize,
resizerSize, resizerSize);
}
}
}
void AudioProcessorEditor::updatePeer()
{
if (isOnDesktop())
if (auto* peer = getPeer())
peer->setConstrainer (constrainer);
}
void AudioProcessorEditor::setScaleFactor (float newScale)
{
hostScaleTransform = AffineTransform::scale (newScale);
setTransform (hostScaleTransform);
editorResized (true);
}
//==============================================================================
typedef ComponentPeer* (*createUnityPeerFunctionType) (Component&);
createUnityPeerFunctionType juce_createUnityPeerFn = nullptr;
ComponentPeer* AudioProcessorEditor::createNewPeer (int styleFlags, void* nativeWindow)
{
if (juce_createUnityPeerFn != nullptr)
{
ignoreUnused (styleFlags, nativeWindow);
return juce_createUnityPeerFn (*this);
}
return Component::createNewPeer (styleFlags, nativeWindow);
}
} // namespace juce

View File

@ -0,0 +1,244 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
class AudioProcessorEditorListener;
//==============================================================================
/**
Base class for the component that acts as the GUI for an AudioProcessor.
Derive your editor component from this class, and create an instance of it
by overriding the AudioProcessor::createEditor() method.
@see AudioProcessor, GenericAudioProcessorEditor
@tags{Audio}
*/
class JUCE_API AudioProcessorEditor : public Component
{
protected:
//==============================================================================
/** Creates an editor for the specified processor. */
AudioProcessorEditor (AudioProcessor&) noexcept;
/** Creates an editor for the specified processor. */
AudioProcessorEditor (AudioProcessor*) noexcept;
public:
/** Destructor. */
~AudioProcessorEditor() override;
//==============================================================================
/** The AudioProcessor that this editor represents. */
AudioProcessor& processor;
/** Returns a pointer to the processor that this editor represents.
This method is here to support legacy code, but it's easier to just use the
AudioProcessorEditor::processor member variable directly to get this object.
*/
AudioProcessor* getAudioProcessor() const noexcept { return &processor; }
//==============================================================================
/** Used by the setParameterHighlighting() method. */
struct ParameterControlHighlightInfo
{
int parameterIndex;
bool isHighlighted;
Colour suggestedColour;
};
/** Some types of plugin can call this to suggest that the control for a particular
parameter should be highlighted.
Currently only AAX plugins will call this, and implementing it is optional.
*/
virtual void setControlHighlight (ParameterControlHighlightInfo);
/** Called by certain plug-in wrappers to find out whether a component is used
to control a parameter.
If the given component represents a particular plugin parameter, then this
method should return the index of that parameter. If not, it should return -1.
Currently only AAX plugins will call this, and implementing it is optional.
*/
virtual int getControlParameterIndex (Component&);
/** Override this method to indicate if your editor supports the presence or
absence of a host-provided MIDI controller.
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later)
support this functionality, and even then the host may choose to ignore this
information.
The default behaviour is to report support for both cases.
*/
virtual bool supportsHostMIDIControllerPresence (bool hostMIDIControllerIsAvailable);
/** Called to indicate if a host is providing a MIDI controller when the host
reconfigures its layout.
Use this as an opportunity to hide or display your own onscreen keyboard or
other input component.
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later)
support this functionality.
*/
virtual void hostMIDIControllerIsAvailable (bool controllerIsAvailable);
/** Can be called by a host to tell the editor that it should use a non-unity
GUI scale.
*/
virtual void setScaleFactor (float newScale);
//==============================================================================
/** Sets whether the editor is resizable by the host and/or user.
@param allowHostToResize whether the editor's parent window can be resized
by the host. Even if this is false, you can still
resize your window yourself by calling setBounds
(for example, when a user clicks on a button in
your editor to drop out a panel) which will bypass
any resizable/constraints checks.
@param useBottomRightCornerResizer if this is true, a ResizableCornerComponent will be
added to the editor's bottom-right to allow the user
to resize the editor regardless of the value of
`allowHostToResize`.
@see setResizeLimits, isResizable
*/
void setResizable (bool allowHostToResize, bool useBottomRightCornerResizer);
/** Returns true if the host is allowed to resize the editor's parent window.
@see setResizable
*/
bool isResizable() const noexcept { return resizableByHost; }
/** This sets the maximum and minimum sizes for the window.
If the window's current size is outside these limits, it will be resized to
make sure it's within them.
If you pass in a different minimum and maximum size, this will mark the editor
as resizable by the host.
A direct call to setBounds() will bypass any constraint checks, but when the
window is dragged by the user or resized by other indirect means, the constrainer
will limit the numbers involved.
Note that if you have set a custom constrainer for this editor then this will have
no effect, and if you have removed the constrainer with `setConstrainer (nullptr);`
then this will re-add the default constrainer with the new limits.
@see setResizable
*/
void setResizeLimits (int newMinimumWidth,
int newMinimumHeight,
int newMaximumWidth,
int newMaximumHeight) noexcept;
/** Returns the bounds constrainer object that this window is using.
You can access this to change its properties.
*/
ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; }
/** Sets the bounds-constrainer object to use for resizing and dragging this window.
A pointer to the object you pass in will be kept, but it won't be deleted
by this object, so it's the caller's responsibility to manage it.
If you pass a nullptr, then no contraints will be placed on the positioning of the window.
*/
void setConstrainer (ComponentBoundsConstrainer* newConstrainer);
/** Calls the window's setBounds method, after first checking these bounds
with the current constrainer.
@see setConstrainer
*/
void setBoundsConstrained (Rectangle<int> newBounds);
/** Gets a context object, if one is available.
Returns nullptr if the host does not provide any information that the editor
can query.
The returned pointer is non-owning, so do not attempt to free it.
*/
AudioProcessorEditorHostContext* getHostContext() const noexcept { return hostContext; }
/** Sets a context object that can be queried to find information that the host
makes available to the plugin.
You will only need to call this function if you are implementing a plugin host.
*/
void setHostContext (AudioProcessorEditorHostContext* context) noexcept { hostContext = context; }
/** The ResizableCornerComponent which is currently being used by this editor,
or nullptr if it does not have one.
*/
std::unique_ptr<ResizableCornerComponent> resizableCorner;
private:
//==============================================================================
struct AudioProcessorEditorListener : public ComponentListener
{
AudioProcessorEditorListener (AudioProcessorEditor& e) : ed (e) {}
void componentMovedOrResized (Component&, bool, bool wasResized) override { ed.editorResized (wasResized); }
void componentParentHierarchyChanged (Component&) override { ed.updatePeer(); }
AudioProcessorEditor& ed;
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditorListener)
};
ComponentPeer* createNewPeer (int styleFlags, void*) override;
//==============================================================================
void initialise();
void editorResized (bool wasResized);
void updatePeer();
void attachConstrainer (ComponentBoundsConstrainer*);
void attachResizableCornerComponent();
//==============================================================================
std::unique_ptr<AudioProcessorEditorListener> resizeListener;
bool resizableByHost = false;
ComponentBoundsConstrainer defaultConstrainer;
ComponentBoundsConstrainer* constrainer = nullptr;
AudioProcessorEditorHostContext* hostContext = nullptr;
Component::SafePointer<Component> splashScreen;
AffineTransform hostScaleTransform;
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor)
};
} // namespace juce

View File

@ -0,0 +1,78 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/** This wraps a context menu for a specific parameter, as provided by the host.
You can choose to create a standard PopupMenu to display the host-provided
options. Alternatively, you can ask the host to display a native menu at
a specific location.
@tags{Audio}
*/
struct HostProvidedContextMenu
{
virtual ~HostProvidedContextMenu() = default;
/** Get a PopupMenu holding entries specified by the host.
Most hosts will populate this menu with options that relate to the
parameter, such as displaying its automation lane. You are free
to modify this menu before displaying it, if you wish to add additional
options.
*/
virtual PopupMenu getEquivalentPopupMenu() const = 0;
/** Asks the host to display its native menu at a location relative
to the top left corner of the editor.
The position you provide should be in logical pixels. To display
the menu next to the mouse cursor, call Component::getMouseXYRelative()
on your editor and pass the result to this function.
*/
virtual void showNativeMenu (Point<int> pos) const = 0;
};
/** Calling AudioProcessorEditor::getHostContext() may return a pointer to an
instance of this class.
At the moment, this can be used to retrieve context menus for parameters in
compatible VST3 hosts. Additional extensions may be added here in the future.
@tags{Audio}
*/
struct AudioProcessorEditorHostContext
{
virtual ~AudioProcessorEditorHostContext() = default;
/** Returns an object which can be used to display a context menu for the
parameter with the given index.
*/
virtual std::unique_ptr<HostProvidedContextMenu> getContextMenuForParameterIndex (const AudioProcessorParameter *) const = 0;
};
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,456 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A type of AudioProcessor which plays back a graph of other AudioProcessors.
Use one of these objects if you want to wire-up a set of AudioProcessors
and play back the result.
Processors can be added to the graph as "nodes" using addNode(), and once
added, you can connect any of their input or output channels to other
nodes using addConnection().
To play back a graph through an audio device, you might want to use an
AudioProcessorPlayer object.
@tags{Audio}
*/
class JUCE_API AudioProcessorGraph : public AudioProcessor,
public ChangeBroadcaster,
private AsyncUpdater
{
public:
//==============================================================================
/** Creates an empty graph. */
AudioProcessorGraph();
/** Destructor.
Any processor objects that have been added to the graph will also be deleted.
*/
~AudioProcessorGraph() override;
/** Each node in the graph has a UID of this type. */
struct NodeID
{
NodeID() {}
explicit NodeID (uint32 i) : uid (i) {}
uint32 uid = 0;
bool operator== (const NodeID& other) const noexcept { return uid == other.uid; }
bool operator!= (const NodeID& other) const noexcept { return uid != other.uid; }
bool operator< (const NodeID& other) const noexcept { return uid < other.uid; }
};
//==============================================================================
/** A special index that represents the midi channel of a node.
This is used as a channel index value if you want to refer to the midi input
or output instead of an audio channel.
*/
enum { midiChannelIndex = 0x1000 };
//==============================================================================
/**
Represents an input or output channel of a node in an AudioProcessorGraph.
*/
struct NodeAndChannel
{
NodeID nodeID;
int channelIndex;
bool isMIDI() const noexcept { return channelIndex == midiChannelIndex; }
bool operator== (const NodeAndChannel& other) const noexcept { return nodeID == other.nodeID && channelIndex == other.channelIndex; }
bool operator!= (const NodeAndChannel& other) const noexcept { return ! operator== (other); }
};
//==============================================================================
/** Represents one of the nodes, or processors, in an AudioProcessorGraph.
To create a node, call AudioProcessorGraph::addNode().
*/
class JUCE_API Node : public ReferenceCountedObject
{
public:
//==============================================================================
/** The ID number assigned to this node.
This is assigned by the graph that owns it, and can't be changed.
*/
const NodeID nodeID;
/** The actual processor object that this node represents. */
AudioProcessor* getProcessor() const noexcept { return processor.get(); }
/** A set of user-definable properties that are associated with this node.
This can be used to attach values to the node for whatever purpose seems
useful. For example, you might store an x and y position if your application
is displaying the nodes on-screen.
*/
NamedValueSet properties;
//==============================================================================
/** Returns if the node is bypassed or not. */
bool isBypassed() const noexcept;
/** Tell this node to bypass processing. */
void setBypassed (bool shouldBeBypassed) noexcept;
//==============================================================================
/** A convenient typedef for referring to a pointer to a node object. */
using Ptr = ReferenceCountedObjectPtr<Node>;
private:
//==============================================================================
friend class AudioProcessorGraph;
template <typename Float>
friend struct GraphRenderSequence;
template <typename Float>
friend struct RenderSequenceBuilder;
struct Connection
{
Node* otherNode;
int otherChannel, thisChannel;
bool operator== (const Connection&) const noexcept;
};
std::unique_ptr<AudioProcessor> processor;
Array<Connection> inputs, outputs;
bool isPrepared = false;
std::atomic<bool> bypassed { false };
Node (NodeID, std::unique_ptr<AudioProcessor>) noexcept;
void setParentGraph (AudioProcessorGraph*) const;
void prepare (double newSampleRate, int newBlockSize, AudioProcessorGraph*, ProcessingPrecision);
void unprepare();
template <typename Sample>
void processBlock (AudioBuffer<Sample>& audio, MidiBuffer& midi)
{
const ScopedLock lock (processorLock);
processor->processBlock (audio, midi);
}
template <typename Sample>
void processBlockBypassed (AudioBuffer<Sample>& audio, MidiBuffer& midi)
{
const ScopedLock lock (processorLock);
processor->processBlockBypassed (audio, midi);
}
CriticalSection processorLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Node)
};
//==============================================================================
/** Represents a connection between two channels of two nodes in an AudioProcessorGraph.
To create a connection, use AudioProcessorGraph::addConnection().
*/
struct JUCE_API Connection
{
//==============================================================================
Connection() = default;
Connection (NodeAndChannel source, NodeAndChannel destination) noexcept;
Connection (const Connection&) = default;
Connection& operator= (const Connection&) = default;
bool operator== (const Connection&) const noexcept;
bool operator!= (const Connection&) const noexcept;
bool operator< (const Connection&) const noexcept;
//==============================================================================
/** The channel and node which is the input source for this connection. */
NodeAndChannel source { {}, 0 };
/** The channel and node which is the input source for this connection. */
NodeAndChannel destination { {}, 0 };
};
//==============================================================================
/** Deletes all nodes and connections from this graph.
Any processor objects in the graph will be deleted.
*/
void clear();
/** Returns the array of nodes in the graph. */
const ReferenceCountedArray<Node>& getNodes() const noexcept { return nodes; }
/** Returns the number of nodes in the graph. */
int getNumNodes() const noexcept { return nodes.size(); }
/** Returns a pointer to one of the nodes in the graph.
This will return nullptr if the index is out of range.
@see getNodeForId
*/
Node::Ptr getNode (int index) const noexcept { return nodes[index]; }
/** Searches the graph for a node with the given ID number and returns it.
If no such node was found, this returns nullptr.
@see getNode
*/
Node* getNodeForId (NodeID) const;
/** Adds a node to the graph.
This creates a new node in the graph, for the specified processor. Once you have
added a processor to the graph, the graph owns it and will delete it later when
it is no longer needed.
The optional nodeId parameter lets you specify a unique ID to use for the node.
If the value is already in use, this method will fail and return an empty node.
If this succeeds, it returns a pointer to the newly-created node.
*/
Node::Ptr addNode (std::unique_ptr<AudioProcessor> newProcessor, NodeID nodeId = {});
/** Deletes a node within the graph which has the specified ID.
This will also delete any connections that are attached to this node.
*/
Node::Ptr removeNode (NodeID);
/** Deletes a node within the graph.
This will also delete any connections that are attached to this node.
*/
Node::Ptr removeNode (Node*);
/** Returns the list of connections in the graph. */
std::vector<Connection> getConnections() const;
/** Returns true if the given connection exists. */
bool isConnected (const Connection&) const noexcept;
/** Returns true if there is a direct connection between any of the channels of
two specified nodes.
*/
bool isConnected (NodeID possibleSourceNodeID, NodeID possibleDestNodeID) const noexcept;
/** Does a recursive check to see if there's a direct or indirect series of connections
between these two nodes.
*/
bool isAnInputTo (Node& source, Node& destination) const noexcept;
/** Returns true if it would be legal to connect the specified points. */
bool canConnect (const Connection&) const;
/** Attempts to connect two specified channels of two nodes.
If this isn't allowed (e.g. because you're trying to connect a midi channel
to an audio one or other such nonsense), then it'll return false.
*/
bool addConnection (const Connection&);
/** Deletes the given connection. */
bool removeConnection (const Connection&);
/** Removes all connections from the specified node. */
bool disconnectNode (NodeID);
/** Returns true if the given connection's channel numbers map on to valid
channels at each end.
Even if a connection is valid when created, its status could change if
a node changes its channel config.
*/
bool isConnectionLegal (const Connection&) const;
/** Performs a sanity checks of all the connections.
This might be useful if some of the processors are doing things like changing
their channel counts, which could render some connections obsolete.
*/
bool removeIllegalConnections();
//==============================================================================
/** A special type of AudioProcessor that can live inside an AudioProcessorGraph
in order to use the audio that comes into and out of the graph itself.
If you create an AudioGraphIOProcessor in "input" mode, it will act as a
node in the graph which delivers the audio that is coming into the parent
graph. This allows you to stream the data to other nodes and process the
incoming audio.
Likewise, one of these in "output" mode can be sent data which it will add to
the sum of data being sent to the graph's output.
@see AudioProcessorGraph
*/
class JUCE_API AudioGraphIOProcessor : public AudioPluginInstance
{
public:
/** Specifies the mode in which this processor will operate.
*/
enum IODeviceType
{
audioInputNode, /**< In this mode, the processor has output channels
representing all the audio input channels that are
coming into its parent audio graph. */
audioOutputNode, /**< In this mode, the processor has input channels
representing all the audio output channels that are
going out of its parent audio graph. */
midiInputNode, /**< In this mode, the processor has a midi output which
delivers the same midi data that is arriving at its
parent graph. */
midiOutputNode /**< In this mode, the processor has a midi input and
any data sent to it will be passed out of the parent
graph. */
};
//==============================================================================
/** Returns the mode of this processor. */
IODeviceType getType() const noexcept { return type; }
/** Returns the parent graph to which this processor belongs, or nullptr if it
hasn't yet been added to one. */
AudioProcessorGraph* getParentGraph() const noexcept { return graph; }
/** True if this is an audio or midi input. */
bool isInput() const noexcept;
/** True if this is an audio or midi output. */
bool isOutput() const noexcept;
//==============================================================================
AudioGraphIOProcessor (IODeviceType);
~AudioGraphIOProcessor() override;
const String getName() const override;
void fillInPluginDescription (PluginDescription&) const override;
void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override;
void releaseResources() override;
void processBlock (AudioBuffer<float>& , MidiBuffer&) override;
void processBlock (AudioBuffer<double>&, MidiBuffer&) override;
bool supportsDoublePrecisionProcessing() const override;
double getTailLengthSeconds() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
bool hasEditor() const override;
AudioProcessorEditor* createEditor() override;
int getNumPrograms() override;
int getCurrentProgram() override;
void setCurrentProgram (int) override;
const String getProgramName (int) override;
void changeProgramName (int, const String&) override;
void getStateInformation (juce::MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
/** @internal */
void setParentGraph (AudioProcessorGraph*);
private:
const IODeviceType type;
AudioProcessorGraph* graph = nullptr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioGraphIOProcessor)
};
//==============================================================================
const String getName() const override;
void prepareToPlay (double, int) override;
void releaseResources() override;
void processBlock (AudioBuffer<float>&, MidiBuffer&) override;
void processBlock (AudioBuffer<double>&, MidiBuffer&) override;
bool supportsDoublePrecisionProcessing() const override;
void reset() override;
void setNonRealtime (bool) noexcept override;
double getTailLengthSeconds() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
bool hasEditor() const override { return false; }
AudioProcessorEditor* createEditor() override { return nullptr; }
int getNumPrograms() override { return 0; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram (int) override { }
const String getProgramName (int) override { return {}; }
void changeProgramName (int, const String&) override { }
void getStateInformation (juce::MemoryBlock&) override;
void setStateInformation (const void* data, int sizeInBytes) override;
private:
struct PrepareSettings
{
ProcessingPrecision precision = ProcessingPrecision::singlePrecision;
double sampleRate = 0.0;
int blockSize = 0;
bool valid = false;
using Tied = std::tuple<const ProcessingPrecision&,
const double&,
const int&,
const bool&>;
Tied tie() const noexcept { return std::tie (precision, sampleRate, blockSize, valid); }
bool operator== (const PrepareSettings& other) const noexcept { return tie() == other.tie(); }
bool operator!= (const PrepareSettings& other) const noexcept { return tie() != other.tie(); }
};
//==============================================================================
ReferenceCountedArray<Node> nodes;
NodeID lastNodeID = {};
struct RenderSequenceFloat;
struct RenderSequenceDouble;
std::unique_ptr<RenderSequenceFloat> renderSequenceFloat;
std::unique_ptr<RenderSequenceDouble> renderSequenceDouble;
PrepareSettings prepareSettings;
friend class AudioGraphIOProcessor;
std::atomic<bool> isPrepared { false };
void topologyChanged();
void unprepare();
void handleAsyncUpdate() override;
void clearRenderingSequence();
void buildRenderingSequence();
bool anyNodesNeedPreparing() const noexcept;
bool isConnected (Node* src, int sourceChannel, Node* dest, int destChannel) const noexcept;
bool isAnInputTo (Node& src, Node& dst, int recursionCheck) const noexcept;
bool canConnect (Node* src, int sourceChannel, Node* dest, int destChannel) const noexcept;
bool isLegal (Node* src, int sourceChannel, Node* dest, int destChannel) const noexcept;
static void getNodeConnections (Node&, std::vector<Connection>&);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph)
};
} // namespace juce

View File

@ -0,0 +1,137 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Base class for listeners that want to know about changes to an AudioProcessor.
Use AudioProcessor::addListener() to register your listener with an AudioProcessor.
@see AudioProcessor
@tags{Audio}
*/
class JUCE_API AudioProcessorListener
{
public:
//==============================================================================
/** Destructor. */
virtual ~AudioProcessorListener() = default;
//==============================================================================
/** Receives a callback when a parameter is changed.
IMPORTANT NOTE: This will be called synchronously when a parameter changes, and
many audio processors will change their parameter during their audio callback.
This means that not only has your handler code got to be completely thread-safe,
but it's also got to be VERY fast, and avoid blocking. If you need to handle
this event on your message thread, use this callback to trigger an AsyncUpdater
or ChangeBroadcaster which you can respond to on the message thread.
*/
virtual void audioProcessorParameterChanged (AudioProcessor* processor,
int parameterIndex,
float newValue) = 0;
/** Provides details about aspects of an AudioProcessor which have changed.
*/
struct JUCE_API ChangeDetails
{
bool latencyChanged = false;
bool parameterInfoChanged = false;
bool programChanged = false;
ChangeDetails withLatencyChanged (bool b) const noexcept { return with (&ChangeDetails::latencyChanged, b); }
ChangeDetails withParameterInfoChanged (bool b) const noexcept { return with (&ChangeDetails::parameterInfoChanged, b); }
ChangeDetails withProgramChanged (bool b) const noexcept { return with (&ChangeDetails::programChanged, b); }
static ChangeDetails getAllChanged()
{
return ChangeDetails{}.withLatencyChanged (true)
.withParameterInfoChanged (true)
.withProgramChanged (true);
}
private:
template <typename Member, typename Value>
ChangeDetails with (Member&& member, Value&& value) const noexcept
{
auto copy = *this;
copy.*member = std::forward<Value> (value);
return copy;
}
};
/** Called to indicate that something else in the plugin has changed, like its
program, number of parameters, etc.
IMPORTANT NOTE: This will be called synchronously, and many audio processors will
call it during their audio callback. This means that not only has your handler code
got to be completely thread-safe, but it's also got to be VERY fast, and avoid
blocking. If you need to handle this event on your message thread, use this callback
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the
message thread.
*/
virtual void audioProcessorChanged (AudioProcessor* processor, const ChangeDetails& details) = 0;
/** Indicates that a parameter change gesture has started.
E.g. if the user is dragging a slider, this would be called when they first
press the mouse button, and audioProcessorParameterChangeGestureEnd would be
called when they release it.
IMPORTANT NOTE: This will be called synchronously, and many audio processors will
call it during their audio callback. This means that not only has your handler code
got to be completely thread-safe, but it's also got to be VERY fast, and avoid
blocking. If you need to handle this event on your message thread, use this callback
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the
message thread.
@see audioProcessorParameterChangeGestureEnd
*/
virtual void audioProcessorParameterChangeGestureBegin (AudioProcessor* processor,
int parameterIndex);
/** Indicates that a parameter change gesture has finished.
E.g. if the user is dragging a slider, this would be called when they release
the mouse button.
IMPORTANT NOTE: This will be called synchronously, and many audio processors will
call it during their audio callback. This means that not only has your handler code
got to be completely thread-safe, but it's also got to be VERY fast, and avoid
blocking. If you need to handle this event on your message thread, use this callback
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the
message thread.
@see audioProcessorParameterChangeGestureBegin
*/
virtual void audioProcessorParameterChangeGestureEnd (AudioProcessor* processor,
int parameterIndex);
};
} // namespace juce

View File

@ -0,0 +1,305 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
class AudioProcessor;
//==============================================================================
/** An abstract base class for parameter objects that can be added to an
AudioProcessor.
@see AudioProcessor::addParameter
@tags{Audio}
*/
class JUCE_API AudioProcessorParameter
{
public:
AudioProcessorParameter() noexcept;
/** Destructor. */
virtual ~AudioProcessorParameter();
/** Called by the host to find out the value of this parameter.
Hosts will expect the value returned to be between 0 and 1.0.
This could be called quite frequently, so try to make your code efficient.
It's also likely to be called by non-UI threads, so the code in here should
be thread-aware.
*/
virtual float getValue() const = 0;
/** The host will call this method to change the value of a parameter.
The host may call this at any time, including during the audio processing
callback, so your implementation has to process this very efficiently and
avoid any kind of locking.
If you want to set the value of a parameter internally, e.g. from your
editor component, then don't call this directly - instead, use the
setValueNotifyingHost() method, which will also send a message to
the host telling it about the change. If the message isn't sent, the host
won't be able to automate your parameters properly.
The value passed will be between 0 and 1.0.
*/
virtual void setValue (float newValue) = 0;
/** A processor should call this when it needs to change one of its parameters.
This could happen when the editor or some other internal operation changes
a parameter. This method will call the setValue() method to change the
value, and will then send a message to the host telling it about the change.
Note that to make sure the host correctly handles automation, you should call
the beginChangeGesture() and endChangeGesture() methods to tell the host when
the user has started and stopped changing the parameter.
*/
void setValueNotifyingHost (float newValue);
/** Sends a signal to the host to tell it that the user is about to start changing this
parameter.
This allows the host to know when a parameter is actively being held by the user, and
it may use this information to help it record automation.
If you call this, it must be matched by a later call to endChangeGesture().
*/
void beginChangeGesture();
/** Tells the host that the user has finished changing this parameter.
This allows the host to know when a parameter is actively being held by the user,
and it may use this information to help it record automation.
A call to this method must follow a call to beginChangeGesture().
*/
void endChangeGesture();
/** This should return the default value for this parameter. */
virtual float getDefaultValue() const = 0;
/** Returns the name to display for this parameter, which should be made
to fit within the given string length.
*/
virtual String getName (int maximumStringLength) const = 0;
/** Some parameters may be able to return a label string for
their units. For example "Hz" or "%".
*/
virtual String getLabel() const = 0;
/** Returns the number of steps that this parameter's range should be quantised into.
If you want a continuous range of values, don't override this method, and allow
the default implementation to return AudioProcessor::getDefaultNumParameterSteps().
If your parameter is boolean, then you may want to make this return 2.
The value that is returned may or may not be used, depending on the host. If you
want the host to display stepped automation values, rather than a continuous
interpolation between successive values, you should override isDiscrete to return true.
@see isDiscrete
*/
virtual int getNumSteps() const;
/** Returns whether the parameter uses discrete values, based on the result of
getNumSteps, or allows the host to select values continuously.
This information may or may not be used, depending on the host. If you
want the host to display stepped automation values, rather than a continuous
interpolation between successive values, override this method to return true.
@see getNumSteps
*/
virtual bool isDiscrete() const;
/** Returns whether the parameter represents a boolean switch, typically with
"On" and "Off" states.
This information may or may not be used, depending on the host. If you
want the host to display a switch, rather than a two item dropdown menu,
override this method to return true. You also need to override
isDiscrete() to return `true` and getNumSteps() to return `2`.
@see isDiscrete getNumSteps
*/
virtual bool isBoolean() const;
/** Returns a textual version of the supplied normalised parameter value.
The default implementation just returns the floating point value
as a string, but this could do anything you need for a custom type
of value.
*/
virtual String getText (float normalisedValue, int /*maximumStringLength*/) const;
/** Should parse a string and return the appropriate value for it. */
virtual float getValueForText (const String& text) const = 0;
/** This can be overridden to tell the host that this parameter operates in the
reverse direction.
(Not all plugin formats or hosts will actually use this information).
*/
virtual bool isOrientationInverted() const;
/** Returns true if the host can automate this parameter.
By default, this returns true.
*/
virtual bool isAutomatable() const;
/** Should return true if this parameter is a "meta" parameter.
A meta-parameter is a parameter that changes other params. It is used
by some hosts (e.g. AudioUnit hosts).
By default this returns false.
*/
virtual bool isMetaParameter() const;
enum Category
{
genericParameter = (0 << 16) | 0, /** If your parameter is not a meter then you should use this category */
inputGain = (1 << 16) | 0, /** Currently not used */
outputGain = (1 << 16) | 1,
/** The following categories tell the host that this parameter is a meter level value
and therefore read-only. Most hosts will display these type of parameters as
a meter in the generic view of your plug-in. Pro-Tools will also show the meter
in the mixer view.
*/
inputMeter = (2 << 16) | 0,
outputMeter = (2 << 16) | 1,
compressorLimiterGainReductionMeter = (2 << 16) | 2,
expanderGateGainReductionMeter = (2 << 16) | 3,
analysisMeter = (2 << 16) | 4,
otherMeter = (2 << 16) | 5
};
/** Returns the parameter's category. */
virtual Category getCategory() const;
/** Returns the index of this parameter in its parent processor's parameter list. */
int getParameterIndex() const noexcept { return parameterIndex; }
//==============================================================================
/** Returns the current value of the parameter as a String.
This function can be called when you are hosting plug-ins to get a
more specialised textual representation of the current value from the
plug-in, for example "On" rather than "1.0".
If you are implementing a plug-in then you should ignore this function
and instead override getText.
*/
virtual String getCurrentValueAsText() const;
/** Returns the set of strings which represent the possible states a parameter
can be in.
If you are hosting a plug-in you can use the result of this function to
populate a ComboBox listing the allowed values.
If you are implementing a plug-in then you do not need to override this.
*/
virtual StringArray getAllValueStrings() const;
//==============================================================================
/**
A base class for listeners that want to know about changes to an
AudioProcessorParameter.
Use AudioProcessorParameter::addListener() to register your listener with
an AudioProcessorParameter.
This Listener replaces most of the functionality in the
AudioProcessorListener class, which will be deprecated and removed.
*/
class JUCE_API Listener
{
public:
/** Destructor. */
virtual ~Listener() = default;
/** Receives a callback when a parameter has been changed.
IMPORTANT NOTE: This will be called synchronously when a parameter changes, and
many audio processors will change their parameter during their audio callback.
This means that not only has your handler code got to be completely thread-safe,
but it's also got to be VERY fast, and avoid blocking. If you need to handle
this event on your message thread, use this callback to trigger an AsyncUpdater
or ChangeBroadcaster which you can respond to on the message thread.
*/
virtual void parameterValueChanged (int parameterIndex, float newValue) = 0;
/** Indicates that a parameter change gesture has started.
E.g. if the user is dragging a slider, this would be called with gestureIsStarting
being true when they first press the mouse button, and it will be called again with
gestureIsStarting being false when they release it.
IMPORTANT NOTE: This will be called synchronously, and many audio processors will
call it during their audio callback. This means that not only has your handler code
got to be completely thread-safe, but it's also got to be VERY fast, and avoid
blocking. If you need to handle this event on your message thread, use this callback
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the
message thread.
*/
virtual void parameterGestureChanged (int parameterIndex, bool gestureIsStarting) = 0;
};
/** Registers a listener to receive events when the parameter's state changes.
If the listener is already registered, this will not register it again.
@see removeListener
*/
void addListener (Listener* newListener);
/** Removes a previously registered parameter listener
@see addListener
*/
void removeListener (Listener* listener);
//==============================================================================
/** @internal */
void sendValueChangedMessageToListeners (float newValue);
private:
//==============================================================================
friend class AudioProcessor;
friend class LegacyAudioParameter;
AudioProcessor* processor = nullptr;
int parameterIndex = -1;
CriticalSection listenerLock;
Array<Listener*> listeners;
mutable StringArray valueStrings;
#if JUCE_DEBUG
bool isPerformingGesture = false;
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameter)
};
} // namespace juce

View File

@ -0,0 +1,321 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
AudioProcessorParameterGroup::AudioProcessorParameterNode::~AudioProcessorParameterNode() = default;
AudioProcessorParameterGroup::AudioProcessorParameterNode::AudioProcessorParameterNode (AudioProcessorParameterNode&& other)
: group (std::move (other.group)), parameter (std::move (other.parameter))
{
if (group != nullptr)
group->parent = parent;
}
AudioProcessorParameterGroup::AudioProcessorParameterNode::AudioProcessorParameterNode (std::unique_ptr<AudioProcessorParameter> param,
AudioProcessorParameterGroup* parentGroup)
: parameter (std::move (param)), parent (parentGroup)
{}
AudioProcessorParameterGroup::AudioProcessorParameterNode::AudioProcessorParameterNode (std::unique_ptr<AudioProcessorParameterGroup> grp,
AudioProcessorParameterGroup* parentGroup)
: group (std::move (grp)), parent (parentGroup)
{
group->parent = parent;
}
AudioProcessorParameterGroup* AudioProcessorParameterGroup::AudioProcessorParameterNode::getParent() const { return parent; }
AudioProcessorParameter* AudioProcessorParameterGroup::AudioProcessorParameterNode::getParameter() const { return parameter.get(); }
AudioProcessorParameterGroup* AudioProcessorParameterGroup::AudioProcessorParameterNode::getGroup() const { return group.get(); }
//==============================================================================
AudioProcessorParameterGroup::AudioProcessorParameterGroup() = default;
AudioProcessorParameterGroup::AudioProcessorParameterGroup (String groupID, String groupName, String subgroupSeparator)
: identifier (std::move (groupID)), name (std::move (groupName)), separator (std::move (subgroupSeparator))
{
}
AudioProcessorParameterGroup::~AudioProcessorParameterGroup() = default;
AudioProcessorParameterGroup::AudioProcessorParameterGroup (AudioProcessorParameterGroup&& other)
: identifier (std::move (other.identifier)),
name (std::move (other.name)),
separator (std::move (other.separator)),
children (std::move (other.children))
{
updateChildParentage();
}
AudioProcessorParameterGroup& AudioProcessorParameterGroup::operator= (AudioProcessorParameterGroup&& other)
{
identifier = std::move (other.identifier);
name = std::move (other.name);
separator = std::move (other.separator);
children = std::move (other.children);
updateChildParentage();
return *this;
}
void AudioProcessorParameterGroup::updateChildParentage()
{
for (auto* child : children)
{
child->parent = this;
if (auto* group = child->getGroup())
group->parent = this;
}
}
String AudioProcessorParameterGroup::getID() const { return identifier; }
String AudioProcessorParameterGroup::getName() const { return name; }
String AudioProcessorParameterGroup::getSeparator() const { return separator; }
const AudioProcessorParameterGroup* AudioProcessorParameterGroup::getParent() const noexcept { return parent; }
void AudioProcessorParameterGroup::setName (String newName) { name = std::move (newName); }
const AudioProcessorParameterGroup::AudioProcessorParameterNode* const* AudioProcessorParameterGroup::begin() const noexcept { return const_cast<const AudioProcessorParameterNode**> (children.begin()); }
const AudioProcessorParameterGroup::AudioProcessorParameterNode* const* AudioProcessorParameterGroup::end() const noexcept { return const_cast<const AudioProcessorParameterNode**> (children.end()); }
void AudioProcessorParameterGroup::append (std::unique_ptr<AudioProcessorParameter> newParameter)
{
children.add (new AudioProcessorParameterNode (std::move (newParameter), this));
}
void AudioProcessorParameterGroup::append (std::unique_ptr<AudioProcessorParameterGroup> newSubGroup)
{
children.add (new AudioProcessorParameterNode (std::move (newSubGroup), this));
}
Array<const AudioProcessorParameterGroup*> AudioProcessorParameterGroup::getSubgroups (bool recursive) const
{
Array<const AudioProcessorParameterGroup*> groups;
getSubgroups (groups, recursive);
return groups;
}
Array<AudioProcessorParameter*> AudioProcessorParameterGroup::getParameters (bool recursive) const
{
Array<AudioProcessorParameter*> parameters;
getParameters (parameters, recursive);
return parameters;
}
Array<const AudioProcessorParameterGroup*> AudioProcessorParameterGroup::getGroupsForParameter (AudioProcessorParameter* parameter) const
{
Array<const AudioProcessorParameterGroup*> groups;
if (auto* group = getGroupForParameter (parameter))
{
while (group != nullptr && group != this)
{
groups.insert (0, group);
group = group->getParent();
}
}
return groups;
}
void AudioProcessorParameterGroup::getSubgroups (Array<const AudioProcessorParameterGroup*>& previousGroups, bool recursive) const
{
for (auto* child : children)
{
if (auto* group = child->getGroup())
{
previousGroups.add (group);
if (recursive)
group->getSubgroups (previousGroups, true);
}
}
}
void AudioProcessorParameterGroup::getParameters (Array<AudioProcessorParameter*>& previousParameters, bool recursive) const
{
for (auto* child : children)
{
if (auto* parameter = child->getParameter())
previousParameters.add (parameter);
else if (recursive)
child->getGroup()->getParameters (previousParameters, true);
}
}
const AudioProcessorParameterGroup* AudioProcessorParameterGroup::getGroupForParameter (AudioProcessorParameter* parameter) const
{
for (auto* child : children)
{
if (child->getParameter() == parameter)
return this;
if (auto* group = child->getGroup())
if (auto* foundGroup = group->getGroupForParameter (parameter))
return foundGroup;
}
return nullptr;
}
//==============================================================================
#if JUCE_UNIT_TESTS
class ParameterGroupTests : public UnitTest
{
public:
ParameterGroupTests()
: UnitTest ("ParameterGroups", UnitTestCategories::audioProcessorParameters)
{}
void runTest() override
{
beginTest ("ParameterGroups");
auto g1 = std::make_unique<AudioProcessorParameterGroup> ("g1", "g1", " - ");
auto* p1 = new AudioParameterFloat ("p1", "p1", { 0.0f, 2.0f }, 0.5f);
auto* p2 = new AudioParameterFloat ("p2", "p2", { 0.0f, 2.0f }, 0.5f);
auto* p3 = new AudioParameterFloat ("p3", "p3", { 0.0f, 2.0f }, 0.5f);
g1->addChild (std::unique_ptr<AudioParameterFloat> (p1));
g1->addChild (std::unique_ptr<AudioParameterFloat> (p2),
std::unique_ptr<AudioParameterFloat> (p3));
auto p4 = std::make_unique<AudioParameterFloat> ("p4", "p4", NormalisableRange<float> (0.0f, 2.0f), 0.5f);
auto p5 = std::make_unique<AudioParameterFloat> ("p5", "p5", NormalisableRange<float> (0.0f, 2.0f), 0.5f);
auto p6 = std::make_unique<AudioParameterFloat> ("p6", "p6", NormalisableRange<float> (0.0f, 2.0f), 0.5f);
g1->addChild (std::move (p4));
g1->addChild (std::move (p5),
std::move (p6));
{
auto topLevelParams = g1->getParameters (false);
auto params = g1->getParameters (true);
expect (topLevelParams == params);
expectEquals (params.size(), 6);
expect (params[0] == (AudioProcessorParameter*) p1);
expect (params[1] == (AudioProcessorParameter*) p2);
expect (params[2] == (AudioProcessorParameter*) p3);
expect (dynamic_cast<AudioParameterFloat*> (params[3])->name == "p4");
expect (dynamic_cast<AudioParameterFloat*> (params[4])->name == "p5");
expect (dynamic_cast<AudioParameterFloat*> (params[5])->name == "p6");
}
auto* p7 = new AudioParameterFloat ("p7", "p7", { 0.0f, 2.0f }, 0.5f);
auto* p8 = new AudioParameterFloat ("p8", "p8", { 0.0f, 2.0f }, 0.5f);
auto* p9 = new AudioParameterFloat ("p9", "p9", { 0.0f, 2.0f }, 0.5f);
auto p10 = std::make_unique<AudioParameterFloat> ("p10", "p10", NormalisableRange<float> (0.0f, 2.0f), 0.5f);
auto p11 = std::make_unique<AudioParameterFloat> ("p11", "p11", NormalisableRange<float> (0.0f, 2.0f), 0.5f);
auto p12 = std::make_unique<AudioParameterFloat> ("p12", "p12", NormalisableRange<float> (0.0f, 2.0f), 0.5f);
auto g2 = std::make_unique<AudioProcessorParameterGroup> ("g2", "g2", " | ", std::unique_ptr<AudioParameterFloat> (p7));
auto g3 = std::make_unique<AudioProcessorParameterGroup> ("g3", "g3", " | ", std::unique_ptr<AudioParameterFloat> (p8), std::unique_ptr<AudioParameterFloat> (p9));
auto g4 = std::make_unique<AudioProcessorParameterGroup> ("g4", "g4", " | ", std::move (p10));
auto g5 = std::make_unique<AudioProcessorParameterGroup> ("g5", "g5", " | ", std::move (p11), std::move (p12));
g1->addChild (std::move (g2));
g4->addChild (std::move (g5));
g1->addChild (std::move (g3), std::move (g4));
{
auto topLevelParams = g1->getParameters (false);
auto params = g1->getParameters (true);
expectEquals (topLevelParams.size(), 6);
expectEquals (params.size(), 12);
expect (params[0] == (AudioProcessorParameter*) p1);
expect (params[1] == (AudioProcessorParameter*) p2);
expect (params[2] == (AudioProcessorParameter*) p3);
expect (dynamic_cast<AudioParameterFloat*> (params[3])->name == "p4");
expect (dynamic_cast<AudioParameterFloat*> (params[4])->name == "p5");
expect (dynamic_cast<AudioParameterFloat*> (params[5])->name == "p6");
expect (params[6] == (AudioProcessorParameter*) p7);
expect (params[7] == (AudioProcessorParameter*) p8);
expect (params[8] == (AudioProcessorParameter*) p9);
expect (dynamic_cast<AudioParameterFloat*> (params[9]) ->name == "p10");
expect (dynamic_cast<AudioParameterFloat*> (params[10])->name == "p11");
expect (dynamic_cast<AudioParameterFloat*> (params[11])->name == "p12");
}
g1->addChild (std::make_unique<AudioProcessorParameterGroup> ("g6", "g6", " | ",
std::make_unique<AudioParameterFloat> ("p13", "p13", NormalisableRange<float> (0.0f, 2.0f), 0.5f),
std::make_unique<AudioProcessorParameterGroup> ("g7", "g7", " | ",
std::make_unique<AudioParameterFloat> ("p14", "p14", NormalisableRange<float> (0.0f, 2.0f), 0.5f)),
std::make_unique<AudioParameterFloat> ("p15", "p15", NormalisableRange<float> (0.0f, 2.0f), 0.5f)));
TestAudioProcessor processor;
processor.addParameter (new AudioParameterFloat ("pstart", "pstart", NormalisableRange<float> (0.0f, 2.0f), 0.5f));
auto groupParams = g1->getParameters (true);
processor.addParameterGroup (std::move (g1));
processor.addParameter (new AudioParameterFloat ("pend", "pend", NormalisableRange<float> (0.0f, 2.0f), 0.5f));
auto& processorParams = processor.getParameters();
expect (dynamic_cast<AudioParameterFloat*> (processorParams.getFirst())->name == "pstart");
expect (dynamic_cast<AudioParameterFloat*> (processorParams.getLast()) ->name == "pend");
auto numParams = processorParams.size();
for (int i = 1; i < numParams - 1; ++i)
expect (processorParams[i] == groupParams[i - 1]);
}
private:
struct TestAudioProcessor : public AudioProcessor
{
const String getName() const override { return "ap"; }
void prepareToPlay (double, int) override {}
void releaseResources() override {}
void processBlock (AudioBuffer<float>&, MidiBuffer&) override {}
using AudioProcessor::processBlock;
double getTailLengthSeconds() const override { return 0.0; }
bool acceptsMidi() const override { return false; }
bool producesMidi() const override { return false; }
AudioProcessorEditor* createEditor() override { return nullptr; }
bool hasEditor() const override { return false; }
int getNumPrograms() override { return 0; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram (int) override {}
const String getProgramName (int) override { return {}; }
void changeProgramName (int, const String&) override {}
void getStateInformation (MemoryBlock&) override {}
void setStateInformation (const void*, int) override {}
};
};
static ParameterGroupTests parameterGroupTests;
#endif
} // namespace juce

View File

@ -0,0 +1,254 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** A class encapsulating a group of AudioProcessorParameters and nested
AudioProcessorParameterGroups.
This class is predominantly write-only; there are methods for adding group
members but none for removing them. Ultimately you will probably want to
add a fully constructed group to an AudioProcessor.
@see AudioProcessor::addParameterGroup
@tags{Audio}
*/
class AudioProcessorParameterGroup
{
public:
//==============================================================================
/** A child of an AudioProcessorParameterGroup.
This can contain either an AudioProcessorParameter or an
AudioProcessorParameterGroup. You can query which using the
getParameter and getGroup methods.
@code
for (auto* child : group)
if (auto* parameter = node.getParameter())
parameter->setValueNotifyingHost (0.5f);
else
node.getGroup()->AddChild (new Parameter());
@endcode
*/
class AudioProcessorParameterNode
{
public:
//==============================================================================
AudioProcessorParameterNode (AudioProcessorParameterNode&&);
~AudioProcessorParameterNode();
/** Returns the parent group or nullptr if this is a top-level group. */
AudioProcessorParameterGroup* getParent() const;
/** Returns a pointer to a parameter if this node contains a parameter, nullptr otherwise. */
AudioProcessorParameter* getParameter() const;
/** Returns a pointer to a group if this node contains a group, nullptr otherwise. */
AudioProcessorParameterGroup* getGroup() const;
private:
//==============================================================================
AudioProcessorParameterNode (std::unique_ptr<AudioProcessorParameter>, AudioProcessorParameterGroup*);
AudioProcessorParameterNode (std::unique_ptr<AudioProcessorParameterGroup>, AudioProcessorParameterGroup*);
std::unique_ptr<AudioProcessorParameterGroup> group;
std::unique_ptr<AudioProcessorParameter> parameter;
AudioProcessorParameterGroup* parent = nullptr;
friend class AudioProcessorParameterGroup;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameterNode)
};
//==============================================================================
/** Creates an empty AudioProcessorParameterGroup with no name or ID. */
AudioProcessorParameterGroup();
/** Creates an empty AudioProcessorParameterGroup.
@param groupID A unique identifier for the group. Keep it basic; don't use any special
characters like "." and avoid pure integer strings which could collide with
legacy parameter IDs.
@param groupName The group's name, which will be displayed in the host.
@param subgroupSeparator A separator string to use between the name of this group and the name of any
subgroups if this group is flattened. AUv3 and VST3 plug-ins can have multiple
layers of nested subgroups, but AU plug-ins cannot have any subgroups.
*/
AudioProcessorParameterGroup (String groupID, String groupName, String subgroupSeparator);
/** Creates an AudioProcessorParameterGroup with a single child.
@param groupID A unique identifier for the group. Keep it basic; don't use any special
characters like "." and avoid pure integer strings which could collide with
legacy parameter IDs.
@param groupName The group's name, which will be displayed in the host.
@param subgroupSeparator A separator string to use between the name of this group and the name of any
subgroups if this group is flattened. AUv3 and VST3 plug-ins can have multiple
layers of nested subgroups, but AU plug-ins cannot have any subgroups.
@param child An AudioProcessorParameter or an AudioProcessorParameterGroup to add to the group.
*/
template <typename ParameterOrGroup>
AudioProcessorParameterGroup (String groupID, String groupName, String subgroupSeparator,
std::unique_ptr<ParameterOrGroup> child)
: AudioProcessorParameterGroup (groupID, groupName, subgroupSeparator)
{
addChild (std::move (child));
}
/** Creates an AudioProcessorParameterGroup with multiple children.
@param groupID A unique identifier for the group. Keep it basic; don't use any special
characters like "." and avoid pure integer strings which could collide with
legacy parameter IDs.
@param groupName The group's name, which will be displayed in the host.
@param subgroupSeparator A separator string to use between the name of this group and the name of any
subgroups if this group is flattened. AUv3 and VST3 plug-ins can have multiple
layers of nested subgroups, but AU plug-ins cannot have any subgroups.
@param firstChild An AudioProcessorParameter or an AudioProcessorParameterGroup to add to the group.
@param remainingChildren A list of more AudioProcessorParameters or AudioProcessorParameterGroups to add to the group.
*/
template <typename ParameterOrGroup, typename... Args>
AudioProcessorParameterGroup (String groupID, String groupName, String subgroupSeparator,
std::unique_ptr<ParameterOrGroup> firstChild, Args&&... remainingChildren)
: AudioProcessorParameterGroup (groupID, groupName, subgroupSeparator, std::move (firstChild))
{
addChild (std::forward<Args> (remainingChildren)...);
}
/** Once a group has been added to an AudioProcessor don't try to mutate it by
moving or swapping it - this will crash most hosts.
*/
AudioProcessorParameterGroup (AudioProcessorParameterGroup&&);
/** Once a group has been added to an AudioProcessor don't try to mutate it by
moving or swapping it - this will crash most hosts.
*/
AudioProcessorParameterGroup& operator= (AudioProcessorParameterGroup&&);
/** Destructor. */
~AudioProcessorParameterGroup();
//==============================================================================
/** Returns the group's ID. */
String getID() const;
/** Returns the group's name. */
String getName() const;
/** Returns the group's separator string. */
String getSeparator() const;
/** Returns the parent of the group, or nullptr if this is a top-level group. */
const AudioProcessorParameterGroup* getParent() const noexcept;
//==============================================================================
/** Changes the name of the group. If you do this after the group has been added
to an AudioProcessor, call updateHostDisplay() to inform the host of the
change. Not all hosts support dynamic group name changes.
*/
void setName (String newName);
//==============================================================================
const AudioProcessorParameterNode* const* begin() const noexcept;
const AudioProcessorParameterNode* const* end() const noexcept;
//==============================================================================
/** Returns all subgroups of this group.
@param recursive If this is true then this method will fetch all nested
subgroups using a depth first search.
*/
Array<const AudioProcessorParameterGroup*> getSubgroups (bool recursive) const;
/** Returns all the parameters in this group.
@param recursive If this is true then this method will fetch all nested
parameters using a depth first search.
*/
Array<AudioProcessorParameter*> getParameters (bool recursive) const;
/** Searches this group recursively for a parameter and returns a depth ordered
list of the groups it belongs to.
*/
Array<const AudioProcessorParameterGroup*> getGroupsForParameter (AudioProcessorParameter*) const;
//==============================================================================
/** Adds a child to the group.
Do not add children to a group which has itself already been added to the
AudioProcessor - the new elements will be ignored.
*/
template <typename ParameterOrGroup>
void addChild (std::unique_ptr<ParameterOrGroup> child)
{
// If you hit a compiler error here then you are attempting to add a
// child that is neither a pointer to an AudioProcessorParameterGroup
// nor a pointer to an AudioProcessorParameter.
append (std::move (child));
}
/** Adds multiple parameters or sub-groups to this group.
Do not add children to a group which has itself already been added to the
AudioProcessor - the new elements will be ignored.
*/
template <typename ParameterOrGroup, typename... Args>
void addChild (std::unique_ptr<ParameterOrGroup> firstChild, Args&&... remainingChildren)
{
addChild (std::move (firstChild));
addChild (std::forward<Args> (remainingChildren)...);
}
#ifndef DOXYGEN
[[deprecated ("This class now has a move operator, so if you're trying to move them around, you "
"should use that, or if you really need to swap two groups, just call std::swap. "
"However, remember that swapping a group that's already owned by an AudioProcessor "
"will most likely crash the host, so don't do that.")]]
void swapWith (AudioProcessorParameterGroup& other) { std::swap (*this, other); }
#endif
private:
//==============================================================================
void getSubgroups (Array<const AudioProcessorParameterGroup*>&, bool recursive) const;
void getParameters (Array<AudioProcessorParameter*>&, bool recursive) const;
const AudioProcessorParameterGroup* getGroupForParameter (AudioProcessorParameter*) const;
void updateChildParentage();
void append (std::unique_ptr<AudioProcessorParameter>);
void append (std::unique_ptr<AudioProcessorParameterGroup>);
//==============================================================================
String identifier, name, separator;
OwnedArray<AudioProcessorParameterNode> children;
AudioProcessorParameterGroup* parent = nullptr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameterGroup)
};
} // namespace juce

View File

@ -0,0 +1,616 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
class ParameterListener : private AudioProcessorParameter::Listener,
private AudioProcessorListener,
private Timer
{
public:
ParameterListener (AudioProcessor& proc, AudioProcessorParameter& param)
: processor (proc), parameter (param), isLegacyParam (LegacyAudioParameter::isLegacy (&param))
{
if (isLegacyParam)
processor.addListener (this);
else
parameter.addListener (this);
startTimer (100);
}
~ParameterListener() override
{
if (isLegacyParam)
processor.removeListener (this);
else
parameter.removeListener (this);
}
AudioProcessorParameter& getParameter() const noexcept
{
return parameter;
}
virtual void handleNewParameterValue() = 0;
private:
//==============================================================================
void parameterValueChanged (int, float) override
{
parameterValueHasChanged = 1;
}
void parameterGestureChanged (int, bool) override {}
//==============================================================================
void audioProcessorParameterChanged (AudioProcessor*, int index, float) override
{
if (index == parameter.getParameterIndex())
parameterValueHasChanged = 1;
}
void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override {}
//==============================================================================
void timerCallback() override
{
if (parameterValueHasChanged.compareAndSetBool (0, 1))
{
handleNewParameterValue();
startTimerHz (50);
}
else
{
startTimer (jmin (250, getTimerInterval() + 10));
}
}
AudioProcessor& processor;
AudioProcessorParameter& parameter;
Atomic<int> parameterValueHasChanged { 0 };
const bool isLegacyParam;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterListener)
};
//==============================================================================
class BooleanParameterComponent final : public Component,
private ParameterListener
{
public:
BooleanParameterComponent (AudioProcessor& proc, AudioProcessorParameter& param)
: ParameterListener (proc, param)
{
// Set the initial value.
handleNewParameterValue();
button.onClick = [this] { buttonClicked(); };
addAndMakeVisible (button);
}
void paint (Graphics&) override {}
void resized() override
{
auto area = getLocalBounds();
area.removeFromLeft (8);
button.setBounds (area.reduced (0, 10));
}
private:
void handleNewParameterValue() override
{
button.setToggleState (isParameterOn(), dontSendNotification);
}
void buttonClicked()
{
if (isParameterOn() != button.getToggleState())
{
getParameter().beginChangeGesture();
getParameter().setValueNotifyingHost (button.getToggleState() ? 1.0f : 0.0f);
getParameter().endChangeGesture();
}
}
bool isParameterOn() const { return getParameter().getValue() >= 0.5f; }
ToggleButton button;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BooleanParameterComponent)
};
//==============================================================================
class SwitchParameterComponent final : public Component,
private ParameterListener
{
public:
SwitchParameterComponent (AudioProcessor& proc, AudioProcessorParameter& param)
: ParameterListener (proc, param)
{
for (auto& button : buttons)
{
button.setRadioGroupId (293847);
button.setClickingTogglesState (true);
}
buttons[0].setButtonText (getParameter().getText (0.0f, 16));
buttons[1].setButtonText (getParameter().getText (1.0f, 16));
buttons[0].setConnectedEdges (Button::ConnectedOnRight);
buttons[1].setConnectedEdges (Button::ConnectedOnLeft);
// Set the initial value.
buttons[0].setToggleState (true, dontSendNotification);
handleNewParameterValue();
buttons[1].onStateChange = [this] { rightButtonChanged(); };
for (auto& button : buttons)
addAndMakeVisible (button);
}
void paint (Graphics&) override {}
void resized() override
{
auto area = getLocalBounds().reduced (0, 8);
area.removeFromLeft (8);
for (auto& button : buttons)
button.setBounds (area.removeFromLeft (80));
}
private:
void handleNewParameterValue() override
{
bool newState = isParameterOn();
if (buttons[1].getToggleState() != newState)
{
buttons[1].setToggleState (newState, dontSendNotification);
buttons[0].setToggleState (! newState, dontSendNotification);
}
}
void rightButtonChanged()
{
auto buttonState = buttons[1].getToggleState();
if (isParameterOn() != buttonState)
{
getParameter().beginChangeGesture();
if (getParameter().getAllValueStrings().isEmpty())
{
getParameter().setValueNotifyingHost (buttonState ? 1.0f : 0.0f);
}
else
{
// When a parameter provides a list of strings we must set its
// value using those strings, rather than a float, because VSTs can
// have uneven spacing between the different allowed values and we
// want the snapping behaviour to be consistent with what we do with
// a combo box.
auto selectedText = buttons[buttonState ? 1 : 0].getButtonText();
getParameter().setValueNotifyingHost (getParameter().getValueForText (selectedText));
}
getParameter().endChangeGesture();
}
}
bool isParameterOn() const
{
if (getParameter().getAllValueStrings().isEmpty())
return getParameter().getValue() > 0.5f;
auto index = getParameter().getAllValueStrings()
.indexOf (getParameter().getCurrentValueAsText());
if (index < 0)
{
// The parameter is producing some unexpected text, so we'll do
// some linear interpolation.
index = roundToInt (getParameter().getValue());
}
return index == 1;
}
TextButton buttons[2];
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SwitchParameterComponent)
};
//==============================================================================
class ChoiceParameterComponent final : public Component,
private ParameterListener
{
public:
ChoiceParameterComponent (AudioProcessor& proc, AudioProcessorParameter& param)
: ParameterListener (proc, param),
parameterValues (getParameter().getAllValueStrings())
{
box.addItemList (parameterValues, 1);
// Set the initial value.
handleNewParameterValue();
box.onChange = [this] { boxChanged(); };
addAndMakeVisible (box);
}
void paint (Graphics&) override {}
void resized() override
{
auto area = getLocalBounds();
area.removeFromLeft (8);
box.setBounds (area.reduced (0, 10));
}
private:
void handleNewParameterValue() override
{
auto index = parameterValues.indexOf (getParameter().getCurrentValueAsText());
if (index < 0)
{
// The parameter is producing some unexpected text, so we'll do
// some linear interpolation.
index = roundToInt (getParameter().getValue() * (float) (parameterValues.size() - 1));
}
box.setSelectedItemIndex (index);
}
void boxChanged()
{
if (getParameter().getCurrentValueAsText() != box.getText())
{
getParameter().beginChangeGesture();
// When a parameter provides a list of strings we must set its
// value using those strings, rather than a float, because VSTs can
// have uneven spacing between the different allowed values.
getParameter().setValueNotifyingHost (getParameter().getValueForText (box.getText()));
getParameter().endChangeGesture();
}
}
ComboBox box;
const StringArray parameterValues;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChoiceParameterComponent)
};
//==============================================================================
class SliderParameterComponent final : public Component,
private ParameterListener
{
public:
SliderParameterComponent (AudioProcessor& proc, AudioProcessorParameter& param)
: ParameterListener (proc, param)
{
if (getParameter().getNumSteps() != AudioProcessor::getDefaultNumParameterSteps())
slider.setRange (0.0, 1.0, 1.0 / (getParameter().getNumSteps() - 1.0));
else
slider.setRange (0.0, 1.0);
slider.setDoubleClickReturnValue (true, param.getDefaultValue());
slider.setScrollWheelEnabled (false);
addAndMakeVisible (slider);
valueLabel.setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId));
valueLabel.setBorderSize ({ 1, 1, 1, 1 });
valueLabel.setJustificationType (Justification::centred);
addAndMakeVisible (valueLabel);
// Set the initial value.
handleNewParameterValue();
slider.onValueChange = [this] { sliderValueChanged(); };
slider.onDragStart = [this] { sliderStartedDragging(); };
slider.onDragEnd = [this] { sliderStoppedDragging(); };
}
void paint (Graphics&) override {}
void resized() override
{
auto area = getLocalBounds().reduced (0, 10);
valueLabel.setBounds (area.removeFromRight (80));
area.removeFromLeft (6);
slider.setBounds (area);
}
private:
void updateTextDisplay()
{
valueLabel.setText (getParameter().getCurrentValueAsText(), dontSendNotification);
}
void handleNewParameterValue() override
{
if (! isDragging)
{
slider.setValue (getParameter().getValue(), dontSendNotification);
updateTextDisplay();
}
}
void sliderValueChanged()
{
auto newVal = (float) slider.getValue();
if (getParameter().getValue() != newVal)
{
if (! isDragging)
getParameter().beginChangeGesture();
getParameter().setValueNotifyingHost ((float) slider.getValue());
updateTextDisplay();
if (! isDragging)
getParameter().endChangeGesture();
}
}
void sliderStartedDragging()
{
isDragging = true;
getParameter().beginChangeGesture();
}
void sliderStoppedDragging()
{
isDragging = false;
getParameter().endChangeGesture();
}
Slider slider { Slider::LinearHorizontal, Slider::TextEntryBoxPosition::NoTextBox };
Label valueLabel;
bool isDragging = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderParameterComponent)
};
//==============================================================================
class ParameterDisplayComponent : public Component,
private AudioProcessorListener,
private AsyncUpdater
{
public:
ParameterDisplayComponent (AudioProcessorEditor& editorIn, AudioProcessorParameter& param)
: editor (editorIn), parameter (param)
{
editor.processor.addListener (this);
parameterName.setText (parameter.getName (128), dontSendNotification);
parameterName.setJustificationType (Justification::centredRight);
parameterName.setInterceptsMouseClicks (false, false);
addAndMakeVisible (parameterName);
parameterLabel.setText (parameter.getLabel(), dontSendNotification);
parameterLabel.setInterceptsMouseClicks (false, false);
addAndMakeVisible (parameterLabel);
addAndMakeVisible (*(parameterComp = createParameterComp (editor.processor)));
setSize (400, 40);
}
~ParameterDisplayComponent() override
{
cancelPendingUpdate();
editor.processor.removeListener (this);
}
void resized() override
{
auto area = getLocalBounds();
parameterName.setBounds (area.removeFromLeft (100));
parameterLabel.setBounds (area.removeFromRight (50));
parameterComp->setBounds (area);
}
void mouseDown (const MouseEvent& e) override
{
if (e.mods.isRightButtonDown())
if (auto* context = editor.getHostContext())
if (auto menu = context->getContextMenuForParameterIndex (&parameter))
menu->getEquivalentPopupMenu().showMenuAsync (PopupMenu::Options().withTargetComponent (this)
.withMousePosition());
}
private:
AudioProcessorEditor& editor;
AudioProcessorParameter& parameter;
Label parameterName, parameterLabel;
std::unique_ptr<Component> parameterComp;
std::unique_ptr<Component> createParameterComp (AudioProcessor& processor) const
{
// The AU, AUv3 and VST (only via a .vstxml file) SDKs support
// marking a parameter as boolean. If you want consistency across
// all formats then it might be best to use a
// SwitchParameterComponent instead.
if (parameter.isBoolean())
return std::make_unique<BooleanParameterComponent> (processor, parameter);
// Most hosts display any parameter with just two steps as a switch.
if (parameter.getNumSteps() == 2)
return std::make_unique<SwitchParameterComponent> (processor, parameter);
// If we have a list of strings to represent the different states a
// parameter can be in then we should present a dropdown allowing a
// user to pick one of them.
if (! parameter.getAllValueStrings().isEmpty()
&& std::abs (parameter.getNumSteps() - parameter.getAllValueStrings().size()) <= 1)
return std::make_unique<ChoiceParameterComponent> (processor, parameter);
// Everything else can be represented as a slider.
return std::make_unique<SliderParameterComponent> (processor, parameter);
}
void audioProcessorParameterChanged (AudioProcessor*, int, float) override {}
void audioProcessorChanged (AudioProcessor*, const ChangeDetails& details) override
{
if (! details.parameterInfoChanged)
return;
if (MessageManager::getInstance()->isThisTheMessageThread())
handleAsyncUpdate();
else
triggerAsyncUpdate();
}
void handleAsyncUpdate() override
{
parameterName .setText (parameter.getName (128), dontSendNotification);
parameterLabel.setText (parameter.getLabel(), dontSendNotification);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterDisplayComponent)
};
//==============================================================================
struct ParamControlItem : public TreeViewItem
{
ParamControlItem (AudioProcessorEditor& editorIn, AudioProcessorParameter& paramIn)
: editor (editorIn), param (paramIn) {}
bool mightContainSubItems() override { return false; }
std::unique_ptr<Component> createItemComponent() override
{
return std::make_unique<ParameterDisplayComponent> (editor, param);
}
int getItemHeight() const override { return 40; }
AudioProcessorEditor& editor;
AudioProcessorParameter& param;
};
struct ParameterGroupItem : public TreeViewItem
{
ParameterGroupItem (AudioProcessorEditor& editor, const AudioProcessorParameterGroup& group)
: name (group.getName())
{
for (auto* node : group)
{
if (auto* param = node->getParameter())
if (param->isAutomatable())
addSubItem (new ParamControlItem (editor, *param));
if (auto* inner = node->getGroup())
{
auto groupItem = std::make_unique<ParameterGroupItem> (editor, *inner);
if (groupItem->getNumSubItems() != 0)
addSubItem (groupItem.release());
}
}
}
bool mightContainSubItems() override { return getNumSubItems() > 0; }
std::unique_ptr<Component> createItemComponent() override
{
return std::make_unique<Label> (name, name);
}
String name;
};
//==============================================================================
struct GenericAudioProcessorEditor::Pimpl
{
Pimpl (AudioProcessorEditor& editor)
: legacyParameters (editor.processor, false),
groupItem (editor, legacyParameters.getGroup())
{
const auto numIndents = getNumIndents (groupItem);
const auto width = 400 + view.getIndentSize() * numIndents;
view.setSize (width, 400);
view.setDefaultOpenness (true);
view.setRootItemVisible (false);
view.setRootItem (&groupItem);
}
static int getNumIndents (const TreeViewItem& item)
{
int maxInner = 0;
for (auto i = 0; i < item.getNumSubItems(); ++i)
maxInner = jmax (maxInner, 1 + getNumIndents (*item.getSubItem (i)));
return maxInner;
}
LegacyAudioParametersWrapper legacyParameters;
ParameterGroupItem groupItem;
TreeView view;
};
//==============================================================================
GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor& p)
: AudioProcessorEditor (p), pimpl (std::make_unique<Pimpl> (*this))
{
auto* viewport = pimpl->view.getViewport();
setOpaque (true);
addAndMakeVisible (pimpl->view);
setResizable (true, false);
setSize (viewport->getViewedComponent()->getWidth() + viewport->getVerticalScrollBar().getWidth(),
jlimit (125, 400, viewport->getViewedComponent()->getHeight()));
}
GenericAudioProcessorEditor::~GenericAudioProcessorEditor() = default;
void GenericAudioProcessorEditor::paint (Graphics& g)
{
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
}
void GenericAudioProcessorEditor::resized()
{
pimpl->view.setBounds (getLocalBounds());
}
} // namespace juce

View File

@ -0,0 +1,65 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A type of UI component that displays the parameters of an AudioProcessor as
a simple list of sliders, combo boxes and switches.
This can be used for showing an editor for a processor that doesn't supply
its own custom editor.
@see AudioProcessor
@tags{Audio}
*/
class JUCE_API GenericAudioProcessorEditor : public AudioProcessorEditor
{
public:
//==============================================================================
GenericAudioProcessorEditor (AudioProcessor&);
~GenericAudioProcessorEditor() override;
//==============================================================================
void paint (Graphics&) override;
void resized() override;
#ifndef DOXYGEN
[[deprecated ("This constructor has been changed to take a reference instead of a pointer.")]]
GenericAudioProcessorEditor (AudioProcessor* p) : GenericAudioProcessorEditor (*p) {}
#endif
private:
//==============================================================================
struct Pimpl;
std::unique_ptr<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericAudioProcessorEditor)
};
} // namespace juce

View File

@ -0,0 +1,114 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
bool PluginDescription::isDuplicateOf (const PluginDescription& other) const noexcept
{
const auto tie = [] (const PluginDescription& d)
{
return std::tie (d.fileOrIdentifier, d.deprecatedUid, d.uniqueId);
};
return tie (*this) == tie (other);
}
static String getPluginDescSuffix (const PluginDescription& d, int uid)
{
return "-" + String::toHexString (d.fileOrIdentifier.hashCode())
+ "-" + String::toHexString (uid);
}
bool PluginDescription::matchesIdentifierString (const String& identifierString) const
{
const auto matches = [&] (int uid)
{
return identifierString.endsWithIgnoreCase (getPluginDescSuffix (*this, uid));
};
return matches (uniqueId) || matches (deprecatedUid);
}
String PluginDescription::createIdentifierString() const
{
return pluginFormatName + "-" + name + getPluginDescSuffix (*this, uniqueId);
}
std::unique_ptr<XmlElement> PluginDescription::createXml() const
{
auto e = std::make_unique<XmlElement> ("PLUGIN");
e->setAttribute ("name", name);
if (descriptiveName != name)
e->setAttribute ("descriptiveName", descriptiveName);
e->setAttribute ("format", pluginFormatName);
e->setAttribute ("category", category);
e->setAttribute ("manufacturer", manufacturerName);
e->setAttribute ("version", version);
e->setAttribute ("file", fileOrIdentifier);
e->setAttribute ("uniqueId", String::toHexString (uniqueId));
e->setAttribute ("isInstrument", isInstrument);
e->setAttribute ("fileTime", String::toHexString (lastFileModTime.toMilliseconds()));
e->setAttribute ("infoUpdateTime", String::toHexString (lastInfoUpdateTime.toMilliseconds()));
e->setAttribute ("numInputs", numInputChannels);
e->setAttribute ("numOutputs", numOutputChannels);
e->setAttribute ("isShell", hasSharedContainer);
e->setAttribute ("uid", String::toHexString (deprecatedUid));
return e;
}
bool PluginDescription::loadFromXml (const XmlElement& xml)
{
if (xml.hasTagName ("PLUGIN"))
{
name = xml.getStringAttribute ("name");
descriptiveName = xml.getStringAttribute ("descriptiveName", name);
pluginFormatName = xml.getStringAttribute ("format");
category = xml.getStringAttribute ("category");
manufacturerName = xml.getStringAttribute ("manufacturer");
version = xml.getStringAttribute ("version");
fileOrIdentifier = xml.getStringAttribute ("file");
isInstrument = xml.getBoolAttribute ("isInstrument", false);
lastFileModTime = Time (xml.getStringAttribute ("fileTime").getHexValue64());
lastInfoUpdateTime = Time (xml.getStringAttribute ("infoUpdateTime").getHexValue64());
numInputChannels = xml.getIntAttribute ("numInputs");
numOutputChannels = xml.getIntAttribute ("numOutputs");
hasSharedContainer = xml.getBoolAttribute ("isShell", false);
deprecatedUid = xml.getStringAttribute ("uid").getHexValue32();
uniqueId = xml.getStringAttribute ("uniqueId", "0").getHexValue32();
return true;
}
return false;
}
} // namespace juce

View File

@ -0,0 +1,177 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A small class to represent some facts about a particular type of plug-in.
This class is for storing and managing the details about a plug-in without
actually having to load an instance of it.
A KnownPluginList contains a list of PluginDescription objects.
@see KnownPluginList
@tags{Audio}
*/
class JUCE_API PluginDescription
{
public:
//==============================================================================
PluginDescription() = default;
PluginDescription (const PluginDescription&) = default;
PluginDescription (PluginDescription&&) = default;
PluginDescription& operator= (const PluginDescription&) = default;
PluginDescription& operator= (PluginDescription&&) = default;
//==============================================================================
/** The name of the plug-in. */
String name;
/** A more descriptive name for the plug-in.
This may be the same as the 'name' field, but some plug-ins may provide an
alternative name.
*/
String descriptiveName;
/** The plug-in format, e.g. "VST", "AudioUnit", etc. */
String pluginFormatName;
/** A category, such as "Dynamics", "Reverbs", etc. */
String category;
/** The manufacturer. */
String manufacturerName;
/** The version. This string doesn't have any particular format. */
String version;
/** Either the file containing the plug-in module, or some other unique way
of identifying it.
E.g. for an AU, this would be an ID string that the component manager
could use to retrieve the plug-in. For a VST, it's the file path.
*/
String fileOrIdentifier;
/** The last time the plug-in file was changed.
This is handy when scanning for new or changed plug-ins.
*/
Time lastFileModTime;
/** The last time that this information was updated. This would typically have
been during a scan when this plugin was first tested or found to have changed.
*/
Time lastInfoUpdateTime;
/** Deprecated: New projects should use uniqueId instead.
A unique ID for the plug-in.
Note that this might not be unique between formats, e.g. a VST and some
other format might actually have the same id.
@see createIdentifierString
*/
int deprecatedUid = 0;
/** A unique ID for the plug-in.
Note that this might not be unique between formats, e.g. a VST and some
other format might actually have the same id.
The uniqueId field replaces the deprecatedUid field, and fixes an issue
where VST3 plugins with matching FUIDs would generate different uid
values depending on the platform. The deprecatedUid field is kept for
backwards compatibility, allowing existing hosts to migrate from the
old uid to the new uniqueId.
@see createIdentifierString
*/
int uniqueId = 0;
/** True if the plug-in identifies itself as a synthesiser. */
bool isInstrument = false;
/** The number of inputs. */
int numInputChannels = 0;
/** The number of outputs. */
int numOutputChannels = 0;
/** True if the plug-in is part of a multi-type container, e.g. a VST Shell. */
bool hasSharedContainer = false;
/** Returns true if the two descriptions refer to the same plug-in.
This isn't quite as simple as them just having the same file (because of
shell plug-ins).
*/
bool isDuplicateOf (const PluginDescription& other) const noexcept;
/** Return true if this description is equivalent to another one which created the
given identifier string.
Note that this isn't quite as simple as them just calling createIdentifierString()
and comparing the strings, because the identifiers can differ (thanks to shell plug-ins).
*/
bool matchesIdentifierString (const String& identifierString) const;
//==============================================================================
/** Returns a string that can be saved and used to uniquely identify the
plugin again.
This contains less info than the XML encoding, and is independent of the
plug-in's file location, so can be used to store a plug-in ID for use
across different machines.
*/
String createIdentifierString() const;
//==============================================================================
/** Creates an XML object containing these details.
@see loadFromXml
*/
std::unique_ptr<XmlElement> createXml() const;
/** Reloads the info in this structure from an XML record that was previously
saved with createXML().
Returns true if the XML was a valid plug-in description.
*/
bool loadFromXml (const XmlElement& xml);
private:
//==============================================================================
JUCE_LEAK_DETECTOR (PluginDescription)
};
} // namespace juce