- Library code update

- project update (added Eigen)

git-svn-id: http://moon:8086/svn/software/trunk/projects/mpsk_rx_gui@94 b431acfa-c32f-4a4a-93f1-934dc6c82436
This commit is contained in:
2014-12-20 20:59:48 +00:00
parent 1b4d8e9f54
commit dc5560392f
312 changed files with 6349 additions and 4195 deletions
+2
View File
@@ -35,11 +35,13 @@
using namespace juce;
#endif
#if ! JUCE_DONT_DECLARE_PROJECTINFO
namespace ProjectInfo
{
const char* const projectName = "M-QAM Transceiver";
const char* const versionString = "1.0.0";
const int versionNumber = 0x10000;
}
#endif
#endif // __APPHEADERFILE_GMZMR9__
@@ -25,7 +25,7 @@
void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fff;
char* intData = static_cast <char*> (dest);
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
@@ -50,7 +50,7 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fff;
char* intData = static_cast <char*> (dest);
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
@@ -75,7 +75,7 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fffff;
char* intData = static_cast <char*> (dest);
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
@@ -100,7 +100,7 @@ void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fffff;
char* intData = static_cast <char*> (dest);
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
@@ -125,7 +125,7 @@ void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fffffff;
char* intData = static_cast <char*> (dest);
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
@@ -150,7 +150,7 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fffffff;
char* intData = static_cast <char*> (dest);
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
@@ -176,7 +176,7 @@ void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* de
{
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
char* d = static_cast <char*> (dest);
char* d = static_cast<char*> (dest);
for (int i = 0; i < numSamples; ++i)
{
@@ -194,7 +194,7 @@ void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* de
{
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
char* d = static_cast <char*> (dest);
char* d = static_cast<char*> (dest);
for (int i = 0; i < numSamples; ++i)
{
@@ -212,7 +212,7 @@ void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* de
void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fff;
const char* intData = static_cast <const char*> (source);
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
@@ -237,7 +237,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* const source, float
void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fff;
const char* intData = static_cast <const char*> (source);
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
@@ -262,7 +262,7 @@ void AudioDataConverters::convertInt16BEToFloat (const void* const source, float
void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fffff;
const char* intData = static_cast <const char*> (source);
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
@@ -287,7 +287,7 @@ void AudioDataConverters::convertInt24LEToFloat (const void* const source, float
void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fffff;
const char* intData = static_cast <const char*> (source);
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
@@ -312,7 +312,7 @@ void AudioDataConverters::convertInt24BEToFloat (const void* const source, float
void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fffffff;
const char* intData = static_cast <const char*> (source);
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
@@ -337,7 +337,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* const source, float
void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fffffff;
const char* intData = static_cast <const char*> (source);
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
@@ -361,7 +361,7 @@ void AudioDataConverters::convertInt32BEToFloat (const void* const source, float
void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const char* s = static_cast <const char*> (source);
const char* s = static_cast<const char*> (source);
for (int i = 0; i < numSamples; ++i)
{
@@ -378,7 +378,7 @@ void AudioDataConverters::convertFloat32LEToFloat (const void* const source, flo
void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const char* s = static_cast <const char*> (source);
const char* s = static_cast<const char*> (source);
for (int i = 0; i < numSamples; ++i)
{
@@ -91,7 +91,7 @@ AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
allocatedBytes (0)
{
jassert (dataToReferTo != nullptr);
jassert (numChans >= 0);
jassert (numChans >= 0 && numSamples >= 0);
allocateChannels (dataToReferTo, 0);
}
@@ -105,7 +105,7 @@ AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
isClear (false)
{
jassert (dataToReferTo != nullptr);
jassert (numChans >= 0);
jassert (numChans >= 0 && startSample >= 0 && numSamples >= 0);
allocateChannels (dataToReferTo, startSample);
}
@@ -114,7 +114,7 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo,
const int newNumSamples) noexcept
{
jassert (dataToReferTo != nullptr);
jassert (newNumChannels >= 0);
jassert (newNumChannels >= 0 && newNumSamples >= 0);
allocatedBytes = 0;
allocatedData.free();
@@ -128,6 +128,8 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo,
void AudioSampleBuffer::allocateChannels (float* const* const dataToReferTo, int offset)
{
jassert (offset >= 0);
// (try to avoid doing a malloc here, as that'll blow up things like Pro-Tools)
if (numChannels < (int) numElementsInArray (preallocatedChannelSpace))
{
@@ -163,6 +165,8 @@ AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other)
}
else
{
isClear = false;
for (int i = 0; i < numChannels; ++i)
FloatVectorOperations::copy (channels[i], other.channels[i], size);
}
@@ -24,8 +24,9 @@
namespace FloatVectorHelpers
{
#define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest));
#define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest));
#define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest));
#define JUCE_INCREMENT_SRC1_SRC2_DEST dest += (16 / sizeof (*dest)); src1 += (16 / sizeof (*dest)); src2 += (16 / sizeof (*dest));
#define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest));
#if JUCE_USE_SSE_INTRINSICS
static bool sse2Present = false;
@@ -122,6 +123,17 @@ namespace FloatVectorHelpers
} \
JUCE_FINISH_VEC_OP (normalOp)
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \
JUCE_BEGIN_VEC_OP \
setupOp \
{ \
Mode::ParallelType (&loadSrc1) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src1) ? Mode::loadA : Mode::loadU; \
Mode::ParallelType (&loadSrc2) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src2) ? Mode::loadA : Mode::loadU; \
void (&storeDst) (Mode::Type* dest, Mode::ParallelType a) = FloatVectorHelpers::isAligned (dest) ? Mode::storeA : Mode::storeU; \
JUCE_VEC_LOOP_TWO_SOURCES (vecOp, loadSrc1, loadSrc2, storeDst, locals, increment); \
} \
JUCE_FINISH_VEC_OP (normalOp)
//==============================================================================
#elif JUCE_USE_ARM_NEON
@@ -193,6 +205,12 @@ namespace FloatVectorHelpers
JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \
JUCE_FINISH_VEC_OP (normalOp)
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \
JUCE_BEGIN_VEC_OP \
setupOp \
JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \
JUCE_FINISH_VEC_OP (normalOp)
//==============================================================================
#else
#define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \
@@ -201,6 +219,8 @@ namespace FloatVectorHelpers
#define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \
for (int i = 0; i < num; ++i) normalOp;
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \
for (int i = 0; i < num; ++i) normalOp;
#endif
//==============================================================================
@@ -212,10 +232,19 @@ namespace FloatVectorHelpers
increment; \
}
#define JUCE_VEC_LOOP_TWO_SOURCES(vecOp, src1Load, src2Load, dstStore, locals, increment) \
for (int i = 0; i < numLongOps; ++i) \
{ \
locals (src1Load, src2Load); \
dstStore (dest, vecOp); \
increment; \
}
#define JUCE_LOAD_NONE(srcLoad, dstLoad)
#define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest);
#define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src);
#define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src);
#define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest);
#define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src);
#define JUCE_LOAD_SRC1_SRC2(src1Load, src2Load) const Mode::ParallelType s1 = src1Load (src1), s2 = src2Load (src2);
#define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src);
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
template<int typeSize> struct ModeType { typedef BasicOps32 Mode; };
@@ -229,7 +258,7 @@ namespace FloatVectorHelpers
static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept
{
const int numLongOps = num / Mode::numParallel;
int numLongOps = num / Mode::numParallel;
#if JUCE_USE_SSE_INTRINSICS
if (numLongOps > 1 && isSSE2Available())
@@ -246,7 +275,7 @@ namespace FloatVectorHelpers
if (isMinimum)
{
for (int i = 1; i < numLongOps; ++i)
while (--numLongOps > 0)
{
src += Mode::numParallel;
val = Mode::min (val, Mode::loadA (src));
@@ -254,7 +283,7 @@ namespace FloatVectorHelpers
}
else
{
for (int i = 1; i < numLongOps; ++i)
while (--numLongOps > 0)
{
src += Mode::numParallel;
val = Mode::max (val, Mode::loadA (src));
@@ -268,7 +297,7 @@ namespace FloatVectorHelpers
if (isMinimum)
{
for (int i = 1; i < numLongOps; ++i)
while (--numLongOps > 0)
{
src += Mode::numParallel;
val = Mode::min (val, Mode::loadU (src));
@@ -276,7 +305,7 @@ namespace FloatVectorHelpers
}
else
{
for (int i = 1; i < numLongOps; ++i)
while (--numLongOps > 0)
{
src += Mode::numParallel;
val = Mode::max (val, Mode::loadU (src));
@@ -288,6 +317,7 @@ namespace FloatVectorHelpers
: Mode::max (val);
num &= (Mode::numParallel - 1);
src += Mode::numParallel;
for (int i = 0; i < num; ++i)
result = isMinimum ? jmin (result, src[i])
@@ -302,7 +332,7 @@ namespace FloatVectorHelpers
static Range<Type> findMinAndMax (const Type* src, int num) noexcept
{
const int numLongOps = num / Mode::numParallel;
int numLongOps = num / Mode::numParallel;
#if JUCE_USE_SSE_INTRINSICS
if (numLongOps > 1 && isSSE2Available())
@@ -318,7 +348,7 @@ namespace FloatVectorHelpers
mn = Mode::loadA (src);
mx = mn;
for (int i = 1; i < numLongOps; ++i)
while (--numLongOps > 0)
{
src += Mode::numParallel;
const ParallelType v = Mode::loadA (src);
@@ -332,7 +362,7 @@ namespace FloatVectorHelpers
mn = Mode::loadU (src);
mx = mn;
for (int i = 1; i < numLongOps; ++i)
while (--numLongOps > 0)
{
src += Mode::numParallel;
const ParallelType v = Mode::loadU (src);
@@ -344,7 +374,9 @@ namespace FloatVectorHelpers
Range<Type> result (Mode::min (mn),
Mode::max (mx));
num &= 3;
num &= (Mode::numParallel - 1);
src += Mode::numParallel;
for (int i = 0; i < num; ++i)
result = result.getUnionWith (src[i]);
@@ -409,7 +441,7 @@ void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src,
void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsmul (src, 1, &multiplier, dest, 1, num);
vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
@@ -420,7 +452,7 @@ void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const f
void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsmulD (src, 1, &multiplier, dest, 1, num);
vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
@@ -440,10 +472,32 @@ void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int
const Mode::ParallelType amountToAdd = Mode::load1 (amount);)
}
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float* src, float amount, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsadd (src, 1, &amount, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType am = Mode::load1 (amount);)
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double* src, double amount, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsaddD (src, 1, &amount, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType am = Mode::load1 (amount);)
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vadd (src, 1, dest, 1, dest, 1, num);
vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif
@@ -452,16 +506,34 @@ void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, in
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vaddD (src, 1, dest, 1, dest, 1, num);
vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src1, const float* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vadd (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src1, const double* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vaddD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsub (src, 1, dest, 1, dest, 1, num);
vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif
@@ -470,12 +542,30 @@ void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* sr
void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsubD (src, 1, dest, 1, dest, 1, num);
vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src1, const float* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsub (src2, 1, src1, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src1, const double* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsubD (src2, 1, src1, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept
{
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)),
@@ -493,7 +583,7 @@ void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const d
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vmul (src, 1, dest, 1, dest, 1, num);
vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif
@@ -502,16 +592,34 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* sr
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vmulD (src, 1, dest, 1, dest, 1, num);
vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src1, const float* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vmul (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src1, const double* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vmulD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsmul (dest, 1, &multiplier, dest, 1, num);
vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST,
const Mode::ParallelType mult = Mode::load1 (multiplier);)
@@ -521,13 +629,27 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplie
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsmulD (dest, 1, &multiplier, dest, 1, num);
vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST,
const Mode::ParallelType mult = Mode::load1 (multiplier);)
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, float multiplier, int num) noexcept
{
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType mult = Mode::load1 (multiplier);)
}
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, double multiplier, int num) noexcept
{
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType mult = Mode::load1 (multiplier);)
}
void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
@@ -640,8 +762,8 @@ public:
const int range = random.nextBool() ? 500 : 10;
const int num = random.nextInt (range) + 1;
HeapBlock<ValueType> buffer1 (num + 16), buffer2 (num + 16);
HeapBlock<int> buffer3 (num + 16);
HeapBlock<ValueType> buffer1 ((size_t) num + 16), buffer2 ((size_t) num + 16);
HeapBlock<int> buffer3 ((size_t) num + 16);
#if JUCE_ARM
ValueType* const data1 = buffer1;
@@ -65,18 +65,36 @@ public:
/** Adds a fixed value to the destination values. */
static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept;
/** Adds a fixed value to each source value and stores it in the destination array. */
static void JUCE_CALLTYPE add (float* dest, float* src, float amount, int numValues) noexcept;
/** Adds a fixed value to each source value and stores it in the destination array. */
static void JUCE_CALLTYPE add (double* dest, double* src, double amount, int numValues) noexcept;
/** Adds the source values to the destination values. */
static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept;
/** Adds the source values to the destination values. */
static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept;
/** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */
static void JUCE_CALLTYPE add (float* dest, const float* src1, const float* src2, int num) noexcept;
/** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */
static void JUCE_CALLTYPE add (double* dest, const double* src1, const double* src2, int num) noexcept;
/** Subtracts the source values from the destination values. */
static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept;
/** Subtracts the source values from the destination values. */
static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept;
/** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */
static void JUCE_CALLTYPE subtract (float* dest, const float* src1, const float* src2, int num) noexcept;
/** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */
static void JUCE_CALLTYPE subtract (double* dest, const double* src1, const double* src2, int num) noexcept;
/** Multiplies each source value by the given multiplier, then adds it to the destination value. */
static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept;
@@ -89,12 +107,24 @@ public:
/** Multiplies the destination values by the source values. */
static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept;
/** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */
static void JUCE_CALLTYPE multiply (float* dest, const float* src1, const float* src2, int numValues) noexcept;
/** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */
static void JUCE_CALLTYPE multiply (double* dest, const double* src1, const double* src2, int numValues) noexcept;
/** Multiplies each of the destination values by a fixed multiplier. */
static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept;
/** Multiplies each of the destination values by a fixed multiplier. */
static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept;
/** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */
static void JUCE_CALLTYPE multiply (float* dest, const float* src, float multiplier, int num) noexcept;
/** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */
static void JUCE_CALLTYPE multiply (double* dest, const double* src, double multiplier, int num) noexcept;
/** Copies a source vector to a destination, negating each value. */
static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept;
@@ -181,7 +181,7 @@ public:
for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
output = allPass[0][j].process (output);
samples[i] = output * wet1 + input * dry;
samples[i] = output * wet1 + samples[i] * dry;
}
}
@@ -57,7 +57,9 @@
#endif
#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers)
#include <Accelerate/Accelerate.h>
#undef Point
#else
#undef JUCE_USE_VDSP_FRAMEWORK
#endif
@@ -1,7 +1,7 @@
{
"id": "juce_audio_basics",
"name": "JUCE audio and midi data classes",
"version": "3.0.5",
"version": "3.0.8",
"description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
@@ -210,7 +210,7 @@ bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes,
const int itemSize = MidiBufferHelpers::getEventDataSize (data);
numBytes = itemSize;
midiData = data + sizeof (int32) + sizeof (uint16);
data += sizeof (int32) + sizeof (uint16) + itemSize;
data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
return true;
}
@@ -223,7 +223,7 @@ bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePositio
samplePosition = MidiBufferHelpers::getEventTime (data);
const int itemSize = MidiBufferHelpers::getEventDataSize (data);
result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition);
data += sizeof (int32) + sizeof (uint16) + itemSize;
data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
return true;
}
@@ -186,9 +186,10 @@ public:
/** Retrieves a copy of the next event from the buffer.
@param result on return, this will be the message (the MidiMessage's timestamp
is not set)
@param samplePosition on return, this will be the position of the event
@param result on return, this will be the message. The MidiMessage's timestamp
is set to the same value as samplePosition.
@param samplePosition on return, this will be the position of the event, as a
sample index in the buffer
@returns true if an event was found, or false if the iterator has reached
the end of the buffer
*/
@@ -203,7 +204,8 @@ public:
temporarily until the MidiBuffer is altered.
@param numBytesOfMidiData on return, this is the number of bytes of data used by the
midi message
@param samplePosition on return, this will be the position of the event
@param samplePosition on return, this will be the position of the event, as a
sample index in the buffer
@returns true if an event was found, or false if the iterator has reached
the end of the buffer
*/
@@ -129,7 +129,7 @@ MidiMessage::MidiMessage (const MidiMessage& other)
{
if (other.allocatedData != nullptr)
{
allocatedData.malloc (size);
allocatedData.malloc ((size_t) size);
memcpy (allocatedData, other.allocatedData, (size_t) size);
}
else
@@ -143,7 +143,7 @@ MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp)
{
if (other.allocatedData != nullptr)
{
allocatedData.malloc (size);
allocatedData.malloc ((size_t) size);
memcpy (allocatedData, other.allocatedData, (size_t) size);
}
else
@@ -255,7 +255,7 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other)
if (other.allocatedData != nullptr)
{
allocatedData.malloc (size);
allocatedData.malloc ((size_t) size);
memcpy (allocatedData, other.allocatedData, (size_t) size);
}
else
@@ -297,7 +297,7 @@ uint8* MidiMessage::allocateSpace (int bytes)
{
if (bytes > 4)
{
allocatedData.malloc (bytes);
allocatedData.malloc ((size_t) bytes);
return allocatedData;
}
@@ -661,7 +661,7 @@ String MidiMessage::getTextFromTextMetaEvent() const
MidiMessage MidiMessage::textMetaEvent (int type, StringRef text)
{
jassert (type > 0 && type < 16)
jassert (type > 0 && type < 16);
MidiMessage result;
@@ -29,6 +29,7 @@ MidiMessageSequence::MidiMessageSequence()
MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other)
{
list.addCopiesOf (other.list);
updateMatchedPairs();
}
MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other)
@@ -52,17 +53,17 @@ void MidiMessageSequence::clear()
list.clear();
}
int MidiMessageSequence::getNumEvents() const
int MidiMessageSequence::getNumEvents() const noexcept
{
return list.size();
}
MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const
MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const noexcept
{
return list [index];
}
double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const
double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const noexcept
{
if (const MidiEventHolder* const meh = list [index])
if (meh->noteOffObject != nullptr)
@@ -71,7 +72,7 @@ double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const
return 0.0;
}
int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const
int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const noexcept
{
if (const MidiEventHolder* const meh = list [index])
return list.indexOf (meh->noteOffObject);
@@ -79,12 +80,12 @@ int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const
return -1;
}
int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const
int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const noexcept
{
return list.indexOf (event);
}
int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const
int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const noexcept
{
const int numEvents = list.size();
@@ -97,17 +98,17 @@ int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const
}
//==============================================================================
double MidiMessageSequence::getStartTime() const
double MidiMessageSequence::getStartTime() const noexcept
{
return getEventTime (0);
}
double MidiMessageSequence::getEndTime() const
double MidiMessageSequence::getEndTime() const noexcept
{
return getEventTime (list.size() - 1);
}
double MidiMessageSequence::getEventTime (const int index) const
double MidiMessageSequence::getEventTime (const int index) const noexcept
{
if (const MidiEventHolder* const meh = list [index])
return meh->message.getTimeStamp();
@@ -181,13 +182,13 @@ void MidiMessageSequence::addSequence (const MidiMessageSequence& other,
}
//==============================================================================
void MidiMessageSequence::sort()
void MidiMessageSequence::sort() noexcept
{
MidiMessageSequenceSorter sorter;
list.sort (sorter, true);
}
void MidiMessageSequence::updateMatchedPairs()
void MidiMessageSequence::updateMatchedPairs() noexcept
{
for (int i = 0; i < list.size(); ++i)
{
@@ -226,7 +227,7 @@ void MidiMessageSequence::updateMatchedPairs()
}
}
void MidiMessageSequence::addTimeToMessages (const double delta)
void MidiMessageSequence::addTimeToMessages (const double delta) noexcept
{
for (int i = list.size(); --i >= 0;)
{
@@ -91,47 +91,47 @@ public:
void clear();
/** Returns the number of events in the sequence. */
int getNumEvents() const;
int getNumEvents() const noexcept;
/** Returns a pointer to one of the events. */
MidiEventHolder* getEventPointer (int index) const;
MidiEventHolder* getEventPointer (int index) const noexcept;
/** Returns the time of the note-up that matches the note-on at this index.
If the event at this index isn't a note-on, it'll just return 0.
@see MidiMessageSequence::MidiEventHolder::noteOffObject
*/
double getTimeOfMatchingKeyUp (int index) const;
double getTimeOfMatchingKeyUp (int index) const noexcept;
/** Returns the index of the note-up that matches the note-on at this index.
If the event at this index isn't a note-on, it'll just return -1.
@see MidiMessageSequence::MidiEventHolder::noteOffObject
*/
int getIndexOfMatchingKeyUp (int index) const;
int getIndexOfMatchingKeyUp (int index) const noexcept;
/** Returns the index of an event. */
int getIndexOf (MidiEventHolder* event) const;
int getIndexOf (MidiEventHolder* event) const noexcept;
/** Returns the index of the first event on or after the given timestamp.
If the time is beyond the end of the sequence, this will return the
number of events.
*/
int getNextIndexAtTime (double timeStamp) const;
int getNextIndexAtTime (double timeStamp) const noexcept;
//==============================================================================
/** Returns the timestamp of the first event in the sequence.
@see getEndTime
*/
double getStartTime() const;
double getStartTime() const noexcept;
/** Returns the timestamp of the last event in the sequence.
@see getStartTime
*/
double getEndTime() const;
double getEndTime() const noexcept;
/** Returns the timestamp of the event at a given index.
If the index is out-of-range, this will return 0.0
*/
double getEventTime (int index) const;
double getEventTime (int index) const noexcept;
//==============================================================================
/** Inserts a midi message into the sequence.
@@ -185,13 +185,13 @@ public:
will scan the list and make sure all the note-offs in the MidiEventHolder
structures are pointing at the correct ones.
*/
void updateMatchedPairs();
void updateMatchedPairs() noexcept;
/** Forces a sort of the sequence.
You may need to call this if you've manually modified the timestamps of some
events such that the overall order now needs updating.
*/
void sort();
void sort() noexcept;
//==============================================================================
/** Copies all the messages for a particular midi channel to another sequence.
@@ -224,7 +224,7 @@ public:
/** Adds an offset to the timestamps of all events in the sequence.
@param deltaTime the amount to add to each timestamp.
*/
void addTimeToMessages (double deltaTime);
void addTimeToMessages (double deltaTime) noexcept;
//==============================================================================
/** Scans through the sequence to determine the state of any midi controllers at
@@ -47,23 +47,28 @@ void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputS
ratio = jmax (0.0, samplesInPerOutputSample);
}
void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected,
double sampleRate)
void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
{
const SpinLock::ScopedLockType sl (ratioLock);
input->prepareToPlay (samplesPerBlockExpected, sampleRate);
buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32);
buffer.clear();
sampsInBuffer = 0;
bufferPos = 0;
subSampleOffset = 0.0;
filterStates.calloc ((size_t) numChannels);
srcBuffers.calloc ((size_t) numChannels);
destBuffers.calloc ((size_t) numChannels);
createLowPass (ratio);
flushBuffers();
}
void ResamplingAudioSource::flushBuffers()
{
buffer.clear();
bufferPos = 0;
sampsInBuffer = 0;
subSampleOffset = 0.0;
resetFilters();
}
@@ -225,7 +230,8 @@ void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double
void ResamplingAudioSource::resetFilters()
{
filterStates.clear ((size_t) numChannels);
if (filterStates != nullptr)
filterStates.clear ((size_t) numChannels);
}
void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs)
@@ -66,6 +66,9 @@ public:
*/
double getResamplingRatio() const noexcept { return ratio; }
/** Clears any buffers and filters that the resampler is using. */
void flushBuffers();
//==============================================================================
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
void releaseResources() override;
@@ -163,10 +163,7 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu
: numSamples;
if (numThisTime > 0)
{
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->renderNextBlock (outputBuffer, startSample, numThisTime);
}
renderVoices (outputBuffer, startSample, numThisTime);
if (useEvent)
handleMidiEvent (m);
@@ -176,6 +173,12 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu
}
}
void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples)
{
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);
}
void Synthesiser::handleMidiEvent (const MidiMessage& m)
{
if (m.isNoteOn())
@@ -184,7 +187,7 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m)
}
else if (m.isNoteOff())
{
noteOff (m.getChannel(), m.getNoteNumber(), true);
noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), true);
}
else if (m.isAllNotesOff() || m.isAllSoundOff())
{
@@ -230,10 +233,10 @@ void Synthesiser::noteOn (const int midiChannel,
if (voice->getCurrentlyPlayingNote() == midiNoteNumber
&& voice->isPlayingChannel (midiChannel))
stopVoice (voice, true);
stopVoice (voice, 1.0f, true);
}
startVoice (findFreeVoice (sound, shouldStealNotes),
startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
sound, midiChannel, midiNoteNumber, velocity);
}
}
@@ -248,7 +251,7 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice,
if (voice != nullptr && sound != nullptr)
{
if (voice->currentlyPlayingSound != nullptr)
voice->stopNote (false);
voice->stopNote (0.0f, false);
voice->startNote (midiNoteNumber, velocity, sound,
lastPitchWheelValues [midiChannel - 1]);
@@ -261,11 +264,11 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice,
}
}
void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff)
void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff)
{
jassert (voice != nullptr);
voice->stopNote (allowTailOff);
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));
@@ -273,6 +276,7 @@ void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff)
void Synthesiser::noteOff (const int midiChannel,
const int midiNoteNumber,
const float velocity,
const bool allowTailOff)
{
const ScopedLock sl (lock);
@@ -291,7 +295,7 @@ void Synthesiser::noteOff (const int midiChannel,
voice->keyIsDown = false;
if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
stopVoice (voice, allowTailOff);
stopVoice (voice, velocity, allowTailOff);
}
}
}
@@ -307,7 +311,7 @@ void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
voice->stopNote (allowTailOff);
voice->stopNote (1.0f, allowTailOff);
}
sustainPedalsDown.clear();
@@ -379,7 +383,7 @@ void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown)
stopVoice (voice, true);
stopVoice (voice, 1.0f, true);
}
sustainPedalsDown.clearBit (midiChannel);
@@ -400,7 +404,7 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
if (isDown)
voice->sostenutoPedalDown = true;
else if (voice->sostenutoPedalDown)
stopVoice (voice, true);
stopVoice (voice, 1.0f, true);
}
}
}
@@ -412,7 +416,9 @@ void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
}
//==============================================================================
SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, const bool stealIfNoneAvailable) const
SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
int midiChannel, int midiNoteNumber,
const bool stealIfNoneAvailable) const
{
const ScopedLock sl (lock);
@@ -425,25 +431,68 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, con
}
if (stealIfNoneAvailable)
return findVoiceToSteal (soundToPlay);
return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
return nullptr;
}
SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay) const
struct VoiceAgeSorter
{
// currently this just steals the one that's been playing the longest, but could be made a bit smarter..
SynthesiserVoice* oldest = nullptr;
static int compareElements (SynthesiserVoice* v1, SynthesiserVoice* v2) noexcept
{
return v1->wasStartedBefore (*v2) ? 1 : (v2->wasStartedBefore (*v1) ? -1 : 0);
}
};
SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
int /*midiChannel*/, int midiNoteNumber) const
{
SynthesiserVoice* bottom = nullptr;
SynthesiserVoice* top = nullptr;
// this is a list of voices we can steal, sorted by how long they've been running
Array<SynthesiserVoice*> usableVoices;
usableVoices.ensureStorageAllocated (voices.size());
for (int i = 0; i < voices.size(); ++i)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->canPlaySound (soundToPlay)
&& (oldest == nullptr || voice->wasStartedBefore (*oldest)))
oldest = voice;
if (voice->canPlaySound (soundToPlay))
{
VoiceAgeSorter sorter;
usableVoices.addSorted (sorter, voice);
const int note = voice->getCurrentlyPlayingNote();
if (bottom == nullptr || note < bottom->getCurrentlyPlayingNote())
bottom = voice;
if (top == nullptr || note > top->getCurrentlyPlayingNote())
top = voice;
}
}
jassert (oldest != nullptr);
return oldest;
jassert (bottom != nullptr && top != nullptr);
// The oldest note that's playing with the target pitch playing is ideal..
for (int i = 0; i < usableVoices.size(); ++i)
{
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
return voice;
}
// ..otherwise, look for the oldest note that isn't the top or bottom note..
for (int i = 0; i < usableVoices.size(); ++i)
{
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
if (voice != bottom && voice != top)
return voice;
}
// ..otherwise, there's only one or two voices to choose from - we'll return the top one..
return top;
}
@@ -55,14 +55,14 @@ public:
The Synthesiser will use this information when deciding which sounds to trigger
for a given note.
*/
virtual bool appliesToNote (const int midiNoteNumber) = 0;
virtual bool appliesToNote (int midiNoteNumber) = 0;
/** Returns true if the sound should be triggered by midi events on a given channel.
The Synthesiser will use this information when deciding which sounds to trigger
for a given note.
*/
virtual bool appliesToChannel (const int midiChannel) = 0;
virtual bool appliesToChannel (int midiChannel) = 0;
/** The class is reference-counted, so this is a handy pointer class for it. */
typedef ReferenceCountedObjectPtr<SynthesiserSound> Ptr;
@@ -127,6 +127,8 @@ public:
This will be called during the rendering callback, so must be fast and thread-safe.
The velocity indicates how quickly the note was released - 0 is slowly, 1 is quickly.
If allowTailOff is false or the voice doesn't want to tail-off, then it must stop all
sound immediately, and must call clearCurrentNote() to reset the state of this voice
and allow the synth to reassign it another sound.
@@ -136,7 +138,7 @@ public:
finishes playing (during the rendering callback), it must make sure that it calls
clearCurrentNote().
*/
virtual void stopNote (bool allowTailOff) = 0;
virtual void stopNote (float velocity, bool allowTailOff) = 0;
/** Called to let the voice know that the pitch wheel has been moved.
This will be called during the rendering callback, so must be fast and thread-safe.
@@ -173,13 +175,6 @@ public:
int startSample,
int numSamples) = 0;
/** Returns true if the voice is currently playing a sound which is mapped to the given
midi channel.
If it's not currently playing, this will return false.
*/
bool isPlayingChannel (int midiChannel) const;
/** Changes the voice's reference sample rate.
The rate is set so that subclasses know the output rate and can set their pitch
@@ -188,7 +183,19 @@ public:
This method is called by the synth, and subclasses can access the current rate with
the currentSampleRate member.
*/
void setCurrentPlaybackSampleRate (double newRate);
virtual void setCurrentPlaybackSampleRate (double newRate);
/** 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.
*/
double getSampleRate() const noexcept { return currentSampleRate; }
/** Returns true if the voice is currently playing a sound which is mapped to the given
midi channel.
If it's not currently playing, this will return false.
*/
bool isPlayingChannel (int midiChannel) const;
/** Returns true if the key that triggered this voice is still held down.
Note that the voice may still be playing after the key was released (e.g because the
@@ -203,13 +210,6 @@ public:
bool wasStartedBefore (const SynthesiserVoice& other) const noexcept;
protected:
//==============================================================================
/** Returns the current target sample rate at which rendering is being done.
This is available for subclasses so they can pitch things correctly.
*/
double getSampleRate() const { return currentSampleRate; }
/** Resets the state of this voice after a sound has finished playing.
The subclass must call this when it finishes playing a note and becomes available
@@ -235,6 +235,11 @@ private:
SynthesiserSound::Ptr currentlyPlayingSound;
bool keyIsDown, sostenutoPedalDown;
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Note the new parameters for this method.
virtual int stopNote (bool) { return 0; }
#endif
JUCE_LEAK_DETECTOR (SynthesiserVoice)
};
@@ -268,8 +273,7 @@ class JUCE_API Synthesiser
public:
//==============================================================================
/** Creates a new synthesiser.
You'll need to add some sounds and voices before it'll make any sound..
You'll need to add some sounds and voices before it'll make any sound.
*/
Synthesiser();
@@ -365,6 +369,7 @@ public:
*/
virtual void noteOff (int midiChannel,
int midiNoteNumber,
float velocity,
bool allowTailOff);
/** Turns off all notes.
@@ -444,7 +449,7 @@ public:
This value is propagated to the voices so that they can use it to render the correct
pitches.
*/
void setCurrentPlaybackSampleRate (double sampleRate);
virtual void setCurrentPlaybackSampleRate (double sampleRate);
/** Creates the next block of audio output.
@@ -463,6 +468,11 @@ public:
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.
*/
double getSampleRate() const noexcept { return sampleRate; }
protected:
//==============================================================================
/** This is used to control access to the rendering callback and the note trigger methods. */
@@ -474,21 +484,34 @@ protected:
/** The last pitch-wheel values for each midi channel. */
int lastPitchWheelValues [16];
/** Searches through the voices to find one that's not currently playing, and which
can play the given sound.
/** Renders the voices for the given range.
By default this just calls renderNextBlock() on each voice, but you may need
to override it to handle custom cases.
*/
virtual void renderVoices (AudioSampleBuffer& outputAudio,
int startSample, int numSamples);
/** Searches through the voices to find one that's not currently playing, and
which can play the given sound.
Returns nullptr if all voices are busy and stealing isn't enabled.
This can be overridden to implement custom voice-stealing algorithms.
To implement a custom note-stealing algorithm, you can either override this
method, or (preferably) override findVoiceToSteal().
*/
virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay,
const bool stealIfNoneAvailable) const;
int midiChannel,
int midiNoteNumber,
bool stealIfNoneAvailable) const;
/** Chooses a voice that is most suitable for being re-used.
The default method returns the one that has been playing for the longest, but
you may want to override this and do something more cunning instead.
The default method will attempt to find the oldest voice that isn't the
bottom or top note being played. If that's not suitable for your synth,
you can override this method and do something more cunning instead.
*/
virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay) const;
virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay,
int midiChannel,
int midiNoteNumber) const;
/** Starts a specified voice playing a particular sound.
@@ -511,11 +534,14 @@ private:
bool shouldStealNotes;
BigInteger sustainPedalsDown;
void stopVoice (SynthesiserVoice*, bool allowTailOff);
void stopVoice (SynthesiserVoice*, float velocity, bool allowTailOff);
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Note the new parameters for this method.
// Note the new parameters for these methods.
virtual int findFreeVoice (const bool) const { return 0; }
virtual int noteOff (int, int, int) { return 0; }
virtual int findFreeVoice (SynthesiserSound*, const bool) { return 0; }
virtual int findVoiceToSteal (SynthesiserSound*) const { return 0; }
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser)
@@ -859,8 +859,11 @@ void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCall
if (name.isEmpty() || isMidiInputEnabled (name))
{
const ScopedLock sl (midiCallbackLock);
midiCallbacks.add (callbackToAdd);
midiCallbackDevices.add (name);
MidiCallbackInfo mc;
mc.deviceName = name;
mc.callback = callbackToAdd;
midiCallbacks.add (mc);
}
}
@@ -868,11 +871,12 @@ void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputC
{
for (int i = midiCallbacks.size(); --i >= 0;)
{
if (midiCallbackDevices[i] == name && midiCallbacks.getUnchecked(i) == callbackToRemove)
const MidiCallbackInfo& mc = midiCallbacks.getReference(i);
if (mc.callback == callbackToRemove && mc.deviceName == name)
{
const ScopedLock sl (midiCallbackLock);
midiCallbacks.remove (i);
midiCallbackDevices.remove (i);
}
}
}
@@ -881,16 +885,14 @@ void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const
{
if (! message.isActiveSense())
{
const bool isDefaultSource = (source == nullptr || source == enabledMidiInputs.getFirst());
const ScopedLock sl (midiCallbackLock);
for (int i = midiCallbackDevices.size(); --i >= 0;)
for (int i = 0; i < midiCallbacks.size(); ++i)
{
const String name (midiCallbackDevices[i]);
const MidiCallbackInfo& mc = midiCallbacks.getReference(i);
if ((isDefaultSource && name.isEmpty()) || (name.isNotEmpty() && name == source->getName()))
midiCallbacks.getUnchecked(i)->handleIncomingMidiMessage (source, message);
if (mc.deviceName.isEmpty() || mc.deviceName == source->getName())
mc.callback->handleIncomingMidiMessage (source, message);
}
}
}
@@ -209,10 +209,9 @@ public:
//==============================================================================
/** Returns the current device properties that are in use.
@see setAudioDeviceSetup
*/
void getAudioDeviceSetup (AudioDeviceSetup& setup);
void getAudioDeviceSetup (AudioDeviceSetup& result);
/** Changes the current device or its settings.
@@ -261,9 +260,7 @@ public:
void setCurrentAudioDeviceType (const String& type,
bool treatAsChosenDevice);
/** Closes the currently-open device.
You can call restartLastAudioDevice() later to reopen it in the same state
that it was just in.
*/
@@ -305,8 +302,8 @@ public:
//==============================================================================
/** Returns the average proportion of available CPU being spent inside the audio callbacks.
Returns a value between 0 and 1.0
@returns A value between 0 and 1.0 to indicate the approximate proportion of CPU
time spent in the callbacks.
*/
double getCpuUsage() const;
@@ -333,16 +330,16 @@ public:
void setMidiInputEnabled (const String& midiInputDeviceName, bool enabled);
/** Returns true if a given midi input device is being used.
@see setMidiInputEnabled
*/
bool isMidiInputEnabled (const String& midiInputDeviceName) const;
/** Registers a listener for callbacks when midi events arrive from a midi input.
The device name can be empty to indicate that it wants events from whatever the
current "default" device is. Or it can be the name of one of the midi input devices
(see MidiInput::getDevices() for the names).
The device name can be empty to indicate that it wants to receive all incoming
events from all the enabled MIDI inputs. Or it can be the name of one of the
MIDI input devices if it just wants the events from that device. (see
MidiInput::getDevices() for the list of device names).
Only devices which are enabled (see the setMidiInputEnabled() method) will have their
events forwarded on to listeners.
@@ -350,8 +347,7 @@ public:
void addMidiInputCallback (const String& midiInputDeviceName,
MidiInputCallback* callback);
/** Removes a listener that was previously registered with addMidiInputCallback().
*/
/** Removes a listener that was previously registered with addMidiInputCallback(). */
void removeMidiInputCallback (const String& midiInputDeviceName,
MidiInputCallback* callback);
@@ -371,22 +367,17 @@ public:
void setDefaultMidiOutput (const String& deviceName);
/** Returns the name of the default midi output.
@see setDefaultMidiOutput, getDefaultMidiOutput
*/
String getDefaultMidiOutputName() const { return defaultMidiOutputName; }
const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputName; }
/** Returns the current default midi output device.
If no device has been selected, or the device can't be opened, this will
return 0.
If no device has been selected, or the device can't be opened, this will return nullptr.
@see getDefaultMidiOutputName
*/
MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput; }
/** Returns a list of the types of device supported.
*/
/** Returns a list of the types of device supported. */
const OwnedArray<AudioIODeviceType>& getAvailableDeviceTypes();
//==============================================================================
@@ -429,9 +420,7 @@ public:
void enableInputLevelMeasurement (bool enableMeasurement);
/** Returns the current input level.
To use this, you must first enable it by calling enableInputLevelMeasurement().
See enableInputLevelMeasurement() for more info.
*/
double getCurrentInputLevel() const;
@@ -468,10 +457,16 @@ private:
int testSoundPosition;
AudioSampleBuffer tempBuffer;
struct MidiCallbackInfo
{
String deviceName;
MidiInputCallback* callback;
};
StringArray midiInsFromXml;
OwnedArray<MidiInput> enabledMidiInputs;
Array<MidiInputCallback*> midiCallbacks;
StringArray midiCallbackDevices;
Array<MidiCallbackInfo> midiCallbacks;
String defaultMidiOutputName;
ScopedPointer<MidiOutput> defaultMidiOutput;
CriticalSection audioCallbackLock, midiCallbackLock;
@@ -22,20 +22,16 @@
==============================================================================
*/
AudioIODevice::AudioIODevice (const String& deviceName, const String& typeName_)
: name (deviceName),
typeName (typeName_)
AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName)
: name (deviceName), typeName (deviceTypeName)
{
}
AudioIODevice::~AudioIODevice()
{
}
AudioIODevice::~AudioIODevice() {}
bool AudioIODevice::hasControlPanel() const
{
return false;
}
void AudioIODeviceCallback::audioDeviceError (const String&) {}
bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; }
bool AudioIODevice::hasControlPanel() const { return false; }
bool AudioIODevice::showControlPanel()
{
@@ -43,6 +39,3 @@ bool AudioIODevice::showControlPanel()
// their hasControlPanel() method.
return false;
}
//==============================================================================
void AudioIODeviceCallback::audioDeviceError (const String&) {}
@@ -289,6 +289,11 @@ public:
*/
virtual bool showControlPanel();
/** On devices which support it, this allows automatic gain control or other
mic processing to be disabled.
If the device doesn't support this operation, it'll return false.
*/
virtual bool setAudioPreprocessingEnabled (bool shouldBeEnabled);
//==============================================================================
protected:
@@ -34,7 +34,7 @@
method. Each of the objects returned can then be used to list the available
devices of that type. E.g.
@code
OwnedArray <AudioIODeviceType> types;
OwnedArray<AudioIODeviceType> types;
myAudioDeviceManager.createAudioDeviceTypes (types);
for (int i = 0; i < types.size(); ++i)
@@ -125,6 +125,7 @@
#if JUCE_USE_ANDROID_OPENSLES
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <SLES/OpenSLES_AndroidConfiguration.h>
#endif
#endif
@@ -1,7 +1,7 @@
{
"id": "juce_audio_devices",
"name": "JUCE audio and midi I/O device classes",
"version": "3.0.5",
"version": "3.0.8",
"description": "Classes to play and record from audio and midi i/o devices.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
@@ -80,7 +80,7 @@ public:
Array<double> getAvailableSampleRates() override
{
static const double rates[] = { 8000.0, 16000.0, 32000.0, 44100.0, 48000.0 };
static const double rates[] = { 8000.0, 16000.0, 32000.0, 44100.0, 48000.0 };
return Array<double> (rates, numElementsInArray (rates));
}
@@ -165,6 +165,33 @@ public:
oldCallback->audioDeviceStopped();
}
bool setAudioPreprocessingEnabled (bool enable) override
{
return recorder != nullptr && recorder->setAudioPreprocessingEnabled (enable);
}
private:
//==================================================================================================
CriticalSection callbackLock;
AudioIODeviceCallback* callback;
int actualBufferSize, sampleRate;
int inputLatency, outputLatency;
bool deviceOpen;
String lastError;
BigInteger activeOutputChans, activeInputChans;
int numInputChannels, numOutputChannels;
AudioSampleBuffer inputBuffer, outputBuffer;
struct Player;
struct Recorder;
AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback)
{
const ScopedLock sl (callbackLock);
AudioIODeviceCallback* const oldCallback = callback;
callback = newCallback;
return oldCallback;
}
void run() override
{
if (recorder != nullptr) recorder->start();
@@ -190,28 +217,6 @@ public:
}
}
private:
//==================================================================================================
CriticalSection callbackLock;
AudioIODeviceCallback* callback;
int actualBufferSize, sampleRate;
int inputLatency, outputLatency;
bool deviceOpen;
String lastError;
BigInteger activeOutputChans, activeInputChans;
int numInputChannels, numOutputChannels;
AudioSampleBuffer inputBuffer, outputBuffer;
struct Player;
struct Recorder;
AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback)
{
const ScopedLock sl (callbackLock);
AudioIODeviceCallback* const oldCallback = callback;
callback = newCallback;
return oldCallback;
}
//==================================================================================================
struct Engine
{
@@ -230,6 +235,7 @@ private:
SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY");
SL_IID_RECORD = (SLInterfaceID*) library.getFunction ("SL_IID_RECORD");
SL_IID_ANDROIDCONFIGURATION = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDCONFIGURATION");
check ((*engineObject)->Realize (engineObject, SL_BOOLEAN_FALSE));
check ((*engineObject)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface));
@@ -271,6 +277,7 @@ private:
SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
SLInterfaceID* SL_IID_PLAY;
SLInterfaceID* SL_IID_RECORD;
SLInterfaceID* SL_IID_ANDROIDCONFIGURATION;
private:
DynamicLibrary library;
@@ -334,8 +341,8 @@ private:
SLDataFormat_PCM pcmFormat =
{
SL_DATAFORMAT_PCM,
numChannels,
sampleRate * 1000, // (sample rate units are millihertz)
(SLuint32) numChannels,
(SLuint32) (sampleRate * 1000), // (sample rate units are millihertz)
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
@@ -434,7 +441,8 @@ private:
struct Recorder
{
Recorder (int numChannels, int sampleRate, Engine& engine)
: recorderObject (nullptr), recorderRecord (nullptr), recorderBufferQueue (nullptr),
: recorderObject (nullptr), recorderRecord (nullptr),
recorderBufferQueue (nullptr), configObject (nullptr),
bufferList (numChannels)
{
jassert (numChannels == 1); // STEREO doesn't always work!!
@@ -442,8 +450,8 @@ private:
SLDataFormat_PCM pcmFormat =
{
SL_DATAFORMAT_PCM,
numChannels,
sampleRate * 1000, // (sample rate units are millihertz)
(SLuint32) numChannels,
(SLuint32) (sampleRate * 1000), // (sample rate units are millihertz)
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
(numChannels == 1) ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT),
@@ -466,6 +474,7 @@ private:
{
check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord));
check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue));
check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDCONFIGURATION, &configObject));
check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this));
check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED));
@@ -532,10 +541,20 @@ private:
}
}
bool setAudioPreprocessingEnabled (bool enable)
{
SLuint32 mode = enable ? SL_ANDROID_RECORDING_PRESET_GENERIC
: SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
return configObject != nullptr
&& check ((*configObject)->SetConfiguration (configObject, SL_ANDROID_KEY_RECORDING_PRESET, &mode, sizeof (mode)));
}
private:
SLObjectItf recorderObject;
SLRecordItf recorderRecord;
SLAndroidSimpleBufferQueueItf recorderBufferQueue;
SLAndroidConfigurationItf configObject;
BufferList bufferList;
@@ -67,7 +67,12 @@ public:
return s;
}
Array<double> getAvailableSampleRates() override { return sampleRates; }
Array<double> getAvailableSampleRates() override
{
// can't find a good way to actually ask the device for which of these it supports..
static const double rates[] = { 8000.0, 16000.0, 22050.0, 32000.0, 44100.0, 48000.0 };
return Array<double> (rates, numElementsInArray (rates));
}
Array<int> getAvailableBufferSizes() override
{
@@ -79,7 +84,7 @@ public:
return r;
}
int getDefaultBufferSize() override { return 1024; }
int getDefaultBufferSize() override { return 1024; }
String open (const BigInteger& inputChannelsWanted,
const BigInteger& outputChannelsWanted,
@@ -117,10 +122,9 @@ public:
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this);
fixAudioRouteIfSetToReceiver();
updateDeviceInfo();
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, targetSampleRate);
updateSampleRates();
updateDeviceInfo();
setSessionFloat32Property (kAudioSessionProperty_PreferredHardwareIOBufferDuration, preferredBufferSize / sampleRate);
updateCurrentBufferSize();
@@ -162,8 +166,15 @@ public:
BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
BigInteger getActiveInputChannels() const override { return activeInputChans; }
int getOutputLatencyInSamples() override { return 0; } //xxx
int getInputLatencyInSamples() override { return 0; } //xxx
int getOutputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareOutputLatency); }
int getInputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareInputLatency); }
int getLatency (AudioSessionPropertyID propID)
{
Float32 latency = 0;
getSessionProperty (propID, latency);
return roundToInt (latency * getCurrentSampleRate());
}
void start (AudioIODeviceCallback* newCallback) override
{
@@ -197,11 +208,16 @@ public:
bool isPlaying() override { return isRunning && callback != nullptr; }
String getLastError() override { return lastError; }
bool setAudioPreprocessingEnabled (bool enable) override
{
return setSessionUInt32Property (kAudioSessionProperty_Mode, enable ? kAudioSessionMode_Default
: kAudioSessionMode_Measurement);
}
private:
//==================================================================================================
CriticalSection callbackLock;
Float64 sampleRate;
Array<Float64> sampleRates;
int numInputChannels, numOutputChannels;
int preferredBufferSize, actualBufferSize;
bool isRunning;
@@ -322,38 +338,6 @@ private:
getSessionProperty (kAudioSessionProperty_AudioInputAvailable, audioInputIsAvailable);
}
void updateSampleRates()
{
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate);
sampleRates.clear();
sampleRates.add (sampleRate);
const int commonSampleRates[] = { 8000, 16000, 22050, 32000, 44100, 48000 };
for (int i = 0; i < numElementsInArray (commonSampleRates); ++i)
{
Float64 rate = (Float64) commonSampleRates[i];
if (rate != sampleRate)
{
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, rate);
Float64 actualSampleRate = 0.0;
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, actualSampleRate);
if (actualSampleRate == rate)
sampleRates.add (actualSampleRate);
}
}
DefaultElementComparator<Float64> comparator;
sampleRates.sort (comparator);
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, sampleRate);
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate);
}
void updateCurrentBufferSize()
{
Float32 bufferDuration = sampleRate > 0 ? (Float32) (preferredBufferSize / sampleRate) : 0.0f;
@@ -455,12 +439,12 @@ private:
static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time,
UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data)
{
return static_cast <iOSAudioIODevice*> (client)->process (flags, time, numFrames, data);
return static_cast<iOSAudioIODevice*> (client)->process (flags, time, numFrames, data);
}
static void routingChangedStatic (void* client, AudioSessionPropertyID, UInt32 /*inDataSize*/, const void* propertyValue)
{
static_cast <iOSAudioIODevice*> (client)->routingChanged (propertyValue);
static_cast<iOSAudioIODevice*> (client)->routingChanged (propertyValue);
}
//==================================================================================================
@@ -552,9 +536,9 @@ private:
return AudioSessionGetProperty (propID, &valueSize, &result);
}
static void setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); }
static void setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); }
static void setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); }
static bool setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; }
static bool setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; }
static bool setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; }
JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice)
};
@@ -41,7 +41,7 @@ namespace
return err;
}
#else
#define JUCE_ALSA_LOG(x)
#define JUCE_ALSA_LOG(x) {}
#define JUCE_CHECKED_RESULT(x) (x)
#endif
@@ -144,6 +144,7 @@ public:
: owner (d),
inputLatency (0),
outputLatency (0),
bitDepth (32),
callback (nullptr),
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
audioProcID (0),
@@ -232,7 +233,7 @@ public:
size = sizeof (nameNSString);
pa.mSelector = kAudioObjectPropertyElementName;
pa.mElement = chanNum + 1;
pa.mElement = (AudioObjectPropertyElement) chanNum + 1;
if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &nameNSString) == noErr)
{
@@ -352,6 +353,22 @@ public:
return (int) lat;
}
int getBitDepthFromDevice (AudioObjectPropertyScope scope) const
{
AudioObjectPropertyAddress pa;
pa.mElement = kAudioObjectPropertyElementMaster;
pa.mSelector = kAudioStreamPropertyPhysicalFormat;
pa.mScope = scope;
AudioStreamBasicDescription asbd;
UInt32 size = sizeof (asbd);
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &asbd)))
return (int) asbd.mBitsPerChannel;
return 0;
}
void updateDetailsFromDevice()
{
stopTimer();
@@ -377,7 +394,7 @@ public:
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr)))
sampleRate = sr;
UInt32 framesPerBuf = bufferSize;
UInt32 framesPerBuf = (UInt32) bufferSize;
size = sizeof (framesPerBuf);
pa.mSelector = kAudioDevicePropertyBufferFrameSize;
AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf);
@@ -392,6 +409,13 @@ public:
StringArray newInNames (getChannelInfo (true, newInChans));
StringArray newOutNames (getChannelInfo (false, newOutChans));
const int inputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeInput);
const int outputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeOutput);
bitDepth = jmax (inputBitDepth, outputBitDepth);
if (bitDepth <= 0)
bitDepth = 32;
// after getting the new values, lock + apply them
const ScopedLock sl (callbackLock);
@@ -728,6 +752,7 @@ public:
//==============================================================================
CoreAudioIODevice& owner;
int inputLatency, outputLatency;
int bitDepth;
BigInteger activeInputChans, activeOutputChans;
StringArray inChanNames, outChanNames;
Array<double> sampleRates;
@@ -886,7 +911,7 @@ public:
Array<int> getAvailableBufferSizes() override { return internal->bufferSizes; }
double getCurrentSampleRate() override { return internal->getSampleRate(); }
int getCurrentBitDepth() override { return 32; } // no way to find out, so just assume it's high..
int getCurrentBitDepth() override { return internal->bitDepth; }
int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); }
int getDefaultBufferSize() override
@@ -1387,7 +1412,7 @@ private:
d.done = (d.numInputChans == 0);
}
for (int tries = 3;;)
for (int tries = 5;;)
{
bool anyRemaining = false;
@@ -1434,7 +1459,7 @@ private:
d.done = (d.numOutputChans == 0);
}
for (int tries = 3;;)
for (int tries = 5;;)
{
bool anyRemaining = false;
@@ -365,7 +365,7 @@ public:
void updateSampleRates()
{
// find a list of sample rates..
const int possibleSampleRates[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
const int possibleSampleRates[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
Array<double> newRates;
if (asioObject != nullptr)
@@ -33,8 +33,8 @@ namespace WasapiClasses
void logFailure (HRESULT hr)
{
(void) hr;
jassert (hr != 0x800401f0); // If you hit this, it means you're trying to call from
// a thread which hasn't been initialised with CoInitialize().
jassert (hr != (HRESULT) 0x800401f0); // If you hit this, it means you're trying to call from
// a thread which hasn't been initialised with CoInitialize().
#if JUCE_WASAPI_LOGGING
if (FAILED (hr))
@@ -182,6 +182,10 @@ void AudioTransportSource::setNextReadPosition (int64 newPosition)
newPosition = (int64) (newPosition * sourceSampleRate / sampleRate);
positionableSource->setNextReadPosition (newPosition);
if (resamplerSource != nullptr)
resamplerSource->flushBuffers();
inputStreamEOF = false;
}
}
@@ -37,7 +37,7 @@ const char* const AiffAudioFormat::appleKey = "apple key";
//==============================================================================
namespace AiffFileHelpers
{
inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); }
inline int chunkName (const char* name) noexcept { return (int) ByteOrder::littleEndianInt (name); }
#if JUCE_MSVC
#pragma pack (push, 1)
@@ -888,13 +888,13 @@ AiffAudioFormat::~AiffAudioFormat()
Array<int> AiffAudioFormat::getPossibleSampleRates()
{
const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 };
return Array <int> (rates);
return Array<int> (rates);
}
Array<int> AiffAudioFormat::getPossibleBitDepths()
{
const int depths[] = { 8, 16, 24, 0 };
return Array <int> (depths);
return Array<int> (depths);
}
bool AiffAudioFormat::canDoStereo() { return true; }
@@ -165,13 +165,13 @@ bool LAMEEncoderAudioFormat::canHandleFile (const File&)
Array<int> LAMEEncoderAudioFormat::getPossibleSampleRates()
{
const int rates[] = { 32000, 44100, 48000, 0 };
return Array <int> (rates);
return Array<int> (rates);
}
Array<int> LAMEEncoderAudioFormat::getPossibleBitDepths()
{
const int depths[] = { 16, 0 };
return Array <int> (depths);
return Array<int> (depths);
}
bool LAMEEncoderAudioFormat::canDoStereo() { return true; }
@@ -65,6 +65,7 @@ const char* const WavAudioFormat::acidNumerator = "acid numerator";
const char* const WavAudioFormat::acidTempo = "acid tempo";
const char* const WavAudioFormat::ISRC = "ISRC";
const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info";
//==============================================================================
namespace WavFileHelpers
@@ -471,6 +472,46 @@ namespace WavFileHelpers
}
}
//==============================================================================
namespace ListInfoChunk
{
static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName)
{
const String value (values.getValue (paramName, String()));
if (value.isEmpty())
return false;
const int valueLength = (int) value.getNumBytesAsUTF8() + 1;
const int chunkLength = valueLength + (valueLength & 1);
out.writeInt (chunkName (paramName));
out.writeInt (chunkLength);
out.write (value.toUTF8(), (size_t) valueLength);
if ((out.getDataSize() & 1) != 0)
out.writeByte (0);
return true;
}
static MemoryBlock createFrom (const StringPairArray& values)
{
static const char* params[] = { "INAM", "IART", "IPRD", "IPRT", "ISFT",
"ISRC", "IGNR", "ICMT", "ICOP", "ICRD" };
MemoryOutputStream out;
out.writeInt (chunkName ("INFO"));
bool anyParamsDefined = false;
for (int i = 0; i < numElementsInArray (params); ++i)
if (writeValue (values, out, params[i]))
anyParamsDefined = true;
return anyParamsDefined ? out.getMemoryBlock() : MemoryBlock();
}
}
//==============================================================================
struct AcidChunk
{
@@ -481,6 +522,38 @@ namespace WavFileHelpers
input.read (this, (int) jmin (sizeof (*this), length));
}
AcidChunk (const StringPairArray& values)
{
zerostruct (*this);
flags = getFlagIfPresent (values, WavAudioFormat::acidOneShot, 0x01)
| getFlagIfPresent (values, WavAudioFormat::acidRootSet, 0x02)
| getFlagIfPresent (values, WavAudioFormat::acidStretch, 0x04)
| getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08)
| getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10);
if (values[WavAudioFormat::acidRootSet].getIntValue() != 0)
rootNote = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidRootNote].getIntValue());
numBeats = ByteOrder::swapIfBigEndian ((uint32) values[WavAudioFormat::acidBeats].getIntValue());
meterDenominator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidDenominator].getIntValue());
meterNumerator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidNumerator].getIntValue());
if (values.containsKey (WavAudioFormat::acidTempo))
tempo = swapFloatByteOrder (values[WavAudioFormat::acidTempo].getFloatValue());
}
static MemoryBlock createFrom (const StringPairArray& values)
{
return AcidChunk (values).toMemoryBlock();
}
MemoryBlock toMemoryBlock() const
{
return (flags != 0 || rootNote != 0 || numBeats != 0 || meterDenominator != 0 || meterNumerator != 0)
? MemoryBlock (this, sizeof (*this)) : MemoryBlock();
}
void addToMetadata (StringPairArray& values) const
{
setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01);
@@ -490,30 +563,65 @@ namespace WavFileHelpers
setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10);
if (flags & 0x02) // root note set
values.set (WavAudioFormat::acidRootNote, String (rootNote));
values.set (WavAudioFormat::acidRootNote, String (ByteOrder::swapIfBigEndian (rootNote)));
values.set (WavAudioFormat::acidBeats, String (numBeats));
values.set (WavAudioFormat::acidDenominator, String (meterDenominator));
values.set (WavAudioFormat::acidNumerator, String (meterNumerator));
values.set (WavAudioFormat::acidTempo, String (tempo));
values.set (WavAudioFormat::acidBeats, String (ByteOrder::swapIfBigEndian (numBeats)));
values.set (WavAudioFormat::acidDenominator, String (ByteOrder::swapIfBigEndian (meterDenominator)));
values.set (WavAudioFormat::acidNumerator, String (ByteOrder::swapIfBigEndian (meterNumerator)));
values.set (WavAudioFormat::acidTempo, String (swapFloatByteOrder (tempo)));
}
void setBoolFlag (StringPairArray& values, const char* name, int32 mask) const
void setBoolFlag (StringPairArray& values, const char* name, uint32 mask) const
{
values.set (name, (flags & mask) ? "1" : "0");
values.set (name, (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0");
}
int32 flags;
int16 rootNote;
int16 reserved1;
static uint32 getFlagIfPresent (const StringPairArray& values, const char* name, uint32 flag)
{
return values[name].getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0;
}
static float swapFloatByteOrder (const float x) noexcept
{
#ifdef JUCE_BIG_ENDIAN
union { uint32 asInt; float asFloat; } n;
n.asFloat = x;
n.asInt = ByteOrder::swap (n.asInt);
return n.asFloat;
#else
return x;
#endif
}
uint32 flags;
uint16 rootNote;
uint16 reserved1;
float reserved2;
int32 numBeats;
int16 meterDenominator;
int16 meterNumerator;
uint32 numBeats;
uint16 meterDenominator;
uint16 meterNumerator;
float tempo;
} JUCE_PACKED;
//==============================================================================
struct TracktionChunk
{
static MemoryBlock createFrom (const StringPairArray& values)
{
const String s = values[WavAudioFormat::tracktionLoopInfo];
MemoryBlock data;
if (s.isNotEmpty())
{
MemoryOutputStream os (data, false);
os.writeString (s);
}
return data;
}
};
//==============================================================================
namespace AXMLChunk
{
@@ -816,6 +924,12 @@ public:
{
AcidChunk (*input, length).addToMetadata (metadataValues);
}
else if (chunkType == chunkName ("Trkn"))
{
MemoryBlock tracktion;
input->readIntoMemoryBlock (tracktion, (ssize_t) length);
metadataValues.set (WavAudioFormat::tracktionLoopInfo, tracktion.toString());
}
else if (chunkEnd <= input->getPosition())
{
break;
@@ -913,12 +1027,15 @@ public:
// key should be removed (or set to "WAV") once this has been done
jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF");
bwavChunk = BWAVChunk::createFrom (metadataValues);
axmlChunk = AXMLChunk::createFrom (metadataValues);
smplChunk = SMPLChunk::createFrom (metadataValues);
instChunk = InstChunk::createFrom (metadataValues);
cueChunk = CueChunk ::createFrom (metadataValues);
listChunk = ListChunk::createFrom (metadataValues);
bwavChunk = BWAVChunk::createFrom (metadataValues);
axmlChunk = AXMLChunk::createFrom (metadataValues);
smplChunk = SMPLChunk::createFrom (metadataValues);
instChunk = InstChunk::createFrom (metadataValues);
cueChunk = CueChunk ::createFrom (metadataValues);
listChunk = ListChunk::createFrom (metadataValues);
listInfoChunk = ListInfoChunk::createFrom (metadataValues);
acidChunk = AcidChunk::createFrom (metadataValues);
trckChunk = TracktionChunk::createFrom (metadataValues);
}
headerPosition = out->getPosition();
@@ -927,12 +1044,6 @@ public:
~WavAudioFormatWriter()
{
if ((bytesWritten & 1) != 0) // pad to an even length
{
++bytesWritten;
output->writeByte (0);
}
writeHeader();
}
@@ -972,8 +1083,22 @@ public:
return true;
}
bool flush() override
{
const int64 lastWritePos = output->getPosition();
writeHeader();
if (output->setPosition (lastWritePos))
return true;
// if this fails, you've given it an output stream that can't seek! It needs
// to be able to seek back to write the header
jassertfalse;
return false;
}
private:
MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk;
MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk;
uint64 lengthInSamples, bytesWritten;
int64 headerPosition;
bool writeFailed;
@@ -998,13 +1123,18 @@ private:
void writeHeader()
{
using namespace WavFileHelpers;
const bool seekedOk = output->setPosition (headerPosition);
(void) seekedOk;
if ((bytesWritten & 1) != 0) // pad to an even length
output->writeByte (0);
// if this fails, you've given it an output stream that can't seek! It needs
// to be able to seek back to write the header
jassert (seekedOk);
using namespace WavFileHelpers;
if (headerPosition != output->getPosition() && ! output->setPosition (headerPosition))
{
// if this fails, you've given it an output stream that can't seek! It needs to be
// able to seek back to go back and write the header after the data has been written.
jassertfalse;
return;
}
const size_t bytesPerFrame = numChannels * bitsPerSample / 8;
uint64 audioDataSize = bytesPerFrame * lengthInSamples;
@@ -1020,6 +1150,9 @@ private:
+ chunkSize (instChunk)
+ chunkSize (cueChunk)
+ chunkSize (listChunk)
+ chunkSize (listInfoChunk)
+ chunkSize (acidChunk)
+ chunkSize (trckChunk)
+ (8 + 28)); // (ds64 chunk)
riffChunkSize += (riffChunkSize & 1);
@@ -1093,12 +1226,15 @@ private:
output->write (subFormat.data4, sizeof (subFormat.data4));
}
writeChunk (bwavChunk, chunkName ("bext"));
writeChunk (axmlChunk, chunkName ("axml"));
writeChunk (smplChunk, chunkName ("smpl"));
writeChunk (instChunk, chunkName ("inst"), 7);
writeChunk (cueChunk, chunkName ("cue "));
writeChunk (listChunk, chunkName ("LIST"));
writeChunk (bwavChunk, chunkName ("bext"));
writeChunk (axmlChunk, chunkName ("axml"));
writeChunk (smplChunk, chunkName ("smpl"));
writeChunk (instChunk, chunkName ("inst"), 7);
writeChunk (cueChunk, chunkName ("cue "));
writeChunk (listChunk, chunkName ("LIST"));
writeChunk (listInfoChunk, chunkName ("LIST"));
writeChunk (acidChunk, chunkName ("acid"));
writeChunk (trckChunk, chunkName ("Trkn"));
writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame));
@@ -135,6 +135,9 @@ public:
/** Metadata property name used when reading an ISRC code from an AXML chunk. */
static const char* const ISRC;
/** Metadata property name used when reading a WAV file with a Tracktion chunk. */
static const char* const tracktionLoopInfo;
//==============================================================================
Array<int> getPossibleSampleRates() override;
Array<int> getPossibleBitDepths() override;
@@ -162,7 +162,7 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer,
}
else
{
HeapBlock<int*> chans (numTargetChannels);
HeapBlock<int*> chans ((size_t) numTargetChannels);
readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels);
}
@@ -173,35 +173,54 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer,
}
}
template <typename SampleType>
static Range<SampleType> getChannelMinAndMax (SampleType* channel, int numSamples) noexcept
void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
Range<float>* const results, const int channelsToRead)
{
return Range<SampleType>::findMinAndMax (channel, numSamples);
}
jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels);
static Range<float> getChannelMinAndMax (float* channel, int numSamples) noexcept
{
return FloatVectorOperations::findMinAndMax (channel, numSamples);
}
template <typename SampleType>
static void getStereoMinAndMax (SampleType* const* channels, const int numChannels, const int numSamples,
SampleType& lmin, SampleType& lmax, SampleType& rmin, SampleType& rmax)
{
Range<SampleType> range (getChannelMinAndMax (channels[0], numSamples));
lmax = jmax (lmax, range.getEnd());
lmin = jmin (lmin, range.getStart());
if (numChannels > 1)
if (numSamples <= 0)
{
range = getChannelMinAndMax (channels[1], numSamples);
rmax = jmax (rmax, range.getEnd());
rmin = jmin (rmin, range.getStart());
for (int i = 0; i < channelsToRead; ++i)
results[i] = Range<float>();
return;
}
else
const int bufferSize = (int) jmin (numSamples, (int64) 4096);
AudioSampleBuffer tempSampleBuffer ((int) channelsToRead, bufferSize);
float* const* const floatBuffer = tempSampleBuffer.getArrayOfWritePointers();
int* const* intBuffer = reinterpret_cast<int* const*> (floatBuffer);
bool isFirstBlock = true;
while (numSamples > 0)
{
rmax = lmax;
rmin = lmin;
const int numToDo = (int) jmin (numSamples, (int64) bufferSize);
if (! read (intBuffer, channelsToRead, startSampleInFile, numToDo, false))
break;
for (int i = 0; i < channelsToRead; ++i)
{
Range<float> r;
if (usesFloatingPointData)
{
r = FloatVectorOperations::findMinAndMax (floatBuffer[i], numToDo);
}
else
{
Range<int> intRange (Range<int>::findMinAndMax (intBuffer[i], numToDo));
r = Range<float> (intRange.getStart() / (float) std::numeric_limits<int>::max(),
intRange.getEnd() / (float) std::numeric_limits<int>::max());
}
results[i] = isFirstBlock ? r : results[i].getUnionWith (r);
}
isFirstBlock = false;
numSamples -= numToDo;
startSampleInFile += numToDo;
}
}
@@ -209,66 +228,20 @@ void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples
float& lowestLeft, float& highestLeft,
float& lowestRight, float& highestRight)
{
if (numSamples <= 0)
Range<float> levels[2];
readMaxLevels (startSampleInFile, numSamples, levels, jmin (2, (int) numChannels));
lowestLeft = levels[0].getStart();
highestLeft = levels[0].getEnd();
if (numChannels > 1)
{
lowestLeft = 0;
lowestRight = 0;
highestLeft = 0;
highestRight = 0;
return;
}
const int bufferSize = (int) jmin (numSamples, (int64) 4096);
AudioSampleBuffer tempSampleBuffer ((int) numChannels, bufferSize);
float* const* const floatBuffer = tempSampleBuffer.getArrayOfWritePointers();
int* const* intBuffer = reinterpret_cast<int* const*> (floatBuffer);
if (usesFloatingPointData)
{
float lmin = 1.0e6f;
float lmax = -lmin;
float rmin = lmin;
float rmax = lmax;
while (numSamples > 0)
{
const int numToDo = (int) jmin (numSamples, (int64) bufferSize);
if (! read (intBuffer, 2, startSampleInFile, numToDo, false))
break;
numSamples -= numToDo;
startSampleInFile += numToDo;
getStereoMinAndMax (floatBuffer, (int) numChannels, numToDo, lmin, lmax, rmin, rmax);
}
lowestLeft = lmin;
highestLeft = lmax;
lowestRight = rmin;
highestRight = rmax;
lowestRight = levels[1].getStart();
highestRight = levels[1].getEnd();
}
else
{
int lmax = std::numeric_limits<int>::min();
int lmin = std::numeric_limits<int>::max();
int rmax = std::numeric_limits<int>::min();
int rmin = std::numeric_limits<int>::max();
while (numSamples > 0)
{
const int numToDo = (int) jmin (numSamples, (int64) bufferSize);
if (! read (intBuffer, 2, startSampleInFile, numToDo, false))
break;
numSamples -= numToDo;
startSampleInFile += numToDo;
getStereoMinAndMax (intBuffer, (int) numChannels, numToDo, lmin, lmax, rmin, rmax);
}
lowestLeft = lmin / (float) std::numeric_limits<int>::max();
highestLeft = lmax / (float) std::numeric_limits<int>::max();
lowestRight = rmin / (float) std::numeric_limits<int>::max();
highestRight = rmax / (float) std::numeric_limits<int>::max();
lowestRight = lowestLeft;
highestRight = highestLeft;
}
}
@@ -121,6 +121,25 @@ public:
bool useReaderLeftChan,
bool useReaderRightChan);
/** Finds the highest and lowest sample levels from a section of the audio stream.
This will read a block of samples from the stream, and measure the
highest and lowest sample levels from the channels in that section, returning
these as normalised floating-point levels.
@param startSample the offset into the audio stream to start reading from. It's
ok for this to be beyond the start or end of the stream.
@param numSamples how many samples to read
@param results this array will be filled with Range values for each channel.
The array must contain numChannels elements.
@param numChannelsToRead the number of channels of data to scan. This must be
more than zero, but not more than the total number of channels
that the reader contains
@see read
*/
virtual void readMaxLevels (int64 startSample, int64 numSamples,
Range<float>* results, int numChannelsToRead);
/** Finds the highest and lowest sample levels from a section of the audio stream.
This will read a block of samples from the stream, and measure the
@@ -138,12 +157,9 @@ public:
channel (if there is one)
@see read
*/
virtual void readMaxLevels (int64 startSample,
int64 numSamples,
float& lowestLeft,
float& highestLeft,
float& lowestRight,
float& highestRight);
virtual void readMaxLevels (int64 startSample, int64 numSamples,
float& lowestLeft, float& highestLeft,
float& lowestRight, float& highestRight);
/** Scans the source looking for a sample whose magnitude is in a specified range.
@@ -183,6 +183,11 @@ bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& sou
return writeFromFloatArrays (chans, numSourceChannels, numSamples);
}
bool AudioFormatWriter::flush()
{
return false;
}
//==============================================================================
class AudioFormatWriter::ThreadedWriter::Buffer : private TimeSliceClient
{
@@ -194,6 +199,8 @@ public:
writer (w),
receiver (nullptr),
samplesWritten (0),
samplesPerFlush (0),
flushSampleCounter (0),
isRunning (true)
{
timeSliceThread.addTimeSliceClient (this);
@@ -266,6 +273,18 @@ public:
}
fifo.finishedRead (size1 + size2);
if (samplesPerFlush > 0)
{
flushSampleCounter -= size1 + size2;
if (flushSampleCounter <= 0)
{
flushSampleCounter = samplesPerFlush;
writer->flush();
}
}
return 0;
}
@@ -279,6 +298,11 @@ public:
samplesWritten = 0;
}
void setFlushInterval (int numSamples) noexcept
{
samplesPerFlush = numSamples;
}
private:
AbstractFifo fifo;
AudioSampleBuffer buffer;
@@ -287,6 +311,7 @@ private:
CriticalSection thumbnailLock;
IncomingDataReceiver* receiver;
int64 samplesWritten;
int samplesPerFlush, flushSampleCounter;
volatile bool isRunning;
JUCE_DECLARE_NON_COPYABLE (Buffer)
@@ -310,3 +335,8 @@ void AudioFormatWriter::ThreadedWriter::setDataReceiver (AudioFormatWriter::Thre
{
buffer->setDataReceiver (receiver);
}
void AudioFormatWriter::ThreadedWriter::setFlushInterval (int numSamplesPerFlush) noexcept
{
buffer->setFlushInterval (numSamplesPerFlush);
}
@@ -91,8 +91,18 @@ public:
to pass it into the method.
@param numSamples the number of samples to write
*/
virtual bool write (const int** samplesToWrite,
int numSamples) = 0;
virtual bool write (const int** samplesToWrite, int numSamples) = 0;
/** Some formats may support a flush operation that makes sure the file is in a
valid state before carrying on.
If supported, this means that by calling flush periodically when writing data
to a large file, then it should still be left in a readable state if your program
crashes.
It goes without saying that this method must be called from the same thread that's
calling write()!
If the format supports flushing and the operation succeeds, this returns true.
*/
virtual bool flush();
//==============================================================================
/** Reads a section of samples from an AudioFormatReader, and writes these to
@@ -197,7 +207,12 @@ public:
The object passed-in must not be deleted while this writer is still using it.
*/
void setDataReceiver (IncomingDataReceiver* receiver);
void setDataReceiver (IncomingDataReceiver*);
/** Sets how many samples should be written before calling the AudioFormatWriter::flush method.
Set this to 0 to disable flushing (this is the default).
*/
void setFlushInterval (int numSamplesPerFlush) noexcept;
private:
class Buffer;
@@ -1,7 +1,7 @@
{
"id": "juce_audio_formats",
"name": "JUCE audio file format codecs",
"version": "3.0.5",
"version": "3.0.8",
"description": "Classes for reading and writing various audio file formats.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
@@ -59,12 +59,12 @@ SamplerSound::~SamplerSound()
{
}
bool SamplerSound::appliesToNote (const int midiNoteNumber)
bool SamplerSound::appliesToNote (int midiNoteNumber)
{
return midiNotes [midiNoteNumber];
}
bool SamplerSound::appliesToChannel (const int /*midiChannel*/)
bool SamplerSound::appliesToChannel (int /*midiChannel*/)
{
return true;
}
@@ -127,7 +127,7 @@ void SamplerVoice::startNote (const int midiNoteNumber,
}
}
void SamplerVoice::stopNote (const bool allowTailOff)
void SamplerVoice::stopNote (float /*velocity*/, bool allowTailOff)
{
if (allowTailOff)
{
@@ -197,7 +197,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa
if (attackReleaseLevel <= 0.0f)
{
stopNote (false);
stopNote (0.0f, false);
break;
}
}
@@ -216,7 +216,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa
if (sourceSamplePosition > playingSound->length)
{
stopNote (false);
stopNote (0.0f, false);
break;
}
}
@@ -82,8 +82,8 @@ public:
//==============================================================================
bool appliesToNote (const int midiNoteNumber) override;
bool appliesToChannel (const int midiChannel) override;
bool appliesToNote (int midiNoteNumber) override;
bool appliesToChannel (int midiChannel) override;
private:
@@ -124,7 +124,7 @@ public:
bool canPlaySound (SynthesiserSound*) override;
void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override;
void stopNote (bool allowTailOff) override;
void stopNote (float velocity, bool allowTailOff) override;
void pitchWheelMoved (int newValue);
void controllerMoved (int controllerNumber, int newValue) override;
@@ -54,7 +54,7 @@ public:
(e.g. VST shells) can use a single DLL to create a set of different plugin
subtypes, so in that case, each subtype is returned as a separate object.
*/
virtual void findAllTypesForFile (OwnedArray <PluginDescription>& results,
virtual void findAllTypesForFile (OwnedArray<PluginDescription>& results,
const String& fileOrIdentifier) = 0;
/** Tries to recreate a type from a previously generated PluginDescription.
@@ -89,7 +89,7 @@ public:
private:
//==============================================================================
OwnedArray <AudioPluginFormat> formats;
OwnedArray<AudioPluginFormat> formats;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormatManager)
};
@@ -37,7 +37,7 @@ public:
//==============================================================================
String getName() const override { return "AudioUnit"; }
void findAllTypesForFile (OwnedArray <PluginDescription>&, const String& fileOrIdentifier) override;
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, double, int) override;
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
@@ -62,7 +62,7 @@ namespace AudioUnitFormatHelpers
static ThreadLocalValue<int> insideCallback;
#endif
String osTypeToString (OSType type)
String osTypeToString (OSType type) noexcept
{
const juce_wchar s[4] = { (juce_wchar) ((type >> 24) & 0xff),
(juce_wchar) ((type >> 16) & 0xff),
@@ -88,9 +88,6 @@ namespace AudioUnitFormatHelpers
String createPluginIdentifier (const AudioComponentDescription& desc)
{
jassert (osTypeToString ('abcd') == "abcd"); // agh, must have got the endianness wrong..
jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto
String s (auIdentifierPrefix);
if (desc.componentType == kAudioUnitType_MusicDevice)
@@ -138,7 +135,7 @@ namespace AudioUnitFormatHelpers
fileOrIdentifier.lastIndexOfChar ('/')) + 1));
StringArray tokens;
tokens.addTokens (s, ",", String());
tokens.addTokens (s, ",", StringRef());
tokens.removeEmptyStrings();
if (tokens.size() == 3)
@@ -215,9 +212,11 @@ namespace AudioUnitFormatHelpers
const short resFileId = CFBundleOpenBundleResourceMap (bundleRef);
UseResFile (resFileId);
for (ResourceIndex i = 1; i <= Count1Resources ('thng'); ++i)
const OSType thngType = stringToOSType ("thng");
for (ResourceIndex i = 1; i <= Count1Resources (thngType); ++i)
{
if (Handle h = Get1IndResource ('thng', i))
if (Handle h = Get1IndResource (thngType, i))
{
HLock (h);
const uint32* const types = (const uint32*) *h;
@@ -346,11 +345,21 @@ public:
refreshParameterList();
updateNumChannels();
producesMidiMessages = canProduceMidiOutput();
setPluginCallbacks();
setPlayConfigDetails (numInputBusChannels * numInputBusses,
numOutputBusChannels * numOutputBusses,
rate, blockSize);
setLatencySamples (0);
if (parameters.size() == 0)
{
// some plugins crash if initialiseAudioUnit() is called too soon (sigh..), so we'll
// only call it here if it seems like they it's one of the awkward plugins that can
// only create their parameters after it has been initialised.
initialiseAudioUnit();
refreshParameterList();
}
setPluginCallbacks();
}
//==============================================================================
@@ -915,7 +924,7 @@ private:
bool automatable;
};
OwnedArray <ParamInfo> parameters;
OwnedArray<ParamInfo> parameters;
MidiDataConcatenator midiConcatenator;
CriticalSection midiInLock;
@@ -972,16 +981,11 @@ private:
for (int i = 0; i < parameters.size(); ++i)
{
const ParamInfo& p = *parameters.getUnchecked(i);
AudioUnitParameter paramToAdd;
paramToAdd.mAudioUnit = audioUnit;
paramToAdd.mParameterID = p.paramID;
paramToAdd.mScope = kAudioUnitScope_Global;
paramToAdd.mElement = 0;
AudioUnitEvent event;
event.mArgument.mParameter = paramToAdd;
event.mArgument.mParameter.mAudioUnit = audioUnit;
event.mArgument.mParameter.mParameterID = parameters.getUnchecked(i)->paramID;
event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
event.mArgument.mParameter.mElement = 0;
event.mEventType = kAudioUnitEvent_ParameterValueChange;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
@@ -992,6 +996,16 @@ private:
event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
}
// Add a listener for program changes
AudioUnitEvent event;
event.mArgument.mProperty.mAudioUnit = audioUnit;
event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_PresentPreset;
event.mArgument.mProperty.mScope = kAudioUnitScope_Global;
event.mArgument.mProperty.mElement = 0;
event.mEventType = kAudioUnitEvent_PropertyChange;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
}
}
@@ -1022,6 +1036,7 @@ private:
break;
default:
sendAllParametersChangedEvents();
break;
}
}
@@ -1600,7 +1615,7 @@ AudioUnitPluginFormat::~AudioUnitPluginFormat()
{
}
void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& results,
void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results,
const String& fileOrIdentifier)
{
if (! fileMightContainThisPluginType (fileOrIdentifier))
@@ -118,8 +118,8 @@ class LADSPAPluginInstance : public AudioPluginInstance
{
public:
LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m)
: plugin (nullptr), handle (nullptr), initialised (false),
tempBuffer (1, 1), module (m)
: module (m), plugin (nullptr), handle (nullptr),
initialised (false), tempBuffer (1, 1)
{
++insideLADSPACallback;
@@ -85,6 +85,18 @@ static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept
//==============================================================================
static Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst::IAudioProcessor* processor,
bool isInput, int busIndex)
{
Steinberg::Vst::SpeakerArrangement arrangement = Steinberg::Vst::SpeakerArr::kEmpty;
if (processor != nullptr)
processor->getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput,
(Steinberg::int32) busIndex, arrangement);
return arrangement;
}
/** For the sake of simplicity, there can only be 1 arrangement type per channel count.
i.e.: 4 channels == k31Cine OR k40Cine
*/
@@ -126,9 +138,9 @@ class ComSmartPtr
{
public:
ComSmartPtr() noexcept : source (nullptr) {}
ComSmartPtr (ObjectType* object) noexcept : source (object) { if (source != nullptr) source->addRef(); }
ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); }
~ComSmartPtr() { if (source != nullptr) source->release(); }
ComSmartPtr (ObjectType* object, bool autoAddRef = true) noexcept : source (object) { if (source != nullptr && autoAddRef) source->addRef(); }
ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); }
~ComSmartPtr() { if (source != nullptr) source->release(); }
operator ObjectType*() const noexcept { return source; }
ObjectType* get() const noexcept { return source; }
@@ -341,7 +353,7 @@ namespace VST3BufferExchange
Bus& bus,
AudioSampleBuffer& buffer,
int numChannels, int channelStartOffset,
int sampleOffset = 0) noexcept
int sampleOffset = 0)
{
const int channelEnd = numChannels + channelStartOffset;
jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels());
@@ -356,37 +368,52 @@ namespace VST3BufferExchange
vstBuffers.silenceFlags = 0;
}
static void mapArrangementToBusses (int& channelIndexOffset, int index,
Array<Steinberg::Vst::AudioBusBuffers>& result,
BusMap& busMapToUse, Steinberg::Vst::SpeakerArrangement arrangement,
AudioSampleBuffer& source)
{
const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits();
if (index >= result.size())
result.add (Steinberg::Vst::AudioBusBuffers());
if (index >= busMapToUse.size())
busMapToUse.add (Bus());
if (numChansForBus > 0)
{
associateBufferTo (result.getReference (index),
busMapToUse.getReference (index),
source, numChansForBus, channelIndexOffset);
}
channelIndexOffset += numChansForBus;
}
static void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result, BusMap& busMapToUse,
const Array<Steinberg::Vst::SpeakerArrangement>& arrangements,
AudioSampleBuffer& source)
{
int channelIndexOffset = 0;
for (int i = 0; i < arrangements.size(); ++i)
mapArrangementToBusses (channelIndexOffset, i, result, busMapToUse,
arrangements.getUnchecked (i), source);
}
static void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result,
Steinberg::Vst::IAudioProcessor& processor,
BusMap& busMapToUse,
bool isInput, int numBusses,
BusMap& busMapToUse, bool isInput, int numBusses,
AudioSampleBuffer& source)
{
int channelIndexOffset = 0;
for (int i = 0; i < numBusses; ++i)
{
Steinberg::Vst::SpeakerArrangement arrangement = 0;
processor.getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput,
(Steinberg::int32) i, arrangement);
const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits();
if (i >= result.size())
result.add (Steinberg::Vst::AudioBusBuffers());
if (i >= busMapToUse.size())
busMapToUse.add (Bus());
if (numChansForBus > 0)
{
associateBufferTo (result.getReference (i),
busMapToUse.getReference (i),
source, numChansForBus, channelIndexOffset);
}
channelIndexOffset += numChansForBus;
}
mapArrangementToBusses (channelIndexOffset, i,
result, busMapToUse,
getArrangementForBus (&processor, isInput, i),
source);
}
}
@@ -31,6 +31,7 @@
#define JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY 1
#endif
#include <map>
#include "juce_VST3Headers.h"
#undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY
@@ -111,6 +112,7 @@ static void createPluginDescription (PluginDescription& description,
description.lastFileModTime = pluginFile.getLastModificationTime();
description.manufacturerName = company;
description.name = name;
description.descriptiveName = name;
description.pluginFormatName = "VST3";
description.numInputChannels = numInputs;
description.numOutputChannels = numOutputs;
@@ -378,32 +380,65 @@ public:
FUnknown* getFUnknown() { return static_cast<Vst::IComponentHandler*> (this); }
static bool hasFlag (Steinberg::int32 source, Steinberg::int32 flag) noexcept
{
return (source & flag) == flag;
}
//==============================================================================
tresult PLUGIN_API beginEdit (Vst::ParamID) override
tresult PLUGIN_API beginEdit (Vst::ParamID paramID) override
{
// XXX todo..
return kResultFalse;
const int index = getIndexOfParamID (paramID);
if (index < 0)
return kResultFalse;
owner->beginParameterChangeGesture (index);
return kResultTrue;
}
tresult PLUGIN_API performEdit (Vst::ParamID id, Vst::ParamValue valueNormalized) override
tresult PLUGIN_API performEdit (Vst::ParamID paramID, Vst::ParamValue valueNormalized) override
{
if (owner != nullptr)
return owner->editController->setParamNormalized (id, valueNormalized);
const int index = getIndexOfParamID (paramID);
return kResultFalse;
if (index < 0)
return kResultFalse;
owner->sendParamChangeMessageToListeners (index, (float) valueNormalized);
return owner->editController->setParamNormalized (paramID, valueNormalized);
}
tresult PLUGIN_API endEdit (Vst::ParamID) override
tresult PLUGIN_API endEdit (Vst::ParamID paramID) override
{
// XXX todo..
return kResultFalse;
const int index = getIndexOfParamID (paramID);
if (index < 0)
return kResultFalse;
owner->endParameterChangeGesture (index);
return kResultTrue;
}
tresult PLUGIN_API restartComponent (Steinberg::int32) override
tresult PLUGIN_API restartComponent (Steinberg::int32 flags) override
{
if (owner != nullptr)
{
owner->reset();
if (hasFlag (flags, Vst::kReloadComponent))
owner->reset();
if (hasFlag (flags, Vst::kIoChanged))
{
const double sampleRate = owner->getSampleRate();
const int blockSize = owner->getBlockSize();
owner->prepareToPlay (sampleRate >= 8000 ? sampleRate : 44100.0,
blockSize > 0 ? blockSize : 1024);
}
if (hasFlag (flags, Vst::kLatencyChanged))
if (owner->processor != nullptr)
owner->setLatencySamples (jmax (0, (int) owner->processor->getLatencySamples()));
owner->updateHostDisplay();
return kResultTrue;
}
@@ -437,9 +472,171 @@ public:
return kResultFalse;
}
//==============================================================================
class ContextMenu : public Vst::IContextMenu
{
public:
ContextMenu (VST3PluginInstance& pluginInstance) : owner (pluginInstance) {}
virtual ~ContextMenu() {}
JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS
Steinberg::int32 PLUGIN_API getItemCount() override { return (Steinberg::int32) items.size(); }
tresult PLUGIN_API addItem (const Item& item, IContextMenuTarget* target) override
{
jassert (target != nullptr);
ItemAndTarget newItem;
newItem.item = item;
newItem.target = target;
items.add (newItem);
return kResultOk;
}
tresult PLUGIN_API removeItem (const Item& toRemove, IContextMenuTarget* target) override
{
for (int i = items.size(); --i >= 0;)
{
ItemAndTarget& item = items.getReference(i);
if (item.item.tag == toRemove.tag && item.target == target)
items.remove (i);
}
return kResultOk;
}
tresult PLUGIN_API getItem (Steinberg::int32 tag, Item& result, IContextMenuTarget** target) override
{
for (int i = 0; i < items.size(); ++i)
{
const ItemAndTarget& item = items.getReference(i);
if (item.item.tag == tag)
{
result = item.item;
if (target != nullptr)
*target = item.target;
return kResultTrue;
}
}
zerostruct (result);
return kResultFalse;
}
tresult PLUGIN_API popup (Steinberg::UCoord x, Steinberg::UCoord y) override
{
Array<const Item*> subItemStack;
OwnedArray<PopupMenu> menuStack;
PopupMenu* topLevelMenu = menuStack.add (new PopupMenu());
for (int i = 0; i < items.size(); ++i)
{
const Item& item = items.getReference (i).item;
PopupMenu* menuToUse = menuStack.getLast();
if (hasFlag (item.flags, Item::kIsGroupStart & ~Item::kIsDisabled))
{
subItemStack.add (&item);
menuStack.add (new PopupMenu());
}
else if (hasFlag (item.flags, Item::kIsGroupEnd))
{
if (const Item* subItem = subItemStack.getLast())
{
if (PopupMenu* m = menuStack [menuStack.size() - 2])
m->addSubMenu (toString (subItem->name), *menuToUse,
! hasFlag (subItem->flags, Item::kIsDisabled),
nullptr,
hasFlag (subItem->flags, Item::kIsChecked));
menuStack.removeLast (1);
subItemStack.removeLast (1);
}
}
else if (hasFlag (item.flags, Item::kIsSeparator))
{
menuToUse->addSeparator();
}
else
{
menuToUse->addItem (item.tag != 0 ? (int) item.tag : (int) zeroTagReplacement,
toString (item.name),
! hasFlag (item.flags, Item::kIsDisabled),
hasFlag (item.flags, Item::kIsChecked));
}
}
PopupMenu::Options options;
if (AudioProcessorEditor* ed = owner.getActiveEditor())
options = options.withTargetScreenArea (ed->getScreenBounds().translated ((int) x, (int) y).withSize (1, 1));
#if JUCE_MODAL_LOOPS_PERMITTED
// Unfortunately, Steinberg's docs explicitly say this should be modal..
handleResult (topLevelMenu->showMenu (options));
#else
topLevelMenu->showMenuAsync (options, ModalCallbackFunction::create (menuFinished, ComSmartPtr<ContextMenu> (this)));
#endif
return kResultOk;
}
#if ! JUCE_MODAL_LOOPS_PERMITTED
static void menuFinished (int modalResult, ComSmartPtr<ContextMenu> menu) { menu->handleResult (modalResult); }
#endif
private:
enum { zeroTagReplacement = 0x7fffffff };
Atomic<int> refCount;
VST3PluginInstance& owner;
struct ItemAndTarget
{
Item item;
ComSmartPtr<IContextMenuTarget> target;
};
Array<ItemAndTarget> items;
void handleResult (int result)
{
if (result == 0)
return;
if (result == zeroTagReplacement)
result = 0;
for (int i = 0; i < items.size(); ++i)
{
const ItemAndTarget& item = items.getReference(i);
if ((int) item.item.tag == result)
{
if (item.target != nullptr)
item.target->executeMenuItem ((Steinberg::int32) result);
break;
}
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContextMenu)
};
Vst::IContextMenu* PLUGIN_API createContextMenu (IPlugView*, const Vst::ParamID*) override
{
jassertfalse;
if (owner != nullptr)
return new ContextMenu (*owner);
return nullptr;
}
@@ -549,9 +746,42 @@ public:
private:
//==============================================================================
Atomic<int32> refCount;
VST3PluginInstance* const owner;
Atomic<int> refCount;
String appName;
VST3PluginInstance* owner;
typedef std::map<Vst::ParamID, int> ParamMapType;
ParamMapType paramToIndexMap;
int getIndexOfParamID (Vst::ParamID paramID)
{
if (owner == nullptr || owner->editController == nullptr)
return -1;
int result = getMappedParamID (paramID);
if (result < 0)
{
const int numParams = owner->editController->getParameterCount();
for (int i = 0; i < numParams; ++i)
{
Vst::ParameterInfo paramInfo;
owner->editController->getParameterInfo (i, paramInfo);
paramToIndexMap[paramInfo.id] = i;
}
result = getMappedParamID (paramID);
}
return result;
}
int getMappedParamID (Vst::ParamID paramID)
{
const ParamMapType::iterator it (paramToIndexMap.find (paramID));
return it != paramToIndexMap.end() ? it->second : -1;
}
//==============================================================================
class Message : public Vst::IMessage
@@ -577,9 +807,9 @@ private:
JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS
FIDString PLUGIN_API getMessageID() { return messageId.toRawUTF8(); }
void PLUGIN_API setMessageID (FIDString id) { messageId = toString (id); }
Vst::IAttributeList* PLUGIN_API getAttributes() { return attributeList; }
FIDString PLUGIN_API getMessageID() override { return messageId.toRawUTF8(); }
void PLUGIN_API setMessageID (FIDString id) override { messageId = toString (id); }
Vst::IAttributeList* PLUGIN_API getAttributes() override { return attributeList; }
var value;
@@ -587,7 +817,7 @@ private:
VST3HostContext& owner;
ComSmartPtr<Vst::IAttributeList> attributeList;
String messageId;
Atomic<int32> refCount;
Atomic<int> refCount;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message)
};
@@ -715,7 +945,7 @@ private:
private:
VST3HostContext* owner;
Atomic<int32> refCount;
Atomic<int> refCount;
//==============================================================================
template<typename Type>
@@ -1195,7 +1425,8 @@ public:
VST3PluginWindow (AudioProcessor* owner, IPlugView* pluginView)
: AudioProcessorEditor (owner),
ComponentMovementWatcher (this),
view (pluginView),
refCount (1),
view (pluginView, false),
pluginHandle (nullptr),
recursiveResize (false)
{
@@ -1213,7 +1444,9 @@ public:
~VST3PluginWindow()
{
warnOnFailure (view->removed());
getAudioProcessor()->editorBeingDeleted (this);
warnOnFailure (view->setFrame (nullptr));
processor.editorBeingDeleted (this);
#if JUCE_MAC
dummyComponent.setView (nullptr);
@@ -1266,20 +1499,32 @@ public:
{
rect.right = (Steinberg::int32) getWidth();
rect.bottom = (Steinberg::int32) getHeight();
view->checkSizeConstraint (&rect);
setSize ((int) rect.getWidth(), (int) rect.getHeight());
#if JUCE_WINDOWS
SetWindowPos (pluginHandle, 0,
pos.x, pos.y, rect.getWidth(), rect.getHeight(),
isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW);
#elif JUCE_MAC
dummyComponent.setBounds (getLocalBounds());
#endif
view->onSize (&rect);
}
else
{
warnOnFailure (view->getSize (&rect));
}
#if JUCE_WINDOWS
SetWindowPos (pluginHandle, 0,
pos.x, pos.y, rect.getWidth(), rect.getHeight(),
isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW);
#elif JUCE_MAC
dummyComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight());
#endif
#if JUCE_WINDOWS
SetWindowPos (pluginHandle, 0,
pos.x, pos.y, rect.getWidth(), rect.getHeight(),
isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW);
#elif JUCE_MAC
dummyComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight());
#endif
}
// Some plugins don't update their cursor correctly when mousing out the window
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
@@ -1364,7 +1609,7 @@ private:
if (peer != nullptr)
pluginHandle = (HandleFormat) peer->getNativeHandle();
#elif JUCE_MAC
dummyComponent.setBounds (getBounds().withZeroOrigin());
dummyComponent.setBounds (getLocalBounds());
addAndMakeVisible (dummyComponent);
pluginHandle = (NSView*) dummyComponent.getView();
jassert (pluginHandle != nil);
@@ -1391,7 +1636,8 @@ public:
midiInputs (new MidiEventList()),
midiOutputs (new MidiEventList()),
isComponentInitialised (false),
isControllerInitialised (false)
isControllerInitialised (false),
isActive (false)
{
host = new VST3HostContext (this);
}
@@ -1413,7 +1659,6 @@ public:
if (isControllerInitialised) editController->terminate();
if (isComponentInitialised) component->terminate();
//Deletion order appears to matter:
componentConnection = nullptr;
editControllerConnection = nullptr;
unitData = nullptr;
@@ -1425,6 +1670,8 @@ public:
editController2 = nullptr;
editController = nullptr;
component = nullptr;
host = nullptr;
module = nullptr;
}
bool initialise()
@@ -1478,8 +1725,27 @@ public:
return module != nullptr ? module->name : String::empty;
}
void repopulateArrangements()
{
inputArrangements.clearQuick();
outputArrangements.clearQuick();
// NB: Some plugins need a valid arrangement despite specifying 0 for their I/O busses
for (int i = 0; i < jmax (1, numInputAudioBusses); ++i)
inputArrangements.add (getArrangementForBus (processor, true, i));
for (int i = 0; i < jmax (1, numOutputAudioBusses); ++i)
outputArrangements.add (getArrangementForBus (processor, false, i));
}
void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override
{
// Avoid redundantly calling things like setActive, which can be a heavy-duty call for some plugins:
if (isActive
&& getSampleRate() == sampleRate
&& getBlockSize() == estimatedSamplesPerBlock)
return;
using namespace Vst;
ProcessSetup setup;
@@ -1495,20 +1761,25 @@ public:
editController->setComponentHandler (host);
Array<SpeakerArrangement> inArrangements, outArrangements;
if (inputArrangements.size() <= 0 || outputArrangements.size() <= 0)
repopulateArrangements();
for (int i = 0; i < numInputAudioBusses; ++i)
inArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (true, true, i).channelCount)));
for (int i = 0; i < numOutputAudioBusses; ++i)
outArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (false, true, i).channelCount)));
warnOnFailure (processor->setBusArrangements (inArrangements.getRawDataPointer(), numInputAudioBusses,
outArrangements.getRawDataPointer(), numOutputAudioBusses));
warnOnFailure (processor->setBusArrangements (inputArrangements.getRawDataPointer(), numInputAudioBusses,
outputArrangements.getRawDataPointer(), numOutputAudioBusses));
// Update the num. busses in case the configuration has been modified by the plugin. (May affect number of channels!):
numInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true);
numOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true);
const int newNumInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true);
const int newNumOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true);
// Repopulate arrangements if the number of busses have changed:
if (numInputAudioBusses != newNumInputAudioBusses
|| numOutputAudioBusses != newNumOutputAudioBusses)
{
numInputAudioBusses = newNumInputAudioBusses;
numOutputAudioBusses = newNumOutputAudioBusses;
repopulateArrangements();
}
// Needed for having the same sample rate in processBlock(); some plugins need this!
setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true),
@@ -1517,21 +1788,30 @@ public:
setStateForAllBusses (true);
setLatencySamples (jmax (0, (int) processor->getLatencySamples()));
warnOnFailure (component->setActive (true));
warnOnFailure (processor->setProcessing (true));
isActive = true;
}
void releaseResources() override
{
if (! isActive)
return; // Avoids redundantly calling things like setActive
JUCE_TRY
{
isActive = false;
setStateForAllBusses (false);
if (processor != nullptr)
processor->setProcessing (false);
warnOnFailure (processor->setProcessing (false));
if (component != nullptr)
component->setActive (false);
warnOnFailure (component->setActive (false));
}
JUCE_CATCH_ALL_ASSERT
}
@@ -1540,7 +1820,8 @@ public:
{
using namespace Vst;
if (processor != nullptr
if (isActive
&& processor != nullptr
&& processor->canProcessSampleSize (kSample32) == kResultTrue)
{
const int numSamples = buffer.getNumSamples();
@@ -1612,15 +1893,22 @@ public:
//==============================================================================
bool silenceInProducesSilenceOut() const override
{
return processor == nullptr;
if (processor != nullptr)
return processor->getTailSamples() == Vst::kNoTail;
return true;
}
/** May return a negative value as a means of informing us that the plugin has "infinite tail," or 0 for "no tail." */
double getTailLengthSeconds() const override
{
if (processor != nullptr)
return (double) jmin ((int) jmax ((Steinberg::uint32) 0, processor->getTailSamples()), 0x7fffffff)
* getSampleRate();
{
const double sampleRate = getSampleRate();
if (sampleRate > 0.0)
return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate;
}
return 0.0;
}
@@ -1640,7 +1928,7 @@ public:
if (getActiveEditor() != nullptr)
return true;
ComSmartPtr<IPlugView> view (tryCreatingView());
ComSmartPtr<IPlugView> view (tryCreatingView(), false);
return view != nullptr;
}
@@ -1706,6 +1994,16 @@ public:
return toString (result);
}
//==============================================================================
void reset() override
{
if (component != nullptr)
{
component->setActive (false);
component->setActive (true);
}
}
//==============================================================================
void getStateInformation (MemoryBlock& destData) override
{
@@ -1785,6 +2083,7 @@ private:
as very poorly specified by the Steinberg SDK
*/
int numInputAudioBusses, numOutputAudioBusses;
Array<Vst::SpeakerArrangement> inputArrangements, outputArrangements; // Caching to improve performance and to avoid possible non-thread-safe calls to getBusArrangements().
VST3BufferExchange::BusMap inputBusMap, outputBusMap;
Array<Vst::AudioBusBuffers> inputBusses, outputBusses;
@@ -1813,7 +2112,11 @@ private:
MemoryBlock mem;
if (mem.fromBase64Encoding (state->getAllSubText()))
stream = new Steinberg::MemoryStream (mem.getData(), (TSize) mem.getSize());
{
stream = new Steinberg::MemoryStream();
stream->setSize ((TSize) mem.getSize());
mem.copyTo (stream->getData(), 0, mem.getSize());
}
}
return stream;
@@ -1829,21 +2132,21 @@ private:
JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS
Steinberg::int32 PLUGIN_API getParameterCount() { return 0; }
Steinberg::int32 PLUGIN_API getParameterCount() override { return 0; }
Vst::IParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32)
Vst::IParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32) override
{
return nullptr;
}
Vst::IParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID&, Steinberg::int32& index)
Vst::IParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID&, Steinberg::int32& index) override
{
index = 0;
return nullptr;
}
private:
Atomic<int32> refCount;
Atomic<int> refCount;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterChangeList)
};
@@ -1851,7 +2154,7 @@ private:
ComSmartPtr<ParameterChangeList> inputParameterChanges, outputParameterChanges;
ComSmartPtr<MidiEventList> midiInputs, midiOutputs;
Vst::ProcessContext timingInfo; //< Only use this in processBlock()!
bool isComponentInitialised, isControllerInitialised;
bool isComponentInitialised, isControllerInitialised, isActive;
//==============================================================================
bool fetchComponentAndController (IPluginFactory* factory, const Steinberg::int32 numClasses)
@@ -2031,11 +2334,11 @@ private:
Vst::BusInfo getBusInfo (bool forInput, bool forAudio, int index = 0) const
{
Vst::BusInfo busInfo;
busInfo.mediaType = forAudio ? Vst::kAudio : Vst::kEvent;
busInfo.direction = forInput ? Vst::kInput : Vst::kOutput;
component->getBusInfo (forAudio ? Vst::kAudio : Vst::kEvent,
forInput ? Vst::kInput : Vst::kOutput,
component->getBusInfo (busInfo.mediaType, busInfo.direction,
(Steinberg::int32) index, busInfo);
return busInfo;
}
@@ -2056,11 +2359,8 @@ private:
{
using namespace VST3BufferExchange;
mapBufferToBusses (inputBusses, *processor, inputBusMap,
true, numInputAudioBusses, buffer);
mapBufferToBusses (outputBusses, *processor, outputBusMap,
false, numOutputAudioBusses, buffer);
mapBufferToBusses (inputBusses, inputBusMap, inputArrangements, buffer);
mapBufferToBusses (outputBusses, outputBusMap, outputArrangements, buffer);
destination.inputs = inputBusses.getRawDataPointer();
destination.outputs = outputBusses.getRawDataPointer();
@@ -29,13 +29,6 @@
#include "../../juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h"
#endif
#if JUCE_MAC
static bool makeFSRefFromPath (FSRef* destFSRef, const String& path)
{
return FSPathMakeRef (reinterpret_cast<const UInt8*> (path.toRawUTF8()), destFSRef, 0) == noErr;
}
#endif
//==============================================================================
#undef PRAGMA_ALIGN_SUPPORTED
#define VST_FORCE_DEPRECATED 0
@@ -84,74 +77,68 @@
#endif
//==============================================================================
const int fxbVersionNum = 1;
struct fxProgram
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCk'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numParams;
char prgName[28];
float params[1]; // variable no. of parameters
};
struct fxSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxBk'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char future[128];
fxProgram programs[1]; // variable no. of programs
};
struct fxChunkSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char future[128];
VstInt32 chunkSize;
char chunk[8]; // variable
};
struct fxProgramSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char name[28];
VstInt32 chunkSize;
char chunk[8]; // variable
};
namespace
{
VstInt32 vst_swap (const VstInt32 x) noexcept
{
#ifdef JUCE_LITTLE_ENDIAN
return (VstInt32) ByteOrder::swap ((uint32) x);
#else
return x;
#endif
}
const int fxbVersionNum = 1;
float vst_swapFloat (const float x) noexcept
struct fxProgram
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCk'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numParams;
char prgName[28];
float params[1]; // variable no. of parameters
};
struct fxSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxBk'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char future[128];
fxProgram programs[1]; // variable no. of programs
};
struct fxChunkSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char future[128];
VstInt32 chunkSize;
char chunk[8]; // variable
};
struct fxProgramSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char name[28];
VstInt32 chunkSize;
char chunk[8]; // variable
};
static VstInt32 fxbName (const char* name) noexcept { return (VstInt32) ByteOrder::bigEndianInt (name); }
static VstInt32 fxbSwap (const VstInt32 x) noexcept { return (VstInt32) ByteOrder::swapIfLittleEndian ((uint32) x); }
static float fxbSwapFloat (const float x) noexcept
{
#ifdef JUCE_LITTLE_ENDIAN
union { uint32 asInt; float asFloat; } n;
@@ -162,8 +149,12 @@ namespace
return x;
#endif
}
}
double getVSTHostTimeNanoseconds()
//==============================================================================
namespace
{
static double getVSTHostTimeNanoseconds() noexcept
{
#if JUCE_WINDOWS
return timeGetTime() * 1000000.0;
@@ -177,40 +168,52 @@ namespace
return micro.lo * 1000.0;
#endif
}
static int shellUIDToCreate = 0;
static int insideVSTCallback = 0;
struct IdleCallRecursionPreventer
{
IdleCallRecursionPreventer() : isMessageThread (MessageManager::getInstance()->isThisTheMessageThread())
{
if (isMessageThread)
++insideVSTCallback;
}
~IdleCallRecursionPreventer()
{
if (isMessageThread)
--insideVSTCallback;
}
const bool isMessageThread;
JUCE_DECLARE_NON_COPYABLE (IdleCallRecursionPreventer)
};
#if JUCE_MAC
static bool makeFSRefFromPath (FSRef* destFSRef, const String& path)
{
return FSPathMakeRef (reinterpret_cast<const UInt8*> (path.toRawUTF8()), destFSRef, 0) == noErr;
}
#endif
#if JUCE_MAC && JUCE_PPC
static void* newCFMFromMachO (void* const machofp) noexcept
{
void* result = (void*) new char[8];
((void**) result)[0] = machofp;
((void**) result)[1] = result;
return result;
}
#endif
}
//==============================================================================
typedef AEffect* (VSTCALLBACK *MainCall) (audioMasterCallback);
static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt);
static int shellUIDToCreate = 0;
static int insideVSTCallback = 0;
class IdleCallRecursionPreventer
{
public:
IdleCallRecursionPreventer()
: isMessageThread (MessageManager::getInstance()->isThisTheMessageThread())
{
if (isMessageThread)
++insideVSTCallback;
}
~IdleCallRecursionPreventer()
{
if (isMessageThread)
--insideVSTCallback;
}
private:
const bool isMessageThread;
JUCE_DECLARE_NON_COPYABLE (IdleCallRecursionPreventer)
};
class VSTPluginWindow;
//==============================================================================
// Change this to disable logging of various VST activities
#ifndef VST_LOGGING
@@ -223,27 +226,12 @@ class VSTPluginWindow;
#define JUCE_VST_LOG(a)
#endif
//==============================================================================
#if JUCE_MAC && JUCE_PPC
static void* NewCFMFromMachO (void* const machofp) noexcept
{
void* result = (void*) new char[8];
((void**) result)[0] = machofp;
((void**) result)[1] = result;
return result;
}
#endif
//==============================================================================
#if JUCE_LINUX
extern Display* display;
extern XContext windowHandleXContext;
typedef void (*EventProcPtr) (XEvent* ev);
namespace
{
static bool xErrorTriggered = false;
@@ -254,6 +242,8 @@ namespace
return 0;
}
typedef void (*EventProcPtr) (XEvent* ev);
static EventProcPtr getPropertyFromXWindow (Window handle, Atom atom)
{
XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler);
@@ -269,8 +259,7 @@ namespace
XSetErrorHandler (oldErrorHandler);
return (userCount == 1 && ! xErrorTriggered) ? *reinterpret_cast<EventProcPtr*> (data)
: 0;
return (userCount == 1 && ! xErrorTriggered) ? *reinterpret_cast<EventProcPtr*> (data) : nullptr;
}
Window getChildWindow (Window windowToCheck)
@@ -279,12 +268,7 @@ namespace
Window* childWindows;
unsigned int numChildren = 0;
XQueryTree (display,
windowToCheck,
&rootWindow,
&parentWindow,
&childWindows,
&numChildren);
XQueryTree (display, windowToCheck, &rootWindow, &parentWindow, &childWindows, &numChildren);
if (numChildren > 0)
return childWindows [0];
@@ -354,9 +338,9 @@ public:
typedef ReferenceCountedObjectPtr<ModuleHandle> Ptr;
static Array <ModuleHandle*>& getActiveModules()
static Array<ModuleHandle*>& getActiveModules()
{
static Array <ModuleHandle*> activeModules;
static Array<ModuleHandle*> activeModules;
return activeModules;
}
@@ -470,7 +454,7 @@ public:
{
if (HGLOBAL hGlob = LoadResource (dllModule, res))
{
const char* data = static_cast <const char*> (LockResource (hGlob));
const char* data = static_cast<const char*> (LockResource (hGlob));
return String::fromUTF8 (data, SizeofResource (dllModule, res));
}
}
@@ -552,7 +536,7 @@ public:
}
}
}
#if JUCE_PPC
#if JUCE_PPC
else
{
FSRef fn;
@@ -605,7 +589,7 @@ public:
}
}
}
#endif
#endif
return ok;
}
@@ -683,7 +667,7 @@ public:
static void disposeMachOFromCFM (void* ptr)
{
delete[] static_cast <UInt32*> (ptr);
delete[] static_cast<UInt32*> (ptr);
}
void coerceAEffectFunctionCalls (AEffect* eff)
@@ -715,11 +699,11 @@ class VSTPluginInstance : public AudioPluginInstance,
private AsyncUpdater
{
public:
VSTPluginInstance (const ModuleHandle::Ptr& module_)
VSTPluginInstance (const ModuleHandle::Ptr& mh)
: effect (nullptr),
module (module_),
module (mh),
usesCocoaNSView (false),
name (module_->pluginName),
name (mh->pluginName),
wantsMidiMessages (false),
initialised (false),
isPowerOn (false),
@@ -741,7 +725,7 @@ public:
{
static void* audioMasterCoerced = nullptr;
if (audioMasterCoerced == nullptr)
audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster);
audioMasterCoerced = newCFMFromMachO ((void*) &audioMaster);
effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced);
}
@@ -803,7 +787,7 @@ public:
char buffer [512] = { 0 };
dispatch (effGetEffectName, 0, 0, buffer, 0);
desc.descriptiveName = String (buffer).trim();
desc.descriptiveName = String::fromUTF8 (buffer).trim();
if (desc.descriptiveName.isEmpty())
desc.descriptiveName = name;
@@ -818,7 +802,7 @@ public:
{
char buffer [kVstMaxVendorStrLen + 8] = { 0 };
dispatch (effGetVendorString, 0, 0, buffer, 0);
desc.manufacturerName = buffer;
desc.manufacturerName = String::fromUTF8 (buffer);
}
desc.version = getVersion();
@@ -1146,8 +1130,7 @@ public:
bool isValidChannel (int index, bool isInput) const
{
return isInput ? (index < getNumInputChannels())
: (index < getNumOutputChannels());
return isPositiveAndBelow (index, isInput ? getNumInputChannels() : getNumOutputChannels());
}
//==============================================================================
@@ -1214,7 +1197,7 @@ public:
char nm[264] = { 0 };
if (dispatch (effGetProgramNameIndexed, jlimit (0, getNumPrograms(), index), -1, nm, 0) != 0)
return String (CharPointer_UTF8 (nm)).trim();
return String::fromUTF8 (nm).trim();
}
}
@@ -1285,7 +1268,8 @@ public:
handleUpdateNowIfNeeded();
for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
ComponentPeer::getPeer(i)->performAnyPendingRepaintsNow();
if (ComponentPeer* p = ComponentPeer::getPeer(i))
p->performAnyPendingRepaintsNow();
}
break;
@@ -1448,20 +1432,20 @@ public:
const fxSet* const set = (const fxSet*) data;
if ((vst_swap (set->chunkMagic) != 'CcnK' && vst_swap (set->chunkMagic) != 'KncC')
|| vst_swap (set->version) > fxbVersionNum)
if ((set->chunkMagic != fxbName ("CcnK") && set->chunkMagic != fxbName ("KncC"))
|| fxbSwap (set->version) > fxbVersionNum)
return false;
if (vst_swap (set->fxMagic) == 'FxBk')
if (set->fxMagic == fxbName ("FxBk"))
{
// bank of programs
if (vst_swap (set->numPrograms) >= 0)
if (fxbSwap (set->numPrograms) >= 0)
{
const int oldProg = getCurrentProgram();
const int numParams = vst_swap (((const fxProgram*) (set->programs))->numParams);
const int numParams = fxbSwap (((const fxProgram*) (set->programs))->numParams);
const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float);
for (int i = 0; i < vst_swap (set->numPrograms); ++i)
for (int i = 0; i < fxbSwap (set->numPrograms); ++i)
{
if (i != oldProg)
{
@@ -1469,7 +1453,7 @@ public:
if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize)
return false;
if (vst_swap (set->numPrograms) > 0)
if (fxbSwap (set->numPrograms) > 0)
setCurrentProgram (i);
if (! restoreProgramSettings (prog))
@@ -1477,7 +1461,7 @@ public:
}
}
if (vst_swap (set->numPrograms) > 0)
if (fxbSwap (set->numPrograms) > 0)
setCurrentProgram (oldProg);
const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen);
@@ -1488,38 +1472,38 @@ public:
return false;
}
}
else if (vst_swap (set->fxMagic) == 'FxCk')
else if (set->fxMagic == fxbName ("FxCk"))
{
// single program
const fxProgram* const prog = (const fxProgram*) data;
if (vst_swap (prog->chunkMagic) != 'CcnK')
if (prog->chunkMagic != fxbName ("CcnK"))
return false;
changeProgramName (getCurrentProgram(), prog->prgName);
for (int i = 0; i < vst_swap (prog->numParams); ++i)
setParameter (i, vst_swapFloat (prog->params[i]));
for (int i = 0; i < fxbSwap (prog->numParams); ++i)
setParameter (i, fxbSwapFloat (prog->params[i]));
}
else if (vst_swap (set->fxMagic) == 'FBCh' || vst_swap (set->fxMagic) == 'hCBF')
else if (set->fxMagic == fxbName ("FBCh") || set->fxMagic == fxbName ("hCBF"))
{
// non-preset chunk
const fxChunkSet* const cset = (const fxChunkSet*) data;
if (vst_swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize)
if (fxbSwap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize)
return false;
setChunkData (cset->chunk, vst_swap (cset->chunkSize), false);
setChunkData (cset->chunk, fxbSwap (cset->chunkSize), false);
}
else if (vst_swap (set->fxMagic) == 'FPCh' || vst_swap (set->fxMagic) == 'hCPF')
else if (set->fxMagic == fxbName ("FPCh") || set->fxMagic == fxbName ("hCPF"))
{
// preset chunk
const fxProgramSet* const cset = (const fxProgramSet*) data;
if (vst_swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize)
if (fxbSwap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize)
return false;
setChunkData (cset->chunk, vst_swap (cset->chunkSize), true);
setChunkData (cset->chunk, fxbSwap (cset->chunkSize), true);
changeProgramName (getCurrentProgram(), cset->name);
}
@@ -1547,14 +1531,14 @@ public:
dest.setSize (totalLen, true);
fxChunkSet* const set = (fxChunkSet*) dest.getData();
set->chunkMagic = vst_swap ('CcnK');
set->chunkMagic = fxbName ("CcnK");
set->byteSize = 0;
set->fxMagic = vst_swap ('FBCh');
set->version = vst_swap (fxbVersionNum);
set->fxID = vst_swap (getUID());
set->fxVersion = vst_swap (getVersionNumber());
set->numPrograms = vst_swap (numPrograms);
set->chunkSize = vst_swap ((VstInt32) chunk.getSize());
set->fxMagic = fxbName ("FBCh");
set->version = fxbSwap (fxbVersionNum);
set->fxID = fxbSwap (getUID());
set->fxVersion = fxbSwap (getVersionNumber());
set->numPrograms = fxbSwap (numPrograms);
set->chunkSize = fxbSwap ((VstInt32) chunk.getSize());
chunk.copyTo (set->chunk, 0, chunk.getSize());
}
@@ -1564,14 +1548,14 @@ public:
dest.setSize (totalLen, true);
fxProgramSet* const set = (fxProgramSet*) dest.getData();
set->chunkMagic = vst_swap ('CcnK');
set->chunkMagic = fxbName ("CcnK");
set->byteSize = 0;
set->fxMagic = vst_swap ('FPCh');
set->version = vst_swap (fxbVersionNum);
set->fxID = vst_swap (getUID());
set->fxVersion = vst_swap (getVersionNumber());
set->numPrograms = vst_swap (numPrograms);
set->chunkSize = vst_swap ((VstInt32) chunk.getSize());
set->fxMagic = fxbName ("FPCh");
set->version = fxbSwap (fxbVersionNum);
set->fxID = fxbSwap (getUID());
set->fxVersion = fxbSwap (getVersionNumber());
set->numPrograms = fxbSwap (numPrograms);
set->chunkSize = fxbSwap ((VstInt32) chunk.getSize());
getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1);
chunk.copyTo (set->chunk, 0, chunk.getSize());
@@ -1586,13 +1570,13 @@ public:
dest.setSize (len, true);
fxSet* const set = (fxSet*) dest.getData();
set->chunkMagic = vst_swap ('CcnK');
set->chunkMagic = fxbName ("CcnK");
set->byteSize = 0;
set->fxMagic = vst_swap ('FxBk');
set->version = vst_swap (fxbVersionNum);
set->fxID = vst_swap (getUID());
set->fxVersion = vst_swap (getVersionNumber());
set->numPrograms = vst_swap (numPrograms);
set->fxMagic = fxbName ("FxBk");
set->version = fxbSwap (fxbVersionNum);
set->fxID = fxbSwap (getUID());
set->fxVersion = fxbSwap (getVersionNumber());
set->numPrograms = fxbSwap (numPrograms);
const int oldProgram = getCurrentProgram();
MemoryBlock oldSettings;
@@ -1687,12 +1671,12 @@ private:
bool restoreProgramSettings (const fxProgram* const prog)
{
if (vst_swap (prog->chunkMagic) == 'CcnK' && vst_swap (prog->fxMagic) == 'FxCk')
if (prog->chunkMagic == fxbName ("CcnK") && prog->fxMagic == fxbName ("FxCk"))
{
changeProgramName (getCurrentProgram(), prog->prgName);
for (int i = 0; i < vst_swap (prog->numParams); ++i)
setParameter (i, vst_swapFloat (prog->params[i]));
for (int i = 0; i < fxbSwap (prog->numParams); ++i)
setParameter (i, fxbSwapFloat (prog->params[i]));
return true;
}
@@ -1741,18 +1725,18 @@ private:
{
const int numParams = getNumParameters();
prog->chunkMagic = vst_swap ('CcnK');
prog->chunkMagic = fxbName ("CcnK");
prog->byteSize = 0;
prog->fxMagic = vst_swap ('FxCk');
prog->version = vst_swap (fxbVersionNum);
prog->fxID = vst_swap (getUID());
prog->fxVersion = vst_swap (getVersionNumber());
prog->numParams = vst_swap (numParams);
prog->fxMagic = fxbName ("FxCk");
prog->version = fxbSwap (fxbVersionNum);
prog->fxID = fxbSwap (getUID());
prog->fxVersion = fxbSwap (getVersionNumber());
prog->numParams = fxbSwap (numParams);
getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1);
for (int i = 0; i < numParams; ++i)
prog->params[i] = vst_swapFloat (getParameter (i));
prog->params[i] = fxbSwapFloat (getParameter (i));
}
void updateStoredProgramNames()
@@ -1894,7 +1878,8 @@ private:
};
//==============================================================================
static Array <VSTPluginWindow*> activeVSTWindows;
class VSTPluginWindow;
static Array<VSTPluginWindow*> activeVSTWindows;
//==============================================================================
class VSTPluginWindow : public AudioProcessorEditor,
@@ -2232,10 +2217,11 @@ private:
#pragma warning (push)
#pragma warning (disable: 4244)
originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWLP_WNDPROC);
if (! pluginWantsKeys)
{
originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWLP_WNDPROC);
SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) vstHookWndProc);
}
#pragma warning (pop)
@@ -2317,26 +2303,24 @@ private:
{
if (isOpen)
{
#if ! (JUCE_MAC && JUCE_SUPPORT_CARBON)
JUCE_VST_LOG ("Closing VST UI: " + plugin.getName());
isOpen = false;
dispatch (effEditClose, 0, 0, 0, 0);
stopTimer();
#if JUCE_WINDOWS
#pragma warning (push)
#pragma warning (disable: 4244)
if (pluginHWND != 0 && IsWindow (pluginHWND))
if (originalWndProc != 0 && pluginHWND != 0 && IsWindow (pluginHWND))
SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
#pragma warning (pop)
originalWndProc = 0;
pluginHWND = 0;
#elif JUCE_LINUX
pluginWindow = 0;
pluginProc = 0;
#endif
#endif
}
}
@@ -2845,7 +2829,7 @@ FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch()
const XmlElement* VSTPluginFormat::getVSTXML (AudioPluginInstance* plugin)
{
if (VSTPluginInstance* const vst = dynamic_cast <VSTPluginInstance*> (plugin))
if (VSTPluginInstance* const vst = dynamic_cast<VSTPluginInstance*> (plugin))
if (vst->module != nullptr)
return vst->module->vstXml.get();
@@ -2854,7 +2838,7 @@ const XmlElement* VSTPluginFormat::getVSTXML (AudioPluginInstance* plugin)
bool VSTPluginFormat::loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize)
{
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin))
if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->loadFromFXBFile (data, dataSize);
return false;
@@ -2862,7 +2846,7 @@ bool VSTPluginFormat::loadFromFXBFile (AudioPluginInstance* plugin, const void*
bool VSTPluginFormat::saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& dest, bool asFXB)
{
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin))
if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->saveToFXBFile (dest, asFXB);
return false;
@@ -2870,7 +2854,7 @@ bool VSTPluginFormat::saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& d
bool VSTPluginFormat::getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset)
{
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin))
if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->getChunkData (result, isPreset, 128);
return false;
@@ -2878,7 +2862,7 @@ bool VSTPluginFormat::getChunkData (AudioPluginInstance* plugin, MemoryBlock& re
bool VSTPluginFormat::setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset)
{
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin))
if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->setChunkData (data, size, isPreset);
return false;
@@ -2888,13 +2872,13 @@ void VSTPluginFormat::setExtraFunctions (AudioPluginInstance* plugin, ExtraFunct
{
ScopedPointer<ExtraFunctions> f (functions);
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin))
if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
vst->extraFunctions = f;
}
VSTPluginFormat::VstIntPtr JUCE_CALLTYPE VSTPluginFormat::dispatcher (AudioPluginInstance* plugin, int32 opcode, int32 index, VstIntPtr value, void* ptr, float opt)
{
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin))
if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->dispatch (opcode, index, value, ptr, opt);
return 0;
@@ -41,8 +41,9 @@
//==============================================================================
#if JUCE_MAC
#if (JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) \
|| ! (defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
#if JUCE_SUPPORT_CARBON \
&& ((JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) \
|| ! (defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6))
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers)
#define Component CarbonDummyCompName
#include <Carbon/Carbon.h>
@@ -112,27 +113,24 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone
setView (v);
[v release];
startTimer (100);
startTimer (30);
}
NSView* getChildView() const
{
if (NSView* parent = (NSView*) getView())
if ([[parent subviews] count] > 0)
return [[parent subviews] objectAtIndex: 0];
return nil;
}
void timerCallback() override
{
if (NSView* parent = (NSView*) getView())
if (NSView* child = getChildView())
{
if ([[parent subviews] count] > 0)
{
if (NSView* child = [[parent subviews] objectAtIndex: 0])
{
NSRect f = [parent frame];
NSSize newSize = [child frame].size;
if (f.size.width != newSize.width || f.size.height != newSize.height)
{
f.size = newSize;
[parent setFrame: f];
}
}
}
stopTimer();
setView (child);
}
}
};
@@ -76,6 +76,7 @@ class AudioProcessor;
#include "processors/juce_AudioPlayHead.h"
#include "processors/juce_AudioProcessorEditor.h"
#include "processors/juce_AudioProcessorListener.h"
#include "processors/juce_AudioProcessorParameter.h"
#include "processors/juce_AudioProcessor.h"
#include "processors/juce_PluginDescription.h"
#include "processors/juce_AudioPluginInstance.h"
@@ -1,7 +1,7 @@
{
"id": "juce_audio_processors",
"name": "JUCE audio plugin hosting classes",
"version": "3.0.5",
"version": "3.0.8",
"description": "Classes for loading and playing VST, AU, or internally-generated audio processors.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
@@ -118,19 +118,6 @@ void AudioProcessor::setParameterNotifyingHost (const int parameterIndex,
sendParamChangeMessageToListeners (parameterIndex, newValue);
}
String AudioProcessor::getParameterName (int parameterIndex, int maximumStringLength)
{
return getParameterName (parameterIndex).substring (0, maximumStringLength);
}
String AudioProcessor::getParameterText (int parameterIndex, int maximumStringLength)
{
return getParameterText (parameterIndex).substring (0, maximumStringLength);
}
int AudioProcessor::getParameterNumSteps (int /*parameterIndex*/) { return 0x7fffffff; }
float AudioProcessor::getParameterDefaultValue (int /*parameterIndex*/) { return 0.0f; }
AudioProcessorListener* AudioProcessor::getListenerLocked (const int index) const noexcept
{
const ScopedLock sl (listenerLock);
@@ -201,9 +188,129 @@ void AudioProcessor::updateHostDisplay()
l->audioProcessorChanged (this);
}
String AudioProcessor::getParameterLabel (int) const { return String(); }
bool AudioProcessor::isParameterAutomatable (int) const { return true; }
bool AudioProcessor::isMetaParameter (int) const { return false; }
const OwnedArray<AudioProcessorParameter>& AudioProcessor::getParameters() const noexcept
{
return managedParameters;
}
int AudioProcessor::getNumParameters()
{
return managedParameters.size();
}
float AudioProcessor::getParameter (int index)
{
if (AudioProcessorParameter* p = getParamChecked (index))
return p->getValue();
return 0;
}
void AudioProcessor::setParameter (int index, float newValue)
{
if (AudioProcessorParameter* p = getParamChecked (index))
p->setValue (newValue);
}
float AudioProcessor::getParameterDefaultValue (int index)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getDefaultValue();
return 0;
}
const String AudioProcessor::getParameterName (int index)
{
if (AudioProcessorParameter* p = getParamChecked (index))
return p->getName (512);
return String();
}
String AudioProcessor::getParameterName (int index, int maximumStringLength)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getName (maximumStringLength);
return getParameterName (index).substring (0, maximumStringLength);
}
const String AudioProcessor::getParameterText (int index)
{
return getParameterText (index, 1024);
}
String AudioProcessor::getParameterText (int index, int maximumStringLength)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getText (p->getValue(), maximumStringLength);
return getParameterText (index).substring (0, maximumStringLength);
}
int AudioProcessor::getParameterNumSteps (int index)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getNumSteps();
return AudioProcessor::getDefaultNumParameterSteps();
}
int AudioProcessor::getDefaultNumParameterSteps() noexcept
{
return 0x7fffffff;
}
String AudioProcessor::getParameterLabel (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getLabel();
return String();
}
bool AudioProcessor::isParameterAutomatable (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->isAutomatable();
return true;
}
bool AudioProcessor::isParameterOrientationInverted (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->isOrientationInverted();
return false;
}
bool AudioProcessor::isMetaParameter (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->isMetaParameter();
return false;
}
AudioProcessorParameter* AudioProcessor::getParamChecked (int index) const noexcept
{
AudioProcessorParameter* p = managedParameters[index];
// If you hit this, then you're either trying to access parameters that are out-of-range,
// or you're not using addParameter and the managed parameter list, but have failed
// to override some essential virtual methods and implement them appropriately.
jassert (p != nullptr);
return p;
}
void AudioProcessor::addParameter (AudioProcessorParameter* p)
{
p->processor = this;
p->parameterIndex = managedParameters.size();
managedParameters.add (p);
}
void AudioProcessor::suspendProcessing (const bool shouldBeSuspended)
{
@@ -230,9 +337,6 @@ AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
AudioProcessorEditor* const ed = createEditor();
// You must make your hasEditor() method return a consistent result!
jassert (hasEditor() == (ed != nullptr));
if (ed != nullptr)
{
// you must give your editor comp a size before returning it..
@@ -242,6 +346,9 @@ AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
activeEditor = ed;
}
// You must make your hasEditor() method return a consistent result!
jassert (hasEditor() == (ed != nullptr));
return ed;
}
@@ -294,6 +401,47 @@ XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeIn
void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {}
void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {}
//==============================================================================
AudioProcessorParameter::AudioProcessorParameter() noexcept
: processor (nullptr), parameterIndex (-1)
{}
AudioProcessorParameter::~AudioProcessorParameter() {}
void AudioProcessorParameter::setValueNotifyingHost (float newValue)
{
// This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0);
return processor->setParameterNotifyingHost (parameterIndex, newValue);
}
void AudioProcessorParameter::beginChangeGesture()
{
// This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0);
processor->beginParameterChangeGesture (parameterIndex);
}
void AudioProcessorParameter::endChangeGesture()
{
// This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0);
processor->endParameterChangeGesture (parameterIndex);
}
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
bool AudioProcessorParameter::isAutomatable() const { return true; }
bool AudioProcessorParameter::isMetaParameter() const { return false; }
int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); }
String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const
{
return String (value, 2);
}
//==============================================================================
bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept
{
@@ -44,11 +44,7 @@ class JUCE_API AudioProcessor
{
protected:
//==============================================================================
/** Constructor.
You can also do your initialisation tasks in the initialiseFilterInfo()
call, which will be made after this object has been created.
*/
/** Constructor. */
AudioProcessor();
public:
@@ -293,9 +289,9 @@ public:
filter will return an empty buffer, but won't block the audio thread like it would
do if you use the getCallbackLock() critical section to synchronise access.
If you're going to use this, your processBlock() method must call isSuspended() and
check whether it's suspended or not. If it is, then it should skip doing any real
processing, either emitting silence or passing the input through unchanged.
Any code that calls processBlock() should call isSuspended() before doing so, and
if the processor is suspended, it should avoid the call and emit silence or
whatever is appropriate.
@see getCallbackLock
*/
@@ -329,7 +325,7 @@ public:
/** Called by the host to tell this processor whether it's being used in a non-realtime
capacity for offline rendering or bouncing.
*/
void setNonRealtime (bool isNonRealtime) noexcept;
virtual void setNonRealtime (bool isNonRealtime) noexcept;
//==============================================================================
/** Creates the filter's UI.
@@ -381,10 +377,10 @@ public:
/** This must return the correct value immediately after the object has been
created, and mustn't change the number of parameters later.
*/
virtual int getNumParameters() = 0;
virtual int getNumParameters();
/** Returns the name of a particular parameter. */
virtual const String getParameterName (int parameterIndex) = 0;
virtual const String getParameterName (int parameterIndex);
/** Called by the host to find out the value of one of the filter's parameters.
@@ -394,10 +390,10 @@ public:
It's also likely to be called by non-UI threads, so the code in here should
be thread-aware.
*/
virtual float getParameter (int parameterIndex) = 0;
virtual float getParameter (int parameterIndex);
/** Returns the value of a parameter as a text string. */
virtual const String getParameterText (int parameterIndex) = 0;
virtual const String getParameterText (int parameterIndex);
/** Returns the name of a parameter as a text string with a preferred maximum length.
If you want to provide customised short versions of your parameter names that
@@ -418,12 +414,18 @@ public:
virtual String getParameterText (int parameterIndex, int maximumStringLength);
/** Returns the number of discrete steps that this parameter can represent.
The default return value if you don't implement this method is 0x7fffffff.
The default return value if you don't implement this method is
AudioProcessor::getDefaultNumParameterSteps().
If your parameter is boolean, then you may want to make this return 2.
The value that is returned may or may not be used, depending on the host.
*/
virtual int getParameterNumSteps (int parameterIndex);
/** Returns the default number of steps for a parameter.
@see getParameterNumSteps
*/
static int getDefaultNumParameterSteps() noexcept;
/** Returns the default value for the parameter.
By default, this just returns 0.
The value that is returned may or may not be used, depending on the host.
@@ -435,6 +437,11 @@ public:
*/
virtual String getParameterLabel (int index) const;
/** This can be overridden to tell the host that particular parameters operate in the
reverse direction. (Not all plugin formats or hosts will actually use this information).
*/
virtual bool isParameterOrientationInverted (int index) const;
/** The host will call this method to change the value of one of the filter's parameters.
The host may call this at any time, including during the audio processing
@@ -448,7 +455,7 @@ public:
The value passed will be between 0 and 1.0.
*/
virtual void setParameter (int parameterIndex, float newValue) = 0;
virtual void setParameter (int parameterIndex, float newValue);
/** Your filter can call this when it needs to change one of its parameters.
@@ -463,16 +470,13 @@ public:
void setParameterNotifyingHost (int parameterIndex, float newValue);
/** Returns true if the host can automate this parameter.
By default, this returns true for all parameters.
*/
virtual bool isParameterAutomatable (int parameterIndex) const;
/** Should return true if this parameter is a "meta" parameter.
A meta-parameter is a parameter that changes other params. It is used
by some hosts (e.g. AudioUnit hosts).
By default this returns false.
*/
virtual bool isMetaParameter (int parameterIndex) const;
@@ -503,6 +507,16 @@ public:
*/
void updateHostDisplay();
//==============================================================================
/** Adds a parameter to the list.
The parameter object will be managed and deleted automatically by the list
when no longer needed.
*/
void addParameter (AudioProcessorParameter*);
/** Returns the current list of parameters. */
const OwnedArray<AudioProcessorParameter>& getParameters() const noexcept;
//==============================================================================
/** Returns the number of preset programs the filter supports.
@@ -659,6 +673,9 @@ private:
CriticalSection callbackLock, listenerLock;
String inputSpeakerArrangement, outputSpeakerArrangement;
OwnedArray<AudioProcessorParameter> managedParameters;
AudioProcessorParameter* getParamChecked (int) const noexcept;
#if JUCE_DEBUG
BigInteger changingParams;
#endif
@@ -22,16 +22,22 @@
==============================================================================
*/
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* const p)
: owner (p)
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p)
{
}
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p)
{
// the filter must be valid..
jassert (owner != nullptr);
jassert (p != nullptr);
}
AudioProcessorEditor::~AudioProcessorEditor()
{
// if this fails, then the wrapper hasn't called editorBeingDeleted() on the
// filter for some reason..
jassert (owner->getActiveEditor() != this);
jassert (processor.getActiveEditor() != this);
}
void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {}
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }
@@ -39,9 +39,11 @@ class JUCE_API AudioProcessorEditor : public Component
{
protected:
//==============================================================================
/** Creates an editor for the specified processor.
*/
AudioProcessorEditor (AudioProcessor* owner);
/** Creates an editor for the specified processor. */
AudioProcessorEditor (AudioProcessor&) noexcept;
/** Creates an editor for the specified processor. */
AudioProcessorEditor (AudioProcessor*) noexcept;
public:
/** Destructor. */
@@ -49,14 +51,40 @@ public:
//==============================================================================
/** Returns a pointer to the processor that this editor represents. */
AudioProcessor* getAudioProcessor() const noexcept { return owner; }
/** The AudioProcessor that this editor represents. */
AudioProcessor& processor;
/** Returns a pointer to the processor that this editor represents.
This method is here to support legacy code, but it's easier to just use the
AudioProcessorEditor::processor member variable directly to get this object.
*/
AudioProcessor* getAudioProcessor() const noexcept { return &processor; }
//==============================================================================
/** Used by the setParameterHighlighting() method. */
struct ParameterControlHighlightInfo
{
int parameterIndex;
bool isHighlighted;
Colour suggestedColour;
};
/** Some types of plugin can call this to suggest that the control for a particular
parameter should be highlighted.
Currently only AAX plugins will call this, and implementing it is optional.
*/
virtual void setControlHighlight (ParameterControlHighlightInfo);
/** Called by certain plug-in wrappers to find out whether a component is used
to control a parameter.
If the given component represents a particular plugin parameter, then this
method should return the index of that parameter. If not, it should return -1.
Currently only AAX plugins will call this, and implementing it is optional.
*/
virtual int getControlParameterIndex (Component&);
private:
//==============================================================================
AudioProcessor* const owner;
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor)
};
@@ -388,11 +388,11 @@ public:
private:
//==============================================================================
ReferenceCountedArray <Node> nodes;
OwnedArray <Connection> connections;
ReferenceCountedArray<Node> nodes;
OwnedArray<Connection> connections;
uint32 lastNodeId;
AudioSampleBuffer renderingBuffers;
OwnedArray <MidiBuffer> midiBuffers;
OwnedArray<MidiBuffer> midiBuffers;
Array<void*> renderingOps;
friend class AudioGraphIOProcessor;
@@ -27,12 +27,12 @@ class ProcessorParameterPropertyComp : public PropertyComponent,
private Timer
{
public:
ProcessorParameterPropertyComp (const String& name, AudioProcessor& p, const int index_)
ProcessorParameterPropertyComp (const String& name, AudioProcessor& p, int paramIndex)
: PropertyComponent (name),
owner (p),
index (index_),
index (paramIndex),
paramHasChanged (false),
slider (p, index_)
slider (p, paramIndex)
{
startTimer (100);
addAndMakeVisible (slider);
@@ -44,15 +44,19 @@ public:
owner.removeListener (this);
}
void refresh()
void refresh() override
{
paramHasChanged = false;
slider.setValue (owner.getParameter (index), dontSendNotification);
if (slider.getThumbBeingDragged() < 0)
slider.setValue (owner.getParameter (index), dontSendNotification);
slider.updateText();
}
void audioProcessorChanged (AudioProcessor*) {}
void audioProcessorChanged (AudioProcessor*) override {}
void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float)
void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) override
{
if (parameterIndex == index)
paramHasChanged = true;
@@ -76,27 +80,34 @@ private:
class ParamSlider : public Slider
{
public:
ParamSlider (AudioProcessor& p, const int index_)
: owner (p),
index (index_)
ParamSlider (AudioProcessor& p, int paramIndex) : owner (p), index (paramIndex)
{
setRange (0.0, 1.0, 0.0);
const int steps = owner.getParameterNumSteps (index);
if (steps > 1 && steps < 0x7fffffff)
setRange (0.0, 1.0, 1.0 / (steps - 1.0));
else
setRange (0.0, 1.0);
setSliderStyle (Slider::LinearBar);
setTextBoxIsEditable (false);
setScrollWheelEnabled (false);
setScrollWheelEnabled (true);
}
void valueChanged()
void valueChanged() override
{
const float newVal = (float) getValue();
if (owner.getParameter (index) != newVal)
{
owner.setParameterNotifyingHost (index, newVal);
updateText();
}
}
String getTextFromValue (double /*value*/)
String getTextFromValue (double /*value*/) override
{
return owner.getParameterText (index);
return owner.getParameterText (index) + " " + owner.getParameterLabel (index).trimEnd();
}
private:
@@ -71,18 +71,26 @@ PluginDescription& PluginDescription::operator= (const PluginDescription& other)
return *this;
}
bool PluginDescription::isDuplicateOf (const PluginDescription& other) const
bool PluginDescription::isDuplicateOf (const PluginDescription& other) const noexcept
{
return fileOrIdentifier == other.fileOrIdentifier
&& uid == other.uid;
}
static String getPluginDescSuffix (const PluginDescription& d)
{
return "-" + String::toHexString (d.fileOrIdentifier.hashCode())
+ "-" + String::toHexString (d.uid);
}
bool PluginDescription::matchesIdentifierString (const String& identifierString) const
{
return identifierString.endsWithIgnoreCase (getPluginDescSuffix (*this));
}
String PluginDescription::createIdentifierString() const
{
return pluginFormatName
+ "-" + name
+ "-" + String::toHexString (fileOrIdentifier.hashCode())
+ "-" + String::toHexString (uid);
return pluginFormatName + "-" + name + getPluginDescSuffix (*this);
}
XmlElement* PluginDescription::createXml() const
@@ -107,7 +107,15 @@ public:
This isn't quite as simple as them just having the same file (because of
shell plug-ins).
*/
bool isDuplicateOf (const PluginDescription& other) const;
bool isDuplicateOf (const PluginDescription& other) const noexcept;
/** Return true if this description is equivalent to another one which created the
given identifier string.
Note that this isn't quite as simple as them just calling createIdentifierString()
and comparing the strings, because the identifers can differ (thanks to shell plug-ins).
*/
bool matchesIdentifierString (const String& identifierString) const;
//==============================================================================
/** Returns a string that can be saved and used to uniquely identify the
@@ -46,7 +46,7 @@ PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifi
PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const
{
for (int i = 0; i < types.size(); ++i)
if (types.getUnchecked(i)->createIdentifierString() == identifierString)
if (types.getUnchecked(i)->matchesIdentifierString (identifierString))
return types.getUnchecked(i);
return nullptr;
@@ -259,15 +259,15 @@ struct PluginSorter
switch (method)
{
case KnownPluginList::sortByCategory: diff = first->category.compareLexicographically (second->category); break;
case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareLexicographically (second->manufacturerName); break;
case KnownPluginList::sortByCategory: diff = first->category.compareNatural (second->category); break;
case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareNatural (second->manufacturerName); break;
case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break;
case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break;
default: break;
}
if (diff == 0)
diff = first->name.compareLexicographically (second->name);
diff = first->name.compareNatural (second->name);
return diff * direction;
}
@@ -75,11 +75,10 @@ class AudioDeviceSelectorComponent::MidiInputSelectorComponentListBox : public
private ListBoxModel
{
public:
MidiInputSelectorComponentListBox (AudioDeviceManager& dm,
const String& noItemsMessage_)
MidiInputSelectorComponentListBox (AudioDeviceManager& dm, const String& noItems)
: ListBox (String::empty, nullptr),
deviceManager (dm),
noItemsMessage (noItemsMessage_)
noItemsMessage (noItems)
{
items = MidiInput::getDevices();
@@ -87,15 +86,12 @@ public:
setOutlineThickness (1);
}
int getNumRows()
int getNumRows() override
{
return items.size();
}
void paintListBoxItem (int row,
Graphics& g,
int width, int height,
bool rowIsSelected)
void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) override
{
if (isPositiveAndBelow (row, items.size()))
{
@@ -118,7 +114,7 @@ public:
}
}
void listBoxItemClicked (int row, const MouseEvent& e)
void listBoxItemClicked (int row, const MouseEvent& e) override
{
selectRow (row);
@@ -126,12 +122,12 @@ public:
flipEnablement (row);
}
void listBoxItemDoubleClicked (int row, const MouseEvent&)
void listBoxItemDoubleClicked (int row, const MouseEvent&) override
{
flipEnablement (row);
}
void returnKeyPressed (int row)
void returnKeyPressed (int row) override
{
flipEnablement (row);
}
@@ -214,7 +210,6 @@ public:
type.scanForDevices();
setup.manager->addChangeListener (this);
updateAllControls();
}
~AudioDeviceSettingsPanel()
@@ -224,83 +219,107 @@ public:
void resized() override
{
const int lx = proportionOfWidth (0.35f);
const int w = proportionOfWidth (0.4f);
const int h = 24;
const int space = 6;
const int dh = h + space;
int y = 0;
if (outputDeviceDropDown != nullptr)
if (AudioDeviceSelectorComponent* parent = findParentComponentOfClass<AudioDeviceSelectorComponent>())
{
outputDeviceDropDown->setBounds (lx, y, w, h);
Rectangle<int> r (proportionOfWidth (0.35f), 0, proportionOfWidth (0.6f), 3000);
if (testButton != nullptr)
testButton->setBounds (proportionOfWidth (0.77f),
outputDeviceDropDown->getY(),
proportionOfWidth (0.18f),
h);
y += dh;
const int maxListBoxHeight = 100;
const int h = parent->getItemHeight();
const int space = h / 4;
if (outputDeviceDropDown != nullptr)
{
Rectangle<int> row (r.removeFromTop (h));
if (testButton != nullptr)
{
testButton->changeWidthToFitText (h);
testButton->setBounds (row.removeFromRight (testButton->getWidth()));
row.removeFromRight (space);
}
outputDeviceDropDown->setBounds (row);
r.removeFromTop (space);
}
if (inputDeviceDropDown != nullptr)
{
Rectangle<int> row (r.removeFromTop (h));
inputLevelMeter->setBounds (row.removeFromRight (testButton != nullptr ? testButton->getWidth() : row.getWidth() / 6));
row.removeFromRight (space);
inputDeviceDropDown->setBounds (row);
r.removeFromTop (space);
}
if (outputChanList != nullptr)
{
outputChanList->setBounds (r.removeFromTop (outputChanList->getBestHeight (maxListBoxHeight)));
outputChanLabel->setBounds (0, outputChanList->getBounds().getCentreY() - h / 2, r.getX(), h);
r.removeFromTop (space);
}
if (inputChanList != nullptr)
{
inputChanList->setBounds (r.removeFromTop (inputChanList->getBestHeight (maxListBoxHeight)));
inputChanLabel->setBounds (0, inputChanList->getBounds().getCentreY() - h / 2, r.getX(), h);
r.removeFromTop (space);
}
r.removeFromTop (space * 2);
if (showAdvancedSettingsButton != nullptr)
{
showAdvancedSettingsButton->setBounds (r.withHeight (h));
showAdvancedSettingsButton->changeWidthToFitText();
}
const bool advancedSettingsVisible = showAdvancedSettingsButton == nullptr
|| ! showAdvancedSettingsButton->isVisible();
if (sampleRateDropDown != nullptr)
{
sampleRateDropDown->setVisible (advancedSettingsVisible);
sampleRateDropDown->setBounds (r.removeFromTop (h));
r.removeFromTop (space);
}
if (bufferSizeDropDown != nullptr)
{
bufferSizeDropDown->setVisible (advancedSettingsVisible);
bufferSizeDropDown->setBounds (r.removeFromTop (h));
r.removeFromTop (space);
}
r.removeFromTop (space);
if (showUIButton != nullptr || resetDeviceButton != nullptr)
{
Rectangle<int> buttons (r.removeFromTop (h));
if (showUIButton != nullptr)
{
showUIButton->setVisible (advancedSettingsVisible);
showUIButton->changeWidthToFitText (h);
showUIButton->setBounds (buttons.removeFromLeft (showUIButton->getWidth()));
buttons.removeFromLeft (space);
}
if (resetDeviceButton != nullptr)
{
resetDeviceButton->setVisible (advancedSettingsVisible);
resetDeviceButton->changeWidthToFitText (h);
resetDeviceButton->setBounds (buttons.removeFromLeft (resetDeviceButton->getWidth()));
}
r.removeFromTop (space);
}
setSize (getWidth(), r.getY());
}
if (inputDeviceDropDown != nullptr)
else
{
inputDeviceDropDown->setBounds (lx, y, w, h);
inputLevelMeter->setBounds (proportionOfWidth (0.77f),
inputDeviceDropDown->getY(),
proportionOfWidth (0.18f),
h);
y += dh;
}
const int maxBoxHeight = 100;
if (outputChanList != nullptr)
{
const int bh = outputChanList->getBestHeight (maxBoxHeight);
outputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh);
y += bh + space;
}
if (inputChanList != nullptr)
{
const int bh = inputChanList->getBestHeight (maxBoxHeight);
inputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh);
y += bh + space;
}
y += space * 2;
if (showAdvancedSettingsButton != nullptr)
{
showAdvancedSettingsButton->changeWidthToFitText (h);
showAdvancedSettingsButton->setTopLeftPosition (lx, y);
}
if (sampleRateDropDown != nullptr)
{
sampleRateDropDown->setVisible (showAdvancedSettingsButton == nullptr
|| ! showAdvancedSettingsButton->isVisible());
sampleRateDropDown->setBounds (lx, y, w, h);
y += dh;
}
if (bufferSizeDropDown != nullptr)
{
bufferSizeDropDown->setVisible (showAdvancedSettingsButton == nullptr
|| ! showAdvancedSettingsButton->isVisible());
bufferSizeDropDown->setBounds (lx, y, w, h);
y += dh;
}
if (showUIButton != nullptr)
{
showUIButton->setVisible (showAdvancedSettingsButton == nullptr
|| ! showAdvancedSettingsButton->isVisible());
showUIButton->changeWidthToFitText (h);
showUIButton->setTopLeftPosition (lx, y);
jassertfalse;
}
}
@@ -398,6 +417,10 @@ public:
{
setup.manager->playTestSound();
}
else if (button == resetDeviceButton)
{
resetDevice();
}
}
void updateAllControls()
@@ -406,6 +429,7 @@ public:
updateInputsComboBox();
updateControlPanelButton();
updateResetButton();
if (AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice())
{
@@ -418,6 +442,7 @@ public:
= new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioOutputType,
TRANS ("(no audio output channels found)")));
outputChanLabel = new Label (String::empty, TRANS("Active output channels:"));
outputChanLabel->setJustificationType (Justification::centredRight);
outputChanLabel->attachToComponent (outputChanList, true);
}
@@ -438,6 +463,7 @@ public:
= new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioInputType,
TRANS("(no audio input channels found)")));
inputChanLabel = new Label (String::empty, TRANS("Active input channels:"));
inputChanLabel->setJustificationType (Justification::centredRight);
inputChanLabel->attachToComponent (inputChanList, true);
}
@@ -468,6 +494,7 @@ public:
inputDeviceDropDown->setSelectedId (-1, dontSendNotification);
}
sendLookAndFeelChange();
resized();
setSize (getWidth(), getLowestY() + 4);
}
@@ -477,6 +504,12 @@ public:
updateAllControls();
}
void resetDevice()
{
setup.manager->closeAudioDevice();
setup.manager->restartLastAudioDevice();
}
private:
AudioIODeviceType& type;
const AudioDeviceSetupDetails setup;
@@ -485,7 +518,7 @@ private:
ScopedPointer<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel;
ScopedPointer<TextButton> testButton;
ScopedPointer<Component> inputLevelMeter;
ScopedPointer<TextButton> showUIButton, showAdvancedSettingsButton;
ScopedPointer<TextButton> showUIButton, showAdvancedSettingsButton, resetDeviceButton;
void showCorrectDeviceName (ComboBox* const box, const bool isInput)
{
@@ -532,7 +565,7 @@ private:
if (currentDevice != nullptr && currentDevice->hasControlPanel())
{
addAndMakeVisible (showUIButton = new TextButton (TRANS ("Show this device's control panel"),
addAndMakeVisible (showUIButton = new TextButton (TRANS ("Control panel"),
TRANS ("Opens the device's own control panel")));
showUIButton->addListener (this);
}
@@ -540,6 +573,28 @@ private:
resized();
}
void updateResetButton()
{
if (AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice())
{
if (currentDevice->hasControlPanel())
{
if (resetDeviceButton == nullptr)
{
addAndMakeVisible (resetDeviceButton = new TextButton (TRANS ("Reset device"),
TRANS ("Resets the audio interface - sometimes needed after changing a device's properties in its custom control panel")));
resetDeviceButton->addListener (this);
resized();
}
return;
}
}
resetDeviceButton = nullptr;
}
void updateOutputsComboBox()
{
if (setup.maxNumOutputChannels > 0 || ! type.hasSeparateInputsAndOutputs())
@@ -557,7 +612,8 @@ private:
if (setup.maxNumOutputChannels > 0)
{
addAndMakeVisible (testButton = new TextButton (TRANS("Test")));
addAndMakeVisible (testButton = new TextButton (TRANS("Test"),
TRANS("Plays a test tone")));
testButton->addListener (this);
}
}
@@ -918,6 +974,7 @@ AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager&
const bool showChannelsAsStereoPairs_,
const bool hideAdvancedOptionsWithButton_)
: deviceManager (dm),
itemHeight (24),
minOutputChannels (minOutputChannels_),
maxOutputChannels (maxOutputChannels_),
minInputChannels (minInputChannels_),
@@ -984,42 +1041,40 @@ AudioDeviceSelectorComponent::~AudioDeviceSelectorComponent()
deviceManager.removeChangeListener (this);
}
void AudioDeviceSelectorComponent::setItemHeight (int newItemHeight)
{
itemHeight = newItemHeight;
resized();
}
void AudioDeviceSelectorComponent::resized()
{
const int lx = proportionOfWidth (0.35f);
const int w = proportionOfWidth (0.4f);
const int h = 24;
const int space = 6;
const int dh = h + space;
int y = 15;
Rectangle<int> r (proportionOfWidth (0.35f), 15, proportionOfWidth (0.6f), 3000);
const int space = itemHeight / 4;
if (deviceTypeDropDown != nullptr)
{
deviceTypeDropDown->setBounds (lx, y, proportionOfWidth (0.3f), h);
y += dh + space * 2;
deviceTypeDropDown->setBounds (r.removeFromTop (itemHeight));
r.removeFromTop (space * 3);
}
if (audioDeviceSettingsComp != nullptr)
{
audioDeviceSettingsComp->setBounds (0, y, getWidth(), audioDeviceSettingsComp->getHeight());
y += audioDeviceSettingsComp->getHeight() + space;
audioDeviceSettingsComp->resized();
audioDeviceSettingsComp->setBounds (r.removeFromTop (audioDeviceSettingsComp->getHeight())
.withX (0).withWidth (getWidth()));
r.removeFromTop (space);
}
if (midiInputsList != nullptr)
{
const int bh = midiInputsList->getBestHeight (jmin (h * 8, getHeight() - y - space - h));
midiInputsList->setBounds (lx, y, w, bh);
y += bh + space;
midiInputsList->setBounds (r.removeFromTop (midiInputsList->getBestHeight (jmin (itemHeight * 8,
getHeight() - r.getY() - space - itemHeight))));
r.removeFromTop (space);
}
if (midiOutputSelector != nullptr)
midiOutputSelector->setBounds (lx, y, w, h);
}
void AudioDeviceSelectorComponent::childBoundsChanged (Component* child)
{
if (child == audioDeviceSettingsComp)
resized();
midiOutputSelector->setBounds (r.removeFromTop (itemHeight));
}
void AudioDeviceSelectorComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
@@ -1069,13 +1124,10 @@ void AudioDeviceSelectorComponent::updateAllControls()
details.maxNumOutputChannels = maxOutputChannels;
details.useStereoPairs = showChannelsAsStereoPairs;
audioDeviceSettingsComp = new AudioDeviceSettingsPanel (*type, details, hideAdvancedOptionsWithButton);
if (audioDeviceSettingsComp != nullptr)
{
addAndMakeVisible (audioDeviceSettingsComp);
audioDeviceSettingsComp->resized();
}
AudioDeviceSettingsPanel* sp = new AudioDeviceSettingsPanel (*type, details, hideAdvancedOptionsWithButton);
audioDeviceSettingsComp = sp;
addAndMakeVisible (sp);
sp->updateAllControls();
}
}
@@ -75,11 +75,15 @@ public:
/** The device manager that this component is controlling */
AudioDeviceManager& deviceManager;
/** Sets the standard height used for items in the panel. */
void setItemHeight (int itemHeight);
/** Returns the standard height used for items in the panel. */
int getItemHeight() const noexcept { return itemHeight; }
//==============================================================================
/** @internal */
void resized() override;
/** @internal */
void childBoundsChanged (Component*) override;
private:
//==============================================================================
@@ -87,6 +91,7 @@ private:
ScopedPointer<Label> deviceTypeDropDownLabel;
ScopedPointer<Component> audioDeviceSettingsComp;
String audioDeviceSettingsCompType;
int itemHeight;
const int minOutputChannels, maxOutputChannels, minInputChannels, maxInputChannels;
const bool showChannelsAsStereoPairs;
const bool hideAdvancedOptionsWithButton;
@@ -39,10 +39,10 @@ struct AudioThumbnail::MinMaxValue
inline char getMinValue() const noexcept { return values[0]; }
inline char getMaxValue() const noexcept { return values[1]; }
inline void setFloat (const float newMin, const float newMax) noexcept
inline void setFloat (Range<float> newRange) noexcept
{
values[0] = (char) jlimit (-128, 127, roundFloatToInt (newMin * 127.0f));
values[1] = (char) jlimit (-128, 127, roundFloatToInt (newMax * 127.0f));
values[0] = (char) jlimit (-128, 127, roundFloatToInt (newRange.getStart() * 127.0f));
values[1] = (char) jlimit (-128, 127, roundFloatToInt (newRange.getEnd() * 127.0f));
if (values[0] == values[1])
{
@@ -115,7 +115,7 @@ public:
}
}
void getLevels (int64 startSample, int numSamples, Array<float>& levels)
void getLevels (int64 startSample, int numSamples, Array<Range<float> >& levels)
{
const ScopedLock sl (readerLock);
@@ -132,11 +132,10 @@ public:
if (reader != nullptr)
{
float l[4] = { 0 };
reader->readMaxLevels (startSample, numSamples, l[0], l[1], l[2], l[3]);
if (levels.size() < (int) reader->numChannels)
levels.insertMultiple (0, Range<float>(), (int) (reader->numChannels - levels.size()));
levels.clearQuick();
levels.addArray ((const float*) l, 4);
reader->readMaxLevels (startSample, numSamples, levels.getRawDataPointer(), (int) reader->numChannels);
lastReaderUseTime = Time::getMillisecondCounter();
}
@@ -202,8 +201,8 @@ public:
private:
AudioThumbnail& owner;
ScopedPointer <InputSource> source;
ScopedPointer <AudioFormatReader> reader;
ScopedPointer<InputSource> source;
ScopedPointer<AudioFormatReader> reader;
CriticalSection readerLock;
uint32 lastReaderUseTime;
@@ -230,23 +229,26 @@ private:
const int lastThumbIndex = sampleToThumbSample (startSample + numToDo);
const int numThumbSamps = lastThumbIndex - firstThumbIndex;
HeapBlock<MinMaxValue> levelData ((size_t) numThumbSamps * 2);
MinMaxValue* levels[2] = { levelData, levelData + numThumbSamps };
HeapBlock<MinMaxValue> levelData ((size_t) numThumbSamps * numChannels);
HeapBlock<MinMaxValue*> levels (numChannels);
for (int i = 0; i < (int) numChannels; ++i)
levels[i] = levelData + i * numThumbSamps;
HeapBlock<Range<float> > levelsRead (numChannels);
for (int i = 0; i < numThumbSamps; ++i)
{
float lowestLeft, highestLeft, lowestRight, highestRight;
reader->readMaxLevels ((firstThumbIndex + i) * owner.samplesPerThumbSample,
owner.samplesPerThumbSample, levelsRead, numChannels);
reader->readMaxLevels ((firstThumbIndex + i) * owner.samplesPerThumbSample, owner.samplesPerThumbSample,
lowestLeft, highestLeft, lowestRight, highestRight);
levels[0][i].setFloat (lowestLeft, highestLeft);
levels[1][i].setFloat (lowestRight, highestRight);
for (int j = 0; j < (int) numChannels; ++j)
levels[j][i].setFloat (levelsRead[j]);
}
{
const ScopedUnlock su (readerLock);
owner.setLevels (levels, firstThumbIndex, 2, numThumbSamps);
owner.setLevels (levels, firstThumbIndex, (int) numChannels, numThumbSamps);
}
numSamplesFinished += numToDo;
@@ -342,7 +344,7 @@ public:
}
private:
Array <MinMaxValue> data;
Array<MinMaxValue> data;
int peakLevel;
void ensureSize (const int thumbSamples)
@@ -391,6 +393,7 @@ public:
const MinMaxValue* cacheData = getData (channelNum, clip.getX() - area.getX());
RectangleList<float> waveform;
waveform.ensureStorageAllocated (clip.getWidth());
float x = (float) clip.getX();
@@ -414,7 +417,7 @@ public:
}
private:
Array <MinMaxValue> data;
Array<MinMaxValue> data;
double cachedStart, cachedTimePerPixel;
int numChannelsCached, numSamplesCached;
bool cacheNeedsRefilling;
@@ -451,7 +454,7 @@ private:
if (timePerPixel * rate <= sampsPerThumbSample && levelData != nullptr)
{
int sample = roundToInt (startTime * rate);
Array<float> levels;
Array<Range<float> > levels;
int i;
for (i = 0; i < numSamples; ++i)
@@ -469,11 +472,10 @@ private:
{
levelData->getLevels (sample, jmax (1, nextSample - sample), levels);
const int totalChans = jmin (levels.size() / 2, numChannelsCached);
const int totalChans = jmin (levels.size(), numChannelsCached);
for (int chan = 0; chan < totalChans; ++chan)
getData (chan, i)->setFloat (levels.getUnchecked (chan * 2),
levels.getUnchecked (chan * 2 + 1));
getData (chan, i)->setFloat (levels.getReference (chan));
}
}
@@ -713,8 +715,7 @@ void AudioThumbnail::addBlock (const int64 startSample, const AudioSampleBuffer&
for (int i = 0; i < numToDo; ++i)
{
const int start = i * samplesPerThumbSample;
Range<float> range (FloatVectorOperations::findMinAndMax (sourceData + start, jmin (samplesPerThumbSample, numSamples - start)));
dest[i].setFloat (range.getStart(), range.getEnd());
dest[i].setFloat (FloatVectorOperations::findMinAndMax (sourceData + start, jmin (samplesPerThumbSample, numSamples - start)));
}
}
@@ -816,7 +816,7 @@ void MidiKeyboardComponent::timerCallback()
const Array<MouseInputSource>& mouseSources = Desktop::getInstance().getMouseSources();
for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi)
updateNoteUnderMouse (getLocalPoint (nullptr, mi->getScreenPosition()), mi->isDragging(), mi->getIndex());
updateNoteUnderMouse (getLocalPoint (nullptr, mi->getScreenPosition()).roundToInt(), mi->isDragging(), mi->getIndex());
}
}
@@ -1,7 +1,7 @@
{
"id": "juce_audio_utils",
"name": "JUCE extra audio utility classes",
"version": "3.0.5",
"version": "3.0.8",
"description": "Classes for audio-related GUI and miscellaneous tasks.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
@@ -35,7 +35,7 @@ AbstractFifo::AbstractFifo (const int capacity) noexcept
AbstractFifo::~AbstractFifo() {}
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady(); }
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; }
int AbstractFifo::getNumReady() const noexcept
{
@@ -42,7 +42,7 @@
- it must be able to be relocated in memory by a memcpy without this causing any problems - so
objects whose functionality relies on external pointers or references to themselves can not be used.
You can of course have an array of pointers to any kind of object, e.g. Array <MyClass*>, but if
You can of course have an array of pointers to any kind of object, e.g. Array<MyClass*>, but if
you do this, the array doesn't take any ownership of the objects - see the OwnedArray class or the
ReferenceCountedArray class for more powerful ways of holding lists of objects.
@@ -65,8 +65,7 @@ private:
public:
//==============================================================================
/** Creates an empty array. */
Array() noexcept
: numUsed (0)
Array() noexcept : numUsed (0)
{
}
@@ -31,7 +31,7 @@ DynamicObject::DynamicObject()
}
DynamicObject::DynamicObject (const DynamicObject& other)
: properties (other.properties)
: ReferenceCountedObject(), properties (other.properties)
{
}
@@ -85,16 +85,9 @@ void DynamicObject::clear()
void DynamicObject::cloneAllProperties()
{
for (LinkedListPointer<NamedValueSet::NamedValue>* i = &(properties.values);;)
{
if (NamedValueSet::NamedValue* const v = i->get())
{
v->value = v->value.clone();
i = &(v->nextListItem);
}
else
break;
}
for (int i = properties.size(); --i >= 0;)
if (var* v = properties.getVarPointerAt (i))
*v = v->clone();
}
DynamicObject::Ptr DynamicObject::clone()
@@ -110,32 +103,27 @@ void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const
if (! allOnOneLine)
out << newLine;
for (LinkedListPointer<NamedValueSet::NamedValue>* i = &(properties.values);;)
const int numValues = properties.size();
for (int i = 0; i < numValues; ++i)
{
if (NamedValueSet::NamedValue* const v = i->get())
if (! allOnOneLine)
JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize);
out << '"';
JSONFormatter::writeString (out, properties.getName (i));
out << "\": ";
JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine);
if (i < numValues - 1)
{
if (! allOnOneLine)
JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize);
out << '"';
JSONFormatter::writeString (out, v->name);
out << "\": ";
JSONFormatter::write (out, v->value, indentLevel + JSONFormatter::indentSize, allOnOneLine);
if (v->nextListItem.get() != nullptr)
{
if (allOnOneLine)
out << ", ";
else
out << ',' << newLine;
}
else if (! allOnOneLine)
out << newLine;
i = &(v->nextListItem);
if (allOnOneLine)
out << ", ";
else
out << ',' << newLine;
}
else
break;
else if (! allOnOneLine)
out << newLine;
}
if (! allOnOneLine)
@@ -58,7 +58,7 @@ public:
virtual bool hasProperty (const Identifier& propertyName) const;
/** Returns a named property.
This returns a void if no such property exists.
This returns var::null if no such property exists.
*/
virtual var getProperty (const Identifier& propertyName) const;
@@ -26,53 +26,37 @@
==============================================================================
*/
NamedValueSet::NamedValue::NamedValue() noexcept
struct NamedValueSet::NamedValue
{
}
NamedValue() noexcept {}
NamedValue (Identifier n, const var& v) : name (n), value (v) {}
NamedValue (const NamedValue& other) : name (other.name), value (other.value) {}
inline NamedValueSet::NamedValue::NamedValue (Identifier n, const var& v)
: name (n), value (v)
{
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
NamedValue (NamedValue&& other) noexcept
: name (static_cast<Identifier&&> (other.name)),
value (static_cast<var&&> (other.value))
{
}
NamedValueSet::NamedValue::NamedValue (const NamedValue& other)
: name (other.name), value (other.value)
{
}
NamedValue (Identifier n, var&& v) : name (n), value (static_cast<var&&> (v))
{
}
NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (const NamedValueSet::NamedValue& other)
{
name = other.name;
value = other.value;
return *this;
}
NamedValue& operator= (NamedValue&& other) noexcept
{
name = static_cast<Identifier&&> (other.name);
value = static_cast<var&&> (other.value);
return *this;
}
#endif
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept
: nextListItem (static_cast<LinkedListPointer<NamedValue>&&> (other.nextListItem)),
name (static_cast<Identifier&&> (other.name)),
value (static_cast<var&&> (other.value))
{
}
bool operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; }
bool operator!= (const NamedValue& other) const noexcept { return ! operator== (other); }
inline NamedValueSet::NamedValue::NamedValue (Identifier n, var&& v)
: name (n), value (static_cast<var&&> (v))
{
}
NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept
{
nextListItem = static_cast<LinkedListPointer<NamedValue>&&> (other.nextListItem);
name = static_cast<Identifier&&> (other.name);
value = static_cast<var&&> (other.value);
return *this;
}
#endif
bool NamedValueSet::NamedValue::operator== (const NamedValueSet::NamedValue& other) const noexcept
{
return name == other.name && value == other.value;
}
Identifier name;
var value;
};
//==============================================================================
NamedValueSet::NamedValueSet() noexcept
@@ -80,20 +64,20 @@ NamedValueSet::NamedValueSet() noexcept
}
NamedValueSet::NamedValueSet (const NamedValueSet& other)
: values (other.values)
{
values.addCopyOfList (other.values);
}
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other)
{
clear();
values.addCopyOfList (other.values);
values = other.values;
return *this;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
: values (static_cast <LinkedListPointer<NamedValue>&&> (other.values))
: values (static_cast <Array<NamedValue>&&> (other.values))
{
}
@@ -104,31 +88,18 @@ NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept
}
#endif
NamedValueSet::~NamedValueSet()
NamedValueSet::~NamedValueSet() noexcept
{
clear();
}
void NamedValueSet::clear()
{
values.deleteAll();
values.clear();
}
bool NamedValueSet::operator== (const NamedValueSet& other) const
{
const NamedValue* i1 = values;
const NamedValue* i2 = other.values;
while (i1 != nullptr && i2 != nullptr)
{
if (! (*i1 == *i2))
return false;
i1 = i1->nextListItem;
i2 = i2->nextListItem;
}
return true;
return values == other.values;
}
bool NamedValueSet::operator!= (const NamedValueSet& other) const
@@ -141,16 +112,15 @@ int NamedValueSet::size() const noexcept
return values.size();
}
const var& NamedValueSet::operator[] (Identifier name) const
const var& NamedValueSet::operator[] (const Identifier& name) const noexcept
{
for (NamedValue* i = values; i != nullptr; i = i->nextListItem)
if (i->name == name)
return i->value;
if (const var* v = getVarPointer (name))
return *v;
return var::null;
}
var NamedValueSet::getWithDefault (Identifier name, const var& defaultReturnValue) const
var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const
{
if (const var* const v = getVarPointer (name))
return *v;
@@ -158,9 +128,9 @@ var NamedValueSet::getWithDefault (Identifier name, const var& defaultReturnValu
return defaultReturnValue;
}
var* NamedValueSet::getVarPointer (Identifier name) const noexcept
var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept
{
for (NamedValue* i = values; i != nullptr; i = i->nextListItem)
for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i)
if (i->name == name)
return &(i->value);
@@ -170,145 +140,121 @@ var* NamedValueSet::getVarPointer (Identifier name) const noexcept
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
bool NamedValueSet::set (Identifier name, var&& newValue)
{
LinkedListPointer<NamedValue>* i = &values;
while (i->get() != nullptr)
if (var* const v = getVarPointer (name))
{
NamedValue* const v = i->get();
if (v->equalsWithSameType (newValue))
return false;
if (v->name == name)
{
if (v->value.equalsWithSameType (newValue))
return false;
v->value = static_cast <var&&> (newValue);
return true;
}
i = &(v->nextListItem);
*v = static_cast<var&&> (newValue);
return true;
}
i->insertNext (new NamedValue (name, static_cast <var&&> (newValue)));
values.add (NamedValue (name, static_cast<var&&> (newValue)));
return true;
}
#endif
bool NamedValueSet::set (Identifier name, const var& newValue)
{
LinkedListPointer<NamedValue>* i = &values;
while (i->get() != nullptr)
if (var* const v = getVarPointer (name))
{
NamedValue* const v = i->get();
if (v->equalsWithSameType (newValue))
return false;
if (v->name == name)
{
if (v->value.equalsWithSameType (newValue))
return false;
v->value = newValue;
return true;
}
i = &(v->nextListItem);
*v = newValue;
return true;
}
i->insertNext (new NamedValue (name, newValue));
values.add (NamedValue (name, newValue));
return true;
}
bool NamedValueSet::contains (Identifier name) const
bool NamedValueSet::contains (const Identifier& name) const noexcept
{
return getVarPointer (name) != nullptr;
}
int NamedValueSet::indexOf (Identifier name) const noexcept
int NamedValueSet::indexOf (const Identifier& name) const noexcept
{
int index = 0;
const int numValues = values.size();
for (NamedValue* i = values; i != nullptr; i = i->nextListItem)
{
if (i->name == name)
return index;
++index;
}
for (int i = 0; i < numValues; ++i)
if (values.getReference(i).name == name)
return i;
return -1;
}
bool NamedValueSet::remove (Identifier name)
bool NamedValueSet::remove (const Identifier& name)
{
LinkedListPointer<NamedValue>* i = &values;
const int numValues = values.size();
for (;;)
for (int i = 0; i < numValues; ++i)
{
NamedValue* const v = i->get();
if (v == nullptr)
break;
if (v->name == name)
if (values.getReference(i).name == name)
{
delete i->removeNext();
values.remove (i);
return true;
}
i = &(v->nextListItem);
}
return false;
}
Identifier NamedValueSet::getName (const int index) const
Identifier NamedValueSet::getName (const int index) const noexcept
{
const NamedValue* const v = values[index];
jassert (v != nullptr);
return v->name;
if (isPositiveAndBelow (index, values.size()))
return values.getReference (index).name;
jassertfalse;
return Identifier();
}
const var& NamedValueSet::getValueAt (const int index) const
const var& NamedValueSet::getValueAt (const int index) const noexcept
{
const NamedValue* const v = values[index];
jassert (v != nullptr);
return v->value;
if (isPositiveAndBelow (index, values.size()))
return values.getReference (index).value;
jassertfalse;
return var::null;
}
var* NamedValueSet::getVarPointerAt (int index) const noexcept
{
if (isPositiveAndBelow (index, values.size()))
return &(values.getReference (index).value);
return nullptr;
}
void NamedValueSet::setFromXmlAttributes (const XmlElement& xml)
{
clear();
LinkedListPointer<NamedValue>::Appender appender (values);
values.clearQuick();
const int numAtts = xml.getNumAttributes(); // xxx inefficient - should write an att iterator..
for (int i = 0; i < numAtts; ++i)
for (const XmlElement::XmlAttributeNode* att = xml.attributes; att != nullptr; att = att->nextListItem)
{
const String& name = xml.getAttributeName (i);
const String& value = xml.getAttributeValue (i);
if (name.startsWith ("base64:"))
if (att->name.toString().startsWith ("base64:"))
{
MemoryBlock mb;
if (mb.fromBase64Encoding (value))
if (mb.fromBase64Encoding (att->value))
{
appender.append (new NamedValue (name.substring (7), var (mb)));
values.add (NamedValue (att->name.toString().substring (7), var (mb)));
continue;
}
}
appender.append (new NamedValue (name, var (value)));
values.add (NamedValue (att->name, var (att->value)));
}
}
void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const
{
for (NamedValue* i = values; i != nullptr; i = i->nextListItem)
for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i)
{
if (const MemoryBlock* mb = i->value.getBinaryData())
{
xml.setAttribute ("base64:" + i->name.toString(),
mb->toBase64Encoding());
xml.setAttribute ("base64:" + i->name.toString(), mb->toBase64Encoding());
}
else
{
@@ -54,7 +54,7 @@ public:
#endif
/** Destructor. */
~NamedValueSet();
~NamedValueSet() noexcept;
bool operator== (const NamedValueSet&) const;
bool operator!= (const NamedValueSet&) const;
@@ -67,12 +67,12 @@ public:
If the name isn't found, this will return a void variant.
@see getProperty
*/
const var& operator[] (Identifier name) const;
const var& operator[] (const Identifier& name) const noexcept;
/** Tries to return the named value, but if no such value is found, this will
instead return the supplied default value.
*/
var getWithDefault (Identifier name, const var& defaultReturnValue) const;
var getWithDefault (const Identifier& name, const var& defaultReturnValue) const;
/** Changes or adds a named value.
@returns true if a value was changed or added; false if the
@@ -89,38 +89,42 @@ public:
#endif
/** Returns true if the set contains an item with the specified name. */
bool contains (Identifier name) const;
bool contains (const Identifier& name) const noexcept;
/** Removes a value from the set.
@returns true if a value was removed; false if there was no value
with the name that was given.
*/
bool remove (Identifier name);
bool remove (const Identifier& name);
/** Returns the name of the value at a given index.
The index must be between 0 and size() - 1.
*/
Identifier getName (int index) const;
Identifier getName (int index) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1.
*/
const var& getValueAt (int index) const;
/** Returns the index of the given name, or -1 if it's not found. */
int indexOf (Identifier name) const noexcept;
/** Removes all values. */
void clear();
//==============================================================================
/** Returns a pointer to the var that holds a named value, or null if there is
no value with this name.
Do not use this method unless you really need access to the internal var object
for some reason - for normal reading and writing always prefer operator[]() and set().
*/
var* getVarPointer (Identifier name) const noexcept;
var* getVarPointer (const Identifier& name) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1.
*/
const var& getValueAt (int index) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1, or this will return a nullptr
*/
var* getVarPointerAt (int index) const noexcept;
/** Returns the index of the given name, or -1 if it's not found. */
int indexOf (const Identifier& name) const noexcept;
/** Removes all values. */
void clear();
//==============================================================================
/** Sets properties to the values of all of an XML element's attributes. */
@@ -133,32 +137,8 @@ public:
private:
//==============================================================================
class NamedValue
{
public:
NamedValue() noexcept;
NamedValue (const NamedValue&);
NamedValue (Identifier, const var&);
NamedValue& operator= (const NamedValue&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
NamedValue (NamedValue&&) noexcept;
NamedValue (Identifier, var&&);
NamedValue& operator= (NamedValue&&) noexcept;
#endif
bool operator== (const NamedValue&) const noexcept;
LinkedListPointer<NamedValue> nextListItem;
Identifier name;
var value;
private:
JUCE_LEAK_DETECTOR (NamedValue)
};
friend class LinkedListPointer<NamedValue>;
LinkedListPointer<NamedValue> values;
friend class DynamicObject;
struct NamedValue;
Array<NamedValue> values;
};
@@ -830,7 +830,7 @@ public:
This will use a comparator object to sort the elements into order. The object
passed must have a method of the form:
@code
int compareElements (ElementType first, ElementType second);
int compareElements (ElementType* first, ElementType* second);
@endcode
..and this method must return:
@@ -38,7 +38,7 @@
The template parameter specifies the class of the object you want to point to - the easiest
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
class by implementing a set of mathods called incReferenceCount(), decReferenceCount(), and
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
should behave.
@@ -72,7 +72,7 @@ public:
const ScopedLockType lock (other.getLock());
numUsed = other.size();
data.setAllocatedSize (numUsed);
memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*));
memcpy (data.elements, other.getRawDataPointer(), (size_t) numUsed * sizeof (ObjectClass*));
for (int i = numUsed; --i >= 0;)
if (ObjectClass* o = data.elements[i])
@@ -476,7 +476,7 @@ bool File::loadFileAsData (MemoryBlock& destBlock) const
return false;
FileInputStream in (*this);
return in.openedOk() && getSize() == in.readIntoMemoryBlock (destBlock);
return in.openedOk() && getSize() == (int64) in.readIntoMemoryBlock (destBlock);
}
String File::loadFileAsString() const
@@ -361,6 +361,14 @@ public:
*/
File getLinkedTarget() const;
/** Returns a unique identifier for the file, if one is available.
Depending on the OS and file-system, this may be a unix inode number or
a win32 file identifier, or 0 if it fails to find one. The number will
be unique on the filesystem, but not globally.
*/
uint64 getFileIdentifier() const;
//==============================================================================
/** Returns the last modification time of this file.
@@ -836,6 +844,11 @@ public:
/** In a plugin, this will return the path of the host executable. */
hostApplicationPath,
#if JUCE_WINDOWS
/** On a Windows machine, returns the location of the Windows/System32 folder. */
windowsSystemDirectory,
#endif
/** The directory in which applications normally get installed.
So on windows, this would be something like "c:\program files", on the
Mac "/Applications", or "/usr" on linux.
@@ -28,41 +28,34 @@
int64 juce_fileSetPosition (void* handle, int64 pos);
//==============================================================================
FileInputStream::FileInputStream (const File& f)
: file (f),
fileHandle (nullptr),
currentPosition (0),
status (Result::ok()),
needToSeek (true)
status (Result::ok())
{
openHandle();
}
FileInputStream::~FileInputStream()
{
closeHandle();
}
//==============================================================================
int64 FileInputStream::getTotalLength()
{
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
return file.getSize();
}
int FileInputStream::read (void* buffer, int bytesToRead)
{
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
// The buffer should never be null, and a negative size is probably a
// sign that something is broken!
jassert (buffer != nullptr && bytesToRead >= 0);
if (needToSeek)
{
if (juce_fileSetPosition (fileHandle, currentPosition) < 0)
return 0;
needToSeek = false;
}
const size_t num = readInternal (buffer, (size_t) bytesToRead);
currentPosition += num;
@@ -81,15 +74,11 @@ int64 FileInputStream::getPosition()
bool FileInputStream::setPosition (int64 pos)
{
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
if (pos != currentPosition)
{
pos = jlimit ((int64) 0, getTotalLength(), pos);
currentPosition = juce_fileSetPosition (fileHandle, pos);
needToSeek |= (currentPosition != pos);
currentPosition = pos;
}
return true;
return currentPosition == pos;
}
@@ -40,10 +40,11 @@ class JUCE_API FileInputStream : public InputStream
{
public:
//==============================================================================
/** Creates a FileInputStream.
/** Creates a FileInputStream to read from the given file.
@param fileToRead the file to read from - if the file can't be accessed for some
reason, then the stream will just contain no data
After creating a FileInputStream, you should use openedOk() or failedToOpen()
to make sure that it's OK before trying to read from it! If it failed, you
can call getStatus() to get more error information.
*/
explicit FileInputStream (const File& fileToRead);
@@ -73,24 +74,23 @@ public:
//==============================================================================
int64 getTotalLength() override;
int read (void* destBuffer, int maxBytesToRead) override;
int read (void*, int) override;
bool isExhausted() override;
int64 getPosition() override;
bool setPosition (int64 pos) override;
bool setPosition (int64) override;
private:
//==============================================================================
File file;
const File file;
void* fileHandle;
int64 currentPosition;
Result status;
bool needToSeek;
void openHandle();
void closeHandle();
size_t readInternal (void* buffer, size_t numBytes);
size_t readInternal (void*, size_t);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream)
};
#endif // JUCE_FILEINPUTSTREAM_H_INCLUDED
@@ -26,32 +26,7 @@
==============================================================================
*/
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns,
const String& directoryWildcardPatterns,
const String& desc)
: FileFilter (desc.isEmpty() ? fileWildcardPatterns
: (desc + " (" + fileWildcardPatterns + ")"))
{
parse (fileWildcardPatterns, fileWildcards);
parse (directoryWildcardPatterns, directoryWildcards);
}
WildcardFileFilter::~WildcardFileFilter()
{
}
bool WildcardFileFilter::isFileSuitable (const File& file) const
{
return match (file, fileWildcards);
}
bool WildcardFileFilter::isDirectorySuitable (const File& file) const
{
return match (file, directoryWildcards);
}
//==============================================================================
void WildcardFileFilter::parse (const String& pattern, StringArray& result)
static void parseWildcard (const String& pattern, StringArray& result)
{
result.addTokens (pattern.toLowerCase(), ";,", "\"'");
@@ -65,7 +40,7 @@ void WildcardFileFilter::parse (const String& pattern, StringArray& result)
result.set (i, "*");
}
bool WildcardFileFilter::match (const File& file, const StringArray& wildcards)
static bool matchWildcard (const File& file, const StringArray& wildcards)
{
const String filename (file.getFileName());
@@ -75,3 +50,27 @@ bool WildcardFileFilter::match (const File& file, const StringArray& wildcards)
return false;
}
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns,
const String& directoryWildcardPatterns,
const String& desc)
: FileFilter (desc.isEmpty() ? fileWildcardPatterns
: (desc + " (" + fileWildcardPatterns + ")"))
{
parseWildcard (fileWildcardPatterns, fileWildcards);
parseWildcard (directoryWildcardPatterns, directoryWildcards);
}
WildcardFileFilter::~WildcardFileFilter()
{
}
bool WildcardFileFilter::isFileSuitable (const File& file) const
{
return matchWildcard (file, fileWildcards);
}
bool WildcardFileFilter::isDirectorySuitable (const File& file) const
{
return matchWildcard (file, directoryWildcards);
}
@@ -75,12 +75,8 @@ private:
//==============================================================================
StringArray fileWildcards, directoryWildcards;
static void parse (const String& pattern, StringArray& result);
static bool match (const File& file, const StringArray& wildcards);
JUCE_LEAK_DETECTOR (WildcardFileFilter)
};
#endif // JUCE_WILDCARDFILEFILTER_H_INCLUDED
@@ -243,9 +243,9 @@ private:
if (r.failed())
return r;
const String propertyName (propertyNameVar.toString());
const Identifier propertyName (propertyNameVar.toString());
if (propertyName.isNotEmpty())
if (propertyName.isValid())
{
t = t.findEndOfWhitespace();
oldT = t;
@@ -102,6 +102,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
static bool isNumericOrUndefined (const var& v) { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool() || v.isUndefined(); }
static int64 getOctalValue (const String& s) { BigInteger b; b.parseString (s, 8); return b.toInt64(); }
static Identifier getPrototypeIdentifier() { static const Identifier i ("prototype"); return i; }
static var* getPropertyPointer (DynamicObject* o, Identifier i) { return o->getProperties().getVarPointer (i); }
//==============================================================================
struct CodeLocation
@@ -139,13 +140,13 @@ struct JavascriptEngine::RootObject : public DynamicObject
{
if (DynamicObject* o = targetObject.getDynamicObject())
{
if (var* prop = o->getProperties().getVarPointer (functionName))
if (const var* prop = getPropertyPointer (o, functionName))
return *prop;
for (DynamicObject* p = o->getProperty (getPrototypeIdentifier()).getDynamicObject(); p != nullptr;
p = p->getProperty (getPrototypeIdentifier()).getDynamicObject())
{
if (var* prop = p->getProperties().getVarPointer (functionName))
if (const var* prop = getPropertyPointer (p, functionName))
return *prop;
}
}
@@ -168,14 +169,14 @@ struct JavascriptEngine::RootObject : public DynamicObject
var* findRootClassProperty (Identifier className, Identifier propName) const
{
if (DynamicObject* cls = root->getProperty (className).getDynamicObject())
return cls->getProperties().getVarPointer (propName);
return getPropertyPointer (cls, propName);
return nullptr;
}
var findSymbolInParentScopes (Identifier name) const
{
if (var* v = scope->getProperties().getVarPointer (name))
if (const var* v = getPropertyPointer (scope, name))
return *v;
return parent != nullptr ? parent->findSymbolInParentScopes (name)
@@ -184,13 +185,11 @@ struct JavascriptEngine::RootObject : public DynamicObject
bool findAndInvokeMethod (Identifier function, const var::NativeFunctionArgs& args, var& result) const
{
const NamedValueSet& props = scope->getProperties();
DynamicObject* target = args.thisObject.getDynamicObject();
if (target == nullptr || target == scope)
{
if (const var* m = props.getVarPointer (function))
if (const var* m = getPropertyPointer (scope, function))
{
if (FunctionObject* fo = dynamic_cast<FunctionObject*> (m->getObject()))
{
@@ -200,6 +199,8 @@ struct JavascriptEngine::RootObject : public DynamicObject
}
}
const NamedValueSet& props = scope->getProperties();
for (int i = 0; i < props.size(); ++i)
if (DynamicObject* o = props.getValueAt (i).getDynamicObject())
if (Scope (this, root, o).findAndInvokeMethod (function, args, result))
@@ -353,7 +354,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
void assign (const Scope& s, const var& newValue) const override
{
if (var* v = s.scope->getProperties().getVarPointer (name))
if (var* v = getPropertyPointer (s.scope, name))
*v = newValue;
else
s.root->setProperty (name, newValue);
@@ -378,7 +379,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
}
if (DynamicObject* o = p.getDynamicObject())
if (var* v = o->getProperties().getVarPointer (child))
if (const var* v = getPropertyPointer (o, child))
return *v;
return var::undefined();
@@ -543,14 +544,14 @@ struct JavascriptEngine::RootObject : public DynamicObject
struct DivideOp : public BinaryOperator
{
DivideOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::divide) {}
var getWithDoubles (double a, double b) const override { return a / b; }
var getWithInts (int64 a, int64 b) const override { return a / b; }
var getWithDoubles (double a, double b) const override { return b != 0 ? a / b : std::numeric_limits<double>::infinity(); }
var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a / (double) b) : var (std::numeric_limits<double>::infinity()); }
};
struct ModuloOp : public BinaryOperator
{
ModuloOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::modulo) {}
var getWithInts (int64 a, int64 b) const override { return a % b; }
var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a % b) : var (std::numeric_limits<double>::infinity()); }
};
struct BitwiseOrOp : public BinaryOperator
@@ -768,7 +769,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
{
FunctionObject() noexcept {}
FunctionObject (const FunctionObject& other) : functionCode (other.functionCode)
FunctionObject (const FunctionObject& other) : DynamicObject(), functionCode (other.functionCode)
{
ExpressionTreeBuilder tb (functionCode);
tb.parseFunctionParamsAndBody (*this);
@@ -843,7 +844,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
String::CharPointerType end (p);
while (isIdentifierBody (*++end)) {}
const size_t len = end - p;
const size_t len = (size_t) (end - p);
#define JUCE_JS_COMPARE_KEYWORD(name, str) if (len == sizeof (str) - 1 && matchToken (TokenTypes::name, len)) return TokenTypes::name;
JUCE_JS_KEYWORDS (JUCE_JS_COMPARE_KEYWORD)
@@ -1145,8 +1146,14 @@ struct JavascriptEngine::RootObject : public DynamicObject
match (TokenTypes::semicolon);
}
s->iterator = parseExpression();
match (TokenTypes::closeParen);
if (matchIf (TokenTypes::closeParen))
s->iterator = new Statement (location);
else
{
s->iterator = parseExpression();
match (TokenTypes::closeParen);
}
s->body = parseStatement();
return s.release();
}
@@ -1555,6 +1562,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
setMethod ("log", Math_log); setMethod ("log10", Math_log10);
setMethod ("exp", Math_exp); setMethod ("pow", Math_pow);
setMethod ("sqr", Math_sqr); setMethod ("sqrt", Math_sqrt);
setMethod ("ceil", Math_ceil); setMethod ("floor", Math_floor);
}
static var Math_pi (Args) { return double_Pi; }
@@ -1587,6 +1595,8 @@ struct JavascriptEngine::RootObject : public DynamicObject
static var Math_pow (Args a) { return pow (getDouble (a, 0), getDouble (a, 1)); }
static var Math_sqr (Args a) { double x = getDouble (a, 0); return x * x; }
static var Math_sqrt (Args a) { return std::sqrt (getDouble (a, 0)); }
static var Math_ceil (Args a) { return std::ceil (getDouble (a, 0)); }
static var Math_floor (Args a) { return std::floor (getDouble (a, 0)); }
static Identifier getClassName() { static const Identifier i ("Math"); return i; }
template <typename Type> static Type sign (Type n) noexcept { return n > 0 ? (Type) 1 : (n < 0 ? (Type) -1 : 0); }
@@ -1649,7 +1659,7 @@ JavascriptEngine::JavascriptEngine() : maximumExecutionTime (15.0), root (new R
JavascriptEngine::~JavascriptEngine() {}
void JavascriptEngine::prepareTimeout() const { root->timeout = Time::getCurrentTime() + maximumExecutionTime; }
void JavascriptEngine::prepareTimeout() const noexcept { root->timeout = Time::getCurrentTime() + maximumExecutionTime; }
void JavascriptEngine::registerNativeObject (Identifier name, DynamicObject* object)
{
@@ -1705,6 +1715,11 @@ var JavascriptEngine::callFunction (Identifier function, const var::NativeFuncti
return returnVal;
}
const NamedValueSet& JavascriptEngine::getRootObjectProperties() const noexcept
{
return root->getProperties();
}
#if JUCE_MSVC
#pragma warning (pop)
#endif
@@ -96,10 +96,13 @@ public:
*/
RelativeTime maximumExecutionTime;
/** Provides access to the set of properties of the root namespace object. */
const NamedValueSet& getRootObjectProperties() const noexcept;
private:
JUCE_PUBLIC_IN_DLL_BUILD (struct RootObject)
ReferenceCountedObjectPtr<RootObject> root;
void prepareTimeout() const;
const ReferenceCountedObjectPtr<RootObject> root;
void prepareTimeout() const noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavascriptEngine)
};
@@ -53,6 +53,8 @@
#if JUCE_WINDOWS
#include <ctime>
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -79,6 +81,7 @@
#if JUCE_LINUX
#include <langinfo.h>
#include <ifaddrs.h>
#endif
#include <pwd.h>
@@ -194,6 +194,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe
#include "threads/juce_ScopedLock.h"
#include "threads/juce_CriticalSection.h"
#include "maths/juce_Range.h"
#include "maths/juce_NormalisableRange.h"
#include "containers/juce_ElementComparator.h"
#include "containers/juce_ArrayAllocationBase.h"
#include "containers/juce_Array.h"
@@ -244,6 +245,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe
#include "misc/juce_Uuid.h"
#include "misc/juce_WindowsRegistry.h"
#include "system/juce_PlatformDefs.h"
#include "system/juce_CompilerSupport.h"
#include "system/juce_SystemStats.h"
#include "threads/juce_ChildProcess.h"
#include "threads/juce_DynamicLibrary.h"
@@ -1,7 +1,7 @@
{
"id": "juce_core",
"name": "JUCE core classes",
"version": "3.0.5",
"version": "3.0.8",
"description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.",
"website": "http://www.juce.com/juce",
"license": "ISC Permissive",
@@ -307,41 +307,28 @@ void BigInteger::negate() noexcept
negative = (! negative) && ! isZero();
}
#if JUCE_USE_INTRINSICS && ! defined (__INTEL_COMPILER)
#if JUCE_USE_MSVC_INTRINSICS && ! defined (__INTEL_COMPILER)
#pragma intrinsic (_BitScanReverse)
#endif
namespace BitFunctions
inline static int highestBitInInt (uint32 n) noexcept
{
inline int countBitsInInt32 (uint32 n) noexcept
{
n -= ((n >> 1) & 0x55555555);
n = (((n >> 2) & 0x33333333) + (n & 0x33333333));
n = (((n >> 4) + n) & 0x0f0f0f0f);
n += (n >> 8);
n += (n >> 16);
return (int) (n & 0x3f);
}
jassert (n != 0); // (the built-in functions may not work for n = 0)
inline int highestBitInInt (uint32 n) noexcept
{
jassert (n != 0); // (the built-in functions may not work for n = 0)
#if JUCE_GCC
return 31 - __builtin_clz (n);
#elif JUCE_USE_INTRINSICS
unsigned long highest;
_BitScanReverse (&highest, n);
return (int) highest;
#else
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
return countBitsInInt32 (n >> 1);
#endif
}
#if JUCE_GCC
return 31 - __builtin_clz (n);
#elif JUCE_USE_MSVC_INTRINSICS
unsigned long highest;
_BitScanReverse (&highest, n);
return (int) highest;
#else
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
return countBitsInInt32 (n >> 1);
#endif
}
int BigInteger::countNumberOfSetBits() const noexcept
@@ -349,7 +336,7 @@ int BigInteger::countNumberOfSetBits() const noexcept
int total = 0;
for (int i = (int) bitToIndex (highestBit) + 1; --i >= 0;)
total += BitFunctions::countBitsInInt32 (values[i]);
total += countNumberOfBits (values[i]);
return total;
}
@@ -361,7 +348,7 @@ int BigInteger::getHighestBit() const noexcept
const uint32 n = values[i];
if (n != 0)
return BitFunctions::highestBitInInt (n) + (i << 5);
return highestBitInInt (n) + (i << 5);
}
return -1;
@@ -364,6 +364,11 @@ inline int roundToInt (const FloatType value) noexcept
#endif
}
inline int roundToInt (int value) noexcept
{
return value;
}
#if JUCE_MSVC
#ifndef __INTEL_COMPILER
#pragma float_control (pop)
@@ -438,6 +443,23 @@ inline int nextPowerOfTwo (int n) noexcept
return n + 1;
}
/** Returns the number of bits in a 32-bit integer. */
inline int countNumberOfBits (uint32 n) noexcept
{
n -= ((n >> 1) & 0x55555555);
n = (((n >> 2) & 0x33333333) + (n & 0x33333333));
n = (((n >> 4) + n) & 0x0f0f0f0f);
n += (n >> 8);
n += (n >> 16);
return (int) (n & 0x3f);
}
/** Returns the number of bits in a 64-bit integer. */
inline int countNumberOfBits (uint64 n) noexcept
{
return countNumberOfBits ((uint32) n) + countNumberOfBits ((uint32) (n >> 32));
}
/** Performs a modulo operation, but can cope with the dividend being negative.
The divisor must be greater than zero.
*/
@@ -77,6 +77,13 @@ public:
: Range (position2, position1);
}
/** Returns a range with a given start and length. */
static Range withStartAndLength (const ValueType startValue, const ValueType length) noexcept
{
jassert (length >= ValueType());
return Range (startValue, startValue + length);
}
/** Returns a range with the specified start position and a length of zero. */
static Range emptyRange (const ValueType start) noexcept
{
@@ -218,7 +218,7 @@ private:
#else
#define JUCE_ATOMICS_WINDOWS 1 // Windows with intrinsics
#if JUCE_USE_INTRINSICS
#if JUCE_USE_MSVC_INTRINSICS
#ifndef __INTEL_COMPILER
#pragma intrinsic (_InterlockedExchange, _InterlockedIncrement, _InterlockedDecrement, _InterlockedCompareExchange, \
_InterlockedCompareExchange64, _InterlockedExchangeAdd, _ReadWriteBarrier)
@@ -87,16 +87,16 @@ public:
//==============================================================================
/** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */
static int littleEndian24Bit (const char* bytes) noexcept;
static int littleEndian24Bit (const void* bytes) noexcept;
/** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */
static int bigEndian24Bit (const char* bytes) noexcept;
static int bigEndian24Bit (const void* bytes) noexcept;
/** Copies a 24-bit number to 3 little-endian bytes. */
static void littleEndian24BitToChars (int value, char* destBytes) noexcept;
static void littleEndian24BitToChars (int value, void* destBytes) noexcept;
/** Copies a 24-bit number to 3 big-endian bytes. */
static void bigEndian24BitToChars (int value, char* destBytes) noexcept;
static void bigEndian24BitToChars (int value, void* destBytes) noexcept;
//==============================================================================
/** Returns true if the current CPU is big-endian. */
@@ -110,16 +110,16 @@ private:
//==============================================================================
#if JUCE_USE_INTRINSICS && ! defined (__INTEL_COMPILER)
#if JUCE_USE_MSVC_INTRINSICS && ! defined (__INTEL_COMPILER)
#pragma intrinsic (_byteswap_ulong)
#endif
inline uint16 ByteOrder::swap (uint16 n) noexcept
{
#if JUCE_USE_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic!
return static_cast <uint16> (_byteswap_ushort (n));
#if JUCE_USE_MSVC_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic!
return static_cast<uint16> (_byteswap_ushort (n));
#else
return static_cast <uint16> ((n << 8) | (n >> 8));
return static_cast<uint16> ((n << 8) | (n >> 8));
#endif
}
@@ -130,7 +130,7 @@ inline uint32 ByteOrder::swap (uint32 n) noexcept
#elif JUCE_GCC && JUCE_INTEL && ! JUCE_NO_INLINE_ASM
asm("bswap %%eax" : "=a"(n) : "a"(n));
return n;
#elif JUCE_USE_INTRINSICS
#elif JUCE_USE_MSVC_INTRINSICS
return _byteswap_ulong (n);
#elif JUCE_MSVC && ! JUCE_NO_INLINE_ASM
__asm {
@@ -150,7 +150,7 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept
{
#if JUCE_MAC || JUCE_IOS
return OSSwapInt64 (value);
#elif JUCE_USE_INTRINSICS
#elif JUCE_USE_MSVC_INTRINSICS
return _byteswap_uint64 (value);
#else
return (((int64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32));
@@ -164,12 +164,12 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept
inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return swap (v); }
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return swap (v); }
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return swap (v); }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast <const uint32*> (bytes); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast <const uint64*> (bytes); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast <const uint16*> (bytes); }
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return swap (*static_cast <const uint32*> (bytes)); }
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast <const uint64*> (bytes)); }
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return swap (*static_cast <const uint16*> (bytes)); }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast<const uint32*> (bytes); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast<const uint64*> (bytes); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast<const uint16*> (bytes); }
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return swap (*static_cast<const uint32*> (bytes)); }
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast<const uint64*> (bytes)); }
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return swap (*static_cast<const uint16*> (bytes)); }
inline bool ByteOrder::isBigEndian() noexcept { return false; }
#else
inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return swap (v); }
@@ -178,19 +178,19 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept
inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return v; }
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return v; }
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return v; }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast <const uint32*> (bytes)); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast <const uint64*> (bytes)); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast <const uint16*> (bytes)); }
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return *static_cast <const uint32*> (bytes); }
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return *static_cast <const uint64*> (bytes); }
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return *static_cast <const uint16*> (bytes); }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast<const uint32*> (bytes)); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast<const uint64*> (bytes)); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast<const uint16*> (bytes)); }
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return *static_cast<const uint32*> (bytes); }
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return *static_cast<const uint64*> (bytes); }
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return *static_cast<const uint16*> (bytes); }
inline bool ByteOrder::isBigEndian() noexcept { return true; }
#endif
inline int ByteOrder::littleEndian24Bit (const char* const bytes) noexcept { return (((int) bytes[2]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[0]); }
inline int ByteOrder::bigEndian24Bit (const char* const bytes) noexcept { return (((int) bytes[0]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[2]); }
inline void ByteOrder::littleEndian24BitToChars (const int value, char* const destBytes) noexcept { destBytes[0] = (char)(value & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)((value >> 16) & 0xff); }
inline void ByteOrder::bigEndian24BitToChars (const int value, char* const destBytes) noexcept { destBytes[0] = (char)((value >> 16) & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)(value & 0xff); }
inline int ByteOrder::littleEndian24Bit (const void* const bytes) noexcept { return (((int) static_cast<const int8*> (bytes)[2]) << 16) | (((int) static_cast<const uint8*> (bytes)[1]) << 8) | ((int) static_cast<const uint8*> (bytes)[0]); }
inline int ByteOrder::bigEndian24Bit (const void* const bytes) noexcept { return (((int) static_cast<const int8*> (bytes)[0]) << 16) | (((int) static_cast<const uint8*> (bytes)[1]) << 8) | ((int) static_cast<const uint8*> (bytes)[2]); }
inline void ByteOrder::littleEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) value; static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) (value >> 16); }
inline void ByteOrder::bigEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) (value >> 16); static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) value; }
#endif // JUCE_BYTEORDER_H_INCLUDED
@@ -88,14 +88,14 @@ MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other)
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept
: data (static_cast <HeapBlock<char>&&> (other.data)),
: data (static_cast<HeapBlock<char>&&> (other.data)),
size (other.size)
{
}
MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept
{
data = static_cast <HeapBlock<char>&&> (other.data);
data = static_cast<HeapBlock<char>&&> (other.data);
size = other.size;
return *this;
}
@@ -346,7 +346,7 @@ void MemoryBlock::loadFromHexString (StringRef hex)
if (c == 0)
{
setSize (static_cast <size_t> (dest - data));
setSize (static_cast<size_t> (dest - data));
return;
}
}
@@ -208,7 +208,7 @@ private:
The template parameter specifies the class of the object you want to point to - the easiest
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
class by implementing a set of mathods called incReferenceCount(), decReferenceCount(), and
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
should behave.
@@ -182,11 +182,11 @@ public:
/** Swaps this object with that of another ScopedPointer.
The two objects simply exchange their pointers.
*/
void swapWith (ScopedPointer <ObjectType>& other) noexcept
void swapWith (ScopedPointer<ObjectType>& other) noexcept
{
// Two ScopedPointers should never be able to refer to the same object - if
// this happens, you must have done something dodgy!
jassert (object != other.object || this == other.getAddress());
jassert (object != other.object || this == other.getAddress() || object == nullptr);
std::swap (object, other.object);
}
@@ -231,7 +231,7 @@ private:
template <class ObjectType>
bool operator== (const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept
{
return static_cast <ObjectType*> (pointer1) == pointer2;
return static_cast<ObjectType*> (pointer1) == pointer2;
}
/** Compares a ScopedPointer with another pointer.
@@ -240,7 +240,7 @@ bool operator== (const ScopedPointer<ObjectType>& pointer1, ObjectType* const po
template <class ObjectType>
bool operator!= (const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept
{
return static_cast <ObjectType*> (pointer1) != pointer2;
return static_cast<ObjectType*> (pointer1) != pointer2;
}
//==============================================================================
@@ -139,7 +139,7 @@ private:
static SharedObjectHolder& getSharedObjectHolder() noexcept
{
static char holder [sizeof (SharedObjectHolder)] = { 0 };
static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { 0 };
return *reinterpret_cast<SharedObjectHolder*> (holder);
}
@@ -158,7 +158,7 @@ public:
public:
Master() noexcept {}
~Master()
~Master() noexcept
{
// You must remember to call clear() in your source object's destructor! See the notes
// for the WeakReference class for an example of how to do this.
@@ -187,7 +187,7 @@ public:
to zero all the references to this object that may be out there. See the WeakReference
class notes for an example of how to do this.
*/
void clear()
void clear() noexcept
{
if (sharedPointer != nullptr)
sharedPointer->clearPointer();
@@ -399,16 +399,21 @@ public final class JuceAppActivity extends Activity
private native void handleKeyDown (long host, int keycode, int textchar);
private native void handleKeyUp (long host, int keycode, int textchar);
public void showKeyboard (boolean shouldShow)
public void showKeyboard (String type)
{
InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
if (imm != null)
{
if (shouldShow)
imm.showSoftInput (this, InputMethodManager.SHOW_FORCED);
if (type.length() > 0)
{
imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
imm.setInputMethod (getWindowToken(), type);
}
else
{
imm.hideSoftInputFromWindow (getWindowToken(), 0);
}
}
}
@@ -271,26 +271,33 @@ public:
JNIEnv* attach() noexcept
{
if (JNIEnv* env = attachToCurrentThread())
if (android.activity != nullptr)
{
SpinLock::ScopedLockType sl (addRemoveLock);
return addEnv (env);
if (JNIEnv* env = attachToCurrentThread())
{
SpinLock::ScopedLockType sl (addRemoveLock);
return addEnv (env);
}
jassertfalse;
}
jassertfalse;
return nullptr;
}
void detach() noexcept
{
jvm->DetachCurrentThread();
if (android.activity != nullptr)
{
jvm->DetachCurrentThread();
const pthread_t thisThread = pthread_self();
const pthread_t thisThread = pthread_self();
SpinLock::ScopedLockType sl (addRemoveLock);
for (int i = 0; i < maxThreads; ++i)
if (threads[i] == thisThread)
threads[i] = 0;
SpinLock::ScopedLockType sl (addRemoveLock);
for (int i = 0; i < maxThreads; ++i)
if (threads[i] == thisThread)
threads[i] = 0;
}
}
JNIEnv* getOrAttach() noexcept
@@ -355,6 +362,12 @@ private:
extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder;
struct AndroidThreadScope
{
AndroidThreadScope() { threadLocalJNIEnvHolder.attach(); }
~AndroidThreadScope() { threadLocalJNIEnvHolder.detach(); }
};
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \
@@ -166,7 +166,7 @@ public:
int numBytes = stream.callIntMethod (HTTPStream.read, javaArray, (jint) bytesToRead);
if (numBytes > 0)
env->GetByteArrayRegion (javaArray, 0, numBytes, static_cast <jbyte*> (buffer));
env->GetByteArrayRegion (javaArray, 0, numBytes, static_cast<jbyte*> (buffer));
env->DeleteLocalRef (javaArray);
return numBytes;

Some files were not shown because too many files have changed in this diff Show More