fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).

This commit is contained in:
Alex Birch
2018-06-17 13:34:53 +01:00
parent a2be47c887
commit dff4d13a1d
1563 changed files with 601601 additions and 3466 deletions

View File

@ -0,0 +1,249 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
PluginDescription AudioPluginInstance::getPluginDescription() const
{
PluginDescription desc;
fillInPluginDescription (desc);
return desc;
}
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 compatibilty you should perform the operation below
// this comment. However the caveat is that for plug-ins which change their
// number of parameters dynamically at runtime you cannot rely upon the
// returned parameter ID mapping to the correct parameter. A comprehensive
// solution to this problem requires some additional work in JUCE's hosting
// code.
return String (parameterIndex);
}
float AudioPluginInstance::getParameter (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getValue();
return 0.0f;
}
void AudioPluginInstance::setParameter (int parameterIndex, float newValue)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
param->setValue (newValue);
}
const String AudioPluginInstance::getParameterName (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getName (1024);
return {};
}
String AudioPluginInstance::getParameterName (int parameterIndex, int maximumStringLength)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getName (maximumStringLength);
return {};
}
const String AudioPluginInstance::getParameterText (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getCurrentValueAsText();
return {};
}
String AudioPluginInstance::getParameterText (int parameterIndex, int maximumStringLength)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getCurrentValueAsText().substring (0, maximumStringLength);
return {};
}
float AudioPluginInstance::getParameterDefaultValue (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getDefaultValue();
return 0.0f;
}
int AudioPluginInstance::getParameterNumSteps (int parameterIndex)
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getNumSteps();
return AudioProcessor::getDefaultNumParameterSteps();
}
bool AudioPluginInstance::isParameterDiscrete (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->isDiscrete();
return false;
}
bool AudioPluginInstance::isParameterAutomatable (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->isAutomatable();
return true;
}
String AudioPluginInstance::getParameterLabel (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getLabel();
return {};
}
bool AudioPluginInstance::isParameterOrientationInverted (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->isOrientationInverted();
return false;
}
bool AudioPluginInstance::isMetaParameter (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->isMetaParameter();
return false;
}
AudioProcessorParameter::Category AudioPluginInstance::getParameterCategory (int parameterIndex) const
{
assertOnceOnDeprecatedMethodUse();
if (auto* param = getParameters()[parameterIndex])
return param->getCategory();
return AudioProcessorParameter::genericParameter;
}
void AudioPluginInstance::assertOnceOnDeprecatedMethodUse() const noexcept
{
if (! deprecationAssertiontriggered)
{
// If you hit this assertion then you are using at least one of the
// methods marked as deprecated in this class. For now you can simply
// continue past this point and subsequent uses of deprecated methods
// will not trigger additional assertions. However, we will shortly be
// removing these methods so you are strongly advised to look at the
// implementation of the corresponding method in this class and use
// that approach instead.
jassertfalse;
}
deprecationAssertiontriggered = true;
}
bool AudioPluginInstance::deprecationAssertiontriggered = false;
AudioPluginInstance::Parameter::Parameter()
{
onStrings.add (TRANS("on"));
onStrings.add (TRANS("yes"));
onStrings.add (TRANS("true"));
offStrings.add (TRANS("off"));
offStrings.add (TRANS("no"));
offStrings.add (TRANS("false"));
}
AudioPluginInstance::Parameter::~Parameter() {}
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;
}
} // namespace juce

View File

@ -0,0 +1,138 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_MSVC
#pragma warning (push, 0)
// MSVC does not like it if you override a deprecated method even if you
// keep the deprecation attribute. Other compilers are more forgiving.
#pragma warning (disable: 4996)
#endif
//==============================================================================
/**
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.
*/
virtual ~AudioPluginInstance() {}
//==============================================================================
/** Fills-in the appropriate parts of this plugin description object. */
virtual void fillInPluginDescription (PluginDescription& description) const = 0;
/** Returns a PluginDescription for this plugin.
This is just a convenience method to avoid calling fillInPluginDescription.
*/
PluginDescription getPluginDescription() const;
/** 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.
*/
virtual void* getPlatformSpecificData() { return nullptr; }
/** For some formats (currently AudioUnit), this forces a reload of the list of
available parameters.
*/
virtual void refreshParameterList() {}
// 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.
JUCE_DEPRECATED (String getParameterID (int index) override);
JUCE_DEPRECATED (float getParameter (int parameterIndex) override);
JUCE_DEPRECATED (void setParameter (int parameterIndex, float newValue) override);
JUCE_DEPRECATED (const String getParameterName (int parameterIndex) override);
JUCE_DEPRECATED (String getParameterName (int parameterIndex, int maximumStringLength) override);
JUCE_DEPRECATED (const String getParameterText (int parameterIndex) override);
JUCE_DEPRECATED (String getParameterText (int parameterIndex, int maximumStringLength) override);
JUCE_DEPRECATED (int getParameterNumSteps (int parameterIndex) override);
JUCE_DEPRECATED (bool isParameterDiscrete (int parameterIndex) const override);
JUCE_DEPRECATED (bool isParameterAutomatable (int parameterIndex) const override);
JUCE_DEPRECATED (float getParameterDefaultValue (int parameterIndex) override);
JUCE_DEPRECATED (String getParameterLabel (int parameterIndex) const override);
JUCE_DEPRECATED (bool isParameterOrientationInverted (int parameterIndex) const override);
JUCE_DEPRECATED (bool isMetaParameter (int parameterIndex) const override);
JUCE_DEPRECATED (AudioProcessorParameter::Category getParameterCategory (int parameterIndex) const override);
protected:
//==============================================================================
/** Structure used to describe plugin parameters */
struct Parameter : public AudioProcessorParameter
{
Parameter();
virtual ~Parameter();
virtual String getText (float value, int maximumStringLength) const override;
virtual float getValueForText (const String& text) const override;
StringArray onStrings, offStrings;
};
AudioPluginInstance() {}
AudioPluginInstance (const BusesProperties& ioLayouts) : AudioProcessor (ioLayouts) {}
template <int numLayouts>
AudioPluginInstance (const short channelLayoutList[numLayouts][2]) : AudioProcessor (channelLayoutList) {}
private:
void assertOnceOnDeprecatedMethodUse() const noexcept;
static bool deprecationAssertiontriggered;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance)
};
#if JUCE_MSVC
#pragma warning (pop)
#endif
} // namespace juce

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,210 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p)
{
initialise();
}
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p)
{
// the filter must be valid..
jassert (p != nullptr);
initialise();
}
AudioProcessorEditor::~AudioProcessorEditor()
{
splashScreen.deleteAndZero();
// if this fails, then the wrapper hasn't called editorBeingDeleted() on the
// filter for some reason..
jassert (processor.getActiveEditor() != this);
removeComponentListener (resizeListener.get());
}
void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {}
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }
bool AudioProcessorEditor::supportsHostMIDIControllerPresence (bool) { return true; }
void AudioProcessorEditor::hostMIDIControllerIsAvailable (bool) {}
void AudioProcessorEditor::initialise()
{
/*
==========================================================================
In accordance with the terms of the JUCE 5 End-Use License Agreement, the
JUCE Code in SECTION A cannot be removed, changed or otherwise rendered
ineffective unless you have a JUCE Indie or Pro license, or are using
JUCE under the GPL v3 license.
End User License Agreement: www.juce.com/juce-5-licence
==========================================================================
*/
// BEGIN SECTION A
splashScreen = new JUCESplashScreen (*this);
// END SECTION A
resizable = false;
attachConstrainer (&defaultConstrainer);
resizeListener.reset (new AudioProcessorEditorListener (*this));
addComponentListener (resizeListener.get());
}
//==============================================================================
void AudioProcessorEditor::setResizable (const bool shouldBeResizable, const bool useBottomRightCornerResizer)
{
if (shouldBeResizable != resizable)
{
resizable = shouldBeResizable;
if (! resizable)
{
setConstrainer (&defaultConstrainer);
if (auto w = getWidth())
{
if (auto h = getHeight())
{
defaultConstrainer.setSizeLimits (w, h, w, h);
resized();
}
}
}
}
bool shouldHaveCornerResizer = (useBottomRightCornerResizer && shouldBeResizable);
if (shouldHaveCornerResizer != (resizableCorner != nullptr))
{
if (shouldHaveCornerResizer)
{
resizableCorner.reset (new ResizableCornerComponent (this, constrainer));
Component::addChildComponent (resizableCorner.get());
resizableCorner->setAlwaysOnTop (true);
}
else
{
resizableCorner.reset();
}
}
}
void AudioProcessorEditor::setResizeLimits (int newMinimumWidth,
int newMinimumHeight,
int newMaximumWidth,
int newMaximumHeight) noexcept
{
// if you've set up a custom constrainer then these settings won't have any effect..
jassert (constrainer == &defaultConstrainer || constrainer == nullptr);
const bool shouldEnableResize = (newMinimumWidth != newMaximumWidth || newMinimumHeight != newMaximumHeight);
const bool shouldHaveCornerResizer = (shouldEnableResize != resizable || resizableCorner != nullptr);
setResizable (shouldEnableResize, shouldHaveCornerResizer);
if (constrainer == nullptr)
setConstrainer (&defaultConstrainer);
defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight,
newMaximumWidth, newMaximumHeight);
setBoundsConstrained (getBounds());
}
void AudioProcessorEditor::setConstrainer (ComponentBoundsConstrainer* newConstrainer)
{
if (constrainer != newConstrainer)
{
resizable = true;
attachConstrainer (newConstrainer);
}
}
void AudioProcessorEditor::attachConstrainer (ComponentBoundsConstrainer* newConstrainer)
{
if (constrainer != newConstrainer)
{
constrainer = newConstrainer;
updatePeer();
}
}
void AudioProcessorEditor::setBoundsConstrained (Rectangle<int> newBounds)
{
if (constrainer != nullptr)
constrainer->setBoundsForComponent (this, newBounds, false, false, false, false);
else
setBounds (newBounds);
}
void AudioProcessorEditor::editorResized (bool wasResized)
{
if (wasResized)
{
bool resizerHidden = false;
if (auto* peer = getPeer())
resizerHidden = peer->isFullScreen() || peer->isKioskMode();
if (resizableCorner != nullptr)
{
resizableCorner->setVisible (! resizerHidden);
const int resizerSize = 18;
resizableCorner->setBounds (getWidth() - resizerSize,
getHeight() - resizerSize,
resizerSize, resizerSize);
}
if (! resizable)
if (auto w = getWidth())
if (auto h = getHeight())
defaultConstrainer.setSizeLimits (w, h, w, h);
}
}
void AudioProcessorEditor::updatePeer()
{
if (isOnDesktop())
if (auto* peer = getPeer())
peer->setConstrainer (constrainer);
}
void AudioProcessorEditor::setScaleFactor (float newScale)
{
setTransform (AffineTransform::scale (newScale));
editorResized (true);
}
} // namespace juce

View File

@ -0,0 +1,212 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class AudioProcessor;
class AudioProcessorEditorListener;
//==============================================================================
/**
Base class for the component that acts as the GUI for an AudioProcessor.
Derive your editor component from this class, and create an instance of it
by overriding the AudioProcessor::createEditor() method.
@see AudioProcessor, GenericAudioProcessorEditor
@tags{Audio}
*/
class JUCE_API AudioProcessorEditor : public Component
{
protected:
//==============================================================================
/** Creates an editor for the specified processor. */
AudioProcessorEditor (AudioProcessor&) noexcept;
/** Creates an editor for the specified processor. */
AudioProcessorEditor (AudioProcessor*) noexcept;
public:
/** Destructor. */
~AudioProcessorEditor();
//==============================================================================
/** The AudioProcessor that this editor represents. */
AudioProcessor& processor;
/** Returns a pointer to the processor that this editor represents.
This method is here to support legacy code, but it's easier to just use the
AudioProcessorEditor::processor member variable directly to get this object.
*/
AudioProcessor* getAudioProcessor() const noexcept { return &processor; }
//==============================================================================
/** Used by the setParameterHighlighting() method. */
struct ParameterControlHighlightInfo
{
int parameterIndex;
bool isHighlighted;
Colour suggestedColour;
};
/** Some types of plugin can call this to suggest that the control for a particular
parameter should be highlighted.
Currently only AAX plugins will call this, and implementing it is optional.
*/
virtual void setControlHighlight (ParameterControlHighlightInfo);
/** Called by certain plug-in wrappers to find out whether a component is used
to control a parameter.
If the given component represents a particular plugin parameter, then this
method should return the index of that parameter. If not, it should return -1.
Currently only AAX plugins will call this, and implementing it is optional.
*/
virtual int getControlParameterIndex (Component&);
/** Override this method to indicate if your editor supports the presence or
absence of a host-provided MIDI controller.
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later)
support this functionality, and even then the host may choose to ignore this
information.
The default behaviour is to report support for both cases.
*/
virtual bool supportsHostMIDIControllerPresence (bool hostMIDIControllerIsAvailable);
/** Called to indicate if a host is providing a MIDI controller when the host
reconfigures its layout.
Use this as an opportunity to hide or display your own onscreen keyboard or
other input component.
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later)
support this functionality.
*/
virtual void hostMIDIControllerIsAvailable (bool controllerIsAvailable);
/** Can be called by a host to tell the editor that it should use a non-unity
GUI scale.
*/
virtual void setScaleFactor (float newScale);
//==============================================================================
/** Marks the host's editor window as resizable
@param allowHostToResize whether the editor's parent window can be resized
by the user or the host. Even if this is false, you
can still resize your window yourself by calling
setBounds (for example, when a user clicks on a button
in your editor to drop out a panel) which will bypass any
resizable/constraints checks. If you are using
your own corner resizer than this will also bypass
any checks.
@param useBottomRightCornerResizer
@see setResizeLimits, isResizable
*/
void setResizable (bool allowHostToResize, bool useBottomRightCornerResizer);
/** Returns true if the host is allowed to resize editor's parent window
@see setResizable
*/
bool isResizable() const noexcept { return resizable; }
/** This sets the maximum and minimum sizes for the window.
If the window's current size is outside these limits, it will be resized to
make sure it's within them.
A direct call to setBounds() will bypass any constraint checks, but when the
window is dragged by the user or resized by other indirect means, the constrainer
will limit the numbers involved.
@see setResizable
*/
void setResizeLimits (int newMinimumWidth,
int newMinimumHeight,
int newMaximumWidth,
int newMaximumHeight) noexcept;
/** Returns the bounds constrainer object that this window is using.
You can access this to change its properties.
*/
ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; }
/** Sets the bounds-constrainer object to use for resizing and dragging this window.
A pointer to the object you pass in will be kept, but it won't be deleted
by this object, so it's the caller's responsibility to manage it.
If you pass a nullptr, then no contraints will be placed on the positioning of the window.
*/
void setConstrainer (ComponentBoundsConstrainer* newConstrainer);
/** Calls the window's setBounds method, after first checking these bounds
with the current constrainer.
@see setConstrainer
*/
void setBoundsConstrained (Rectangle<int> newBounds);
std::unique_ptr<ResizableCornerComponent> resizableCorner;
private:
//==============================================================================
struct AudioProcessorEditorListener : ComponentListener
{
AudioProcessorEditorListener (AudioProcessorEditor& e) : ed (e) {}
void componentMovedOrResized (Component&, bool, bool wasResized) override { ed.editorResized (wasResized); }
void componentParentHierarchyChanged (Component&) override { ed.updatePeer(); }
AudioProcessorEditor& ed;
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditorListener)
};
//==============================================================================
void initialise();
void editorResized (bool wasResized);
void updatePeer();
void attachConstrainer (ComponentBoundsConstrainer*);
//==============================================================================
std::unique_ptr<AudioProcessorEditorListener> resizeListener;
bool resizable;
ComponentBoundsConstrainer defaultConstrainer;
ComponentBoundsConstrainer* constrainer = {};
Component::SafePointer<Component> splashScreen;
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor)
};
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,404 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A type of AudioProcessor which plays back a graph of other AudioProcessors.
Use one of these objects if you want to wire-up a set of AudioProcessors
and play back the result.
Processors can be added to the graph as "nodes" using addNode(), and once
added, you can connect any of their input or output channels to other
nodes using addConnection().
To play back a graph through an audio device, you might want to use an
AudioProcessorPlayer object.
@tags{Audio}
*/
class JUCE_API AudioProcessorGraph : public AudioProcessor,
public ChangeBroadcaster,
private AsyncUpdater
{
public:
//==============================================================================
/** Creates an empty graph. */
AudioProcessorGraph();
/** Destructor.
Any processor objects that have been added to the graph will also be deleted.
*/
~AudioProcessorGraph();
/** Each node in the graph has a UID of this type. */
using NodeID = uint32;
//==============================================================================
/** A special index that represents the midi channel of a node.
This is used as a channel index value if you want to refer to the midi input
or output instead of an audio channel.
*/
enum { midiChannelIndex = 0x1000 };
//==============================================================================
/**
Represents an input or output channel of a node in an AudioProcessorGraph.
*/
struct NodeAndChannel
{
NodeID nodeID;
int channelIndex;
bool isMIDI() const noexcept { return channelIndex == midiChannelIndex; }
bool operator== (const NodeAndChannel& other) const noexcept { return nodeID == other.nodeID && channelIndex == other.channelIndex; }
bool operator!= (const NodeAndChannel& other) const noexcept { return ! operator== (other); }
};
//==============================================================================
/** Represents one of the nodes, or processors, in an AudioProcessorGraph.
To create a node, call AudioProcessorGraph::addNode().
*/
class JUCE_API Node : public ReferenceCountedObject
{
public:
//==============================================================================
/** The ID number assigned to this node.
This is assigned by the graph that owns it, and can't be changed.
*/
const NodeID nodeID;
/** The actual processor object that this node represents. */
AudioProcessor* getProcessor() const noexcept { return processor.get(); }
/** A set of user-definable properties that are associated with this node.
This can be used to attach values to the node for whatever purpose seems
useful. For example, you might store an x and y position if your application
is displaying the nodes on-screen.
*/
NamedValueSet properties;
//==============================================================================
/** Returns if the node is bypassed or not. */
bool isBypassed() const noexcept;
/** Tell this node to bypass processing. */
void setBypassed (bool shouldBeBypassed) noexcept;
//==============================================================================
/** A convenient typedef for referring to a pointer to a node object. */
using Ptr = ReferenceCountedObjectPtr<Node>;
private:
//==============================================================================
friend class AudioProcessorGraph;
struct Connection
{
Node* otherNode;
int otherChannel, thisChannel;
bool operator== (const Connection&) const noexcept;
};
const std::unique_ptr<AudioProcessor> processor;
Array<Connection> inputs, outputs;
bool isPrepared = false, bypassed = false;
Node (NodeID, AudioProcessor*) noexcept;
void setParentGraph (AudioProcessorGraph*) const;
void prepare (double newSampleRate, int newBlockSize, AudioProcessorGraph*, ProcessingPrecision);
void unprepare();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Node)
};
//==============================================================================
/** Represents a connection between two channels of two nodes in an AudioProcessorGraph.
To create a connection, use AudioProcessorGraph::addConnection().
*/
struct JUCE_API Connection
{
//==============================================================================
Connection (NodeAndChannel source, NodeAndChannel destination) noexcept;
Connection (const Connection&) = default;
Connection& operator= (const Connection&) = default;
bool operator== (const Connection&) const noexcept;
bool operator!= (const Connection&) const noexcept;
bool operator< (const Connection&) const noexcept;
//==============================================================================
/** The channel and node which is the input source for this connection. */
NodeAndChannel source;
/** The channel and node which is the input source for this connection. */
NodeAndChannel destination;
};
//==============================================================================
/** Deletes all nodes and connections from this graph.
Any processor objects in the graph will be deleted.
*/
void clear();
/** Returns the array of nodes in the graph. */
const ReferenceCountedArray<Node>& getNodes() const noexcept { return nodes; }
/** Returns the number of nodes in the graph. */
int getNumNodes() const noexcept { return nodes.size(); }
/** Returns a pointer to one of the nodes in the graph.
This will return nullptr if the index is out of range.
@see getNodeForId
*/
Node* getNode (int index) const noexcept { return nodes [index]; }
/** Searches the graph for a node with the given ID number and returns it.
If no such node was found, this returns nullptr.
@see getNode
*/
Node* getNodeForId (NodeID) const;
/** Adds a node to the graph.
This creates a new node in the graph, for the specified processor. Once you have
added a processor to the graph, the graph owns it and will delete it later when
it is no longer needed.
The optional nodeId parameter lets you specify an ID to use for the node, but
if the value is already in use, this new node will overwrite the old one.
If this succeeds, it returns a pointer to the newly-created node.
*/
Node::Ptr addNode (AudioProcessor* newProcessor, NodeID nodeId = {});
/** Deletes a node within the graph which has the specified ID.
This will also delete any connections that are attached to this node.
*/
bool removeNode (NodeID);
/** Deletes a node within the graph.
This will also delete any connections that are attached to this node.
*/
bool removeNode (Node*);
/** Returns the list of connections in the graph. */
std::vector<Connection> getConnections() const;
/** Returns true if the given connection exists. */
bool isConnected (const Connection&) const noexcept;
/** Returns true if there is a direct connection between any of the channels of
two specified nodes.
*/
bool isConnected (NodeID possibleSourceNodeID, NodeID possibleDestNodeID) const noexcept;
/** Does a recursive check to see if there's a direct or indirect series of connections
between these two nodes.
*/
bool isAnInputTo (Node& source, Node& destination) const noexcept;
/** Returns true if it would be legal to connect the specified points. */
bool canConnect (const Connection&) const;
/** Attempts to connect two specified channels of two nodes.
If this isn't allowed (e.g. because you're trying to connect a midi channel
to an audio one or other such nonsense), then it'll return false.
*/
bool addConnection (const Connection&);
/** Deletes the given connection. */
bool removeConnection (const Connection&);
/** Removes all connections from the specified node. */
bool disconnectNode (NodeID);
/** Returns true if the given connection's channel numbers map on to valid
channels at each end.
Even if a connection is valid when created, its status could change if
a node changes its channel config.
*/
bool isConnectionLegal (const Connection&) const;
/** Performs a sanity checks of all the connections.
This might be useful if some of the processors are doing things like changing
their channel counts, which could render some connections obsolete.
*/
bool removeIllegalConnections();
//==============================================================================
/** A special type of AudioProcessor that can live inside an AudioProcessorGraph
in order to use the audio that comes into and out of the graph itself.
If you create an AudioGraphIOProcessor in "input" mode, it will act as a
node in the graph which delivers the audio that is coming into the parent
graph. This allows you to stream the data to other nodes and process the
incoming audio.
Likewise, one of these in "output" mode can be sent data which it will add to
the sum of data being sent to the graph's output.
@see AudioProcessorGraph
*/
class JUCE_API AudioGraphIOProcessor : public AudioPluginInstance
{
public:
/** Specifies the mode in which this processor will operate.
*/
enum IODeviceType
{
audioInputNode, /**< In this mode, the processor has output channels
representing all the audio input channels that are
coming into its parent audio graph. */
audioOutputNode, /**< In this mode, the processor has input channels
representing all the audio output channels that are
going out of its parent audio graph. */
midiInputNode, /**< In this mode, the processor has a midi output which
delivers the same midi data that is arriving at its
parent graph. */
midiOutputNode /**< In this mode, the processor has a midi input and
any data sent to it will be passed out of the parent
graph. */
};
//==============================================================================
/** Returns the mode of this processor. */
IODeviceType getType() const noexcept { return type; }
/** Returns the parent graph to which this processor belongs, or nullptr if it
hasn't yet been added to one. */
AudioProcessorGraph* getParentGraph() const noexcept { return graph; }
/** True if this is an audio or midi input. */
bool isInput() const noexcept;
/** True if this is an audio or midi output. */
bool isOutput() const noexcept;
//==============================================================================
AudioGraphIOProcessor (IODeviceType);
~AudioGraphIOProcessor();
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:
//==============================================================================
ReferenceCountedArray<Node> nodes;
NodeID lastNodeID = {};
struct RenderSequenceFloat;
struct RenderSequenceDouble;
std::unique_ptr<RenderSequenceFloat> renderSequenceFloat;
std::unique_ptr<RenderSequenceDouble> renderSequenceDouble;
friend class AudioGraphIOProcessor;
Atomic<int> isPrepared { 0 };
void topologyChanged();
void handleAsyncUpdate() override;
void clearRenderingSequence();
void buildRenderingSequence();
bool anyNodesNeedPreparing() const noexcept;
bool isConnected (Node* src, int sourceChannel, Node* dest, int destChannel) const noexcept;
bool isAnInputTo (Node& src, Node& dst, int recursionCheck) const noexcept;
bool canConnect (Node* src, int sourceChannel, Node* dest, int destChannel) const noexcept;
bool isLegal (Node* src, int sourceChannel, Node* dest, int destChannel) const noexcept;
static void getNodeConnections (Node&, std::vector<Connection>&);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph)
};
} // namespace juce

View File

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

View File

@ -0,0 +1,557 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class ParameterListener : private AudioProcessorParameter::Listener,
private AudioProcessorListener,
private Timer
{
public:
ParameterListener (AudioProcessor& p, AudioProcessorParameter& param)
: processor (p), parameter (param)
{
if (LegacyAudioParameter::isLegacy (&parameter))
processor.addListener (this);
else
parameter.addListener (this);
startTimer (100);
}
virtual ~ParameterListener()
{
if (LegacyAudioParameter::isLegacy (&parameter))
processor.removeListener (this);
else
parameter.removeListener (this);
}
AudioProcessorParameter& getParameter() noexcept
{
return parameter;
}
virtual void handleNewParameterValue() = 0;
private:
//==============================================================================
void parameterValueChanged (int, float) override
{
parameterValueHasChanged = 1;
}
void parameterGestureChanged (int, bool) override {}
//==============================================================================
void audioProcessorParameterChanged (AudioProcessor*, int index, float) override
{
if (index == parameter.getParameterIndex())
parameterValueHasChanged = 1;
}
void audioProcessorChanged (AudioProcessor*) override {}
//==============================================================================
void timerCallback() override
{
if (parameterValueHasChanged.compareAndSetBool (0, 1))
{
handleNewParameterValue();
startTimerHz (50);
}
else
{
startTimer (jmin (250, getTimerInterval() + 10));
}
}
AudioProcessor& processor;
AudioProcessorParameter& parameter;
Atomic<int> parameterValueHasChanged { 0 };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterListener)
};
class BooleanParameterComponent final : public Component,
private ParameterListener
{
public:
BooleanParameterComponent (AudioProcessor& processor, AudioProcessorParameter& param)
: ParameterListener (processor, param)
{
// Set the initial value.
handleNewParameterValue();
button.onClick = [this]() { buttonClicked(); };
addAndMakeVisible (button);
}
void paint (Graphics&) override {}
void resized() override
{
auto area = getLocalBounds();
area.removeFromLeft (8);
button.setBounds (area.reduced (0, 10));
}
private:
void handleNewParameterValue() override
{
auto parameterState = getParameterState (getParameter().getValue());
if (button.getToggleState() != parameterState)
button.setToggleState (parameterState, dontSendNotification);
}
void buttonClicked()
{
if (getParameterState (getParameter().getValue()) != button.getToggleState())
{
getParameter().beginChangeGesture();
getParameter().setValueNotifyingHost (button.getToggleState() ? 1.0f : 0.0f);
getParameter().endChangeGesture();
}
}
bool getParameterState (float value) const noexcept
{
return value >= 0.5f;
}
ToggleButton button;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BooleanParameterComponent)
};
class SwitchParameterComponent final : public Component,
private ParameterListener
{
public:
SwitchParameterComponent (AudioProcessor& processor, AudioProcessorParameter& param)
: ParameterListener (processor, param)
{
auto* leftButton = buttons.add (new TextButton());
auto* rightButton = buttons.add (new TextButton());
for (auto* button : buttons)
{
button->setRadioGroupId (293847);
button->setClickingTogglesState (true);
}
leftButton ->setButtonText (getParameter().getText (0.0f, 16));
rightButton->setButtonText (getParameter().getText (1.0f, 16));
leftButton ->setConnectedEdges (Button::ConnectedOnRight);
rightButton->setConnectedEdges (Button::ConnectedOnLeft);
// Set the initial value.
leftButton->setToggleState (true, dontSendNotification);
handleNewParameterValue();
rightButton->onStateChange = [this]() { rightButtonChanged(); };
for (auto* button : buttons)
addAndMakeVisible (button);
}
void paint (Graphics&) override {}
void resized() override
{
auto area = getLocalBounds().reduced (0, 8);
area.removeFromLeft (8);
for (auto* button : buttons)
button->setBounds (area.removeFromLeft (80));
}
private:
void handleNewParameterValue() override
{
bool newState = getParameterState();
if (buttons[1]->getToggleState() != newState)
{
buttons[1]->setToggleState (newState, dontSendNotification);
buttons[0]->setToggleState (! newState, dontSendNotification);
}
}
void rightButtonChanged()
{
auto buttonState = buttons[1]->getToggleState();
if (getParameterState() != buttonState)
{
getParameter().beginChangeGesture();
if (getParameter().getAllValueStrings().isEmpty())
{
getParameter().setValueNotifyingHost (buttonState ? 1.0f : 0.0f);
}
else
{
// When a parameter provides a list of strings we must set its
// value using those strings, rather than a float, because VSTs can
// have uneven spacing between the different allowed values and we
// want the snapping behaviour to be consistent with what we do with
// a combo box.
String selectedText = buttonState ? buttons[1]->getButtonText() : buttons[0]->getButtonText();
getParameter().setValueNotifyingHost (getParameter().getValueForText (selectedText));
}
getParameter().endChangeGesture();
}
}
bool getParameterState()
{
if (getParameter().getAllValueStrings().isEmpty())
return getParameter().getValue() > 0.5f;
auto index = getParameter().getAllValueStrings()
.indexOf (getParameter().getCurrentValueAsText());
if (index < 0)
{
// The parameter is producing some unexpected text, so we'll do
// some linear interpolation.
index = roundToInt (getParameter().getValue());
}
return index == 1;
}
OwnedArray<TextButton> buttons;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SwitchParameterComponent)
};
class ChoiceParameterComponent final : public Component,
private ParameterListener
{
public:
ChoiceParameterComponent (AudioProcessor& processor, AudioProcessorParameter& param)
: ParameterListener (processor, param),
parameterValues (getParameter().getAllValueStrings())
{
box.addItemList (parameterValues, 1);
// Set the initial value.
handleNewParameterValue();
box.onChange = [this]() { boxChanged(); };
addAndMakeVisible (box);
}
void paint (Graphics&) override {}
void resized() override
{
auto area = getLocalBounds();
area.removeFromLeft (8);
box.setBounds (area.reduced (0, 10));
}
private:
void handleNewParameterValue() override
{
auto index = parameterValues.indexOf (getParameter().getCurrentValueAsText());
if (index < 0)
{
// The parameter is producing some unexpected text, so we'll do
// some linear interpolation.
index = roundToInt (getParameter().getValue() * (parameterValues.size() - 1));
}
box.setSelectedItemIndex (index);
}
void boxChanged()
{
if (getParameter().getCurrentValueAsText() != box.getText())
{
getParameter().beginChangeGesture();
// When a parameter provides a list of strings we must set its
// value using those strings, rather than a float, because VSTs can
// have uneven spacing between the different allowed values.
getParameter().setValueNotifyingHost (getParameter().getValueForText (box.getText()));
getParameter().endChangeGesture();
}
}
ComboBox box;
const StringArray parameterValues;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChoiceParameterComponent)
};
class SliderParameterComponent final : public Component,
private ParameterListener
{
public:
SliderParameterComponent (AudioProcessor& processor, AudioProcessorParameter& param)
: ParameterListener (processor, param)
{
if (getParameter().getNumSteps() != AudioProcessor::getDefaultNumParameterSteps())
slider.setRange (0.0, 1.0, 1.0 / (getParameter().getNumSteps() - 1.0));
else
slider.setRange (0.0, 1.0);
slider.setScrollWheelEnabled (false);
addAndMakeVisible (slider);
valueLabel.setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId));
valueLabel.setBorderSize ({ 1, 1, 1, 1 });
valueLabel.setJustificationType (Justification::centred);
addAndMakeVisible (valueLabel);
// Set the initial value.
handleNewParameterValue();
slider.onValueChange = [this]() { sliderValueChanged(); };
slider.onDragStart = [this]() { sliderStartedDragging(); };
slider.onDragEnd = [this]() { sliderStoppedDragging(); };
}
void paint (Graphics&) override {}
void resized() override
{
auto area = getLocalBounds().reduced (0, 10);
valueLabel.setBounds (area.removeFromRight (80));
area.removeFromLeft (6);
slider.setBounds (area);
}
private:
void updateTextDisplay()
{
valueLabel.setText (getParameter().getCurrentValueAsText(), dontSendNotification);
}
void handleNewParameterValue() override
{
if (! isDragging)
{
slider.setValue (getParameter().getValue(), dontSendNotification);
updateTextDisplay();
}
}
void sliderValueChanged()
{
auto newVal = (float) slider.getValue();
if (getParameter().getValue() != newVal)
{
if (! isDragging)
getParameter().beginChangeGesture();
getParameter().setValueNotifyingHost ((float) slider.getValue());
updateTextDisplay();
if (! isDragging)
getParameter().endChangeGesture();
}
}
void sliderStartedDragging()
{
isDragging = true;
getParameter().beginChangeGesture();
}
void sliderStoppedDragging()
{
isDragging = false;
getParameter().endChangeGesture();
}
Slider slider { Slider::LinearHorizontal, Slider::TextEntryBoxPosition::NoTextBox };
Label valueLabel;
bool isDragging = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderParameterComponent)
};
class ParameterDisplayComponent : public Component
{
public:
ParameterDisplayComponent (AudioProcessor& processor, AudioProcessorParameter& param)
: parameter (param)
{
parameterName.setText (parameter.getName (128), dontSendNotification);
parameterName.setJustificationType (Justification::centredRight);
addAndMakeVisible (parameterName);
parameterLabel.setText (parameter.getLabel(), dontSendNotification);
addAndMakeVisible (parameterLabel);
if (param.isBoolean())
{
// The AU, AUv3 and VST (only via a .vstxml file) SDKs support
// marking a parameter as boolean. If you want consistency across
// all formats then it might be best to use a
// SwitchParameterComponent instead.
parameterComp.reset (new BooleanParameterComponent (processor, param));
}
else if (param.getNumSteps() == 2)
{
// Most hosts display any parameter with just two steps as a switch.
parameterComp.reset (new SwitchParameterComponent (processor, param));
}
else if (! param.getAllValueStrings().isEmpty())
{
// If we have a list of strings to represent the different states a
// parameter can be in then we should present a dropdown allowing a
// user to pick one of them.
parameterComp.reset (new ChoiceParameterComponent (processor, param));
}
else
{
// Everything else can be represented as a slider.
parameterComp.reset (new SliderParameterComponent (processor, param));
}
addAndMakeVisible (parameterComp.get());
setSize (400, 40);
}
void paint (Graphics&) override {}
void resized() override
{
auto area = getLocalBounds();
parameterName.setBounds (area.removeFromLeft (100));
parameterLabel.setBounds (area.removeFromRight (50));
parameterComp->setBounds (area);
}
private:
AudioProcessorParameter& parameter;
Label parameterName, parameterLabel;
std::unique_ptr<Component> parameterComp;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterDisplayComponent)
};
class ParametersPanel : public Component
{
public:
ParametersPanel (AudioProcessor& processor, const Array<AudioProcessorParameter*>& parameters)
{
for (auto* param : parameters)
if (param->isAutomatable())
addAndMakeVisible (paramComponents.add (new ParameterDisplayComponent (processor, *param)));
if (auto* comp = paramComponents[0])
setSize (comp->getWidth(), comp->getHeight() * paramComponents.size());
else
setSize (400, 100);
}
void paint (Graphics& g) override
{
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
}
void resized() override
{
auto area = getLocalBounds();
for (auto* comp : paramComponents)
comp->setBounds (area.removeFromTop (comp->getHeight()));
}
private:
OwnedArray<ParameterDisplayComponent> paramComponents;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParametersPanel)
};
//==============================================================================
struct GenericAudioProcessorEditor::Pimpl
{
Pimpl (GenericAudioProcessorEditor& parent)
: owner (parent)
{
auto* p = parent.getAudioProcessor();
jassert (p != nullptr);
juceParameters.update (*p, false);
owner.setOpaque (true);
view.setViewedComponent (new ParametersPanel (*p, juceParameters.params));
owner.addAndMakeVisible (view);
view.setScrollBarsShown (true, false);
}
//==============================================================================
GenericAudioProcessorEditor& owner;
LegacyAudioParametersWrapper juceParameters;
Viewport view;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
};
//==============================================================================
GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p)
: AudioProcessorEditor (p), pimpl (new Pimpl (*this))
{
setSize (pimpl->view.getViewedComponent()->getWidth() + pimpl->view.getVerticalScrollBar().getWidth(),
jmin (pimpl->view.getViewedComponent()->getHeight(), 400));
}
GenericAudioProcessorEditor::~GenericAudioProcessorEditor() {}
void GenericAudioProcessorEditor::paint (Graphics& g)
{
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
}
void GenericAudioProcessorEditor::resized()
{
pimpl->view.setBounds (getLocalBounds());
}
} // namespace juce

View File

@ -0,0 +1,61 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A type of UI component that displays the parameters of an AudioProcessor as
a simple list of sliders, combo boxes and switches.
This can be used for showing an editor for a processor that doesn't supply
its own custom editor.
@see AudioProcessor
@tags{Audio}
*/
class JUCE_API GenericAudioProcessorEditor : public AudioProcessorEditor
{
public:
//==============================================================================
GenericAudioProcessorEditor (AudioProcessor* owner);
~GenericAudioProcessorEditor();
//==============================================================================
void paint (Graphics&) override;
void resized() override;
private:
//==============================================================================
struct Pimpl;
std::unique_ptr<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericAudioProcessorEditor)
};
} // namespace juce

View File

@ -0,0 +1,151 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
PluginDescription::PluginDescription()
: uid (0),
isInstrument (false),
numInputChannels (0),
numOutputChannels (0),
hasSharedContainer (false)
{
}
PluginDescription::~PluginDescription()
{
}
PluginDescription::PluginDescription (const PluginDescription& other)
: name (other.name),
descriptiveName (other.descriptiveName),
pluginFormatName (other.pluginFormatName),
category (other.category),
manufacturerName (other.manufacturerName),
version (other.version),
fileOrIdentifier (other.fileOrIdentifier),
lastFileModTime (other.lastFileModTime),
lastInfoUpdateTime (other.lastInfoUpdateTime),
uid (other.uid),
isInstrument (other.isInstrument),
numInputChannels (other.numInputChannels),
numOutputChannels (other.numOutputChannels),
hasSharedContainer (other.hasSharedContainer)
{
}
PluginDescription& PluginDescription::operator= (const PluginDescription& other)
{
name = other.name;
descriptiveName = other.descriptiveName;
pluginFormatName = other.pluginFormatName;
category = other.category;
manufacturerName = other.manufacturerName;
version = other.version;
fileOrIdentifier = other.fileOrIdentifier;
uid = other.uid;
isInstrument = other.isInstrument;
lastFileModTime = other.lastFileModTime;
lastInfoUpdateTime = other.lastInfoUpdateTime;
numInputChannels = other.numInputChannels;
numOutputChannels = other.numOutputChannels;
hasSharedContainer = other.hasSharedContainer;
return *this;
}
bool PluginDescription::isDuplicateOf (const PluginDescription& other) const noexcept
{
return fileOrIdentifier == other.fileOrIdentifier
&& uid == other.uid;
}
static String getPluginDescSuffix (const PluginDescription& d)
{
return "-" + String::toHexString (d.fileOrIdentifier.hashCode())
+ "-" + String::toHexString (d.uid);
}
bool PluginDescription::matchesIdentifierString (const String& identifierString) const
{
return identifierString.endsWithIgnoreCase (getPluginDescSuffix (*this));
}
String PluginDescription::createIdentifierString() const
{
return pluginFormatName + "-" + name + getPluginDescSuffix (*this);
}
XmlElement* PluginDescription::createXml() const
{
XmlElement* const e = new 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 ("uid", String::toHexString (uid));
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);
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");
uid = xml.getStringAttribute ("uid").getHexValue32();
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);
return true;
}
return false;
}
} // namespace juce

View File

@ -0,0 +1,158 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
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();
PluginDescription (const PluginDescription& other);
PluginDescription& operator= (const PluginDescription& other);
~PluginDescription();
//==============================================================================
/** 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;
/** 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 uid;
/** True if the plug-in identifies itself as a synthesiser. */
bool isInstrument;
/** The number of inputs. */
int numInputChannels;
/** The number of outputs. */
int numOutputChannels;
/** True if the plug-in is part of a multi-type container, e.g. a VST Shell. */
bool hasSharedContainer;
/** 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
*/
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