@@ -1,135 +0,0 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for juce_audio_processors # | |||
# ---------------------------------- # | |||
# Created by falkTX | |||
# | |||
CWD=../.. | |||
MODULENAME=juce_audio_processors | |||
include ../Makefile.mk | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
BUILD_CXX_FLAGS += $(JUCE_AUDIO_PROCESSORS_FLAGS) -I$(CWD)/includes/ladspa -I$(CWD)/includes/vst2 -I$(CWD)/includes/vst3 -I.. | |||
ifeq ($(CARLA_VESTIGE_HEADER),true) | |||
BUILD_CXX_FLAGS += -DVESTIGE_HEADER | |||
else | |||
# needed by vst3 | |||
BUILD_CXX_FLAGS += -w | |||
ifeq ($(DEBUG),true) | |||
BUILD_CXX_FLAGS += -DDEVELOPMENT -D_DEBUG | |||
else | |||
BUILD_CXX_FLAGS += -DRELEASE | |||
endif | |||
# needed by vst3 on mingw | |||
ifeq ($(WIN32),true) | |||
BUILD_CXX_FLAGS += -D_NATIVE_WCHAR_T_DEFINED -D__wchar_t=wchar_t -fpermissive | |||
endif | |||
endif | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
ifeq ($(MACOS),true) | |||
OBJS = $(OBJDIR)/$(MODULENAME).mm.o | |||
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).mm.posix32.o | |||
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).mm.posix64.o | |||
else | |||
OBJS = $(OBJDIR)/$(MODULENAME).cpp.o | |||
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).cpp.posix32.o | |||
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).cpp.posix64.o | |||
endif | |||
OBJS_win32 = $(OBJDIR)/$(MODULENAME).cpp.win32.o | |||
OBJS_win64 = $(OBJDIR)/$(MODULENAME).cpp.win64.o | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
all: $(MODULEDIR)/$(MODULENAME).a | |||
posix32: $(MODULEDIR)/$(MODULENAME).posix32.a | |||
posix64: $(MODULEDIR)/$(MODULENAME).posix64.a | |||
win32: $(MODULEDIR)/$(MODULENAME).win32.a | |||
win64: $(MODULEDIR)/$(MODULENAME).win64.a | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
clean: | |||
rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.a | |||
debug: | |||
$(MAKE) DEBUG=true | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
$(MODULEDIR)/$(MODULENAME).a: $(OBJS) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
$(MODULEDIR)/$(MODULENAME).posix32.a: $(OBJS_posix32) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).posix32.a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
$(MODULEDIR)/$(MODULENAME).posix64.a: $(OBJS_posix64) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).posix64.a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
$(MODULEDIR)/$(MODULENAME).win32.a: $(OBJS_win32) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).win32.a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
$(MODULEDIR)/$(MODULENAME).win64.a: $(OBJS_win64) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).win64.a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
$(OBJDIR)/$(MODULENAME).cpp.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $<" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
$(OBJDIR)/$(MODULENAME).cpp.%32.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $< (32bit)" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ | |||
$(OBJDIR)/$(MODULENAME).cpp.%64.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $< (64bit)" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
$(OBJDIR)/$(MODULENAME).mm.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $<" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ | |||
$(OBJDIR)/$(MODULENAME).mm.%32.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $< (32bit)" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -ObjC++ -c -o $@ | |||
$(OBJDIR)/$(MODULENAME).mm.%64.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $< (64bit)" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -ObjC++ -c -o $@ | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
-include $(OBJS:%.o=%.d) | |||
-include $(OBJS_posix32:%.o=%.d) | |||
-include $(OBJS_posix64:%.o=%.d) | |||
-include $(OBJS_win32:%.o=%.d) | |||
-include $(OBJS_win64:%.o=%.d) | |||
# ---------------------------------------------------------------------------------------------------------------------------- |
@@ -1,214 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
namespace AudioPluginFormatHelpers | |||
{ | |||
struct CallbackInvoker | |||
{ | |||
struct InvokeOnMessageThread : public CallbackMessage | |||
{ | |||
InvokeOnMessageThread (AudioPluginInstance* inInstance, const String& inError, | |||
AudioPluginFormat::InstantiationCompletionCallback* inCompletion, | |||
CallbackInvoker* invoker) | |||
: instance (inInstance), error (inError), compCallback (inCompletion), owner (invoker) | |||
{} | |||
void messageCallback() override { compCallback->completionCallback (instance, error); } | |||
//============================================================================== | |||
AudioPluginInstance* instance; | |||
String error; | |||
ScopedPointer<AudioPluginFormat::InstantiationCompletionCallback> compCallback; | |||
ScopedPointer<CallbackInvoker> owner; | |||
}; | |||
//============================================================================== | |||
CallbackInvoker (AudioPluginFormat::InstantiationCompletionCallback* cc) : completion (cc) | |||
{} | |||
void completionCallback (AudioPluginInstance* instance, const String& error) | |||
{ | |||
(new InvokeOnMessageThread (instance, error, completion, this))->post(); | |||
} | |||
static void staticCompletionCallback (void* userData, AudioPluginInstance* instance, const String& error) | |||
{ | |||
reinterpret_cast<CallbackInvoker*> (userData)->completionCallback (instance, error); | |||
} | |||
//============================================================================== | |||
AudioPluginFormat::InstantiationCompletionCallback* completion; | |||
}; | |||
} | |||
AudioPluginFormat::AudioPluginFormat() noexcept {} | |||
AudioPluginFormat::~AudioPluginFormat() {} | |||
AudioPluginInstance* AudioPluginFormat::createInstanceFromDescription (const PluginDescription& desc, | |||
double initialSampleRate, | |||
int initialBufferSize) | |||
{ | |||
String errorMessage; | |||
return createInstanceFromDescription (desc, initialSampleRate, initialBufferSize, errorMessage); | |||
} | |||
//============================================================================== | |||
struct EventSignaler : public AudioPluginFormat::InstantiationCompletionCallback | |||
{ | |||
EventSignaler (WaitableEvent& inEvent, AudioPluginInstance*& inInstance, String& inErrorMessage) | |||
: event (inEvent), outInstance (inInstance), outErrorMessage (inErrorMessage) | |||
{} | |||
void completionCallback (AudioPluginInstance* newInstance, const String& result) override | |||
{ | |||
outInstance = newInstance; | |||
outErrorMessage = result; | |||
event.signal(); | |||
} | |||
static void staticCompletionCallback (void* userData, AudioPluginInstance* pluginInstance, const String& error) | |||
{ | |||
reinterpret_cast<EventSignaler*> (userData)->completionCallback (pluginInstance, error); | |||
} | |||
WaitableEvent& event; | |||
AudioPluginInstance*& outInstance; | |||
String& outErrorMessage; | |||
JUCE_DECLARE_NON_COPYABLE (EventSignaler) | |||
}; | |||
AudioPluginInstance* AudioPluginFormat::createInstanceFromDescription (const PluginDescription& desc, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
String& errorMessage) | |||
{ | |||
if (MessageManager::getInstance()->isThisTheMessageThread() | |||
&& requiresUnblockedMessageThreadDuringCreation(desc)) | |||
{ | |||
errorMessage = NEEDS_TRANS ("This plug-in cannot be instantiated synchronously"); | |||
return nullptr; | |||
} | |||
WaitableEvent waitForCreation; | |||
AudioPluginInstance* instance = nullptr; | |||
ScopedPointer<EventSignaler> eventSignaler (new EventSignaler (waitForCreation, instance, errorMessage)); | |||
if (! MessageManager::getInstance()->isThisTheMessageThread()) | |||
createPluginInstanceAsync (desc, initialSampleRate, initialBufferSize, eventSignaler.release()); | |||
else | |||
createPluginInstance (desc, initialSampleRate, initialBufferSize, | |||
eventSignaler, EventSignaler::staticCompletionCallback); | |||
waitForCreation.wait(); | |||
return instance; | |||
} | |||
void AudioPluginFormat::createPluginInstanceAsync (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
AudioPluginFormat::InstantiationCompletionCallback* callback) | |||
{ | |||
if (MessageManager::getInstance()->isThisTheMessageThread()) | |||
{ | |||
createPluginInstanceOnMessageThread (description, initialSampleRate, initialBufferSize, callback); | |||
return; | |||
} | |||
//============================================================================== | |||
struct InvokeOnMessageThread : public CallbackMessage | |||
{ | |||
InvokeOnMessageThread (AudioPluginFormat* myself, | |||
const PluginDescription& descriptionParam, | |||
double initialSampleRateParam, | |||
int initialBufferSizeParam, | |||
AudioPluginFormat::InstantiationCompletionCallback* callbackParam) | |||
: owner (myself), descr (descriptionParam), sampleRate (initialSampleRateParam), | |||
bufferSize (initialBufferSizeParam), call (callbackParam) | |||
{} | |||
void messageCallback() override | |||
{ | |||
owner->createPluginInstanceOnMessageThread (descr, sampleRate, bufferSize, call); | |||
} | |||
AudioPluginFormat* owner; | |||
PluginDescription descr; | |||
double sampleRate; | |||
int bufferSize; | |||
AudioPluginFormat::InstantiationCompletionCallback* call; | |||
}; | |||
(new InvokeOnMessageThread (this, description, initialSampleRate, initialBufferSize, callback))->post(); | |||
} | |||
void AudioPluginFormat::createPluginInstanceAsync (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
std::function<void (AudioPluginInstance*, const String&)> f) | |||
{ | |||
struct CallbackInvoker : public AudioPluginFormat::InstantiationCompletionCallback | |||
{ | |||
CallbackInvoker (std::function<void (AudioPluginInstance*, const String&)> inCompletion) | |||
: completion (inCompletion) | |||
{} | |||
void completionCallback (AudioPluginInstance* instance, const String& error) override | |||
{ | |||
completion (instance, error); | |||
} | |||
std::function<void (AudioPluginInstance*, const String&)> completion; | |||
}; | |||
createPluginInstanceAsync (description, initialSampleRate, initialBufferSize, new CallbackInvoker (f)); | |||
} | |||
void AudioPluginFormat::createPluginInstanceOnMessageThread (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
AudioPluginFormat::InstantiationCompletionCallback* callback) | |||
{ | |||
jassert (callback != nullptr); | |||
jassert (MessageManager::getInstance()->isThisTheMessageThread()); | |||
//============================================================================== | |||
//============================================================================== | |||
AudioPluginFormatHelpers::CallbackInvoker* completion = new AudioPluginFormatHelpers::CallbackInvoker (callback); | |||
createPluginInstance (description, initialSampleRate, initialBufferSize, completion, | |||
AudioPluginFormatHelpers::CallbackInvoker::staticCompletionCallback); | |||
} | |||
} // namespace juce |
@@ -1,168 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
The base class for a type of plugin format, such as VST, AudioUnit, LADSPA, etc. | |||
@see AudioPluginFormatManager | |||
*/ | |||
class JUCE_API AudioPluginFormat | |||
{ | |||
public: | |||
//============================================================================== | |||
struct JUCE_API InstantiationCompletionCallback | |||
{ | |||
virtual ~InstantiationCompletionCallback() {} | |||
virtual void completionCallback (AudioPluginInstance* instance, const String& error) = 0; | |||
JUCE_LEAK_DETECTOR (InstantiationCompletionCallback) | |||
}; | |||
//============================================================================== | |||
/** 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 AudioPluginFormatManager::createInstance | |||
*/ | |||
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, | |||
double initialSampleRate, | |||
int initialBufferSize); | |||
/** Same as above but with the possibility of returning an error message. | |||
@see AudioPluginFormatManager::createInstance | |||
*/ | |||
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
String& errorMessage); | |||
/** Tries to recreate a type from a previously generated PluginDescription. | |||
@see AudioPluginFormatManager::createInstanceAsync | |||
*/ | |||
void createPluginInstanceAsync (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
InstantiationCompletionCallback* completionCallback); | |||
void createPluginInstanceAsync (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
std::function<void (AudioPluginInstance*, const String&)> completionCallback); | |||
/** 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&) = 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. | |||
@param directoriesToSearch This specifies which directories shall be | |||
searched for plug-ins. | |||
@param recursive Should the search recursively traverse folders. | |||
@param allowPluginsWhichRequireAsynchronousInstantiation | |||
If this is false then plug-ins which require | |||
asynchronous creation will be excluded. | |||
*/ | |||
virtual StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, | |||
bool recursive, | |||
bool allowPluginsWhichRequireAsynchronousInstantiation = false) = 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: | |||
//============================================================================== | |||
friend class AudioPluginFormatManager; | |||
AudioPluginFormat() noexcept; | |||
/** Implementors must override this function. This is guaranteed to be called on | |||
the message thread. You may call the callback on any thread. | |||
*/ | |||
virtual void createPluginInstance (const PluginDescription&, double initialSampleRate, | |||
int initialBufferSize, void* userData, | |||
void (*callback) (void*, AudioPluginInstance*, const String&)) = 0; | |||
virtual bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept = 0; | |||
private: | |||
/** @internal */ | |||
void createPluginInstanceOnMessageThread (const PluginDescription&, double rate, int size, | |||
AudioPluginFormat::InstantiationCompletionCallback*); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormat) | |||
}; | |||
} // namespace juce |
@@ -1,183 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
namespace PluginFormatManagerHelpers | |||
{ | |||
struct ErrorCallbackOnMessageThread : public CallbackMessage | |||
{ | |||
ErrorCallbackOnMessageThread (const String& inError, | |||
AudioPluginFormat::InstantiationCompletionCallback* inCallback) | |||
: error (inError), callback (inCallback) | |||
{ | |||
} | |||
void messageCallback() override { callback->completionCallback (nullptr, error); } | |||
String error; | |||
ScopedPointer<AudioPluginFormat::InstantiationCompletionCallback> callback; | |||
}; | |||
struct ErrorLambdaOnMessageThread : public CallbackMessage | |||
{ | |||
ErrorLambdaOnMessageThread (const String& inError, | |||
std::function<void (AudioPluginInstance*, const String&)> f) | |||
: error (inError), lambda (f) | |||
{ | |||
} | |||
void messageCallback() override { lambda (nullptr, error); } | |||
String error; | |||
std::function<void (AudioPluginInstance*, const String&)> lambda; | |||
}; | |||
} | |||
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 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_IOS) | |||
jassert (dynamic_cast<VSTPluginFormat*> (formats[i]) == nullptr); | |||
#endif | |||
#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS) | |||
jassert (dynamic_cast<VST3PluginFormat*> (formats[i]) == nullptr); | |||
#endif | |||
#if JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS) | |||
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 || JUCE_IOS) | |||
formats.add (new AudioUnitPluginFormat()); | |||
#endif | |||
#if JUCE_PLUGINHOST_VST && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_IOS) | |||
formats.add (new VSTPluginFormat()); | |||
#endif | |||
#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS) | |||
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 | |||
{ | |||
if (AudioPluginFormat* format = findFormatForDescription (description, errorMessage)) | |||
return format->createInstanceFromDescription (description, rate, blockSize, errorMessage); | |||
return nullptr; | |||
} | |||
void AudioPluginFormatManager::createPluginInstanceAsync (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
AudioPluginFormat::InstantiationCompletionCallback* callback) | |||
{ | |||
String error; | |||
if (AudioPluginFormat* format = findFormatForDescription (description, error)) | |||
return format->createPluginInstanceAsync (description, initialSampleRate, initialBufferSize, callback); | |||
(new PluginFormatManagerHelpers::ErrorCallbackOnMessageThread (error, callback))->post(); | |||
} | |||
void AudioPluginFormatManager::createPluginInstanceAsync (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
std::function<void (AudioPluginInstance*, const String&)> f) | |||
{ | |||
String error; | |||
if (AudioPluginFormat* format = findFormatForDescription (description, error)) | |||
return format->createPluginInstanceAsync (description, initialSampleRate, initialBufferSize, f); | |||
(new PluginFormatManagerHelpers::ErrorLambdaOnMessageThread (error, f))->post(); | |||
} | |||
AudioPluginFormat* AudioPluginFormatManager::findFormatForDescription (const PluginDescription& description, String& errorMessage) const | |||
{ | |||
errorMessage = String(); | |||
for (int i = 0; i < formats.size(); ++i) | |||
{ | |||
AudioPluginFormat* format; | |||
if ((format = formats.getUnchecked (i))->getName() == description.pluginFormatName | |||
&& format->fileMightContainThisPluginType (description.fileOrIdentifier)) | |||
return format; | |||
} | |||
errorMessage = NEEDS_TRANS ("No compatible plug-in format exists for this plug-in"); | |||
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; | |||
} | |||
} // namespace juce |
@@ -1,138 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
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. | |||
If you intend to instantiate a AudioUnit v3 plug-in then you must either | |||
use the non-blocking asynchrous version below - or call this method from a | |||
thread other than the message thread and without blocking the message | |||
thread. | |||
*/ | |||
AudioPluginInstance* createPluginInstance (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
String& errorMessage) const; | |||
/** Tries to asynchronously load the type for this description, by trying | |||
all the formats that this manager knows about. | |||
The caller must supply a callback object which will be called when | |||
the instantantiation has completed. | |||
If it can't load the plugin then the callback function will be called | |||
passing a nullptr as the instance argument along with an error message. | |||
The callback function will be called on the message thread so the caller | |||
must not block the message thread. | |||
The callback object will be deleted automatically after it has been | |||
invoked. | |||
The caller is responsible for deleting the instance that is passed to | |||
the callback function. | |||
If you intend to instantiate a AudioUnit v3 plug-in then you must use | |||
this non-blocking asynchrous version - or call the synchrous method | |||
from an auxiliary thread. | |||
*/ | |||
void createPluginInstanceAsync (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
AudioPluginFormat::InstantiationCompletionCallback* callback); | |||
void createPluginInstanceAsync (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
std::function<void (AudioPluginInstance*, const String&)> completionCallback); | |||
/** 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: | |||
//============================================================================== | |||
//@internal | |||
AudioPluginFormat* findFormatForDescription (const PluginDescription&, String& errorMessage) const; | |||
OwnedArray<AudioPluginFormat> formats; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormatManager) | |||
}; | |||
} // namespace juce |
@@ -1,535 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
// This macro can be set if you need to override this internal name for some reason.. | |||
#ifndef JUCE_STATE_DICTIONARY_KEY | |||
#define JUCE_STATE_DICTIONARY_KEY "jucePluginState" | |||
#endif | |||
namespace juce | |||
{ | |||
struct AudioUnitHelpers | |||
{ | |||
class ChannelRemapper | |||
{ | |||
public: | |||
ChannelRemapper (AudioProcessor& p) : processor (p), inputLayoutMap (nullptr), outputLayoutMap (nullptr) {} | |||
~ChannelRemapper() {} | |||
void alloc() | |||
{ | |||
const int numInputBuses = AudioUnitHelpers::getBusCount (&processor, true); | |||
const int numOutputBuses = AudioUnitHelpers::getBusCount (&processor, false); | |||
initializeChannelMapArray (true, numInputBuses); | |||
initializeChannelMapArray (false, numOutputBuses); | |||
for (int busIdx = 0; busIdx < numInputBuses; ++busIdx) | |||
fillLayoutChannelMaps (true, busIdx); | |||
for (int busIdx = 0; busIdx < numOutputBuses; ++busIdx) | |||
fillLayoutChannelMaps (false, busIdx); | |||
} | |||
void release() | |||
{ | |||
inputLayoutMap = outputLayoutMap = nullptr; | |||
inputLayoutMapPtrStorage.free(); | |||
outputLayoutMapPtrStorage.free(); | |||
inputLayoutMapStorage.free(); | |||
outputLayoutMapStorage.free(); | |||
} | |||
inline const int* get (bool input, int bus) const noexcept { return (input ? inputLayoutMap : outputLayoutMap) [bus]; } | |||
private: | |||
//============================================================================== | |||
AudioProcessor& processor; | |||
HeapBlock<int*> inputLayoutMapPtrStorage, outputLayoutMapPtrStorage; | |||
HeapBlock<int> inputLayoutMapStorage, outputLayoutMapStorage; | |||
int** inputLayoutMap; | |||
int** outputLayoutMap; | |||
//============================================================================== | |||
void initializeChannelMapArray (bool isInput, const int numBuses) | |||
{ | |||
HeapBlock<int*>& layoutMapPtrStorage = isInput ? inputLayoutMapPtrStorage : outputLayoutMapPtrStorage; | |||
HeapBlock<int>& layoutMapStorage = isInput ? inputLayoutMapStorage : outputLayoutMapStorage; | |||
int**& layoutMap = isInput ? inputLayoutMap : outputLayoutMap; | |||
const int totalInChannels = processor.getTotalNumInputChannels(); | |||
const int totalOutChannels = processor.getTotalNumOutputChannels(); | |||
layoutMapPtrStorage.calloc (static_cast<size_t> (numBuses)); | |||
layoutMapStorage.calloc (static_cast<size_t> (isInput ? totalInChannels : totalOutChannels)); | |||
layoutMap = layoutMapPtrStorage. get(); | |||
int ch = 0; | |||
for (int busIdx = 0; busIdx < numBuses; ++busIdx) | |||
{ | |||
layoutMap[busIdx] = layoutMapStorage.get() + ch; | |||
ch += processor.getChannelCountOfBus (isInput, busIdx); | |||
} | |||
} | |||
void fillLayoutChannelMaps (bool isInput, int busNr) | |||
{ | |||
int* layoutMap = (isInput ? inputLayoutMap : outputLayoutMap)[busNr]; | |||
auto channelFormat = processor.getChannelLayoutOfBus (isInput, busNr); | |||
AudioChannelLayout coreAudioLayout; | |||
zerostruct (coreAudioLayout); | |||
coreAudioLayout.mChannelLayoutTag = CoreAudioLayouts::toCoreAudio (channelFormat); | |||
const int numChannels = channelFormat.size(); | |||
auto coreAudioChannels = CoreAudioLayouts::getCoreAudioLayoutChannels (coreAudioLayout); | |||
for (int i = 0; i < numChannels; ++i) | |||
layoutMap[i] = coreAudioChannels.indexOf (channelFormat.getTypeOfChannel (i)); | |||
} | |||
}; | |||
//============================================================================== | |||
class CoreAudioBufferList | |||
{ | |||
public: | |||
CoreAudioBufferList() { reset(); } | |||
//============================================================================== | |||
void prepare (int inChannels, int outChannels, int maxFrames) | |||
{ | |||
const int numChannels = jmax (inChannels, outChannels); | |||
scratch.setSize (numChannels, maxFrames); | |||
channels.calloc (static_cast<size_t> (numChannels)); | |||
reset(); | |||
} | |||
void release() | |||
{ | |||
scratch.setSize (0, 0); | |||
channels.free(); | |||
} | |||
void reset() noexcept | |||
{ | |||
pushIdx = 0; | |||
popIdx = 0; | |||
zeromem (channels.get(), sizeof(float*) * static_cast<size_t> (scratch.getNumChannels())); | |||
} | |||
//============================================================================== | |||
float* setBuffer (const int idx, float* ptr = nullptr) noexcept | |||
{ | |||
jassert (idx < scratch.getNumChannels()); | |||
return (channels [idx] = uniqueBuffer (idx, ptr)); | |||
} | |||
//============================================================================== | |||
float* push() noexcept | |||
{ | |||
jassert (pushIdx < scratch.getNumChannels()); | |||
return channels [pushIdx++]; | |||
} | |||
void push (AudioBufferList& bufferList, const int* channelMap) noexcept | |||
{ | |||
jassert (pushIdx < scratch.getNumChannels()); | |||
if (bufferList.mNumberBuffers > 0) | |||
{ | |||
const UInt32 n = bufferList.mBuffers [0].mDataByteSize / | |||
(bufferList.mBuffers [0].mNumberChannels * sizeof (float)); | |||
const bool isInterleaved = isAudioBufferInterleaved (bufferList); | |||
const int numChannels = static_cast<int> (isInterleaved ? bufferList.mBuffers [0].mNumberChannels | |||
: bufferList.mNumberBuffers); | |||
for (int ch = 0; ch < numChannels; ++ch) | |||
{ | |||
float* data = push(); | |||
int mappedChannel = channelMap [ch]; | |||
if (isInterleaved || static_cast<float*> (bufferList.mBuffers [mappedChannel].mData) != data) | |||
copyAudioBuffer (bufferList, mappedChannel, n, data); | |||
} | |||
} | |||
} | |||
//============================================================================== | |||
float* pop() noexcept | |||
{ | |||
jassert (popIdx < scratch.getNumChannels()); | |||
return channels[popIdx++]; | |||
} | |||
void pop (AudioBufferList& buffer, const int* channelMap) noexcept | |||
{ | |||
if (buffer.mNumberBuffers > 0) | |||
{ | |||
const UInt32 n = buffer.mBuffers [0].mDataByteSize / (buffer.mBuffers [0].mNumberChannels * sizeof (float)); | |||
const bool isInterleaved = isAudioBufferInterleaved (buffer); | |||
const int numChannels = static_cast<int> (isInterleaved ? buffer.mBuffers [0].mNumberChannels : buffer.mNumberBuffers); | |||
for (int ch = 0; ch < numChannels; ++ch) | |||
{ | |||
int mappedChannel = channelMap [ch]; | |||
float* nextBuffer = pop(); | |||
if (nextBuffer == buffer.mBuffers [mappedChannel].mData && ! isInterleaved) | |||
continue; // no copying necessary | |||
if (buffer.mBuffers [mappedChannel].mData == nullptr && ! isInterleaved) | |||
buffer.mBuffers [mappedChannel].mData = nextBuffer; | |||
else | |||
copyAudioBuffer (nextBuffer, mappedChannel, n, buffer); | |||
} | |||
} | |||
} | |||
//============================================================================== | |||
AudioSampleBuffer& getBuffer (UInt32 frames) noexcept | |||
{ | |||
jassert (pushIdx == scratch.getNumChannels()); | |||
#if JUCE_DEBUG | |||
for (int i = 0; i < pushIdx; ++i) | |||
jassert (channels [i] != nullptr); | |||
#endif | |||
mutableBuffer.setDataToReferTo (channels, pushIdx, static_cast<int> (frames)); | |||
return mutableBuffer; | |||
} | |||
private: | |||
float* uniqueBuffer (int idx, float* buffer) noexcept | |||
{ | |||
if (buffer == nullptr) | |||
return scratch.getWritePointer (idx); | |||
for (int ch = 0; ch < idx; ++ch) | |||
if (buffer == channels[ch]) | |||
return scratch.getWritePointer (idx); | |||
return buffer; | |||
} | |||
//============================================================================== | |||
AudioSampleBuffer scratch; | |||
AudioSampleBuffer mutableBuffer; | |||
HeapBlock<float*> channels; | |||
int pushIdx, popIdx; | |||
}; | |||
static bool isAudioBufferInterleaved (const AudioBufferList& audioBuffer) noexcept | |||
{ | |||
return (audioBuffer.mNumberBuffers == 1 && audioBuffer.mBuffers[0].mNumberChannels > 1); | |||
} | |||
static void clearAudioBuffer (const AudioBufferList& audioBuffer) noexcept | |||
{ | |||
for (unsigned int ch = 0; ch < audioBuffer.mNumberBuffers; ++ch) | |||
zeromem (audioBuffer.mBuffers[ch].mData, audioBuffer.mBuffers[ch].mDataByteSize); | |||
} | |||
static void copyAudioBuffer (const AudioBufferList& audioBuffer, const int channel, const UInt32 size, float* dst) noexcept | |||
{ | |||
if (! isAudioBufferInterleaved (audioBuffer)) | |||
{ | |||
jassert (channel < static_cast<int> (audioBuffer.mNumberBuffers)); | |||
jassert (audioBuffer.mBuffers[channel].mDataByteSize == (size * sizeof (float))); | |||
memcpy (dst, audioBuffer.mBuffers[channel].mData, size * sizeof (float)); | |||
} | |||
else | |||
{ | |||
const int numChannels = static_cast<int> (audioBuffer.mBuffers[0].mNumberChannels); | |||
const UInt32 n = static_cast<UInt32> (numChannels) * size; | |||
const float* src = static_cast<const float*> (audioBuffer.mBuffers[0].mData); | |||
jassert (channel < numChannels); | |||
jassert (audioBuffer.mBuffers[0].mDataByteSize == (n * sizeof (float))); | |||
for (const float* inData = src; inData < (src + n); inData += numChannels) | |||
*dst++ = inData[channel]; | |||
} | |||
} | |||
static void copyAudioBuffer (const float *src, const int channel, const UInt32 size, AudioBufferList& audioBuffer) noexcept | |||
{ | |||
if (! isAudioBufferInterleaved (audioBuffer)) | |||
{ | |||
jassert (channel < static_cast<int> (audioBuffer.mNumberBuffers)); | |||
jassert (audioBuffer.mBuffers[channel].mDataByteSize == (size * sizeof (float))); | |||
memcpy (audioBuffer.mBuffers[channel].mData, src, size * sizeof (float)); | |||
} | |||
else | |||
{ | |||
const int numChannels = static_cast<int> (audioBuffer.mBuffers[0].mNumberChannels); | |||
const UInt32 n = static_cast<UInt32> (numChannels) * size; | |||
float* dst = static_cast<float*> (audioBuffer.mBuffers[0].mData); | |||
jassert (channel < numChannels); | |||
jassert (audioBuffer.mBuffers[0].mDataByteSize == (n * sizeof (float))); | |||
for (float* outData = dst; outData < (dst + n); outData += numChannels) | |||
outData[channel] = *src++; | |||
} | |||
} | |||
template <int numLayouts> | |||
static bool isLayoutSupported (const AudioProcessor& processor, | |||
bool isInput, int busIdx, | |||
int numChannels, | |||
const short (&channelLayoutList) [numLayouts][2], | |||
bool hasLayoutMap = true) | |||
{ | |||
if (const AudioProcessor::Bus* bus = processor.getBus (isInput, busIdx)) | |||
{ | |||
if (! bus->isNumberOfChannelsSupported (numChannels)) | |||
return false; | |||
if (! hasLayoutMap) | |||
return true; | |||
const int numConfigs = sizeof (channelLayoutList) / sizeof (short[2]); | |||
for (int i = 0; i < numConfigs; ++i) | |||
{ | |||
if (channelLayoutList[i][isInput ? 0 : 1] == numChannels) | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
static Array<AUChannelInfo> getAUChannelInfo (const AudioProcessor& processor) | |||
{ | |||
Array<AUChannelInfo> channelInfo; | |||
auto hasMainInputBus = (AudioUnitHelpers::getBusCount (&processor, true) > 0); | |||
auto hasMainOutputBus = (AudioUnitHelpers::getBusCount (&processor, false) > 0); | |||
if ((! hasMainInputBus) && (! hasMainOutputBus)) | |||
{ | |||
// midi effect plug-in: no audio | |||
AUChannelInfo info; | |||
info.inChannels = 0; | |||
info.outChannels = 0; | |||
return { &info, 1 }; | |||
} | |||
auto layout = processor.getBusesLayout(); | |||
auto maxNumChanToCheckFor = 9; | |||
auto defaultInputs = processor.getChannelCountOfBus (true, 0); | |||
auto defaultOutputs = processor.getChannelCountOfBus (false, 0); | |||
SortedSet<int> supportedChannels; | |||
// add the current configuration | |||
if (defaultInputs != 0 || defaultOutputs != 0) | |||
supportedChannels.add ((defaultInputs << 16) | defaultOutputs); | |||
for (auto inChanNum = hasMainInputBus ? 1 : 0; inChanNum <= (hasMainInputBus ? maxNumChanToCheckFor : 0); ++inChanNum) | |||
{ | |||
auto inLayout = layout; | |||
if (auto* inBus = processor.getBus (true, 0)) | |||
if (! isNumberOfChannelsSupported (inBus, inChanNum, inLayout)) | |||
continue; | |||
for (auto outChanNum = hasMainOutputBus ? 1 : 0; outChanNum <= (hasMainOutputBus ? maxNumChanToCheckFor : 0); ++outChanNum) | |||
{ | |||
auto outLayout = inLayout; | |||
if (auto* outBus = processor.getBus (false, 0)) | |||
if (! isNumberOfChannelsSupported (outBus, outChanNum, outLayout)) | |||
continue; | |||
supportedChannels.add (((hasMainInputBus ? outLayout.getMainInputChannels() : 0) << 16) | |||
| (hasMainOutputBus ? outLayout.getMainOutputChannels() : 0)); | |||
} | |||
} | |||
auto hasInOutMismatch = false; | |||
for (auto supported : supportedChannels) | |||
{ | |||
auto numInputs = (supported >> 16) & 0xffff; | |||
auto numOutputs = (supported >> 0) & 0xffff; | |||
if (numInputs != numOutputs) | |||
{ | |||
hasInOutMismatch = true; | |||
break; | |||
} | |||
} | |||
auto hasUnsupportedInput = ! hasMainInputBus, hasUnsupportedOutput = ! hasMainOutputBus; | |||
for (auto inChanNum = hasMainInputBus ? 1 : 0; inChanNum <= (hasMainInputBus ? maxNumChanToCheckFor : 0); ++inChanNum) | |||
{ | |||
auto channelConfiguration = (inChanNum << 16) | (hasInOutMismatch ? defaultOutputs : inChanNum); | |||
if (! supportedChannels.contains (channelConfiguration)) | |||
{ | |||
hasUnsupportedInput = true; | |||
break; | |||
} | |||
} | |||
for (auto outChanNum = hasMainOutputBus ? 1 : 0; outChanNum <= (hasMainOutputBus ? maxNumChanToCheckFor : 0); ++outChanNum) | |||
{ | |||
auto channelConfiguration = ((hasInOutMismatch ? defaultInputs : outChanNum) << 16) | outChanNum; | |||
if (! supportedChannels.contains (channelConfiguration)) | |||
{ | |||
hasUnsupportedOutput = true; | |||
break; | |||
} | |||
} | |||
for (auto supported : supportedChannels) | |||
{ | |||
auto numInputs = (supported >> 16) & 0xffff; | |||
auto numOutputs = (supported >> 0) & 0xffff; | |||
AUChannelInfo info; | |||
// see here: https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html | |||
info.inChannels = static_cast<SInt16> (hasMainInputBus ? (hasUnsupportedInput ? numInputs : (hasInOutMismatch && (! hasUnsupportedOutput) ? -2 : -1)) : 0); | |||
info.outChannels = static_cast<SInt16> (hasMainOutputBus ? (hasUnsupportedOutput ? numOutputs : (hasInOutMismatch && (! hasUnsupportedInput) ? -2 : -1)) : 0); | |||
if (info.inChannels == -2 && info.outChannels == -2) | |||
info.inChannels = -1; | |||
int j; | |||
for (j = 0; j < channelInfo.size(); ++j) | |||
if (info.inChannels == channelInfo.getReference (j).inChannels | |||
&& info.outChannels == channelInfo.getReference (j).outChannels) | |||
break; | |||
if (j >= channelInfo.size()) | |||
channelInfo.add (info); | |||
} | |||
return channelInfo; | |||
} | |||
static bool isNumberOfChannelsSupported (const AudioProcessor::Bus* b, int numChannels, AudioProcessor::BusesLayout& inOutCurrentLayout) | |||
{ | |||
auto potentialSets = AudioChannelSet::channelSetsWithNumberOfChannels (static_cast<int> (numChannels)); | |||
for (auto set : potentialSets) | |||
{ | |||
auto copy = inOutCurrentLayout; | |||
if (b->isLayoutSupported (set, ©)) | |||
{ | |||
inOutCurrentLayout = copy; | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
//============================================================================== | |||
static int getBusCount (const AudioProcessor* juceFilter, bool isInput) | |||
{ | |||
int busCount = juceFilter->getBusCount (isInput); | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
short configs[][2] = {JucePlugin_PreferredChannelConfigurations}; | |||
const int numConfigs = sizeof (configs) / sizeof (short[2]); | |||
bool hasOnlyZeroChannels = true; | |||
for (int i = 0; i < numConfigs && hasOnlyZeroChannels == true; ++i) | |||
if (configs[i][isInput ? 0 : 1] != 0) | |||
hasOnlyZeroChannels = false; | |||
busCount = jmin (busCount, hasOnlyZeroChannels ? 0 : 1); | |||
#endif | |||
return busCount; | |||
} | |||
static bool setBusesLayout (AudioProcessor* juceFilter, const AudioProcessor::BusesLayout& requestedLayouts) | |||
{ | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
AudioProcessor::BusesLayout copy (requestedLayouts); | |||
for (int dir = 0; dir < 2; ++dir) | |||
{ | |||
const bool isInput = (dir == 0); | |||
const int actualBuses = juceFilter->getBusCount (isInput); | |||
const int auNumBuses = getBusCount (juceFilter, isInput); | |||
Array<AudioChannelSet>& buses = (isInput ? copy.inputBuses : copy.outputBuses); | |||
for (int i = auNumBuses; i < actualBuses; ++i) | |||
buses.add (AudioChannelSet::disabled()); | |||
} | |||
return juceFilter->setBusesLayout (copy); | |||
#else | |||
return juceFilter->setBusesLayout (requestedLayouts); | |||
#endif | |||
} | |||
static AudioProcessor::BusesLayout getBusesLayout (const AudioProcessor* juceFilter) | |||
{ | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
AudioProcessor::BusesLayout layout = juceFilter->getBusesLayout(); | |||
for (int dir = 0; dir < 2; ++dir) | |||
{ | |||
const bool isInput = (dir == 0); | |||
const int actualBuses = juceFilter->getBusCount (isInput); | |||
const int auNumBuses = getBusCount (juceFilter, isInput); | |||
auto& buses = (isInput ? layout.inputBuses : layout.outputBuses); | |||
for (int i = auNumBuses; i < actualBuses; ++i) | |||
buses.removeLast(); | |||
} | |||
return layout; | |||
#else | |||
return juceFilter->getBusesLayout(); | |||
#endif | |||
} | |||
}; | |||
} // namespace juce |
@@ -1,79 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if (JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS)) || 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; | |||
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; | |||
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; | |||
bool pluginNeedsRescanning (const PluginDescription&) override; | |||
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive, bool) override; | |||
bool doesPluginStillExist (const PluginDescription&) override; | |||
FileSearchPath getDefaultLocationsToSearch() override; | |||
bool canScanForPlugins() const override { return true; } | |||
private: | |||
//============================================================================== | |||
void createPluginInstance (const PluginDescription&, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
void* userData, | |||
void (*callback) (void*, AudioPluginInstance*, const String&)) override; | |||
bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginFormat) | |||
}; | |||
#endif | |||
//============================================================================== | |||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 | |||
enum | |||
{ | |||
/** Custom AudioUnit property used to indicate MPE support */ | |||
kAudioUnitProperty_SupportsMPE = 58 | |||
}; | |||
#endif | |||
} |
@@ -1,718 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX | |||
#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.lastInfoUpdateTime = Time::getCurrentTime(); | |||
desc.pluginFormatName = "LADSPA"; | |||
desc.category = getCategory(); | |||
desc.manufacturerName = plugin != nullptr ? String (plugin->Maker) : String(); | |||
desc.version = getVersion(); | |||
desc.numInputChannels = getTotalNumInputChannels(); | |||
desc.numOutputChannels = getTotalNumOutputChannels(); | |||
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; } | |||
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 = getTotalNumInputChannels(), e = getTotalNumOutputChannels(); i < e; ++i) | |||
buffer.clear (i, 0, numSamples); | |||
} | |||
bool isInputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getTotalNumInputChannels()); } | |||
bool isOutputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getTotalNumOutputChannels()); } | |||
const String getInputChannelName (const int index) const | |||
{ | |||
if (isPositiveAndBelow (index, getTotalNumInputChannels())) | |||
return String (plugin->PortNames [inputs [index]]).trim(); | |||
return {}; | |||
} | |||
const String getOutputChannelName (const int index) const | |||
{ | |||
if (isPositiveAndBelow (index, getTotalNumInputChannels())) | |||
return String (plugin->PortNames [outputs [index]]).trim(); | |||
return {}; | |||
} | |||
//============================================================================== | |||
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 {}; | |||
} | |||
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 {}; | |||
} | |||
//============================================================================== | |||
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 {}; | |||
} | |||
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; | |||
} | |||
} | |||
} | |||
} | |||
void LADSPAPluginFormat::createPluginInstance (const PluginDescription& desc, | |||
double sampleRate, int blockSize, | |||
void* userData, | |||
void (*callback) (void*, AudioPluginInstance*, const String&)) | |||
{ | |||
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(); | |||
} | |||
String errorMsg; | |||
if (result == nullptr) | |||
errorMsg = String (NEEDS_TRANS ("Unable to load XXX plug-in file")).replace ("XXX", "LADSPA"); | |||
callback (userData, result.release(), errorMsg); | |||
} | |||
bool LADSPAPluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept | |||
{ | |||
return false; | |||
} | |||
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, bool) | |||
{ | |||
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 (":", ";")); | |||
} | |||
} // namespace juce | |||
#endif |
@@ -1,70 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if (JUCE_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; | |||
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; | |||
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; | |||
bool pluginNeedsRescanning (const PluginDescription&) override; | |||
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive, bool) override; | |||
bool doesPluginStillExist (const PluginDescription&) override; | |||
FileSearchPath getDefaultLocationsToSearch() override; | |||
bool canScanForPlugins() const override { return true; } | |||
private: | |||
//============================================================================== | |||
void createPluginInstance (const PluginDescription&, double initialSampleRate, | |||
int initialBufferSize, void* userData, | |||
void (*callback) (void*, AudioPluginInstance*, const String&)) override; | |||
bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override; | |||
private: | |||
void recursiveFileSearch (StringArray&, const File&, bool recursive); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginFormat) | |||
}; | |||
#endif | |||
} |
@@ -1,633 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
#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; \ | |||
} | |||
//============================================================================== | |||
inline juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); } | |||
inline 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 | |||
inline juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); } | |||
inline juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); } | |||
inline void toString128 (Steinberg::Vst::String128 result, const char* source) | |||
{ | |||
Steinberg::UString (result, 128).fromAscii (source); | |||
} | |||
inline void toString128 (Steinberg::Vst::String128 result, const juce::String& source) | |||
{ | |||
Steinberg::UString (result, 128).fromAscii (source.toUTF8()); | |||
} | |||
inline 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 inline 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 inline 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(); | |||
} | |||
static inline Steinberg::Vst::Speaker getSpeakerType (const AudioChannelSet& set, AudioChannelSet::ChannelType type) noexcept | |||
{ | |||
using namespace Steinberg::Vst; | |||
switch (type) | |||
{ | |||
case AudioChannelSet::left: return kSpeakerL; | |||
case AudioChannelSet::right: return kSpeakerR; | |||
case AudioChannelSet::centre: return (set == AudioChannelSet::mono() ? kSpeakerM : kSpeakerC); | |||
case AudioChannelSet::LFE: return kSpeakerLfe; | |||
case AudioChannelSet::leftSurround: return kSpeakerLs; | |||
case AudioChannelSet::rightSurround: return kSpeakerRs; | |||
case AudioChannelSet::leftCentre: return kSpeakerLc; | |||
case AudioChannelSet::rightCentre: return kSpeakerRc; | |||
case AudioChannelSet::centreSurround: return kSpeakerCs; | |||
case AudioChannelSet::leftSurroundSide: return (1 << 26); /* kSpeakerLcs */ | |||
case AudioChannelSet::rightSurroundSide: return (1 << 27); /* kSpeakerRcs */ | |||
case AudioChannelSet::topMiddle: return (1 << 11); /* kSpeakerTm */ | |||
case AudioChannelSet::topFrontLeft: return kSpeakerTfl; | |||
case AudioChannelSet::topFrontCentre: return kSpeakerTfc; | |||
case AudioChannelSet::topFrontRight: return kSpeakerTfr; | |||
case AudioChannelSet::topRearLeft: return kSpeakerTrl; | |||
case AudioChannelSet::topRearCentre: return kSpeakerTrc; | |||
case AudioChannelSet::topRearRight: return kSpeakerTrr; | |||
case AudioChannelSet::LFE2: return kSpeakerLfe2; | |||
case AudioChannelSet::leftSurroundRear: return kSpeakerSl; | |||
case AudioChannelSet::rightSurroundRear: return kSpeakerSr; | |||
case AudioChannelSet::wideLeft: return kSpeakerPl; | |||
case AudioChannelSet::wideRight: return kSpeakerPr; | |||
case AudioChannelSet::ambisonicW: return (1 << 20); /* kSpeakerW */ | |||
case AudioChannelSet::ambisonicX: return (1 << 21); /* kSpeakerX */ | |||
case AudioChannelSet::ambisonicY: return (1 << 22); /* kSpeakerY */ | |||
case AudioChannelSet::ambisonicZ: return (1 << 23); /* kSpeakerZ */ | |||
case AudioChannelSet::topSideLeft: return (1 << 24); /* kSpeakerTsl */ | |||
case AudioChannelSet::topSideRight: return (1 << 25); /* kSpeakerTsr */ | |||
case AudioChannelSet::discreteChannel0: return kSpeakerM; | |||
default: | |||
break; | |||
} | |||
switch (static_cast<int> (type)) | |||
{ | |||
case (int) AudioChannelSet::discreteChannel0 + 3: return (1 << 28); /* kSpeakerBfl */ | |||
case (int) AudioChannelSet::discreteChannel0 + 4: return (1 << 29); /* kSpeakerBfc */ | |||
case (int) AudioChannelSet::discreteChannel0 + 5: return (1 << 30); /* kSpeakerBfr */ | |||
default: | |||
break; | |||
} | |||
auto channelIndex = static_cast<Steinberg::Vst::Speaker> (type) - (static_cast<Steinberg::Vst::Speaker> (AudioChannelSet::discreteChannel0) + 6ull); | |||
return (1ull << (channelIndex + 33ull /* last speaker in vst layout + 1 */)); | |||
} | |||
static inline AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::SpeakerArrangement arr, Steinberg::Vst::Speaker type) noexcept | |||
{ | |||
using namespace Steinberg::Vst; | |||
switch (type) | |||
{ | |||
case kSpeakerL: return AudioChannelSet::left; | |||
case kSpeakerR: return AudioChannelSet::right; | |||
case kSpeakerC: return AudioChannelSet::centre; | |||
case kSpeakerLfe: return AudioChannelSet::LFE; | |||
case kSpeakerLs: return AudioChannelSet::leftSurround; | |||
case kSpeakerRs: return AudioChannelSet::rightSurround; | |||
case kSpeakerLc: return AudioChannelSet::leftCentre; | |||
case kSpeakerRc: return AudioChannelSet::rightCentre; | |||
case kSpeakerCs: return AudioChannelSet::centreSurround; | |||
case kSpeakerSl: return AudioChannelSet::leftSurroundRear; | |||
case kSpeakerSr: return AudioChannelSet::rightSurroundRear; | |||
case (1 << 11): return AudioChannelSet::topMiddle; /* kSpeakerTm */ | |||
case kSpeakerTfl: return AudioChannelSet::topFrontLeft; | |||
case kSpeakerTfc: return AudioChannelSet::topFrontCentre; | |||
case kSpeakerTfr: return AudioChannelSet::topFrontRight; | |||
case kSpeakerTrl: return AudioChannelSet::topRearLeft; | |||
case kSpeakerTrc: return AudioChannelSet::topRearCentre; | |||
case kSpeakerTrr: return AudioChannelSet::topRearRight; | |||
case kSpeakerLfe2: return AudioChannelSet::LFE2; | |||
case (1 << 19): return ((arr & kSpeakerC) != 0 ? AudioChannelSet::discreteChannel0 : AudioChannelSet::centre); | |||
case (1 << 20): return AudioChannelSet::ambisonicW; /* kSpeakerW */ | |||
case (1 << 21): return AudioChannelSet::ambisonicX; /* kSpeakerX */ | |||
case (1 << 22): return AudioChannelSet::ambisonicY; /* kSpeakerY */ | |||
case (1 << 23): return AudioChannelSet::ambisonicZ; /* kSpeakerZ */ | |||
case (1 << 24): return AudioChannelSet::topSideLeft; /* kSpeakerTsl */ | |||
case (1 << 25): return AudioChannelSet::topSideRight; /* kSpeakerTsr */ | |||
case (1 << 26): return AudioChannelSet::leftSurroundSide; /* kSpeakerLcs */ | |||
case (1 << 27): return AudioChannelSet::rightSurroundSide; /* kSpeakerRcs */ | |||
case (1 << 28): return static_cast<AudioChannelSet::ChannelType> ((int)AudioChannelSet::discreteChannel0 + 3); /* kSpeakerBfl */ | |||
case (1 << 29): return static_cast<AudioChannelSet::ChannelType> ((int)AudioChannelSet::discreteChannel0 + 4); /* kSpeakerBfc */ | |||
case (1 << 30): return static_cast<AudioChannelSet::ChannelType> ((int)AudioChannelSet::discreteChannel0 + 5); /* kSpeakerBfr */ | |||
case kSpeakerPl: return AudioChannelSet::wideLeft; | |||
case kSpeakerPr: return AudioChannelSet::wideRight; | |||
default: break; | |||
} | |||
auto channelType = BigInteger (static_cast<int64> (type)).findNextSetBit (0); | |||
// VST3 <-> JUCE layout conversion error: report this bug to the JUCE forum | |||
jassert (channelType >= 33); | |||
return static_cast<AudioChannelSet::ChannelType> (static_cast<int> (AudioChannelSet::discreteChannel0) + 6 + (channelType - 33)); | |||
} | |||
static inline Steinberg::Vst::SpeakerArrangement getVst3SpeakerArrangement (const AudioChannelSet& channels) noexcept | |||
{ | |||
using namespace Steinberg::Vst::SpeakerArr; | |||
if (channels == AudioChannelSet::disabled()) return kEmpty; | |||
else if (channels == AudioChannelSet::mono()) return kMono; | |||
else if (channels == AudioChannelSet::stereo()) return kStereo; | |||
else if (channels == AudioChannelSet::createLCR()) return k30Cine; | |||
else if (channels == AudioChannelSet::createLRS()) return k30Music; | |||
else if (channels == AudioChannelSet::createLCRS()) return k40Cine; | |||
else if (channels == AudioChannelSet::create5point0()) return k50; | |||
else if (channels == AudioChannelSet::create5point1()) return k51; | |||
else if (channels == AudioChannelSet::create6point0()) return k60Cine; | |||
else if (channels == AudioChannelSet::create6point1()) return k61Cine; | |||
else if (channels == AudioChannelSet::create6point0Music()) return k60Music; | |||
else if (channels == AudioChannelSet::create6point1Music()) return k61Music; | |||
else if (channels == AudioChannelSet::create7point0()) return k70Music; | |||
else if (channels == AudioChannelSet::create7point0SDDS()) return k70Cine; | |||
else if (channels == AudioChannelSet::create7point1()) return k71CineSideFill; | |||
else if (channels == AudioChannelSet::create7point1SDDS()) return k71Cine; | |||
else if (channels == AudioChannelSet::ambisonic()) return kBFormat; | |||
else if (channels == AudioChannelSet::quadraphonic()) return k40Music; | |||
else if (channels == AudioChannelSet::create7point0point2()) return k71_2 & ~(Steinberg::Vst::kSpeakerLfe); | |||
else if (channels == AudioChannelSet::create7point1point2()) return k71_2; | |||
Steinberg::Vst::SpeakerArrangement result = 0; | |||
Array<AudioChannelSet::ChannelType> types (channels.getChannelTypes()); | |||
for (int i = 0; i < types.size(); ++i) | |||
result |= getSpeakerType (channels, types.getReference(i)); | |||
return result; | |||
} | |||
static inline AudioChannelSet getChannelSetForSpeakerArrangement (Steinberg::Vst::SpeakerArrangement arr) noexcept | |||
{ | |||
using namespace Steinberg::Vst::SpeakerArr; | |||
if (arr == kEmpty) return AudioChannelSet::disabled(); | |||
else if (arr == kMono) return AudioChannelSet::mono(); | |||
else if (arr == kStereo) return AudioChannelSet::stereo(); | |||
else if (arr == k30Cine) return AudioChannelSet::createLCR(); | |||
else if (arr == k30Music) return AudioChannelSet::createLRS(); | |||
else if (arr == k40Cine) return AudioChannelSet::createLCRS(); | |||
else if (arr == k50) return AudioChannelSet::create5point0(); | |||
else if (arr == k51) return AudioChannelSet::create5point1(); | |||
else if (arr == k60Cine) return AudioChannelSet::create6point0(); | |||
else if (arr == k61Cine) return AudioChannelSet::create6point1(); | |||
else if (arr == k60Music) return AudioChannelSet::create6point0Music(); | |||
else if (arr == k61Music) return AudioChannelSet::create6point1Music(); | |||
else if (arr == k70Music) return AudioChannelSet::create7point0(); | |||
else if (arr == k70Cine) return AudioChannelSet::create7point0SDDS(); | |||
else if (arr == k71CineSideFill) return AudioChannelSet::create7point1(); | |||
else if (arr == k71Cine) return AudioChannelSet::create7point1SDDS(); | |||
else if (arr == kBFormat) return AudioChannelSet::ambisonic(); | |||
else if (arr == k40Music) return AudioChannelSet::quadraphonic(); | |||
else if (arr == k71_2) return AudioChannelSet::create7point1point2(); | |||
else if (arr == (k71_2 & ~(Steinberg::Vst::kSpeakerLfe))) return AudioChannelSet::create7point0point2(); | |||
AudioChannelSet result; | |||
BigInteger vstChannels (static_cast<int64> (arr)); | |||
for (auto bit = vstChannels.findNextSetBit (0); bit != -1; bit = vstChannels.findNextSetBit (bit + 1)) | |||
{ | |||
AudioChannelSet::ChannelType channelType = getChannelType (arr, 1ull << static_cast<uint64> (bit)); | |||
if (channelType != AudioChannelSet::unknown) | |||
result.addChannel (channelType); | |||
} | |||
// VST3 <-> JUCE layout conversion error: report this bug to the JUCE forum | |||
jassert (result.size() == vstChannels.countNumberOfSetBits()); | |||
return result; | |||
} | |||
//============================================================================== | |||
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, (int) 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 = (uint32) 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) | |||
}; | |||
//============================================================================== | |||
template <typename FloatType> | |||
struct VST3BufferExchange | |||
{ | |||
typedef Array<FloatType*> Bus; | |||
typedef Array<Bus> BusMap; | |||
static inline void assignRawPointer (Steinberg::Vst::AudioBusBuffers& vstBuffers, float** raw) { vstBuffers.channelBuffers32 = raw; } | |||
static inline void assignRawPointer (Steinberg::Vst::AudioBusBuffers& vstBuffers, double** raw) { vstBuffers.channelBuffers64 = raw; } | |||
/** 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 | |||
*/ | |||
static void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers, | |||
Bus& bus, | |||
AudioBuffer<FloatType>& 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)); | |||
assignRawPointer (vstBuffers, (numChannels > 0 ? bus.getRawDataPointer() : nullptr)); | |||
vstBuffers.numChannels = numChannels; | |||
vstBuffers.silenceFlags = 0; | |||
} | |||
static void mapArrangementToBuses (int& channelIndexOffset, int index, | |||
Array<Steinberg::Vst::AudioBusBuffers>& result, | |||
BusMap& busMapToUse, const AudioChannelSet& arrangement, | |||
AudioBuffer<FloatType>& source) | |||
{ | |||
const int numChansForBus = arrangement.size(); | |||
if (index >= result.size()) | |||
result.add (Steinberg::Vst::AudioBusBuffers()); | |||
if (index >= busMapToUse.size()) | |||
busMapToUse.add (Bus()); | |||
associateBufferTo (result.getReference (index), | |||
busMapToUse.getReference (index), | |||
source, numChansForBus, channelIndexOffset); | |||
channelIndexOffset += numChansForBus; | |||
} | |||
static inline void mapBufferToBuses (Array<Steinberg::Vst::AudioBusBuffers>& result, BusMap& busMapToUse, | |||
const Array<AudioChannelSet>& arrangements, | |||
AudioBuffer<FloatType>& source) | |||
{ | |||
int channelIndexOffset = 0; | |||
for (int i = 0; i < arrangements.size(); ++i) | |||
mapArrangementToBuses (channelIndexOffset, i, result, busMapToUse, | |||
arrangements.getUnchecked (i), source); | |||
} | |||
static inline void mapBufferToBuses (Array<Steinberg::Vst::AudioBusBuffers>& result, | |||
Steinberg::Vst::IAudioProcessor& processor, | |||
BusMap& busMapToUse, bool isInput, int numBuses, | |||
AudioBuffer<FloatType>& source) | |||
{ | |||
int channelIndexOffset = 0; | |||
for (int i = 0; i < numBuses; ++i) | |||
mapArrangementToBuses (channelIndexOffset, i, | |||
result, busMapToUse, | |||
getArrangementForBus (&processor, isInput, i), | |||
source); | |||
} | |||
}; | |||
template <typename FloatType> | |||
struct VST3FloatAndDoubleBusMapCompositeHelper {}; | |||
struct VST3FloatAndDoubleBusMapComposite | |||
{ | |||
VST3BufferExchange<float>::BusMap floatVersion; | |||
VST3BufferExchange<double>::BusMap doubleVersion; | |||
template <typename FloatType> | |||
inline typename VST3BufferExchange<FloatType>::BusMap& get() { return VST3FloatAndDoubleBusMapCompositeHelper<FloatType>::get (*this); } | |||
}; | |||
template <> struct VST3FloatAndDoubleBusMapCompositeHelper<float> | |||
{ | |||
static inline VST3BufferExchange<float>::BusMap& get (VST3FloatAndDoubleBusMapComposite& impl) { return impl.floatVersion; } | |||
}; | |||
template <> struct VST3FloatAndDoubleBusMapCompositeHelper<double> | |||
{ | |||
static inline VST3BufferExchange<double>::BusMap& get (VST3FloatAndDoubleBusMapComposite& impl) { return impl.doubleVersion; } | |||
}; | |||
} // namespace juce |
@@ -1,186 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
// 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" | |||
#pragma clang diagnostic ignored "-Wunused-function" | |||
#pragma clang diagnostic ignored "-Wsign-conversion" | |||
#pragma clang diagnostic ignored "-Wsign-compare" | |||
#pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor" | |||
#pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||
#pragma clang diagnostic ignored "-Wextra-semi" | |||
#endif | |||
#undef DEVELOPMENT | |||
#define DEVELOPMENT 0 // This avoids a Clang warning in Steinberg code about unused values | |||
/* 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 Projucer 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/gui/iplugviewcontentscalesupport.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 <pluginterfaces/vst/ivstmidicontrollers.h> | |||
#include <pluginterfaces/vst/ivstchannelcontextinfo.h> | |||
#include <public.sdk/source/common/memorystream.h> | |||
#include <public.sdk/source/vst/vsteditcontroller.h> | |||
#else | |||
#if JUCE_MINGW | |||
#define _set_abort_behavior(...) | |||
#endif | |||
#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/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 <pluginterfaces/gui/iplugviewcontentscalesupport.h> | |||
#include <pluginterfaces/vst/ivstmidicontrollers.h> | |||
#include <pluginterfaces/vst/ivstchannelcontextinfo.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 (IPluginFactory) | |||
DEF_CLASS_IID (IPluginFactory2) | |||
DEF_CLASS_IID (IPluginFactory3) | |||
DEF_CLASS_IID (IPlugViewContentScaleSupport) | |||
} | |||
#endif //JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY | |||
#if _MSC_VER | |||
#pragma warning (pop) | |||
#elif __clang__ | |||
#pragma clang diagnostic pop | |||
#endif | |||
#if JUCE_WINDOWS | |||
#include <windows.h> | |||
#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 |
@@ -1,71 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if (JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS)) || DOXYGEN | |||
/** | |||
Implements a plugin format for VST3s. | |||
*/ | |||
class JUCE_API VST3PluginFormat : public AudioPluginFormat | |||
{ | |||
public: | |||
/** Constructor */ | |||
VST3PluginFormat(); | |||
/** Destructor */ | |||
~VST3PluginFormat(); | |||
//============================================================================== | |||
String getName() const override { return "VST3"; } | |||
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) 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) override; | |||
bool doesPluginStillExist (const PluginDescription&) override; | |||
FileSearchPath getDefaultLocationsToSearch() override; | |||
bool canScanForPlugins() const override { return true; } | |||
private: | |||
void createPluginInstance (const PluginDescription&, double initialSampleRate, | |||
int initialBufferSize, void* userData, | |||
void (*callback) (void*, AudioPluginInstance*, const String&)) override; | |||
bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override; | |||
private: | |||
//============================================================================== | |||
void recursiveFileSearch (StringArray&, const File&, bool recursive); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginFormat) | |||
}; | |||
#endif // JUCE_PLUGINHOST_VST3 | |||
} // namespace juce |
@@ -1,298 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
struct SpeakerMappings : private AudioChannelSet // (inheritance only to give easier access to items in the namespace) | |||
{ | |||
struct Mapping | |||
{ | |||
int32 vst2; | |||
ChannelType channels[13]; | |||
bool matches (const Array<ChannelType>& chans) const noexcept | |||
{ | |||
const int n = sizeof (channels) / sizeof (ChannelType); | |||
for (int i = 0; i < n; ++i) | |||
{ | |||
if (channels[i] == unknown) return (i == chans.size()); | |||
if (i == chans.size()) return (channels[i] == unknown); | |||
if (channels[i] != chans.getUnchecked(i)) | |||
return false; | |||
} | |||
return true; | |||
} | |||
}; | |||
static AudioChannelSet vstArrangementTypeToChannelSet (int32 arr, int fallbackNumChannels) | |||
{ | |||
if (arr == vstSpeakerConfigTypeEmpty) return AudioChannelSet::disabled(); | |||
else if (arr == vstSpeakerConfigTypeMono) return AudioChannelSet::mono(); | |||
else if (arr == vstSpeakerConfigTypeLR) return AudioChannelSet::stereo(); | |||
else if (arr == vstSpeakerConfigTypeLRC) return AudioChannelSet::createLCR(); | |||
else if (arr == vstSpeakerConfigTypeLRS) return AudioChannelSet::createLRS(); | |||
else if (arr == vstSpeakerConfigTypeLRCS) return AudioChannelSet::createLCRS(); | |||
else if (arr == vstSpeakerConfigTypeLRCLsRs) return AudioChannelSet::create5point0(); | |||
else if (arr == vstSpeakerConfigTypeLRCLfeLsRs) return AudioChannelSet::create5point1(); | |||
else if (arr == vstSpeakerConfigTypeLRCLsRsCs) return AudioChannelSet::create6point0(); | |||
else if (arr == vstSpeakerConfigTypeLRCLfeLsRsCs) return AudioChannelSet::create6point1(); | |||
else if (arr == vstSpeakerConfigTypeLRLsRsSlSr) return AudioChannelSet::create6point0Music(); | |||
else if (arr == vstSpeakerConfigTypeLRLfeLsRsSlSr) return AudioChannelSet::create6point1Music(); | |||
else if (arr == vstSpeakerConfigTypeLRCLsRsSlSr) return AudioChannelSet::create7point0(); | |||
else if (arr == vstSpeakerConfigTypeLRCLsRsLcRc) return AudioChannelSet::create7point0SDDS(); | |||
else if (arr == vstSpeakerConfigTypeLRCLfeLsRsSlSr) return AudioChannelSet::create7point1(); | |||
else if (arr == vstSpeakerConfigTypeLRCLfeLsRsLcRc) return AudioChannelSet::create7point1SDDS(); | |||
else if (arr == vstSpeakerConfigTypeLRLsRs) return AudioChannelSet::quadraphonic(); | |||
for (const Mapping* m = getMappings(); m->vst2 != vstSpeakerConfigTypeEmpty; ++m) | |||
{ | |||
if (m->vst2 == arr) | |||
{ | |||
AudioChannelSet s; | |||
for (int i = 0; m->channels[i] != 0; ++i) | |||
s.addChannel (m->channels[i]); | |||
return s; | |||
} | |||
} | |||
return AudioChannelSet::discreteChannels (fallbackNumChannels); | |||
} | |||
static AudioChannelSet vstArrangementTypeToChannelSet (const VstSpeakerConfiguration& arr) | |||
{ | |||
return vstArrangementTypeToChannelSet (arr.type, arr.numberOfChannels); | |||
} | |||
static int32 channelSetToVstArrangementType (AudioChannelSet channels) | |||
{ | |||
if (channels == AudioChannelSet::disabled()) return vstSpeakerConfigTypeEmpty; | |||
else if (channels == AudioChannelSet::mono()) return vstSpeakerConfigTypeMono; | |||
else if (channels == AudioChannelSet::stereo()) return vstSpeakerConfigTypeLR; | |||
else if (channels == AudioChannelSet::createLCR()) return vstSpeakerConfigTypeLRC; | |||
else if (channels == AudioChannelSet::createLRS()) return vstSpeakerConfigTypeLRS; | |||
else if (channels == AudioChannelSet::createLCRS()) return vstSpeakerConfigTypeLRCS; | |||
else if (channels == AudioChannelSet::create5point0()) return vstSpeakerConfigTypeLRCLsRs; | |||
else if (channels == AudioChannelSet::create5point1()) return vstSpeakerConfigTypeLRCLfeLsRs; | |||
else if (channels == AudioChannelSet::create6point0()) return vstSpeakerConfigTypeLRCLsRsCs; | |||
else if (channels == AudioChannelSet::create6point1()) return vstSpeakerConfigTypeLRCLfeLsRsCs; | |||
else if (channels == AudioChannelSet::create6point0Music()) return vstSpeakerConfigTypeLRLsRsSlSr; | |||
else if (channels == AudioChannelSet::create6point1Music()) return vstSpeakerConfigTypeLRLfeLsRsSlSr; | |||
else if (channels == AudioChannelSet::create7point0()) return vstSpeakerConfigTypeLRCLsRsSlSr; | |||
else if (channels == AudioChannelSet::create7point0SDDS()) return vstSpeakerConfigTypeLRCLsRsLcRc; | |||
else if (channels == AudioChannelSet::create7point1()) return vstSpeakerConfigTypeLRCLfeLsRsSlSr; | |||
else if (channels == AudioChannelSet::create7point1SDDS()) return vstSpeakerConfigTypeLRCLfeLsRsLcRc; | |||
else if (channels == AudioChannelSet::quadraphonic()) return vstSpeakerConfigTypeLRLsRs; | |||
Array<AudioChannelSet::ChannelType> chans (channels.getChannelTypes()); | |||
if (channels == AudioChannelSet::disabled()) | |||
return vstSpeakerConfigTypeEmpty; | |||
for (const Mapping* m = getMappings(); m->vst2 != vstSpeakerConfigTypeEmpty; ++m) | |||
if (m->matches (chans)) | |||
return m->vst2; | |||
return vstSpeakerConfigTypeUser; | |||
} | |||
class VstSpeakerConfigurationHolder | |||
{ | |||
public: | |||
VstSpeakerConfigurationHolder () { clear(); } | |||
VstSpeakerConfigurationHolder (const VstSpeakerConfiguration& vstConfig) { operator= (vstConfig); } | |||
VstSpeakerConfigurationHolder (const VstSpeakerConfigurationHolder& other) { operator= (other.get()); } | |||
VstSpeakerConfigurationHolder (VstSpeakerConfigurationHolder&& other) : storage (static_cast<HeapBlock<VstSpeakerConfiguration>&&> (other.storage)) { other.clear(); } | |||
VstSpeakerConfigurationHolder (const AudioChannelSet& channels) | |||
{ | |||
auto numberOfChannels = channels.size(); | |||
VstSpeakerConfiguration& dst = *allocate (numberOfChannels); | |||
dst.type = channelSetToVstArrangementType (channels); | |||
dst.numberOfChannels = numberOfChannels; | |||
for (int i = 0; i < dst.numberOfChannels; ++i) | |||
{ | |||
VstIndividualSpeakerInfo& speaker = dst.speakers[i]; | |||
zeromem (&speaker, sizeof (VstIndividualSpeakerInfo)); | |||
speaker.type = getSpeakerType (channels.getTypeOfChannel (i)); | |||
} | |||
} | |||
VstSpeakerConfigurationHolder& operator= (const VstSpeakerConfigurationHolder& vstConfig) { return operator=(vstConfig.get()); } | |||
VstSpeakerConfigurationHolder& operator= (const VstSpeakerConfiguration& vstConfig) | |||
{ | |||
VstSpeakerConfiguration& dst = *allocate (vstConfig.numberOfChannels); | |||
dst.type = vstConfig.type; | |||
dst.numberOfChannels = vstConfig.numberOfChannels; | |||
for (int i = 0; i < dst.numberOfChannels; ++i) | |||
dst.speakers[i] = vstConfig.speakers[i]; | |||
return *this; | |||
} | |||
VstSpeakerConfigurationHolder& operator= (VstSpeakerConfigurationHolder && vstConfig) | |||
{ | |||
storage = static_cast<HeapBlock<VstSpeakerConfiguration>&&> (vstConfig.storage); | |||
vstConfig.clear(); | |||
return *this; | |||
} | |||
const VstSpeakerConfiguration& get() const { return *storage.get(); } | |||
private: | |||
JUCE_LEAK_DETECTOR (VstSpeakerConfigurationHolder) | |||
HeapBlock<VstSpeakerConfiguration> storage; | |||
VstSpeakerConfiguration* allocate (int numChannels) | |||
{ | |||
auto arrangementSize = sizeof (VstSpeakerConfiguration) | |||
+ sizeof (VstIndividualSpeakerInfo) * static_cast<size_t> (jmax (8, numChannels) - 8); | |||
storage.malloc (1, arrangementSize); | |||
return storage.get(); | |||
} | |||
void clear() | |||
{ | |||
VstSpeakerConfiguration& dst = *allocate (0); | |||
dst.type = vstSpeakerConfigTypeEmpty; | |||
dst.numberOfChannels = 0; | |||
} | |||
}; | |||
static const Mapping* getMappings() noexcept | |||
{ | |||
static const Mapping mappings[] = | |||
{ | |||
{ vstSpeakerConfigTypeMono, { centre, unknown } }, | |||
{ vstSpeakerConfigTypeLR, { left, right, unknown } }, | |||
{ vstSpeakerConfigTypeLsRs, { leftSurround, rightSurround, unknown } }, | |||
{ vstSpeakerConfigTypeLcRc, { leftCentre, rightCentre, unknown } }, | |||
{ vstSpeakerConfigTypeSlSr, { leftSurroundRear, rightSurroundRear, unknown } }, | |||
{ vstSpeakerConfigTypeCLfe, { centre, LFE, unknown } }, | |||
{ vstSpeakerConfigTypeLRC, { left, right, centre, unknown } }, | |||
{ vstSpeakerConfigTypeLRS, { left, right, surround, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLfe, { left, right, centre, LFE, unknown } }, | |||
{ vstSpeakerConfigTypeLRLfeS, { left, right, LFE, surround, unknown } }, | |||
{ vstSpeakerConfigTypeLRCS, { left, right, centre, surround, unknown } }, | |||
{ vstSpeakerConfigTypeLRLsRs, { left, right, leftSurround, rightSurround, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLfeS, { left, right, centre, LFE, surround, unknown } }, | |||
{ vstSpeakerConfigTypeLRLfeLsRs, { left, right, LFE, leftSurround, rightSurround, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLsRs, { left, right, centre, leftSurround, rightSurround, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLfeLsRs, { left, right, centre, LFE, leftSurround, rightSurround, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLsRsCs, { left, right, centre, leftSurround, rightSurround, surround, unknown } }, | |||
{ vstSpeakerConfigTypeLRLsRsSlSr, { left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLfeLsRsCs, { left, right, centre, LFE, leftSurround, rightSurround, surround, unknown } }, | |||
{ vstSpeakerConfigTypeLRLfeLsRsSlSr, { left, right, LFE, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLsRsLcRc, { left, right, centre, leftSurround, rightSurround, topFrontLeft, topFrontRight, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLsRsSlSr, { left, right, centre, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLfeLsRsLcRc, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLfeLsRsSlSr, { left, right, centre, LFE, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLsRsLcRcCs, { left, right, centre, leftSurround, rightSurround, topFrontLeft, topFrontRight, surround, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLsRsCsSlSr, { left, right, centre, leftSurround, rightSurround, surround, leftSurroundRear, rightSurroundRear, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLfeLsRsLcRcCs, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, surround, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLfeLsRsCsSlSr, { left, right, centre, LFE, leftSurround, rightSurround, surround, leftSurroundRear, rightSurroundRear, unknown } }, | |||
{ vstSpeakerConfigTypeLRCLfeLsRsTflTfcTfrTrlTrrLfe2, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontCentre, topFrontRight, topRearLeft, topRearRight, LFE2, unknown } }, | |||
{ vstSpeakerConfigTypeEmpty, { unknown } } | |||
}; | |||
return mappings; | |||
} | |||
static inline int32 getSpeakerType (AudioChannelSet::ChannelType type) noexcept | |||
{ | |||
switch (type) | |||
{ | |||
case AudioChannelSet::left: return vstIndividualSpeakerTypeLeft; | |||
case AudioChannelSet::right: return vstIndividualSpeakerTypeRight; | |||
case AudioChannelSet::centre: return vstIndividualSpeakerTypeCentre; | |||
case AudioChannelSet::LFE: return vstIndividualSpeakerTypeLFE; | |||
case AudioChannelSet::leftSurround: return vstIndividualSpeakerTypeLeftSurround; | |||
case AudioChannelSet::rightSurround: return vstIndividualSpeakerTypeRightSurround; | |||
case AudioChannelSet::leftCentre: return vstIndividualSpeakerTypeLeftCentre; | |||
case AudioChannelSet::rightCentre: return vstIndividualSpeakerTypeRightCentre; | |||
case AudioChannelSet::surround: return vstIndividualSpeakerTypeSurround; | |||
case AudioChannelSet::leftSurroundRear: return vstIndividualSpeakerTypeLeftRearSurround; | |||
case AudioChannelSet::rightSurroundRear: return vstIndividualSpeakerTypeRightRearSurround; | |||
case AudioChannelSet::topMiddle: return vstIndividualSpeakerTypeTopMiddle; | |||
case AudioChannelSet::topFrontLeft: return vstIndividualSpeakerTypeTopFrontLeft; | |||
case AudioChannelSet::topFrontCentre: return vstIndividualSpeakerTypeTopFrontCentre; | |||
case AudioChannelSet::topFrontRight: return vstIndividualSpeakerTypeTopFrontRight; | |||
case AudioChannelSet::topRearLeft: return vstIndividualSpeakerTypeTopRearLeft; | |||
case AudioChannelSet::topRearCentre: return vstIndividualSpeakerTypeTopRearCentre; | |||
case AudioChannelSet::topRearRight: return vstIndividualSpeakerTypeTopRearRight; | |||
case AudioChannelSet::LFE2: return vstIndividualSpeakerTypeLFE2; | |||
default: break; | |||
} | |||
return 0; | |||
} | |||
static inline AudioChannelSet::ChannelType getChannelType (int32 type) noexcept | |||
{ | |||
switch (type) | |||
{ | |||
case vstIndividualSpeakerTypeLeft: return AudioChannelSet::left; | |||
case vstIndividualSpeakerTypeRight: return AudioChannelSet::right; | |||
case vstIndividualSpeakerTypeCentre: return AudioChannelSet::centre; | |||
case vstIndividualSpeakerTypeLFE: return AudioChannelSet::LFE; | |||
case vstIndividualSpeakerTypeLeftSurround: return AudioChannelSet::leftSurround; | |||
case vstIndividualSpeakerTypeRightSurround: return AudioChannelSet::rightSurround; | |||
case vstIndividualSpeakerTypeLeftCentre: return AudioChannelSet::leftCentre; | |||
case vstIndividualSpeakerTypeRightCentre: return AudioChannelSet::rightCentre; | |||
case vstIndividualSpeakerTypeSurround: return AudioChannelSet::surround; | |||
case vstIndividualSpeakerTypeLeftRearSurround: return AudioChannelSet::leftSurroundRear; | |||
case vstIndividualSpeakerTypeRightRearSurround: return AudioChannelSet::rightSurroundRear; | |||
case vstIndividualSpeakerTypeTopMiddle: return AudioChannelSet::topMiddle; | |||
case vstIndividualSpeakerTypeTopFrontLeft: return AudioChannelSet::topFrontLeft; | |||
case vstIndividualSpeakerTypeTopFrontCentre: return AudioChannelSet::topFrontCentre; | |||
case vstIndividualSpeakerTypeTopFrontRight: return AudioChannelSet::topFrontRight; | |||
case vstIndividualSpeakerTypeTopRearLeft: return AudioChannelSet::topRearLeft; | |||
case vstIndividualSpeakerTypeTopRearCentre: return AudioChannelSet::topRearCentre; | |||
case vstIndividualSpeakerTypeTopRearRight: return AudioChannelSet::topRearRight; | |||
case vstIndividualSpeakerTypeLFE2: return AudioChannelSet::LFE2; | |||
default: break; | |||
} | |||
return AudioChannelSet::unknown; | |||
} | |||
}; | |||
} // namespace juce |
@@ -1,485 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
#define JUCE_VSTINTERFACE_H_INCLUDED | |||
using namespace juce; | |||
#if JUCE_MSVC | |||
#define VSTINTERFACECALL __cdecl | |||
#pragma pack(push) | |||
#pragma pack(8) | |||
#elif JUCE_MAC || JUCE_IOS | |||
#define VSTINTERFACECALL | |||
#if JUCE_64BIT | |||
#pragma options align=power | |||
#else | |||
#pragma options align=mac68k | |||
#endif | |||
#else | |||
#define VSTINTERFACECALL | |||
#pragma pack(push, 8) | |||
#endif | |||
const int32 juceVstInterfaceVersion = 2400; | |||
const int32 juceVstInterfaceIdentifier = 0x56737450; // The "magic" identifier in the SDK is 'VstP'. | |||
//============================================================================== | |||
struct VstEffectInterface | |||
{ | |||
int32 interfaceIdentifier; | |||
pointer_sized_int (VSTINTERFACECALL* dispatchFunction) (VstEffectInterface*, int32 op, int32 index, pointer_sized_int value, void* ptr, float opt); | |||
void (VSTINTERFACECALL* processAudioFunction) (VstEffectInterface*, float** inputs, float** outputs, int32 numSamples); | |||
void (VSTINTERFACECALL* setParameterValueFunction) (VstEffectInterface*, int32 parameterIndex, float value); | |||
float (VSTINTERFACECALL* getParameterValueFunction) (VstEffectInterface*, int32 parameterIndex); | |||
int32 numPrograms; | |||
int32 numParameters; | |||
int32 numInputChannels; | |||
int32 numOutputChannels; | |||
int32 flags; | |||
pointer_sized_int hostSpace1; | |||
pointer_sized_int hostSpace2; | |||
int32 latency; | |||
int32 deprecated1; | |||
int32 deprecated2; | |||
float deprecated3; | |||
void* effectPointer; | |||
void* userPointer; | |||
int32 plugInIdentifier; | |||
int32 plugInVersion; | |||
void (VSTINTERFACECALL* processAudioInplaceFunction) (VstEffectInterface*, float** inputs, float** outputs, int32 numSamples); | |||
void (VSTINTERFACECALL* processDoubleAudioInplaceFunction) (VstEffectInterface*, double** inputs, double** outputs, int32 numSamples); | |||
char emptySpace[56]; | |||
}; | |||
typedef pointer_sized_int (VSTINTERFACECALL* VstHostCallback) (VstEffectInterface*, int32 op, int32 index, pointer_sized_int value, void* ptr, float opt); | |||
enum VstEffectInterfaceFlags | |||
{ | |||
vstEffectFlagHasEditor = 1, | |||
vstEffectFlagInplaceAudio = 16, | |||
vstEffectFlagDataInChunks = 32, | |||
vstEffectFlagIsSynth = 256, | |||
vstEffectFlagInplaceDoubleAudio = 4096 | |||
}; | |||
//============================================================================== | |||
enum VstHostToPlugInOpcodes | |||
{ | |||
plugInOpcodeOpen, | |||
plugInOpcodeClose, | |||
plugInOpcodeSetCurrentProgram, | |||
plugInOpcodeGetCurrentProgram, | |||
plugInOpcodeSetCurrentProgramName, | |||
plugInOpcodeGetCurrentProgramName, | |||
plugInOpcodeGetParameterLabel, | |||
plugInOpcodeGetParameterText, | |||
plugInOpcodeGetParameterName, | |||
plugInOpcodeSetSampleRate = plugInOpcodeGetParameterName + 2, | |||
plugInOpcodeSetBlockSize, | |||
plugInOpcodeResumeSuspend, | |||
plugInOpcodeGetEditorBounds, | |||
plugInOpcodeOpenEditor, | |||
plugInOpcodeCloseEditor, | |||
plugInOpcodeDrawEditor, | |||
plugInOpcodeGetMouse, | |||
plugInOpcodeEditorIdle = plugInOpcodeGetMouse + 2, | |||
plugInOpcodeeffEditorTop, | |||
plugInOpcodeSleepEditor, | |||
plugInOpcodeIdentify, | |||
plugInOpcodeGetData, | |||
plugInOpcodeSetData, | |||
plugInOpcodePreAudioProcessingEvents, | |||
plugInOpcodeIsParameterAutomatable, | |||
plugInOpcodeParameterValueForText, | |||
plugInOpcodeGetProgramName = plugInOpcodeParameterValueForText + 2, | |||
plugInOpcodeConnectInput = plugInOpcodeGetProgramName + 2, | |||
plugInOpcodeConnectOutput, | |||
plugInOpcodeGetInputPinProperties, | |||
plugInOpcodeGetOutputPinProperties, | |||
plugInOpcodeGetPlugInCategory, | |||
plugInOpcodeSetSpeakerConfiguration = plugInOpcodeGetPlugInCategory + 7, | |||
plugInOpcodeSetBypass = plugInOpcodeSetSpeakerConfiguration + 2, | |||
plugInOpcodeGetPlugInName, | |||
plugInOpcodeGetManufacturerName = plugInOpcodeGetPlugInName + 2, | |||
plugInOpcodeGetManufacturerProductName, | |||
plugInOpcodeGetManufacturerVersion, | |||
plugInOpcodeManufacturerSpecific, | |||
plugInOpcodeCanPlugInDo, | |||
plugInOpcodeGetTailSize, | |||
plugInOpcodeIdle, | |||
plugInOpcodeKeyboardFocusRequired = plugInOpcodeIdle + 4, | |||
plugInOpcodeGetVstInterfaceVersion, | |||
plugInOpcodeGetCurrentMidiProgram = plugInOpcodeGetVstInterfaceVersion + 5, | |||
plugInOpcodeGetSpeakerArrangement = plugInOpcodeGetCurrentMidiProgram + 6, | |||
plugInOpcodeNextPlugInUniqueID, | |||
plugInOpcodeStartProcess, | |||
plugInOpcodeStopProcess, | |||
plugInOpcodeSetNumberOfSamplesToProcess, | |||
plugInOpcodeSetSampleFloatType = plugInOpcodeSetNumberOfSamplesToProcess + 4, | |||
pluginOpcodeGetNumMidiInputChannels, | |||
pluginOpcodeGetNumMidiOutputChannels, | |||
plugInOpcodeMaximum = pluginOpcodeGetNumMidiOutputChannels | |||
}; | |||
enum VstPlugInToHostOpcodes | |||
{ | |||
hostOpcodeParameterChanged, | |||
hostOpcodeVstVersion, | |||
hostOpcodeCurrentId, | |||
hostOpcodeIdle, | |||
hostOpcodePinConnected, | |||
hostOpcodePlugInWantsMidi = hostOpcodePinConnected + 2, | |||
hostOpcodeGetTimingInfo, | |||
hostOpcodePreAudioProcessingEvents, | |||
hostOpcodeSetTime, | |||
hostOpcodeTempoAt, | |||
hostOpcodeGetNumberOfAutomatableParameters, | |||
hostOpcodeGetParameterInterval, | |||
hostOpcodeIOModified, | |||
hostOpcodeNeedsIdle, | |||
hostOpcodeWindowSize, | |||
hostOpcodeGetSampleRate, | |||
hostOpcodeGetBlockSize, | |||
hostOpcodeGetInputLatency, | |||
hostOpcodeGetOutputLatency, | |||
hostOpcodeGetPreviousPlugIn, | |||
hostOpcodeGetNextPlugIn, | |||
hostOpcodeWillReplace, | |||
hostOpcodeGetCurrentAudioProcessingLevel, | |||
hostOpcodeGetAutomationState, | |||
hostOpcodeOfflineStart, | |||
hostOpcodeOfflineReadSource, | |||
hostOpcodeOfflineWrite, | |||
hostOpcodeOfflineGetCurrentPass, | |||
hostOpcodeOfflineGetCurrentMetaPass, | |||
hostOpcodeSetOutputSampleRate, | |||
hostOpcodeGetOutputSpeakerConfiguration, | |||
hostOpcodeGetManufacturerName, | |||
hostOpcodeGetProductName, | |||
hostOpcodeGetManufacturerVersion, | |||
hostOpcodeManufacturerSpecific, | |||
hostOpcodeSetIcon, | |||
hostOpcodeCanHostDo, | |||
hostOpcodeGetLanguage, | |||
hostOpcodeOpenEditorWindow, | |||
hostOpcodeCloseEditorWindow, | |||
hostOpcodeGetDirectory, | |||
hostOpcodeUpdateView, | |||
hostOpcodeParameterChangeGestureBegin, | |||
hostOpcodeParameterChangeGestureEnd, | |||
}; | |||
//============================================================================== | |||
enum VstProcessingSampleType | |||
{ | |||
vstProcessingSampleTypeFloat, | |||
vstProcessingSampleTypeDouble | |||
}; | |||
//============================================================================== | |||
// These names must be identical to the Steinberg SDK so JUCE users can set | |||
// exactly what they want. | |||
enum VstPlugInCategory | |||
{ | |||
kPlugCategUnknown, | |||
kPlugCategEffect, | |||
kPlugCategSynth, | |||
kPlugCategAnalysis, | |||
kPlugCategMastering, | |||
kPlugCategSpacializer, | |||
kPlugCategRoomFx, | |||
kPlugSurroundFx, | |||
kPlugCategRestoration, | |||
kPlugCategOfflineProcess, | |||
kPlugCategShell, | |||
kPlugCategGenerator | |||
}; | |||
//============================================================================== | |||
struct VstEditorBounds | |||
{ | |||
int16 upper; | |||
int16 leftmost; | |||
int16 lower; | |||
int16 rightmost; | |||
}; | |||
//============================================================================== | |||
enum VstMaxStringLengths | |||
{ | |||
vstMaxNameLength = 64, | |||
vstMaxParameterOrPinLabelLength = 64, | |||
vstMaxParameterOrPinShortLabelLength = 8, | |||
vstMaxCategoryLength = 24, | |||
vstMaxManufacturerStringLength = 64, | |||
vstMaxPlugInNameStringLength = 64 | |||
}; | |||
//============================================================================== | |||
struct VstPinInfo | |||
{ | |||
char text[vstMaxParameterOrPinLabelLength]; | |||
int32 flags; | |||
int32 configurationType; | |||
char shortText[vstMaxParameterOrPinShortLabelLength]; | |||
char unused[48]; | |||
}; | |||
enum VstPinInfoFlags | |||
{ | |||
vstPinInfoFlagIsActive = 1, | |||
vstPinInfoFlagIsStereo = 2, | |||
vstPinInfoFlagValid = 4 | |||
}; | |||
//============================================================================== | |||
struct VstEvent | |||
{ | |||
int32 type; | |||
int32 size; | |||
int32 sampleOffset; | |||
int32 flags; | |||
char content[16]; | |||
}; | |||
enum VstEventTypes | |||
{ | |||
vstMidiEventType = 1, | |||
vstSysExEventType = 6 | |||
}; | |||
struct VstEventBlock | |||
{ | |||
int32 numberOfEvents; | |||
pointer_sized_int future; | |||
VstEvent* events[2]; | |||
}; | |||
struct VstMidiEvent | |||
{ | |||
int32 type; | |||
int32 size; | |||
int32 sampleOffset; | |||
int32 flags; | |||
int32 noteSampleLength; | |||
int32 noteSampleOffset; | |||
char midiData[4]; | |||
char tuning; | |||
char noteVelocityOff; | |||
char future1; | |||
char future2; | |||
}; | |||
enum VstMidiEventFlags | |||
{ | |||
vstMidiEventIsRealtime = 1 | |||
}; | |||
struct VstSysExEvent | |||
{ | |||
int32 type; | |||
int32 size; | |||
int32 offsetSamples; | |||
int32 flags; | |||
int32 sysExDumpSize; | |||
pointer_sized_int future1; | |||
char* sysExDump; | |||
pointer_sized_int future2; | |||
}; | |||
//============================================================================== | |||
struct VstTimingInformation | |||
{ | |||
double samplePosition; | |||
double sampleRate; | |||
double systemTimeNanoseconds; | |||
double musicalPosition; | |||
double tempoBPM; | |||
double lastBarPosition; | |||
double loopStartPosition; | |||
double loopEndPosition; | |||
int32 timeSignatureNumerator; | |||
int32 timeSignatureDenominator; | |||
int32 smpteOffset; | |||
int32 smpteRate; | |||
int32 samplesToNearestClock; | |||
int32 flags; | |||
}; | |||
enum VstTimingInformationFlags | |||
{ | |||
vstTimingInfoFlagTransportChanged = 1, | |||
vstTimingInfoFlagCurrentlyPlaying = 2, | |||
vstTimingInfoFlagLoopActive = 4, | |||
vstTimingInfoFlagCurrentlyRecording = 8, | |||
vstTimingInfoFlagAutomationWriteModeActive = 64, | |||
vstTimingInfoFlagAutomationReadModeActive = 128, | |||
vstTimingInfoFlagNanosecondsValid = 256, | |||
vstTimingInfoFlagMusicalPositionValid = 512, | |||
vstTimingInfoFlagTempoValid = 1024, | |||
vstTimingInfoFlagLastBarPositionValid = 2048, | |||
vstTimingInfoFlagLoopPositionValid = 4096, | |||
vstTimingInfoFlagTimeSignatureValid = 8192, | |||
vstTimingInfoFlagSmpteValid = 16384, | |||
vstTimingInfoFlagNearestClockValid = 32768 | |||
}; | |||
//============================================================================== | |||
enum VstSmpteRates | |||
{ | |||
vstSmpteRateFps24, | |||
vstSmpteRateFps25, | |||
vstSmpteRateFps2997, | |||
vstSmpteRateFps30, | |||
vstSmpteRateFps2997drop, | |||
vstSmpteRateFps30drop, | |||
vstSmpteRate16mmFilm, | |||
vstSmpteRate35mmFilm, | |||
vstSmpteRateFps239 = vstSmpteRate35mmFilm + 3, | |||
vstSmpteRateFps249 , | |||
vstSmpteRateFps599, | |||
vstSmpteRateFps60 | |||
}; | |||
//============================================================================== | |||
struct VstIndividualSpeakerInfo | |||
{ | |||
float azimuthalAngle; | |||
float elevationAngle; | |||
float radius; | |||
float reserved; | |||
char label[vstMaxNameLength]; | |||
int32 type; | |||
char unused[28]; | |||
}; | |||
enum VstIndividualSpeakerType | |||
{ | |||
vstIndividualSpeakerTypeUndefined = 0x7fffffff, | |||
vstIndividualSpeakerTypeMono = 0, | |||
vstIndividualSpeakerTypeLeft, | |||
vstIndividualSpeakerTypeRight, | |||
vstIndividualSpeakerTypeCentre, | |||
vstIndividualSpeakerTypeLFE, | |||
vstIndividualSpeakerTypeLeftSurround, | |||
vstIndividualSpeakerTypeRightSurround, | |||
vstIndividualSpeakerTypeLeftCentre, | |||
vstIndividualSpeakerTypeRightCentre, | |||
vstIndividualSpeakerTypeSurround, | |||
vstIndividualSpeakerTypeCentreSurround = vstIndividualSpeakerTypeSurround, | |||
vstIndividualSpeakerTypeLeftRearSurround, | |||
vstIndividualSpeakerTypeRightRearSurround, | |||
vstIndividualSpeakerTypeTopMiddle, | |||
vstIndividualSpeakerTypeTopFrontLeft, | |||
vstIndividualSpeakerTypeTopFrontCentre, | |||
vstIndividualSpeakerTypeTopFrontRight, | |||
vstIndividualSpeakerTypeTopRearLeft, | |||
vstIndividualSpeakerTypeTopRearCentre, | |||
vstIndividualSpeakerTypeTopRearRight, | |||
vstIndividualSpeakerTypeLFE2 | |||
}; | |||
struct VstSpeakerConfiguration | |||
{ | |||
int32 type; | |||
int32 numberOfChannels; | |||
VstIndividualSpeakerInfo speakers[8]; | |||
}; | |||
enum VstSpeakerConfigurationType | |||
{ | |||
vstSpeakerConfigTypeUser = -2, | |||
vstSpeakerConfigTypeEmpty = -1, | |||
vstSpeakerConfigTypeMono = 0, | |||
vstSpeakerConfigTypeLR, | |||
vstSpeakerConfigTypeLsRs, | |||
vstSpeakerConfigTypeLcRc, | |||
vstSpeakerConfigTypeSlSr, | |||
vstSpeakerConfigTypeCLfe, | |||
vstSpeakerConfigTypeLRC, | |||
vstSpeakerConfigTypeLRS, | |||
vstSpeakerConfigTypeLRCLfe, | |||
vstSpeakerConfigTypeLRLfeS, | |||
vstSpeakerConfigTypeLRCS, | |||
vstSpeakerConfigTypeLRLsRs, | |||
vstSpeakerConfigTypeLRCLfeS, | |||
vstSpeakerConfigTypeLRLfeLsRs, | |||
vstSpeakerConfigTypeLRCLsRs, | |||
vstSpeakerConfigTypeLRCLfeLsRs, | |||
vstSpeakerConfigTypeLRCLsRsCs, | |||
vstSpeakerConfigTypeLRLsRsSlSr, | |||
vstSpeakerConfigTypeLRCLfeLsRsCs, | |||
vstSpeakerConfigTypeLRLfeLsRsSlSr, | |||
vstSpeakerConfigTypeLRCLsRsLcRc, | |||
vstSpeakerConfigTypeLRCLsRsSlSr, | |||
vstSpeakerConfigTypeLRCLfeLsRsLcRc, | |||
vstSpeakerConfigTypeLRCLfeLsRsSlSr, | |||
vstSpeakerConfigTypeLRCLsRsLcRcCs, | |||
vstSpeakerConfigTypeLRCLsRsCsSlSr, | |||
vstSpeakerConfigTypeLRCLfeLsRsLcRcCs, | |||
vstSpeakerConfigTypeLRCLfeLsRsCsSlSr, | |||
vstSpeakerConfigTypeLRCLfeLsRsTflTfcTfrTrlTrrLfe2 | |||
}; | |||
#if JUCE_BIG_ENDIAN | |||
#define JUCE_MULTICHAR_CONSTANT(a, b, c, d) (a | (((uint32) b) << 8) | (((uint32) c) << 16) | (((uint32) d) << 24)) | |||
#else | |||
#define JUCE_MULTICHAR_CONSTANT(a, b, c, d) (d | (((uint32) c) << 8) | (((uint32) b) << 16) | (((uint32) a) << 24)) | |||
#endif | |||
enum PresonusExtensionConstants | |||
{ | |||
presonusVendorID = JUCE_MULTICHAR_CONSTANT ('P', 'r', 'e', 'S'), | |||
presonusSetContentScaleFactor = JUCE_MULTICHAR_CONSTANT ('A', 'e', 'C', 's') | |||
}; | |||
//============================================================================== | |||
struct vst2FxBank | |||
{ | |||
int32 magic1; | |||
int32 size; | |||
int32 magic2; | |||
int32 version1; | |||
int32 fxID; | |||
int32 version2; | |||
int32 elements; | |||
int32 current; | |||
char shouldBeZero[124]; | |||
int32 chunkSize; | |||
char chunk[1]; | |||
}; | |||
#if JUCE_MSVC | |||
#pragma pack(pop) | |||
#elif JUCE_MAC || JUCE_IOS | |||
#pragma options align=reset | |||
#else | |||
#pragma pack(pop) | |||
#endif |
@@ -1,191 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
// NB: this must come first, *before* the header-guard. | |||
#ifdef JUCE_VSTINTERFACE_H_INCLUDED | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** 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->numberOfEvents = 0; | |||
} | |||
void addEvent (const void* const midiData, const int numBytes, const int frameOffset) | |||
{ | |||
ensureSize (numEventsUsed + 1); | |||
VstMidiEvent* const e = (VstMidiEvent*) (events->events [numEventsUsed]); | |||
events->numberOfEvents = ++numEventsUsed; | |||
if (numBytes <= 4) | |||
{ | |||
if (e->type == vstSysExEventType) | |||
{ | |||
delete[] (((VstSysExEvent*) e)->sysExDump); | |||
e->type = vstMidiEventType; | |||
e->size = sizeof (VstMidiEvent); | |||
e->noteSampleLength = 0; | |||
e->noteSampleOffset = 0; | |||
e->tuning = 0; | |||
e->noteVelocityOff = 0; | |||
} | |||
e->sampleOffset = frameOffset; | |||
memcpy (e->midiData, midiData, (size_t) numBytes); | |||
} | |||
else | |||
{ | |||
VstSysExEvent* const se = (VstSysExEvent*) e; | |||
if (se->type == vstSysExEventType) | |||
delete[] se->sysExDump; | |||
se->sysExDump = new char [(size_t) numBytes]; | |||
memcpy (se->sysExDump, midiData, (size_t) numBytes); | |||
se->type = vstSysExEventType; | |||
se->size = sizeof (VstSysExEvent); | |||
se->offsetSamples = frameOffset; | |||
se->flags = 0; | |||
se->sysExDumpSize = numBytes; | |||
se->future1 = 0; | |||
se->future2 = 0; | |||
} | |||
} | |||
//============================================================================== | |||
// Handy method to pull the events out of an event buffer supplied by the host | |||
// or plugin. | |||
static void addEventsToMidiBuffer (const VstEventBlock* events, MidiBuffer& dest) | |||
{ | |||
for (int i = 0; i < events->numberOfEvents; ++i) | |||
{ | |||
const VstEvent* const e = events->events[i]; | |||
if (e != nullptr) | |||
{ | |||
if (e->type == vstMidiEventType) | |||
{ | |||
dest.addEvent ((const juce::uint8*) ((const VstMidiEvent*) e)->midiData, | |||
4, e->sampleOffset); | |||
} | |||
else if (e->type == vstSysExEventType) | |||
{ | |||
dest.addEvent ((const juce::uint8*) ((const VstSysExEvent*) e)->sysExDump, | |||
(int) ((const VstSysExEvent*) e)->sysExDumpSize, | |||
e->sampleOffset); | |||
} | |||
} | |||
} | |||
} | |||
//============================================================================== | |||
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<VstEventBlock> events; | |||
private: | |||
int numEventsUsed, numEventsAllocated; | |||
static VstEvent* allocateVSTEvent() | |||
{ | |||
auto e = (VstEvent*) std::calloc (1, sizeof (VstMidiEvent) > sizeof (VstSysExEvent) ? sizeof (VstMidiEvent) | |||
: sizeof (VstSysExEvent)); | |||
e->type = vstMidiEventType; | |||
e->size = sizeof (VstMidiEvent); | |||
return e; | |||
} | |||
static void freeVSTEvent (VstEvent* e) | |||
{ | |||
if (e->type == vstSysExEventType) | |||
delete[] (((VstSysExEvent*) e)->sysExDump); | |||
std::free (e); | |||
} | |||
}; | |||
} // namespace juce | |||
#endif // JUCE_VSTINTERFACE_H_INCLUDED |
@@ -1,132 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
#if (JUCE_PLUGINHOST_VST || DOXYGEN) | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Implements a plugin format manager for VSTs. | |||
*/ | |||
class JUCE_API VSTPluginFormat : public AudioPluginFormat | |||
{ | |||
public: | |||
//============================================================================== | |||
VSTPluginFormat(); | |||
~VSTPluginFormat(); | |||
//============================================================================== | |||
/** Attempts to retrieve 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); | |||
/** Given a suitable function pointer to a VSTPluginMain function, this will attempt to | |||
instantiate and return a plugin for it. | |||
*/ | |||
static AudioPluginInstance* createCustomVSTFromMainCall (void* entryPointFunction, | |||
double initialSampleRate, | |||
int initialBufferSize); | |||
//============================================================================== | |||
/** 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); | |||
//============================================================================== | |||
/** This simply calls directly to the VST's AEffect::dispatcher() function. */ | |||
static pointer_sized_int JUCE_CALLTYPE dispatcher (AudioPluginInstance*, int32, int32, pointer_sized_int, void*, float); | |||
/** Given a VstEffectInterface* (aka vst::AEffect*), this will return the juce AudioPluginInstance | |||
that is being used to wrap it | |||
*/ | |||
static AudioPluginInstance* getPluginInstanceFromVstEffectInterface (void* aEffect); | |||
//============================================================================== | |||
String getName() const override { return "VST"; } | |||
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) 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) 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 createPluginInstance (const PluginDescription&, double initialSampleRate, | |||
int initialBufferSize, void* userData, | |||
void (*callback) (void*, AudioPluginInstance*, const String&)) override; | |||
bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override; | |||
private: | |||
void recursiveFileSearch (StringArray&, const File&, bool recursive); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginFormat) | |||
}; | |||
} // namespace juce | |||
#endif |
@@ -1,185 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
#ifdef JUCE_AUDIO_PROCESSORS_H_INCLUDED | |||
/* 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 | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | |||
#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)) | |||
#include <Carbon/Carbon.h> | |||
#include "../juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h" | |||
#endif | |||
#endif | |||
#if JUCE_PLUGINHOST_VST && JUCE_LINUX && ! JUCE_AUDIOPROCESSOR_NO_GUI | |||
#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 | |||
#if JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS) | |||
#include <AudioUnit/AudioUnit.h> | |||
#endif | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
static inline bool arrayContainsPlugin (const OwnedArray<PluginDescription>& list, | |||
const PluginDescription& desc) | |||
{ | |||
for (auto* p : list) | |||
if (p->isDuplicateOf (desc)) | |||
return true; | |||
return false; | |||
} | |||
#if JUCE_MAC || JUCE_IOS | |||
#if JUCE_IOS | |||
#define JUCE_IOS_MAC_VIEW UIView | |||
typedef UIViewComponent ViewComponentBaseClass; | |||
#else | |||
#define JUCE_IOS_MAC_VIEW NSView | |||
typedef NSViewComponent ViewComponentBaseClass; | |||
#endif | |||
//============================================================================== | |||
struct AutoResizingNSViewComponent : public ViewComponentBaseClass, | |||
private AsyncUpdater | |||
{ | |||
AutoResizingNSViewComponent(); | |||
void childBoundsChanged (Component*) override; | |||
void handleAsyncUpdate() override; | |||
bool recursive; | |||
}; | |||
//============================================================================== | |||
struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent, | |||
private Timer | |||
{ | |||
AutoResizingNSViewComponentWithParent(); | |||
JUCE_IOS_MAC_VIEW* getChildView() const; | |||
void timerCallback() override; | |||
}; | |||
//============================================================================== | |||
AutoResizingNSViewComponent::AutoResizingNSViewComponent() | |||
: recursive (false) {} | |||
void AutoResizingNSViewComponent::childBoundsChanged (Component*) override | |||
{ | |||
if (recursive) | |||
{ | |||
triggerAsyncUpdate(); | |||
} | |||
else | |||
{ | |||
recursive = true; | |||
resizeToFitView(); | |||
recursive = true; | |||
} | |||
} | |||
void AutoResizingNSViewComponent::handleAsyncUpdate() override | |||
{ | |||
resizeToFitView(); | |||
} | |||
//============================================================================== | |||
AutoResizingNSViewComponentWithParent::AutoResizingNSViewComponentWithParent() | |||
{ | |||
JUCE_IOS_MAC_VIEW* v = [[JUCE_IOS_MAC_VIEW alloc] init]; | |||
setView (v); | |||
[v release]; | |||
startTimer (30); | |||
} | |||
JUCE_IOS_MAC_VIEW* AutoResizingNSViewComponentWithParent::getChildView() const | |||
{ | |||
if (JUCE_IOS_MAC_VIEW* parent = (JUCE_IOS_MAC_VIEW*) getView()) | |||
if ([[parent subviews] count] > 0) | |||
return [[parent subviews] objectAtIndex: 0]; | |||
return nil; | |||
} | |||
void AutoResizingNSViewComponentWithParent::timerCallback() override | |||
{ | |||
if (JUCE_IOS_MAC_VIEW* child = getChildView()) | |||
{ | |||
stopTimer(); | |||
setView (child); | |||
} | |||
} | |||
#endif | |||
} // namespace juce | |||
#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_AudioProcessorGraph.cpp" | |||
#if ! JUCE_AUDIOPROCESSOR_NO_GUI | |||
#include "processors/juce_AudioProcessorEditor.cpp" | |||
#include "processors/juce_GenericAudioProcessorEditor.cpp" | |||
#endif | |||
#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" | |||
#include "utilities/juce_AudioProcessorParameters.cpp" | |||
#include "utilities/juce_AudioProcessorValueTreeState.cpp" |
@@ -1,124 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
/******************************************************************************* | |||
The block below describes the properties of this module, and is read by | |||
the Projucer to automatically generate project code that uses it. | |||
For details about the syntax and how to create or use a module, see the | |||
JUCE Module Format.txt file. | |||
BEGIN_JUCE_MODULE_DECLARATION | |||
ID: juce_audio_processors | |||
vendor: juce | |||
version: 5.1.2 | |||
name: JUCE audio processor classes | |||
description: Classes for loading and playing VST, AU, or internally-generated audio processors. | |||
website: http://www.juce.com/juce | |||
license: GPL/Commercial | |||
dependencies: juce_gui_extra, juce_audio_basics | |||
OSXFrameworks: CoreAudio CoreMIDI AudioToolbox | |||
iOSFrameworks: AudioToolbox | |||
END_JUCE_MODULE_DECLARATION | |||
*******************************************************************************/ | |||
#pragma once | |||
#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. | |||
@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 || JUCE_IOS) | |||
#define JUCE_SUPPORT_CARBON 1 | |||
#endif | |||
#ifndef JUCE_SUPPORT_LEGACY_AUDIOPROCESSOR | |||
#define JUCE_SUPPORT_LEGACY_AUDIOPROCESSOR 1 | |||
#endif | |||
//============================================================================== | |||
#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" | |||
#include "utilities/juce_AudioProcessorParameterWithID.h" | |||
#include "utilities/juce_AudioParameterFloat.h" | |||
#include "utilities/juce_AudioParameterInt.h" | |||
#include "utilities/juce_AudioParameterBool.h" | |||
#include "utilities/juce_AudioParameterChoice.h" | |||
#include "utilities/juce_AudioProcessorValueTreeState.h" |
@@ -1,89 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Base class for 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() {} | |||
AudioPluginInstance (const BusesProperties& ioLayouts) : AudioProcessor (ioLayouts) {} | |||
template <int numLayouts> | |||
AudioPluginInstance (const short channelLayoutList[numLayouts][2]) : AudioProcessor (channelLayoutList) {} | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance) | |||
}; | |||
} // namespace juce |
@@ -1,189 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p) | |||
{ | |||
initialise(); | |||
} | |||
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p) | |||
{ | |||
// the filter must be valid.. | |||
jassert (p != nullptr); | |||
initialise(); | |||
} | |||
AudioProcessorEditor::~AudioProcessorEditor() | |||
{ | |||
// if this fails, then the wrapper hasn't called editorBeingDeleted() on the | |||
// filter for some reason.. | |||
jassert (processor.getActiveEditor() != this); | |||
removeComponentListener (resizeListener); | |||
} | |||
void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {} | |||
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; } | |||
bool AudioProcessorEditor::supportsHostMIDIControllerPresence (bool) { return true; } | |||
void AudioProcessorEditor::hostMIDIControllerIsAvailable (bool) {} | |||
void AudioProcessorEditor::initialise() | |||
{ | |||
resizable = false; | |||
attachConstrainer (&defaultConstrainer); | |||
addComponentListener (resizeListener = new AudioProcessorEditorListener (*this)); | |||
} | |||
//============================================================================== | |||
void AudioProcessorEditor::setResizable (const bool shouldBeResizable, const bool useBottomRightCornerResizer) | |||
{ | |||
if (shouldBeResizable != resizable) | |||
{ | |||
resizable = shouldBeResizable; | |||
if (! resizable) | |||
{ | |||
setConstrainer (&defaultConstrainer); | |||
if (auto w = getWidth()) | |||
{ | |||
if (auto h = getHeight()) | |||
{ | |||
defaultConstrainer.setSizeLimits (w, h, w, h); | |||
resized(); | |||
} | |||
} | |||
} | |||
} | |||
bool shouldHaveCornerResizer = (useBottomRightCornerResizer && shouldBeResizable); | |||
if (shouldHaveCornerResizer != (resizableCorner != nullptr)) | |||
{ | |||
if (shouldHaveCornerResizer) | |||
{ | |||
Component::addChildComponent (resizableCorner = new ResizableCornerComponent (this, constrainer)); | |||
resizableCorner->setAlwaysOnTop (true); | |||
} | |||
else | |||
{ | |||
resizableCorner = nullptr; | |||
} | |||
} | |||
} | |||
void AudioProcessorEditor::setResizeLimits (int newMinimumWidth, | |||
int newMinimumHeight, | |||
int newMaximumWidth, | |||
int newMaximumHeight) noexcept | |||
{ | |||
// if you've set up a custom constrainer then these settings won't have any effect.. | |||
jassert (constrainer == &defaultConstrainer || constrainer == nullptr); | |||
const bool shouldEnableResize = (newMinimumWidth != newMaximumWidth || newMinimumHeight != newMaximumHeight); | |||
const bool shouldHaveCornerResizer = (shouldEnableResize != resizable || resizableCorner != nullptr); | |||
setResizable (shouldEnableResize, shouldHaveCornerResizer); | |||
if (constrainer == nullptr) | |||
setConstrainer (&defaultConstrainer); | |||
defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight, | |||
newMaximumWidth, newMaximumHeight); | |||
setBoundsConstrained (getBounds()); | |||
} | |||
void AudioProcessorEditor::setConstrainer (ComponentBoundsConstrainer* newConstrainer) | |||
{ | |||
if (constrainer != newConstrainer) | |||
{ | |||
resizable = true; | |||
attachConstrainer (newConstrainer); | |||
} | |||
} | |||
void AudioProcessorEditor::attachConstrainer (ComponentBoundsConstrainer* newConstrainer) | |||
{ | |||
if (constrainer != newConstrainer) | |||
{ | |||
constrainer = newConstrainer; | |||
updatePeer(); | |||
} | |||
} | |||
void AudioProcessorEditor::setBoundsConstrained (Rectangle<int> newBounds) | |||
{ | |||
if (constrainer != nullptr) | |||
constrainer->setBoundsForComponent (this, newBounds, false, false, false, false); | |||
else | |||
setBounds (newBounds); | |||
} | |||
void AudioProcessorEditor::editorResized (bool wasResized) | |||
{ | |||
if (wasResized) | |||
{ | |||
bool resizerHidden = false; | |||
if (auto* peer = getPeer()) | |||
resizerHidden = peer->isFullScreen() || peer->isKioskMode(); | |||
if (resizableCorner != nullptr) | |||
{ | |||
resizableCorner->setVisible (! resizerHidden); | |||
const int resizerSize = 18; | |||
resizableCorner->setBounds (getWidth() - resizerSize, | |||
getHeight() - resizerSize, | |||
resizerSize, resizerSize); | |||
} | |||
if (! resizable) | |||
if (auto w = getWidth()) | |||
if (auto h = getHeight()) | |||
defaultConstrainer.setSizeLimits (w, h, w, h); | |||
} | |||
} | |||
void AudioProcessorEditor::updatePeer() | |||
{ | |||
if (isOnDesktop()) | |||
if (auto* peer = getPeer()) | |||
peer->setConstrainer (constrainer); | |||
} | |||
void AudioProcessorEditor::setScaleFactor (float newScale) | |||
{ | |||
setTransform (AffineTransform::scale (newScale)); | |||
editorResized (true); | |||
} | |||
} // namespace juce |
@@ -1,209 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
class AudioProcessor; | |||
class AudioProcessorEditorListener; | |||
//============================================================================== | |||
/** | |||
Base class for the component that acts as the GUI for an AudioProcessor. | |||
Derive your editor component from this class, and create an instance of it | |||
by overriding the AudioProcessor::createEditor() method. | |||
@see AudioProcessor, GenericAudioProcessorEditor | |||
*/ | |||
class JUCE_API AudioProcessorEditor : public Component | |||
{ | |||
protected: | |||
//============================================================================== | |||
/** Creates an editor for the specified processor. */ | |||
AudioProcessorEditor (AudioProcessor&) noexcept; | |||
/** Creates an editor for the specified processor. */ | |||
AudioProcessorEditor (AudioProcessor*) noexcept; | |||
public: | |||
/** Destructor. */ | |||
~AudioProcessorEditor(); | |||
//============================================================================== | |||
/** The AudioProcessor that this editor represents. */ | |||
AudioProcessor& processor; | |||
/** Returns a pointer to the processor that this editor represents. | |||
This method is here to support legacy code, but it's easier to just use the | |||
AudioProcessorEditor::processor member variable directly to get this object. | |||
*/ | |||
AudioProcessor* getAudioProcessor() const noexcept { return &processor; } | |||
//============================================================================== | |||
/** Used by the setParameterHighlighting() method. */ | |||
struct ParameterControlHighlightInfo | |||
{ | |||
int parameterIndex; | |||
bool isHighlighted; | |||
Colour suggestedColour; | |||
}; | |||
/** Some types of plugin can call this to suggest that the control for a particular | |||
parameter should be highlighted. | |||
Currently only AAX plugins will call this, and implementing it is optional. | |||
*/ | |||
virtual void setControlHighlight (ParameterControlHighlightInfo); | |||
/** Called by certain plug-in wrappers to find out whether a component is used | |||
to control a parameter. | |||
If the given component represents a particular plugin parameter, then this | |||
method should return the index of that parameter. If not, it should return -1. | |||
Currently only AAX plugins will call this, and implementing it is optional. | |||
*/ | |||
virtual int getControlParameterIndex (Component&); | |||
/** Override this method to indicate if your editor supports the presence or | |||
absence of a host-provided MIDI controller. | |||
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later) | |||
support this functionality, and even then the host may choose to ignore this | |||
information. | |||
The default behaviour is to report support for both cases. | |||
*/ | |||
virtual bool supportsHostMIDIControllerPresence (bool hostMIDIControllerIsAvailable); | |||
/** Called to indicate if a host is providing a MIDI controller when the host | |||
reconfigures its layout. | |||
Use this as an opportunity to hide or display your own onscreen keyboard or | |||
other input component. | |||
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later) | |||
support this functionality. | |||
*/ | |||
virtual void hostMIDIControllerIsAvailable (bool controllerIsAvailable); | |||
/** Can be called by a host to tell the editor that it should use a non-unity | |||
GUI scale. | |||
*/ | |||
virtual void setScaleFactor (float newScale); | |||
//============================================================================== | |||
/** Marks the host's editor window as resizable | |||
@param allowHostToResize whether the editor's parent window can be resized | |||
by the user or the host. Even if this is false, you | |||
can still resize your window yourself by calling | |||
setBounds (for example, when a user clicks on a button | |||
in your editor to drop out a panel) which will bypass any | |||
resizable/constraints checks. If you are using | |||
your own corner resizer than this will also bypass | |||
any checks. | |||
@param useBottomRightCornerResizer | |||
@see setResizeLimits, isResizable | |||
*/ | |||
void setResizable (bool allowHostToResize, bool useBottomRightCornerResizer); | |||
/** Returns true if the host is allowed to resize editor's parent window | |||
@see setResizable | |||
*/ | |||
bool isResizable() const noexcept { return resizable; } | |||
/** This sets the maximum and minimum sizes for the window. | |||
If the window's current size is outside these limits, it will be resized to | |||
make sure it's within them. | |||
A direct call to setBounds() will bypass any constraint checks, but when the | |||
window is dragged by the user or resized by other indirect means, the constrainer | |||
will limit the numbers involved. | |||
@see setResizable | |||
*/ | |||
void setResizeLimits (int newMinimumWidth, | |||
int newMinimumHeight, | |||
int newMaximumWidth, | |||
int newMaximumHeight) noexcept; | |||
/** Returns the bounds constrainer object that this window is using. | |||
You can access this to change its properties. | |||
*/ | |||
ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; } | |||
/** Sets the bounds-constrainer object to use for resizing and dragging this window. | |||
A pointer to the object you pass in will be kept, but it won't be deleted | |||
by this object, so it's the caller's responsibility to manage it. | |||
If you pass a nullptr, then no contraints will be placed on the positioning of the window. | |||
*/ | |||
void setConstrainer (ComponentBoundsConstrainer* newConstrainer); | |||
/** Calls the window's setBounds method, after first checking these bounds | |||
with the current constrainer. | |||
@see setConstrainer | |||
*/ | |||
void setBoundsConstrained (Rectangle<int> newBounds); | |||
ScopedPointer<ResizableCornerComponent> resizableCorner; | |||
private: | |||
//============================================================================== | |||
struct AudioProcessorEditorListener : ComponentListener | |||
{ | |||
AudioProcessorEditorListener (AudioProcessorEditor& e) : ed (e) {} | |||
void componentMovedOrResized (Component&, bool, bool wasResized) override { ed.editorResized (wasResized); } | |||
void componentParentHierarchyChanged (Component&) override { ed.updatePeer(); } | |||
AudioProcessorEditor& ed; | |||
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditorListener) | |||
}; | |||
//============================================================================== | |||
void initialise(); | |||
void editorResized (bool wasResized); | |||
void updatePeer(); | |||
void attachConstrainer (ComponentBoundsConstrainer*); | |||
//============================================================================== | |||
ScopedPointer<AudioProcessorEditorListener> resizeListener; | |||
bool resizable; | |||
ComponentBoundsConstrainer defaultConstrainer; | |||
ComponentBoundsConstrainer* constrainer = {}; | |||
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) | |||
}; | |||
} // namespace juce |
@@ -1,411 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A type of AudioProcessor which plays back a graph of other AudioProcessors. | |||
Use one of these objects if you want to wire-up a set of AudioProcessors | |||
and play back the result. | |||
Processors can be added to the graph as "nodes" using addNode(), and once | |||
added, you can connect any of their input or output channels to other | |||
nodes using addConnection(). | |||
To play back a graph through an audio device, you might want to use an | |||
AudioProcessorPlayer object. | |||
*/ | |||
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 newSampleRate, int newBlockSize, AudioProcessorGraph*, ProcessingPrecision); | |||
void unprepare(); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Node) | |||
}; | |||
//============================================================================== | |||
/** Represents a connection between two channels of two nodes in an AudioProcessorGraph. | |||
To create a connection, use AudioProcessorGraph::addConnection(). | |||
*/ | |||
struct JUCE_API Connection | |||
{ | |||
//============================================================================== | |||
Connection (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 noexcept { return nodes.size(); } | |||
/** Returns a pointer to one of the nodes in the graph. | |||
This will return nullptr if the index is out of range. | |||
@see getNodeForId | |||
*/ | |||
Node* getNode (const int index) const noexcept { return nodes [index]; } | |||
/** Searches the graph for a node with the given ID number and returns it. | |||
If no such node was found, this returns nullptr. | |||
@see getNode | |||
*/ | |||
Node* getNodeForId (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); | |||
/** Deletes a node within the graph. | |||
This will also delete any connections that are attached to this node. | |||
*/ | |||
bool removeNode (Node* node); | |||
//============================================================================== | |||
/** 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 noexcept { return type; } | |||
/** Returns the parent graph to which this processor belongs, or nullptr if it | |||
hasn't yet been added to one. */ | |||
AudioProcessorGraph* getParentGraph() const noexcept { return graph; } | |||
/** True if this is an audio or midi input. */ | |||
bool isInput() const noexcept; | |||
/** True if this is an audio or midi output. */ | |||
bool isOutput() const noexcept; | |||
//============================================================================== | |||
AudioGraphIOProcessor (const IODeviceType type); | |||
~AudioGraphIOProcessor(); | |||
const String getName() const override; | |||
void fillInPluginDescription (PluginDescription&) const override; | |||
void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override; | |||
void releaseResources() override; | |||
void processBlock (AudioBuffer<float>& , MidiBuffer&) override; | |||
void processBlock (AudioBuffer<double>&, MidiBuffer&) override; | |||
bool supportsDoublePrecisionProcessing() const override; | |||
double getTailLengthSeconds() const override; | |||
bool acceptsMidi() const override; | |||
bool producesMidi() const override; | |||
#if ! JUCE_AUDIOPROCESSOR_NO_GUI | |||
bool hasEditor() const override; | |||
AudioProcessorEditor* createEditor() override; | |||
#endif | |||
int getNumPrograms() override; | |||
int getCurrentProgram() override; | |||
void setCurrentProgram (int) override; | |||
const String getProgramName (int) override; | |||
void changeProgramName (int, const String&) override; | |||
void getStateInformation (juce::MemoryBlock& destData) override; | |||
void setStateInformation (const void* data, int sizeInBytes) override; | |||
/** @internal */ | |||
void setParentGraph (AudioProcessorGraph*); | |||
private: | |||
const IODeviceType type; | |||
AudioProcessorGraph* graph; | |||
//============================================================================== | |||
template <typename floatType> | |||
void processAudio (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioGraphIOProcessor) | |||
}; | |||
//============================================================================== | |||
const String getName() const override; | |||
void prepareToPlay (double, int) override; | |||
void releaseResources() override; | |||
void processBlock (AudioBuffer<float>&, MidiBuffer&) override; | |||
void processBlock (AudioBuffer<double>&, MidiBuffer&) override; | |||
bool supportsDoublePrecisionProcessing() const override; | |||
void reset() override; | |||
void setNonRealtime (bool) noexcept override; | |||
void setPlayHead (AudioPlayHead*) override; | |||
double getTailLengthSeconds() const override; | |||
bool acceptsMidi() const override; | |||
bool producesMidi() const override; | |||
#if ! JUCE_AUDIOPROCESSOR_NO_GUI | |||
bool hasEditor() const override { return false; } | |||
AudioProcessorEditor* createEditor() override { return nullptr; } | |||
#endif | |||
int getNumPrograms() override { return 0; } | |||
int getCurrentProgram() override { return 0; } | |||
void setCurrentProgram (int) override { } | |||
const String getProgramName (int) override { return {}; } | |||
void changeProgramName (int, const String&) override { } | |||
void getStateInformation (juce::MemoryBlock&) override; | |||
void setStateInformation (const void* data, int sizeInBytes) override; | |||
private: | |||
//============================================================================== | |||
template <typename floatType> | |||
void processAudio (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages); | |||
template <typename floatType> | |||
void sliceAndProcess (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages); | |||
//============================================================================== | |||
ReferenceCountedArray<Node> nodes; | |||
OwnedArray<Connection> connections; | |||
uint32 lastNodeId; | |||
OwnedArray<MidiBuffer> midiBuffers; | |||
Array<void*> renderingOps; | |||
friend class AudioGraphIOProcessor; | |||
struct AudioProcessorGraphBufferHelpers; | |||
ScopedPointer<AudioProcessorGraphBufferHelpers> audioBuffers; | |||
MidiBuffer* currentMidiInputBuffer; | |||
MidiBuffer currentMidiOutputBuffer; | |||
bool isPrepared; | |||
void handleAsyncUpdate() override; | |||
void clearRenderingSequence(); | |||
void buildRenderingSequence(); | |||
bool isAnInputTo (uint32 possibleInputId, uint32 possibleDestinationId, int recursionCheck) const; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph) | |||
}; | |||
} // namespace juce |
@@ -1,107 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Base class for listeners that want to know about changes to an AudioProcessor. | |||
Use AudioProcessor::addListener() to register your listener with an AudioProcessor. | |||
@see AudioProcessor | |||
*/ | |||
class JUCE_API AudioProcessorListener | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Destructor. */ | |||
virtual ~AudioProcessorListener() {} | |||
//============================================================================== | |||
/** Receives a callback when a parameter is changed. | |||
IMPORTANT NOTE: this will be called synchronously when a parameter changes, and | |||
many audio processors will change their parameter during their audio callback. | |||
This means that not only has your handler code got to be completely thread-safe, | |||
but it's also got to be VERY fast, and avoid blocking. If you need to handle | |||
this event on your message thread, use this callback to trigger an AsyncUpdater | |||
or ChangeBroadcaster which you can respond to on the message thread. | |||
*/ | |||
virtual void audioProcessorParameterChanged (AudioProcessor* processor, | |||
int parameterIndex, | |||
float newValue) = 0; | |||
/** Called to indicate that something else in the plugin has changed, like its | |||
program, number of parameters, etc. | |||
IMPORTANT NOTE: this will be called synchronously, and many audio processors will | |||
call it during their audio callback. This means that not only has your handler code | |||
got to be completely thread-safe, but it's also got to be VERY fast, and avoid | |||
blocking. If you need to handle this event on your message thread, use this callback | |||
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the | |||
message thread. | |||
*/ | |||
virtual void audioProcessorChanged (AudioProcessor* processor) = 0; | |||
/** Indicates that a parameter change gesture has started. | |||
E.g. if the user is dragging a slider, this would be called when they first | |||
press the mouse button, and audioProcessorParameterChangeGestureEnd would be | |||
called when they release it. | |||
IMPORTANT NOTE: this will be called synchronously, and many audio processors will | |||
call it during their audio callback. This means that not only has your handler code | |||
got to be completely thread-safe, but it's also got to be VERY fast, and avoid | |||
blocking. If you need to handle this event on your message thread, use this callback | |||
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the | |||
message thread. | |||
@see audioProcessorParameterChangeGestureEnd | |||
*/ | |||
virtual void audioProcessorParameterChangeGestureBegin (AudioProcessor* processor, | |||
int parameterIndex); | |||
/** Indicates that a parameter change gesture has finished. | |||
E.g. if the user is dragging a slider, this would be called when they release | |||
the mouse button. | |||
IMPORTANT NOTE: this will be called synchronously, and many audio processors will | |||
call it during their audio callback. This means that not only has your handler code | |||
got to be completely thread-safe, but it's also got to be VERY fast, and avoid | |||
blocking. If you need to handle this event on your message thread, use this callback | |||
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the | |||
message thread. | |||
@see audioProcessorParameterChangeGestureBegin | |||
*/ | |||
virtual void audioProcessorParameterChangeGestureEnd (AudioProcessor* processor, | |||
int parameterIndex); | |||
}; | |||
} // namespace juce |
@@ -1,197 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** An abstract base class for parameter objects that can be added to an | |||
AudioProcessor. | |||
@see AudioProcessor::addParameter | |||
*/ | |||
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 setValue() method to change the | |||
value, and will then send a message to the host telling it about the change. | |||
Note that to make sure the host correctly handles automation, you should call | |||
the beginChangeGesture() and endChangeGesture() methods to tell the host when | |||
the user has started and stopped changing the parameter. | |||
*/ | |||
void setValueNotifyingHost (float newValue); | |||
/** Sends a signal to the host to tell it that the user is about to start changing this | |||
parameter. | |||
This allows the host to know when a parameter is actively being held by the user, and | |||
it may use this information to help it record automation. | |||
If you call this, it must be matched by a later call to endChangeGesture(). | |||
*/ | |||
void beginChangeGesture(); | |||
/** Tells the host that the user has finished changing this parameter. | |||
This allows the host to know when a parameter is actively being held by the user, | |||
and it may use this information to help it record automation. | |||
A call to this method must follow a call to beginChangeGesture(). | |||
*/ | |||
void endChangeGesture(); | |||
/** This should return the default value for this parameter. */ | |||
virtual float getDefaultValue() const = 0; | |||
/** Returns the name to display for this parameter, which should be made | |||
to fit within the given string length. | |||
*/ | |||
virtual String getName (int maximumStringLength) const = 0; | |||
/** Some parameters may be able to return a label string for | |||
their units. For example "Hz" or "%". | |||
*/ | |||
virtual String getLabel() const = 0; | |||
/** Returns the number of steps that this parameter's range should be quantised into. | |||
If you want a continuous range of values, don't override this method, and allow | |||
the default implementation to return AudioProcessor::getDefaultNumParameterSteps(). | |||
If your parameter is boolean, then you may want to make this return 2. | |||
The value that is returned may or may not be used, depending on the host. If you | |||
want the host to display stepped automation values, rather than a continuous | |||
interpolation between successive values, you should override isDiscrete to return true. | |||
@see isDiscrete | |||
*/ | |||
virtual int getNumSteps() const; | |||
/** Returns whether the parameter uses discrete values, based on the result of | |||
getNumSteps, or allows the host to select values continuously. | |||
This information may or may not be used, depending on the host. If you | |||
want the host to display stepped automation values, rather than a continuous | |||
interpolation between successive values, override this method to return true. | |||
@see getNumSteps | |||
*/ | |||
virtual bool isDiscrete() const; | |||
/** Returns a textual version of the supplied parameter value. | |||
The default implementation just returns the floating point value | |||
as a string, but this could do anything you need for a custom type | |||
of value. | |||
*/ | |||
virtual String getText (float value, int /*maximumStringLength*/) const; | |||
/** Should parse a string and return the appropriate value for it. */ | |||
virtual float getValueForText (const String& text) const = 0; | |||
/** This can be overridden to tell the host that this parameter operates in the | |||
reverse direction. | |||
(Not all plugin formats or hosts will actually use this information). | |||
*/ | |||
virtual bool isOrientationInverted() const; | |||
/** Returns true if the host can automate this parameter. | |||
By default, this returns true. | |||
*/ | |||
virtual bool isAutomatable() const; | |||
/** Should return true if this parameter is a "meta" parameter. | |||
A meta-parameter is a parameter that changes other params. It is used | |||
by some hosts (e.g. AudioUnit hosts). | |||
By default this returns false. | |||
*/ | |||
virtual bool isMetaParameter() const; | |||
enum Category | |||
{ | |||
genericParameter = (0 << 16) | 0, /** If your parameter is not a meter then you should use this category */ | |||
inputGain = (1 << 16) | 0, /** Currently not used */ | |||
outputGain = (1 << 16) | 1, | |||
/** The following categories tell the host that this parameter is a meter level value | |||
and therefore read-only. Most hosts will display these type of parameters as | |||
a meter in the generic view of your plug-in. Pro-Tools will also show the meter | |||
in the mixer view. | |||
*/ | |||
inputMeter = (2 << 16) | 0, | |||
outputMeter = (2 << 16) | 1, | |||
compressorLimiterGainReductionMeter = (2 << 16) | 2, | |||
expanderGateGainReductionMeter = (2 << 16) | 3, | |||
analysisMeter = (2 << 16) | 4, | |||
otherMeter = (2 << 16) | 5 | |||
}; | |||
/** Returns the parameter's category. */ | |||
virtual Category getCategory() const; | |||
/** Returns the index of this parameter in its parent processor's parameter list. */ | |||
int getParameterIndex() const noexcept { return parameterIndex; } | |||
private: | |||
friend class AudioProcessor; | |||
AudioProcessor* processor; | |||
int parameterIndex; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameter) | |||
}; | |||
} // namespace juce |
@@ -1,192 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
class 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(); | |||
startTimerHz (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); | |||
const AudioProcessorParameter::Category category = p.getParameterCategory (index); | |||
const bool isLevelMeter = (((category & 0xffff0000) >> 16) == 2); | |||
if (steps > 1 && steps < 0x7fffffff) | |||
setRange (0.0, 1.0, 1.0 / (steps - 1.0)); | |||
else | |||
setRange (0.0, 1.0); | |||
setEnabled (! isLevelMeter); | |||
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(); | |||
} | |||
} | |||
void startedDragging() override | |||
{ | |||
owner.beginParameterChangeGesture(index); | |||
} | |||
void stoppedDragging() override | |||
{ | |||
owner.endParameterChangeGesture(index); | |||
} | |||
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()); | |||
} | |||
} // namespace juce |
@@ -1,58 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A type of UI component that displays the parameters of an AudioProcessor as | |||
a simple list of sliders. | |||
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) | |||
}; | |||
} // namespace juce |
@@ -1,151 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
PluginDescription::PluginDescription() | |||
: uid (0), | |||
isInstrument (false), | |||
numInputChannels (0), | |||
numOutputChannels (0), | |||
hasSharedContainer (false) | |||
{ | |||
} | |||
PluginDescription::~PluginDescription() | |||
{ | |||
} | |||
PluginDescription::PluginDescription (const PluginDescription& other) | |||
: name (other.name), | |||
descriptiveName (other.descriptiveName), | |||
pluginFormatName (other.pluginFormatName), | |||
category (other.category), | |||
manufacturerName (other.manufacturerName), | |||
version (other.version), | |||
fileOrIdentifier (other.fileOrIdentifier), | |||
lastFileModTime (other.lastFileModTime), | |||
lastInfoUpdateTime (other.lastInfoUpdateTime), | |||
uid (other.uid), | |||
isInstrument (other.isInstrument), | |||
numInputChannels (other.numInputChannels), | |||
numOutputChannels (other.numOutputChannels), | |||
hasSharedContainer (other.hasSharedContainer) | |||
{ | |||
} | |||
PluginDescription& PluginDescription::operator= (const PluginDescription& other) | |||
{ | |||
name = other.name; | |||
descriptiveName = other.descriptiveName; | |||
pluginFormatName = other.pluginFormatName; | |||
category = other.category; | |||
manufacturerName = other.manufacturerName; | |||
version = other.version; | |||
fileOrIdentifier = other.fileOrIdentifier; | |||
uid = other.uid; | |||
isInstrument = other.isInstrument; | |||
lastFileModTime = other.lastFileModTime; | |||
lastInfoUpdateTime = other.lastInfoUpdateTime; | |||
numInputChannels = other.numInputChannels; | |||
numOutputChannels = other.numOutputChannels; | |||
hasSharedContainer = other.hasSharedContainer; | |||
return *this; | |||
} | |||
bool PluginDescription::isDuplicateOf (const PluginDescription& other) const noexcept | |||
{ | |||
return fileOrIdentifier == other.fileOrIdentifier | |||
&& uid == other.uid; | |||
} | |||
static String getPluginDescSuffix (const PluginDescription& d) | |||
{ | |||
return "-" + String::toHexString (d.fileOrIdentifier.hashCode()) | |||
+ "-" + String::toHexString (d.uid); | |||
} | |||
bool PluginDescription::matchesIdentifierString (const String& identifierString) const | |||
{ | |||
return identifierString.endsWithIgnoreCase (getPluginDescSuffix (*this)); | |||
} | |||
String PluginDescription::createIdentifierString() const | |||
{ | |||
return pluginFormatName + "-" + name + getPluginDescSuffix (*this); | |||
} | |||
XmlElement* PluginDescription::createXml() const | |||
{ | |||
XmlElement* const e = new XmlElement ("PLUGIN"); | |||
e->setAttribute ("name", name); | |||
if (descriptiveName != name) | |||
e->setAttribute ("descriptiveName", descriptiveName); | |||
e->setAttribute ("format", pluginFormatName); | |||
e->setAttribute ("category", category); | |||
e->setAttribute ("manufacturer", manufacturerName); | |||
e->setAttribute ("version", version); | |||
e->setAttribute ("file", fileOrIdentifier); | |||
e->setAttribute ("uid", String::toHexString (uid)); | |||
e->setAttribute ("isInstrument", isInstrument); | |||
e->setAttribute ("fileTime", String::toHexString (lastFileModTime.toMilliseconds())); | |||
e->setAttribute ("infoUpdateTime", String::toHexString (lastInfoUpdateTime.toMilliseconds())); | |||
e->setAttribute ("numInputs", numInputChannels); | |||
e->setAttribute ("numOutputs", numOutputChannels); | |||
e->setAttribute ("isShell", hasSharedContainer); | |||
return e; | |||
} | |||
bool PluginDescription::loadFromXml (const XmlElement& xml) | |||
{ | |||
if (xml.hasTagName ("PLUGIN")) | |||
{ | |||
name = xml.getStringAttribute ("name"); | |||
descriptiveName = xml.getStringAttribute ("descriptiveName", name); | |||
pluginFormatName = xml.getStringAttribute ("format"); | |||
category = xml.getStringAttribute ("category"); | |||
manufacturerName = xml.getStringAttribute ("manufacturer"); | |||
version = xml.getStringAttribute ("version"); | |||
fileOrIdentifier = xml.getStringAttribute ("file"); | |||
uid = xml.getStringAttribute ("uid").getHexValue32(); | |||
isInstrument = xml.getBoolAttribute ("isInstrument", false); | |||
lastFileModTime = Time (xml.getStringAttribute ("fileTime").getHexValue64()); | |||
lastInfoUpdateTime = Time (xml.getStringAttribute ("infoUpdateTime").getHexValue64()); | |||
numInputChannels = xml.getIntAttribute ("numInputs"); | |||
numOutputChannels = xml.getIntAttribute ("numOutputs"); | |||
hasSharedContainer = xml.getBoolAttribute ("isShell", false); | |||
return true; | |||
} | |||
return false; | |||
} | |||
} // namespace juce |
@@ -1,156 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A small class to represent some facts about a particular type of plug-in. | |||
This class is for storing and managing the details about a plug-in without | |||
actually having to load an instance of it. | |||
A KnownPluginList contains a list of PluginDescription objects. | |||
@see KnownPluginList | |||
*/ | |||
class JUCE_API PluginDescription | |||
{ | |||
public: | |||
//============================================================================== | |||
PluginDescription(); | |||
PluginDescription (const PluginDescription& other); | |||
PluginDescription& operator= (const PluginDescription& other); | |||
~PluginDescription(); | |||
//============================================================================== | |||
/** The name of the plug-in. */ | |||
String name; | |||
/** A more descriptive name for the plug-in. | |||
This may be the same as the 'name' field, but some plug-ins may provide an | |||
alternative name. | |||
*/ | |||
String descriptiveName; | |||
/** The plug-in format, e.g. "VST", "AudioUnit", etc. */ | |||
String pluginFormatName; | |||
/** A category, such as "Dynamics", "Reverbs", etc. */ | |||
String category; | |||
/** The manufacturer. */ | |||
String manufacturerName; | |||
/** The version. This string doesn't have any particular format. */ | |||
String version; | |||
/** Either the file containing the plug-in module, or some other unique way | |||
of identifying it. | |||
E.g. for an AU, this would be an ID string that the component manager | |||
could use to retrieve the plug-in. For a VST, it's the file path. | |||
*/ | |||
String fileOrIdentifier; | |||
/** The last time the plug-in file was changed. | |||
This is handy when scanning for new or changed plug-ins. | |||
*/ | |||
Time lastFileModTime; | |||
/** The last time that this information was updated. This would typically have | |||
been during a scan when this plugin was first tested or found to have changed. | |||
*/ | |||
Time lastInfoUpdateTime; | |||
/** A unique ID for the plug-in. | |||
Note that this might not be unique between formats, e.g. a VST and some | |||
other format might actually have the same id. | |||
@see createIdentifierString | |||
*/ | |||
int uid; | |||
/** True if the plug-in identifies itself as a synthesiser. */ | |||
bool isInstrument; | |||
/** The number of inputs. */ | |||
int numInputChannels; | |||
/** The number of outputs. */ | |||
int numOutputChannels; | |||
/** True if the plug-in is part of a multi-type container, e.g. a VST Shell. */ | |||
bool hasSharedContainer; | |||
/** Returns true if the two descriptions refer to the same plug-in. | |||
This isn't quite as simple as them just having the same file (because of | |||
shell plug-ins). | |||
*/ | |||
bool isDuplicateOf (const PluginDescription& other) const noexcept; | |||
/** Return true if this description is equivalent to another one which created the | |||
given identifier string. | |||
Note that this isn't quite as simple as them just calling createIdentifierString() | |||
and comparing the strings, because the identifiers can differ (thanks to shell plug-ins). | |||
*/ | |||
bool matchesIdentifierString (const String& identifierString) const; | |||
//============================================================================== | |||
/** Returns a string that can be saved and used to uniquely identify the | |||
plugin again. | |||
This contains less info than the XML encoding, and is independent of the | |||
plug-in's file location, so can be used to store a plug-in ID for use | |||
across different machines. | |||
*/ | |||
String createIdentifierString() const; | |||
//============================================================================== | |||
/** Creates an XML object containing these details. | |||
@see loadFromXml | |||
*/ | |||
XmlElement* createXml() const; | |||
/** Reloads the info in this structure from an XML record that was previously | |||
saved with createXML(). | |||
Returns true if the XML was a valid plug-in description. | |||
*/ | |||
bool loadFromXml (const XmlElement& xml); | |||
private: | |||
//============================================================================== | |||
JUCE_LEAK_DETECTOR (PluginDescription) | |||
}; | |||
} // namespace juce |
@@ -1,587 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
KnownPluginList::KnownPluginList() {} | |||
KnownPluginList::~KnownPluginList() {} | |||
void KnownPluginList::clear() | |||
{ | |||
ScopedLock lock (typesArrayLock); | |||
if (! types.isEmpty()) | |||
{ | |||
types.clear(); | |||
sendChangeMessage(); | |||
} | |||
} | |||
PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifier) const | |||
{ | |||
ScopedLock lock (typesArrayLock); | |||
for (auto* desc : types) | |||
if (desc->fileOrIdentifier == fileOrIdentifier) | |||
return desc; | |||
return nullptr; | |||
} | |||
PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const | |||
{ | |||
ScopedLock lock (typesArrayLock); | |||
for (auto* desc : types) | |||
if (desc->matchesIdentifierString (identifierString)) | |||
return desc; | |||
return nullptr; | |||
} | |||
bool KnownPluginList::addType (const PluginDescription& type) | |||
{ | |||
{ | |||
ScopedLock lock (typesArrayLock); | |||
for (auto* desc : types) | |||
{ | |||
if (desc->isDuplicateOf (type)) | |||
{ | |||
// strange - found a duplicate plugin with different info.. | |||
jassert (desc->name == type.name); | |||
jassert (desc->isInstrument == type.isInstrument); | |||
*desc = type; | |||
return false; | |||
} | |||
} | |||
types.insert (0, new PluginDescription (type)); | |||
} | |||
sendChangeMessage(); | |||
return true; | |||
} | |||
void KnownPluginList::removeType (const int index) | |||
{ | |||
{ | |||
ScopedLock lock (typesArrayLock); | |||
types.remove (index); | |||
} | |||
sendChangeMessage(); | |||
} | |||
bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier, | |||
AudioPluginFormat& formatToUse) const | |||
{ | |||
if (getTypeForFile (fileOrIdentifier) == nullptr) | |||
return false; | |||
ScopedLock lock (typesArrayLock); | |||
for (auto* d : types) | |||
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; | |||
ScopedLock lock (typesArrayLock); | |||
for (auto* d : types) | |||
{ | |||
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 (auto* desc : found) | |||
{ | |||
jassert (desc != nullptr); | |||
addType (*desc); | |||
typesFound.add (new PluginDescription (*desc)); | |||
} | |||
return ! found.isEmpty(); | |||
} | |||
void KnownPluginList::scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager, | |||
const StringArray& files, | |||
OwnedArray<PluginDescription>& typesFound) | |||
{ | |||
for (const auto& filenameOrID : files) | |||
{ | |||
bool found = false; | |||
for (int j = 0; j < formatManager.getNumFormats(); ++j) | |||
{ | |||
auto* 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 (auto& subFile : subFiles) | |||
s.add (subFile.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, true); break; | |||
case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareNatural (second->manufacturerName, true); break; | |||
case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break; | |||
case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break; | |||
case KnownPluginList::sortByInfoUpdateTime: diff = compare (first->lastInfoUpdateTime, second->lastInfoUpdateTime); break; | |||
default: break; | |||
} | |||
if (diff == 0) | |||
diff = first->name.compareNatural (second->name, true); | |||
return diff * direction; | |||
} | |||
private: | |||
static String lastPathPart (const String& path) | |||
{ | |||
return path.replaceCharacter ('\\', '/').upToLastOccurrenceOf ("/", false, false); | |||
} | |||
static int compare (Time a, Time b) noexcept | |||
{ | |||
if (a < b) return -1; | |||
if (b < a) return 1; | |||
return 0; | |||
} | |||
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; | |||
{ | |||
ScopedLock lock (typesArrayLock); | |||
oldOrder.addArray (types); | |||
PluginSorter sorter (method, forwards); | |||
types.sort (sorter, true); | |||
newOrder.addArray (types); | |||
} | |||
if (oldOrder != newOrder) | |||
sendChangeMessage(); | |||
} | |||
} | |||
//============================================================================== | |||
XmlElement* KnownPluginList::createXml() const | |||
{ | |||
auto e = new XmlElement ("KNOWNPLUGINS"); | |||
{ | |||
ScopedLock lock (typesArrayLock); | |||
for (int i = types.size(); --i >= 0;) | |||
e->prependChildElement (types.getUnchecked(i)->createXml()); | |||
} | |||
for (auto& b : blacklist) | |||
e->createNewChildElement ("BLACKLISTED")->setAttribute ("id", b); | |||
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 (auto* pd : allPlugins) | |||
{ | |||
auto 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;) | |||
{ | |||
auto& sub = *tree.subFolders.getUnchecked(i); | |||
optimiseFolders (sub, concatenateName || (tree.subFolders.size() > 1)); | |||
if (sub.plugins.isEmpty()) | |||
{ | |||
for (auto* s : sub.subFolders) | |||
{ | |||
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 (auto* pd : sorted) | |||
{ | |||
auto thisType = (sortMethod == KnownPluginList::sortByCategory ? pd->category | |||
: pd->manufacturerName); | |||
if (! thisType.containsNonWhitespaceChars()) | |||
thisType = "Other"; | |||
if (! thisType.equalsIgnoreCase (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 | |||
auto firstSubFolder = path.upToFirstOccurrenceOf ("/", false, false); | |||
auto 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; | |||
} | |||
} | |||
auto 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 bool addToMenu (const KnownPluginList::PluginTree& tree, PopupMenu& m, | |||
const OwnedArray<PluginDescription>& allPlugins, | |||
const String& currentlyTickedPluginID) | |||
{ | |||
bool isTicked = false; | |||
for (auto* sub : tree.subFolders) | |||
{ | |||
PopupMenu subMenu; | |||
const bool isItemTicked = addToMenu (*sub, subMenu, allPlugins, currentlyTickedPluginID); | |||
isTicked = isTicked || isItemTicked; | |||
m.addSubMenu (sub->folder, subMenu, true, nullptr, isItemTicked, 0); | |||
} | |||
for (auto* plugin : tree.plugins) | |||
{ | |||
auto name = plugin->name; | |||
if (containsDuplicateNames (tree.plugins, name)) | |||
name << " (" << plugin->pluginFormatName << ')'; | |||
const bool isItemTicked = plugin->matchesIdentifierString (currentlyTickedPluginID); | |||
isTicked = isTicked || isItemTicked; | |||
m.addItem (allPlugins.indexOf (plugin) + menuIdBase, name, true, isItemTicked); | |||
} | |||
return isTicked; | |||
} | |||
}; | |||
KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortMethod) const | |||
{ | |||
Array<PluginDescription*> sorted; | |||
{ | |||
ScopedLock lock (typesArrayLock); | |||
PluginSorter sorter (sortMethod, true); | |||
for (auto* t : types) | |||
sorted.addSorted (sorter, t); | |||
} | |||
auto* 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 (auto* p : sorted) | |||
tree->plugins.add (p); | |||
} | |||
return tree; | |||
} | |||
//============================================================================== | |||
void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod, | |||
const String& currentlyTickedPluginID) const | |||
{ | |||
ScopedPointer<PluginTree> tree (createTree (sortMethod)); | |||
PluginTreeUtils::addToMenu (*tree, menu, types, currentlyTickedPluginID); | |||
} | |||
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 (auto* job = ThreadPoolJob::getCurrentThreadPoolJob()) | |||
return job->shouldExit(); | |||
return false; | |||
} | |||
} // namespace juce |
@@ -1,225 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
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, | |||
sortByInfoUpdateTime | |||
}; | |||
//============================================================================== | |||
/** 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 String& currentlyTickedPluginID = String()) 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, typesArrayLock; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownPluginList) | |||
}; | |||
} // namespace juce |
@@ -1,136 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
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, | |||
bool allowPluginsWhichRequireAsynchronousInstantiation) | |||
: list (listToAddTo), | |||
format (formatToLookFor), | |||
deadMansPedalFile (deadMansPedal), | |||
allowAsync (allowPluginsWhichRequireAsynchronousInstantiation) | |||
{ | |||
directoriesToSearch.removeRedundantPaths(); | |||
filesOrIdentifiersToScan = format.searchPathsForPlugins (directoriesToSearch, recursive, allowAsync); | |||
// 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.. | |||
for (auto& crashed : readDeadMansPedalFile (deadMansPedalFile)) | |||
for (int j = filesOrIdentifiersToScan.size(); --j >= 0;) | |||
if (crashed == 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 (bool dontRescanIfAlreadyInList, | |||
String& nameOfPluginBeingScanned) | |||
{ | |||
const int index = --nextIndex; | |||
if (index >= 0) | |||
{ | |||
auto file = filesOrIdentifiersToScan [index]; | |||
if (file.isNotEmpty() && ! (dontRescanIfAlreadyInList && 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... | |||
auto 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.. | |||
for (auto& crashedPlugin : readDeadMansPedalFile (file)) | |||
list.addToBlacklist (crashedPlugin); | |||
} | |||
} // namespace juce |
@@ -1,131 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
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(), 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. | |||
@param allowPluginsWhichRequireAsynchronousInstantiation | |||
If this is false then the scanner will exclude plug-ins | |||
asynchronous creation - such as AUv3 plug-ins. | |||
*/ | |||
PluginDirectoryScanner (KnownPluginList& listToAddResultsTo, | |||
AudioPluginFormat& formatToLookFor, | |||
FileSearchPath directoriesToSearch, | |||
bool searchRecursively, | |||
const File& deadMansPedalFile, | |||
bool allowPluginsWhichRequireAsynchronousInstantiation = false); | |||
/** 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 = 0; | |||
const bool allowAsync; | |||
void updateProgress(); | |||
void setDeadMansPedalFile (const StringArray& newContents); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginDirectoryScanner) | |||
}; | |||
} // namespace juce |
@@ -1,593 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
class 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 | |||
{ | |||
const auto defaultColour = owner.findColour (ListBox::backgroundColourId); | |||
const auto c = rowIsSelected ? defaultColour.interpolatedWith (owner.findColour (ListBox::textColourId), 0.5f) | |||
: defaultColour; | |||
g.fillAll (c); | |||
} | |||
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()) | |||
{ | |||
const auto defaultTextColour = owner.findColour (ListBox::textColourId); | |||
g.setColour (isBlacklisted ? Colours::red | |||
: columnId == nameCol ? defaultTextColour | |||
: defaultTextColour.interpolatedWith (Colours::transparentBlack, 0.3f)); | |||
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.removeSelectedPlugins(); | |||
} | |||
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 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, | |||
bool allowPluginsWhichRequireAsynchronousInstantiation) | |||
: formatManager (manager), | |||
list (listToEdit), | |||
deadMansPedalFile (deadMansPedal), | |||
optionsButton ("Options..."), | |||
propertiesToUse (props), | |||
allowAsync (allowPluginsWhichRequireAsynchronousInstantiation), | |||
numThreads (allowAsync ? 1 : 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::setScanDialogText (const String& title, const String& content) | |||
{ | |||
dialogTitle = title; | |||
dialogText = content; | |||
} | |||
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::removeSelectedPlugins() | |||
{ | |||
const SparseSet<int> selected (table.getSelectedRows()); | |||
for (int i = table.getNumRows(); --i >= 0;) | |||
if (selected.contains (i)) | |||
removePluginItem (i); | |||
} | |||
void PluginListComponent::setTableModel (TableListBoxModel* model) | |||
{ | |||
table.setModel (nullptr); | |||
tableModel = model; | |||
table.setModel (tableModel); | |||
table.getHeader().reSortTable(); | |||
table.updateContent(); | |||
table.repaint(); | |||
} | |||
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::removePluginItem (int index) | |||
{ | |||
if (index < list.getNumTypes()) | |||
list.removeType (index); | |||
else | |||
list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); | |||
} | |||
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: removeSelectedPlugins(); 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, | |||
bool allowPluginsWhichRequireAsynchronousInstantiation, int threads, | |||
const String& title, const String& text) | |||
: owner (plc), formatToScan (format), propertiesToUse (properties), | |||
pathChooserWindow (TRANS("Select folders to scan..."), String(), AlertWindow::NoIcon), | |||
progressWindow (title, text, AlertWindow::NoIcon), | |||
progress (0.0), numThreads (threads), allowAsync (allowPluginsWhichRequireAsynchronousInstantiation), | |||
finished (false) | |||
{ | |||
FileSearchPath path (formatToScan.getDefaultLocationsToSearch()); | |||
// You need to use at least one thread when scanning plug-ins asynchronously | |||
jassert (! allowAsync || (numThreads > 0)); | |||
if (path.getNumPaths() > 0) // if the path is empty, then paths aren't used for this format. | |||
{ | |||
#if ! JUCE_IOS | |||
if (propertiesToUse != nullptr) | |||
path = getLastSearchPath (*propertiesToUse, formatToScan); | |||
#endif | |||
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 allowAsync, 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(), | |||
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, allowAsync); | |||
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, allowAsync, numThreads, | |||
dialogTitle.isNotEmpty() ? dialogTitle : TRANS("Scanning for plug-ins..."), | |||
dialogText.isNotEmpty() ? dialogText : TRANS("Searching for all possible plug-in files...")); | |||
} | |||
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 (", ")); | |||
} | |||
} // namespace juce |
@@ -1,132 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A 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 Button::Listener | |||
{ | |||
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, | |||
bool allowPluginsWhichRequireAsynchronousInstantiation = false); | |||
/** Destructor. */ | |||
~PluginListComponent(); | |||
/** Changes the text in the panel's options button. */ | |||
void setOptionsButtonText (const String& newText); | |||
/** Changes the text in the progress dialog box that is shown when scanning. */ | |||
void setScanDialogText (const String& textForProgressWindowTitle, | |||
const String& textForProgressWindowDescription); | |||
/** 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 when | |||
allowPluginsWhichRequireAsynchronousInstantiation is false). If | |||
allowPluginsWhichRequireAsynchronousInstantiation is true then numThreads must not | |||
be zero (it is one by 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; | |||
/** Removes the plugins currently selected in the table. */ | |||
void removeSelectedPlugins(); | |||
/** Sets a custom table model to be used. | |||
This will take ownership of the model and delete it when no longer needed. | |||
*/ | |||
void setTableModel (TableListBoxModel* model); | |||
/** Returns the table used to display the plugin list. */ | |||
TableListBox& getTableListBox() noexcept { return table; } | |||
private: | |||
//============================================================================== | |||
AudioPluginFormatManager& formatManager; | |||
KnownPluginList& list; | |||
File deadMansPedalFile; | |||
TableListBox table; | |||
TextButton optionsButton; | |||
PropertiesFile* propertiesToUse; | |||
String dialogTitle, dialogText; | |||
bool allowAsync; | |||
int numThreads; | |||
class TableModel; | |||
ScopedPointer<TableListBoxModel> 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 removeMissingPlugins(); | |||
void removePluginItem (int index); | |||
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) | |||
}; | |||
} // namespace juce |
@@ -1,71 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
/** | |||
Provides a class of AudioProcessorParameter that can be used as a boolean value. | |||
@see AudioParameterFloat, AudioParameterInt, AudioParameterChoice | |||
*/ | |||
class JUCE_API AudioParameterBool : public AudioProcessorParameterWithID | |||
{ | |||
public: | |||
/** Creates a AudioParameterBool with an ID and name. | |||
On creation, its value is set to the default value. | |||
*/ | |||
AudioParameterBool (const String& parameterID, const String& name, bool defaultValue, | |||
const String& label = String()); | |||
/** Destructor. */ | |||
~AudioParameterBool(); | |||
/** Returns the parameter's current boolean value. */ | |||
bool get() const noexcept { return value >= 0.5f; } | |||
/** Returns the parameter's current boolean value. */ | |||
operator bool() const noexcept { return get(); } | |||
/** Changes the parameter's current value to a new boolean. */ | |||
AudioParameterBool& operator= (bool newValue); | |||
private: | |||
//============================================================================== | |||
float value, defaultValue; | |||
float getValue() const override; | |||
void setValue (float newValue) override; | |||
float getDefaultValue() const override; | |||
int getNumSteps() const override; | |||
bool isDiscrete() const override; | |||
String getText (float, int) const override; | |||
float getValueForText (const String&) const override; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterBool) | |||
}; | |||
} // namespace juce |
@@ -1,86 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
/** | |||
Provides a class of AudioProcessorParameter that can be used to select | |||
an indexed, named choice from a list. | |||
@see AudioParameterFloat, AudioParameterInt, AudioParameterBool | |||
*/ | |||
class JUCE_API AudioParameterChoice : public AudioProcessorParameterWithID | |||
{ | |||
public: | |||
/** Creates a AudioParameterChoice with an ID, name, and list of items. | |||
On creation, its value is set to the default index. | |||
*/ | |||
AudioParameterChoice (const String& parameterID, const String& name, | |||
const StringArray& choices, | |||
int defaultItemIndex, | |||
const String& label = String()); | |||
/** Destructor. */ | |||
~AudioParameterChoice(); | |||
/** Returns the current index of the selected item. */ | |||
int getIndex() const noexcept { return roundToInt (value); } | |||
/** Returns the current index of the selected item. */ | |||
operator int() const noexcept { return getIndex(); } | |||
/** Returns the name of the currently selected item. */ | |||
String getCurrentChoiceName() const noexcept { return choices[getIndex()]; } | |||
/** Returns the name of the currently selected item. */ | |||
operator String() const noexcept { return getCurrentChoiceName(); } | |||
/** Changes the selected item to a new index. */ | |||
AudioParameterChoice& operator= (int newValue); | |||
/** Provides access to the list of choices that this parameter is working with. */ | |||
const StringArray choices; | |||
private: | |||
//============================================================================== | |||
float value, defaultValue; | |||
float getValue() const override; | |||
void setValue (float newValue) override; | |||
float getDefaultValue() const override; | |||
int getNumSteps() const override; | |||
bool isDiscrete() const override; | |||
String getText (float, int) const override; | |||
float getValueForText (const String&) const override; | |||
int limitRange (int) const noexcept; | |||
float convertTo0to1 (int) const noexcept; | |||
int convertFrom0to1 (float) const noexcept; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterChoice) | |||
}; | |||
} // namespace juce |
@@ -1,87 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
/** | |||
A subclass of AudioProcessorParameter that provides an easy way to create a | |||
parameter which maps onto a given NormalisableRange. | |||
@see AudioParameterInt, AudioParameterBool, AudioParameterChoice | |||
*/ | |||
class JUCE_API AudioParameterFloat : public AudioProcessorParameterWithID | |||
{ | |||
public: | |||
/** Creates a AudioParameterFloat with an ID, name, and range. | |||
On creation, its value is set to the default value. | |||
*/ | |||
AudioParameterFloat (const String& parameterID, const String& name, | |||
NormalisableRange<float> normalisableRange, | |||
float defaultValue, | |||
const String& label = String(), | |||
Category category = AudioProcessorParameter::genericParameter); | |||
/** Creates a AudioParameterFloat with an ID, name, and range. | |||
On creation, its value is set to the default value. | |||
For control over skew factors, you can use the other | |||
constructor and provide a NormalisableRange. | |||
*/ | |||
AudioParameterFloat (String parameterID, String name, | |||
float minValue, | |||
float maxValue, | |||
float defaultValue); | |||
/** Destructor. */ | |||
~AudioParameterFloat(); | |||
/** Returns the parameter's current value. */ | |||
float get() const noexcept { return value; } | |||
/** Returns the parameter's current value. */ | |||
operator float() const noexcept { return value; } | |||
/** Changes the parameter's current value. */ | |||
AudioParameterFloat& operator= (float newValue); | |||
/** Provides access to the parameter's range. */ | |||
NormalisableRange<float> range; | |||
private: | |||
//============================================================================== | |||
float value, defaultValue; | |||
float getValue() const override; | |||
void setValue (float newValue) override; | |||
float getDefaultValue() const override; | |||
int getNumSteps() const override; | |||
String getText (float, int) const override; | |||
float getValueForText (const String&) const override; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterFloat) | |||
}; | |||
} // namespace juce |
@@ -1,84 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
/** | |||
Provides a class of AudioProcessorParameter that can be used as an | |||
integer value with a given range. | |||
@see AudioParameterFloat, AudioParameterBool, AudioParameterChoice | |||
*/ | |||
class JUCE_API AudioParameterInt : public AudioProcessorParameterWithID | |||
{ | |||
public: | |||
/** Creates an AudioParameterInt with an ID, name, and range. | |||
Note that the min and max range values are inclusive. | |||
On creation, its value is set to the default value. | |||
*/ | |||
AudioParameterInt (const String& parameterID, const String& name, | |||
int minValue, int maxValue, | |||
int defaultValue, | |||
const String& label = String()); | |||
/** Destructor. */ | |||
~AudioParameterInt(); | |||
/** Returns the parameter's current value as an integer. */ | |||
int get() const noexcept { return roundToInt (value); } | |||
/** Returns the parameter's current value as an integer. */ | |||
operator int() const noexcept { return get(); } | |||
/** Changes the parameter's current value to a new integer. | |||
The value passed in will be snapped to the permitted range before being used. | |||
*/ | |||
AudioParameterInt& operator= (int newValue); | |||
/** Returns the parameter's range. */ | |||
Range<int> getRange() const noexcept { return Range<int> (minValue, maxValue); } | |||
private: | |||
//============================================================================== | |||
int minValue, maxValue; | |||
float value, defaultValue; | |||
float getValue() const override; | |||
void setValue (float newValue) override; | |||
float getDefaultValue() const override; | |||
int getNumSteps() const override; | |||
String getText (float, int) const override; | |||
float getValueForText (const String&) const override; | |||
int limitRange (int) const noexcept; | |||
float convertTo0to1 (int) const noexcept; | |||
int convertFrom0to1 (float) const noexcept; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterInt) | |||
}; | |||
} // namespace juce |
@@ -1,69 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
/** | |||
This abstract base class is used by some AudioProcessorParameter helper classes. | |||
@see AudioParameterFloat, AudioParameterInt, AudioParameterBool, AudioParameterChoice | |||
*/ | |||
class JUCE_API AudioProcessorParameterWithID : public AudioProcessorParameter | |||
{ | |||
public: | |||
/** Creation of this object requires providing a name and ID which will be | |||
constant for its lifetime. | |||
*/ | |||
AudioProcessorParameterWithID (const String& parameterID, | |||
const String& name, | |||
const String& label = String(), | |||
Category category = AudioProcessorParameter::genericParameter); | |||
/** Destructor. */ | |||
~AudioProcessorParameterWithID(); | |||
/** Provides access to the parameter's ID string. */ | |||
const String paramID; | |||
/** Provides access to the parameter's name. */ | |||
const String name; | |||
/** Provides access to the parameter's label. */ | |||
const String label; | |||
/** Provides access to the parameter's category. */ | |||
const Category category; | |||
private: | |||
String getName (int) const override; | |||
String getLabel() const override; | |||
Category getCategory() const override; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameterWithID) | |||
}; | |||
} // namespace juce |
@@ -1,175 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
// This file contains the implementations of the various AudioParameter[XYZ] classes.. | |||
AudioProcessorParameterWithID::AudioProcessorParameterWithID (const String& idToUse, | |||
const String& nameToUse, | |||
const String& labelToUse, | |||
AudioProcessorParameter::Category categoryToUse) | |||
: paramID (idToUse), name (nameToUse), label (labelToUse), category (categoryToUse) {} | |||
AudioProcessorParameterWithID::~AudioProcessorParameterWithID() {} | |||
String AudioProcessorParameterWithID::getName (int maximumStringLength) const { return name.substring (0, maximumStringLength); } | |||
String AudioProcessorParameterWithID::getLabel() const { return label; } | |||
AudioProcessorParameter::Category AudioProcessorParameterWithID::getCategory() const { return category; } | |||
//============================================================================== | |||
AudioParameterFloat::AudioParameterFloat (const String& idToUse, const String& nameToUse, | |||
NormalisableRange<float> r, float def, | |||
const String& labelToUse, Category categoryToUse) | |||
: AudioProcessorParameterWithID (idToUse, nameToUse, labelToUse, categoryToUse), | |||
range (r), value (def), defaultValue (def) | |||
{ | |||
} | |||
AudioParameterFloat::AudioParameterFloat (String pid, String nm, float minValue, float maxValue, float def) | |||
: AudioProcessorParameterWithID (pid, nm), range (minValue, maxValue), value (def), defaultValue (def) | |||
{ | |||
} | |||
AudioParameterFloat::~AudioParameterFloat() {} | |||
float AudioParameterFloat::getValue() const { return range.convertTo0to1 (value); } | |||
void AudioParameterFloat::setValue (float newValue) { value = range.convertFrom0to1 (newValue); } | |||
float AudioParameterFloat::getDefaultValue() const { return range.convertTo0to1 (defaultValue); } | |||
int AudioParameterFloat::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); } | |||
float AudioParameterFloat::getValueForText (const String& text) const { return range.convertTo0to1 (text.getFloatValue()); } | |||
String AudioParameterFloat::getText (float v, int length) const | |||
{ | |||
String asText (range.convertFrom0to1 (v), 2); | |||
return length > 0 ? asText.substring (0, length) : asText; | |||
} | |||
AudioParameterFloat& AudioParameterFloat::operator= (float newValue) | |||
{ | |||
if (value != newValue) | |||
setValueNotifyingHost (range.convertTo0to1 (newValue)); | |||
return *this; | |||
} | |||
//============================================================================== | |||
AudioParameterInt::AudioParameterInt (const String& idToUse, const String& nameToUse, | |||
int mn, int mx, int def, | |||
const String& labelToUse) | |||
: AudioProcessorParameterWithID (idToUse, nameToUse, labelToUse), | |||
minValue (mn), maxValue (mx), | |||
value ((float) def), | |||
defaultValue (convertTo0to1 (def)) | |||
{ | |||
jassert (minValue < maxValue); // must have a non-zero range of values! | |||
} | |||
AudioParameterInt::~AudioParameterInt() {} | |||
int AudioParameterInt::limitRange (int v) const noexcept { return jlimit (minValue, maxValue, v); } | |||
float AudioParameterInt::convertTo0to1 (int v) const noexcept { return (limitRange (v) - minValue) / (float) (maxValue - minValue); } | |||
int AudioParameterInt::convertFrom0to1 (float v) const noexcept { return limitRange (roundToInt ((v * (float) (maxValue - minValue)) + minValue)); } | |||
float AudioParameterInt::getValue() const { return convertTo0to1 (roundToInt (value)); } | |||
void AudioParameterInt::setValue (float newValue) { value = (float) convertFrom0to1 (newValue); } | |||
float AudioParameterInt::getDefaultValue() const { return defaultValue; } | |||
int AudioParameterInt::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); } | |||
float AudioParameterInt::getValueForText (const String& text) const { return convertTo0to1 (text.getIntValue()); } | |||
String AudioParameterInt::getText (float v, int /*length*/) const { return String (convertFrom0to1 (v)); } | |||
AudioParameterInt& AudioParameterInt::operator= (int newValue) | |||
{ | |||
if (get() != newValue) | |||
setValueNotifyingHost (convertTo0to1 (newValue)); | |||
return *this; | |||
} | |||
//============================================================================== | |||
AudioParameterBool::AudioParameterBool (const String& idToUse, const String& nameToUse, | |||
bool def, const String& labelToUse) | |||
: AudioProcessorParameterWithID (idToUse, nameToUse, labelToUse), | |||
value (def ? 1.0f : 0.0f), | |||
defaultValue (value) | |||
{ | |||
} | |||
AudioParameterBool::~AudioParameterBool() {} | |||
float AudioParameterBool::getValue() const { return value; } | |||
void AudioParameterBool::setValue (float newValue) { value = newValue; } | |||
float AudioParameterBool::getDefaultValue() const { return defaultValue; } | |||
int AudioParameterBool::getNumSteps() const { return 2; } | |||
bool AudioParameterBool::isDiscrete() const { return true; } | |||
float AudioParameterBool::getValueForText (const String& text) const { return text.getIntValue() != 0 ? 1.0f : 0.0f; } | |||
String AudioParameterBool::getText (float v, int /*length*/) const { return String ((int) (v > 0.5f ? 1 : 0)); } | |||
AudioParameterBool& AudioParameterBool::operator= (bool newValue) | |||
{ | |||
if (get() != newValue) | |||
setValueNotifyingHost (newValue ? 1.0f : 0.0f); | |||
return *this; | |||
} | |||
//============================================================================== | |||
AudioParameterChoice::AudioParameterChoice (const String& idToUse, const String& nameToUse, | |||
const StringArray& c, int def, const String& labelToUse) | |||
: AudioProcessorParameterWithID (idToUse, nameToUse, labelToUse), choices (c), | |||
value ((float) def), | |||
defaultValue (convertTo0to1 (def)) | |||
{ | |||
jassert (choices.size() > 0); // you must supply an actual set of items to choose from! | |||
} | |||
AudioParameterChoice::~AudioParameterChoice() {} | |||
int AudioParameterChoice::limitRange (int v) const noexcept { return jlimit (0, choices.size() - 1, v); } | |||
float AudioParameterChoice::convertTo0to1 (int v) const noexcept { return jlimit (0.0f, 1.0f, (v + 0.5f) / (float) choices.size()); } | |||
int AudioParameterChoice::convertFrom0to1 (float v) const noexcept { return limitRange ((int) (v * (float) choices.size())); } | |||
float AudioParameterChoice::getValue() const { return convertTo0to1 (roundToInt (value)); } | |||
void AudioParameterChoice::setValue (float newValue) { value = (float) convertFrom0to1 (newValue); } | |||
float AudioParameterChoice::getDefaultValue() const { return defaultValue; } | |||
int AudioParameterChoice::getNumSteps() const { return choices.size(); } | |||
bool AudioParameterChoice::isDiscrete() const { return true; } | |||
float AudioParameterChoice::getValueForText (const String& text) const { return convertTo0to1 (choices.indexOf (text)); } | |||
String AudioParameterChoice::getText (float v, int /*length*/) const { return choices [convertFrom0to1 (v)]; } | |||
AudioParameterChoice& AudioParameterChoice::operator= (int newValue) | |||
{ | |||
if (getIndex() != newValue) | |||
setValueNotifyingHost (convertTo0to1 (newValue)); | |||
return *this; | |||
} | |||
} // namespace juce |
@@ -1,576 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParameterWithID, | |||
private ValueTree::Listener | |||
{ | |||
Parameter (AudioProcessorValueTreeState& s, | |||
const String& parameterID, const String& paramName, const String& labelText, | |||
NormalisableRange<float> r, float defaultVal, | |||
std::function<String (float)> valueToText, | |||
std::function<float (const String&)> textToValue, | |||
bool meta, | |||
bool automatable, | |||
bool discrete) | |||
: AudioProcessorParameterWithID (parameterID, paramName, labelText), | |||
owner (s), valueToTextFunction (valueToText), textToValueFunction (textToValue), | |||
range (r), value (defaultVal), defaultValue (defaultVal), | |||
listenersNeedCalling (true), | |||
isMetaParam (meta), | |||
isAutomatableParam (automatable), | |||
isDiscreteParam (discrete) | |||
{ | |||
state.addListener (this); | |||
needsUpdate.set (1); | |||
} | |||
~Parameter() | |||
{ | |||
// should have detached all callbacks before destroying the parameters! | |||
jassert (listeners.size() <= 1); | |||
} | |||
float getValue() const override { return range.convertTo0to1 (value); } | |||
float getDefaultValue() const override { return range.convertTo0to1 (defaultValue); } | |||
float getValueForText (const String& text) const override | |||
{ | |||
return range.convertTo0to1 (textToValueFunction != nullptr ? textToValueFunction (text) | |||
: text.getFloatValue()); | |||
} | |||
String getText (float v, int length) const override | |||
{ | |||
return valueToTextFunction != nullptr ? valueToTextFunction (range.convertFrom0to1 (v)) | |||
: AudioProcessorParameter::getText (v, length); | |||
} | |||
int getNumSteps() const override | |||
{ | |||
if (range.interval > 0) | |||
return (static_cast<int> ((range.end - range.start) / range.interval) + 1); | |||
return AudioProcessor::getDefaultNumParameterSteps(); | |||
} | |||
void setValue (float newValue) override | |||
{ | |||
newValue = range.snapToLegalValue (range.convertFrom0to1 (newValue)); | |||
if (value != newValue || listenersNeedCalling) | |||
{ | |||
value = newValue; | |||
listeners.call (&AudioProcessorValueTreeState::Listener::parameterChanged, paramID, value); | |||
listenersNeedCalling = false; | |||
needsUpdate.set (1); | |||
} | |||
} | |||
void setNewState (const ValueTree& v) | |||
{ | |||
state = v; | |||
updateFromValueTree(); | |||
} | |||
void setUnnormalisedValue (float newUnnormalisedValue) | |||
{ | |||
if (value != newUnnormalisedValue) | |||
{ | |||
const float newValue = range.convertTo0to1 (newUnnormalisedValue); | |||
setValueNotifyingHost (newValue); | |||
} | |||
} | |||
void updateFromValueTree() | |||
{ | |||
setUnnormalisedValue (state.getProperty (owner.valuePropertyID, defaultValue)); | |||
} | |||
void copyValueToValueTree() | |||
{ | |||
if (state.isValid()) | |||
state.setPropertyExcludingListener (this, owner.valuePropertyID, value, owner.undoManager); | |||
} | |||
void valueTreePropertyChanged (ValueTree&, const Identifier& property) override | |||
{ | |||
if (property == owner.valuePropertyID) | |||
updateFromValueTree(); | |||
} | |||
void valueTreeChildAdded (ValueTree&, ValueTree&) override {} | |||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {} | |||
void valueTreeChildOrderChanged (ValueTree&, int, int) override {} | |||
void valueTreeParentChanged (ValueTree&) override {} | |||
static Parameter* getParameterForID (AudioProcessor& processor, StringRef paramID) noexcept | |||
{ | |||
const int numParams = processor.getParameters().size(); | |||
for (int i = 0; i < numParams; ++i) | |||
{ | |||
AudioProcessorParameter* const ap = processor.getParameters().getUnchecked(i); | |||
// When using this class, you must allow it to manage all the parameters in your AudioProcessor, and | |||
// not add any parameter objects of other types! | |||
jassert (dynamic_cast<Parameter*> (ap) != nullptr); | |||
Parameter* const p = static_cast<Parameter*> (ap); | |||
if (paramID == p->paramID) | |||
return p; | |||
} | |||
return nullptr; | |||
} | |||
bool isMetaParameter() const override { return isMetaParam; } | |||
bool isAutomatable() const override { return isAutomatableParam; } | |||
bool isDiscrete() const override { return isDiscreteParam; } | |||
AudioProcessorValueTreeState& owner; | |||
ValueTree state; | |||
ListenerList<AudioProcessorValueTreeState::Listener> listeners; | |||
std::function<String (float)> valueToTextFunction; | |||
std::function<float (const String&)> textToValueFunction; | |||
NormalisableRange<float> range; | |||
float value, defaultValue; | |||
Atomic<int> needsUpdate; | |||
bool listenersNeedCalling; | |||
const bool isMetaParam, isAutomatableParam, isDiscreteParam; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Parameter) | |||
}; | |||
//============================================================================== | |||
AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& p, UndoManager* um) | |||
: processor (p), | |||
undoManager (um), | |||
valueType ("PARAM"), | |||
valuePropertyID ("value"), | |||
idPropertyID ("id"), | |||
updatingConnections (false) | |||
{ | |||
startTimerHz (10); | |||
state.addListener (this); | |||
} | |||
AudioProcessorValueTreeState::~AudioProcessorValueTreeState() {} | |||
AudioProcessorParameterWithID* AudioProcessorValueTreeState::createAndAddParameter (const String& paramID, const String& paramName, | |||
const String& labelText, NormalisableRange<float> r, | |||
float defaultVal, std::function<String (float)> valueToTextFunction, | |||
std::function<float (const String&)> textToValueFunction, | |||
bool isMetaParameter, | |||
bool isAutomatableParameter, | |||
bool isDiscreteParameter) | |||
{ | |||
// All parameters must be created before giving this manager a ValueTree state! | |||
jassert (! state.isValid()); | |||
#if ! JUCE_LINUX | |||
jassert (MessageManager::getInstance()->isThisTheMessageThread()); | |||
#endif | |||
Parameter* p = new Parameter (*this, paramID, paramName, labelText, r, | |||
defaultVal, valueToTextFunction, textToValueFunction, | |||
isMetaParameter, isAutomatableParameter, | |||
isDiscreteParameter); | |||
processor.addParameter (p); | |||
return p; | |||
} | |||
void AudioProcessorValueTreeState::addParameterListener (StringRef paramID, Listener* listener) | |||
{ | |||
if (Parameter* p = Parameter::getParameterForID (processor, paramID)) | |||
p->listeners.add (listener); | |||
} | |||
void AudioProcessorValueTreeState::removeParameterListener (StringRef paramID, Listener* listener) | |||
{ | |||
if (Parameter* p = Parameter::getParameterForID (processor, paramID)) | |||
p->listeners.remove (listener); | |||
} | |||
Value AudioProcessorValueTreeState::getParameterAsValue (StringRef paramID) const | |||
{ | |||
if (Parameter* p = Parameter::getParameterForID (processor, paramID)) | |||
return p->state.getPropertyAsValue (valuePropertyID, undoManager); | |||
return Value(); | |||
} | |||
NormalisableRange<float> AudioProcessorValueTreeState::getParameterRange (StringRef paramID) const noexcept | |||
{ | |||
if (Parameter* p = Parameter::getParameterForID (processor, paramID)) | |||
return p->range; | |||
return NormalisableRange<float>(); | |||
} | |||
AudioProcessorParameterWithID* AudioProcessorValueTreeState::getParameter (StringRef paramID) const noexcept | |||
{ | |||
return Parameter::getParameterForID (processor, paramID); | |||
} | |||
float* AudioProcessorValueTreeState::getRawParameterValue (StringRef paramID) const noexcept | |||
{ | |||
if (Parameter* p = Parameter::getParameterForID (processor, paramID)) | |||
return &(p->value); | |||
return nullptr; | |||
} | |||
ValueTree AudioProcessorValueTreeState::getOrCreateChildValueTree (const String& paramID) | |||
{ | |||
ValueTree v (state.getChildWithProperty (idPropertyID, paramID)); | |||
if (! v.isValid()) | |||
{ | |||
v = ValueTree (valueType); | |||
v.setProperty (idPropertyID, paramID, undoManager); | |||
state.addChild (v, -1, undoManager); | |||
} | |||
return v; | |||
} | |||
void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees() | |||
{ | |||
if (! updatingConnections) | |||
{ | |||
ScopedValueSetter<bool> svs (updatingConnections, true, false); | |||
const int numParams = processor.getParameters().size(); | |||
for (int i = 0; i < numParams; ++i) | |||
{ | |||
AudioProcessorParameter* const ap = processor.getParameters().getUnchecked(i); | |||
jassert (dynamic_cast<Parameter*> (ap) != nullptr); | |||
Parameter* p = static_cast<Parameter*> (ap); | |||
p->setNewState (getOrCreateChildValueTree (p->paramID)); | |||
} | |||
} | |||
} | |||
void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree& tree, const Identifier& property) | |||
{ | |||
if (property == idPropertyID && tree.hasType (valueType) && tree.getParent() == state) | |||
updateParameterConnectionsToChildTrees(); | |||
} | |||
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree) | |||
{ | |||
if (parent == state && tree.hasType (valueType)) | |||
updateParameterConnectionsToChildTrees(); | |||
} | |||
void AudioProcessorValueTreeState::valueTreeChildRemoved (ValueTree& parent, ValueTree& tree, int) | |||
{ | |||
if (parent == state && tree.hasType (valueType)) | |||
updateParameterConnectionsToChildTrees(); | |||
} | |||
void AudioProcessorValueTreeState::valueTreeRedirected (ValueTree& v) | |||
{ | |||
if (v == state) | |||
updateParameterConnectionsToChildTrees(); | |||
} | |||
void AudioProcessorValueTreeState::valueTreeChildOrderChanged (ValueTree&, int, int) {} | |||
void AudioProcessorValueTreeState::valueTreeParentChanged (ValueTree&) {} | |||
void AudioProcessorValueTreeState::timerCallback() | |||
{ | |||
const int numParams = processor.getParameters().size(); | |||
bool anythingUpdated = false; | |||
for (int i = 0; i < numParams; ++i) | |||
{ | |||
AudioProcessorParameter* const ap = processor.getParameters().getUnchecked(i); | |||
jassert (dynamic_cast<Parameter*> (ap) != nullptr); | |||
Parameter* p = static_cast<Parameter*> (ap); | |||
if (p->needsUpdate.compareAndSetBool (0, 1)) | |||
{ | |||
p->copyValueToValueTree(); | |||
anythingUpdated = true; | |||
} | |||
} | |||
startTimer (anythingUpdated ? 1000 / 50 | |||
: jlimit (50, 500, getTimerInterval() + 20)); | |||
} | |||
AudioProcessorValueTreeState::Listener::Listener() {} | |||
AudioProcessorValueTreeState::Listener::~Listener() {} | |||
//============================================================================== | |||
struct AttachedControlBase : public AudioProcessorValueTreeState::Listener, | |||
public AsyncUpdater | |||
{ | |||
AttachedControlBase (AudioProcessorValueTreeState& s, const String& p) | |||
: state (s), paramID (p), lastValue (0) | |||
{ | |||
state.addParameterListener (paramID, this); | |||
} | |||
void removeListener() | |||
{ | |||
state.removeParameterListener (paramID, this); | |||
} | |||
void setNewUnnormalisedValue (float newUnnormalisedValue) | |||
{ | |||
if (AudioProcessorParameter* p = state.getParameter (paramID)) | |||
{ | |||
const float newValue = state.getParameterRange (paramID) | |||
.convertTo0to1 (newUnnormalisedValue); | |||
if (p->getValue() != newValue) | |||
p->setValueNotifyingHost (newValue); | |||
} | |||
} | |||
void sendInitialUpdate() | |||
{ | |||
if (float* v = state.getRawParameterValue (paramID)) | |||
parameterChanged (paramID, *v); | |||
} | |||
void parameterChanged (const String&, float newValue) override | |||
{ | |||
lastValue = newValue; | |||
if (MessageManager::getInstance()->isThisTheMessageThread()) | |||
{ | |||
cancelPendingUpdate(); | |||
setValue (newValue); | |||
} | |||
else | |||
{ | |||
triggerAsyncUpdate(); | |||
} | |||
} | |||
void beginParameterChange() | |||
{ | |||
if (AudioProcessorParameter* p = state.getParameter (paramID)) | |||
p->beginChangeGesture(); | |||
} | |||
void endParameterChange() | |||
{ | |||
if (AudioProcessorParameter* p = state.getParameter (paramID)) | |||
p->endChangeGesture(); | |||
} | |||
void handleAsyncUpdate() override | |||
{ | |||
setValue (lastValue); | |||
} | |||
virtual void setValue (float) = 0; | |||
AudioProcessorValueTreeState& state; | |||
String paramID; | |||
float lastValue; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttachedControlBase) | |||
}; | |||
//============================================================================== | |||
struct AudioProcessorValueTreeState::SliderAttachment::Pimpl : private AttachedControlBase, | |||
private Slider::Listener | |||
{ | |||
Pimpl (AudioProcessorValueTreeState& s, const String& p, Slider& sl) | |||
: AttachedControlBase (s, p), slider (sl), ignoreCallbacks (false) | |||
{ | |||
NormalisableRange<float> range (s.getParameterRange (paramID)); | |||
slider.setRange (range.start, range.end, range.interval); | |||
slider.setSkewFactor (range.skew, range.symmetricSkew); | |||
if (AudioProcessorParameter* param = state.getParameter (paramID)) | |||
slider.setDoubleClickReturnValue (true, range.convertFrom0to1 (param->getDefaultValue())); | |||
sendInitialUpdate(); | |||
slider.addListener (this); | |||
} | |||
~Pimpl() | |||
{ | |||
slider.removeListener (this); | |||
removeListener(); | |||
} | |||
void setValue (float newValue) override | |||
{ | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
{ | |||
ScopedValueSetter<bool> svs (ignoreCallbacks, true); | |||
slider.setValue (newValue, sendNotificationSync); | |||
} | |||
} | |||
void sliderValueChanged (Slider* s) override | |||
{ | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
if ((! ignoreCallbacks) && (! ModifierKeys::getCurrentModifiers().isRightButtonDown())) | |||
setNewUnnormalisedValue ((float) s->getValue()); | |||
} | |||
void sliderDragStarted (Slider*) override { beginParameterChange(); } | |||
void sliderDragEnded (Slider*) override { endParameterChange(); } | |||
Slider& slider; | |||
bool ignoreCallbacks; | |||
CriticalSection selfCallbackMutex; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
}; | |||
AudioProcessorValueTreeState::SliderAttachment::SliderAttachment (AudioProcessorValueTreeState& s, const String& p, Slider& sl) | |||
: pimpl (new Pimpl (s, p, sl)) | |||
{ | |||
} | |||
AudioProcessorValueTreeState::SliderAttachment::~SliderAttachment() {} | |||
//============================================================================== | |||
struct AudioProcessorValueTreeState::ComboBoxAttachment::Pimpl : private AttachedControlBase, | |||
private ComboBox::Listener | |||
{ | |||
Pimpl (AudioProcessorValueTreeState& s, const String& p, ComboBox& c) | |||
: AttachedControlBase (s, p), combo (c), ignoreCallbacks (false) | |||
{ | |||
sendInitialUpdate(); | |||
combo.addListener (this); | |||
} | |||
~Pimpl() | |||
{ | |||
combo.removeListener (this); | |||
removeListener(); | |||
} | |||
void setValue (float newValue) override | |||
{ | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
{ | |||
ScopedValueSetter<bool> svs (ignoreCallbacks, true); | |||
combo.setSelectedItemIndex (roundToInt (newValue), sendNotificationSync); | |||
} | |||
} | |||
void comboBoxChanged (ComboBox* comboBox) override | |||
{ | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
if (! ignoreCallbacks) | |||
{ | |||
beginParameterChange(); | |||
setNewUnnormalisedValue ((float) comboBox->getSelectedId() - 1.0f); | |||
endParameterChange(); | |||
} | |||
} | |||
ComboBox& combo; | |||
bool ignoreCallbacks; | |||
CriticalSection selfCallbackMutex; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
}; | |||
AudioProcessorValueTreeState::ComboBoxAttachment::ComboBoxAttachment (AudioProcessorValueTreeState& s, const String& p, ComboBox& c) | |||
: pimpl (new Pimpl (s, p, c)) | |||
{ | |||
} | |||
AudioProcessorValueTreeState::ComboBoxAttachment::~ComboBoxAttachment() {} | |||
//============================================================================== | |||
struct AudioProcessorValueTreeState::ButtonAttachment::Pimpl : private AttachedControlBase, | |||
private Button::Listener | |||
{ | |||
Pimpl (AudioProcessorValueTreeState& s, const String& p, Button& b) | |||
: AttachedControlBase (s, p), button (b), ignoreCallbacks (false) | |||
{ | |||
sendInitialUpdate(); | |||
button.addListener (this); | |||
} | |||
~Pimpl() | |||
{ | |||
button.removeListener (this); | |||
removeListener(); | |||
} | |||
void setValue (float newValue) override | |||
{ | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
{ | |||
ScopedValueSetter<bool> svs (ignoreCallbacks, true); | |||
button.setToggleState (newValue >= 0.5f, sendNotificationSync); | |||
} | |||
} | |||
void buttonClicked (Button* b) override | |||
{ | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
if (! ignoreCallbacks) | |||
{ | |||
beginParameterChange(); | |||
setNewUnnormalisedValue (b->getToggleState() ? 1.0f : 0.0f); | |||
endParameterChange(); | |||
} | |||
} | |||
Button& button; | |||
bool ignoreCallbacks; | |||
CriticalSection selfCallbackMutex; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
}; | |||
AudioProcessorValueTreeState::ButtonAttachment::ButtonAttachment (AudioProcessorValueTreeState& s, const String& p, Button& b) | |||
: pimpl (new Pimpl (s, p, b)) | |||
{ | |||
} | |||
AudioProcessorValueTreeState::ButtonAttachment::~ButtonAttachment() {} | |||
} // namespace juce |
@@ -1,238 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
27th April 2017). | |||
End User License Agreement: www.juce.com/juce-5-licence | |||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||
Or: You may also use this code under the terms of the GPL v3 (see | |||
www.gnu.org/licenses). | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
/** | |||
This class contains a ValueTree which is used to manage an AudioProcessor's entire state. | |||
It has its own internal class of parameter object which are linked to values | |||
within its ValueTree, and which are each identified by a string ID. | |||
You can get access to the underlying ValueTree object via the state member variable, | |||
so you can add extra properties to it as necessary. | |||
It also provides some utility child classes for connecting parameters directly to | |||
GUI controls like sliders. | |||
To use: | |||
1) Create an AudioProcessorValueTreeState, and give it some parameters using createAndAddParameter(). | |||
2) Initialise the state member variable with a type name. | |||
*/ | |||
class JUCE_API AudioProcessorValueTreeState : private Timer, | |||
private ValueTree::Listener | |||
{ | |||
public: | |||
/** Creates a state object for a given processor. | |||
The UndoManager is optional and can be a nullptr. | |||
After creating your state object, you should add parameters with the | |||
createAndAddParameter() method. Note that each AudioProcessorValueTreeState | |||
should be attached to only one processor, and must have the same lifetime as the | |||
processor, as they will have dependencies on each other. | |||
*/ | |||
AudioProcessorValueTreeState (AudioProcessor& processorToConnectTo, | |||
UndoManager* undoManagerToUse); | |||
/** Destructor. */ | |||
~AudioProcessorValueTreeState(); | |||
/** Creates and returns a new parameter object for controlling a parameter | |||
with the given ID. | |||
Calling this will create and add a special type of AudioProcessorParameter to the | |||
AudioProcessor to which this state is attached. | |||
@param parameterID A unique string ID for the new parameter | |||
@param parameterName The name that the parameter will return from AudioProcessorParameter::getName() | |||
@param labelText The label that the parameter will return from AudioProcessorParameter::getLabel() | |||
@param valueRange A mapping that will be used to determine the value range which this parameter uses | |||
@param defaultValue A default value for the parameter (in non-normalised units) | |||
@param valueToTextFunction A function that will convert a non-normalised value to a string for the | |||
AudioProcessorParameter::getText() method. This can be nullptr to use the | |||
default implementation | |||
@param textToValueFunction The inverse of valueToTextFunction | |||
@param isMetaParameter Set this value to true if this should be a meta parameter | |||
@param isAutomatableParameter Set this value to false if this parameter should not be automatable | |||
@param isDiscrete Set this value to true to make this parameter take discrete values in a host. | |||
@see AudioProcessorParameter::isDiscrete | |||
@returns the parameter object that was created | |||
*/ | |||
AudioProcessorParameterWithID* createAndAddParameter (const String& parameterID, | |||
const String& parameterName, | |||
const String& labelText, | |||
NormalisableRange<float> valueRange, | |||
float defaultValue, | |||
std::function<String (float)> valueToTextFunction, | |||
std::function<float (const String&)> textToValueFunction, | |||
bool isMetaParameter = false, | |||
bool isAutomatableParameter = true, | |||
bool isDiscrete = false); | |||
/** Returns a parameter by its ID string. */ | |||
AudioProcessorParameterWithID* getParameter (StringRef parameterID) const noexcept; | |||
/** Returns a pointer to a floating point representation of a particular | |||
parameter which a realtime process can read to find out its current value. | |||
*/ | |||
float* getRawParameterValue (StringRef parameterID) const noexcept; | |||
/** A listener class that can be attached to an AudioProcessorValueTreeState. | |||
Use AudioProcessorValueTreeState::addParameterListener() to register a callback. | |||
*/ | |||
struct JUCE_API Listener | |||
{ | |||
Listener(); | |||
virtual ~Listener(); | |||
/** This callback method is called by the AudioProcessorValueTreeState when a parameter changes. */ | |||
virtual void parameterChanged (const String& parameterID, float newValue) = 0; | |||
}; | |||
/** Attaches a callback to one of the parameters, which will be called when the parameter changes. */ | |||
void addParameterListener (StringRef parameterID, Listener* listener); | |||
/** Removes a callback that was previously added with addParameterCallback(). */ | |||
void removeParameterListener (StringRef parameterID, Listener* listener); | |||
/** Returns a Value object that can be used to control a particular parameter. */ | |||
Value getParameterAsValue (StringRef parameterID) const; | |||
/** Returns the range that was set when the given parameter was created. */ | |||
NormalisableRange<float> getParameterRange (StringRef parameterID) const noexcept; | |||
/** A reference to the processor with which this state is associated. */ | |||
AudioProcessor& processor; | |||
/** The state of the whole processor. | |||
This must be initialised after all calls to createAndAddParameter(). | |||
You can replace this with your own ValueTree object, and can add properties and | |||
children to the tree. This class will automatically add children for each of the | |||
parameter objects that are created by createAndAddParameter(). | |||
*/ | |||
ValueTree state; | |||
/** Provides access to the undo manager that this object is using. */ | |||
UndoManager* const undoManager; | |||
//============================================================================== | |||
/** An object of this class maintains a connection between a Slider and a parameter | |||
in an AudioProcessorValueTreeState. | |||
During the lifetime of this SliderAttachment object, it keeps the two things in | |||
sync, making it easy to connect a slider to a parameter. When this object is | |||
deleted, the connection is broken. Make sure that your AudioProcessorValueTreeState | |||
and Slider aren't deleted before this object! | |||
*/ | |||
class JUCE_API SliderAttachment | |||
{ | |||
public: | |||
SliderAttachment (AudioProcessorValueTreeState& stateToControl, | |||
const String& parameterID, | |||
Slider& sliderToControl); | |||
~SliderAttachment(); | |||
private: | |||
struct Pimpl; | |||
friend struct ContainerDeletePolicy<Pimpl>; | |||
ScopedPointer<Pimpl> pimpl; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderAttachment) | |||
}; | |||
//============================================================================== | |||
/** An object of this class maintains a connection between a ComboBox and a parameter | |||
in an AudioProcessorValueTreeState. | |||
During the lifetime of this ComboBoxAttachment object, it keeps the two things in | |||
sync, making it easy to connect a combo box to a parameter. When this object is | |||
deleted, the connection is broken. Make sure that your AudioProcessorValueTreeState | |||
and ComboBox aren't deleted before this object! | |||
*/ | |||
class JUCE_API ComboBoxAttachment | |||
{ | |||
public: | |||
ComboBoxAttachment (AudioProcessorValueTreeState& stateToControl, | |||
const String& parameterID, | |||
ComboBox& comboBoxToControl); | |||
~ComboBoxAttachment(); | |||
private: | |||
struct Pimpl; | |||
friend struct ContainerDeletePolicy<Pimpl>; | |||
ScopedPointer<Pimpl> pimpl; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBoxAttachment) | |||
}; | |||
//============================================================================== | |||
/** An object of this class maintains a connection between a Button and a parameter | |||
in an AudioProcessorValueTreeState. | |||
During the lifetime of this ButtonAttachment object, it keeps the two things in | |||
sync, making it easy to connect a button to a parameter. When this object is | |||
deleted, the connection is broken. Make sure that your AudioProcessorValueTreeState | |||
and Button aren't deleted before this object! | |||
*/ | |||
class JUCE_API ButtonAttachment | |||
{ | |||
public: | |||
ButtonAttachment (AudioProcessorValueTreeState& stateToControl, | |||
const String& parameterID, | |||
Button& buttonToControl); | |||
~ButtonAttachment(); | |||
private: | |||
struct Pimpl; | |||
friend struct ContainerDeletePolicy<Pimpl>; | |||
ScopedPointer<Pimpl> pimpl; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonAttachment) | |||
}; | |||
private: | |||
//============================================================================== | |||
struct Parameter; | |||
friend struct Parameter; | |||
ValueTree getOrCreateChildValueTree (const String&); | |||
void timerCallback() override; | |||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override; | |||
void valueTreeChildAdded (ValueTree&, ValueTree&) override; | |||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override; | |||
void valueTreeChildOrderChanged (ValueTree&, int, int) override; | |||
void valueTreeParentChanged (ValueTree&) override; | |||
void valueTreeRedirected (ValueTree&) override; | |||
void updateParameterConnectionsToChildTrees(); | |||
Identifier valueType, valuePropertyID, idPropertyID; | |||
bool updatingConnections; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorValueTreeState) | |||
}; | |||
} // namespace juce |