1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 #include <juce_core/system/juce_TargetPlatform.h>
27 #include "../utility/juce_CheckSettingMacros.h"
28 
29 #if JucePlugin_Build_RTAS
30 
31 #ifdef _MSC_VER
32  // (this is a workaround for a build problem in VC9)
33  #define _DO_NOT_DECLARE_INTERLOCKED_INTRINSICS_IN_MEMORY
34  #include <intrin.h>
35 
36  #ifndef JucePlugin_WinBag_path
37   #error "You need to define the JucePlugin_WinBag_path value!"
38  #endif
39 #endif
40 
41 #include "juce_RTAS_DigiCode_Header.h"
42 
43 #ifdef _MSC_VER
44  #include <Mac2Win.H>
45 #endif
46 
47 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Widiomatic-parentheses",
48                                      "-Wnon-virtual-dtor",
49                                      "-Wcomment")
50 
51 /* Note about include paths
52    ------------------------
53 
54    To be able to include all the Digidesign headers correctly, you'll need to add this
55    lot to your include path:
56 
57     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\EffectClasses
58     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses
59     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses\Interfaces
60     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Utilities
61     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\RTASP_Adapt
62     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\CoreClasses
63     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Controls
64     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Meters
65     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ViewClasses
66     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\DSPClasses
67     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Interfaces
68     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\common
69     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\common\Platform
70     c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\SignalProcessing\Public
71     C:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugIns\DSPManager\Interfaces
72     c:\yourdirectory\PT_80_SDK\AlturaPorts\SADriver\Interfaces
73     c:\yourdirectory\PT_80_SDK\AlturaPorts\DigiPublic\Interfaces
74     c:\yourdirectory\PT_80_SDK\AlturaPorts\Fic\Interfaces\DAEClient
75     c:\yourdirectory\PT_80_SDK\AlturaPorts\NewFileLibs\Cmn
76     c:\yourdirectory\PT_80_SDK\AlturaPorts\NewFileLibs\DOA
77     c:\yourdirectory\PT_80_SDK\AlturaPorts\AlturaSource\PPC_H
78     c:\yourdirectory\PT_80_SDK\AlturaPorts\AlturaSource\AppSupport
79     c:\yourdirectory\PT_80_SDK\AlturaPorts\DigiPublic
80     c:\yourdirectory\PT_80_SDK\AvidCode\AVX2sdk\AVX\avx2\avx2sdk\inc
81     c:\yourdirectory\PT_80_SDK\xplat\AVX\avx2\avx2sdk\inc
82 
83    NB. If you hit a huge pile of bugs around here, make sure that you've not got the
84    Apple QuickTime headers before the PT headers in your path, because there are
85    some filename clashes between them.
86 
87 */
88 #include <CEffectGroupMIDI.h>
89 #include <CEffectProcessMIDI.h>
90 #include <CEffectProcessRTAS.h>
91 #include <CCustomView.h>
92 #include <CEffectTypeRTAS.h>
93 #include <CPluginControl.h>
94 #include <CPluginControl_OnOff.h>
95 #include <FicProcessTokens.h>
96 #include <ExternalVersionDefines.h>
97 
98 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
99 
100 //==============================================================================
101 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4263 4264 4250)
102 
103 #include "../utility/juce_IncludeModuleHeaders.h"
104 
105 #ifdef _MSC_VER
106  #pragma pack (pop)
107 
108  // This JUCE_RTAS_LINK_TO_DEBUG_LIB setting can be used to force linkage
109  // against only the release build of the RTAS lib, since in older SDKs there
110  // can be problems with the debug build.
111  #if JUCE_DEBUG && ! defined (JUCE_RTAS_LINK_TO_DEBUG_LIB)
112   #define JUCE_RTAS_LINK_TO_DEBUG_LIB 1
113  #endif
114 
115  #if JUCE_RTAS_LINK_TO_DEBUG_LIB
116   #define PT_LIB_PATH  JucePlugin_WinBag_path "\\Debug\\lib\\"
117  #else
118   #define PT_LIB_PATH  JucePlugin_WinBag_path "\\Release\\lib\\"
119  #endif
120 
121  #pragma comment(lib, PT_LIB_PATH "DAE.lib")
122  #pragma comment(lib, PT_LIB_PATH "DigiExt.lib")
123  #pragma comment(lib, PT_LIB_PATH "DSI.lib")
124  #pragma comment(lib, PT_LIB_PATH "PluginLib.lib")
125  #pragma comment(lib, PT_LIB_PATH "DSPManager.lib")
126  #pragma comment(lib, PT_LIB_PATH "DSPManagerClientLib.lib")
127  #pragma comment(lib, PT_LIB_PATH "RTASClientLib.lib")
128 #endif
129 
130 #undef MemoryBlock
131 
132 //==============================================================================
133 #if JUCE_WINDOWS
134   extern void JUCE_CALLTYPE attachSubWindow (void* hostWindow, int& titleW, int& titleH, Component* comp);
135   extern void JUCE_CALLTYPE resizeHostWindow (void* hostWindow, int& titleW, int& titleH, Component* comp);
136  #if ! JucePlugin_EditorRequiresKeyboardFocus
137   extern void JUCE_CALLTYPE passFocusToHostWindow (void* hostWindow);
138  #endif
139 #else
140   extern void* attachSubWindow (void* hostWindowRef, Component* comp);
141   extern void removeSubWindow (void* nsWindow, Component* comp);
142   extern void forwardCurrentKeyEventToHostWindow();
143 #endif
144 
145 #if ! (JUCE_DEBUG || defined (JUCE_RTAS_PLUGINGESTALT_IS_CACHEABLE))
146  #define JUCE_RTAS_PLUGINGESTALT_IS_CACHEABLE 1
147 #endif
148 
149 const int midiBufferSize = 1024;
150 const OSType juceChunkType = 'juce';
151 static const int bypassControlIndex = 1;
152 
153 static int numInstances = 0;
154 
155 using namespace juce;
156 
157 //==============================================================================
158 class JucePlugInProcess  : public CEffectProcessMIDI,
159                            public CEffectProcessRTAS,
160                            public AudioProcessorListener,
161                            public AudioPlayHead
162 {
163 public:
164     //==============================================================================
165     // RTAS builds will be removed from JUCE in the next release
JucePlugInProcess()166     JUCE_DEPRECATED_WITH_BODY (JucePlugInProcess(),
167     {
168         juceFilter.reset (createPluginFilterOfType (AudioProcessor::wrapperType_RTAS));
169 
170         AddChunk (juceChunkType, "Juce Audio Plugin Data");
171 
172         ++numInstances;
173     })
174 
175     ~JucePlugInProcess()
176     {
177         JUCE_AUTORELEASEPOOL
178         {
179             if (mLoggedIn)
180                 MIDILogOut();
181 
182             midiBufferNode.reset();
183             midiTransport.reset();
184 
185             if (juceFilter != nullptr)
186             {
187                 juceFilter->releaseResources();
188                 juceFilter.reset();
189             }
190 
191             if (--numInstances == 0)
192             {
193                #if JUCE_MAC
194                 // Hack to allow any NSWindows to clear themselves up before returning to PT..
195                 for (int i = 20; --i >= 0;)
196                     MessageManager::getInstance()->runDispatchLoopUntil (1);
197                #endif
198 
199                 shutdownJuce_GUI();
200             }
201         }
202     }
203 
204     //==============================================================================
205     class JuceCustomUIView  : public CCustomView,
206                               public Timer
207     {
208     public:
209         //==============================================================================
JuceCustomUIView(AudioProcessor * ap,JucePlugInProcess * p)210         JuceCustomUIView (AudioProcessor* ap, JucePlugInProcess* p)
211             : filter (ap), process (p)
212         {
213             // setting the size in here crashes PT for some reason, so keep it simple..
214         }
215 
~JuceCustomUIView()216         ~JuceCustomUIView()
217         {
218             deleteEditorComp();
219         }
220 
221         //==============================================================================
updateSize()222         void updateSize()
223         {
224             if (editorComp == nullptr)
225             {
226                 editorComp.reset (filter->createEditorIfNeeded());
227                 jassert (editorComp != nullptr);
228             }
229 
230             if (editorComp->getWidth() != 0 && editorComp->getHeight() != 0)
231             {
232                 Rect oldRect;
233                 GetRect (&oldRect);
234 
235                 Rect r;
236                 r.left = 0;
237                 r.top = 0;
238                 r.right = editorComp->getWidth();
239                 r.bottom = editorComp->getHeight();
240                 SetRect (&r);
241 
242                 if (oldRect.right != r.right || oldRect.bottom != r.bottom)
243                     startTimer (50);
244             }
245         }
246 
timerCallback()247         void timerCallback() override
248         {
249             if (! Component::isMouseButtonDownAnywhere())
250             {
251                 stopTimer();
252 
253                 // Send a token to the host to tell it about the resize
254                 SSetProcessWindowResizeToken token (process->fRootNameId, process->fRootNameId);
255                 FicSDSDispatchToken (&token);
256             }
257         }
258 
attachToWindow(GrafPtr port)259         void attachToWindow (GrafPtr port)
260         {
261             if (port != 0)
262             {
263                 JUCE_AUTORELEASEPOOL
264                 {
265                     updateSize();
266 
267                    #if JUCE_WINDOWS
268                     auto hostWindow = (void*) ASI_GethWnd ((WindowPtr) port);
269                    #else
270                     auto hostWindow = (void*) GetWindowFromPort (port);
271                    #endif
272                     wrapper.reset();
273                     wrapper.reset (new EditorCompWrapper (hostWindow, editorComp.get(), this));
274                 }
275             }
276             else
277             {
278                 deleteEditorComp();
279             }
280         }
281 
DrawContents(Rect *)282         void DrawContents (Rect*) override
283         {
284            #if JUCE_WINDOWS
285             if (wrapper != nullptr)
286                 if (auto peer = wrapper->getPeer())
287                     peer->repaint (wrapper->getLocalBounds());  // (seems to be required in PT6.4, but not in 7.x)
288            #endif
289         }
290 
DrawBackground(Rect *)291         void DrawBackground (Rect*) override  {}
292 
293         //==============================================================================
294     private:
295         AudioProcessor* const filter;
296         JucePlugInProcess* const process;
297         std::unique_ptr<Component> wrapper;
298         std::unique_ptr<AudioProcessorEditor> editorComp;
299 
deleteEditorComp()300         void deleteEditorComp()
301         {
302             if (editorComp != nullptr || wrapper != nullptr)
303             {
304                 JUCE_AUTORELEASEPOOL
305                 {
306                     PopupMenu::dismissAllActiveMenus();
307 
308                     if (Component* const modalComponent = Component::getCurrentlyModalComponent())
309                         modalComponent->exitModalState (0);
310 
311                     filter->editorBeingDeleted (editorComp.get());
312 
313                     editorComp.reset();
314                     wrapper.reset();
315                 }
316             }
317         }
318 
319         //==============================================================================
320         // A component to hold the AudioProcessorEditor, and cope with some housekeeping
321         // chores when it changes or repaints.
322         class EditorCompWrapper  : public Component
323                                  #if ! JUCE_MAC
324                                    , public FocusChangeListener
325                                  #endif
326         {
327         public:
EditorCompWrapper(void * hostWindow_,Component * editorComp,JuceCustomUIView * owner_)328             EditorCompWrapper (void* hostWindow_,
329                                Component* editorComp,
330                                JuceCustomUIView* owner_)
331                 : hostWindow (hostWindow_),
332                   owner (owner_),
333                   titleW (0),
334                   titleH (0)
335             {
336                #if ! JucePlugin_EditorRequiresKeyboardFocus
337                 setMouseClickGrabsKeyboardFocus (false);
338                 setWantsKeyboardFocus (false);
339                #endif
340                 setOpaque (true);
341                 setBroughtToFrontOnMouseClick (true);
342                 setBounds (editorComp->getBounds());
343                 editorComp->setTopLeftPosition (0, 0);
344                 addAndMakeVisible (*editorComp);
345 
346                #if JUCE_WINDOWS
347                 attachSubWindow (hostWindow, titleW, titleH, this);
348                #else
349                 nsWindow = attachSubWindow (hostWindow, this);
350                #endif
351                 setVisible (true);
352 
353                #if JUCE_WINDOWS && ! JucePlugin_EditorRequiresKeyboardFocus
354                 Desktop::getInstance().addFocusChangeListener (this);
355                #endif
356             }
357 
~EditorCompWrapper()358             ~EditorCompWrapper()
359             {
360                 removeChildComponent (getEditor());
361 
362                #if JUCE_WINDOWS && ! JucePlugin_EditorRequiresKeyboardFocus
363                 Desktop::getInstance().removeFocusChangeListener (this);
364                #endif
365 
366                #if JUCE_MAC
367                 removeSubWindow (nsWindow, this);
368                #endif
369             }
370 
paint(Graphics &)371             void paint (Graphics&) override {}
372 
resized()373             void resized() override
374             {
375                 if (Component* const ed = getEditor())
376                     ed->setBounds (getLocalBounds());
377 
378                 repaint();
379             }
380 
381            #if JUCE_WINDOWS
globalFocusChanged(Component *)382             void globalFocusChanged (Component*) override
383             {
384                #if ! JucePlugin_EditorRequiresKeyboardFocus
385                 if (hasKeyboardFocus (true))
386                     passFocusToHostWindow (hostWindow);
387                #endif
388             }
389            #endif
390 
childBoundsChanged(Component * child)391             void childBoundsChanged (Component* child) override
392             {
393                 setSize (child->getWidth(), child->getHeight());
394                 child->setTopLeftPosition (0, 0);
395 
396                #if JUCE_WINDOWS
397                 resizeHostWindow (hostWindow, titleW, titleH, this);
398                #endif
399                 owner->updateSize();
400             }
401 
userTriedToCloseWindow()402             void userTriedToCloseWindow() override {}
403 
404            #if JUCE_MAC && JucePlugin_EditorRequiresKeyboardFocus
keyPressed(const KeyPress & kp)405             bool keyPressed (const KeyPress& kp) override
406             {
407                 owner->updateSize();
408                 forwardCurrentKeyEventToHostWindow();
409                 return true;
410             }
411            #endif
412 
413         private:
414             //==============================================================================
415             void* const hostWindow;
416             void* nsWindow;
417             JuceCustomUIView* const owner;
418             int titleW, titleH;
419 
getEditor() const420             Component* getEditor() const        { return getChildComponent (0); }
421 
422             JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper)
423         };
424     };
425 
getView() const426     JuceCustomUIView* getView() const
427     {
428         return dynamic_cast<JuceCustomUIView*> (fOurPlugInView);
429     }
430 
GetViewRect(Rect * size)431     void GetViewRect (Rect* size) override
432     {
433         if (JuceCustomUIView* const v = getView())
434             v->updateSize();
435 
436         CEffectProcessRTAS::GetViewRect (size);
437     }
438 
CreateCPlugInView()439     CPlugInView* CreateCPlugInView() override
440     {
441         return new JuceCustomUIView (juceFilter.get(), this);
442     }
443 
SetViewPort(GrafPtr port)444     void SetViewPort (GrafPtr port) override
445     {
446         CEffectProcessRTAS::SetViewPort (port);
447 
448         if (JuceCustomUIView* const v = getView())
449             v->attachToWindow (port);
450     }
451 
452     //==============================================================================
GetDelaySamplesLong(long * aNumSamples)453     ComponentResult GetDelaySamplesLong (long* aNumSamples) override
454     {
455         if (aNumSamples != nullptr)
456             *aNumSamples = juceFilter != nullptr ? juceFilter->getLatencySamples() : 0;
457 
458         return noErr;
459     }
460 
461     //==============================================================================
EffectInit()462     void EffectInit() override
463     {
464         sampleRate = (double) GetSampleRate();
465         jassert (sampleRate > 0);
466         const int maxBlockSize = (int) CEffectProcessRTAS::GetMaximumRTASQuantum();
467         jassert (maxBlockSize > 0);
468 
469         SFicPlugInStemFormats stems;
470         GetProcessType()->GetStemFormats (&stems);
471 
472         juceFilter->setPlayConfigDetails (fNumInputs, fNumOutputs, sampleRate, maxBlockSize);
473 
474         AddControl (new CPluginControl_OnOff ('bypa', "Master Bypass\nMastrByp\nMByp\nByp", false, true));
475         DefineMasterBypassControlIndex (bypassControlIndex);
476 
477         const int numParameters = juceFilter->getNumParameters();
478 
479        #if JUCE_FORCE_USE_LEGACY_PARAM_IDS
480         const bool usingManagedParameters = false;
481        #else
482         const bool usingManagedParameters = (juceFilter->getParameters().size() == numParameters);
483        #endif
484 
485         for (int i = 0; i < numParameters; ++i)
486         {
487             OSType rtasParamID = static_cast<OSType> (usingManagedParameters ? juceFilter->getParameterID (i).hashCode() : i);
488             AddControl (new JucePluginControl (*juceFilter, i, rtasParamID));
489         }
490 
491         // we need to do this midi log-in to get timecode, regardless of whether
492         // the plugin actually uses midi...
493         if (MIDILogIn() == noErr)
494         {
495            #if JucePlugin_WantsMidiInput
496             if (CEffectType* const type = dynamic_cast<CEffectType*> (this->GetProcessType()))
497             {
498                 char nodeName[80] = { 0 };
499                 type->GetProcessTypeName (63, nodeName);
500                 nodeName[nodeName[0] + 1] = 0;
501 
502                 midiBufferNode.reset (new CEffectMIDIOtherBufferedNode (&mMIDIWorld,
503                                                                         8192,
504                                                                         eLocalNode,
505                                                                         nodeName + 1,
506                                                                         midiBuffer));
507 
508                 midiBufferNode->Initialize (0xffff, true);
509             }
510            #endif
511         }
512 
513         midiTransport.reset (new CEffectMIDITransport (&mMIDIWorld));
514         midiEvents.ensureSize (2048);
515 
516         channels.calloc (jmax (juceFilter->getTotalNumInputChannels(),
517                                juceFilter->getTotalNumOutputChannels()));
518 
519         juceFilter->setPlayHead (this);
520         juceFilter->addListener (this);
521 
522         juceFilter->prepareToPlay (sampleRate, maxBlockSize);
523     }
524 
RenderAudio(float ** inputs,float ** outputs,long numSamples)525     void RenderAudio (float** inputs, float** outputs, long numSamples) override
526     {
527        #if JucePlugin_WantsMidiInput
528         midiEvents.clear();
529 
530         const Cmn_UInt32 bufferSize = mRTGlobals->mHWBufferSizeInSamples;
531 
532         if (midiBufferNode != nullptr)
533         {
534             if (midiBufferNode->GetAdvanceScheduleTime() != bufferSize)
535                 midiBufferNode->SetAdvanceScheduleTime (bufferSize);
536 
537             if (midiBufferNode->FillMIDIBuffer (mRTGlobals->mRunningTime, numSamples) == noErr)
538             {
539                 jassert (midiBufferNode->GetBufferPtr() != nullptr);
540                 const int numMidiEvents = midiBufferNode->GetBufferSize();
541 
542                 for (int i = 0; i < numMidiEvents; ++i)
543                 {
544                     const DirectMidiPacket& m = midiBuffer[i];
545 
546                     jassert ((int) m.mTimestamp < numSamples);
547 
548                     midiEvents.addEvent (m.mData, m.mLength,
549                                          jlimit (0, (int) numSamples - 1, (int) m.mTimestamp));
550                 }
551             }
552         }
553        #endif
554 
555        #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
556         const int numMidiEventsComingIn = midiEvents.getNumEvents();
557         ignoreUnused (numMidiEventsComingIn);
558        #endif
559 
560         {
561             const ScopedLock sl (juceFilter->getCallbackLock());
562 
563             const int numIn  = juceFilter->getTotalNumInputChannels();
564             const int numOut = juceFilter->getTotalNumOutputChannels();
565             const int totalChans = jmax (numIn, numOut);
566 
567             if (juceFilter->isSuspended())
568             {
569                 for (int i = 0; i < numOut; ++i)
570                     FloatVectorOperations::clear (outputs [i], numSamples);
571             }
572             else
573             {
574                 {
575                     int i;
576                     for (i = 0; i < numOut; ++i)
577                     {
578                         channels[i] = outputs [i];
579 
580                         if (i < numIn && inputs != outputs)
581                             FloatVectorOperations::copy (outputs [i], inputs[i], numSamples);
582                     }
583 
584                     for (; i < numIn; ++i)
585                         channels [i] = inputs [i];
586                 }
587 
588                 AudioBuffer<float> chans (channels, totalChans, numSamples);
589 
590                 if (mBypassed)
591                     juceFilter->processBlockBypassed (chans, midiEvents);
592                 else
593                     juceFilter->processBlock (chans, midiEvents);
594             }
595         }
596 
597         if (! midiEvents.isEmpty())
598         {
599            #if JucePlugin_ProducesMidiOutput
600             for (const auto metadata : midiEvents)
601             {
602                 //jassert (metadata.samplePosition >= 0 && metadata.samplePosition < (int) numSamples);
603             }
604            #elif JUCE_DEBUG || JUCE_LOG_ASSERTIONS
605             // if your plugin creates midi messages, you'll need to set
606             // the JucePlugin_ProducesMidiOutput macro to 1 in your
607             // JucePluginCharacteristics.h file
608             jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
609            #endif
610 
611             midiEvents.clear();
612         }
613     }
614 
615     //==============================================================================
GetChunkSize(OSType chunkID,long * size)616     ComponentResult GetChunkSize (OSType chunkID, long* size) override
617     {
618         if (chunkID == juceChunkType)
619         {
620             tempFilterData.reset();
621             juceFilter->getStateInformation (tempFilterData);
622 
623             *size = sizeof (SFicPlugInChunkHeader) + tempFilterData.getSize();
624             return noErr;
625         }
626 
627         return CEffectProcessMIDI::GetChunkSize (chunkID, size);
628     }
629 
GetChunk(OSType chunkID,SFicPlugInChunk * chunk)630     ComponentResult GetChunk (OSType chunkID, SFicPlugInChunk* chunk) override
631     {
632         if (chunkID == juceChunkType)
633         {
634             if (tempFilterData.getSize() == 0)
635                 juceFilter->getStateInformation (tempFilterData);
636 
637             chunk->fSize = sizeof (SFicPlugInChunkHeader) + tempFilterData.getSize();
638             tempFilterData.copyTo ((void*) chunk->fData, 0, tempFilterData.getSize());
639 
640             tempFilterData.reset();
641 
642             return noErr;
643         }
644 
645         return CEffectProcessMIDI::GetChunk (chunkID, chunk);
646     }
647 
SetChunk(OSType chunkID,SFicPlugInChunk * chunk)648     ComponentResult SetChunk (OSType chunkID, SFicPlugInChunk* chunk) override
649     {
650         if (chunkID == juceChunkType)
651         {
652             tempFilterData.reset();
653 
654             if (chunk->fSize - sizeof (SFicPlugInChunkHeader) > 0)
655             {
656                 juceFilter->setStateInformation ((void*) chunk->fData,
657                                                  chunk->fSize - sizeof (SFicPlugInChunkHeader));
658             }
659 
660             return noErr;
661         }
662 
663         return CEffectProcessMIDI::SetChunk (chunkID, chunk);
664     }
665 
666     //==============================================================================
UpdateControlValue(long controlIndex,long value)667     ComponentResult UpdateControlValue (long controlIndex, long value) override
668     {
669         if (controlIndex != bypassControlIndex)
670         {
671             auto paramIndex = controlIndex - 2;
672             auto floatValue = longToFloat (value);
673 
674             if (auto* param = juceFilter->getParameters()[paramIndex])
675             {
676                 param->setValue (floatValue);
677                 param->sendValueChangedMessageToListeners (floatValue);
678             }
679             else
680             {
681                 juceFilter->setParameter (paramIndex, floatValue);
682             }
683         }
684         else
685         {
686             mBypassed = (value > 0);
687         }
688 
689         return CProcess::UpdateControlValue (controlIndex, value);
690     }
691 
692    #if JUCE_WINDOWS
HandleKeystroke(EventRecord * e)693     Boolean HandleKeystroke (EventRecord* e) override
694     {
695         if (Component* modalComp = Component::getCurrentlyModalComponent())
696         {
697             if (Component* focused = modalComp->getCurrentlyFocusedComponent())
698             {
699                 switch (e->message & charCodeMask)
700                 {
701                     case kReturnCharCode:
702                     case kEnterCharCode:    focused->keyPressed (KeyPress (KeyPress::returnKey)); break;
703                     case kEscapeCharCode:   focused->keyPressed (KeyPress (KeyPress::escapeKey)); break;
704                     default: break;
705                 }
706 
707                 return true;
708             }
709         }
710 
711         return false;
712     }
713    #endif
714 
715     //==============================================================================
getCurrentPosition(AudioPlayHead::CurrentPositionInfo & info)716     bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) override
717     {
718         Cmn_Float64 bpm = 120.0;
719         Cmn_Int32 num = 4, denom = 4;
720         Cmn_Int64 ticks = 0;
721         Cmn_Bool isPlaying = false;
722 
723         if (midiTransport != nullptr)
724         {
725             midiTransport->GetCurrentTempo (&bpm);
726             midiTransport->IsTransportPlaying (&isPlaying);
727             midiTransport->GetCurrentMeter (&num, &denom);
728 
729             // (The following is a work-around because GetCurrentTickPosition() doesn't work correctly).
730             Cmn_Int64 sampleLocation;
731 
732             if (isPlaying)
733                 midiTransport->GetCurrentRTASSampleLocation (&sampleLocation);
734             else
735                 midiTransport->GetCurrentTDMSampleLocation (&sampleLocation);
736 
737             midiTransport->GetCustomTickPosition (&ticks, sampleLocation);
738 
739             info.timeInSamples = (int64) sampleLocation;
740             info.timeInSeconds = sampleLocation / sampleRate;
741         }
742         else
743         {
744             info.timeInSamples = 0;
745             info.timeInSeconds = 0;
746         }
747 
748         info.bpm = bpm;
749         info.timeSigNumerator = num;
750         info.timeSigDenominator = denom;
751         info.isPlaying = isPlaying;
752         info.isRecording = false;
753         info.ppqPosition = ticks / 960000.0;
754         info.ppqPositionOfLastBarStart = 0; //xxx no idea how to get this correctly..
755         info.isLooping = false;
756         info.ppqLoopStart = 0;
757         info.ppqLoopEnd = 0;
758 
759         double framesPerSec = 24.0;
760 
761         switch (fTimeCodeInfo.mFrameRate)
762         {
763             case ficFrameRate_24Frame:       info.frameRate = AudioPlayHead::fps24;       break;
764             case ficFrameRate_25Frame:       info.frameRate = AudioPlayHead::fps25;       framesPerSec = 25.0; break;
765             case ficFrameRate_2997NonDrop:   info.frameRate = AudioPlayHead::fps2997;     framesPerSec = 30.0 * 1000.0 / 1001.0; break;
766             case ficFrameRate_2997DropFrame: info.frameRate = AudioPlayHead::fps2997drop; framesPerSec = 30.0 * 1000.0 / 1001.0; break;
767             case ficFrameRate_30NonDrop:     info.frameRate = AudioPlayHead::fps30;       framesPerSec = 30.0; break;
768             case ficFrameRate_30DropFrame:   info.frameRate = AudioPlayHead::fps30drop;   framesPerSec = 30.0; break;
769             case ficFrameRate_23976:         info.frameRate = AudioPlayHead::fps23976;    framesPerSec = 24.0 * 1000.0 / 1001.0; break;
770             default:                         info.frameRate = AudioPlayHead::fpsUnknown;  break;
771         }
772 
773         info.editOriginTime = fTimeCodeInfo.mFrameOffset / framesPerSec;
774 
775         return true;
776     }
777 
audioProcessorParameterChanged(AudioProcessor *,int index,float newValue)778     void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) override
779     {
780         SetControlValue (index + 2, floatToLong (newValue));
781     }
782 
audioProcessorParameterChangeGestureBegin(AudioProcessor *,int index)783     void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override
784     {
785         TouchControl (index + 2);
786     }
787 
audioProcessorParameterChangeGestureEnd(AudioProcessor *,int index)788     void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override
789     {
790         ReleaseControl (index + 2);
791     }
792 
audioProcessorChanged(AudioProcessor *,const ChangeDetails &)793     void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override
794     {
795         // xxx is there an RTAS equivalent?
796     }
797 
798 private:
799     std::unique_ptr<AudioProcessor> juceFilter;
800     MidiBuffer midiEvents;
801     std::unique_ptr<CEffectMIDIOtherBufferedNode> midiBufferNode;
802     std::unique_ptr<CEffectMIDITransport> midiTransport;
803     DirectMidiPacket midiBuffer [midiBufferSize];
804 
805     juce::MemoryBlock tempFilterData;
806     HeapBlock<float*> channels;
807     double sampleRate = 44100.0;
808 
longToFloat(const long n)809     static float longToFloat (const long n) noexcept
810     {
811         return (float) ((((double) n) + (double) 0x80000000) / (double) 0xffffffff);
812     }
813 
floatToLong(const float n)814     static long floatToLong (const float n) noexcept
815     {
816         return roundToInt (jlimit (-(double) 0x80000000, (double) 0x7fffffff,
817                                    n * (double) 0xffffffff - (double) 0x80000000));
818     }
819 
bypassBuffers(float ** const inputs,float ** const outputs,const long numSamples) const820     void bypassBuffers (float** const inputs, float** const outputs, const long numSamples) const
821     {
822         for (int i = fNumOutputs; --i >= 0;)
823         {
824             if (i < fNumInputs)
825                 FloatVectorOperations::copy (outputs[i], inputs[i], numSamples);
826             else
827                 FloatVectorOperations::clear (outputs[i], numSamples);
828         }
829     }
830 
831     //==============================================================================
832     class JucePluginControl   : public CPluginControl
833     {
834     public:
835         //==============================================================================
JucePluginControl(AudioProcessor & p,const int i,OSType rtasParamID)836         JucePluginControl (AudioProcessor& p, const int i, OSType rtasParamID)
837             : processor (p), index (i), paramID (rtasParamID)
838         {
839             CPluginControl::SetValue (GetDefaultValue());
840         }
841 
842         //==============================================================================
GetID() const843         OSType GetID() const            { return paramID; }
GetDefaultValue() const844         long GetDefaultValue() const    { return floatToLong (processor.getParameterDefaultValue (index)); }
SetDefaultValue(long)845         void SetDefaultValue (long)     {}
GetNumSteps() const846         long GetNumSteps() const        { return processor.getParameterNumSteps (index); }
847 
ConvertStringToValue(const char * valueString) const848         long ConvertStringToValue (const char* valueString) const
849         {
850             return floatToLong (String (valueString).getFloatValue());
851         }
852 
IsKeyValid(long key) const853         Cmn_Bool IsKeyValid (long key) const    { return true; }
854 
GetNameOfLength(char * name,int maxLength,OSType inControllerType) const855         void GetNameOfLength (char* name, int maxLength, OSType inControllerType) const
856         {
857             // Pro-tools expects all your parameters to have valid names!
858             jassert (processor.getParameterName (index, maxLength).isNotEmpty());
859 
860             processor.getParameterName (index, maxLength).copyToUTF8 (name, (size_t) maxLength + 1);
861         }
862 
GetPriority() const863         long GetPriority() const        { return kFicCooperativeTaskPriority; }
864 
GetOrientation() const865         long GetOrientation() const
866         {
867             return processor.isParameterOrientationInverted (index)
868                      ? kDAE_RightMinLeftMax | kDAE_TopMinBottomMax | kDAE_RotarySingleDotMode | kDAE_RotaryRightMinLeftMax
869                      : kDAE_LeftMinRightMax | kDAE_BottomMinTopMax | kDAE_RotarySingleDotMode | kDAE_RotaryLeftMinRightMax;
870         }
871 
GetControlType() const872         long GetControlType() const     { return kDAE_ContinuousValues; }
873 
GetValueString(char * valueString,int maxLength,long value) const874         void GetValueString (char* valueString, int maxLength, long value) const
875         {
876             processor.getParameterText (index, maxLength).copyToUTF8 (valueString, (size_t) maxLength + 1);
877         }
878 
IsAutomatable() const879         Cmn_Bool IsAutomatable() const
880         {
881             return processor.isParameterAutomatable (index);
882         }
883 
884     private:
885         //==============================================================================
886         AudioProcessor& processor;
887         const int index;
888         const OSType paramID;
889 
890         JUCE_DECLARE_NON_COPYABLE (JucePluginControl)
891     };
892 };
893 
894 //==============================================================================
895 class JucePlugInGroup   : public CEffectGroupMIDI
896 {
897 public:
898     //==============================================================================
JucePlugInGroup()899     JucePlugInGroup()
900     {
901         DefineManufacturerNamesAndID (JucePlugin_Manufacturer, JucePlugin_RTASManufacturerCode);
902         DefinePlugInNamesAndVersion (createRTASName().toUTF8(), JucePlugin_VersionCode);
903 
904        #if JUCE_RTAS_PLUGINGESTALT_IS_CACHEABLE
905         AddGestalt (pluginGestalt_IsCacheable);
906        #endif
907     }
908 
~JucePlugInGroup()909     ~JucePlugInGroup()
910     {
911         shutdownJuce_GUI();
912     }
913 
rtasChannelSet(int numChannels)914     static AudioChannelSet rtasChannelSet (int numChannels)
915     {
916         if (numChannels == 0) return AudioChannelSet::disabled();
917         if (numChannels == 1) return AudioChannelSet::mono();
918         if (numChannels == 2) return AudioChannelSet::stereo();
919         if (numChannels == 3) return AudioChannelSet::createLCR();
920         if (numChannels == 4) return AudioChannelSet::quadraphonic();
921         if (numChannels == 5) return AudioChannelSet::create5point0();
922         if (numChannels == 6) return AudioChannelSet::create5point1();
923 
924         #if PT_VERS_MAJOR >= 9
925         if (numChannels == 7) return AudioChannelSet::create7point0();
926         if (numChannels == 8) return AudioChannelSet::create7point1();
927         #else
928         if (numChannels == 7) return AudioChannelSet::create7point0SDDS();
929         if (numChannels == 8) return AudioChannelSet::create7point1SDDS();
930         #endif
931 
932         jassertfalse;
933 
934         return AudioChannelSet::discreteChannels (numChannels);
935     }
936 
937     //==============================================================================
CreateEffectTypes()938     void CreateEffectTypes()
939     {
940         std::unique_ptr<AudioProcessor> plugin (createPluginFilterOfType (AudioProcessor::wrapperType_RTAS));
941 
942        #ifndef JucePlugin_PreferredChannelConfigurations
943         #error You need to set the "Plugin Channel Configurations" field in the Projucer to build RTAS plug-ins
944        #endif
945 
946         const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
947         const int numConfigs = numElementsInArray (channelConfigs);
948 
949         // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
950         // value in your JucePluginCharacteristics.h file..
951         jassert (numConfigs > 0);
952 
953         for (int i = 0; i < numConfigs; ++i)
954         {
955             if (channelConfigs[i][0] <= 8 && channelConfigs[i][1] <= 8)
956             {
957                 const AudioChannelSet inputLayout  (rtasChannelSet (channelConfigs[i][0]));
958                 const AudioChannelSet outputLayout (rtasChannelSet (channelConfigs[i][1]));
959 
960                 const int32 pluginId = plugin->getAAXPluginIDForMainBusConfig (inputLayout, outputLayout, false);
961 
962                 CEffectType* const type
963                     = new CEffectTypeRTAS (pluginId,
964                                            JucePlugin_RTASProductId,
965                                            JucePlugin_RTASCategory);
966 
967                 type->DefineTypeNames (createRTASName().toRawUTF8());
968                 type->DefineSampleRateSupport (eSupports48kAnd96kAnd192k);
969 
970                 type->DefineStemFormats (getFormatForChans (channelConfigs [i][0] != 0 ? channelConfigs [i][0] : channelConfigs [i][1]),
971                                          getFormatForChans (channelConfigs [i][1] != 0 ? channelConfigs [i][1] : channelConfigs [i][0]));
972 
973                #if ! JucePlugin_RTASDisableBypass
974                 type->AddGestalt (pluginGestalt_CanBypass);
975                #endif
976 
977                #if JucePlugin_RTASDisableMultiMono
978                 type->AddGestalt (pluginGestalt_DoesntSupportMultiMono);
979                #endif
980 
981                 type->AddGestalt (pluginGestalt_SupportsVariableQuanta);
982                 type->AttachEffectProcessCreator (createNewProcess);
983 
984                 AddEffectType (type);
985             }
986         }
987     }
988 
Initialize()989     void Initialize()
990     {
991         CEffectGroupMIDI::Initialize();
992     }
993 
994     //==============================================================================
995 private:
createNewProcess()996     static CEffectProcess* createNewProcess()
997     {
998        #if JUCE_WINDOWS
999         Process::setCurrentModuleInstanceHandle (gThisModule);
1000        #endif
1001         PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_RTAS;
1002         initialiseJuce_GUI();
1003 
1004         return new JucePlugInProcess();
1005     }
1006 
createRTASName()1007     static String createRTASName()
1008     {
1009         return String (JucePlugin_Name) + "\n"
1010                  + String (JucePlugin_Desc);
1011     }
1012 
getFormatForChans(const int numChans)1013     static EPlugIn_StemFormat getFormatForChans (const int numChans) noexcept
1014     {
1015         switch (numChans)
1016         {
1017             case 0:   return ePlugIn_StemFormat_Generic;
1018             case 1:   return ePlugIn_StemFormat_Mono;
1019             case 2:   return ePlugIn_StemFormat_Stereo;
1020             case 3:   return ePlugIn_StemFormat_LCR;
1021             case 4:   return ePlugIn_StemFormat_Quad;
1022             case 5:   return ePlugIn_StemFormat_5dot0;
1023             case 6:   return ePlugIn_StemFormat_5dot1;
1024 
1025            #if PT_VERS_MAJOR >= 9
1026             case 7:   return ePlugIn_StemFormat_7dot0DTS;
1027             case 8:   return ePlugIn_StemFormat_7dot1DTS;
1028            #else
1029             case 7:   return ePlugIn_StemFormat_7dot0;
1030             case 8:   return ePlugIn_StemFormat_7dot1;
1031            #endif
1032 
1033             default:  jassertfalse; break; // hmm - not a valid number of chans for RTAS..
1034         }
1035 
1036         return ePlugIn_StemFormat_Generic;
1037     }
1038 };
1039 
1040 void initialiseMacRTAS();
1041 
CreateProcessGroup()1042 CProcessGroupInterface* CProcessGroup::CreateProcessGroup()
1043 {
1044    #if JUCE_MAC
1045     initialiseMacRTAS();
1046    #endif
1047 
1048     return new JucePlugInGroup();
1049 }
1050 
1051 JUCE_END_IGNORE_WARNINGS_MSVC
1052 
1053 #endif
1054