The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

793 lines
31KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #include <juce_core/system/juce_TargetPlatform.h>
  19. #if JucePlugin_Build_Unity
  20. #include "../utility/juce_IncludeModuleHeaders.h"
  21. #include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp>
  22. #if JUCE_WINDOWS
  23. #include "../utility/juce_IncludeSystemHeaders.h"
  24. #endif
  25. #include "juce_UnityPluginInterface.h"
  26. //==============================================================================
  27. namespace juce
  28. {
  29. typedef ComponentPeer* (*createUnityPeerFunctionType) (Component&);
  30. extern createUnityPeerFunctionType juce_createUnityPeerFn;
  31. //==============================================================================
  32. class UnityPeer : public ComponentPeer,
  33. public AsyncUpdater
  34. {
  35. public:
  36. UnityPeer (Component& ed)
  37. : ComponentPeer (ed, 0),
  38. mouseWatcher (*this)
  39. {
  40. getEditor().setResizable (false, false);
  41. }
  42. //==============================================================================
  43. Rectangle<int> getBounds() const override { return bounds; }
  44. Point<float> localToGlobal (Point<float> relativePosition) override { return relativePosition + getBounds().getPosition().toFloat(); }
  45. Point<float> globalToLocal (Point<float> screenPosition) override { return screenPosition - getBounds().getPosition().toFloat(); }
  46. using ComponentPeer::localToGlobal;
  47. using ComponentPeer::globalToLocal;
  48. StringArray getAvailableRenderingEngines() override { return StringArray ("Software Renderer"); }
  49. void setBounds (const Rectangle<int>& newBounds, bool) override
  50. {
  51. bounds = newBounds;
  52. mouseWatcher.setBoundsToWatch (bounds);
  53. }
  54. bool contains (Point<int> localPos, bool) const override
  55. {
  56. if (isPositiveAndBelow (localPos.getX(), getBounds().getWidth())
  57. && isPositiveAndBelow (localPos.getY(), getBounds().getHeight()))
  58. return true;
  59. return false;
  60. }
  61. void handleAsyncUpdate() override
  62. {
  63. fillPixels();
  64. }
  65. //==============================================================================
  66. AudioProcessorEditor& getEditor() { return *dynamic_cast<AudioProcessorEditor*> (&getComponent()); }
  67. void setPixelDataHandle (uint8* handle, int width, int height)
  68. {
  69. pixelData = handle;
  70. textureWidth = width;
  71. textureHeight = height;
  72. renderImage = Image (new UnityBitmapImage (pixelData, width, height));
  73. }
  74. // N.B. This is NOT an efficient way to do this and you shouldn't use this method in your own code.
  75. // It works for our purposes here but a much more efficient way would be to use a GL texture.
  76. void fillPixels()
  77. {
  78. if (pixelData == nullptr)
  79. return;
  80. LowLevelGraphicsSoftwareRenderer renderer (renderImage);
  81. renderer.addTransform (AffineTransform::verticalFlip ((float) getComponent().getHeight()));
  82. handlePaint (renderer);
  83. for (int i = 0; i < textureWidth * textureHeight * 4; i += 4)
  84. {
  85. auto r = pixelData[i + 2];
  86. auto g = pixelData[i + 1];
  87. auto b = pixelData[i + 0];
  88. pixelData[i + 0] = r;
  89. pixelData[i + 1] = g;
  90. pixelData[i + 2] = b;
  91. }
  92. }
  93. void forwardMouseEvent (Point<float> position, ModifierKeys mods)
  94. {
  95. ModifierKeys::currentModifiers = mods;
  96. handleMouseEvent (juce::MouseInputSource::mouse, position, mods, juce::MouseInputSource::defaultPressure,
  97. juce::MouseInputSource::defaultOrientation, juce::Time::currentTimeMillis());
  98. }
  99. void forwardKeyPress (int code, String name, ModifierKeys mods)
  100. {
  101. ModifierKeys::currentModifiers = mods;
  102. handleKeyPress (getKeyPress (code, name));
  103. }
  104. private:
  105. //==============================================================================
  106. struct UnityBitmapImage : public ImagePixelData
  107. {
  108. UnityBitmapImage (uint8* data, int w, int h)
  109. : ImagePixelData (Image::PixelFormat::ARGB, w, h),
  110. imageData (data),
  111. lineStride (width * pixelStride)
  112. {
  113. }
  114. std::unique_ptr<ImageType> createType() const override
  115. {
  116. return std::make_unique<SoftwareImageType>();
  117. }
  118. std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
  119. {
  120. return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (this));
  121. }
  122. void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
  123. {
  124. ignoreUnused (mode);
  125. const auto offset = (size_t) x * (size_t) pixelStride + (size_t) y * (size_t) lineStride;
  126. bitmap.data = imageData + offset;
  127. bitmap.size = (size_t) (lineStride * height) - offset;
  128. bitmap.pixelFormat = pixelFormat;
  129. bitmap.lineStride = lineStride;
  130. bitmap.pixelStride = pixelStride;
  131. }
  132. ImagePixelData::Ptr clone() override
  133. {
  134. auto im = new UnityBitmapImage (imageData, width, height);
  135. for (int i = 0; i < height; ++i)
  136. memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride);
  137. return im;
  138. }
  139. uint8* imageData;
  140. int pixelStride = 4, lineStride;
  141. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityBitmapImage)
  142. };
  143. //==============================================================================
  144. struct MouseWatcher : public Timer
  145. {
  146. MouseWatcher (ComponentPeer& o) : owner (o) {}
  147. void timerCallback() override
  148. {
  149. auto pos = Desktop::getMousePosition();
  150. if (boundsToWatch.contains (pos) && pos != lastMousePos)
  151. {
  152. auto ms = Desktop::getInstance().getMainMouseSource();
  153. if (! ms.getCurrentModifiers().isLeftButtonDown())
  154. owner.handleMouseEvent (juce::MouseInputSource::mouse, owner.globalToLocal (pos.toFloat()), {},
  155. juce::MouseInputSource::defaultPressure, juce::MouseInputSource::defaultOrientation, juce::Time::currentTimeMillis());
  156. lastMousePos = pos;
  157. }
  158. }
  159. void setBoundsToWatch (Rectangle<int> b)
  160. {
  161. if (boundsToWatch != b)
  162. boundsToWatch = b;
  163. startTimer (250);
  164. }
  165. ComponentPeer& owner;
  166. Rectangle<int> boundsToWatch;
  167. Point<int> lastMousePos;
  168. };
  169. //==============================================================================
  170. KeyPress getKeyPress (int keyCode, String name)
  171. {
  172. if (keyCode >= 32 && keyCode <= 64)
  173. return { keyCode, ModifierKeys::currentModifiers, juce::juce_wchar (keyCode) };
  174. if (keyCode >= 91 && keyCode <= 122)
  175. return { keyCode, ModifierKeys::currentModifiers, name[0] };
  176. if (keyCode >= 256 && keyCode <= 265)
  177. return { juce::KeyPress::numberPad0 + (keyCode - 256), ModifierKeys::currentModifiers, juce::String (keyCode - 256).getCharPointer()[0] };
  178. if (keyCode == 8) return { juce::KeyPress::backspaceKey, ModifierKeys::currentModifiers, {} };
  179. if (keyCode == 127) return { juce::KeyPress::deleteKey, ModifierKeys::currentModifiers, {} };
  180. if (keyCode == 9) return { juce::KeyPress::tabKey, ModifierKeys::currentModifiers, {} };
  181. if (keyCode == 13) return { juce::KeyPress::returnKey, ModifierKeys::currentModifiers, {} };
  182. if (keyCode == 27) return { juce::KeyPress::escapeKey, ModifierKeys::currentModifiers, {} };
  183. if (keyCode == 32) return { juce::KeyPress::spaceKey, ModifierKeys::currentModifiers, {} };
  184. if (keyCode == 266) return { juce::KeyPress::numberPadDecimalPoint, ModifierKeys::currentModifiers, {} };
  185. if (keyCode == 267) return { juce::KeyPress::numberPadDivide, ModifierKeys::currentModifiers, {} };
  186. if (keyCode == 268) return { juce::KeyPress::numberPadMultiply, ModifierKeys::currentModifiers, {} };
  187. if (keyCode == 269) return { juce::KeyPress::numberPadSubtract, ModifierKeys::currentModifiers, {} };
  188. if (keyCode == 270) return { juce::KeyPress::numberPadAdd, ModifierKeys::currentModifiers, {} };
  189. if (keyCode == 272) return { juce::KeyPress::numberPadEquals, ModifierKeys::currentModifiers, {} };
  190. if (keyCode == 273) return { juce::KeyPress::upKey, ModifierKeys::currentModifiers, {} };
  191. if (keyCode == 274) return { juce::KeyPress::downKey, ModifierKeys::currentModifiers, {} };
  192. if (keyCode == 275) return { juce::KeyPress::rightKey, ModifierKeys::currentModifiers, {} };
  193. if (keyCode == 276) return { juce::KeyPress::leftKey, ModifierKeys::currentModifiers, {} };
  194. return {};
  195. }
  196. //==============================================================================
  197. Rectangle<int> bounds;
  198. MouseWatcher mouseWatcher;
  199. uint8* pixelData = nullptr;
  200. int textureWidth, textureHeight;
  201. Image renderImage;
  202. //==============================================================================
  203. void setMinimised (bool) override {}
  204. bool isMinimised() const override { return false; }
  205. void setFullScreen (bool) override {}
  206. bool isFullScreen() const override { return false; }
  207. bool setAlwaysOnTop (bool) override { return false; }
  208. void toFront (bool) override {}
  209. void toBehind (ComponentPeer*) override {}
  210. bool isFocused() const override { return true; }
  211. void grabFocus() override {}
  212. void* getNativeHandle() const override { return nullptr; }
  213. OptionalBorderSize getFrameSizeIfPresent() const override { return {}; }
  214. BorderSize<int> getFrameSize() const override { return {}; }
  215. void setVisible (bool) override {}
  216. void setTitle (const String&) override {}
  217. void setIcon (const Image&) override {}
  218. void textInputRequired (Point<int>, TextInputTarget&) override {}
  219. void setAlpha (float) override {}
  220. void performAnyPendingRepaintsNow() override {}
  221. void repaint (const Rectangle<int>&) override {}
  222. //==============================================================================
  223. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityPeer)
  224. };
  225. static ComponentPeer* createUnityPeer (Component& c) { return new UnityPeer (c); }
  226. //==============================================================================
  227. class AudioProcessorUnityWrapper
  228. {
  229. public:
  230. AudioProcessorUnityWrapper (bool isTemporary)
  231. {
  232. pluginInstance.reset (createPluginFilterOfType (AudioProcessor::wrapperType_Unity));
  233. if (! isTemporary && pluginInstance->hasEditor())
  234. {
  235. pluginInstanceEditor.reset (pluginInstance->createEditorIfNeeded());
  236. pluginInstanceEditor->setVisible (true);
  237. pluginInstanceEditor->addToDesktop (0);
  238. }
  239. juceParameters.update (*pluginInstance, false);
  240. }
  241. ~AudioProcessorUnityWrapper()
  242. {
  243. if (pluginInstanceEditor != nullptr)
  244. {
  245. pluginInstanceEditor->removeFromDesktop();
  246. PopupMenu::dismissAllActiveMenus();
  247. pluginInstanceEditor->processor.editorBeingDeleted (pluginInstanceEditor.get());
  248. pluginInstanceEditor = nullptr;
  249. }
  250. }
  251. void create (UnityAudioEffectState* state)
  252. {
  253. // only supported in Unity plugin API > 1.0
  254. if (state->structSize >= sizeof (UnityAudioEffectState))
  255. samplesPerBlock = static_cast<int> (state->dspBufferSize);
  256. #ifdef JucePlugin_PreferredChannelConfigurations
  257. short configs[][2] = { JucePlugin_PreferredChannelConfigurations };
  258. const int numConfigs = sizeof (configs) / sizeof (short[2]);
  259. jassertquiet (numConfigs > 0 && (configs[0][0] > 0 || configs[0][1] > 0));
  260. pluginInstance->setPlayConfigDetails (configs[0][0], configs[0][1], state->sampleRate, samplesPerBlock);
  261. #else
  262. pluginInstance->setRateAndBufferSizeDetails (state->sampleRate, samplesPerBlock);
  263. #endif
  264. pluginInstance->prepareToPlay (state->sampleRate, samplesPerBlock);
  265. scratchBuffer.setSize (jmax (pluginInstance->getTotalNumInputChannels(), pluginInstance->getTotalNumOutputChannels()), samplesPerBlock);
  266. }
  267. void release()
  268. {
  269. pluginInstance->releaseResources();
  270. }
  271. void reset()
  272. {
  273. pluginInstance->reset();
  274. }
  275. void process (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed)
  276. {
  277. // If the plugin has a bypass parameter, set it to the current bypass state
  278. if (auto* param = pluginInstance->getBypassParameter())
  279. if (isBypassed != (param->getValue() >= 0.5f))
  280. param->setValueNotifyingHost (isBypassed ? 1.0f : 0.0f);
  281. for (int pos = 0; pos < bufferSize;)
  282. {
  283. auto max = jmin (bufferSize - pos, samplesPerBlock);
  284. processBuffers (inBuffer + (pos * numInChannels), outBuffer + (pos * numOutChannels), max, numInChannels, numOutChannels, isBypassed);
  285. pos += max;
  286. }
  287. }
  288. void declareParameters (UnityAudioEffectDefinition& definition)
  289. {
  290. static std::unique_ptr<UnityAudioParameterDefinition> parametersPtr;
  291. static int numParams = 0;
  292. if (parametersPtr == nullptr)
  293. {
  294. numParams = (int) juceParameters.size();
  295. parametersPtr.reset (static_cast<UnityAudioParameterDefinition*> (std::calloc (static_cast<size_t> (numParams),
  296. sizeof (UnityAudioParameterDefinition))));
  297. parameterDescriptions.clear();
  298. for (int i = 0; i < numParams; ++i)
  299. {
  300. auto* parameter = juceParameters.getParamForIndex (i);
  301. auto& paramDef = parametersPtr.get()[i];
  302. const auto nameLength = (size_t) numElementsInArray (paramDef.name);
  303. const auto unitLength = (size_t) numElementsInArray (paramDef.unit);
  304. parameter->getName ((int) nameLength - 1).copyToUTF8 (paramDef.name, nameLength);
  305. if (parameter->getLabel().isNotEmpty())
  306. parameter->getLabel().copyToUTF8 (paramDef.unit, unitLength);
  307. parameterDescriptions.add (parameter->getName (15));
  308. paramDef.description = parameterDescriptions[i].toRawUTF8();
  309. paramDef.defaultVal = parameter->getDefaultValue();
  310. paramDef.min = 0.0f;
  311. paramDef.max = 1.0f;
  312. paramDef.displayScale = 1.0f;
  313. paramDef.displayExponent = 1.0f;
  314. }
  315. }
  316. definition.numParameters = static_cast<uint32> (numParams);
  317. definition.parameterDefintions = parametersPtr.get();
  318. }
  319. void setParameter (int index, float value) { juceParameters.getParamForIndex (index)->setValueNotifyingHost (value); }
  320. float getParameter (int index) const noexcept { return juceParameters.getParamForIndex (index)->getValue(); }
  321. String getParameterString (int index) const noexcept
  322. {
  323. auto* param = juceParameters.getParamForIndex (index);
  324. return param->getText (param->getValue(), 16);
  325. }
  326. int getNumInputChannels() const noexcept { return pluginInstance->getTotalNumInputChannels(); }
  327. int getNumOutputChannels() const noexcept { return pluginInstance->getTotalNumOutputChannels(); }
  328. bool hasEditor() const noexcept { return pluginInstance->hasEditor(); }
  329. UnityPeer& getEditorPeer() const
  330. {
  331. auto* peer = dynamic_cast<UnityPeer*> (pluginInstanceEditor->getPeer());
  332. jassert (peer != nullptr);
  333. return *peer;
  334. }
  335. private:
  336. //==============================================================================
  337. void processBuffers (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed)
  338. {
  339. int ch;
  340. for (ch = 0; ch < numInChannels; ++ch)
  341. {
  342. using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
  343. using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const>;
  344. DstSampleType dstData (scratchBuffer.getWritePointer (ch));
  345. SrcSampleType srcData (inBuffer + ch, numInChannels);
  346. dstData.convertSamples (srcData, bufferSize);
  347. }
  348. for (; ch < numOutChannels; ++ch)
  349. scratchBuffer.clear (ch, 0, bufferSize);
  350. {
  351. const ScopedLock sl (pluginInstance->getCallbackLock());
  352. if (pluginInstance->isSuspended())
  353. {
  354. scratchBuffer.clear();
  355. }
  356. else
  357. {
  358. MidiBuffer mb;
  359. if (isBypassed && pluginInstance->getBypassParameter() == nullptr)
  360. pluginInstance->processBlockBypassed (scratchBuffer, mb);
  361. else
  362. pluginInstance->processBlock (scratchBuffer, mb);
  363. }
  364. }
  365. for (ch = 0; ch < numOutChannels; ++ch)
  366. {
  367. using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst>;
  368. using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
  369. DstSampleType dstData (outBuffer + ch, numOutChannels);
  370. SrcSampleType srcData (scratchBuffer.getReadPointer (ch));
  371. dstData.convertSamples (srcData, bufferSize);
  372. }
  373. }
  374. //==============================================================================
  375. std::unique_ptr<AudioProcessor> pluginInstance;
  376. std::unique_ptr<AudioProcessorEditor> pluginInstanceEditor;
  377. int samplesPerBlock = 1024;
  378. StringArray parameterDescriptions;
  379. AudioBuffer<float> scratchBuffer;
  380. LegacyAudioParametersWrapper juceParameters;
  381. //==============================================================================
  382. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorUnityWrapper)
  383. };
  384. //==============================================================================
  385. static HashMap<int, AudioProcessorUnityWrapper*>& getWrapperMap()
  386. {
  387. static HashMap<int, AudioProcessorUnityWrapper*> wrapperMap;
  388. return wrapperMap;
  389. }
  390. static void onWrapperCreation (AudioProcessorUnityWrapper* wrapperToAdd)
  391. {
  392. getWrapperMap().set (std::abs (Random::getSystemRandom().nextInt (65536)), wrapperToAdd);
  393. }
  394. static void onWrapperDeletion (AudioProcessorUnityWrapper* wrapperToRemove)
  395. {
  396. getWrapperMap().removeValue (wrapperToRemove);
  397. }
  398. //==============================================================================
  399. namespace UnityCallbacks
  400. {
  401. static int UNITY_INTERFACE_API createCallback (UnityAudioEffectState* state)
  402. {
  403. auto* pluginInstance = new AudioProcessorUnityWrapper (false);
  404. pluginInstance->create (state);
  405. state->effectData = pluginInstance;
  406. onWrapperCreation (pluginInstance);
  407. return 0;
  408. }
  409. static int UNITY_INTERFACE_API releaseCallback (UnityAudioEffectState* state)
  410. {
  411. auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
  412. pluginInstance->release();
  413. onWrapperDeletion (pluginInstance);
  414. delete pluginInstance;
  415. if (getWrapperMap().size() == 0)
  416. shutdownJuce_GUI();
  417. return 0;
  418. }
  419. static int UNITY_INTERFACE_API resetCallback (UnityAudioEffectState* state)
  420. {
  421. auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
  422. pluginInstance->reset();
  423. return 0;
  424. }
  425. static int UNITY_INTERFACE_API setPositionCallback (UnityAudioEffectState* state, unsigned int pos)
  426. {
  427. ignoreUnused (state, pos);
  428. return 0;
  429. }
  430. static int UNITY_INTERFACE_API setFloatParameterCallback (UnityAudioEffectState* state, int index, float value)
  431. {
  432. auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
  433. pluginInstance->setParameter (index, value);
  434. return 0;
  435. }
  436. static int UNITY_INTERFACE_API getFloatParameterCallback (UnityAudioEffectState* state, int index, float* value, char* valueStr)
  437. {
  438. auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
  439. *value = pluginInstance->getParameter (index);
  440. pluginInstance->getParameterString (index).copyToUTF8 (valueStr, 15);
  441. return 0;
  442. }
  443. static int UNITY_INTERFACE_API getFloatBufferCallback (UnityAudioEffectState* state, const char* name, float* buffer, int numSamples)
  444. {
  445. ignoreUnused (numSamples);
  446. auto nameStr = String (name);
  447. if (nameStr == "Editor")
  448. {
  449. auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
  450. buffer[0] = pluginInstance->hasEditor() ? 1.0f : 0.0f;
  451. }
  452. else if (nameStr == "ID")
  453. {
  454. auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
  455. for (HashMap<int, AudioProcessorUnityWrapper*>::Iterator i (getWrapperMap()); i.next();)
  456. {
  457. if (i.getValue() == pluginInstance)
  458. {
  459. buffer[0] = (float) i.getKey();
  460. break;
  461. }
  462. }
  463. return 0;
  464. }
  465. else if (nameStr == "Size")
  466. {
  467. auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
  468. auto& editor = pluginInstance->getEditorPeer().getEditor();
  469. buffer[0] = (float) editor.getBounds().getWidth();
  470. buffer[1] = (float) editor.getBounds().getHeight();
  471. buffer[2] = (float) editor.getConstrainer()->getMinimumWidth();
  472. buffer[3] = (float) editor.getConstrainer()->getMinimumHeight();
  473. buffer[4] = (float) editor.getConstrainer()->getMaximumWidth();
  474. buffer[5] = (float) editor.getConstrainer()->getMaximumHeight();
  475. }
  476. return 0;
  477. }
  478. static int UNITY_INTERFACE_API processCallback (UnityAudioEffectState* state, float* inBuffer, float* outBuffer,
  479. unsigned int bufferSize, int numInChannels, int numOutChannels)
  480. {
  481. auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
  482. if (pluginInstance != nullptr)
  483. {
  484. auto isPlaying = ((state->flags & stateIsPlaying) != 0);
  485. auto isMuted = ((state->flags & stateIsMuted) != 0);
  486. auto isPaused = ((state->flags & stateIsPaused) != 0);
  487. const auto bypassed = ! isPlaying || (isMuted || isPaused);
  488. pluginInstance->process (inBuffer, outBuffer, static_cast<int> (bufferSize), numInChannels, numOutChannels, bypassed);
  489. }
  490. else
  491. {
  492. FloatVectorOperations::clear (outBuffer, static_cast<int> (bufferSize) * numOutChannels);
  493. }
  494. return 0;
  495. }
  496. }
  497. //==============================================================================
  498. static void declareEffect (UnityAudioEffectDefinition& definition)
  499. {
  500. memset (&definition, 0, sizeof (definition));
  501. std::unique_ptr<AudioProcessorUnityWrapper> wrapper = std::make_unique<AudioProcessorUnityWrapper> (true);
  502. String name (JucePlugin_Name);
  503. if (! name.startsWithIgnoreCase ("audioplugin"))
  504. name = "audioplugin_" + name;
  505. name.copyToUTF8 (definition.name, (size_t) numElementsInArray (definition.name));
  506. definition.structSize = sizeof (UnityAudioEffectDefinition);
  507. definition.parameterStructSize = sizeof (UnityAudioParameterDefinition);
  508. definition.apiVersion = UNITY_AUDIO_PLUGIN_API_VERSION;
  509. definition.pluginVersion = JucePlugin_VersionCode;
  510. // effects must set this to 0, generators > 0
  511. definition.channels = (wrapper->getNumInputChannels() != 0 ? 0
  512. : static_cast<uint32> (wrapper->getNumOutputChannels()));
  513. wrapper->declareParameters (definition);
  514. definition.create = UnityCallbacks::createCallback;
  515. definition.release = UnityCallbacks::releaseCallback;
  516. definition.reset = UnityCallbacks::resetCallback;
  517. definition.setPosition = UnityCallbacks::setPositionCallback;
  518. definition.process = UnityCallbacks::processCallback;
  519. definition.setFloatParameter = UnityCallbacks::setFloatParameterCallback;
  520. definition.getFloatParameter = UnityCallbacks::getFloatParameterCallback;
  521. definition.getFloatBuffer = UnityCallbacks::getFloatBufferCallback;
  522. }
  523. } // namespace juce
  524. UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API UnityGetAudioEffectDefinitions (UnityAudioEffectDefinition*** definitionsPtr)
  525. {
  526. if (juce::getWrapperMap().size() == 0)
  527. juce::initialiseJuce_GUI();
  528. static bool hasInitialised = false;
  529. if (! hasInitialised)
  530. {
  531. juce::PluginHostType::jucePlugInClientCurrentWrapperType = juce::AudioProcessor::wrapperType_Unity;
  532. juce::juce_createUnityPeerFn = juce::createUnityPeer;
  533. hasInitialised = true;
  534. }
  535. auto* definition = new UnityAudioEffectDefinition();
  536. juce::declareEffect (*definition);
  537. *definitionsPtr = &definition;
  538. return 1;
  539. }
  540. //==============================================================================
  541. static juce::ModifierKeys unityModifiersToJUCE (UnityEventModifiers mods, bool mouseDown, int mouseButton = -1)
  542. {
  543. int flags = 0;
  544. if (mouseDown)
  545. {
  546. if (mouseButton == 0)
  547. flags |= juce::ModifierKeys::leftButtonModifier;
  548. else if (mouseButton == 1)
  549. flags |= juce::ModifierKeys::rightButtonModifier;
  550. else if (mouseButton == 2)
  551. flags |= juce::ModifierKeys::middleButtonModifier;
  552. }
  553. if (mods == 0)
  554. return flags;
  555. if ((mods & UnityEventModifiers::shift) != 0) flags |= juce::ModifierKeys::shiftModifier;
  556. if ((mods & UnityEventModifiers::control) != 0) flags |= juce::ModifierKeys::ctrlModifier;
  557. if ((mods & UnityEventModifiers::alt) != 0) flags |= juce::ModifierKeys::altModifier;
  558. if ((mods & UnityEventModifiers::command) != 0) flags |= juce::ModifierKeys::commandModifier;
  559. return { flags };
  560. }
  561. //==============================================================================
  562. static juce::AudioProcessorUnityWrapper* getWrapperChecked (int id)
  563. {
  564. auto* wrapper = juce::getWrapperMap()[id];
  565. jassert (wrapper != nullptr);
  566. return wrapper;
  567. }
  568. //==============================================================================
  569. static void UNITY_INTERFACE_API onRenderEvent (int id)
  570. {
  571. getWrapperChecked (id)->getEditorPeer().triggerAsyncUpdate();
  572. }
  573. UNITY_INTERFACE_EXPORT renderCallback UNITY_INTERFACE_API getRenderCallback()
  574. {
  575. return onRenderEvent;
  576. }
  577. UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityInitialiseTexture (int id, void* data, int w, int h)
  578. {
  579. getWrapperChecked (id)->getEditorPeer().setPixelDataHandle (reinterpret_cast<juce::uint8*> (data), w, h);
  580. }
  581. UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDown (int id, float x, float y, UnityEventModifiers unityMods, int button)
  582. {
  583. getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button));
  584. }
  585. UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDrag (int id, float x, float y, UnityEventModifiers unityMods, int button)
  586. {
  587. getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button));
  588. }
  589. UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseUp (int id, float x, float y, UnityEventModifiers unityMods)
  590. {
  591. getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, false));
  592. }
  593. UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityKeyEvent (int id, int code, UnityEventModifiers mods, const char* name)
  594. {
  595. getWrapperChecked (id)->getEditorPeer().forwardKeyPress (code, name, unityModifiersToJUCE (mods, false));
  596. }
  597. UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unitySetScreenBounds (int id, float x, float y, float w, float h)
  598. {
  599. getWrapperChecked (id)->getEditorPeer().getEditor().setBounds ({ (int) x, (int) y, (int) w, (int) h });
  600. }
  601. //==============================================================================
  602. #if JUCE_WINDOWS
  603. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes")
  604. extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD reason, LPVOID)
  605. {
  606. if (reason == DLL_PROCESS_ATTACH)
  607. juce::Process::setCurrentModuleInstanceHandle (instance);
  608. return true;
  609. }
  610. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  611. #endif
  612. #endif