1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-13 00:04:19 +00:00

Added Animated App template and examples

This commit is contained in:
Felix Faire 2014-10-29 15:55:23 +00:00
parent fefcf7aca6
commit ff6520a89a
1141 changed files with 438491 additions and 94 deletions

View file

@ -0,0 +1,26 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
AudioPluginFormat::AudioPluginFormat() noexcept {}
AudioPluginFormat::~AudioPluginFormat() {}

View file

@ -0,0 +1,112 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPLUGINFORMAT_H_INCLUDED
#define JUCE_AUDIOPLUGINFORMAT_H_INCLUDED
//==============================================================================
/**
The base class for a type of plugin format, such as VST, AudioUnit, LADSPA, etc.
@see AudioFormatManager
*/
class JUCE_API AudioPluginFormat
{
public:
//==============================================================================
/** Destructor. */
virtual ~AudioPluginFormat();
//==============================================================================
/** Returns the format name.
E.g. "VST", "AudioUnit", etc.
*/
virtual String getName() const = 0;
/** This tries to create descriptions for all the plugin types available in
a binary module file.
The file will be some kind of DLL or bundle.
Normally there will only be one type returned, but some plugins
(e.g. VST shells) can use a single DLL to create a set of different plugin
subtypes, so in that case, each subtype is returned as a separate object.
*/
virtual void findAllTypesForFile (OwnedArray<PluginDescription>& results,
const String& fileOrIdentifier) = 0;
/** Tries to recreate a type from a previously generated PluginDescription.
@see PluginDescription::createInstance
*/
virtual AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc,
double initialSampleRate,
int initialBufferSize) = 0;
/** Should do a quick check to see if this file or directory might be a plugin of
this format.
This is for searching for potential files, so it shouldn't actually try to
load the plugin or do anything time-consuming.
*/
virtual bool fileMightContainThisPluginType (const String& fileOrIdentifier) = 0;
/** Returns a readable version of the name of the plugin that this identifier refers to. */
virtual String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) = 0;
/** Returns true if this plugin's version or date has changed and it should be re-checked. */
virtual bool pluginNeedsRescanning (const PluginDescription&) = 0;
/** Checks whether this plugin could possibly be loaded.
It doesn't actually need to load it, just to check whether the file or component
still exists.
*/
virtual bool doesPluginStillExist (const PluginDescription& desc) = 0;
/** Returns true if this format needs to run a scan to find its list of plugins. */
virtual bool canScanForPlugins() const = 0;
/** Searches a suggested set of directories for any plugins in this format.
The path might be ignored, e.g. by AUs, which are found by the OS rather
than manually.
*/
virtual StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch,
bool recursive) = 0;
/** Returns the typical places to look for this kind of plugin.
Note that if this returns no paths, it means that the format doesn't search in
files or folders, e.g. AudioUnits.
*/
virtual FileSearchPath getDefaultLocationsToSearch() = 0;
protected:
//==============================================================================
AudioPluginFormat() noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormat)
};
#endif // JUCE_AUDIOPLUGINFORMAT_H_INCLUDED

View file

@ -0,0 +1,104 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
AudioPluginFormatManager::AudioPluginFormatManager() {}
AudioPluginFormatManager::~AudioPluginFormatManager() {}
//==============================================================================
void AudioPluginFormatManager::addDefaultFormats()
{
#if JUCE_DEBUG
// you should only call this method once!
for (int i = formats.size(); --i >= 0;)
{
#if JUCE_PLUGINHOST_VST
jassert (dynamic_cast <VSTPluginFormat*> (formats[i]) == nullptr);
#endif
#if JUCE_PLUGINHOST_VST3
jassert (dynamic_cast <VST3PluginFormat*> (formats[i]) == nullptr);
#endif
#if JUCE_PLUGINHOST_AU && JUCE_MAC
jassert (dynamic_cast <AudioUnitPluginFormat*> (formats[i]) == nullptr);
#endif
#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX
jassert (dynamic_cast <LADSPAPluginFormat*> (formats[i]) == nullptr);
#endif
}
#endif
#if JUCE_PLUGINHOST_AU && JUCE_MAC
formats.add (new AudioUnitPluginFormat());
#endif
#if JUCE_PLUGINHOST_VST
formats.add (new VSTPluginFormat());
#endif
#if JUCE_PLUGINHOST_VST3
formats.add (new VST3PluginFormat());
#endif
#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX
formats.add (new LADSPAPluginFormat());
#endif
}
int AudioPluginFormatManager::getNumFormats()
{
return formats.size();
}
AudioPluginFormat* AudioPluginFormatManager::getFormat (const int index)
{
return formats [index];
}
void AudioPluginFormatManager::addFormat (AudioPluginFormat* const format)
{
formats.add (format);
}
AudioPluginInstance* AudioPluginFormatManager::createPluginInstance (const PluginDescription& description, double rate,
int blockSize, String& errorMessage) const
{
for (int i = 0; i < formats.size(); ++i)
if (AudioPluginInstance* result = formats.getUnchecked(i)->createInstanceFromDescription (description, rate, blockSize))
return result;
errorMessage = doesPluginStillExist (description) ? TRANS ("This plug-in failed to load correctly")
: TRANS ("This plug-in file no longer exists");
return nullptr;
}
bool AudioPluginFormatManager::doesPluginStillExist (const PluginDescription& description) const
{
for (int i = 0; i < formats.size(); ++i)
if (formats.getUnchecked(i)->getName() == description.pluginFormatName)
return formats.getUnchecked(i)->doesPluginStillExist (description);
return false;
}

View file

@ -0,0 +1,99 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPLUGINFORMATMANAGER_H_INCLUDED
#define JUCE_AUDIOPLUGINFORMATMANAGER_H_INCLUDED
//==============================================================================
/**
This maintains a list of known AudioPluginFormats.
@see AudioPluginFormat
*/
class JUCE_API AudioPluginFormatManager
{
public:
//==============================================================================
AudioPluginFormatManager();
/** Destructor. */
~AudioPluginFormatManager();
//==============================================================================
/** Adds any formats that it knows about, e.g. VST.
*/
void addDefaultFormats();
//==============================================================================
/** Returns the number of types of format that are available.
Use getFormat() to get one of them.
*/
int getNumFormats();
/** Returns one of the available formats.
@see getNumFormats
*/
AudioPluginFormat* getFormat (int index);
//==============================================================================
/** Adds a format to the list.
The object passed in will be owned and deleted by the manager.
*/
void addFormat (AudioPluginFormat* format);
//==============================================================================
/** Tries to load the type for this description, by trying all the formats
that this manager knows about.
The caller is responsible for deleting the object that is returned.
If it can't load the plugin, it returns nullptr and leaves a message in the
errorMessage string.
*/
AudioPluginInstance* createPluginInstance (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
String& errorMessage) const;
/** Checks that the file or component for this plugin actually still exists.
(This won't try to load the plugin)
*/
bool doesPluginStillExist (const PluginDescription& description) const;
private:
//==============================================================================
OwnedArray<AudioPluginFormat> formats;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormatManager)
};
#endif // JUCE_AUDIOPLUGINFORMATMANAGER_H_INCLUDED

View file

@ -0,0 +1,55 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#if (JUCE_PLUGINHOST_AU && JUCE_MAC) || DOXYGEN
//==============================================================================
/**
Implements a plugin format manager for AudioUnits.
*/
class JUCE_API AudioUnitPluginFormat : public AudioPluginFormat
{
public:
//==============================================================================
AudioUnitPluginFormat();
~AudioUnitPluginFormat();
//==============================================================================
String getName() const override { return "AudioUnit"; }
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, double, int) override;
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
bool pluginNeedsRescanning (const PluginDescription&) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive);
bool doesPluginStillExist (const PluginDescription&) override;
FileSearchPath getDefaultLocationsToSearch() override;
bool canScanForPlugins() const override { return true; }
private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginFormat)
};
#endif

View file

@ -0,0 +1,703 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX
} // (juce namespace)
#include <ladspa.h>
namespace juce
{
static int shellLADSPAUIDToCreate = 0;
static int insideLADSPACallback = 0;
#define JUCE_LADSPA_LOGGING 1
#if JUCE_LADSPA_LOGGING
#define JUCE_LADSPA_LOG(x) Logger::writeToLog (x);
#else
#define JUCE_LADSPA_LOG(x)
#endif
//==============================================================================
class LADSPAModuleHandle : public ReferenceCountedObject
{
public:
LADSPAModuleHandle (const File& f)
: file (f), moduleMain (nullptr)
{
getActiveModules().add (this);
}
~LADSPAModuleHandle()
{
getActiveModules().removeFirstMatchingValue (this);
close();
}
typedef ReferenceCountedObjectPtr<LADSPAModuleHandle> Ptr;
static Array <LADSPAModuleHandle*>& getActiveModules()
{
static Array <LADSPAModuleHandle*> activeModules;
return activeModules;
}
static LADSPAModuleHandle* findOrCreateModule (const File& file)
{
for (int i = getActiveModules().size(); --i >= 0;)
{
LADSPAModuleHandle* const module = getActiveModules().getUnchecked(i);
if (module->file == file)
return module;
}
++insideLADSPACallback;
shellLADSPAUIDToCreate = 0;
JUCE_LADSPA_LOG ("Loading LADSPA module: " + file.getFullPathName());
ScopedPointer<LADSPAModuleHandle> m (new LADSPAModuleHandle (file));
if (! m->open())
m = nullptr;
--insideLADSPACallback;
return m.release();
}
File file;
LADSPA_Descriptor_Function moduleMain;
private:
DynamicLibrary module;
bool open()
{
module.open (file.getFullPathName());
moduleMain = (LADSPA_Descriptor_Function) module.getFunction ("ladspa_descriptor");
return moduleMain != nullptr;
}
void close()
{
module.close();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAModuleHandle)
};
//==============================================================================
class LADSPAPluginInstance : public AudioPluginInstance
{
public:
LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m)
: module (m), plugin (nullptr), handle (nullptr),
initialised (false), tempBuffer (1, 1)
{
++insideLADSPACallback;
name = module->file.getFileNameWithoutExtension();
JUCE_LADSPA_LOG ("Creating LADSPA instance: " + name);
if (module->moduleMain != nullptr)
{
plugin = module->moduleMain (shellLADSPAUIDToCreate);
if (plugin == nullptr)
{
JUCE_LADSPA_LOG ("Cannot find any valid descriptor in shared library");
--insideLADSPACallback;
return;
}
}
else
{
JUCE_LADSPA_LOG ("Cannot find any valid plugin in shared library");
--insideLADSPACallback;
return;
}
const double sampleRate = getSampleRate() > 0 ? getSampleRate() : 44100.0;
handle = plugin->instantiate (plugin, (uint32) sampleRate);
--insideLADSPACallback;
}
~LADSPAPluginInstance()
{
const ScopedLock sl (lock);
jassert (insideLADSPACallback == 0);
if (handle != nullptr && plugin != nullptr && plugin->cleanup != nullptr)
plugin->cleanup (handle);
initialised = false;
module = nullptr;
plugin = nullptr;
handle = nullptr;
}
void initialise (double initialSampleRate, int initialBlockSize)
{
setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize);
if (initialised || plugin == nullptr || handle == nullptr)
return;
JUCE_LADSPA_LOG ("Initialising LADSPA: " + name);
initialised = true;
inputs.clear();
outputs.clear();
parameters.clear();
for (unsigned int i = 0; i < plugin->PortCount; ++i)
{
const LADSPA_PortDescriptor portDesc = plugin->PortDescriptors[i];
if ((portDesc & LADSPA_PORT_CONTROL) != 0)
parameters.add (i);
if ((portDesc & LADSPA_PORT_AUDIO) != 0)
{
if ((portDesc & LADSPA_PORT_INPUT) != 0) inputs.add (i);
if ((portDesc & LADSPA_PORT_OUTPUT) != 0) outputs.add (i);
}
}
parameterValues.calloc (parameters.size());
for (int i = 0; i < parameters.size(); ++i)
plugin->connect_port (handle, parameters[i], &(parameterValues[i].scaled));
setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize);
setCurrentProgram (0);
setLatencySamples (0);
// Some plugins crash if this doesn't happen:
if (plugin->activate != nullptr) plugin->activate (handle);
if (plugin->deactivate != nullptr) plugin->deactivate (handle);
}
//==============================================================================
// AudioPluginInstance methods:
void fillInPluginDescription (PluginDescription& desc) const
{
desc.name = getName();
desc.fileOrIdentifier = module->file.getFullPathName();
desc.uid = getUID();
desc.lastFileModTime = module->file.getLastModificationTime();
desc.pluginFormatName = "LADSPA";
desc.category = getCategory();
desc.manufacturerName = plugin != nullptr ? String (plugin->Maker) : String();
desc.version = getVersion();
desc.numInputChannels = getNumInputChannels();
desc.numOutputChannels = getNumOutputChannels();
desc.isInstrument = false;
}
const String getName() const
{
if (plugin != nullptr && plugin->Label != nullptr)
return plugin->Label;
return name;
}
int getUID() const
{
if (plugin != nullptr && plugin->UniqueID != 0)
return (int) plugin->UniqueID;
return module->file.hashCode();
}
String getVersion() const { return LADSPA_VERSION; }
String getCategory() const { return "Effect"; }
bool acceptsMidi() const { return false; }
bool producesMidi() const { return false; }
bool silenceInProducesSilenceOut() const { return plugin == nullptr; } // ..any way to get a proper answer for these?
double getTailLengthSeconds() const { return 0.0; }
//==============================================================================
void prepareToPlay (double newSampleRate, int samplesPerBlockExpected)
{
setLatencySamples (0);
initialise (newSampleRate, samplesPerBlockExpected);
if (initialised)
{
tempBuffer.setSize (jmax (1, outputs.size()), samplesPerBlockExpected);
// dodgy hack to force some plugins to initialise the sample rate..
if (getNumParameters() > 0)
{
const float old = getParameter (0);
setParameter (0, (old < 0.5f) ? 1.0f : 0.0f);
setParameter (0, old);
}
if (plugin->activate != nullptr)
plugin->activate (handle);
}
}
void releaseResources()
{
if (handle != nullptr && plugin->deactivate != nullptr)
plugin->deactivate (handle);
tempBuffer.setSize (1, 1);
}
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
const int numSamples = buffer.getNumSamples();
if (initialised && plugin != nullptr && handle != nullptr)
{
for (int i = 0; i < inputs.size(); ++i)
plugin->connect_port (handle, inputs[i],
i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr);
if (plugin->run != nullptr)
{
for (int i = 0; i < outputs.size(); ++i)
plugin->connect_port (handle, outputs.getUnchecked(i),
i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr);
plugin->run (handle, numSamples);
return;
}
if (plugin->run_adding != nullptr)
{
tempBuffer.setSize (outputs.size(), numSamples);
tempBuffer.clear();
for (int i = 0; i < outputs.size(); ++i)
plugin->connect_port (handle, outputs.getUnchecked(i), tempBuffer.getWritePointer (i));
plugin->run_adding (handle, numSamples);
for (int i = 0; i < outputs.size(); ++i)
if (i < buffer.getNumChannels())
buffer.copyFrom (i, 0, tempBuffer, i, 0, numSamples);
return;
}
jassertfalse; // no callback to use?
}
for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
buffer.clear (i, 0, numSamples);
}
bool isInputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumInputChannels()); }
bool isOutputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumInputChannels()); }
const String getInputChannelName (const int index) const
{
if (isPositiveAndBelow (index, getNumInputChannels()))
return String (plugin->PortNames [inputs [index]]).trim();
return String();
}
const String getOutputChannelName (const int index) const
{
if (isPositiveAndBelow (index, getNumInputChannels()))
return String (plugin->PortNames [outputs [index]]).trim();
return String();
}
//==============================================================================
int getNumParameters() { return handle != nullptr ? parameters.size() : 0; }
bool isParameterAutomatable (int index) const
{
return plugin != nullptr
&& (plugin->PortDescriptors [parameters[index]] & LADSPA_PORT_INPUT) != 0;
}
float getParameter (int index)
{
if (plugin != nullptr && isPositiveAndBelow (index, parameters.size()))
{
const ScopedLock sl (lock);
return parameterValues[index].unscaled;
}
return 0.0f;
}
void setParameter (int index, float newValue)
{
if (plugin != nullptr && isPositiveAndBelow (index, parameters.size()))
{
const ScopedLock sl (lock);
ParameterValue& p = parameterValues[index];
if (p.unscaled != newValue)
p = ParameterValue (getNewParamScaled (plugin->PortRangeHints [parameters[index]], newValue), newValue);
}
}
const String getParameterName (int index)
{
if (plugin != nullptr)
{
jassert (isPositiveAndBelow (index, parameters.size()));
return String (plugin->PortNames [parameters [index]]).trim();
}
return String();
}
const String getParameterText (int index)
{
if (plugin != nullptr)
{
jassert (index >= 0 && index < parameters.size());
const LADSPA_PortRangeHint& hint = plugin->PortRangeHints [parameters [index]];
if (LADSPA_IS_HINT_INTEGER (hint.HintDescriptor))
return String ((int) parameterValues[index].scaled);
return String (parameterValues[index].scaled, 4);
}
return String();
}
//==============================================================================
int getNumPrograms() { return 0; }
int getCurrentProgram() { return 0; }
void setCurrentProgram (int newIndex)
{
if (plugin != nullptr)
for (int i = 0; i < parameters.size(); ++i)
parameterValues[i] = getParamValue (plugin->PortRangeHints [parameters[i]]);
}
const String getProgramName (int index)
{
// XXX
return String();
}
void changeProgramName (int index, const String& newName)
{
// XXX
}
//==============================================================================
void getStateInformation (MemoryBlock& destData)
{
destData.setSize (sizeof (float) * getNumParameters());
destData.fillWith (0);
float* const p = (float*) ((char*) destData.getData());
for (int i = 0; i < getNumParameters(); ++i)
p[i] = getParameter(i);
}
void getCurrentProgramStateInformation (MemoryBlock& destData)
{
getStateInformation (destData);
}
void setStateInformation (const void* data, int sizeInBytes)
{
const float* p = static_cast <const float*> (data);
for (int i = 0; i < getNumParameters(); ++i)
setParameter (i, p[i]);
}
void setCurrentProgramStateInformation (const void* data, int sizeInBytes)
{
setStateInformation (data, sizeInBytes);
}
bool hasEditor() const
{
return false;
}
AudioProcessorEditor* createEditor()
{
return nullptr;
}
bool isValid() const
{
return handle != nullptr;
}
LADSPAModuleHandle::Ptr module;
const LADSPA_Descriptor* plugin;
private:
LADSPA_Handle handle;
String name;
CriticalSection lock;
bool initialised;
AudioSampleBuffer tempBuffer;
Array<int> inputs, outputs, parameters;
struct ParameterValue
{
inline ParameterValue() noexcept : scaled (0), unscaled (0) {}
inline ParameterValue (float s, float u) noexcept : scaled (s), unscaled (u) {}
float scaled, unscaled;
};
HeapBlock<ParameterValue> parameterValues;
//==============================================================================
static float scaledValue (float low, float high, float alpha, bool useLog) noexcept
{
if (useLog && low > 0 && high > 0)
return expf (logf (low) * (1.0f - alpha) + logf (high) * alpha);
return low + (high - low) * alpha;
}
static float toIntIfNecessary (const LADSPA_PortRangeHintDescriptor& desc, float value)
{
return LADSPA_IS_HINT_INTEGER (desc) ? ((float) (int) value) : value;
}
float getNewParamScaled (const LADSPA_PortRangeHint& hint, float newValue) const
{
const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor;
if (LADSPA_IS_HINT_TOGGLED (desc))
return (newValue < 0.5f) ? 0.0f : 1.0f;
const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f;
const float lower = hint.LowerBound * scale;
const float upper = hint.UpperBound * scale;
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_BOUNDED_ABOVE (desc))
return toIntIfNecessary (desc, scaledValue (lower, upper, newValue, LADSPA_IS_HINT_LOGARITHMIC (desc)));
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc)) return toIntIfNecessary (desc, newValue);
if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc)) return toIntIfNecessary (desc, newValue * upper);
return 0.0f;
}
ParameterValue getParamValue (const LADSPA_PortRangeHint& hint) const
{
const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor;
if (LADSPA_IS_HINT_HAS_DEFAULT (desc))
{
if (LADSPA_IS_HINT_DEFAULT_0 (desc)) return ParameterValue();
if (LADSPA_IS_HINT_DEFAULT_1 (desc)) return ParameterValue (1.0f, 1.0f);
if (LADSPA_IS_HINT_DEFAULT_100 (desc)) return ParameterValue (100.0f, 0.5f);
if (LADSPA_IS_HINT_DEFAULT_440 (desc)) return ParameterValue (440.0f, 0.5f);
const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f;
const float lower = hint.LowerBound * scale;
const float upper = hint.UpperBound * scale;
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_DEFAULT_MINIMUM (desc)) return ParameterValue (lower, 0.0f);
if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc) && LADSPA_IS_HINT_DEFAULT_MAXIMUM (desc)) return ParameterValue (upper, 1.0f);
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc))
{
const bool useLog = LADSPA_IS_HINT_LOGARITHMIC (desc);
if (LADSPA_IS_HINT_DEFAULT_LOW (desc)) return ParameterValue (scaledValue (lower, upper, 0.25f, useLog), 0.25f);
if (LADSPA_IS_HINT_DEFAULT_MIDDLE (desc)) return ParameterValue (scaledValue (lower, upper, 0.50f, useLog), 0.50f);
if (LADSPA_IS_HINT_DEFAULT_HIGH (desc)) return ParameterValue (scaledValue (lower, upper, 0.75f, useLog), 0.75f);
}
}
return ParameterValue();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginInstance)
};
//==============================================================================
//==============================================================================
LADSPAPluginFormat::LADSPAPluginFormat() {}
LADSPAPluginFormat::~LADSPAPluginFormat() {}
void LADSPAPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& results,
const String& fileOrIdentifier)
{
if (! fileMightContainThisPluginType (fileOrIdentifier))
return;
PluginDescription desc;
desc.fileOrIdentifier = fileOrIdentifier;
desc.uid = 0;
ScopedPointer<LADSPAPluginInstance> instance (dynamic_cast<LADSPAPluginInstance*> (createInstanceFromDescription (desc, 44100.0, 512)));
if (instance == nullptr || ! instance->isValid())
return;
instance->initialise (44100.0, 512);
instance->fillInPluginDescription (desc);
if (instance->module->moduleMain != nullptr)
{
for (int uid = 0;; ++uid)
{
if (const LADSPA_Descriptor* plugin = instance->module->moduleMain (uid))
{
desc.uid = uid;
desc.name = plugin->Name != nullptr ? plugin->Name : "Unknown";
if (! arrayContainsPlugin (results, desc))
results.add (new PluginDescription (desc));
}
else
{
break;
}
}
}
}
AudioPluginInstance* LADSPAPluginFormat::createInstanceFromDescription (const PluginDescription& desc,
double sampleRate, int blockSize)
{
ScopedPointer<LADSPAPluginInstance> result;
if (fileMightContainThisPluginType (desc.fileOrIdentifier))
{
File file (desc.fileOrIdentifier);
const File previousWorkingDirectory (File::getCurrentWorkingDirectory());
file.getParentDirectory().setAsCurrentWorkingDirectory();
const LADSPAModuleHandle::Ptr module (LADSPAModuleHandle::findOrCreateModule (file));
if (module != nullptr)
{
shellLADSPAUIDToCreate = desc.uid;
result = new LADSPAPluginInstance (module);
if (result->plugin != nullptr && result->isValid())
result->initialise (sampleRate, blockSize);
else
result = nullptr;
}
previousWorkingDirectory.setAsCurrentWorkingDirectory();
}
return result.release();
}
bool LADSPAPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
{
const File f (File::createFileWithoutCheckingPath (fileOrIdentifier));
return f.existsAsFile() && f.hasFileExtension (".so");
}
String LADSPAPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
{
return fileOrIdentifier;
}
bool LADSPAPluginFormat::pluginNeedsRescanning (const PluginDescription& desc)
{
return File (desc.fileOrIdentifier).getLastModificationTime() != desc.lastFileModTime;
}
bool LADSPAPluginFormat::doesPluginStillExist (const PluginDescription& desc)
{
return File::createFileWithoutCheckingPath (desc.fileOrIdentifier).exists();
}
StringArray LADSPAPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive)
{
StringArray results;
for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j)
recursiveFileSearch (results, directoriesToSearch[j], recursive);
return results;
}
void LADSPAPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive)
{
DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories);
while (iter.next())
{
const File f (iter.getFile());
bool isPlugin = false;
if (fileMightContainThisPluginType (f.getFullPathName()))
{
isPlugin = true;
results.add (f.getFullPathName());
}
if (recursive && (! isPlugin) && f.isDirectory())
recursiveFileSearch (results, f, true);
}
}
FileSearchPath LADSPAPluginFormat::getDefaultLocationsToSearch()
{
return FileSearchPath (SystemStats::getEnvironmentVariable ("LADSPA_PATH",
"/usr/lib/ladspa;/usr/local/lib/ladspa;~/.ladspa")
.replace (":", ";"));
}
#endif

View file

@ -0,0 +1,57 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#if (JUCE_PLUGINHOST_LADSPA && JUCE_LINUX) || DOXYGEN
//==============================================================================
/**
Implements a plugin format manager for LADSPA plugins.
*/
class JUCE_API LADSPAPluginFormat : public AudioPluginFormat
{
public:
//==============================================================================
LADSPAPluginFormat();
~LADSPAPluginFormat();
//==============================================================================
String getName() const override { return "LADSPA"; }
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override;
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
bool pluginNeedsRescanning (const PluginDescription&) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override;
bool doesPluginStillExist (const PluginDescription&) override;
FileSearchPath getDefaultLocationsToSearch() override;
bool canScanForPlugins() const override { return true; }
private:
void recursiveFileSearch (StringArray&, const File&, bool recursive);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginFormat)
};
#endif

View file

@ -0,0 +1,420 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_VST3COMMON_H_INCLUDED
#define JUCE_VST3COMMON_H_INCLUDED
//==============================================================================
#define JUCE_DECLARE_VST3_COM_REF_METHODS \
Steinberg::uint32 PLUGIN_API addRef() override { return (Steinberg::uint32) ++refCount; } \
Steinberg::uint32 PLUGIN_API release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; }
#define JUCE_DECLARE_VST3_COM_QUERY_METHODS \
Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID, void** obj) override \
{ \
jassertfalse; \
*obj = nullptr; \
return Steinberg::kNotImplemented; \
}
static bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexcept
{
return std::memcmp (a, b, sizeof (Steinberg::TUID)) == 0;
}
#define TEST_FOR_AND_RETURN_IF_VALID(iidToTest, ClassType) \
if (doUIDsMatch (iidToTest, ClassType::iid)) \
{ \
addRef(); \
*obj = dynamic_cast<ClassType*> (this); \
return Steinberg::kResultOk; \
}
#define TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID(iidToTest, CommonClassType, SourceClassType) \
if (doUIDsMatch (iidToTest, CommonClassType::iid)) \
{ \
addRef(); \
*obj = (CommonClassType*) static_cast<SourceClassType*> (this); \
return Steinberg::kResultOk; \
}
//==============================================================================
static juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); }
static juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); }
// NB: The casts are handled by a Steinberg::UString operator
static juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
static juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
static void toString128 (Steinberg::Vst::String128 result, const juce::String& source)
{
Steinberg::UString (result, 128).fromAscii (source.toUTF8());
}
static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept
{
return reinterpret_cast<Steinberg::Vst::TChar*> (source.toUTF16().getAddress());
}
#if JUCE_WINDOWS
static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND;
#else
static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView;
#endif
//==============================================================================
static Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst::IAudioProcessor* processor,
bool isInput, int busIndex)
{
Steinberg::Vst::SpeakerArrangement arrangement = Steinberg::Vst::SpeakerArr::kEmpty;
if (processor != nullptr)
processor->getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput,
(Steinberg::int32) busIndex, arrangement);
return arrangement;
}
/** For the sake of simplicity, there can only be 1 arrangement type per channel count.
i.e.: 4 channels == k31Cine OR k40Cine
*/
static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept
{
using namespace Steinberg::Vst::SpeakerArr;
switch (numChannels)
{
case 0: return kEmpty;
case 1: return kMono;
case 2: return kStereo;
case 3: return k30Cine;
case 4: return k31Cine;
case 5: return k50;
case 6: return k51;
case 7: return k61Cine;
case 8: return k71CineFullFront;
case 9: return k90;
case 10: return k91;
case 11: return k101;
case 12: return k111;
case 13: return k130;
case 14: return k131;
case 24: return (Steinberg::Vst::SpeakerArrangement) 1929904127; // k222
default: break;
}
jassert (numChannels >= 0);
juce::BigInteger bi;
bi.setRange (0, jmin (numChannels, (int) (sizeof (Steinberg::Vst::SpeakerArrangement) * 8)), true);
return (Steinberg::Vst::SpeakerArrangement) bi.toInt64();
}
//==============================================================================
template <class ObjectType>
class ComSmartPtr
{
public:
ComSmartPtr() noexcept : source (nullptr) {}
ComSmartPtr (ObjectType* object, bool autoAddRef = true) noexcept : source (object) { if (source != nullptr && autoAddRef) source->addRef(); }
ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); }
~ComSmartPtr() { if (source != nullptr) source->release(); }
operator ObjectType*() const noexcept { return source; }
ObjectType* get() const noexcept { return source; }
ObjectType& operator*() const noexcept { return *source; }
ObjectType* operator->() const noexcept { return source; }
ComSmartPtr& operator= (const ComSmartPtr& other) { return operator= (other.source); }
ComSmartPtr& operator= (ObjectType* const newObjectToTakePossessionOf)
{
ComSmartPtr p (newObjectToTakePossessionOf);
std::swap (p.source, source);
return *this;
}
bool operator== (ObjectType* const other) noexcept { return source == other; }
bool operator!= (ObjectType* const other) noexcept { return source != other; }
bool loadFrom (Steinberg::FUnknown* o)
{
*this = nullptr;
return o != nullptr && o->queryInterface (ObjectType::iid, (void**) &source) == Steinberg::kResultOk;
}
bool loadFrom (Steinberg::IPluginFactory* factory, const Steinberg::TUID& uuid)
{
jassert (factory != nullptr);
*this = nullptr;
return factory->createInstance (uuid, ObjectType::iid, (void**) &source) == Steinberg::kResultOk;
}
private:
ObjectType* source;
};
//==============================================================================
class MidiEventList : public Steinberg::Vst::IEventList
{
public:
MidiEventList() {}
virtual ~MidiEventList() {}
JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS
//==============================================================================
void clear()
{
events.clearQuick();
}
Steinberg::int32 PLUGIN_API getEventCount() override
{
return (Steinberg::int32) events.size();
}
// NB: This has to cope with out-of-range indexes from some plugins.
Steinberg::tresult PLUGIN_API getEvent (Steinberg::int32 index, Steinberg::Vst::Event& e) override
{
if (isPositiveAndBelow ((int) index, events.size()))
{
e = events.getReference ((int) index);
return Steinberg::kResultTrue;
}
return Steinberg::kResultFalse;
}
Steinberg::tresult PLUGIN_API addEvent (Steinberg::Vst::Event& e) override
{
events.add (e);
return Steinberg::kResultTrue;
}
//==============================================================================
static void toMidiBuffer (MidiBuffer& result, Steinberg::Vst::IEventList& eventList)
{
const int32 numEvents = eventList.getEventCount();
for (Steinberg::int32 i = 0; i < numEvents; ++i)
{
Steinberg::Vst::Event e;
if (eventList.getEvent (i, e) == Steinberg::kResultOk)
{
switch (e.type)
{
case Steinberg::Vst::Event::kNoteOnEvent:
result.addEvent (MidiMessage::noteOn (createSafeChannel (e.noteOn.channel),
createSafeNote (e.noteOn.pitch),
(Steinberg::uint8) denormaliseToMidiValue (e.noteOn.velocity)),
e.sampleOffset);
break;
case Steinberg::Vst::Event::kNoteOffEvent:
result.addEvent (MidiMessage::noteOff (createSafeChannel (e.noteOff.channel),
createSafeNote (e.noteOff.pitch),
(Steinberg::uint8) denormaliseToMidiValue (e.noteOff.velocity)),
e.sampleOffset);
break;
case Steinberg::Vst::Event::kPolyPressureEvent:
result.addEvent (MidiMessage::aftertouchChange (createSafeChannel (e.polyPressure.channel),
createSafeNote (e.polyPressure.pitch),
denormaliseToMidiValue (e.polyPressure.pressure)),
e.sampleOffset);
break;
case Steinberg::Vst::Event::kDataEvent:
result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, e.data.size),
e.sampleOffset);
break;
default:
break;
}
}
}
}
static void toEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer)
{
MidiBuffer::Iterator iterator (midiBuffer);
MidiMessage msg;
int midiEventPosition = 0;
enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states that no more than 2048 events are allowed at once
int numEvents = 0;
while (iterator.getNextEvent (msg, midiEventPosition))
{
if (++numEvents > maxNumEvents)
break;
Steinberg::Vst::Event e = { 0 };
if (msg.isNoteOn())
{
e.type = Steinberg::Vst::Event::kNoteOnEvent;
e.noteOn.channel = createSafeChannel (msg.getChannel());
e.noteOn.pitch = createSafeNote (msg.getNoteNumber());
e.noteOn.velocity = normaliseMidiValue (msg.getVelocity());
e.noteOn.length = 0;
e.noteOn.tuning = 0.0f;
e.noteOn.noteId = -1;
}
else if (msg.isNoteOff())
{
e.type = Steinberg::Vst::Event::kNoteOffEvent;
e.noteOff.channel = createSafeChannel (msg.getChannel());
e.noteOff.pitch = createSafeNote (msg.getNoteNumber());
e.noteOff.velocity = normaliseMidiValue (msg.getVelocity());
e.noteOff.tuning = 0.0f;
e.noteOff.noteId = -1;
}
else if (msg.isSysEx())
{
e.type = Steinberg::Vst::Event::kDataEvent;
e.data.bytes = msg.getSysExData();
e.data.size = msg.getSysExDataSize();
e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx;
}
else if (msg.isAftertouch())
{
e.type = Steinberg::Vst::Event::kPolyPressureEvent;
e.polyPressure.channel = createSafeChannel (msg.getChannel());
e.polyPressure.pitch = createSafeNote (msg.getNoteNumber());
e.polyPressure.pressure = normaliseMidiValue (msg.getAfterTouchValue());
}
else
{
continue;
}
e.busIndex = 0;
e.sampleOffset = midiEventPosition;
result.addEvent (e);
}
}
private:
Array<Steinberg::Vst::Event, CriticalSection> events;
Atomic<int> refCount;
static Steinberg::int16 createSafeChannel (int channel) noexcept { return (Steinberg::int16) jlimit (0, 15, channel - 1); }
static int createSafeChannel (Steinberg::int16 channel) noexcept { return (int) jlimit (1, 16, channel + 1); }
static Steinberg::int16 createSafeNote (int note) noexcept { return (Steinberg::int16) jlimit (0, 127, note); }
static int createSafeNote (Steinberg::int16 note) noexcept { return jlimit (0, 127, (int) note); }
static float normaliseMidiValue (int value) noexcept { return jlimit (0.0f, 1.0f, (float) value / 127.0f); }
static int denormaliseToMidiValue (float value) noexcept { return roundToInt (jlimit (0.0f, 127.0f, value * 127.0f)); }
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList)
};
//==============================================================================
namespace VST3BufferExchange
{
typedef Array<float*> Bus;
typedef Array<Bus> BusMap;
/** Assigns a series of AudioSampleBuffer's channels to an AudioBusBuffers'
@warning For speed, does not check the channel count and offsets
according to the AudioSampleBuffer
*/
void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers,
Bus& bus,
AudioSampleBuffer& buffer,
int numChannels, int channelStartOffset,
int sampleOffset = 0)
{
const int channelEnd = numChannels + channelStartOffset;
jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels());
bus.clearQuick();
for (int i = channelStartOffset; i < channelEnd; ++i)
bus.add (buffer.getWritePointer (i, sampleOffset));
vstBuffers.channelBuffers32 = bus.getRawDataPointer();
vstBuffers.numChannels = numChannels;
vstBuffers.silenceFlags = 0;
}
static void mapArrangementToBusses (int& channelIndexOffset, int index,
Array<Steinberg::Vst::AudioBusBuffers>& result,
BusMap& busMapToUse, Steinberg::Vst::SpeakerArrangement arrangement,
AudioSampleBuffer& source)
{
const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits();
if (index >= result.size())
result.add (Steinberg::Vst::AudioBusBuffers());
if (index >= busMapToUse.size())
busMapToUse.add (Bus());
if (numChansForBus > 0)
{
associateBufferTo (result.getReference (index),
busMapToUse.getReference (index),
source, numChansForBus, channelIndexOffset);
}
channelIndexOffset += numChansForBus;
}
static void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result, BusMap& busMapToUse,
const Array<Steinberg::Vst::SpeakerArrangement>& arrangements,
AudioSampleBuffer& source)
{
int channelIndexOffset = 0;
for (int i = 0; i < arrangements.size(); ++i)
mapArrangementToBusses (channelIndexOffset, i, result, busMapToUse,
arrangements.getUnchecked (i), source);
}
static void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result,
Steinberg::Vst::IAudioProcessor& processor,
BusMap& busMapToUse, bool isInput, int numBusses,
AudioSampleBuffer& source)
{
int channelIndexOffset = 0;
for (int i = 0; i < numBusses; ++i)
mapArrangementToBusses (channelIndexOffset, i,
result, busMapToUse,
getArrangementForBus (&processor, isInput, i),
source);
}
}
#endif // JUCE_VST3COMMON_H_INCLUDED

View file

@ -0,0 +1,172 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_VST3HEADERS_H_INCLUDED
#define JUCE_VST3HEADERS_H_INCLUDED
#undef Point
#undef Component
// Wow, those Steinberg guys really don't worry too much about compiler warnings.
#if _MSC_VER
#pragma warning (disable: 4505)
#pragma warning (push, 0)
#pragma warning (disable: 4702)
#elif __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#pragma clang diagnostic ignored "-Wreorder"
#pragma clang diagnostic ignored "-Wunsequenced"
#pragma clang diagnostic ignored "-Wint-to-pointer-cast"
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Woverloaded-virtual"
#pragma clang diagnostic ignored "-Wshadow"
#pragma clang diagnostic ignored "-Wdeprecated-register"
#endif
/* These files come with the Steinberg VST3 SDK - to get them, you'll need to
visit the Steinberg website and agree to whatever is currently required to
get them.
Then, you'll need to make sure your include path contains your "VST3 SDK"
directory (or whatever you've named it on your machine). The Introjucer has
a special box for setting this path.
*/
#if JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY
#include <base/source/fstring.h>
#include <pluginterfaces/base/conststringtable.h>
#include <pluginterfaces/base/funknown.h>
#include <pluginterfaces/base/ipluginbase.h>
#include <pluginterfaces/base/ustring.h>
#include <pluginterfaces/gui/iplugview.h>
#include <pluginterfaces/vst/ivstattributes.h>
#include <pluginterfaces/vst/ivstaudioprocessor.h>
#include <pluginterfaces/vst/ivstcomponent.h>
#include <pluginterfaces/vst/ivstcontextmenu.h>
#include <pluginterfaces/vst/ivsteditcontroller.h>
#include <pluginterfaces/vst/ivstevents.h>
#include <pluginterfaces/vst/ivsthostapplication.h>
#include <pluginterfaces/vst/ivstmessage.h>
#include <pluginterfaces/vst/ivstmidicontrollers.h>
#include <pluginterfaces/vst/ivstparameterchanges.h>
#include <pluginterfaces/vst/ivstplugview.h>
#include <pluginterfaces/vst/ivstprocesscontext.h>
#include <pluginterfaces/vst/vsttypes.h>
#include <pluginterfaces/vst/ivstunits.h>
#include <public.sdk/source/common/memorystream.h>
#else
#include <base/source/baseiids.cpp>
#include <base/source/fatomic.cpp>
#include <base/source/fbuffer.cpp>
#include <base/source/fdebug.cpp>
#include <base/source/fobject.cpp>
#include <base/source/frect.cpp>
#include <base/source/fstreamer.cpp>
#include <base/source/fstring.cpp>
#include <base/source/fthread.cpp>
#include <base/source/updatehandler.cpp>
#include <pluginterfaces/base/conststringtable.cpp>
#include <pluginterfaces/base/funknown.cpp>
#include <pluginterfaces/base/ipluginbase.h>
#include <pluginterfaces/base/ustring.cpp>
#include <pluginterfaces/gui/iplugview.h>
#include <public.sdk/source/common/memorystream.cpp>
#include <public.sdk/source/common/pluginview.cpp>
#include <public.sdk/source/vst/vsteditcontroller.cpp>
#include <public.sdk/source/vst/vstbus.cpp>
#include <public.sdk/source/vst/vstinitiids.cpp>
#include <public.sdk/source/vst/vstcomponent.cpp>
#include <public.sdk/source/vst/vstcomponentbase.cpp>
#include <public.sdk/source/vst/vstparameters.cpp>
#include <public.sdk/source/vst/hosting/hostclasses.cpp>
//==============================================================================
namespace Steinberg
{
/** Missing IIDs */
DEF_CLASS_IID (IPluginBase)
DEF_CLASS_IID (IPlugView)
DEF_CLASS_IID (IPlugFrame)
DEF_CLASS_IID (IBStream)
DEF_CLASS_IID (ISizeableStream)
DEF_CLASS_IID (IPluginFactory)
DEF_CLASS_IID (IPluginFactory2)
DEF_CLASS_IID (IPluginFactory3)
}
#endif //JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY
#if _MSC_VER
#pragma warning (pop)
#elif __clang__
#pragma clang diagnostic pop
#endif
//==============================================================================
#undef ASSERT
#undef WARNING
#undef PRINTSYSERROR
#undef DEBUGSTR
#undef DBPRT0
#undef DBPRT1
#undef DBPRT2
#undef DBPRT3
#undef DBPRT4
#undef DBPRT5
#undef min
#undef max
#undef MIN
#undef MAX
#undef calloc
#undef free
#undef malloc
#undef realloc
#undef NEW
#undef NEWVEC
#undef VERIFY
#undef VERIFY_IS
#undef VERIFY_NOT
#undef META_CREATE_FUNC
#undef CLASS_CREATE_FUNC
#undef SINGLE_CREATE_FUNC
#undef _META_CLASS
#undef _META_CLASS_IFACE
#undef _META_CLASS_SINGLE
#undef META_CLASS
#undef META_CLASS_IFACE
#undef META_CLASS_SINGLE
#undef SINGLETON
#undef OBJ_METHODS
#undef QUERY_INTERFACE
#undef LICENCE_UID
#undef BEGIN_FACTORY
#undef DEF_CLASS
#undef DEF_CLASS1
#undef DEF_CLASS2
#undef DEF_CLASS_W
#undef END_FACTORY
#undef Point
#undef Component
#endif // JUCE_VST3HEADERS_H_INCLUDED

View file

@ -0,0 +1,72 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_VST3PLUGINFORMAT_H_INCLUDED
#define JUCE_VST3PLUGINFORMAT_H_INCLUDED
#if JUCE_PLUGINHOST_VST3
/**
Implements a plugin format for VST3s.
*/
class JUCE_API VST3PluginFormat : public AudioPluginFormat
{
public:
/** Constructor */
VST3PluginFormat();
/** Destructor */
~VST3PluginFormat();
//==============================================================================
/** @internal */
String getName() const override { return "VST3"; }
/** @internal */
void findAllTypesForFile (OwnedArray<PluginDescription>& results, const String& fileOrIdentifier) override;
/** @internal */
AudioPluginInstance* createInstanceFromDescription (const PluginDescription& description, double, int) override;
/** @internal */
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
/** @internal */
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
/** @internal */
bool pluginNeedsRescanning (const PluginDescription& description) override;
/** @internal */
StringArray searchPathsForPlugins (const FileSearchPath& searchPath, bool recursive) override;
/** @internal */
bool doesPluginStillExist (const PluginDescription& description) override;
/** @internal */
FileSearchPath getDefaultLocationsToSearch() override;
/** @internal */
bool canScanForPlugins() const override { return true; }
private:
//==============================================================================
void recursiveFileSearch (StringArray&, const File&, bool recursive);
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginFormat)
};
#endif // JUCE_PLUGINHOST_VST3
#endif // JUCE_VST3PLUGINFORMAT_H_INCLUDED

View file

@ -0,0 +1,187 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifdef __aeffect__ // NB: this must come first, *before* the header-guard.
#ifndef JUCE_VSTMIDIEVENTLIST_H_INCLUDED
#define JUCE_VSTMIDIEVENTLIST_H_INCLUDED
//==============================================================================
/** Holds a set of VSTMidiEvent objects and makes it easy to add
events to the list.
This is used by both the VST hosting code and the plugin wrapper.
*/
class VSTMidiEventList
{
public:
//==============================================================================
VSTMidiEventList()
: numEventsUsed (0), numEventsAllocated (0)
{
}
~VSTMidiEventList()
{
freeEvents();
}
//==============================================================================
void clear()
{
numEventsUsed = 0;
if (events != nullptr)
events->numEvents = 0;
}
void addEvent (const void* const midiData, const int numBytes, const int frameOffset)
{
ensureSize (numEventsUsed + 1);
VstMidiEvent* const e = (VstMidiEvent*) (events->events [numEventsUsed]);
events->numEvents = ++numEventsUsed;
if (numBytes <= 4)
{
if (e->type == kVstSysExType)
{
delete[] (((VstMidiSysexEvent*) e)->sysexDump);
e->type = kVstMidiType;
e->byteSize = sizeof (VstMidiEvent);
e->noteLength = 0;
e->noteOffset = 0;
e->detune = 0;
e->noteOffVelocity = 0;
}
e->deltaFrames = frameOffset;
memcpy (e->midiData, midiData, (size_t) numBytes);
}
else
{
VstMidiSysexEvent* const se = (VstMidiSysexEvent*) e;
if (se->type == kVstSysExType)
delete[] se->sysexDump;
se->sysexDump = new char [numBytes];
memcpy (se->sysexDump, midiData, (size_t) numBytes);
se->type = kVstSysExType;
se->byteSize = sizeof (VstMidiSysexEvent);
se->deltaFrames = frameOffset;
se->flags = 0;
se->dumpBytes = numBytes;
se->resvd1 = 0;
se->resvd2 = 0;
}
}
//==============================================================================
// Handy method to pull the events out of an event buffer supplied by the host
// or plugin.
static void addEventsToMidiBuffer (const VstEvents* events, MidiBuffer& dest)
{
for (int i = 0; i < events->numEvents; ++i)
{
const VstEvent* const e = events->events[i];
if (e != nullptr)
{
if (e->type == kVstMidiType)
{
dest.addEvent ((const juce::uint8*) ((const VstMidiEvent*) e)->midiData,
4, e->deltaFrames);
}
else if (e->type == kVstSysExType)
{
dest.addEvent ((const juce::uint8*) ((const VstMidiSysexEvent*) e)->sysexDump,
(int) ((const VstMidiSysexEvent*) e)->dumpBytes,
e->deltaFrames);
}
}
}
}
//==============================================================================
void ensureSize (int numEventsNeeded)
{
if (numEventsNeeded > numEventsAllocated)
{
numEventsNeeded = (numEventsNeeded + 32) & ~31;
const size_t size = 20 + sizeof (VstEvent*) * (size_t) numEventsNeeded;
if (events == nullptr)
events.calloc (size, 1);
else
events.realloc (size, 1);
for (int i = numEventsAllocated; i < numEventsNeeded; ++i)
events->events[i] = allocateVSTEvent();
numEventsAllocated = numEventsNeeded;
}
}
void freeEvents()
{
if (events != nullptr)
{
for (int i = numEventsAllocated; --i >= 0;)
freeVSTEvent (events->events[i]);
events.free();
numEventsUsed = 0;
numEventsAllocated = 0;
}
}
//==============================================================================
HeapBlock <VstEvents> events;
private:
int numEventsUsed, numEventsAllocated;
static VstEvent* allocateVSTEvent()
{
VstEvent* const e = (VstEvent*) std::calloc (1, sizeof (VstMidiEvent) > sizeof (VstMidiSysexEvent) ? sizeof (VstMidiEvent)
: sizeof (VstMidiSysexEvent));
e->type = kVstMidiType;
e->byteSize = sizeof (VstMidiEvent);
return e;
}
static void freeVSTEvent (VstEvent* e)
{
if (e->type == kVstSysExType)
delete[] (((VstMidiSysexEvent*) e)->sysexDump);
std::free (e);
}
};
#endif // JUCE_VSTMIDIEVENTLIST_H_INCLUDED
#endif

View file

@ -0,0 +1,113 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#if JUCE_PLUGINHOST_VST || DOXYGEN
//==============================================================================
/**
Implements a plugin format manager for VSTs.
*/
class JUCE_API VSTPluginFormat : public AudioPluginFormat
{
public:
//==============================================================================
VSTPluginFormat();
~VSTPluginFormat();
//==============================================================================
/** Attempts to retreive the VSTXML data from a plugin.
Will return nullptr if the plugin isn't a VST, or if it doesn't have any VSTXML.
*/
static const XmlElement* getVSTXML (AudioPluginInstance* plugin);
/** Attempts to reload a VST plugin's state from some FXB or FXP data. */
static bool loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize);
/** Attempts to save a VST's state to some FXP or FXB data. */
static bool saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& result, bool asFXB);
/** Attempts to get a VST's state as a chunk of memory. */
static bool getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset);
/** Attempts to set a VST's state from a chunk of memory. */
static bool setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset);
//==============================================================================
/** Base class for some extra functions that can be attached to a VST plugin instance. */
class ExtraFunctions
{
public:
virtual ~ExtraFunctions() {}
/** This should return 10000 * the BPM at this position in the current edit. */
virtual int64 getTempoAt (int64 samplePos) = 0;
/** This should return the host's automation state.
@returns 0 = not supported, 1 = off, 2 = read, 3 = write, 4 = read/write
*/
virtual int getAutomationState() = 0;
};
/** Provides an ExtraFunctions callback object for a plugin to use.
The plugin will take ownership of the object and will delete it automatically.
*/
static void setExtraFunctions (AudioPluginInstance* plugin, ExtraFunctions* functions);
//==============================================================================
#if JUCE_64BIT
typedef int64 VstIntPtr;
#else
typedef int32 VstIntPtr;
#endif
/** This simply calls directly to the VST's AEffect::dispatcher() function. */
static VstIntPtr JUCE_CALLTYPE dispatcher (AudioPluginInstance*, int32, int32, VstIntPtr, void*, float);
//==============================================================================
String getName() const override { return "VST"; }
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override;
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
bool pluginNeedsRescanning (const PluginDescription&) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override;
bool doesPluginStillExist (const PluginDescription&) override;
FileSearchPath getDefaultLocationsToSearch() override;
bool canScanForPlugins() const override { return true; }
/** Can be overridden to receive a callback when each member of a shell plugin is about to be
tested during a call to findAllTypesForFile().
Only the name and uid members of the PluginDescription are guaranteed to be valid when
this is called.
*/
virtual void aboutToScanVSTShellPlugin (const PluginDescription&);
private:
void recursiveFileSearch (StringArray&, const File&, bool recursive);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginFormat)
};
#endif

View file

@ -0,0 +1,158 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#if defined (JUCE_AUDIO_PROCESSORS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE
/* When you add this cpp file to your project, you mustn't include it in a file where you've
already included any other headers - just put it inside a file on its own, possibly with your config
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
header files that the compiler may be using.
*/
#error "Incorrect use of JUCE cpp file"
#endif
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../juce_core/native/juce_BasicNativeHeaders.h"
#include "juce_audio_processors.h"
#include "../juce_gui_extra/juce_gui_extra.h"
//==============================================================================
#if JUCE_MAC
#if JUCE_SUPPORT_CARBON \
&& ((JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) \
|| ! (defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6))
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers)
#define Component CarbonDummyCompName
#include <Carbon/Carbon.h>
#undef Point
#undef Component
#endif
#endif
#if JUCE_PLUGINHOST_VST && JUCE_LINUX
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#undef KeyPress
#endif
#if ! JUCE_WINDOWS && ! JUCE_MAC
#undef JUCE_PLUGINHOST_VST3
#define JUCE_PLUGINHOST_VST3 0
#endif
//==============================================================================
namespace juce
{
static inline bool arrayContainsPlugin (const OwnedArray<PluginDescription>& list,
const PluginDescription& desc)
{
for (int i = list.size(); --i >= 0;)
if (list.getUnchecked(i)->isDuplicateOf (desc))
return true;
return false;
}
#if JUCE_MAC
//==============================================================================
struct AutoResizingNSViewComponent : public NSViewComponent,
private AsyncUpdater
{
AutoResizingNSViewComponent() : recursive (false) {}
void childBoundsChanged (Component*) override
{
if (recursive)
{
triggerAsyncUpdate();
}
else
{
recursive = true;
resizeToFitView();
recursive = true;
}
}
void handleAsyncUpdate() override { resizeToFitView(); }
bool recursive;
};
//==============================================================================
struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent,
private Timer
{
AutoResizingNSViewComponentWithParent()
{
NSView* v = [[NSView alloc] init];
setView (v);
[v release];
startTimer (30);
}
NSView* getChildView() const
{
if (NSView* parent = (NSView*) getView())
if ([[parent subviews] count] > 0)
return [[parent subviews] objectAtIndex: 0];
return nil;
}
void timerCallback() override
{
if (NSView* child = getChildView())
{
stopTimer();
setView (child);
}
}
};
#endif
#if JUCE_CLANG
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
#include "format/juce_AudioPluginFormat.cpp"
#include "format/juce_AudioPluginFormatManager.cpp"
#include "processors/juce_AudioProcessor.cpp"
#include "processors/juce_AudioProcessorEditor.cpp"
#include "processors/juce_AudioProcessorGraph.cpp"
#include "processors/juce_GenericAudioProcessorEditor.cpp"
#include "processors/juce_PluginDescription.cpp"
#include "format_types/juce_LADSPAPluginFormat.cpp"
#include "format_types/juce_VSTPluginFormat.cpp"
#include "format_types/juce_VST3PluginFormat.cpp"
#include "format_types/juce_AudioUnitPluginFormat.mm"
#include "scanning/juce_KnownPluginList.cpp"
#include "scanning/juce_PluginDirectoryScanner.cpp"
#include "scanning/juce_PluginListComponent.cpp"
}

View file

@ -0,0 +1,98 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIO_PROCESSORS_H_INCLUDED
#define JUCE_AUDIO_PROCESSORS_H_INCLUDED
#include "../juce_gui_basics/juce_gui_basics.h"
#include "../juce_audio_basics/juce_audio_basics.h"
//=============================================================================
/** Config: JUCE_PLUGINHOST_VST
Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be
installed on your machine.
@see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU, JUCE_PLUGINHOST_VST3
*/
#ifndef JUCE_PLUGINHOST_VST
#define JUCE_PLUGINHOST_VST 0
#endif
/** Config: JUCE_PLUGINHOST_VST3
Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be
installed on your machine.
@see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU
*/
#ifndef JUCE_PLUGINHOST_VST3
#define JUCE_PLUGINHOST_VST3 0
#endif
/** Config: JUCE_PLUGINHOST_AU
Enables the AudioUnit plugin hosting classes. This is Mac-only, of course.
@see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_VST3
*/
#ifndef JUCE_PLUGINHOST_AU
#define JUCE_PLUGINHOST_AU 0
#endif
#if ! (JUCE_PLUGINHOST_AU || JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_VST3)
// #error "You need to set either the JUCE_PLUGINHOST_AU and/or JUCE_PLUGINHOST_VST and/or JUCE_PLUGINHOST_VST3 flags if you're using this module!"
#endif
#if ! (defined (JUCE_SUPPORT_CARBON) || JUCE_64BIT)
#define JUCE_SUPPORT_CARBON 1
#endif
//=============================================================================
//=============================================================================
namespace juce
{
class AudioProcessor;
#include "processors/juce_AudioPlayHead.h"
#include "processors/juce_AudioProcessorEditor.h"
#include "processors/juce_AudioProcessorListener.h"
#include "processors/juce_AudioProcessorParameter.h"
#include "processors/juce_AudioProcessor.h"
#include "processors/juce_PluginDescription.h"
#include "processors/juce_AudioPluginInstance.h"
#include "processors/juce_AudioProcessorGraph.h"
#include "processors/juce_GenericAudioProcessorEditor.h"
#include "format/juce_AudioPluginFormat.h"
#include "format/juce_AudioPluginFormatManager.h"
#include "scanning/juce_KnownPluginList.h"
#include "format_types/juce_AudioUnitPluginFormat.h"
#include "format_types/juce_LADSPAPluginFormat.h"
#include "format_types/juce_VSTMidiEventList.h"
#include "format_types/juce_VSTPluginFormat.h"
#include "format_types/juce_VST3PluginFormat.h"
#include "scanning/juce_PluginDirectoryScanner.h"
#include "scanning/juce_PluginListComponent.h"
}
#endif // JUCE_AUDIO_PROCESSORS_H_INCLUDED

View file

@ -0,0 +1,25 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#include "juce_audio_processors.cpp"

View file

@ -0,0 +1,25 @@
{
"id": "juce_audio_processors",
"name": "JUCE audio plugin hosting classes",
"version": "3.0.8",
"description": "Classes for loading and playing VST, AU, or internally-generated audio processors.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
"dependencies": [ { "id": "juce_gui_extra", "version": "matching" },
{ "id": "juce_audio_basics", "version": "matching" } ],
"include": "juce_audio_processors.h",
"compile": [ { "file": "juce_audio_processors.cpp", "target": "! xcode" },
{ "file": "juce_audio_processors.mm", "target": "xcode" } ],
"browse": [ "processors/*",
"format/*",
"format_types/*",
"scanning/*"
],
"OSXFrameworks": "CoreAudio CoreMIDI AudioToolbox",
"iOSFrameworks": "AudioToolbox"
}

View file

@ -0,0 +1,140 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPLAYHEAD_H_INCLUDED
#define JUCE_AUDIOPLAYHEAD_H_INCLUDED
//==============================================================================
/**
A subclass of AudioPlayHead can supply information about the position and
status of a moving play head during audio playback.
One of these can be supplied to an AudioProcessor object so that it can find
out about the position of the audio that it is rendering.
@see AudioProcessor::setPlayHead, AudioProcessor::getPlayHead
*/
class JUCE_API AudioPlayHead
{
protected:
//==============================================================================
AudioPlayHead() {}
public:
virtual ~AudioPlayHead() {}
//==============================================================================
/** Frame rate types. */
enum FrameRateType
{
fps24 = 0,
fps25 = 1,
fps2997 = 2,
fps30 = 3,
fps2997drop = 4,
fps30drop = 5,
fpsUnknown = 99
};
//==============================================================================
/** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method.
*/
struct JUCE_API CurrentPositionInfo
{
/** The tempo in BPM */
double bpm;
/** Time signature numerator, e.g. the 3 of a 3/4 time sig */
int timeSigNumerator;
/** Time signature denominator, e.g. the 4 of a 3/4 time sig */
int timeSigDenominator;
/** The current play position, in samples from the start of the edit. */
int64 timeInSamples;
/** The current play position, in seconds from the start of the edit. */
double timeInSeconds;
/** For timecode, the position of the start of the edit, in seconds from 00:00:00:00. */
double editOriginTime;
/** The current play position, in pulses-per-quarter-note. */
double ppqPosition;
/** The position of the start of the last bar, in pulses-per-quarter-note.
This is the time from the start of the edit to the start of the current
bar, in ppq units.
Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If
it's not available, the value will be 0.
*/
double ppqPositionOfLastBarStart;
/** The video frame rate, if applicable. */
FrameRateType frameRate;
/** True if the transport is currently playing. */
bool isPlaying;
/** True if the transport is currently recording.
(When isRecording is true, then isPlaying will also be true).
*/
bool isRecording;
/** The current cycle start position in pulses-per-quarter-note.
Note that not all hosts or plugin formats may provide this value.
@see isLooping
*/
double ppqLoopStart;
/** The current cycle end position in pulses-per-quarter-note.
Note that not all hosts or plugin formats may provide this value.
@see isLooping
*/
double ppqLoopEnd;
/** True if the transport is currently looping. */
bool isLooping;
//==============================================================================
bool operator== (const CurrentPositionInfo& other) const noexcept;
bool operator!= (const CurrentPositionInfo& other) const noexcept;
void resetToDefault();
};
//==============================================================================
/** Fills-in the given structure with details about the transport's
position at the start of the current processing block.
This method must ONLY be called from within your AudioProcessor::processBlock()
method. Calling it at any other time will probably cause a nasty crash.
*/
virtual bool getCurrentPosition (CurrentPositionInfo& result) = 0;
};
#endif // JUCE_AUDIOPLAYHEAD_H_INCLUDED

View file

@ -0,0 +1,86 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPLUGININSTANCE_H_INCLUDED
#define JUCE_AUDIOPLUGININSTANCE_H_INCLUDED
//==============================================================================
/**
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
*/
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
{
PluginDescription desc;
fillInPluginDescription (desc);
return desc;
}
/** 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() {}
protected:
//==============================================================================
AudioPluginInstance() {}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance)
};
#endif // JUCE_AUDIOPLUGININSTANCE_H_INCLUDED

View file

@ -0,0 +1,474 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
static ThreadLocalValue<AudioProcessor::WrapperType> wrapperTypeBeingCreated;
void JUCE_CALLTYPE AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::WrapperType type)
{
wrapperTypeBeingCreated = type;
}
AudioProcessor::AudioProcessor()
: wrapperType (wrapperTypeBeingCreated.get()),
playHead (nullptr),
sampleRate (0),
blockSize (0),
numInputChannels (0),
numOutputChannels (0),
latencySamples (0),
suspended (false),
nonRealtime (false)
{
}
AudioProcessor::~AudioProcessor()
{
// ooh, nasty - the editor should have been deleted before the filter
// that it refers to is deleted..
jassert (activeEditor == nullptr);
#if JUCE_DEBUG
// This will fail if you've called beginParameterChangeGesture() for one
// or more parameters without having made a corresponding call to endParameterChangeGesture...
jassert (changingParams.countNumberOfSetBits() == 0);
#endif
}
void AudioProcessor::setPlayHead (AudioPlayHead* const newPlayHead)
{
playHead = newPlayHead;
}
void AudioProcessor::addListener (AudioProcessorListener* const newListener)
{
const ScopedLock sl (listenerLock);
listeners.addIfNotAlreadyThere (newListener);
}
void AudioProcessor::removeListener (AudioProcessorListener* const listenerToRemove)
{
const ScopedLock sl (listenerLock);
listeners.removeFirstMatchingValue (listenerToRemove);
}
void AudioProcessor::setPlayConfigDetails (const int newNumIns,
const int newNumOuts,
const double newSampleRate,
const int newBlockSize) noexcept
{
sampleRate = newSampleRate;
blockSize = newBlockSize;
if (numInputChannels != newNumIns || numOutputChannels != newNumOuts)
{
numInputChannels = newNumIns;
numOutputChannels = newNumOuts;
numChannelsChanged();
}
}
void AudioProcessor::numChannelsChanged() {}
void AudioProcessor::setSpeakerArrangement (const String& inputs, const String& outputs)
{
inputSpeakerArrangement = inputs;
outputSpeakerArrangement = outputs;
}
void AudioProcessor::setNonRealtime (const bool newNonRealtime) noexcept
{
nonRealtime = newNonRealtime;
}
void AudioProcessor::setLatencySamples (const int newLatency)
{
if (latencySamples != newLatency)
{
latencySamples = newLatency;
updateHostDisplay();
}
}
void AudioProcessor::setParameterNotifyingHost (const int parameterIndex,
const float newValue)
{
setParameter (parameterIndex, newValue);
sendParamChangeMessageToListeners (parameterIndex, newValue);
}
AudioProcessorListener* AudioProcessor::getListenerLocked (const int index) const noexcept
{
const ScopedLock sl (listenerLock);
return listeners [index];
}
void AudioProcessor::sendParamChangeMessageToListeners (const int parameterIndex, const float newValue)
{
if (isPositiveAndBelow (parameterIndex, getNumParameters()))
{
for (int i = listeners.size(); --i >= 0;)
if (AudioProcessorListener* l = getListenerLocked (i))
l->audioProcessorParameterChanged (this, parameterIndex, newValue);
}
else
{
jassertfalse; // called with an out-of-range parameter index!
}
}
void AudioProcessor::beginParameterChangeGesture (int parameterIndex)
{
if (isPositiveAndBelow (parameterIndex, getNumParameters()))
{
#if JUCE_DEBUG
// This means you've called beginParameterChangeGesture twice in succession without a matching
// call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it.
jassert (! changingParams [parameterIndex]);
changingParams.setBit (parameterIndex);
#endif
for (int i = listeners.size(); --i >= 0;)
if (AudioProcessorListener* l = getListenerLocked (i))
l->audioProcessorParameterChangeGestureBegin (this, parameterIndex);
}
else
{
jassertfalse; // called with an out-of-range parameter index!
}
}
void AudioProcessor::endParameterChangeGesture (int parameterIndex)
{
if (isPositiveAndBelow (parameterIndex, getNumParameters()))
{
#if JUCE_DEBUG
// This means you've called endParameterChangeGesture without having previously called
// endParameterChangeGesture. That might be fine in most hosts, but better to keep the
// calls matched correctly.
jassert (changingParams [parameterIndex]);
changingParams.clearBit (parameterIndex);
#endif
for (int i = listeners.size(); --i >= 0;)
if (AudioProcessorListener* l = getListenerLocked (i))
l->audioProcessorParameterChangeGestureEnd (this, parameterIndex);
}
else
{
jassertfalse; // called with an out-of-range parameter index!
}
}
void AudioProcessor::updateHostDisplay()
{
for (int i = listeners.size(); --i >= 0;)
if (AudioProcessorListener* l = getListenerLocked (i))
l->audioProcessorChanged (this);
}
const OwnedArray<AudioProcessorParameter>& AudioProcessor::getParameters() const noexcept
{
return managedParameters;
}
int AudioProcessor::getNumParameters()
{
return managedParameters.size();
}
float AudioProcessor::getParameter (int index)
{
if (AudioProcessorParameter* p = getParamChecked (index))
return p->getValue();
return 0;
}
void AudioProcessor::setParameter (int index, float newValue)
{
if (AudioProcessorParameter* p = getParamChecked (index))
p->setValue (newValue);
}
float AudioProcessor::getParameterDefaultValue (int index)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getDefaultValue();
return 0;
}
const String AudioProcessor::getParameterName (int index)
{
if (AudioProcessorParameter* p = getParamChecked (index))
return p->getName (512);
return String();
}
String AudioProcessor::getParameterName (int index, int maximumStringLength)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getName (maximumStringLength);
return getParameterName (index).substring (0, maximumStringLength);
}
const String AudioProcessor::getParameterText (int index)
{
return getParameterText (index, 1024);
}
String AudioProcessor::getParameterText (int index, int maximumStringLength)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getText (p->getValue(), maximumStringLength);
return getParameterText (index).substring (0, maximumStringLength);
}
int AudioProcessor::getParameterNumSteps (int index)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getNumSteps();
return AudioProcessor::getDefaultNumParameterSteps();
}
int AudioProcessor::getDefaultNumParameterSteps() noexcept
{
return 0x7fffffff;
}
String AudioProcessor::getParameterLabel (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getLabel();
return String();
}
bool AudioProcessor::isParameterAutomatable (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->isAutomatable();
return true;
}
bool AudioProcessor::isParameterOrientationInverted (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->isOrientationInverted();
return false;
}
bool AudioProcessor::isMetaParameter (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->isMetaParameter();
return false;
}
AudioProcessorParameter* AudioProcessor::getParamChecked (int index) const noexcept
{
AudioProcessorParameter* p = managedParameters[index];
// If you hit this, then you're either trying to access parameters that are out-of-range,
// or you're not using addParameter and the managed parameter list, but have failed
// to override some essential virtual methods and implement them appropriately.
jassert (p != nullptr);
return p;
}
void AudioProcessor::addParameter (AudioProcessorParameter* p)
{
p->processor = this;
p->parameterIndex = managedParameters.size();
managedParameters.add (p);
}
void AudioProcessor::suspendProcessing (const bool shouldBeSuspended)
{
const ScopedLock sl (callbackLock);
suspended = shouldBeSuspended;
}
void AudioProcessor::reset() {}
void AudioProcessor::processBlockBypassed (AudioSampleBuffer&, MidiBuffer&) {}
//==============================================================================
void AudioProcessor::editorBeingDeleted (AudioProcessorEditor* const editor) noexcept
{
const ScopedLock sl (callbackLock);
if (activeEditor == editor)
activeEditor = nullptr;
}
AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
{
if (activeEditor != nullptr)
return activeEditor;
AudioProcessorEditor* const ed = createEditor();
if (ed != nullptr)
{
// you must give your editor comp a size before returning it..
jassert (ed->getWidth() > 0 && ed->getHeight() > 0);
const ScopedLock sl (callbackLock);
activeEditor = ed;
}
// You must make your hasEditor() method return a consistent result!
jassert (hasEditor() == (ed != nullptr));
return ed;
}
//==============================================================================
void AudioProcessor::getCurrentProgramStateInformation (juce::MemoryBlock& destData)
{
getStateInformation (destData);
}
void AudioProcessor::setCurrentProgramStateInformation (const void* data, int sizeInBytes)
{
setStateInformation (data, sizeInBytes);
}
//==============================================================================
// magic number to identify memory blocks that we've stored as XML
const uint32 magicXmlNumber = 0x21324356;
void AudioProcessor::copyXmlToBinary (const XmlElement& xml, juce::MemoryBlock& destData)
{
{
MemoryOutputStream out (destData, false);
out.writeInt (magicXmlNumber);
out.writeInt (0);
xml.writeToStream (out, String(), true, false);
out.writeByte (0);
}
// go back and write the string length..
static_cast<uint32*> (destData.getData())[1]
= ByteOrder::swapIfBigEndian ((uint32) destData.getSize() - 9);
}
XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeInBytes)
{
if (sizeInBytes > 8
&& ByteOrder::littleEndianInt (data) == magicXmlNumber)
{
const int stringLength = (int) ByteOrder::littleEndianInt (addBytesToPointer (data, 4));
if (stringLength > 0)
return XmlDocument::parse (String::fromUTF8 (static_cast<const char*> (data) + 8,
jmin ((sizeInBytes - 8), stringLength)));
}
return nullptr;
}
//==============================================================================
void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {}
void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {}
//==============================================================================
AudioProcessorParameter::AudioProcessorParameter() noexcept
: processor (nullptr), parameterIndex (-1)
{}
AudioProcessorParameter::~AudioProcessorParameter() {}
void AudioProcessorParameter::setValueNotifyingHost (float newValue)
{
// This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0);
return processor->setParameterNotifyingHost (parameterIndex, newValue);
}
void AudioProcessorParameter::beginChangeGesture()
{
// This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0);
processor->beginParameterChangeGesture (parameterIndex);
}
void AudioProcessorParameter::endChangeGesture()
{
// This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0);
processor->endParameterChangeGesture (parameterIndex);
}
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
bool AudioProcessorParameter::isAutomatable() const { return true; }
bool AudioProcessorParameter::isMetaParameter() const { return false; }
int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); }
String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const
{
return String (value, 2);
}
//==============================================================================
bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept
{
return timeInSamples == other.timeInSamples
&& ppqPosition == other.ppqPosition
&& editOriginTime == other.editOriginTime
&& ppqPositionOfLastBarStart == other.ppqPositionOfLastBarStart
&& frameRate == other.frameRate
&& isPlaying == other.isPlaying
&& isRecording == other.isRecording
&& bpm == other.bpm
&& timeSigNumerator == other.timeSigNumerator
&& timeSigDenominator == other.timeSigDenominator
&& ppqLoopStart == other.ppqLoopStart
&& ppqLoopEnd == other.ppqLoopEnd
&& isLooping == other.isLooping;
}
bool AudioPlayHead::CurrentPositionInfo::operator!= (const CurrentPositionInfo& other) const noexcept
{
return ! operator== (other);
}
void AudioPlayHead::CurrentPositionInfo::resetToDefault()
{
zerostruct (*this);
timeSigNumerator = 4;
timeSigDenominator = 4;
bpm = 120;
}

View file

@ -0,0 +1,689 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPROCESSOR_H_INCLUDED
#define JUCE_AUDIOPROCESSOR_H_INCLUDED
//==============================================================================
/**
Base class for audio processing filters or plugins.
This is intended to act as a base class of audio filter that is general enough to
be wrapped as a VST, AU, RTAS, etc, or used internally.
It is also used by the plugin hosting code as the wrapper around an instance
of a loaded plugin.
Derive your filter class from this base class, and if you're building a plugin,
you should implement a global function called createPluginFilter() which creates
and returns a new instance of your subclass.
*/
class JUCE_API AudioProcessor
{
protected:
//==============================================================================
/** Constructor. */
AudioProcessor();
public:
/** Destructor. */
virtual ~AudioProcessor();
//==============================================================================
/** Returns the name of this processor. */
virtual const String getName() const = 0;
//==============================================================================
/** Called before playback starts, to let the filter prepare itself.
The sample rate is the target sample rate, and will remain constant until
playback stops.
The estimatedSamplesPerBlock value is a HINT about the typical number of
samples that will be processed for each callback, but isn't any kind
of guarantee. The actual block sizes that the host uses may be different
each time the callback happens, and may be more or less than this value.
*/
virtual void prepareToPlay (double sampleRate,
int estimatedSamplesPerBlock) = 0;
/** Called after playback has stopped, to let the filter free up any resources it
no longer needs.
*/
virtual void releaseResources() = 0;
/** Renders the next block.
When this method is called, the buffer contains a number of channels which is
at least as great as the maximum number of input and output channels that
this filter is using. It will be filled with the filter's input data and
should be replaced with the filter's output.
So for example if your filter has 2 input channels and 4 output channels, then
the buffer will contain 4 channels, the first two being filled with the
input data. Your filter should read these, do its processing, and replace
the contents of all 4 channels with its output.
Or if your filter has 5 inputs and 2 outputs, the buffer will have 5 channels,
all filled with data, and your filter should overwrite the first 2 of these
with its output. But be VERY careful not to write anything to the last 3
channels, as these might be mapped to memory that the host assumes is read-only!
Note that if you have more outputs than inputs, then only those channels that
correspond to an input channel are guaranteed to contain sensible data - e.g.
in the case of 2 inputs and 4 outputs, the first two channels contain the input,
but the last two channels may contain garbage, so you should be careful not to
let this pass through without being overwritten or cleared.
Also note that the buffer may have more channels than are strictly necessary,
but you should only read/write from the ones that your filter is supposed to
be using.
The number of samples in these buffers is NOT guaranteed to be the same for every
callback, and may be more or less than the estimated value given to prepareToPlay().
Your code must be able to cope with variable-sized blocks, or you're going to get
clicks and crashes!
If the filter is receiving a midi input, then the midiMessages array will be filled
with the midi messages for this block. Each message's timestamp will indicate the
message's time, as a number of samples from the start of the block.
Any messages left in the midi buffer when this method has finished are assumed to
be the filter's midi output. This means that your filter should be careful to
clear any incoming messages from the array if it doesn't want them to be passed-on.
Be very careful about what you do in this callback - it's going to be called by
the audio thread, so any kind of interaction with the UI is absolutely
out of the question. If you change a parameter in here and need to tell your UI to
update itself, the best way is probably to inherit from a ChangeBroadcaster, let
the UI components register as listeners, and then call sendChangeMessage() inside the
processBlock() method to send out an asynchronous message. You could also use
the AsyncUpdater class in a similar way.
*/
virtual void processBlock (AudioSampleBuffer& buffer,
MidiBuffer& midiMessages) = 0;
/** Renders the next block when the processor is being bypassed.
The default implementation of this method will pass-through any incoming audio, but
you may override this method e.g. to add latency compensation to the data to match
the processor's latency characteristics. This will avoid situations where bypassing
will shift the signal forward in time, possibly creating pre-echo effects and odd timings.
Another use for this method would be to cross-fade or morph between the wet (not bypassed)
and dry (bypassed) signals.
*/
virtual void processBlockBypassed (AudioSampleBuffer& buffer,
MidiBuffer& midiMessages);
//==============================================================================
/** Returns the current AudioPlayHead object that should be used to find
out the state and position of the playhead.
You can call this from your processBlock() method, and use the AudioPlayHead
object to get the details about the time of the start of the block currently
being processed.
If the host hasn't supplied a playhead object, this will return nullptr.
*/
AudioPlayHead* getPlayHead() const noexcept { return playHead; }
//==============================================================================
/** Returns the current sample rate.
This can be called from your processBlock() method - it's not guaranteed
to be valid at any other time, and may return 0 if it's unknown.
*/
double getSampleRate() const noexcept { return sampleRate; }
/** Returns the current typical block size that is being used.
This can be called from your processBlock() method - it's not guaranteed
to be valid at any other time.
Remember it's not the ONLY block size that may be used when calling
processBlock, it's just the normal one. The actual block sizes used may be
larger or smaller than this, and will vary between successive calls.
*/
int getBlockSize() const noexcept { return blockSize; }
//==============================================================================
/** Returns the number of input channels that the host will be sending the filter.
If writing a plugin, your configuration macros should specify the number of
channels that your filter would prefer to have, and this method lets
you know how many the host is actually using.
Note that this method is only valid during or after the prepareToPlay()
method call. Until that point, the number of channels will be unknown.
*/
int getNumInputChannels() const noexcept { return numInputChannels; }
/** Returns the number of output channels that the host will be sending the filter.
If writing a plugin, your configuration macros should specify the number of
channels that your filter would prefer to have, and this method lets
you know how many the host is actually using.
Note that this method is only valid during or after the prepareToPlay()
method call. Until that point, the number of channels will be unknown.
*/
int getNumOutputChannels() const noexcept { return numOutputChannels; }
/** Returns a string containing a whitespace-separated list of speaker types
corresponding to each input channel.
For example in a 5.1 arrangement, the string may be "L R C Lfe Ls Rs"
If the speaker arrangement is unknown, the returned string will be empty.
*/
const String& getInputSpeakerArrangement() const noexcept { return inputSpeakerArrangement; }
/** Returns a string containing a whitespace-separated list of speaker types
corresponding to each output channel.
For example in a 5.1 arrangement, the string may be "L R C Lfe Ls Rs"
If the speaker arrangement is unknown, the returned string will be empty.
*/
const String& getOutputSpeakerArrangement() const noexcept { return outputSpeakerArrangement; }
//==============================================================================
/** Returns the name of one of the processor's input channels.
The processor might not supply very useful names for channels, and this might be
something like "1", "2", "left", "right", etc.
*/
virtual const String getInputChannelName (int channelIndex) const = 0;
/** Returns the name of one of the processor's output channels.
The processor might not supply very useful names for channels, and this might be
something like "1", "2", "left", "right", etc.
*/
virtual const String getOutputChannelName (int channelIndex) const = 0;
/** Returns true if the specified channel is part of a stereo pair with its neighbour. */
virtual bool isInputChannelStereoPair (int index) const = 0;
/** Returns true if the specified channel is part of a stereo pair with its neighbour. */
virtual bool isOutputChannelStereoPair (int index) const = 0;
/** This returns the number of samples delay that the filter imposes on the audio
passing through it.
The host will call this to find the latency - the filter itself should set this value
by calling setLatencySamples() as soon as it can during its initialisation.
*/
int getLatencySamples() const noexcept { return latencySamples; }
/** The filter should call this to set the number of samples delay that it introduces.
The filter should call this as soon as it can during initialisation, and can call it
later if the value changes.
*/
void setLatencySamples (int newLatency);
/** Returns true if a silent input always produces a silent output. */
virtual bool silenceInProducesSilenceOut() const = 0;
/** Returns the length of the filter's tail, in seconds. */
virtual double getTailLengthSeconds() const = 0;
/** Returns true if the processor wants midi messages. */
virtual bool acceptsMidi() const = 0;
/** Returns true if the processor produces midi messages. */
virtual bool producesMidi() const = 0;
//==============================================================================
/** This returns a critical section that will automatically be locked while the host
is calling the processBlock() method.
Use it from your UI or other threads to lock access to variables that are used
by the process callback, but obviously be careful not to keep it locked for
too long, because that could cause stuttering playback. If you need to do something
that'll take a long time and need the processing to stop while it happens, use the
suspendProcessing() method instead.
@see suspendProcessing
*/
const CriticalSection& getCallbackLock() const noexcept { return callbackLock; }
/** Enables and disables the processing callback.
If you need to do something time-consuming on a thread and would like to make sure
the audio processing callback doesn't happen until you've finished, use this
to disable the callback and re-enable it again afterwards.
E.g.
@code
void loadNewPatch()
{
suspendProcessing (true);
..do something that takes ages..
suspendProcessing (false);
}
@endcode
If the host tries to make an audio callback while processing is suspended, the
filter will return an empty buffer, but won't block the audio thread like it would
do if you use the getCallbackLock() critical section to synchronise access.
Any code that calls processBlock() should call isSuspended() before doing so, and
if the processor is suspended, it should avoid the call and emit silence or
whatever is appropriate.
@see getCallbackLock
*/
void suspendProcessing (bool shouldBeSuspended);
/** Returns true if processing is currently suspended.
@see suspendProcessing
*/
bool isSuspended() const noexcept { return suspended; }
/** A plugin can override this to be told when it should reset any playing voices.
The default implementation does nothing, but a host may call this to tell the
plugin that it should stop any tails or sounds that have been left running.
*/
virtual void reset();
//==============================================================================
/** Returns true if the processor is being run in an offline mode for rendering.
If the processor is being run live on realtime signals, this returns false.
If the mode is unknown, this will assume it's realtime and return false.
This value may be unreliable until the prepareToPlay() method has been called,
and could change each time prepareToPlay() is called.
@see setNonRealtime()
*/
bool isNonRealtime() const noexcept { return nonRealtime; }
/** Called by the host to tell this processor whether it's being used in a non-realtime
capacity for offline rendering or bouncing.
*/
virtual void setNonRealtime (bool isNonRealtime) noexcept;
//==============================================================================
/** Creates the filter's UI.
This can return nullptr if you want a UI-less filter, in which case the host may create
a generic UI that lets the user twiddle the parameters directly.
If you do want to pass back a component, the component should be created and set to
the correct size before returning it. If you implement this method, you must
also implement the hasEditor() method and make it return true.
Remember not to do anything silly like allowing your filter to keep a pointer to
the component that gets created - it could be deleted later without any warning, which
would make your pointer into a dangler. Use the getActiveEditor() method instead.
The correct way to handle the connection between an editor component and its
filter is to use something like a ChangeBroadcaster so that the editor can
register itself as a listener, and be told when a change occurs. This lets them
safely unregister themselves when they are deleted.
Here are a few things to bear in mind when writing an editor:
- Initially there won't be an editor, until the user opens one, or they might
not open one at all. Your filter mustn't rely on it being there.
- An editor object may be deleted and a replacement one created again at any time.
- It's safe to assume that an editor will be deleted before its filter.
@see hasEditor
*/
virtual AudioProcessorEditor* createEditor() = 0;
/** Your filter must override this and return true if it can create an editor component.
@see createEditor
*/
virtual bool hasEditor() const = 0;
//==============================================================================
/** Returns the active editor, if there is one.
Bear in mind this can return nullptr, even if an editor has previously been opened.
*/
AudioProcessorEditor* getActiveEditor() const noexcept { return activeEditor; }
/** Returns the active editor, or if there isn't one, it will create one.
This may call createEditor() internally to create the component.
*/
AudioProcessorEditor* createEditorIfNeeded();
//==============================================================================
/** This must return the correct value immediately after the object has been
created, and mustn't change the number of parameters later.
*/
virtual int getNumParameters();
/** Returns the name of a particular parameter. */
virtual const String getParameterName (int parameterIndex);
/** Called by the host to find out the value of one of the filter's parameters.
The host 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 getParameter (int parameterIndex);
/** Returns the value of a parameter as a text string. */
virtual const String getParameterText (int parameterIndex);
/** Returns the name of a parameter as a text string with a preferred maximum length.
If you want to provide customised short versions of your parameter names that
will look better in constrained spaces (e.g. the displays on hardware controller
devices or mixing desks) then you should implement this method.
If you don't override it, the default implementation will call getParameterText(int),
and truncate the result.
*/
virtual String getParameterName (int parameterIndex, int maximumStringLength);
/** Returns the value of a parameter as a text string with a preferred maximum length.
If you want to provide customised short versions of your parameter values that
will look better in constrained spaces (e.g. the displays on hardware controller
devices or mixing desks) then you should implement this method.
If you don't override it, the default implementation will call getParameterText(int),
and truncate the result.
*/
virtual String getParameterText (int parameterIndex, int maximumStringLength);
/** Returns the number of discrete steps that this parameter can represent.
The default return value if you don't implement this method is
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.
*/
virtual int getParameterNumSteps (int parameterIndex);
/** Returns the default number of steps for a parameter.
@see getParameterNumSteps
*/
static int getDefaultNumParameterSteps() noexcept;
/** Returns the default value for the parameter.
By default, this just returns 0.
The value that is returned may or may not be used, depending on the host.
*/
virtual float getParameterDefaultValue (int parameterIndex);
/** Some plugin types may be able to return a label string for a
parameter's units.
*/
virtual String getParameterLabel (int index) const;
/** This can be overridden to tell the host that particular parameters operate in the
reverse direction. (Not all plugin formats or hosts will actually use this information).
*/
virtual bool isParameterOrientationInverted (int index) const;
/** The host will call this method to change the value of one of the filter's parameters.
The host may call this at any time, including during the audio processing
callback, so the filter has to process this very fast and avoid blocking.
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
setParameterNotifyingHost() 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 setParameter (int parameterIndex, float newValue);
/** Your filter can 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 setParameter() 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 beginParameterChangeGesture() and endParameterChangeGesture() methods to
tell the host when the user has started and stopped changing the parameter.
*/
void setParameterNotifyingHost (int parameterIndex, float newValue);
/** Returns true if the host can automate this parameter.
By default, this returns true for all parameters.
*/
virtual bool isParameterAutomatable (int parameterIndex) 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 (int parameterIndex) const;
/** 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 endParameterChangeGesture().
*/
void beginParameterChangeGesture (int parameterIndex);
/** 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 beginParameterChangeGesture().
*/
void endParameterChangeGesture (int parameterIndex);
/** The filter can call this when something (apart from a parameter value) has changed.
It sends a hint to the host that something like the program, number of parameters,
etc, has changed, and that it should update itself.
*/
void updateHostDisplay();
//==============================================================================
/** Adds a parameter to the list.
The parameter object will be managed and deleted automatically by the list
when no longer needed.
*/
void addParameter (AudioProcessorParameter*);
/** Returns the current list of parameters. */
const OwnedArray<AudioProcessorParameter>& getParameters() const noexcept;
//==============================================================================
/** Returns the number of preset programs the filter supports.
The value returned must be valid as soon as this object is created, and
must not change over its lifetime.
This value shouldn't be less than 1.
*/
virtual int getNumPrograms() = 0;
/** Returns the number of the currently active program. */
virtual int getCurrentProgram() = 0;
/** Called by the host to change the current program. */
virtual void setCurrentProgram (int index) = 0;
/** Must return the name of a given program. */
virtual const String getProgramName (int index) = 0;
/** Called by the host to rename a program. */
virtual void changeProgramName (int index, const String& newName) = 0;
//==============================================================================
/** The host will call this method when it wants to save the filter's internal state.
This must copy any info about the filter's state into the block of memory provided,
so that the host can store this and later restore it using setStateInformation().
Note that there's also a getCurrentProgramStateInformation() method, which only
stores the current program, not the state of the entire filter.
See also the helper function copyXmlToBinary() for storing settings as XML.
@see getCurrentProgramStateInformation
*/
virtual void getStateInformation (juce::MemoryBlock& destData) = 0;
/** The host will call this method if it wants to save the state of just the filter's
current program.
Unlike getStateInformation, this should only return the current program's state.
Not all hosts support this, and if you don't implement it, the base class
method just calls getStateInformation() instead. If you do implement it, be
sure to also implement getCurrentProgramStateInformation.
@see getStateInformation, setCurrentProgramStateInformation
*/
virtual void getCurrentProgramStateInformation (juce::MemoryBlock& destData);
/** This must restore the filter's state from a block of data previously created
using getStateInformation().
Note that there's also a setCurrentProgramStateInformation() method, which tries
to restore just the current program, not the state of the entire filter.
See also the helper function getXmlFromBinary() for loading settings as XML.
@see setCurrentProgramStateInformation
*/
virtual void setStateInformation (const void* data, int sizeInBytes) = 0;
/** The host will call this method if it wants to restore the state of just the filter's
current program.
Not all hosts support this, and if you don't implement it, the base class
method just calls setStateInformation() instead. If you do implement it, be
sure to also implement getCurrentProgramStateInformation.
@see setStateInformation, getCurrentProgramStateInformation
*/
virtual void setCurrentProgramStateInformation (const void* data, int sizeInBytes);
/** This method is called when the number of input or output channels is changed. */
virtual void numChannelsChanged();
//==============================================================================
/** Adds a listener that will be called when an aspect of this processor changes. */
virtual void addListener (AudioProcessorListener* newListener);
/** Removes a previously added listener. */
virtual void removeListener (AudioProcessorListener* listenerToRemove);
//==============================================================================
/** Tells the processor to use this playhead object.
The processor will not take ownership of the object, so the caller must delete it when
it is no longer being used.
*/
virtual void setPlayHead (AudioPlayHead* newPlayHead);
//==============================================================================
/** This is called by the processor to specify its details before being played. */
void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize) noexcept;
//==============================================================================
/** Not for public use - this is called before deleting an editor component. */
void editorBeingDeleted (AudioProcessorEditor*) noexcept;
/** Not for public use - this is called to initialise the processor before playing. */
void setSpeakerArrangement (const String& inputs, const String& outputs);
/** Flags to indicate the type of plugin context in which a processor is being used. */
enum WrapperType
{
wrapperType_Undefined = 0,
wrapperType_VST,
wrapperType_VST3,
wrapperType_AudioUnit,
wrapperType_RTAS,
wrapperType_AAX,
wrapperType_Standalone
};
/** When loaded by a plugin wrapper, this flag will be set to indicate the type
of plugin within which the processor is running.
*/
WrapperType wrapperType;
//==============================================================================
/** Helper function that just converts an xml element into a binary blob.
Use this in your filter's getStateInformation() method if you want to
store its state as xml.
Then use getXmlFromBinary() to reverse this operation and retrieve the XML
from a binary blob.
*/
static void copyXmlToBinary (const XmlElement& xml,
juce::MemoryBlock& destData);
/** Retrieves an XML element that was stored as binary with the copyXmlToBinary() method.
This might return nullptr if the data's unsuitable or corrupted. Otherwise it will return
an XmlElement object that the caller must delete when no longer needed.
*/
static XmlElement* getXmlFromBinary (const void* data, int sizeInBytes);
/** @internal */
static void JUCE_CALLTYPE setTypeOfNextNewPlugin (WrapperType);
protected:
/** @internal */
AudioPlayHead* playHead;
/** @internal */
void sendParamChangeMessageToListeners (int parameterIndex, float newValue);
private:
Array<AudioProcessorListener*> listeners;
Component::SafePointer<AudioProcessorEditor> activeEditor;
double sampleRate;
int blockSize, numInputChannels, numOutputChannels, latencySamples;
bool suspended, nonRealtime;
CriticalSection callbackLock, listenerLock;
String inputSpeakerArrangement, outputSpeakerArrangement;
OwnedArray<AudioProcessorParameter> managedParameters;
AudioProcessorParameter* getParamChecked (int) const noexcept;
#if JUCE_DEBUG
BigInteger changingParams;
#endif
AudioProcessorListener* getListenerLocked (int) const noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessor)
};
#endif // JUCE_AUDIOPROCESSOR_H_INCLUDED

View file

@ -0,0 +1,43 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p)
{
}
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p)
{
// the filter must be valid..
jassert (p != nullptr);
}
AudioProcessorEditor::~AudioProcessorEditor()
{
// if this fails, then the wrapper hasn't called editorBeingDeleted() on the
// filter for some reason..
jassert (processor.getActiveEditor() != this);
}
void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {}
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }

View file

@ -0,0 +1,92 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED
#define JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED
//==============================================================================
/**
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
*/
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&);
private:
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor)
};
#endif // JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED

View file

@ -0,0 +1,413 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED
#define JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED
//==============================================================================
/**
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.
*/
class JUCE_API AudioProcessorGraph : public AudioProcessor,
private AsyncUpdater
{
public:
//==============================================================================
/** Creates an empty graph. */
AudioProcessorGraph();
/** Destructor.
Any processor objects that have been added to the graph will also be deleted.
*/
~AudioProcessorGraph();
//==============================================================================
/** 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 uint32 nodeId;
/** The actual processor object that this node represents. */
AudioProcessor* getProcessor() const noexcept { return processor; }
/** 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;
//==============================================================================
/** A convenient typedef for referring to a pointer to a node object. */
typedef ReferenceCountedObjectPtr <Node> Ptr;
private:
//==============================================================================
friend class AudioProcessorGraph;
const ScopedPointer<AudioProcessor> processor;
bool isPrepared;
Node (uint32 nodeId, AudioProcessor*) noexcept;
void setParentGraph (AudioProcessorGraph*) const;
void prepare (double sampleRate, int blockSize, AudioProcessorGraph*);
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
{
public:
//==============================================================================
Connection (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex) noexcept;
//==============================================================================
/** The ID number of the node which is the input source for this connection.
@see AudioProcessorGraph::getNodeForId
*/
uint32 sourceNodeId;
/** The index of the output channel of the source node from which this
connection takes its data.
If this value is the special number AudioProcessorGraph::midiChannelIndex, then
it is referring to the source node's midi output. Otherwise, it is the zero-based
index of an audio output channel in the source node.
*/
int sourceChannelIndex;
/** The ID number of the node which is the destination for this connection.
@see AudioProcessorGraph::getNodeForId
*/
uint32 destNodeId;
/** The index of the input channel of the destination node to which this
connection delivers its data.
If this value is the special number AudioProcessorGraph::midiChannelIndex, then
it is referring to the destination node's midi input. Otherwise, it is the zero-based
index of an audio input channel in the destination node.
*/
int destChannelIndex;
private:
//==============================================================================
JUCE_LEAK_DETECTOR (Connection)
};
//==============================================================================
/** Deletes all nodes and connections from this graph.
Any processor objects in the graph will be deleted.
*/
void clear();
/** Returns the number of nodes in the graph. */
int getNumNodes() const { 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 (const int index) const { 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 (const uint32 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* addNode (AudioProcessor* newProcessor, uint32 nodeId = 0);
/** 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 (uint32 nodeId);
//==============================================================================
/** Returns the number of connections in the graph. */
int getNumConnections() const { return connections.size(); }
/** Returns a pointer to one of the connections in the graph. */
const Connection* getConnection (int index) const { return connections [index]; }
/** Searches for a connection between some specified channels.
If no such connection is found, this returns nullptr.
*/
const Connection* getConnectionBetween (uint32 sourceNodeId,
int sourceChannelIndex,
uint32 destNodeId,
int destChannelIndex) const;
/** Returns true if there is a connection between any of the channels of
two specified nodes.
*/
bool isConnected (uint32 possibleSourceNodeId,
uint32 possibleDestNodeId) const;
/** Returns true if it would be legal to connect the specified points. */
bool canConnect (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex) 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 (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex);
/** Deletes the connection with the specified index. */
void removeConnection (int index);
/** Deletes any connection between two specified points.
Returns true if a connection was actually deleted.
*/
bool removeConnection (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex);
/** Removes all connections from the specified node. */
bool disconnectNode (uint32 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* 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 number 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.
*/
static const int midiChannelIndex;
//==============================================================================
/** 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 { 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 { return graph; }
/** True if this is an audio or midi input. */
bool isInput() const;
/** True if this is an audio or midi output. */
bool isOutput() const;
//==============================================================================
AudioGraphIOProcessor (const IODeviceType type);
~AudioGraphIOProcessor();
const String getName() const;
void fillInPluginDescription (PluginDescription&) const;
void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock);
void releaseResources();
void processBlock (AudioSampleBuffer&, MidiBuffer&);
const String getInputChannelName (int channelIndex) const;
const String getOutputChannelName (int channelIndex) const;
bool isInputChannelStereoPair (int index) const;
bool isOutputChannelStereoPair (int index) const;
bool silenceInProducesSilenceOut() const;
double getTailLengthSeconds() const;
bool acceptsMidi() const;
bool producesMidi() const;
bool hasEditor() const;
AudioProcessorEditor* createEditor();
int getNumParameters();
const String getParameterName (int);
float getParameter (int);
const String getParameterText (int);
void setParameter (int, float);
int getNumPrograms();
int getCurrentProgram();
void setCurrentProgram (int);
const String getProgramName (int);
void changeProgramName (int, const String&);
void getStateInformation (juce::MemoryBlock& destData);
void setStateInformation (const void* data, int sizeInBytes);
/** @internal */
void setParentGraph (AudioProcessorGraph*);
private:
const IODeviceType type;
AudioProcessorGraph* graph;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioGraphIOProcessor)
};
//==============================================================================
// AudioProcessor methods:
const String getName() const;
void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock);
void releaseResources();
void processBlock (AudioSampleBuffer&, MidiBuffer&);
void reset();
const String getInputChannelName (int channelIndex) const;
const String getOutputChannelName (int channelIndex) const;
bool isInputChannelStereoPair (int index) const;
bool isOutputChannelStereoPair (int index) const;
bool silenceInProducesSilenceOut() const;
double getTailLengthSeconds() const;
bool acceptsMidi() const;
bool producesMidi() const;
bool hasEditor() const { return false; }
AudioProcessorEditor* createEditor() { return nullptr; }
int getNumParameters() { return 0; }
const String getParameterName (int) { return String(); }
float getParameter (int) { return 0; }
const String getParameterText (int) { return String(); }
void setParameter (int, float) { }
int getNumPrograms() { return 0; }
int getCurrentProgram() { return 0; }
void setCurrentProgram (int) { }
const String getProgramName (int) { return String(); }
void changeProgramName (int, const String&) { }
void getStateInformation (juce::MemoryBlock&);
void setStateInformation (const void* data, int sizeInBytes);
private:
//==============================================================================
ReferenceCountedArray<Node> nodes;
OwnedArray<Connection> connections;
uint32 lastNodeId;
AudioSampleBuffer renderingBuffers;
OwnedArray<MidiBuffer> midiBuffers;
Array<void*> renderingOps;
friend class AudioGraphIOProcessor;
AudioSampleBuffer* currentAudioInputBuffer;
AudioSampleBuffer currentAudioOutputBuffer;
MidiBuffer* currentMidiInputBuffer;
MidiBuffer currentMidiOutputBuffer;
void handleAsyncUpdate() override;
void clearRenderingSequence();
void buildRenderingSequence();
bool isAnInputTo (uint32 possibleInputId, uint32 possibleDestinationId, int recursionCheck) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph)
};
#endif // JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED

View file

@ -0,0 +1,106 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPROCESSORLISTENER_H_INCLUDED
#define JUCE_AUDIOPROCESSORLISTENER_H_INCLUDED
//==============================================================================
/**
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
*/
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);
};
#endif // JUCE_AUDIOPROCESSORLISTENER_H_INCLUDED

View file

@ -0,0 +1,158 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPROCESSORPARAMETER_H_INCLUDED
#define JUCE_AUDIOPROCESSORPARAMETER_H_INCLUDED
//==============================================================================
/** An abstract base class for parameter objects that can be added to an
AudioProcessor.
@see AudioProcessor::addParameter
*/
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 one of the filter's parameters.
The host may call this at any time, including during the audio processing
callback, so the filter has to process this very fast and avoid blocking.
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;
/** Your filter can 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 setParameter() 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 discrete interval 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.
*/
virtual int getNumSteps() 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;
/** Returns the index of this parameter in its parent processor's parameter list. */
int getParameterIndex() const noexcept { return parameterIndex; }
private:
friend class AudioProcessor;
AudioProcessor* processor;
int parameterIndex;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameter)
};
#endif // JUCE_AUDIOPROCESSORPARAMETER_H_INCLUDED

View file

@ -0,0 +1,172 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
class ProcessorParameterPropertyComp : public PropertyComponent,
private AudioProcessorListener,
private Timer
{
public:
ProcessorParameterPropertyComp (const String& name, AudioProcessor& p, int paramIndex)
: PropertyComponent (name),
owner (p),
index (paramIndex),
paramHasChanged (false),
slider (p, paramIndex)
{
startTimer (100);
addAndMakeVisible (slider);
owner.addListener (this);
}
~ProcessorParameterPropertyComp()
{
owner.removeListener (this);
}
void refresh() override
{
paramHasChanged = false;
if (slider.getThumbBeingDragged() < 0)
slider.setValue (owner.getParameter (index), dontSendNotification);
slider.updateText();
}
void audioProcessorChanged (AudioProcessor*) override {}
void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) override
{
if (parameterIndex == index)
paramHasChanged = true;
}
void timerCallback() override
{
if (paramHasChanged)
{
refresh();
startTimer (1000 / 50);
}
else
{
startTimer (jmin (1000 / 4, getTimerInterval() + 10));
}
}
private:
//==============================================================================
class ParamSlider : public Slider
{
public:
ParamSlider (AudioProcessor& p, int paramIndex) : owner (p), index (paramIndex)
{
const int steps = owner.getParameterNumSteps (index);
if (steps > 1 && steps < 0x7fffffff)
setRange (0.0, 1.0, 1.0 / (steps - 1.0));
else
setRange (0.0, 1.0);
setSliderStyle (Slider::LinearBar);
setTextBoxIsEditable (false);
setScrollWheelEnabled (true);
}
void valueChanged() override
{
const float newVal = (float) getValue();
if (owner.getParameter (index) != newVal)
{
owner.setParameterNotifyingHost (index, newVal);
updateText();
}
}
String getTextFromValue (double /*value*/) override
{
return owner.getParameterText (index) + " " + owner.getParameterLabel (index).trimEnd();
}
private:
//==============================================================================
AudioProcessor& owner;
const int index;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamSlider)
};
AudioProcessor& owner;
const int index;
bool volatile paramHasChanged;
ParamSlider slider;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProcessorParameterPropertyComp)
};
//==============================================================================
GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p)
: AudioProcessorEditor (p)
{
jassert (p != nullptr);
setOpaque (true);
addAndMakeVisible (panel);
Array <PropertyComponent*> params;
const int numParams = p->getNumParameters();
int totalHeight = 0;
for (int i = 0; i < numParams; ++i)
{
String name (p->getParameterName (i));
if (name.trim().isEmpty())
name = "Unnamed";
ProcessorParameterPropertyComp* const pc = new ProcessorParameterPropertyComp (name, *p, i);
params.add (pc);
totalHeight += pc->getPreferredHeight();
}
panel.addProperties (params);
setSize (400, jlimit (25, 400, totalHeight));
}
GenericAudioProcessorEditor::~GenericAudioProcessorEditor()
{
}
void GenericAudioProcessorEditor::paint (Graphics& g)
{
g.fillAll (Colours::white);
}
void GenericAudioProcessorEditor::resized()
{
panel.setBounds (getLocalBounds());
}

View file

@ -0,0 +1,58 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_GENERICAUDIOPROCESSOREDITOR_H_INCLUDED
#define JUCE_GENERICAUDIOPROCESSOREDITOR_H_INCLUDED
//==============================================================================
/**
A type of UI component that displays the parameters of an AudioProcessor as
a simple list of sliders.
This can be used for showing an editor for a processor that doesn't supply
its own custom editor.
@see AudioProcessor
*/
class JUCE_API GenericAudioProcessorEditor : public AudioProcessorEditor
{
public:
//==============================================================================
GenericAudioProcessorEditor (AudioProcessor* owner);
~GenericAudioProcessorEditor();
//==============================================================================
void paint (Graphics&) override;
void resized() override;
private:
//==============================================================================
PropertyPanel panel;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericAudioProcessorEditor)
};
#endif // JUCE_GENERICAUDIOPROCESSOREDITOR_H_INCLUDED

View file

@ -0,0 +1,140 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
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),
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;
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 ("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());
numInputChannels = xml.getIntAttribute ("numInputs");
numOutputChannels = xml.getIntAttribute ("numOutputs");
hasSharedContainer = xml.getBoolAttribute ("isShell", false);
return true;
}
return false;
}

View file

@ -0,0 +1,151 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_PLUGINDESCRIPTION_H_INCLUDED
#define JUCE_PLUGINDESCRIPTION_H_INCLUDED
//==============================================================================
/**
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
*/
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;
/** 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 identifers 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)
};
#endif // JUCE_PLUGINDESCRIPTION_H_INCLUDED

View file

@ -0,0 +1,549 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
KnownPluginList::KnownPluginList() {}
KnownPluginList::~KnownPluginList() {}
void KnownPluginList::clear()
{
if (types.size() > 0)
{
types.clear();
sendChangeMessage();
}
}
PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifier) const
{
for (int i = 0; i < types.size(); ++i)
if (types.getUnchecked(i)->fileOrIdentifier == fileOrIdentifier)
return types.getUnchecked(i);
return nullptr;
}
PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const
{
for (int i = 0; i < types.size(); ++i)
if (types.getUnchecked(i)->matchesIdentifierString (identifierString))
return types.getUnchecked(i);
return nullptr;
}
bool KnownPluginList::addType (const PluginDescription& type)
{
for (int i = types.size(); --i >= 0;)
{
if (types.getUnchecked(i)->isDuplicateOf (type))
{
// strange - found a duplicate plugin with different info..
jassert (types.getUnchecked(i)->name == type.name);
jassert (types.getUnchecked(i)->isInstrument == type.isInstrument);
*types.getUnchecked(i) = type;
return false;
}
}
types.insert (0, new PluginDescription (type));
sendChangeMessage();
return true;
}
void KnownPluginList::removeType (const int index)
{
types.remove (index);
sendChangeMessage();
}
bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier,
AudioPluginFormat& formatToUse) const
{
if (getTypeForFile (fileOrIdentifier) == nullptr)
return false;
for (int i = types.size(); --i >= 0;)
{
const PluginDescription* const d = types.getUnchecked(i);
if (d->fileOrIdentifier == fileOrIdentifier
&& formatToUse.pluginNeedsRescanning (*d))
return false;
}
return true;
}
void KnownPluginList::setCustomScanner (CustomScanner* newScanner)
{
scanner = newScanner;
}
bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier,
const bool dontRescanIfAlreadyInList,
OwnedArray <PluginDescription>& typesFound,
AudioPluginFormat& format)
{
const ScopedLock sl (scanLock);
if (dontRescanIfAlreadyInList
&& getTypeForFile (fileOrIdentifier) != nullptr)
{
bool needsRescanning = false;
for (int i = types.size(); --i >= 0;)
{
const PluginDescription* const d = types.getUnchecked(i);
if (d->fileOrIdentifier == fileOrIdentifier && d->pluginFormatName == format.getName())
{
if (format.pluginNeedsRescanning (*d))
needsRescanning = true;
else
typesFound.add (new PluginDescription (*d));
}
}
if (! needsRescanning)
return false;
}
if (blacklist.contains (fileOrIdentifier))
return false;
OwnedArray <PluginDescription> found;
{
const ScopedUnlock sl2 (scanLock);
if (scanner != nullptr)
{
if (! scanner->findPluginTypesFor (format, found, fileOrIdentifier))
addToBlacklist (fileOrIdentifier);
}
else
{
format.findAllTypesForFile (found, fileOrIdentifier);
}
}
for (int i = 0; i < found.size(); ++i)
{
PluginDescription* const desc = found.getUnchecked(i);
jassert (desc != nullptr);
addType (*desc);
typesFound.add (new PluginDescription (*desc));
}
return found.size() > 0;
}
void KnownPluginList::scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager,
const StringArray& files,
OwnedArray <PluginDescription>& typesFound)
{
for (int i = 0; i < files.size(); ++i)
{
const String filenameOrID (files[i]);
bool found = false;
for (int j = 0; j < formatManager.getNumFormats(); ++j)
{
AudioPluginFormat* const format = formatManager.getFormat (j);
if (format->fileMightContainThisPluginType (filenameOrID)
&& scanAndAddFile (filenameOrID, true, typesFound, *format))
{
found = true;
break;
}
}
if (! found)
{
const File f (filenameOrID);
if (f.isDirectory())
{
StringArray s;
{
Array<File> subFiles;
f.findChildFiles (subFiles, File::findFilesAndDirectories, false);
for (int j = 0; j < subFiles.size(); ++j)
s.add (subFiles.getReference(j).getFullPathName());
}
scanAndAddDragAndDroppedFiles (formatManager, s, typesFound);
}
}
}
scanFinished();
}
void KnownPluginList::scanFinished()
{
if (scanner != nullptr)
scanner->scanFinished();
}
const StringArray& KnownPluginList::getBlacklistedFiles() const
{
return blacklist;
}
void KnownPluginList::addToBlacklist (const String& pluginID)
{
if (! blacklist.contains (pluginID))
{
blacklist.add (pluginID);
sendChangeMessage();
}
}
void KnownPluginList::removeFromBlacklist (const String& pluginID)
{
const int index = blacklist.indexOf (pluginID);
if (index >= 0)
{
blacklist.remove (index);
sendChangeMessage();
}
}
void KnownPluginList::clearBlacklistedFiles()
{
if (blacklist.size() > 0)
{
blacklist.clear();
sendChangeMessage();
}
}
//==============================================================================
struct PluginSorter
{
PluginSorter (KnownPluginList::SortMethod sortMethod, bool forwards) noexcept
: method (sortMethod), direction (forwards ? 1 : -1) {}
int compareElements (const PluginDescription* const first,
const PluginDescription* const second) const
{
int diff = 0;
switch (method)
{
case KnownPluginList::sortByCategory: diff = first->category.compareNatural (second->category); break;
case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareNatural (second->manufacturerName); break;
case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break;
case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break;
default: break;
}
if (diff == 0)
diff = first->name.compareNatural (second->name);
return diff * direction;
}
private:
static String lastPathPart (const String& path)
{
return path.replaceCharacter ('\\', '/').upToLastOccurrenceOf ("/", false, false);
}
const KnownPluginList::SortMethod method;
const int direction;
JUCE_DECLARE_NON_COPYABLE (PluginSorter)
};
void KnownPluginList::sort (const SortMethod method, bool forwards)
{
if (method != defaultOrder)
{
Array<PluginDescription*> oldOrder, newOrder;
oldOrder.addArray (types);
PluginSorter sorter (method, forwards);
types.sort (sorter, true);
newOrder.addArray (types);
if (oldOrder != newOrder)
sendChangeMessage();
}
}
//==============================================================================
XmlElement* KnownPluginList::createXml() const
{
XmlElement* const e = new XmlElement ("KNOWNPLUGINS");
for (int i = types.size(); --i >= 0;)
e->prependChildElement (types.getUnchecked(i)->createXml());
for (int i = 0; i < blacklist.size(); ++i)
e->createNewChildElement ("BLACKLISTED")->setAttribute ("id", blacklist[i]);
return e;
}
void KnownPluginList::recreateFromXml (const XmlElement& xml)
{
clear();
clearBlacklistedFiles();
if (xml.hasTagName ("KNOWNPLUGINS"))
{
forEachXmlChildElement (xml, e)
{
PluginDescription info;
if (e->hasTagName ("BLACKLISTED"))
blacklist.add (e->getStringAttribute ("id"));
else if (info.loadFromXml (*e))
addType (info);
}
}
}
//==============================================================================
struct PluginTreeUtils
{
enum { menuIdBase = 0x324503f4 };
static void buildTreeByFolder (KnownPluginList::PluginTree& tree, const Array <PluginDescription*>& allPlugins)
{
for (int i = 0; i < allPlugins.size(); ++i)
{
PluginDescription* const pd = allPlugins.getUnchecked (i);
String path (pd->fileOrIdentifier.replaceCharacter ('\\', '/')
.upToLastOccurrenceOf ("/", false, false));
if (path.substring (1, 2) == ":")
path = path.substring (2);
addPlugin (tree, pd, path);
}
optimiseFolders (tree, false);
}
static void optimiseFolders (KnownPluginList::PluginTree& tree, bool concatenateName)
{
for (int i = tree.subFolders.size(); --i >= 0;)
{
KnownPluginList::PluginTree& sub = *tree.subFolders.getUnchecked(i);
optimiseFolders (sub, concatenateName || (tree.subFolders.size() > 1));
if (sub.plugins.size() == 0)
{
for (int j = 0; j < sub.subFolders.size(); ++j)
{
KnownPluginList::PluginTree* const s = sub.subFolders.getUnchecked(j);
if (concatenateName)
s->folder = sub.folder + "/" + s->folder;
tree.subFolders.add (s);
}
sub.subFolders.clear (false);
tree.subFolders.remove (i);
}
}
}
static void buildTreeByCategory (KnownPluginList::PluginTree& tree,
const Array <PluginDescription*>& sorted,
const KnownPluginList::SortMethod sortMethod)
{
String lastType;
ScopedPointer<KnownPluginList::PluginTree> current (new KnownPluginList::PluginTree());
for (int i = 0; i < sorted.size(); ++i)
{
const PluginDescription* const pd = sorted.getUnchecked(i);
String thisType (sortMethod == KnownPluginList::sortByCategory ? pd->category
: pd->manufacturerName);
if (! thisType.containsNonWhitespaceChars())
thisType = "Other";
if (thisType != lastType)
{
if (current->plugins.size() + current->subFolders.size() > 0)
{
current->folder = lastType;
tree.subFolders.add (current.release());
current = new KnownPluginList::PluginTree();
}
lastType = thisType;
}
current->plugins.add (pd);
}
if (current->plugins.size() + current->subFolders.size() > 0)
{
current->folder = lastType;
tree.subFolders.add (current.release());
}
}
static void addPlugin (KnownPluginList::PluginTree& tree, PluginDescription* const pd, String path)
{
if (path.isEmpty())
{
tree.plugins.add (pd);
}
else
{
#if JUCE_MAC
if (path.containsChar (':'))
path = path.fromFirstOccurrenceOf (":", false, false); // avoid the special AU formatting nonsense on Mac..
#endif
const String firstSubFolder (path.upToFirstOccurrenceOf ("/", false, false));
const String remainingPath (path.fromFirstOccurrenceOf ("/", false, false));
for (int i = tree.subFolders.size(); --i >= 0;)
{
KnownPluginList::PluginTree& subFolder = *tree.subFolders.getUnchecked(i);
if (subFolder.folder.equalsIgnoreCase (firstSubFolder))
{
addPlugin (subFolder, pd, remainingPath);
return;
}
}
KnownPluginList::PluginTree* const newFolder = new KnownPluginList::PluginTree();
newFolder->folder = firstSubFolder;
tree.subFolders.add (newFolder);
addPlugin (*newFolder, pd, remainingPath);
}
}
static bool containsDuplicateNames (const Array<const PluginDescription*>& plugins, const String& name)
{
int matches = 0;
for (int i = 0; i < plugins.size(); ++i)
if (plugins.getUnchecked(i)->name == name)
if (++matches > 1)
return true;
return false;
}
static void addToMenu (const KnownPluginList::PluginTree& tree, PopupMenu& m, const OwnedArray <PluginDescription>& allPlugins)
{
for (int i = 0; i < tree.subFolders.size(); ++i)
{
const KnownPluginList::PluginTree& sub = *tree.subFolders.getUnchecked(i);
PopupMenu subMenu;
addToMenu (sub, subMenu, allPlugins);
m.addSubMenu (sub.folder, subMenu);
}
for (int i = 0; i < tree.plugins.size(); ++i)
{
const PluginDescription* const plugin = tree.plugins.getUnchecked(i);
String name (plugin->name);
if (containsDuplicateNames (tree.plugins, name))
name << " (" << plugin->pluginFormatName << ')';
m.addItem (allPlugins.indexOf (plugin) + menuIdBase, name, true, false);
}
}
};
KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortMethod) const
{
Array <PluginDescription*> sorted;
{
PluginSorter sorter (sortMethod, true);
for (int i = 0; i < types.size(); ++i)
sorted.addSorted (sorter, types.getUnchecked(i));
}
PluginTree* tree = new PluginTree();
if (sortMethod == sortByCategory || sortMethod == sortByManufacturer || sortMethod == sortByFormat)
{
PluginTreeUtils::buildTreeByCategory (*tree, sorted, sortMethod);
}
else if (sortMethod == sortByFileSystemLocation)
{
PluginTreeUtils::buildTreeByFolder (*tree, sorted);
}
else
{
for (int i = 0; i < sorted.size(); ++i)
tree->plugins.add (sorted.getUnchecked(i));
}
return tree;
}
//==============================================================================
void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const
{
ScopedPointer<PluginTree> tree (createTree (sortMethod));
PluginTreeUtils::addToMenu (*tree, menu, types);
}
int KnownPluginList::getIndexChosenByMenu (const int menuResultCode) const
{
const int i = menuResultCode - PluginTreeUtils::menuIdBase;
return isPositiveAndBelow (i, types.size()) ? i : -1;
}
//==============================================================================
KnownPluginList::CustomScanner::CustomScanner() {}
KnownPluginList::CustomScanner::~CustomScanner() {}
void KnownPluginList::CustomScanner::scanFinished() {}
bool KnownPluginList::CustomScanner::shouldExit() const noexcept
{
if (ThreadPoolJob* job = ThreadPoolJob::getCurrentThreadPoolJob())
return job->shouldExit();
return false;
}

View file

@ -0,0 +1,223 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_KNOWNPLUGINLIST_H_INCLUDED
#define JUCE_KNOWNPLUGINLIST_H_INCLUDED
//==============================================================================
/**
Manages a list of plugin types.
This can be easily edited, saved and loaded, and used to create instances of
the plugin types in it.
@see PluginListComponent
*/
class JUCE_API KnownPluginList : public ChangeBroadcaster
{
public:
//==============================================================================
/** Creates an empty list. */
KnownPluginList();
/** Destructor. */
~KnownPluginList();
//==============================================================================
/** Clears the list. */
void clear();
/** Returns the number of types currently in the list.
@see getType
*/
int getNumTypes() const noexcept { return types.size(); }
/** Returns one of the types.
@see getNumTypes
*/
PluginDescription* getType (int index) const noexcept { return types [index]; }
/** Type iteration. */
PluginDescription** begin() const noexcept { return types.begin(); }
/** Type iteration. */
PluginDescription** end() const noexcept { return types.end(); }
/** Looks for a type in the list which comes from this file. */
PluginDescription* getTypeForFile (const String& fileOrIdentifier) const;
/** Looks for a type in the list which matches a plugin type ID.
The identifierString parameter must have been created by
PluginDescription::createIdentifierString().
*/
PluginDescription* getTypeForIdentifierString (const String& identifierString) const;
/** Adds a type manually from its description. */
bool addType (const PluginDescription& type);
/** Removes a type. */
void removeType (int index);
/** Looks for all types that can be loaded from a given file, and adds them
to the list.
If dontRescanIfAlreadyInList is true, then the file will only be loaded and
re-tested if it's not already in the list, or if the file's modification
time has changed since the list was created. If dontRescanIfAlreadyInList is
false, the file will always be reloaded and tested.
Returns true if any new types were added, and all the types found in this
file (even if it was already known and hasn't been re-scanned) get returned
in the array.
*/
bool scanAndAddFile (const String& possiblePluginFileOrIdentifier,
bool dontRescanIfAlreadyInList,
OwnedArray <PluginDescription>& typesFound,
AudioPluginFormat& formatToUse);
/** Tells a custom scanner that a scan has finished, and it can release any resources. */
void scanFinished();
/** Returns true if the specified file is already known about and if it
hasn't been modified since our entry was created.
*/
bool isListingUpToDate (const String& possiblePluginFileOrIdentifier,
AudioPluginFormat& formatToUse) const;
/** Scans and adds a bunch of files that might have been dragged-and-dropped.
If any types are found in the files, their descriptions are returned in the array.
*/
void scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager,
const StringArray& filenames,
OwnedArray <PluginDescription>& typesFound);
//==============================================================================
/** Returns the list of blacklisted files. */
const StringArray& getBlacklistedFiles() const;
/** Adds a plugin ID to the black-list. */
void addToBlacklist (const String& pluginID);
/** Removes a plugin ID from the black-list. */
void removeFromBlacklist (const String& pluginID);
/** Clears all the blacklisted files. */
void clearBlacklistedFiles();
//==============================================================================
/** Sort methods used to change the order of the plugins in the list.
*/
enum SortMethod
{
defaultOrder = 0,
sortAlphabetically,
sortByCategory,
sortByManufacturer,
sortByFormat,
sortByFileSystemLocation
};
//==============================================================================
/** Adds all the plugin types to a popup menu so that the user can select one.
Depending on the sort method, it may add sub-menus for categories,
manufacturers, etc.
Use getIndexChosenByMenu() to find out the type that was chosen.
*/
void addToMenu (PopupMenu& menu, SortMethod sortMethod) const;
/** Converts a menu item index that has been chosen into its index in this list.
Returns -1 if it's not an ID that was used.
@see addToMenu
*/
int getIndexChosenByMenu (int menuResultCode) const;
//==============================================================================
/** Sorts the list. */
void sort (SortMethod method, bool forwards);
//==============================================================================
/** Creates some XML that can be used to store the state of this list. */
XmlElement* createXml() const;
/** Recreates the state of this list from its stored XML format. */
void recreateFromXml (const XmlElement& xml);
//==============================================================================
/** A structure that recursively holds a tree of plugins.
@see KnownPluginList::createTree()
*/
struct PluginTree
{
String folder; /**< The name of this folder in the tree */
OwnedArray<PluginTree> subFolders;
Array<const PluginDescription*> plugins;
};
/** Creates a PluginTree object containing all the known plugins. */
PluginTree* createTree (const SortMethod sortMethod) const;
//==============================================================================
class CustomScanner
{
public:
CustomScanner();
virtual ~CustomScanner();
/** Attempts to load the given file and find a list of plugins in it.
@returns true if the plugin loaded, false if it crashed
*/
virtual bool findPluginTypesFor (AudioPluginFormat& format,
OwnedArray <PluginDescription>& result,
const String& fileOrIdentifier) = 0;
/** Called when a scan has finished, to allow clean-up of resources. */
virtual void scanFinished();
/** Returns true if the current scan should be abandoned.
Any blocking methods should check this value repeatedly and return if
if becomes true.
*/
bool shouldExit() const noexcept;
};
/** Supplies a custom scanner to be used in future scans.
The KnownPluginList will take ownership of the object passed in.
*/
void setCustomScanner (CustomScanner*);
private:
//==============================================================================
OwnedArray<PluginDescription> types;
StringArray blacklist;
ScopedPointer<CustomScanner> scanner;
CriticalSection scanLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownPluginList)
};
#endif // JUCE_KNOWNPLUGINLIST_H_INCLUDED

View file

@ -0,0 +1,136 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
static StringArray readDeadMansPedalFile (const File& file)
{
StringArray lines;
file.readLines (lines);
lines.removeEmptyStrings();
return lines;
}
PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo,
AudioPluginFormat& formatToLookFor,
FileSearchPath directoriesToSearch,
const bool recursive,
const File& deadMansPedal)
: list (listToAddTo),
format (formatToLookFor),
deadMansPedalFile (deadMansPedal),
progress (0)
{
directoriesToSearch.removeRedundantPaths();
filesOrIdentifiersToScan = format.searchPathsForPlugins (directoriesToSearch, recursive);
// If any plugins have crashed recently when being loaded, move them to the
// end of the list to give the others a chance to load correctly..
const StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile));
for (int i = 0; i < crashedPlugins.size(); ++i)
{
const String f = crashedPlugins[i];
for (int j = filesOrIdentifiersToScan.size(); --j >= 0;)
if (f == filesOrIdentifiersToScan[j])
filesOrIdentifiersToScan.move (j, -1);
}
applyBlacklistingsFromDeadMansPedal (listToAddTo, deadMansPedalFile);
nextIndex.set (filesOrIdentifiersToScan.size());
}
PluginDirectoryScanner::~PluginDirectoryScanner()
{
list.scanFinished();
}
//==============================================================================
String PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const
{
return format.getNameOfPluginFromIdentifier (filesOrIdentifiersToScan [nextIndex.get() - 1]);
}
void PluginDirectoryScanner::updateProgress()
{
progress = (1.0f - nextIndex.get() / (float) filesOrIdentifiersToScan.size());
}
bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList,
String& nameOfPluginBeingScanned)
{
const int index = --nextIndex;
if (index >= 0)
{
const String file (filesOrIdentifiersToScan [index]);
if (file.isNotEmpty() && ! list.isListingUpToDate (file, format))
{
nameOfPluginBeingScanned = format.getNameOfPluginFromIdentifier (file);
OwnedArray <PluginDescription> typesFound;
// Add this plugin to the end of the dead-man's pedal list in case it crashes...
StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile));
crashedPlugins.removeString (file);
crashedPlugins.add (file);
setDeadMansPedalFile (crashedPlugins);
list.scanAndAddFile (file, dontRescanIfAlreadyInList, typesFound, format);
// Managed to load without crashing, so remove it from the dead-man's-pedal..
crashedPlugins.removeString (file);
setDeadMansPedalFile (crashedPlugins);
if (typesFound.size() == 0 && ! list.getBlacklistedFiles().contains (file))
failedFiles.add (file);
}
}
updateProgress();
return index > 0;
}
bool PluginDirectoryScanner::skipNextFile()
{
updateProgress();
return --nextIndex > 0;
}
void PluginDirectoryScanner::setDeadMansPedalFile (const StringArray& newContents)
{
if (deadMansPedalFile.getFullPathName().isNotEmpty())
deadMansPedalFile.replaceWithText (newContents.joinIntoString ("\n"), true, true);
}
void PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (KnownPluginList& list, const File& file)
{
// If any plugins have crashed recently when being loaded, move them to the
// end of the list to give the others a chance to load correctly..
const StringArray crashedPlugins (readDeadMansPedalFile (file));
for (int i = 0; i < crashedPlugins.size(); ++i)
list.addToBlacklist (crashedPlugins[i]);
}

View file

@ -0,0 +1,127 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_PLUGINDIRECTORYSCANNER_H_INCLUDED
#define JUCE_PLUGINDIRECTORYSCANNER_H_INCLUDED
//==============================================================================
/**
Scans a directory for plugins, and adds them to a KnownPluginList.
To use one of these, create it and call scanNextFile() repeatedly, until
it returns false.
*/
class JUCE_API PluginDirectoryScanner
{
public:
//==============================================================================
/**
Creates a scanner.
@param listToAddResultsTo this will get the new types added to it.
@param formatToLookFor this is the type of format that you want to look for
@param directoriesToSearch the path to search
@param searchRecursively true to search recursively
@param deadMansPedalFile if this isn't File::nonexistent, then it will
be used as a file to store the names of any plugins
that crash during initialisation. If there are
any plugins listed in it, then these will always
be scanned after all other possible files have
been tried - in this way, even if there's a few
dodgy plugins in your path, then a couple of rescans
will still manage to find all the proper plugins.
It's probably best to choose a file in the user's
application data directory (alongside your app's
settings file) for this. The file format it uses
is just a list of filenames of the modules that
failed.
*/
PluginDirectoryScanner (KnownPluginList& listToAddResultsTo,
AudioPluginFormat& formatToLookFor,
FileSearchPath directoriesToSearch,
bool searchRecursively,
const File& deadMansPedalFile);
/** Destructor. */
~PluginDirectoryScanner();
//==============================================================================
/** Tries the next likely-looking file.
If dontRescanIfAlreadyInList is true, then the file will only be loaded and
re-tested if it's not already in the list, or if the file's modification
time has changed since the list was created. If dontRescanIfAlreadyInList is
false, the file will always be reloaded and tested.
The nameOfPluginBeingScanned will be updated to the name of the plugin being
scanned before the scan starts.
Returns false when there are no more files to try.
*/
bool scanNextFile (bool dontRescanIfAlreadyInList,
String& nameOfPluginBeingScanned);
/** Skips over the next file without scanning it.
Returns false when there are no more files to try.
*/
bool skipNextFile();
/** Returns the description of the plugin that will be scanned during the next
call to scanNextFile().
This is handy if you want to show the user which file is currently getting
scanned.
*/
String getNextPluginFileThatWillBeScanned() const;
/** Returns the estimated progress, between 0 and 1. */
float getProgress() const { return progress; }
/** This returns a list of all the filenames of things that looked like being
a plugin file, but which failed to open for some reason.
*/
const StringArray& getFailedFiles() const noexcept { return failedFiles; }
/** Reads the given dead-mans-pedal file and applies its contents to the list. */
static void applyBlacklistingsFromDeadMansPedal (KnownPluginList& listToApplyTo,
const File& deadMansPedalFile);
private:
//==============================================================================
KnownPluginList& list;
AudioPluginFormat& format;
StringArray filesOrIdentifiersToScan;
File deadMansPedalFile;
StringArray failedFiles;
Atomic<int> nextIndex;
float progress;
void updateProgress();
void setDeadMansPedalFile (const StringArray& newContents);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginDirectoryScanner)
};
#endif // JUCE_PLUGINDIRECTORYSCANNER_H_INCLUDED

View file

@ -0,0 +1,554 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
class PluginListComponent::TableModel : public TableListBoxModel
{
public:
TableModel (PluginListComponent& c, KnownPluginList& l) : owner (c), list (l) {}
int getNumRows() override
{
return list.getNumTypes() + list.getBlacklistedFiles().size();
}
void paintRowBackground (Graphics& g, int /*rowNumber*/, int /*width*/, int /*height*/, bool rowIsSelected) override
{
if (rowIsSelected)
g.fillAll (owner.findColour (TextEditor::highlightColourId));
}
enum
{
nameCol = 1,
typeCol = 2,
categoryCol = 3,
manufacturerCol = 4,
descCol = 5
};
void paintCell (Graphics& g, int row, int columnId, int width, int height, bool /*rowIsSelected*/) override
{
String text;
bool isBlacklisted = row >= list.getNumTypes();
if (isBlacklisted)
{
if (columnId == nameCol)
text = list.getBlacklistedFiles() [row - list.getNumTypes()];
else if (columnId == descCol)
text = TRANS("Deactivated after failing to initialise correctly");
}
else if (const PluginDescription* const desc = list.getType (row))
{
switch (columnId)
{
case nameCol: text = desc->name; break;
case typeCol: text = desc->pluginFormatName; break;
case categoryCol: text = desc->category.isNotEmpty() ? desc->category : "-"; break;
case manufacturerCol: text = desc->manufacturerName; break;
case descCol: text = getPluginDescription (*desc); break;
default: jassertfalse; break;
}
}
if (text.isNotEmpty())
{
g.setColour (isBlacklisted ? Colours::red
: columnId == nameCol ? Colours::black
: Colours::grey);
g.setFont (Font (height * 0.7f, Font::bold));
g.drawFittedText (text, 4, 0, width - 6, height, Justification::centredLeft, 1, 0.9f);
}
}
void deleteKeyPressed (int) override
{
owner.removeSelected();
}
void sortOrderChanged (int newSortColumnId, bool isForwards) override
{
switch (newSortColumnId)
{
case nameCol: list.sort (KnownPluginList::sortAlphabetically, isForwards); break;
case typeCol: list.sort (KnownPluginList::sortByFormat, isForwards); break;
case categoryCol: list.sort (KnownPluginList::sortByCategory, isForwards); break;
case manufacturerCol: list.sort (KnownPluginList::sortByManufacturer, isForwards); break;
case descCol: break;
default: jassertfalse; break;
}
}
static void removePluginItem (KnownPluginList& list, int index)
{
if (index < list.getNumTypes())
list.removeType (index);
else
list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]);
}
static String getPluginDescription (const PluginDescription& desc)
{
StringArray items;
if (desc.descriptiveName != desc.name)
items.add (desc.descriptiveName);
items.add (desc.version);
items.removeEmptyStrings();
return items.joinIntoString (" - ");
}
PluginListComponent& owner;
KnownPluginList& list;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableModel)
};
//==============================================================================
PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, KnownPluginList& listToEdit,
const File& deadMansPedal, PropertiesFile* const props)
: formatManager (manager),
list (listToEdit),
deadMansPedalFile (deadMansPedal),
optionsButton ("Options..."),
propertiesToUse (props),
numThreads (0)
{
tableModel = new TableModel (*this, listToEdit);
TableHeaderComponent& header = table.getHeader();
header.addColumn (TRANS("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards);
header.addColumn (TRANS("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable);
header.addColumn (TRANS("Category"), TableModel::categoryCol, 100, 100, 200);
header.addColumn (TRANS("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300);
header.addColumn (TRANS("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable);
table.setHeaderHeight (22);
table.setRowHeight (20);
table.setModel (tableModel);
table.setMultipleSelectionEnabled (true);
addAndMakeVisible (table);
addAndMakeVisible (optionsButton);
optionsButton.addListener (this);
optionsButton.setTriggeredOnMouseDown (true);
setSize (400, 600);
list.addChangeListener (this);
updateList();
table.getHeader().reSortTable();
PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (list, deadMansPedalFile);
deadMansPedalFile.deleteFile();
}
PluginListComponent::~PluginListComponent()
{
list.removeChangeListener (this);
}
void PluginListComponent::setOptionsButtonText (const String& newText)
{
optionsButton.setButtonText (newText);
resized();
}
void PluginListComponent::setNumberOfThreadsForScanning (int num)
{
numThreads = num;
}
void PluginListComponent::resized()
{
Rectangle<int> r (getLocalBounds().reduced (2));
optionsButton.setBounds (r.removeFromBottom (24));
optionsButton.changeWidthToFitText (24);
r.removeFromBottom (3);
table.setBounds (r);
}
void PluginListComponent::changeListenerCallback (ChangeBroadcaster*)
{
table.getHeader().reSortTable();
updateList();
}
void PluginListComponent::updateList()
{
table.updateContent();
table.repaint();
}
void PluginListComponent::removeSelected()
{
const SparseSet<int> selected (table.getSelectedRows());
for (int i = table.getNumRows(); --i >= 0;)
if (selected.contains (i))
TableModel::removePluginItem (list, i);
}
bool PluginListComponent::canShowSelectedFolder() const
{
if (const PluginDescription* const desc = list.getType (table.getSelectedRow()))
return File::createFileWithoutCheckingPath (desc->fileOrIdentifier).exists();
return false;
}
void PluginListComponent::showSelectedFolder()
{
if (canShowSelectedFolder())
if (const PluginDescription* const desc = list.getType (table.getSelectedRow()))
File (desc->fileOrIdentifier).getParentDirectory().startAsProcess();
}
void PluginListComponent::removeMissingPlugins()
{
for (int i = list.getNumTypes(); --i >= 0;)
if (! formatManager.doesPluginStillExist (*list.getType (i)))
list.removeType (i);
}
void PluginListComponent::optionsMenuStaticCallback (int result, PluginListComponent* pluginList)
{
if (pluginList != nullptr)
pluginList->optionsMenuCallback (result);
}
void PluginListComponent::optionsMenuCallback (int result)
{
switch (result)
{
case 0: break;
case 1: list.clear(); break;
case 2: removeSelected(); break;
case 3: showSelectedFolder(); break;
case 4: removeMissingPlugins(); break;
default:
if (AudioPluginFormat* format = formatManager.getFormat (result - 10))
scanFor (*format);
break;
}
}
void PluginListComponent::buttonClicked (Button* button)
{
if (button == &optionsButton)
{
PopupMenu menu;
menu.addItem (1, TRANS("Clear list"));
menu.addItem (2, TRANS("Remove selected plug-in from list"), table.getNumSelectedRows() > 0);
menu.addItem (3, TRANS("Show folder containing selected plug-in"), canShowSelectedFolder());
menu.addItem (4, TRANS("Remove any plug-ins whose files no longer exist"));
menu.addSeparator();
for (int i = 0; i < formatManager.getNumFormats(); ++i)
{
AudioPluginFormat* const format = formatManager.getFormat (i);
if (format->canScanForPlugins())
menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plug-ins");
}
menu.showMenuAsync (PopupMenu::Options().withTargetComponent (&optionsButton),
ModalCallbackFunction::forComponent (optionsMenuStaticCallback, this));
}
}
bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/)
{
return true;
}
void PluginListComponent::filesDropped (const StringArray& files, int, int)
{
OwnedArray <PluginDescription> typesFound;
list.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound);
}
FileSearchPath PluginListComponent::getLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format)
{
return FileSearchPath (properties.getValue ("lastPluginScanPath_" + format.getName(),
format.getDefaultLocationsToSearch().toString()));
}
void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format,
const FileSearchPath& newPath)
{
properties.setValue ("lastPluginScanPath_" + format.getName(), newPath.toString());
}
//==============================================================================
class PluginListComponent::Scanner : private Timer
{
public:
Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties, int threads)
: owner (plc), formatToScan (format), propertiesToUse (properties),
pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon),
progressWindow (TRANS("Scanning for plug-ins..."),
TRANS("Searching for all possible plug-in files..."), AlertWindow::NoIcon),
progress (0.0), numThreads (threads), finished (false)
{
FileSearchPath path (formatToScan.getDefaultLocationsToSearch());
if (path.getNumPaths() > 0) // if the path is empty, then paths aren't used for this format.
{
if (propertiesToUse != nullptr)
path = getLastSearchPath (*propertiesToUse, formatToScan);
pathList.setSize (500, 300);
pathList.setPath (path);
pathChooserWindow.addCustomComponent (&pathList);
pathChooserWindow.addButton (TRANS("Scan"), 1, KeyPress (KeyPress::returnKey));
pathChooserWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey));
pathChooserWindow.enterModalState (true,
ModalCallbackFunction::forComponent (startScanCallback,
&pathChooserWindow, this),
false);
}
else
{
startScan();
}
}
~Scanner()
{
if (pool != nullptr)
{
pool->removeAllJobs (true, 60000);
pool = nullptr;
}
}
private:
PluginListComponent& owner;
AudioPluginFormat& formatToScan;
PropertiesFile* propertiesToUse;
ScopedPointer<PluginDirectoryScanner> scanner;
AlertWindow pathChooserWindow, progressWindow;
FileSearchPathListComponent pathList;
String pluginBeingScanned;
double progress;
int numThreads;
bool finished;
ScopedPointer<ThreadPool> pool;
static void startScanCallback (int result, AlertWindow* alert, Scanner* scanner)
{
if (alert != nullptr && scanner != nullptr)
{
if (result != 0)
scanner->warnUserAboutStupidPaths();
else
scanner->finishedScan();
}
}
// Try to dissuade people from to scanning their entire C: drive, or other system folders.
void warnUserAboutStupidPaths()
{
for (int i = 0; i < pathList.getPath().getNumPaths(); ++i)
{
const File f (pathList.getPath()[i]);
if (isStupidPath (f))
{
AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
TRANS("Plugin Scanning"),
TRANS("If you choose to scan folders that contain non-plugin files, "
"then scanning may take a long time, and can cause crashes when "
"attempting to load unsuitable files.")
+ newLine
+ TRANS ("Are you sure you want to scan the folder \"XYZ\"?")
.replace ("XYZ", f.getFullPathName()),
TRANS ("Scan"),
String::empty,
nullptr,
ModalCallbackFunction::create (warnAboutStupidPathsCallback, this));
return;
}
}
startScan();
}
static bool isStupidPath (const File& f)
{
Array<File> roots;
File::findFileSystemRoots (roots);
if (roots.contains (f))
return true;
File::SpecialLocationType pathsThatWouldBeStupidToScan[]
= { File::globalApplicationsDirectory,
File::userHomeDirectory,
File::userDocumentsDirectory,
File::userDesktopDirectory,
File::tempDirectory,
File::userMusicDirectory,
File::userMoviesDirectory,
File::userPicturesDirectory };
for (int i = 0; i < numElementsInArray (pathsThatWouldBeStupidToScan); ++i)
{
const File sillyFolder (File::getSpecialLocation (pathsThatWouldBeStupidToScan[i]));
if (f == sillyFolder || sillyFolder.isAChildOf (f))
return true;
}
return false;
}
static void warnAboutStupidPathsCallback (int result, Scanner* scanner)
{
if (result != 0)
scanner->startScan();
else
scanner->finishedScan();
}
void startScan()
{
pathChooserWindow.setVisible (false);
scanner = new PluginDirectoryScanner (owner.list, formatToScan, pathList.getPath(),
true, owner.deadMansPedalFile);
if (propertiesToUse != nullptr)
{
setLastSearchPath (*propertiesToUse, formatToScan, pathList.getPath());
propertiesToUse->saveIfNeeded();
}
progressWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey));
progressWindow.addProgressBarComponent (progress);
progressWindow.enterModalState();
if (numThreads > 0)
{
pool = new ThreadPool (numThreads);
for (int i = numThreads; --i >= 0;)
pool->addJob (new ScanJob (*this), true);
}
startTimer (20);
}
void finishedScan()
{
owner.scanFinished (scanner != nullptr ? scanner->getFailedFiles()
: StringArray());
}
void timerCallback() override
{
if (pool == nullptr)
{
if (doNextScan())
startTimer (20);
}
if (! progressWindow.isCurrentlyModal())
finished = true;
if (finished)
finishedScan();
else
progressWindow.setMessage (TRANS("Testing") + ":\n\n" + pluginBeingScanned);
}
bool doNextScan()
{
if (scanner->scanNextFile (true, pluginBeingScanned))
{
progress = scanner->getProgress();
return true;
}
finished = true;
return false;
}
struct ScanJob : public ThreadPoolJob
{
ScanJob (Scanner& s) : ThreadPoolJob ("pluginscan"), scanner (s) {}
JobStatus runJob()
{
while (scanner.doNextScan() && ! shouldExit())
{}
return jobHasFinished;
}
Scanner& scanner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScanJob)
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Scanner)
};
void PluginListComponent::scanFor (AudioPluginFormat& format)
{
currentScanner = new Scanner (*this, format, propertiesToUse, numThreads);
}
bool PluginListComponent::isScanning() const noexcept
{
return currentScanner != nullptr;
}
void PluginListComponent::scanFinished (const StringArray& failedFiles)
{
StringArray shortNames;
for (int i = 0; i < failedFiles.size(); ++i)
shortNames.add (File::createFileWithoutCheckingPath (failedFiles[i]).getFileName());
currentScanner = nullptr; // mustn't delete this before using the failed files array
if (shortNames.size() > 0)
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
TRANS("Scan complete"),
TRANS("Note that the following files appeared to be plugin files, but failed to load correctly")
+ ":\n\n"
+ shortNames.joinIntoString (", "));
}

View file

@ -0,0 +1,114 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_PLUGINLISTCOMPONENT_H_INCLUDED
#define JUCE_PLUGINLISTCOMPONENT_H_INCLUDED
//==============================================================================
/**
A component displaying a list of plugins, with options to scan for them,
add, remove and sort them.
*/
class JUCE_API PluginListComponent : public Component,
public FileDragAndDropTarget,
private ChangeListener,
private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
{
public:
//==============================================================================
/**
Creates the list component.
For info about the deadMansPedalFile, see the PluginDirectoryScanner constructor.
The properties file, if supplied, is used to store the user's last search paths.
*/
PluginListComponent (AudioPluginFormatManager& formatManager,
KnownPluginList& listToRepresent,
const File& deadMansPedalFile,
PropertiesFile* propertiesToUse);
/** Destructor. */
~PluginListComponent();
/** Changes the text in the panel's options button. */
void setOptionsButtonText (const String& newText);
/** Sets how many threads to simultaneously scan for plugins.
If this is 0, then all scanning happens on the message thread (this is the default)
*/
void setNumberOfThreadsForScanning (int numThreads);
/** Returns the last search path stored in a given properties file for the specified format. */
static FileSearchPath getLastSearchPath (PropertiesFile&, AudioPluginFormat&);
/** Stores a search path in a properties file for the given format. */
static void setLastSearchPath (PropertiesFile&, AudioPluginFormat&, const FileSearchPath&);
/** Triggers an asynchronous scan for the given format. */
void scanFor (AudioPluginFormat&);
/** Returns true if there's currently a scan in progress. */
bool isScanning() const noexcept;
private:
//==============================================================================
AudioPluginFormatManager& formatManager;
KnownPluginList& list;
File deadMansPedalFile;
TableListBox table;
TextButton optionsButton;
PropertiesFile* propertiesToUse;
int numThreads;
class TableModel;
friend class TableModel;
friend struct ContainerDeletePolicy<TableModel>;
ScopedPointer<TableModel> tableModel;
class Scanner;
friend class Scanner;
friend struct ContainerDeletePolicy<Scanner>;
ScopedPointer<Scanner> currentScanner;
void scanFinished (const StringArray&);
static void optionsMenuStaticCallback (int, PluginListComponent*);
void optionsMenuCallback (int);
void updateList();
void showSelectedFolder();
bool canShowSelectedFolder() const;
void removeSelected();
void removeMissingPlugins();
void resized() override;
bool isInterestedInFileDrag (const StringArray&) override;
void filesDropped (const StringArray&, int, int) override;
void buttonClicked (Button*) override;
void changeListenerCallback (ChangeBroadcaster*) override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginListComponent)
};
#endif // JUCE_PLUGINLISTCOMPONENT_H_INCLUDED