@@ -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 |