upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)

This commit is contained in:
Alex Birch
2019-06-22 20:41:38 +01:00
parent d22c2cd4fa
commit 9ee566b251
1140 changed files with 67534 additions and 105952 deletions

View File

@ -41,6 +41,9 @@
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wextra-semi"
#if __has_warning("-Wpragma-pack")
#pragma clang diagnostic ignored "-Wpragma-pack"
#endif
#endif
#ifdef _MSC_VER
@ -162,22 +165,53 @@ namespace AAXClasses
static AAXChannelStreamOrder aaxChannelOrder[] =
{
{ AAX_eStemFormat_Mono, { AudioChannelSet::centre, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_Stereo, { AudioChannelSet::left, AudioChannelSet::right, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_LCR, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_LCRS, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::centreSurround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_Quad, { AudioChannelSet::left, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_5_0, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_5_1, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_6_0, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::centreSurround, AudioChannelSet::rightSurround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_6_1, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::centreSurround, AudioChannelSet::rightSurround, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_0_SDDS, { AudioChannelSet::left, AudioChannelSet::leftCentre, AudioChannelSet::centre, AudioChannelSet::rightCentre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_0_DTS, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide, AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_1_SDDS, { AudioChannelSet::left, AudioChannelSet::leftCentre, AudioChannelSet::centre, AudioChannelSet::rightCentre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_1_DTS, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide, AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_0_2, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide, AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::topSideLeft, AudioChannelSet::topSideRight, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_1_2, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide, AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::LFE, AudioChannelSet::topSideLeft, AudioChannelSet::topSideRight } },
{ AAX_eStemFormat_None, { AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_Mono, { AudioChannelSet::centre, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown,
AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_Stereo, { AudioChannelSet::left, AudioChannelSet::right, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown,
AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_LCR, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::unknown, AudioChannelSet::unknown,
AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_LCRS, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::centreSurround, AudioChannelSet::unknown,
AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_Quad, { AudioChannelSet::left, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::unknown,
AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_5_0, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround,
AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_5_1, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround,
AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_6_0, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::centreSurround,
AudioChannelSet::rightSurround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_6_1, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::centreSurround,
AudioChannelSet::rightSurround, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_0_SDDS, { AudioChannelSet::left, AudioChannelSet::leftCentre, AudioChannelSet::centre, AudioChannelSet::rightCentre, AudioChannelSet::right,
AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_0_DTS, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide,
AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_1_SDDS, { AudioChannelSet::left, AudioChannelSet::leftCentre, AudioChannelSet::centre, AudioChannelSet::rightCentre, AudioChannelSet::right,
AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_1_DTS, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide,
AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_0_2, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide,
AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::topSideLeft, AudioChannelSet::topSideRight, AudioChannelSet::unknown } },
{ AAX_eStemFormat_7_1_2, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide,
AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::LFE, AudioChannelSet::topSideLeft, AudioChannelSet::topSideRight } },
{ AAX_eStemFormat_None, { AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown,
AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
};
static AAX_EStemFormat aaxFormats[] =
@ -624,6 +658,8 @@ namespace AAXClasses
static void AAX_CALLBACK algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[], const void* const instancesEnd);
static Array<JuceAAX_Processor*> activeProcessors;
//==============================================================================
class JuceAAX_Processor : public AAX_CEffectParameters,
public juce::AudioPlayHead,
@ -642,11 +678,30 @@ namespace AAXClasses
rebuildChannelMapArrays();
AAX_CEffectParameters::GetNumberOfChunks (&juceChunkIndex);
activeProcessors.add (this);
}
~JuceAAX_Processor()
{
activeProcessors.removeAllInstancesOf (this);
}
static AAX_CEffectParameters* AAX_CALLBACK Create()
{
PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_AAX;
if (PluginHostType::jucePlugInIsRunningInAudioSuiteFn == nullptr)
{
PluginHostType::jucePlugInIsRunningInAudioSuiteFn = [] (AudioProcessor& processor)
{
for (auto* p : activeProcessors)
if (&p->getPluginInstance() == &processor)
return p->isInAudioSuite();
return false;
};
}
return new JuceAAX_Processor();
}
@ -849,7 +904,7 @@ namespace AAXClasses
AAX_Result GetParameterNumberofSteps (AAX_CParamID paramID, int32_t* result) const
{
if (auto* param = getParameterFromID (paramID))
*result = param->getNumSteps();
*result = getSafeNumberOfParameterSteps (*param);
return AAX_SUCCESS;
}
@ -1091,7 +1146,9 @@ namespace AAXClasses
}
}
else
{
isSuspended = true;
}
if (isSuspended)
{
@ -1311,6 +1368,14 @@ namespace AAXClasses
return foundValid;
}
bool isInAudioSuite()
{
AAX_CBoolean res;
Controller()->GetIsAudioSuite (&res);
return res;
}
private:
friend class JuceAAX_GUI;
friend void AAX_CALLBACK AAXClasses::algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[], const void* const instancesEnd);
@ -1405,6 +1470,14 @@ namespace AAXClasses
return false;
}
// Some older Pro Tools control surfaces (EUCON [PT version 12.4] and
// Avid S6 before version 2.1) cannot cope with a large number of
// parameter steps.
static int32_t getSafeNumberOfParameterSteps (const AudioProcessorParameter& param)
{
return jmin (param.getNumSteps(), 2048);
}
void addAudioProcessorParameters()
{
auto& audioProcessor = getPluginInstance();
@ -1446,7 +1519,6 @@ namespace AAXClasses
aaxParamIDs.add (paramID);
auto aaxParamID = aaxParamIDs.getReference (parameterIndex++).getCharPointer();
paramMap.set (AAXClasses::getAAXParamHash (aaxParamID), juceParam);
// is this a meter?
@ -1465,7 +1537,7 @@ namespace AAXClasses
parameter->AddShortenedName (juceParam->getName (4).toRawUTF8());
auto parameterNumSteps = juceParam->getNumSteps();
auto parameterNumSteps = getSafeNumberOfParameterSteps (*juceParam);
parameter->SetNumberOfSteps ((uint32_t) parameterNumSteps);
#if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
@ -1492,10 +1564,6 @@ namespace AAXClasses
bool getMainBusFormats (AudioChannelSet& inputSet, AudioChannelSet& outputSet)
{
auto& audioProcessor = getPluginInstance();
#if ! JucePlugin_IsMidiEffect
auto inputBuses = audioProcessor.getBusCount (true);
auto outputBuses = audioProcessor.getBusCount (false);
#endif
#if JucePlugin_IsMidiEffect
// MIDI effect plug-ins do not support any audio channels
@ -1505,24 +1573,25 @@ namespace AAXClasses
inputSet = outputSet = AudioChannelSet();
return true;
#else
auto inputBuses = audioProcessor.getBusCount (true);
auto outputBuses = audioProcessor.getBusCount (false);
AAX_EStemFormat inputStemFormat = AAX_eStemFormat_None;
check (Controller()->GetInputStemFormat (&inputStemFormat));
AAX_EStemFormat outputStemFormat = AAX_eStemFormat_None;
check (Controller()->GetOutputStemFormat (&outputStemFormat));
#if JucePlugin_IsSynth
if (inputBuses == 0)
inputStemFormat = AAX_eStemFormat_None;
#endif
#if JucePlugin_IsSynth
if (inputBuses == 0)
inputStemFormat = AAX_eStemFormat_None;
#endif
inputSet = (inputBuses > 0 ? channelSetFromStemFormat (inputStemFormat, false) : AudioChannelSet());
outputSet = (outputBuses > 0 ? channelSetFromStemFormat (outputStemFormat, false) : AudioChannelSet());
if ( (inputSet == AudioChannelSet::disabled() && inputStemFormat != AAX_eStemFormat_None)
|| (outputSet == AudioChannelSet::disabled() && outputStemFormat != AAX_eStemFormat_None)
|| (inputSet != AudioChannelSet::disabled() && inputBuses == 0)
|| (outputSet != AudioChannelSet::disabled() && outputBuses == 0))
if ((inputSet == AudioChannelSet::disabled() && inputStemFormat != AAX_eStemFormat_None) || (outputSet == AudioChannelSet::disabled() && outputStemFormat != AAX_eStemFormat_None)
|| (inputSet != AudioChannelSet::disabled() && inputBuses == 0) || (outputSet != AudioChannelSet::disabled() && outputBuses == 0))
return false;
return true;
@ -1577,6 +1646,27 @@ namespace AAXClasses
}
}
if (isInAudioSuite())
{
// AudioSuite doesnt support multiple output buses
for (int i = 1; i < newLayout.outputBuses.size(); ++i)
newLayout.outputBuses.getReference (i) = AudioChannelSet::disabled();
if (! audioProcessor.checkBusesLayoutSupported (newLayout))
{
// your plug-in needs to support a single output bus if running in AudioSuite
jassertfalse;
if (isPrepared)
{
isPrepared = false;
audioProcessor.releaseResources();
}
return AAX_ERROR_UNIMPLEMENTED;
}
}
const bool layoutChanged = (oldLayout != newLayout);
if (layoutChanged)
@ -1607,6 +1697,9 @@ namespace AAXClasses
audioProcessor.prepareToPlay (sampleRate, lastBufferSize);
maxBufferSize = lastBufferSize;
midiBuffer.ensureSize (2048);
midiBuffer.clear();
sideChainBuffer.calloc (static_cast<size_t> (maxBufferSize));
}
@ -1704,6 +1797,103 @@ namespace AAXClasses
updateSidechainState();
}
//==============================================================================
static AudioProcessor::CurveData::Type aaxCurveTypeToJUCE (AAX_CTypeID type) noexcept
{
switch (type)
{
case AAX_eCurveType_EQ: return AudioProcessor::CurveData::Type::EQ;
case AAX_eCurveType_Dynamics: return AudioProcessor::CurveData::Type::Dynamics;
case AAX_eCurveType_Reduction: return AudioProcessor::CurveData::Type::GainReduction;
default: break;
}
return AudioProcessor::CurveData::Type::Unknown;
}
uint32_t getAAXMeterIdForParamId (const String& paramID) const noexcept
{
int idx;
for (idx = 0; idx < aaxMeters.size(); ++idx)
if (LegacyAudioParameter::getParamID (aaxMeters[idx], false) == paramID)
break;
// you sepecified a parameter id in your curve but the parameter does not have the meter
// category
jassert (idx < aaxMeters.size());
return 'Metr' + static_cast<AAX_CTypeID> (idx);
}
//==============================================================================
AAX_Result GetCurveData (AAX_CTypeID iCurveType, const float * iValues, uint32_t iNumValues, float * oValues ) const override
{
auto curveType = aaxCurveTypeToJUCE (iCurveType);
if (curveType != AudioProcessor::CurveData::Type::Unknown)
{
auto& audioProcessor = getPluginInstance();
auto curve = audioProcessor.getResponseCurve (curveType);
if (curve.curve)
{
if (oValues != nullptr && iValues != nullptr)
{
for (uint32_t i = 0; i < iNumValues; ++i)
oValues[i] = curve.curve (iValues[i]);
}
return AAX_SUCCESS;
}
}
return AAX_ERROR_UNIMPLEMENTED;
}
AAX_Result GetCurveDataMeterIds (AAX_CTypeID iCurveType, uint32_t *oXMeterId, uint32_t *oYMeterId) const override
{
auto curveType = aaxCurveTypeToJUCE (iCurveType);
if (curveType != AudioProcessor::CurveData::Type::Unknown)
{
auto& audioProcessor = getPluginInstance();
auto curve = audioProcessor.getResponseCurve (curveType);
if (curve.curve && curve.xMeterID.isNotEmpty() && curve.yMeterID.isNotEmpty())
{
if (oXMeterId != nullptr) *oXMeterId = getAAXMeterIdForParamId (curve.xMeterID);
if (oYMeterId != nullptr) *oYMeterId = getAAXMeterIdForParamId (curve.yMeterID);
return AAX_SUCCESS;
}
}
return AAX_ERROR_UNIMPLEMENTED;
}
AAX_Result GetCurveDataDisplayRange (AAX_CTypeID iCurveType, float *oXMin, float *oXMax, float *oYMin, float *oYMax) const override
{
auto curveType = aaxCurveTypeToJUCE (iCurveType);
if (curveType != AudioProcessor::CurveData::Type::Unknown)
{
auto& audioProcessor = getPluginInstance();
auto curve = audioProcessor.getResponseCurve (curveType);
if (curve.curve)
{
if (oXMin != nullptr) *oXMin = curve.xRange.getStart();
if (oXMax != nullptr) *oXMax = curve.xRange.getEnd();
if (oYMin != nullptr) *oYMin = curve.yRange.getStart();
if (oYMax != nullptr) *oYMax = curve.yRange.getEnd();
return AAX_SUCCESS;
}
}
return AAX_ERROR_UNIMPLEMENTED;
}
//==============================================================================
inline int getParamIndexFromID (AAX_CParamID paramID) const noexcept
{
@ -1792,7 +1982,7 @@ namespace AAXClasses
// and the size of the data returned. To avoid generating
// it again in GetChunk, we need to store it somewhere.
// However, as GetChunkSize and GetChunk can be called
// on different threads, we store it in thread dependant storage
// on different threads, we store it in thread dependent storage
// in a hash map with the thread id as a key.
mutable ThreadLocalValue<ChunkMemoryBlock> perThreadFilterData;
CriticalSection perThreadDataLock;
@ -1997,11 +2187,17 @@ namespace AAXClasses
check (desc.AddSideChainIn (JUCEAlgorithmIDs::sideChainBuffers));
properties->AddProperty (AAX_eProperty_SupportsSideChainInput, true);
}
else
{
// AAX does not allow there to be any gaps in the fields of the algorithm context structure
// so just add a dummy one here if there aren't any side chains
check (desc.AddPrivateData (JUCEAlgorithmIDs::sideChainBuffers, sizeof (uintptr_t)));
}
auto maxAuxBuses = jmax (0, jmin (15, fullLayout.outputBuses.size() - 1));
// add the output buses
// This is incrdibly dumb: the output bus format must be well defined
// This is incredibly dumb: the output bus format must be well defined
// for every main bus in/out format pair. This means that there cannot
// be two configurations with different aux formats but
// identical main bus in/out formats.

View File

@ -48,7 +48,7 @@
#include <CoreMIDI/CoreMIDI.h>
#include "CAXException.h"
//temporaray location
//temporary location
enum
{
kMidiMessage_NoteOff = 0x80,

View File

@ -130,7 +130,6 @@ public:
MusicDeviceBase (component,
(UInt32) AudioUnitHelpers::getBusCount (juceFilter.get(), true),
(UInt32) AudioUnitHelpers::getBusCount (juceFilter.get(), false)),
isBypassed (false),
mapper (*juceFilter)
{
inParameterChangedCallback = false;
@ -474,6 +473,31 @@ public:
{
switch (inID)
{
case kAudioUnitProperty_ParameterClumpName:
if (auto* clumpNameInfo = (AudioUnitParameterNameInfo*) outData)
{
if (juceFilter != nullptr)
{
auto clumpIndex = clumpNameInfo->inID - 1;
const auto* group = parameterGroups[(int) clumpIndex];
auto name = group->getName();
while (group->getParent() != &juceFilter->getParameterTree())
{
group = group->getParent();
name = group->getName() + group->getSeparator() + name;
}
clumpNameInfo->outName = name.toCFString();
return noErr;
}
}
// Failed to find a group corresponding to the clump ID.
jassertfalse;
break;
case juceFilterObjectPropertyID:
((void**) outData)[0] = (void*) static_cast<AudioProcessor*> (juceFilter.get());
((void**) outData)[1] = (void*) this;
@ -879,6 +903,14 @@ public:
if (param->isMetaParameter())
outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;
auto parameterGroupHierarchy = juceFilter->getParameterTree().getGroupsForParameter (param);
if (! parameterGroupHierarchy.isEmpty())
{
outParameterInfo.flags |= kAudioUnitParameterFlag_HasClump;
outParameterInfo.clumpID = (UInt32) parameterGroups.indexOf (parameterGroupHierarchy.getLast()) + 1;
}
// Is this a meter?
if (((param->getCategory() & 0xffff0000) >> 16) == 2)
{
@ -1429,7 +1461,6 @@ public:
public:
EditorCompHolder (AudioProcessorEditor* const editor)
{
setSize (editor->getWidth(), editor->getHeight());
addAndMakeVisible (editor);
#if ! JucePlugin_EditorRequiresKeyboardFocus
@ -1439,6 +1470,7 @@ public:
#endif
ignoreUnused (fakeMouseGenerator);
setBounds (getSizeToContainChild());
}
~EditorCompHolder()
@ -1447,13 +1479,21 @@ public:
// have been transferred to another parent which takes over ownership.
}
Rectangle<int> getSizeToContainChild()
{
if (auto* editor = getChildComponent (0))
return getLocalArea (editor, editor->getLocalBounds());
return {};
}
static NSView* createViewFor (AudioProcessor* filter, JuceAU* au, AudioProcessorEditor* const editor)
{
EditorCompHolder* editorCompHolder = new EditorCompHolder (editor);
NSRect r = makeNSRect (editorCompHolder->getLocalBounds());
auto* editorCompHolder = new EditorCompHolder (editor);
auto r = makeNSRect (editorCompHolder->getSizeToContainChild());
static JuceUIViewClass cls;
NSView* view = [[cls.createInstance() initWithFrame: r] autorelease];
auto* view = [[cls.createInstance() initWithFrame: r] autorelease];
JuceUIViewClass::setFilter (view, filter);
JuceUIViewClass::setAU (view, au);
@ -1470,29 +1510,33 @@ public:
editorCompHolder->addToDesktop (0, (void*) view);
editorCompHolder->setVisible (view);
return view;
}
void childBoundsChanged (Component*) override
{
if (Component* editor = getChildComponent(0))
auto b = getSizeToContainChild();
if (lastBounds != b)
{
const int w = jmax (32, editor->getWidth());
const int h = jmax (32, editor->getHeight());
lastBounds = b;
if (getWidth() != w || getHeight() != h)
setSize (w, h);
auto w = jmax (32, b.getWidth());
auto h = jmax (32, b.getHeight());
NSView* view = (NSView*) getWindowHandle();
NSRect r = [[view superview] frame];
r.size.width = editor->getWidth();
r.size.height = editor->getHeight();
setSize (w, h);
auto* view = (NSView*) getWindowHandle();
auto r = [[view superview] frame];
r.size.width = w;
r.size.height = h;
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
[CATransaction setValue:(id) kCFBooleanTrue forKey:kCATransactionDisableActions];
[[view superview] setFrame: r];
[view setFrame: makeNSRect (editor->getLocalBounds())];
[view setFrame: makeNSRect (b)];
[CATransaction commit];
[view setNeedsDisplay: YES];
@ -1525,6 +1569,7 @@ public:
private:
FakeMouseMoveGenerator fakeMouseGenerator;
Rectangle<int> lastBounds;
JUCE_DECLARE_NON_COPYABLE (EditorCompHolder)
};
@ -1674,7 +1719,7 @@ private:
//==============================================================================
AudioUnitHelpers::CoreAudioBufferList audioBuffer;
MidiBuffer midiEvents, incomingEvents;
bool prepared, isBypassed;
bool prepared = false, isBypassed = false;
//==============================================================================
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS
@ -1687,6 +1732,7 @@ private:
LegacyAudioParametersWrapper juceParameters;
HashMap<int32, AudioProcessorParameter*> paramMap;
Array<AudioUnitParameterID> auParamIDs;
Array<const AudioProcessorParameterGroup*> parameterGroups;
//==============================================================================
AudioUnitEvent auEvent;
@ -1839,6 +1885,8 @@ private:
//==============================================================================
void addParameters()
{
parameterGroups = juceFilter->getParameterTree().getSubgroups (true);
juceParameters.update (*juceFilter, forceUseLegacyParamIDs);
const int numParams = juceParameters.getNumParameters();
@ -1877,6 +1925,7 @@ private:
OwnedArray<const __CFString>* stringValues = nullptr;
auto initialValue = param->getValue();
bool paramIsLegacy = dynamic_cast<LegacyAudioParameter*> (param) != nullptr;
if (param->isDiscrete() && (! forceUseLegacyParamIDs))
{
@ -1886,17 +1935,26 @@ private:
const auto maxValue = getMaximumParameterValue (param);
auto getTextValue = [param, paramIsLegacy] (float value)
{
if (paramIsLegacy)
{
param->setValue (value);
return param->getCurrentValueAsText();
}
return param->getText (value, 256);
};
for (int i = 0; i < numSteps; ++i)
{
auto value = (float) i / maxValue;
// Once legacy parameters are deprecated this can be replaced by getText
param->setValue (value);
stringValues->add (CFStringCreateCopy (nullptr, (param->getCurrentValueAsText().toCFString())));
stringValues->add (CFStringCreateCopy (nullptr, (getTextValue (value).toCFString())));
}
}
param->setValue (initialValue);
if (paramIsLegacy)
param->setValue (initialValue);
parameterValueStringArrays.add (stringValues);
}
@ -1928,7 +1986,9 @@ private:
AudioProcessorParameter* getParameterForAUParameterID (AudioUnitParameterID address) const noexcept
{
return paramMap[static_cast<int32> (address)];
auto index = static_cast<int32> (address);
return forceUseLegacyParamIDs ? juceParameters.getParamForIndex (index)
: paramMap[index];
}
//==============================================================================

View File

@ -847,6 +847,9 @@ public:
processor.setRateAndBufferSizeDetails (sampleRate, static_cast<int> (maxFrames));
processor.prepareToPlay (sampleRate, static_cast<int> (maxFrames));
midiMessages.ensureSize (2048);
midiMessages.clear();
zeromem (&lastAudioHead, sizeof (lastAudioHead));
hostMusicalContextCallback = [getAudioUnit() musicalContextBlock];
hostTransportStateCallback = [getAudioUnit() transportStateBlock];
@ -887,7 +890,7 @@ public:
for (auto i = 0u; i < n; ++i)
{
if (auto* viewConfiguration = [configs objectAtIndex:i])
if (auto viewConfiguration = [configs objectAtIndex: i])
{
if (editor->supportsHostMIDIControllerPresence ([viewConfiguration hostHasController] == YES))
{
@ -930,10 +933,12 @@ public:
return;
}
if (isPositiveAndBelow (idx, juceParameters.getNumParameters()))
if (auto* juceParam = juceParameters.getParamForIndex (idx))
{
if (AUParameter* param = [paramTree.get() parameterWithAddress: getAUParameterAddressForIndex (idx)])
{
newValue *= getMaximumParameterValue (juceParam);
if (editorObserverToken != nullptr)
[param setValue: newValue originator: editorObserverToken];
else
@ -1152,107 +1157,175 @@ private:
#endif
}
std::unique_ptr<AUParameter, NSObjectDeleter> createParameter (AudioProcessorParameter* parameter)
{
const String name (parameter->getName (512));
AudioUnitParameterUnit unit = kAudioUnitParameterUnit_Generic;
AudioUnitParameterOptions flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
| kAudioUnitParameterFlag_IsReadable
| kAudioUnitParameterFlag_HasCFNameString
| kAudioUnitParameterFlag_ValuesHaveStrings);
if (! forceLegacyParamIDs)
flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution;
// Set whether the param is automatable (unnamed parameters aren't allowed to be automated).
if (name.isEmpty() || ! parameter->isAutomatable())
flags |= kAudioUnitParameterFlag_NonRealTime;
const bool isParameterDiscrete = parameter->isDiscrete();
if (! isParameterDiscrete)
flags |= kAudioUnitParameterFlag_CanRamp;
if (parameter->isMetaParameter())
flags |= kAudioUnitParameterFlag_IsGlobalMeta;
std::unique_ptr<NSMutableArray, NSObjectDeleter> valueStrings;
// Is this a meter?
if (((parameter->getCategory() & 0xffff0000) >> 16) == 2)
{
flags &= ~kAudioUnitParameterFlag_IsWritable;
flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
unit = kAudioUnitParameterUnit_LinearGain;
}
else
{
if (! forceLegacyParamIDs)
{
if (parameter->isDiscrete())
{
unit = parameter->isBoolean() ? kAudioUnitParameterUnit_Boolean : kAudioUnitParameterUnit_Indexed;
auto maxValue = getMaximumParameterValue (parameter);
auto numSteps = parameter->getNumSteps();
// Some hosts can't handle the huge numbers of discrete parameter values created when
// using the default number of steps.
jassert (numSteps != AudioProcessor::getDefaultNumParameterSteps());
valueStrings.reset ([NSMutableArray new]);
for (int i = 0; i < numSteps; ++i)
[valueStrings.get() addObject: juceStringToNS (parameter->getText ((float) i / maxValue, 0))];
}
}
}
AUParameterAddress address = generateAUParameterAddress (parameter);
#if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS
// If you hit this assertion then you have either put a parameter in two groups or you are
// very unlucky and the hash codes of your parameter ids are not unique.
jassert (! paramMap.contains (static_cast<int64> (address)));
paramAddresses.add (address);
paramMap.set (static_cast<int64> (address), parameter->getParameterIndex());
#endif
auto getParameterIdentifier = [parameter]
{
if (auto* paramWithID = dynamic_cast<AudioProcessorParameterWithID*> (parameter))
return paramWithID->paramID;
// This could clash if any groups have been given integer IDs!
return String (parameter->getParameterIndex());
};
std::unique_ptr<AUParameter, NSObjectDeleter> param;
@try
{
// Create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
param.reset([[AUParameterTree createParameterWithIdentifier: juceStringToNS (getParameterIdentifier())
name: juceStringToNS (name)
address: address
min: 0.0f
max: getMaximumParameterValue (parameter)
unit: unit
unitName: nullptr
flags: flags
valueStrings: valueStrings.get()
dependentParameters: nullptr]
retain]);
}
@catch (NSException* exception)
{
// Do you have duplicate identifiers in any of your groups or parameters,
// or do your identifiers have unusual characters in them?
jassertfalse;
}
[param.get() setValue: parameter->getDefaultValue()];
[overviewParams.get() addObject: [NSNumber numberWithUnsignedLongLong: address]];
return param;
}
std::unique_ptr<AUParameterGroup, NSObjectDeleter> createParameterGroup (AudioProcessorParameterGroup* group)
{
std::unique_ptr<NSMutableArray<AUParameterNode*>, NSObjectDeleter> children ([NSMutableArray<AUParameterNode*> new]);
for (auto* node : *group)
{
if (auto* childGroup = node->getGroup())
[children.get() addObject: createParameterGroup (childGroup).get()];
else
[children.get() addObject: createParameter (node->getParameter()).get()];
}
std::unique_ptr<AUParameterGroup, NSObjectDeleter> result;
@try
{
// Create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
result.reset ([[AUParameterTree createGroupWithIdentifier: juceStringToNS (group->getID())
name: juceStringToNS (group->getName())
children: children.get()]
retain]);
}
@catch (NSException* exception)
{
// Do you have duplicate identifiers in any of your groups or parameters,
// or do your identifiers have unusual characters in them?
jassertfalse;
}
return result;
}
void addParameters()
{
std::unique_ptr<NSMutableArray<AUParameterNode*>, NSObjectDeleter> params ([[NSMutableArray<AUParameterNode*> alloc] init]);
overviewParams.reset ([[NSMutableArray<NSNumber*> alloc] init]);
auto& processor = getAudioProcessor();
juceParameters.update (processor, forceLegacyParamIDs);
const int n = juceParameters.getNumParameters();
// This is updated when we build the tree.
overviewParams.reset ([NSMutableArray<NSNumber*> new]);
for (int idx = 0; idx < n; ++idx)
{
auto* juceParam = juceParameters.getParamForIndex (idx);
std::unique_ptr<NSMutableArray<AUParameterNode*>, NSObjectDeleter> topLevelNodes ([NSMutableArray<AUParameterNode*> new]);
const String identifier (idx);
const String name = juceParam->getName (512);
AudioUnitParameterUnit unit = kAudioUnitParameterUnit_Generic;
AudioUnitParameterOptions flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
| kAudioUnitParameterFlag_IsReadable
| kAudioUnitParameterFlag_HasCFNameString
| kAudioUnitParameterFlag_ValuesHaveStrings);
if (! forceLegacyParamIDs)
flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution;
// set whether the param is automatable (unnamed parameters aren't allowed to be automated)
if (name.isEmpty() || ! juceParam->isAutomatable())
flags |= kAudioUnitParameterFlag_NonRealTime;
const bool isParameterDiscrete = juceParam->isDiscrete();
if (! isParameterDiscrete)
flags |= kAudioUnitParameterFlag_CanRamp;
if (juceParam->isMetaParameter())
flags |= kAudioUnitParameterFlag_IsGlobalMeta;
std::unique_ptr<NSMutableArray, NSObjectDeleter> valueStrings;
// is this a meter?
if (((juceParam->getCategory() & 0xffff0000) >> 16) == 2)
{
flags &= ~kAudioUnitParameterFlag_IsWritable;
flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
unit = kAudioUnitParameterUnit_LinearGain;
}
for (auto* node : processor.getParameterTree())
if (auto* childGroup = node->getGroup())
[topLevelNodes.get() addObject: createParameterGroup (childGroup).get()];
else
{
if (! forceLegacyParamIDs)
{
if (juceParam->isDiscrete())
{
unit = juceParam->isBoolean() ? kAudioUnitParameterUnit_Boolean : kAudioUnitParameterUnit_Indexed;
auto maxValue = getMaximumParameterValue (juceParam);
auto numSteps = juceParam->getNumSteps();
[topLevelNodes.get() addObject: createParameter (node->getParameter()).get()];
// Some hosts can't handle the huge numbers of discrete parameter values created when
// using the default number of steps.
jassert (numSteps != AudioProcessor::getDefaultNumParameterSteps());
valueStrings.reset ([NSMutableArray new]);
for (int i = 0; i < numSteps; ++i)
[valueStrings.get() addObject: juceStringToNS (juceParam->getText ((float) i / maxValue, 0))];
}
}
}
AUParameterAddress address = generateAUParameterAddress (juceParam);
#if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
// Consider yourself very unlucky if you hit this assertion. The hash codes of your
// parameter ids are not unique.
jassert (! paramMap.contains (static_cast<int64> (address)));
paramAddresses.add (address);
paramMap.set (static_cast<int64> (address), idx);
#endif
// create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
std::unique_ptr<AUParameter, NSObjectDeleter> param ([[AUParameterTree createParameterWithIdentifier: juceStringToNS (identifier)
name: juceStringToNS (name)
address: address
min: 0.0f
max: getMaximumParameterValue (juceParam)
unit: unit
unitName: nullptr
flags: flags
valueStrings: valueStrings.get()
dependentParameters: nullptr] retain]);
[param.get() setValue: juceParam->getDefaultValue()];
[params.get() addObject: param.get()];
[overviewParams.get() addObject: [NSNumber numberWithUnsignedLongLong:address]];
@try
{
// Create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
paramTree.reset ([[AUParameterTree createTreeWithChildren: topLevelNodes.get()] retain]);
}
// create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
paramTree.reset ([[AUParameterTree createTreeWithChildren: params.get()] retain]);
@catch (NSException* exception)
{
// Do you have duplicate identifiers in any of your groups or parameters,
// or do your identifiers have unusual characters in them?
jassertfalse;
}
paramObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedFromHost);
paramProvider = CreateObjCBlock (this, &JuceAudioUnitv3::getValue);
@ -1458,11 +1531,11 @@ private:
#endif
midiMessages.clear();
}
// copy back
audioBuffer.pop (*outBusBuffers[(int) outputBusNumber]->get(),
mapper.get (false, (int) outputBusNumber));
// copy back
audioBuffer.pop (*outBusBuffers[(int) outputBusNumber]->get(),
mapper.get (false, (int) outputBusNumber));
}
return noErr;
}
@ -1577,13 +1650,8 @@ private:
{
const String& juceParamID = LegacyAudioParameter::getParamID (param, forceLegacyParamIDs);
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS
auto result = juceParamID.getIntValue();
#else
auto result = juceParamID.hashCode64();
#endif
return static_cast<AUParameterAddress> (result);
return static_cast<AUParameterAddress> (forceLegacyParamIDs ? juceParamID.getIntValue()
: juceParamID.hashCode64());
}
AudioProcessorParameter* getJuceParameterForAUAddress (AUParameterAddress address) const noexcept
@ -1654,6 +1722,17 @@ JuceAudioUnitv3Base* JuceAudioUnitv3Base::create (AUAudioUnit* audioUnit, AudioC
return new JuceAudioUnitv3 (audioUnit, descr, options, error);
}
#if JUCE_IOS
namespace juce
{
struct UIViewPeerControllerReceiver
{
virtual ~UIViewPeerControllerReceiver();
virtual void setViewController (UIViewController*) = 0;
};
}
#endif
//==============================================================================
class JuceAUViewController
{
@ -1661,15 +1740,13 @@ public:
JuceAUViewController (AUViewController<AUAudioUnitFactory>* p)
: myself (p)
{
jassert (MessageManager::getInstance()->isThisTheMessageThread());
PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_AudioUnitv3;
initialiseJuce_GUI();
}
~JuceAUViewController()
{
jassert (MessageManager::getInstance()->isThisTheMessageThread());
JUCE_ASSERT_MESSAGE_THREAD
if (processorHolder != nullptr)
JuceAudioUnitv3::removeEditor (getAudioProcessor());
@ -1678,7 +1755,7 @@ public:
//==============================================================================
void loadView()
{
jassert (MessageManager::getInstance()->isThisTheMessageThread());
JUCE_ASSERT_MESSAGE_THREAD
if (AudioProcessor* p = createPluginFilterOfType (AudioProcessor::wrapperType_AudioUnitv3))
{
@ -1705,6 +1782,9 @@ public:
#if JUCE_IOS
if (JUCE_IOS_MAC_VIEW* peerView = [[[myself view] subviews] objectAtIndex: 0])
[peerView setContentMode: UIViewContentModeTop];
if (auto* peer = dynamic_cast<UIViewPeerControllerReceiver*> (editor->getPeer()))
peer->setViewController (myself);
#endif
}
}
@ -1812,7 +1892,7 @@ private:
//==============================================================================
AUAudioUnit* createAudioUnitOnMessageThread (const AudioComponentDescription& descr, NSError** error)
{
jassert (MessageManager::getInstance()->isThisTheMessageThread());
JUCE_ASSERT_MESSAGE_THREAD
[myself view]; // this will call [view load] and ensure that the AudioProcessor has been instantiated

View File

@ -117,6 +117,9 @@ public:
//==============================================================================
void systemRequestedQuit() override
{
if (mainWindow.get() != nullptr)
mainWindow->pluginHolder->savePluginState();
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
{
Timer::callAfterDelay (100, []()

View File

@ -89,12 +89,14 @@ public:
if (preferredSetupOptions != nullptr)
options.reset (new AudioDeviceManager::AudioDeviceSetup (*preferredSetupOptions));
if (inChannels > 0 && RuntimePermissions::isRequired (RuntimePermissions::recordAudio)
auto audioInputRequired = (inChannels > 0);
if (audioInputRequired && RuntimePermissions::isRequired (RuntimePermissions::recordAudio)
&& ! RuntimePermissions::isGranted (RuntimePermissions::recordAudio))
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this, preferredDefaultDeviceName] (bool granted) { init (granted, preferredDefaultDeviceName); });
else
init (true, preferredDefaultDeviceName);
init (audioInputRequired, preferredDefaultDeviceName);
}
void init (bool enableAudioInput, const String& preferredDefaultDeviceName)
@ -118,7 +120,6 @@ public:
//==============================================================================
virtual void createPlugin()
{
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
processor.reset (::createPluginFilterOfType (AudioProcessor::wrapperType_Standalone));
#else
@ -427,7 +428,8 @@ private:
deviceSelector (deviceManagerToUse,
minAudioInputChannels, maxAudioInputChannels,
minAudioOutputChannels, maxAudioOutputChannels,
true, false,
true,
(pluginHolder.processor.get() != nullptr && pluginHolder.processor->producesMidi()),
true, false),
shouldMuteLabel ("Feedback Loop:", "Feedback Loop:"),
shouldMuteButton ("Mute audio input")
@ -508,10 +510,12 @@ private:
emptyBuffer.clear();
player.audioDeviceAboutToStart (device);
player.setMidiOutput (deviceManager.getDefaultMidiOutput());
}
void audioDeviceStopped() override
{
player.setMidiOutput (nullptr);
player.audioDeviceStopped();
emptyBuffer.setSize (0, 0);
}

View File

@ -0,0 +1,191 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
#define UNITY_AUDIO_PLUGIN_API_VERSION 0x010401
#if JUCE_WINDOWS
#define UNITY_INTERFACE_API __stdcall
#define UNITY_INTERFACE_EXPORT __declspec(dllexport)
#else
#define UNITY_INTERFACE_API
#define UNITY_INTERFACE_EXPORT __attribute__ ((visibility("default")))
#endif
//==============================================================================
struct UnityAudioEffectState;
typedef int (UNITY_INTERFACE_API * createCallback) (UnityAudioEffectState* state);
typedef int (UNITY_INTERFACE_API * releaseCallback) (UnityAudioEffectState* state);
typedef int (UNITY_INTERFACE_API * resetCallback) (UnityAudioEffectState* state);
typedef int (UNITY_INTERFACE_API * processCallback) (UnityAudioEffectState* state, float* inBuffer, float* outBuffer, unsigned int bufferSize,
int numInChannels, int numOutChannels);
typedef int (UNITY_INTERFACE_API * setPositionCallback) (UnityAudioEffectState* state, unsigned int pos);
typedef int (UNITY_INTERFACE_API * setFloatParameterCallback) (UnityAudioEffectState* state, int index, float value);
typedef int (UNITY_INTERFACE_API * getFloatParameterCallback) (UnityAudioEffectState* state, int index, float* value, char* valuestr);
typedef int (UNITY_INTERFACE_API * getFloatBufferCallback) (UnityAudioEffectState* state, const char* name, float* buffer, int numsamples);
typedef int (UNITY_INTERFACE_API * distanceAttenuationCallback) (UnityAudioEffectState* state, float distanceIn, float attenuationIn, float* attenuationOut);
typedef void (UNITY_INTERFACE_API * renderCallback) (int eventId);
//==============================================================================
enum UnityAudioEffectDefinitionFlags
{
isSideChainTarget = 1,
isSpatializer = 2,
isAmbisonicDecoder = 4,
appliesDistanceAttenuation = 8
};
enum UnityAudioEffectStateFlags
{
stateIsPlaying = 1,
stateIsPaused = 2,
stateIsMuted = 8,
statIsSideChainTarget = 16
};
enum UnityEventModifiers
{
shift = 1,
control = 2,
alt = 4,
command = 8,
numeric = 16,
capsLock = 32,
functionKey = 64
};
//==============================================================================
struct UnityAudioSpatializerData
{
float listenerMatrix[16];
float sourceMatrix[16];
float spatialBlend;
float reverbZoneMix;
float spread;
float stereoPan;
distanceAttenuationCallback attenuationCallback;
float minDistance;
float maxDistance;
};
struct UnityAudioAmbisonicData
{
float listenerMatrix[16];
float sourceMatrix[16];
float spatialBlend;
float reverbZoneMix;
float spread;
float stereoPan;
distanceAttenuationCallback attenuationCallback;
int ambisonicOutChannels;
float volume;
};
struct UnityAudioEffectState
{
juce::uint32 structSize;
juce::uint32 sampleRate;
juce::uint64 dspCurrentTick;
juce::uint64 dspPreviousTick;
float* sidechainBuffer;
void* effectData;
juce::uint32 flags;
void* internal;
UnityAudioSpatializerData* spatializerData;
juce::uint32 dspBufferSize;
juce::uint32 hostAPIVersion;
UnityAudioAmbisonicData* ambisonicData;
template<typename T>
inline T* getEffectData() const
{
jassert (effectData != nullptr);
jassert (internal != nullptr);
return (T*) effectData;
}
};
struct UnityAudioParameterDefinition
{
char name[16];
char unit[16];
const char* description;
float min;
float max;
float defaultVal;
float displayScale;
float displayExponent;
};
struct UnityAudioEffectDefinition
{
juce::uint32 structSize;
juce::uint32 parameterStructSize;
juce::uint32 apiVersion;
juce::uint32 pluginVersion;
juce::uint32 channels;
juce::uint32 numParameters;
juce::uint64 flags;
char name[32];
createCallback create;
releaseCallback release;
resetCallback reset;
processCallback process;
setPositionCallback setPosition;
UnityAudioParameterDefinition* parameterDefintions;
setFloatParameterCallback setFloatParameter;
getFloatParameterCallback getFloatParameter;
getFloatBufferCallback getFloatBuffer;
};
//==============================================================================
// Unity callback
extern "C" UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API UnityGetAudioEffectDefinitions (UnityAudioEffectDefinition*** definitionsPtr);
// GUI script callbacks
extern "C" UNITY_INTERFACE_EXPORT renderCallback UNITY_INTERFACE_API getRenderCallback();
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityInitialiseTexture (int id, void* textureHandle, int w, int h);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDown (int id, float x, float y, UnityEventModifiers mods, int button);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDrag (int id, float x, float y, UnityEventModifiers mods, int button);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseUp (int id, float x, float y, UnityEventModifiers mods);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityKeyEvent (int id, int code, UnityEventModifiers mods, const char* name);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unitySetScreenBounds (int id, float x, float y, float w, float h);

View File

@ -0,0 +1,768 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#if JucePlugin_Build_Unity
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp"
#if JUCE_WINDOWS
#include "../utility/juce_IncludeSystemHeaders.h"
#endif
#include "juce_UnityPluginInterface.h"
//==============================================================================
namespace juce
{
typedef ComponentPeer* (*createUnityPeerFunctionType) (Component&);
extern createUnityPeerFunctionType juce_createUnityPeerFn;
//==============================================================================
class UnityPeer : public ComponentPeer,
public AsyncUpdater
{
public:
UnityPeer (Component& ed)
: ComponentPeer (ed, 0),
mouseWatcher (*this)
{
getEditor().setResizable (false, false);
}
//==============================================================================
Rectangle<int> getBounds() const override { return bounds; }
Point<float> localToGlobal (Point<float> relativePosition) override { return relativePosition + getBounds().getPosition().toFloat(); }
Point<float> globalToLocal (Point<float> screenPosition) override { return screenPosition - getBounds().getPosition().toFloat(); }
StringArray getAvailableRenderingEngines() override { return StringArray ("Software Renderer"); }
void setBounds (const Rectangle<int>& newBounds, bool) override
{
bounds = newBounds;
mouseWatcher.setBoundsToWatch (bounds);
}
bool contains (Point<int> localPos, bool) const override
{
if (isPositiveAndBelow (localPos.getX(), getBounds().getWidth())
&& isPositiveAndBelow (localPos.getY(), getBounds().getHeight()))
return true;
return false;
}
void handleAsyncUpdate() override
{
fillPixels();
}
//==============================================================================
AudioProcessorEditor& getEditor() { return *dynamic_cast<AudioProcessorEditor*> (&getComponent()); }
void setPixelDataHandle (uint8* handle, int width, int height)
{
pixelData = handle;
textureWidth = width;
textureHeight = height;
renderImage = Image (new UnityBitmapImage (pixelData, width, height));
}
// N.B. This is NOT an efficient way to do this and you shouldn't use this method in your own code.
// It works for our purposes here but a much more efficient way would be to use a GL texture.
void fillPixels()
{
if (pixelData == nullptr)
return;
LowLevelGraphicsSoftwareRenderer renderer (renderImage);
renderer.addTransform (AffineTransform::verticalFlip ((float) getComponent().getHeight()));
handlePaint (renderer);
for (int i = 0; i < textureWidth * textureHeight * 4; i += 4)
{
auto r = pixelData[i + 2];
auto g = pixelData[i + 1];
auto b = pixelData[i + 0];
pixelData[i + 0] = r;
pixelData[i + 1] = g;
pixelData[i + 2] = b;
}
}
void forwardMouseEvent (Point<float> position, ModifierKeys mods)
{
ModifierKeys::currentModifiers = mods;
handleMouseEvent (juce::MouseInputSource::mouse, position, mods, juce::MouseInputSource::invalidPressure,
juce::MouseInputSource::invalidOrientation, juce::Time::currentTimeMillis());
}
void forwardKeyPress (int code, String name, ModifierKeys mods)
{
ModifierKeys::currentModifiers = mods;
handleKeyPress (getKeyPress (code, name));
}
private:
//==============================================================================
struct UnityBitmapImage : public ImagePixelData
{
UnityBitmapImage (uint8* data, int w, int h)
: ImagePixelData (Image::PixelFormat::ARGB, w, h),
imageData (data),
lineStride (width * pixelStride)
{
}
ImageType* createType() const override { return new SoftwareImageType(); }
LowLevelGraphicsContext* createLowLevelContext() override { return new LowLevelGraphicsSoftwareRenderer (Image (this)); }
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
{
ignoreUnused (mode);
bitmap.data = imageData + x * pixelStride + y * lineStride;
bitmap.pixelFormat = pixelFormat;
bitmap.lineStride = lineStride;
bitmap.pixelStride = pixelStride;
}
ImagePixelData::Ptr clone() override
{
auto im = new UnityBitmapImage (imageData, width, height);
for (int i = 0; i < height; ++i)
memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride);
return im;
}
uint8* imageData;
int pixelStride = 4, lineStride;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityBitmapImage)
};
//==============================================================================
struct MouseWatcher : public Timer
{
MouseWatcher (ComponentPeer& o) : owner (o) {}
void timerCallback() override
{
auto pos = Desktop::getMousePosition();
if (boundsToWatch.contains (pos) && pos != lastMousePos)
{
auto ms = Desktop::getInstance().getMainMouseSource();
if (! ms.getCurrentModifiers().isLeftButtonDown())
owner.handleMouseEvent (juce::MouseInputSource::mouse, owner.globalToLocal (pos.toFloat()), {},
juce::MouseInputSource::invalidPressure, juce::MouseInputSource::invalidOrientation, juce::Time::currentTimeMillis());
lastMousePos = pos;
}
}
void setBoundsToWatch (Rectangle<int> b)
{
if (boundsToWatch != b)
boundsToWatch = b;
startTimer (250);
}
ComponentPeer& owner;
Rectangle<int> boundsToWatch;
Point<int> lastMousePos;
};
//==============================================================================
KeyPress getKeyPress (int keyCode, String name)
{
if (keyCode >= 32 && keyCode <= 64)
return { keyCode, ModifierKeys::currentModifiers, juce::juce_wchar (keyCode) };
if (keyCode >= 91 && keyCode <= 122)
return { keyCode, ModifierKeys::currentModifiers, name[0] };
if (keyCode >= 256 && keyCode <= 265)
return { juce::KeyPress::numberPad0 + (keyCode - 256), ModifierKeys::currentModifiers, juce::String (keyCode - 256).getCharPointer()[0] };
if (keyCode == 8) return { juce::KeyPress::backspaceKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 127) return { juce::KeyPress::deleteKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 9) return { juce::KeyPress::tabKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 13) return { juce::KeyPress::returnKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 27) return { juce::KeyPress::escapeKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 32) return { juce::KeyPress::spaceKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 266) return { juce::KeyPress::numberPadDecimalPoint, ModifierKeys::currentModifiers, {} };
if (keyCode == 267) return { juce::KeyPress::numberPadDivide, ModifierKeys::currentModifiers, {} };
if (keyCode == 268) return { juce::KeyPress::numberPadMultiply, ModifierKeys::currentModifiers, {} };
if (keyCode == 269) return { juce::KeyPress::numberPadSubtract, ModifierKeys::currentModifiers, {} };
if (keyCode == 270) return { juce::KeyPress::numberPadAdd, ModifierKeys::currentModifiers, {} };
if (keyCode == 272) return { juce::KeyPress::numberPadEquals, ModifierKeys::currentModifiers, {} };
if (keyCode == 273) return { juce::KeyPress::upKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 274) return { juce::KeyPress::downKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 275) return { juce::KeyPress::rightKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 276) return { juce::KeyPress::leftKey, ModifierKeys::currentModifiers, {} };
return {};
}
//==============================================================================
Rectangle<int> bounds;
MouseWatcher mouseWatcher;
uint8* pixelData = nullptr;
int textureWidth, textureHeight;
Image renderImage;
//==============================================================================
void setMinimised (bool) override {}
bool isMinimised() const override { return false; }
void setFullScreen (bool) override {}
bool isFullScreen() const override { return false; }
bool setAlwaysOnTop (bool) override { return false; }
void toFront (bool) override {}
void toBehind (ComponentPeer*) override {}
bool isFocused() const override { return true; }
void grabFocus() override {}
void* getNativeHandle() const override { return nullptr; }
BorderSize<int> getFrameSize() const override { return {}; }
void setVisible (bool) override {}
void setTitle (const String&) override {}
void setIcon (const Image&) override {}
void textInputRequired (Point<int>, TextInputTarget&) override {}
void setAlpha (float) override {}
void performAnyPendingRepaintsNow() override {}
void repaint (const Rectangle<int>&) override {}
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityPeer)
};
ComponentPeer* createUnityPeer (Component& c) { return new UnityPeer (c); }
//==============================================================================
class AudioProcessorUnityWrapper
{
public:
AudioProcessorUnityWrapper (bool isTemporary)
{
pluginInstance.reset (createPluginFilterOfType (AudioProcessor::wrapperType_Unity));
if (! isTemporary && pluginInstance->hasEditor())
{
pluginInstanceEditor.reset (pluginInstance->createEditorIfNeeded());
pluginInstanceEditor->setVisible (true);
pluginInstanceEditor->addToDesktop (0);
}
juceParameters.update (*pluginInstance, false);
}
~AudioProcessorUnityWrapper()
{
if (pluginInstanceEditor != nullptr)
{
pluginInstanceEditor->removeFromDesktop();
PopupMenu::dismissAllActiveMenus();
pluginInstanceEditor->processor.editorBeingDeleted (pluginInstanceEditor.get());
pluginInstanceEditor = nullptr;
}
}
void create (UnityAudioEffectState* state)
{
// only supported in Unity plugin API > 1.0
if (state->structSize >= sizeof (UnityAudioEffectState))
samplesPerBlock = static_cast<int> (state->dspBufferSize);
#ifdef JucePlugin_PreferredChannelConfigurations
short configs[][2] = { JucePlugin_PreferredChannelConfigurations };
const int numConfigs = sizeof (configs) / sizeof (short[2]);
jassert (numConfigs > 0 && (configs[0][0] > 0 || configs[0][1] > 0));
pluginInstance->setPlayConfigDetails (configs[0][0], configs[0][1], state->sampleRate, samplesPerBlock);
#else
pluginInstance->setRateAndBufferSizeDetails (state->sampleRate, samplesPerBlock);
#endif
pluginInstance->prepareToPlay (state->sampleRate, samplesPerBlock);
scratchBuffer.setSize (jmax (pluginInstance->getTotalNumInputChannels(), pluginInstance->getTotalNumOutputChannels()), samplesPerBlock);
}
void release()
{
pluginInstance->releaseResources();
}
void reset()
{
pluginInstance->reset();
}
void process (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed)
{
for (int pos = 0; pos < bufferSize;)
{
auto max = jmin (bufferSize - pos, samplesPerBlock);
processBuffers (inBuffer + (pos * numInChannels), outBuffer + (pos * numOutChannels), max, numInChannels, numOutChannels, isBypassed);
pos += max;
}
}
void declareParameters (UnityAudioEffectDefinition& definition)
{
static std::unique_ptr<UnityAudioParameterDefinition> parametersPtr;
static int numParams = 0;
if (parametersPtr == nullptr)
{
numParams = juceParameters.params.size();
parametersPtr.reset (static_cast<UnityAudioParameterDefinition*> (std::calloc (static_cast<size_t> (numParams),
sizeof (UnityAudioParameterDefinition))));
parameterDescriptions.clear();
for (int i = 0; i < numParams; ++i)
{
auto* parameter = juceParameters.params[i];
auto& paramDef = parametersPtr.get()[i];
strncpy (paramDef.name, parameter->getName (15).toRawUTF8(), 15);
if (parameter->getLabel().isNotEmpty())
strncpy (paramDef.unit, parameter->getLabel().toRawUTF8(), 15);
parameterDescriptions.add (parameter->getName (15));
paramDef.description = parameterDescriptions[i].toRawUTF8();
paramDef.defaultVal = parameter->getDefaultValue();
paramDef.min = 0.0f;
paramDef.max = 1.0f;
paramDef.displayScale = 1.0f;
paramDef.displayExponent = 1.0f;
}
}
definition.numParameters = static_cast<uint32> (numParams);
definition.parameterDefintions = parametersPtr.get();
}
void setParameter (int index, float value) { juceParameters.getParamForIndex (index)->setValueNotifyingHost (value); }
float getParameter (int index) const noexcept { return juceParameters.getParamForIndex (index)->getValue(); }
String getParameterString (int index) const noexcept
{
auto* param = juceParameters.getParamForIndex (index);
return param->getText (param->getValue(), 16);
}
int getNumInputChannels() const noexcept { return pluginInstance->getTotalNumInputChannels(); }
int getNumOutputChannels() const noexcept { return pluginInstance->getTotalNumOutputChannels(); }
bool hasEditor() const noexcept { return pluginInstance->hasEditor(); }
UnityPeer& getEditorPeer() const
{
auto* peer = dynamic_cast<UnityPeer*> (pluginInstanceEditor->getPeer());
jassert (peer != nullptr);
return *peer;
}
private:
//==============================================================================
void processBuffers (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed)
{
int ch;
for (ch = 0; ch < numInChannels; ++ch)
{
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const>;
DstSampleType dstData (scratchBuffer.getWritePointer (ch));
SrcSampleType srcData (inBuffer + ch, numInChannels);
dstData.convertSamples (srcData, bufferSize);
}
for (; ch < numOutChannels; ++ch)
scratchBuffer.clear (ch, 0, bufferSize);
{
const ScopedLock sl (pluginInstance->getCallbackLock());
if (pluginInstance->isSuspended())
{
scratchBuffer.clear();
}
else
{
MidiBuffer mb;
if (isBypassed)
pluginInstance->processBlockBypassed (scratchBuffer, mb);
else
pluginInstance->processBlock (scratchBuffer, mb);
}
}
for (ch = 0; ch < numOutChannels; ++ch)
{
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst>;
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
DstSampleType dstData (outBuffer + ch, numOutChannels);
SrcSampleType srcData (scratchBuffer.getReadPointer (ch));
dstData.convertSamples (srcData, bufferSize);
}
}
//==============================================================================
std::unique_ptr<AudioProcessor> pluginInstance;
std::unique_ptr<AudioProcessorEditor> pluginInstanceEditor;
int samplesPerBlock = 1024;
StringArray parameterDescriptions;
AudioBuffer<float> scratchBuffer;
LegacyAudioParametersWrapper juceParameters;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorUnityWrapper)
};
//==============================================================================
HashMap<int, AudioProcessorUnityWrapper*>& getWrapperMap()
{
static HashMap<int, AudioProcessorUnityWrapper*> wrapperMap;
return wrapperMap;
}
static void onWrapperCreation (AudioProcessorUnityWrapper* wrapperToAdd)
{
getWrapperMap().set (std::abs (Random::getSystemRandom().nextInt (65536)), wrapperToAdd);
}
static void onWrapperDeletion (AudioProcessorUnityWrapper* wrapperToRemove)
{
getWrapperMap().removeValue (wrapperToRemove);
}
//==============================================================================
namespace UnityCallbacks
{
int UNITY_INTERFACE_API createCallback (UnityAudioEffectState* state)
{
auto* pluginInstance = new AudioProcessorUnityWrapper (false);
pluginInstance->create (state);
state->effectData = pluginInstance;
onWrapperCreation (pluginInstance);
return 0;
}
int UNITY_INTERFACE_API releaseCallback (UnityAudioEffectState* state)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
pluginInstance->release();
onWrapperDeletion (pluginInstance);
delete pluginInstance;
if (getWrapperMap().size() == 0)
shutdownJuce_GUI();
return 0;
}
int UNITY_INTERFACE_API resetCallback (UnityAudioEffectState* state)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
pluginInstance->reset();
return 0;
}
int UNITY_INTERFACE_API setPositionCallback (UnityAudioEffectState* state, unsigned int pos)
{
ignoreUnused (state, pos);
return 0;
}
int UNITY_INTERFACE_API setFloatParameterCallback (UnityAudioEffectState* state, int index, float value)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
pluginInstance->setParameter (index, value);
return 0;
}
int UNITY_INTERFACE_API getFloatParameterCallback (UnityAudioEffectState* state, int index, float* value, char* valueStr)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
*value = pluginInstance->getParameter (index);
strncpy (valueStr, pluginInstance->getParameterString (index).toRawUTF8(), 15);
return 0;
}
int UNITY_INTERFACE_API getFloatBufferCallback (UnityAudioEffectState* state, const char* name, float* buffer, int numSamples)
{
ignoreUnused (numSamples);
auto nameStr = String (name);
if (nameStr == "Editor")
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
buffer[0] = pluginInstance->hasEditor() ? 1.0f : 0.0f;
}
else if (nameStr == "ID")
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
for (HashMap<int, AudioProcessorUnityWrapper*>::Iterator i (getWrapperMap()); i.next();)
{
if (i.getValue() == pluginInstance)
{
buffer[0] = (float) i.getKey();
break;
}
}
return 0;
}
else if (nameStr == "Size")
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
auto& editor = pluginInstance->getEditorPeer().getEditor();
buffer[0] = (float) editor.getBounds().getWidth();
buffer[1] = (float) editor.getBounds().getHeight();
buffer[2] = (float) editor.getConstrainer()->getMinimumWidth();
buffer[3] = (float) editor.getConstrainer()->getMinimumHeight();
buffer[4] = (float) editor.getConstrainer()->getMaximumWidth();
buffer[5] = (float) editor.getConstrainer()->getMaximumHeight();
}
return 0;
}
int UNITY_INTERFACE_API processCallback (UnityAudioEffectState* state, float* inBuffer, float* outBuffer,
unsigned int bufferSize, int numInChannels, int numOutChannels)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
if (pluginInstance != nullptr)
{
auto isPlaying = ((state->flags & stateIsPlaying) != 0);
auto isMuted = ((state->flags & stateIsMuted) != 0);
auto isPaused = ((state->flags & stateIsPaused) != 0);
auto bypassed = ! isPlaying || (isMuted || isPaused);
pluginInstance->process (inBuffer, outBuffer, static_cast<int> (bufferSize), numInChannels, numOutChannels, bypassed);
}
else
{
FloatVectorOperations::clear (outBuffer, static_cast<int> (bufferSize) * numOutChannels);
}
return 0;
}
}
//==============================================================================
static void declareEffect (UnityAudioEffectDefinition& definition)
{
memset (&definition, 0, sizeof (definition));
std::unique_ptr<AudioProcessorUnityWrapper> wrapper = std::make_unique<AudioProcessorUnityWrapper> (true);
String name (JucePlugin_Name);
if (! name.startsWithIgnoreCase ("audioplugin"))
name = "audioplugin_" + name;
strcpy (definition.name, name.toRawUTF8());
definition.structSize = sizeof (UnityAudioEffectDefinition);
definition.parameterStructSize = sizeof (UnityAudioParameterDefinition);
definition.apiVersion = UNITY_AUDIO_PLUGIN_API_VERSION;
definition.pluginVersion = JucePlugin_VersionCode;
// effects must set this to 0, generators > 0
definition.channels = (wrapper->getNumInputChannels() != 0 ? 0
: static_cast<uint32> (wrapper->getNumOutputChannels()));
wrapper->declareParameters (definition);
definition.create = UnityCallbacks::createCallback;
definition.release = UnityCallbacks::releaseCallback;
definition.reset = UnityCallbacks::resetCallback;
definition.setPosition = UnityCallbacks::setPositionCallback;
definition.process = UnityCallbacks::processCallback;
definition.setFloatParameter = UnityCallbacks::setFloatParameterCallback;
definition.getFloatParameter = UnityCallbacks::getFloatParameterCallback;
definition.getFloatBuffer = UnityCallbacks::getFloatBufferCallback;
}
} // namespace juce
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API UnityGetAudioEffectDefinitions (UnityAudioEffectDefinition*** definitionsPtr)
{
if (juce::getWrapperMap().size() == 0)
juce::initialiseJuce_GUI();
static bool hasInitialised = false;
if (! hasInitialised)
{
juce::PluginHostType::jucePlugInClientCurrentWrapperType = juce::AudioProcessor::wrapperType_Unity;
juce::juce_createUnityPeerFn = juce::createUnityPeer;
hasInitialised = true;
}
auto* definition = new UnityAudioEffectDefinition();
juce::declareEffect (*definition);
*definitionsPtr = &definition;
return 1;
}
//==============================================================================
static juce::ModifierKeys unityModifiersToJUCE (UnityEventModifiers mods, bool mouseDown, int mouseButton = -1)
{
int flags = 0;
if (mouseDown)
{
if (mouseButton == 0)
flags |= juce::ModifierKeys::leftButtonModifier;
else if (mouseButton == 1)
flags |= juce::ModifierKeys::rightButtonModifier;
else if (mouseButton == 2)
flags |= juce::ModifierKeys::middleButtonModifier;
}
if (mods == 0)
return flags;
if ((mods & UnityEventModifiers::shift) != 0) flags |= juce::ModifierKeys::shiftModifier;
if ((mods & UnityEventModifiers::control) != 0) flags |= juce::ModifierKeys::ctrlModifier;
if ((mods & UnityEventModifiers::alt) != 0) flags |= juce::ModifierKeys::altModifier;
if ((mods & UnityEventModifiers::command) != 0) flags |= juce::ModifierKeys::commandModifier;
return { flags };
}
//==============================================================================
static juce::AudioProcessorUnityWrapper* getWrapperChecked (int id)
{
auto* wrapper = juce::getWrapperMap()[id];
jassert (wrapper != nullptr);
return wrapper;
}
//==============================================================================
static void UNITY_INTERFACE_API onRenderEvent (int id)
{
getWrapperChecked (id)->getEditorPeer().triggerAsyncUpdate();
}
UNITY_INTERFACE_EXPORT renderCallback UNITY_INTERFACE_API getRenderCallback()
{
return onRenderEvent;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityInitialiseTexture (int id, void* data, int w, int h)
{
getWrapperChecked (id)->getEditorPeer().setPixelDataHandle (reinterpret_cast<juce::uint8*> (data), w, h);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDown (int id, float x, float y, UnityEventModifiers unityMods, int button)
{
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button));
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDrag (int id, float x, float y, UnityEventModifiers unityMods, int button)
{
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button));
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseUp (int id, float x, float y, UnityEventModifiers unityMods)
{
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, false));
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityKeyEvent (int id, int code, UnityEventModifiers mods, const char* name)
{
getWrapperChecked (id)->getEditorPeer().forwardKeyPress (code, name, unityModifiersToJUCE (mods, false));
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unitySetScreenBounds (int id, float x, float y, float w, float h)
{
getWrapperChecked (id)->getEditorPeer().getEditor().setBounds ({ (int) x, (int) y, (int) w, (int) h });
}
//==============================================================================
#if JUCE_WINDOWS
extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
juce::Process::setCurrentModuleInstanceHandle (instance);
return true;
}
#endif
#endif

View File

@ -27,14 +27,14 @@
namespace juce
{
/** An interface to allow an AudioProcessor to receive VST specific calls from
/** An interface to allow an AudioProcessor to send and receive VST specific calls from
the host.
@tags{Audio}
*/
struct VSTCallbackHandler
{
virtual ~VSTCallbackHandler() {}
virtual ~VSTCallbackHandler() = default;
/** This is called by the VST plug-in wrapper when it receives unhandled
plug-in "can do" calls from the host.
@ -55,6 +55,22 @@ struct VSTCallbackHandler
pointer_sized_int value,
void* ptr,
float opt) = 0;
// Note: VS2013 prevents a "using" declaration here
/** The host callback function type. */
typedef pointer_sized_int (VstHostCallbackType) (int32 opcode,
int32 index,
pointer_sized_int value,
void* ptr,
float opt);
/** This is called once by the VST plug-in wrapper after its constructor.
You can use the supplied function to query the VST host.
*/
virtual void handleVstHostCallbackAvailable (std::function<VstHostCallbackType>&& callback)
{
ignoreUnused (callback);
}
};
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -50,7 +50,10 @@
#endif
#if JUCE_VST3_CAN_REPLACE_VST2
#include "../../juce_audio_processors/format_types/juce_VSTInterface.h"
namespace Vst2
{
#include "pluginterfaces/vst2.x/vstfxstore.h"
}
#endif
#ifndef JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS
@ -83,7 +86,6 @@ using namespace Steinberg;
extern JUCE_API void* attachComponentToWindowRefVST (Component*, void* parentWindowOrView, bool isNSView);
extern JUCE_API void detachComponentFromWindowRefVST (Component*, void* nsWindow, bool isNSView);
extern JUCE_API void setNativeHostWindowSizeVST (void* window, Component*, int newWidth, int newHeight, bool isNSView);
#endif
//==============================================================================
@ -123,6 +125,11 @@ public:
return getParamForVSTParamID (bypassParamID);
}
static Vst::UnitID getUnitID (const AudioProcessorParameterGroup* group)
{
return group == nullptr ? Vst::kRootUnitId : group->getID().hashCode();
}
int getNumParameters() const noexcept { return vstParamIDs.size(); }
bool isUsingManagedParameters() const noexcept { return juceParameters.isUsingManagedParameters(); }
@ -191,7 +198,7 @@ private:
{
// we need to remain backward compatible with the old bypass id
if (vst3WrapperProvidedBypassParam)
vstParamID = static_cast<Vst::ParamID> (isUsingManagedParameters() ? paramBypass : numParameters);
vstParamID = static_cast<Vst::ParamID> ((isUsingManagedParameters() && ! forceLegacyParamIDs) ? paramBypass : numParameters);
bypassParamID = vstParamID;
}
@ -327,10 +334,12 @@ public:
struct Param : public Vst::Parameter
{
Param (JuceVST3EditController& editController, AudioProcessorParameter& p,
Vst::ParamID vstParamID, bool isBypassParameter, bool forceLegacyParamIDs)
Vst::ParamID vstParamID, Vst::UnitID vstUnitID,
bool isBypassParameter, bool forceLegacyParamIDs)
: owner (editController), param (p)
{
info.id = vstParamID;
info.unitId = vstUnitID;
toString128 (info.title, param.getName (128));
toString128 (info.shortTitle, param.getName (8));
@ -346,7 +355,6 @@ public:
info.defaultNormalizedValue = param.getDefaultValue();
jassert (info.defaultNormalizedValue >= 0 && info.defaultNormalizedValue <= 1.0f);
info.unitId = Vst::kRootUnitId;
// Is this a meter?
if (((param.getCategory() & 0xffff0000) >> 16) == 2)
@ -711,6 +719,7 @@ private:
//==============================================================================
Atomic<int> vst3IsPlaying { 0 };
float lastScaleFactorReceived = 1.0f;
void setupParameters()
{
@ -737,8 +746,10 @@ private:
{
auto vstParamID = audioProcessor->getVSTParamIDForIndex (i);
auto* juceParam = audioProcessor->getParamForVSTParamID (vstParamID);
auto* parameterGroup = pluginInstance->parameterTree.getGroupsForParameter (juceParam).getLast();
auto unitID = JuceAudioProcessor::getUnitID (parameterGroup);
parameters.addParameter (new Param (*this, *juceParam, vstParamID,
parameters.addParameter (new Param (*this, *juceParam, vstParamID, unitID,
(vstParamID == audioProcessor->bypassParamID), forceLegacyParamIDs));
}
@ -797,7 +808,14 @@ private:
: Vst::EditorView (&ec, nullptr),
owner (&ec), pluginInstance (p)
{
editorScaleFactor = ec.lastScaleFactorReceived;
component.reset (new ContentWrapperComponent (*this, p));
#if JUCE_MAC
if (getHostType().type == PluginHostType::SteinbergCubase10)
cubase10Workaround.reset (new Cubase10WindowResizeWorkaround (*this));
#endif
}
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
@ -841,6 +859,10 @@ private:
macHostWindow = juce::attachComponentToWindowRefVST (component.get(), parent, isNSView);
#endif
#if ! JUCE_MAC
setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) editorScaleFactor);
#endif
component->resizeHostWindow();
systemWindow = parent;
attachedToParent();
@ -880,10 +902,41 @@ private:
if (component != nullptr)
{
component->setSize (rect.getWidth(), rect.getHeight());
auto w = rect.getWidth();
auto h = rect.getHeight();
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
w = roundToInt (w / editorScaleFactor);
h = roundToInt (h / editorScaleFactor);
if (getHostType().type == PluginHostType::SteinbergCubase10)
{
auto integerScaleFactor = (int) std::round (editorScaleFactor);
// Workaround for Cubase 10 sending double-scaled bounds when opening editor
if (isWithin ((int) w, component->getWidth() * integerScaleFactor, 2)
&& isWithin ((int) h, component->getHeight() * integerScaleFactor, 2))
{
w /= integerScaleFactor;
h /= integerScaleFactor;
}
}
#endif
component->setSize (w, h);
#if JUCE_MAC
if (cubase10Workaround != nullptr)
cubase10Workaround->triggerAsyncUpdate();
else
#endif
if (auto* peer = component->getPeer())
peer->updateBounds();
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
if (getHostType().type == PluginHostType::SteinbergCubase10)
component->resizeHostWindow();
#endif
}
return kResultTrue;
@ -897,7 +950,16 @@ private:
{
if (size != nullptr && component != nullptr)
{
*size = ViewRect (0, 0, component->getWidth(), component->getHeight());
auto w = component->getWidth();
auto h = component->getHeight();
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
w = roundToInt (w * editorScaleFactor);
h = roundToInt (h * editorScaleFactor);
#endif
*size = ViewRect (0, 0, w, h);
return kResultTrue;
}
@ -919,17 +981,67 @@ private:
{
if (auto* editor = component->pluginEditor.get())
{
// checkSizeConstraint
auto juceRect = editor->getLocalArea (component.get(), Rectangle<int>::leftTopRightBottom (rectToCheck->left, rectToCheck->top,
rectToCheck->right, rectToCheck->bottom));
if (auto* constrainer = editor->getConstrainer())
{
Rectangle<int> limits (0, 0, constrainer->getMaximumWidth(), constrainer->getMaximumHeight());
constrainer->checkBounds (juceRect, editor->getBounds(), limits, false, false, false, false);
auto minW = (double) constrainer->getMinimumWidth();
auto maxW = (double) constrainer->getMaximumWidth();
auto minH = (double) constrainer->getMinimumHeight();
auto maxH = (double) constrainer->getMaximumHeight();
juceRect = component->getLocalArea (editor, juceRect);
rectToCheck->right = rectToCheck->left + juceRect.getWidth();
rectToCheck->bottom = rectToCheck->top + juceRect.getHeight();
auto width = (double) (rectToCheck->right - rectToCheck->left);
auto height = (double) (rectToCheck->bottom - rectToCheck->top);
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
width /= editorScaleFactor;
height /= editorScaleFactor;
#endif
width = jlimit (minW, maxW, width);
height = jlimit (minH, maxH, height);
auto aspectRatio = constrainer->getFixedAspectRatio();
if (aspectRatio != 0.0)
{
bool adjustWidth = (width / height > aspectRatio);
if (getHostType().type == PluginHostType::SteinbergCubase9)
{
if (editor->getWidth() == width && editor->getHeight() != height)
adjustWidth = true;
else if (editor->getHeight() == height && editor->getWidth() != width)
adjustWidth = false;
}
if (adjustWidth)
{
width = height * aspectRatio;
if (width > maxW || width < minW)
{
width = jlimit (minW, maxW, width);
height = width / aspectRatio;
}
}
else
{
height = width / aspectRatio;
if (height > maxH || height < minH)
{
height = jlimit (minH, maxH, height);
width = height * aspectRatio;
}
}
}
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
width *= editorScaleFactor;
height *= editorScaleFactor;
#endif
rectToCheck->right = rectToCheck->left + roundToInt (width);
rectToCheck->bottom = rectToCheck->top + roundToInt (height);
}
}
@ -942,17 +1054,40 @@ private:
tresult PLUGIN_API setContentScaleFactor (Steinberg::IPlugViewContentScaleSupport::ScaleFactor factor) override
{
#if (JUCE_MAC || JUCE_IOS)
ignoreUnused (factor);
#else
if (auto* editor = component->pluginEditor.get())
#if ! JUCE_MAC
// Cubase 10 doesn't support non-integer scale factors...
if (getHostType().type == PluginHostType::SteinbergCubase10)
{
editor->setScaleFactor (factor);
return kResultTrue;
if (component.get() != nullptr)
if (auto* peer = component->getPeer())
factor = static_cast<Steinberg::IPlugViewContentScaleSupport::ScaleFactor> (peer->getPlatformScaleFactor());
}
#endif
if (! approximatelyEqual ((float) factor, editorScaleFactor))
{
editorScaleFactor = (float) factor;
if (auto* o = owner.get())
o->lastScaleFactorReceived = editorScaleFactor;
if (component == nullptr)
return kResultFalse;
#if JUCE_WINDOWS && ! JUCE_WIN_PER_MONITOR_DPI_AWARE
if (auto* ed = component->pluginEditor.get())
ed->setScaleFactor ((float) factor);
#endif
component->resizeHostWindow();
component->setTopLeftPosition (0, 0);
component->repaint();
}
return kResultTrue;
#else
ignoreUnused (factor);
return kResultFalse;
#endif
}
private:
@ -969,8 +1104,8 @@ private:
struct ContentWrapperComponent : public Component
{
ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin)
: pluginEditor (plugin.createEditorIfNeeded()),
owner (editor)
: pluginEditor (plugin.createEditorIfNeeded()),
owner (editor)
{
setOpaque (true);
setBroughtToFrontOnMouseClick (true);
@ -1032,10 +1167,27 @@ private:
}
}
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
void checkScaleFactorIsCorrect()
{
if (auto* peer = pluginEditor->getPeer())
{
auto peerScaleFactor = (float) peer->getPlatformScaleFactor();
if (! approximatelyEqual (peerScaleFactor, owner.editorScaleFactor))
owner.setContentScaleFactor (peerScaleFactor);
}
}
#endif
void resized() override
{
if (pluginEditor != nullptr)
{
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
checkScaleFactorIsCorrect();
#endif
if (! isResizingParentToFitChild)
{
lastBounds = getLocalBounds();
@ -1074,17 +1226,21 @@ private:
#if JUCE_WINDOWS
setSize (w, h);
#else
if (owner.macHostWindow != nullptr && ! (host.isWavelab() || host.isReaper() || host.isBitwigStudio()))
juce::setNativeHostWindowSizeVST (owner.macHostWindow, this, w, h, owner.isNSView);
#endif
if (owner.plugFrame != nullptr)
{
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
w = roundToInt (w * owner.editorScaleFactor);
h = roundToInt (h * owner.editorScaleFactor);
#endif
ViewRect newSize (0, 0, w, h);
isResizingParentToFitChild = true;
owner.plugFrame->resizeView (&owner, &newSize);
isResizingParentToFitChild = false;
{
const ScopedValueSetter<bool> resizingParentSetter (isResizingParentToFitChild, true);
owner.plugFrame->resizeView (&owner, &newSize);
}
#if JUCE_MAC
if (host.isWavelab() || host.isReaper())
@ -1118,8 +1274,27 @@ private:
#if JUCE_MAC
void* macHostWindow = nullptr;
bool isNSView = false;
// On macOS Cubase 10 resizes the host window after calling onSize() resulting in the peer
// bounds being a step behind the plug-in. Calling updateBounds() asynchronously seems to fix things...
struct Cubase10WindowResizeWorkaround : public AsyncUpdater
{
Cubase10WindowResizeWorkaround (JuceVST3Editor& o) : owner (o) {}
void handleAsyncUpdate() override
{
if (auto* peer = owner.component->getPeer())
peer->updateBounds();
}
JuceVST3Editor& owner;
};
std::unique_ptr<Cubase10WindowResizeWorkaround> cubase10Workaround;
#endif
float editorScaleFactor = 1.0f;
#if JUCE_WINDOWS
WindowsHooks hooks;
#endif
@ -1163,6 +1338,7 @@ public:
short configs[][2] = { JucePlugin_PreferredChannelConfigurations };
const int numConfigs = sizeof (configs) / sizeof (short[2]);
ignoreUnused (numConfigs);
jassert (numConfigs > 0 && (configs[0][0] > 0 || configs[0][1] > 0));
pluginInstance->setPlayConfigDetails (configs[0][0], configs[0][1], 44100.0, 1024);
@ -1173,6 +1349,8 @@ public:
// and not AudioChannelSet::discreteChannels (2) etc.
jassert (checkBusFormatsAreNotDiscrete());
parameterGroups = pluginInstance->parameterTree.getSubgroups (true);
comPluginInstance = new JuceAudioProcessor (pluginInstance);
zerostruct (processContext);
@ -1440,16 +1618,16 @@ public:
bool loadVST2CcnKBlock (const char* data, int size)
{
auto bank = (const vst2FxBank*) data;
auto bank = (const Vst2::fxBank*) data;
jassert ('CcnK' == htonl (bank->magic1));
jassert ('FBCh' == htonl (bank->magic2));
jassert (htonl (bank->version1) == 1 || htonl (bank->version1) == 2);
jassert ('CcnK' == htonl (bank->chunkMagic));
jassert ('FBCh' == htonl (bank->fxMagic));
jassert (htonl (bank->version) == 1 || htonl (bank->version) == 2);
jassert (JucePlugin_VSTUniqueID == htonl (bank->fxID));
setStateInformation (bank->chunk,
jmin ((int) (size - (bank->chunk - data)),
(int) htonl (bank->chunkSize)));
setStateInformation (bank->content.data.chunk,
jmin ((int) (size - (bank->content.data.chunk - data)),
(int) htonl (bank->content.data.size)));
return true;
}
@ -1642,16 +1820,16 @@ public:
return status;
const int bankBlockSize = 160;
vst2FxBank bank;
Vst2::fxBank bank;
zerostruct (bank);
bank.magic1 = (int32) htonl ('CcnK');
bank.size = (int32) htonl (bankBlockSize - 8 + (unsigned int) mem.getSize());
bank.magic2 = (int32) htonl ('FBCh');
bank.version1 = (int32) htonl (2);
bank.fxID = (int32) htonl (JucePlugin_VSTUniqueID);
bank.version2 = (int32) htonl (JucePlugin_VersionCode);
bank.chunkSize = (int32) htonl ((unsigned int) mem.getSize());
bank.chunkMagic = (int32) htonl ('CcnK');
bank.byteSize = (int32) htonl (bankBlockSize - 8 + (unsigned int) mem.getSize());
bank.fxMagic = (int32) htonl ('FBCh');
bank.version = (int32) htonl (2);
bank.fxID = (int32) htonl (JucePlugin_VSTUniqueID);
bank.fxVersion = (int32) htonl (JucePlugin_VersionCode);
bank.content.data.size = (int32) htonl ((unsigned int) mem.getSize());
status = state->write (&bank, bankBlockSize);
@ -1665,7 +1843,7 @@ public:
//==============================================================================
Steinberg::int32 PLUGIN_API getUnitCount() override
{
return 1;
return parameterGroups.size() + 1;
}
tresult PLUGIN_API getUnitInfo (Steinberg::int32 unitIndex, Vst::UnitInfo& info) override
@ -1681,7 +1859,17 @@ public:
return kResultTrue;
}
zerostruct (info);
if (auto* group = parameterGroups[unitIndex - 1])
{
info.id = JuceAudioProcessor::getUnitID (group);
info.parentUnitId = JuceAudioProcessor::getUnitID (group->getParent());
info.programListId = Vst::kNoProgramListId;
toString128 (info.name, group->getName());
return kResultTrue;
}
return kResultFalse;
}
@ -1860,7 +2048,13 @@ public:
{
info.mediaType = Vst::kEvent;
info.direction = dir;
#ifdef JucePlugin_VSTNumMidiInputs
info.channelCount = JucePlugin_VSTNumMidiInputs;
#else
info.channelCount = 16;
#endif
toString128 (info.name, TRANS("MIDI Input"));
info.busType = Vst::kMain;
return kResultTrue;
@ -1872,7 +2066,13 @@ public:
{
info.mediaType = Vst::kEvent;
info.direction = dir;
#ifdef JucePlugin_VSTNumMidiOutputs
info.channelCount = JucePlugin_VSTNumMidiOutputs;
#else
info.channelCount = 16;
#endif
toString128 (info.name, TRANS("MIDI Output"));
info.busType = Vst::kMain;
return kResultTrue;
@ -2033,6 +2233,9 @@ public:
if (tailLengthSeconds <= 0.0 || processSetup.sampleRate <= 0.0)
return Vst::kNoTail;
if (tailLengthSeconds == std::numeric_limits<double>::infinity())
return Vst::kInfiniteTail;
return (Steinberg::uint32) roundToIntAccurate (tailLengthSeconds * processSetup.sampleRate);
}
@ -2162,43 +2365,6 @@ public:
}
private:
//==============================================================================
Atomic<int> refCount { 1 };
AudioProcessor* pluginInstance;
ComSmartPtr<Vst::IHostApplication> host;
ComSmartPtr<JuceAudioProcessor> comPluginInstance;
ComSmartPtr<JuceVST3EditController> juceVST3EditController;
/**
Since VST3 does not provide a way of knowing the buffer size and sample rate at any point,
this object needs to be copied on every call to process() to be up-to-date...
*/
Vst::ProcessContext processContext;
Vst::ProcessSetup processSetup;
MidiBuffer midiBuffer;
Array<float*> channelListFloat;
Array<double*> channelListDouble;
AudioBuffer<float> emptyBufferFloat;
AudioBuffer<double> emptyBufferDouble;
#if JucePlugin_WantsMidiInput
bool isMidiInputBusEnabled = true;
#else
bool isMidiInputBusEnabled = false;
#endif
#if JucePlugin_ProducesMidiOutput
bool isMidiOutputBusEnabled = true;
#else
bool isMidiOutputBusEnabled = false;
#endif
ScopedJuceInitialiser_GUI libraryInitialiser;
static const char* kJucePrivateDataIdentifier;
//==============================================================================
template <typename FloatType>
void processAudio (Vst::ProcessData& data, Array<FloatType*>& channelList)
@ -2410,9 +2576,51 @@ private:
p.setRateAndBufferSizeDetails (sampleRate, bufferSize);
p.prepareToPlay (sampleRate, bufferSize);
midiBuffer.ensureSize (2048);
midiBuffer.clear();
}
//==============================================================================
Atomic<int> refCount { 1 };
AudioProcessor* pluginInstance;
ComSmartPtr<Vst::IHostApplication> host;
ComSmartPtr<JuceAudioProcessor> comPluginInstance;
ComSmartPtr<JuceVST3EditController> juceVST3EditController;
/**
Since VST3 does not provide a way of knowing the buffer size and sample rate at any point,
this object needs to be copied on every call to process() to be up-to-date...
*/
Vst::ProcessContext processContext;
Vst::ProcessSetup processSetup;
MidiBuffer midiBuffer;
Array<float*> channelListFloat;
Array<double*> channelListDouble;
AudioBuffer<float> emptyBufferFloat;
AudioBuffer<double> emptyBufferDouble;
#if JucePlugin_WantsMidiInput
bool isMidiInputBusEnabled = true;
#else
bool isMidiInputBusEnabled = false;
#endif
#if JucePlugin_ProducesMidiOutput
bool isMidiOutputBusEnabled = true;
#else
bool isMidiOutputBusEnabled = false;
#endif
ScopedJuceInitialiser_GUI libraryInitialiser;
static const char* kJucePrivateDataIdentifier;
Array<const AudioProcessorParameterGroup*> parameterGroups;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3Component)
};
@ -2531,7 +2739,7 @@ bool shutdownModule()
//==============================================================================
/** This typedef represents VST3's createInstance() function signature */
typedef FUnknown* (*CreateFunction) (Vst::IHostApplication*);
using CreateFunction = FUnknown* (*)(Vst::IHostApplication*);
static FUnknown* createComponentInstance (Vst::IHostApplication* host)
{

View File

@ -35,7 +35,7 @@
ID: juce_audio_plugin_client
vendor: juce
version: 5.3.2
version: 5.4.3
name: JUCE audio plugin wrapper classes
description: Classes for building VST, VST3, AudioUnit, AAX and RTAS plugins.
website: http://www.juce.com/juce
@ -54,6 +54,17 @@
#include <juce_audio_basics/juce_audio_basics.h>
#include <juce_audio_processors/juce_audio_processors.h>
/** Config: JUCE_VST3_CAN_REPLACE_VST2
Enable this if you want your VST3 plug-in to load and save VST2 compatible
state. This allows hosts to replace VST2 plug-ins with VST3 plug-ins. If
you change this option then your VST3 plug-in will be incompatible with
previous versions.
*/
#ifndef JUCE_VST3_CAN_REPLACE_VST2
#define JUCE_VST3_CAN_REPLACE_VST2 1
#endif
/** Config: JUCE_FORCE_USE_LEGACY_PARAM_IDS
Enable this if you want to force JUCE to use a continuous parameter

View File

@ -0,0 +1,27 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "Unity/juce_Unity_Wrapper.cpp"

View File

@ -30,7 +30,8 @@
#if ! (JucePlugin_Build_VST || JucePlugin_Build_VST3 \
|| JucePlugin_Build_AU || JucePlugin_Build_AUv3 \
||JucePlugin_Build_RTAS || JucePlugin_Build_AAX \
|| JucePlugin_Build_Standalone || JucePlugin_Build_LV2)
|| JucePlugin_Build_Standalone || JucePlugin_Build_LV2 \
|| JucePlugin_Build_Unity)
#error "You need to enable at least one plugin format!"
#endif
@ -91,30 +92,3 @@
#undef JucePlugin_Build_AAX
#define JucePlugin_Build_AAX 0
#endif
//==============================================================================
#if JucePlugin_Build_VST
#if JucePlugin_VersionCode < 0x010000 // Major < 0
#if (JucePlugin_VersionCode & 0x00FF00) > (9 * 0x100) // check if Minor number exceeeds 9
#warning When version has "major" = 0, VST2 has trouble displaying "minor" exceeding 9
#endif
#if (JucePlugin_VersionCode & 0xFF) > 9 // check if Bugfix number exceeeds 9
#warning When version has "major" = 0, VST2 has trouble displaying "bugfix" exceeding 9
#endif
#elif JucePlugin_VersionCode >= 0x650000 // Major >= 101
#if (JucePlugin_VersionCode & 0x00FF00) > (99 * 0x100) // check if Minor number exceeeds 99
#warning When version has "major" > 100, VST2 has trouble displaying "minor" exceeding 99
#endif
#if (JucePlugin_VersionCode & 0xFF) > 99 // check if Bugfix number exceeeds 99
#warning When version has "major" > 100, VST2 has trouble displaying "bugfix" exceeding 99
#endif
#endif // JucePlugin_VersionCode
#endif // JucePlugin_Build_VST

View File

@ -41,8 +41,8 @@ class PluginHostType
public:
//==============================================================================
PluginHostType() : type (getHostType()) {}
PluginHostType (const PluginHostType& other) noexcept : type (other.type) {}
PluginHostType& operator= (const PluginHostType& other) noexcept { type = other.type; return *this; }
PluginHostType (const PluginHostType& other) = default;
PluginHostType& operator= (const PluginHostType& other) = default;
//==============================================================================
/** Represents the host type and also its version for some hosts. */
@ -63,11 +63,14 @@ public:
BitwigStudio, /**< Represents Bitwig Studio. */
CakewalkSonar8, /**< Represents Cakewalk Sonar 8. */
CakewalkSonarGeneric, /**< Represents Cakewalk Sonar. */
CakewalkByBandlab, /**< Represents Cakewalk by Bandlab. */
DaVinciResolve, /**< Represents DaVinci Resolve. */
DigitalPerformer, /**< Represents Digital Performer. */
FinalCut, /**< Represents Apple Final Cut Pro. */
FruityLoops, /**< Represents Fruity Loops. */
JUCEPluginHost, /**< Represents the JUCE AudioPluginHost */
MagixSamplitude, /**< Represents Magix Samplitude. */
MagixSequoia, /**< Represents Magix Sequoia. */
MergingPyramix, /**< Represents Merging Pyramix. */
MuseReceptorGeneric, /**< Represents Muse Receptor. */
Reaper, /**< Represents Cockos Reaper. */
@ -81,6 +84,8 @@ public:
SteinbergCubase8, /**< Represents Steinberg Cubase 8. */
SteinbergCubase8_5, /**< Represents Steinberg Cubase 8.5. */
SteinbergCubase9, /**< Represents Steinberg Cubase 9. */
SteinbergCubase9_5, /**< Represents Steinberg Cubase 9.5. */
SteinbergCubase10, /**< Represents Steinberg Cubase 10. */
SteinbergCubaseGeneric, /**< Represents Steinberg Cubase. */
SteinbergNuendo3, /**< Represents Steinberg Nuendo 3. */
SteinbergNuendo4, /**< Represents Steinberg Nuendo 4. */
@ -112,7 +117,7 @@ public:
/** Returns true if the host is Bitwig Studio. */
bool isBitwigStudio() const noexcept { return type == BitwigStudio; }
/** Returns true if the host is any version of Steinberg Cubase. */
bool isCubase() const noexcept { return type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubase5Bridged || type == SteinbergCubase6 || type == SteinbergCubase7 || type == SteinbergCubase8 || type == SteinbergCubase8_5 || type == SteinbergCubase9 || type == SteinbergCubaseGeneric; }
bool isCubase() const noexcept { return type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubase5Bridged || type == SteinbergCubase6 || type == SteinbergCubase7 || type == SteinbergCubase8 || type == SteinbergCubase8_5 || type == SteinbergCubase9 || type == SteinbergCubase9_5 || type == SteinbergCubase10 || type == SteinbergCubaseGeneric; }
/** Returns true if the host is Steinberg Cubase 7 or later. */
bool isCubase7orLater() const noexcept { return isCubase() && ! (type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubase6); }
/** Returns true if the host is Steinberg Cubase 5 Bridged. */
@ -127,6 +132,8 @@ public:
bool isFruityLoops() const noexcept { return type == FruityLoops; }
/** Returns true if the host is Apple GarageBand. */
bool isGarageBand() const noexcept { return type == AppleGarageBand; }
/** Returns true if the host is the JUCE AudioPluginHost */
bool isJUCEPluginHost() const noexcept { return type == JUCEPluginHost; }
/** Returns true if the host is Apple Logic Pro. */
bool isLogic() const noexcept { return type == AppleLogic; }
/** Returns true if the host is Apple MainStage. */
@ -149,8 +156,10 @@ public:
bool isSADiE() const noexcept { return type == SADiE; }
/** Returns true if the host is Magix Samplitude. */
bool isSamplitude() const noexcept { return type == MagixSamplitude; }
/** Returns true if the host is Magix Sequoia. */
bool isSequoia() const noexcept { return type == MagixSequoia; }
/** Returns true if the host is any version of Cakewalk Sonar. */
bool isSonar() const noexcept { return type == CakewalkSonar8 || type == CakewalkSonarGeneric; }
bool isSonar() const noexcept { return type == CakewalkSonar8 || type == CakewalkSonarGeneric || type == CakewalkByBandlab; }
/** Returns true if the host is Steinberg's VST3 Test Host. */
bool isSteinbergTestHost() const noexcept { return type == SteinbergTestHost; }
/** Returns true if the host is any product from Steinberg. */
@ -190,11 +199,14 @@ public:
case BitwigStudio: return "Bitwig Studio";
case CakewalkSonar8: return "Cakewalk Sonar 8";
case CakewalkSonarGeneric: return "Cakewalk Sonar";
case CakewalkByBandlab: return "Cakewalk by Bandlab";
case DaVinciResolve: return "DaVinci Resolve";
case DigitalPerformer: return "DigitalPerformer";
case FinalCut: return "Final Cut";
case FruityLoops: return "FruityLoops";
case JUCEPluginHost: return "JUCE AudioPluginHost";
case MagixSamplitude: return "Magix Samplitude";
case MagixSequoia: return "Magix Sequoia";
case MergingPyramix: return "Pyramix";
case MuseReceptorGeneric: return "Muse Receptor";
case Reaper: return "Reaper";
@ -208,6 +220,8 @@ public:
case SteinbergCubase8: return "Steinberg Cubase 8";
case SteinbergCubase8_5: return "Steinberg Cubase 8.5";
case SteinbergCubase9: return "Steinberg Cubase 9";
case SteinbergCubase9_5: return "Steinberg Cubase 9.5";
case SteinbergCubase10: return "Steinberg Cubase 10";
case SteinbergCubaseGeneric: return "Steinberg Cubase";
case SteinbergNuendo3: return "Steinberg Nuendo 3";
case SteinbergNuendo4: return "Steinberg Nuendo 4";
@ -259,11 +273,15 @@ public:
*/
static AudioProcessor::WrapperType getPluginLoadedAs() noexcept { return jucePlugInClientCurrentWrapperType; }
/** Returns true if the AudioProcessor instance is an AAX plug-in running in AudioSuite. */
static bool isInAAXAudioSuite (AudioProcessor&);
//==============================================================================
#ifndef DOXYGEN
// @internal
static AudioProcessor::WrapperType jucePlugInClientCurrentWrapperType;
static std::function<bool(AudioProcessor&)> jucePlugInIsRunningInAudioSuiteFn;
#endif
private:
@ -295,6 +313,8 @@ private:
if (hostPath.containsIgnoreCase ("Cubase 8.app")) return SteinbergCubase8;
if (hostPath.containsIgnoreCase ("Cubase 8.5.app")) return SteinbergCubase8_5;
if (hostPath.containsIgnoreCase ("Cubase 9.app")) return SteinbergCubase9;
if (hostPath.containsIgnoreCase ("Cubase 9.5.app")) return SteinbergCubase9_5;
if (hostPath.containsIgnoreCase ("Cubase 10.app")) return SteinbergCubase10;
if (hostFilename.containsIgnoreCase ("Cubase")) return SteinbergCubaseGeneric;
if (hostPath.containsIgnoreCase ("Wavelab 7")) return SteinbergWavelab7;
if (hostPath.containsIgnoreCase ("Wavelab 8")) return SteinbergWavelab8;
@ -309,6 +329,8 @@ private:
if (hostFilename.containsIgnoreCase ("Renoise")) return Renoise;
if (hostFilename.containsIgnoreCase ("Resolve")) return DaVinciResolve;
if (hostFilename.startsWith ("Bitwig")) return BitwigStudio;
if (hostFilename.containsIgnoreCase ("OsxFL")) return FruityLoops;
if (hostFilename.containsIgnoreCase ("AudioPluginHost")) return JUCEPluginHost;
#elif JUCE_WINDOWS
if (hostFilename.containsIgnoreCase ("Live 6.")) return AbletonLive6;
@ -320,6 +342,7 @@ private:
if (hostFilename.containsIgnoreCase ("ProTools")) return AvidProTools;
if (hostPath.containsIgnoreCase ("SONAR 8")) return CakewalkSonar8;
if (hostFilename.containsIgnoreCase ("SONAR")) return CakewalkSonarGeneric;
if (hostFilename.containsIgnoreCase ("Cakewalk.exe")) return CakewalkByBandlab;
if (hostFilename.containsIgnoreCase ("GarageBand")) return AppleGarageBand;
if (hostFilename.containsIgnoreCase ("Logic")) return AppleLogic;
if (hostFilename.containsIgnoreCase ("MainStage")) return AppleMainStage;
@ -333,9 +356,13 @@ private:
if (hostFilename.containsIgnoreCase ("Cubase7")) return SteinbergCubase7;
if (hostFilename.containsIgnoreCase ("Cubase8.exe")) return SteinbergCubase8;
if (hostFilename.containsIgnoreCase ("Cubase8.5.exe")) return SteinbergCubase8_5;
// Cubase 9 scans plug-ins with a separate executable "vst2xscanner"
// Later version of Cubase scan plug-ins with a separate executable "vst2xscanner"
if (hostFilename.containsIgnoreCase ("Cubase9.5.exe")
|| hostPath.containsIgnoreCase ("Cubase 9.5")) return SteinbergCubase9_5;
if (hostFilename.containsIgnoreCase ("Cubase9.exe")
|| hostPath.containsIgnoreCase ("Cubase 9")) return SteinbergCubase9;
if (hostFilename.containsIgnoreCase ("Cubase10.exe")
|| hostPath.containsIgnoreCase ("Cubase 10")) return SteinbergCubase10;
if (hostFilename.containsIgnoreCase ("Cubase")) return SteinbergCubaseGeneric;
if (hostFilename.containsIgnoreCase ("VSTBridgeApp")) return SteinbergCubase5Bridged;
if (hostPath.containsIgnoreCase ("Wavelab 5")) return SteinbergWavelab5;
@ -353,16 +380,19 @@ private:
if (hostFilename.containsIgnoreCase ("VST_Scanner")) return VBVSTScanner;
if (hostPath.containsIgnoreCase ("Merging Technologies")) return MergingPyramix;
if (hostFilename.startsWithIgnoreCase ("Sam")) return MagixSamplitude;
if (hostFilename.startsWithIgnoreCase ("Sequoia")) return MagixSequoia;
if (hostFilename.containsIgnoreCase ("Renoise")) return Renoise;
if (hostFilename.containsIgnoreCase ("Resolve")) return DaVinciResolve;
if (hostPath.containsIgnoreCase ("Bitwig Studio")) return BitwigStudio;
if (hostFilename.containsIgnoreCase ("Sadie")) return SADiE;
if (hostFilename.containsIgnoreCase ("AudioPluginHost")) return JUCEPluginHost;
#elif JUCE_LINUX
if (hostFilename.containsIgnoreCase ("Ardour")) return Ardour;
if (hostFilename.startsWithIgnoreCase ("Waveform")) return TracktionWaveform;
if (hostFilename.containsIgnoreCase ("Tracktion")) return TracktionGeneric;
if (hostFilename.startsWith ("Bitwig")) return BitwigStudio;
if (hostFilename.containsIgnoreCase ("AudioPluginHost")) return JUCEPluginHost;
#elif JUCE_IOS
#elif JUCE_ANDROID

View File

@ -38,6 +38,23 @@ namespace juce
{
AudioProcessor::WrapperType PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_Undefined;
std::function<bool(AudioProcessor&)> PluginHostType::jucePlugInIsRunningInAudioSuiteFn = nullptr;
#if JucePlugin_Build_Unity
bool juce_isRunningInUnity() { return PluginHostType::getPluginLoadedAs() == AudioProcessor::wrapperType_Unity; }
#endif
#if JUCE_MODULE_AVAILABLE_juce_opengl
bool juce_shouldDoubleScaleNativeGLWindow()
{
auto wrapperType = PluginHostType::getPluginLoadedAs();
if (wrapperType == AudioProcessor::wrapperType_VST || wrapperType == AudioProcessor::wrapperType_VST3)
return getHostType().type == PluginHostType::SteinbergCubase10;
return false;
}
#endif
#ifndef JUCE_VST3_CAN_REPLACE_VST2
#define JUCE_VST3_CAN_REPLACE_VST2 1
@ -151,12 +168,12 @@ bool JUCE_API handleManufacturerSpecificVST2Opcode (int32 index, pointer_sized_i
extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
#if JucePlugin_Enable_IAA && JucePlugin_Build_Standalone && JUCE_IOS && (! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP)
extern bool JUCE_CALLTYPE juce_isInterAppAudioConnected();
extern void JUCE_CALLTYPE juce_switchToHostApplication();
extern bool JUCE_CALLTYPE juce_isInterAppAudioConnected();
extern void JUCE_CALLTYPE juce_switchToHostApplication();
#if JUCE_MODULE_AVAILABLE_juce_gui_basics
extern Image JUCE_CALLTYPE juce_getIAAHostIcon (int);
#endif
#if JUCE_MODULE_AVAILABLE_juce_gui_basics
extern Image JUCE_CALLTYPE juce_getIAAHostIcon (int);
#endif
#endif
AudioProcessor* JUCE_API JUCE_CALLTYPE createPluginFilterOfType (AudioProcessor::WrapperType type)
@ -189,6 +206,20 @@ void PluginHostType::switchToHostApplication() const
#endif
}
bool PluginHostType::isInAAXAudioSuite (AudioProcessor& processor)
{
#if JucePlugin_Build_AAX
if (PluginHostType::getPluginLoadedAs() == AudioProcessor::wrapperType_AAX
&& jucePlugInIsRunningInAudioSuiteFn != nullptr)
{
return jucePlugInIsRunningInAudioSuiteFn (processor);
}
#endif
ignoreUnused (processor);
return false;
}
#if JUCE_MODULE_AVAILABLE_juce_gui_basics
namespace juce {