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:
parent
fefcf7aca6
commit
ff6520a89a
1141 changed files with 438491 additions and 94 deletions
|
|
@ -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() {}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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; }
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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]);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 (", "));
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue