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:
@ -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.
|
||||
|
@ -48,7 +48,7 @@
|
||||
#include <CoreMIDI/CoreMIDI.h>
|
||||
#include "CAXException.h"
|
||||
|
||||
//temporaray location
|
||||
//temporary location
|
||||
enum
|
||||
{
|
||||
kMidiMessage_NoteOff = 0x80,
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
@ -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
|
||||
|
||||
|
@ -117,6 +117,9 @@ public:
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
if (mainWindow.get() != nullptr)
|
||||
mainWindow->pluginHolder->savePluginState();
|
||||
|
||||
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
|
||||
{
|
||||
Timer::callAfterDelay (100, []()
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
768
modules/juce_audio_plugin_client/Unity/juce_Unity_Wrapper.cpp
Normal file
768
modules/juce_audio_plugin_client/Unity/juce_Unity_Wrapper.cpp
Normal 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
|
@ -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
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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"
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
Reference in New Issue
Block a user