@@ -313,7 +313,6 @@ endfunction() | |||
function(_juce_add_plugin_wrapper_target format path out_path) | |||
_juce_module_sources("${path}" "${out_path}" out_var headers) | |||
list(FILTER out_var EXCLUDE REGEX "/juce_audio_plugin_client_utils.cpp$") | |||
set(target_name juce_audio_plugin_client_${format}) | |||
_juce_add_interface_library("${target_name}" ${out_var}) | |||
@@ -452,16 +451,6 @@ function(juce_add_module module_path) | |||
_juce_add_plugin_wrapper_target(${kind} "${module_path}" "${base_path}") | |||
endforeach() | |||
set(utils_source | |||
"${base_path}/${module_name}/juce_audio_plugin_client_utils.cpp") | |||
add_library(juce_audio_plugin_client_utils INTERFACE) | |||
target_sources(juce_audio_plugin_client_utils INTERFACE "${utils_source}") | |||
if(JUCE_ARG_ALIAS_NAMESPACE) | |||
add_library(${JUCE_ARG_ALIAS_NAMESPACE}::juce_audio_plugin_client_utils | |||
ALIAS juce_audio_plugin_client_utils) | |||
endif() | |||
file(GLOB_RECURSE all_module_files | |||
CONFIGURE_DEPENDS LIST_DIRECTORIES FALSE | |||
RELATIVE "${module_parent_path}" | |||
@@ -1211,7 +1211,7 @@ endfunction() | |||
function(_juce_configure_plugin_targets target) | |||
_juce_set_output_name(${target} $<TARGET_PROPERTY:${target},JUCE_PRODUCT_NAME>_SharedCode) | |||
target_link_libraries(${target} PRIVATE juce::juce_audio_plugin_client_utils) | |||
target_link_libraries(${target} PRIVATE juce::juce_audio_plugin_client) | |||
get_target_property(enabled_formats ${target} JUCE_FORMATS) | |||
@@ -1,45 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2022 - Raw Material Software Limited | |||
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 7 End-User License | |||
Agreement and JUCE Privacy Policy. | |||
End User License Agreement: www.juce.com/juce-7-licence | |||
Privacy Policy: www.juce.com/juce-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. | |||
============================================================================== | |||
*/ | |||
#include <juce_core/system/juce_TargetPlatform.h> | |||
#include <juce_audio_plugin_client/detail/juce_CheckSettingMacros.h> | |||
#if JucePlugin_Enable_ARA | |||
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h> | |||
#include <juce_audio_plugin_client/detail/juce_IncludeModuleHeaders.h> | |||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter", "-Wgnu-zero-variadic-macro-arguments", "-Wmissing-prototypes") | |||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4100) | |||
#include <ARA_Library/PlugIn/ARAPlug.cpp> | |||
#include <ARA_Library/Dispatch/ARAPlugInDispatch.cpp> | |||
#include <ARA_Library/Utilities/ARAPitchInterpretation.cpp> | |||
#include <ARA_Library/Utilities/ARAChannelArrangement.cpp> | |||
JUCE_END_IGNORE_WARNINGS_MSVC | |||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
#endif |
@@ -1,170 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2022 - Raw Material Software Limited | |||
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 7 End-User License | |||
Agreement and JUCE Privacy Policy. | |||
End User License Agreement: www.juce.com/juce-7-licence | |||
Privacy Policy: www.juce.com/juce-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. | |||
============================================================================== | |||
*/ | |||
#include <juce_core/system/juce_TargetPlatform.h> | |||
#include <juce_audio_plugin_client/detail/juce_CheckSettingMacros.h> | |||
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h> | |||
#include <juce_audio_plugin_client/detail/juce_IncludeModuleHeaders.h> | |||
#include <juce_audio_plugin_client/detail/juce_WindowsHooks.h> | |||
#include <juce_audio_devices/juce_audio_devices.h> | |||
#include <juce_gui_extra/juce_gui_extra.h> | |||
#include <juce_audio_utils/juce_audio_utils.h> | |||
// You can set this flag in your build if you need to specify a different | |||
// standalone JUCEApplication class for your app to use. If you don't | |||
// set it then by default we'll just create a simple one as below. | |||
#if ! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP | |||
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h> | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
class StandaloneFilterApp : public JUCEApplication | |||
{ | |||
public: | |||
StandaloneFilterApp() | |||
{ | |||
PropertiesFile::Options options; | |||
options.applicationName = getApplicationName(); | |||
options.filenameSuffix = ".settings"; | |||
options.osxLibrarySubFolder = "Application Support"; | |||
#if JUCE_LINUX || JUCE_BSD | |||
options.folderName = "~/.config"; | |||
#else | |||
options.folderName = ""; | |||
#endif | |||
appProperties.setStorageParameters (options); | |||
} | |||
const String getApplicationName() override { return CharPointer_UTF8 (JucePlugin_Name); } | |||
const String getApplicationVersion() override { return JucePlugin_VersionString; } | |||
bool moreThanOneInstanceAllowed() override { return true; } | |||
void anotherInstanceStarted (const String&) override {} | |||
virtual StandaloneFilterWindow* createWindow() | |||
{ | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
StandalonePluginHolder::PluginInOuts channels[] = { JucePlugin_PreferredChannelConfigurations }; | |||
#endif | |||
return new StandaloneFilterWindow (getApplicationName(), | |||
LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId), | |||
appProperties.getUserSettings(), | |||
false, {}, nullptr | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
, juce::Array<StandalonePluginHolder::PluginInOuts> (channels, juce::numElementsInArray (channels)) | |||
#else | |||
, {} | |||
#endif | |||
#if JUCE_DONT_AUTO_OPEN_MIDI_DEVICES_ON_MOBILE | |||
, false | |||
#endif | |||
); | |||
} | |||
//============================================================================== | |||
void initialise (const String&) override | |||
{ | |||
mainWindow.reset (createWindow()); | |||
#if JUCE_STANDALONE_FILTER_WINDOW_USE_KIOSK_MODE | |||
Desktop::getInstance().setKioskModeComponent (mainWindow.get(), false); | |||
#endif | |||
mainWindow->setVisible (true); | |||
} | |||
void shutdown() override | |||
{ | |||
mainWindow = nullptr; | |||
appProperties.saveIfNeeded(); | |||
} | |||
//============================================================================== | |||
void systemRequestedQuit() override | |||
{ | |||
if (mainWindow.get() != nullptr) | |||
mainWindow->pluginHolder->savePluginState(); | |||
if (ModalComponentManager::getInstance()->cancelAllModalComponents()) | |||
{ | |||
Timer::callAfterDelay (100, []() | |||
{ | |||
if (auto app = JUCEApplicationBase::getInstance()) | |||
app->systemRequestedQuit(); | |||
}); | |||
} | |||
else | |||
{ | |||
quit(); | |||
} | |||
} | |||
protected: | |||
ApplicationProperties appProperties; | |||
std::unique_ptr<StandaloneFilterWindow> mainWindow; | |||
}; | |||
} // namespace juce | |||
#if JucePlugin_Build_Standalone && JUCE_IOS | |||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") | |||
using namespace juce; | |||
bool JUCE_CALLTYPE juce_isInterAppAudioConnected() | |||
{ | |||
if (auto holder = StandalonePluginHolder::getInstance()) | |||
return holder->isInterAppAudioConnected(); | |||
return false; | |||
} | |||
void JUCE_CALLTYPE juce_switchToHostApplication() | |||
{ | |||
if (auto holder = StandalonePluginHolder::getInstance()) | |||
holder->switchToHostApplication(); | |||
} | |||
Image JUCE_CALLTYPE juce_getIAAHostIcon (int size) | |||
{ | |||
if (auto holder = StandalonePluginHolder::getInstance()) | |||
return holder->getIAAHostIcon (size); | |||
return Image(); | |||
} | |||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
#endif | |||
#endif |
@@ -1,774 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2022 - Raw Material Software Limited | |||
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 7 End-User License | |||
Agreement and JUCE Privacy Policy. | |||
End User License Agreement: www.juce.com/juce-7-licence | |||
Privacy Policy: www.juce.com/juce-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. | |||
============================================================================== | |||
*/ | |||
#include <juce_core/system/juce_TargetPlatform.h> | |||
#if JucePlugin_Build_Unity | |||
#include <juce_audio_plugin_client/detail/juce_PluginUtilities.h> | |||
#include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp> | |||
#if JUCE_WINDOWS | |||
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h> | |||
#endif | |||
#include "juce_UnityPluginInterface.h" | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
typedef ComponentPeer* (*createUnityPeerFunctionType) (Component&); | |||
extern createUnityPeerFunctionType juce_createUnityPeerFn; | |||
//============================================================================== | |||
class UnityPeer : public ComponentPeer, | |||
public AsyncUpdater | |||
{ | |||
public: | |||
UnityPeer (Component& ed) | |||
: ComponentPeer (ed, 0), | |||
mouseWatcher (*this) | |||
{ | |||
getEditor().setResizable (false, false); | |||
} | |||
//============================================================================== | |||
Rectangle<int> getBounds() const override { return bounds; } | |||
Point<float> localToGlobal (Point<float> relativePosition) override { return relativePosition + getBounds().getPosition().toFloat(); } | |||
Point<float> globalToLocal (Point<float> screenPosition) override { return screenPosition - getBounds().getPosition().toFloat(); } | |||
using ComponentPeer::localToGlobal; | |||
using ComponentPeer::globalToLocal; | |||
StringArray getAvailableRenderingEngines() override { return StringArray ("Software Renderer"); } | |||
void setBounds (const Rectangle<int>& newBounds, bool) override | |||
{ | |||
bounds = newBounds; | |||
mouseWatcher.setBoundsToWatch (bounds); | |||
} | |||
bool contains (Point<int> localPos, bool) const override | |||
{ | |||
if (isPositiveAndBelow (localPos.getX(), getBounds().getWidth()) | |||
&& isPositiveAndBelow (localPos.getY(), getBounds().getHeight())) | |||
return true; | |||
return false; | |||
} | |||
void handleAsyncUpdate() override | |||
{ | |||
fillPixels(); | |||
} | |||
//============================================================================== | |||
AudioProcessorEditor& getEditor() { return *dynamic_cast<AudioProcessorEditor*> (&getComponent()); } | |||
void setPixelDataHandle (uint8* handle, int width, int height) | |||
{ | |||
pixelData = handle; | |||
textureWidth = width; | |||
textureHeight = height; | |||
renderImage = Image (new UnityBitmapImage (pixelData, width, height)); | |||
} | |||
// N.B. This is NOT an efficient way to do this and you shouldn't use this method in your own code. | |||
// It works for our purposes here but a much more efficient way would be to use a GL texture. | |||
void fillPixels() | |||
{ | |||
if (pixelData == nullptr) | |||
return; | |||
LowLevelGraphicsSoftwareRenderer renderer (renderImage); | |||
renderer.addTransform (AffineTransform::verticalFlip ((float) getComponent().getHeight())); | |||
handlePaint (renderer); | |||
for (int i = 0; i < textureWidth * textureHeight * 4; i += 4) | |||
{ | |||
auto r = pixelData[i + 2]; | |||
auto g = pixelData[i + 1]; | |||
auto b = pixelData[i + 0]; | |||
pixelData[i + 0] = r; | |||
pixelData[i + 1] = g; | |||
pixelData[i + 2] = b; | |||
} | |||
} | |||
void forwardMouseEvent (Point<float> position, ModifierKeys mods) | |||
{ | |||
ModifierKeys::currentModifiers = mods; | |||
handleMouseEvent (juce::MouseInputSource::mouse, position, mods, juce::MouseInputSource::defaultPressure, | |||
juce::MouseInputSource::defaultOrientation, juce::Time::currentTimeMillis()); | |||
} | |||
void forwardKeyPress (int code, String name, ModifierKeys mods) | |||
{ | |||
ModifierKeys::currentModifiers = mods; | |||
handleKeyPress (getKeyPress (code, name)); | |||
} | |||
private: | |||
//============================================================================== | |||
struct UnityBitmapImage : public ImagePixelData | |||
{ | |||
UnityBitmapImage (uint8* data, int w, int h) | |||
: ImagePixelData (Image::PixelFormat::ARGB, w, h), | |||
imageData (data), | |||
lineStride (width * pixelStride) | |||
{ | |||
} | |||
std::unique_ptr<ImageType> createType() const override | |||
{ | |||
return std::make_unique<SoftwareImageType>(); | |||
} | |||
std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override | |||
{ | |||
return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (this)); | |||
} | |||
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, [[maybe_unused]] Image::BitmapData::ReadWriteMode mode) override | |||
{ | |||
const auto offset = (size_t) x * (size_t) pixelStride + (size_t) y * (size_t) lineStride; | |||
bitmap.data = imageData + offset; | |||
bitmap.size = (size_t) (lineStride * height) - offset; | |||
bitmap.pixelFormat = pixelFormat; | |||
bitmap.lineStride = lineStride; | |||
bitmap.pixelStride = pixelStride; | |||
} | |||
ImagePixelData::Ptr clone() override | |||
{ | |||
auto im = new UnityBitmapImage (imageData, width, height); | |||
for (int i = 0; i < height; ++i) | |||
memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride); | |||
return im; | |||
} | |||
uint8* imageData; | |||
int pixelStride = 4, lineStride; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityBitmapImage) | |||
}; | |||
//============================================================================== | |||
struct MouseWatcher : public Timer | |||
{ | |||
MouseWatcher (ComponentPeer& o) : owner (o) {} | |||
void timerCallback() override | |||
{ | |||
auto pos = Desktop::getMousePosition(); | |||
if (boundsToWatch.contains (pos) && pos != lastMousePos) | |||
{ | |||
auto ms = Desktop::getInstance().getMainMouseSource(); | |||
if (! ms.getCurrentModifiers().isLeftButtonDown()) | |||
owner.handleMouseEvent (juce::MouseInputSource::mouse, owner.globalToLocal (pos.toFloat()), {}, | |||
juce::MouseInputSource::defaultPressure, juce::MouseInputSource::defaultOrientation, juce::Time::currentTimeMillis()); | |||
lastMousePos = pos; | |||
} | |||
} | |||
void setBoundsToWatch (Rectangle<int> b) | |||
{ | |||
if (boundsToWatch != b) | |||
boundsToWatch = b; | |||
startTimer (250); | |||
} | |||
ComponentPeer& owner; | |||
Rectangle<int> boundsToWatch; | |||
Point<int> lastMousePos; | |||
}; | |||
//============================================================================== | |||
KeyPress getKeyPress (int keyCode, String name) | |||
{ | |||
if (keyCode >= 32 && keyCode <= 64) | |||
return { keyCode, ModifierKeys::currentModifiers, juce::juce_wchar (keyCode) }; | |||
if (keyCode >= 91 && keyCode <= 122) | |||
return { keyCode, ModifierKeys::currentModifiers, name[0] }; | |||
if (keyCode >= 256 && keyCode <= 265) | |||
return { juce::KeyPress::numberPad0 + (keyCode - 256), ModifierKeys::currentModifiers, juce::String (keyCode - 256).getCharPointer()[0] }; | |||
if (keyCode == 8) return { juce::KeyPress::backspaceKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 127) return { juce::KeyPress::deleteKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 9) return { juce::KeyPress::tabKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 13) return { juce::KeyPress::returnKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 27) return { juce::KeyPress::escapeKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 32) return { juce::KeyPress::spaceKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 266) return { juce::KeyPress::numberPadDecimalPoint, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 267) return { juce::KeyPress::numberPadDivide, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 268) return { juce::KeyPress::numberPadMultiply, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 269) return { juce::KeyPress::numberPadSubtract, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 270) return { juce::KeyPress::numberPadAdd, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 272) return { juce::KeyPress::numberPadEquals, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 273) return { juce::KeyPress::upKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 274) return { juce::KeyPress::downKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 275) return { juce::KeyPress::rightKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 276) return { juce::KeyPress::leftKey, ModifierKeys::currentModifiers, {} }; | |||
return {}; | |||
} | |||
//============================================================================== | |||
Rectangle<int> bounds; | |||
MouseWatcher mouseWatcher; | |||
uint8* pixelData = nullptr; | |||
int textureWidth, textureHeight; | |||
Image renderImage; | |||
//============================================================================== | |||
void setMinimised (bool) override {} | |||
bool isMinimised() const override { return false; } | |||
void setFullScreen (bool) override {} | |||
bool isFullScreen() const override { return false; } | |||
bool setAlwaysOnTop (bool) override { return false; } | |||
void toFront (bool) override {} | |||
void toBehind (ComponentPeer*) override {} | |||
bool isFocused() const override { return true; } | |||
void grabFocus() override {} | |||
void* getNativeHandle() const override { return nullptr; } | |||
OptionalBorderSize getFrameSizeIfPresent() const override { return {}; } | |||
BorderSize<int> getFrameSize() const override { return {}; } | |||
void setVisible (bool) override {} | |||
void setTitle (const String&) override {} | |||
void setIcon (const Image&) override {} | |||
void textInputRequired (Point<int>, TextInputTarget&) override {} | |||
void setAlpha (float) override {} | |||
void performAnyPendingRepaintsNow() override {} | |||
void repaint (const Rectangle<int>&) override {} | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityPeer) | |||
}; | |||
static ComponentPeer* createUnityPeer (Component& c) { return new UnityPeer (c); } | |||
//============================================================================== | |||
class AudioProcessorUnityWrapper | |||
{ | |||
public: | |||
AudioProcessorUnityWrapper (bool isTemporary) | |||
{ | |||
pluginInstance = createPluginFilterOfType (AudioProcessor::wrapperType_Unity); | |||
if (! isTemporary && pluginInstance->hasEditor()) | |||
{ | |||
pluginInstanceEditor.reset (pluginInstance->createEditorIfNeeded()); | |||
pluginInstanceEditor->setVisible (true); | |||
detail::PluginUtilities::addToDesktop (*pluginInstanceEditor, nullptr); | |||
} | |||
juceParameters.update (*pluginInstance, false); | |||
} | |||
~AudioProcessorUnityWrapper() | |||
{ | |||
if (pluginInstanceEditor != nullptr) | |||
{ | |||
pluginInstanceEditor->removeFromDesktop(); | |||
PopupMenu::dismissAllActiveMenus(); | |||
pluginInstanceEditor->processor.editorBeingDeleted (pluginInstanceEditor.get()); | |||
pluginInstanceEditor = nullptr; | |||
} | |||
} | |||
void create (UnityAudioEffectState* state) | |||
{ | |||
// only supported in Unity plugin API > 1.0 | |||
if (state->structSize >= sizeof (UnityAudioEffectState)) | |||
samplesPerBlock = static_cast<int> (state->dspBufferSize); | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
short configs[][2] = { JucePlugin_PreferredChannelConfigurations }; | |||
const int numConfigs = sizeof (configs) / sizeof (short[2]); | |||
jassertquiet (numConfigs > 0 && (configs[0][0] > 0 || configs[0][1] > 0)); | |||
pluginInstance->setPlayConfigDetails (configs[0][0], configs[0][1], state->sampleRate, samplesPerBlock); | |||
#else | |||
pluginInstance->setRateAndBufferSizeDetails (state->sampleRate, samplesPerBlock); | |||
#endif | |||
pluginInstance->prepareToPlay (state->sampleRate, samplesPerBlock); | |||
scratchBuffer.setSize (jmax (pluginInstance->getTotalNumInputChannels(), pluginInstance->getTotalNumOutputChannels()), samplesPerBlock); | |||
} | |||
void release() | |||
{ | |||
pluginInstance->releaseResources(); | |||
} | |||
void reset() | |||
{ | |||
pluginInstance->reset(); | |||
} | |||
void process (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed) | |||
{ | |||
// If the plugin has a bypass parameter, set it to the current bypass state | |||
if (auto* param = pluginInstance->getBypassParameter()) | |||
if (isBypassed != (param->getValue() >= 0.5f)) | |||
param->setValueNotifyingHost (isBypassed ? 1.0f : 0.0f); | |||
for (int pos = 0; pos < bufferSize;) | |||
{ | |||
auto max = jmin (bufferSize - pos, samplesPerBlock); | |||
processBuffers (inBuffer + (pos * numInChannels), outBuffer + (pos * numOutChannels), max, numInChannels, numOutChannels, isBypassed); | |||
pos += max; | |||
} | |||
} | |||
void declareParameters (UnityAudioEffectDefinition& definition) | |||
{ | |||
static std::unique_ptr<UnityAudioParameterDefinition> parametersPtr; | |||
static int numParams = 0; | |||
if (parametersPtr == nullptr) | |||
{ | |||
numParams = (int) juceParameters.size(); | |||
parametersPtr.reset (static_cast<UnityAudioParameterDefinition*> (std::calloc (static_cast<size_t> (numParams), | |||
sizeof (UnityAudioParameterDefinition)))); | |||
parameterDescriptions.clear(); | |||
for (int i = 0; i < numParams; ++i) | |||
{ | |||
auto* parameter = juceParameters.getParamForIndex (i); | |||
auto& paramDef = parametersPtr.get()[i]; | |||
const auto nameLength = (size_t) numElementsInArray (paramDef.name); | |||
const auto unitLength = (size_t) numElementsInArray (paramDef.unit); | |||
parameter->getName ((int) nameLength - 1).copyToUTF8 (paramDef.name, nameLength); | |||
if (parameter->getLabel().isNotEmpty()) | |||
parameter->getLabel().copyToUTF8 (paramDef.unit, unitLength); | |||
parameterDescriptions.add (parameter->getName (15)); | |||
paramDef.description = parameterDescriptions[i].toRawUTF8(); | |||
paramDef.defaultVal = parameter->getDefaultValue(); | |||
paramDef.min = 0.0f; | |||
paramDef.max = 1.0f; | |||
paramDef.displayScale = 1.0f; | |||
paramDef.displayExponent = 1.0f; | |||
} | |||
} | |||
definition.numParameters = static_cast<uint32> (numParams); | |||
definition.parameterDefintions = parametersPtr.get(); | |||
} | |||
void setParameter (int index, float value) { juceParameters.getParamForIndex (index)->setValueNotifyingHost (value); } | |||
float getParameter (int index) const noexcept { return juceParameters.getParamForIndex (index)->getValue(); } | |||
String getParameterString (int index) const noexcept | |||
{ | |||
auto* param = juceParameters.getParamForIndex (index); | |||
return param->getText (param->getValue(), 16); | |||
} | |||
int getNumInputChannels() const noexcept { return pluginInstance->getTotalNumInputChannels(); } | |||
int getNumOutputChannels() const noexcept { return pluginInstance->getTotalNumOutputChannels(); } | |||
bool hasEditor() const noexcept { return pluginInstance->hasEditor(); } | |||
UnityPeer& getEditorPeer() const | |||
{ | |||
auto* peer = dynamic_cast<UnityPeer*> (pluginInstanceEditor->getPeer()); | |||
jassert (peer != nullptr); | |||
return *peer; | |||
} | |||
private: | |||
//============================================================================== | |||
void processBuffers (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed) | |||
{ | |||
int ch; | |||
for (ch = 0; ch < numInChannels; ++ch) | |||
{ | |||
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>; | |||
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const>; | |||
DstSampleType dstData (scratchBuffer.getWritePointer (ch)); | |||
SrcSampleType srcData (inBuffer + ch, numInChannels); | |||
dstData.convertSamples (srcData, bufferSize); | |||
} | |||
for (; ch < numOutChannels; ++ch) | |||
scratchBuffer.clear (ch, 0, bufferSize); | |||
{ | |||
const ScopedLock sl (pluginInstance->getCallbackLock()); | |||
if (pluginInstance->isSuspended()) | |||
{ | |||
scratchBuffer.clear(); | |||
} | |||
else | |||
{ | |||
MidiBuffer mb; | |||
if (isBypassed && pluginInstance->getBypassParameter() == nullptr) | |||
pluginInstance->processBlockBypassed (scratchBuffer, mb); | |||
else | |||
pluginInstance->processBlock (scratchBuffer, mb); | |||
} | |||
} | |||
for (ch = 0; ch < numOutChannels; ++ch) | |||
{ | |||
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst>; | |||
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>; | |||
DstSampleType dstData (outBuffer + ch, numOutChannels); | |||
SrcSampleType srcData (scratchBuffer.getReadPointer (ch)); | |||
dstData.convertSamples (srcData, bufferSize); | |||
} | |||
} | |||
//============================================================================== | |||
std::unique_ptr<AudioProcessor> pluginInstance; | |||
std::unique_ptr<AudioProcessorEditor> pluginInstanceEditor; | |||
int samplesPerBlock = 1024; | |||
StringArray parameterDescriptions; | |||
AudioBuffer<float> scratchBuffer; | |||
LegacyAudioParametersWrapper juceParameters; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorUnityWrapper) | |||
}; | |||
//============================================================================== | |||
static HashMap<int, AudioProcessorUnityWrapper*>& getWrapperMap() | |||
{ | |||
static HashMap<int, AudioProcessorUnityWrapper*> wrapperMap; | |||
return wrapperMap; | |||
} | |||
static void onWrapperCreation (AudioProcessorUnityWrapper* wrapperToAdd) | |||
{ | |||
getWrapperMap().set (std::abs (Random::getSystemRandom().nextInt (65536)), wrapperToAdd); | |||
} | |||
static void onWrapperDeletion (AudioProcessorUnityWrapper* wrapperToRemove) | |||
{ | |||
getWrapperMap().removeValue (wrapperToRemove); | |||
} | |||
//============================================================================== | |||
static UnityAudioEffectDefinition getEffectDefinition() | |||
{ | |||
const auto wrapper = std::make_unique<AudioProcessorUnityWrapper> (true); | |||
const String originalName { JucePlugin_Name }; | |||
const auto name = (! originalName.startsWithIgnoreCase ("audioplugin") ? "audioplugin_" : "") + originalName; | |||
UnityAudioEffectDefinition result{}; | |||
name.copyToUTF8 (result.name, (size_t) numElementsInArray (result.name)); | |||
result.structSize = sizeof (UnityAudioEffectDefinition); | |||
result.parameterStructSize = sizeof (UnityAudioParameterDefinition); | |||
result.apiVersion = UNITY_AUDIO_PLUGIN_API_VERSION; | |||
result.pluginVersion = JucePlugin_VersionCode; | |||
// effects must set this to 0, generators > 0 | |||
result.channels = (wrapper->getNumInputChannels() != 0 ? 0 | |||
: static_cast<uint32> (wrapper->getNumOutputChannels())); | |||
wrapper->declareParameters (result); | |||
result.create = [] (UnityAudioEffectState* state) | |||
{ | |||
auto* pluginInstance = new AudioProcessorUnityWrapper (false); | |||
pluginInstance->create (state); | |||
state->effectData = pluginInstance; | |||
onWrapperCreation (pluginInstance); | |||
return 0; | |||
}; | |||
result.release = [] (UnityAudioEffectState* state) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
pluginInstance->release(); | |||
onWrapperDeletion (pluginInstance); | |||
delete pluginInstance; | |||
if (getWrapperMap().size() == 0) | |||
shutdownJuce_GUI(); | |||
return 0; | |||
}; | |||
result.reset = [] (UnityAudioEffectState* state) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
pluginInstance->reset(); | |||
return 0; | |||
}; | |||
result.setPosition = [] (UnityAudioEffectState* state, unsigned int pos) | |||
{ | |||
ignoreUnused (state, pos); | |||
return 0; | |||
}; | |||
result.process = [] (UnityAudioEffectState* state, | |||
float* inBuffer, | |||
float* outBuffer, | |||
unsigned int bufferSize, | |||
int numInChannels, | |||
int numOutChannels) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
if (pluginInstance != nullptr) | |||
{ | |||
auto isPlaying = ((state->flags & stateIsPlaying) != 0); | |||
auto isMuted = ((state->flags & stateIsMuted) != 0); | |||
auto isPaused = ((state->flags & stateIsPaused) != 0); | |||
const auto bypassed = ! isPlaying || (isMuted || isPaused); | |||
pluginInstance->process (inBuffer, outBuffer, static_cast<int> (bufferSize), numInChannels, numOutChannels, bypassed); | |||
} | |||
else | |||
{ | |||
FloatVectorOperations::clear (outBuffer, static_cast<int> (bufferSize) * numOutChannels); | |||
} | |||
return 0; | |||
}; | |||
result.setFloatParameter = [] (UnityAudioEffectState* state, int index, float value) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
pluginInstance->setParameter (index, value); | |||
return 0; | |||
}; | |||
result.getFloatParameter = [] (UnityAudioEffectState* state, int index, float* value, char* valueStr) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
*value = pluginInstance->getParameter (index); | |||
pluginInstance->getParameterString (index).copyToUTF8 (valueStr, 15); | |||
return 0; | |||
}; | |||
result.getFloatBuffer = [] (UnityAudioEffectState* state, const char* kind, float* buffer, int numSamples) | |||
{ | |||
ignoreUnused (numSamples); | |||
const StringRef kindStr { kind }; | |||
if (kindStr == StringRef ("Editor")) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
buffer[0] = pluginInstance->hasEditor() ? 1.0f : 0.0f; | |||
} | |||
else if (kindStr == StringRef ("ID")) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
for (HashMap<int, AudioProcessorUnityWrapper*>::Iterator i (getWrapperMap()); i.next();) | |||
{ | |||
if (i.getValue() == pluginInstance) | |||
{ | |||
buffer[0] = (float) i.getKey(); | |||
break; | |||
} | |||
} | |||
return 0; | |||
} | |||
else if (kindStr == StringRef ("Size")) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
auto& editor = pluginInstance->getEditorPeer().getEditor(); | |||
buffer[0] = (float) editor.getBounds().getWidth(); | |||
buffer[1] = (float) editor.getBounds().getHeight(); | |||
buffer[2] = (float) editor.getConstrainer()->getMinimumWidth(); | |||
buffer[3] = (float) editor.getConstrainer()->getMinimumHeight(); | |||
buffer[4] = (float) editor.getConstrainer()->getMaximumWidth(); | |||
buffer[5] = (float) editor.getConstrainer()->getMaximumHeight(); | |||
} | |||
return 0; | |||
}; | |||
return result; | |||
} | |||
} // namespace juce | |||
// From reading the example code, it seems that the triple indirection indicates | |||
// an out-value of an array of pointers. That is, after calling this function, definitionsPtr | |||
// should point to a pre-existing/static array of pointer-to-effect-definition. | |||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API UnityGetAudioEffectDefinitions (UnityAudioEffectDefinition*** definitionsPtr) | |||
{ | |||
if (juce::getWrapperMap().size() == 0) | |||
juce::initialiseJuce_GUI(); | |||
static std::once_flag flag; | |||
std::call_once (flag, [] { juce::juce_createUnityPeerFn = juce::createUnityPeer; }); | |||
static auto definition = juce::getEffectDefinition(); | |||
static UnityAudioEffectDefinition* definitions[] { &definition }; | |||
*definitionsPtr = definitions; | |||
return 1; | |||
} | |||
//============================================================================== | |||
static juce::ModifierKeys unityModifiersToJUCE (UnityEventModifiers mods, bool mouseDown, int mouseButton = -1) | |||
{ | |||
int flags = 0; | |||
if (mouseDown) | |||
{ | |||
if (mouseButton == 0) | |||
flags |= juce::ModifierKeys::leftButtonModifier; | |||
else if (mouseButton == 1) | |||
flags |= juce::ModifierKeys::rightButtonModifier; | |||
else if (mouseButton == 2) | |||
flags |= juce::ModifierKeys::middleButtonModifier; | |||
} | |||
if (mods == 0) | |||
return flags; | |||
if ((mods & UnityEventModifiers::shift) != 0) flags |= juce::ModifierKeys::shiftModifier; | |||
if ((mods & UnityEventModifiers::control) != 0) flags |= juce::ModifierKeys::ctrlModifier; | |||
if ((mods & UnityEventModifiers::alt) != 0) flags |= juce::ModifierKeys::altModifier; | |||
if ((mods & UnityEventModifiers::command) != 0) flags |= juce::ModifierKeys::commandModifier; | |||
return { flags }; | |||
} | |||
//============================================================================== | |||
static juce::AudioProcessorUnityWrapper* getWrapperChecked (int id) | |||
{ | |||
auto* wrapper = juce::getWrapperMap()[id]; | |||
jassert (wrapper != nullptr); | |||
return wrapper; | |||
} | |||
//============================================================================== | |||
static void UNITY_INTERFACE_API onRenderEvent (int id) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().triggerAsyncUpdate(); | |||
} | |||
UNITY_INTERFACE_EXPORT renderCallback UNITY_INTERFACE_API getRenderCallback() | |||
{ | |||
return onRenderEvent; | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityInitialiseTexture (int id, void* data, int w, int h) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().setPixelDataHandle (reinterpret_cast<juce::uint8*> (data), w, h); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDown (int id, float x, float y, UnityEventModifiers unityMods, int button) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button)); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDrag (int id, float x, float y, UnityEventModifiers unityMods, int button) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button)); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseUp (int id, float x, float y, UnityEventModifiers unityMods) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, false)); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityKeyEvent (int id, int code, UnityEventModifiers mods, const char* name) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().forwardKeyPress (code, name, unityModifiersToJUCE (mods, false)); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unitySetScreenBounds (int id, float x, float y, float w, float h) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().getEditor().setBounds ({ (int) x, (int) y, (int) w, (int) h }); | |||
} | |||
//============================================================================== | |||
#if JUCE_WINDOWS | |||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") | |||
extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD reason, LPVOID) | |||
{ | |||
if (reason == DLL_PROCESS_ATTACH) | |||
juce::Process::setCurrentModuleInstanceHandle (instance); | |||
return true; | |||
} | |||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
#endif | |||
#endif |
@@ -1,145 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2022 - Raw Material Software Limited | |||
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 7 End-User License | |||
Agreement and JUCE Privacy Policy. | |||
End User License Agreement: www.juce.com/juce-7-licence | |||
Privacy Policy: www.juce.com/juce-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. | |||
============================================================================== | |||
*/ | |||
#include <juce_audio_plugin_client/detail/juce_PluginUtilities.h> | |||
#if JUCE_WINDOWS | |||
#include <windows.h> | |||
#endif | |||
namespace juce::detail | |||
{ | |||
#if JucePlugin_Build_Unity | |||
bool isRunningInUnity(); | |||
bool isRunningInUnity() { return PluginHostType::getPluginLoadedAs() == AudioProcessor::wrapperType_Unity; } | |||
#endif | |||
#if VST3_REPLACEMENT_AVAILABLE | |||
// NB: Nasty old-fashioned code in here because it's copied from the Steinberg example code. | |||
void PluginUtilities::getUUIDForVST2ID (bool forControllerUID, uint8 uuid[16]) | |||
{ | |||
#if JUCE_MSVC | |||
const auto juce_sprintf = [] (auto&& head, auto&&... tail) { sprintf_s (head, numElementsInArray (head), tail...); }; | |||
const auto juce_strcpy = [] (auto&& head, auto&&... tail) { strcpy_s (head, numElementsInArray (head), tail...); }; | |||
const auto juce_strcat = [] (auto&& head, auto&&... tail) { strcat_s (head, numElementsInArray (head), tail...); }; | |||
const auto juce_sscanf = [] (auto&&... args) { sscanf_s (args...); }; | |||
#else | |||
const auto juce_sprintf = [] (auto&& head, auto&&... tail) { snprintf (head, (size_t) numElementsInArray (head), tail...); }; | |||
const auto juce_strcpy = [] (auto&&... args) { strcpy (args...); }; | |||
const auto juce_strcat = [] (auto&&... args) { strcat (args...); }; | |||
const auto juce_sscanf = [] (auto&&... args) { sscanf (args...); }; | |||
#endif | |||
char uidString[33]; | |||
const int vstfxid = (('V' << 16) | ('S' << 8) | (forControllerUID ? 'E' : 'T')); | |||
char vstfxidStr[7] = { 0 }; | |||
juce_sprintf (vstfxidStr, "%06X", vstfxid); | |||
juce_strcpy (uidString, vstfxidStr); | |||
char uidStr[9] = { 0 }; | |||
juce_sprintf (uidStr, "%08X", JucePlugin_VSTUniqueID); | |||
juce_strcat (uidString, uidStr); | |||
char nameidStr[3] = { 0 }; | |||
const size_t len = strlen (JucePlugin_Name); | |||
for (size_t i = 0; i <= 8; ++i) | |||
{ | |||
juce::uint8 c = i < len ? static_cast<juce::uint8> (JucePlugin_Name[i]) : 0; | |||
if (c >= 'A' && c <= 'Z') | |||
c += 'a' - 'A'; | |||
juce_sprintf (nameidStr, "%02X", c); | |||
juce_strcat (uidString, nameidStr); | |||
} | |||
unsigned long p0; | |||
unsigned int p1, p2; | |||
unsigned int p3[8]; | |||
juce_sscanf (uidString, "%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X", | |||
&p0, &p1, &p2, &p3[0], &p3[1], &p3[2], &p3[3], &p3[4], &p3[5], &p3[6], &p3[7]); | |||
union q0_u { | |||
uint32 word; | |||
uint8 bytes[4]; | |||
} q0; | |||
union q1_u { | |||
uint16 half; | |||
uint8 bytes[2]; | |||
} q1, q2; | |||
q0.word = static_cast<uint32> (p0); | |||
q1.half = static_cast<uint16> (p1); | |||
q2.half = static_cast<uint16> (p2); | |||
// VST3 doesn't use COM compatible UUIDs on non windows platforms | |||
#if ! JUCE_WINDOWS | |||
q0.word = ByteOrder::swap (q0.word); | |||
q1.half = ByteOrder::swap (q1.half); | |||
q2.half = ByteOrder::swap (q2.half); | |||
#endif | |||
for (int i = 0; i < 4; ++i) | |||
uuid[i+0] = q0.bytes[i]; | |||
for (int i = 0; i < 2; ++i) | |||
uuid[i+4] = q1.bytes[i]; | |||
for (int i = 0; i < 2; ++i) | |||
uuid[i+6] = q2.bytes[i]; | |||
for (int i = 0; i < 8; ++i) | |||
uuid[i+8] = static_cast<uint8> (p3[i]); | |||
} | |||
#endif | |||
#if JucePlugin_Build_VST | |||
bool PluginUtilities::handleManufacturerSpecificVST2Opcode ([[maybe_unused]] int32 index, | |||
[[maybe_unused]] pointer_sized_int value, | |||
[[maybe_unused]] void* ptr, | |||
float) | |||
{ | |||
#if VST3_REPLACEMENT_AVAILABLE | |||
if ((index == (int32) ByteOrder::bigEndianInt ("stCA") || index == (int32) ByteOrder::bigEndianInt ("stCa")) | |||
&& value == (int32) ByteOrder::bigEndianInt ("FUID") && ptr != nullptr) | |||
{ | |||
uint8 fuid[16]; | |||
getUUIDForVST2ID (false, fuid); | |||
::memcpy (ptr, fuid, 16); | |||
return true; | |||
} | |||
#endif | |||
return false; | |||
} | |||
#endif | |||
} // namespace juce |
@@ -30,6 +30,13 @@ | |||
namespace juce::detail | |||
{ | |||
bool isRunningInUnity(); | |||
#if JucePlugin_Build_Unity | |||
bool isRunningInUnity() { return PluginHostType::getPluginLoadedAs() == AudioProcessor::wrapperType_Unity; } | |||
#else | |||
bool isRunningInUnity() { return false; } | |||
#endif | |||
struct PluginUtilities | |||
{ | |||
PluginUtilities() = delete; | |||
@@ -61,20 +68,106 @@ struct PluginUtilities | |||
#define JUCE_VST3_CAN_REPLACE_VST2 1 | |||
#endif | |||
#if JucePlugin_Build_VST3 && JUCE_VST3_CAN_REPLACE_VST2 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD) | |||
#define VST3_REPLACEMENT_AVAILABLE 1 | |||
static void getUUIDForVST2ID (bool forControllerUID, uint8 uuid[16]); | |||
#else | |||
#define VST3_REPLACEMENT_AVAILABLE 0 | |||
#endif | |||
// NB: Nasty old-fashioned code in here because it's copied from the Steinberg example code. | |||
static void getUUIDForVST2ID (bool forControllerUID, uint8 uuid[16]) | |||
{ | |||
#if JUCE_WINDOWS && ! JUCE_MINGW | |||
const auto juce_sprintf = [] (auto&& head, auto&&... tail) { sprintf_s (head, (size_t) numElementsInArray (head), tail...); }; | |||
const auto juce_strcpy = [] (auto&& head, auto&&... tail) { strcpy_s (head, (size_t) numElementsInArray (head), tail...); }; | |||
const auto juce_strcat = [] (auto&& head, auto&&... tail) { strcat_s (head, (size_t) numElementsInArray (head), tail...); }; | |||
const auto juce_sscanf = [] (auto&&... args) { sscanf_s (args...); }; | |||
#else | |||
const auto juce_sprintf = [] (auto&& head, auto&&... tail) { snprintf (head, (size_t) numElementsInArray (head), tail...); }; | |||
const auto juce_strcpy = [] (auto&&... args) { strcpy (args...); }; | |||
const auto juce_strcat = [] (auto&&... args) { strcat (args...); }; | |||
const auto juce_sscanf = [] (auto&&... args) { sscanf (args...); }; | |||
#endif | |||
char uidString[33]; | |||
const int vstfxid = (('V' << 16) | ('S' << 8) | (forControllerUID ? 'E' : 'T')); | |||
char vstfxidStr[7] = { 0 }; | |||
juce_sprintf (vstfxidStr, "%06X", vstfxid); | |||
juce_strcpy (uidString, vstfxidStr); | |||
char uidStr[9] = { 0 }; | |||
juce_sprintf (uidStr, "%08X", JucePlugin_VSTUniqueID); | |||
juce_strcat (uidString, uidStr); | |||
char nameidStr[3] = { 0 }; | |||
const size_t len = strlen (JucePlugin_Name); | |||
for (size_t i = 0; i <= 8; ++i) | |||
{ | |||
juce::uint8 c = i < len ? static_cast<juce::uint8> (JucePlugin_Name[i]) : 0; | |||
if (c >= 'A' && c <= 'Z') | |||
c += 'a' - 'A'; | |||
juce_sprintf (nameidStr, "%02X", c); | |||
juce_strcat (uidString, nameidStr); | |||
} | |||
unsigned long p0; | |||
unsigned int p1, p2; | |||
unsigned int p3[8]; | |||
juce_sscanf (uidString, "%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X", | |||
&p0, &p1, &p2, &p3[0], &p3[1], &p3[2], &p3[3], &p3[4], &p3[5], &p3[6], &p3[7]); | |||
union q0_u { | |||
uint32 word; | |||
uint8 bytes[4]; | |||
} q0; | |||
union q1_u { | |||
uint16 half; | |||
uint8 bytes[2]; | |||
} q1, q2; | |||
q0.word = static_cast<uint32> (p0); | |||
q1.half = static_cast<uint16> (p1); | |||
q2.half = static_cast<uint16> (p2); | |||
// VST3 doesn't use COM compatible UUIDs on non windows platforms | |||
#if ! JUCE_WINDOWS | |||
q0.word = ByteOrder::swap (q0.word); | |||
q1.half = ByteOrder::swap (q1.half); | |||
q2.half = ByteOrder::swap (q2.half); | |||
#endif | |||
for (int i = 0; i < 4; ++i) | |||
uuid[i+0] = q0.bytes[i]; | |||
for (int i = 0; i < 2; ++i) | |||
uuid[i+4] = q1.bytes[i]; | |||
for (int i = 0; i < 2; ++i) | |||
uuid[i+6] = q2.bytes[i]; | |||
for (int i = 0; i < 8; ++i) | |||
uuid[i+8] = static_cast<uint8> (p3[i]); | |||
} | |||
#if JucePlugin_Build_VST | |||
static bool handleManufacturerSpecificVST2Opcode (int32 index, | |||
pointer_sized_int value, | |||
void* ptr, | |||
float); | |||
static bool handleManufacturerSpecificVST2Opcode ([[maybe_unused]] int32 index, | |||
[[maybe_unused]] pointer_sized_int value, | |||
[[maybe_unused]] void* ptr, | |||
float) | |||
{ | |||
#if JUCE_VST3_CAN_REPLACE_VST2 | |||
if ((index == (int32) ByteOrder::bigEndianInt ("stCA") || index == (int32) ByteOrder::bigEndianInt ("stCa")) | |||
&& value == (int32) ByteOrder::bigEndianInt ("FUID") && ptr != nullptr) | |||
{ | |||
uint8 fuid[16]; | |||
getUUIDForVST2ID (false, fuid); | |||
::memcpy (ptr, fuid, 16); | |||
return true; | |||
} | |||
#endif | |||
return false; | |||
} | |||
#endif | |||
}; | |||
@@ -38,15 +38,59 @@ struct VSTWindowUtilities | |||
static void* attachComponentToWindowRefVST (Component* comp, | |||
int desktopFlags, | |||
void* parentWindowOrView); | |||
void* parentWindowOrView) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
NSView* parentView = [(NSView*) parentWindowOrView retain]; | |||
const auto defaultFlags = JucePlugin_EditorRequiresKeyboardFocus | |||
? 0 | |||
: ComponentPeer::windowIgnoresKeyPresses; | |||
comp->addToDesktop (desktopFlags | defaultFlags, parentView); | |||
// (this workaround is because Wavelab provides a zero-size parent view..) | |||
if ([parentView frame].size.height == 0) | |||
[((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint]; | |||
comp->setVisible (true); | |||
comp->toFront (false); | |||
[[parentView window] setAcceptsMouseMovedEvents: YES]; | |||
return parentView; | |||
} | |||
} | |||
static void detachComponentFromWindowRefVST (Component* comp, | |||
void* window); | |||
void* window) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
comp->removeFromDesktop(); | |||
[(id) window release]; | |||
} | |||
} | |||
static void setNativeHostWindowSizeVST (void* window, | |||
Component* component, | |||
int newWidth, | |||
int newHeight); | |||
int newHeight) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
if (NSView* hostView = (NSView*) window) | |||
{ | |||
const int dx = newWidth - component->getWidth(); | |||
const int dy = newHeight - component->getHeight(); | |||
NSRect r = [hostView frame]; | |||
r.size.width += dx; | |||
r.size.height += dy; | |||
r.origin.y -= dy; | |||
[hostView setFrame: r]; | |||
} | |||
} | |||
} | |||
}; | |||
} // namespace juce::detail | |||
@@ -1,96 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2022 - Raw Material Software Limited | |||
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 7 End-User License | |||
Agreement and JUCE Privacy Policy. | |||
End User License Agreement: www.juce.com/juce-7-licence | |||
Privacy Policy: www.juce.com/juce-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. | |||
============================================================================== | |||
*/ | |||
#pragma once | |||
#include <juce_core/system/juce_TargetPlatform.h> | |||
#if JUCE_MAC | |||
#include <juce_audio_plugin_client/detail/juce_CheckSettingMacros.h> | |||
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h> | |||
#include <juce_audio_plugin_client/detail/juce_VSTWindowUtilities.h> | |||
namespace juce::detail | |||
{ | |||
void* VSTWindowUtilities::attachComponentToWindowRefVST (Component* comp, | |||
int desktopFlags, | |||
void* parentWindowOrView) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
NSView* parentView = [(NSView*) parentWindowOrView retain]; | |||
const auto defaultFlags = JucePlugin_EditorRequiresKeyboardFocus | |||
? 0 | |||
: ComponentPeer::windowIgnoresKeyPresses; | |||
comp->addToDesktop (desktopFlags | defaultFlags, parentView); | |||
// (this workaround is because Wavelab provides a zero-size parent view..) | |||
if ([parentView frame].size.height == 0) | |||
[((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint]; | |||
comp->setVisible (true); | |||
comp->toFront (false); | |||
[[parentView window] setAcceptsMouseMovedEvents: YES]; | |||
return parentView; | |||
} | |||
} | |||
void VSTWindowUtilities::detachComponentFromWindowRefVST (Component* comp, void* window) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
comp->removeFromDesktop(); | |||
[(id) window release]; | |||
} | |||
} | |||
void VSTWindowUtilities::setNativeHostWindowSizeVST (void* window, | |||
Component* component, | |||
int newWidth, | |||
int newHeight) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
if (NSView* hostView = (NSView*) window) | |||
{ | |||
const int dx = newWidth - component->getWidth(); | |||
const int dy = newHeight - component->getHeight(); | |||
NSRect r = [hostView frame]; | |||
r.size.width += dx; | |||
r.size.height += dy; | |||
r.origin.y -= dy; | |||
[hostView setFrame: r]; | |||
} | |||
} | |||
} | |||
} // namespace juce::detail | |||
#endif |
@@ -24,4 +24,4 @@ | |||
*/ | |||
#define JUCE_INCLUDED_AAX_IN_MM 1 | |||
#include "AAX/juce_AAX_Wrapper.cpp" | |||
#include "juce_audio_plugin_client_AAX.cpp" |
@@ -25,7 +25,7 @@ | |||
#include <juce_core/system/juce_TargetPlatform.h> | |||
#if JucePlugin_Build_AAX && (JUCE_MAC || JUCE_WINDOWS) | |||
#if JucePlugin_Build_AAX | |||
#include <AAX_Version.h> | |||
@@ -23,4 +23,23 @@ | |||
============================================================================== | |||
*/ | |||
#include "ARA/juce_ARA_Wrapper.cpp" | |||
#include <juce_core/system/juce_TargetPlatform.h> | |||
#include <juce_audio_plugin_client/detail/juce_CheckSettingMacros.h> | |||
#if JucePlugin_Enable_ARA | |||
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h> | |||
#include <juce_audio_plugin_client/detail/juce_IncludeModuleHeaders.h> | |||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter", "-Wgnu-zero-variadic-macro-arguments", "-Wmissing-prototypes") | |||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4100) | |||
#include <ARA_Library/PlugIn/ARAPlug.cpp> | |||
#include <ARA_Library/Dispatch/ARAPlugInDispatch.cpp> | |||
#include <ARA_Library/Utilities/ARAPitchInterpretation.cpp> | |||
#include <ARA_Library/Utilities/ARAChannelArrangement.cpp> | |||
JUCE_END_IGNORE_WARNINGS_MSVC | |||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
#endif |
@@ -86,18 +86,18 @@ enum MIDICVStatus : unsigned int | |||
#endif | |||
#include "AU/AudioUnitSDK/AUBase.cpp" | |||
#include "AU/AudioUnitSDK/AUBuffer.cpp" | |||
#include "AU/AudioUnitSDK/AUBufferAllocator.cpp" | |||
#include "AU/AudioUnitSDK/AUEffectBase.cpp" | |||
#include "AU/AudioUnitSDK/AUInputElement.cpp" | |||
#include "AU/AudioUnitSDK/AUMIDIBase.cpp" | |||
#include "AU/AudioUnitSDK/AUMIDIEffectBase.cpp" | |||
#include "AU/AudioUnitSDK/AUOutputElement.cpp" | |||
#include "AU/AudioUnitSDK/AUPlugInDispatch.cpp" | |||
#include "AU/AudioUnitSDK/AUScopeElement.cpp" | |||
#include "AU/AudioUnitSDK/ComponentBase.cpp" | |||
#include "AU/AudioUnitSDK/MusicDeviceBase.cpp" | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUBase.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUBuffer.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUBufferAllocator.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUEffectBase.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUInputElement.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUMIDIBase.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUMIDIEffectBase.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUOutputElement.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUPlugInDispatch.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUScopeElement.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/ComponentBase.cpp> | |||
#include <juce_audio_plugin_client/AU/AudioUnitSDK/MusicDeviceBase.cpp> | |||
#undef verify | |||
#undef verify_noerr | |||
@@ -31,7 +31,152 @@ | |||
#error To compile AudioUnitv3 and/or Standalone plug-ins, you need to add the juce_audio_utils and juce_audio_devices modules! | |||
#endif | |||
#include "Standalone/juce_StandaloneFilterApp.cpp" | |||
#include <juce_core/system/juce_TargetPlatform.h> | |||
#include <juce_audio_plugin_client/detail/juce_CheckSettingMacros.h> | |||
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h> | |||
#include <juce_audio_plugin_client/detail/juce_IncludeModuleHeaders.h> | |||
#include <juce_audio_plugin_client/detail/juce_WindowsHooks.h> | |||
#include <juce_audio_plugin_client/detail/juce_PluginUtilities.h> | |||
#include <juce_audio_devices/juce_audio_devices.h> | |||
#include <juce_gui_extra/juce_gui_extra.h> | |||
#include <juce_audio_utils/juce_audio_utils.h> | |||
// You can set this flag in your build if you need to specify a different | |||
// standalone JUCEApplication class for your app to use. If you don't | |||
// set it then by default we'll just create a simple one as below. | |||
#if ! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP | |||
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h> | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
class StandaloneFilterApp : public JUCEApplication | |||
{ | |||
public: | |||
StandaloneFilterApp() | |||
{ | |||
PropertiesFile::Options options; | |||
options.applicationName = getApplicationName(); | |||
options.filenameSuffix = ".settings"; | |||
options.osxLibrarySubFolder = "Application Support"; | |||
#if JUCE_LINUX || JUCE_BSD | |||
options.folderName = "~/.config"; | |||
#else | |||
options.folderName = ""; | |||
#endif | |||
appProperties.setStorageParameters (options); | |||
} | |||
const String getApplicationName() override { return CharPointer_UTF8 (JucePlugin_Name); } | |||
const String getApplicationVersion() override { return JucePlugin_VersionString; } | |||
bool moreThanOneInstanceAllowed() override { return true; } | |||
void anotherInstanceStarted (const String&) override {} | |||
virtual StandaloneFilterWindow* createWindow() | |||
{ | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
StandalonePluginHolder::PluginInOuts channels[] = { JucePlugin_PreferredChannelConfigurations }; | |||
#endif | |||
return new StandaloneFilterWindow (getApplicationName(), | |||
LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId), | |||
appProperties.getUserSettings(), | |||
false, {}, nullptr | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
, juce::Array<StandalonePluginHolder::PluginInOuts> (channels, juce::numElementsInArray (channels)) | |||
#else | |||
, {} | |||
#endif | |||
#if JUCE_DONT_AUTO_OPEN_MIDI_DEVICES_ON_MOBILE | |||
, false | |||
#endif | |||
); | |||
} | |||
//============================================================================== | |||
void initialise (const String&) override | |||
{ | |||
mainWindow.reset (createWindow()); | |||
#if JUCE_STANDALONE_FILTER_WINDOW_USE_KIOSK_MODE | |||
Desktop::getInstance().setKioskModeComponent (mainWindow.get(), false); | |||
#endif | |||
mainWindow->setVisible (true); | |||
} | |||
void shutdown() override | |||
{ | |||
mainWindow = nullptr; | |||
appProperties.saveIfNeeded(); | |||
} | |||
//============================================================================== | |||
void systemRequestedQuit() override | |||
{ | |||
if (mainWindow.get() != nullptr) | |||
mainWindow->pluginHolder->savePluginState(); | |||
if (ModalComponentManager::getInstance()->cancelAllModalComponents()) | |||
{ | |||
Timer::callAfterDelay (100, []() | |||
{ | |||
if (auto app = JUCEApplicationBase::getInstance()) | |||
app->systemRequestedQuit(); | |||
}); | |||
} | |||
else | |||
{ | |||
quit(); | |||
} | |||
} | |||
protected: | |||
ApplicationProperties appProperties; | |||
std::unique_ptr<StandaloneFilterWindow> mainWindow; | |||
}; | |||
} // namespace juce | |||
#if JucePlugin_Build_Standalone && JUCE_IOS | |||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") | |||
using namespace juce; | |||
bool JUCE_CALLTYPE juce_isInterAppAudioConnected() | |||
{ | |||
if (auto holder = StandalonePluginHolder::getInstance()) | |||
return holder->isInterAppAudioConnected(); | |||
return false; | |||
} | |||
void JUCE_CALLTYPE juce_switchToHostApplication() | |||
{ | |||
if (auto holder = StandalonePluginHolder::getInstance()) | |||
holder->switchToHostApplication(); | |||
} | |||
Image JUCE_CALLTYPE juce_getIAAHostIcon (int size) | |||
{ | |||
if (auto holder = StandalonePluginHolder::getInstance()) | |||
return holder->getIAAHostIcon (size); | |||
return Image(); | |||
} | |||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
#endif | |||
#endif | |||
#if JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP | |||
extern juce::JUCEApplicationBase* juce_CreateApplication(); | |||
@@ -23,4 +23,752 @@ | |||
============================================================================== | |||
*/ | |||
#include "Unity/juce_Unity_Wrapper.cpp" | |||
#include <juce_core/system/juce_TargetPlatform.h> | |||
#if JucePlugin_Build_Unity | |||
#include <juce_audio_plugin_client/detail/juce_PluginUtilities.h> | |||
#include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp> | |||
#if JUCE_WINDOWS | |||
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h> | |||
#endif | |||
#include <juce_audio_plugin_client/Unity/juce_UnityPluginInterface.h> | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
typedef ComponentPeer* (*createUnityPeerFunctionType) (Component&); | |||
extern createUnityPeerFunctionType juce_createUnityPeerFn; | |||
//============================================================================== | |||
class UnityPeer : public ComponentPeer, | |||
public AsyncUpdater | |||
{ | |||
public: | |||
UnityPeer (Component& ed) | |||
: ComponentPeer (ed, 0), | |||
mouseWatcher (*this) | |||
{ | |||
getEditor().setResizable (false, false); | |||
} | |||
//============================================================================== | |||
Rectangle<int> getBounds() const override { return bounds; } | |||
Point<float> localToGlobal (Point<float> relativePosition) override { return relativePosition + getBounds().getPosition().toFloat(); } | |||
Point<float> globalToLocal (Point<float> screenPosition) override { return screenPosition - getBounds().getPosition().toFloat(); } | |||
using ComponentPeer::localToGlobal; | |||
using ComponentPeer::globalToLocal; | |||
StringArray getAvailableRenderingEngines() override { return StringArray ("Software Renderer"); } | |||
void setBounds (const Rectangle<int>& newBounds, bool) override | |||
{ | |||
bounds = newBounds; | |||
mouseWatcher.setBoundsToWatch (bounds); | |||
} | |||
bool contains (Point<int> localPos, bool) const override | |||
{ | |||
if (isPositiveAndBelow (localPos.getX(), getBounds().getWidth()) | |||
&& isPositiveAndBelow (localPos.getY(), getBounds().getHeight())) | |||
return true; | |||
return false; | |||
} | |||
void handleAsyncUpdate() override | |||
{ | |||
fillPixels(); | |||
} | |||
//============================================================================== | |||
AudioProcessorEditor& getEditor() { return *dynamic_cast<AudioProcessorEditor*> (&getComponent()); } | |||
void setPixelDataHandle (uint8* handle, int width, int height) | |||
{ | |||
pixelData = handle; | |||
textureWidth = width; | |||
textureHeight = height; | |||
renderImage = Image (new UnityBitmapImage (pixelData, width, height)); | |||
} | |||
// N.B. This is NOT an efficient way to do this and you shouldn't use this method in your own code. | |||
// It works for our purposes here but a much more efficient way would be to use a GL texture. | |||
void fillPixels() | |||
{ | |||
if (pixelData == nullptr) | |||
return; | |||
LowLevelGraphicsSoftwareRenderer renderer (renderImage); | |||
renderer.addTransform (AffineTransform::verticalFlip ((float) getComponent().getHeight())); | |||
handlePaint (renderer); | |||
for (int i = 0; i < textureWidth * textureHeight * 4; i += 4) | |||
{ | |||
auto r = pixelData[i + 2]; | |||
auto g = pixelData[i + 1]; | |||
auto b = pixelData[i + 0]; | |||
pixelData[i + 0] = r; | |||
pixelData[i + 1] = g; | |||
pixelData[i + 2] = b; | |||
} | |||
} | |||
void forwardMouseEvent (Point<float> position, ModifierKeys mods) | |||
{ | |||
ModifierKeys::currentModifiers = mods; | |||
handleMouseEvent (juce::MouseInputSource::mouse, position, mods, juce::MouseInputSource::defaultPressure, | |||
juce::MouseInputSource::defaultOrientation, juce::Time::currentTimeMillis()); | |||
} | |||
void forwardKeyPress (int code, String name, ModifierKeys mods) | |||
{ | |||
ModifierKeys::currentModifiers = mods; | |||
handleKeyPress (getKeyPress (code, name)); | |||
} | |||
private: | |||
//============================================================================== | |||
struct UnityBitmapImage : public ImagePixelData | |||
{ | |||
UnityBitmapImage (uint8* data, int w, int h) | |||
: ImagePixelData (Image::PixelFormat::ARGB, w, h), | |||
imageData (data), | |||
lineStride (width * pixelStride) | |||
{ | |||
} | |||
std::unique_ptr<ImageType> createType() const override | |||
{ | |||
return std::make_unique<SoftwareImageType>(); | |||
} | |||
std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override | |||
{ | |||
return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (this)); | |||
} | |||
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, [[maybe_unused]] Image::BitmapData::ReadWriteMode mode) override | |||
{ | |||
const auto offset = (size_t) x * (size_t) pixelStride + (size_t) y * (size_t) lineStride; | |||
bitmap.data = imageData + offset; | |||
bitmap.size = (size_t) (lineStride * height) - offset; | |||
bitmap.pixelFormat = pixelFormat; | |||
bitmap.lineStride = lineStride; | |||
bitmap.pixelStride = pixelStride; | |||
} | |||
ImagePixelData::Ptr clone() override | |||
{ | |||
auto im = new UnityBitmapImage (imageData, width, height); | |||
for (int i = 0; i < height; ++i) | |||
memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride); | |||
return im; | |||
} | |||
uint8* imageData; | |||
int pixelStride = 4, lineStride; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityBitmapImage) | |||
}; | |||
//============================================================================== | |||
struct MouseWatcher : public Timer | |||
{ | |||
MouseWatcher (ComponentPeer& o) : owner (o) {} | |||
void timerCallback() override | |||
{ | |||
auto pos = Desktop::getMousePosition(); | |||
if (boundsToWatch.contains (pos) && pos != lastMousePos) | |||
{ | |||
auto ms = Desktop::getInstance().getMainMouseSource(); | |||
if (! ms.getCurrentModifiers().isLeftButtonDown()) | |||
owner.handleMouseEvent (juce::MouseInputSource::mouse, owner.globalToLocal (pos.toFloat()), {}, | |||
juce::MouseInputSource::defaultPressure, juce::MouseInputSource::defaultOrientation, juce::Time::currentTimeMillis()); | |||
lastMousePos = pos; | |||
} | |||
} | |||
void setBoundsToWatch (Rectangle<int> b) | |||
{ | |||
if (boundsToWatch != b) | |||
boundsToWatch = b; | |||
startTimer (250); | |||
} | |||
ComponentPeer& owner; | |||
Rectangle<int> boundsToWatch; | |||
Point<int> lastMousePos; | |||
}; | |||
//============================================================================== | |||
KeyPress getKeyPress (int keyCode, String name) | |||
{ | |||
if (keyCode >= 32 && keyCode <= 64) | |||
return { keyCode, ModifierKeys::currentModifiers, juce::juce_wchar (keyCode) }; | |||
if (keyCode >= 91 && keyCode <= 122) | |||
return { keyCode, ModifierKeys::currentModifiers, name[0] }; | |||
if (keyCode >= 256 && keyCode <= 265) | |||
return { juce::KeyPress::numberPad0 + (keyCode - 256), ModifierKeys::currentModifiers, juce::String (keyCode - 256).getCharPointer()[0] }; | |||
if (keyCode == 8) return { juce::KeyPress::backspaceKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 127) return { juce::KeyPress::deleteKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 9) return { juce::KeyPress::tabKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 13) return { juce::KeyPress::returnKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 27) return { juce::KeyPress::escapeKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 32) return { juce::KeyPress::spaceKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 266) return { juce::KeyPress::numberPadDecimalPoint, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 267) return { juce::KeyPress::numberPadDivide, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 268) return { juce::KeyPress::numberPadMultiply, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 269) return { juce::KeyPress::numberPadSubtract, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 270) return { juce::KeyPress::numberPadAdd, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 272) return { juce::KeyPress::numberPadEquals, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 273) return { juce::KeyPress::upKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 274) return { juce::KeyPress::downKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 275) return { juce::KeyPress::rightKey, ModifierKeys::currentModifiers, {} }; | |||
if (keyCode == 276) return { juce::KeyPress::leftKey, ModifierKeys::currentModifiers, {} }; | |||
return {}; | |||
} | |||
//============================================================================== | |||
Rectangle<int> bounds; | |||
MouseWatcher mouseWatcher; | |||
uint8* pixelData = nullptr; | |||
int textureWidth, textureHeight; | |||
Image renderImage; | |||
//============================================================================== | |||
void setMinimised (bool) override {} | |||
bool isMinimised() const override { return false; } | |||
void setFullScreen (bool) override {} | |||
bool isFullScreen() const override { return false; } | |||
bool setAlwaysOnTop (bool) override { return false; } | |||
void toFront (bool) override {} | |||
void toBehind (ComponentPeer*) override {} | |||
bool isFocused() const override { return true; } | |||
void grabFocus() override {} | |||
void* getNativeHandle() const override { return nullptr; } | |||
OptionalBorderSize getFrameSizeIfPresent() const override { return {}; } | |||
BorderSize<int> getFrameSize() const override { return {}; } | |||
void setVisible (bool) override {} | |||
void setTitle (const String&) override {} | |||
void setIcon (const Image&) override {} | |||
void textInputRequired (Point<int>, TextInputTarget&) override {} | |||
void setAlpha (float) override {} | |||
void performAnyPendingRepaintsNow() override {} | |||
void repaint (const Rectangle<int>&) override {} | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityPeer) | |||
}; | |||
static ComponentPeer* createUnityPeer (Component& c) { return new UnityPeer (c); } | |||
//============================================================================== | |||
class AudioProcessorUnityWrapper | |||
{ | |||
public: | |||
AudioProcessorUnityWrapper (bool isTemporary) | |||
{ | |||
pluginInstance = createPluginFilterOfType (AudioProcessor::wrapperType_Unity); | |||
if (! isTemporary && pluginInstance->hasEditor()) | |||
{ | |||
pluginInstanceEditor.reset (pluginInstance->createEditorIfNeeded()); | |||
pluginInstanceEditor->setVisible (true); | |||
detail::PluginUtilities::addToDesktop (*pluginInstanceEditor, nullptr); | |||
} | |||
juceParameters.update (*pluginInstance, false); | |||
} | |||
~AudioProcessorUnityWrapper() | |||
{ | |||
if (pluginInstanceEditor != nullptr) | |||
{ | |||
pluginInstanceEditor->removeFromDesktop(); | |||
PopupMenu::dismissAllActiveMenus(); | |||
pluginInstanceEditor->processor.editorBeingDeleted (pluginInstanceEditor.get()); | |||
pluginInstanceEditor = nullptr; | |||
} | |||
} | |||
void create (UnityAudioEffectState* state) | |||
{ | |||
// only supported in Unity plugin API > 1.0 | |||
if (state->structSize >= sizeof (UnityAudioEffectState)) | |||
samplesPerBlock = static_cast<int> (state->dspBufferSize); | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
short configs[][2] = { JucePlugin_PreferredChannelConfigurations }; | |||
const int numConfigs = sizeof (configs) / sizeof (short[2]); | |||
jassertquiet (numConfigs > 0 && (configs[0][0] > 0 || configs[0][1] > 0)); | |||
pluginInstance->setPlayConfigDetails (configs[0][0], configs[0][1], state->sampleRate, samplesPerBlock); | |||
#else | |||
pluginInstance->setRateAndBufferSizeDetails (state->sampleRate, samplesPerBlock); | |||
#endif | |||
pluginInstance->prepareToPlay (state->sampleRate, samplesPerBlock); | |||
scratchBuffer.setSize (jmax (pluginInstance->getTotalNumInputChannels(), pluginInstance->getTotalNumOutputChannels()), samplesPerBlock); | |||
} | |||
void release() | |||
{ | |||
pluginInstance->releaseResources(); | |||
} | |||
void reset() | |||
{ | |||
pluginInstance->reset(); | |||
} | |||
void process (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed) | |||
{ | |||
// If the plugin has a bypass parameter, set it to the current bypass state | |||
if (auto* param = pluginInstance->getBypassParameter()) | |||
if (isBypassed != (param->getValue() >= 0.5f)) | |||
param->setValueNotifyingHost (isBypassed ? 1.0f : 0.0f); | |||
for (int pos = 0; pos < bufferSize;) | |||
{ | |||
auto max = jmin (bufferSize - pos, samplesPerBlock); | |||
processBuffers (inBuffer + (pos * numInChannels), outBuffer + (pos * numOutChannels), max, numInChannels, numOutChannels, isBypassed); | |||
pos += max; | |||
} | |||
} | |||
void declareParameters (UnityAudioEffectDefinition& definition) | |||
{ | |||
static std::unique_ptr<UnityAudioParameterDefinition> parametersPtr; | |||
static int numParams = 0; | |||
if (parametersPtr == nullptr) | |||
{ | |||
numParams = (int) juceParameters.size(); | |||
parametersPtr.reset (static_cast<UnityAudioParameterDefinition*> (std::calloc (static_cast<size_t> (numParams), | |||
sizeof (UnityAudioParameterDefinition)))); | |||
parameterDescriptions.clear(); | |||
for (int i = 0; i < numParams; ++i) | |||
{ | |||
auto* parameter = juceParameters.getParamForIndex (i); | |||
auto& paramDef = parametersPtr.get()[i]; | |||
const auto nameLength = (size_t) numElementsInArray (paramDef.name); | |||
const auto unitLength = (size_t) numElementsInArray (paramDef.unit); | |||
parameter->getName ((int) nameLength - 1).copyToUTF8 (paramDef.name, nameLength); | |||
if (parameter->getLabel().isNotEmpty()) | |||
parameter->getLabel().copyToUTF8 (paramDef.unit, unitLength); | |||
parameterDescriptions.add (parameter->getName (15)); | |||
paramDef.description = parameterDescriptions[i].toRawUTF8(); | |||
paramDef.defaultVal = parameter->getDefaultValue(); | |||
paramDef.min = 0.0f; | |||
paramDef.max = 1.0f; | |||
paramDef.displayScale = 1.0f; | |||
paramDef.displayExponent = 1.0f; | |||
} | |||
} | |||
definition.numParameters = static_cast<uint32> (numParams); | |||
definition.parameterDefintions = parametersPtr.get(); | |||
} | |||
void setParameter (int index, float value) { juceParameters.getParamForIndex (index)->setValueNotifyingHost (value); } | |||
float getParameter (int index) const noexcept { return juceParameters.getParamForIndex (index)->getValue(); } | |||
String getParameterString (int index) const noexcept | |||
{ | |||
auto* param = juceParameters.getParamForIndex (index); | |||
return param->getText (param->getValue(), 16); | |||
} | |||
int getNumInputChannels() const noexcept { return pluginInstance->getTotalNumInputChannels(); } | |||
int getNumOutputChannels() const noexcept { return pluginInstance->getTotalNumOutputChannels(); } | |||
bool hasEditor() const noexcept { return pluginInstance->hasEditor(); } | |||
UnityPeer& getEditorPeer() const | |||
{ | |||
auto* peer = dynamic_cast<UnityPeer*> (pluginInstanceEditor->getPeer()); | |||
jassert (peer != nullptr); | |||
return *peer; | |||
} | |||
private: | |||
//============================================================================== | |||
void processBuffers (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed) | |||
{ | |||
int ch; | |||
for (ch = 0; ch < numInChannels; ++ch) | |||
{ | |||
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>; | |||
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const>; | |||
DstSampleType dstData (scratchBuffer.getWritePointer (ch)); | |||
SrcSampleType srcData (inBuffer + ch, numInChannels); | |||
dstData.convertSamples (srcData, bufferSize); | |||
} | |||
for (; ch < numOutChannels; ++ch) | |||
scratchBuffer.clear (ch, 0, bufferSize); | |||
{ | |||
const ScopedLock sl (pluginInstance->getCallbackLock()); | |||
if (pluginInstance->isSuspended()) | |||
{ | |||
scratchBuffer.clear(); | |||
} | |||
else | |||
{ | |||
MidiBuffer mb; | |||
if (isBypassed && pluginInstance->getBypassParameter() == nullptr) | |||
pluginInstance->processBlockBypassed (scratchBuffer, mb); | |||
else | |||
pluginInstance->processBlock (scratchBuffer, mb); | |||
} | |||
} | |||
for (ch = 0; ch < numOutChannels; ++ch) | |||
{ | |||
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst>; | |||
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>; | |||
DstSampleType dstData (outBuffer + ch, numOutChannels); | |||
SrcSampleType srcData (scratchBuffer.getReadPointer (ch)); | |||
dstData.convertSamples (srcData, bufferSize); | |||
} | |||
} | |||
//============================================================================== | |||
std::unique_ptr<AudioProcessor> pluginInstance; | |||
std::unique_ptr<AudioProcessorEditor> pluginInstanceEditor; | |||
int samplesPerBlock = 1024; | |||
StringArray parameterDescriptions; | |||
AudioBuffer<float> scratchBuffer; | |||
LegacyAudioParametersWrapper juceParameters; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorUnityWrapper) | |||
}; | |||
//============================================================================== | |||
static HashMap<int, AudioProcessorUnityWrapper*>& getWrapperMap() | |||
{ | |||
static HashMap<int, AudioProcessorUnityWrapper*> wrapperMap; | |||
return wrapperMap; | |||
} | |||
static void onWrapperCreation (AudioProcessorUnityWrapper* wrapperToAdd) | |||
{ | |||
getWrapperMap().set (std::abs (Random::getSystemRandom().nextInt (65536)), wrapperToAdd); | |||
} | |||
static void onWrapperDeletion (AudioProcessorUnityWrapper* wrapperToRemove) | |||
{ | |||
getWrapperMap().removeValue (wrapperToRemove); | |||
} | |||
//============================================================================== | |||
static UnityAudioEffectDefinition getEffectDefinition() | |||
{ | |||
const auto wrapper = std::make_unique<AudioProcessorUnityWrapper> (true); | |||
const String originalName { JucePlugin_Name }; | |||
const auto name = (! originalName.startsWithIgnoreCase ("audioplugin") ? "audioplugin_" : "") + originalName; | |||
UnityAudioEffectDefinition result{}; | |||
name.copyToUTF8 (result.name, (size_t) numElementsInArray (result.name)); | |||
result.structSize = sizeof (UnityAudioEffectDefinition); | |||
result.parameterStructSize = sizeof (UnityAudioParameterDefinition); | |||
result.apiVersion = UNITY_AUDIO_PLUGIN_API_VERSION; | |||
result.pluginVersion = JucePlugin_VersionCode; | |||
// effects must set this to 0, generators > 0 | |||
result.channels = (wrapper->getNumInputChannels() != 0 ? 0 | |||
: static_cast<uint32> (wrapper->getNumOutputChannels())); | |||
wrapper->declareParameters (result); | |||
result.create = [] (UnityAudioEffectState* state) | |||
{ | |||
auto* pluginInstance = new AudioProcessorUnityWrapper (false); | |||
pluginInstance->create (state); | |||
state->effectData = pluginInstance; | |||
onWrapperCreation (pluginInstance); | |||
return 0; | |||
}; | |||
result.release = [] (UnityAudioEffectState* state) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
pluginInstance->release(); | |||
onWrapperDeletion (pluginInstance); | |||
delete pluginInstance; | |||
if (getWrapperMap().size() == 0) | |||
shutdownJuce_GUI(); | |||
return 0; | |||
}; | |||
result.reset = [] (UnityAudioEffectState* state) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
pluginInstance->reset(); | |||
return 0; | |||
}; | |||
result.setPosition = [] (UnityAudioEffectState* state, unsigned int pos) | |||
{ | |||
ignoreUnused (state, pos); | |||
return 0; | |||
}; | |||
result.process = [] (UnityAudioEffectState* state, | |||
float* inBuffer, | |||
float* outBuffer, | |||
unsigned int bufferSize, | |||
int numInChannels, | |||
int numOutChannels) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
if (pluginInstance != nullptr) | |||
{ | |||
auto isPlaying = ((state->flags & stateIsPlaying) != 0); | |||
auto isMuted = ((state->flags & stateIsMuted) != 0); | |||
auto isPaused = ((state->flags & stateIsPaused) != 0); | |||
const auto bypassed = ! isPlaying || (isMuted || isPaused); | |||
pluginInstance->process (inBuffer, outBuffer, static_cast<int> (bufferSize), numInChannels, numOutChannels, bypassed); | |||
} | |||
else | |||
{ | |||
FloatVectorOperations::clear (outBuffer, static_cast<int> (bufferSize) * numOutChannels); | |||
} | |||
return 0; | |||
}; | |||
result.setFloatParameter = [] (UnityAudioEffectState* state, int index, float value) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
pluginInstance->setParameter (index, value); | |||
return 0; | |||
}; | |||
result.getFloatParameter = [] (UnityAudioEffectState* state, int index, float* value, char* valueStr) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
*value = pluginInstance->getParameter (index); | |||
pluginInstance->getParameterString (index).copyToUTF8 (valueStr, 15); | |||
return 0; | |||
}; | |||
result.getFloatBuffer = [] (UnityAudioEffectState* state, const char* kind, float* buffer, int numSamples) | |||
{ | |||
ignoreUnused (numSamples); | |||
const StringRef kindStr { kind }; | |||
if (kindStr == StringRef ("Editor")) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
buffer[0] = pluginInstance->hasEditor() ? 1.0f : 0.0f; | |||
} | |||
else if (kindStr == StringRef ("ID")) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
for (HashMap<int, AudioProcessorUnityWrapper*>::Iterator i (getWrapperMap()); i.next();) | |||
{ | |||
if (i.getValue() == pluginInstance) | |||
{ | |||
buffer[0] = (float) i.getKey(); | |||
break; | |||
} | |||
} | |||
return 0; | |||
} | |||
else if (kindStr == StringRef ("Size")) | |||
{ | |||
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>(); | |||
auto& editor = pluginInstance->getEditorPeer().getEditor(); | |||
buffer[0] = (float) editor.getBounds().getWidth(); | |||
buffer[1] = (float) editor.getBounds().getHeight(); | |||
buffer[2] = (float) editor.getConstrainer()->getMinimumWidth(); | |||
buffer[3] = (float) editor.getConstrainer()->getMinimumHeight(); | |||
buffer[4] = (float) editor.getConstrainer()->getMaximumWidth(); | |||
buffer[5] = (float) editor.getConstrainer()->getMaximumHeight(); | |||
} | |||
return 0; | |||
}; | |||
return result; | |||
} | |||
} // namespace juce | |||
// From reading the example code, it seems that the triple indirection indicates | |||
// an out-value of an array of pointers. That is, after calling this function, definitionsPtr | |||
// should point to a pre-existing/static array of pointer-to-effect-definition. | |||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API UnityGetAudioEffectDefinitions (UnityAudioEffectDefinition*** definitionsPtr) | |||
{ | |||
if (juce::getWrapperMap().size() == 0) | |||
juce::initialiseJuce_GUI(); | |||
static std::once_flag flag; | |||
std::call_once (flag, [] { juce::juce_createUnityPeerFn = juce::createUnityPeer; }); | |||
static auto definition = juce::getEffectDefinition(); | |||
static UnityAudioEffectDefinition* definitions[] { &definition }; | |||
*definitionsPtr = definitions; | |||
return 1; | |||
} | |||
//============================================================================== | |||
static juce::ModifierKeys unityModifiersToJUCE (UnityEventModifiers mods, bool mouseDown, int mouseButton = -1) | |||
{ | |||
int flags = 0; | |||
if (mouseDown) | |||
{ | |||
if (mouseButton == 0) | |||
flags |= juce::ModifierKeys::leftButtonModifier; | |||
else if (mouseButton == 1) | |||
flags |= juce::ModifierKeys::rightButtonModifier; | |||
else if (mouseButton == 2) | |||
flags |= juce::ModifierKeys::middleButtonModifier; | |||
} | |||
if (mods == 0) | |||
return flags; | |||
if ((mods & UnityEventModifiers::shift) != 0) flags |= juce::ModifierKeys::shiftModifier; | |||
if ((mods & UnityEventModifiers::control) != 0) flags |= juce::ModifierKeys::ctrlModifier; | |||
if ((mods & UnityEventModifiers::alt) != 0) flags |= juce::ModifierKeys::altModifier; | |||
if ((mods & UnityEventModifiers::command) != 0) flags |= juce::ModifierKeys::commandModifier; | |||
return { flags }; | |||
} | |||
//============================================================================== | |||
static juce::AudioProcessorUnityWrapper* getWrapperChecked (int id) | |||
{ | |||
auto* wrapper = juce::getWrapperMap()[id]; | |||
jassert (wrapper != nullptr); | |||
return wrapper; | |||
} | |||
//============================================================================== | |||
static void UNITY_INTERFACE_API onRenderEvent (int id) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().triggerAsyncUpdate(); | |||
} | |||
UNITY_INTERFACE_EXPORT renderCallback UNITY_INTERFACE_API getRenderCallback() | |||
{ | |||
return onRenderEvent; | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityInitialiseTexture (int id, void* data, int w, int h) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().setPixelDataHandle (reinterpret_cast<juce::uint8*> (data), w, h); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDown (int id, float x, float y, UnityEventModifiers unityMods, int button) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button)); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDrag (int id, float x, float y, UnityEventModifiers unityMods, int button) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button)); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseUp (int id, float x, float y, UnityEventModifiers unityMods) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, false)); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityKeyEvent (int id, int code, UnityEventModifiers mods, const char* name) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().forwardKeyPress (code, name, unityModifiersToJUCE (mods, false)); | |||
} | |||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unitySetScreenBounds (int id, float x, float y, float w, float h) | |||
{ | |||
getWrapperChecked (id)->getEditorPeer().getEditor().setBounds ({ (int) x, (int) y, (int) w, (int) h }); | |||
} | |||
//============================================================================== | |||
#if JUCE_WINDOWS | |||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") | |||
extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD reason, LPVOID) | |||
{ | |||
if (reason == DLL_PROCESS_ATTACH) | |||
juce::Process::setCurrentModuleInstanceHandle (instance); | |||
return true; | |||
} | |||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
#endif | |||
#endif |
@@ -23,4 +23,4 @@ | |||
============================================================================== | |||
*/ | |||
#include "detail/juce_PluginUtilities.cpp" | |||
#include "juce_audio_plugin_client_VST2.cpp" |
@@ -23,6 +23,4 @@ | |||
============================================================================== | |||
*/ | |||
#if JucePlugin_Build_VST || JucePlugin_Build_VST3 | |||
#include "detail/juce_VSTWindowUtilities.mm" | |||
#endif | |||
#include "juce_audio_plugin_client_VST3.cpp" |
@@ -27,7 +27,7 @@ extern HWND juce_messageWindowHandle; | |||
namespace detail | |||
{ | |||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity | |||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client | |||
bool isRunningInUnity(); | |||
#else | |||
constexpr bool isRunningInUnity() { return false; } | |||
@@ -50,7 +50,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-function-type") | |||
#endif | |||
void juce_repeatLastProcessPriority(); | |||
bool juce_isRunningInWine(); | |||
using CheckEventBlockedByModalComps = bool (*) (const MSG&); | |||
extern CheckEventBlockedByModalComps isEventBlockedByModalComps; | |||