@@ -25,6 +25,4 @@ | |||
#include "modules/juce_gui_extra/juce_gui_extra.h" | |||
#include "modules/juce_tracktion_marketplace/juce_tracktion_marketplace.h" | |||
#include "modules/juce_audio_plugin_client/utility/juce_CheckSettingMacros.h" | |||
#endif // JUCE_PLUGIN_MAIN_H_INCLUDED |
@@ -139,7 +139,7 @@ | |||
@see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU, JUCE_PLUGINHOST_VST3 | |||
*/ | |||
#define JUCE_PLUGINHOST_VST 0 | |||
#define JUCE_PLUGINHOST_VST 1 | |||
/** Config: JUCE_PLUGINHOST_VST3 | |||
Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be | |||
@@ -29,17 +29,6 @@ namespace FloatVectorHelpers | |||
#define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); | |||
#if JUCE_USE_SSE_INTRINSICS | |||
static bool sse2Present = false; | |||
static bool isSSE2Available() noexcept | |||
{ | |||
if (sse2Present) | |||
return true; | |||
sse2Present = SystemStats::hasSSE2(); | |||
return sse2Present; | |||
} | |||
inline static bool isAligned (const void* p) noexcept | |||
{ | |||
return (((pointer_sized_int) p) & 15) == 0; | |||
@@ -113,7 +102,6 @@ namespace FloatVectorHelpers | |||
#define JUCE_BEGIN_VEC_OP \ | |||
typedef FloatVectorHelpers::ModeType<sizeof(*dest)>::Mode Mode; \ | |||
if (FloatVectorHelpers::isSSE2Available()) \ | |||
{ \ | |||
const int numLongOps = num / Mode::numParallel; | |||
@@ -372,11 +360,7 @@ namespace FloatVectorHelpers | |||
{ | |||
int numLongOps = num / Mode::numParallel; | |||
#if JUCE_USE_SSE_INTRINSICS | |||
if (numLongOps > 1 && isSSE2Available()) | |||
#else | |||
if (numLongOps > 1) | |||
#endif | |||
{ | |||
ParallelType val; | |||
@@ -446,11 +430,7 @@ namespace FloatVectorHelpers | |||
{ | |||
int numLongOps = num / Mode::numParallel; | |||
#if JUCE_USE_SSE_INTRINSICS | |||
if (numLongOps > 1 && isSSE2Available()) | |||
#else | |||
if (numLongOps > 1) | |||
#endif | |||
{ | |||
ParallelType mn, mx; | |||
@@ -1002,12 +982,19 @@ double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int | |||
void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept | |||
{ | |||
#if JUCE_USE_SSE_INTRINSICS | |||
if (FloatVectorHelpers::isSSE2Available()) | |||
_MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF); | |||
_MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF); | |||
#endif | |||
ignoreUnused (shouldEnable); | |||
} | |||
void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport() noexcept | |||
{ | |||
#if JUCE_USE_SSE_INTRINSICS | |||
const int mxcsr = _mm_getcsr(); | |||
_mm_setcsr (mxcsr | 0x8040); // add the DAZ and FZ bits | |||
#endif | |||
} | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_UNIT_TESTS | |||
@@ -198,6 +198,12 @@ public: | |||
Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE | |||
*/ | |||
static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; | |||
/** On Intel CPUs, this method enables the SSE flush-to-zero and denormalised-are-zero modes. | |||
This effectively sets the DAZ and FZ bits of the MXCSR register. It's a convenient thing to | |||
call before audio processing code where you really want to avoid denormalisation performance hits. | |||
*/ | |||
static void JUCE_CALLTYPE disableDenormalisedNumberSupport() noexcept; | |||
}; | |||
@@ -0,0 +1,63 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
struct CatmullRomAlgorithm | |||
{ | |||
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept | |||
{ | |||
const float y0 = inputs[3]; | |||
const float y1 = inputs[2]; | |||
const float y2 = inputs[1]; | |||
const float y3 = inputs[0]; | |||
const float halfY0 = 0.5f * y0; | |||
const float halfY3 = 0.5f * y3; | |||
return y1 + offset * ((0.5f * y2 - halfY0) | |||
+ (offset * (((y0 + 2.0f * y2) - (halfY3 + 2.5f * y1)) | |||
+ (offset * ((halfY3 + 1.5f * y1) - (halfY0 + 1.5f * y2)))))); | |||
} | |||
}; | |||
CatmullRomInterpolator::CatmullRomInterpolator() noexcept { reset(); } | |||
CatmullRomInterpolator::~CatmullRomInterpolator() noexcept {} | |||
void CatmullRomInterpolator::reset() noexcept | |||
{ | |||
subSamplePos = 1.0; | |||
for (int i = 0; i < numElementsInArray (lastInputSamples); ++i) | |||
lastInputSamples[i] = 0; | |||
} | |||
int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept | |||
{ | |||
return interpolate<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut); | |||
} | |||
int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept | |||
{ | |||
return interpolateAdding<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain); | |||
} |
@@ -0,0 +1,89 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
/** | |||
Interpolator for resampling a stream of floats using Catmull-Rom interpolation. | |||
Note that the resampler is stateful, so when there's a break in the continuity | |||
of the input stream you're feeding it, you should call reset() before feeding | |||
it any new data. And like with any other stateful filter, if you're resampling | |||
multiple channels, make sure each one uses its own CatmullRomInterpolator | |||
object. | |||
@see LagrangeInterpolator | |||
*/ | |||
class JUCE_API CatmullRomInterpolator | |||
{ | |||
public: | |||
CatmullRomInterpolator() noexcept; | |||
~CatmullRomInterpolator() noexcept; | |||
/** Resets the state of the interpolator. | |||
Call this when there's a break in the continuity of the input data stream. | |||
*/ | |||
void reset() noexcept; | |||
/** Resamples a stream of samples. | |||
@param speedRatio the number of input samples to use for each output sample | |||
@param inputSamples the source data to read from. This must contain at | |||
least (speedRatio * numOutputSamplesToProduce) samples. | |||
@param outputSamples the buffer to write the results into | |||
@param numOutputSamplesToProduce the number of output samples that should be created | |||
@returns the actual number of input samples that were used | |||
*/ | |||
int process (double speedRatio, | |||
const float* inputSamples, | |||
float* outputSamples, | |||
int numOutputSamplesToProduce) noexcept; | |||
/** Resamples a stream of samples, adding the results to the output data | |||
with a gain. | |||
@param speedRatio the number of input samples to use for each output sample | |||
@param inputSamples the source data to read from. This must contain at | |||
least (speedRatio * numOutputSamplesToProduce) samples. | |||
@param outputSamples the buffer to write the results to - the result values will be added | |||
to any pre-existing data in this buffer after being multiplied by | |||
the gain factor | |||
@param numOutputSamplesToProduce the number of output samples that should be created | |||
@param gain a gain factor to multiply the resulting samples by before | |||
adding them to the destination buffer | |||
@returns the actual number of input samples that were used | |||
*/ | |||
int processAdding (double speedRatio, | |||
const float* inputSamples, | |||
float* outputSamples, | |||
int numOutputSamplesToProduce, | |||
float gain) noexcept; | |||
private: | |||
float lastInputSamples[5]; | |||
double subSamplePos; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CatmullRomInterpolator) | |||
}; |
@@ -22,185 +22,179 @@ | |||
============================================================================== | |||
*/ | |||
namespace LagrangeHelpers | |||
namespace | |||
{ | |||
template <int k> | |||
struct ResampleHelper | |||
{ | |||
static forcedinline void calc (float& a, float b) { a *= b * (1.0f / k); } | |||
}; | |||
template<> | |||
struct ResampleHelper <0> | |||
{ | |||
static forcedinline void calc (float&, float) {} | |||
}; | |||
template <int k> | |||
static forcedinline float calcCoefficient (float input, const float offset) noexcept | |||
static forcedinline void pushInterpolationSample (float* lastInputSamples, const float newValue) noexcept | |||
{ | |||
ResampleHelper <0 - k>::calc (input, -2.0f - offset); | |||
ResampleHelper <1 - k>::calc (input, -1.0f - offset); | |||
ResampleHelper <2 - k>::calc (input, 0.0f - offset); | |||
ResampleHelper <3 - k>::calc (input, 1.0f - offset); | |||
ResampleHelper <4 - k>::calc (input, 2.0f - offset); | |||
return input; | |||
lastInputSamples[4] = lastInputSamples[3]; | |||
lastInputSamples[3] = lastInputSamples[2]; | |||
lastInputSamples[2] = lastInputSamples[1]; | |||
lastInputSamples[1] = lastInputSamples[0]; | |||
lastInputSamples[0] = newValue; | |||
} | |||
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept | |||
{ | |||
return calcCoefficient<0> (inputs[4], offset) | |||
+ calcCoefficient<1> (inputs[3], offset) | |||
+ calcCoefficient<2> (inputs[2], offset) | |||
+ calcCoefficient<3> (inputs[1], offset) | |||
+ calcCoefficient<4> (inputs[0], offset); | |||
} | |||
static forcedinline void push (float* inputs, const float newValue) noexcept | |||
{ | |||
inputs[4] = inputs[3]; | |||
inputs[3] = inputs[2]; | |||
inputs[2] = inputs[1]; | |||
inputs[1] = inputs[0]; | |||
inputs[0] = newValue; | |||
} | |||
} | |||
//============================================================================== | |||
LagrangeInterpolator::LagrangeInterpolator() { reset(); } | |||
LagrangeInterpolator::~LagrangeInterpolator() {} | |||
void LagrangeInterpolator::reset() noexcept | |||
{ | |||
subSamplePos = 1.0; | |||
for (int i = 0; i < numElementsInArray (lastInputSamples); ++i) | |||
lastInputSamples[i] = 0; | |||
} | |||
int LagrangeInterpolator::process (const double actualRatio, const float* in, | |||
float* out, const int numOut) noexcept | |||
{ | |||
if (actualRatio == 1.0) | |||
static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input, int numOut) noexcept | |||
{ | |||
memcpy (out, in, (size_t) numOut * sizeof (float)); | |||
if (numOut >= 4) | |||
if (numOut >= 5) | |||
{ | |||
const float* end = in + numOut; | |||
for (int i = 0; i < 4; ++i) | |||
lastInputSamples[i] = *--end; | |||
for (int i = 0; i < 5; ++i) | |||
lastInputSamples[i] = input[--numOut]; | |||
} | |||
else | |||
{ | |||
for (int i = 0; i < numOut; ++i) | |||
LagrangeHelpers::push (lastInputSamples, in[i]); | |||
pushInterpolationSample (lastInputSamples, input[i]); | |||
} | |||
return numOut; | |||
} | |||
const float* const originalIn = in; | |||
double pos = subSamplePos; | |||
if (actualRatio < 1.0) | |||
template <typename InterpolatorType> | |||
static int interpolate (float* lastInputSamples, double& subSamplePos, const double actualRatio, | |||
const float* in, float* out, const int numOut) noexcept | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
if (actualRatio == 1.0) | |||
{ | |||
if (pos >= 1.0) | |||
memcpy (out, in, (size_t) numOut * sizeof (float)); | |||
pushInterpolationSamples (lastInputSamples, in, numOut); | |||
return numOut; | |||
} | |||
const float* const originalIn = in; | |||
double pos = subSamplePos; | |||
if (actualRatio < 1.0) | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
LagrangeHelpers::push (lastInputSamples, *in++); | |||
pos -= 1.0; | |||
if (pos >= 1.0) | |||
{ | |||
pushInterpolationSample (lastInputSamples, *in++); | |||
pos -= 1.0; | |||
} | |||
*out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); | |||
pos += actualRatio; | |||
} | |||
*out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos); | |||
pos += actualRatio; | |||
} | |||
} | |||
else | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
else | |||
{ | |||
while (pos < actualRatio) | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
LagrangeHelpers::push (lastInputSamples, *in++); | |||
pos += 1.0; | |||
while (pos < actualRatio) | |||
{ | |||
pushInterpolationSample (lastInputSamples, *in++); | |||
pos += 1.0; | |||
} | |||
pos -= actualRatio; | |||
*out++ = InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); | |||
} | |||
pos -= actualRatio; | |||
*out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, 1.0f - (float) pos); | |||
} | |||
} | |||
subSamplePos = pos; | |||
return (int) (in - originalIn); | |||
} | |||
subSamplePos = pos; | |||
return (int) (in - originalIn); | |||
} | |||
int LagrangeInterpolator::processAdding (const double actualRatio, const float* in, | |||
float* out, const int numOut, const float gain) noexcept | |||
{ | |||
if (actualRatio == 1.0) | |||
template <typename InterpolatorType> | |||
static int interpolateAdding (float* lastInputSamples, double& subSamplePos, const double actualRatio, | |||
const float* in, float* out, const int numOut, const float gain) noexcept | |||
{ | |||
if (gain != 1.0f) | |||
{ | |||
for (int i = 0; i < numOut; ++i) | |||
out[i] += in[i] * gain; | |||
} | |||
else | |||
if (actualRatio == 1.0) | |||
{ | |||
for (int i = 0; i < numOut; ++i) | |||
out[i] += in[i]; | |||
FloatVectorOperations::addWithMultiply (out, in, gain, numOut); | |||
pushInterpolationSamples (lastInputSamples, in, numOut); | |||
return numOut; | |||
} | |||
if (numOut >= 4) | |||
{ | |||
const float* end = in + numOut; | |||
const float* const originalIn = in; | |||
double pos = subSamplePos; | |||
for (int i = 0; i < 4; ++i) | |||
lastInputSamples[i] = *--end; | |||
if (actualRatio < 1.0) | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
if (pos >= 1.0) | |||
{ | |||
pushInterpolationSample (lastInputSamples, *in++); | |||
pos -= 1.0; | |||
} | |||
*out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); | |||
pos += actualRatio; | |||
} | |||
} | |||
else | |||
{ | |||
for (int i = 0; i < numOut; ++i) | |||
LagrangeHelpers::push (lastInputSamples, in[i]); | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
while (pos < actualRatio) | |||
{ | |||
pushInterpolationSample (lastInputSamples, *in++); | |||
pos += 1.0; | |||
} | |||
pos -= actualRatio; | |||
*out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); | |||
} | |||
} | |||
return numOut; | |||
subSamplePos = pos; | |||
return (int) (in - originalIn); | |||
} | |||
} | |||
const float* const originalIn = in; | |||
double pos = subSamplePos; | |||
//============================================================================== | |||
template <int k> | |||
struct LagrangeResampleHelper | |||
{ | |||
static forcedinline void calc (float& a, float b) noexcept { a *= b * (1.0f / k); } | |||
}; | |||
if (actualRatio < 1.0) | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
if (pos >= 1.0) | |||
{ | |||
LagrangeHelpers::push (lastInputSamples, *in++); | |||
pos -= 1.0; | |||
} | |||
template<> | |||
struct LagrangeResampleHelper<0> | |||
{ | |||
static forcedinline void calc (float&, float) noexcept {} | |||
}; | |||
*out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos); | |||
pos += actualRatio; | |||
} | |||
} | |||
else | |||
struct LagrangeAlgorithm | |||
{ | |||
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
while (pos < actualRatio) | |||
{ | |||
LagrangeHelpers::push (lastInputSamples, *in++); | |||
pos += 1.0; | |||
} | |||
return calcCoefficient<0> (inputs[4], offset) | |||
+ calcCoefficient<1> (inputs[3], offset) | |||
+ calcCoefficient<2> (inputs[2], offset) | |||
+ calcCoefficient<3> (inputs[1], offset) | |||
+ calcCoefficient<4> (inputs[0], offset); | |||
} | |||
pos -= actualRatio; | |||
*out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); | |||
} | |||
template <int k> | |||
static forcedinline float calcCoefficient (float input, const float offset) noexcept | |||
{ | |||
LagrangeResampleHelper<0 - k>::calc (input, -2.0f - offset); | |||
LagrangeResampleHelper<1 - k>::calc (input, -1.0f - offset); | |||
LagrangeResampleHelper<2 - k>::calc (input, 0.0f - offset); | |||
LagrangeResampleHelper<3 - k>::calc (input, 1.0f - offset); | |||
LagrangeResampleHelper<4 - k>::calc (input, 2.0f - offset); | |||
return input; | |||
} | |||
}; | |||
LagrangeInterpolator::LagrangeInterpolator() noexcept { reset(); } | |||
LagrangeInterpolator::~LagrangeInterpolator() noexcept {} | |||
subSamplePos = pos; | |||
return (int) (in - originalIn); | |||
void LagrangeInterpolator::reset() noexcept | |||
{ | |||
subSamplePos = 1.0; | |||
for (int i = 0; i < numElementsInArray (lastInputSamples); ++i) | |||
lastInputSamples[i] = 0; | |||
} | |||
int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept | |||
{ | |||
return interpolate<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut); | |||
} | |||
int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept | |||
{ | |||
return interpolateAdding<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain); | |||
} |
@@ -22,11 +22,7 @@ | |||
============================================================================== | |||
*/ | |||
#ifndef JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED | |||
#define JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED | |||
//============================================================================== | |||
/** | |||
Interpolator for resampling a stream of floats using 4-point lagrange interpolation. | |||
@@ -35,12 +31,14 @@ | |||
it any new data. And like with any other stateful filter, if you're resampling | |||
multiple channels, make sure each one uses its own LagrangeInterpolator | |||
object. | |||
@see CatmullRomInterpolator | |||
*/ | |||
class JUCE_API LagrangeInterpolator | |||
{ | |||
public: | |||
LagrangeInterpolator(); | |||
~LagrangeInterpolator(); | |||
LagrangeInterpolator() noexcept; | |||
~LagrangeInterpolator() noexcept; | |||
/** Resets the state of the interpolator. | |||
Call this when there's a break in the continuity of the input data stream. | |||
@@ -89,6 +87,3 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator) | |||
}; | |||
#endif // JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED |
@@ -49,7 +49,7 @@ public: | |||
{ | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
/** Reset to a new sample rate and ramp length. */ | |||
void reset (double sampleRate, double rampLengthInSeconds) noexcept | |||
{ | |||
@@ -59,7 +59,7 @@ public: | |||
countdown = 0; | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
/** Set a new target value. */ | |||
void setValue (FloatType newValue) noexcept | |||
{ | |||
@@ -75,7 +75,7 @@ public: | |||
} | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
/** Compute the next value. */ | |||
FloatType getNextValue() noexcept | |||
{ | |||
@@ -88,7 +88,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
FloatType currentValue, target, step; | |||
int countdown, stepsToTarget; | |||
}; | |||
@@ -58,9 +58,7 @@ | |||
#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 | |||
@@ -81,6 +79,7 @@ namespace juce | |||
#include "effects/juce_IIRFilter.cpp" | |||
#include "effects/juce_IIRFilterOld.cpp" | |||
#include "effects/juce_LagrangeInterpolator.cpp" | |||
#include "effects/juce_CatmullRomInterpolator.cpp" | |||
#include "effects/juce_FFT.cpp" | |||
#include "midi/juce_MidiBuffer.cpp" | |||
#include "midi/juce_MidiFile.cpp" | |||
@@ -27,7 +27,7 @@ | |||
#include "../juce_core/juce_core.h" | |||
//============================================================================= | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -41,6 +41,7 @@ namespace juce | |||
#include "effects/juce_IIRFilter.h" | |||
#include "effects/juce_IIRFilterOld.h" | |||
#include "effects/juce_LagrangeInterpolator.h" | |||
#include "effects/juce_CatmullRomInterpolator.h" | |||
#include "effects/juce_FFT.h" | |||
#include "effects/juce_LinearSmoothedValue.h" | |||
#include "effects/juce_Reverb.h" | |||
@@ -371,7 +371,7 @@ int MidiMessage::getNoteNumber() const noexcept | |||
void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept | |||
{ | |||
if (isNoteOnOrOff()) | |||
if (isNoteOnOrOff() || isAftertouch()) | |||
getData()[1] = (uint8) (newNoteNumber & 127); | |||
} | |||
@@ -156,23 +156,34 @@ struct MidiMessageSequenceSorter | |||
} | |||
}; | |||
void MidiMessageSequence::addSequence (const MidiMessageSequence& other, double timeAdjustment) | |||
{ | |||
for (int i = 0; i < other.list.size(); ++i) | |||
{ | |||
const MidiMessage& m = other.list.getUnchecked(i)->message; | |||
MidiEventHolder* const newOne = new MidiEventHolder (m); | |||
newOne->message.addToTimeStamp (timeAdjustment); | |||
list.add (newOne); | |||
} | |||
sort(); | |||
} | |||
void MidiMessageSequence::addSequence (const MidiMessageSequence& other, | |||
double timeAdjustment, | |||
double firstAllowableTime, | |||
double endOfAllowableDestTimes) | |||
{ | |||
firstAllowableTime -= timeAdjustment; | |||
endOfAllowableDestTimes -= timeAdjustment; | |||
for (int i = 0; i < other.list.size(); ++i) | |||
{ | |||
const MidiMessage& m = other.list.getUnchecked(i)->message; | |||
const double t = m.getTimeStamp(); | |||
const double t = m.getTimeStamp() + timeAdjustment; | |||
if (t >= firstAllowableTime && t < endOfAllowableDestTimes) | |||
{ | |||
MidiEventHolder* const newOne = new MidiEventHolder (m); | |||
newOne->message.setTimeStamp (timeAdjustment + t); | |||
newOne->message.setTimeStamp (t); | |||
list.add (newOne); | |||
} | |||
@@ -160,7 +160,6 @@ public: | |||
void deleteEvent (int index, bool deleteMatchingNoteUp); | |||
/** Merges another sequence into this one. | |||
Remember to call updateMatchedPairs() after using this method. | |||
@param other the sequence to add from | |||
@@ -178,6 +177,16 @@ public: | |||
double firstAllowableDestTime, | |||
double endOfAllowableDestTimes); | |||
/** Merges another sequence into this one. | |||
Remember to call updateMatchedPairs() after using this method. | |||
@param other the sequence to add from | |||
@param timeAdjustmentDelta an amount to add to the timestamps of the midi events | |||
as they are read from the other sequence | |||
*/ | |||
void addSequence (const MidiMessageSequence& other, | |||
double timeAdjustmentDelta); | |||
//============================================================================== | |||
/** Makes sure all the note-on and note-off pairs are up-to-date. | |||
@@ -331,7 +331,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void expectContainsRPN (const MidiBuffer& midiBuffer, | |||
int channel, | |||
int parameterNumber, | |||
@@ -343,7 +343,7 @@ private: | |||
expectContainsRPN (midiBuffer, expected); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected) | |||
{ | |||
MidiBuffer::Iterator iter (midiBuffer); | |||
@@ -26,7 +26,7 @@ | |||
#define JUCE_MIDIRPNDETECTOR_H_INCLUDED | |||
//========================================================================== | |||
//============================================================================== | |||
/** Represents a MIDI RPN (registered parameter number) or NRPN (non-registered | |||
parameter number) message. | |||
*/ | |||
@@ -77,7 +77,7 @@ public: | |||
*/ | |||
void reset() noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Takes the next in a stream of incoming MIDI CC messages and returns true | |||
if it forms the last of a sequence that makes an RPN or NPRN. | |||
@@ -91,7 +91,7 @@ public: | |||
MidiRPNMessage& result) noexcept; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
struct ChannelState | |||
{ | |||
ChannelState() noexcept; | |||
@@ -104,7 +104,7 @@ private: | |||
bool isNRPN; | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
ChannelState states[16]; | |||
JUCE_LEAK_DETECTOR (MidiRPNDetector) | |||
@@ -120,11 +120,11 @@ private: | |||
class JUCE_API MidiRPNGenerator | |||
{ | |||
public: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Generates a MIDI sequence representing the given RPN or NRPN message. */ | |||
static MidiBuffer generate (MidiRPNMessage message); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Generates a MIDI sequence representing an RPN or NRPN message with the | |||
given parameters. | |||
@@ -135,14 +135,6 @@ void MPEInstrument::removeListener (Listener* const listenerToRemove) noexcept | |||
listeners.remove (listenerToRemove); | |||
} | |||
MPEInstrument::Listener::Listener() | |||
{ | |||
} | |||
MPEInstrument::Listener::~Listener() | |||
{ | |||
} | |||
//============================================================================== | |||
void MPEInstrument::processNextMidiEvent (const MidiMessage& message) | |||
{ | |||
@@ -1976,7 +1968,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
/* This mock class is used for unit testing whether the methods of | |||
MPEInstrument are called correctly. | |||
*/ | |||
@@ -2074,7 +2066,7 @@ private: | |||
ScopedPointer<MPENote> lastNoteFinished; | |||
private: | |||
//====================================================================== | |||
//============================================================================== | |||
void noteAdded (MPENote) override { noteAddedCallCounter++; } | |||
void notePressureChanged (MPENote) override { notePressureChangedCallCounter++; } | |||
@@ -2089,7 +2081,7 @@ private: | |||
} | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
template <int initial7BitPressure, int initial14BitPitchbend, int initial7BitTimbre> | |||
class CustomInitialValuesTest : public MPEInstrument | |||
{ | |||
@@ -2109,7 +2101,7 @@ private: | |||
} | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
void expectNote (MPENote noteToTest, | |||
int noteOnVelocity7Bit, | |||
int pressure7Bit, | |||
@@ -2141,7 +2133,7 @@ private: | |||
expect (std::fabs (expected - actual) < maxAbsoluteError); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
MPEZoneLayout testLayout; | |||
}; | |||
@@ -68,7 +68,7 @@ public: | |||
/** Destructor. */ | |||
virtual ~MPEInstrument(); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Returns the current zone layout of the instrument. | |||
This happens by value, to enforce thread-safety and class invariants. | |||
@@ -98,7 +98,7 @@ public: | |||
*/ | |||
bool isMasterChannel (int midiChannel) const noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** The MPE note tracking mode. In case there is more than one note playing | |||
simultaneously on the same MIDI channel, this determines which of these | |||
notes will be modulated by an incoming MPE message on that channel | |||
@@ -123,7 +123,7 @@ public: | |||
/** Set the MPE tracking mode for the timbre dimension. */ | |||
void setTimbreTrackingMode (TrackingMode modeToUse); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Process a MIDI message and trigger the appropriate method calls | |||
(noteOn, noteOff etc.) | |||
@@ -132,7 +132,7 @@ public: | |||
*/ | |||
virtual void processNextMidiEvent (const MidiMessage& message); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Request a note-on on the given channel, with the given initial note | |||
number and velocity. | |||
If the message arrives on a valid note channel, this will create a | |||
@@ -187,7 +187,7 @@ public: | |||
*/ | |||
void releaseAllNotes(); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Returns the number of MPE notes currently played by the | |||
instrument. | |||
*/ | |||
@@ -221,7 +221,7 @@ public: | |||
*/ | |||
MPENote getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Derive from this class to be informed about any changes in the expressive | |||
MIDI notes played by this instrument. | |||
@@ -230,14 +230,11 @@ public: | |||
Therefore you should never do heavy work such as graphics rendering etc. | |||
inside those callbacks. | |||
*/ | |||
class Listener | |||
class JUCE_API Listener | |||
{ | |||
public: | |||
/** Constructor. */ | |||
Listener(); | |||
/** Destructor. */ | |||
virtual ~Listener(); | |||
virtual ~Listener() {} | |||
/** Implement this callback to be informed whenever a new expressive | |||
MIDI note is triggered. | |||
@@ -278,14 +275,14 @@ public: | |||
virtual void noteReleased (MPENote finishedNote) = 0; | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Adds a listener. */ | |||
void addListener (Listener* const listenerToAdd) noexcept; | |||
void addListener (Listener* listenerToAdd) noexcept; | |||
/** Removes a listener. */ | |||
void removeListener (Listener* const listenerToRemove) noexcept; | |||
void removeListener (Listener* listenerToRemove) noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Puts the instrument into legacy mode. | |||
As a side effect, this will discard all currently playing notes, | |||
and call noteReleased for all of them. | |||
@@ -324,7 +321,7 @@ public: | |||
void setLegacyModePitchbendRange (int pitchbendRange); | |||
protected: | |||
//========================================================================== | |||
//============================================================================== | |||
/** This method defines what initial pitchbend value should be used for newly | |||
triggered notes. The default is to use the last pitchbend value | |||
that has been received on the same MIDI channel (or no pitchbend | |||
@@ -354,7 +351,7 @@ protected: | |||
MPEValue midiNoteOnVelocity) const; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
CriticalSection lock; | |||
Array<MPENote> notes; | |||
MPEZoneLayout zoneLayout; | |||
@@ -162,7 +162,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void testMidiBuffer (MidiBuffer& buffer, const uint8* expectedBytes, int expectedBytesSize) | |||
{ | |||
uint8 actualBytes[128] = { 0 }; | |||
@@ -171,7 +171,7 @@ private: | |||
expectEquals (std::memcmp (actualBytes, expectedBytes, (std::size_t) expectedBytesSize), 0); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void extractRawBinaryData (const MidiBuffer& midiBuffer, const uint8* bufferToCopyTo, std::size_t maxBytes) | |||
{ | |||
std::size_t pos = 0; | |||
@@ -103,7 +103,7 @@ class MPENoteTests : public UnitTest | |||
public: | |||
MPENoteTests() : UnitTest ("MPENote class") {} | |||
//========================================================================== | |||
//============================================================================== | |||
void runTest() override | |||
{ | |||
beginTest ("getFrequencyInHertz"); | |||
@@ -116,7 +116,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void expectEqualsWithinOneCent (double frequencyInHertzActual, | |||
double frequencyInHertzExpected) | |||
{ | |||
@@ -39,7 +39,7 @@ | |||
*/ | |||
struct JUCE_API MPENote | |||
{ | |||
//========================================================================== | |||
//============================================================================== | |||
enum KeyState | |||
{ | |||
off = 0, | |||
@@ -48,7 +48,7 @@ struct JUCE_API MPENote | |||
keyDownAndSustained = 3 | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Constructor. | |||
@param midiChannel The MIDI channel of the note, between 2 and 16. | |||
@@ -88,7 +88,7 @@ struct JUCE_API MPENote | |||
/** Checks whether the MPE note is valid. */ | |||
bool isValid() const noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
// Invariants that define the note. | |||
/** A unique ID. Useful to distinguish the note from other simultaneously | |||
@@ -107,7 +107,7 @@ struct JUCE_API MPENote | |||
*/ | |||
uint8 initialNote; | |||
//========================================================================== | |||
//============================================================================== | |||
// The five dimensions of continuous expressive control | |||
/** The velocity ("strike") of the note-on. | |||
@@ -146,7 +146,7 @@ struct JUCE_API MPENote | |||
*/ | |||
MPEValue noteOffVelocity; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Current effective pitchbend of the note in units of semitones, relative | |||
to initialNote. You should use this to compute the actual effective pitch | |||
of the note. This value is computed and set by an MPEInstrument to the | |||
@@ -163,7 +163,7 @@ struct JUCE_API MPENote | |||
*/ | |||
KeyState keyState; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Returns the current frequency of the note in Hertz. This is the a sum of | |||
the initialNote and the totalPitchbendInSemitones, converted to Hertz. | |||
*/ | |||
@@ -55,7 +55,7 @@ | |||
class JUCE_API MPESynthesiser : public MPESynthesiserBase | |||
{ | |||
public: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Constructor. | |||
You'll need to add some voices before it'll make any sound. | |||
@@ -75,7 +75,7 @@ public: | |||
/** Destructor. */ | |||
~MPESynthesiser(); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Deletes all voices. */ | |||
void clearVoices(); | |||
@@ -116,7 +116,7 @@ public: | |||
*/ | |||
virtual void turnOffAllVoices (bool allowTailOff); | |||
//========================================================================== | |||
//============================================================================== | |||
/** If set to true, then the synth will try to take over an existing voice if | |||
it runs out and needs to play another note. | |||
@@ -128,7 +128,7 @@ public: | |||
/** Returns true if note-stealing is enabled. */ | |||
bool isVoiceStealingEnabled() const noexcept { return shouldStealVoices; } | |||
//========================================================================== | |||
//============================================================================== | |||
/** Tells the synthesiser what the sample rate is for the audio it's being used to render. | |||
This overrides the implementation in MPESynthesiserBase, to additionally | |||
@@ -137,7 +137,7 @@ public: | |||
*/ | |||
void setCurrentPlaybackSampleRate (double newRate) override; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Handle incoming MIDI events. | |||
This method will be called automatically according to the MIDI data passed | |||
@@ -238,7 +238,7 @@ protected: | |||
*/ | |||
virtual void noteKeyStateChanged (MPENote changedNote) override; | |||
//========================================================================== | |||
//============================================================================== | |||
/** This will simply call renderNextBlock for each currently active | |||
voice and fill the buffer with the sum. | |||
Override this method if you need to do more work to render your audio. | |||
@@ -255,7 +255,7 @@ protected: | |||
int startSample, | |||
int numSamples) override; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Searches through the voices to find one that's not currently playing, and | |||
which can play the given MPE note. | |||
@@ -298,11 +298,11 @@ protected: | |||
*/ | |||
void stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff); | |||
//========================================================================== | |||
//============================================================================== | |||
OwnedArray<MPESynthesiserVoice> voices; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
bool shouldStealVoices; | |||
CriticalSection voicesLock; | |||
@@ -47,7 +47,7 @@ | |||
struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener | |||
{ | |||
public: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Constructor. */ | |||
MPESynthesiserBase(); | |||
@@ -61,7 +61,7 @@ public: | |||
*/ | |||
MPESynthesiserBase (MPEInstrument* instrument); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Returns the synthesiser's internal MPE zone layout. | |||
This happens by value, to enforce thread-safety and class invariants. | |||
*/ | |||
@@ -73,7 +73,7 @@ public: | |||
*/ | |||
void setZoneLayout (MPEZoneLayout newLayout); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Tells the synthesiser what the sample rate is for the audio it's being | |||
used to render. | |||
*/ | |||
@@ -84,7 +84,7 @@ public: | |||
*/ | |||
double getSampleRate() const noexcept { return sampleRate; } | |||
//========================================================================== | |||
//============================================================================== | |||
/** Creates the next block of audio output. | |||
Call this to make sound. This will chop up the AudioBuffer into subBlock | |||
@@ -99,7 +99,7 @@ public: | |||
int startSample, | |||
int numSamples); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Handle incoming MIDI events (called from renderNextBlock). | |||
The default implementation provided here simply forwards everything | |||
@@ -113,7 +113,7 @@ public: | |||
*/ | |||
virtual void handleMidiEvent (const MidiMessage&); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Sets a minimum limit on the size to which audio sub-blocks will be divided when rendering. | |||
When rendering, the audio blocks that are passed into renderNextBlock() will be split up | |||
@@ -130,7 +130,7 @@ public: | |||
*/ | |||
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Puts the synthesiser into legacy mode. | |||
@param pitchbendRange The note pitchbend range in semitones to use when in legacy mode. | |||
@@ -160,7 +160,7 @@ public: | |||
void setLegacyModePitchbendRange (int pitchbendRange); | |||
protected: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Implement this method to render your audio inside. | |||
@see renderNextBlock | |||
*/ | |||
@@ -176,14 +176,14 @@ protected: | |||
int /*numSamples*/) {} | |||
protected: | |||
//========================================================================== | |||
//============================================================================== | |||
/** @internal */ | |||
ScopedPointer<MPEInstrument> instrument; | |||
/** @internal */ | |||
CriticalSection renderAudioLock; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
double sampleRate; | |||
int minimumSubBlockSize; | |||
@@ -37,7 +37,7 @@ | |||
class JUCE_API MPESynthesiserVoice | |||
{ | |||
public: | |||
//======================================================================== | |||
//============================================================================== | |||
/** Constructor. */ | |||
MPESynthesiserVoice(); | |||
@@ -160,7 +160,7 @@ public: | |||
bool wasStartedBefore (const MPESynthesiserVoice& other) const noexcept; | |||
protected: | |||
//========================================================================== | |||
//============================================================================== | |||
/** 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 | |||
@@ -175,12 +175,12 @@ protected: | |||
*/ | |||
void clearCurrentNote() noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
double currentSampleRate; | |||
MPENote currentlyPlayingNote; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
friend class MPESynthesiser; | |||
uint32 noteStartTime; | |||
@@ -144,7 +144,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void expectValuesConsistent (MPEValue value, | |||
int expectedValueAs7BitInt, | |||
int expectedValueAs14BitInt, | |||
@@ -157,7 +157,7 @@ private: | |||
expectFloatWithinRelativeError (value.asUnsignedFloat(), expectedValueAsUnsignedFloat, 0.0001f); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void expectFloatWithinRelativeError (float actualValue, float expectedValue, float maxRelativeError) | |||
{ | |||
const float maxAbsoluteError = jmax (1.0f, std::fabs (expectedValue)) * maxRelativeError; | |||
@@ -37,7 +37,7 @@ | |||
class JUCE_API MPEValue | |||
{ | |||
public: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Default constructor. Constructs an MPEValue corresponding | |||
to the centre value. | |||
*/ | |||
@@ -87,7 +87,7 @@ public: | |||
bool operator!= (const MPEValue& other) const noexcept; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
MPEValue (int normalisedValue); | |||
int normalisedValue; | |||
}; | |||
@@ -144,7 +144,7 @@ bool MPEZone::truncateToFit (MPEZone other) noexcept | |||
return true; | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
bool MPEZone::operator== (const MPEZone& other) const noexcept | |||
{ | |||
return masterChannel == other.masterChannel | |||
@@ -284,7 +284,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void testOverlapsWith (int masterChannelFirst, int numNoteChannelsFirst, | |||
int masterChannelSecond, int numNoteChannelsSecond, | |||
bool expectedRetVal) | |||
@@ -296,7 +296,7 @@ private: | |||
expect (second.overlapsWith (first) == expectedRetVal); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void testTruncateToFit (int masterChannelFirst, int numNoteChannelsFirst, | |||
int masterChannelSecond, int numNoteChannelsSecond, | |||
bool expectedRetVal, | |||
@@ -127,7 +127,7 @@ struct JUCE_API MPEZone | |||
bool operator!= (const MPEZone& other) const noexcept; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
int masterChannel; | |||
int numNoteChannels; | |||
int perNotePitchbendRange; | |||
@@ -197,14 +197,6 @@ void MPEZoneLayout::removeListener (Listener* const listenerToRemove) noexcept | |||
listeners.remove (listenerToRemove); | |||
} | |||
MPEZoneLayout::Listener::Listener() | |||
{ | |||
} | |||
MPEZoneLayout::Listener::~Listener() | |||
{ | |||
} | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_UNIT_TESTS | |||
@@ -125,18 +125,15 @@ public: | |||
*/ | |||
MPEZone* getZoneByNoteChannel (int midiChannel) const noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Listener class. Derive from this class to allow your class to be | |||
notified about changes to the zone layout. | |||
*/ | |||
class Listener | |||
{ | |||
public: | |||
/** Constructor. */ | |||
Listener(); | |||
/** Destructor. */ | |||
virtual ~Listener(); | |||
virtual ~Listener() {} | |||
/** Implement this callback to be notified about any changes to this | |||
MPEZoneLayout. Will be called whenever a zone is added, zones are | |||
@@ -145,7 +142,7 @@ public: | |||
virtual void zoneLayoutChanged (const MPEZoneLayout& layout) = 0; | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Adds a listener. */ | |||
void addListener (Listener* const listenerToAdd) noexcept; | |||
@@ -153,7 +150,7 @@ public: | |||
void removeListener (Listener* const listenerToRemove) noexcept; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
Array<MPEZone> zones; | |||
MidiRPNDetector rpnDetector; | |||
ListenerList<Listener> listeners; | |||
@@ -46,7 +46,7 @@ public: | |||
@param source the input source to read from | |||
@param backgroundThread a background thread that will be used for the | |||
background read-ahead. This object must not be deleted | |||
until after any BufferedAudioSources that are using it | |||
until after any BufferingAudioSources that are using it | |||
have been deleted! | |||
@param deleteSourceWhenDeleted if true, then the input source object will | |||
be deleted when this object is deleted | |||
@@ -993,9 +993,9 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) | |||
class AudioSampleBufferSource : public PositionableAudioSource | |||
{ | |||
public: | |||
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer) | |||
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer, bool playOnAllChannels) | |||
: buffer (audioBuffer, ownBuffer), | |||
position (0), looping (false) | |||
position (0), looping (false), playAcrossAllChannels (playOnAllChannels) | |||
{} | |||
//============================================================================== | |||
@@ -1029,8 +1029,11 @@ public: | |||
if (samplesToCopy > 0) | |||
{ | |||
const int maxInChannels = buffer->getNumChannels(); | |||
const int maxOutChannels = jmin (bufferToFill.buffer->getNumChannels(), jmax (maxInChannels, 2)); | |||
int maxInChannels = buffer->getNumChannels(); | |||
int maxOutChannels = bufferToFill.buffer->getNumChannels(); | |||
if (! playAcrossAllChannels) | |||
maxOutChannels = jmin (maxOutChannels, maxInChannels); | |||
for (int i = 0; i < maxOutChannels; ++i) | |||
bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer, | |||
@@ -1047,7 +1050,7 @@ private: | |||
//============================================================================== | |||
OptionalScopedPointer<AudioSampleBuffer> buffer; | |||
int position; | |||
bool looping; | |||
bool looping, playAcrossAllChannels; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource) | |||
}; | |||
@@ -1080,10 +1083,10 @@ void AudioDeviceManager::playSound (AudioFormatReader* reader, bool deleteWhenFi | |||
playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true); | |||
} | |||
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished) | |||
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished, bool playOnAllOutputChannels) | |||
{ | |||
if (buffer != nullptr) | |||
playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished), true); | |||
playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished, playOnAllOutputChannels), true); | |||
} | |||
void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished) | |||
@@ -1134,7 +1137,7 @@ void AudioDeviceManager::playTestSound() | |||
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); | |||
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); | |||
playSound (newSound, true); | |||
playSound (newSound, true, true); | |||
} | |||
//============================================================================== | |||
@@ -445,8 +445,15 @@ public: | |||
This will output the sound contained in an audio sample buffer. If | |||
deleteWhenFinished is true then the audio sample buffer will be | |||
automatically deleted once the sound has finished playing. | |||
If playOnAllOutputChannels is true, then if there are more output channels | |||
than buffer channels, then the ones that are available will be re-used on | |||
multiple outputs so that something is sent to all output channels. If it | |||
is false, then the buffer will just be played on the first output channels. | |||
*/ | |||
void playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished = false); | |||
void playSound (AudioSampleBuffer* buffer, | |||
bool deleteWhenFinished = false, | |||
bool playOnAllOutputChannels = false); | |||
//============================================================================== | |||
/** Turns on level-measuring. | |||
@@ -31,7 +31,12 @@ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#include "../juce_core/native/juce_BasicNativeHeaders.h" | |||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1 | |||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 | |||
#include "juce_audio_devices.h" | |||
//============================================================================== | |||
@@ -141,7 +146,6 @@ namespace juce | |||
//============================================================================== | |||
#if JUCE_MAC | |||
#include "../juce_core/native/juce_osx_ObjCHelpers.h" | |||
#include "native/juce_mac_CoreAudio.cpp" | |||
#include "native/juce_mac_CoreMidi.cpp" | |||
@@ -160,8 +164,6 @@ namespace juce | |||
//============================================================================== | |||
#elif JUCE_WINDOWS | |||
#include "../juce_core/native/juce_win32_ComSmartPtr.h" | |||
#include "../juce_events/native/juce_win32_HiddenMessageWindow.h" | |||
#if JUCE_WASAPI | |||
#include "native/juce_win32_WASAPI.cpp" | |||
@@ -203,7 +205,6 @@ namespace juce | |||
//============================================================================== | |||
#elif JUCE_ANDROID | |||
#include "../juce_core/native/juce_android_JNIHelpers.h" | |||
#include "native/juce_android_Audio.cpp" | |||
#include "native/juce_android_Midi.cpp" | |||
@@ -29,7 +29,7 @@ | |||
#include "../juce_audio_basics/juce_audio_basics.h" | |||
#include "../juce_audio_formats/juce_audio_formats.h" | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_ASIO | |||
Enables ASIO audio devices (MS Windows only). | |||
Turning this on means that you'll need to have the Steinberg ASIO SDK installed | |||
@@ -90,7 +90,7 @@ | |||
#endif | |||
#endif | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_USE_CDREADER | |||
Enables the AudioCDReader class (on supported platforms). | |||
*/ | |||
@@ -105,7 +105,7 @@ | |||
#define JUCE_USE_CDBURNER 0 | |||
#endif | |||
//============================================================================= | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -195,25 +195,51 @@ public: | |||
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT, | |||
(jint) (minBufferSizeOut * numDeviceOutputChannels * sizeof (int16)), MODE_STREAM)); | |||
if (env->CallIntMethod (outputDevice, AudioTrack.getState) != STATE_UNINITIALIZED) | |||
int outputDeviceState = env->CallIntMethod (outputDevice, AudioTrack.getState); | |||
if (outputDeviceState > 0) | |||
{ | |||
isRunning = true; | |||
} | |||
else | |||
outputDevice.clear(); // failed to open the device | |||
{ | |||
// failed to open the device | |||
outputDevice.clear(); | |||
lastError = "Error opening audio output device: android.media.AudioTrack failed with state = " + String (outputDeviceState); | |||
} | |||
} | |||
if (numClientInputChannels > 0 && numDeviceInputChannelsAvailable > 0) | |||
{ | |||
numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable); | |||
inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor, | |||
0 /* (default audio source) */, sampleRate, | |||
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO, | |||
ENCODING_PCM_16BIT, | |||
(jint) (minBufferSizeIn * numDeviceInputChannels * sizeof (int16)))); | |||
if (env->CallIntMethod (inputDevice, AudioRecord.getState) != STATE_UNINITIALIZED) | |||
isRunning = true; | |||
if (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)) | |||
{ | |||
// If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio | |||
// before trying to open an audio input device. This is not going to work! | |||
jassertfalse; | |||
inputDevice.clear(); | |||
lastError = "Error opening audio input device: the app was not granted android.permission.RECORD_AUDIO"; | |||
} | |||
else | |||
inputDevice.clear(); // failed to open the device | |||
{ | |||
numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable); | |||
inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor, | |||
0 /* (default audio source) */, sampleRate, | |||
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO, | |||
ENCODING_PCM_16BIT, | |||
(jint) (minBufferSizeIn * numDeviceInputChannels * sizeof (int16)))); | |||
int inputDeviceState = env->CallIntMethod (inputDevice, AudioRecord.getState); | |||
if (inputDeviceState > 0) | |||
{ | |||
isRunning = true; | |||
} | |||
else | |||
{ | |||
// failed to open the device | |||
inputDevice.clear(); | |||
lastError = "Error opening audio input device: android.media.AudioRecord failed with state = " + String (inputDeviceState); | |||
} | |||
} | |||
} | |||
if (isRunning) | |||
@@ -368,7 +394,7 @@ public: | |||
int minBufferSizeOut, minBufferSizeIn; | |||
private: | |||
//================================================================================================== | |||
//============================================================================== | |||
CriticalSection callbackLock; | |||
AudioIODeviceCallback* callback; | |||
jint sampleRate; | |||
@@ -145,12 +145,32 @@ public: | |||
<< ", sampleRate = " << sampleRate); | |||
if (numInputChannels > 0) | |||
recorder = engine.createRecorder (numInputChannels, sampleRate, | |||
audioBuffersToEnqueue, actualBufferSize); | |||
{ | |||
if (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)) | |||
{ | |||
// If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio | |||
// before trying to open an audio input device. This is not going to work! | |||
jassertfalse; | |||
lastError = "Error opening OpenSL input device: the app was not granted android.permission.RECORD_AUDIO"; | |||
} | |||
else | |||
{ | |||
recorder = engine.createRecorder (numInputChannels, sampleRate, | |||
audioBuffersToEnqueue, actualBufferSize); | |||
if (recorder == nullptr) | |||
lastError = "Error opening OpenSL input device: creating Recorder failed."; | |||
} | |||
} | |||
if (numOutputChannels > 0) | |||
player = engine.createPlayer (numOutputChannels, sampleRate, | |||
audioBuffersToEnqueue, actualBufferSize); | |||
{ | |||
player = engine.createPlayer (numOutputChannels, sampleRate, | |||
audioBuffersToEnqueue, actualBufferSize); | |||
if (player == nullptr) | |||
lastError = "Error opening OpenSL input device: creating Player failed."; | |||
} | |||
// pre-fill buffers | |||
for (int i = 0; i < audioBuffersToEnqueue; ++i) | |||
@@ -220,7 +240,7 @@ public: | |||
} | |||
private: | |||
//================================================================================================== | |||
//============================================================================== | |||
CriticalSection callbackLock; | |||
AudioIODeviceCallback* callback; | |||
int actualBufferSize, sampleRate; | |||
@@ -242,7 +262,7 @@ private: | |||
defaultBufferSizeIsMultipleOfNative = 1 | |||
}; | |||
//================================================================================================== | |||
//============================================================================== | |||
static String audioManagerGetProperty (const String& property) | |||
{ | |||
const LocalRef<jstring> jProperty (javaString (property)); | |||
@@ -281,7 +301,7 @@ private: | |||
return androidHasSystemFeature ("android.hardware.audio.low_latency"); | |||
} | |||
//================================================================================================== | |||
//============================================================================== | |||
AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback) | |||
{ | |||
const ScopedLock sl (callbackLock); | |||
@@ -331,7 +351,7 @@ private: | |||
DBG ("Unable to set audio thread priority: priority is still " << priority); | |||
} | |||
//================================================================================================== | |||
//============================================================================== | |||
struct Engine | |||
{ | |||
Engine() | |||
@@ -400,7 +420,7 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine) | |||
}; | |||
//================================================================================================== | |||
//============================================================================== | |||
struct BufferList | |||
{ | |||
BufferList (const int numChannels_, const int numBuffers_, const int numSamples_) | |||
@@ -444,7 +464,7 @@ private: | |||
WaitableEvent dataArrived; | |||
}; | |||
//================================================================================================== | |||
//============================================================================== | |||
struct Player | |||
{ | |||
Player (int numChannels, int sampleRate, Engine& engine, int playerNumBuffers, int playerBufferSize) | |||
@@ -559,7 +579,7 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player) | |||
}; | |||
//================================================================================================== | |||
//============================================================================== | |||
struct Recorder | |||
{ | |||
Recorder (int numChannels, int sampleRate, Engine& engine, const int numBuffers, const int numSamples) | |||
@@ -24,7 +24,9 @@ | |||
class iOSAudioIODevice; | |||
//================================================================================================== | |||
static const char* const iOSAudioDeviceName = "iOS Audio"; | |||
//============================================================================== | |||
struct AudioSessionHolder | |||
{ | |||
AudioSessionHolder(); | |||
@@ -74,7 +76,7 @@ bool getNotificationValueForKey (NSNotification* notification, NSString* key, NS | |||
} // juce namespace | |||
//================================================================================================== | |||
//============================================================================== | |||
@interface iOSAudioSessionNative : NSObject | |||
{ | |||
@private | |||
@@ -178,7 +180,7 @@ bool getNotificationValueForKey (NSNotification* notification, NSString* key, NS | |||
@end | |||
//================================================================================================== | |||
//============================================================================== | |||
namespace juce { | |||
#ifndef JUCE_IOS_AUDIO_LOGGING | |||
@@ -203,11 +205,12 @@ static void logNSError (NSError* e) | |||
#define JUCE_NSERROR_CHECK(X) { NSError* error = nil; X; logNSError (error); } | |||
//================================================================================================== | |||
//============================================================================== | |||
class iOSAudioIODevice : public AudioIODevice | |||
{ | |||
public: | |||
iOSAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, "Audio") | |||
iOSAudioIODevice (const String& deviceName) | |||
: AudioIODevice (deviceName, iOSAudioDeviceName) | |||
{ | |||
sessionHolder->activeDevices.add (this); | |||
updateSampleRateAndAudioInput(); | |||
@@ -350,13 +353,14 @@ public: | |||
{ | |||
isRunning = false; | |||
setAudioSessionActive (false); | |||
if (audioUnit != 0) | |||
{ | |||
AudioOutputUnitStart (audioUnit); | |||
AudioComponentInstanceDispose (audioUnit); | |||
audioUnit = 0; | |||
} | |||
setAudioSessionActive (false); | |||
} | |||
} | |||
@@ -463,13 +467,13 @@ public: | |||
AudioOutputUnitStart (audioUnit); | |||
} | |||
if (callback) | |||
if (callback != nullptr) | |||
callback->audioDeviceAboutToStart (this); | |||
} | |||
} | |||
private: | |||
//================================================================================================== | |||
//============================================================================== | |||
SharedResourcePointer<AudioSessionHolder> sessionHolder; | |||
CriticalSection callbackLock; | |||
NSTimeInterval sampleRate = 0; | |||
@@ -505,7 +509,7 @@ private: | |||
} | |||
} | |||
//================================================================================================== | |||
//============================================================================== | |||
OSStatus process (AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, | |||
const UInt32 numFrames, AudioBufferList* data) | |||
{ | |||
@@ -592,21 +596,22 @@ private: | |||
auto session = [AVAudioSession sharedInstance]; | |||
sampleRate = session.sampleRate; | |||
audioInputIsAvailable = session.isInputAvailable; | |||
JUCE_IOS_AUDIO_LOG ("AVAudioSession: sampleRate: " << sampleRate << "Hz, audioInputAvailable: " << (int) audioInputIsAvailable); | |||
actualBufferSize = roundToInt (sampleRate * session.IOBufferDuration); | |||
JUCE_IOS_AUDIO_LOG ("AVAudioSession: sampleRate: " << sampleRate | |||
<< "Hz, audioInputAvailable: " << (int) audioInputIsAvailable); | |||
} | |||
void updateCurrentBufferSize() | |||
{ | |||
auto session = [AVAudioSession sharedInstance]; | |||
NSTimeInterval bufferDuration = sampleRate > 0 ? (NSTimeInterval) (preferredBufferSize / sampleRate) : 0.0; | |||
JUCE_NSERROR_CHECK ([session setPreferredIOBufferDuration: bufferDuration | |||
error: &error]); | |||
NSTimeInterval bufferDuration = sampleRate > 0 ? (NSTimeInterval) ((preferredBufferSize + 1) / sampleRate) : 0.0; | |||
bufferDuration = session.IOBufferDuration; | |||
actualBufferSize = roundToInt (sampleRate * bufferDuration); | |||
JUCE_NSERROR_CHECK ([[AVAudioSession sharedInstance] setPreferredIOBufferDuration: bufferDuration | |||
error: &error]); | |||
updateSampleRateAndAudioInput(); | |||
} | |||
//================================================================================================== | |||
//============================================================================== | |||
static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, | |||
UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) | |||
{ | |||
@@ -614,7 +619,7 @@ private: | |||
return static_cast<iOSAudioIODevice*> (client)->process (flags, time, numFrames, data); | |||
} | |||
//================================================================================================== | |||
//============================================================================== | |||
void resetFormat (const int numChannels) noexcept | |||
{ | |||
zerostruct (format); | |||
@@ -712,10 +717,10 @@ private: | |||
class iOSAudioIODeviceType : public AudioIODeviceType | |||
{ | |||
public: | |||
iOSAudioIODeviceType() : AudioIODeviceType ("iOS Audio") {} | |||
iOSAudioIODeviceType() : AudioIODeviceType (iOSAudioDeviceName) {} | |||
void scanForDevices() {} | |||
StringArray getDeviceNames (bool /*wantInputNames*/) const { return StringArray ("iOS Audio"); } | |||
StringArray getDeviceNames (bool /*wantInputNames*/) const { return StringArray (iOSAudioDeviceName); } | |||
int getDefaultDeviceIndex (bool /*forInput*/) const { return 0; } | |||
int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const { return d != nullptr ? 0 : -1; } | |||
bool hasSeparateInputsAndOutputs() const { return false; } | |||
@@ -738,7 +743,7 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() | |||
return new iOSAudioIODeviceType(); | |||
} | |||
//================================================================================================== | |||
//============================================================================== | |||
AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; } | |||
AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; } | |||
@@ -31,6 +31,12 @@ | |||
#ifdef __clang__ | |||
#pragma clang diagnostic push | |||
#pragma clang diagnostic ignored "-Wnonnull" // aovid some spurious 10.11 SDK warnings | |||
// The AudioHardwareService stuff was deprecated in 10.11 but there's no replacement yet, | |||
// so we'll have to silence the warnings here and revisit it in a future OS version.. | |||
#if defined (MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_11 | |||
#pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||
#endif | |||
#endif | |||
//============================================================================== | |||
@@ -382,65 +388,84 @@ public: | |||
return 0; | |||
} | |||
void updateDetailsFromDevice() | |||
int getFrameSizeFromDevice() const | |||
{ | |||
stopTimer(); | |||
AudioObjectPropertyAddress pa; | |||
pa.mScope = kAudioObjectPropertyScopeWildcard; | |||
pa.mElement = kAudioObjectPropertyElementMaster; | |||
pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |||
if (deviceID == 0) | |||
return; | |||
UInt32 framesPerBuf = (UInt32) bufferSize; | |||
UInt32 size = sizeof (framesPerBuf); | |||
AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf); | |||
return (int) framesPerBuf; | |||
} | |||
// this collects all the new details from the device without any locking, then | |||
// locks + swaps them afterwards. | |||
bool isDeviceAlive() const | |||
{ | |||
AudioObjectPropertyAddress pa; | |||
pa.mScope = kAudioObjectPropertyScopeWildcard; | |||
pa.mElement = kAudioObjectPropertyElementMaster; | |||
pa.mSelector = kAudioDevicePropertyDeviceIsAlive; | |||
UInt32 isAlive; | |||
UInt32 isAlive = 0; | |||
UInt32 size = sizeof (isAlive); | |||
pa.mSelector = kAudioDevicePropertyDeviceIsAlive; | |||
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive)) && isAlive == 0) | |||
return; | |||
return deviceID != 0 | |||
&& OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive)) | |||
&& isAlive != 0; | |||
} | |||
const double currentRate = getNominalSampleRate(); | |||
if (currentRate > 0) | |||
sampleRate = currentRate; | |||
bool updateDetailsFromDevice() | |||
{ | |||
stopTimer(); | |||
UInt32 framesPerBuf = (UInt32) bufferSize; | |||
size = sizeof (framesPerBuf); | |||
pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |||
AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf); | |||
if (! isDeviceAlive()) | |||
return false; | |||
// this collects all the new details from the device without any locking, then | |||
// locks + swaps them afterwards. | |||
Array<int> newBufferSizes (getBufferSizesFromDevice()); | |||
Array<double> newSampleRates (getSampleRatesFromDevice()); | |||
const double newSampleRate = getNominalSampleRate(); | |||
const int newBufferSize = getFrameSizeFromDevice(); | |||
inputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput); | |||
outputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput); | |||
Array<int> newBufferSizes = getBufferSizesFromDevice(); | |||
Array<double> newSampleRates = getSampleRatesFromDevice(); | |||
const int newInputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput); | |||
const int newOutputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput); | |||
Array<CallbackDetailsForChannel> newInChans, newOutChans; | |||
StringArray newInNames (getChannelInfo (true, newInChans)); | |||
StringArray newOutNames (getChannelInfo (false, newOutChans)); | |||
const int inputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeInput); | |||
const int outputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeOutput); | |||
const int newBitDepth = jmax (getBitDepthFromDevice (kAudioDevicePropertyScopeInput), | |||
getBitDepthFromDevice (kAudioDevicePropertyScopeOutput)); | |||
{ | |||
const ScopedLock sl (callbackLock); | |||
bitDepth = jmax (inputBitDepth, outputBitDepth); | |||
if (bitDepth <= 0) | |||
bitDepth = 32; | |||
bitDepth = newBitDepth > 0 ? newBitDepth : 32; | |||
// after getting the new values, lock + apply them | |||
const ScopedLock sl (callbackLock); | |||
if (newSampleRate > 0) | |||
sampleRate = newSampleRate; | |||
bufferSize = (int) framesPerBuf; | |||
allocateTempBuffers(); | |||
inputLatency = newInputLatency; | |||
outputLatency = newOutputLatency; | |||
bufferSize = newBufferSize; | |||
sampleRates.swapWith (newSampleRates); | |||
bufferSizes.swapWith (newBufferSizes); | |||
sampleRates.swapWith (newSampleRates); | |||
bufferSizes.swapWith (newBufferSizes); | |||
inChanNames.swapWith (newInNames); | |||
outChanNames.swapWith (newOutNames); | |||
inChanNames.swapWith (newInNames); | |||
outChanNames.swapWith (newOutNames); | |||
inputChannelInfo.swapWith (newInChans); | |||
outputChannelInfo.swapWith (newOutChans); | |||
inputChannelInfo.swapWith (newInChans); | |||
outputChannelInfo.swapWith (newOutChans); | |||
allocateTempBuffers(); | |||
} | |||
return true; | |||
} | |||
//============================================================================== | |||
@@ -763,9 +788,10 @@ public: | |||
stopTimer(); | |||
const double oldSampleRate = sampleRate; | |||
const int oldBufferSize = bufferSize; | |||
updateDetailsFromDevice(); | |||
if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) | |||
if (! updateDetailsFromDevice()) | |||
owner.stop(); | |||
else if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) | |||
owner.restart(); | |||
} | |||
@@ -320,9 +320,9 @@ public: | |||
classId (clsID), | |||
inputLatency (0), | |||
outputLatency (0), | |||
minSize (0), maxSize (0), | |||
preferredSize (0), | |||
granularity (0), | |||
minBufferSize (0), maxBufferSize (0), | |||
preferredBufferSize (0), | |||
bufferGranularity (0), | |||
numClockSources (0), | |||
currentBlockSizeSamples (0), | |||
currentBitDepth (16), | |||
@@ -403,7 +403,7 @@ public: | |||
Array<double> getAvailableSampleRates() override { return sampleRates; } | |||
Array<int> getAvailableBufferSizes() override { return bufferSizes; } | |||
int getDefaultBufferSize() override { return preferredSize; } | |||
int getDefaultBufferSize() override { return preferredBufferSize; } | |||
String open (const BigInteger& inputChannels, | |||
const BigInteger& outputChannels, | |||
@@ -469,10 +469,10 @@ public: | |||
removeCurrentDriver(); | |||
loadDriver(); | |||
const String error (initDriver()); | |||
String initError = initDriver(); | |||
if (error.isNotEmpty()) | |||
JUCE_ASIO_LOG ("ASIOInit: " + error); | |||
if (initError.isNotEmpty()) | |||
JUCE_ASIO_LOG ("ASIOInit: " + initError); | |||
needToReset = false; | |||
} | |||
@@ -489,7 +489,7 @@ public: | |||
if (err != ASE_OK) | |||
{ | |||
currentBlockSizeSamples = preferredSize; | |||
currentBlockSizeSamples = preferredBufferSize; | |||
JUCE_ASIO_LOG_ERROR ("create buffers 2", err); | |||
asioObject->disposeBuffers(); | |||
@@ -561,8 +561,7 @@ public: | |||
} | |||
readLatencies(); | |||
asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); | |||
refreshBufferSizes(); | |||
deviceIsOpen = true; | |||
JUCE_ASIO_LOG ("starting"); | |||
@@ -762,7 +761,7 @@ private: | |||
Array<double> sampleRates; | |||
Array<int> bufferSizes; | |||
long inputLatency, outputLatency; | |||
long minSize, maxSize, preferredSize, granularity; | |||
long minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity; | |||
ASIOClockSource clocks[32]; | |||
int numClockSources; | |||
@@ -829,23 +828,27 @@ private: | |||
} | |||
} | |||
int readBufferSizes (int bufferSizeSamples) | |||
long refreshBufferSizes() | |||
{ | |||
minSize = 0; | |||
maxSize = 0; | |||
granularity = 0; | |||
return asioObject->getBufferSize (&minBufferSize, &maxBufferSize, &preferredBufferSize, &bufferGranularity); | |||
} | |||
int readBufferSizes (int bufferSizeSamples) | |||
{ | |||
minBufferSize = 0; | |||
maxBufferSize = 0; | |||
bufferGranularity = 0; | |||
long newPreferredSize = 0; | |||
if (asioObject->getBufferSize (&minSize, &maxSize, &newPreferredSize, &granularity) == ASE_OK) | |||
if (asioObject->getBufferSize (&minBufferSize, &maxBufferSize, &newPreferredSize, &bufferGranularity) == ASE_OK) | |||
{ | |||
if (preferredSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredSize) | |||
if (preferredBufferSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredBufferSize) | |||
shouldUsePreferredSize = true; | |||
if (bufferSizeSamples < minSize || bufferSizeSamples > maxSize) | |||
if (bufferSizeSamples < minBufferSize || bufferSizeSamples > maxBufferSize) | |||
shouldUsePreferredSize = true; | |||
preferredSize = newPreferredSize; | |||
preferredBufferSize = newPreferredSize; | |||
} | |||
// unfortunate workaround for certain drivers which crash if you make | |||
@@ -855,11 +858,11 @@ private: | |||
if (shouldUsePreferredSize) | |||
{ | |||
JUCE_ASIO_LOG ("Using preferred size for buffer.."); | |||
long err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); | |||
long err = refreshBufferSizes(); | |||
if (err == ASE_OK) | |||
{ | |||
bufferSizeSamples = (int) preferredSize; | |||
bufferSizeSamples = (int) preferredBufferSize; | |||
} | |||
else | |||
{ | |||
@@ -1082,8 +1085,8 @@ private: | |||
if (i < 2) | |||
{ | |||
// clear the channels that are used with the dummy stuff | |||
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredSize); | |||
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredSize); | |||
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredBufferSize); | |||
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredBufferSize); | |||
} | |||
} | |||
} | |||
@@ -1203,9 +1206,9 @@ private: | |||
inputFormat.calloc (chansToAllocate); | |||
outputFormat.calloc (chansToAllocate); | |||
if ((err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity)) == 0) | |||
if ((err = refreshBufferSizes()) == 0) | |||
{ | |||
addBufferSizes (minSize, maxSize, preferredSize, granularity); | |||
addBufferSizes (minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity); | |||
double currentRate = getSampleRate(); | |||
@@ -1226,8 +1229,8 @@ private: | |||
updateSampleRates(); | |||
readLatencies(); // ..doing these steps because cubase does so at this stage | |||
createDummyBuffers (preferredSize); // in initialisation, and some devices fail if we don't. | |||
readLatencies(); // ..doing these steps because cubase does so at this stage | |||
createDummyBuffers (preferredBufferSize); // in initialisation, and some devices fail if we don't. | |||
readLatencies(); | |||
// start and stop because cubase does it.. | |||
@@ -1419,6 +1422,12 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice) | |||
}; | |||
template <> | |||
struct ASIOAudioIODevice::ASIOCallbackFunctions <sizeof(currentASIODev) / sizeof(currentASIODev[0])> | |||
{ | |||
static void setCallbacksForDevice (ASIOCallbacks&, ASIOAudioIODevice*) noexcept {} | |||
}; | |||
//============================================================================== | |||
class ASIOAudioIODeviceType : public AudioIODeviceType | |||
{ | |||
@@ -112,6 +112,11 @@ bool check (HRESULT hr) | |||
#define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name | |||
#endif | |||
#if JUCE_MINGW && defined (KSDATAFORMAT_SUBTYPE_PCM) | |||
#undef KSDATAFORMAT_SUBTYPE_PCM | |||
#undef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT | |||
#endif | |||
#ifndef KSDATAFORMAT_SUBTYPE_PCM | |||
#define KSDATAFORMAT_SUBTYPE_PCM uuidFromString ("00000001-0000-0010-8000-00aa00389b71") | |||
#define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71") | |||
@@ -33,7 +33,7 @@ | |||
#ifndef FLAC__PRIVATE__METADATA_H | |||
#define FLAC__PRIVATE__METADATA_H | |||
#include "FLAC/metadata.h" | |||
#include "../../../metadata.h" | |||
/* WATCHOUT: all malloc()ed data in the block is free()ed; this may not | |||
* be a consistent state (e.g. PICTURE) or equivalent to the initial | |||
@@ -112,19 +112,28 @@ struct CoreAudioFormatMetatdata | |||
}; | |||
//============================================================================== | |||
struct UserDefinedChunk | |||
static StringPairArray parseUserDefinedChunk (InputStream& input, int64 size) | |||
{ | |||
UserDefinedChunk (InputStream& input, int64 size) | |||
StringPairArray infoStrings; | |||
const int64 originalPosition = input.getPosition(); | |||
uint8 uuid[16]; | |||
input.read (uuid, sizeof (uuid)); | |||
if (memcmp (uuid, "\x29\x81\x92\x73\xB5\xBF\x4A\xEF\xB7\x8D\x62\xD1\xEF\x90\xBB\x2C", 16) == 0) | |||
{ | |||
// a user defined chunk contains 16 bytes of a UUID first | |||
uuid[1] = input.readInt64BigEndian(); | |||
uuid[0] = input.readInt64BigEndian(); | |||
const uint32 numEntries = (uint32) input.readIntBigEndian(); | |||
input.skipNextBytes (size - 16); | |||
for (uint32 i = 0; i < numEntries && input.getPosition() < originalPosition + size; ++i) | |||
{ | |||
String keyName = input.readString(); | |||
infoStrings.set (keyName, input.readString()); | |||
} | |||
} | |||
int64 uuid[2]; | |||
}; | |||
input.setPosition (originalPosition + size); | |||
return infoStrings; | |||
} | |||
//============================================================================== | |||
static StringPairArray parseMidiChunk (InputStream& input, int64 size) | |||
@@ -288,7 +297,7 @@ struct CoreAudioFormatMetatdata | |||
} | |||
else if (chunkHeader.chunkType == chunkName ("uuid")) | |||
{ | |||
UserDefinedChunk userDefinedChunk (input, chunkHeader.chunkSize); | |||
metadataValues.addArray (parseUserDefinedChunk (input, chunkHeader.chunkSize)); | |||
} | |||
else if (chunkHeader.chunkType == chunkName ("data")) | |||
{ | |||
@@ -27,15 +27,11 @@ | |||
} // (juce namespace) | |||
#if ! JUCE_WINDOWS | |||
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) | |||
#define Component CarbonDummyCompName | |||
#include <QuickTime/Movies.h> | |||
#include <QuickTime/QTML.h> | |||
#include <QuickTime/QuickTimeComponents.h> | |||
#include <QuickTime/MediaHandlers.h> | |||
#include <QuickTime/ImageCodec.h> | |||
#undef Point | |||
#undef Component | |||
#else | |||
#if JUCE_MSVC | |||
#pragma warning (push) | |||
@@ -31,19 +31,18 @@ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#include "../juce_core/native/juce_BasicNativeHeaders.h" | |||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1 | |||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
#include "juce_audio_formats.h" | |||
//============================================================================== | |||
#if JUCE_MAC | |||
#define Point CarbonDummyPointName | |||
#define Component CarbonDummyCompName | |||
#if JUCE_QUICKTIME | |||
#import <QTKit/QTKit.h> | |||
#endif | |||
#include <AudioToolbox/AudioToolbox.h> | |||
#undef Component | |||
#undef Point | |||
#elif JUCE_IOS | |||
#import <AudioToolbox/AudioToolbox.h> | |||
@@ -85,14 +84,9 @@ namespace juce | |||
{ | |||
#if JUCE_ANDROID | |||
#include "../juce_core/native/juce_android_JNIHelpers.h" | |||
#undef JUCE_QUICKTIME | |||
#endif | |||
#if JUCE_WINDOWS | |||
#include "../juce_core/native/juce_win32_ComSmartPtr.h" | |||
#endif | |||
#include "format/juce_AudioFormat.cpp" | |||
#include "format/juce_AudioFormatManager.cpp" | |||
#include "format/juce_AudioFormatReader.cpp" | |||
@@ -27,7 +27,7 @@ | |||
#include "../juce_audio_basics/juce_audio_basics.h" | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_USE_FLAC | |||
Enables the FLAC audio codec classes (available on all platforms). | |||
If your app doesn't need to read FLAC files, you might want to disable this to | |||
@@ -81,7 +81,7 @@ | |||
#define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 | |||
#endif | |||
//============================================================================= | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -29,8 +29,8 @@ | |||
#include "../utility/juce_IncludeSystemHeaders.h" | |||
#include "../utility/juce_IncludeModuleHeaders.h" | |||
#include "../utility/juce_WindowsHooks.h" | |||
#include "../utility/juce_PluginBusUtilities.h" | |||
#undef Component | |||
#ifdef __clang__ | |||
#pragma clang diagnostic push | |||
@@ -40,7 +40,7 @@ | |||
#ifdef _MSC_VER | |||
#pragma warning (push) | |||
#pragma warning (disable : 4127) | |||
#pragma warning (disable : 4127 4512) | |||
#endif | |||
#include "AAX_Exports.cpp" | |||
@@ -93,8 +93,6 @@ | |||
#undef check | |||
using juce::Component; | |||
const int32_t juceChunkType = 'juce'; | |||
//============================================================================== | |||
@@ -115,6 +113,15 @@ struct AAXClasses | |||
return AAX::IsParameterIDEqual (paramID, cDefaultMasterBypassID) != 0; | |||
} | |||
// maps a channel index of an AAX format to an index of a juce format | |||
struct AAXChannelStreamOrder | |||
{ | |||
AAX_EStemFormat aaxStemFormat; | |||
AudioChannelSet::ChannelType speakerOrder[8]; | |||
}; | |||
static AAXChannelStreamOrder aaxChannelOrder[]; | |||
static AAX_EStemFormat getFormatForAudioChannelSet (const AudioChannelSet& set, bool ignoreLayout) noexcept | |||
{ | |||
// if the plug-in ignores layout, it is ok to convert between formats only by their numchannnels | |||
@@ -223,6 +230,24 @@ struct AAXClasses | |||
return Colours::black; | |||
} | |||
static int juceChannelIndexToAax (int juceIndex, const AudioChannelSet& channelSet) | |||
{ | |||
AAX_EStemFormat currentLayout = getFormatForAudioChannelSet (channelSet, false); | |||
int layoutIndex; | |||
for (layoutIndex = 0; aaxChannelOrder[layoutIndex].aaxStemFormat != currentLayout; ++layoutIndex) | |||
if (aaxChannelOrder[layoutIndex].aaxStemFormat == 0) return juceIndex; | |||
const AAXChannelStreamOrder& channelOrder = aaxChannelOrder[layoutIndex]; | |||
const AudioChannelSet::ChannelType channelType = channelSet.getTypeOfChannel (static_cast<int> (juceIndex)); | |||
for (int i = 0; i < 8 && channelOrder.speakerOrder[i] != 0; ++i) | |||
if (channelOrder.speakerOrder[i] == channelType) | |||
return i; | |||
return juceIndex; | |||
} | |||
//============================================================================== | |||
class JuceAAX_Processor; | |||
@@ -381,7 +406,7 @@ struct AAXClasses | |||
} | |||
private: | |||
struct ContentWrapperComponent : public juce::Component | |||
struct ContentWrapperComponent : public Component | |||
{ | |||
ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor& plugin) | |||
: owner (gui) | |||
@@ -449,6 +474,10 @@ struct AAXClasses | |||
ScopedPointer<AudioProcessorEditor> pluginEditor; | |||
JuceAAX_GUI& owner; | |||
#if JUCE_WINDOWS | |||
WindowsHooks hooks; | |||
#endif | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent) | |||
}; | |||
@@ -839,7 +868,7 @@ struct AAXClasses | |||
jassert (idx < (mainNumIns + 1)); | |||
if (idx < mainNumIns) | |||
return inputs[idx]; | |||
return inputs[inputLayoutMap[idx]]; | |||
return (sidechain != -1 ? inputs[sidechain] : sideChainBuffer.getData()); | |||
} | |||
@@ -860,26 +889,32 @@ struct AAXClasses | |||
{ | |||
const int mainNumIns = numIns > 0 ? pluginInstance->busArrangement.inputBuses.getReference (0).channels.size() : 0; | |||
const int sidechain = busUtils.getNumEnabledBuses (true) >= 2 ? sideChainBufferIdx : -1; | |||
const int numChans = jmax (numIns, numOuts); | |||
if (numChans == 0) return; | |||
if (channelList.size() <= numChans) | |||
channelList.insertMultiple (-1, nullptr, 1 + numChans - channelList.size()); | |||
float** channels = channelList.getRawDataPointer(); | |||
if (numOuts >= numIns) | |||
{ | |||
for (int i = 0; i < numOuts; ++i) | |||
channels[i] = outputs[outputLayoutMap[i]]; | |||
for (int i = 0; i < numIns; ++i) | |||
memcpy (outputs[i], getAudioBufferForInput (inputs, sidechain, mainNumIns, i), (size_t) bufferSize * sizeof (float)); | |||
memcpy (channels[i], getAudioBufferForInput (inputs, sidechain, mainNumIns, i), (size_t) bufferSize * sizeof (float)); | |||
process (outputs, numOuts, bufferSize, bypass, midiNodeIn, midiNodesOut); | |||
process (channels, numOuts, bufferSize, bypass, midiNodeIn, midiNodesOut); | |||
} | |||
else | |||
{ | |||
if (channelList.size() <= numIns) | |||
channelList.insertMultiple (-1, nullptr, 1 + numIns - channelList.size()); | |||
float** channels = channelList.getRawDataPointer(); | |||
for (int i = 0; i < numOuts; ++i) | |||
channels[i] = outputs[outputLayoutMap[i]]; | |||
for (int i = 0; i < numOuts; ++i) | |||
{ | |||
memcpy (outputs[i], getAudioBufferForInput (inputs, sidechain, mainNumIns, i), (size_t) bufferSize * sizeof (float)); | |||
channels[i] = outputs[i]; | |||
} | |||
memcpy (channels[i], getAudioBufferForInput (inputs, sidechain, mainNumIns, i), (size_t) bufferSize * sizeof (float)); | |||
for (int i = numOuts; i < numIns; ++i) | |||
channels[i] = const_cast<float*> (getAudioBufferForInput (inputs, sidechain, mainNumIns, i)); | |||
@@ -1060,6 +1095,9 @@ struct AAXClasses | |||
|| (busUtils.getBusCount (false) > 0 && busUtils.getChannelSet (false, 0) != outputSet) | |||
|| (hasSidechain && busUtils.getNumChannels(true, 1) != 1)) | |||
return AAX_ERROR_UNIMPLEMENTED; | |||
rebuildChannelMapArrays (true); | |||
rebuildChannelMapArrays (false); | |||
#endif | |||
audioProcessor.setRateAndBufferSizeDetails (sampleRate, maxBufferSize); | |||
@@ -1071,6 +1109,30 @@ struct AAXClasses | |||
return AAX_SUCCESS; | |||
} | |||
void rebuildChannelMapArrays (bool isInput) | |||
{ | |||
Array<int>& layoutMap = isInput ? inputLayoutMap : outputLayoutMap; | |||
layoutMap.clear(); | |||
const int n = isInput ? jmin (busUtils.getBusCount (true), 1) : busUtils.getBusCount (false); | |||
int chOffset = 0; | |||
for (int busIdx = 0; busIdx < n; ++busIdx) | |||
{ | |||
const AudioChannelSet channelFormat = busUtils.getChannelSet (isInput, busIdx); | |||
if (channelFormat != AudioChannelSet::disabled()) | |||
{ | |||
const int numChannels = channelFormat.size(); | |||
for (int ch = 0; ch < numChannels; ++ch) | |||
layoutMap.add (juceChannelIndexToAax (ch, channelFormat) + chOffset); | |||
chOffset += numChannels; | |||
} | |||
} | |||
} | |||
ScopedJuceInitialiser_GUI libraryInitialiser; | |||
ScopedPointer<AudioProcessor> pluginInstance; | |||
@@ -1082,6 +1144,7 @@ struct AAXClasses | |||
int lastBufferSize, maxBufferSize; | |||
bool hasSidechain; | |||
HeapBlock<float> sideChainBuffer; | |||
Array<int> inputLayoutMap, outputLayoutMap; | |||
struct ChunkMemoryBlock : public ReferenceCountedObject | |||
{ | |||
@@ -1308,6 +1371,10 @@ struct AAXClasses | |||
properties->AddProperty (AAX_eProperty_Constraint_MultiMonoSupport, true); | |||
#endif | |||
#if JucePlugin_AAXDisableDynamicProcessing | |||
properties->AddProperty (AAX_eProperty_Constraint_AlwaysProcess, true); | |||
#endif | |||
if (enableAuxBusesForCurrentFormat (busUtils, inputLayout, outputLayout)) | |||
{ | |||
check (desc.AddSideChainIn (JUCEAlgorithmIDs::sideChainBuffers)); | |||
@@ -1442,6 +1509,25 @@ struct AAXClasses | |||
} | |||
}; | |||
//============================================================================== | |||
AAXClasses::AAXChannelStreamOrder AAXClasses::aaxChannelOrder[] = | |||
{ | |||
{AAX_eStemFormat_Mono, {AudioChannelSet::centre, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_Stereo, {AudioChannelSet::left, AudioChannelSet::right, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_LCR, {AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_LCRS, {AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::surround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_Quad, {AudioChannelSet::left, AudioChannelSet::right, AudioChannelSet::surroundLeft, AudioChannelSet::surroundRight, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_5_0, {AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::surroundLeft, AudioChannelSet::surroundRight, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_5_1, {AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::surroundLeft, AudioChannelSet::surroundRight, AudioChannelSet::subbass, AudioChannelSet::unknown, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_6_0, {AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::surroundLeft, AudioChannelSet::surround, AudioChannelSet::surroundRight, AudioChannelSet::unknown, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_6_1, {AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::surroundLeft, AudioChannelSet::surround, AudioChannelSet::surroundRight, AudioChannelSet::subbass, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_7_0_SDDS, {AudioChannelSet::left, AudioChannelSet::centreLeft, AudioChannelSet::centre, AudioChannelSet::centreRight, AudioChannelSet::right, AudioChannelSet::surroundLeft, AudioChannelSet::surroundRight, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_7_0_DTS, {AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::sideLeft, AudioChannelSet::sideRight, AudioChannelSet::surroundLeft, AudioChannelSet::surroundRight, AudioChannelSet::unknown}}, | |||
{AAX_eStemFormat_7_1_SDDS, {AudioChannelSet::left, AudioChannelSet::centreLeft, AudioChannelSet::centre, AudioChannelSet::centreRight, AudioChannelSet::right, AudioChannelSet::surroundLeft, AudioChannelSet::surroundRight, AudioChannelSet::subbass}}, | |||
{AAX_eStemFormat_7_1_DTS, {AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::sideLeft, AudioChannelSet::sideRight, AudioChannelSet::surroundLeft, AudioChannelSet::surroundRight, AudioChannelSet::subbass}}, | |||
{AAX_eStemFormat_None, {AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown}}, | |||
}; | |||
//============================================================================== | |||
AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection*); | |||
AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection) | |||
@@ -48,22 +48,7 @@ | |||
#include <AudioUnit/AudioUnit.h> | |||
#include <AudioToolbox/AudioUnitUtilities.h> | |||
#include <CoreMIDI/MIDIServices.h> | |||
#if JUCE_SUPPORT_CARBON | |||
#define Point CarbonDummyPointName | |||
#define Component CarbonDummyCompName | |||
#endif | |||
/* | |||
Got an include error here? | |||
You probably need to install Apple's AU classes - see the | |||
juce website for more info on how to get them: | |||
http://www.juce.com/forum/topic/aus-xcode | |||
*/ | |||
#include "CoreAudioUtilityClasses/MusicDeviceBase.h" | |||
#undef Point | |||
#undef Component | |||
/** The BUILD_AU_CARBON_UI flag lets you specify whether old-school carbon hosts are supported as | |||
well as ones that can open a cocoa view. If this is enabled, you'll need to also add the AUCarbonBase | |||
@@ -78,10 +63,7 @@ | |||
#endif | |||
#if BUILD_AU_CARBON_UI | |||
#undef Button | |||
#define Point CarbonDummyPointName | |||
#include "CoreAudioUtilityClasses/AUCarbonViewBase.h" | |||
#undef Point | |||
#endif | |||
#ifdef __clang__ | |||
@@ -89,12 +71,12 @@ | |||
#endif | |||
#define JUCE_MAC_WINDOW_VISIBITY_BODGE 1 | |||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | |||
#include "../utility/juce_IncludeModuleHeaders.h" | |||
#include "../utility/juce_FakeMouseMoveGenerator.h" | |||
#include "../utility/juce_CarbonVisibility.h" | |||
#include "../utility/juce_PluginBusUtilities.h" | |||
#include "../../juce_core/native/juce_osx_ObjCHelpers.h" | |||
//============================================================================== | |||
static Array<void*> activePlugins, activeUIs; | |||
@@ -976,7 +958,7 @@ public: | |||
} | |||
//============================================================================== | |||
ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags, | |||
ComponentResult Render (AudioUnitRenderActionFlags& ioActionFlags, | |||
const AudioTimeStamp& inTimeStamp, | |||
const UInt32 nFrames) override | |||
{ | |||
@@ -988,7 +970,7 @@ public: | |||
for (unsigned int i = 0; i < numInputBuses; ++i) | |||
{ | |||
AudioUnitRenderActionFlags flags = ioActionFlags; | |||
AUInputElement* input = GetInput (i); | |||
AUInputElement* input = GetInput (i); | |||
OSStatus result = input->PullInput (flags, inTimeStamp, i, nFrames); | |||
@@ -1025,20 +1007,23 @@ public: | |||
for (unsigned int chIdx = 0; chIdx < numOutChannels; ++chIdx) | |||
{ | |||
int mappedInChIdx = numInChannels > 0 ? inputLayoutMap.getReference (static_cast<int> (busIdx))[static_cast<int> (chIdx)] : 0; | |||
int mappedOutChIdx = outputLayoutMap.getReference (static_cast<int> (busIdx))[static_cast<int> (chIdx)]; | |||
const bool isOutputInterleaved = (numOutChannels > 1) && (outBuffer.mNumberBuffers == 1); | |||
float* outData = isOutputInterleaved ? scratchBuffers[scratchIdx++] : static_cast<float*> (outBuffer.mBuffers[chIdx].mData); | |||
float* outData = isOutputInterleaved ? scratchBuffers[scratchIdx++] : static_cast<float*> (outBuffer.mBuffers[mappedOutChIdx].mData); | |||
if (chIdx < numInChannels) | |||
{ | |||
const AudioBufferList& inBuffer = input->GetBufferList(); | |||
const bool isInputInterleaved = (numInChannels > 1) && (inBuffer.mNumberBuffers == 1); | |||
const float* inData = static_cast<float*> (inBuffer.mBuffers[isInputInterleaved ? 0 : chIdx].mData); | |||
const float* inData = static_cast<float*> (inBuffer.mBuffers[isInputInterleaved ? 0 : mappedInChIdx].mData); | |||
if (isInputInterleaved) | |||
{ | |||
for (unsigned int i = 0; i < nFrames; ++i) | |||
{ | |||
outData [i] = inData[chIdx]; | |||
outData [i] = inData[mappedInChIdx]; | |||
inData += numInChannels; | |||
} | |||
} | |||
@@ -1056,15 +1041,17 @@ public: | |||
for (unsigned int chIdx = 0; chIdx < numInChannels; ++chIdx) | |||
{ | |||
int mappedInChIdx = inputLayoutMap.getReference (static_cast<int> (busIdx))[static_cast<int> (chIdx)]; | |||
float* buffer = isInputInterleaved ? scratchBuffers[scratchIdx++] | |||
: static_cast<float*> (inBuffer.mBuffers[chIdx].mData); | |||
: static_cast<float*> (inBuffer.mBuffers[mappedInChIdx].mData); | |||
if (isInputInterleaved) | |||
{ | |||
const float* inData = static_cast<float*> (inBuffer.mBuffers[0].mData); | |||
for (unsigned int i = 0; i < nFrames; ++i) | |||
{ | |||
buffer [i] = inData [chIdx]; | |||
buffer [i] = inData [mappedInChIdx]; | |||
inData += numInChannels; | |||
} | |||
} | |||
@@ -1114,32 +1101,45 @@ public: | |||
const unsigned int numInChannels = (input != nullptr ? input ->GetStreamFormat().mChannelsPerFrame : 0); | |||
const unsigned int numOutChannels = (output != nullptr ? output->GetStreamFormat().mChannelsPerFrame : 0); | |||
if (numOutChannels > 0 && numInChannels >= numOutChannels) | |||
if (numInChannels >= numOutChannels) | |||
{ | |||
// the input buffers were used. We must copy the output | |||
if (output->WillAllocateBuffer()) | |||
output->PrepareBuffer (nFrames); | |||
const AudioBufferList& outBuffer = output->GetBufferList(); | |||
const bool isOutputInterleaved = (numOutChannels > 1) && (outBuffer.mNumberBuffers == 1); | |||
for (unsigned int chIdx = 0; chIdx < numOutChannels; ++chIdx) | |||
if (numOutChannels > 0) | |||
{ | |||
float* outData = static_cast<float*> (outBuffer.mBuffers[isOutputInterleaved ? 0 : chIdx].mData); | |||
const float* buffer = static_cast<float*> (channels [idx++]); | |||
// the input buffers were used. We must copy the output | |||
if (output->WillAllocateBuffer()) | |||
output->PrepareBuffer (nFrames); | |||
if (isOutputInterleaved) | |||
const AudioBufferList& outBuffer = output->GetBufferList(); | |||
const bool isOutputInterleaved = (numOutChannels > 1) && (outBuffer.mNumberBuffers == 1); | |||
for (unsigned int chIdx = 0; chIdx < numOutChannels; ++chIdx) | |||
{ | |||
for (unsigned int i = 0; i < nFrames; ++i) | |||
int mappedOutChIdx = outputLayoutMap.getReference (static_cast<int> (busIdx))[static_cast<int> (chIdx)]; | |||
float* outData = static_cast<float*> (outBuffer.mBuffers[isOutputInterleaved ? 0 : mappedOutChIdx].mData); | |||
float* buffer = static_cast<float*> (channels [idx]); | |||
if (isOutputInterleaved) | |||
{ | |||
outData [chIdx] = buffer[i]; | |||
outData += numOutChannels; | |||
for (unsigned int i = 0; i < nFrames; ++i) | |||
{ | |||
outData [mappedOutChIdx] = buffer[i]; | |||
outData += numOutChannels; | |||
} | |||
} | |||
else | |||
std::copy (buffer, buffer + nFrames, outData); | |||
zeromem (buffer, sizeof(float) * nFrames); | |||
idx++; | |||
} | |||
else | |||
std::copy (buffer, buffer + nFrames, outData); | |||
idx += numInChannels - numOutChannels; | |||
} | |||
else | |||
{ | |||
for (unsigned int chIdx = 0; chIdx < numOutChannels; ++chIdx) | |||
zeromem (channels [chIdx], sizeof(float) * nFrames); | |||
} | |||
idx += numInChannels - numOutChannels; | |||
} | |||
} | |||
} | |||
@@ -1187,12 +1187,6 @@ public: | |||
midiEvents.clear(); | |||
} | |||
#if ! JucePlugin_SilenceInProducesSilenceOut | |||
ioActionFlags &= (AudioUnitRenderActionFlags) ~kAudioUnitRenderAction_OutputIsSilence; | |||
#else | |||
ignoreUnused (ioActionFlags); | |||
#endif | |||
return noErr; | |||
} | |||
@@ -1545,6 +1539,7 @@ private: | |||
Array<AUChannelInfo> channelInfo; | |||
Array<Array<AudioChannelLayoutTag> > supportedInputLayouts, supportedOutputLayouts; | |||
Array<AudioChannelLayoutTag> currentInputLayout, currentOutputLayout; | |||
Array<Array<int> > inputLayoutMap, outputLayoutMap; | |||
//============================================================================== | |||
static OSStatus scopeToDirection (AudioUnitScope scope, bool& isInput) noexcept | |||
@@ -1601,13 +1596,22 @@ private: | |||
OSStatus syncProcessorWithAudioUnit() | |||
{ | |||
OSStatus err; | |||
const int numInputBuses = busUtils.getBusCount (true); | |||
const int numOutputBuses = busUtils.getBusCount (false); | |||
const int numInputElements = static_cast<int> (GetScope(kAudioUnitScope_Input). GetNumberOfElements()); | |||
const int numOutputElements = static_cast<int> (GetScope(kAudioUnitScope_Output).GetNumberOfElements()); | |||
for (int i = 0; i < numInputElements; ++i) | |||
inputLayoutMap. clear(); | |||
outputLayoutMap.clear(); | |||
inputLayoutMap. resize (numInputBuses); | |||
outputLayoutMap.resize (numOutputBuses); | |||
for (int i = 0; i < numInputBuses; ++i) | |||
if ((err = syncProcessorWithAudioUnitForBus (true, i)) != noErr) return err; | |||
for (int i = 0; i < numOutputElements; ++i) | |||
for (int i = 0; i < numOutputBuses; ++i) | |||
if ((err = syncProcessorWithAudioUnitForBus (false, i)) != noErr) return err; | |||
if (numInputElements != busUtils.getNumEnabledBuses (true) || numOutputElements != busUtils.getNumEnabledBuses (false)) | |||
@@ -1626,23 +1630,29 @@ private: | |||
//============================================================================== | |||
OSStatus syncProcessorWithAudioUnitForBus (bool isInput, int busNr) | |||
{ | |||
if (const AUIOElement* element = GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, (UInt32) busNr)) | |||
{ | |||
const int numChannels = static_cast<int> (element->GetStreamFormat().NumberChannels()); | |||
jassert (isPositiveAndBelow (busNr, busUtils.getBusCount (isInput))); | |||
AudioChannelLayoutTag currentLayoutTag = isInput ? currentInputLayout[busNr] : currentOutputLayout[busNr]; | |||
const int tagNumChannels = currentLayoutTag & 0xffff; | |||
const int numAUElements = static_cast<int> (GetScope(isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output).GetNumberOfElements()); | |||
const AUIOElement* element = (busNr < numAUElements ? GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, (UInt32) busNr) : nullptr); | |||
const int numChannels = (element != nullptr ? static_cast<int> (element->GetStreamFormat().NumberChannels()) : 0); | |||
if (numChannels != tagNumChannels) | |||
return kAudioUnitErr_FormatNotSupported; | |||
AudioChannelLayoutTag currentLayoutTag = isInput ? currentInputLayout[busNr] : currentOutputLayout[busNr]; | |||
const int tagNumChannels = currentLayoutTag & 0xffff; | |||
if (juceFilter->setPreferredBusArrangement (isInput, busNr, CALayoutTagToChannelSet(currentLayoutTag))) | |||
return noErr; | |||
} | |||
else | |||
jassertfalse; | |||
if (numChannels != tagNumChannels) | |||
return kAudioUnitErr_FormatNotSupported; | |||
const AudioChannelSet channelFormat = CALayoutTagToChannelSet(currentLayoutTag); | |||
if (! juceFilter->setPreferredBusArrangement (isInput, busNr, channelFormat)) | |||
return kAudioUnitErr_FormatNotSupported; | |||
return kAudioUnitErr_FormatNotSupported; | |||
Array<int>& layoutMap = (isInput ? inputLayoutMap : outputLayoutMap).getReference (busNr); | |||
for (int i = 0; i < numChannels; ++i) | |||
layoutMap.add (auChannelIndexToJuce (i, channelFormat)); | |||
return noErr; | |||
} | |||
OSStatus syncAudioUnitWithChannelSet (bool isInput, int busNr, const AudioChannelSet& channelSet) | |||
@@ -1877,6 +1887,50 @@ private: | |||
} | |||
//============================================================================== | |||
// maps a channel index into an AU format to an index of a juce format | |||
struct AUChannelStreamOrder | |||
{ | |||
AudioChannelLayoutTag auLayoutTag; | |||
AudioChannelLabel speakerOrder[8]; | |||
}; | |||
static AUChannelStreamOrder auChannelStreamOrder[]; | |||
static int auChannelIndexToJuce (int auIndex, const AudioChannelSet& channelSet) | |||
{ | |||
if (auIndex >= 8) return auIndex; | |||
AudioChannelLayoutTag currentLayout = ChannelSetToCALayoutTag (channelSet); | |||
int layoutIndex; | |||
for (layoutIndex = 0; auChannelStreamOrder[layoutIndex].auLayoutTag != currentLayout; ++layoutIndex) | |||
if (auChannelStreamOrder[layoutIndex].auLayoutTag == 0) return auIndex; | |||
const AudioChannelSet::ChannelType channelType | |||
= CoreAudioChannelLabelToJuceType (auChannelStreamOrder[layoutIndex].speakerOrder[auIndex]); | |||
const int juceIndex = channelSet.getChannelTypes().indexOf (channelType); | |||
return juceIndex >= 0 ? juceIndex : auIndex; | |||
} | |||
static int juceChannelIndexToAu (int juceIndex, const AudioChannelSet& channelSet) | |||
{ | |||
AudioChannelLayoutTag currentLayout = ChannelSetToCALayoutTag (channelSet); | |||
int layoutIndex; | |||
for (layoutIndex = 0; auChannelStreamOrder[layoutIndex].auLayoutTag != currentLayout; ++layoutIndex) | |||
if (auChannelStreamOrder[layoutIndex].auLayoutTag == 0) return juceIndex; | |||
const AUChannelStreamOrder& channelOrder = auChannelStreamOrder[layoutIndex]; | |||
const AudioChannelSet::ChannelType channelType = channelSet.getTypeOfChannel (juceIndex); | |||
for (int i = 0; i < 8 && channelOrder.speakerOrder[i] != 0; ++i) | |||
if (CoreAudioChannelLabelToJuceType (channelOrder.speakerOrder[i]) == channelType) | |||
return i; | |||
return juceIndex; | |||
} | |||
static AudioChannelSet::ChannelType CoreAudioChannelLabelToJuceType (AudioChannelLabel label) noexcept | |||
{ | |||
if (label >= kAudioChannelLabel_Discrete_0 && label <= kAudioChannelLabel_Discrete_65535) | |||
@@ -1911,10 +1965,58 @@ private: | |||
case kAudioChannelLabel_RearSurroundRight: return AudioChannelSet::topRearRight; | |||
case kAudioChannelLabel_TopBackCenter: return AudioChannelSet::topRearCentre; | |||
case kAudioChannelLabel_LFE2: return AudioChannelSet::subbass2; | |||
case kAudioChannelLabel_LeftWide: return AudioChannelSet::wideLeft; | |||
case kAudioChannelLabel_RightWide: return AudioChannelSet::wideRight; | |||
case kAudioChannelLabel_Ambisonic_W: return AudioChannelSet::ambisonicW; | |||
case kAudioChannelLabel_Ambisonic_X: return AudioChannelSet::ambisonicX; | |||
case kAudioChannelLabel_Ambisonic_Y: return AudioChannelSet::ambisonicY; | |||
case kAudioChannelLabel_Ambisonic_Z: return AudioChannelSet::ambisonicZ; | |||
default: return AudioChannelSet::unknown; | |||
} | |||
} | |||
static AudioChannelLabel JuceChannelTypeToCoreAudioLabel (const AudioChannelSet::ChannelType& label) noexcept | |||
{ | |||
if (label >= AudioChannelSet::discreteChannel0) | |||
{ | |||
const unsigned int discreteChannelNum = label - AudioChannelSet::discreteChannel0;; | |||
return static_cast<AudioChannelLabel> (kAudioChannelLabel_Discrete_0 + discreteChannelNum); | |||
} | |||
switch (label) | |||
{ | |||
case AudioChannelSet::centre: return kAudioChannelLabel_Center; | |||
case AudioChannelSet::left: return kAudioChannelLabel_Left; | |||
case AudioChannelSet::right: return kAudioChannelLabel_Right; | |||
case AudioChannelSet::subbass: return kAudioChannelLabel_LFEScreen; | |||
case AudioChannelSet::surroundLeft: return kAudioChannelLabel_LeftSurround; | |||
case AudioChannelSet::surroundRight: return kAudioChannelLabel_RightSurround; | |||
case AudioChannelSet::centreLeft: return kAudioChannelLabel_LeftCenter; | |||
case AudioChannelSet::centreRight: return kAudioChannelLabel_RightCenter; | |||
case AudioChannelSet::surround: return kAudioChannelLabel_CenterSurround; | |||
case AudioChannelSet::sideLeft: return kAudioChannelLabel_LeftSurroundDirect; | |||
case AudioChannelSet::sideRight: return kAudioChannelLabel_RightSurroundDirect; | |||
case AudioChannelSet::topMiddle: return kAudioChannelLabel_TopCenterSurround; | |||
case AudioChannelSet::topFrontLeft: return kAudioChannelLabel_VerticalHeightLeft; | |||
case AudioChannelSet::topFrontRight: return kAudioChannelLabel_VerticalHeightRight; | |||
case AudioChannelSet::topFrontCentre: return kAudioChannelLabel_VerticalHeightCenter; | |||
case AudioChannelSet::topRearLeft: return kAudioChannelLabel_RearSurroundLeft; | |||
case AudioChannelSet::topRearRight: return kAudioChannelLabel_RearSurroundRight; | |||
case AudioChannelSet::topRearCentre: return kAudioChannelLabel_TopBackCenter; | |||
case AudioChannelSet::subbass2: return kAudioChannelLabel_LFE2; | |||
case AudioChannelSet::wideLeft: return kAudioChannelLabel_LeftWide; | |||
case AudioChannelSet::wideRight: return kAudioChannelLabel_RightWide; | |||
case AudioChannelSet::ambisonicW: return kAudioChannelLabel_Ambisonic_W; | |||
case AudioChannelSet::ambisonicX: return kAudioChannelLabel_Ambisonic_X; | |||
case AudioChannelSet::ambisonicY: return kAudioChannelLabel_Ambisonic_Y; | |||
case AudioChannelSet::ambisonicZ: return kAudioChannelLabel_Ambisonic_Z; | |||
case AudioChannelSet::unknown: return kAudioChannelLabel_Unknown; | |||
case AudioChannelSet::discreteChannel0: return kAudioChannelLabel_Discrete_0; | |||
} | |||
return kAudioChannelLabel_Unknown; | |||
} | |||
static AudioChannelSet CoreAudioChannelBitmapToJuceType (UInt32 bitmap) noexcept | |||
{ | |||
AudioChannelSet set; | |||
@@ -1962,6 +2064,7 @@ private: | |||
{ | |||
switch (tag) | |||
{ | |||
case kAudioChannelLayoutTag_Unknown: return AudioChannelSet::disabled(); | |||
case kAudioChannelLayoutTag_Mono: return AudioChannelSet::mono(); | |||
case kAudioChannelLayoutTag_Stereo: | |||
case kAudioChannelLayoutTag_StereoHeadphones: | |||
@@ -1979,6 +2082,10 @@ private: | |||
case kAudioChannelLayoutTag_MPEG_7_1_C: return AudioChannelSet::create7point1(); | |||
case kAudioChannelLayoutTag_AudioUnit_7_0_Front: return AudioChannelSet::createFront7point0(); | |||
case kAudioChannelLayoutTag_AudioUnit_7_1_Front: return AudioChannelSet::createFront7point1(); | |||
case kAudioChannelLayoutTag_MPEG_3_0_A: | |||
case kAudioChannelLayoutTag_MPEG_3_0_B: return AudioChannelSet::createLCR(); | |||
case kAudioChannelLayoutTag_MPEG_4_0_A: | |||
case kAudioChannelLayoutTag_MPEG_4_0_B: return AudioChannelSet::createLCRS(); | |||
} | |||
if (int numChannels = static_cast<int> (tag) & 0xffff) | |||
@@ -1993,6 +2100,8 @@ private: | |||
{ | |||
if (set == AudioChannelSet::mono()) return kAudioChannelLayoutTag_Mono; | |||
if (set == AudioChannelSet::stereo()) return kAudioChannelLayoutTag_Stereo; | |||
if (set == AudioChannelSet::createLCR()) return kAudioChannelLayoutTag_MPEG_3_0_A; | |||
if (set == AudioChannelSet::createLCRS()) return kAudioChannelLayoutTag_MPEG_4_0_A; | |||
if (set == AudioChannelSet::quadraphonic()) return kAudioChannelLayoutTag_Quadraphonic; | |||
if (set == AudioChannelSet::pentagonal()) return kAudioChannelLayoutTag_Pentagonal; | |||
if (set == AudioChannelSet::hexagonal()) return kAudioChannelLayoutTag_Hexagonal; | |||
@@ -2006,6 +2115,7 @@ private: | |||
if (set == AudioChannelSet::create7point1()) return kAudioChannelLayoutTag_MPEG_7_1_C; | |||
if (set == AudioChannelSet::createFront7point0()) return kAudioChannelLayoutTag_AudioUnit_7_0_Front; | |||
if (set == AudioChannelSet::createFront7point1()) return kAudioChannelLayoutTag_AudioUnit_7_1_Front; | |||
if (set == AudioChannelSet::disabled()) return kAudioChannelLayoutTag_Unknown; | |||
return static_cast<AudioChannelLayoutTag> ((int) kAudioChannelLayoutTag_DiscreteInOrder | set.size()); | |||
} | |||
@@ -2048,6 +2158,31 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE (JuceAU) | |||
}; | |||
JuceAU::AUChannelStreamOrder JuceAU::auChannelStreamOrder[] = | |||
{ | |||
{kAudioChannelLayoutTag_Mono, {kAudioChannelLabel_Center, 0, 0, 0, 0, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_Stereo, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, 0, 0, 0, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_StereoHeadphones, {kAudioChannelLabel_HeadphonesLeft, kAudioChannelLabel_HeadphonesRight, 0, 0, 0, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_Binaural, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, 0, 0, 0, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_Quadraphonic, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, 0, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_Pentagonal, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_Center, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_Hexagonal, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_Center, kAudioChannelLabel_CenterSurround, 0, 0}}, | |||
{kAudioChannelLayoutTag_Octagonal, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_Center, kAudioChannelLabel_CenterSurround, kAudioChannelLabel_LeftWide, kAudioChannelLabel_RightWide}}, | |||
{kAudioChannelLayoutTag_Ambisonic_B_Format, {kAudioChannelLabel_Ambisonic_W, kAudioChannelLabel_Ambisonic_X, kAudioChannelLabel_Ambisonic_Y, kAudioChannelLabel_Ambisonic_Z, 0, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_MPEG_5_0_B, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_Center, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_MPEG_5_1_A, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, 0, 0}}, | |||
{kAudioChannelLayoutTag_AudioUnit_6_0, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_Center, kAudioChannelLabel_CenterSurround, 0, 0}}, | |||
{kAudioChannelLayoutTag_MPEG_6_1_A, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_CenterSurround, 0}}, | |||
{kAudioChannelLayoutTag_AudioUnit_7_0, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_Center, kAudioChannelLabel_RearSurroundLeft, kAudioChannelLabel_RearSurroundRight, 0}}, | |||
{kAudioChannelLayoutTag_MPEG_7_1_C, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_RearSurroundLeft, kAudioChannelLabel_RearSurroundRight}}, | |||
{kAudioChannelLayoutTag_AudioUnit_7_0_Front,{kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_Center, kAudioChannelLabel_LeftCenter, kAudioChannelLabel_RightCenter, 0}}, | |||
{kAudioChannelLayoutTag_AudioUnit_7_1_Front,{kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, kAudioChannelLabel_LeftCenter, kAudioChannelLabel_RightCenter}}, | |||
{kAudioChannelLayoutTag_MPEG_3_0_A, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_Center, 0, 0, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_MPEG_3_0_B, {kAudioChannelLabel_Center, kAudioChannelLabel_Left, kAudioChannelLabel_Right, 0, 0, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_MPEG_4_0_A, {kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_Center, kAudioChannelLabel_CenterSurround, 0, 0, 0, 0}}, | |||
{kAudioChannelLayoutTag_MPEG_4_0_B, {kAudioChannelLabel_Center, kAudioChannelLabel_Left, kAudioChannelLabel_Right, kAudioChannelLabel_CenterSurround, 0, 0, 0, 0}}, | |||
{0, {0,0,0,0,0,0,0,0}} | |||
}; | |||
//============================================================================== | |||
#if BUILD_AU_CARBON_UI | |||
@@ -64,8 +64,6 @@ | |||
#define PLUGIN_SDK_DIRECTMIDI 1 | |||
#define DIGI_PASCAL | |||
#define Point CarbonDummyPointName | |||
#define Component CarbonDummyCompName | |||
#include <MacAlwaysInclude.h> | |||
#endif | |||
@@ -86,9 +86,6 @@ | |||
#include <FicProcessTokens.h> | |||
#include <ExternalVersionDefines.h> | |||
#undef Point | |||
#undef Component | |||
//============================================================================== | |||
#ifdef _MSC_VER | |||
#pragma pack (push, 8) | |||
@@ -122,19 +119,18 @@ | |||
#pragma comment(lib, PT_LIB_PATH "RTASClientLib.lib") | |||
#endif | |||
#undef Component | |||
#undef MemoryBlock | |||
//============================================================================== | |||
#if JUCE_WINDOWS | |||
extern void JUCE_CALLTYPE attachSubWindow (void* hostWindow, int& titleW, int& titleH, juce::Component* comp); | |||
extern void JUCE_CALLTYPE resizeHostWindow (void* hostWindow, int& titleW, int& titleH, juce::Component* comp); | |||
extern void JUCE_CALLTYPE attachSubWindow (void* hostWindow, int& titleW, int& titleH, Component* comp); | |||
extern void JUCE_CALLTYPE resizeHostWindow (void* hostWindow, int& titleW, int& titleH, Component* comp); | |||
#if ! JucePlugin_EditorRequiresKeyboardFocus | |||
extern void JUCE_CALLTYPE passFocusToHostWindow (void* hostWindow); | |||
#endif | |||
#else | |||
extern void* attachSubWindow (void* hostWindowRef, juce::Component* comp); | |||
extern void removeSubWindow (void* nsWindow, juce::Component* comp); | |||
extern void* attachSubWindow (void* hostWindowRef, Component* comp); | |||
extern void removeSubWindow (void* nsWindow, Component* comp); | |||
extern void forwardCurrentKeyEventToHostWindow(); | |||
#endif | |||
@@ -240,7 +236,7 @@ public: | |||
void timerCallback() override | |||
{ | |||
if (! juce::Component::isMouseButtonDownAnywhere()) | |||
if (! Component::isMouseButtonDownAnywhere()) | |||
{ | |||
stopTimer(); | |||
@@ -290,7 +286,7 @@ public: | |||
private: | |||
AudioProcessor* const filter; | |||
JucePlugInProcess* const process; | |||
ScopedPointer<juce::Component> wrapper; | |||
ScopedPointer<Component> wrapper; | |||
ScopedPointer<AudioProcessorEditor> editorComp; | |||
void deleteEditorComp() | |||
@@ -301,7 +297,7 @@ public: | |||
{ | |||
PopupMenu::dismissAllActiveMenus(); | |||
if (juce::Component* const modalComponent = juce::Component::getCurrentlyModalComponent()) | |||
if (Component* const modalComponent = Component::getCurrentlyModalComponent()) | |||
modalComponent->exitModalState (0); | |||
filter->editorBeingDeleted (editorComp); | |||
@@ -315,7 +311,7 @@ public: | |||
//============================================================================== | |||
// A component to hold the AudioProcessorEditor, and cope with some housekeeping | |||
// chores when it changes or repaints. | |||
class EditorCompWrapper : public juce::Component | |||
class EditorCompWrapper : public Component | |||
#if ! JUCE_MAC | |||
, public FocusChangeListener | |||
#endif | |||
@@ -368,14 +364,14 @@ public: | |||
void resized() override | |||
{ | |||
if (juce::Component* const ed = getEditor()) | |||
if (Component* const ed = getEditor()) | |||
ed->setBounds (getLocalBounds()); | |||
repaint(); | |||
} | |||
#if JUCE_WINDOWS | |||
void globalFocusChanged (juce::Component*) override | |||
void globalFocusChanged (Component*) override | |||
{ | |||
#if ! JucePlugin_EditorRequiresKeyboardFocus | |||
if (hasKeyboardFocus (true)) | |||
@@ -384,7 +380,7 @@ public: | |||
} | |||
#endif | |||
void childBoundsChanged (juce::Component* child) override | |||
void childBoundsChanged (Component* child) override | |||
{ | |||
setSize (child->getWidth(), child->getHeight()); | |||
child->setTopLeftPosition (0, 0); | |||
@@ -413,7 +409,7 @@ public: | |||
JuceCustomUIView* const owner; | |||
int titleW, titleH; | |||
juce::Component* getEditor() const { return getChildComponent (0); } | |||
Component* getEditor() const { return getChildComponent (0); } | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper) | |||
}; | |||
@@ -666,9 +662,9 @@ public: | |||
#if JUCE_WINDOWS | |||
Boolean HandleKeystroke (EventRecord* e) override | |||
{ | |||
if (juce::Component* modalComp = juce::Component::getCurrentlyModalComponent()) | |||
if (Component* modalComp = Component::getCurrentlyModalComponent()) | |||
{ | |||
if (juce::Component* focused = modalComp->getCurrentlyFocusedComponent()) | |||
if (Component* focused = modalComp->getCurrentlyFocusedComponent()) | |||
{ | |||
switch (e->message & charCodeMask) | |||
{ | |||
@@ -40,15 +40,27 @@ class StandalonePluginHolder | |||
public: | |||
/** Creates an instance of the default plugin. | |||
The settings object can be a PropertySet that the class should use to | |||
store its settings - the object that is passed-in will be owned by this | |||
class and deleted automatically when no longer needed. (It can also be null) | |||
The settings object can be a PropertySet that the class should use to store its | |||
settings - the takeOwnershipOfSettings indicates whether this object will delete | |||
the settings automatically when no longer needed. The settings can also be nullptr. | |||
A default device name can be passed in. | |||
Preferably a complete setup options object can be used, which takes precedence over | |||
the preferredDefaultDeviceName and allows you to select the input & output device names, | |||
sample rate, buffer size etc. | |||
In all instances, the settingsToUse will take precedence over the "preferred" options if not null. | |||
*/ | |||
StandalonePluginHolder (PropertySet* settingsToUse, bool takeOwnershipOfSettings) | |||
StandalonePluginHolder (PropertySet* settingsToUse, | |||
bool takeOwnershipOfSettings = true, | |||
const String& preferredDefaultDeviceName = String(), | |||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr) | |||
: settings (settingsToUse, takeOwnershipOfSettings) | |||
{ | |||
createPlugin(); | |||
setupAudioDevices(); | |||
setupAudioDevices (preferredDefaultDeviceName, preferredSetupOptions); | |||
reloadPluginState(); | |||
startPlaying(); | |||
} | |||
@@ -189,7 +201,8 @@ public: | |||
} | |||
} | |||
void reloadAudioDeviceState() | |||
void reloadAudioDeviceState (const String& preferredDefaultDeviceName, | |||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions) | |||
{ | |||
ScopedPointer<XmlElement> savedState; | |||
@@ -199,7 +212,9 @@ public: | |||
deviceManager.initialise (processor->getTotalNumInputChannels(), | |||
processor->getTotalNumOutputChannels(), | |||
savedState, | |||
true); | |||
true, | |||
preferredDefaultDeviceName, | |||
preferredSetupOptions); | |||
} | |||
//============================================================================== | |||
@@ -232,19 +247,20 @@ public: | |||
AudioProcessorPlayer player; | |||
private: | |||
void setupAudioDevices() | |||
void setupAudioDevices (const String& preferredDefaultDeviceName, | |||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions) | |||
{ | |||
deviceManager.addAudioCallback (&player); | |||
deviceManager.addMidiInputCallback (String::empty, &player); | |||
deviceManager.addMidiInputCallback (String(), &player); | |||
reloadAudioDeviceState(); | |||
reloadAudioDeviceState (preferredDefaultDeviceName, preferredSetupOptions); | |||
} | |||
void shutDownAudioDevices() | |||
{ | |||
saveAudioDeviceState(); | |||
deviceManager.removeMidiInputCallback (String::empty, &player); | |||
deviceManager.removeMidiInputCallback (String(), &player); | |||
deviceManager.removeAudioCallback (&player); | |||
} | |||
@@ -273,7 +289,9 @@ public: | |||
StandaloneFilterWindow (const String& title, | |||
Colour backgroundColour, | |||
PropertySet* settingsToUse, | |||
bool takeOwnershipOfSettings) | |||
bool takeOwnershipOfSettings, | |||
const String& preferredDefaultDeviceName = String(), | |||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr) | |||
: DocumentWindow (title, backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton), | |||
optionsButton ("options") | |||
{ | |||
@@ -283,7 +301,8 @@ public: | |||
optionsButton.addListener (this); | |||
optionsButton.setTriggeredOnMouseDown (true); | |||
pluginHolder = new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings); | |||
pluginHolder = new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings, | |||
preferredDefaultDeviceName, preferredSetupOptions); | |||
createEditorComp(); | |||
@@ -110,7 +110,6 @@ | |||
class JuceVSTWrapper; | |||
static bool recursionCheck = false; | |||
static juce::uint32 lastMasterIdleCall = 0; | |||
namespace juce | |||
{ | |||
@@ -277,14 +276,43 @@ public: | |||
#endif | |||
hostWindow (0) | |||
{ | |||
int maxNumInChannels, maxNumOutChannels; | |||
busUtils.findAllCompatibleLayouts(); | |||
// VST-2 does not support disabling buses: so always enable all of them | |||
if (busUtils.hasDynamicInBuses() || busUtils.hasDynamicOutBuses()) | |||
busUtils.enableAllBuses(); | |||
const int totalNumInChannels = busUtils.findTotalNumChannels(true); | |||
const int totalNumOutChannels = busUtils.findTotalNumChannels(false); | |||
{ | |||
PluginBusUtilities::ScopedBusRestorer busRestorer (busUtils); | |||
maxNumInChannels = busUtils.getBusCount (true) > 0 ? busUtils.getSupportedBusLayouts (true, 0).maxNumberOfChannels() : 0; | |||
maxNumOutChannels = busUtils.getBusCount (false) > 0 ? busUtils.getSupportedBusLayouts (false, 0).maxNumberOfChannels() : 0; | |||
if (hostOnlySupportsStereo()) | |||
{ | |||
maxNumInChannels = jmin (maxNumInChannels, 2); | |||
maxNumOutChannels = jmin (maxNumOutChannels, 2); | |||
} | |||
// try setting the number of channels | |||
if (maxNumInChannels > 0) | |||
filter->setPreferredBusArrangement (true, 0, busUtils.getDefaultLayoutForChannelNumAndBus (true, 0, maxNumInChannels)); | |||
if (maxNumOutChannels > 0) | |||
filter->setPreferredBusArrangement (false, 0, busUtils.getDefaultLayoutForChannelNumAndBus (false, 0, maxNumOutChannels)); | |||
resetAuxChannelsToDefaultLayout (true); | |||
resetAuxChannelsToDefaultLayout (false); | |||
maxNumInChannels = busUtils.findTotalNumChannels (true); | |||
maxNumOutChannels = busUtils.findTotalNumChannels (false); | |||
if ((busUtils.getBusCount (true) > 0 && busUtils.getDefaultLayoutForBus (true, 0) .size() > maxNumInChannels) | |||
|| (busUtils.getBusCount (false) > 0 && busUtils.getDefaultLayoutForBus (false, 0).size() > maxNumOutChannels)) | |||
busRestorer.release(); | |||
} | |||
filter->setRateAndBufferSizeDetails (0, 0); | |||
filter->setPlayHead (this); | |||
@@ -295,8 +323,8 @@ public: | |||
setUniqueID ((int) (JucePlugin_VSTUniqueID)); | |||
setNumInputs (totalNumInChannels); | |||
setNumOutputs (totalNumOutChannels); | |||
setNumInputs (maxNumInChannels); | |||
setNumOutputs (maxNumOutChannels); | |||
canProcessReplacing (true); | |||
canDoubleReplacing (filter->supportsDoublePrecisionProcessing()); | |||
@@ -544,7 +572,8 @@ public: | |||
tmpBuffers.channels[i] = inputs[i]; | |||
{ | |||
AudioBuffer<FloatType> chans (tmpBuffers.channels, jmax (numIn, numOut), numSamples); | |||
const int numChannels = jmax (numIn, numOut); | |||
AudioBuffer<FloatType> chans (tmpBuffers.channels, numChannels, numSamples); | |||
if (isBypassed) | |||
filter->processBlockBypassed (chans, midiEvents); | |||
@@ -641,31 +670,20 @@ public: | |||
{ | |||
isProcessing = true; | |||
const int numInChans = filter->busArrangement.getTotalNumInputChannels(); | |||
const int numOutChans = filter->busArrangement.getTotalNumOutputChannels(); | |||
setNumInputs (numInChans); | |||
setNumOutputs (numOutChans); | |||
floatTempBuffers.channels.calloc ((size_t) (numInChans + numOutChans)); | |||
doubleTempBuffers.channels.calloc ((size_t) (numInChans + numOutChans)); | |||
double rate = getSampleRate(); | |||
jassert (rate > 0); | |||
if (rate <= 0.0) | |||
rate = 44100.0; | |||
floatTempBuffers .channels.calloc ((size_t) (cEffect.numInputs + cEffect.numOutputs)); | |||
doubleTempBuffers.channels.calloc ((size_t) (cEffect.numInputs + cEffect.numOutputs)); | |||
const double currentRate = getSampleRate(); | |||
const int currentBlockSize = getBlockSize(); | |||
jassert (currentBlockSize > 0); | |||
firstProcessCallback = true; | |||
filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */); | |||
filter->setRateAndBufferSizeDetails (rate, currentBlockSize); | |||
filter->setRateAndBufferSizeDetails (currentRate, currentBlockSize); | |||
deleteTempChannels(); | |||
filter->prepareToPlay (rate, currentBlockSize); | |||
filter->prepareToPlay (currentRate, currentBlockSize); | |||
midiEvents.ensureSize (2048); | |||
midiEvents.clear(); | |||
@@ -901,10 +919,6 @@ public: | |||
bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput, | |||
VstSpeakerArrangement* pluginOutput) override | |||
{ | |||
if ((busUtils.getBusCount (true) == 0 || busUtils.busIgnoresLayout(true, 0)) | |||
&& (busUtils.getBusCount (false) == 0 || busUtils.busIgnoresLayout(false, 0))) | |||
return false; | |||
if (pluginInput != nullptr && filter->busArrangement.inputBuses.size() == 0) | |||
return false; | |||
@@ -913,34 +927,64 @@ public: | |||
PluginBusUtilities::ScopedBusRestorer busRestorer (busUtils); | |||
if (pluginInput != nullptr) | |||
resetAuxChannelsToDefaultLayout (true); | |||
resetAuxChannelsToDefaultLayout (false); | |||
if (pluginInput != nullptr && pluginInput->numChannels >= 0) | |||
{ | |||
AudioChannelSet newType = SpeakerMappings::vstArrangementTypeToChannelSet (*pluginInput); | |||
AudioChannelSet newType; | |||
// subtract the number of channels which are used by the aux channels | |||
int mainNumChannels = pluginInput->numChannels - busUtils.findTotalNumChannels (true, 1); | |||
if (mainNumChannels <= 0) | |||
return false; | |||
if (mainNumChannels > busUtils.getSupportedBusLayouts (true, 0).maxNumberOfChannels()) | |||
return false; | |||
newType = SpeakerMappings::vstArrangementTypeToChannelSet (*pluginInput); | |||
if (mainNumChannels != newType.size()) | |||
newType = AudioChannelSet::canonicalChannelSet(mainNumChannels); | |||
if (busUtils.getChannelSet (true, 0) != newType) | |||
if (! filter->setPreferredBusArrangement (true, 0, newType)) | |||
return false; | |||
} | |||
if (pluginOutput != nullptr) | |||
if (pluginOutput != nullptr && pluginOutput->numChannels >= 0) | |||
{ | |||
AudioChannelSet newType = SpeakerMappings::vstArrangementTypeToChannelSet (*pluginOutput); | |||
AudioChannelSet newType; | |||
// subtract the number of channels which are used by the aux channels | |||
int mainNumChannels = pluginOutput->numChannels - busUtils.findTotalNumChannels (false, 1); | |||
if (mainNumChannels <= 0) | |||
return false; | |||
if (mainNumChannels > busUtils.getSupportedBusLayouts (false, 0).maxNumberOfChannels()) | |||
return false; | |||
newType = SpeakerMappings::vstArrangementTypeToChannelSet (*pluginOutput); | |||
if (mainNumChannels != newType.size()) | |||
newType = AudioChannelSet::canonicalChannelSet(mainNumChannels); | |||
AudioChannelSet oldOutputLayout = busUtils.getChannelSet (false, 0); | |||
AudioChannelSet oldInputLayout = busUtils.getChannelSet (true, 0); | |||
if (busUtils.getChannelSet (false, 0) != newType) | |||
if (! filter->setPreferredBusArrangement (false, 0, newType)) | |||
return false; | |||
// did this change the input layout? | |||
if (oldInputLayout != busUtils.getChannelSet (true, 0) && pluginInput != nullptr) | |||
return false; | |||
} | |||
busRestorer.release(); | |||
const int totalNumInChannels = busUtils.findTotalNumChannels(true); | |||
const int totalNumOutChannels = busUtils.findTotalNumChannels(false); | |||
filter->setRateAndBufferSizeDetails(0, 0); | |||
setNumInputs (totalNumInChannels); | |||
setNumOutputs(totalNumOutChannels); | |||
ioChanged(); | |||
return true; | |||
} | |||
@@ -950,22 +994,35 @@ public: | |||
*pluginInput = 0; | |||
*pluginOutput = 0; | |||
if ((busUtils.getBusCount (true) == 0 || busUtils.busIgnoresLayout(true, 0)) | |||
&& (busUtils.getBusCount (false) == 0 || busUtils.busIgnoresLayout(false, 0))) | |||
if (! AudioEffectX::allocateArrangement (pluginInput, busUtils.findTotalNumChannels (true))) | |||
return false; | |||
if (! AudioEffectX::allocateArrangement (pluginInput, busUtils.getNumChannels (true, 0))) | |||
return false; | |||
if (! AudioEffectX::allocateArrangement (pluginOutput, busUtils.getNumChannels (false, 0))) | |||
if (! AudioEffectX::allocateArrangement (pluginOutput, busUtils.findTotalNumChannels (false))) | |||
{ | |||
AudioEffectX::deallocateArrangement (pluginInput); | |||
*pluginInput = 0; | |||
return false; | |||
} | |||
SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (true, 0), **pluginInput); | |||
SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (false, 0), **pluginOutput); | |||
if (busUtils.getBusCount (true) > 1) | |||
{ | |||
AudioChannelSet layout = AudioChannelSet::canonicalChannelSet (busUtils.findTotalNumChannels(true)); | |||
SpeakerMappings::channelSetToVstArrangement (layout, **pluginInput); | |||
} | |||
else | |||
{ | |||
SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (true, 0), **pluginInput); | |||
} | |||
if (busUtils.getBusCount (false) > 1) | |||
{ | |||
AudioChannelSet layout = AudioChannelSet::canonicalChannelSet (busUtils.findTotalNumChannels(false)); | |||
SpeakerMappings::channelSetToVstArrangement (layout, **pluginOutput); | |||
} | |||
else | |||
{ | |||
SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (false, 0), **pluginOutput); | |||
} | |||
return true; | |||
} | |||
@@ -985,11 +1042,14 @@ public: | |||
bool getPinProperties (VstPinProperties& properties, bool direction, int index) const | |||
{ | |||
// index refers to the absolute index when combining all channels of every bus | |||
if (index >= (direction ? cEffect.numInputs : cEffect.numOutputs)) | |||
return false; | |||
const int n = busUtils.getBusCount(direction); | |||
int busIdx; | |||
for (busIdx = 0; busIdx < n; ++busIdx) | |||
{ | |||
const int numChans = busUtils.getNumChannels(direction, busIdx); | |||
const int numChans = busUtils.getNumChannels (direction, busIdx); | |||
if (index < numChans) | |||
break; | |||
@@ -997,25 +1057,30 @@ public: | |||
} | |||
if (busIdx >= n) | |||
return false; | |||
{ | |||
properties.flags = kVstPinUseSpeaker; | |||
properties.label[0] = 0; | |||
properties.shortLabel[0] = 0; | |||
properties.arrangementType = kSpeakerArrEmpty; | |||
return true; | |||
} | |||
const AudioProcessor::AudioProcessorBus& busInfo = busUtils.getFilterBus (direction).getReference (busIdx); | |||
busInfo.name.copyToUTF8 (properties.label, (size_t) (kVstMaxLabelLen - 1)); | |||
busInfo.name.copyToUTF8 (properties.shortLabel, (size_t) (kVstMaxShortLabelLen - 1)); | |||
String channelName = busInfo.name; | |||
VstInt32 type = SpeakerMappings::channelSetToVstArrangementType (busInfo.channels); | |||
channelName += | |||
String (" ") + AudioChannelSet::getAbbreviatedChannelTypeName (busInfo.channels.getTypeOfChannel(index)); | |||
if (type != kSpeakerArrEmpty) | |||
{ | |||
properties.flags = kVstPinUseSpeaker | kVstPinIsActive; | |||
properties.arrangementType = type; | |||
} | |||
else | |||
{ | |||
properties.flags = 0; | |||
properties.arrangementType = 0; | |||
} | |||
channelName.copyToUTF8 (properties.label, (size_t) (kVstMaxLabelLen - 1)); | |||
channelName.copyToUTF8 (properties.shortLabel, (size_t) (kVstMaxShortLabelLen - 1)); | |||
properties.flags = kVstPinUseSpeaker | kVstPinIsActive; | |||
properties.arrangementType = SpeakerMappings::channelSetToVstArrangementType (busInfo.channels); | |||
if (properties.arrangementType == kSpeakerArrEmpty) | |||
properties.flags &= ~kVstPinIsActive; | |||
if (busInfo.channels.size() == 2) | |||
properties.flags |= kVstPinIsStereo; | |||
@@ -1070,11 +1135,14 @@ public: | |||
{ | |||
Array<AudioChannelSet::ChannelType> chans (channels.getChannelTypes()); | |||
if (channels == AudioChannelSet::disabled()) | |||
return kSpeakerArrEmpty; | |||
for (const Mapping* m = getMappings(); m->vst2 != kSpeakerArrEmpty; ++m) | |||
if (m->matches (chans)) | |||
return m->vst2; | |||
return kSpeakerArrEmpty; | |||
return kSpeakerArrUserDefined; | |||
} | |||
static void channelSetToVstArrangement (const AudioChannelSet& channels, VstSpeakerArrangement& result) | |||
@@ -1134,25 +1202,25 @@ public: | |||
{ | |||
switch (type) | |||
{ | |||
case AudioChannelSet::ChannelType::left: return kSpeakerL; | |||
case AudioChannelSet::ChannelType::right: return kSpeakerR; | |||
case AudioChannelSet::ChannelType::centre: return kSpeakerC; | |||
case AudioChannelSet::ChannelType::subbass: return kSpeakerLfe; | |||
case AudioChannelSet::ChannelType::surroundLeft: return kSpeakerLs; | |||
case AudioChannelSet::ChannelType::surroundRight: return kSpeakerRs; | |||
case AudioChannelSet::ChannelType::centreLeft: return kSpeakerLc; | |||
case AudioChannelSet::ChannelType::centreRight: return kSpeakerRc; | |||
case AudioChannelSet::ChannelType::surround: return kSpeakerS; | |||
case AudioChannelSet::ChannelType::sideLeft: return kSpeakerSl; | |||
case AudioChannelSet::ChannelType::sideRight: return kSpeakerSr; | |||
case AudioChannelSet::ChannelType::topMiddle: return kSpeakerTm; | |||
case AudioChannelSet::ChannelType::topFrontLeft: return kSpeakerTfl; | |||
case AudioChannelSet::ChannelType::topFrontCentre: return kSpeakerTfc; | |||
case AudioChannelSet::ChannelType::topFrontRight: return kSpeakerTfr; | |||
case AudioChannelSet::ChannelType::topRearLeft: return kSpeakerTrl; | |||
case AudioChannelSet::ChannelType::topRearCentre: return kSpeakerTrc; | |||
case AudioChannelSet::ChannelType::topRearRight: return kSpeakerTrr; | |||
case AudioChannelSet::ChannelType::subbass2: return kSpeakerLfe2; | |||
case AudioChannelSet::left: return kSpeakerL; | |||
case AudioChannelSet::right: return kSpeakerR; | |||
case AudioChannelSet::centre: return kSpeakerC; | |||
case AudioChannelSet::subbass: return kSpeakerLfe; | |||
case AudioChannelSet::surroundLeft: return kSpeakerLs; | |||
case AudioChannelSet::surroundRight: return kSpeakerRs; | |||
case AudioChannelSet::centreLeft: return kSpeakerLc; | |||
case AudioChannelSet::centreRight: return kSpeakerRc; | |||
case AudioChannelSet::surround: return kSpeakerS; | |||
case AudioChannelSet::sideLeft: return kSpeakerSl; | |||
case AudioChannelSet::sideRight: return kSpeakerSr; | |||
case AudioChannelSet::topMiddle: return kSpeakerTm; | |||
case AudioChannelSet::topFrontLeft: return kSpeakerTfl; | |||
case AudioChannelSet::topFrontCentre: return kSpeakerTfc; | |||
case AudioChannelSet::topFrontRight: return kSpeakerTfr; | |||
case AudioChannelSet::topRearLeft: return kSpeakerTrl; | |||
case AudioChannelSet::topRearCentre: return kSpeakerTrc; | |||
case AudioChannelSet::topRearRight: return kSpeakerTrr; | |||
case AudioChannelSet::subbass2: return kSpeakerLfe2; | |||
default: break; | |||
} | |||
@@ -1163,29 +1231,29 @@ public: | |||
{ | |||
switch (type) | |||
{ | |||
case kSpeakerL: return AudioChannelSet::ChannelType::left; | |||
case kSpeakerR: return AudioChannelSet::ChannelType::right; | |||
case kSpeakerC: return AudioChannelSet::ChannelType::centre; | |||
case kSpeakerLfe: return AudioChannelSet::ChannelType::subbass; | |||
case kSpeakerLs: return AudioChannelSet::ChannelType::surroundLeft; | |||
case kSpeakerRs: return AudioChannelSet::ChannelType::surroundRight; | |||
case kSpeakerLc: return AudioChannelSet::ChannelType::centreLeft; | |||
case kSpeakerRc: return AudioChannelSet::ChannelType::centreRight; | |||
case kSpeakerS: return AudioChannelSet::ChannelType::surround; | |||
case kSpeakerSl: return AudioChannelSet::ChannelType::sideLeft; | |||
case kSpeakerSr: return AudioChannelSet::ChannelType::sideRight; | |||
case kSpeakerTm: return AudioChannelSet::ChannelType::topMiddle; | |||
case kSpeakerTfl: return AudioChannelSet::ChannelType::topFrontLeft; | |||
case kSpeakerTfc: return AudioChannelSet::ChannelType::topFrontCentre; | |||
case kSpeakerTfr: return AudioChannelSet::ChannelType::topFrontRight; | |||
case kSpeakerTrl: return AudioChannelSet::ChannelType::topRearLeft; | |||
case kSpeakerTrc: return AudioChannelSet::ChannelType::topRearCentre; | |||
case kSpeakerTrr: return AudioChannelSet::ChannelType::topRearRight; | |||
case kSpeakerLfe2: return AudioChannelSet::ChannelType::subbass2; | |||
case kSpeakerL: return AudioChannelSet::left; | |||
case kSpeakerR: return AudioChannelSet::right; | |||
case kSpeakerC: return AudioChannelSet::centre; | |||
case kSpeakerLfe: return AudioChannelSet::subbass; | |||
case kSpeakerLs: return AudioChannelSet::surroundLeft; | |||
case kSpeakerRs: return AudioChannelSet::surroundRight; | |||
case kSpeakerLc: return AudioChannelSet::centreLeft; | |||
case kSpeakerRc: return AudioChannelSet::centreRight; | |||
case kSpeakerS: return AudioChannelSet::surround; | |||
case kSpeakerSl: return AudioChannelSet::sideLeft; | |||
case kSpeakerSr: return AudioChannelSet::sideRight; | |||
case kSpeakerTm: return AudioChannelSet::topMiddle; | |||
case kSpeakerTfl: return AudioChannelSet::topFrontLeft; | |||
case kSpeakerTfc: return AudioChannelSet::topFrontCentre; | |||
case kSpeakerTfr: return AudioChannelSet::topFrontRight; | |||
case kSpeakerTrl: return AudioChannelSet::topRearLeft; | |||
case kSpeakerTrc: return AudioChannelSet::topRearCentre; | |||
case kSpeakerTrr: return AudioChannelSet::topRearRight; | |||
case kSpeakerLfe2: return AudioChannelSet::subbass2; | |||
default: break; | |||
} | |||
return AudioChannelSet::ChannelType::unknown; | |||
return AudioChannelSet::unknown; | |||
} | |||
}; | |||
@@ -1249,25 +1317,6 @@ public: | |||
if (hostWindow != 0) | |||
checkWindowVisibilityVST (hostWindow, editorComp, useNSView); | |||
#endif | |||
tryMasterIdle(); | |||
} | |||
void tryMasterIdle() | |||
{ | |||
if (Component::isMouseButtonDownAnywhere() && ! recursionCheck) | |||
{ | |||
const juce::uint32 now = juce::Time::getMillisecondCounter(); | |||
if (now > lastMasterIdleCall + 20 && editorComp != nullptr) | |||
{ | |||
lastMasterIdleCall = now; | |||
recursionCheck = true; | |||
masterIdle(); | |||
recursionCheck = false; | |||
} | |||
} | |||
} | |||
void doIdleCallback() | |||
@@ -1276,7 +1325,7 @@ public: | |||
if (MessageManager::getInstance()->isThisTheMessageThread() | |||
&& ! recursionCheck) | |||
{ | |||
recursionCheck = true; | |||
ScopedValueSetter<bool> svs (recursionCheck, true, false); | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
@@ -1285,8 +1334,6 @@ public: | |||
for (int i = ComponentPeer::getNumPeers(); --i >= 0;) | |||
if (ComponentPeer* p = ComponentPeer::getPeer(i)) | |||
p->performAnyPendingRepaintsNow(); | |||
recursionCheck = false; | |||
} | |||
} | |||
} | |||
@@ -1322,7 +1369,7 @@ public: | |||
PopupMenu::dismissAllActiveMenus(); | |||
jassert (! recursionCheck); | |||
recursionCheck = true; | |||
ScopedValueSetter<bool> svs (recursionCheck, true, false); | |||
if (editorComp != nullptr) | |||
{ | |||
@@ -1333,7 +1380,6 @@ public: | |||
if (canDeleteLaterIfModal) | |||
{ | |||
shouldDeleteEditor = true; | |||
recursionCheck = false; | |||
return; | |||
} | |||
} | |||
@@ -1358,8 +1404,6 @@ public: | |||
#if JUCE_LINUX | |||
hostWindow = 0; | |||
#endif | |||
recursionCheck = false; | |||
} | |||
} | |||
@@ -1422,7 +1466,7 @@ public: | |||
{ | |||
editorSize.left = 0; | |||
editorSize.top = 0; | |||
editorSize.right = (VstInt16) editorComp->getWidth(); | |||
editorSize.right = (VstInt16) editorComp->getWidth(); | |||
editorSize.bottom = (VstInt16) editorComp->getHeight(); | |||
*((ERect**) ptr) = &editorSize; | |||
@@ -1515,8 +1559,7 @@ public: | |||
//============================================================================== | |||
// A component to hold the AudioProcessorEditor, and cope with some housekeeping | |||
// chores when it changes or repaints. | |||
class EditorCompWrapper : public Component, | |||
public AsyncUpdater | |||
class EditorCompWrapper : public Component | |||
{ | |||
public: | |||
EditorCompWrapper (JuceVSTWrapper& w, AudioProcessorEditor* editor) | |||
@@ -1533,6 +1576,8 @@ public: | |||
if (! getHostType().isReceptor()) | |||
addMouseListener (this, true); | |||
#endif | |||
ignoreUnused (fakeMouseGenerator); | |||
} | |||
~EditorCompWrapper() | |||
@@ -1543,15 +1588,6 @@ public: | |||
void paint (Graphics&) override {} | |||
void paintOverChildren (Graphics&) override | |||
{ | |||
// this causes an async call to masterIdle() to help | |||
// creaky old DAWs like Nuendo repaint themselves while we're | |||
// repainting. Otherwise they just seem to give up and sit there | |||
// waiting. | |||
triggerAsyncUpdate(); | |||
} | |||
#if JUCE_MAC | |||
bool keyPressed (const KeyPress&) override | |||
{ | |||
@@ -1605,11 +1641,6 @@ public: | |||
} | |||
} | |||
void handleAsyncUpdate() override | |||
{ | |||
wrapper.tryMasterIdle(); | |||
} | |||
#if JUCE_WINDOWS | |||
void mouseDown (const MouseEvent&) override | |||
{ | |||
@@ -1710,7 +1741,7 @@ private: | |||
if (filter != nullptr) | |||
{ | |||
int numChannels = filter->getTotalNumInputChannels() + filter->getTotalNumOutputChannels(); | |||
int numChannels = cEffect.numInputs + cEffect.numOutputs; | |||
tmpBuffers.tempChannels.insertMultiple (0, nullptr, numChannels); | |||
} | |||
} | |||
@@ -1721,6 +1752,29 @@ private: | |||
deleteTempChannels (doubleTempBuffers); | |||
} | |||
//============================================================================== | |||
void resetAuxChannelsToDefaultLayout (bool isInput) const | |||
{ | |||
// set side-chain and aux channels to their default layout | |||
for (int busIdx = 1; busIdx < busUtils.getBusCount (isInput); ++busIdx) | |||
{ | |||
bool success = filter->setPreferredBusArrangement (isInput, busIdx, busUtils.getDefaultLayoutForBus (isInput, busIdx)); | |||
// VST 2 only supports a static channel layout on aux/sidechain channels | |||
// You must at least support the default layout regardless of the layout of the main bus. | |||
// If this is a problem for your plug-in, then consider using VST-3. | |||
jassert (success); | |||
ignoreUnused (success); | |||
} | |||
} | |||
bool hostOnlySupportsStereo () const | |||
{ | |||
const PluginHostType host (getHostType ()); | |||
// there are probably more hosts that need listing here | |||
return host.isAbletonLive(); | |||
} | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVSTWrapper) | |||
}; | |||
@@ -34,9 +34,6 @@ | |||
#include "../utility/juce_FakeMouseMoveGenerator.h" | |||
#include "../utility/juce_CarbonVisibility.h" | |||
#undef Component | |||
#undef Point | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -66,6 +63,11 @@ static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, vo | |||
updateEditorCompBoundsVST ((Component*) user); | |||
return noErr; | |||
} | |||
static bool shouldManuallyCloseHostWindow() | |||
{ | |||
return getHostType().isCubase7orLater() || getHostType().isRenoise(); | |||
} | |||
#endif | |||
//============================================================================== | |||
@@ -87,7 +89,7 @@ void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, | |||
{ | |||
NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: parentWindowOrView]; | |||
if (getHostType().isCubase7orLater()) | |||
if (shouldManuallyCloseHostWindow()) | |||
{ | |||
[hostWindow setReleasedWhenClosed: NO]; | |||
} | |||
@@ -212,7 +214,7 @@ void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSVi | |||
comp->removeFromDesktop(); | |||
[pluginView release]; | |||
if (getHostType().isCubase7orLater()) | |||
if (shouldManuallyCloseHostWindow()) | |||
[hostWindow close]; | |||
else | |||
[hostWindow release]; | |||
@@ -38,6 +38,10 @@ | |||
#define JUCE_VST3_CAN_REPLACE_VST2 1 | |||
#endif | |||
#ifndef JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | |||
#define JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS 1 | |||
#endif | |||
#if JUCE_VST3_CAN_REPLACE_VST2 | |||
#if JUCE_MSVC | |||
#pragma warning (push) | |||
@@ -51,9 +55,6 @@ | |||
#endif | |||
#endif | |||
#undef Point | |||
#undef Component | |||
namespace juce | |||
{ | |||
@@ -480,9 +481,11 @@ private: | |||
parameters.addParameter (new BypassParam (*pluginInstance, numParameters)); | |||
} | |||
// We need to account for the bypass parameter in the numParameters passed to | |||
// the next function | |||
#if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | |||
// (NB: the +1 is to account for the bypass parameter) | |||
initialiseMidiControllerMappings (pluginInstance->getNumParameters() + 1); | |||
#endif | |||
audioProcessorChanged (pluginInstance); | |||
} | |||
} | |||
@@ -639,7 +642,7 @@ private: | |||
private: | |||
//============================================================================== | |||
class ContentWrapperComponent : public juce::Component | |||
class ContentWrapperComponent : public Component | |||
{ | |||
public: | |||
ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin) | |||
@@ -1569,11 +1572,13 @@ public: | |||
const int id = (int) paramQueue->getParameterId(); | |||
if (isPositiveAndBelow (id, pluginInstance->getNumParameters())) | |||
pluginInstance->setParameter (id, (float) value); | |||
pluginInstance->setParameter (id, static_cast<float> (value)); | |||
else if (id == vstBypassParameterId) | |||
setBypassed (static_cast<float> (value) != 0.0f); | |||
#if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | |||
else | |||
addParameterChangeToMidiBuffer (offsetSamples, id, value); | |||
#endif | |||
} | |||
} | |||
} | |||
@@ -28,7 +28,6 @@ | |||
#include "../juce_gui_basics/juce_gui_basics.h" | |||
#include "../juce_audio_basics/juce_audio_basics.h" | |||
#include "../juce_audio_processors/juce_audio_processors.h" | |||
#include "utility/juce_CheckSettingMacros.h" | |||
namespace juce | |||
{ | |||
@@ -31,6 +31,11 @@ | |||
#error "You need to enable at least one plugin format!" | |||
#endif | |||
#ifdef JUCE_CHECKSETTINGMACROS_H | |||
#error "This header should never be included twice! Otherwise something is wrong." | |||
#endif | |||
#define JUCE_CHECKSETTINGMACROS_H | |||
#ifndef JucePlugin_IsSynth | |||
#error "You need to define the JucePlugin_IsSynth value!" | |||
#endif | |||
@@ -55,10 +60,6 @@ | |||
#error "JucePlugin_Latency is now deprecated - instead, call the AudioProcessor::setLatencySamples() method if your plugin has a non-zero delay" | |||
#endif | |||
#ifndef JucePlugin_SilenceInProducesSilenceOut | |||
#error "You need to define the JucePlugin_SilenceInProducesSilenceOut value!" | |||
#endif | |||
#ifndef JucePlugin_EditorRequiresKeyboardFocus | |||
#error "You need to define the JucePlugin_EditorRequiresKeyboardFocus value!" | |||
#endif | |||
@@ -31,12 +31,12 @@ using namespace juce; | |||
namespace juce | |||
{ | |||
#if JUCE_MAC | |||
#define Point juce::Point | |||
#define Component juce::Component | |||
#define Component juce::Component | |||
void repostCurrentNSEvent(); | |||
#endif | |||
#if JUCE_MAC | |||
#define Point juce::Point | |||
void repostCurrentNSEvent(); | |||
#endif | |||
//============================================================================== | |||
inline const PluginHostType& getHostType() | |||
@@ -50,23 +50,14 @@ | |||
#define JUCE_SUPPORT_CARBON 1 | |||
#endif | |||
#define Point CarbonDummyPointName | |||
#if JUCE_SUPPORT_CARBON | |||
#define Component CarbonDummyCompName | |||
#endif | |||
#ifdef __OBJC__ | |||
#include <Cocoa/Cocoa.h> | |||
#endif | |||
#if JUCE_SUPPORT_CARBON | |||
#include <Carbon/Carbon.h> | |||
#undef Component | |||
#endif | |||
#undef Point | |||
#include <objc/runtime.h> | |||
#include <objc/objc.h> | |||
#include <objc/message.h> | |||
@@ -72,6 +72,15 @@ struct PluginBusUtilities | |||
return nullptr; | |||
} | |||
int maxNumberOfChannels() const noexcept | |||
{ | |||
int maxChannels = 0; | |||
for (int i = 0; i < supportedLayouts.size(); ++i) | |||
maxChannels = jmax (maxChannels, supportedLayouts.getReference (i).size()); | |||
return maxChannels; | |||
} | |||
int defaultLayoutIndex; | |||
bool busIgnoresLayout, canBeDisabled, isEnabledByDefault; | |||
SortedSet<AudioChannelSet> supportedLayouts; | |||
@@ -88,12 +97,12 @@ struct PluginBusUtilities | |||
bool hasOutputs (int bus) const noexcept { return isBusEnabled (false, bus); } | |||
int getNumEnabledBuses (bool inputBus) const noexcept { int i; for (i = 0; i < getBusCount (inputBus); ++i) if (! isBusEnabled (inputBus, i)) break; return i; } | |||
int findTotalNumChannels (bool isInput) const noexcept | |||
int findTotalNumChannels (bool isInput, int busOffset = 0) const noexcept | |||
{ | |||
int total = 0; | |||
const AudioBusArray& ioBuses = getFilterBus (isInput); | |||
for (int i = 0; i < ioBuses.size(); ++i) | |||
for (int i = busOffset; i < ioBuses.size(); ++i) | |||
total += ioBuses.getReference (i).channels.size(); | |||
return total; | |||
@@ -341,7 +350,11 @@ private: | |||
case 2: | |||
sets.add (AudioChannelSet::stereo()); | |||
break; | |||
case 3: | |||
sets.add (AudioChannelSet::createLCR()); | |||
break; | |||
case 4: | |||
sets.add (AudioChannelSet::createLCRS()); | |||
sets.add (AudioChannelSet::quadraphonic()); | |||
sets.add (AudioChannelSet::ambisonic()); | |||
break; | |||
@@ -351,6 +364,7 @@ private: | |||
break; | |||
case 6: | |||
sets.add (AudioChannelSet::hexagonal()); | |||
sets.add (AudioChannelSet::create5point1()); | |||
sets.add (AudioChannelSet::create6point0()); | |||
break; | |||
case 7: | |||
@@ -53,6 +53,7 @@ public: | |||
MergingPyramix, | |||
MuseReceptorGeneric, | |||
Reaper, | |||
Renoise, | |||
SteinbergCubase4, | |||
SteinbergCubase5, | |||
SteinbergCubase5Bridged, | |||
@@ -80,31 +81,33 @@ public: | |||
HostType type; | |||
//============================================================================== | |||
bool isAbletonLive() const noexcept { return type == AbletonLive6 || type == AbletonLive7 || type == AbletonLive8 || type == AbletonLiveGeneric; } | |||
bool isAdobeAudition() const noexcept { return type == AdobeAudition; } | |||
bool isArdour() const noexcept { return type == Ardour; } | |||
bool isDigitalPerformer() const noexcept { return type == DigitalPerformer; } | |||
bool isCubase() const noexcept { return type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubase5Bridged || type == SteinbergCubase6 || type == SteinbergCubase7 || type == SteinbergCubase8 || type == SteinbergCubaseGeneric; } | |||
bool isCubase7orLater() const noexcept { return isCubase() && ! (type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubase6); } | |||
bool isCubaseBridged() const noexcept { return type == SteinbergCubase5Bridged; } | |||
bool isLogic() const noexcept { return type == AppleLogic; } | |||
bool isFinalCut() const noexcept { return type == FinalCut; } | |||
bool isFruityLoops() const noexcept { return type == FruityLoops; } | |||
bool isNuendo() const noexcept { return type == SteinbergNuendo3 || type == SteinbergNuendo4 || type == SteinbergNuendo5 || type == SteinbergNuendoGeneric; } | |||
bool isPremiere() const noexcept { return type == AdobePremierePro; } | |||
bool isPyramix() const noexcept { return type == MergingPyramix; } | |||
bool isReceptor() const noexcept { return type == MuseReceptorGeneric; } | |||
bool isReaper() const noexcept { return type == Reaper; } | |||
bool isSamplitude() const noexcept { return type == MagixSamplitude; } | |||
bool isSonar() const noexcept { return type == CakewalkSonar8 || type == CakewalkSonarGeneric; } | |||
bool isSteinbergTestHost() const noexcept{ return type == SteinbergTestHost; } | |||
bool isSteinberg() const noexcept { return isCubase() || isNuendo() || isWavelab() || isSteinbergTestHost(); } | |||
bool isStudioOne() const noexcept { return type == StudioOne; } | |||
bool isTracktion() const noexcept { return type == Tracktion3 || type == TracktionGeneric; } | |||
bool isVBVSTScanner() const noexcept { return type == VBVSTScanner; } | |||
bool isWaveBurner() const noexcept { return type == WaveBurner; } | |||
bool isWavelab() const noexcept { return isWavelabLegacy() || type == SteinbergWavelab7 || type == SteinbergWavelab8 || type == SteinbergWavelabGeneric; } | |||
bool isWavelabLegacy() const noexcept { return type == SteinbergWavelab5 || type == SteinbergWavelab6; } | |||
bool isAbletonLive() const noexcept { return type == AbletonLive6 || type == AbletonLive7 || type == AbletonLive8 || type == AbletonLiveGeneric; } | |||
bool isAdobeAudition() const noexcept { return type == AdobeAudition; } | |||
bool isArdour() const noexcept { return type == Ardour; } | |||
bool isDigitalPerformer() const noexcept { return type == DigitalPerformer; } | |||
bool isCubase() const noexcept { return type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubase5Bridged || type == SteinbergCubase6 || type == SteinbergCubase7 || type == SteinbergCubase8 || type == SteinbergCubaseGeneric; } | |||
bool isCubase7orLater() const noexcept { return isCubase() && ! (type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubase6); } | |||
bool isCubaseBridged() const noexcept { return type == SteinbergCubase5Bridged; } | |||
bool isLogic() const noexcept { return type == AppleLogic; } | |||
bool isFinalCut() const noexcept { return type == FinalCut; } | |||
bool isFruityLoops() const noexcept { return type == FruityLoops; } | |||
bool isNuendo() const noexcept { return type == SteinbergNuendo3 || type == SteinbergNuendo4 || type == SteinbergNuendo5 || type == SteinbergNuendoGeneric; } | |||
bool isPremiere() const noexcept { return type == AdobePremierePro; } | |||
bool isPyramix() const noexcept { return type == MergingPyramix; } | |||
bool isReceptor() const noexcept { return type == MuseReceptorGeneric; } | |||
bool isReaper() const noexcept { return type == Reaper; } | |||
bool isSamplitude() const noexcept { return type == MagixSamplitude; } | |||
bool isSonar() const noexcept { return type == CakewalkSonar8 || type == CakewalkSonarGeneric; } | |||
bool isSteinbergTestHost() const noexcept { return type == SteinbergTestHost; } | |||
bool isSteinberg() const noexcept { return isCubase() || isNuendo() || isWavelab() || isSteinbergTestHost(); } | |||
bool isStudioOne() const noexcept { return type == StudioOne; } | |||
bool isTracktion() const noexcept { return type == Tracktion3 || type == TracktionGeneric; } | |||
bool isVBVSTScanner() const noexcept { return type == VBVSTScanner; } | |||
bool isWaveBurner() const noexcept { return type == WaveBurner; } | |||
bool isWavelab() const noexcept { return isWavelabLegacy() || type == SteinbergWavelab7 || type == SteinbergWavelab8 || type == SteinbergWavelabGeneric; } | |||
bool isWavelabLegacy() const noexcept { return type == SteinbergWavelab5 || type == SteinbergWavelab6; } | |||
bool isRenoise() const noexcept { return type == Renoise; } | |||
bool isProTools() const noexcept { return type == DigidesignProTools; } | |||
//============================================================================== | |||
const char* getHostDescription() const noexcept | |||
@@ -128,6 +131,7 @@ public: | |||
case MergingPyramix: return "Pyramix"; | |||
case MuseReceptorGeneric: return "Muse Receptor"; | |||
case Reaper: return "Reaper"; | |||
case Renoise: return "Renoise"; | |||
case SteinbergCubase4: return "Steinberg Cubase 4"; | |||
case SteinbergCubase5: return "Steinberg Cubase 5"; | |||
case SteinbergCubase5Bridged: return "Steinberg Cubase 5 Bridged"; | |||
@@ -198,6 +202,7 @@ private: | |||
if (hostPath.containsIgnoreCase ("Studio One")) return StudioOne; | |||
if (hostPath.containsIgnoreCase ("Tracktion 3")) return Tracktion3; | |||
if (hostFilename.containsIgnoreCase ("Tracktion")) return TracktionGeneric; | |||
if (hostFilename.containsIgnoreCase ("Renoise")) return Renoise; | |||
#elif JUCE_WINDOWS | |||
if (hostFilename.containsIgnoreCase ("Live 6.")) return AbletonLive6; | |||
@@ -235,6 +240,7 @@ private: | |||
if (hostFilename.containsIgnoreCase ("VST_Scanner")) return VBVSTScanner; | |||
if (hostPath.containsIgnoreCase ("Merging Technologies")) return MergingPyramix; | |||
if (hostFilename.startsWithIgnoreCase ("Sam")) return MagixSamplitude; | |||
if (hostFilename.containsIgnoreCase ("Renoise")) return Renoise; | |||
#elif JUCE_LINUX | |||
if (hostFilename.containsIgnoreCase ("Ardour")) return Ardour; | |||
@@ -27,7 +27,6 @@ | |||
#endif | |||
#include "../../juce_core/system/juce_TargetPlatform.h" | |||
#include "../utility/juce_CheckSettingMacros.h" | |||
#include "juce_IncludeModuleHeaders.h" | |||
#if _MSC_VER || JUCE_MINGW | |||
@@ -387,11 +387,6 @@ public: | |||
void* getPlatformSpecificData() override { return audioUnit; } | |||
const String getName() const override { return pluginName; } | |||
bool silenceInProducesSilenceOut() const override | |||
{ | |||
return getTailLengthSeconds() <= 0; | |||
} | |||
double getTailLengthSeconds() const override | |||
{ | |||
Float64 tail = 0; | |||
@@ -252,7 +252,6 @@ public: | |||
bool acceptsMidi() const { return false; } | |||
bool producesMidi() const { return false; } | |||
bool silenceInProducesSilenceOut() const { return plugin == nullptr; } // ..any way to get a proper answer for these? | |||
double getTailLengthSeconds() const { return 0.0; } | |||
//============================================================================== | |||
@@ -143,25 +143,25 @@ static inline Steinberg::Vst::Speaker getSpeakerType (AudioChannelSet::ChannelTy | |||
switch (type) | |||
{ | |||
case AudioChannelSet::ChannelType::left: return kSpeakerL; | |||
case AudioChannelSet::ChannelType::right: return kSpeakerR; | |||
case AudioChannelSet::ChannelType::centre: return kSpeakerC; | |||
case AudioChannelSet::ChannelType::subbass: return kSpeakerLfe; | |||
case AudioChannelSet::ChannelType::surroundLeft: return kSpeakerLs; | |||
case AudioChannelSet::ChannelType::surroundRight: return kSpeakerRs; | |||
case AudioChannelSet::ChannelType::centreLeft: return kSpeakerLc; | |||
case AudioChannelSet::ChannelType::centreRight: return kSpeakerRc; | |||
case AudioChannelSet::ChannelType::surround: return kSpeakerS; | |||
case AudioChannelSet::ChannelType::sideLeft: return kSpeakerSl; | |||
case AudioChannelSet::ChannelType::sideRight: return kSpeakerSr; | |||
case AudioChannelSet::ChannelType::topMiddle: return kSpeakerTm; | |||
case AudioChannelSet::ChannelType::topFrontLeft: return kSpeakerTfl; | |||
case AudioChannelSet::ChannelType::topFrontCentre: return kSpeakerTfc; | |||
case AudioChannelSet::ChannelType::topFrontRight: return kSpeakerTfr; | |||
case AudioChannelSet::ChannelType::topRearLeft: return kSpeakerTrl; | |||
case AudioChannelSet::ChannelType::topRearCentre: return kSpeakerTrc; | |||
case AudioChannelSet::ChannelType::topRearRight: return kSpeakerTrr; | |||
case AudioChannelSet::ChannelType::subbass2: return kSpeakerLfe2; | |||
case AudioChannelSet::left: return kSpeakerL; | |||
case AudioChannelSet::right: return kSpeakerR; | |||
case AudioChannelSet::centre: return kSpeakerC; | |||
case AudioChannelSet::subbass: return kSpeakerLfe; | |||
case AudioChannelSet::surroundLeft: return kSpeakerLs; | |||
case AudioChannelSet::surroundRight: return kSpeakerRs; | |||
case AudioChannelSet::centreLeft: return kSpeakerLc; | |||
case AudioChannelSet::centreRight: return kSpeakerRc; | |||
case AudioChannelSet::surround: return kSpeakerS; | |||
case AudioChannelSet::sideLeft: return kSpeakerSl; | |||
case AudioChannelSet::sideRight: return kSpeakerSr; | |||
case AudioChannelSet::topMiddle: return kSpeakerTm; | |||
case AudioChannelSet::topFrontLeft: return kSpeakerTfl; | |||
case AudioChannelSet::topFrontCentre: return kSpeakerTfc; | |||
case AudioChannelSet::topFrontRight: return kSpeakerTfr; | |||
case AudioChannelSet::topRearLeft: return kSpeakerTrl; | |||
case AudioChannelSet::topRearCentre: return kSpeakerTrc; | |||
case AudioChannelSet::topRearRight: return kSpeakerTrr; | |||
case AudioChannelSet::subbass2: return kSpeakerLfe2; | |||
default: break; | |||
} | |||
@@ -174,29 +174,29 @@ static inline AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::Speak | |||
switch (type) | |||
{ | |||
case kSpeakerL: return AudioChannelSet::ChannelType::left; | |||
case kSpeakerR: return AudioChannelSet::ChannelType::right; | |||
case kSpeakerC: return AudioChannelSet::ChannelType::centre; | |||
case kSpeakerLfe: return AudioChannelSet::ChannelType::subbass; | |||
case kSpeakerLs: return AudioChannelSet::ChannelType::surroundLeft; | |||
case kSpeakerRs: return AudioChannelSet::ChannelType::surroundRight; | |||
case kSpeakerLc: return AudioChannelSet::ChannelType::centreLeft; | |||
case kSpeakerRc: return AudioChannelSet::ChannelType::centreRight; | |||
case kSpeakerS: return AudioChannelSet::ChannelType::surround; | |||
case kSpeakerSl: return AudioChannelSet::ChannelType::sideLeft; | |||
case kSpeakerSr: return AudioChannelSet::ChannelType::sideRight; | |||
case kSpeakerTm: return AudioChannelSet::ChannelType::topMiddle; | |||
case kSpeakerTfl: return AudioChannelSet::ChannelType::topFrontLeft; | |||
case kSpeakerTfc: return AudioChannelSet::ChannelType::topFrontCentre; | |||
case kSpeakerTfr: return AudioChannelSet::ChannelType::topFrontRight; | |||
case kSpeakerTrl: return AudioChannelSet::ChannelType::topRearLeft; | |||
case kSpeakerTrc: return AudioChannelSet::ChannelType::topRearCentre; | |||
case kSpeakerTrr: return AudioChannelSet::ChannelType::topRearRight; | |||
case kSpeakerLfe2: return AudioChannelSet::ChannelType::subbass2; | |||
case kSpeakerL: return AudioChannelSet::left; | |||
case kSpeakerR: return AudioChannelSet::right; | |||
case kSpeakerC: return AudioChannelSet::centre; | |||
case kSpeakerLfe: return AudioChannelSet::subbass; | |||
case kSpeakerLs: return AudioChannelSet::surroundLeft; | |||
case kSpeakerRs: return AudioChannelSet::surroundRight; | |||
case kSpeakerLc: return AudioChannelSet::centreLeft; | |||
case kSpeakerRc: return AudioChannelSet::centreRight; | |||
case kSpeakerS: return AudioChannelSet::surround; | |||
case kSpeakerSl: return AudioChannelSet::sideLeft; | |||
case kSpeakerSr: return AudioChannelSet::sideRight; | |||
case kSpeakerTm: return AudioChannelSet::topMiddle; | |||
case kSpeakerTfl: return AudioChannelSet::topFrontLeft; | |||
case kSpeakerTfc: return AudioChannelSet::topFrontCentre; | |||
case kSpeakerTfr: return AudioChannelSet::topFrontRight; | |||
case kSpeakerTrl: return AudioChannelSet::topRearLeft; | |||
case kSpeakerTrc: return AudioChannelSet::topRearCentre; | |||
case kSpeakerTrr: return AudioChannelSet::topRearRight; | |||
case kSpeakerLfe2: return AudioChannelSet::subbass2; | |||
default: break; | |||
} | |||
return AudioChannelSet::ChannelType::unknown; | |||
return AudioChannelSet::unknown; | |||
} | |||
static inline Steinberg::Vst::SpeakerArrangement getSpeakerArrangement (const AudioChannelSet& channels) noexcept | |||
@@ -25,9 +25,6 @@ | |||
#ifndef JUCE_VST3HEADERS_H_INCLUDED | |||
#define JUCE_VST3HEADERS_H_INCLUDED | |||
#undef Point | |||
#undef Component | |||
// Wow, those Steinberg guys really don't worry too much about compiler warnings. | |||
#if _MSC_VER | |||
#pragma warning (disable: 4505) | |||
@@ -85,8 +82,6 @@ | |||
#if JUCE_MINGW | |||
#define _set_abort_behavior(...) | |||
#endif | |||
#define Point CarbonDummyPointName // The VST headers include some system headers that need | |||
// to match the name our hacky Carbon workaround used. | |||
#include <base/source/baseiids.cpp> | |||
#include <base/source/fatomic.cpp> | |||
#include <base/source/fbuffer.cpp> | |||
@@ -112,7 +107,6 @@ | |||
#include <public.sdk/source/vst/vstcomponentbase.cpp> | |||
#include <public.sdk/source/vst/vstparameters.cpp> | |||
#include <public.sdk/source/vst/hosting/hostclasses.cpp> | |||
#undef Point | |||
//============================================================================== | |||
namespace Steinberg | |||
@@ -177,7 +171,5 @@ namespace Steinberg | |||
#undef DEF_CLASS2 | |||
#undef DEF_CLASS_W | |||
#undef END_FACTORY | |||
#undef Point | |||
#undef Component | |||
#endif // JUCE_VST3HEADERS_H_INCLUDED |
@@ -1861,23 +1861,15 @@ public: | |||
bool producesMidi() const override { return getBusInfo (false, false).channelCount > 0; } | |||
//============================================================================== | |||
bool silenceInProducesSilenceOut() const override | |||
{ | |||
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) | |||
{ | |||
const double currentSampleRate = getSampleRate(); | |||
const double sampleRate = getSampleRate(); | |||
if (currentSampleRate > 0.0) | |||
return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / currentSampleRate; | |||
if (sampleRate > 0.0) | |||
return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate; | |||
} | |||
return 0.0; | |||
@@ -890,23 +890,18 @@ public: | |||
return uid; | |||
} | |||
bool silenceInProducesSilenceOut() const override | |||
{ | |||
return effect == nullptr || (effect->flags & effFlagsNoSoundInStop) != 0; | |||
} | |||
double getTailLengthSeconds() const override | |||
{ | |||
if (effect == nullptr) | |||
return 0.0; | |||
const double currentSampleRate = getSampleRate(); | |||
const double sampleRate = getSampleRate(); | |||
if (currentSampleRate <= 0) | |||
if (sampleRate <= 0) | |||
return 0.0; | |||
VstIntPtr samples = dispatch (effGetTailSize, 0, 0, 0, 0); | |||
return samples / currentSampleRate; | |||
return samples / sampleRate; | |||
} | |||
bool acceptsMidi() const override { return wantsMidiMessages; } | |||
@@ -2915,11 +2910,11 @@ FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() | |||
const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); | |||
FileSearchPath paths; | |||
paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath", | |||
paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath", | |||
programFiles + "\\Steinberg\\VstPlugins")); | |||
paths.removeNonExistentPaths(); | |||
paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath", | |||
paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath", | |||
programFiles + "\\VstPlugins")); | |||
return paths; | |||
#endif | |||
@@ -31,7 +31,8 @@ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#include "../juce_core/native/juce_BasicNativeHeaders.h" | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
#include "juce_audio_processors.h" | |||
#include "../juce_gui_extra/juce_gui_extra.h" | |||
@@ -40,11 +41,7 @@ | |||
#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> | |||
#undef Point | |||
#undef Component | |||
#endif | |||
#endif | |||
@@ -29,7 +29,7 @@ | |||
#include "../juce_audio_basics/juce_audio_basics.h" | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_PLUGINHOST_VST | |||
Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be | |||
installed on your machine. | |||
@@ -67,8 +67,8 @@ | |||
#define JUCE_SUPPORT_CARBON 1 | |||
#endif | |||
//============================================================================= | |||
//============================================================================= | |||
//============================================================================== | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -28,8 +28,11 @@ bool AudioChannelSet::operator== (const AudioChannelSet& other) const noexcept | |||
bool AudioChannelSet::operator!= (const AudioChannelSet& other) const noexcept { return channels != other.channels; } | |||
bool AudioChannelSet::operator< (const AudioChannelSet& other) const noexcept { return channels < other.channels; } | |||
const char* AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type) noexcept | |||
String AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type) | |||
{ | |||
if (type >= discreteChannel0) | |||
return String ("Discrete ") + String (type - discreteChannel0 + 1); | |||
switch (type) | |||
{ | |||
case left: return NEEDS_TRANS("Left"); | |||
@@ -63,8 +66,11 @@ const char* AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType ty | |||
return "Unknown"; | |||
} | |||
const char* AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelType type) noexcept | |||
String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelType type) | |||
{ | |||
if (type >= discreteChannel0) | |||
return String (type - discreteChannel0 + 1); | |||
switch (type) | |||
{ | |||
case left: return "L"; | |||
@@ -151,17 +157,17 @@ AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet ( | |||
AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); } | |||
AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); } | |||
AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight)); } | |||
AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight) | (1u << centre)); } | |||
AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight) | (1u << centre) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight) | (1u << centre) | (1u << surround) | (1u << wideLeft) | (1u << wideRight)); } | |||
AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight)); } | |||
AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centre)); } | |||
AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centre) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centre) | (1u << surround) | (1u << wideLeft) | (1u << wideRight)); } | |||
AudioChannelSet AudioChannelSet::ambisonic() { return AudioChannelSet ((1u << ambisonicW) | (1u << ambisonicX) | (1u << ambisonicY) | (1u << ambisonicZ)); } | |||
AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight)); } | |||
AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight)); } | |||
AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << topRearLeft) | (1u << topRearRight)); } | |||
AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << topRearLeft) | (1u << topRearRight)); } | |||
AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << sideLeft) | (1u << sideRight)); } | |||
AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << sideLeft) | (1u << sideRight)); } | |||
AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << sideLeft) | (1u << sideRight) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << sideLeft) | (1u << sideRight) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << sideLeft) | (1u << sideRight) | (1u << surroundLeft) | (1u << surroundRight)); } | |||
AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << sideLeft) | (1u << sideRight) | (1u << surroundLeft) | (1u << surroundRight)); } | |||
AudioChannelSet AudioChannelSet::createFront7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centreLeft) | (1u << centreRight)); } | |||
AudioChannelSet AudioChannelSet::createFront7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centreLeft) | (1u << centreRight)); } | |||
@@ -145,10 +145,10 @@ public: | |||
}; | |||
/** Returns the name of a given channel type. For example, this method may return "Surround Left". */ | |||
static const char* getChannelTypeName (ChannelType) noexcept; | |||
static String getChannelTypeName (ChannelType); | |||
/** Returns the abbreviated name of a channel type. For example, this method may return "Ls". */ | |||
static const char* getAbbreviatedChannelTypeName (ChannelType) noexcept; | |||
static String getAbbreviatedChannelTypeName (ChannelType); | |||
//============================================================================== | |||
/** Adds a channel to the set. */ | |||
@@ -32,7 +32,7 @@ void JUCE_CALLTYPE AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::Wrapp | |||
AudioProcessor::AudioProcessor() | |||
: wrapperType (wrapperTypeBeingCreated.get()), | |||
playHead (nullptr), | |||
sampleRate (0), | |||
currentSampleRate (0), | |||
blockSize (0), | |||
latencySamples (0), | |||
#if JUCE_DEBUG | |||
@@ -42,23 +42,25 @@ AudioProcessor::AudioProcessor() | |||
nonRealtime (false), | |||
processingPrecision (singlePrecision) | |||
{ | |||
#if ! JucePlugin_IsMidiEffect | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; | |||
#else | |||
const short channelConfigs[][2] = { {2, 2} }; | |||
#endif | |||
int numChannelConfigs = sizeof (channelConfigs) / sizeof (*channelConfigs); | |||
if (numChannelConfigs > 0) | |||
{ | |||
#if ! JucePlugin_IsSynth | |||
busArrangement.inputBuses.add (AudioProcessorBus ("Input", AudioChannelSet::canonicalChannelSet (channelConfigs[0][0]))); | |||
#endif | |||
busArrangement.outputBuses.add (AudioProcessorBus ("Output", AudioChannelSet::canonicalChannelSet (channelConfigs[0][1]))); | |||
} | |||
#endif | |||
#if ! JucePlugin_IsMidiEffect | |||
#if ! JucePlugin_IsSynth | |||
busArrangement.inputBuses.add (AudioProcessorBus ("Input", AudioChannelSet::canonicalChannelSet (channelConfigs[0][0]))); | |||
#endif | |||
busArrangement.outputBuses.add (AudioProcessorBus ("Output", AudioChannelSet::canonicalChannelSet (channelConfigs[0][1]))); | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
#if ! JucePlugin_IsSynth | |||
AudioProcessor::setPreferredBusArrangement (true, 0, AudioChannelSet::stereo()); | |||
#endif | |||
AudioProcessor::setPreferredBusArrangement (false, 0, AudioChannelSet::stereo()); | |||
#endif | |||
#endif | |||
updateSpeakerFormatStrings(); | |||
} | |||
@@ -123,7 +125,7 @@ void AudioProcessor::setPlayConfigDetails (const int newNumIns, | |||
void AudioProcessor::setRateAndBufferSizeDetails (double newSampleRate, int newBlockSize) noexcept | |||
{ | |||
sampleRate = newSampleRate; | |||
currentSampleRate = newSampleRate; | |||
blockSize = newBlockSize; | |||
} | |||
@@ -317,6 +317,14 @@ public: | |||
changing the channel layout of other buses, for example, if your plug-in requires the same | |||
number of input and output channels. | |||
For most basic plug-ins, which do not require side-chains, aux buses or detailed audio | |||
channel layout information, it is easier to specify the acceptable channel configurations | |||
via the "PlugIn Channel Configurations" field in the Introjucer. In this case, you should | |||
not override this method. | |||
If, on the other hand, you decide to override this method then you need to make sure that | |||
"PlugIn Channel Configurations" field in the Introjucer is empty. | |||
Note, that you must not do any heavy allocations or calculations in this callback as it may | |||
be called several hundred times during initialization. If you require any layout specific | |||
allocations then defer these to prepareToPlay callback. | |||
@@ -423,7 +431,7 @@ public: | |||
This can be called from your processBlock() method - it's not guaranteed | |||
to be valid at any other time, and may return 0 if it's unknown. | |||
*/ | |||
double getSampleRate() const noexcept { return sampleRate; } | |||
double getSampleRate() const noexcept { return currentSampleRate; } | |||
/** Returns the current typical block size that is being used. | |||
@@ -453,9 +461,6 @@ public: | |||
*/ | |||
void setLatencySamples (int newLatency); | |||
/** Returns true if a silent input always produces a silent output. */ | |||
virtual bool silenceInProducesSilenceOut() const = 0; | |||
/** Returns the length of the filter's tail, in seconds. */ | |||
virtual double getTailLengthSeconds() const = 0; | |||
@@ -883,7 +888,7 @@ public: | |||
//============================================================================== | |||
/** This is called by the processor to specify its details before being played. Use this | |||
version of the function if you are not interested in any sidechain or aux buses | |||
version of the function if you are not interested in any sidechain and/or aux buses | |||
and do not care about the layout of channels. Otherwise use setRateAndBufferSizeDetails.*/ | |||
void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize); | |||
@@ -919,7 +924,7 @@ public: | |||
WrapperType wrapperType; | |||
//============================================================================== | |||
#ifndef DOXYGEN | |||
#ifndef DOXYGEN | |||
/** Deprecated: use getTotalNumInputChannels instead. */ | |||
JUCE_DEPRECATED_WITH_BODY (int getNumInputChannels() const noexcept, { return getTotalNumInputChannels(); }) | |||
JUCE_DEPRECATED_WITH_BODY (int getNumOutputChannels() const noexcept, { return getTotalNumOutputChannels(); }) | |||
@@ -946,7 +951,7 @@ public: | |||
the constructor. */ | |||
JUCE_DEPRECATED (virtual bool isInputChannelStereoPair (int index) const); | |||
JUCE_DEPRECATED (virtual bool isOutputChannelStereoPair (int index) const); | |||
#endif | |||
#endif | |||
//============================================================================== | |||
/** Helper function that just converts an xml element into a binary blob. | |||
@@ -982,7 +987,7 @@ private: | |||
#if ! JUCE_AUDIO_PROCESSOR_NO_GUI | |||
Component::SafePointer<AudioProcessorEditor> activeEditor; | |||
#endif | |||
double sampleRate; | |||
double currentSampleRate; | |||
int blockSize, latencySamples; | |||
#if JUCE_DEBUG | |||
bool textRecursionCheck; | |||
@@ -1005,6 +1010,9 @@ private: | |||
void disableNonMainBuses (bool isInput); | |||
void updateSpeakerFormatStrings(); | |||
// This method is no longer used - you can delete it from your AudioProcessor classes. | |||
JUCE_DEPRECATED_WITH_BODY (virtual bool silenceInProducesSilenceOut() const, { return false; }); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessor) | |||
}; | |||
@@ -1460,7 +1460,6 @@ void AudioProcessorGraph::processAudio (AudioBuffer<FloatType>& buffer, MidiBuff | |||
midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0); | |||
} | |||
bool AudioProcessorGraph::silenceInProducesSilenceOut() const { return false; } | |||
double AudioProcessorGraph::getTailLengthSeconds() const { return 0; } | |||
bool AudioProcessorGraph::acceptsMidi() const { return true; } | |||
bool AudioProcessorGraph::producesMidi() const { return true; } | |||
@@ -1601,11 +1600,6 @@ void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<doubl | |||
processAudio (buffer, midiMessages); | |||
} | |||
bool AudioProcessorGraph::AudioGraphIOProcessor::silenceInProducesSilenceOut() const | |||
{ | |||
return isOutput(); | |||
} | |||
double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const | |||
{ | |||
return 0; | |||
@@ -311,7 +311,6 @@ public: | |||
void processBlock (AudioBuffer<double>&, MidiBuffer&) override; | |||
bool supportsDoublePrecisionProcessing() const override; | |||
bool silenceInProducesSilenceOut() const override; | |||
double getTailLengthSeconds() const override; | |||
bool acceptsMidi() const override; | |||
bool producesMidi() const override; | |||
@@ -356,7 +355,6 @@ public: | |||
void setNonRealtime (bool) noexcept override; | |||
void setPlayHead (AudioPlayHead*) override; | |||
bool silenceInProducesSilenceOut() const override; | |||
double getTailLengthSeconds() const override; | |||
bool acceptsMidi() const override; | |||
bool producesMidi() const override; | |||
@@ -50,14 +50,17 @@ void AudioParameterFloat::setValue (float newValue) { value | |||
float AudioParameterFloat::getDefaultValue() const { return range.convertTo0to1 (defaultValue); } | |||
int AudioParameterFloat::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); } | |||
float AudioParameterFloat::getValueForText (const String& text) const { return range.convertTo0to1 (text.getFloatValue()); } | |||
String AudioParameterFloat::getText (float v, int length) const { return String (range.convertFrom0to1 (v), 2).substring (0, length); } | |||
AudioParameterFloat& AudioParameterFloat::operator= (float newValue) | |||
String AudioParameterFloat::getText (float v, int length) const | |||
{ | |||
const float normalisedValue = range.convertTo0to1 (newValue); | |||
String asText (range.convertFrom0to1 (v), 2); | |||
return length > 0 ? asText.substring (0, length) : asText; | |||
} | |||
if (value != normalisedValue) | |||
setValueNotifyingHost (normalisedValue); | |||
AudioParameterFloat& AudioParameterFloat::operator= (float newValue) | |||
{ | |||
if (value != newValue) | |||
setValueNotifyingHost (range.convertTo0to1 (newValue)); | |||
return *this; | |||
} | |||
@@ -87,10 +90,8 @@ String AudioParameterInt::getText (float v, int /*length*/) const { retur | |||
AudioParameterInt& AudioParameterInt::operator= (int newValue) | |||
{ | |||
const float normalisedValue = convertTo0to1 (newValue); | |||
if (value != normalisedValue) | |||
setValueNotifyingHost (normalisedValue); | |||
if (get() != newValue) | |||
setValueNotifyingHost (convertTo0to1 (newValue)); | |||
return *this; | |||
} | |||
@@ -115,10 +116,8 @@ String AudioParameterBool::getText (float v, int /*length*/) const { retur | |||
AudioParameterBool& AudioParameterBool::operator= (bool newValue) | |||
{ | |||
const float normalisedValue = newValue ? 1.0f : 0.0f; | |||
if (value != normalisedValue) | |||
setValueNotifyingHost (normalisedValue); | |||
if (get() != newValue) | |||
setValueNotifyingHost (newValue ? 1.0f : 0.0f); | |||
return *this; | |||
} | |||
@@ -148,10 +147,8 @@ String AudioParameterChoice::getText (float v, int /*length*/) const { retur | |||
AudioParameterChoice& AudioParameterChoice::operator= (int newValue) | |||
{ | |||
const float normalisedValue = convertTo0to1 (newValue); | |||
if (value != normalisedValue) | |||
setValueNotifyingHost (normalisedValue); | |||
if (getIndex() != newValue) | |||
setValueNotifyingHost (convertTo0to1 (newValue)); | |||
return *this; | |||
} |
@@ -258,21 +258,21 @@ void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees() | |||
} | |||
} | |||
void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree&, const Identifier& property) | |||
void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree& tree, const Identifier& property) | |||
{ | |||
if (property == idPropertyID) | |||
if (property == idPropertyID && tree.hasType (valueType) && tree.getParent() == state) | |||
updateParameterConnectionsToChildTrees(); | |||
} | |||
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree&) | |||
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree) | |||
{ | |||
if (parent == state) | |||
if (parent == state && tree.hasType (valueType)) | |||
updateParameterConnectionsToChildTrees(); | |||
} | |||
void AudioProcessorValueTreeState::valueTreeChildRemoved (ValueTree& parent, ValueTree&, int) | |||
void AudioProcessorValueTreeState::valueTreeChildRemoved (ValueTree& parent, ValueTree& tree, int) | |||
{ | |||
if (parent == state) | |||
if (parent == state && tree.hasType (valueType)) | |||
updateParameterConnectionsToChildTrees(); | |||
} | |||
@@ -112,7 +112,7 @@ public: | |||
*/ | |||
void getChannelAsPath (Path& result, const Range<float>* levels, int numLevels, int nextSample); | |||
//========================================================================== | |||
//============================================================================== | |||
/** @internal */ | |||
void paint (Graphics&) override; | |||
@@ -270,6 +270,13 @@ int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const | |||
return x; | |||
} | |||
int MidiKeyboardComponent::getTotalKeyboardWidth() const noexcept | |||
{ | |||
int x, w; | |||
getKeyPos (rangeEnd, x, w); | |||
return x + w; | |||
} | |||
int MidiKeyboardComponent::getNoteAtPosition (Point<int> p) | |||
{ | |||
float v; | |||
@@ -209,6 +209,9 @@ public: | |||
*/ | |||
int getKeyStartPosition (int midiNoteNumber) const; | |||
/** Returns the total width needed to fit all the keys in the available range. */ | |||
int getTotalKeyboardWidth() const noexcept; | |||
/** Returns the key at a given coordinate. */ | |||
int getNoteAtPosition (Point<int> position); | |||
@@ -31,7 +31,9 @@ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#include "../juce_core/native/juce_BasicNativeHeaders.h" | |||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
#include "juce_audio_utils.h" | |||
namespace juce | |||
@@ -54,7 +56,6 @@ namespace juce | |||
#elif JUCE_IOS | |||
#include "native/juce_ios_BluetoothMidiDevicePairingDialogue.mm" | |||
#elif JUCE_ANDROID | |||
#include "../juce_core/native/juce_android_JNIHelpers.h" | |||
#include "native/juce_android_BluetoothMidiDevicePairingDialogue.cpp" | |||
#elif JUCE_LINUX | |||
#include "native/juce_linux_BluetoothMidiDevicePairingDialogue.cpp" | |||
@@ -30,7 +30,7 @@ | |||
#include "../juce_audio_formats/juce_audio_formats.h" | |||
#include "../juce_audio_processors/juce_audio_processors.h" | |||
//============================================================================= | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -64,7 +64,7 @@ struct AndroidBluetoothMidiInterface | |||
return retval; | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
static bool pairBluetoothMidiDevice (const String& bluetoothAddress) | |||
{ | |||
JNIEnv* env = getEnv(); | |||
@@ -90,7 +90,7 @@ struct AndroidBluetoothMidiInterface | |||
javaString (bluetoothAddress).get()); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
static String getHumanReadableStringForBluetoothAddress (const String& address) | |||
{ | |||
JNIEnv* env = getEnv(); | |||
@@ -111,7 +111,7 @@ struct AndroidBluetoothMidiInterface | |||
return juceString (string); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
static bool isBluetoothDevicePaired (const String& address) | |||
{ | |||
JNIEnv* env = getEnv(); | |||
@@ -166,7 +166,7 @@ class AndroidBluetoothMidiDevicesListBox : public ListBox, | |||
private Timer | |||
{ | |||
public: | |||
//========================================================================== | |||
//============================================================================== | |||
AndroidBluetoothMidiDevicesListBox() | |||
: timerPeriodInMs (1000) | |||
{ | |||
@@ -184,7 +184,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
typedef AndroidBluetoothMidiDevice::ConnectionStatus DeviceStatus; | |||
int getNumRows() override | |||
@@ -226,7 +226,7 @@ private: | |||
} | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
static Colour getDeviceNameFontColour (DeviceStatus deviceStatus) noexcept | |||
{ | |||
if (deviceStatus == AndroidBluetoothMidiDevice::offline) | |||
@@ -261,7 +261,7 @@ private: | |||
return "Status unknown"; | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void listBoxItemClicked (int row, const MouseEvent&) override | |||
{ | |||
const AndroidBluetoothMidiDevice& device = devices.getReference (row); | |||
@@ -278,7 +278,7 @@ private: | |||
updateDeviceList(); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
struct PairDeviceThread : public Thread, | |||
private AsyncUpdater | |||
{ | |||
@@ -310,7 +310,7 @@ private: | |||
Component::SafePointer<AndroidBluetoothMidiDevicesListBox> owner; | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
void disconnectedDeviceClicked (int row) | |||
{ | |||
stopTimer(); | |||
@@ -332,7 +332,7 @@ private: | |||
AndroidBluetoothMidiInterface::unpairBluetoothMidiDevice (device.bluetoothAddress); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void updateDeviceList() | |||
{ | |||
StringArray bluetoothAddresses = AndroidBluetoothMidiInterface::getBluetoothMidiDevicesNearby(); | |||
@@ -427,6 +427,15 @@ private: | |||
//============================================================================== | |||
bool BluetoothMidiDevicePairingDialogue::open() | |||
{ | |||
if (! RuntimePermissions::isGranted (RuntimePermissions::bluetoothMidi)) | |||
{ | |||
// If you hit this assert, you probably forgot to get RuntimePermissions::bluetoothMidi. | |||
// This is not going to work, boo! The pairing dialogue won't be able to scan for or | |||
// find any devices, it will just display an empty list, so don't bother opening it. | |||
jassertfalse; | |||
return false; | |||
} | |||
BluetoothMidiSelectorOverlay* overlay = new BluetoothMidiSelectorOverlay; | |||
return true; | |||
} | |||
@@ -424,6 +424,13 @@ public: | |||
return entry != nullptr ? entry->value : ValueType(); | |||
} | |||
/** Resets the iterator to its starting position. */ | |||
void reset() noexcept | |||
{ | |||
entry = nullptr; | |||
index = 0; | |||
} | |||
private: | |||
//============================================================================== | |||
const HashMap& hashMap; | |||
@@ -70,8 +70,24 @@ File& File::operator= (File&& other) noexcept | |||
const File File::nonexistent; | |||
//============================================================================== | |||
static String removeEllipsis (const String& path) | |||
{ | |||
StringArray toks; | |||
toks.addTokens (path, File::separatorString, StringRef()); | |||
for (int i = 1; i < toks.size(); ++i) | |||
{ | |||
if (toks[i] == ".." && toks[i - 1] != "..") | |||
{ | |||
toks.removeRange (i - 1, 2); | |||
i = jmax (0, i - 2); | |||
} | |||
} | |||
return toks.joinIntoString (File::separatorString); | |||
} | |||
String File::parseAbsolutePath (const String& p) | |||
{ | |||
if (p.isEmpty()) | |||
@@ -81,6 +97,9 @@ String File::parseAbsolutePath (const String& p) | |||
// Windows.. | |||
String path (p.replaceCharacter ('/', '\\')); | |||
if (path.contains ("\\..\\")) | |||
path = removeEllipsis (path); | |||
if (path.startsWithChar (separator)) | |||
{ | |||
if (path[1] != separator) | |||
@@ -120,6 +139,9 @@ String File::parseAbsolutePath (const String& p) | |||
String path (p); | |||
if (path.contains ("/../")) | |||
path = removeEllipsis (path); | |||
if (path.startsWithChar ('~')) | |||
{ | |||
if (path[1] == separator || path[1] == 0) | |||
@@ -347,62 +369,69 @@ int64 File::hashCode64() const { return fullPath.hashCode64(); } | |||
//============================================================================== | |||
bool File::isAbsolutePath (StringRef path) | |||
{ | |||
return path.text[0] == separator | |||
const juce_wchar firstChar = *(path.text); | |||
return firstChar == separator | |||
#if JUCE_WINDOWS | |||
|| (path.isNotEmpty() && path.text[1] == ':'); | |||
|| (firstChar != 0 && path.text[1] == ':'); | |||
#else | |||
|| path.text[0] == '~'; | |||
|| firstChar == '~'; | |||
#endif | |||
} | |||
File File::getChildFile (StringRef relativePath) const | |||
{ | |||
if (isAbsolutePath (relativePath)) | |||
return File (String (relativePath.text)); | |||
String::CharPointerType r = relativePath.text; | |||
if (relativePath[0] != '.') | |||
return File (addTrailingSeparator (fullPath) + relativePath); | |||
if (isAbsolutePath (r)) | |||
return File (String (r)); | |||
String path (fullPath); | |||
// It's relative, so remove any ../ or ./ bits at the start.. | |||
#if JUCE_WINDOWS | |||
if (relativePath.text.indexOf ((juce_wchar) '/') >= 0) | |||
return getChildFile (String (relativePath.text).replaceCharacter ('/', '\\')); | |||
if (r.indexOf ((juce_wchar) '/') >= 0) | |||
return getChildFile (String (r).replaceCharacter ('/', '\\')); | |||
#endif | |||
while (relativePath[0] == '.') | |||
String path (fullPath); | |||
while (*r == '.') | |||
{ | |||
const juce_wchar secondChar = relativePath[1]; | |||
String::CharPointerType lastPos = r; | |||
const juce_wchar secondChar = *++r; | |||
if (secondChar == '.') | |||
if (secondChar == '.') // remove "../" | |||
{ | |||
const juce_wchar thirdChar = relativePath[2]; | |||
const juce_wchar thirdChar = *++r; | |||
if (thirdChar == 0 || thirdChar == separator) | |||
if (thirdChar == separator || thirdChar == 0) | |||
{ | |||
const int lastSlash = path.lastIndexOfChar (separator); | |||
if (lastSlash >= 0) | |||
path = path.substring (0, lastSlash); | |||
relativePath = relativePath.text + (thirdChar == 0 ? 2 : 3); | |||
while (*r == separator) // ignore duplicate slashes | |||
++r; | |||
} | |||
else | |||
{ | |||
r = lastPos; | |||
break; | |||
} | |||
} | |||
else if (secondChar == separator) | |||
else if (secondChar == separator || secondChar == 0) // remove "./" | |||
{ | |||
relativePath = relativePath.text + 2; | |||
while (*r == separator) // ignore duplicate slashes | |||
++r; | |||
} | |||
else | |||
{ | |||
r = lastPos; | |||
break; | |||
} | |||
} | |||
return File (addTrailingSeparator (path) + relativePath); | |||
path = addTrailingSeparator (path); | |||
path.appendCharPointer (r); | |||
return File (path); | |||
} | |||
File File::getSiblingFile (StringRef fileName) const | |||
@@ -1020,6 +1049,17 @@ public: | |||
expect (tempFile.getSiblingFile ("foo").isAChildOf (temp)); | |||
expect (tempFile.hasWriteAccess()); | |||
expect (home.getChildFile (".") == home); | |||
expect (home.getChildFile ("..") == home.getParentDirectory()); | |||
expect (home.getChildFile (".xyz").getFileName() == ".xyz"); | |||
expect (home.getChildFile ("..xyz").getFileName() == "..xyz"); | |||
expect (home.getChildFile ("...xyz").getFileName() == "...xyz"); | |||
expect (home.getChildFile ("./xyz") == home.getChildFile ("xyz")); | |||
expect (home.getChildFile ("././xyz") == home.getChildFile ("xyz")); | |||
expect (home.getChildFile ("../xyz") == home.getParentDirectory().getChildFile ("xyz")); | |||
expect (home.getChildFile (".././xyz") == home.getParentDirectory().getChildFile ("xyz")); | |||
expect (home.getChildFile ("./../xyz") == home.getParentDirectory().getChildFile ("xyz")); | |||
{ | |||
FileOutputStream fo (tempFile); | |||
fo.write ("0123456789", 10); | |||
@@ -48,7 +48,7 @@ public: | |||
//============================================================================== | |||
/** Creates an (invalid) file object. | |||
The file is initially set to an empty path, so getFullPath() will return | |||
The file is initially set to an empty path, so getFullPathName() will return | |||
an empty string, and comparing the file to File::nonexistent will return | |||
true. | |||
@@ -66,10 +66,10 @@ public: | |||
//============================================================================== | |||
/** Returns true if the filename matches one of the patterns specified. */ | |||
bool isFileSuitable (const File& file) const; | |||
bool isFileSuitable (const File& file) const override; | |||
/** This always returns true. */ | |||
bool isDirectorySuitable (const File& file) const; | |||
bool isDirectorySuitable (const File& file) const override; | |||
private: | |||
//============================================================================== | |||
@@ -44,7 +44,8 @@ | |||
#define JUCE_JS_KEYWORDS(X) \ | |||
X(var, "var") X(if_, "if") X(else_, "else") X(do_, "do") X(null_, "null") \ | |||
X(while_, "while") X(for_, "for") X(break_, "break") X(continue_, "continue") X(undefined, "undefined") \ | |||
X(function, "function") X(return_, "return") X(true_, "true") X(false_, "false") X(new_, "new") | |||
X(function, "function") X(return_, "return") X(true_, "true") X(false_, "false") X(new_, "new") \ | |||
X(typeof_, "typeof") | |||
namespace TokenTypes | |||
{ | |||
@@ -71,6 +72,7 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
setMethod ("trace", trace); | |||
setMethod ("charToInt", charToInt); | |||
setMethod ("parseInt", IntegerClass::parseInt); | |||
setMethod ("typeof", typeof_internal); | |||
} | |||
Time timeout; | |||
@@ -97,12 +99,13 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
&& (((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid())) || a == b); | |||
} | |||
static String getTokenName (TokenType t) { return t[0] == '$' ? String (t + 1) : ("'" + String (t) + "'"); } | |||
static bool isFunction (const var& v) { return dynamic_cast<FunctionObject*> (v.getObject()) != nullptr; } | |||
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, const Identifier& i) { return o->getProperties().getVarPointer (i); } | |||
static String getTokenName (TokenType t) { return t[0] == '$' ? String (t + 1) : ("'" + String (t) + "'"); } | |||
static bool isFunction (const var& v) noexcept { return dynamic_cast<FunctionObject*> (v.getObject()) != nullptr; } | |||
static bool isNumeric (const var& v) noexcept { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool(); } | |||
static bool isNumericOrUndefined (const var& v) noexcept { return isNumeric (v) || 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, const Identifier& i) noexcept { return o->getProperties().getVarPointer (i); } | |||
//============================================================================== | |||
struct CodeLocation | |||
@@ -1079,7 +1082,7 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
if (matchIf (TokenTypes::while_)) return parseDoOrWhileLoop (false); | |||
if (matchIf (TokenTypes::do_)) return parseDoOrWhileLoop (true); | |||
if (matchIf (TokenTypes::for_)) return parseForLoop(); | |||
if (matchIf (TokenTypes::return_)) return new ReturnStatement (location, matchIf (TokenTypes::semicolon) ? new Expression (location) : parseExpression()); | |||
if (matchIf (TokenTypes::return_)) return parseReturn(); | |||
if (matchIf (TokenTypes::break_)) return new BreakStatement (location); | |||
if (matchIf (TokenTypes::continue_)) return new ContinueStatement (location); | |||
if (matchIf (TokenTypes::function)) return parseFunction(); | |||
@@ -1111,6 +1114,16 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
return s.release(); | |||
} | |||
Statement* parseReturn() | |||
{ | |||
if (matchIf (TokenTypes::semicolon)) | |||
return new ReturnStatement (location, new Expression (location)); | |||
ReturnStatement* r = new ReturnStatement (location, parseExpression()); | |||
matchIf (TokenTypes::semicolon); | |||
return r; | |||
} | |||
Statement* parseVar() | |||
{ | |||
ScopedPointer<VarStatement> s (new VarStatement (location)); | |||
@@ -1345,12 +1358,21 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
return new PostAssignment (location, e, new OpType (location, lhs2, one)); | |||
} | |||
Expression* parseTypeof() | |||
{ | |||
ScopedPointer<FunctionCall> f (new FunctionCall (location)); | |||
f->object = new UnqualifiedName (location, "typeof"); | |||
f->arguments.add (parseExpression()); | |||
return f.release(); | |||
} | |||
Expression* parseUnary() | |||
{ | |||
if (matchIf (TokenTypes::minus)) { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new SubtractionOp (location, a, b); } | |||
if (matchIf (TokenTypes::logicalNot)) { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new EqualsOp (location, a, b); } | |||
if (matchIf (TokenTypes::plusplus)) return parsePreIncDec<AdditionOp>(); | |||
if (matchIf (TokenTypes::minusminus)) return parsePreIncDec<SubtractionOp>(); | |||
if (matchIf (TokenTypes::typeof_)) return parseTypeof(); | |||
return parseFactor(); | |||
} | |||
@@ -1478,6 +1500,7 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
setMethod ("contains", contains); | |||
setMethod ("remove", remove); | |||
setMethod ("join", join); | |||
setMethod ("push", push); | |||
} | |||
static Identifier getClassName() { static const Identifier i ("Array"); return i; } | |||
@@ -1508,6 +1531,19 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
return strings.joinIntoString (getString (a, 0)); | |||
} | |||
static var push (Args a) | |||
{ | |||
if (Array<var>* array = a.thisObject.getArray()) | |||
{ | |||
for (int i = 0; i < a.numArguments; ++i) | |||
array->add (a.arguments[i]); | |||
return array->size(); | |||
} | |||
return var::undefined(); | |||
} | |||
}; | |||
//============================================================================== | |||
@@ -1638,6 +1674,19 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
static var trace (Args a) { Logger::outputDebugString (JSON::toString (a.thisObject)); return var::undefined(); } | |||
static var charToInt (Args a) { return (int) (getString (a, 0)[0]); } | |||
static var typeof_internal (Args a) | |||
{ | |||
var v (get (a, 0)); | |||
if (v.isVoid()) return "void"; | |||
if (v.isString()) return "string"; | |||
if (isNumeric (v)) return "number"; | |||
if (isFunction (v) || v.isMethod()) return "function"; | |||
if (v.isObject()) return "object"; | |||
return "undefined"; | |||
} | |||
static var exec (Args a) | |||
{ | |||
if (RootObject* root = dynamic_cast<RootObject*> (a.thisObject.getObject())) | |||
@@ -35,7 +35,10 @@ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#include "native/juce_BasicNativeHeaders.h" | |||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1 | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
#include "juce_core.h" | |||
#include <locale> | |||
@@ -80,6 +83,7 @@ | |||
#if JUCE_LINUX | |||
#include <langinfo.h> | |||
#include <ifaddrs.h> | |||
#include <sys/resource.h> | |||
#if JUCE_USE_CURL | |||
#include <curl/curl.h> | |||
@@ -140,6 +144,7 @@ namespace juce | |||
#include "maths/juce_Expression.cpp" | |||
#include "maths/juce_Random.cpp" | |||
#include "memory/juce_MemoryBlock.cpp" | |||
#include "misc/juce_RuntimePermissions.cpp" | |||
#include "misc/juce_Result.cpp" | |||
#include "misc/juce_Uuid.cpp" | |||
#include "network/juce_MACAddress.cpp" | |||
@@ -180,10 +185,6 @@ namespace juce | |||
#include "files/juce_WildcardFileFilter.cpp" | |||
//============================================================================== | |||
#if JUCE_MAC || JUCE_IOS | |||
#include "native/juce_osx_ObjCHelpers.h" | |||
#endif | |||
#if JUCE_ANDROID | |||
#include "native/juce_android_JNIHelpers.h" | |||
#endif | |||
@@ -203,7 +204,6 @@ namespace juce | |||
//============================================================================== | |||
#elif JUCE_WINDOWS | |||
#include "native/juce_win32_ComSmartPtr.h" | |||
#include "native/juce_win32_Files.cpp" | |||
#include "native/juce_win32_Network.cpp" | |||
#include "native/juce_win32_Registry.cpp" | |||
@@ -229,6 +229,7 @@ namespace juce | |||
#include "native/juce_android_Network.cpp" | |||
#include "native/juce_android_SystemStats.cpp" | |||
#include "native/juce_android_Threads.cpp" | |||
#include "native/juce_android_RuntimePermissions.cpp" | |||
#endif | |||
@@ -41,7 +41,7 @@ | |||
#include "system/juce_TargetPlatform.h" | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_FORCE_DEBUG | |||
Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, | |||
@@ -51,7 +51,7 @@ | |||
//#define JUCE_FORCE_DEBUG 0 | |||
#endif | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_LOG_ASSERTIONS | |||
If this flag is enabled, the jassert and jassertfalse macros will always use Logger::writeToLog() | |||
@@ -71,7 +71,7 @@ | |||
#endif | |||
#endif | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_CHECK_MEMORY_LEAKS | |||
Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector | |||
@@ -81,7 +81,7 @@ | |||
#define JUCE_CHECK_MEMORY_LEAKS 1 | |||
#endif | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
In a Visual C++ build, this can be used to stop the required system libs being | |||
@@ -130,8 +130,12 @@ | |||
#define JUCE_STRING_UTF_TYPE 8 | |||
#endif | |||
//============================================================================= | |||
//============================================================================= | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_CORE_INCLUDE_NATIVE_HEADERS | |||
#include "native/juce_BasicNativeHeaders.h" | |||
#endif | |||
#include "system/juce_StandardHeader.h" | |||
@@ -188,6 +192,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe | |||
#include "threads/juce_CriticalSection.h" | |||
#include "maths/juce_Range.h" | |||
#include "maths/juce_NormalisableRange.h" | |||
#include "maths/juce_StatisticsAccumulator.h" | |||
#include "containers/juce_ElementComparator.h" | |||
#include "containers/juce_ArrayAllocationBase.h" | |||
#include "containers/juce_Array.h" | |||
@@ -237,6 +242,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe | |||
#include "maths/juce_BigInteger.h" | |||
#include "maths/juce_Expression.h" | |||
#include "maths/juce_Random.h" | |||
#include "misc/juce_RuntimePermissions.h" | |||
#include "misc/juce_Uuid.h" | |||
#include "misc/juce_WindowsRegistry.h" | |||
#include "system/juce_SystemStats.h" | |||
@@ -269,6 +275,19 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe | |||
#include "containers/juce_PropertySet.h" | |||
#include "memory/juce_SharedResourcePointer.h" | |||
#if JUCE_CORE_INCLUDE_OBJC_HELPERS && (JUCE_MAC || JUCE_IOS) | |||
#include "native/juce_osx_ObjCHelpers.h" | |||
#endif | |||
#if JUCE_CORE_INCLUDE_COM_SMART_PTR && JUCE_WINDOWS | |||
#include "native/juce_win32_ComSmartPtr.h" | |||
#endif | |||
#if JUCE_CORE_INCLUDE_JNI_HELPERS && JUCE_ANDROID | |||
#include "native/juce_android_JNIHelpers.h" | |||
#endif | |||
#ifndef DOXYGEN | |||
/* | |||
As the very long class names here try to explain, the purpose of this code is to cause | |||
@@ -315,7 +315,7 @@ inline static int highestBitInInt (uint32 n) noexcept | |||
{ | |||
jassert (n != 0); // (the built-in functions may not work for n = 0) | |||
#if JUCE_GCC | |||
#if JUCE_GCC || JUCE_CLANG | |||
return 31 - __builtin_clz (n); | |||
#elif JUCE_USE_MSVC_INTRINSICS | |||
unsigned long highest; | |||
@@ -324,9 +324,9 @@ template <> | |||
inline float juce_hypot (float a, float b) noexcept | |||
{ | |||
#if JUCE_MSVC | |||
return (_hypotf (a, b)); | |||
return _hypotf (a, b); | |||
#else | |||
return (hypotf (a, b)); | |||
return hypotf (a, b); | |||
#endif | |||
} | |||
#endif | |||
@@ -356,12 +356,16 @@ const float float_Pi = 3.14159265358979323846f; | |||
/** Converts an angle in degrees to radians. */ | |||
template <typename FloatType> | |||
FloatType degreesToRadians (FloatType degrees) noexcept { return degrees * static_cast<FloatType> (double_Pi / 180.0); } | |||
inline float degreesToRadians (float degrees) noexcept { return degrees * (float_Pi / 180.0f); } | |||
/** Converts an angle in degrees to radians. */ | |||
inline double degreesToRadians (double degrees) noexcept { return degrees * (double_Pi / 180.0); } | |||
/** Converts an angle in radians to degrees. */ | |||
template <typename FloatType> | |||
FloatType radiansToDegrees (FloatType radians) noexcept { return radians * static_cast<FloatType> (180.0 / double_Pi); } | |||
inline float radiansToDegrees (float radians) noexcept { return radians * (180.0f / float_Pi); } | |||
/** Converts an angle in radians to degrees. */ | |||
inline double radiansToDegrees (double radians) noexcept { return radians * (180.0 / double_Pi); } | |||
//============================================================================== | |||
@@ -0,0 +1,145 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the juce_core module of the JUCE library. | |||
Copyright (c) 2016 - ROLI Ltd. | |||
Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
or without fee is hereby granted, provided that the above copyright notice and this | |||
permission notice appear in all copies. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
------------------------------------------------------------------------------ | |||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
using any other modules, be sure to check that you also comply with their license. | |||
For more details, visit www.juce.com | |||
============================================================================== | |||
*/ | |||
#ifndef JUCE_STATISTICSACCUMULATOR_H_INCLUDED | |||
#define JUCE_STATISTICSACCUMULATOR_H_INCLUDED | |||
//============================================================================== | |||
/** | |||
A class that measures various statistics about a series of floating point | |||
values that it is given. | |||
*/ | |||
template <typename FloatType> | |||
class StatisticsAccumulator | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Constructs a new StatisticsAccumulator. */ | |||
StatisticsAccumulator() noexcept | |||
: count (0), | |||
minimum ( std::numeric_limits<FloatType>::infinity()), | |||
maximum (-std::numeric_limits<FloatType>::infinity()) | |||
{} | |||
//============================================================================== | |||
/** Add a new value to the accumulator. | |||
This will update all running statistics accordingly. | |||
*/ | |||
void addValue (FloatType v) noexcept | |||
{ | |||
jassert (juce_isfinite (v)); | |||
sum += v; | |||
sumSquares += v * v; | |||
++count; | |||
if (v > maximum) maximum = v; | |||
if (v < minimum) minimum = v; | |||
} | |||
/** Reset the accumulator. | |||
This will reset all currently saved statistcs. | |||
*/ | |||
void reset() noexcept { *this = StatisticsAccumulator<FloatType>(); } | |||
//============================================================================== | |||
/** Returns the average (arithmetic mean) of all previously added values. | |||
If no values have been added yet, this will return zero. | |||
*/ | |||
FloatType getAverage() const noexcept | |||
{ | |||
return count > 0 ? sum / (FloatType) count | |||
: FloatType(); | |||
} | |||
/** Returns the variance of all previously added values. | |||
If no values have been added yet, this will return zero. | |||
*/ | |||
FloatType getVariance() const noexcept | |||
{ | |||
return count > 0 ? (sumSquares - sum * sum / (FloatType) count) / (FloatType) count | |||
: FloatType(); | |||
} | |||
/** Returns the standard deviation of all previously added values. | |||
If no values have been added yet, this will return zero. | |||
*/ | |||
FloatType getStandardDeviation() const noexcept | |||
{ | |||
return std::sqrt (getVariance()); | |||
} | |||
/** Returns the smallest of all previously added values. | |||
If no values have been added yet, this will return positive infinity. | |||
*/ | |||
FloatType getMinValue() const noexcept | |||
{ | |||
return minimum; | |||
} | |||
/** Returns the largest of all previously added values. | |||
If no values have been added yet, this will return negative infinity. | |||
*/ | |||
FloatType getMaxValue() const noexcept | |||
{ | |||
return maximum; | |||
} | |||
/** Returns how many values have been added to this accumulator. */ | |||
size_t getCount() const noexcept | |||
{ | |||
return count; | |||
} | |||
private: | |||
//============================================================================== | |||
struct KahanSum | |||
{ | |||
KahanSum() noexcept : sum(), error() {} | |||
operator FloatType() const noexcept { return sum; } | |||
void JUCE_NO_ASSOCIATIVE_MATH_OPTIMISATIONS operator+= (FloatType value) noexcept | |||
{ | |||
FloatType correctedValue = value - error; | |||
FloatType newSum = sum + correctedValue; | |||
error = (newSum - sum) - correctedValue; | |||
sum = newSum; | |||
} | |||
FloatType sum, error; | |||
}; | |||
//============================================================================== | |||
size_t count; | |||
KahanSum sum, sumSquares; | |||
FloatType minimum, maximum; | |||
}; | |||
#endif // JUCE_STATISTICSACCUMULATOR_H_INCLUDED |
@@ -201,7 +201,7 @@ private: | |||
#endif | |||
//============================================================================== | |||
#elif (JUCE_GCC || JUCE_CLANG) && ! JUCE_MSVC | |||
#elif JUCE_GCC || JUCE_CLANG | |||
#define JUCE_ATOMICS_GCC 1 // GCC with intrinsics | |||
#if JUCE_IOS || JUCE_ANDROID // (64-bit ops will compile but not link on these mobile OSes) | |||
@@ -157,7 +157,7 @@ inline uint32 ByteOrder::swap (uint32 n) noexcept | |||
{ | |||
#if JUCE_MAC || JUCE_IOS | |||
return OSSwapInt32 (n); | |||
#elif JUCE_GCC && JUCE_INTEL && ! JUCE_NO_INLINE_ASM | |||
#elif (JUCE_GCC || JUCE_CLANG) && JUCE_INTEL && ! JUCE_NO_INLINE_ASM | |||
asm("bswap %%eax" : "=a"(n) : "a"(n)); | |||
return n; | |||
#elif JUCE_USE_MSVC_INTRINSICS | |||
@@ -180,6 +180,12 @@ private: | |||
//============================================================================== | |||
ScopedPointer<ObjectType> object; | |||
bool shouldDelete; | |||
// This is here to avoid people accidentally taking a second owned copy of | |||
// a scoped pointer, which is almost certainly not what you intended to do! | |||
// If you hit a problem with this, you probably meant to say | |||
// myPointer.setOwned (myScopedPointer.release()) | |||
void setOwned (const ScopedPointer<ObjectType>&) JUCE_DELETED_FUNCTION; | |||
}; | |||
@@ -0,0 +1,48 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the juce_core module of the JUCE library. | |||
Copyright (c) 2016 - ROLI Ltd. | |||
Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
or without fee is hereby granted, provided that the above copyright notice and this | |||
permission notice appear in all copies. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
------------------------------------------------------------------------------ | |||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
using any other modules, be sure to check that you also comply with their license. | |||
For more details, visit www.juce.com | |||
============================================================================== | |||
*/ | |||
#if ! JUCE_ANDROID // We currently don't request runtime permissions on any other platform | |||
// than Android, so this file contains a dummy implementation for those. | |||
// This may change in the future. | |||
void RuntimePermissions::request (PermissionID /*permission*/, Callback callback) | |||
{ | |||
callback (true); | |||
} | |||
bool RuntimePermissions::isRequired (PermissionID /*permission*/) | |||
{ | |||
return false; | |||
} | |||
bool RuntimePermissions::isGranted (PermissionID /*permission*/) | |||
{ | |||
return true; | |||
} | |||
#endif |
@@ -0,0 +1,131 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the juce_core module of the JUCE library. | |||
Copyright (c) 2016 - ROLI Ltd. | |||
Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
or without fee is hereby granted, provided that the above copyright notice and this | |||
permission notice appear in all copies. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
------------------------------------------------------------------------------ | |||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
using any other modules, be sure to check that you also comply with their license. | |||
For more details, visit www.juce.com | |||
============================================================================== | |||
*/ | |||
#ifndef JUCE_RUNTIMEPERMISSIONS_H_INCLUDED | |||
#define JUCE_RUNTIMEPERMISSIONS_H_INCLUDED | |||
//============================================================================== | |||
/** | |||
Class to handle app runtime permissions for certain functionality on some platforms. | |||
The use of this class is currently only required if the app should run on | |||
Android API level 23 and higher. | |||
On lower API levels, the permissions are specified in the app manifest. On iOS, | |||
runtime permission requests are handled automatically by the Apple APIs and not | |||
manually in the app code. On Windows, OS X, and Linux, runtime permissions are not | |||
used at all. In all these cases, request() will simply call through to the | |||
callback with no overhead and pass true (making it safe to use on all platforms). | |||
For example, to enable audio recording on Android in your cross-platform app, | |||
you could modify your code as follows: | |||
Old code: | |||
audioDeviceManager.initialise (2, 2, nullptr, true, String(), nullptr); | |||
New code: | |||
RuntimePermissions::request ( | |||
RuntimePermissions::audioRecording, | |||
[this] (bool wasGranted) | |||
{ | |||
if (! wasGranted) | |||
{ | |||
// e.g. display an error or initialise with 0 input channels | |||
return; | |||
} | |||
audioDeviceManager.initialise (2, 2, nullptr, true, String(), nullptr); | |||
} | |||
); | |||
*/ | |||
class JUCE_API RuntimePermissions | |||
{ | |||
public: | |||
//========================================================================== | |||
enum PermissionID | |||
{ | |||
/** Permission to access the microphone (required on Android). | |||
You need to request this, for example, to initialise an AudioDeviceManager with | |||
a non-zero number of input channels, and to open the default audio input device. | |||
*/ | |||
recordAudio = 1, | |||
/** Permission to scan for and pair to Bluetooth MIDI devices (required on Android). | |||
You need to request this before calling BluetoothMidiDevicePairingDialogue::open(), | |||
otherwise no devices will be found. | |||
*/ | |||
bluetoothMidi = 2, | |||
}; | |||
//========================================================================== | |||
/** Function type of runtime permission request callbacks. */ | |||
#if JUCE_COMPILER_SUPPORTS_LAMBDAS | |||
typedef std::function<void (bool)> Callback; | |||
#else | |||
typedef void (*Callback) (bool); | |||
#endif | |||
//========================================================================== | |||
/** Call this method to request a runtime permission. | |||
@param permission The PermissionID of the permission you want to request. | |||
@param callback The callback to be called after the request has been granted | |||
or denied; the argument passed will be true if the permission | |||
has been granted and false otherwise. | |||
If no runtime request is required or possible to obtain the permission, the | |||
callback will be called immediately. The argument passed in will be true | |||
if the permission is granted or no permission is required on this platform, | |||
and false otherwise. | |||
If a runtime request is required to obtain the permission, the callback | |||
will be called asynchronously after the OS has granted or denied the requested | |||
permission (typically by displaying a dialog box to the user and waiting until | |||
the user has responded). | |||
*/ | |||
static void request (PermissionID permission, Callback callback); | |||
/** Returns whether a runtime request is required to obtain the permission | |||
on the current platform. | |||
*/ | |||
static bool isRequired (PermissionID permission); | |||
/** Returns true if the app has been already granted this permission, either | |||
via a previous runtime request or otherwise, or no permission is necessary. | |||
Note that this can be false even if isRequired returns false. In this case, | |||
the permission can not be obtained at all at runtime. | |||
*/ | |||
static bool isGranted (PermissionID permission); | |||
}; | |||
#endif // JUCE_RUNTIMEPERMISSIONS_H_INCLUDED |
@@ -0,0 +1,14 @@ | |||
private native void androidRuntimePermissionsCallback (boolean permissionWasGranted, long ptrToCallback); | |||
@Override | |||
public void onRequestPermissionsResult (int permissionID, String permissions[], int[] grantResults) | |||
{ | |||
boolean permissionsGranted = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED); | |||
if (! permissionsGranted) | |||
Log.d ("JUCE", "onRequestPermissionsResult: runtime permission was DENIED: " + getAndroidPermissionName (permissionID)); | |||
Long ptrToCallback = permissionCallbackPtrMap.get (permissionID); | |||
permissionCallbackPtrMap.remove (permissionID); | |||
androidRuntimePermissionsCallback (permissionsGranted, ptrToCallback); | |||
} |
@@ -30,14 +30,14 @@ import android.content.DialogInterface; | |||
import android.content.Context; | |||
import android.content.Intent; | |||
import android.content.res.Configuration; | |||
import android.content.pm.PackageInfo; | |||
import android.content.pm.PackageManager; | |||
import android.net.Uri; | |||
import android.os.Bundle; | |||
import android.os.Looper; | |||
import android.os.Handler; | |||
import android.os.Build; | |||
import android.os.Process; | |||
import android.os.ParcelUuid; | |||
import android.os.Environment; | |||
import android.view.*; | |||
import android.view.inputmethod.BaseInputConnection; | |||
import android.view.inputmethod.EditorInfo; | |||
@@ -49,19 +49,16 @@ import android.text.InputType; | |||
import android.util.DisplayMetrics; | |||
import android.util.Log; | |||
import java.lang.Runnable; | |||
import java.util.List; | |||
import java.util.Arrays; | |||
import java.util.ArrayList; | |||
import java.util.HashSet; | |||
import java.util.Hashtable; | |||
import java.util.TimerTask; | |||
import java.util.*; | |||
import java.io.*; | |||
import java.net.URL; | |||
import java.net.HttpURLConnection; | |||
import android.media.AudioManager; | |||
import android.media.MediaScannerConnection; | |||
import android.media.MediaScannerConnection.MediaScannerConnectionClient; | |||
import android.support.v4.content.ContextCompat; | |||
import android.support.v4.app.ActivityCompat; | |||
import android.Manifest; | |||
$$JuceAndroidMidiImports$$ // If you get an error here, you need to re-save your project with the introjucer! | |||
@@ -74,6 +71,73 @@ public class JuceAppActivity extends Activity | |||
System.loadLibrary ("juce_jni"); | |||
} | |||
//============================================================================== | |||
public boolean isPermissionDeclaredInManifest (int permissionID) | |||
{ | |||
String permissionToCheck = getAndroidPermissionName(permissionID); | |||
try | |||
{ | |||
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS); | |||
if (info.requestedPermissions != null) | |||
for (String permission : info.requestedPermissions) | |||
if (permission.equals (permissionToCheck)) | |||
return true; | |||
} | |||
catch (PackageManager.NameNotFoundException e) | |||
{ | |||
Log.d ("JUCE", "isPermissionDeclaredInManifest: PackageManager.NameNotFoundException = " + e.toString()); | |||
} | |||
Log.d ("JUCE", "isPermissionDeclaredInManifest: could not find requested permission " + permissionToCheck); | |||
return false; | |||
} | |||
//============================================================================== | |||
// these have to match the values of enum PermissionID in C++ class RuntimePermissions: | |||
private static final int JUCE_PERMISSIONS_RECORD_AUDIO = 1; | |||
private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2; | |||
private static String getAndroidPermissionName (int permissionID) | |||
{ | |||
switch (permissionID) | |||
{ | |||
case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO; | |||
case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION; | |||
} | |||
// unknown permission ID! | |||
assert false; | |||
return new String(); | |||
} | |||
public boolean isPermissionGranted (int permissionID) | |||
{ | |||
return ContextCompat.checkSelfPermission (this, getAndroidPermissionName (permissionID)) == PackageManager.PERMISSION_GRANTED; | |||
} | |||
private Map<Integer, Long> permissionCallbackPtrMap; | |||
public void requestRuntimePermission (int permissionID, long ptrToCallback) | |||
{ | |||
String permissionName = getAndroidPermissionName (permissionID); | |||
if (ContextCompat.checkSelfPermission (this, permissionName) != PackageManager.PERMISSION_GRANTED) | |||
{ | |||
// remember callbackPtr, request permissions, and let onRequestPermissionResult call callback asynchronously | |||
permissionCallbackPtrMap.put (permissionID, ptrToCallback); | |||
ActivityCompat.requestPermissions (this, new String[]{permissionName}, permissionID); | |||
} | |||
else | |||
{ | |||
// permissions were already granted before, we can call callback directly | |||
androidRuntimePermissionsCallback (true, ptrToCallback); | |||
} | |||
} | |||
$$JuceAndroidRuntimePermissionsCode$$ // If you get an error here, you need to re-save your project with the introjucer! | |||
//============================================================================== | |||
public static class MidiPortID extends Object | |||
{ | |||
@@ -132,10 +196,13 @@ public class JuceAppActivity extends Activity | |||
super.onCreate (savedInstanceState); | |||
isScreenSaverEnabled = true; | |||
hideActionBar(); | |||
viewHolder = new ViewHolder (this); | |||
setContentView (viewHolder); | |||
setVolumeControlStream (AudioManager.STREAM_MUSIC); | |||
permissionCallbackPtrMap = new HashMap<Integer, Long>(); | |||
} | |||
@Override | |||
@@ -174,6 +241,49 @@ public class JuceAppActivity extends Activity | |||
getApplicationInfo().dataDir); | |||
} | |||
private void hideActionBar() | |||
{ | |||
// get "getActionBar" method | |||
java.lang.reflect.Method getActionBarMethod = null; | |||
try | |||
{ | |||
getActionBarMethod = this.getClass().getMethod ("getActionBar"); | |||
} | |||
catch (SecurityException e) { return; } | |||
catch (NoSuchMethodException e) { return; } | |||
if (getActionBarMethod == null) return; | |||
// invoke "getActionBar" method | |||
Object actionBar = null; | |||
try | |||
{ | |||
actionBar = getActionBarMethod.invoke (this); | |||
} | |||
catch (java.lang.IllegalArgumentException e) { return; } | |||
catch (java.lang.IllegalAccessException e) { return; } | |||
catch (java.lang.reflect.InvocationTargetException e) { return; } | |||
if (actionBar == null) return; | |||
// get "hide" method | |||
java.lang.reflect.Method actionBarHideMethod = null; | |||
try | |||
{ | |||
actionBarHideMethod = actionBar.getClass().getMethod ("hide"); | |||
} | |||
catch (SecurityException e) { return; } | |||
catch (NoSuchMethodException e) { return; } | |||
if (actionBarHideMethod == null) return; | |||
// invoke "hide" method | |||
try | |||
{ | |||
actionBarHideMethod.invoke (actionBar); | |||
} | |||
catch (java.lang.IllegalArgumentException e) {} | |||
catch (java.lang.IllegalAccessException e) {} | |||
catch (java.lang.reflect.InvocationTargetException e) {} | |||
} | |||
//============================================================================== | |||
private native void launchApp (String appFile, String appDataDir); | |||
private native void quitApp(); | |||
@@ -691,7 +801,7 @@ public class JuceAppActivity extends Activity | |||
int format, int width, int height); | |||
} | |||
public NativeSurfaceView createNativeSurfaceView(long nativeSurfacePtr) | |||
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr) | |||
{ | |||
return new NativeSurfaceView (this, nativeSurfacePtr); | |||
} | |||
@@ -917,6 +1027,17 @@ public class JuceAppActivity extends Activity | |||
: locale.getDisplayLanguage (java.util.Locale.US); | |||
} | |||
private static final String getFileLocation (String type) | |||
{ | |||
return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath(); | |||
} | |||
public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); } | |||
public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); } | |||
public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); } | |||
public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); } | |||
public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); } | |||
//============================================================================== | |||
private final class SingleMediaScanner implements MediaScannerConnectionClient | |||
{ | |||
@@ -1041,23 +1162,24 @@ public class JuceAppActivity extends Activity | |||
return null; | |||
java.lang.reflect.Method method; | |||
try { | |||
try | |||
{ | |||
method = obj.getClass().getMethod ("getProperty", String.class); | |||
} catch (SecurityException e) { | |||
return null; | |||
} catch (NoSuchMethodException e) { | |||
return null; | |||
} | |||
catch (SecurityException e) { return null; } | |||
catch (NoSuchMethodException e) { return null; } | |||
if (method == null) | |||
return null; | |||
try { | |||
try | |||
{ | |||
return (String) method.invoke (obj, property); | |||
} catch (java.lang.IllegalArgumentException e) { | |||
} catch (java.lang.IllegalAccessException e) { | |||
} catch (java.lang.reflect.InvocationTargetException e) { | |||
} | |||
catch (java.lang.IllegalArgumentException e) {} | |||
catch (java.lang.IllegalAccessException e) {} | |||
catch (java.lang.reflect.InvocationTargetException e) {} | |||
return null; | |||
} | |||
@@ -1075,8 +1197,9 @@ public class JuceAppActivity extends Activity | |||
private static class JuceThread extends Thread | |||
{ | |||
public JuceThread (long host) | |||
public JuceThread (long host, String threadName, long threadStackSize) | |||
{ | |||
super (null, null, threadName, threadStackSize); | |||
_this = host; | |||
} | |||
@@ -1089,9 +1212,8 @@ public class JuceAppActivity extends Activity | |||
private long _this; | |||
} | |||
public final Thread createNewThread(long host) | |||
public final Thread createNewThread(long host, String threadName, long threadStackSize) | |||
{ | |||
return new JuceThread(host); | |||
return new JuceThread(host, threadName, threadStackSize); | |||
} | |||
} |