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