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