1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Move files without UI dependencies to juce_audio_processors_headless

This commit is contained in:
reuk 2025-08-15 15:40:51 +01:00
parent 1f5d09d3fc
commit 407cc5b004
No known key found for this signature in database
73 changed files with 115 additions and 80 deletions

View file

@ -0,0 +1,294 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR 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 { TRANS ("on"), TRANS ("yes"), TRANS ("true") },
offStrings { TRANS ("off"), TRANS ("no"), TRANS ("false") }
{
}
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))
{
jassertquiet (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))
{
jassertquiet (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,191 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR 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;
using HostedParameter = HostedAudioProcessorParameter;
/** 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;
/** @cond */
/** 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;
/** @endcond */
protected:
//==============================================================================
/** Structure used to describe plugin parameters */
struct Parameter : public HostedParameter
{
public:
Parameter();
String getText (float value, int maximumStringLength) const override;
float getValueForText (const String& text) const override;
private:
const 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,472 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR 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
{
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
{
constexpr NodeID() = default;
explicit constexpr NodeID (uint32 i) : uid (i) {}
uint32 uid = 0;
constexpr bool operator== (const NodeID& other) const noexcept { return uid == other.uid; }
constexpr bool operator!= (const NodeID& other) const noexcept { return uid != other.uid; }
constexpr 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.
*/
class NodeAndChannel
{
constexpr auto tie() const { return std::tie (nodeID, channelIndex); }
public:
NodeID nodeID;
int channelIndex;
constexpr bool isMIDI() const noexcept { return channelIndex == midiChannelIndex; }
constexpr bool operator== (const NodeAndChannel& other) const noexcept { return tie() == other.tie(); }
constexpr bool operator!= (const NodeAndChannel& other) const noexcept { return tie() != other.tie(); }
constexpr bool operator< (const NodeAndChannel& other) const noexcept { return tie() < other.tie(); }
};
//==============================================================================
/** 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
{
if (processor != nullptr)
{
if (auto* bypassParam = processor->getBypassParameter())
return ! approximatelyEqual (bypassParam->getValue(), 0.0f);
}
return bypassed;
}
/** Tell this node to bypass processing. */
void setBypassed (bool shouldBeBypassed) noexcept
{
if (processor != nullptr)
{
if (auto* bypassParam = processor->getBypassParameter())
bypassParam->setValueNotifyingHost (shouldBeBypassed ? 1.0f : 0.0f);
}
bypassed = shouldBeBypassed;
}
//==============================================================================
/** A convenient typedef for referring to a pointer to a node object. */
using Ptr = ReferenceCountedObjectPtr<Node>;
/** @internal
Returns true if setBypassed (true) was called on this node.
This behaviour is different from isBypassed(), which may additionally return true if
the node has a bypass parameter that is not set to 0.
*/
bool userRequestedBypass() const { return bypassed; }
/** @internal
To create a new node, use AudioProcessorGraph::addNode.
*/
Node (NodeID n, std::unique_ptr<AudioProcessor> p) noexcept
: nodeID (n), processor (std::move (p))
{
jassert (processor != nullptr);
}
private:
//==============================================================================
std::unique_ptr<AudioProcessor> processor;
std::atomic<bool> bypassed { false };
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
{
//==============================================================================
constexpr Connection() = default;
constexpr Connection (NodeAndChannel sourceIn, NodeAndChannel destinationIn) noexcept
: source (sourceIn), destination (destinationIn) {}
constexpr Connection (const Connection&) = default;
constexpr Connection& operator= (const Connection&) = default;
constexpr bool operator== (const Connection& other) const noexcept
{
return source == other.source && destination == other.destination;
}
constexpr bool operator!= (const Connection& other) const noexcept
{
return ! operator== (other);
}
constexpr bool operator< (const Connection& other) const noexcept
{
const auto tie = [] (auto& x)
{
return std::tie (x.source.nodeID,
x.destination.nodeID,
x.source.channelIndex,
x.destination.channelIndex);
};
return tie (*this) < tie (other);
}
//==============================================================================
/** 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 };
};
//==============================================================================
/** Indicates how the graph should be updated after a change.
If you need to make lots of changes to a graph (e.g. lots of separate calls
to addNode, addConnection etc.) you can avoid rebuilding the graph on each
change by using the async update kind.
*/
enum class UpdateKind
{
sync, ///< Graph should be rebuilt immediately after modification.
async, ///< Graph rebuild should be delayed. If you make several changes to the graph
///< inside the same call stack, these changes will be applied in one go.
none ///< Graph should not be rebuilt automatically. Use rebuild() to trigger a graph
///< rebuild.
};
//==============================================================================
/** Deletes all nodes and connections from this graph.
Any processor objects in the graph will be deleted.
*/
void clear (UpdateKind = UpdateKind::sync);
/** Returns the array of nodes in the graph. */
const ReferenceCountedArray<Node>& getNodes() const noexcept;
/** Returns the number of nodes in the graph. */
int getNumNodes() const noexcept { return getNodes().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 getNodes()[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, std::optional<NodeID> nodeId = std::nullopt, UpdateKind = UpdateKind::sync);
/** 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, UpdateKind = UpdateKind::sync);
/** Deletes a node within the graph.
This will also delete any connections that are attached to this node.
*/
Node::Ptr removeNode (Node*, UpdateKind = UpdateKind::sync);
/** 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 (const Node& source, const Node& destination) const noexcept;
/** Does a recursive check to see if there's a direct or indirect series of connections
between these two nodes.
*/
bool isAnInputTo (NodeID source, NodeID 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&, UpdateKind = UpdateKind::sync);
/** Deletes the given connection. */
bool removeConnection (const Connection&, UpdateKind = UpdateKind::sync);
/** Removes all connections from the specified node. */
bool disconnectNode (NodeID, UpdateKind = UpdateKind::sync);
/** 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 (UpdateKind = UpdateKind::sync);
/** Rebuilds the graph if necessary.
This function will only ever rebuild the graph on the main thread. If this function is
called from another thread, the rebuild request will be dispatched asynchronously to the
main thread.
*/
void rebuild();
//==============================================================================
/** 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:
class Pimpl;
std::unique_ptr<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph)
};
} // namespace juce

View file

@ -0,0 +1,41 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {}
void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {}
} // namespace juce

View file

@ -0,0 +1,200 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
class AudioProcessor;
//==============================================================================
/**
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
{
/** @see withLatencyChanged */
bool latencyChanged = false;
/** @see withParameterInfoChanged */
bool parameterInfoChanged = false;
/** @see withProgramChanged */
bool programChanged = false;
/** @see withNonParameterStateChanged */
bool nonParameterStateChanged = false;
/** Indicates that the AudioProcessor's latency has changed.
Most of the time, you won't need to use this function directly.
AudioProcessor::setLatencySamples() will automatically call
AudioProcessor::updateHostDisplay(), indicating that the latency has changed.
@see latencyChanged
*/
[[nodiscard]] ChangeDetails withLatencyChanged (bool b) const noexcept { return with (&ChangeDetails::latencyChanged, b); }
/** Indicates that some attributes of the AudioProcessor's parameters have changed.
When this flag is set, the host should rescan the AudioProcessor's parameters, and
update its controls to match. This is often used to update the names of a plugin's
parameters in the host.
@see parameterInfoChanged
*/
[[nodiscard]] ChangeDetails withParameterInfoChanged (bool b) const noexcept { return with (&ChangeDetails::parameterInfoChanged, b); }
/** Indicates that the loaded program has changed.
When this flag is set, the host should call AudioProcessor::getCurrentProgram() and
update any preset list views to display the program that is currently in use.
@see programChanged
*/
[[nodiscard]] ChangeDetails withProgramChanged (bool b) const noexcept { return with (&ChangeDetails::programChanged, b); }
/** Indicates that the plugin state has changed (but not its parameters!).
An AudioProcessor can call updateHostDisplay with this flag set to notify the host that
its state has changed in a way that requires re-saving.
If a host receives a call to audioProcessorChanged with this flag set, it should offer
to save the plugin state before taking any actions that might irrevocably destroy the
current plugin state, such as closing the project.
@see nonParameterStateChanged
*/
[[nodiscard]] ChangeDetails withNonParameterStateChanged (bool b) const noexcept { return with (&ChangeDetails::nonParameterStateChanged, b); }
/** Returns the default set of flags that will be used when
AudioProcessor::updateHostDisplay() is called with no arguments.
*/
static ChangeDetails getDefaultFlags()
{
return ChangeDetails{}.withLatencyChanged (true)
.withParameterInfoChanged (true)
.withProgramChanged (true);
}
[[deprecated ("The naming of this function is misleading. Use getDefaultFlags instead.")]]
static ChangeDetails getAllChanged()
{
return getDefaultFlags();
}
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,171 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
AudioProcessorParameter::~AudioProcessorParameter()
{
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This will fail if you've called beginChangeGesture() without having made
// a corresponding call to endChangeGesture...
jassert (! isPerformingGesture);
#endif
}
void AudioProcessorParameter::setParameterIndex (int index) noexcept
{
jassert (parameterIndex < 0 && 0 <= index);
parameterIndex = index;
}
void AudioProcessorParameter::setOwner (Listener* listenerIn) noexcept
{
jassert (finalListener == nullptr);
finalListener = listenerIn;
}
void AudioProcessorParameter::setValueNotifyingHost (float newValue)
{
setValue (newValue);
sendValueChangedMessageToListeners (newValue);
}
void AudioProcessorParameter::beginChangeGesture()
{
// This method can't be used until the parameter has been attached to a processor!
jassert (parameterIndex >= 0);
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This means you've called beginChangeGesture twice in succession without
// a matching call to endChangeGesture. That might be fine in most hosts,
// but it would be better to avoid doing it.
jassert (! isPerformingGesture);
isPerformingGesture = true;
#endif
ScopedLock lock (listenerLock);
for (int i = listeners.size(); --i >= 0;)
if (auto* l = listeners[i])
l->parameterGestureChanged (getParameterIndex(), true);
if (finalListener != nullptr)
finalListener->parameterGestureChanged (getParameterIndex(), true);
}
void AudioProcessorParameter::endChangeGesture()
{
// This method can't be used until the parameter has been attached to a processor!
jassert (parameterIndex >= 0);
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This means you've called endChangeGesture without having previously
// called beginChangeGesture. That might be fine in most hosts, but it
// would be better to keep the calls matched correctly.
jassert (isPerformingGesture);
isPerformingGesture = false;
#endif
ScopedLock lock (listenerLock);
for (int i = listeners.size(); --i >= 0;)
if (auto* l = listeners[i])
l->parameterGestureChanged (getParameterIndex(), false);
if (finalListener != nullptr)
finalListener->parameterGestureChanged (getParameterIndex(), false);
}
void AudioProcessorParameter::sendValueChangedMessageToListeners (float newValue)
{
ScopedLock lock (listenerLock);
for (int i = listeners.size(); --i >= 0;)
if (auto* l = listeners [i])
l->parameterValueChanged (getParameterIndex(), newValue);
if (finalListener != nullptr)
finalListener->parameterValueChanged (getParameterIndex(), newValue);
}
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
bool AudioProcessorParameter::isAutomatable() const { return true; }
bool AudioProcessorParameter::isMetaParameter() const { return false; }
AudioProcessorParameter::Category AudioProcessorParameter::getCategory() const { return genericParameter; }
int AudioProcessorParameter::getNumSteps() const { return getDefaultNumParameterSteps(); }
bool AudioProcessorParameter::isDiscrete() const { return false; }
bool AudioProcessorParameter::isBoolean() const { return false; }
String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const
{
return String (value, 2);
}
String AudioProcessorParameter::getCurrentValueAsText() const
{
return getText (getValue(), 1024);
}
StringArray AudioProcessorParameter::getAllValueStrings() const
{
if (isDiscrete() && valueStrings.isEmpty())
{
auto maxIndex = getNumSteps() - 1;
for (int i = 0; i < getNumSteps(); ++i)
valueStrings.add (getText ((float) i / (float) maxIndex, 1024));
}
return valueStrings;
}
void AudioProcessorParameter::addListener (AudioProcessorParameter::Listener* newListener)
{
const ScopedLock sl (listenerLock);
listeners.addIfNotAlreadyThere (newListener);
}
void AudioProcessorParameter::removeListener (AudioProcessorParameter::Listener* listenerToRemove)
{
const ScopedLock sl (listenerLock);
listeners.removeFirstMatchingValue (listenerToRemove);
}
int AudioProcessorParameter::getDefaultNumParameterSteps() noexcept
{
return 0x7fffffff;
}
} // namespace juce

View file

@ -0,0 +1,387 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** 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 = default;
/** The version hint supplied to this constructor is used in Audio Unit plugins to aid ordering
parameter identifiers when JUCE_FORCE_USE_LEGACY_PARAM_IDS is not enabled.
When adding a parameter that is not present in a previous version of the Audio Unit, you
must ensure that the version hint supplied is a number higher than that of any parameter in
any previous plugin version.
For example, in the first release of a plugin, every parameter was created with "1" as a
version hint. If you add some parameters in the second release of the plugin, all of the
new parameters should have "2" as a version hint. Additional parameters added in subsequent
plugin versions should have "3", "4", and so forth, increasing monotonically.
Note that adding or removing parameters with a version hint that is lower than the maximum
version hint of all parameters will break saved automation in some hosts, so be careful!
A version hint of "0" will be treated as though the version hint has not been set
explicitly. When targeting the AU format, the version hint may be checked at runtime in
debug builds to ensure that it has been set.
Rationale:
According to <a href="https://developer.apple.com/documentation/audiotoolbox/audiounitparameter?language=objc">Apple's Documentation</a>:
> An audio unit parameter is uniquely identified by the combination of its scope, element, and ID.
However, Logic Pro and GarageBand have a known limitation that causes them to use parameter
indices instead of IDs to identify parameters. The effect of this is that adding parameters
to a later version of a plugin can break automation saved with an earlier version of the
plugin if the indices of existing parameters are changed. It is *always* unsafe to remove
parameters from an Audio Unit plugin that will be used in one of these hosts, because
removing a parameter will always modify the indices of following parameters.
In order to work around this limitation, parameters in AUv2 plugins are sorted first by
their version hint, and then by the hash of their string identifier. As long as the
parameters from later versions of the plugin always have a version hint that is higher than
the parameters from earlier versions of the plugin, recall of automation data will work as
expected in Logic and GarageBand.
Note that we can't just use the JUCE parameter index directly in order to preserve ordering.
This would require all new parameters to be added at the end of the parameter list, which
would make it impossible to add parameters to existing parameter groups. It would also make
it awkward to structure code sensibly, undoing all of the benefits of string-based parameter
identifiers.
At time of writing, AUv3 plugins seem to be affected by the same issue, but there does not
appear to be any API to control parameter indices in this format. Therefore, when building
AUv3 plugins you must not add or remove parameters in subsequent plugin versions if you
wish to support Logic and GarageBand.
*/
explicit AudioProcessorParameter (int versionHint)
: version (versionHint) {}
/** 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 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; }
/** @internal
This should only be called by the owner of the parameter after it has been added to
a processor. Do not call this function; changing the parameter index *will* break things!
*/
void setParameterIndex (int) noexcept;
//==============================================================================
/** 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;
//==============================================================================
/** @see AudioProcessorParameter (int) */
int getVersionHint() const { return version; }
//==============================================================================
/**
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;
};
/** @internal
This should only be called by the owner of the parameter after it has been added to
a processor. Do not call this function; changing the owner *will* break things!
*/
void setOwner (Listener* listener) noexcept;
/** 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);
/** Returns the default number of steps for a parameter.
NOTE! This method is deprecated! It's recommended that you use
AudioProcessorParameter::getNumSteps() instead.
@see getParameterNumSteps
*/
static int getDefaultNumParameterSteps() noexcept;
private:
//==============================================================================
int parameterIndex = -1;
int version = 0;
CriticalSection listenerLock;
Array<Listener*> listeners;
Listener* finalListener = nullptr;
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,330 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR 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 final : 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 final : 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,263 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR 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 JUCE_API 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)...);
}
/** @cond */
[[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); }
/** @endcond */
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,61 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A parameter with functions that are useful for plugin hosts.
@tags{Audio}
*/
struct JUCE_API HostedAudioProcessorParameter : public AudioProcessorParameter
{
using AudioProcessorParameter::AudioProcessorParameter;
/** Returns an ID that 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;
};
} // namespace juce

View file

@ -0,0 +1,126 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR 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
{
const auto idToUse = uniqueId != 0 ? uniqueId : deprecatedUid;
return pluginFormatName + "-" + name + getPluginDescSuffix (*this, idToUse);
}
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 ("hasARAExtension", hasARAExtension);
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);
hasARAExtension = xml.getBoolAttribute ("hasARAExtension", false);
deprecatedUid = xml.getStringAttribute ("uid").getHexValue32();
uniqueId = xml.getStringAttribute ("uniqueId", "0").getHexValue32();
return true;
}
return false;
}
} // namespace juce

View file

@ -0,0 +1,189 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR 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;
/** True if the plug-in is ARA enabled and can supply a valid ARAFactoryWrapper. */
bool hasARAExtension = 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