1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/extras/audio plugins/wrapper/formats/RTAS/juce_RTASWrapper.cpp
2007-08-10 15:58:46 +00:00

1021 lines
34 KiB
C++

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-7 by Raw Material Software ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the
GNU General Public License, as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
JUCE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with JUCE; if not, visit www.gnu.org/licenses or write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
------------------------------------------------------------------------------
If you'd like to release a closed-source product which uses JUCE, commercial
licenses are also available: visit www.rawmaterialsoftware.com/juce for
more information.
==============================================================================
*/
#include "juce_RTASCompileFlags.h"
#ifdef _MSC_VER
// the Digidesign projects all use a struct alignment of 2..
#pragma pack (2)
#pragma warning (disable: 4267)
#include "ForcedInclude.h"
#include "Mac2Win.H"
#endif
/* Note about include paths
------------------------
To be able to include all the Digidesign headers correctly, you'll need to add this
lot to your include path:
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\EffectClasses
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses\Interfaces
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Utilities
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\RTASP_Adapt
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\CoreClasses
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Controls
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Meters
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ViewClasses
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\DSPClasses
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Interfaces
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\common
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\common\Platform
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\SignalProcessing\Public
c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugIns\DSPManager\Interfaces
c:\yourdirectory\PT_711_SDK\AlturaPorts\SADriver\Interfaces
c:\yourdirectory\PT_711_SDK\AlturaPorts\DigiPublic\Interfaces
c:\yourdirectory\PT_711_SDK\AlturaPorts\Fic\Interfaces\DAEClient
c:\yourdirectory\PT_711_SDK\AlturaPorts\NewFileLibs\Cmn
c:\yourdirectory\PT_711_SDK\AlturaPorts\NewFileLibs\DOA
c:\yourdirectory\PT_711_SDK\AlturaPorts\AlturaSource\PPC_H
c:\yourdirectory\PT_711_SDK\AlturaPorts\AlturaSource\AppSupport
*/
#include "CEffectGroupMIDI.h"
#include "CEffectProcessMIDI.h"
#include "CEffectProcessRTAS.h"
#include "CCustomView.h"
#include "CEffectTypeRTAS.h"
#include "CPluginControl.h"
#include "CPluginControl_OnOff.h"
//==============================================================================
#include "../../juce_AudioFilterBase.h"
#include "../../juce_AudioFilterEditor.h"
#include "../../juce_IncludeCharacteristics.h"
#undef Component
//==============================================================================
#if JUCE_WIN32
extern void JUCE_CALLTYPE attachSubWindow (void* hostWindow, int& titleW, int& titleH, juce::Component* comp);
extern void JUCE_CALLTYPE resizeHostWindow (void* hostWindow, int& titleW, int& titleH, juce::Component* comp);
#if ! JucePlugin_EditorRequiresKeyboardFocus
extern void JUCE_CALLTYPE passFocusToHostWindow (void* hostWindow);
#endif
#endif
const int midiBufferSize = 1024;
const OSType juceChunkType = 'juce';
static const int bypassControlIndex = 1;
//==============================================================================
static float longToFloat (const long n) throw()
{
return (float) ((((double) n) + (double) 0x80000000) / (double) 0xffffffff);
}
static long floatToLong (const float n) throw()
{
return roundDoubleToInt (jlimit (-(double) 0x80000000,
(double) 0x7fffffff,
n * (double) 0xffffffff - (double) 0x80000000));
}
//==============================================================================
class JucePlugInProcess : public CEffectProcessMIDI,
public CEffectProcessRTAS,
public AudioFilterBase::FilterNativeCallbacks,
public AsyncUpdater
{
public:
//==============================================================================
JucePlugInProcess()
: prepared (false),
midiBufferNode (0),
midiTransport (0),
sampleRate (44100.0)
{
juceFilter = createPluginFilter();
jassert (juceFilter != 0);
AddChunk (juceChunkType, "Juce Audio Plugin Data");
}
~JucePlugInProcess()
{
if (mLoggedIn)
MIDILogOut();
deleteAndZero (midiBufferNode);
deleteAndZero (midiTransport);
if (prepared)
juceFilter->releaseResources();
delete juceFilter;
}
//==============================================================================
class JuceCustomUIView : public CCustomView
{
public:
//==============================================================================
JuceCustomUIView (AudioFilterBase* const filter_)
: filter (filter_),
editorComp (0),
wrapper (0)
{
// setting the size in here crashes PT for some reason, so keep it simple..
}
~JuceCustomUIView()
{
deleteEditorComp();
}
//==============================================================================
void updateSize()
{
if (editorComp == 0)
{
editorComp = filter->createEditorIfNeeded();
jassert (editorComp != 0);
}
Rect r;
r.left = 0;
r.top = 0;
r.right = editorComp->getWidth();
r.bottom = editorComp->getHeight();
SetRect (&r);
}
void attachToWindow (GrafPtr port)
{
if (port != 0)
{
updateSize();
#if JUCE_WIN32
void* const hostWindow = (void*) ASI_GethWnd ((WindowPtr) port);
#else
void* const hostWindow = (void*) GetWindowFromPort (port);
#endif
deleteAndZero (wrapper);
wrapper = new EditorCompWrapper (hostWindow, editorComp, this);
}
else
{
deleteEditorComp();
}
}
void DrawContents (Rect* r)
{
if (wrapper != 0)
{
ComponentPeer* const peer = wrapper->getPeer();
if (peer != 0)
{
#if JUCE_WIN32
// (seems to be required in PT6.4, but not in 7.x)
peer->repaint (0, 0, wrapper->getWidth(), wrapper->getHeight());
#elif JUCE_PPC
// This crap is needed because if you resize a window, PT doesn't
// update its clip region, so only part of your new window gets drawn.
// This overrides the clipping region that's being passed into the Draw
// method.
Rect visible;
GetVisibleRect (&visible);
RestoreFocus();
Focus (&visible);
#endif
peer->performAnyPendingRepaintsNow();
}
}
}
void DrawBackground (Rect* r)
{
}
//==============================================================================
private:
AudioFilterBase* const filter;
juce::Component* wrapper;
AudioFilterEditor* editorComp;
void deleteEditorComp()
{
if (editorComp != 0)
{
PopupMenu::dismissAllActiveMenus();
juce::Component* const modalComponent = juce::Component::getCurrentlyModalComponent();
if (modalComponent != 0)
modalComponent->exitModalState (0);
filter->editorBeingDeleted (editorComp);
deleteAndZero (editorComp);
deleteAndZero (wrapper);
}
}
//==============================================================================
// A component to hold the AudioFilterEditor, and cope with some housekeeping
// chores when it changes or repaints.
class EditorCompWrapper : public juce::Component,
#if JUCE_MAC
public Timer
#else
public FocusChangeListener
#endif
{
public:
EditorCompWrapper (void* const hostWindow_,
AudioFilterEditor* const editorComp,
JuceCustomUIView* const owner_)
: hostWindow (hostWindow_),
owner (owner_),
titleW (0),
titleH (0)
#if JUCE_MAC
, forcedRepaintTimer (0)
#endif
{
#if ! JucePlugin_EditorRequiresKeyboardFocus
setWantsKeyboardFocus (false);
#endif
setOpaque (true);
setBroughtToFrontOnMouseClick (true);
setBounds (editorComp->getBounds());
editorComp->setTopLeftPosition (0, 0);
addAndMakeVisible (editorComp);
#if JUCE_WIN32
attachSubWindow (hostWindow, titleW, titleH, this);
setVisible (true);
#else
SetAutomaticControlDragTrackingEnabledForWindow ((WindowRef) hostWindow_, true);
WindowAttributes attributes;
GetWindowAttributes ((WindowRef) hostWindow_, &attributes);
parentView = 0;
if ((attributes & kWindowCompositingAttribute) != 0)
{
HIViewRef root = HIViewGetRoot ((WindowRef) hostWindow_);
HIViewFindByID (root, kHIViewWindowContentID, &parentView);
if (parentView == 0)
parentView = root;
}
else
{
GetRootControl ((WindowRef) hostWindow_, (ControlRef*) &parentView);
if (parentView == 0)
CreateRootControl ((WindowRef) hostWindow_, (ControlRef*) &parentView);
}
jassert (parentView != 0);
Rect clientRect;
GetWindowBounds ((WindowRef) hostWindow, kWindowContentRgn, &clientRect);
titleW = clientRect.right - clientRect.left;
titleH = jmax (0, (clientRect.bottom - clientRect.top) - getHeight());
setTopLeftPosition (0, 0);
HIViewSetNeedsDisplay (parentView, true);
setVisible (true);
addToDesktop (ComponentPeer::windowRepaintedExplictly, (void*) parentView);
HIViewRef pluginView = HIViewGetFirstSubview (parentView);
#if ! JucePlugin_EditorRequiresKeyboardFocus
HIViewSetActivated (pluginView, false);
#endif
/* This is a convoluted and desperate workaround for a Digi (or maybe Apple)
layout bug. Until the parent control gets some kind of mouse-move
event, then our plugin's HIView remains stuck at (0, 0) in the
window (despite drawing correctly), which blocks mouse events from
getting to the widgets above it.
After days of frustration the only hack I can find that works
is to use this arcane function to redirect mouse events to
the parent, while running a timer to spot the moment when our
view mysteriously snaps back to its correct location.
If anyone at Digi or Apple is reading this and they realise that it's
their fault, could they please give me back the week of my life that
they made me waste on this rubbish. Thankyou.
*/
SetControlSupervisor (pluginView, parentView);
startTimer (150);
#endif
#if JUCE_WIN32 && ! JucePlugin_EditorRequiresKeyboardFocus
Desktop::getInstance().addFocusChangeListener (this);
#endif
}
~EditorCompWrapper()
{
#if JUCE_WIN32 && ! JucePlugin_EditorRequiresKeyboardFocus
Desktop::getInstance().removeFocusChangeListener (this);
#endif
#if JUCE_MAC
delete forcedRepaintTimer;
#endif
}
void paint (Graphics& g)
{
#if JUCE_MAC
if (forcedRepaintTimer != 0)
forcedRepaintTimer->stopTimer();
#endif
}
void resized()
{
juce::Component* const c = getChildComponent (0);
if (c != 0)
c->setBounds (0, 0, getWidth(), getHeight());
repaint();
}
#if JUCE_MAC
void timerCallback()
{
// Wait for the moment when PT deigns to allow our view to
// take up its actual location (see rant above)
HIViewRef content = 0;
HIViewFindByID (HIViewGetRoot ((WindowRef) hostWindow), kHIViewWindowContentID, &content);
HIPoint p = { 0.0f, 0.0f };
HIViewRef v = HIViewGetFirstSubview (parentView);
HIViewConvertPoint (&p, v, content);
if (p.y > 12)
{
HIViewRef v = HIViewGetFirstSubview (parentView);
SetControlSupervisor (v, 0);
stopTimer();
forcedRepaintTimer = new RepaintCheckTimer (*this);
}
}
#endif
#if JUCE_WIN32
void globalFocusChanged (juce::Component*)
{
#if ! JucePlugin_EditorRequiresKeyboardFocus
if (hasKeyboardFocus (true))
passFocusToHostWindow (hostWindow);
#endif
}
#endif
void childBoundsChanged (juce::Component* child)
{
setSize (child->getWidth(), child->getHeight());
child->setTopLeftPosition (0, 0);
#if JUCE_WIN32
resizeHostWindow (hostWindow, titleW, titleH, this);
#else
Rect r;
GetWindowBounds ((WindowRef) hostWindow, kWindowContentRgn, &r);
HIRect p;
zerostruct (p);
HIViewConvertRect (&p, parentView, 0); // find the X position of our view in case there's space to the left of it
r.right = r.left + jmax (titleW, ((int) p.origin.x) + getWidth());
r.bottom = r.top + getHeight() + titleH;
SetWindowBounds ((WindowRef) hostWindow, kWindowContentRgn, &r);
owner->updateSize();
owner->Invalidate();
#endif
}
//==============================================================================
juce_UseDebuggingNewOperator
#if JUCE_MAC
protected:
void internalRepaint (int x, int y, int w, int h)
{
Component::internalRepaint (x, y, w, h);
owner->Invalidate();
if (forcedRepaintTimer != 0 && ! forcedRepaintTimer->isTimerRunning())
forcedRepaintTimer->startTimer (1000 / 25);
}
HIViewRef parentView;
#endif
private:
void* const hostWindow;
JuceCustomUIView* const owner;
int titleW, titleH;
#if JUCE_MAC
// if PT makes us wait too long for a redraw after we've
// asked for one, this should kick in and force one to happen
class RepaintCheckTimer : public Timer
{
public:
RepaintCheckTimer (EditorCompWrapper& owner_)
: owner (owner_)
{
}
void timerCallback()
{
stopTimer();
ComponentPeer* const peer = owner.getPeer();
if (peer != 0)
peer->performAnyPendingRepaintsNow();
}
EditorCompWrapper& owner;
};
RepaintCheckTimer* forcedRepaintTimer;
#endif
};
};
JuceCustomUIView* getView() const
{
return dynamic_cast <JuceCustomUIView*> (fOurPlugInView);
}
void GetViewRect (Rect* size)
{
if (getView() != 0)
getView()->updateSize();
CEffectProcessRTAS::GetViewRect (size);
}
CPlugInView* CreateCPlugInView()
{
return new JuceCustomUIView (juceFilter);
}
void SetViewPort (GrafPtr port)
{
CEffectProcessRTAS::SetViewPort (port);
if (getView() != 0)
getView()->attachToWindow (port);
}
//==============================================================================
protected:
ComponentResult GetDelaySamplesLong (long* aNumSamples)
{
if (aNumSamples != 0)
*aNumSamples = JucePlugin_Latency;
return noErr;
}
//==============================================================================
void EffectInit()
{
SFicPlugInStemFormats stems;
GetProcessType()->GetStemFormats (&stems);
juceFilter->numInputChannels = fNumInputs;
juceFilter->numOutputChannels = fNumOutputs;
AddControl (new CPluginControl_OnOff ('bypa', "Master Bypass\nMastrByp\nMByp\nByp", false, true));
DefineMasterBypassControlIndex (bypassControlIndex);
for (int i = 0; i < juceFilter->getNumParameters(); ++i)
AddControl (new JucePluginControl (juceFilter, i));
// we need to do this midi log-in to get timecode, regardless of whether
// the plugin actually uses midi...
if (MIDILogIn() == noErr)
{
#if JucePlugin_WantsMidiInput
CEffectType* const type = dynamic_cast <CEffectType*> (this->GetProcessType());
if (type != 0)
{
char nodeName [64];
type->GetProcessTypeName (63, nodeName);
p2cstrcpy (nodeName, reinterpret_cast <unsigned char*> (nodeName));
midiBufferNode = new CEffectMIDIOtherBufferedNode (&mMIDIWorld,
8192,
eLocalNode,
nodeName,
midiBuffer);
midiBufferNode->Initialize (1, true);
}
#endif
}
midiTransport = new CEffectMIDITransport (&mMIDIWorld);
juceFilter->initialiseInternal (this);
}
void handleAsyncUpdate()
{
if (! prepared)
{
sampleRate = gProcessGroup->GetSampleRate();
jassert (sampleRate > 0);
juceFilter->prepareToPlay (sampleRate,
mRTGlobals->mHWBufferSizeInSamples);
prepared = true;
}
}
void RenderAudio (float** inputs, float** outputs, long numSamples)
{
if (! prepared)
{
triggerAsyncUpdate();
bypassBuffers (inputs, outputs, numSamples);
return;
}
if (mBypassed)
{
bypassBuffers (inputs, outputs, numSamples);
return;
}
#if JucePlugin_WantsMidiInput
midiEvents.clear();
const Cmn_UInt32 bufferSize = mRTGlobals->mHWBufferSizeInSamples;
if (midiBufferNode->GetAdvanceScheduleTime() != bufferSize)
midiBufferNode->SetAdvanceScheduleTime (bufferSize);
if (midiBufferNode->FillMIDIBuffer (mRTGlobals->mRunningTime, numSamples) == noErr)
{
jassert (midiBufferNode->GetBufferPtr() != 0);
const int numMidiEvents = midiBufferNode->GetBufferSize();
for (int i = 0; i < numMidiEvents; ++i)
{
const DirectMidiPacket& m = midiBuffer[i];
jassert ((int) m.mTimestamp < numSamples);
midiEvents.addEvent (m.mData, m.mLength,
jlimit (0, (int) numSamples - 1, (int) m.mTimestamp));
}
}
#endif
#if defined (JUCE_DEBUG) || JUCE_LOG_ASSERTIONS
const int numMidiEventsComingIn = midiEvents.getNumEvents();
#endif
{
const AudioSampleBuffer input (inputs, juceFilter->numInputChannels, numSamples);
AudioSampleBuffer output (outputs, juceFilter->numOutputChannels, numSamples);
const ScopedLock sl (juceFilter->getCallbackLock());
if (juceFilter->suspended)
bypassBuffers (inputs, outputs, numSamples);
else
juceFilter->processBlock (input, output, false, midiEvents);
}
if (! midiEvents.isEmpty())
{
#if JucePlugin_ProducesMidiOutput
const uint8* midiEventData;
int midiEventSize, midiEventPosition;
MidiBuffer::Iterator i (midiEvents);
while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
{
// jassert (midiEventPosition >= 0 && midiEventPosition < (int) numSamples);
//xxx
}
#else
// if your plugin creates midi messages, you'll need to set
// the JucePlugin_ProducesMidiOutput macro to 1 in your
// JucePluginCharacteristics.h file
jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
#endif
midiEvents.clear();
}
}
//==============================================================================
ComponentResult GetChunkSize (OSType chunkID, long* size)
{
if (chunkID == juceChunkType)
{
tempFilterData.setSize (0);
juceFilter->getStateInformation (tempFilterData);
*size = sizeof (SFicPlugInChunkHeader) + tempFilterData.getSize();
return noErr;
}
return CEffectProcessMIDI::GetChunkSize (chunkID, size);
}
ComponentResult GetChunk (OSType chunkID, SFicPlugInChunk* chunk)
{
if (chunkID == juceChunkType)
{
if (tempFilterData.getSize() == 0)
juceFilter->getStateInformation (tempFilterData);
chunk->fSize = sizeof (SFicPlugInChunkHeader) + tempFilterData.getSize();
tempFilterData.copyTo ((void*) chunk->fData, 0, tempFilterData.getSize());
tempFilterData.setSize (0);
return noErr;
}
return CEffectProcessMIDI::GetChunk (chunkID, chunk);
}
ComponentResult SetChunk (OSType chunkID, SFicPlugInChunk* chunk)
{
if (chunkID == juceChunkType)
{
tempFilterData.setSize (0);
if (chunk->fSize - sizeof (SFicPlugInChunkHeader) > 0)
{
juceFilter->setStateInformation ((void*) chunk->fData,
chunk->fSize - sizeof (SFicPlugInChunkHeader));
}
return noErr;
}
return CEffectProcessMIDI::SetChunk (chunkID, chunk);
}
//==============================================================================
ComponentResult UpdateControlValue (long controlIndex, long value)
{
if (controlIndex != bypassControlIndex)
juceFilter->setParameter (controlIndex - 2, longToFloat (value));
else
mBypassed = (value > 0);
return CProcess::UpdateControlValue (controlIndex, value);
}
//==============================================================================
bool JUCE_CALLTYPE getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info)
{
// this method can only be called while the plugin is running
jassert (prepared);
Cmn_Float64 bpm = 120.0;
Cmn_Int32 num = 4, denom = 4;
Cmn_Int64 ticks = 0;
Cmn_Bool isPlaying = false;
if (midiTransport != 0)
{
midiTransport->GetCurrentTempo (&bpm);
midiTransport->IsTransportPlaying (&isPlaying);
midiTransport->GetCurrentMeter (&num, &denom);
midiTransport->GetCurrentTickPosition (&ticks);
}
info.bpm = bpm;
info.timeSigNumerator = num;
info.timeSigDenominator = denom;
info.isPlaying = isPlaying;
info.isRecording = false;
info.ppqPosition = ticks / 960000.0;
info.ppqPositionOfLastBarStart = 0; //xxx no idea how to get this correctly..
// xxx incorrect if there are tempo changes, but there's no
// other way of getting this info..
info.timeInSeconds = ticks * (60.0 / 960000.0) / bpm;
double framesPerSec = 24.0;
switch (fTimeCodeInfo.mFrameRate)
{
case ficFrameRate_24Frame:
info.frameRate = AudioFilterBase::CurrentPositionInfo::fps24;
break;
case ficFrameRate_25Frame:
info.frameRate = AudioFilterBase::CurrentPositionInfo::fps25;
framesPerSec = 25.0;
break;
case ficFrameRate_2997NonDrop:
info.frameRate = AudioFilterBase::CurrentPositionInfo::fps2997;
framesPerSec = 29.97002997;
break;
case ficFrameRate_2997DropFrame:
info.frameRate = AudioFilterBase::CurrentPositionInfo::fps2997drop;
framesPerSec = 29.97002997;
break;
case ficFrameRate_30NonDrop:
info.frameRate = AudioFilterBase::CurrentPositionInfo::fps30;
framesPerSec = 30.0;
break;
case ficFrameRate_30DropFrame:
info.frameRate = AudioFilterBase::CurrentPositionInfo::fps30drop;
framesPerSec = 30.0;
break;
case ficFrameRate_23976:
// xxx not strictly true..
info.frameRate = AudioFilterBase::CurrentPositionInfo::fps24;
framesPerSec = 23.976;
break;
default:
info.frameRate = AudioFilterBase::CurrentPositionInfo::fpsUnknown;
break;
}
info.editOriginTime = fTimeCodeInfo.mFrameOffset / framesPerSec;
return true;
}
void JUCE_CALLTYPE informHostOfParameterChange (int index, float newValue)
{
if (juceFilter != 0)
juceFilter->setParameter (index, newValue);
SetControlValue (index + 2, floatToLong (newValue));
}
//==============================================================================
private:
AudioFilterBase* juceFilter;
MidiBuffer midiEvents;
CEffectMIDIOtherBufferedNode* midiBufferNode;
CEffectMIDITransport* midiTransport;
DirectMidiPacket midiBuffer [midiBufferSize];
juce::MemoryBlock tempFilterData;
bool prepared;
double sampleRate;
void bypassBuffers (float** const inputs, float** const outputs, const long numSamples) const
{
for (int i = fNumOutputs; --i >= 0;)
{
if (i < fNumInputs)
memcpy (outputs[i], inputs[i], numSamples * sizeof (float));
else
zeromem (outputs[i], numSamples * sizeof (float));
}
}
//==============================================================================
class JucePluginControl : public CPluginControl
{
public:
//==============================================================================
JucePluginControl (AudioFilterBase* const juceFilter_, const int index_)
: juceFilter (juceFilter_),
index (index_)
{
}
~JucePluginControl()
{
}
//==============================================================================
OSType GetID() const { return index + 1; }
long GetDefaultValue() const { return floatToLong (0); }
void SetDefaultValue (long) {}
long GetNumSteps() const { return 0xffffffff; }
long ConvertStringToValue (const char* valueString) const
{
return floatToLong (String (valueString).getFloatValue());
}
Cmn_Bool IsKeyValid(long key) const { return true; }
void GetNameOfLength (char* name, int maxLength, OSType inControllerType) const
{
juceFilter->getParameterName (index).copyToBuffer (name, maxLength);
}
long GetPriority() const { return kFicCooperativeTaskPriority; }
long GetOrientation() const
{
return kDAE_LeftMinRightMax | kDAE_BottomMinTopMax
| kDAE_RotarySingleDotMode | kDAE_RotaryLeftMinRightMax;
}
long GetControlType() const { return kDAE_ContinuousValues; }
void GetValueString (char* valueString, int maxLength, long value) const
{
juceFilter->getParameterText (index).copyToBuffer (valueString, maxLength);
}
Cmn_Bool IsAutomatable() const { return true; }
private:
//==============================================================================
AudioFilterBase* const juceFilter;
const int index;
JucePluginControl (const JucePluginControl&);
const JucePluginControl& operator= (const JucePluginControl&);
};
};
//==============================================================================
class JucePlugInGroup : public CEffectGroupMIDI
{
public:
//==============================================================================
JucePlugInGroup()
{
DefineManufacturerNamesAndID (JucePlugin_Manufacturer, JucePlugin_RTASManufacturerCode);
DefinePlugInNamesAndVersion (createRTASName(), JucePlugin_VersionCode);
#ifndef JUCE_DEBUG
AddGestalt (pluginGestalt_IsCacheable);
#endif
}
~JucePlugInGroup()
{
shutdownJuce_GUI();
shutdownJuce_NonGUI();
}
//==============================================================================
void CreateEffectTypes()
{
const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
const int numConfigs = numElementsInArray (channelConfigs);
// You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
// value in your JucePluginCharacteristics.h file..
jassert (numConfigs > 0);
for (int i = 0; i < numConfigs; ++i)
{
CEffectType* const type
= new CEffectTypeRTAS ('jcaa' + i,
JucePlugin_RTASProductId,
JucePlugin_RTASCategory);
type->DefineTypeNames (createRTASName());
type->DefineSampleRateSupport (eSupports48kAnd96kAnd192k);
type->DefineStemFormats (getFormatForChans (channelConfigs [i][0]),
getFormatForChans (channelConfigs [i][1]));
type->AddGestalt (pluginGestalt_CanBypass);
type->AddGestalt (pluginGestalt_SupportsVariableQuanta);
type->AttachEffectProcessCreator (createNewProcess);
AddEffectType (type);
}
}
void Initialize()
{
CEffectGroupMIDI::Initialize();
}
//==============================================================================
private:
static CEffectProcess* createNewProcess()
{
// Juce setup
#if JUCE_WIN32
PlatformUtilities::setCurrentModuleInstanceHandle (gThisModule);
#endif
initialiseJuce_GUI();
return new JucePlugInProcess();
}
static const String createRTASName()
{
return String (JucePlugin_Name) + T("\n")
+ String (JucePlugin_Name).substring (0, 4);
}
static EPlugIn_StemFormat getFormatForChans (const int numChans) throw()
{
switch (numChans)
{
case 1:
return ePlugIn_StemFormat_Mono;
case 2:
return ePlugIn_StemFormat_Stereo;
case 3:
return ePlugIn_StemFormat_LCR;
case 4:
return ePlugIn_StemFormat_Quad;
case 5:
return ePlugIn_StemFormat_5dot0;
case 6:
return ePlugIn_StemFormat_5dot1;
case 7:
return ePlugIn_StemFormat_6dot1;
case 8:
return ePlugIn_StemFormat_7dot1;
default:
jassertfalse // hmm - not a valid number of chans for RTAS..
break;
}
return ePlugIn_StemFormat_Generic;
}
};
CProcessGroupInterface* CProcessGroup::CreateProcessGroup()
{
initialiseJuce_NonGUI();
return new JucePlugInGroup();
}