upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
@ -39,10 +39,10 @@ class JUCE_API AudioPlayHead
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
AudioPlayHead() {}
|
||||
AudioPlayHead() = default;
|
||||
|
||||
public:
|
||||
virtual ~AudioPlayHead() {}
|
||||
virtual ~AudioPlayHead() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Frame rate types. */
|
||||
|
@ -75,6 +75,27 @@ String AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type)
|
||||
case ambisonicZ: return NEEDS_TRANS("Ambisonic Z");
|
||||
case topSideLeft: return NEEDS_TRANS("Top Side Left");
|
||||
case topSideRight: return NEEDS_TRANS("Top Side Right");
|
||||
case ambisonicACN4: return NEEDS_TRANS("Ambisonic 4");
|
||||
case ambisonicACN5: return NEEDS_TRANS("Ambisonic 5");
|
||||
case ambisonicACN6: return NEEDS_TRANS("Ambisonic 6");
|
||||
case ambisonicACN7: return NEEDS_TRANS("Ambisonic 7");
|
||||
case ambisonicACN8: return NEEDS_TRANS("Ambisonic 8");
|
||||
case ambisonicACN9: return NEEDS_TRANS("Ambisonic 9");
|
||||
case ambisonicACN10: return NEEDS_TRANS("Ambisonic 10");
|
||||
case ambisonicACN11: return NEEDS_TRANS("Ambisonic 11");
|
||||
case ambisonicACN12: return NEEDS_TRANS("Ambisonic 12");
|
||||
case ambisonicACN13: return NEEDS_TRANS("Ambisonic 13");
|
||||
case ambisonicACN14: return NEEDS_TRANS("Ambisonic 14");
|
||||
case ambisonicACN15: return NEEDS_TRANS("Ambisonic 15");
|
||||
case bottomFrontLeft: return NEEDS_TRANS("Bottom Front Left");
|
||||
case bottomFrontCentre: return NEEDS_TRANS("Bottom Front Centre");
|
||||
case bottomFrontRight: return NEEDS_TRANS("Bottom Front Right");
|
||||
case bottomSideLeft: return NEEDS_TRANS("Bottom Side Left");
|
||||
case bottomSideRight: return NEEDS_TRANS("Bottom Side Right");
|
||||
case bottomRearLeft: return NEEDS_TRANS("Bottom Rear Left");
|
||||
case bottomRearCentre: return NEEDS_TRANS("Bottom Rear Centre");
|
||||
case bottomRearRight: return NEEDS_TRANS("Bottom Rear Right");
|
||||
case discreteChannel0: return NEEDS_TRANS("Discrete channel");
|
||||
default: break;
|
||||
}
|
||||
|
||||
@ -115,8 +136,28 @@ String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelT
|
||||
case ambisonicACN1: return "ACN1";
|
||||
case ambisonicACN2: return "ACN2";
|
||||
case ambisonicACN3: return "ACN3";
|
||||
case ambisonicACN4: return "ACN4";
|
||||
case ambisonicACN5: return "ACN5";
|
||||
case ambisonicACN6: return "ACN6";
|
||||
case ambisonicACN7: return "ACN7";
|
||||
case ambisonicACN8: return "ACN8";
|
||||
case ambisonicACN9: return "ACN9";
|
||||
case ambisonicACN10: return "ACN10";
|
||||
case ambisonicACN11: return "ACN11";
|
||||
case ambisonicACN12: return "ACN12";
|
||||
case ambisonicACN13: return "ACN13";
|
||||
case ambisonicACN14: return "ACN14";
|
||||
case ambisonicACN15: return "ACN15";
|
||||
case topSideLeft: return "Tsl";
|
||||
case topSideRight: return "Tsr";
|
||||
case bottomFrontLeft: return "Bfl";
|
||||
case bottomFrontCentre: return "Bfc";
|
||||
case bottomFrontRight: return "Bfr";
|
||||
case bottomSideLeft: return "Bsl";
|
||||
case bottomSideRight: return "Bsr";
|
||||
case bottomRearLeft: return "Brl";
|
||||
case bottomRearCentre: return "Brc";
|
||||
case bottomRearRight: return "Brr";
|
||||
default: break;
|
||||
}
|
||||
|
||||
@ -130,38 +171,61 @@ AudioChannelSet::ChannelType AudioChannelSet::getChannelTypeFromAbbreviation (co
|
||||
{
|
||||
if (abbr.length() > 0 && (abbr[0] >= '0' && abbr[0] <= '9'))
|
||||
return static_cast<AudioChannelSet::ChannelType> (static_cast<int> (discreteChannel0)
|
||||
+ abbr.getIntValue() + 1);
|
||||
|
||||
if (abbr == "L") return left;
|
||||
if (abbr == "R") return right;
|
||||
if (abbr == "C") return centre;
|
||||
if (abbr == "Lfe") return LFE;
|
||||
if (abbr == "Ls") return leftSurround;
|
||||
if (abbr == "Rs") return rightSurround;
|
||||
if (abbr == "Lc") return leftCentre;
|
||||
if (abbr == "Rc") return rightCentre;
|
||||
if (abbr == "Cs") return centreSurround;
|
||||
if (abbr == "Lrs") return leftSurroundRear;
|
||||
if (abbr == "Rrs") return rightSurroundRear;
|
||||
if (abbr == "Tm") return topMiddle;
|
||||
if (abbr == "Tfl") return topFrontLeft;
|
||||
if (abbr == "Tfc") return topFrontCentre;
|
||||
if (abbr == "Tfr") return topFrontRight;
|
||||
if (abbr == "Trl") return topRearLeft;
|
||||
if (abbr == "Trc") return topRearCentre;
|
||||
if (abbr == "Trr") return topRearRight;
|
||||
if (abbr == "Wl") return wideLeft;
|
||||
if (abbr == "Wr") return wideRight;
|
||||
if (abbr == "Lfe2") return LFE2;
|
||||
if (abbr == "Lss") return leftSurroundSide;
|
||||
if (abbr == "Rss") return rightSurroundSide;
|
||||
if (abbr == "W") return ambisonicW;
|
||||
if (abbr == "X") return ambisonicX;
|
||||
if (abbr == "Y") return ambisonicY;
|
||||
if (abbr == "Z") return ambisonicZ;
|
||||
if (abbr == "Tsl") return topSideLeft;
|
||||
if (abbr == "Tsr") return topSideRight;
|
||||
+ abbr.getIntValue() - 1);
|
||||
|
||||
if (abbr == "L") return left;
|
||||
if (abbr == "R") return right;
|
||||
if (abbr == "C") return centre;
|
||||
if (abbr == "Lfe") return LFE;
|
||||
if (abbr == "Ls") return leftSurround;
|
||||
if (abbr == "Rs") return rightSurround;
|
||||
if (abbr == "Lc") return leftCentre;
|
||||
if (abbr == "Rc") return rightCentre;
|
||||
if (abbr == "Cs") return centreSurround;
|
||||
if (abbr == "Lrs") return leftSurroundRear;
|
||||
if (abbr == "Rrs") return rightSurroundRear;
|
||||
if (abbr == "Tm") return topMiddle;
|
||||
if (abbr == "Tfl") return topFrontLeft;
|
||||
if (abbr == "Tfc") return topFrontCentre;
|
||||
if (abbr == "Tfr") return topFrontRight;
|
||||
if (abbr == "Trl") return topRearLeft;
|
||||
if (abbr == "Trc") return topRearCentre;
|
||||
if (abbr == "Trr") return topRearRight;
|
||||
if (abbr == "Wl") return wideLeft;
|
||||
if (abbr == "Wr") return wideRight;
|
||||
if (abbr == "Lfe2") return LFE2;
|
||||
if (abbr == "Lss") return leftSurroundSide;
|
||||
if (abbr == "Rss") return rightSurroundSide;
|
||||
if (abbr == "W") return ambisonicW;
|
||||
if (abbr == "X") return ambisonicX;
|
||||
if (abbr == "Y") return ambisonicY;
|
||||
if (abbr == "Z") return ambisonicZ;
|
||||
if (abbr == "ACN0") return ambisonicACN0;
|
||||
if (abbr == "ACN1") return ambisonicACN1;
|
||||
if (abbr == "ACN2") return ambisonicACN2;
|
||||
if (abbr == "ACN3") return ambisonicACN3;
|
||||
if (abbr == "ACN4") return ambisonicACN4;
|
||||
if (abbr == "ACN5") return ambisonicACN5;
|
||||
if (abbr == "ACN6") return ambisonicACN6;
|
||||
if (abbr == "ACN7") return ambisonicACN7;
|
||||
if (abbr == "ACN8") return ambisonicACN8;
|
||||
if (abbr == "ACN9") return ambisonicACN9;
|
||||
if (abbr == "ACN10") return ambisonicACN10;
|
||||
if (abbr == "ACN11") return ambisonicACN11;
|
||||
if (abbr == "ACN12") return ambisonicACN12;
|
||||
if (abbr == "ACN13") return ambisonicACN13;
|
||||
if (abbr == "ACN14") return ambisonicACN14;
|
||||
if (abbr == "ACN15") return ambisonicACN15;
|
||||
if (abbr == "Tsl") return topSideLeft;
|
||||
if (abbr == "Tsr") return topSideRight;
|
||||
if (abbr == "Bfl") return bottomFrontLeft;
|
||||
if (abbr == "Bfc") return bottomFrontCentre;
|
||||
if (abbr == "Bfr") return bottomFrontRight;
|
||||
if (abbr == "Bsl") return bottomSideLeft;
|
||||
if (abbr == "Bsr") return bottomSideRight;
|
||||
if (abbr == "Brl") return bottomRearLeft;
|
||||
if (abbr == "Brc") return bottomRearCentre;
|
||||
if (abbr == "Brr") return bottomRearRight;
|
||||
return unknown;
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
/** Creates an empty channel set.
|
||||
You can call addChannel to add channels to the set.
|
||||
*/
|
||||
AudioChannelSet() noexcept {}
|
||||
AudioChannelSet() = default;
|
||||
|
||||
/** Creates a zero-channel set which can be used to indicate that a
|
||||
bus is disabled. */
|
||||
@ -365,7 +365,21 @@ public:
|
||||
ambisonicZ = ambisonicACN2, /**< Same as first-order ambisonic channel number 2. */
|
||||
|
||||
//==============================================================================
|
||||
discreteChannel0 = 64 /**< Non-typed individual channels are indexed upwards from this value. */
|
||||
bottomFrontLeft = 62, /**< Bottom Front Left (Bfl) */
|
||||
bottomFrontCentre = 63, /**< Bottom Front Centre (Bfc) */
|
||||
bottomFrontRight = 64, /**< Bottom Front Right (Bfr) */
|
||||
|
||||
proxymityLeft = 65, /**< Proximity Left (Pl) */
|
||||
proximityRight = 66, /**< Proximity Right (Pr) */
|
||||
|
||||
bottomSideLeft = 67, /**< Bottom Side Left (Bsl) */
|
||||
bottomSideRight = 68, /**< Bottom Side Right (Bsr) */
|
||||
bottomRearLeft = 69, /**< Bottom Rear Left (Brl) */
|
||||
bottomRearCentre = 70, /**< Bottom Rear Center (Brc) */
|
||||
bottomRearRight = 71, /**< Bottom Rear Right (Brr) */
|
||||
|
||||
//==============================================================================
|
||||
discreteChannel0 = 128 /**< Non-typed individual channels are indexed upwards from this value. */
|
||||
};
|
||||
|
||||
/** Returns the name of a given channel type. For example, this method may return "Surround Left". */
|
||||
|
@ -23,16 +23,16 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
auto maxVal = (double) 0x7fff;
|
||||
auto intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
@ -43,21 +43,21 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
*(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
auto maxVal = (double) 0x7fff;
|
||||
auto intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
@ -68,15 +68,15 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
*(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fffff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
auto maxVal = (double) 0x7fffff;
|
||||
auto intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
@ -98,10 +98,10 @@ void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fffff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
auto maxVal = (double) 0x7fffff;
|
||||
auto intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
@ -123,16 +123,16 @@ void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fffffff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
auto maxVal = (double) 0x7fffffff;
|
||||
auto intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
@ -143,21 +143,21 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
*(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fffffff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
auto maxVal = (double) 0x7fffffff;
|
||||
auto intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
@ -168,12 +168,12 @@ void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
*(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* dest, int numSamples, int destBytesPerSample)
|
||||
{
|
||||
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
|
||||
|
||||
@ -181,28 +181,28 @@ void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* de
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(float*) d = source[i];
|
||||
*reinterpret_cast<float*> (d) = source[i];
|
||||
|
||||
#if JUCE_BIG_ENDIAN
|
||||
*(uint32*) d = ByteOrder::swap (*(uint32*) d);
|
||||
*reinterpret_cast<uint32*> (d) = ByteOrder::swap (*reinterpret_cast<uint32*> (d));
|
||||
#endif
|
||||
|
||||
d += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* dest, int numSamples, int destBytesPerSample)
|
||||
{
|
||||
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
|
||||
|
||||
char* d = static_cast<char*> (dest);
|
||||
auto d = static_cast<char*> (dest);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(float*) d = source[i];
|
||||
*reinterpret_cast<float*> (d) = source[i];
|
||||
|
||||
#if JUCE_LITTLE_ENDIAN
|
||||
*(uint32*) d = ByteOrder::swap (*(uint32*) d);
|
||||
*reinterpret_cast<uint32*> (d) = ByteOrder::swap (*reinterpret_cast<uint32*> (d));
|
||||
#endif
|
||||
|
||||
d += destBytesPerSample;
|
||||
@ -210,16 +210,16 @@ void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* de
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
void AudioDataConverters::convertInt16LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
auto intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData);
|
||||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint16*> (intData));
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
@ -230,21 +230,21 @@ void AudioDataConverters::convertInt16LEToFloat (const void* const source, float
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData);
|
||||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint16*> (intData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
void AudioDataConverters::convertInt16BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
auto intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData);
|
||||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint16*> (intData));
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
@ -255,15 +255,15 @@ void AudioDataConverters::convertInt16BEToFloat (const void* const source, float
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData);
|
||||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint16*> (intData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
void AudioDataConverters::convertInt24LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fffff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
auto intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
@ -285,10 +285,10 @@ void AudioDataConverters::convertInt24LEToFloat (const void* const source, float
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
void AudioDataConverters::convertInt24BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fffff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
auto intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
@ -310,16 +310,16 @@ void AudioDataConverters::convertInt24BEToFloat (const void* const source, float
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
void AudioDataConverters::convertInt32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample)
|
||||
{
|
||||
const auto scale = 1.0f / (float) 0x7fffffff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
const float scale = 1.0f / (float) 0x7fffffff;
|
||||
auto intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData);
|
||||
dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint32*> (intData));
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
@ -330,21 +330,21 @@ void AudioDataConverters::convertInt32LEToFloat (const void* const source, float
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData);
|
||||
dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint32*> (intData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
void AudioDataConverters::convertInt32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample)
|
||||
{
|
||||
const auto scale = 1.0f / (float) 0x7fffffff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
const float scale = 1.0f / (float) 0x7fffffff;
|
||||
auto intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData);
|
||||
dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint32*> (intData));
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
@ -355,21 +355,21 @@ void AudioDataConverters::convertInt32BEToFloat (const void* const source, float
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData);
|
||||
dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint32*> (intData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
void AudioDataConverters::convertFloat32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample)
|
||||
{
|
||||
const char* s = static_cast<const char*> (source);
|
||||
auto s = static_cast<const char*> (source);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = *(float*)s;
|
||||
dest[i] = *reinterpret_cast<const float*> (s);
|
||||
|
||||
#if JUCE_BIG_ENDIAN
|
||||
uint32* const d = (uint32*) (dest + i);
|
||||
auto d = reinterpret_cast<uint32*> (dest + i);
|
||||
*d = ByteOrder::swap (*d);
|
||||
#endif
|
||||
|
||||
@ -377,16 +377,16 @@ void AudioDataConverters::convertFloat32LEToFloat (const void* const source, flo
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
void AudioDataConverters::convertFloat32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample)
|
||||
{
|
||||
const char* s = static_cast<const char*> (source);
|
||||
auto s = static_cast<const char*> (source);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = *(float*)s;
|
||||
dest[i] = *reinterpret_cast<const float*> (s);
|
||||
|
||||
#if JUCE_LITTLE_ENDIAN
|
||||
uint32* const d = (uint32*) (dest + i);
|
||||
auto d = reinterpret_cast<uint32*> (dest + i);
|
||||
*d = ByteOrder::swap (*d);
|
||||
#endif
|
||||
|
||||
@ -396,10 +396,7 @@ void AudioDataConverters::convertFloat32BEToFloat (const void* const source, flo
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void AudioDataConverters::convertFloatToFormat (const DataFormat destFormat,
|
||||
const float* const source,
|
||||
void* const dest,
|
||||
const int numSamples)
|
||||
void AudioDataConverters::convertFloatToFormat (DataFormat destFormat, const float* source, void* dest, int numSamples)
|
||||
{
|
||||
switch (destFormat)
|
||||
{
|
||||
@ -415,10 +412,7 @@ void AudioDataConverters::convertFloatToFormat (const DataFormat destFormat,
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFormatToFloat (const DataFormat sourceFormat,
|
||||
const void* const source,
|
||||
float* const dest,
|
||||
const int numSamples)
|
||||
void AudioDataConverters::convertFormatToFloat (DataFormat sourceFormat, const void* source, float* dest, int numSamples)
|
||||
{
|
||||
switch (sourceFormat)
|
||||
{
|
||||
@ -435,15 +429,12 @@ void AudioDataConverters::convertFormatToFloat (const DataFormat sourceFormat,
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDataConverters::interleaveSamples (const float** const source,
|
||||
float* const dest,
|
||||
const int numSamples,
|
||||
const int numChannels)
|
||||
void AudioDataConverters::interleaveSamples (const float** source, float* dest, int numSamples, int numChannels)
|
||||
{
|
||||
for (int chan = 0; chan < numChannels; ++chan)
|
||||
{
|
||||
int i = chan;
|
||||
const float* src = source [chan];
|
||||
auto i = chan;
|
||||
auto src = source [chan];
|
||||
|
||||
for (int j = 0; j < numSamples; ++j)
|
||||
{
|
||||
@ -453,15 +444,12 @@ void AudioDataConverters::interleaveSamples (const float** const source,
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::deinterleaveSamples (const float* const source,
|
||||
float** const dest,
|
||||
const int numSamples,
|
||||
const int numChannels)
|
||||
void AudioDataConverters::deinterleaveSamples (const float* source, float** dest, int numSamples, int numChannels)
|
||||
{
|
||||
for (int chan = 0; chan < numChannels; ++chan)
|
||||
{
|
||||
int i = chan;
|
||||
float* dst = dest [chan];
|
||||
auto i = chan;
|
||||
auto dst = dest [chan];
|
||||
|
||||
for (int j = 0; j < numSamples; ++j)
|
||||
{
|
||||
@ -492,7 +480,7 @@ public:
|
||||
static void test (UnitTest& unitTest, bool inPlace, Random& r)
|
||||
{
|
||||
const int numSamples = 2048;
|
||||
int32 original [numSamples], converted [numSamples], reversed [numSamples];
|
||||
int32 original[numSamples], converted[numSamples], reversed[numSamples];
|
||||
|
||||
{
|
||||
AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::NonConst> d (original);
|
||||
@ -514,13 +502,13 @@ public:
|
||||
}
|
||||
|
||||
// convert data from the source to dest format..
|
||||
std::unique_ptr<AudioData::Converter> conv (new AudioData::ConverterInstance <AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::Const>,
|
||||
AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::NonConst>>());
|
||||
std::unique_ptr<AudioData::Converter> conv (new AudioData::ConverterInstance<AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::Const>,
|
||||
AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::NonConst>>());
|
||||
conv->convertSamples (inPlace ? reversed : converted, original, numSamples);
|
||||
|
||||
// ..and back again..
|
||||
conv.reset (new AudioData::ConverterInstance <AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::Const>,
|
||||
AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::NonConst>>());
|
||||
conv.reset (new AudioData::ConverterInstance<AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::Const>,
|
||||
AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::NonConst>>());
|
||||
if (! inPlace)
|
||||
zeromem (reversed, sizeof (reversed));
|
||||
|
||||
@ -582,7 +570,7 @@ public:
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
Random r = getRandom();
|
||||
auto r = getRandom();
|
||||
beginTest ("Round-trip conversion: Int8");
|
||||
Test1 <AudioData::Int8>::test (*this, r);
|
||||
beginTest ("Round-trip conversion: Int16");
|
||||
|
@ -277,8 +277,8 @@ public:
|
||||
class NonInterleaved
|
||||
{
|
||||
public:
|
||||
inline NonInterleaved() noexcept {}
|
||||
inline NonInterleaved (const NonInterleaved&) noexcept {}
|
||||
inline NonInterleaved() = default;
|
||||
inline NonInterleaved (const NonInterleaved&) = default;
|
||||
inline NonInterleaved (const int) noexcept {}
|
||||
inline void copyFrom (const NonInterleaved&) noexcept {}
|
||||
template <class SampleFormatType> inline void advanceData (SampleFormatType& s) noexcept { s.advance(); }
|
||||
@ -292,15 +292,15 @@ public:
|
||||
class Interleaved
|
||||
{
|
||||
public:
|
||||
inline Interleaved() noexcept : numInterleavedChannels (1) {}
|
||||
inline Interleaved (const Interleaved& other) noexcept : numInterleavedChannels (other.numInterleavedChannels) {}
|
||||
inline Interleaved() noexcept {}
|
||||
inline Interleaved (const Interleaved& other) = default;
|
||||
inline Interleaved (const int numInterleavedChans) noexcept : numInterleavedChannels (numInterleavedChans) {}
|
||||
inline void copyFrom (const Interleaved& other) noexcept { numInterleavedChannels = other.numInterleavedChannels; }
|
||||
template <class SampleFormatType> inline void advanceData (SampleFormatType& s) noexcept { s.skip (numInterleavedChannels); }
|
||||
template <class SampleFormatType> inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numInterleavedChannels * numSamples); }
|
||||
template <class SampleFormatType> inline void clear (SampleFormatType& s, int numSamples) noexcept { while (--numSamples >= 0) { s.clear(); s.skip (numInterleavedChannels); } }
|
||||
template <class SampleFormatType> inline int getNumBytesBetweenSamples (const SampleFormatType&) const noexcept { return numInterleavedChannels * SampleFormatType::bytesPerSample; }
|
||||
int numInterleavedChannels;
|
||||
int numInterleavedChannels = 1;
|
||||
enum { isInterleavedType = 1 };
|
||||
};
|
||||
|
||||
@ -587,7 +587,7 @@ public:
|
||||
class Converter
|
||||
{
|
||||
public:
|
||||
virtual ~Converter() {}
|
||||
virtual ~Converter() = default;
|
||||
|
||||
/** Converts a sequence of samples from the converter's source format into the dest format. */
|
||||
virtual void convertSamples (void* destSamples, const void* sourceSamples, int numSamples) const = 0;
|
||||
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
AudioProcessLoadMeasurer::AudioProcessLoadMeasurer() {}
|
||||
AudioProcessLoadMeasurer::~AudioProcessLoadMeasurer() {}
|
||||
|
||||
void AudioProcessLoadMeasurer::reset()
|
||||
{
|
||||
reset (0, 0);
|
||||
}
|
||||
|
||||
void AudioProcessLoadMeasurer::reset (double sampleRate, int blockSize)
|
||||
{
|
||||
cpuUsageMs = 0;
|
||||
xruns = 0;
|
||||
|
||||
if (sampleRate > 0.0 && blockSize > 0)
|
||||
{
|
||||
msPerBlock = 1000.0 * blockSize / sampleRate;
|
||||
timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
msPerBlock = 0;
|
||||
timeToCpuScale = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioProcessLoadMeasurer::registerBlockRenderTime (double milliseconds)
|
||||
{
|
||||
const double filterAmount = 0.2;
|
||||
cpuUsageMs += filterAmount * (milliseconds - cpuUsageMs);
|
||||
|
||||
if (milliseconds > msPerBlock)
|
||||
++xruns;
|
||||
}
|
||||
|
||||
double AudioProcessLoadMeasurer::getLoadAsProportion() const { return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); }
|
||||
double AudioProcessLoadMeasurer::getLoadAsPercentage() const { return 100.0 * getLoadAsProportion(); }
|
||||
|
||||
int AudioProcessLoadMeasurer::getXRunCount() const { return xruns; }
|
||||
|
||||
AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p)
|
||||
: owner (p), startTime (Time::getMillisecondCounterHiRes())
|
||||
{
|
||||
}
|
||||
|
||||
AudioProcessLoadMeasurer::ScopedTimer::~ScopedTimer()
|
||||
{
|
||||
owner.registerBlockRenderTime (Time::getMillisecondCounterHiRes() - startTime);
|
||||
}
|
||||
|
||||
} // namespace juce
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Maintains an ongoing measurement of the proportion of time which is being
|
||||
spent inside an audio callback.
|
||||
*/
|
||||
class JUCE_API AudioProcessLoadMeasurer
|
||||
{
|
||||
public:
|
||||
/** */
|
||||
AudioProcessLoadMeasurer();
|
||||
|
||||
/** Destructor. */
|
||||
~AudioProcessLoadMeasurer();
|
||||
|
||||
//==============================================================================
|
||||
/** Resets the state. */
|
||||
void reset();
|
||||
|
||||
/** Resets the counter, in preparation for use with the given sample rate and block size. */
|
||||
void reset (double sampleRate, int blockSize);
|
||||
|
||||
/** Returns the current load as a proportion 0 to 1.0 */
|
||||
double getLoadAsProportion() const;
|
||||
|
||||
/** Returns the current load as a percentage 0 to 100.0 */
|
||||
double getLoadAsPercentage() const;
|
||||
|
||||
/** Returns the number of over- (or under-) runs recorded since the state was reset. */
|
||||
int getXRunCount() const;
|
||||
|
||||
//==============================================================================
|
||||
/** This class measures the time between its construction and destruction and
|
||||
adds it to an AudioProcessLoadMeasurer.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
{
|
||||
AudioProcessLoadMeasurer::ScopedTimer timer (myProcessLoadMeasurer);
|
||||
myCallback->doTheCallback();
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
struct JUCE_API ScopedTimer
|
||||
{
|
||||
ScopedTimer (AudioProcessLoadMeasurer&);
|
||||
~ScopedTimer();
|
||||
|
||||
private:
|
||||
AudioProcessLoadMeasurer& owner;
|
||||
double startTime;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ScopedTimer)
|
||||
};
|
||||
|
||||
/** Can be called manually to add the time of a callback to the stats.
|
||||
Normally you probably would never call this - it's simpler and more robust to
|
||||
use a ScopedTimer to measure the time using an RAII pattern.
|
||||
*/
|
||||
void registerBlockRenderTime (double millisecondsTaken);
|
||||
|
||||
private:
|
||||
double cpuUsageMs = 0, timeToCpuScale = 0, msPerBlock = 0;
|
||||
int xruns = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace juce
|
@ -172,14 +172,14 @@ public:
|
||||
/** Destructor.
|
||||
This will free any memory allocated by the buffer.
|
||||
*/
|
||||
~AudioBuffer() noexcept {}
|
||||
~AudioBuffer() = default;
|
||||
|
||||
/** Move constructor */
|
||||
AudioBuffer (AudioBuffer&& other) noexcept
|
||||
: numChannels (other.numChannels),
|
||||
size (other.size),
|
||||
allocatedBytes (other.allocatedBytes),
|
||||
allocatedData (static_cast<HeapBlock<char, true>&&> (other.allocatedData)),
|
||||
allocatedData (std::move (other.allocatedData)),
|
||||
isClear (other.isClear)
|
||||
{
|
||||
if (numChannels < (int) numElementsInArray (preallocatedChannelSpace))
|
||||
@ -205,7 +205,7 @@ public:
|
||||
numChannels = other.numChannels;
|
||||
size = other.size;
|
||||
allocatedBytes = other.allocatedBytes;
|
||||
allocatedData = static_cast<HeapBlock<char, true>&&> (other.allocatedData);
|
||||
allocatedData = std::move (other.allocatedData);
|
||||
isClear = other.isClear;
|
||||
|
||||
if (numChannels < (int) numElementsInArray (preallocatedChannelSpace))
|
||||
@ -1080,7 +1080,7 @@ private:
|
||||
allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (Type) + channelListSize + 32;
|
||||
allocatedData.malloc (allocatedBytes);
|
||||
channels = reinterpret_cast<Type**> (allocatedData.get());
|
||||
auto* chan = (Type*) (allocatedData + channelListSize);
|
||||
auto chan = reinterpret_cast<Type*> (allocatedData + channelListSize);
|
||||
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
{
|
||||
|
@ -878,7 +878,7 @@ void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, cons
|
||||
JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, )
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier,
|
||||
Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))),
|
||||
Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 (reinterpret_cast<const __m128i*> (src)))),
|
||||
JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
#endif
|
||||
|
@ -1,215 +0,0 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Utility class for linearly smoothed values like volume etc. that should
|
||||
not change abruptly but as a linear ramp, to avoid audio glitches.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
template <typename FloatType>
|
||||
class LinearSmoothedValue
|
||||
{
|
||||
public:
|
||||
/** Constructor. */
|
||||
LinearSmoothedValue() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor. */
|
||||
LinearSmoothedValue (FloatType initialValue) noexcept
|
||||
: currentValue (initialValue), target (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reset to a new sample rate and ramp length.
|
||||
@param sampleRate The sampling rate
|
||||
@param rampLengthInSeconds The duration of the ramp in seconds
|
||||
*/
|
||||
void reset (double sampleRate, double rampLengthInSeconds) noexcept
|
||||
{
|
||||
jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
|
||||
stepsToTarget = (int) std::floor (rampLengthInSeconds * sampleRate);
|
||||
currentValue = target;
|
||||
countdown = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Set a new target value.
|
||||
|
||||
@param newValue The new target value
|
||||
@param force If true, the value will be set immediately, bypassing the ramp
|
||||
*/
|
||||
void setValue (FloatType newValue, bool force = false) noexcept
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
target = currentValue = newValue;
|
||||
countdown = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (target != newValue)
|
||||
{
|
||||
target = newValue;
|
||||
countdown = stepsToTarget;
|
||||
|
||||
if (countdown <= 0)
|
||||
currentValue = target;
|
||||
else
|
||||
step = (target - currentValue) / (FloatType) countdown;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Compute the next value.
|
||||
@returns Smoothed value
|
||||
*/
|
||||
FloatType getNextValue() noexcept
|
||||
{
|
||||
if (countdown <= 0)
|
||||
return target;
|
||||
|
||||
--countdown;
|
||||
currentValue += step;
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
/** Returns true if the current value is currently being interpolated. */
|
||||
bool isSmoothing() const noexcept
|
||||
{
|
||||
return countdown > 0;
|
||||
}
|
||||
|
||||
/** Returns the target value towards which the smoothed value is currently moving. */
|
||||
FloatType getTargetValue() const noexcept
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Applies a linear smoothed gain to a stream of samples
|
||||
S[i] *= gain
|
||||
@param samples Pointer to a raw array of samples
|
||||
@param numSamples Length of array of samples
|
||||
*/
|
||||
void applyGain (FloatType* samples, int numSamples) noexcept
|
||||
{
|
||||
jassert(numSamples >= 0);
|
||||
|
||||
if (isSmoothing())
|
||||
{
|
||||
for (int i = 0; i < numSamples; i++)
|
||||
samples[i] *= getNextValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
FloatVectorOperations::multiply (samples, target, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Computes output as linear smoothed gain applied to a stream of samples.
|
||||
Sout[i] = Sin[i] * gain
|
||||
@param samplesOut A pointer to a raw array of output samples
|
||||
@param samplesIn A pointer to a raw array of input samples
|
||||
@param numSamples The length of the array of samples
|
||||
*/
|
||||
void applyGain (FloatType* samplesOut, const FloatType* samplesIn, int numSamples) noexcept
|
||||
{
|
||||
jassert (numSamples >= 0);
|
||||
|
||||
if (isSmoothing())
|
||||
{
|
||||
for (int i = 0; i < numSamples; i++)
|
||||
samplesOut[i] = samplesIn[i] * getNextValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Applies a linear smoothed gain to a buffer */
|
||||
void applyGain (AudioBuffer<FloatType>& buffer, int numSamples) noexcept
|
||||
{
|
||||
jassert (numSamples >= 0);
|
||||
|
||||
if (isSmoothing())
|
||||
{
|
||||
if (buffer.getNumChannels() == 1)
|
||||
{
|
||||
FloatType* samples = buffer.getWritePointer(0);
|
||||
|
||||
for (int i = 0; i < numSamples; i++)
|
||||
samples[i] *= getNextValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numSamples; i++)
|
||||
{
|
||||
const FloatType gain = getNextValue();
|
||||
|
||||
for (int channel = 0; channel < buffer.getNumChannels(); channel++)
|
||||
buffer.setSample (channel, i, buffer.getSample (channel, i) * gain);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.applyGain (0, numSamples, target);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Skip the next numSamples samples.
|
||||
|
||||
This is identical to calling getNextValue numSamples times.
|
||||
@see getNextValue
|
||||
*/
|
||||
void skip (int numSamples) noexcept
|
||||
{
|
||||
if (numSamples >= countdown)
|
||||
{
|
||||
currentValue = target;
|
||||
countdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentValue += (step * static_cast<FloatType> (numSamples));
|
||||
countdown -= numSamples;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
FloatType currentValue = 0, target = 0, step = 0;
|
||||
int countdown = 0, stepsToTarget = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
@ -56,9 +56,11 @@
|
||||
#include "buffers/juce_AudioDataConverters.cpp"
|
||||
#include "buffers/juce_FloatVectorOperations.cpp"
|
||||
#include "buffers/juce_AudioChannelSet.cpp"
|
||||
#include "effects/juce_IIRFilter.cpp"
|
||||
#include "effects/juce_LagrangeInterpolator.cpp"
|
||||
#include "effects/juce_CatmullRomInterpolator.cpp"
|
||||
#include "buffers/juce_AudioProcessLoadMeasurer.cpp"
|
||||
#include "utilities/juce_IIRFilter.cpp"
|
||||
#include "utilities/juce_LagrangeInterpolator.cpp"
|
||||
#include "utilities/juce_CatmullRomInterpolator.cpp"
|
||||
#include "utilities/juce_SmoothedValue.cpp"
|
||||
#include "midi/juce_MidiBuffer.cpp"
|
||||
#include "midi/juce_MidiFile.cpp"
|
||||
#include "midi/juce_MidiKeyboardState.cpp"
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
ID: juce_audio_basics
|
||||
vendor: juce
|
||||
version: 5.3.2
|
||||
version: 5.4.3
|
||||
name: JUCE audio and MIDI data classes
|
||||
description: Classes for audio buffer manipulation, midi message handling, synthesis, etc.
|
||||
website: http://www.juce.com/juce
|
||||
@ -84,12 +84,14 @@
|
||||
#include "buffers/juce_FloatVectorOperations.h"
|
||||
#include "buffers/juce_AudioSampleBuffer.h"
|
||||
#include "buffers/juce_AudioChannelSet.h"
|
||||
#include "effects/juce_Decibels.h"
|
||||
#include "effects/juce_IIRFilter.h"
|
||||
#include "effects/juce_LagrangeInterpolator.h"
|
||||
#include "effects/juce_CatmullRomInterpolator.h"
|
||||
#include "effects/juce_LinearSmoothedValue.h"
|
||||
#include "effects/juce_Reverb.h"
|
||||
#include "buffers/juce_AudioProcessLoadMeasurer.h"
|
||||
#include "utilities/juce_Decibels.h"
|
||||
#include "utilities/juce_IIRFilter.h"
|
||||
#include "utilities/juce_LagrangeInterpolator.h"
|
||||
#include "utilities/juce_CatmullRomInterpolator.h"
|
||||
#include "utilities/juce_SmoothedValue.h"
|
||||
#include "utilities/juce_Reverb.h"
|
||||
#include "utilities/juce_ADSR.h"
|
||||
#include "midi/juce_MidiMessage.h"
|
||||
#include "midi/juce_MidiBuffer.h"
|
||||
#include "midi/juce_MidiMessageSequence.h"
|
||||
|
@ -57,9 +57,16 @@ namespace MidiBufferHelpers
|
||||
}
|
||||
else if (byte == 0xff)
|
||||
{
|
||||
int n;
|
||||
const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n);
|
||||
size = jmin (maxBytes, n + 2 + bytesLeft);
|
||||
if (maxBytes == 1)
|
||||
{
|
||||
size = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int n;
|
||||
const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n);
|
||||
size = jmin (maxBytes, n + 2 + bytesLeft);
|
||||
}
|
||||
}
|
||||
else if (byte >= 0x80)
|
||||
{
|
||||
|
@ -169,14 +169,14 @@ MidiFile& MidiFile::operator= (const MidiFile& other)
|
||||
}
|
||||
|
||||
MidiFile::MidiFile (MidiFile&& other)
|
||||
: tracks (static_cast<OwnedArray<MidiMessageSequence>&&> (other.tracks)),
|
||||
: tracks (std::move (other.tracks)),
|
||||
timeFormat (other.timeFormat)
|
||||
{
|
||||
}
|
||||
|
||||
MidiFile& MidiFile::operator= (MidiFile&& other)
|
||||
{
|
||||
tracks = static_cast<OwnedArray<MidiMessageSequence>&&> (other.tracks);
|
||||
tracks = std::move (other.tracks);
|
||||
timeFormat = other.timeFormat;
|
||||
return *this;
|
||||
}
|
||||
@ -245,7 +245,7 @@ double MidiFile::getLastTimestamp() const
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool MidiFile::readFrom (InputStream& sourceStream)
|
||||
bool MidiFile::readFrom (InputStream& sourceStream, bool createMatchingNoteOffs)
|
||||
{
|
||||
clear();
|
||||
MemoryBlock data;
|
||||
@ -276,7 +276,7 @@ bool MidiFile::readFrom (InputStream& sourceStream)
|
||||
break;
|
||||
|
||||
if (chunkType == (int) ByteOrder::bigEndianInt ("MTrk"))
|
||||
readNextTrack (d, chunkSize);
|
||||
readNextTrack (d, chunkSize, createMatchingNoteOffs);
|
||||
|
||||
size -= (size_t) chunkSize + 8;
|
||||
d += chunkSize;
|
||||
@ -290,7 +290,7 @@ bool MidiFile::readFrom (InputStream& sourceStream)
|
||||
return false;
|
||||
}
|
||||
|
||||
void MidiFile::readNextTrack (const uint8* data, int size)
|
||||
void MidiFile::readNextTrack (const uint8* data, int size, bool createMatchingNoteOffs)
|
||||
{
|
||||
double time = 0;
|
||||
uint8 lastStatusByte = 0;
|
||||
@ -337,7 +337,9 @@ void MidiFile::readNextTrack (const uint8* data, int size)
|
||||
});
|
||||
|
||||
addTrack (result);
|
||||
tracks.getLast()->updateMatchedPairs();
|
||||
|
||||
if (createMatchingNoteOffs)
|
||||
tracks.getLast()->updateMatchedPairs();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -361,7 +363,7 @@ void MidiFile::convertTimestampTicksToSeconds()
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool MidiFile::writeTo (OutputStream& out, int midiFileType)
|
||||
bool MidiFile::writeTo (OutputStream& out, int midiFileType) const
|
||||
{
|
||||
jassert (midiFileType >= 0 && midiFileType <= 2);
|
||||
|
||||
@ -379,7 +381,7 @@ bool MidiFile::writeTo (OutputStream& out, int midiFileType)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MidiFile::writeTrack (OutputStream& mainOut, const MidiMessageSequence& ms)
|
||||
bool MidiFile::writeTrack (OutputStream& mainOut, const MidiMessageSequence& ms) const
|
||||
{
|
||||
MemoryOutputStream out;
|
||||
|
||||
|
@ -156,16 +156,25 @@ public:
|
||||
terms of midi ticks. To convert them to seconds, use the convertTimestampTicksToSeconds()
|
||||
method.
|
||||
|
||||
@param sourceStream the source stream
|
||||
@param createMatchingNoteOffs if true, any missing note-offs for previous note-ons will
|
||||
be automatically added at the end of the file by calling
|
||||
MidiMessageSequence::updateMatchedPairs on each track.
|
||||
|
||||
@returns true if the stream was read successfully
|
||||
*/
|
||||
bool readFrom (InputStream& sourceStream);
|
||||
bool readFrom (InputStream& sourceStream, bool createMatchingNoteOffs = true);
|
||||
|
||||
/** Writes the midi tracks as a standard midi file.
|
||||
The midiFileType value is written as the file's format type, which can be 0, 1
|
||||
or 2 - see the midi file spec for more info about that.
|
||||
|
||||
@param destStream the destination stream
|
||||
@param midiFileType the type of midi file
|
||||
|
||||
@returns true if the operation succeeded.
|
||||
*/
|
||||
bool writeTo (OutputStream& destStream, int midiFileType = 1);
|
||||
bool writeTo (OutputStream& destStream, int midiFileType = 1) const;
|
||||
|
||||
/** Converts the timestamp of all the midi events from midi ticks to seconds.
|
||||
|
||||
@ -174,14 +183,13 @@ public:
|
||||
*/
|
||||
void convertTimestampTicksToSeconds();
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
OwnedArray<MidiMessageSequence> tracks;
|
||||
short timeFormat;
|
||||
|
||||
void readNextTrack (const uint8*, int size);
|
||||
bool writeTrack (OutputStream&, const MidiMessageSequence&);
|
||||
void readNextTrack (const uint8*, int, bool);
|
||||
bool writeTrack (OutputStream&, const MidiMessageSequence&) const;
|
||||
|
||||
JUCE_LEAK_DETECTOR (MidiFile)
|
||||
};
|
||||
|
@ -38,8 +38,8 @@ class JUCE_API MidiKeyboardStateListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MidiKeyboardStateListener() noexcept {}
|
||||
virtual ~MidiKeyboardStateListener() {}
|
||||
MidiKeyboardStateListener() = default;
|
||||
virtual ~MidiKeyboardStateListener() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Called when one of the MidiKeyboardState's keys is pressed.
|
||||
|
@ -224,9 +224,16 @@ MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const
|
||||
}
|
||||
else if (byte == 0xff)
|
||||
{
|
||||
int n;
|
||||
const int bytesLeft = readVariableLengthVal (src + 1, n);
|
||||
size = jmin (sz + 1, n + 2 + bytesLeft);
|
||||
if (sz == 1)
|
||||
{
|
||||
size = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int n;
|
||||
const int bytesLeft = readVariableLengthVal (src + 1, n);
|
||||
size = jmin (sz + 1, n + 2 + bytesLeft);
|
||||
}
|
||||
|
||||
auto dest = allocateSpace (size);
|
||||
*dest = (uint8) byte;
|
||||
|
@ -83,19 +83,19 @@ public:
|
||||
complete message, and will return the number of bytes it used. This lets
|
||||
you read a sequence of midi messages from a file or stream.
|
||||
|
||||
@param data the data to read from
|
||||
@param maxBytesToUse the maximum number of bytes it's allowed to read
|
||||
@param numBytesUsed returns the number of bytes that were actually needed
|
||||
@param lastStatusByte in a sequence of midi messages, the initial byte
|
||||
can be dropped from a message if it's the same as the
|
||||
first byte of the previous message, so this lets you
|
||||
supply the byte to use if the first byte of the message
|
||||
has in fact been dropped.
|
||||
@param timeStamp the time to give the midi message - this value doesn't
|
||||
use any particular units, so will be application-specific
|
||||
@param data the data to read from
|
||||
@param maxBytesToUse the maximum number of bytes it's allowed to read
|
||||
@param numBytesUsed returns the number of bytes that were actually needed
|
||||
@param lastStatusByte in a sequence of midi messages, the initial byte
|
||||
can be dropped from a message if it's the same as the
|
||||
first byte of the previous message, so this lets you
|
||||
supply the byte to use if the first byte of the message
|
||||
has in fact been dropped.
|
||||
@param timeStamp the time to give the midi message - this value doesn't
|
||||
use any particular units, so will be application-specific
|
||||
@param sysexHasEmbeddedLength when reading sysexes, this flag indicates whether
|
||||
to expect the data to begin with a variable-length field
|
||||
indicating its size
|
||||
to expect the data to begin with a variable-length
|
||||
field indicating its size
|
||||
*/
|
||||
MidiMessage (const void* data, int maxBytesToUse,
|
||||
int& numBytesUsed, uint8 lastStatusByte,
|
||||
|
@ -24,7 +24,7 @@ namespace juce
|
||||
{
|
||||
|
||||
MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) : message (mm) {}
|
||||
MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (static_cast<MidiMessage&&> (mm)) {}
|
||||
MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (std::move (mm)) {}
|
||||
MidiMessageSequence::MidiEventHolder::~MidiEventHolder() {}
|
||||
|
||||
//==============================================================================
|
||||
@ -53,13 +53,13 @@ MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence&
|
||||
}
|
||||
|
||||
MidiMessageSequence::MidiMessageSequence (MidiMessageSequence&& other) noexcept
|
||||
: list (static_cast<OwnedArray<MidiEventHolder>&&> (other.list))
|
||||
: list (std::move (other.list))
|
||||
{
|
||||
}
|
||||
|
||||
MidiMessageSequence& MidiMessageSequence::operator= (MidiMessageSequence&& other) noexcept
|
||||
{
|
||||
list = static_cast<OwnedArray<MidiEventHolder>&&> (other.list);
|
||||
list = std::move (other.list);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiM
|
||||
|
||||
MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiMessage&& newMessage, double timeAdjustment)
|
||||
{
|
||||
return addEvent (new MidiEventHolder (static_cast<MidiMessage&&> (newMessage)), timeAdjustment);
|
||||
return addEvent (new MidiEventHolder (std::move (newMessage)), timeAdjustment);
|
||||
}
|
||||
|
||||
void MidiMessageSequence::deleteEvent (int index, bool deleteMatchingNoteUp)
|
||||
|
@ -129,12 +129,12 @@ void MPEInstrument::setTimbreTrackingMode (TrackingMode modeToUse)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MPEInstrument::addListener (Listener* const listenerToAdd) noexcept
|
||||
void MPEInstrument::addListener (Listener* listenerToAdd)
|
||||
{
|
||||
listeners.add (listenerToAdd);
|
||||
}
|
||||
|
||||
void MPEInstrument::removeListener (Listener* const listenerToRemove) noexcept
|
||||
void MPEInstrument::removeListener (Listener* listenerToRemove)
|
||||
{
|
||||
listeners.remove (listenerToRemove);
|
||||
}
|
||||
@ -156,7 +156,7 @@ void MPEInstrument::processNextMidiEvent (const MidiMessage& message)
|
||||
//==============================================================================
|
||||
void MPEInstrument::processMidiNoteOnMessage (const MidiMessage& message)
|
||||
{
|
||||
// Note: if a note-on with velocity = 0 is used to convey a note-off,
|
||||
// Note: If a note-on with velocity = 0 is used to convey a note-off,
|
||||
// then the actual note-off velocity is not known. In this case,
|
||||
// the MPE convention is to use note-off velocity = 64.
|
||||
|
||||
|
@ -241,7 +241,7 @@ public:
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Listener() {}
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Implement this callback to be informed whenever a new expressive MIDI
|
||||
note is triggered.
|
||||
@ -271,7 +271,7 @@ public:
|
||||
MPE note's key state (whether the key is down and/or the note is
|
||||
sustained) has changed.
|
||||
|
||||
Note: if the key state changes to MPENote::off, noteReleased is
|
||||
Note: If the key state changes to MPENote::off, noteReleased is
|
||||
called instead.
|
||||
*/
|
||||
virtual void noteKeyStateChanged (MPENote changedNote) = 0;
|
||||
@ -286,10 +286,10 @@ public:
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener. */
|
||||
void addListener (Listener* listenerToAdd) noexcept;
|
||||
void addListener (Listener* listenerToAdd);
|
||||
|
||||
/** Removes a listener. */
|
||||
void removeListener (Listener* listenerToRemove) noexcept;
|
||||
void removeListener (Listener* listenerToRemove);
|
||||
|
||||
//==============================================================================
|
||||
/** Puts the instrument into legacy mode.
|
||||
@ -352,8 +352,7 @@ private:
|
||||
|
||||
struct MPEDimension
|
||||
{
|
||||
MPEDimension() noexcept : trackingMode (lastNotePlayedOnChannel) {}
|
||||
TrackingMode trackingMode;
|
||||
TrackingMode trackingMode = lastNotePlayedOnChannel;
|
||||
MPEValue lastValueReceivedOnChannel[16];
|
||||
MPEValue MPENote::* value;
|
||||
MPEValue& getValue (MPENote& note) noexcept { return note.*(value); }
|
||||
|
@ -215,7 +215,7 @@ private:
|
||||
std::size_t pos = 0;
|
||||
MidiBuffer::Iterator iter (midiBuffer);
|
||||
MidiMessage midiMessage;
|
||||
int samplePosition; // Note: not actually used, so no need to initialise.
|
||||
int samplePosition; // Note: Not actually used, so no need to initialise.
|
||||
|
||||
while (iter.getNextEvent (midiMessage, samplePosition))
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ namespace juce
|
||||
class instead. You just need to take care to send them to the appropriate
|
||||
per-note MIDI channel.
|
||||
|
||||
Note: if you are working with an MPEZoneLayout object inside your app,
|
||||
Note: If you are working with an MPEZoneLayout object inside your app,
|
||||
you should not use the message sequences provided here. Instead, you should
|
||||
change the zone layout programmatically with the member functions provided in the
|
||||
MPEZoneLayout class itself. You should also make sure that the Expressive
|
||||
|
@ -132,7 +132,7 @@ struct JUCE_API MPENote
|
||||
*/
|
||||
MPEValue pressure { MPEValue::centreValue() };
|
||||
|
||||
/** Inital value of timbre when the note was triggered.
|
||||
/** Initial value of timbre when the note was triggered.
|
||||
This should never change during the lifetime of an MPENote object.
|
||||
*/
|
||||
MPEValue initialTimbre { MPEValue::centreValue() };
|
||||
|
@ -42,13 +42,16 @@ MPESynthesiser::~MPESynthesiser()
|
||||
void MPESynthesiser::startVoice (MPESynthesiserVoice* voice, MPENote noteToStart)
|
||||
{
|
||||
jassert (voice != nullptr);
|
||||
|
||||
voice->currentlyPlayingNote = noteToStart;
|
||||
voice->noteOnTime = lastNoteOnCounter++;
|
||||
voice->noteStarted();
|
||||
}
|
||||
|
||||
void MPESynthesiser::stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff)
|
||||
{
|
||||
jassert (voice != nullptr);
|
||||
|
||||
voice->currentlyPlayingNote = noteToStop;
|
||||
voice->noteStopped (allowTailOff);
|
||||
}
|
||||
@ -197,7 +200,7 @@ MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceF
|
||||
// compilers generating code containing heap allocations..
|
||||
struct Sorter
|
||||
{
|
||||
bool operator() (const MPESynthesiserVoice* a, const MPESynthesiserVoice* b) const noexcept { return a->wasStartedBefore (*b); }
|
||||
bool operator() (const MPESynthesiserVoice* a, const MPESynthesiserVoice* b) const noexcept { return a->noteOnTime < b->noteOnTime; }
|
||||
};
|
||||
|
||||
std::sort (usableVoices.begin(), usableVoices.end(), Sorter());
|
||||
|
@ -72,7 +72,7 @@ public:
|
||||
MPESynthesiser (MPEInstrument* instrument);
|
||||
|
||||
/** Destructor. */
|
||||
~MPESynthesiser();
|
||||
~MPESynthesiser() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Deletes all voices. */
|
||||
@ -188,7 +188,7 @@ protected:
|
||||
renderNextBlock(). Do not call it yourself, otherwise the internal MPE note state
|
||||
will become inconsistent.
|
||||
*/
|
||||
virtual void noteAdded (MPENote newNote) override;
|
||||
void noteAdded (MPENote newNote) override;
|
||||
|
||||
/** Stops playing a note.
|
||||
|
||||
@ -203,7 +203,7 @@ protected:
|
||||
renderNextBlock(). Do not call it yourself, otherwise the internal MPE note state
|
||||
will become inconsistent.
|
||||
*/
|
||||
virtual void noteReleased (MPENote finishedNote) override;
|
||||
void noteReleased (MPENote finishedNote) override;
|
||||
|
||||
/** Will find any voice that is currently playing changedNote, update its
|
||||
currently playing note, and call its notePressureChanged method.
|
||||
@ -211,7 +211,7 @@ protected:
|
||||
This method will be called automatically according to the midi data passed into
|
||||
renderNextBlock(). Do not call it yourself.
|
||||
*/
|
||||
virtual void notePressureChanged (MPENote changedNote) override;
|
||||
void notePressureChanged (MPENote changedNote) override;
|
||||
|
||||
/** Will find any voice that is currently playing changedNote, update its
|
||||
currently playing note, and call its notePitchbendChanged method.
|
||||
@ -219,7 +219,7 @@ protected:
|
||||
This method will be called automatically according to the midi data passed into
|
||||
renderNextBlock(). Do not call it yourself.
|
||||
*/
|
||||
virtual void notePitchbendChanged (MPENote changedNote) override;
|
||||
void notePitchbendChanged (MPENote changedNote) override;
|
||||
|
||||
/** Will find any voice that is currently playing changedNote, update its
|
||||
currently playing note, and call its noteTimbreChanged method.
|
||||
@ -227,7 +227,7 @@ protected:
|
||||
This method will be called automatically according to the midi data passed into
|
||||
renderNextBlock(). Do not call it yourself.
|
||||
*/
|
||||
virtual void noteTimbreChanged (MPENote changedNote) override;
|
||||
void noteTimbreChanged (MPENote changedNote) override;
|
||||
|
||||
/** Will find any voice that is currently playing changedNote, update its
|
||||
currently playing note, and call its noteKeyStateChanged method.
|
||||
@ -235,24 +235,24 @@ protected:
|
||||
This method will be called automatically according to the midi data passed into
|
||||
renderNextBlock(). Do not call it yourself.
|
||||
*/
|
||||
virtual void noteKeyStateChanged (MPENote changedNote) override;
|
||||
void noteKeyStateChanged (MPENote changedNote) override;
|
||||
|
||||
//==============================================================================
|
||||
/** This will simply call renderNextBlock for each currently active
|
||||
voice and fill the buffer with the sum.
|
||||
Override this method if you need to do more work to render your audio.
|
||||
*/
|
||||
virtual void renderNextSubBlock (AudioBuffer<float>& outputAudio,
|
||||
int startSample,
|
||||
int numSamples) override;
|
||||
void renderNextSubBlock (AudioBuffer<float>& outputAudio,
|
||||
int startSample,
|
||||
int numSamples) override;
|
||||
|
||||
/** This will simply call renderNextBlock for each currently active
|
||||
voice and fill the buffer with the sum. (souble-precision version)
|
||||
Override this method if you need to do more work to render your audio.
|
||||
*/
|
||||
virtual void renderNextSubBlock (AudioBuffer<double>& outputAudio,
|
||||
int startSample,
|
||||
int numSamples) override;
|
||||
void renderNextSubBlock (AudioBuffer<double>& outputAudio,
|
||||
int startSample,
|
||||
int numSamples) override;
|
||||
|
||||
//==============================================================================
|
||||
/** Searches through the voices to find one that's not currently playing, and
|
||||
@ -304,6 +304,7 @@ protected:
|
||||
private:
|
||||
//==============================================================================
|
||||
bool shouldStealVoices = false;
|
||||
uint32 lastNoteOnCounter = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiser)
|
||||
};
|
||||
|
@ -42,11 +42,6 @@ bool MPESynthesiserVoice::isPlayingButReleased() const noexcept
|
||||
return isActive() && currentlyPlayingNote.keyState == MPENote::off;
|
||||
}
|
||||
|
||||
bool MPESynthesiserVoice::wasStartedBefore (const MPESynthesiserVoice& other) const noexcept
|
||||
{
|
||||
return noteStartTime < other.noteStartTime;
|
||||
}
|
||||
|
||||
void MPESynthesiserVoice::clearCurrentNote() noexcept
|
||||
{
|
||||
currentlyPlayingNote = MPENote();
|
||||
|
@ -156,8 +156,10 @@ public:
|
||||
*/
|
||||
double getSampleRate() const noexcept { return currentSampleRate; }
|
||||
|
||||
/** Returns true if this voice started playing its current note before the other voice did. */
|
||||
bool wasStartedBefore (const MPESynthesiserVoice& other) const noexcept;
|
||||
/** This will be set to an incrementing counter value in MPESynthesiser::startVoice()
|
||||
and can be used to determine the order in which voices started.
|
||||
*/
|
||||
uint32 noteOnTime = 0;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
@ -182,7 +184,6 @@ protected:
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class MPESynthesiser;
|
||||
uint32 noteStartTime = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiserVoice)
|
||||
};
|
||||
|
@ -80,13 +80,7 @@ public:
|
||||
*/
|
||||
struct Zone
|
||||
{
|
||||
Zone (const Zone& other) noexcept
|
||||
: numMemberChannels (other.numMemberChannels),
|
||||
perNotePitchbendRange (other.perNotePitchbendRange),
|
||||
masterPitchbendRange (other.masterPitchbendRange),
|
||||
lowerZone (other.lowerZone)
|
||||
{
|
||||
}
|
||||
Zone (const Zone& other) = default;
|
||||
|
||||
bool isLowerZone() const noexcept { return lowerZone; }
|
||||
bool isUpperZone() const noexcept { return ! lowerZone; }
|
||||
@ -185,7 +179,7 @@ public:
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Listener() {}
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Implement this callback to be notified about any changes to this
|
||||
MPEZoneLayout. Will be called whenever a zone is added, zones are
|
||||
|
@ -32,9 +32,7 @@ namespace juce
|
||||
struct JUCE_API AudioSourceChannelInfo
|
||||
{
|
||||
/** Creates an uninitialised AudioSourceChannelInfo. */
|
||||
AudioSourceChannelInfo() noexcept
|
||||
{
|
||||
}
|
||||
AudioSourceChannelInfo() = default;
|
||||
|
||||
/** Creates an AudioSourceChannelInfo. */
|
||||
AudioSourceChannelInfo (AudioBuffer<float>* bufferToUse,
|
||||
@ -113,11 +111,11 @@ class JUCE_API AudioSource
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates an AudioSource. */
|
||||
AudioSource() noexcept {}
|
||||
AudioSource() = default;
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AudioSource() {}
|
||||
virtual ~AudioSource() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Tells the source to prepare for playing.
|
||||
|
@ -66,7 +66,7 @@ public:
|
||||
The input source may be deleted depending on whether the deleteSourceWhenDeleted
|
||||
flag was set in the constructor.
|
||||
*/
|
||||
~BufferingAudioSource();
|
||||
~BufferingAudioSource() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Implementation of the AudioSource method. */
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
bool deleteSourceWhenDeleted);
|
||||
|
||||
/** Destructor. */
|
||||
~ChannelRemappingAudioSource();
|
||||
~ChannelRemappingAudioSource() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Specifies a number of channels that this audio source must produce from its
|
||||
|
@ -43,7 +43,7 @@ public:
|
||||
bool deleteInputWhenDeleted);
|
||||
|
||||
/** Destructor. */
|
||||
~IIRFilterAudioSource();
|
||||
~IIRFilterAudioSource() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the filter to use the same parameters as the one being passed in. */
|
||||
|
@ -49,7 +49,8 @@ void MemoryAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferT
|
||||
auto max = 0, pos = 0;
|
||||
auto n = buffer.getNumSamples(), m = bufferToFill.numSamples;
|
||||
|
||||
for (auto i = position; (i < n || isLooping) && (pos < m); i += max)
|
||||
int i;
|
||||
for (i = position; (i < n || isLooping) && (pos < m); i += max)
|
||||
{
|
||||
max = jmin (m - pos, n - (i % n));
|
||||
|
||||
@ -65,6 +66,8 @@ void MemoryAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferT
|
||||
|
||||
if (pos < m)
|
||||
dst.clear (bufferToFill.startSample + pos, m - pos);
|
||||
|
||||
position = (i % n);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
MixerAudioSource();
|
||||
|
||||
/** Destructor. */
|
||||
~MixerAudioSource();
|
||||
~MixerAudioSource() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds an input source to the mixer.
|
||||
|
@ -40,11 +40,11 @@ class JUCE_API PositionableAudioSource : public AudioSource
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates the PositionableAudioSource. */
|
||||
PositionableAudioSource() noexcept {}
|
||||
PositionableAudioSource() = default;
|
||||
|
||||
public:
|
||||
/** Destructor */
|
||||
~PositionableAudioSource() {}
|
||||
~PositionableAudioSource() override = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Tells the stream to move to a new position.
|
||||
|
@ -47,7 +47,7 @@ public:
|
||||
int numChannels = 2);
|
||||
|
||||
/** Destructor. */
|
||||
~ResamplingAudioSource();
|
||||
~ResamplingAudioSource() override;
|
||||
|
||||
/** Changes the resampling ratio.
|
||||
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
bool deleteInputWhenDeleted);
|
||||
|
||||
/** Destructor. */
|
||||
~ReverbAudioSource();
|
||||
~ReverbAudioSource() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the parameters from the reverb. */
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
ToneGeneratorAudioSource();
|
||||
|
||||
/** Destructor. */
|
||||
~ToneGeneratorAudioSource();
|
||||
~ToneGeneratorAudioSource() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the signal's amplitude. */
|
||||
|
@ -217,6 +217,18 @@ void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
|
||||
template void Synthesiser::processNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
|
||||
template void Synthesiser::processNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
|
||||
|
||||
void Synthesiser::renderNextBlock (AudioBuffer<float>& outputAudio, const MidiBuffer& inputMidi,
|
||||
int startSample, int numSamples)
|
||||
{
|
||||
processNextBlock (outputAudio, inputMidi, startSample, numSamples);
|
||||
}
|
||||
|
||||
void Synthesiser::renderNextBlock (AudioBuffer<double>& outputAudio, const MidiBuffer& inputMidi,
|
||||
int startSample, int numSamples)
|
||||
{
|
||||
processNextBlock (outputAudio, inputMidi, startSample, numSamples);
|
||||
}
|
||||
|
||||
void Synthesiser::renderVoices (AudioBuffer<float>& buffer, int startSample, int numSamples)
|
||||
{
|
||||
for (auto* voice : voices)
|
||||
@ -323,7 +335,7 @@ void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool
|
||||
voice->stopNote (velocity, allowTailOff);
|
||||
|
||||
// the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
|
||||
jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
|
||||
jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == nullptr));
|
||||
}
|
||||
|
||||
void Synthesiser::noteOff (const int midiChannel,
|
||||
@ -338,7 +350,7 @@ void Synthesiser::noteOff (const int midiChannel,
|
||||
if (voice->getCurrentlyPlayingNote() == midiNoteNumber
|
||||
&& voice->isPlayingChannel (midiChannel))
|
||||
{
|
||||
if (SynthesiserSound* const sound = voice->getCurrentlyPlayingSound())
|
||||
if (auto sound = voice->getCurrentlyPlayingSound())
|
||||
{
|
||||
if (sound->appliesToNote (midiNoteNumber)
|
||||
&& sound->appliesToChannel (midiChannel))
|
||||
|
@ -46,7 +46,7 @@ protected:
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~SynthesiserSound();
|
||||
~SynthesiserSound() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this sound should be played when a given midi note is pressed.
|
||||
@ -352,7 +352,7 @@ public:
|
||||
int getNumSounds() const noexcept { return sounds.size(); }
|
||||
|
||||
/** Returns one of the sounds. */
|
||||
SynthesiserSound* getSound (int index) const noexcept { return sounds [index]; }
|
||||
SynthesiserSound::Ptr getSound (int index) const noexcept { return sounds[index]; }
|
||||
|
||||
/** Adds a new sound to the synthesiser.
|
||||
|
||||
@ -525,21 +525,15 @@ public:
|
||||
both to the audio output buffer and the midi input buffer, so any midi events
|
||||
with timestamps outside the specified region will be ignored.
|
||||
*/
|
||||
inline void renderNextBlock (AudioBuffer<float>& outputAudio,
|
||||
const MidiBuffer& inputMidi,
|
||||
int startSample,
|
||||
int numSamples)
|
||||
{
|
||||
processNextBlock (outputAudio, inputMidi, startSample, numSamples);
|
||||
}
|
||||
void renderNextBlock (AudioBuffer<float>& outputAudio,
|
||||
const MidiBuffer& inputMidi,
|
||||
int startSample,
|
||||
int numSamples);
|
||||
|
||||
inline void renderNextBlock (AudioBuffer<double>& outputAudio,
|
||||
const MidiBuffer& inputMidi,
|
||||
int startSample,
|
||||
int numSamples)
|
||||
{
|
||||
processNextBlock (outputAudio, inputMidi, startSample, numSamples);
|
||||
}
|
||||
void renderNextBlock (AudioBuffer<double>& outputAudio,
|
||||
const MidiBuffer& inputMidi,
|
||||
int startSample,
|
||||
int numSamples);
|
||||
|
||||
/** Returns the current target sample rate at which rendering is being done.
|
||||
Subclasses may need to know this so that they can pitch things correctly.
|
||||
@ -632,12 +626,6 @@ protected:
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename floatType>
|
||||
void processNextBlock (AudioBuffer<floatType>& outputAudio,
|
||||
const MidiBuffer& inputMidi,
|
||||
int startSample,
|
||||
int numSamples);
|
||||
//==============================================================================
|
||||
double sampleRate = 0;
|
||||
uint32 lastNoteOnCounter = 0;
|
||||
int minimumSubBlockSize = 32;
|
||||
@ -645,6 +633,9 @@ private:
|
||||
bool shouldStealNotes = true;
|
||||
BigInteger sustainPedalsDown;
|
||||
|
||||
template <typename floatType>
|
||||
void processNextBlock (AudioBuffer<floatType>&, const MidiBuffer&, int startSample, int numSamples);
|
||||
|
||||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
|
||||
// Note the new parameters for these methods.
|
||||
virtual int findFreeVoice (const bool) const { return 0; }
|
||||
|
255
modules/juce_audio_basics/utilities/juce_ADSR.h
Normal file
255
modules/juce_audio_basics/utilities/juce_ADSR.h
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
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 very simple ADSR envelope class.
|
||||
|
||||
To use it, call setSampleRate() with the current sample rate and give it some parameters
|
||||
with setParameters() then call getNextSample() to get the envelope value to be applied
|
||||
to each audio sample or applyEnvelopeToBuffer() to apply the envelope to a whole buffer.
|
||||
*/
|
||||
class ADSR
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
ADSR()
|
||||
{
|
||||
setSampleRate (44100.0);
|
||||
setParameters ({});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Holds the parameters being used by an ADSR object. */
|
||||
struct Parameters
|
||||
{
|
||||
/** Attack time in seconds. */
|
||||
float attack = 0.1f;
|
||||
|
||||
/** Decay time in seconds. */
|
||||
float decay = 0.1f;
|
||||
|
||||
/** Sustain level. */
|
||||
float sustain = 1.0f;
|
||||
|
||||
/** Release time in seconds. */
|
||||
float release = 0.1f;
|
||||
};
|
||||
|
||||
/** Sets the parameters that will be used by an ADSR object.
|
||||
|
||||
You must have called setSampleRate() with the correct sample rate before
|
||||
this otherwise the values may be incorrect!
|
||||
|
||||
@see getParameters
|
||||
*/
|
||||
void setParameters (const Parameters& newParameters)
|
||||
{
|
||||
currentParameters = newParameters;
|
||||
|
||||
sustainLevel = newParameters.sustain;
|
||||
calculateRates (newParameters);
|
||||
|
||||
if (currentState != State::idle)
|
||||
checkCurrentState();
|
||||
}
|
||||
|
||||
/** Returns the parameters currently being used by an ADSR object.
|
||||
|
||||
@see setParameters
|
||||
*/
|
||||
const Parameters& getParameters() const { return currentParameters; }
|
||||
|
||||
/** Returns true if the envelope is in its attack, decay, sustain or release stage. */
|
||||
bool isActive() const noexcept { return currentState != State::idle; }
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the sample rate that will be used for the envelope.
|
||||
|
||||
This must be called before the getNextSample() or setParameters() methods.
|
||||
*/
|
||||
void setSampleRate (double sampleRate)
|
||||
{
|
||||
jassert (sampleRate > 0.0);
|
||||
sr = sampleRate;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Resets the envelope to an idle state. */
|
||||
void reset()
|
||||
{
|
||||
envelopeVal = 0.0f;
|
||||
currentState = State::idle;
|
||||
|
||||
if (resetReleaseRate)
|
||||
{
|
||||
releaseRate = static_cast<float> (sustainLevel / (currentParameters.release * sr));
|
||||
resetReleaseRate = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts the attack phase of the envelope. */
|
||||
void noteOn()
|
||||
{
|
||||
if (attackRate > 0.0f)
|
||||
{
|
||||
currentState = State::attack;
|
||||
}
|
||||
else if (decayRate > 0.0f)
|
||||
{
|
||||
envelopeVal = 1.0f;
|
||||
currentState = State::decay;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = State::sustain;
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts the release phase of the envelope. */
|
||||
void noteOff()
|
||||
{
|
||||
if (currentState != State::idle)
|
||||
{
|
||||
if (releaseRate > 0.0f)
|
||||
{
|
||||
if (currentState != State::sustain)
|
||||
{
|
||||
releaseRate = static_cast<float> (envelopeVal / (currentParameters.release * sr));
|
||||
resetReleaseRate = true;
|
||||
}
|
||||
|
||||
currentState = State::release;
|
||||
}
|
||||
else
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the next sample value for an ADSR object.
|
||||
|
||||
@see applyEnvelopeToBuffer
|
||||
*/
|
||||
float getNextSample()
|
||||
{
|
||||
if (currentState == State::idle)
|
||||
return 0.0f;
|
||||
|
||||
if (currentState == State::attack)
|
||||
{
|
||||
envelopeVal += attackRate;
|
||||
|
||||
if (envelopeVal >= 1.0f)
|
||||
{
|
||||
envelopeVal = 1.0f;
|
||||
|
||||
if (decayRate > 0.0f)
|
||||
currentState = State::decay;
|
||||
else
|
||||
currentState = State::sustain;
|
||||
}
|
||||
}
|
||||
else if (currentState == State::decay)
|
||||
{
|
||||
envelopeVal -= decayRate;
|
||||
|
||||
if (envelopeVal <= sustainLevel)
|
||||
{
|
||||
envelopeVal = sustainLevel;
|
||||
currentState = State::sustain;
|
||||
}
|
||||
}
|
||||
else if (currentState == State::sustain)
|
||||
{
|
||||
envelopeVal = sustainLevel;
|
||||
}
|
||||
else if (currentState == State::release)
|
||||
{
|
||||
envelopeVal -= releaseRate;
|
||||
|
||||
if (envelopeVal <= 0.0f)
|
||||
reset();
|
||||
}
|
||||
|
||||
return envelopeVal;
|
||||
}
|
||||
|
||||
/** This method will conveniently apply the next numSamples number of envelope values
|
||||
to an AudioBuffer.
|
||||
|
||||
@see getNextSample
|
||||
*/
|
||||
template<typename FloatType>
|
||||
void applyEnvelopeToBuffer (AudioBuffer<FloatType>& buffer, int startSample, int numSamples)
|
||||
{
|
||||
jassert (startSample + numSamples <= buffer.getNumSamples());
|
||||
|
||||
auto numChannels = buffer.getNumChannels();
|
||||
|
||||
while (--numSamples >= 0)
|
||||
{
|
||||
auto env = getNextSample();
|
||||
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
buffer.getWritePointer (i)[startSample] *= env;
|
||||
|
||||
++startSample;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void calculateRates (const Parameters& parameters)
|
||||
{
|
||||
// need to call setSampleRate() first!
|
||||
jassert (sr > 0.0);
|
||||
|
||||
attackRate = (parameters.attack > 0.0f ? static_cast<float> (1.0f / (parameters.attack * sr)) : -1.0f);
|
||||
decayRate = (parameters.decay > 0.0f ? static_cast<float> ((1.0f - sustainLevel) / (parameters.decay * sr)) : -1.0f);
|
||||
releaseRate = (parameters.release > 0.0f ? static_cast<float> (sustainLevel / (parameters.release * sr)) : -1.0f);
|
||||
}
|
||||
|
||||
void checkCurrentState()
|
||||
{
|
||||
if (currentState == State::attack && attackRate <= 0.0f) currentState = decayRate > 0.0f ? State::decay : State::sustain;
|
||||
else if (currentState == State::decay && decayRate <= 0.0f) currentState = State::sustain;
|
||||
else if (currentState == State::release && releaseRate <= 0.0f) reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
enum class State { idle, attack, decay, sustain, release };
|
||||
|
||||
State currentState = State::idle;
|
||||
Parameters currentParameters;
|
||||
|
||||
double sr = 0.0;
|
||||
float envelopeVal = 0.0f, sustainLevel = 0.0f, attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f;
|
||||
bool resetReleaseRate = false;
|
||||
};
|
||||
|
||||
} // namespace juce
|
@ -242,6 +242,10 @@ namespace
|
||||
}
|
||||
|
||||
subSamplePos = pos;
|
||||
|
||||
if (wrap == 0)
|
||||
return (int) (in - originalIn);
|
||||
|
||||
return ((int) (in - originalIn) + wrap) % wrap;
|
||||
}
|
||||
|
||||
@ -353,6 +357,10 @@ namespace
|
||||
}
|
||||
|
||||
subSamplePos = pos;
|
||||
|
||||
if (wrap == 0)
|
||||
return (int) (in - originalIn);
|
||||
|
||||
return ((int) (in - originalIn) + wrap) % wrap;
|
||||
}
|
||||
|
@ -49,22 +49,13 @@ public:
|
||||
/** Holds the parameters being used by a Reverb object. */
|
||||
struct Parameters
|
||||
{
|
||||
Parameters() noexcept
|
||||
: roomSize (0.5f),
|
||||
damping (0.5f),
|
||||
wetLevel (0.33f),
|
||||
dryLevel (0.4f),
|
||||
width (1.0f),
|
||||
freezeMode (0)
|
||||
{}
|
||||
|
||||
float roomSize; /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */
|
||||
float damping; /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */
|
||||
float wetLevel; /**< Wet level, 0 to 1.0 */
|
||||
float dryLevel; /**< Dry level, 0 to 1.0 */
|
||||
float width; /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */
|
||||
float freezeMode; /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5
|
||||
put the reverb into a continuous feedback loop. */
|
||||
float roomSize = 0.5f; /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */
|
||||
float damping = 0.5f; /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */
|
||||
float wetLevel = 0.33f; /**< Wet level, 0 to 1.0 */
|
||||
float dryLevel = 0.4f; /**< Dry level, 0 to 1.0 */
|
||||
float width = 1.0f; /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */
|
||||
float freezeMode = 0.0f; /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5
|
||||
put the reverb into a continuous feedback loop. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
@ -81,9 +72,9 @@ public:
|
||||
const float dryScaleFactor = 2.0f;
|
||||
|
||||
const float wet = newParams.wetLevel * wetScaleFactor;
|
||||
dryGain.setValue (newParams.dryLevel * dryScaleFactor);
|
||||
wetGain1.setValue (0.5f * wet * (1.0f + newParams.width));
|
||||
wetGain2.setValue (0.5f * wet * (1.0f - newParams.width));
|
||||
dryGain.setTargetValue (newParams.dryLevel * dryScaleFactor);
|
||||
wetGain1.setTargetValue (0.5f * wet * (1.0f + newParams.width));
|
||||
wetGain2.setTargetValue (0.5f * wet * (1.0f - newParams.width));
|
||||
|
||||
gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f;
|
||||
parameters = newParams;
|
||||
@ -216,15 +207,15 @@ private:
|
||||
|
||||
void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept
|
||||
{
|
||||
damping.setValue (dampingToUse);
|
||||
feedback.setValue (roomSizeToUse);
|
||||
damping.setTargetValue (dampingToUse);
|
||||
feedback.setTargetValue (roomSizeToUse);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class CombFilter
|
||||
{
|
||||
public:
|
||||
CombFilter() noexcept : bufferSize (0), bufferIndex (0), last (0) {}
|
||||
CombFilter() noexcept {}
|
||||
|
||||
void setSize (const int size)
|
||||
{
|
||||
@ -259,8 +250,8 @@ private:
|
||||
|
||||
private:
|
||||
HeapBlock<float> buffer;
|
||||
int bufferSize, bufferIndex;
|
||||
float last;
|
||||
int bufferSize = 0, bufferIndex = 0;
|
||||
float last = 0.0f;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CombFilter)
|
||||
};
|
||||
@ -269,7 +260,7 @@ private:
|
||||
class AllPassFilter
|
||||
{
|
||||
public:
|
||||
AllPassFilter() noexcept : bufferSize (0), bufferIndex (0) {}
|
||||
AllPassFilter() noexcept {}
|
||||
|
||||
void setSize (const int size)
|
||||
{
|
||||
@ -300,7 +291,7 @@ private:
|
||||
|
||||
private:
|
||||
HeapBlock<float> buffer;
|
||||
int bufferSize, bufferIndex;
|
||||
int bufferSize = 0, bufferIndex = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AllPassFilter)
|
||||
};
|
||||
@ -314,7 +305,7 @@ private:
|
||||
CombFilter comb [numChannels][numCombs];
|
||||
AllPassFilter allPass [numChannels][numAllPasses];
|
||||
|
||||
LinearSmoothedValue<float> damping, feedback, dryGain, wetGain1, wetGain2;
|
||||
SmoothedValue<float> damping, feedback, dryGain, wetGain1, wetGain2;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb)
|
||||
};
|
92
modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp
Normal file
92
modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2018 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
static CommonSmoothedValueTests <SmoothedValue<float, ValueSmoothingTypes::Linear>> commonLinearSmoothedValueTests;
|
||||
static CommonSmoothedValueTests <SmoothedValue<float, ValueSmoothingTypes::Multiplicative>> commonMultiplicativeSmoothedValueTests;
|
||||
|
||||
class SmoothedValueTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
SmoothedValueTests()
|
||||
: UnitTest ("SmoothedValueTests", "SmoothedValues")
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Linear moving target");
|
||||
{
|
||||
SmoothedValue<float, ValueSmoothingTypes::Linear> sv;
|
||||
|
||||
sv.reset (12);
|
||||
float initialValue = 0.0f;
|
||||
sv.setCurrentAndTargetValue (initialValue);
|
||||
sv.setTargetValue (1.0f);
|
||||
|
||||
auto delta = sv.getNextValue() - initialValue;
|
||||
|
||||
sv.skip (6);
|
||||
|
||||
auto newInitialValue = sv.getCurrentValue();
|
||||
sv.setTargetValue (newInitialValue + 2.0f);
|
||||
auto doubleDelta = sv.getNextValue() - newInitialValue;
|
||||
|
||||
expectWithinAbsoluteError (doubleDelta, delta * 2.0f, 1.0e-7f);
|
||||
}
|
||||
|
||||
beginTest ("Multiplicative curve");
|
||||
{
|
||||
SmoothedValue<double, ValueSmoothingTypes::Multiplicative> sv;
|
||||
|
||||
auto numSamples = 12;
|
||||
AudioBuffer<double> values (2, numSamples + 1);
|
||||
|
||||
sv.reset (numSamples);
|
||||
sv.setCurrentAndTargetValue (1.0);
|
||||
sv.setTargetValue (2.0f);
|
||||
|
||||
values.setSample (0, 0, sv.getCurrentValue());
|
||||
|
||||
for (int i = 1; i < values.getNumSamples(); ++i)
|
||||
values.setSample (0, i, sv.getNextValue());
|
||||
|
||||
sv.setTargetValue (1.0f);
|
||||
values.setSample (1, values.getNumSamples() - 1, sv.getCurrentValue());
|
||||
|
||||
for (int i = values.getNumSamples() - 2; i >= 0 ; --i)
|
||||
values.setSample (1, i, sv.getNextValue());
|
||||
|
||||
for (int i = 0; i < values.getNumSamples(); ++i)
|
||||
expectWithinAbsoluteError (values.getSample (0, i), values.getSample (1, i), 1.0e-9);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static SmoothedValueTests smoothedValueTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
622
modules/juce_audio_basics/utilities/juce_SmoothedValue.h
Normal file
622
modules/juce_audio_basics/utilities/juce_SmoothedValue.h
Normal file
@ -0,0 +1,622 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
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 base class for the smoothed value classes.
|
||||
|
||||
This class is used to provide common functionality to the SmoothedValue and
|
||||
dsp::LogRampedValue classes.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
template <typename SmoothedValueType>
|
||||
class SmoothedValueBase
|
||||
{
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename T> struct FloatTypeHelper;
|
||||
|
||||
template <template <typename> class SmoothedValueClass, typename FloatType>
|
||||
struct FloatTypeHelper <SmoothedValueClass <FloatType>>
|
||||
{
|
||||
using Type = FloatType;
|
||||
};
|
||||
|
||||
template <template <typename, typename> class SmoothedValueClass, typename FloatType, typename SmoothingType>
|
||||
struct FloatTypeHelper <SmoothedValueClass <FloatType, SmoothingType>>
|
||||
{
|
||||
using Type = FloatType;
|
||||
};
|
||||
|
||||
public:
|
||||
using FloatType = typename FloatTypeHelper<SmoothedValueType>::Type;
|
||||
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
SmoothedValueBase() = default;
|
||||
|
||||
virtual ~SmoothedValueBase() {}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the current value is currently being interpolated. */
|
||||
bool isSmoothing() const noexcept { return countdown > 0; }
|
||||
|
||||
/** Returns the current value of the ramp. */
|
||||
FloatType getCurrentValue() const noexcept { return currentValue; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the target value towards which the smoothed value is currently moving. */
|
||||
FloatType getTargetValue() const noexcept { return target; }
|
||||
|
||||
/** Sets the current value and the target value.
|
||||
@param newValue the new value to take
|
||||
*/
|
||||
void setCurrentAndTargetValue (FloatType newValue)
|
||||
{
|
||||
target = currentValue = newValue;
|
||||
countdown = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Applies a smoothed gain to a stream of samples
|
||||
S[i] *= gain
|
||||
@param samples Pointer to a raw array of samples
|
||||
@param numSamples Length of array of samples
|
||||
*/
|
||||
void applyGain (FloatType* samples, int numSamples) noexcept
|
||||
{
|
||||
jassert (numSamples >= 0);
|
||||
|
||||
if (isSmoothing())
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
samples[i] *= getNextSmoothedValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
FloatVectorOperations::multiply (samples, target, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
/** Computes output as a smoothed gain applied to a stream of samples.
|
||||
Sout[i] = Sin[i] * gain
|
||||
@param samplesOut A pointer to a raw array of output samples
|
||||
@param samplesIn A pointer to a raw array of input samples
|
||||
@param numSamples The length of the array of samples
|
||||
*/
|
||||
void applyGain (FloatType* samplesOut, const FloatType* samplesIn, int numSamples) noexcept
|
||||
{
|
||||
jassert (numSamples >= 0);
|
||||
|
||||
if (isSmoothing())
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
samplesOut[i] = samplesIn[i] * getNextSmoothedValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
/** Applies a smoothed gain to a buffer */
|
||||
void applyGain (AudioBuffer<FloatType>& buffer, int numSamples) noexcept
|
||||
{
|
||||
jassert (numSamples >= 0);
|
||||
|
||||
if (isSmoothing())
|
||||
{
|
||||
if (buffer.getNumChannels() == 1)
|
||||
{
|
||||
auto* samples = buffer.getWritePointer (0);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
samples[i] *= getNextSmoothedValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto i = 0; i < numSamples; ++i)
|
||||
{
|
||||
auto gain = getNextSmoothedValue();
|
||||
|
||||
for (int channel = 0; channel < buffer.getNumChannels(); channel++)
|
||||
buffer.setSample (channel, i, buffer.getSample (channel, i) * gain);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.applyGain (0, numSamples, target);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
FloatType getNextSmoothedValue() noexcept
|
||||
{
|
||||
return static_cast <SmoothedValueType*> (this)->getNextValue();
|
||||
}
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
FloatType currentValue = 0;
|
||||
FloatType target = currentValue;
|
||||
int countdown = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A namespace containing a set of types used for specifying the smoothing
|
||||
behaviour of the SmoothedValue class.
|
||||
|
||||
For example:
|
||||
@code
|
||||
SmoothedValue<float, ValueSmoothingTypes::Multiplicative> frequency (1.0f);
|
||||
@endcode
|
||||
*/
|
||||
namespace ValueSmoothingTypes
|
||||
{
|
||||
/** Used to indicate a linear smoothing between values. */
|
||||
struct Linear {};
|
||||
|
||||
/** Used to indicate a smoothing between multiplicative values. */
|
||||
struct Multiplicative {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A utility class for values that need smoothing to avoid audio glitches.
|
||||
|
||||
A ValueSmoothingTypes::Linear template parameter selects linear smoothing,
|
||||
which increments the SmoothedValue linearly towards its target value.
|
||||
|
||||
@code
|
||||
SmoothedValue<float, ValueSmoothingTypes::Linear> yourSmoothedValue;
|
||||
@endcode
|
||||
|
||||
A ValueSmoothingTypes::Multiplicative template parameter selects
|
||||
multiplicative smoothing increments towards the target value.
|
||||
|
||||
@code
|
||||
SmoothedValue<float, ValueSmoothingTypes::Multiplicative> yourSmoothedValue;
|
||||
@endcode
|
||||
|
||||
Multiplicative smoothing is useful when you are dealing with
|
||||
exponential/logarithmic values like volume in dB or frequency in Hz. For
|
||||
example a 12 step ramp from 440.0 Hz (A4) to 880.0 Hz (A5) will increase the
|
||||
frequency with an equal temperament tuning across the octave. A 10 step
|
||||
smoothing from 1.0 (0 dB) to 3.16228 (10 dB) will increase the value in
|
||||
increments of 1 dB.
|
||||
|
||||
Note that when you are using multiplicative smoothing you cannot ever reach a
|
||||
target value of zero!
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
template <typename FloatType, typename SmoothingType = ValueSmoothingTypes::Linear>
|
||||
class SmoothedValue : public SmoothedValueBase <SmoothedValue <FloatType, SmoothingType>>
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
SmoothedValue() noexcept
|
||||
: SmoothedValue ((FloatType) (std::is_same<SmoothingType, ValueSmoothingTypes::Linear>::value ? 0 : 1))
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor. */
|
||||
SmoothedValue (FloatType initialValue) noexcept
|
||||
{
|
||||
// Multiplicative smoothed values cannot ever reach 0!
|
||||
jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && initialValue == 0));
|
||||
|
||||
// Visual Studio can't handle base class initialisation with CRTP
|
||||
this->currentValue = initialValue;
|
||||
this->target = this->currentValue;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reset to a new sample rate and ramp length.
|
||||
@param sampleRate The sample rate
|
||||
@param rampLengthInSeconds The duration of the ramp in seconds
|
||||
*/
|
||||
void reset (double sampleRate, double rampLengthInSeconds) noexcept
|
||||
{
|
||||
jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
|
||||
reset ((int) std::floor (rampLengthInSeconds * sampleRate));
|
||||
}
|
||||
|
||||
/** Set a new ramp length directly in samples.
|
||||
@param numSteps The number of samples over which the ramp should be active
|
||||
*/
|
||||
void reset (int numSteps) noexcept
|
||||
{
|
||||
stepsToTarget = numSteps;
|
||||
this->setCurrentAndTargetValue (this->target);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Set the next value to ramp towards.
|
||||
@param newValue The new target value
|
||||
*/
|
||||
void setTargetValue (FloatType newValue) noexcept
|
||||
{
|
||||
if (newValue == this->target)
|
||||
return;
|
||||
|
||||
if (stepsToTarget <= 0)
|
||||
{
|
||||
this->setCurrentAndTargetValue (newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// Multiplicative smoothed values cannot ever reach 0!
|
||||
jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && newValue == 0));
|
||||
|
||||
this->target = newValue;
|
||||
this->countdown = stepsToTarget;
|
||||
|
||||
setStepSize();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Compute the next value.
|
||||
@returns Smoothed value
|
||||
*/
|
||||
FloatType getNextValue() noexcept
|
||||
{
|
||||
if (! this->isSmoothing())
|
||||
return this->target;
|
||||
|
||||
--(this->countdown);
|
||||
|
||||
if (this->isSmoothing())
|
||||
setNextValue();
|
||||
else
|
||||
this->currentValue = this->target;
|
||||
|
||||
return this->currentValue;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Skip the next numSamples samples.
|
||||
This is identical to calling getNextValue numSamples times. It returns
|
||||
the new current value.
|
||||
@see getNextValue
|
||||
*/
|
||||
FloatType skip (int numSamples) noexcept
|
||||
{
|
||||
if (numSamples >= this->countdown)
|
||||
{
|
||||
this->setCurrentAndTargetValue (this->target);
|
||||
return this->target;
|
||||
}
|
||||
|
||||
skipCurrentValue (numSamples);
|
||||
|
||||
this->countdown -= numSamples;
|
||||
return this->currentValue;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** THIS FUNCTION IS DEPRECATED.
|
||||
|
||||
Use `setTargetValue (float)` and `setCurrentAndTargetValue()` instead:
|
||||
|
||||
lsv.setValue (x, false); -> lsv.setTargetValue (x);
|
||||
lsv.setValue (x, true); -> lsv.setCurrentAndTargetValue (x);
|
||||
|
||||
@param newValue The new target value
|
||||
@param force If true, the value will be set immediately, bypassing the ramp
|
||||
*/
|
||||
JUCE_DEPRECATED_WITH_BODY (void setValue (FloatType newValue, bool force = false) noexcept,
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
this->setCurrentAndTargetValue (newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
setTargetValue (newValue);
|
||||
})
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename T>
|
||||
using LinearVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Linear>::value, void>::type;
|
||||
|
||||
template <typename T>
|
||||
using MultiplicativeVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Multiplicative>::value, void>::type;
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = SmoothingType>
|
||||
LinearVoid<T> setStepSize() noexcept
|
||||
{
|
||||
step = (this->target - this->currentValue) / (FloatType) this->countdown;
|
||||
}
|
||||
|
||||
template <typename T = SmoothingType>
|
||||
MultiplicativeVoid<T> setStepSize()
|
||||
{
|
||||
step = std::exp ((std::log (std::abs (this->target)) - std::log (std::abs (this->currentValue))) / this->countdown);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = SmoothingType>
|
||||
LinearVoid<T> setNextValue() noexcept
|
||||
{
|
||||
this->currentValue += step;
|
||||
}
|
||||
|
||||
template <typename T = SmoothingType>
|
||||
MultiplicativeVoid<T> setNextValue() noexcept
|
||||
{
|
||||
this->currentValue *= step;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = SmoothingType>
|
||||
LinearVoid<T> skipCurrentValue (int numSamples) noexcept
|
||||
{
|
||||
this->currentValue += step * (FloatType) numSamples;
|
||||
}
|
||||
|
||||
template <typename T = SmoothingType>
|
||||
MultiplicativeVoid<T> skipCurrentValue (int numSamples)
|
||||
{
|
||||
this->currentValue *= (FloatType) std::pow (step, numSamples);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
FloatType step = FloatType();
|
||||
int stepsToTarget = 0;
|
||||
};
|
||||
|
||||
template <typename FloatType>
|
||||
using LinearSmoothedValue = SmoothedValue <FloatType, ValueSmoothingTypes::Linear>;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
template <class SmoothedValueType>
|
||||
class CommonSmoothedValueTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
CommonSmoothedValueTests()
|
||||
: UnitTest ("CommonSmoothedValueTests", "SmoothedValues")
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Initial state");
|
||||
{
|
||||
SmoothedValueType sv;
|
||||
|
||||
auto value = sv.getCurrentValue();
|
||||
expectEquals (sv.getTargetValue(), value);
|
||||
|
||||
sv.getNextValue();
|
||||
expectEquals (sv.getCurrentValue(), value);
|
||||
expect (! sv.isSmoothing());
|
||||
}
|
||||
|
||||
beginTest ("Resetting");
|
||||
{
|
||||
auto initialValue = 15.0f;
|
||||
|
||||
SmoothedValueType sv (initialValue);
|
||||
sv.reset (3);
|
||||
expectEquals (sv.getCurrentValue(), initialValue);
|
||||
|
||||
auto targetValue = initialValue + 1.0f;
|
||||
sv.setTargetValue (targetValue);
|
||||
expectEquals (sv.getTargetValue(), targetValue);
|
||||
expectEquals (sv.getCurrentValue(), initialValue);
|
||||
expect (sv.isSmoothing());
|
||||
|
||||
auto currentValue = sv.getNextValue();
|
||||
expect (currentValue > initialValue);
|
||||
expectEquals (sv.getCurrentValue(), currentValue);
|
||||
expectEquals (sv.getTargetValue(), targetValue);
|
||||
expect (sv.isSmoothing());
|
||||
|
||||
sv.reset (5);
|
||||
|
||||
expectEquals (sv.getCurrentValue(), targetValue);
|
||||
expectEquals (sv.getTargetValue(), targetValue);
|
||||
expect (! sv.isSmoothing());
|
||||
|
||||
sv.getNextValue();
|
||||
expectEquals (sv.getCurrentValue(), targetValue);
|
||||
|
||||
sv.setTargetValue (1.5f);
|
||||
sv.getNextValue();
|
||||
|
||||
float newStart = 0.2f;
|
||||
sv.setCurrentAndTargetValue (newStart);
|
||||
expectEquals (sv.getNextValue(), newStart);
|
||||
expectEquals (sv.getTargetValue(), newStart);
|
||||
expectEquals (sv.getCurrentValue(), newStart);
|
||||
expect (! sv.isSmoothing());
|
||||
}
|
||||
|
||||
beginTest ("Sample rate");
|
||||
{
|
||||
SmoothedValueType svSamples { 3.0f };
|
||||
auto svTime = svSamples;
|
||||
|
||||
auto numSamples = 12;
|
||||
|
||||
svSamples.reset (numSamples);
|
||||
svTime.reset (numSamples * 2, 1.0);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
svTime.skip (1);
|
||||
expectWithinAbsoluteError (svSamples.getNextValue(),
|
||||
svTime.getNextValue(),
|
||||
1.0e-7f);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Block processing");
|
||||
{
|
||||
SmoothedValueType sv (1.0f);
|
||||
|
||||
sv.reset (12);
|
||||
sv.setTargetValue (2.0f);
|
||||
|
||||
const auto numSamples = 15;
|
||||
|
||||
AudioBuffer<float> referenceData (1, numSamples);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
referenceData.setSample (0, i, sv.getNextValue());
|
||||
|
||||
expect (referenceData.getSample (0, 0) > 0);
|
||||
expect (referenceData.getSample (0, 10) < sv.getTargetValue());
|
||||
expectWithinAbsoluteError (referenceData.getSample (0, 11),
|
||||
sv.getTargetValue(),
|
||||
1.0e-7f);
|
||||
|
||||
auto getUnitData = [] (int numSamplesToGenerate)
|
||||
{
|
||||
AudioBuffer<float> result (1, numSamplesToGenerate);
|
||||
|
||||
for (int i = 0; i < numSamplesToGenerate; ++i)
|
||||
result.setSample (0, i, 1.0f);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
auto compareData = [this](const AudioBuffer<float>& test,
|
||||
const AudioBuffer<float>& reference)
|
||||
{
|
||||
for (int i = 0; i < test.getNumSamples(); ++i)
|
||||
expectWithinAbsoluteError (test.getSample (0, i),
|
||||
reference.getSample (0, i),
|
||||
1.0e-7f);
|
||||
};
|
||||
|
||||
auto testData = getUnitData (numSamples);
|
||||
sv.setCurrentAndTargetValue (1.0f);
|
||||
sv.setTargetValue (2.0f);
|
||||
sv.applyGain (testData.getWritePointer (0), numSamples);
|
||||
compareData (testData, referenceData);
|
||||
|
||||
testData = getUnitData (numSamples);
|
||||
AudioBuffer<float> destData (1, numSamples);
|
||||
sv.setCurrentAndTargetValue (1.0f);
|
||||
sv.setTargetValue (2.0f);
|
||||
sv.applyGain (destData.getWritePointer (0),
|
||||
testData.getReadPointer (0),
|
||||
numSamples);
|
||||
compareData (destData, referenceData);
|
||||
compareData (testData, getUnitData (numSamples));
|
||||
|
||||
testData = getUnitData (numSamples);
|
||||
sv.setCurrentAndTargetValue (1.0f);
|
||||
sv.setTargetValue (2.0f);
|
||||
sv.applyGain (testData, numSamples);
|
||||
compareData (testData, referenceData);
|
||||
}
|
||||
|
||||
beginTest ("Skip");
|
||||
{
|
||||
SmoothedValueType sv;
|
||||
|
||||
sv.reset (12);
|
||||
sv.setCurrentAndTargetValue (1.0f);
|
||||
sv.setTargetValue (2.0f);
|
||||
|
||||
Array<float> reference;
|
||||
|
||||
for (int i = 0; i < 15; ++i)
|
||||
reference.add (sv.getNextValue());
|
||||
|
||||
sv.setCurrentAndTargetValue (1.0f);
|
||||
sv.setTargetValue (2.0f);
|
||||
|
||||
expectWithinAbsoluteError (sv.skip (1), reference[0], 1.0e-6f);
|
||||
expectWithinAbsoluteError (sv.skip (1), reference[1], 1.0e-6f);
|
||||
expectWithinAbsoluteError (sv.skip (2), reference[3], 1.0e-6f);
|
||||
sv.skip (3);
|
||||
expectWithinAbsoluteError (sv.getCurrentValue(), reference[6], 1.0e-6f);
|
||||
expectEquals (sv.skip (300), sv.getTargetValue());
|
||||
expectEquals (sv.getCurrentValue(), sv.getTargetValue());
|
||||
}
|
||||
|
||||
beginTest ("Negative");
|
||||
{
|
||||
SmoothedValueType sv;
|
||||
|
||||
auto numValues = 12;
|
||||
sv.reset (numValues);
|
||||
|
||||
std::vector<std::pair<float, float>> ranges = { { -1.0f, -2.0f },
|
||||
{ -100.0f, -3.0f } };
|
||||
|
||||
for (auto range : ranges)
|
||||
{
|
||||
auto start = range.first, end = range.second;
|
||||
|
||||
sv.setCurrentAndTargetValue (start);
|
||||
sv.setTargetValue (end);
|
||||
|
||||
auto val = sv.skip (numValues / 2);
|
||||
|
||||
if (end > start)
|
||||
expect (val > start && val < end);
|
||||
else
|
||||
expect (val < start && val > end);
|
||||
|
||||
auto nextVal = sv.getNextValue();
|
||||
expect (end > start ? (nextVal > val) : (nextVal < val));
|
||||
|
||||
auto endVal = sv.skip (500);
|
||||
expectEquals (endVal, end);
|
||||
expectEquals (sv.getNextValue(), end);
|
||||
expectEquals (sv.getCurrentValue(), end);
|
||||
|
||||
sv.setCurrentAndTargetValue (start);
|
||||
sv.setTargetValue (end);
|
||||
|
||||
SmoothedValueType positiveSv { -start };
|
||||
positiveSv.reset (numValues);
|
||||
positiveSv.setTargetValue (-end);
|
||||
|
||||
for (int i = 0; i < numValues + 2; ++i)
|
||||
expectEquals (sv.getNextValue(), -positiveSv.getNextValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user