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.

791 lines
30KB

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