git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce

subrepo:
  subdir:   "deps/juce"
  merged:   "b13f9084e"
upstream:
  origin:   "https://github.com/essej/JUCE.git"
  branch:   "sono6good"
  commit:   "b13f9084e"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
This commit is contained in:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
AudioParameterBool::AudioParameterBool (const String& idToUse, const String& nameToUse,
bool def, const String& labelToUse,
std::function<String (bool, int)> stringFromBool,
std::function<bool (const String&)> boolFromString)
: RangedAudioParameter (idToUse, nameToUse, labelToUse),
value (def ? 1.0f : 0.0f),
defaultValue (value),
stringFromBoolFunction (stringFromBool),
boolFromStringFunction (boolFromString)
{
if (stringFromBoolFunction == nullptr)
stringFromBoolFunction = [] (bool v, int) { return v ? TRANS("On") : TRANS("Off"); };
if (boolFromStringFunction == nullptr)
{
StringArray onStrings;
onStrings.add (TRANS("on"));
onStrings.add (TRANS("yes"));
onStrings.add (TRANS("true"));
StringArray offStrings;
offStrings.add (TRANS("off"));
offStrings.add (TRANS("no"));
offStrings.add (TRANS("false"));
boolFromStringFunction = [onStrings, offStrings] (const String& text)
{
String lowercaseText (text.toLowerCase());
for (auto& testText : onStrings)
if (lowercaseText == testText)
return true;
for (auto& testText : offStrings)
if (lowercaseText == testText)
return false;
return text.getIntValue() != 0;
};
}
}
AudioParameterBool::~AudioParameterBool()
{
#if __cpp_lib_atomic_is_always_lock_free
static_assert (std::atomic<float>::is_always_lock_free,
"AudioParameterBool requires a lock-free std::atomic<float>");
#endif
}
float AudioParameterBool::getValue() const { return value; }
void AudioParameterBool::setValue (float newValue) { value = newValue; valueChanged (get()); }
float AudioParameterBool::getDefaultValue() const { return defaultValue; }
int AudioParameterBool::getNumSteps() const { return 2; }
bool AudioParameterBool::isDiscrete() const { return true; }
bool AudioParameterBool::isBoolean() const { return true; }
void AudioParameterBool::valueChanged (bool) {}
float AudioParameterBool::getValueForText (const String& text) const
{
return boolFromStringFunction (text) ? 1.0f : 0.0f;
}
String AudioParameterBool::getText (float v, int maximumLength) const
{
return stringFromBoolFunction (v >= 0.5f, maximumLength);
}
AudioParameterBool& AudioParameterBool::operator= (bool newValue)
{
if (get() != newValue)
setValueNotifyingHost (newValue ? 1.0f : 0.0f);
return *this;
}
} // namespace juce

View File

@@ -0,0 +1,98 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/**
Provides a class of AudioProcessorParameter that can be used as a boolean value.
@see AudioParameterFloat, AudioParameterInt, AudioParameterChoice
@tags{Audio}
*/
class JUCE_API AudioParameterBool : public RangedAudioParameter
{
public:
/** Creates a AudioParameterBool with the specified parameters.
@param parameterID The parameter ID to use
@param parameterName The parameter name to use
@param defaultValue The default value
@param parameterLabel An optional label for the parameter's value
@param stringFromBool An optional lambda function that converts a bool
value to a string with a maximum length. This may
be used by hosts to display the parameter's value.
@param boolFromString An optional lambda function that parses a string and
converts it into a bool value. Some hosts use this
to allow users to type in parameter values.
*/
AudioParameterBool (const String& parameterID, const String& parameterName, bool defaultValue,
const String& parameterLabel = String(),
std::function<String (bool value, int maximumStringLength)> stringFromBool = nullptr,
std::function<bool (const String& text)> boolFromString = nullptr);
/** Destructor. */
~AudioParameterBool() override;
/** Returns the parameter's current boolean value. */
bool get() const noexcept { return value >= 0.5f; }
/** Returns the parameter's current boolean value. */
operator bool() const noexcept { return get(); }
/** Changes the parameter's current value to a new boolean. */
AudioParameterBool& operator= (bool newValue);
/** Returns the range of values that the parameter can take. */
const NormalisableRange<float>& getNormalisableRange() const override { return range; }
protected:
/** Override this method if you are interested in receiving callbacks
when the parameter value changes.
*/
virtual void valueChanged (bool newValue);
private:
//==============================================================================
float getValue() const override;
void setValue (float newValue) override;
float getDefaultValue() const override;
int getNumSteps() const override;
bool isDiscrete() const override;
bool isBoolean() const override;
String getText (float, int) const override;
float getValueForText (const String&) const override;
const NormalisableRange<float> range { 0.0f, 1.0f, 1.0f };
std::atomic<float> value;
const float defaultValue;
std::function<String (bool, int)> stringFromBoolFunction;
std::function<bool (const String&)> boolFromStringFunction;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterBool)
};
} // namespace juce

View File

@@ -0,0 +1,136 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
AudioParameterChoice::AudioParameterChoice (const String& idToUse, const String& nameToUse,
const StringArray& c, int def, const String& labelToUse,
std::function<String (int, int)> stringFromIndex,
std::function<int (const String&)> indexFromString)
: RangedAudioParameter (idToUse, nameToUse, labelToUse), choices (c),
range ([this]
{
NormalisableRange<float> rangeWithInterval { 0.0f, (float) choices.size() - 1.0f,
[] (float, float end, float v) { return jlimit (0.0f, end, v * end); },
[] (float, float end, float v) { return jlimit (0.0f, 1.0f, v / end); },
[] (float start, float end, float v) { return (float) roundToInt (juce::jlimit (start, end, v)); } };
rangeWithInterval.interval = 1.0f;
return rangeWithInterval;
}()),
value ((float) def),
defaultValue (convertTo0to1 ((float) def)),
stringFromIndexFunction (stringFromIndex),
indexFromStringFunction (indexFromString)
{
jassert (choices.size() > 1); // you must supply an actual set of items to choose from!
if (stringFromIndexFunction == nullptr)
stringFromIndexFunction = [this] (int index, int) { return choices [index]; };
if (indexFromStringFunction == nullptr)
indexFromStringFunction = [this] (const String& text) { return choices.indexOf (text); };
}
AudioParameterChoice::~AudioParameterChoice()
{
#if __cpp_lib_atomic_is_always_lock_free
static_assert (std::atomic<float>::is_always_lock_free,
"AudioParameterChoice requires a lock-free std::atomic<float>");
#endif
}
float AudioParameterChoice::getValue() const { return convertTo0to1 (value); }
void AudioParameterChoice::setValue (float newValue) { value = convertFrom0to1 (newValue); valueChanged (getIndex()); }
float AudioParameterChoice::getDefaultValue() const { return defaultValue; }
int AudioParameterChoice::getNumSteps() const { return choices.size(); }
bool AudioParameterChoice::isDiscrete() const { return true; }
float AudioParameterChoice::getValueForText (const String& text) const { return convertTo0to1 ((float) indexFromStringFunction (text)); }
String AudioParameterChoice::getText (float v, int length) const { return stringFromIndexFunction ((int) convertFrom0to1 (v), length); }
void AudioParameterChoice::valueChanged (int) {}
AudioParameterChoice& AudioParameterChoice::operator= (int newValue)
{
if (getIndex() != newValue)
setValueNotifyingHost (convertTo0to1 ((float) newValue));
return *this;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
struct AudioParameterChoiceTests : public UnitTest
{
AudioParameterChoiceTests()
: UnitTest ("AudioParameterChoice", UnitTestCategories::audioProcessorParameters)
{}
void runTest() override
{
beginTest ("Three options switches at the correct points");
{
AudioParameterChoice choice ({}, {}, { "a", "b", "c" }, {});
choice.setValueNotifyingHost (0.0f);
expectEquals (choice.getIndex(), 0);
choice.setValueNotifyingHost (0.2f);
expectEquals (choice.getIndex(), 0);
choice.setValueNotifyingHost (0.3f);
expectEquals (choice.getIndex(), 1);
choice.setValueNotifyingHost (0.7f);
expectEquals (choice.getIndex(), 1);
choice.setValueNotifyingHost (0.8f);
expectEquals (choice.getIndex(), 2);
choice.setValueNotifyingHost (1.0f);
expectEquals (choice.getIndex(), 2);
}
beginTest ("Out-of-bounds input");
{
AudioParameterChoice choiceParam ({}, {}, { "a", "b", "c" }, {});
choiceParam.setValueNotifyingHost (-0.5f);
expectEquals (choiceParam.getIndex(), 0);
choiceParam.setValueNotifyingHost (1.5f);
expectEquals (choiceParam.getIndex(), 2);
}
}
};
static AudioParameterChoiceTests audioParameterChoiceTests;
#endif
} // namespace juce

View File

@@ -0,0 +1,110 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/**
Provides a class of AudioProcessorParameter that can be used to select
an indexed, named choice from a list.
@see AudioParameterFloat, AudioParameterInt, AudioParameterBool
@tags{Audio}
*/
class JUCE_API AudioParameterChoice : public RangedAudioParameter
{
public:
/** Creates a AudioParameterChoice with the specified parameters.
@param parameterID The parameter ID to use
@param parameterName The parameter name to use
@param choices The set of choices to use
@param defaultItemIndex The index of the default choice
@param parameterLabel An optional label for the parameter's value
@param stringFromIndex An optional lambda function that converts a choice
index to a string with a maximum length. This may
be used by hosts to display the parameter's value.
@param indexFromString An optional lambda function that parses a string and
converts it into a choice index. Some hosts use this
to allow users to type in parameter values.
*/
AudioParameterChoice (const String& parameterID, const String& parameterName,
const StringArray& choices,
int defaultItemIndex,
const String& parameterLabel = String(),
std::function<String (int index, int maximumStringLength)> stringFromIndex = nullptr,
std::function<int (const String& text)> indexFromString = nullptr);
/** Destructor. */
~AudioParameterChoice() override;
/** Returns the current index of the selected item. */
int getIndex() const noexcept { return roundToInt (value.load()); }
/** Returns the current index of the selected item. */
operator int() const noexcept { return getIndex(); }
/** Returns the name of the currently selected item. */
String getCurrentChoiceName() const noexcept { return choices[getIndex()]; }
/** Returns the name of the currently selected item. */
operator String() const noexcept { return getCurrentChoiceName(); }
/** Changes the selected item to a new index. */
AudioParameterChoice& operator= (int newValue);
/** Returns the range of values that the parameter can take. */
const NormalisableRange<float>& getNormalisableRange() const override { return range; }
/** Provides access to the list of choices that this parameter is working with. */
const StringArray choices;
protected:
/** Override this method if you are interested in receiving callbacks
when the parameter value changes.
*/
virtual void valueChanged (int newValue);
private:
//==============================================================================
float getValue() const override;
void setValue (float newValue) override;
float getDefaultValue() const override;
int getNumSteps() const override;
bool isDiscrete() const override;
String getText (float, int) const override;
float getValueForText (const String&) const override;
const NormalisableRange<float> range;
std::atomic<float> value;
const float defaultValue;
std::function<String (int, int)> stringFromIndexFunction;
std::function<int (const String&)> indexFromStringFunction;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterChoice)
};
} // namespace juce

View File

@@ -0,0 +1,102 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
AudioParameterFloat::AudioParameterFloat (const String& idToUse, const String& nameToUse,
NormalisableRange<float> r, float def,
const String& labelToUse, Category categoryToUse,
std::function<String (float, int)> stringFromValue,
std::function<float (const String&)> valueFromString)
: RangedAudioParameter (idToUse, nameToUse, labelToUse, categoryToUse),
range (r), value (def), defaultValue (def),
stringFromValueFunction (stringFromValue),
valueFromStringFunction (valueFromString)
{
if (stringFromValueFunction == nullptr)
{
auto numDecimalPlacesToDisplay = [this]
{
int numDecimalPlaces = 7;
if (range.interval != 0.0f)
{
if (approximatelyEqual (std::abs (range.interval - std::floor (range.interval)), 0.0f))
return 0;
auto v = std::abs (roundToInt (range.interval * pow (10, numDecimalPlaces)));
while ((v % 10) == 0 && numDecimalPlaces > 0)
{
--numDecimalPlaces;
v /= 10;
}
}
return numDecimalPlaces;
}();
stringFromValueFunction = [numDecimalPlacesToDisplay] (float v, int length)
{
String asText (v, numDecimalPlacesToDisplay);
return length > 0 ? asText.substring (0, length) : asText;
};
}
if (valueFromStringFunction == nullptr)
valueFromStringFunction = [] (const String& text) { return text.getFloatValue(); };
}
AudioParameterFloat::AudioParameterFloat (String pid, String nm, float minValue, float maxValue, float def)
: AudioParameterFloat (pid, nm, { minValue, maxValue, 0.01f }, def)
{
}
AudioParameterFloat::~AudioParameterFloat()
{
#if __cpp_lib_atomic_is_always_lock_free
static_assert (std::atomic<float>::is_always_lock_free,
"AudioParameterFloat requires a lock-free std::atomic<float>");
#endif
}
float AudioParameterFloat::getValue() const { return convertTo0to1 (value); }
void AudioParameterFloat::setValue (float newValue) { value = convertFrom0to1 (newValue); valueChanged (get()); }
float AudioParameterFloat::getDefaultValue() const { return convertTo0to1 (defaultValue); }
int AudioParameterFloat::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); }
String AudioParameterFloat::getText (float v, int length) const { return stringFromValueFunction (convertFrom0to1 (v), length); }
float AudioParameterFloat::getValueForText (const String& text) const { return convertTo0to1 (valueFromStringFunction (text)); }
void AudioParameterFloat::valueChanged (float) {}
AudioParameterFloat& AudioParameterFloat::operator= (float newValue)
{
if (value != newValue)
setValueNotifyingHost (convertTo0to1 (newValue));
return *this;
}
} // namespace juce

View File

@@ -0,0 +1,116 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/**
A subclass of AudioProcessorParameter that provides an easy way to create a
parameter which maps onto a given NormalisableRange.
@see AudioParameterInt, AudioParameterBool, AudioParameterChoice
@tags{Audio}
*/
class JUCE_API AudioParameterFloat : public RangedAudioParameter
{
public:
/** Creates a AudioParameterFloat with the specified parameters.
@param parameterID The parameter ID to use
@param parameterName The parameter name to use
@param normalisableRange The NormalisableRange to use
@param defaultValue The non-normalised default value
@param parameterLabel An optional label for the parameter's value
@param parameterCategory An optional parameter category
@param stringFromValue An optional lambda function that converts a non-normalised
value to a string with a maximum length. This may
be used by hosts to display the parameter's value.
@param valueFromString An optional lambda function that parses a string and
converts it into a non-normalised value. Some hosts use
this to allow users to type in parameter values.
*/
AudioParameterFloat (const String& parameterID,
const String& parameterName,
NormalisableRange<float> normalisableRange,
float defaultValue,
const String& parameterLabel = String(),
Category parameterCategory = AudioProcessorParameter::genericParameter,
std::function<String (float value, int maximumStringLength)> stringFromValue = nullptr,
std::function<float (const String& text)> valueFromString = nullptr);
/** Creates a AudioParameterFloat with an ID, name, and range.
On creation, its value is set to the default value.
For control over skew factors, you can use the other
constructor and provide a NormalisableRange.
*/
AudioParameterFloat (String parameterID,
String parameterName,
float minValue,
float maxValue,
float defaultValue);
/** Destructor. */
~AudioParameterFloat() override;
/** Returns the parameter's current value. */
float get() const noexcept { return value; }
/** Returns the parameter's current value. */
operator float() const noexcept { return value; }
/** Changes the parameter's current value. */
AudioParameterFloat& operator= (float newValue);
/** Returns the range of values that the parameter can take. */
const NormalisableRange<float>& getNormalisableRange() const override { return range; }
/** Provides access to the parameter's range. */
NormalisableRange<float> range;
protected:
/** Override this method if you are interested in receiving callbacks
when the parameter value changes.
*/
virtual void valueChanged (float newValue);
private:
//==============================================================================
float getValue() const override;
void setValue (float newValue) override;
float getDefaultValue() const override;
int getNumSteps() const override;
String getText (float, int) const override;
float getValueForText (const String&) const override;
std::atomic<float> value;
const float defaultValue;
std::function<String (float, int)> stringFromValueFunction;
std::function<float (const String&)> valueFromStringFunction;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterFloat)
};
} // namespace juce

View File

@@ -0,0 +1,141 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
AudioParameterInt::AudioParameterInt (const String& idToUse, const String& nameToUse,
int minValue, int maxValue, int def,
const String& labelToUse,
std::function<String (int, int)> stringFromInt,
std::function<int (const String&)> intFromString)
: RangedAudioParameter (idToUse, nameToUse, labelToUse),
range ([minValue, maxValue]
{
NormalisableRange<float> rangeWithInterval { (float) minValue, (float) maxValue,
[] (float start, float end, float v) { return jlimit (start, end, v * (end - start) + start); },
[] (float start, float end, float v) { return jlimit (0.0f, 1.0f, (v - start) / (end - start)); },
[] (float start, float end, float v) { return (float) roundToInt (juce::jlimit (start, end, v)); } };
rangeWithInterval.interval = 1.0f;
return rangeWithInterval;
}()),
value ((float) def),
defaultValue (convertTo0to1 ((float) def)),
stringFromIntFunction (stringFromInt),
intFromStringFunction (intFromString)
{
jassert (minValue < maxValue); // must have a non-zero range of values!
if (stringFromIntFunction == nullptr)
stringFromIntFunction = [] (int v, int) { return String (v); };
if (intFromStringFunction == nullptr)
intFromStringFunction = [] (const String& text) { return text.getIntValue(); };
}
AudioParameterInt::~AudioParameterInt()
{
#if __cpp_lib_atomic_is_always_lock_free
static_assert (std::atomic<float>::is_always_lock_free,
"AudioParameterInt requires a lock-free std::atomic<float>");
#endif
}
float AudioParameterInt::getValue() const { return convertTo0to1 (value); }
void AudioParameterInt::setValue (float newValue) { value = convertFrom0to1 (newValue); valueChanged (get()); }
float AudioParameterInt::getDefaultValue() const { return defaultValue; }
int AudioParameterInt::getNumSteps() const { return ((int) getNormalisableRange().getRange().getLength()) + 1; }
float AudioParameterInt::getValueForText (const String& text) const { return convertTo0to1 ((float) intFromStringFunction (text)); }
String AudioParameterInt::getText (float v, int length) const { return stringFromIntFunction ((int) convertFrom0to1 (v), length); }
void AudioParameterInt::valueChanged (int) {}
AudioParameterInt& AudioParameterInt::operator= (int newValue)
{
if (get() != newValue)
setValueNotifyingHost (convertTo0to1 ((float) newValue));
return *this;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
struct AudioParameterIntTests : public UnitTest
{
AudioParameterIntTests()
: UnitTest ("AudioParameterInt", UnitTestCategories::audioProcessorParameters)
{}
void runTest() override
{
beginTest ("Three options switches at the correct points");
{
AudioParameterInt intParam ({}, {}, 1, 3, 1);
intParam.setValueNotifyingHost (0.0f);
expectEquals (intParam.get(), 1);
intParam.setValueNotifyingHost (0.2f);
expectEquals (intParam.get(), 1);
intParam.setValueNotifyingHost (0.3f);
expectEquals (intParam.get(), 2);
intParam.setValueNotifyingHost (0.7f);
expectEquals (intParam.get(), 2);
intParam.setValueNotifyingHost (0.8f);
expectEquals (intParam.get(), 3);
intParam.setValueNotifyingHost (1.0f);
expectEquals (intParam.get(), 3);
}
beginTest ("Out-of-bounds input");
{
AudioParameterInt intParam ({}, {}, -1, 2, 0);
intParam.setValueNotifyingHost (-0.5f);
expectEquals (intParam.get(), -1);
intParam.setValueNotifyingHost (1.5f);
expectEquals (intParam.get(), 2);
intParam = -5;
expectEquals (intParam.get(), -1);
intParam = 5;
expectEquals (intParam.get(), 2);
}
}
};
static AudioParameterIntTests audioParameterIntTests;
#endif
} // namespace juce

View File

@@ -0,0 +1,106 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/**
Provides a class of AudioProcessorParameter that can be used as an
integer value with a given range.
@see AudioParameterFloat, AudioParameterBool, AudioParameterChoice
@tags{Audio}
*/
class JUCE_API AudioParameterInt : public RangedAudioParameter
{
public:
/** Creates a AudioParameterInt with the specified parameters.
@param parameterID The parameter ID to use
@param parameterName The parameter name to use
@param minValue The minimum parameter value
@param maxValue The maximum parameter value
@param defaultValue The default value
@param parameterLabel An optional label for the parameter's value
@param stringFromInt An optional lambda function that converts a int
value to a string with a maximum length. This may
be used by hosts to display the parameter's value.
@param intFromString An optional lambda function that parses a string
and converts it into an int. Some hosts use this
to allow users to type in parameter values.
*/
AudioParameterInt (const String& parameterID, const String& parameterName,
int minValue, int maxValue,
int defaultValue,
const String& parameterLabel = String(),
std::function<String (int value, int maximumStringLength)> stringFromInt = nullptr,
std::function<int (const String& text)> intFromString = nullptr);
/** Destructor. */
~AudioParameterInt() override;
/** Returns the parameter's current value as an integer. */
int get() const noexcept { return roundToInt (value.load()); }
/** Returns the parameter's current value as an integer. */
operator int() const noexcept { return get(); }
/** Changes the parameter's current value to a new integer.
The value passed in will be snapped to the permitted range before being used.
*/
AudioParameterInt& operator= (int newValue);
/** Returns the parameter's range. */
Range<int> getRange() const noexcept { return { (int) getNormalisableRange().start, (int) getNormalisableRange().end }; }
/** Returns the range of values that the parameter can take. */
const NormalisableRange<float>& getNormalisableRange() const override { return range; }
protected:
/** Override this method if you are interested in receiving callbacks
when the parameter value changes.
*/
virtual void valueChanged (int newValue);
private:
//==============================================================================
float getValue() const override;
void setValue (float newValue) override;
float getDefaultValue() const override;
int getNumSteps() const override;
String getText (float, int) const override;
float getValueForText (const String&) const override;
const NormalisableRange<float> range;
std::atomic<float> value;
const float defaultValue;
std::function<String (int, int)> stringFromIntFunction;
std::function<int (const String&)> intFromStringFunction;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterInt)
};
} // namespace juce

View File

@@ -0,0 +1,40 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
AudioProcessorParameterWithID::AudioProcessorParameterWithID (const String& idToUse,
const String& nameToUse,
const String& labelToUse,
AudioProcessorParameter::Category categoryToUse)
: paramID (idToUse), name (nameToUse), label (labelToUse), category (categoryToUse) {}
AudioProcessorParameterWithID::~AudioProcessorParameterWithID() {}
String AudioProcessorParameterWithID::getName (int maximumStringLength) const { return name.substring (0, maximumStringLength); }
String AudioProcessorParameterWithID::getLabel() const { return label; }
AudioProcessorParameter::Category AudioProcessorParameterWithID::getCategory() const { return category; }
} // namespace juce

View File

@@ -0,0 +1,70 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/**
This abstract base class is used by some AudioProcessorParameter helper classes.
@see AudioParameterFloat, AudioParameterInt, AudioParameterBool, AudioParameterChoice
@tags{Audio}
*/
class JUCE_API AudioProcessorParameterWithID : public AudioProcessorParameter
{
public:
/** The creation of this object requires providing a name and ID which will be
constant for its lifetime.
*/
AudioProcessorParameterWithID (const String& parameterID,
const String& parameterName,
const String& parameterLabel = {},
Category parameterCategory = AudioProcessorParameter::genericParameter);
/** Destructor. */
~AudioProcessorParameterWithID() override;
/** Provides access to the parameter's ID string. */
const String paramID;
/** Provides access to the parameter's name. */
const String name;
/** Provides access to the parameter's label. */
const String label;
/** Provides access to the parameter's category. */
const Category category;
String getName (int) const override;
String getLabel() const override;
Category getCategory() const override;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameterWithID)
};
} // namespace juce

View File

@@ -0,0 +1,963 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
//==============================================================================
AudioProcessorValueTreeState::Parameter::Parameter (const String& parameterID,
const String& parameterName,
const String& labelText,
NormalisableRange<float> valueRange,
float defaultParameterValue,
std::function<String (float)> valueToTextFunction,
std::function<float (const String&)> textToValueFunction,
bool isMetaParameter,
bool isAutomatableParameter,
bool isDiscrete,
AudioProcessorParameter::Category parameterCategory,
bool isBoolean)
: AudioParameterFloat (parameterID,
parameterName,
valueRange,
defaultParameterValue,
labelText,
parameterCategory,
valueToTextFunction == nullptr ? std::function<String (float v, int)>()
: [valueToTextFunction] (float v, int) { return valueToTextFunction (v); },
std::move (textToValueFunction)),
unsnappedDefault (valueRange.convertTo0to1 (defaultParameterValue)),
metaParameter (isMetaParameter),
automatable (isAutomatableParameter),
discrete (isDiscrete),
boolean (isBoolean)
{
}
float AudioProcessorValueTreeState::Parameter::getDefaultValue() const { return unsnappedDefault; }
int AudioProcessorValueTreeState::Parameter::getNumSteps() const { return RangedAudioParameter::getNumSteps(); }
bool AudioProcessorValueTreeState::Parameter::isMetaParameter() const { return metaParameter; }
bool AudioProcessorValueTreeState::Parameter::isAutomatable() const { return automatable; }
bool AudioProcessorValueTreeState::Parameter::isDiscrete() const { return discrete; }
bool AudioProcessorValueTreeState::Parameter::isBoolean() const { return boolean; }
void AudioProcessorValueTreeState::Parameter::valueChanged (float newValue)
{
if (lastValue == newValue)
return;
lastValue = newValue;
if (onValueChanged != nullptr)
onValueChanged();
}
//==============================================================================
class AudioProcessorValueTreeState::ParameterAdapter : private AudioProcessorParameter::Listener
{
private:
using Listener = AudioProcessorValueTreeState::Listener;
public:
explicit ParameterAdapter (RangedAudioParameter& parameterIn)
: parameter (parameterIn),
// For legacy reasons, the unnormalised value should *not* be snapped on construction
unnormalisedValue (getRange().convertFrom0to1 (parameter.getDefaultValue()))
{
parameter.addListener (this);
if (auto* ptr = dynamic_cast<Parameter*> (&parameter))
ptr->onValueChanged = [this] { parameterValueChanged ({}, {}); };
}
~ParameterAdapter() override { parameter.removeListener (this); }
void addListener (Listener* l) { listeners.add (l); }
void removeListener (Listener* l) { listeners.remove (l); }
RangedAudioParameter& getParameter() { return parameter; }
const RangedAudioParameter& getParameter() const { return parameter; }
const NormalisableRange<float>& getRange() const { return parameter.getNormalisableRange(); }
float getDenormalisedDefaultValue() const { return denormalise (parameter.getDefaultValue()); }
void setDenormalisedValue (float value)
{
if (value == unnormalisedValue)
return;
setNormalisedValue (normalise (value));
}
float getDenormalisedValueForText (const String& text) const
{
return denormalise (parameter.getValueForText (text));
}
String getTextForDenormalisedValue (float value) const
{
return parameter.getText (normalise (value), 0);
}
float getDenormalisedValue() const { return unnormalisedValue; }
std::atomic<float>& getRawDenormalisedValue() { return unnormalisedValue; }
bool flushToTree (const Identifier& key, UndoManager* um)
{
auto needsUpdateTestValue = true;
if (! needsUpdate.compare_exchange_strong (needsUpdateTestValue, false))
return false;
if (auto valueProperty = tree.getPropertyPointer (key))
{
if ((float) *valueProperty != unnormalisedValue)
{
ScopedValueSetter<bool> svs (ignoreParameterChangedCallbacks, true);
tree.setProperty (key, unnormalisedValue.load(), um);
}
}
else
{
tree.setProperty (key, unnormalisedValue.load(), nullptr);
}
return true;
}
ValueTree tree;
private:
void parameterGestureChanged (int, bool) override {}
void parameterValueChanged (int, float) override
{
const auto newValue = denormalise (parameter.getValue());
if (unnormalisedValue == newValue && ! listenersNeedCalling)
return;
unnormalisedValue = newValue;
listeners.call ([this] (Listener& l) { l.parameterChanged (parameter.paramID, unnormalisedValue); });
listenersNeedCalling = false;
needsUpdate = true;
}
float denormalise (float normalised) const
{
return getParameter().convertFrom0to1 (normalised);
}
float normalise (float denormalised) const
{
return getParameter().convertTo0to1 (denormalised);
}
void setNormalisedValue (float value)
{
if (ignoreParameterChangedCallbacks)
return;
parameter.setValueNotifyingHost (value);
}
class LockedListeners
{
public:
template <typename Fn>
void call (Fn&& fn)
{
const CriticalSection::ScopedLockType lock (mutex);
listeners.call (std::forward<Fn> (fn));
}
void add (Listener* l)
{
const CriticalSection::ScopedLockType lock (mutex);
listeners.add (l);
}
void remove (Listener* l)
{
const CriticalSection::ScopedLockType lock (mutex);
listeners.remove (l);
}
private:
CriticalSection mutex;
ListenerList<Listener> listeners;
};
RangedAudioParameter& parameter;
LockedListeners listeners;
std::atomic<float> unnormalisedValue { 0.0f };
std::atomic<bool> needsUpdate { true }, listenersNeedCalling { true };
bool ignoreParameterChangedCallbacks { false };
};
//==============================================================================
AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& processorToConnectTo,
UndoManager* undoManagerToUse,
const Identifier& valueTreeType,
ParameterLayout parameterLayout)
: AudioProcessorValueTreeState (processorToConnectTo, undoManagerToUse)
{
struct PushBackVisitor : ParameterLayout::Visitor
{
explicit PushBackVisitor (AudioProcessorValueTreeState& stateIn)
: state (&stateIn) {}
void visit (std::unique_ptr<RangedAudioParameter> param) const override
{
if (param == nullptr)
{
jassertfalse;
return;
}
state->addParameterAdapter (*param);
state->processor.addParameter (param.release());
}
void visit (std::unique_ptr<AudioProcessorParameterGroup> group) const override
{
if (group == nullptr)
{
jassertfalse;
return;
}
for (const auto param : group->getParameters (true))
{
if (const auto rangedParam = dynamic_cast<RangedAudioParameter*> (param))
{
state->addParameterAdapter (*rangedParam);
}
else
{
// If you hit this assertion then you are attempting to add a parameter that is
// not derived from RangedAudioParameter to the AudioProcessorValueTreeState.
jassertfalse;
}
}
state->processor.addParameterGroup (move (group));
}
AudioProcessorValueTreeState* state;
};
for (auto& item : parameterLayout.parameters)
item->accept (PushBackVisitor (*this));
state = ValueTree (valueTreeType);
}
AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& p, UndoManager* um)
: processor (p), undoManager (um)
{
startTimerHz (10);
state.addListener (this);
}
AudioProcessorValueTreeState::~AudioProcessorValueTreeState()
{
stopTimer();
}
//==============================================================================
RangedAudioParameter* AudioProcessorValueTreeState::createAndAddParameter (const String& paramID,
const String& paramName,
const String& labelText,
NormalisableRange<float> range,
float defaultVal,
std::function<String (float)> valueToTextFunction,
std::function<float (const String&)> textToValueFunction,
bool isMetaParameter,
bool isAutomatableParameter,
bool isDiscreteParameter,
AudioProcessorParameter::Category category,
bool isBooleanParameter)
{
return createAndAddParameter (std::make_unique<Parameter> (paramID,
paramName,
labelText,
range,
defaultVal,
std::move (valueToTextFunction),
std::move (textToValueFunction),
isMetaParameter,
isAutomatableParameter,
isDiscreteParameter,
category,
isBooleanParameter));
}
RangedAudioParameter* AudioProcessorValueTreeState::createAndAddParameter (std::unique_ptr<RangedAudioParameter> param)
{
if (param == nullptr)
return nullptr;
// All parameters must be created before giving this manager a ValueTree state!
jassert (! state.isValid());
if (getParameter (param->paramID) != nullptr)
return nullptr;
addParameterAdapter (*param);
processor.addParameter (param.get());
return param.release();
}
//==============================================================================
void AudioProcessorValueTreeState::addParameterAdapter (RangedAudioParameter& param)
{
adapterTable.emplace (param.paramID, std::make_unique<ParameterAdapter> (param));
}
AudioProcessorValueTreeState::ParameterAdapter* AudioProcessorValueTreeState::getParameterAdapter (StringRef paramID) const
{
auto it = adapterTable.find (paramID);
return it == adapterTable.end() ? nullptr : it->second.get();
}
void AudioProcessorValueTreeState::addParameterListener (StringRef paramID, Listener* listener)
{
if (auto* p = getParameterAdapter (paramID))
p->addListener (listener);
}
void AudioProcessorValueTreeState::removeParameterListener (StringRef paramID, Listener* listener)
{
if (auto* p = getParameterAdapter (paramID))
p->removeListener (listener);
}
Value AudioProcessorValueTreeState::getParameterAsValue (StringRef paramID) const
{
if (auto* adapter = getParameterAdapter (paramID))
if (adapter->tree.isValid())
return adapter->tree.getPropertyAsValue (valuePropertyID, undoManager);
return {};
}
NormalisableRange<float> AudioProcessorValueTreeState::getParameterRange (StringRef paramID) const noexcept
{
if (auto* p = getParameterAdapter (paramID))
return p->getRange();
return {};
}
RangedAudioParameter* AudioProcessorValueTreeState::getParameter (StringRef paramID) const noexcept
{
if (auto adapter = getParameterAdapter (paramID))
return &adapter->getParameter();
return nullptr;
}
std::atomic<float>* AudioProcessorValueTreeState::getRawParameterValue (StringRef paramID) const noexcept
{
if (auto* p = getParameterAdapter (paramID))
return &p->getRawDenormalisedValue();
return nullptr;
}
ValueTree AudioProcessorValueTreeState::copyState()
{
ScopedLock lock (valueTreeChanging);
flushParameterValuesToValueTree();
return state.createCopy();
}
void AudioProcessorValueTreeState::replaceState (const ValueTree& newState)
{
ScopedLock lock (valueTreeChanging);
state = newState;
if (undoManager != nullptr)
undoManager->clearUndoHistory();
}
void AudioProcessorValueTreeState::setNewState (ValueTree vt)
{
jassert (vt.getParent() == state);
if (auto* p = getParameterAdapter (vt.getProperty (idPropertyID).toString()))
{
p->tree = vt;
p->setDenormalisedValue (p->tree.getProperty (valuePropertyID, p->getDenormalisedDefaultValue()));
}
}
void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees()
{
ScopedLock lock (valueTreeChanging);
for (auto& p : adapterTable)
p.second->tree = ValueTree();
for (const auto& child : state)
setNewState (child);
for (auto& p : adapterTable)
{
auto& adapter = *p.second;
if (! adapter.tree.isValid())
{
adapter.tree = ValueTree (valueType);
adapter.tree.setProperty (idPropertyID, adapter.getParameter().paramID, nullptr);
state.appendChild (adapter.tree, nullptr);
}
}
flushParameterValuesToValueTree();
}
void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
{
if (tree.hasType (valueType) && tree.getParent() == state)
setNewState (tree);
}
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree)
{
if (parent == state && tree.hasType (valueType))
setNewState (tree);
}
void AudioProcessorValueTreeState::valueTreeRedirected (ValueTree& v)
{
if (v == state)
updateParameterConnectionsToChildTrees();
}
bool AudioProcessorValueTreeState::flushParameterValuesToValueTree()
{
ScopedLock lock (valueTreeChanging);
bool anyUpdated = false;
for (auto& p : adapterTable)
anyUpdated |= p.second->flushToTree (valuePropertyID, undoManager);
return anyUpdated;
}
void AudioProcessorValueTreeState::timerCallback()
{
auto anythingUpdated = flushParameterValuesToValueTree();
startTimer (anythingUpdated ? 1000 / 50
: jlimit (50, 500, getTimerInterval() + 20));
}
//==============================================================================
template <typename Attachment, typename Control>
std::unique_ptr<Attachment> makeAttachment (const AudioProcessorValueTreeState& stateToUse,
const String& parameterID,
Control& control)
{
if (auto* parameter = stateToUse.getParameter (parameterID))
return std::make_unique<Attachment> (*parameter, control, stateToUse.undoManager);
jassertfalse;
return nullptr;
}
AudioProcessorValueTreeState::SliderAttachment::SliderAttachment (AudioProcessorValueTreeState& stateToUse,
const String& parameterID,
Slider& slider)
: attachment (makeAttachment<SliderParameterAttachment> (stateToUse, parameterID, slider))
{
}
AudioProcessorValueTreeState::ComboBoxAttachment::ComboBoxAttachment (AudioProcessorValueTreeState& stateToUse,
const String& parameterID,
ComboBox& combo)
: attachment (makeAttachment<ComboBoxParameterAttachment> (stateToUse, parameterID, combo))
{
}
AudioProcessorValueTreeState::ButtonAttachment::ButtonAttachment (AudioProcessorValueTreeState& stateToUse,
const String& parameterID,
Button& button)
: attachment (makeAttachment<ButtonParameterAttachment> (stateToUse, parameterID, button))
{
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
struct ParameterAdapterTests : public UnitTest
{
ParameterAdapterTests()
: UnitTest ("Parameter Adapter", UnitTestCategories::audioProcessorParameters)
{}
void runTest() override
{
beginTest ("The default value is returned correctly");
{
const auto test = [&] (NormalisableRange<float> range, float value)
{
AudioParameterFloat param ({}, {}, range, value, {});
AudioProcessorValueTreeState::ParameterAdapter adapter (param);
expectEquals (adapter.getDenormalisedDefaultValue(), value);
};
test ({ -100, 100 }, 0);
test ({ -2.5, 12.5 }, 10);
}
beginTest ("Denormalised parameter values can be retrieved");
{
const auto test = [&] (NormalisableRange<float> range, float value)
{
AudioParameterFloat param ({}, {}, range, {}, {});
AudioProcessorValueTreeState::ParameterAdapter adapter (param);
adapter.setDenormalisedValue (value);
expectEquals (adapter.getDenormalisedValue(), value);
expectEquals (adapter.getRawDenormalisedValue().load(), value);
};
test ({ -20, -10 }, -15);
test ({ 0, 7.5 }, 2.5);
}
beginTest ("Floats can be converted to text");
{
const auto test = [&] (NormalisableRange<float> range, float value, String expected)
{
AudioParameterFloat param ({}, {}, range, {}, {});
AudioProcessorValueTreeState::ParameterAdapter adapter (param);
expectEquals (adapter.getTextForDenormalisedValue (value), expected);
};
test ({ -100, 100 }, 0, "0.0000000");
test ({ -2.5, 12.5 }, 10, "10.0000000");
test ({ -20, -10 }, -15, "-15.0000000");
test ({ 0, 7.5 }, 2.5, "2.5000000");
}
beginTest ("Text can be converted to floats");
{
const auto test = [&] (NormalisableRange<float> range, String text, float expected)
{
AudioParameterFloat param ({}, {}, range, {}, {});
AudioProcessorValueTreeState::ParameterAdapter adapter (param);
expectEquals (adapter.getDenormalisedValueForText (text), expected);
};
test ({ -100, 100 }, "0.0", 0);
test ({ -2.5, 12.5 }, "10.0", 10);
test ({ -20, -10 }, "-15.0", -15);
test ({ 0, 7.5 }, "2.5", 2.5);
}
}
};
static ParameterAdapterTests parameterAdapterTests;
namespace
{
template <typename ValueType>
inline bool operator== (const NormalisableRange<ValueType>& a,
const NormalisableRange<ValueType>& b)
{
return std::tie (a.start, a.end, a.interval, a.skew, a.symmetricSkew)
== std::tie (b.start, b.end, b.interval, b.skew, b.symmetricSkew);
}
template <typename ValueType>
inline bool operator!= (const NormalisableRange<ValueType>& a,
const NormalisableRange<ValueType>& b)
{
return ! (a == b);
}
} // namespace
class AudioProcessorValueTreeStateTests : public UnitTest
{
private:
using Parameter = AudioProcessorValueTreeState::Parameter;
using ParameterGroup = AudioProcessorParameterGroup;
using ParameterLayout = AudioProcessorValueTreeState::ParameterLayout;
class TestAudioProcessor : public AudioProcessor
{
public:
TestAudioProcessor() = default;
explicit TestAudioProcessor (ParameterLayout layout)
: state (*this, nullptr, "state", std::move (layout)) {}
const String getName() const override { return {}; }
void prepareToPlay (double, int) override {}
void releaseResources() override {}
void processBlock (AudioBuffer<float>&, MidiBuffer&) override {}
using AudioProcessor::processBlock;
double getTailLengthSeconds() const override { return {}; }
bool acceptsMidi() const override { return {}; }
bool producesMidi() const override { return {}; }
AudioProcessorEditor* createEditor() override { return {}; }
bool hasEditor() const override { return {}; }
int getNumPrograms() override { return 1; }
int getCurrentProgram() override { return {}; }
void setCurrentProgram (int) override {}
const String getProgramName (int) override { return {}; }
void changeProgramName (int, const String&) override {}
void getStateInformation (MemoryBlock&) override {}
void setStateInformation (const void*, int) override {}
AudioProcessorValueTreeState state { *this, nullptr };
};
struct Listener final : public AudioProcessorValueTreeState::Listener
{
void parameterChanged (const String& idIn, float valueIn) override
{
id = idIn;
value = valueIn;
}
String id;
float value{};
};
public:
AudioProcessorValueTreeStateTests()
: UnitTest ("Audio Processor Value Tree State", UnitTestCategories::audioProcessorParameters)
{}
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262)
void runTest() override
{
ScopedJuceInitialiser_GUI scopedJuceInitialiser_gui;
beginTest ("After calling createAndAddParameter, the number of parameters increases by one");
{
TestAudioProcessor proc;
proc.state.createAndAddParameter (std::make_unique<Parameter> (String(), String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr));
expectEquals (proc.getParameters().size(), 1);
}
beginTest ("After creating a normal named parameter, we can later retrieve that parameter");
{
TestAudioProcessor proc;
const auto key = "id";
const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr));
expect (proc.state.getParameter (key) == param);
}
beginTest ("After construction, the value tree has the expected format");
{
TestAudioProcessor proc ({
std::make_unique<AudioProcessorParameterGroup> ("A", "", "",
std::make_unique<AudioParameterBool> ("a", "", false),
std::make_unique<AudioParameterFloat> ("b", "", NormalisableRange<float>{}, 0.0f)),
std::make_unique<AudioProcessorParameterGroup> ("B", "", "",
std::make_unique<AudioParameterInt> ("c", "", 0, 1, 0),
std::make_unique<AudioParameterChoice> ("d", "", StringArray { "foo", "bar" }, 0)) });
const auto valueTree = proc.state.copyState();
expectEquals (valueTree.getNumChildren(), 4);
for (auto child : valueTree)
{
expect (child.hasType ("PARAM"));
expect (child.hasProperty ("id"));
expect (child.hasProperty ("value"));
}
}
beginTest ("Meta parameters can be created");
{
TestAudioProcessor proc;
const auto key = "id";
const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr, true));
expect (param->isMetaParameter());
}
beginTest ("Automatable parameters can be created");
{
TestAudioProcessor proc;
const auto key = "id";
const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr, false, true));
expect (param->isAutomatable());
}
beginTest ("Discrete parameters can be created");
{
TestAudioProcessor proc;
const auto key = "id";
const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr, false, false, true));
expect (param->isDiscrete());
}
beginTest ("Custom category parameters can be created");
{
TestAudioProcessor proc;
const auto key = "id";
const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr, false, false, false,
AudioProcessorParameter::Category::inputMeter));
expect (param->category == AudioProcessorParameter::Category::inputMeter);
}
beginTest ("Boolean parameters can be created");
{
TestAudioProcessor proc;
const auto key = "id";
const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr, false, false, false,
AudioProcessorParameter::Category::genericParameter, true));
expect (param->isBoolean());
}
beginTest ("After creating a custom named parameter, we can later retrieve that parameter");
{
const auto key = "id";
auto param = std::make_unique<AudioParameterBool> (key, "", false);
const auto paramPtr = param.get();
TestAudioProcessor proc (std::move (param));
expect (proc.state.getParameter (key) == paramPtr);
}
beginTest ("After adding a normal parameter that already exists, the AudioProcessor parameters are unchanged");
{
TestAudioProcessor proc;
const auto key = "id";
const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr));
proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr));
expectEquals (proc.getParameters().size(), 1);
expect (proc.getParameters().getFirst() == param);
}
beginTest ("After setting a parameter value, that value is reflected in the state");
{
TestAudioProcessor proc;
const auto key = "id";
const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr));
const auto value = 0.5f;
param->setValueNotifyingHost (value);
expectEquals (proc.state.getRawParameterValue (key)->load(), value);
}
beginTest ("After adding an APVTS::Parameter, its value is the default value");
{
TestAudioProcessor proc;
const auto key = "id";
const auto value = 5.0f;
proc.state.createAndAddParameter (std::make_unique<Parameter> (
key,
String(),
String(),
NormalisableRange<float> (0.0f, 100.0f, 10.0f),
value,
nullptr,
nullptr));
expectEquals (proc.state.getRawParameterValue (key)->load(), value);
}
beginTest ("Listeners receive notifications when parameters change");
{
Listener listener;
TestAudioProcessor proc;
const auto key = "id";
const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr));
proc.state.addParameterListener (key, &listener);
const auto value = 0.5f;
param->setValueNotifyingHost (value);
expectEquals (listener.id, String { key });
expectEquals (listener.value, value);
}
beginTest ("Bool parameters have a range of 0-1");
{
const auto key = "id";
TestAudioProcessor proc (std::make_unique<AudioParameterBool> (key, "", false));
expect (proc.state.getParameterRange (key) == NormalisableRange<float> (0.0f, 1.0f, 1.0f));
}
beginTest ("Float parameters retain their specified range");
{
const auto key = "id";
const auto range = NormalisableRange<float> { -100, 100, 0.7f, 0.2f, true };
TestAudioProcessor proc (std::make_unique<AudioParameterFloat> (key, "", range, 0.0f));
expect (proc.state.getParameterRange (key) == range);
}
beginTest ("Int parameters retain their specified range");
{
const auto key = "id";
const auto min = -27;
const auto max = 53;
TestAudioProcessor proc (std::make_unique<AudioParameterInt> (key, "", min, max, 0));
expect (proc.state.getParameterRange (key) == NormalisableRange<float> (float (min), float (max), 1.0f));
}
beginTest ("Choice parameters retain their specified range");
{
const auto key = "id";
const auto choices = StringArray { "", "", "" };
TestAudioProcessor proc (std::make_unique<AudioParameterChoice> (key, "", choices, 0));
expect (proc.state.getParameterRange (key) == NormalisableRange<float> (0.0f, (float) (choices.size() - 1), 1.0f));
expect (proc.state.getParameter (key)->getNumSteps() == choices.size());
}
beginTest ("When the parameter value is changed, normal parameter values are updated");
{
TestAudioProcessor proc;
const auto key = "id";
const auto initialValue = 0.2f;
auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
initialValue, nullptr, nullptr));
proc.state.state = ValueTree { "state" };
auto value = proc.state.getParameterAsValue (key);
expectEquals (float (value.getValue()), initialValue);
const auto newValue = 0.75f;
value = newValue;
expectEquals (param->getValue(), newValue);
expectEquals (proc.state.getRawParameterValue (key)->load(), newValue);
}
beginTest ("When the parameter value is changed, custom parameter values are updated");
{
const auto key = "id";
const auto choices = StringArray ("foo", "bar", "baz");
auto param = std::make_unique<AudioParameterChoice> (key, "", choices, 0);
const auto paramPtr = param.get();
TestAudioProcessor proc (std::move (param));
const auto newValue = 2.0f;
auto value = proc.state.getParameterAsValue (key);
value = newValue;
expectEquals (paramPtr->getCurrentChoiceName(), choices[int (newValue)]);
expectEquals (proc.state.getRawParameterValue (key)->load(), newValue);
}
beginTest ("When the parameter value is changed, listeners are notified");
{
Listener listener;
TestAudioProcessor proc;
const auto key = "id";
proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
0.0f, nullptr, nullptr));
proc.state.addParameterListener (key, &listener);
proc.state.state = ValueTree { "state" };
const auto newValue = 0.75f;
proc.state.getParameterAsValue (key) = newValue;
expectEquals (listener.value, newValue);
expectEquals (listener.id, String { key });
}
beginTest ("When the parameter value is changed, listeners are notified");
{
const auto key = "id";
const auto choices = StringArray { "foo", "bar", "baz" };
Listener listener;
TestAudioProcessor proc (std::make_unique<AudioParameterChoice> (key, "", choices, 0));
proc.state.addParameterListener (key, &listener);
const auto newValue = 2.0f;
proc.state.getParameterAsValue (key) = newValue;
expectEquals (listener.value, newValue);
expectEquals (listener.id, String (key));
}
}
JUCE_END_IGNORE_WARNINGS_MSVC
};
static AudioProcessorValueTreeStateTests audioProcessorValueTreeStateTests;
#endif
} // namespace juce

View File

@@ -0,0 +1,563 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/**
This class contains a ValueTree that is used to manage an AudioProcessor's entire state.
It has its own internal class of parameter object that is linked to values
within its ValueTree, and which are each identified by a string ID.
You can get access to the underlying ValueTree object via the state member variable,
so you can add extra properties to it as necessary.
It also provides some utility child classes for connecting parameters directly to
GUI controls like sliders.
The favoured constructor of this class takes a collection of RangedAudioParameters or
AudioProcessorParameterGroups of RangedAudioParameters and adds them to the attached
AudioProcessor directly.
The deprecated way of using this class is as follows:
1) Create an AudioProcessorValueTreeState, and give it some parameters using createAndAddParameter().
2) Initialise the state member variable with a type name.
The deprecated constructor will be removed from the API in a future version of JUCE!
@tags{Audio}
*/
class JUCE_API AudioProcessorValueTreeState : private Timer,
private ValueTree::Listener
{
public:
//==============================================================================
/** A class to contain a set of RangedAudioParameters and AudioProcessorParameterGroups
containing RangedAudioParameters.
This class is used in the AudioProcessorValueTreeState constructor to allow
arbitrarily grouped RangedAudioParameters to be passed to an AudioProcessor.
*/
class JUCE_API ParameterLayout final
{
private:
//==============================================================================
template <typename It>
using ValidIfIterator = decltype (std::next (std::declval<It>()));
public:
//==============================================================================
template <typename... Items>
ParameterLayout (std::unique_ptr<Items>... items) { add (std::move (items)...); }
template <typename It, typename = ValidIfIterator<It>>
ParameterLayout (It begin, It end) { add (begin, end); }
template <typename... Items>
void add (std::unique_ptr<Items>... items)
{
parameters.reserve (parameters.size() + sizeof... (items));
// We can replace this with some nicer code once generic lambdas become available. A
// sequential context like an array initialiser is required to ensure we get the correct
// order from the parameter pack.
int unused[] { (parameters.emplace_back (MakeContents() (std::move (items))), 0)... };
ignoreUnused (unused);
}
template <typename It, typename = ValidIfIterator<It>>
void add (It begin, It end)
{
parameters.reserve (parameters.size() + std::size_t (std::distance (begin, end)));
std::transform (std::make_move_iterator (begin),
std::make_move_iterator (end),
std::back_inserter (parameters),
MakeContents());
}
ParameterLayout (const ParameterLayout& other) = delete;
ParameterLayout (ParameterLayout&& other) noexcept { swap (other); }
ParameterLayout& operator= (const ParameterLayout& other) = delete;
ParameterLayout& operator= (ParameterLayout&& other) noexcept { swap (other); return *this; }
void swap (ParameterLayout& other) noexcept { std::swap (other.parameters, parameters); }
private:
//==============================================================================
struct Visitor
{
virtual ~Visitor() = default;
// If you have a compiler error telling you that there is no matching
// member function to call for 'visit', then you are probably attempting
// to add a parameter that is not derived from RangedAudioParameter to
// the AudioProcessorValueTreeState.
virtual void visit (std::unique_ptr<RangedAudioParameter>) const = 0;
virtual void visit (std::unique_ptr<AudioProcessorParameterGroup>) const = 0;
};
struct ParameterStorageBase
{
virtual ~ParameterStorageBase() = default;
virtual void accept (const Visitor& visitor) = 0;
};
template <typename Contents>
struct ParameterStorage : ParameterStorageBase
{
explicit ParameterStorage (std::unique_ptr<Contents> input) : contents (std::move (input)) {}
void accept (const Visitor& visitor) override { visitor.visit (std::move (contents)); }
std::unique_ptr<Contents> contents;
};
struct MakeContents final
{
template <typename Item>
std::unique_ptr<ParameterStorageBase> operator() (std::unique_ptr<Item> item) const
{
return std::unique_ptr<ParameterStorageBase> (new ParameterStorage<Item> (std::move (item)));
}
};
void add() {}
friend class AudioProcessorValueTreeState;
std::vector<std::unique_ptr<ParameterStorageBase>> parameters;
};
//==============================================================================
/** Creates a state object for a given processor, and sets up all the parameters
that will control that processor.
You should *not* assign a new ValueTree to the state, or call
createAndAddParameter, after using this constructor.
Note that each AudioProcessorValueTreeState should be attached to only one
processor, and must have the same lifetime as the processor, as they will
have dependencies on each other.
The ParameterLayout parameter has a set of constructors that allow you to
add multiple RangedAudioParameters and AudioProcessorParameterGroups containing
RangedAudioParameters to the AudioProcessorValueTreeState inside this constructor.
@code
YourAudioProcessor()
: apvts (*this, &undoManager, "PARAMETERS",
{ std::make_unique<AudioParameterFloat> ("a", "Parameter A", NormalisableRange<float> (-100.0f, 100.0f), 0),
std::make_unique<AudioParameterInt> ("b", "Parameter B", 0, 5, 2) })
@endcode
To add parameters programatically you can call `add` repeatedly on a
ParameterLayout instance:
@code
AudioProcessorValueTreeState::ParameterLayout createParameterLayout()
{
AudioProcessorValueTreeState::ParameterLayout layout;
for (int i = 1; i < 9; ++i)
layout.add (std::make_unique<AudioParameterInt> (String (i), String (i), 0, i, 0));
return layout;
}
YourAudioProcessor()
: apvts (*this, &undoManager, "PARAMETERS", createParameterLayout())
{
}
@endcode
@param processorToConnectTo The Processor that will be managed by this object
@param undoManagerToUse An optional UndoManager to use; pass nullptr if no UndoManager is required
@param valueTreeType The identifier used to initialise the internal ValueTree
@param parameterLayout An object that holds all parameters and parameter groups that the
AudioProcessor should use.
*/
AudioProcessorValueTreeState (AudioProcessor& processorToConnectTo,
UndoManager* undoManagerToUse,
const Identifier& valueTreeType,
ParameterLayout parameterLayout);
/** This constructor is discouraged and will be deprecated in a future version of JUCE!
Use the other constructor instead.
Creates a state object for a given processor.
The UndoManager is optional and can be a nullptr. After creating your state object,
you should add parameters with the createAndAddParameter() method. Note that each
AudioProcessorValueTreeState should be attached to only one processor, and must have
the same lifetime as the processor, as they will have dependencies on each other.
*/
AudioProcessorValueTreeState (AudioProcessor& processorToConnectTo, UndoManager* undoManagerToUse);
/** Destructor. */
~AudioProcessorValueTreeState() override;
//==============================================================================
#ifndef DOXYGEN
/** Previous calls to
@code
createAndAddParameter (paramID1, paramName1, ...);
@endcode
can be replaced with
@code
using Parameter = AudioProcessorValueTreeState::Parameter;
createAndAddParameter (std::make_unique<Parameter> (paramID1, paramName1, ...));
@endcode
However, a much better approach is to use the AudioProcessorValueTreeState
constructor directly
@code
using Parameter = AudioProcessorValueTreeState::Parameter;
YourAudioProcessor()
: apvts (*this, &undoManager, "PARAMETERS", { std::make_unique<Parameter> (paramID1, paramName1, ...),
std::make_unique<Parameter> (paramID2, paramName2, ...),
... })
@endcode
@see AudioProcessorValueTreeState::AudioProcessorValueTreeState
This function creates and returns a new parameter object for controlling a
parameter with the given ID.
Calling this will create and add a special type of AudioProcessorParameter to the
AudioProcessor to which this state is attached.
*/
[[deprecated ("This function is deprecated and will be removed in a future version of JUCE! "
"See the method docs for a code example of the replacement methods.")]]
RangedAudioParameter* createAndAddParameter (const String& parameterID,
const String& parameterName,
const String& labelText,
NormalisableRange<float> valueRange,
float defaultValue,
std::function<String (float)> valueToTextFunction,
std::function<float (const String&)> textToValueFunction,
bool isMetaParameter = false,
bool isAutomatableParameter = true,
bool isDiscrete = false,
AudioProcessorParameter::Category parameterCategory = AudioProcessorParameter::genericParameter,
bool isBoolean = false);
#endif
/** This function adds a parameter to the attached AudioProcessor and that parameter will
be managed by this AudioProcessorValueTreeState object.
*/
RangedAudioParameter* createAndAddParameter (std::unique_ptr<RangedAudioParameter> parameter);
//==============================================================================
/** Returns a parameter by its ID string. */
RangedAudioParameter* getParameter (StringRef parameterID) const noexcept;
/** Returns a pointer to a floating point representation of a particular parameter which a realtime
process can read to find out its current value.
Note that calling this method from within AudioProcessorValueTreeState::Listener::parameterChanged()
is not guaranteed to return an up-to-date value for the parameter.
*/
std::atomic<float>* getRawParameterValue (StringRef parameterID) const noexcept;
//==============================================================================
/** A listener class that can be attached to an AudioProcessorValueTreeState.
Use AudioProcessorValueTreeState::addParameterListener() to register a callback.
*/
struct JUCE_API Listener
{
virtual ~Listener() = default;
/** This callback method is called by the AudioProcessorValueTreeState when a parameter changes.
Within this call, retrieving the value of the parameter that has changed via the getRawParameterValue()
or getParameter() methods is not guaranteed to return the up-to-date value. If you need this you should
instead use the newValue parameter.
*/
virtual void parameterChanged (const String& parameterID, float newValue) = 0;
};
/** Attaches a callback to one of the parameters, which will be called when the parameter changes. */
void addParameterListener (StringRef parameterID, Listener* listener);
/** Removes a callback that was previously added with addParameterCallback(). */
void removeParameterListener (StringRef parameterID, Listener* listener);
//==============================================================================
/** Returns a Value object that can be used to control a particular parameter. */
Value getParameterAsValue (StringRef parameterID) const;
/** Returns the range that was set when the given parameter was created. */
NormalisableRange<float> getParameterRange (StringRef parameterID) const noexcept;
//==============================================================================
/** Returns a copy of the state value tree.
The AudioProcessorValueTreeState's ValueTree is updated internally on the
message thread, but there may be cases when you may want to access the state
from a different thread (getStateInformation is a good example). This method
flushes all pending audio parameter value updates and returns a copy of the
state in a thread safe way.
Note: This method uses locks to synchronise thread access, so whilst it is
thread-safe, it is not realtime-safe. Do not call this method from within
your audio processing code!
*/
ValueTree copyState();
/** Replaces the state value tree.
The AudioProcessorValueTreeState's ValueTree is updated internally on the
message thread, but there may be cases when you may want to modify the state
from a different thread (setStateInformation is a good example). This method
allows you to replace the state in a thread safe way.
Note: This method uses locks to synchronise thread access, so whilst it is
thread-safe, it is not realtime-safe. Do not call this method from within
your audio processing code!
*/
void replaceState (const ValueTree& newState);
//==============================================================================
/** A reference to the processor with which this state is associated. */
AudioProcessor& processor;
/** The state of the whole processor.
This must be initialised after all calls to createAndAddParameter().
You can replace this with your own ValueTree object, and can add properties and
children to the tree. This class will automatically add children for each of the
parameter objects that are created by createAndAddParameter().
*/
ValueTree state;
/** Provides access to the undo manager that this object is using. */
UndoManager* const undoManager;
private:
//==============================================================================
class ParameterAdapter;
public:
//==============================================================================
/** A parameter class that maintains backwards compatibility with deprecated
AudioProcessorValueTreeState functionality.
Previous calls to
@code
createAndAddParameter (paramID1, paramName1, ...);
@endcode
can be replaced with
@code
using Parameter = AudioProcessorValueTreeState::Parameter;
createAndAddParameter (std::make_unique<Parameter> (paramID1, paramName1, ...));
@endcode
However, a much better approach is to use the AudioProcessorValueTreeState
constructor directly
@code
using Parameter = AudioProcessorValueTreeState::Parameter;
YourAudioProcessor()
: apvts (*this, &undoManager, "PARAMETERS", { std::make_unique<Parameter> (paramID1, paramName1, ...),
std::make_unique<Parameter> (paramID2, paramName2, ...),
... })
@endcode
*/
class Parameter final : public AudioParameterFloat
{
public:
Parameter (const String& parameterID,
const String& parameterName,
const String& labelText,
NormalisableRange<float> valueRange,
float defaultValue,
std::function<String (float)> valueToTextFunction,
std::function<float (const String&)> textToValueFunction,
bool isMetaParameter = false,
bool isAutomatableParameter = true,
bool isDiscrete = false,
AudioProcessorParameter::Category parameterCategory = AudioProcessorParameter::genericParameter,
bool isBoolean = false);
float getDefaultValue() const override;
int getNumSteps() const override;
bool isMetaParameter() const override;
bool isAutomatable() const override;
bool isDiscrete() const override;
bool isBoolean() const override;
private:
void valueChanged (float) override;
std::function<void()> onValueChanged;
const float unsnappedDefault;
const bool metaParameter, automatable, discrete, boolean;
std::atomic<float> lastValue { -1.0f };
friend class AudioProcessorValueTreeState::ParameterAdapter;
};
//==============================================================================
/** An object of this class maintains a connection between a Slider and a parameter
in an AudioProcessorValueTreeState.
During the lifetime of this SliderAttachment object, it keeps the two things in
sync, making it easy to connect a slider to a parameter. When this object is
deleted, the connection is broken. Make sure that your AudioProcessorValueTreeState
and Slider aren't deleted before this object!
*/
class JUCE_API SliderAttachment
{
public:
SliderAttachment (AudioProcessorValueTreeState& stateToUse,
const String& parameterID,
Slider& slider);
private:
std::unique_ptr<SliderParameterAttachment> attachment;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderAttachment)
};
//==============================================================================
/** An object of this class maintains a connection between a ComboBox and a parameter
in an AudioProcessorValueTreeState.
Combobox items will be spaced linearly across the range of the parameter. For
example if the range is specified by NormalisableRange<float> (-0.5f, 0.5f, 0.5f)
and you add three items then the first will be mapped to a value of -0.5, the
second to 0, and the third to 0.5.
During the lifetime of this ComboBoxAttachment object, it keeps the two things in
sync, making it easy to connect a combo box to a parameter. When this object is
deleted, the connection is broken. Make sure that your AudioProcessorValueTreeState
and ComboBox aren't deleted before this object!
*/
class JUCE_API ComboBoxAttachment
{
public:
ComboBoxAttachment (AudioProcessorValueTreeState& stateToUse,
const String& parameterID,
ComboBox& combo);
private:
std::unique_ptr<ComboBoxParameterAttachment> attachment;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBoxAttachment)
};
//==============================================================================
/** An object of this class maintains a connection between a Button and a parameter
in an AudioProcessorValueTreeState.
During the lifetime of this ButtonAttachment object, it keeps the two things in
sync, making it easy to connect a button to a parameter. When this object is
deleted, the connection is broken. Make sure that your AudioProcessorValueTreeState
and Button aren't deleted before this object!
*/
class JUCE_API ButtonAttachment
{
public:
ButtonAttachment (AudioProcessorValueTreeState& stateToUse,
const String& parameterID,
Button& button);
private:
std::unique_ptr<ButtonParameterAttachment> attachment;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonAttachment)
};
private:
//==============================================================================
/** Code that looks like this:
@code
auto paramA = apvts.createParameter ("a", "Parameter A", {}, { -100, 100 }, ...);
auto paramB = apvts.createParameter ("b", "Parameter B", {}, { 0, 5 }, ...);
addParameterGroup (std::make_unique<AudioProcessorParameterGroup> ("g1", "Group 1", " | ", std::move (paramA), std::move (paramB)));
apvts.state = ValueTree (Identifier ("PARAMETERS"));
@endcode
can instead create the APVTS like this, avoiding the two-step initialization process and leveraging one of JUCE's
pre-built parameter types (or your own custom type derived from RangedAudioParameter):
@code
using Parameter = AudioProcessorValueTreeState::Parameter;
YourAudioProcessor()
: apvts (*this, &undoManager, "PARAMETERS",
{ std::make_unique<AudioProcessorParameterGroup> ("g1", "Group 1", " | ",
std::make_unique<Parameter> ("a", "Parameter A", "", NormalisableRange<float> (-100, 100), ...),
std::make_unique<Parameter> ("b", "Parameter B", "", NormalisableRange<float> (0, 5), ...)) })
@endcode
*/
[[deprecated ("This method was introduced to allow you to use AudioProcessorValueTreeState parameters in "
"an AudioProcessorParameterGroup, but there is now a much nicer way to achieve this. See the "
"method docs for a code example.")]]
std::unique_ptr<RangedAudioParameter> createParameter (const String&, const String&, const String&, NormalisableRange<float>,
float, std::function<String (float)>, std::function<float (const String&)>,
bool, bool, bool, AudioProcessorParameter::Category, bool);
//==============================================================================
#if JUCE_UNIT_TESTS
friend struct ParameterAdapterTests;
#endif
void addParameterAdapter (RangedAudioParameter&);
ParameterAdapter* getParameterAdapter (StringRef) const;
bool flushParameterValuesToValueTree();
void setNewState (ValueTree);
void timerCallback() override;
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
void valueTreeRedirected (ValueTree&) override;
void updateParameterConnectionsToChildTrees();
const Identifier valueType { "PARAM" }, valuePropertyID { "value" }, idPropertyID { "id" };
struct StringRefLessThan final
{
bool operator() (StringRef a, StringRef b) const noexcept { return a.text.compare (b.text) < 0; }
};
std::map<StringRef, std::unique_ptr<ParameterAdapter>, StringRefLessThan> adapterTable;
CriticalSection valueTreeChanging;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorValueTreeState)
};
} // namespace juce

View File

@@ -0,0 +1,88 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** Create a derived implementation of this class and pass it to
AudioPluginInstance::getExtensions() to retrieve format-specific
information about a plugin instance.
Note that the references passed to the visit member functions are only
guaranteed to live for the duration of the function call, so don't
store pointers to these objects! If you need to store and reuse
format-specific information, it is recommended to copy the result
of the function calls that you care about. For example, you should
store the result of VST::getAEffectPtr() rather than storing a pointer
to the VST instance.
@tags{Audio}
*/
struct ExtensionsVisitor
{
/** Indicates that there is no platform specific information available. */
struct Unknown {};
/** Can be used to retrieve information about a VST3 that is wrapped by an AudioProcessor. */
struct VST3Client
{
virtual ~VST3Client() = default;
virtual void* getIComponentPtr() const noexcept = 0;
virtual MemoryBlock getPreset() const = 0;
virtual bool setPreset (const MemoryBlock&) const = 0;
};
/** Can be used to retrieve information about an AudioUnit that is wrapped by an AudioProcessor. */
struct AudioUnitClient
{
virtual ~AudioUnitClient() = default;
virtual void* getAudioUnitHandle() const noexcept = 0;
};
/** Can be used to retrieve information about a VST that is wrapped by an AudioProcessor. */
struct VSTClient
{
virtual ~VSTClient() = default;
virtual void* getAEffectPtr() const noexcept = 0;
};
virtual ~ExtensionsVisitor() = default;
/** Will be called if there is no platform specific information available. */
virtual void visitUnknown (const Unknown&) {}
/** Called with VST3-specific information. */
virtual void visitVST3Client (const VST3Client&) {}
/** Called with VST-specific information. */
virtual void visitVSTClient (const VSTClient&) {}
/** Called with AU-specific information. */
virtual void visitAudioUnitClient (const AudioUnitClient&) {}
};
} // namespace juce

View File

@@ -0,0 +1,271 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
ParameterAttachment::ParameterAttachment (RangedAudioParameter& param,
std::function<void (float)> parameterChangedCallback,
UndoManager* um)
: parameter (param),
undoManager (um),
setValue (std::move (parameterChangedCallback))
{
parameter.addListener (this);
}
ParameterAttachment::~ParameterAttachment()
{
parameter.removeListener (this);
cancelPendingUpdate();
}
void ParameterAttachment::sendInitialUpdate()
{
parameterValueChanged ({}, parameter.getValue());
}
void ParameterAttachment::setValueAsCompleteGesture (float newDenormalisedValue)
{
callIfParameterValueChanged (newDenormalisedValue, [this] (float f)
{
beginGesture();
parameter.setValueNotifyingHost (f);
endGesture();
});
}
void ParameterAttachment::beginGesture()
{
if (undoManager != nullptr)
undoManager->beginNewTransaction();
parameter.beginChangeGesture();
}
void ParameterAttachment::setValueAsPartOfGesture (float newDenormalisedValue)
{
callIfParameterValueChanged (newDenormalisedValue, [this] (float f)
{
parameter.setValueNotifyingHost (f);
});
}
void ParameterAttachment::endGesture()
{
parameter.endChangeGesture();
}
template <typename Callback>
void ParameterAttachment::callIfParameterValueChanged (float newDenormalisedValue,
Callback&& callback)
{
const auto newValue = normalise (newDenormalisedValue);
if (parameter.getValue() != newValue)
callback (newValue);
}
void ParameterAttachment::parameterValueChanged (int, float newValue)
{
lastValue = newValue;
if (MessageManager::getInstance()->isThisTheMessageThread())
{
cancelPendingUpdate();
handleAsyncUpdate();
}
else
{
triggerAsyncUpdate();
}
}
void ParameterAttachment::handleAsyncUpdate()
{
if (setValue != nullptr)
setValue (parameter.convertFrom0to1 (lastValue));
}
//==============================================================================
SliderParameterAttachment::SliderParameterAttachment (RangedAudioParameter& param,
Slider& s,
UndoManager* um)
: slider (s),
attachment (param, [this] (float f) { setValue (f); }, um)
{
slider.valueFromTextFunction = [&param] (const String& text) { return (double) param.convertFrom0to1 (param.getValueForText (text)); };
slider.textFromValueFunction = [&param] (double value) { return param.getText (param.convertTo0to1 ((float) value), 0); };
slider.setDoubleClickReturnValue (true, param.convertFrom0to1 (param.getDefaultValue()));
auto range = param.getNormalisableRange();
auto convertFrom0To1Function = [range] (double currentRangeStart,
double currentRangeEnd,
double normalisedValue) mutable
{
range.start = (float) currentRangeStart;
range.end = (float) currentRangeEnd;
return (double) range.convertFrom0to1 ((float) normalisedValue);
};
auto convertTo0To1Function = [range] (double currentRangeStart,
double currentRangeEnd,
double mappedValue) mutable
{
range.start = (float) currentRangeStart;
range.end = (float) currentRangeEnd;
return (double) range.convertTo0to1 ((float) mappedValue);
};
auto snapToLegalValueFunction = [range] (double currentRangeStart,
double currentRangeEnd,
double mappedValue) mutable
{
range.start = (float) currentRangeStart;
range.end = (float) currentRangeEnd;
return (double) range.snapToLegalValue ((float) mappedValue);
};
NormalisableRange<double> newRange { (double) range.start,
(double) range.end,
std::move (convertFrom0To1Function),
std::move (convertTo0To1Function),
std::move (snapToLegalValueFunction) };
newRange.interval = range.interval;
newRange.skew = range.skew;
newRange.symmetricSkew = range.symmetricSkew;
slider.setNormalisableRange (newRange);
sendInitialUpdate();
slider.valueChanged();
slider.addListener (this);
}
SliderParameterAttachment::~SliderParameterAttachment()
{
slider.removeListener (this);
}
void SliderParameterAttachment::sendInitialUpdate() { attachment.sendInitialUpdate(); }
void SliderParameterAttachment::setValue (float newValue)
{
const ScopedValueSetter<bool> svs (ignoreCallbacks, true);
slider.setValue (newValue, sendNotificationSync);
}
void SliderParameterAttachment::sliderValueChanged (Slider*)
{
if (ignoreCallbacks || ModifierKeys::currentModifiers.isRightButtonDown())
return;
attachment.setValueAsPartOfGesture ((float) slider.getValue());
}
//==============================================================================
ComboBoxParameterAttachment::ComboBoxParameterAttachment (RangedAudioParameter& param,
ComboBox& c,
UndoManager* um)
: comboBox (c),
storedParameter (param),
attachment (param, [this] (float f) { setValue (f); }, um)
{
sendInitialUpdate();
comboBox.addListener (this);
}
ComboBoxParameterAttachment::~ComboBoxParameterAttachment()
{
comboBox.removeListener (this);
}
void ComboBoxParameterAttachment::sendInitialUpdate()
{
attachment.sendInitialUpdate();
}
void ComboBoxParameterAttachment::setValue (float newValue)
{
const auto normValue = storedParameter.convertTo0to1 (newValue);
const auto index = roundToInt (normValue * (float) (comboBox.getNumItems() - 1));
if (index == comboBox.getSelectedItemIndex())
return;
const ScopedValueSetter<bool> svs (ignoreCallbacks, true);
comboBox.setSelectedItemIndex (index, sendNotificationSync);
}
void ComboBoxParameterAttachment::comboBoxChanged (ComboBox*)
{
if (ignoreCallbacks)
return;
const auto numItems = comboBox.getNumItems();
const auto selected = (float) comboBox.getSelectedItemIndex();
const auto newValue = numItems > 1 ? selected / (float) (numItems - 1)
: 0.0f;
attachment.setValueAsCompleteGesture (storedParameter.convertFrom0to1 (newValue));
}
//==============================================================================
ButtonParameterAttachment::ButtonParameterAttachment (RangedAudioParameter& param,
Button& b,
UndoManager* um)
: button (b),
attachment (param, [this] (float f) { setValue (f); }, um)
{
sendInitialUpdate();
button.addListener (this);
}
ButtonParameterAttachment::~ButtonParameterAttachment()
{
button.removeListener (this);
}
void ButtonParameterAttachment::sendInitialUpdate()
{
attachment.sendInitialUpdate();
}
void ButtonParameterAttachment::setValue (float newValue)
{
const ScopedValueSetter<bool> svs (ignoreCallbacks, true);
button.setToggleState (newValue >= 0.5f, sendNotificationSync);
}
void ButtonParameterAttachment::buttonClicked (Button*)
{
if (ignoreCallbacks)
return;
attachment.setValueAsCompleteGesture (button.getToggleState() ? 1.0f : 0.0f);
}
} // namespace juce

View File

@@ -0,0 +1,251 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/** Used to implement 'attachments' or 'controllers' that link a plug-in
parameter to a UI element.
To implement a new attachment type, create a new class which includes an
instance of this class as a data member. Your class should pass a function
to the constructor of the ParameterAttachment, which will then be called on
the message thread when the parameter changes. You can use this function to
update the state of the UI control. Your class should also register as a
listener of the UI control and respond to respond to changes in the UI element
by calling either setValueAsCompleteGesture or beginGesture,
setValueAsPartOfGesture and endGesture.
Make sure to call `sendInitialUpdate` at the end of your new attachment's
constructor, so that the UI immediately reflects the state of the parameter.
@tags{Audio}
*/
class ParameterAttachment : private AudioProcessorParameter::Listener,
private AsyncUpdater
{
public:
/** Listens to a parameter and calls the the provided function in response to
parameter changes. If an undoManager is supplied `beginNewTransaction` will
be called on it whenever the UI requests a parameter change via this attachment.
@param parameter The parameter to which this attachment will listen
@param parameterChangedCallback The function that will be called on the message thread in response
to parameter changes
@param undoManager The UndoManager that will be used to begin transactions when the UI
requests a parameter change.
*/
ParameterAttachment (RangedAudioParameter& parameter,
std::function<void (float)> parameterChangedCallback,
UndoManager* undoManager = nullptr);
/** Destructor. */
~ParameterAttachment() override;
/** Calls the parameterChangedCallback function that was registered in
the constructor, making the UI reflect the current parameter state.
This function should be called after doing any necessary setup on
the UI control that is being managed (e.g. adding ComboBox entries,
making buttons toggle-able).
*/
void sendInitialUpdate();
/** Triggers a full gesture message on the managed parameter.
Call this in the listener callback of the UI control in response
to a one-off change in the UI like a button-press.
*/
void setValueAsCompleteGesture (float newDenormalisedValue);
/** Begins a gesture on the managed parameter.
Call this when the UI is about to begin a continuous interaction,
like when the mouse button is pressed on a slider.
*/
void beginGesture();
/** Updates the parameter value during a gesture.
Call this during a continuous interaction, like a slider value
changed callback.
*/
void setValueAsPartOfGesture (float newDenormalisedValue);
/** Ends a gesture on the managed parameter.
Call this when the UI has finished a continuous interaction,
like when the mouse button is released on a slider.
*/
void endGesture();
private:
float normalise (float f) const { return parameter.convertTo0to1 (f); }
template <typename Callback>
void callIfParameterValueChanged (float newDenormalisedValue, Callback&& callback);
void parameterValueChanged (int, float) override;
void parameterGestureChanged (int, bool) override {}
void handleAsyncUpdate() override;
RangedAudioParameter& parameter;
std::atomic<float> lastValue { 0.0f };
UndoManager* undoManager = nullptr;
std::function<void (float)> setValue;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterAttachment)
};
//==============================================================================
/** An object of this class maintains a connection between a Slider and a
plug-in parameter.
During the lifetime of this object it keeps the two things in sync, making
it easy to connect a slider to a parameter. When this object is deleted, the
connection is broken. Make sure that your parameter and Slider are not
deleted before this object!
@tags{Audio}
*/
class SliderParameterAttachment : private Slider::Listener
{
public:
/** Creates a connection between a plug-in parameter and a Slider.
@param parameter The parameter to use
@param slider The Slider to use
@param undoManager An optional UndoManager
*/
SliderParameterAttachment (RangedAudioParameter& parameter, Slider& slider,
UndoManager* undoManager = nullptr);
/** Destructor. */
~SliderParameterAttachment() override;
/** Call this after setting up your slider in the case where you need to do
extra setup after constructing this attachment.
*/
void sendInitialUpdate();
private:
void setValue (float newValue);
void sliderValueChanged (Slider*) override;
void sliderDragStarted (Slider*) override { attachment.beginGesture(); }
void sliderDragEnded (Slider*) override { attachment.endGesture(); }
Slider& slider;
ParameterAttachment attachment;
bool ignoreCallbacks = false;
};
//==============================================================================
/** An object of this class maintains a connection between a ComboBox and a
plug-in parameter.
ComboBox items will be spaced linearly across the range of the parameter. For
example if the range is specified by NormalisableRange<float> (-0.5f, 0.5f, 0.5f)
and you add three items then the first will be mapped to a value of -0.5, the
second to 0, and the third to 0.5.
During the lifetime of this object it keeps the two things in sync, making it
easy to connect a combo box to a parameter. When this object is deleted, the
connection is broken. Make sure that your parameter and ComboBox are not deleted
before this object!
@tags{Audio}
*/
class ComboBoxParameterAttachment : private ComboBox::Listener
{
public:
/** Creates a connection between a plug-in parameter and a ComboBox.
@param parameter The parameter to use
@param combo The ComboBox to use
@param undoManager An optional UndoManager
*/
ComboBoxParameterAttachment (RangedAudioParameter& parameter, ComboBox& combo,
UndoManager* undoManager = nullptr);
/** Destructor. */
~ComboBoxParameterAttachment() override;
/** Call this after setting up your combo box in the case where you need to do
extra setup after constructing this attachment.
*/
void sendInitialUpdate();
private:
void setValue (float newValue);
void comboBoxChanged (ComboBox*) override;
ComboBox& comboBox;
RangedAudioParameter& storedParameter;
ParameterAttachment attachment;
bool ignoreCallbacks = false;
};
//==============================================================================
/** An object of this class maintains a connection between a Button and a
plug-in parameter.
During the lifetime of this object it keeps the two things in sync, making it
easy to connect a button to a parameter. When this object is deleted, the
connection is broken. Make sure that your parameter and Button are not deleted
before this object!
@tags{Audio}
*/
class ButtonParameterAttachment : private Button::Listener
{
public:
/** Creates a connection between a plug-in parameter and a Button.
@param parameter The parameter to use
@param button The Button to use
@param undoManager An optional UndoManager
*/
ButtonParameterAttachment (RangedAudioParameter& parameter, Button& button,
UndoManager* undoManager = nullptr);
/** Destructor. */
~ButtonParameterAttachment() override;
/** Call this after setting up your button in the case where you need to do
extra setup after constructing this attachment.
*/
void sendInitialUpdate();
private:
void setValue (float newValue);
void buttonClicked (Button*) override;
Button& button;
ParameterAttachment attachment;
bool ignoreCallbacks = false;
};
} // namespace juce

View File

@@ -0,0 +1,315 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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_Enable_IAA && JucePlugin_Build_Standalone && JUCE_IOS && (! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP)
bool JUCE_CALLTYPE juce_isInterAppAudioConnected();
void JUCE_CALLTYPE juce_switchToHostApplication();
juce::Image JUCE_CALLTYPE juce_getIAAHostIcon (int);
#endif
namespace juce
{
Image JUCE_API getIconFromApplication (const String&, const int);
AudioProcessor::WrapperType PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_Undefined;
std::function<bool (AudioProcessor&)> PluginHostType::jucePlugInIsRunningInAudioSuiteFn = nullptr;
String PluginHostType::hostIdReportedByWrapper;
bool PluginHostType::isInterAppAudioConnected() const
{
#if JucePlugin_Enable_IAA && JucePlugin_Build_Standalone && JUCE_IOS && (! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP)
if (getPluginLoadedAs() == AudioProcessor::wrapperType_Standalone)
return juce_isInterAppAudioConnected();
#endif
return false;
}
void PluginHostType::switchToHostApplication() const
{
#if JucePlugin_Enable_IAA && JucePlugin_Build_Standalone && JUCE_IOS && (! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP)
if (getPluginLoadedAs() == AudioProcessor::wrapperType_Standalone)
juce_switchToHostApplication();
#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;
}
Image PluginHostType::getHostIcon (int size) const
{
ignoreUnused (size);
#if JucePlugin_Enable_IAA && JucePlugin_Build_Standalone && JUCE_IOS && (! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP)
if (isInterAppAudioConnected())
return juce_getIAAHostIcon (size);
#endif
#if JUCE_MAC
String bundlePath (getHostPath().upToLastOccurrenceOf (".app", true, true));
return getIconFromApplication (bundlePath, size);
#endif
return Image();
}
const char* PluginHostType::getHostDescription() const noexcept
{
switch (type)
{
case AbletonLive6: return "Ableton Live 6";
case AbletonLive7: return "Ableton Live 7";
case AbletonLive8: return "Ableton Live 8";
case AbletonLive9: return "Ableton Live 9";
case AbletonLive10: return "Ableton Live 10";
case AbletonLive11: return "Ableton Live 11";
case AbletonLiveGeneric: return "Ableton Live";
case AdobeAudition: return "Adobe Audition";
case AdobePremierePro: return "Adobe Premiere";
case AppleGarageBand: return "Apple GarageBand";
case AppleLogic: return "Apple Logic";
case AppleMainStage: return "Apple MainStage";
case Ardour: return "Ardour";
case AULab: return "AU Lab";
case AvidProTools: return "ProTools";
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 pluginval: return "pluginval";
case MergingPyramix: return "Pyramix";
case MuseReceptorGeneric: return "Muse Receptor";
case Reaper: return "Reaper";
case Reason: return "Reason";
case Renoise: return "Renoise";
case SADiE: return "SADiE";
case SteinbergCubase4: return "Steinberg Cubase 4";
case SteinbergCubase5: return "Steinberg Cubase 5";
case SteinbergCubase5Bridged: return "Steinberg Cubase 5 Bridged";
case SteinbergCubase6: return "Steinberg Cubase 6";
case SteinbergCubase7: return "Steinberg Cubase 7";
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 SteinbergCubase10_5: return "Steinberg Cubase 10.5";
case SteinbergCubaseGeneric: return "Steinberg Cubase";
case SteinbergNuendo3: return "Steinberg Nuendo 3";
case SteinbergNuendo4: return "Steinberg Nuendo 4";
case SteinbergNuendo5: return "Steinberg Nuendo 5";
case SteinbergNuendoGeneric: return "Steinberg Nuendo";
case SteinbergWavelab5: return "Steinberg Wavelab 5";
case SteinbergWavelab6: return "Steinberg Wavelab 6";
case SteinbergWavelab7: return "Steinberg Wavelab 7";
case SteinbergWavelab8: return "Steinberg Wavelab 8";
case SteinbergWavelabGeneric: return "Steinberg Wavelab";
case SteinbergTestHost: return "Steinberg TestHost";
case StudioOne: return "Studio One";
case Tracktion3: return "Tracktion 3";
case TracktionGeneric: return "Tracktion";
case TracktionWaveform: return "Tracktion Waveform";
case VBVSTScanner: return "VBVSTScanner";
case ViennaEnsemblePro: return "Vienna Ensemble Pro";
case WaveBurner: return "WaveBurner";
case UnknownHost:
default: break;
}
return "Unknown";
}
PluginHostType::HostType PluginHostType::getHostType()
{
auto hostPath = getHostPath();
auto hostFilename = File (hostPath).getFileName();
#if JUCE_MAC
if (hostPath.containsIgnoreCase ("Final Cut Pro.app")) return FinalCut;
if (hostPath.containsIgnoreCase ("Final Cut Pro Trial.app")) return FinalCut;
if (hostPath.containsIgnoreCase ("Live 6")) return AbletonLive6;
if (hostPath.containsIgnoreCase ("Live 7")) return AbletonLive7;
if (hostPath.containsIgnoreCase ("Live 8")) return AbletonLive8;
if (hostPath.containsIgnoreCase ("Live 9")) return AbletonLive9;
if (hostPath.containsIgnoreCase ("Live 10")) return AbletonLive10;
if (hostPath.containsIgnoreCase ("Live 11")) return AbletonLive11;
if (hostFilename.containsIgnoreCase ("Live")) return AbletonLiveGeneric;
if (hostFilename.containsIgnoreCase ("Audition")) return AdobeAudition;
if (hostFilename.containsIgnoreCase ("Adobe Premiere")) return AdobePremierePro;
if (hostFilename.containsIgnoreCase ("GarageBand")) return AppleGarageBand;
if (hostFilename.containsIgnoreCase ("Logic")) return AppleLogic;
if (hostFilename.containsIgnoreCase ("MainStage")) return AppleMainStage;
if (hostFilename.containsIgnoreCase ("AU Lab")) return AULab;
if (hostFilename.containsIgnoreCase ("Pro Tools")) return AvidProTools;
if (hostFilename.containsIgnoreCase ("Nuendo 3")) return SteinbergNuendo3;
if (hostFilename.containsIgnoreCase ("Nuendo 4")) return SteinbergNuendo4;
if (hostFilename.containsIgnoreCase ("Nuendo 5")) return SteinbergNuendo5;
if (hostFilename.containsIgnoreCase ("Nuendo")) return SteinbergNuendoGeneric;
if (hostFilename.containsIgnoreCase ("Cubase 4")) return SteinbergCubase4;
if (hostFilename.containsIgnoreCase ("Cubase 5")) return SteinbergCubase5;
if (hostFilename.containsIgnoreCase ("Cubase 6")) return SteinbergCubase6;
if (hostFilename.containsIgnoreCase ("Cubase 7")) return SteinbergCubase7;
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 (hostPath.containsIgnoreCase ("Cubase 10.5.app")) return SteinbergCubase10_5;
if (hostFilename.containsIgnoreCase ("Cubase")) return SteinbergCubaseGeneric;
if (hostPath.containsIgnoreCase ("Wavelab 7")) return SteinbergWavelab7;
if (hostPath.containsIgnoreCase ("Wavelab 8")) return SteinbergWavelab8;
if (hostFilename.containsIgnoreCase ("Wavelab")) return SteinbergWavelabGeneric;
if (hostFilename.containsIgnoreCase ("WaveBurner")) return WaveBurner;
if (hostPath.containsIgnoreCase ("Digital Performer")) return DigitalPerformer;
if (hostFilename.containsIgnoreCase ("reaper")) return Reaper;
if (hostFilename.containsIgnoreCase ("Reason")) return Reason;
if (hostPath.containsIgnoreCase ("Studio One")) return StudioOne;
if (hostFilename.startsWithIgnoreCase ("Waveform")) return TracktionWaveform;
if (hostPath.containsIgnoreCase ("Tracktion 3")) return Tracktion3;
if (hostFilename.containsIgnoreCase ("Tracktion")) return TracktionGeneric;
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 ("pluginval")) return pluginval;
if (hostFilename.containsIgnoreCase ("AudioPluginHost")) return JUCEPluginHost;
if (hostFilename.containsIgnoreCase ("Vienna Ensemble Pro")) return ViennaEnsemblePro;
if (hostIdReportedByWrapper == "com.apple.logic.pro") return AppleLogic;
if (hostIdReportedByWrapper == "com.apple.garageband") return AppleGarageBand;
if (hostIdReportedByWrapper == "com.apple.mainstage") return AppleMainStage;
const auto procName = nsStringToJuce ([[NSRunningApplication currentApplication] localizedName]);
const auto matchesInOrder = [&] (const StringArray& strings)
{
return procName.matchesWildcard ("AUHostingService*(" + strings.joinIntoString ("*") + ")", false);
};
// Depending on localization settings, spaces are not always plain ascii spaces
if (matchesInOrder ({ "Logic", "Pro" })) return AppleLogic;
if (matchesInOrder ({ "GarageBand" })) return AppleGarageBand;
if (matchesInOrder ({ "MainStage" })) return AppleMainStage;
if (matchesInOrder ({ "Final", "Cut", "Pro" })) return FinalCut;
#elif JUCE_WINDOWS
if (hostFilename.containsIgnoreCase ("Live 6")) return AbletonLive6;
if (hostFilename.containsIgnoreCase ("Live 7")) return AbletonLive7;
if (hostFilename.containsIgnoreCase ("Live 8")) return AbletonLive8;
if (hostFilename.containsIgnoreCase ("Live 9")) return AbletonLive9;
if (hostFilename.containsIgnoreCase ("Live 10")) return AbletonLive10;
if (hostFilename.containsIgnoreCase ("Live 11")) return AbletonLive11;
if (hostFilename.containsIgnoreCase ("Live ")) return AbletonLiveGeneric;
if (hostFilename.containsIgnoreCase ("Audition")) return AdobeAudition;
if (hostFilename.containsIgnoreCase ("Adobe Premiere")) return AdobePremierePro;
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;
if (hostFilename.startsWithIgnoreCase ("Waveform")) return TracktionWaveform;
if (hostPath.containsIgnoreCase ("Tracktion 3")) return Tracktion3;
if (hostFilename.containsIgnoreCase ("Tracktion")) return TracktionGeneric;
if (hostFilename.containsIgnoreCase ("reaper")) return Reaper;
if (hostFilename.containsIgnoreCase ("Cubase4")) return SteinbergCubase4;
if (hostFilename.containsIgnoreCase ("Cubase5")) return SteinbergCubase5;
if (hostFilename.containsIgnoreCase ("Cubase6")) return SteinbergCubase6;
if (hostFilename.containsIgnoreCase ("Cubase7")) return SteinbergCubase7;
if (hostFilename.containsIgnoreCase ("Cubase8.exe")) return SteinbergCubase8;
if (hostFilename.containsIgnoreCase ("Cubase8.5.exe")) return SteinbergCubase8_5;
// 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.5.exe")
|| hostPath.containsIgnoreCase ("Cubase 10.5")) return SteinbergCubase10_5;
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;
if (hostPath.containsIgnoreCase ("Wavelab 6")) return SteinbergWavelab6;
if (hostPath.containsIgnoreCase ("Wavelab 7")) return SteinbergWavelab7;
if (hostPath.containsIgnoreCase ("Wavelab 8")) return SteinbergWavelab8;
if (hostPath.containsIgnoreCase ("Nuendo")) return SteinbergNuendoGeneric;
if (hostFilename.containsIgnoreCase ("Wavelab")) return SteinbergWavelabGeneric;
if (hostFilename.containsIgnoreCase ("TestHost")) return SteinbergTestHost;
if (hostFilename.containsIgnoreCase ("rm-host")) return MuseReceptorGeneric;
if (hostFilename.startsWith ("FL")) return FruityLoops;
if (hostFilename.contains ("ilbridge.")) return FruityLoops;
if (hostPath.containsIgnoreCase ("Studio One")) return StudioOne;
if (hostPath.containsIgnoreCase ("Digital Performer")) return DigitalPerformer;
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 ("Reason")) return Reason;
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 ("pluginval")) return pluginval;
if (hostFilename.containsIgnoreCase ("AudioPluginHost")) return JUCEPluginHost;
if (hostFilename.containsIgnoreCase ("Vienna Ensemble Pro")) return ViennaEnsemblePro;
#elif JUCE_LINUX || JUCE_BSD
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 ("pluginval")) return pluginval;
if (hostFilename.containsIgnoreCase ("AudioPluginHost")) return JUCEPluginHost;
#elif JUCE_IOS
#elif JUCE_ANDROID
#else
#error
#endif
return UnknownHost;
}
} // namespace juce

View File

@@ -0,0 +1,247 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A useful utility class to determine the host or DAW in which your plugin is
loaded.
Declare a PluginHostType object in your class to use it.
@tags{Audio}
*/
class PluginHostType
{
public:
//==============================================================================
PluginHostType() : type (getHostType()) {}
PluginHostType (const PluginHostType& other) = default;
PluginHostType& operator= (const PluginHostType& other) = default;
//==============================================================================
/** Represents the host type and also its version for some hosts. */
enum HostType
{
UnknownHost, /**< Represents an unknown host. */
AbletonLive6, /**< Represents Ableton Live 6. */
AbletonLive7, /**< Represents Ableton Live 7. */
AbletonLive8, /**< Represents Ableton Live 8. */
AbletonLive9, /**< Represents Ableton Live 9. */
AbletonLive10, /**< Represents Ableton Live 10. */
AbletonLive11, /**< Represents Ableton Live 11. */
AbletonLiveGeneric, /**< Represents Ableton Live. */
AdobeAudition, /**< Represents Adobe Audition. */
AdobePremierePro, /**< Represents Adobe Premiere Pro. */
AppleGarageBand, /**< Represents Apple GarageBand. */
AppleLogic, /**< Represents Apple Logic Pro. */
AppleMainStage, /**< Represents Apple Main Stage. */
Ardour, /**< Represents Ardour. */
AULab, /**< Represents AU Lab. */
AvidProTools, /**< Represents Avid Pro Tools. */
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. */
pluginval, /**< Represents pluginval. */
Reaper, /**< Represents Cockos Reaper. */
Reason, /**< Represents Reason. */
Renoise, /**< Represents Renoise. */
SADiE, /**< Represents SADiE. */
SteinbergCubase4, /**< Represents Steinberg Cubase 4. */
SteinbergCubase5, /**< Represents Steinberg Cubase 5. */
SteinbergCubase5Bridged, /**< Represents Steinberg Cubase 5 Bridged. */
SteinbergCubase6, /**< Represents Steinberg Cubase 6. */
SteinbergCubase7, /**< Represents Steinberg Cubase 7. */
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. */
SteinbergCubase10_5, /**< Represents Steinberg Cubase 10.5. */
SteinbergCubaseGeneric, /**< Represents Steinberg Cubase. */
SteinbergNuendo3, /**< Represents Steinberg Nuendo 3. */
SteinbergNuendo4, /**< Represents Steinberg Nuendo 4. */
SteinbergNuendo5, /**< Represents Steinberg Nuendo 5. */
SteinbergNuendoGeneric, /**< Represents Steinberg Nuendo. */
SteinbergWavelab5, /**< Represents Steinberg Wavelab 5. */
SteinbergWavelab6, /**< Represents Steinberg Wavelab 6. */
SteinbergWavelab7, /**< Represents Steinberg Wavelab 7. */
SteinbergWavelab8, /**< Represents Steinberg Wavelab 8. */
SteinbergWavelabGeneric, /**< Represents Steinberg Wavelab. */
SteinbergTestHost, /**< Represents Steinberg's VST3 Test Host. */
StudioOne, /**< Represents PreSonus Studio One. */
Tracktion3, /**< Represents Tracktion 3. */
TracktionGeneric, /**< Represents Tracktion. */
TracktionWaveform, /**< Represents Tracktion Waveform. */
VBVSTScanner, /**< Represents VB Audio VST Scanner. */
ViennaEnsemblePro, /**< Represents Vienna Ensemble Pro. */
WaveBurner /**< Represents Apple WaveBurner. */
};
HostType type;
//==============================================================================
/** Returns true if the host is any version of Ableton Live. */
bool isAbletonLive() const noexcept { return type == AbletonLive6 || type == AbletonLive7 || type == AbletonLive8
|| type == AbletonLive9 || type == AbletonLive10 || type == AbletonLive11
|| type == AbletonLiveGeneric; }
/** Returns true if the host is Adobe Audition. */
bool isAdobeAudition() const noexcept { return type == AdobeAudition; }
/** Returns true if the host is Ardour. */
bool isArdour() const noexcept { return type == Ardour; }
/** Returns true if the host is AU Lab. */
bool isAULab() const noexcept { return type == AULab; }
/** 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 == SteinbergCubase9_5 || type == SteinbergCubase10 || type == SteinbergCubase10_5 || 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. */
bool isCubaseBridged() const noexcept { return type == SteinbergCubase5Bridged; }
/** Returns true if the host is DaVinci Resolve. */
bool isDaVinciResolve() const noexcept { return type == DaVinciResolve; }
/** Returns true if the host is Digital Performer. */
bool isDigitalPerformer() const noexcept { return type == DigitalPerformer; }
/** Returns true if the host is Apple Final Cut Pro. */
bool isFinalCut() const noexcept { return type == FinalCut; }
/** Returns true if the host is Fruity Loops. */
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. */
bool isMainStage() const noexcept { return type == AppleMainStage; }
/** Returns true if the host is any version of Steinberg Nuendo. */
bool isNuendo() const noexcept { return type == SteinbergNuendo3 || type == SteinbergNuendo4 || type == SteinbergNuendo5 || type == SteinbergNuendoGeneric; }
/** Returns true if the host is pluginval. */
bool isPluginval() const noexcept { return type == pluginval; }
/** Returns true if the host is Adobe Premiere Pro. */
bool isPremiere() const noexcept { return type == AdobePremierePro; }
/** Returns true if the host is Avid Pro Tools. */
bool isProTools() const noexcept { return type == AvidProTools; }
/** Returns true if the host is Merging Pyramix. */
bool isPyramix() const noexcept { return type == MergingPyramix; }
/** Returns true if the host is Muse Receptor. */
bool isReceptor() const noexcept { return type == MuseReceptorGeneric; }
/** Returns true if the host is Cockos Reaper. */
bool isReaper() const noexcept { return type == Reaper; }
/** Returns true if the host is Reason. */
bool isReason() const noexcept { return type == Reason; }
/** Returns true if the host is Renoise. */
bool isRenoise() const noexcept { return type == Renoise; }
/** Returns true if the host is SADiE. */
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 || 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. */
bool isSteinberg() const noexcept { return isCubase() || isNuendo() || isWavelab() || isSteinbergTestHost(); }
/** Returns true if the host is PreSonus Studio One. */
bool isStudioOne() const noexcept { return type == StudioOne; }
/** Returns true if the host is any version of Tracktion. */
bool isTracktion() const noexcept { return type == Tracktion3 || type == TracktionGeneric || isTracktionWaveform(); }
/** Returns true if the host is Tracktion Waveform. */
bool isTracktionWaveform() const noexcept { return type == TracktionWaveform; }
/** Returns true if the host is VB Audio VST Scanner. */
bool isVBVSTScanner() const noexcept { return type == VBVSTScanner; }
/** Returns true if the host is Vienna Ensemble Pro. */
bool isViennaEnsemblePro() const noexcept { return type == ViennaEnsemblePro; }
/** Returns true if the host is Apple WaveBurner. */
bool isWaveBurner() const noexcept { return type == WaveBurner; }
/** Returns true if the host is any version of Steinberg WaveLab. */
bool isWavelab() const noexcept { return isWavelabLegacy() || type == SteinbergWavelab7 || type == SteinbergWavelab8 || type == SteinbergWavelabGeneric; }
/** Returns true if the host is Steinberg WaveLab 6 or below. */
bool isWavelabLegacy() const noexcept { return type == SteinbergWavelab5 || type == SteinbergWavelab6; }
//==============================================================================
/** Returns a human-readable description of the host. */
const char* getHostDescription() const noexcept;
//==============================================================================
/** Returns true if the plugin is connected with Inter-App Audio on iOS. */
bool isInterAppAudioConnected() const;
/** Switches to the host application when Inter-App Audio is used on iOS. */
void switchToHostApplication() const;
/** Gets the host app's icon when Inter-App Audio is used on iOS. */
Image getHostIcon (int size) const;
//==============================================================================
/** Returns the complete absolute path of the host application executable. */
static String getHostPath()
{
return File::getSpecialLocation (File::hostApplicationPath).getFullPathName();
}
//==============================================================================
/**
Returns the plug-in format via which the plug-in file was loaded. This value is
identical to AudioProcessor::wrapperType of the main audio processor of this
plug-in. This function is useful for code that does not have access to the
plug-in's main audio processor.
@see AudioProcessor::wrapperType
*/
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;
static String hostIdReportedByWrapper;
#endif
private:
static HostType getHostType();
};
} // namespace juce

View File

@@ -0,0 +1,59 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
RangedAudioParameter::RangedAudioParameter (const String& parameterID,
const String& parameterName,
const String& parameterLabel,
Category parameterCategory)
: AudioProcessorParameterWithID (parameterID, parameterName, parameterLabel, parameterCategory)
{
}
int RangedAudioParameter::getNumSteps() const
{
const auto& range = getNormalisableRange();
if (range.interval > 0)
return (static_cast<int> ((range.end - range.start) / range.interval) + 1);
return AudioProcessor::getDefaultNumParameterSteps();
}
float RangedAudioParameter::convertTo0to1 (float v) const noexcept
{
const auto& range = getNormalisableRange();
return range.convertTo0to1 (range.snapToLegalValue (v));
}
float RangedAudioParameter::convertFrom0to1 (float v) const noexcept
{
const auto& range = getNormalisableRange();
return range.snapToLegalValue (range.convertFrom0to1 (jlimit (0.0f, 1.0f, v)));
}
} // namespace juce

View File

@@ -0,0 +1,63 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/**
This abstract base class is used by some AudioProcessorParameter helper classes.
@see AudioParameterFloat, AudioParameterInt, AudioParameterBool, AudioParameterChoice
@tags{Audio}
*/
class JUCE_API RangedAudioParameter : public AudioProcessorParameterWithID
{
public:
/** The creation of this object requires providing a name and ID which will be
constant for its lifetime.
*/
RangedAudioParameter (const String& parameterID,
const String& parameterName,
const String& parameterLabel = {},
Category parameterCategory = AudioProcessorParameter::genericParameter);
/** Returns the range of values that the parameter can take. */
virtual const NormalisableRange<float>& getNormalisableRange() const = 0;
/** Returns the number of steps for this parameter based on the normalisable range's interval.
If you are using lambda functions to define the normalisable range's snapping behaviour
then you should override this function so that it returns the number of snapping points.
*/
int getNumSteps() const override;
/** Normalises and snaps a value based on the normalisable range. */
float convertTo0to1 (float v) const noexcept;
/** Denormalises and snaps a value based on the normalisable range. */
float convertFrom0to1 (float v) const noexcept;
};
} // namespace juce

View File

@@ -0,0 +1,95 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
// Forward declaration to avoid leaking implementation details.
namespace Steinberg
{
class FUnknown;
using TUID = char[16];
}
namespace juce
{
/** An interface to allow an AudioProcessor to implement extended VST3-specific
functionality.
To use this class, ensure that your AudioProcessor publicly inherits
from VST3ClientExtensions.
@see VSTCallbackHandler
@tags{Audio}
*/
struct VST3ClientExtensions
{
virtual ~VST3ClientExtensions() = default;
/** This function may be used by implementations of queryInterface()
in the VST3's implementation of IEditController to return
additional supported interfaces.
*/
virtual int32_t queryIEditController (const Steinberg::TUID, void** obj)
{
*obj = nullptr;
return -1;
}
/** This function may be used by implementations of queryInterface()
in the VST3's implementation of IAudioProcessor to return
additional supported interfaces.
*/
virtual int32_t queryIAudioProcessor (const Steinberg::TUID, void** obj)
{
*obj = nullptr;
return -1;
}
/** This may be called by the VST3 wrapper when the host sets an
IComponentHandler for the plugin to use.
You should not make any assumptions about how and when this will be
called - this function may not be called at all!
*/
virtual void setIComponentHandler (Steinberg::FUnknown*) {}
/** This may be called shortly after the AudioProcessor is constructed
with the current IHostApplication.
You should not make any assumptions about how and when this will be
called - this function may not be called at all!
*/
virtual void setIHostApplication (Steinberg::FUnknown*) {}
/** This function will be called to check whether the first input bus
should be designated as "kMain" or "kAux". Return true if the
first bus should be kMain, or false if the bus should be kAux.
All other input buses will always be designated kAux.
*/
virtual bool getPluginHasMainInput() const { return true; }
};
} // namespace juce

View File

@@ -0,0 +1,80 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
namespace juce
{
/** An interface to allow an AudioProcessor to send and receive VST specific calls from
the host.
To use this class, ensure that your AudioProcessor publicly inherits
from VSTCallbackHandler.
@see VST3ClientExtensions
@tags{Audio}
*/
struct 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.
*/
virtual pointer_sized_int handleVstPluginCanDo (int32 index,
pointer_sized_int value,
void* ptr,
float opt)
{
ignoreUnused (index, value, ptr, opt);
return 0;
}
/** This is called by the VST plug-in wrapper when it receives unhandled
vendor specific calls from the host.
*/
virtual pointer_sized_int handleVstManufacturerSpecific (int32 index,
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