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.

786 lines
31KB

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