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.

1139 lines
38KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "juce_RTASCompileFlags.h"
  24. #ifdef _MSC_VER
  25. // the Digidesign projects all use a struct alignment of 2..
  26. #pragma pack (2)
  27. #pragma warning (disable: 4267)
  28. #include "ForcedInclude.h"
  29. #include "Mac2Win.H"
  30. #endif
  31. /* Note about include paths
  32. ------------------------
  33. To be able to include all the Digidesign headers correctly, you'll need to add this
  34. lot to your include path:
  35. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\EffectClasses
  36. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses
  37. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses\Interfaces
  38. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Utilities
  39. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\RTASP_Adapt
  40. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\CoreClasses
  41. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Controls
  42. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Meters
  43. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ViewClasses
  44. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\DSPClasses
  45. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Interfaces
  46. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\common
  47. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\common\Platform
  48. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\SignalProcessing\Public
  49. c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugIns\DSPManager\Interfaces
  50. c:\yourdirectory\PT_711_SDK\AlturaPorts\SADriver\Interfaces
  51. c:\yourdirectory\PT_711_SDK\AlturaPorts\DigiPublic\Interfaces
  52. c:\yourdirectory\PT_711_SDK\AlturaPorts\Fic\Interfaces\DAEClient
  53. c:\yourdirectory\PT_711_SDK\AlturaPorts\NewFileLibs\Cmn
  54. c:\yourdirectory\PT_711_SDK\AlturaPorts\NewFileLibs\DOA
  55. c:\yourdirectory\PT_711_SDK\AlturaPorts\AlturaSource\PPC_H
  56. c:\yourdirectory\PT_711_SDK\AlturaPorts\AlturaSource\AppSupport
  57. NB. If you hit a huge pile of bugs around here, make sure that you've not got the
  58. Apple QuickTime headers before the PT headers in your path, because there are
  59. some filename clashes between them.
  60. */
  61. #include "CEffectGroupMIDI.h"
  62. #include "CEffectProcessMIDI.h"
  63. #include "CEffectProcessRTAS.h"
  64. #include "CCustomView.h"
  65. #include "CEffectTypeRTAS.h"
  66. #include "CPluginControl.h"
  67. #include "CPluginControl_OnOff.h"
  68. #include "FicProcessTokens.h"
  69. //==============================================================================
  70. #ifdef _MSC_VER
  71. #pragma pack (push, 8)
  72. #endif
  73. // On the mac, the amalgamated build works ok, but causes a few strange problems in windows,
  74. // so stick with the normal one on win32 for now...
  75. #ifdef _MSC_VER
  76. #include "../../../../../juce.h"
  77. #else
  78. #include "../../../../../juce_amalgamated.h"
  79. #endif
  80. #include "../../juce_IncludeCharacteristics.h"
  81. #ifdef _MSC_VER
  82. #pragma pack (pop)
  83. #endif
  84. #undef Component
  85. #undef MemoryBlock
  86. //==============================================================================
  87. #if JUCE_WIN32
  88. extern void JUCE_CALLTYPE attachSubWindow (void* hostWindow, int& titleW, int& titleH, juce::Component* comp);
  89. extern void JUCE_CALLTYPE resizeHostWindow (void* hostWindow, int& titleW, int& titleH, juce::Component* comp);
  90. #if ! JucePlugin_EditorRequiresKeyboardFocus
  91. extern void JUCE_CALLTYPE passFocusToHostWindow (void* hostWindow);
  92. #endif
  93. #endif
  94. const int midiBufferSize = 1024;
  95. const OSType juceChunkType = 'juce';
  96. static const int bypassControlIndex = 1;
  97. //==============================================================================
  98. /** Somewhere in the codebase of your plugin, you need to implement this function
  99. and make it return a new instance of the filter subclass that you're building.
  100. */
  101. extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
  102. //==============================================================================
  103. static float longToFloat (const long n) throw()
  104. {
  105. return (float) ((((double) n) + (double) 0x80000000) / (double) 0xffffffff);
  106. }
  107. static long floatToLong (const float n) throw()
  108. {
  109. return roundDoubleToInt (jlimit (-(double) 0x80000000,
  110. (double) 0x7fffffff,
  111. n * (double) 0xffffffff - (double) 0x80000000));
  112. }
  113. static int numInstances = 0;
  114. //==============================================================================
  115. class JucePlugInProcess : public CEffectProcessMIDI,
  116. public CEffectProcessRTAS,
  117. public AudioProcessorListener,
  118. public AudioPlayHead,
  119. public AsyncUpdater
  120. {
  121. public:
  122. //==============================================================================
  123. JucePlugInProcess()
  124. : channels (0),
  125. prepared (false),
  126. midiBufferNode (0),
  127. midiTransport (0),
  128. sampleRate (44100.0)
  129. {
  130. juceFilter = createPluginFilter();
  131. jassert (juceFilter != 0);
  132. AddChunk (juceChunkType, "Juce Audio Plugin Data");
  133. ++numInstances;
  134. }
  135. ~JucePlugInProcess()
  136. {
  137. if (mLoggedIn)
  138. MIDILogOut();
  139. deleteAndZero (midiBufferNode);
  140. deleteAndZero (midiTransport);
  141. if (prepared)
  142. juceFilter->releaseResources();
  143. delete juceFilter;
  144. juce_free (channels);
  145. if (--numInstances == 0)
  146. shutdownJuce_GUI();
  147. }
  148. //==============================================================================
  149. class JuceCustomUIView : public CCustomView,
  150. public Timer
  151. {
  152. public:
  153. //==============================================================================
  154. JuceCustomUIView (AudioProcessor* const filter_,
  155. JucePlugInProcess* const process_)
  156. : filter (filter_),
  157. process (process_),
  158. editorComp (0),
  159. wrapper (0)
  160. {
  161. // setting the size in here crashes PT for some reason, so keep it simple..
  162. }
  163. ~JuceCustomUIView()
  164. {
  165. deleteEditorComp();
  166. }
  167. //==============================================================================
  168. void updateSize()
  169. {
  170. if (editorComp == 0)
  171. {
  172. editorComp = filter->createEditorIfNeeded();
  173. jassert (editorComp != 0);
  174. }
  175. Rect oldRect;
  176. GetRect (&oldRect);
  177. Rect r;
  178. r.left = 0;
  179. r.top = 0;
  180. r.right = editorComp->getWidth();
  181. r.bottom = editorComp->getHeight();
  182. SetRect (&r);
  183. if ((oldRect.right != r.right) || (oldRect.bottom != r.bottom))
  184. startTimer (50);
  185. }
  186. void timerCallback()
  187. {
  188. if (! JUCE_NAMESPACE::Component::isMouseButtonDownAnywhere())
  189. {
  190. stopTimer();
  191. // Send a token to the host to tell it about the resize
  192. SSetProcessWindowResizeToken token (process->fRootNameId, process->fRootNameId);
  193. FicSDSDispatchToken (&token);
  194. }
  195. }
  196. void attachToWindow (GrafPtr port)
  197. {
  198. if (port != 0)
  199. {
  200. updateSize();
  201. #if JUCE_WIN32
  202. void* const hostWindow = (void*) ASI_GethWnd ((WindowPtr) port);
  203. #else
  204. void* const hostWindow = (void*) GetWindowFromPort (port);
  205. #endif
  206. deleteAndZero (wrapper);
  207. wrapper = new EditorCompWrapper (hostWindow, editorComp, this);
  208. }
  209. else
  210. {
  211. deleteEditorComp();
  212. }
  213. }
  214. void DrawContents (Rect*)
  215. {
  216. if (wrapper != 0)
  217. {
  218. ComponentPeer* const peer = wrapper->getPeer();
  219. if (peer != 0)
  220. {
  221. #if JUCE_WIN32
  222. // (seems to be required in PT6.4, but not in 7.x)
  223. peer->repaint (0, 0, wrapper->getWidth(), wrapper->getHeight());
  224. #elif JUCE_PPC
  225. // This crap is needed because if you resize a window, PT doesn't
  226. // update its clip region, so only part of your new window gets drawn.
  227. // This overrides the clipping region that's being passed into the Draw
  228. // method.
  229. Rect visible;
  230. GetVisibleRect (&visible);
  231. RestoreFocus();
  232. Focus (&visible);
  233. #endif
  234. peer->performAnyPendingRepaintsNow();
  235. }
  236. }
  237. }
  238. void DrawBackground (Rect* r)
  239. {
  240. }
  241. //==============================================================================
  242. private:
  243. AudioProcessor* const filter;
  244. JucePlugInProcess* const process;
  245. juce::Component* wrapper;
  246. AudioProcessorEditor* editorComp;
  247. void deleteEditorComp()
  248. {
  249. if (editorComp != 0)
  250. {
  251. PopupMenu::dismissAllActiveMenus();
  252. juce::Component* const modalComponent = juce::Component::getCurrentlyModalComponent();
  253. if (modalComponent != 0)
  254. modalComponent->exitModalState (0);
  255. filter->editorBeingDeleted (editorComp);
  256. deleteAndZero (editorComp);
  257. deleteAndZero (wrapper);
  258. }
  259. }
  260. //==============================================================================
  261. // A component to hold the AudioProcessorEditor, and cope with some housekeeping
  262. // chores when it changes or repaints.
  263. class EditorCompWrapper : public juce::Component,
  264. #if JUCE_MAC
  265. public Timer
  266. #else
  267. public FocusChangeListener
  268. #endif
  269. {
  270. public:
  271. EditorCompWrapper (void* const hostWindow_,
  272. AudioProcessorEditor* const editorComp,
  273. JuceCustomUIView* const owner_)
  274. : hostWindow (hostWindow_),
  275. owner (owner_),
  276. titleW (0),
  277. titleH (0)
  278. #if JUCE_MAC
  279. , forcedRepaintTimer (0)
  280. #endif
  281. {
  282. #if ! JucePlugin_EditorRequiresKeyboardFocus
  283. setWantsKeyboardFocus (false);
  284. #endif
  285. setOpaque (true);
  286. setBroughtToFrontOnMouseClick (true);
  287. setBounds (editorComp->getBounds());
  288. editorComp->setTopLeftPosition (0, 0);
  289. addAndMakeVisible (editorComp);
  290. #if JUCE_WIN32
  291. attachSubWindow (hostWindow, titleW, titleH, this);
  292. setVisible (true);
  293. #else
  294. SetAutomaticControlDragTrackingEnabledForWindow ((WindowRef) hostWindow_, true);
  295. WindowAttributes attributes;
  296. GetWindowAttributes ((WindowRef) hostWindow_, &attributes);
  297. parentView = 0;
  298. if ((attributes & kWindowCompositingAttribute) != 0)
  299. {
  300. HIViewRef root = HIViewGetRoot ((WindowRef) hostWindow_);
  301. HIViewFindByID (root, kHIViewWindowContentID, &parentView);
  302. if (parentView == 0)
  303. parentView = root;
  304. }
  305. else
  306. {
  307. GetRootControl ((WindowRef) hostWindow_, (ControlRef*) &parentView);
  308. if (parentView == 0)
  309. CreateRootControl ((WindowRef) hostWindow_, (ControlRef*) &parentView);
  310. }
  311. jassert (parentView != 0);
  312. Rect clientRect;
  313. GetWindowBounds ((WindowRef) hostWindow, kWindowContentRgn, &clientRect);
  314. titleW = clientRect.right - clientRect.left;
  315. titleH = jmax (0, (clientRect.bottom - clientRect.top) - getHeight());
  316. setTopLeftPosition (0, 0);
  317. HIViewSetNeedsDisplay (parentView, true);
  318. setVisible (true);
  319. addToDesktop (ComponentPeer::windowRepaintedExplictly, (void*) parentView);
  320. HIViewRef pluginView = HIViewGetFirstSubview (parentView);
  321. #if ! JucePlugin_EditorRequiresKeyboardFocus
  322. HIViewSetActivated (pluginView, false);
  323. #endif
  324. /* This is a convoluted and desperate workaround for a Digi (or maybe Apple)
  325. layout bug. Until the parent control gets some kind of mouse-move
  326. event, then our plugin's HIView remains stuck at (0, 0) in the
  327. window (despite drawing correctly), which blocks mouse events from
  328. getting to the widgets above it.
  329. After days of frustration the only hack I can find that works
  330. is to use this arcane function to redirect mouse events to
  331. the parent, while running a timer to spot the moment when our
  332. view mysteriously snaps back to its correct location.
  333. If anyone at Digi or Apple is reading this and they realise that it's
  334. their fault, could they please give me back the week of my life that
  335. they made me waste on this rubbish. Thankyou.
  336. */
  337. SetControlSupervisor (pluginView, parentView);
  338. startTimer (150);
  339. #endif
  340. #if JUCE_WIN32 && ! JucePlugin_EditorRequiresKeyboardFocus
  341. Desktop::getInstance().addFocusChangeListener (this);
  342. #endif
  343. }
  344. ~EditorCompWrapper()
  345. {
  346. #if JUCE_WIN32 && ! JucePlugin_EditorRequiresKeyboardFocus
  347. Desktop::getInstance().removeFocusChangeListener (this);
  348. #endif
  349. #if JUCE_MAC
  350. delete forcedRepaintTimer;
  351. #endif
  352. }
  353. void paint (Graphics&)
  354. {
  355. #if JUCE_MAC
  356. if (forcedRepaintTimer != 0)
  357. forcedRepaintTimer->stopTimer();
  358. #endif
  359. }
  360. void resized()
  361. {
  362. juce::Component* const c = getChildComponent (0);
  363. if (c != 0)
  364. c->setBounds (0, 0, getWidth(), getHeight());
  365. repaint();
  366. }
  367. #if JUCE_MAC
  368. void timerCallback()
  369. {
  370. // Wait for the moment when PT deigns to allow our view to
  371. // take up its actual location (see rant above)
  372. HIViewRef content = 0;
  373. HIViewFindByID (HIViewGetRoot ((WindowRef) hostWindow), kHIViewWindowContentID, &content);
  374. HIPoint p = { 0.0f, 0.0f };
  375. HIViewRef v = HIViewGetFirstSubview (parentView);
  376. HIViewConvertPoint (&p, v, content);
  377. if (p.y > 12)
  378. {
  379. if (p.x != titleW || p.y != titleH)
  380. {
  381. GrafPtr oldport;
  382. GetPort (&oldport);
  383. SetPort (owner->GetViewPort());
  384. SetOrigin (-titleW, -titleH);
  385. SetPort (oldport);
  386. }
  387. HIViewRef v = HIViewGetFirstSubview (parentView);
  388. SetControlSupervisor (v, 0);
  389. stopTimer();
  390. forcedRepaintTimer = new RepaintCheckTimer (*this);
  391. }
  392. }
  393. #endif
  394. #if JUCE_WIN32
  395. void globalFocusChanged (juce::Component*)
  396. {
  397. #if ! JucePlugin_EditorRequiresKeyboardFocus
  398. if (hasKeyboardFocus (true))
  399. passFocusToHostWindow (hostWindow);
  400. #endif
  401. }
  402. #endif
  403. void childBoundsChanged (juce::Component* child)
  404. {
  405. setSize (child->getWidth(), child->getHeight());
  406. child->setTopLeftPosition (0, 0);
  407. #if JUCE_WIN32
  408. resizeHostWindow (hostWindow, titleW, titleH, this);
  409. owner->updateSize();
  410. #else
  411. Rect r;
  412. GetWindowBounds ((WindowRef) hostWindow, kWindowContentRgn, &r);
  413. HIRect p;
  414. zerostruct (p);
  415. HIViewConvertRect (&p, parentView, 0); // find the X position of our view in case there's space to the left of it
  416. r.right = r.left + jmax (titleW, ((int) p.origin.x) + getWidth());
  417. r.bottom = r.top + getHeight() + titleH;
  418. SetWindowBounds ((WindowRef) hostWindow, kWindowContentRgn, &r);
  419. owner->updateSize();
  420. owner->Invalidate();
  421. #endif
  422. }
  423. //==============================================================================
  424. juce_UseDebuggingNewOperator
  425. #if JUCE_MAC
  426. protected:
  427. void internalRepaint (int x, int y, int w, int h)
  428. {
  429. Component::internalRepaint (x, y, w, h);
  430. owner->Invalidate();
  431. if (forcedRepaintTimer != 0 && ! forcedRepaintTimer->isTimerRunning())
  432. forcedRepaintTimer->startTimer (1000 / 25);
  433. }
  434. HIViewRef parentView;
  435. #endif
  436. private:
  437. void* const hostWindow;
  438. JuceCustomUIView* const owner;
  439. int titleW, titleH;
  440. #if JUCE_MAC
  441. // if PT makes us wait too long for a redraw after we've
  442. // asked for one, this should kick in and force one to happen
  443. class RepaintCheckTimer : public Timer
  444. {
  445. public:
  446. RepaintCheckTimer (EditorCompWrapper& owner_)
  447. : owner (owner_)
  448. {
  449. }
  450. void timerCallback()
  451. {
  452. stopTimer();
  453. ComponentPeer* const peer = owner.getPeer();
  454. if (peer != 0)
  455. peer->performAnyPendingRepaintsNow();
  456. }
  457. EditorCompWrapper& owner;
  458. };
  459. RepaintCheckTimer* forcedRepaintTimer;
  460. #endif
  461. };
  462. };
  463. JuceCustomUIView* getView() const
  464. {
  465. return dynamic_cast <JuceCustomUIView*> (fOurPlugInView);
  466. }
  467. void GetViewRect (Rect* size)
  468. {
  469. if (getView() != 0)
  470. getView()->updateSize();
  471. CEffectProcessRTAS::GetViewRect (size);
  472. }
  473. CPlugInView* CreateCPlugInView()
  474. {
  475. return new JuceCustomUIView (juceFilter, this);
  476. }
  477. void SetViewPort (GrafPtr port)
  478. {
  479. CEffectProcessRTAS::SetViewPort (port);
  480. if (getView() != 0)
  481. getView()->attachToWindow (port);
  482. }
  483. //==============================================================================
  484. protected:
  485. ComponentResult GetDelaySamplesLong (long* aNumSamples)
  486. {
  487. if (aNumSamples != 0)
  488. *aNumSamples = juceFilter != 0 ? juceFilter->getLatencySamples() : 0;
  489. return noErr;
  490. }
  491. //==============================================================================
  492. void EffectInit()
  493. {
  494. SFicPlugInStemFormats stems;
  495. GetProcessType()->GetStemFormats (&stems);
  496. juceFilter->setPlayConfigDetails (fNumInputs, fNumOutputs,
  497. juceFilter->getSampleRate(), juceFilter->getBlockSize());
  498. AddControl (new CPluginControl_OnOff ('bypa', "Master Bypass\nMastrByp\nMByp\nByp", false, true));
  499. DefineMasterBypassControlIndex (bypassControlIndex);
  500. for (int i = 0; i < juceFilter->getNumParameters(); ++i)
  501. AddControl (new JucePluginControl (juceFilter, i));
  502. // we need to do this midi log-in to get timecode, regardless of whether
  503. // the plugin actually uses midi...
  504. if (MIDILogIn() == noErr)
  505. {
  506. #if JucePlugin_WantsMidiInput
  507. CEffectType* const type = dynamic_cast <CEffectType*> (this->GetProcessType());
  508. if (type != 0)
  509. {
  510. char nodeName [64];
  511. type->GetProcessTypeName (63, nodeName);
  512. p2cstrcpy (nodeName, reinterpret_cast <unsigned char*> (nodeName));
  513. midiBufferNode = new CEffectMIDIOtherBufferedNode (&mMIDIWorld,
  514. 8192,
  515. eLocalNode,
  516. nodeName,
  517. midiBuffer);
  518. midiBufferNode->Initialize (1, true);
  519. }
  520. #endif
  521. }
  522. midiTransport = new CEffectMIDITransport (&mMIDIWorld);
  523. juceFilter->setPlayHead (this);
  524. juceFilter->addListener (this);
  525. }
  526. void handleAsyncUpdate()
  527. {
  528. if (! prepared)
  529. {
  530. sampleRate = gProcessGroup->GetSampleRate();
  531. jassert (sampleRate > 0);
  532. juce_free (channels);
  533. channels = (float**) juce_calloc (sizeof (float*) * jmax (juceFilter->getNumInputChannels(),
  534. juceFilter->getNumOutputChannels()));
  535. juceFilter->setPlayConfigDetails (fNumInputs, fNumOutputs,
  536. sampleRate, mRTGlobals->mHWBufferSizeInSamples);
  537. juceFilter->prepareToPlay (sampleRate,
  538. mRTGlobals->mHWBufferSizeInSamples);
  539. prepared = true;
  540. }
  541. }
  542. void RenderAudio (float** inputs, float** outputs, long numSamples)
  543. {
  544. if (! prepared)
  545. {
  546. triggerAsyncUpdate();
  547. bypassBuffers (inputs, outputs, numSamples);
  548. return;
  549. }
  550. if (mBypassed)
  551. {
  552. bypassBuffers (inputs, outputs, numSamples);
  553. return;
  554. }
  555. #if JucePlugin_WantsMidiInput
  556. midiEvents.clear();
  557. const Cmn_UInt32 bufferSize = mRTGlobals->mHWBufferSizeInSamples;
  558. if (midiBufferNode->GetAdvanceScheduleTime() != bufferSize)
  559. midiBufferNode->SetAdvanceScheduleTime (bufferSize);
  560. if (midiBufferNode->FillMIDIBuffer (mRTGlobals->mRunningTime, numSamples) == noErr)
  561. {
  562. jassert (midiBufferNode->GetBufferPtr() != 0);
  563. const int numMidiEvents = midiBufferNode->GetBufferSize();
  564. for (int i = 0; i < numMidiEvents; ++i)
  565. {
  566. const DirectMidiPacket& m = midiBuffer[i];
  567. jassert ((int) m.mTimestamp < numSamples);
  568. midiEvents.addEvent (m.mData, m.mLength,
  569. jlimit (0, (int) numSamples - 1, (int) m.mTimestamp));
  570. }
  571. }
  572. #endif
  573. #if defined (JUCE_DEBUG) || JUCE_LOG_ASSERTIONS
  574. const int numMidiEventsComingIn = midiEvents.getNumEvents();
  575. #endif
  576. {
  577. const ScopedLock sl (juceFilter->getCallbackLock());
  578. const int numIn = juceFilter->getNumInputChannels();
  579. const int numOut = juceFilter->getNumOutputChannels();
  580. const int totalChans = jmax (numIn, numOut);
  581. if (juceFilter->isSuspended())
  582. {
  583. for (int i = 0; i < numOut; ++i)
  584. zeromem (outputs [i], sizeof (float) * numSamples);
  585. }
  586. else
  587. {
  588. {
  589. int i;
  590. for (i = 0; i < numOut; ++i)
  591. {
  592. channels[i] = outputs [i];
  593. if (i < numIn && inputs != outputs)
  594. memcpy (outputs [i], inputs[i], sizeof (float) * numSamples);
  595. }
  596. for (; i < numIn; ++i)
  597. channels [i] = inputs [i];
  598. }
  599. AudioSampleBuffer chans (channels, totalChans, numSamples);
  600. juceFilter->processBlock (chans, midiEvents);
  601. }
  602. }
  603. if (! midiEvents.isEmpty())
  604. {
  605. #if JucePlugin_ProducesMidiOutput
  606. const uint8* midiEventData;
  607. int midiEventSize, midiEventPosition;
  608. MidiBuffer::Iterator i (midiEvents);
  609. while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
  610. {
  611. // jassert (midiEventPosition >= 0 && midiEventPosition < (int) numSamples);
  612. //xxx
  613. }
  614. #else
  615. // if your plugin creates midi messages, you'll need to set
  616. // the JucePlugin_ProducesMidiOutput macro to 1 in your
  617. // JucePluginCharacteristics.h file
  618. jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
  619. #endif
  620. midiEvents.clear();
  621. }
  622. }
  623. //==============================================================================
  624. ComponentResult GetChunkSize (OSType chunkID, long* size)
  625. {
  626. if (chunkID == juceChunkType)
  627. {
  628. tempFilterData.setSize (0);
  629. juceFilter->getStateInformation (tempFilterData);
  630. *size = sizeof (SFicPlugInChunkHeader) + tempFilterData.getSize();
  631. return noErr;
  632. }
  633. return CEffectProcessMIDI::GetChunkSize (chunkID, size);
  634. }
  635. ComponentResult GetChunk (OSType chunkID, SFicPlugInChunk* chunk)
  636. {
  637. if (chunkID == juceChunkType)
  638. {
  639. if (tempFilterData.getSize() == 0)
  640. juceFilter->getStateInformation (tempFilterData);
  641. chunk->fSize = sizeof (SFicPlugInChunkHeader) + tempFilterData.getSize();
  642. tempFilterData.copyTo ((void*) chunk->fData, 0, tempFilterData.getSize());
  643. tempFilterData.setSize (0);
  644. return noErr;
  645. }
  646. return CEffectProcessMIDI::GetChunk (chunkID, chunk);
  647. }
  648. ComponentResult SetChunk (OSType chunkID, SFicPlugInChunk* chunk)
  649. {
  650. if (chunkID == juceChunkType)
  651. {
  652. tempFilterData.setSize (0);
  653. if (chunk->fSize - sizeof (SFicPlugInChunkHeader) > 0)
  654. {
  655. juceFilter->setStateInformation ((void*) chunk->fData,
  656. chunk->fSize - sizeof (SFicPlugInChunkHeader));
  657. }
  658. return noErr;
  659. }
  660. return CEffectProcessMIDI::SetChunk (chunkID, chunk);
  661. }
  662. //==============================================================================
  663. ComponentResult UpdateControlValue (long controlIndex, long value)
  664. {
  665. if (controlIndex != bypassControlIndex)
  666. juceFilter->setParameter (controlIndex - 2, longToFloat (value));
  667. else
  668. mBypassed = (value > 0);
  669. return CProcess::UpdateControlValue (controlIndex, value);
  670. }
  671. //==============================================================================
  672. bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info)
  673. {
  674. // this method can only be called while the plugin is running
  675. jassert (prepared);
  676. Cmn_Float64 bpm = 120.0;
  677. Cmn_Int32 num = 4, denom = 4;
  678. Cmn_Int64 ticks = 0;
  679. Cmn_Bool isPlaying = false;
  680. if (midiTransport != 0)
  681. {
  682. midiTransport->GetCurrentTempo (&bpm);
  683. midiTransport->IsTransportPlaying (&isPlaying);
  684. midiTransport->GetCurrentMeter (&num, &denom);
  685. midiTransport->GetCurrentTickPosition (&ticks);
  686. }
  687. info.bpm = bpm;
  688. info.timeSigNumerator = num;
  689. info.timeSigDenominator = denom;
  690. info.isPlaying = isPlaying;
  691. info.isRecording = false;
  692. info.ppqPosition = ticks / 960000.0;
  693. info.ppqPositionOfLastBarStart = 0; //xxx no idea how to get this correctly..
  694. // xxx incorrect if there are tempo changes, but there's no
  695. // other way of getting this info..
  696. info.timeInSeconds = ticks * (60.0 / 960000.0) / bpm;
  697. double framesPerSec = 24.0;
  698. switch (fTimeCodeInfo.mFrameRate)
  699. {
  700. case ficFrameRate_24Frame:
  701. info.frameRate = AudioPlayHead::fps24;
  702. break;
  703. case ficFrameRate_25Frame:
  704. info.frameRate = AudioPlayHead::fps25;
  705. framesPerSec = 25.0;
  706. break;
  707. case ficFrameRate_2997NonDrop:
  708. info.frameRate = AudioPlayHead::fps2997;
  709. framesPerSec = 29.97002997;
  710. break;
  711. case ficFrameRate_2997DropFrame:
  712. info.frameRate = AudioPlayHead::fps2997drop;
  713. framesPerSec = 29.97002997;
  714. break;
  715. case ficFrameRate_30NonDrop:
  716. info.frameRate = AudioPlayHead::fps30;
  717. framesPerSec = 30.0;
  718. break;
  719. case ficFrameRate_30DropFrame:
  720. info.frameRate = AudioPlayHead::fps30drop;
  721. framesPerSec = 30.0;
  722. break;
  723. case ficFrameRate_23976:
  724. // xxx not strictly true..
  725. info.frameRate = AudioPlayHead::fps24;
  726. framesPerSec = 23.976;
  727. break;
  728. default:
  729. info.frameRate = AudioPlayHead::fpsUnknown;
  730. break;
  731. }
  732. info.editOriginTime = fTimeCodeInfo.mFrameOffset / framesPerSec;
  733. return true;
  734. }
  735. void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
  736. {
  737. SetControlValue (index + 2, floatToLong (newValue));
  738. }
  739. void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index)
  740. {
  741. TouchControl (index + 2);
  742. }
  743. void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index)
  744. {
  745. ReleaseControl (index + 2);
  746. }
  747. void audioProcessorChanged (AudioProcessor*)
  748. {
  749. // xxx is there an RTAS equivalent?
  750. }
  751. //==============================================================================
  752. private:
  753. AudioProcessor* juceFilter;
  754. MidiBuffer midiEvents;
  755. CEffectMIDIOtherBufferedNode* midiBufferNode;
  756. CEffectMIDITransport* midiTransport;
  757. DirectMidiPacket midiBuffer [midiBufferSize];
  758. JUCE_NAMESPACE::MemoryBlock tempFilterData;
  759. float** channels;
  760. bool prepared;
  761. double sampleRate;
  762. void bypassBuffers (float** const inputs, float** const outputs, const long numSamples) const
  763. {
  764. for (int i = fNumOutputs; --i >= 0;)
  765. {
  766. if (i < fNumInputs)
  767. memcpy (outputs[i], inputs[i], numSamples * sizeof (float));
  768. else
  769. zeromem (outputs[i], numSamples * sizeof (float));
  770. }
  771. }
  772. //==============================================================================
  773. class JucePluginControl : public CPluginControl
  774. {
  775. public:
  776. //==============================================================================
  777. JucePluginControl (AudioProcessor* const juceFilter_, const int index_)
  778. : juceFilter (juceFilter_),
  779. index (index_)
  780. {
  781. }
  782. ~JucePluginControl()
  783. {
  784. }
  785. //==============================================================================
  786. OSType GetID() const { return index + 1; }
  787. long GetDefaultValue() const { return floatToLong (0); }
  788. void SetDefaultValue (long) {}
  789. long GetNumSteps() const { return 0xffffffff; }
  790. long ConvertStringToValue (const char* valueString) const
  791. {
  792. return floatToLong (String (valueString).getFloatValue());
  793. }
  794. Cmn_Bool IsKeyValid (long key) const { return true; }
  795. void GetNameOfLength (char* name, int maxLength, OSType inControllerType) const
  796. {
  797. juceFilter->getParameterName (index).copyToBuffer (name, maxLength);
  798. }
  799. long GetPriority() const { return kFicCooperativeTaskPriority; }
  800. long GetOrientation() const
  801. {
  802. return kDAE_LeftMinRightMax | kDAE_BottomMinTopMax
  803. | kDAE_RotarySingleDotMode | kDAE_RotaryLeftMinRightMax;
  804. }
  805. long GetControlType() const { return kDAE_ContinuousValues; }
  806. void GetValueString (char* valueString, int maxLength, long value) const
  807. {
  808. juceFilter->getParameterText (index).copyToBuffer (valueString, maxLength);
  809. }
  810. Cmn_Bool IsAutomatable() const
  811. {
  812. return juceFilter->isParameterAutomatable (index);
  813. }
  814. private:
  815. //==============================================================================
  816. AudioProcessor* const juceFilter;
  817. const int index;
  818. JucePluginControl (const JucePluginControl&);
  819. const JucePluginControl& operator= (const JucePluginControl&);
  820. };
  821. };
  822. //==============================================================================
  823. class JucePlugInGroup : public CEffectGroupMIDI
  824. {
  825. public:
  826. //==============================================================================
  827. JucePlugInGroup()
  828. {
  829. DefineManufacturerNamesAndID (JucePlugin_Manufacturer, JucePlugin_RTASManufacturerCode);
  830. DefinePlugInNamesAndVersion (createRTASName(), JucePlugin_VersionCode);
  831. #ifndef JUCE_DEBUG
  832. AddGestalt (pluginGestalt_IsCacheable);
  833. #endif
  834. }
  835. ~JucePlugInGroup()
  836. {
  837. shutdownJuce_GUI();
  838. shutdownJuce_NonGUI();
  839. }
  840. //==============================================================================
  841. void CreateEffectTypes()
  842. {
  843. const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
  844. const int numConfigs = numElementsInArray (channelConfigs);
  845. // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
  846. // value in your JucePluginCharacteristics.h file..
  847. jassert (numConfigs > 0);
  848. for (int i = 0; i < numConfigs; ++i)
  849. {
  850. CEffectType* const type
  851. = new CEffectTypeRTAS ('jcaa' + i,
  852. JucePlugin_RTASProductId,
  853. JucePlugin_RTASCategory);
  854. type->DefineTypeNames (createRTASName());
  855. type->DefineSampleRateSupport (eSupports48kAnd96kAnd192k);
  856. type->DefineStemFormats (getFormatForChans (channelConfigs [i][0]),
  857. getFormatForChans (channelConfigs [i][1]));
  858. type->AddGestalt (pluginGestalt_CanBypass);
  859. type->AddGestalt (pluginGestalt_SupportsVariableQuanta);
  860. type->AttachEffectProcessCreator (createNewProcess);
  861. AddEffectType (type);
  862. }
  863. }
  864. void Initialize()
  865. {
  866. CEffectGroupMIDI::Initialize();
  867. }
  868. //==============================================================================
  869. private:
  870. static CEffectProcess* createNewProcess()
  871. {
  872. // Juce setup
  873. #if JUCE_WIN32
  874. PlatformUtilities::setCurrentModuleInstanceHandle (gThisModule);
  875. #endif
  876. initialiseJuce_GUI();
  877. return new JucePlugInProcess();
  878. }
  879. static const String createRTASName()
  880. {
  881. return String (JucePlugin_Name) + T("\n")
  882. + String (JucePlugin_Name).substring (0, 4);
  883. }
  884. static EPlugIn_StemFormat getFormatForChans (const int numChans) throw()
  885. {
  886. switch (numChans)
  887. {
  888. case 0:
  889. return ePlugIn_StemFormat_Generic;
  890. case 1:
  891. return ePlugIn_StemFormat_Mono;
  892. case 2:
  893. return ePlugIn_StemFormat_Stereo;
  894. case 3:
  895. return ePlugIn_StemFormat_LCR;
  896. case 4:
  897. return ePlugIn_StemFormat_Quad;
  898. case 5:
  899. return ePlugIn_StemFormat_5dot0;
  900. case 6:
  901. return ePlugIn_StemFormat_5dot1;
  902. case 7:
  903. return ePlugIn_StemFormat_6dot1;
  904. case 8:
  905. return ePlugIn_StemFormat_7dot1;
  906. default:
  907. jassertfalse // hmm - not a valid number of chans for RTAS..
  908. break;
  909. }
  910. return ePlugIn_StemFormat_Generic;
  911. }
  912. };
  913. CProcessGroupInterface* CProcessGroup::CreateProcessGroup()
  914. {
  915. initialiseJuce_NonGUI();
  916. return new JucePlugInGroup();
  917. }