Add remaining files
[juce-lv2.git] / juce / source / src / audio / plugin_client / RTAS / juce_RTAS_Wrapper.cpp
blobb222ab2a2bec827fbd0a48cd83c32b7338a3b040
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #ifdef _MSC_VER
27 // (this is a workaround for a build problem in VC9)
28 #define _DO_NOT_DECLARE_INTERLOCKED_INTRINSICS_IN_MEMORY
29 #include <intrin.h>
30 #endif
32 #include "juce_RTAS_DigiCode_Header.h"
34 #if JucePlugin_Build_RTAS
36 #ifdef _MSC_VER
37 #include <Mac2Win.H>
38 #endif
40 /* Note about include paths
41 ------------------------
43 To be able to include all the Digidesign headers correctly, you'll need to add this
44 lot to your include path:
46 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\EffectClasses
47 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses
48 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses\Interfaces
49 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Utilities
50 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\RTASP_Adapt
51 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\CoreClasses
52 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Controls
53 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Meters
54 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ViewClasses
55 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\DSPClasses
56 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Interfaces
57 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\common
58 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\common\Platform
59 c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\SignalProcessing\Public
60 C:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugIns\DSPManager\Interfaces
61 c:\yourdirectory\PT_80_SDK\AlturaPorts\SADriver\Interfaces
62 c:\yourdirectory\PT_80_SDK\AlturaPorts\DigiPublic\Interfaces
63 c:\yourdirectory\PT_80_SDK\AlturaPorts\Fic\Interfaces\DAEClient
64 c:\yourdirectory\PT_80_SDK\AlturaPorts\NewFileLibs\Cmn
65 c:\yourdirectory\PT_80_SDK\AlturaPorts\NewFileLibs\DOA
66 c:\yourdirectory\PT_80_SDK\AlturaPorts\AlturaSource\PPC_H
67 c:\yourdirectory\PT_80_SDK\AlturaPorts\AlturaSource\AppSupport
68 c:\yourdirectory\PT_80_SDK\AvidCode\AVX2sdk\AVX\avx2\avx2sdk\inc
69 C:\yourdirectory\PT_80_SDK\xplat\AVX\avx2\avx2sdk\inc
71 NB. If you hit a huge pile of bugs around here, make sure that you've not got the
72 Apple QuickTime headers before the PT headers in your path, because there are
73 some filename clashes between them.
76 #include <CEffectGroupMIDI.h>
77 #include <CEffectProcessMIDI.h>
78 #include <CEffectProcessRTAS.h>
79 #include <CCustomView.h>
80 #include <CEffectTypeRTAS.h>
81 #include <CPluginControl.h>
82 #include <CPluginControl_OnOff.h>
83 #include <FicProcessTokens.h>
85 //==============================================================================
86 #ifdef _MSC_VER
87 #pragma pack (push, 8)
88 #endif
90 #include "../juce_PluginHeaders.h"
92 #ifdef _MSC_VER
93 #pragma pack (pop)
95 #if JUCE_DEBUGxxx // (the debug lib in the 8.0 SDK fails to link, so we'll stick to the release one...)
96 #define PT_LIB_PATH JucePlugin_WinBag_path "\\Debug\\lib\\"
97 #else
98 #define PT_LIB_PATH JucePlugin_WinBag_path "\\Release\\lib\\"
99 #endif
101 #pragma comment(lib, PT_LIB_PATH "DAE.lib")
102 #pragma comment(lib, PT_LIB_PATH "DigiExt.lib")
103 #pragma comment(lib, PT_LIB_PATH "DSI.lib")
104 #pragma comment(lib, PT_LIB_PATH "PluginLib.lib")
105 #endif
107 #undef Component
108 #undef MemoryBlock
110 //==============================================================================
111 #if JUCE_WINDOWS
112 extern void JUCE_CALLTYPE attachSubWindow (void* hostWindow, int& titleW, int& titleH, JUCE_NAMESPACE::Component* comp);
113 extern void JUCE_CALLTYPE resizeHostWindow (void* hostWindow, int& titleW, int& titleH, JUCE_NAMESPACE::Component* comp);
114 #if ! JucePlugin_EditorRequiresKeyboardFocus
115 extern void JUCE_CALLTYPE passFocusToHostWindow (void* hostWindow);
116 #endif
117 #else
118 extern void* attachSubWindow (void* hostWindowRef, JUCE_NAMESPACE::Component* comp);
119 extern void removeSubWindow (void* nsWindow, JUCE_NAMESPACE::Component* comp);
120 extern void forwardCurrentKeyEventToHostWindow();
121 #endif
123 const int midiBufferSize = 1024;
124 const OSType juceChunkType = 'juce';
125 static const int bypassControlIndex = 1;
127 //==============================================================================
128 /** Somewhere in the codebase of your plugin, you need to implement this function
129 and make it return a new instance of the filter subclass that you're building.
131 extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
133 static int numInstances = 0;
136 //==============================================================================
137 class JucePlugInProcess : public CEffectProcessMIDI,
138 public CEffectProcessRTAS,
139 public AudioProcessorListener,
140 public AudioPlayHead
142 public:
143 //==============================================================================
144 JucePlugInProcess()
145 : prepared (false),
146 sampleRate (44100.0)
148 asyncUpdater = new InternalAsyncUpdater (*this);
149 juceFilter = createPluginFilter();
150 jassert (juceFilter != 0);
152 AddChunk (juceChunkType, "Juce Audio Plugin Data");
154 ++numInstances;
157 ~JucePlugInProcess()
159 JUCE_AUTORELEASEPOOL
161 if (mLoggedIn)
162 MIDILogOut();
164 midiBufferNode = nullptr;
165 midiTransport = nullptr;
167 if (prepared)
168 juceFilter->releaseResources();
170 delete juceFilter;
171 asyncUpdater = 0;
173 if (--numInstances == 0)
175 #if JUCE_MAC
176 // Hack to allow any NSWindows to clear themselves up before returning to PT..
177 for (int i = 20; --i >= 0;)
178 MessageManager::getInstance()->runDispatchLoopUntil (1);
179 #endif
181 shutdownJuce_GUI();
185 //==============================================================================
186 class JuceCustomUIView : public CCustomView,
187 public Timer
189 public:
190 //==============================================================================
191 JuceCustomUIView (AudioProcessor* const filter_,
192 JucePlugInProcess* const process_)
193 : filter (filter_),
194 process (process_)
196 // setting the size in here crashes PT for some reason, so keep it simple..
199 ~JuceCustomUIView()
201 deleteEditorComp();
204 //==============================================================================
205 void updateSize()
207 if (editorComp == nullptr)
209 editorComp = filter->createEditorIfNeeded();
210 jassert (editorComp != nullptr);
213 if (editorComp->getWidth() != 0 && editorComp->getHeight() != 0)
215 Rect oldRect;
216 GetRect (&oldRect);
218 Rect r;
219 r.left = 0;
220 r.top = 0;
221 r.right = editorComp->getWidth();
222 r.bottom = editorComp->getHeight();
223 SetRect (&r);
225 if ((oldRect.right != r.right) || (oldRect.bottom != r.bottom))
226 startTimer (50);
230 void timerCallback()
232 if (! JUCE_NAMESPACE::Component::isMouseButtonDownAnywhere())
234 stopTimer();
236 // Send a token to the host to tell it about the resize
237 SSetProcessWindowResizeToken token (process->fRootNameId, process->fRootNameId);
238 FicSDSDispatchToken (&token);
242 void attachToWindow (GrafPtr port)
244 if (port != 0)
246 JUCE_AUTORELEASEPOOL
247 updateSize();
249 #if JUCE_WINDOWS
250 void* const hostWindow = (void*) ASI_GethWnd ((WindowPtr) port);
251 #else
252 void* const hostWindow = (void*) GetWindowFromPort (port);
253 #endif
254 wrapper = nullptr;
255 wrapper = new EditorCompWrapper (hostWindow, editorComp, this);
257 process->touchAllParameters();
259 else
261 deleteEditorComp();
265 void DrawContents (Rect*)
267 #if JUCE_WINDOWS
268 if (wrapper != nullptr)
270 ComponentPeer* const peer = wrapper->getPeer();
272 if (peer != nullptr)
274 // (seems to be required in PT6.4, but not in 7.x)
275 peer->repaint (wrapper->getLocalBounds());
278 #endif
281 void DrawBackground (Rect*) {}
283 //==============================================================================
284 private:
285 AudioProcessor* const filter;
286 JucePlugInProcess* const process;
287 ScopedPointer<JUCE_NAMESPACE::Component> wrapper;
288 ScopedPointer<AudioProcessorEditor> editorComp;
290 void deleteEditorComp()
292 if (editorComp != 0 || wrapper != nullptr)
294 JUCE_AUTORELEASEPOOL
295 PopupMenu::dismissAllActiveMenus();
297 JUCE_NAMESPACE::Component* const modalComponent = JUCE_NAMESPACE::Component::getCurrentlyModalComponent();
298 if (modalComponent != nullptr)
299 modalComponent->exitModalState (0);
301 filter->editorBeingDeleted (editorComp);
303 editorComp = nullptr;
304 wrapper = nullptr;
308 //==============================================================================
309 // A component to hold the AudioProcessorEditor, and cope with some housekeeping
310 // chores when it changes or repaints.
311 class EditorCompWrapper : public JUCE_NAMESPACE::Component
312 #if ! JUCE_MAC
313 , public FocusChangeListener
314 #endif
316 public:
317 EditorCompWrapper (void* const hostWindow_,
318 Component* const editorComp,
319 JuceCustomUIView* const owner_)
320 : hostWindow (hostWindow_),
321 owner (owner_),
322 titleW (0),
323 titleH (0)
325 #if ! JucePlugin_EditorRequiresKeyboardFocus
326 setMouseClickGrabsKeyboardFocus (false);
327 setWantsKeyboardFocus (false);
328 #endif
329 setOpaque (true);
330 setBroughtToFrontOnMouseClick (true);
331 setBounds (editorComp->getBounds());
332 editorComp->setTopLeftPosition (0, 0);
333 addAndMakeVisible (editorComp);
335 #if JUCE_WINDOWS
336 attachSubWindow (hostWindow, titleW, titleH, this);
337 #else
338 nsWindow = attachSubWindow (hostWindow, this);
339 #endif
340 setVisible (true);
342 #if JUCE_WINDOWS && ! JucePlugin_EditorRequiresKeyboardFocus
343 Desktop::getInstance().addFocusChangeListener (this);
344 #endif
347 ~EditorCompWrapper()
349 #if JUCE_WINDOWS && ! JucePlugin_EditorRequiresKeyboardFocus
350 Desktop::getInstance().removeFocusChangeListener (this);
351 #endif
353 #if JUCE_MAC
354 removeSubWindow (nsWindow, this);
355 #endif
358 void paint (Graphics&)
362 void resized()
364 JUCE_NAMESPACE::Component* const c = getChildComponent (0);
366 if (c != nullptr)
367 c->setBounds (0, 0, getWidth(), getHeight());
369 repaint();
372 #if JUCE_WINDOWS
373 void globalFocusChanged (JUCE_NAMESPACE::Component*)
375 #if ! JucePlugin_EditorRequiresKeyboardFocus
376 if (hasKeyboardFocus (true))
377 passFocusToHostWindow (hostWindow);
378 #endif
380 #endif
382 void childBoundsChanged (JUCE_NAMESPACE::Component* child)
384 setSize (child->getWidth(), child->getHeight());
385 child->setTopLeftPosition (0, 0);
387 #if JUCE_WINDOWS
388 resizeHostWindow (hostWindow, titleW, titleH, this);
389 #endif
390 owner->updateSize();
393 void userTriedToCloseWindow()
397 #if JUCE_MAC && JucePlugin_EditorRequiresKeyboardFocus
398 bool keyPressed (const KeyPress& kp)
400 owner->updateSize();
401 forwardCurrentKeyEventToHostWindow();
402 return true;
404 #endif
406 private:
407 //==============================================================================
408 void* const hostWindow;
409 void* nsWindow;
410 JuceCustomUIView* const owner;
411 int titleW, titleH;
413 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper);
417 JuceCustomUIView* getView() const
419 return dynamic_cast <JuceCustomUIView*> (fOurPlugInView);
422 void GetViewRect (Rect* size)
424 if (getView() != nullptr)
425 getView()->updateSize();
427 CEffectProcessRTAS::GetViewRect (size);
430 CPlugInView* CreateCPlugInView()
432 return new JuceCustomUIView (juceFilter, this);
435 void SetViewPort (GrafPtr port)
437 CEffectProcessRTAS::SetViewPort (port);
439 if (getView() != nullptr)
440 getView()->attachToWindow (port);
443 //==============================================================================
444 protected:
445 ComponentResult GetDelaySamplesLong (long* aNumSamples)
447 if (aNumSamples != nullptr)
448 *aNumSamples = juceFilter != nullptr ? juceFilter->getLatencySamples() : 0;
450 return noErr;
453 //==============================================================================
454 void EffectInit()
456 SFicPlugInStemFormats stems;
457 GetProcessType()->GetStemFormats (&stems);
459 juceFilter->setPlayConfigDetails (fNumInputs, fNumOutputs,
460 juceFilter->getSampleRate(), juceFilter->getBlockSize());
462 AddControl (new CPluginControl_OnOff ('bypa', "Master Bypass\nMastrByp\nMByp\nByp", false, true));
463 DefineMasterBypassControlIndex (bypassControlIndex);
465 for (int i = 0; i < juceFilter->getNumParameters(); ++i)
466 AddControl (new JucePluginControl (juceFilter, i));
468 // we need to do this midi log-in to get timecode, regardless of whether
469 // the plugin actually uses midi...
470 if (MIDILogIn() == noErr)
472 #if JucePlugin_WantsMidiInput
473 CEffectType* const type = dynamic_cast <CEffectType*> (this->GetProcessType());
475 if (type != nullptr)
477 char nodeName [64];
478 type->GetProcessTypeName (63, nodeName);
479 p2cstrcpy (nodeName, reinterpret_cast <unsigned char*> (nodeName));
481 midiBufferNode = new CEffectMIDIOtherBufferedNode (&mMIDIWorld,
482 8192,
483 eLocalNode,
484 nodeName,
485 midiBuffer);
487 midiBufferNode->Initialize (1, true);
489 #endif
492 midiTransport = new CEffectMIDITransport (&mMIDIWorld);
494 juceFilter->setPlayHead (this);
495 juceFilter->addListener (this);
497 midiEvents.ensureSize (2048);
500 void handleAsyncUpdate()
502 if (! prepared)
504 sampleRate = gProcessGroup->GetSampleRate();
505 jassert (sampleRate > 0);
507 channels.calloc (jmax (juceFilter->getNumInputChannels(),
508 juceFilter->getNumOutputChannels()));
510 juceFilter->setPlayConfigDetails (fNumInputs, fNumOutputs,
511 sampleRate, mRTGlobals->mHWBufferSizeInSamples);
513 juceFilter->prepareToPlay (sampleRate,
514 mRTGlobals->mHWBufferSizeInSamples);
516 prepared = true;
520 void RenderAudio (float** inputs, float** outputs, long numSamples)
522 if (! prepared)
524 asyncUpdater->triggerAsyncUpdate();
525 bypassBuffers (inputs, outputs, numSamples);
526 return;
529 if (mBypassed)
531 bypassBuffers (inputs, outputs, numSamples);
532 return;
535 #if JucePlugin_WantsMidiInput
536 midiEvents.clear();
538 const Cmn_UInt32 bufferSize = mRTGlobals->mHWBufferSizeInSamples;
540 if (midiBufferNode != 0)
542 if (midiBufferNode->GetAdvanceScheduleTime() != bufferSize)
543 midiBufferNode->SetAdvanceScheduleTime (bufferSize);
545 if (midiBufferNode->FillMIDIBuffer (mRTGlobals->mRunningTime, numSamples) == noErr)
547 jassert (midiBufferNode->GetBufferPtr() != 0);
548 const int numMidiEvents = midiBufferNode->GetBufferSize();
550 for (int i = 0; i < numMidiEvents; ++i)
552 const DirectMidiPacket& m = midiBuffer[i];
554 jassert ((int) m.mTimestamp < numSamples);
556 midiEvents.addEvent (m.mData, m.mLength,
557 jlimit (0, (int) numSamples - 1, (int) m.mTimestamp));
561 #endif
563 #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
564 const int numMidiEventsComingIn = midiEvents.getNumEvents();
565 (void) numMidiEventsComingIn;
566 #endif
569 const ScopedLock sl (juceFilter->getCallbackLock());
571 const int numIn = juceFilter->getNumInputChannels();
572 const int numOut = juceFilter->getNumOutputChannels();
573 const int totalChans = jmax (numIn, numOut);
575 if (juceFilter->isSuspended())
577 for (int i = 0; i < numOut; ++i)
578 zeromem (outputs [i], sizeof (float) * numSamples);
580 else
583 int i;
584 for (i = 0; i < numOut; ++i)
586 channels[i] = outputs [i];
588 if (i < numIn && inputs != outputs)
589 memcpy (outputs [i], inputs[i], sizeof (float) * numSamples);
592 for (; i < numIn; ++i)
593 channels [i] = inputs [i];
596 AudioSampleBuffer chans (channels, totalChans, numSamples);
598 juceFilter->processBlock (chans, midiEvents);
602 if (! midiEvents.isEmpty())
604 #if JucePlugin_ProducesMidiOutput
605 const JUCE_NAMESPACE::uint8* midiEventData;
606 int midiEventSize, midiEventPosition;
607 MidiBuffer::Iterator i (midiEvents);
609 while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
611 //jassert (midiEventPosition >= 0 && midiEventPosition < (int) numSamples);
613 #else
614 // if your plugin creates midi messages, you'll need to set
615 // the JucePlugin_ProducesMidiOutput macro to 1 in your
616 // JucePluginCharacteristics.h file
617 jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
618 #endif
620 midiEvents.clear();
624 //==============================================================================
625 ComponentResult GetChunkSize (OSType chunkID, long* size)
627 if (chunkID == juceChunkType)
629 tempFilterData.setSize (0);
630 juceFilter->getStateInformation (tempFilterData);
632 *size = sizeof (SFicPlugInChunkHeader) + tempFilterData.getSize();
633 return noErr;
636 return CEffectProcessMIDI::GetChunkSize (chunkID, size);
639 ComponentResult GetChunk (OSType chunkID, SFicPlugInChunk* chunk)
641 if (chunkID == juceChunkType)
643 if (tempFilterData.getSize() == 0)
644 juceFilter->getStateInformation (tempFilterData);
646 chunk->fSize = sizeof (SFicPlugInChunkHeader) + tempFilterData.getSize();
647 tempFilterData.copyTo ((void*) chunk->fData, 0, tempFilterData.getSize());
649 tempFilterData.setSize (0);
651 return noErr;
654 return CEffectProcessMIDI::GetChunk (chunkID, chunk);
657 ComponentResult SetChunk (OSType chunkID, SFicPlugInChunk* chunk)
659 if (chunkID == juceChunkType)
661 tempFilterData.setSize (0);
663 if (chunk->fSize - sizeof (SFicPlugInChunkHeader) > 0)
665 juceFilter->setStateInformation ((void*) chunk->fData,
666 chunk->fSize - sizeof (SFicPlugInChunkHeader));
669 return noErr;
672 return CEffectProcessMIDI::SetChunk (chunkID, chunk);
675 //==============================================================================
676 ComponentResult UpdateControlValue (long controlIndex, long value)
678 if (controlIndex != bypassControlIndex)
679 juceFilter->setParameter (controlIndex - 2, longToFloat (value));
680 else
681 mBypassed = (value > 0);
683 return CProcess::UpdateControlValue (controlIndex, value);
686 //==============================================================================
687 bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info)
689 // this method can only be called while the plugin is running
690 jassert (prepared);
692 Cmn_Float64 bpm = 120.0;
693 Cmn_Int32 num = 4, denom = 4;
694 Cmn_Int64 ticks = 0;
695 Cmn_Bool isPlaying = false;
697 if (midiTransport != 0)
699 midiTransport->GetCurrentTempo (&bpm);
700 midiTransport->IsTransportPlaying (&isPlaying);
701 midiTransport->GetCurrentMeter (&num, &denom);
703 // (The following is a work-around because GetCurrentTickPosition() doesn't work correctly).
704 Cmn_Int64 sampleLocation;
706 if (isPlaying)
707 midiTransport->GetCurrentRTASSampleLocation (&sampleLocation);
708 else
709 midiTransport->GetCurrentTDMSampleLocation (&sampleLocation);
711 midiTransport->GetCustomTickPosition (&ticks, sampleLocation);
714 info.bpm = bpm;
715 info.timeSigNumerator = num;
716 info.timeSigDenominator = denom;
717 info.isPlaying = isPlaying;
718 info.isRecording = false;
719 info.ppqPosition = ticks / 960000.0;
720 info.ppqPositionOfLastBarStart = 0; //xxx no idea how to get this correctly..
722 // xxx incorrect if there are tempo changes, but there's no
723 // other way of getting this info..
724 info.timeInSeconds = ticks * (60.0 / 960000.0) / bpm;
726 double framesPerSec = 24.0;
728 switch (fTimeCodeInfo.mFrameRate)
730 case ficFrameRate_24Frame: info.frameRate = AudioPlayHead::fps24; break;
731 case ficFrameRate_25Frame: info.frameRate = AudioPlayHead::fps25; framesPerSec = 25.0; break;
732 case ficFrameRate_2997NonDrop: info.frameRate = AudioPlayHead::fps2997; framesPerSec = 29.97002997; break;
733 case ficFrameRate_2997DropFrame: info.frameRate = AudioPlayHead::fps2997drop; framesPerSec = 29.97002997; break;
734 case ficFrameRate_30NonDrop: info.frameRate = AudioPlayHead::fps30; framesPerSec = 30.0; break;
735 case ficFrameRate_30DropFrame: info.frameRate = AudioPlayHead::fps30drop; framesPerSec = 30.0; break;
736 case ficFrameRate_23976: info.frameRate = AudioPlayHead::fps24; framesPerSec = 23.976; break;
737 default: info.frameRate = AudioPlayHead::fpsUnknown; break;
740 info.editOriginTime = fTimeCodeInfo.mFrameOffset / framesPerSec;
742 return true;
745 void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
747 SetControlValue (index + 2, floatToLong (newValue));
750 void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index)
752 TouchControl (index + 2);
755 void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index)
757 ReleaseControl (index + 2);
760 void audioProcessorChanged (AudioProcessor*)
762 // xxx is there an RTAS equivalent?
765 void touchAllParameters()
767 for (int i = 0; i < juceFilter->getNumParameters(); ++i)
769 audioProcessorParameterChangeGestureBegin (0, i);
770 audioProcessorParameterChanged (0, i, juceFilter->getParameter (i));
771 audioProcessorParameterChangeGestureEnd (0, i);
775 public:
776 // Need to use an intermediate class here rather than inheriting from AsyncUpdater, so that it can
777 // be deleted before shutting down juce in our destructor.
778 class InternalAsyncUpdater : public AsyncUpdater
780 public:
781 InternalAsyncUpdater (JucePlugInProcess& owner_) : owner (owner_) {}
782 void handleAsyncUpdate() { owner.handleAsyncUpdate(); }
784 private:
785 JucePlugInProcess& owner;
788 //==============================================================================
789 private:
790 AudioProcessor* juceFilter;
791 MidiBuffer midiEvents;
792 ScopedPointer<CEffectMIDIOtherBufferedNode> midiBufferNode;
793 ScopedPointer<CEffectMIDITransport> midiTransport;
794 DirectMidiPacket midiBuffer [midiBufferSize];
796 ScopedPointer<InternalAsyncUpdater> asyncUpdater;
798 JUCE_NAMESPACE::MemoryBlock tempFilterData;
799 HeapBlock <float*> channels;
800 bool prepared;
801 double sampleRate;
803 static float longToFloat (const long n) noexcept
805 return (float) ((((double) n) + (double) 0x80000000) / (double) 0xffffffff);
808 static long floatToLong (const float n) noexcept
810 return roundToInt (jlimit (-(double) 0x80000000, (double) 0x7fffffff,
811 n * (double) 0xffffffff - (double) 0x80000000));
814 void bypassBuffers (float** const inputs, float** const outputs, const long numSamples) const
816 for (int i = fNumOutputs; --i >= 0;)
818 if (i < fNumInputs)
819 memcpy (outputs[i], inputs[i], numSamples * sizeof (float));
820 else
821 zeromem (outputs[i], numSamples * sizeof (float));
825 //==============================================================================
826 class JucePluginControl : public CPluginControl
828 public:
829 //==============================================================================
830 JucePluginControl (AudioProcessor* const juceFilter_, const int index_)
831 : juceFilter (juceFilter_),
832 index (index_)
836 //==============================================================================
837 OSType GetID() const { return index + 1; }
838 long GetDefaultValue() const { return floatToLong (0); }
839 void SetDefaultValue (long) {}
840 long GetNumSteps() const { return 0xffffffff; }
842 long ConvertStringToValue (const char* valueString) const
844 return floatToLong (String (valueString).getFloatValue());
847 Cmn_Bool IsKeyValid (long key) const { return true; }
849 void GetNameOfLength (char* name, int maxLength, OSType inControllerType) const
851 juceFilter->getParameterName (index).copyToUTF8 (name, maxLength);
854 long GetPriority() const { return kFicCooperativeTaskPriority; }
856 long GetOrientation() const
858 return kDAE_LeftMinRightMax | kDAE_BottomMinTopMax
859 | kDAE_RotarySingleDotMode | kDAE_RotaryLeftMinRightMax;
862 long GetControlType() const { return kDAE_ContinuousValues; }
864 void GetValueString (char* valueString, int maxLength, long value) const
866 juceFilter->getParameterText (index).copyToUTF8 (valueString, maxLength);
869 Cmn_Bool IsAutomatable() const
871 return juceFilter->isParameterAutomatable (index);
874 private:
875 //==============================================================================
876 AudioProcessor* const juceFilter;
877 const int index;
879 JUCE_DECLARE_NON_COPYABLE (JucePluginControl);
883 //==============================================================================
884 class JucePlugInGroup : public CEffectGroupMIDI
886 public:
887 //==============================================================================
888 JucePlugInGroup()
890 DefineManufacturerNamesAndID (JucePlugin_Manufacturer, JucePlugin_RTASManufacturerCode);
891 DefinePlugInNamesAndVersion (createRTASName().toUTF8(), JucePlugin_VersionCode);
893 #ifndef JUCE_DEBUG
894 AddGestalt (pluginGestalt_IsCacheable);
895 #endif
898 ~JucePlugInGroup()
900 shutdownJuce_GUI();
903 //==============================================================================
904 void CreateEffectTypes()
906 const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
907 const int numConfigs = numElementsInArray (channelConfigs);
909 // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
910 // value in your JucePluginCharacteristics.h file..
911 jassert (numConfigs > 0);
913 for (int i = 0; i < numConfigs; ++i)
915 CEffectType* const type
916 = new CEffectTypeRTAS ('jcaa' + i,
917 JucePlugin_RTASProductId,
918 JucePlugin_RTASCategory);
920 type->DefineTypeNames (createRTASName().toUTF8().getAddress());
921 type->DefineSampleRateSupport (eSupports48kAnd96kAnd192k);
923 type->DefineStemFormats (getFormatForChans (channelConfigs [i][0] != 0 ? channelConfigs [i][0] : channelConfigs [i][1]),
924 getFormatForChans (channelConfigs [i][1] != 0 ? channelConfigs [i][1] : channelConfigs [i][0]));
926 type->AddGestalt (pluginGestalt_CanBypass);
927 type->AddGestalt (pluginGestalt_SupportsVariableQuanta);
928 type->AttachEffectProcessCreator (createNewProcess);
930 AddEffectType (type);
934 void Initialize()
936 CEffectGroupMIDI::Initialize();
939 //==============================================================================
940 private:
941 static CEffectProcess* createNewProcess()
943 #if JUCE_WINDOWS
944 PlatformUtilities::setCurrentModuleInstanceHandle (gThisModule);
945 #endif
947 initialiseJuce_GUI();
949 return new JucePlugInProcess();
952 static String createRTASName()
954 return String (JucePlugin_Name) + "\n"
955 + String (JucePlugin_Name).substring (0, 4);
958 static EPlugIn_StemFormat getFormatForChans (const int numChans) noexcept
960 switch (numChans)
962 case 0: return ePlugIn_StemFormat_Generic;
963 case 1: return ePlugIn_StemFormat_Mono;
964 case 2: return ePlugIn_StemFormat_Stereo;
965 case 3: return ePlugIn_StemFormat_LCR;
966 case 4: return ePlugIn_StemFormat_Quad;
967 case 5: return ePlugIn_StemFormat_5dot0;
968 case 6: return ePlugIn_StemFormat_5dot1;
969 case 7: return ePlugIn_StemFormat_6dot1;
970 case 8: return ePlugIn_StemFormat_7dot1;
972 default: jassertfalse; break; // hmm - not a valid number of chans for RTAS..
975 return ePlugIn_StemFormat_Generic;
979 void initialiseMacRTAS();
981 CProcessGroupInterface* CProcessGroup::CreateProcessGroup()
983 #if JUCE_MAC
984 initialiseMacRTAS();
985 #endif
987 return new JucePlugInGroup();
990 #endif